202 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* Text Widget/Automatic scrolling
 | 
						|
 *
 | 
						|
 * This example demonstrates how to use the gravity of 
 | 
						|
 * GtkTextMarks to keep a text view scrolled to the bottom
 | 
						|
 * while appending text.
 | 
						|
 */
 | 
						|
 | 
						|
#include <gtk/gtk.h>
 | 
						|
#include "demo-common.h"
 | 
						|
 | 
						|
/* Scroll to the end of the buffer.
 | 
						|
 */
 | 
						|
static gboolean
 | 
						|
scroll_to_end (GtkTextView *textview)
 | 
						|
{
 | 
						|
  GtkTextBuffer *buffer;
 | 
						|
  GtkTextIter iter;
 | 
						|
  GtkTextMark *mark;
 | 
						|
  char *spaces;
 | 
						|
  static int count;
 | 
						|
 | 
						|
  buffer = gtk_text_view_get_buffer (textview);
 | 
						|
  
 | 
						|
  /* Get "end" mark. It's located at the end of buffer because 
 | 
						|
   * of right gravity
 | 
						|
   */
 | 
						|
  mark = gtk_text_buffer_get_mark (buffer, "end");
 | 
						|
  gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
 | 
						|
 | 
						|
  /* and insert some text at its position, the iter will be 
 | 
						|
   * revalidated after insertion to point to the end of inserted text
 | 
						|
   */
 | 
						|
  spaces = g_strnfill (count++, ' ');
 | 
						|
  gtk_text_buffer_insert (buffer, &iter, "\n", -1);
 | 
						|
  gtk_text_buffer_insert (buffer, &iter, spaces, -1);
 | 
						|
  gtk_text_buffer_insert (buffer, &iter,
 | 
						|
                          "Scroll to end scroll to end scroll "
 | 
						|
                          "to end scroll to end ",
 | 
						|
                          -1);
 | 
						|
  g_free (spaces);
 | 
						|
 | 
						|
  /* Now scroll the end mark onscreen.
 | 
						|
   */
 | 
						|
  gtk_text_view_scroll_mark_onscreen (textview, mark);
 | 
						|
 | 
						|
  /* Emulate typewriter behavior, shift to the left if we 
 | 
						|
   * are far enough to the right.
 | 
						|
   */
 | 
						|
  if (count > 150)
 | 
						|
    count = 0;
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
/* Scroll to the bottom of the buffer.
 | 
						|
 */
 | 
						|
static gboolean
 | 
						|
scroll_to_bottom (GtkTextView *textview)
 | 
						|
{
 | 
						|
  GtkTextBuffer *buffer;
 | 
						|
  GtkTextIter iter;
 | 
						|
  GtkTextMark *mark;
 | 
						|
  char *spaces;
 | 
						|
  static int count;
 | 
						|
 | 
						|
  buffer = gtk_text_view_get_buffer (textview);
 | 
						|
  
 | 
						|
  /* Get end iterator */
 | 
						|
  gtk_text_buffer_get_end_iter (buffer, &iter);
 | 
						|
 | 
						|
  /* and insert some text at it, the iter will be revalidated
 | 
						|
   * after insertion to point to the end of inserted text
 | 
						|
   */
 | 
						|
  spaces = g_strnfill (count++, ' ');
 | 
						|
  gtk_text_buffer_insert (buffer, &iter, "\n", -1);
 | 
						|
  gtk_text_buffer_insert (buffer, &iter, spaces, -1);
 | 
						|
  gtk_text_buffer_insert (buffer, &iter,
 | 
						|
                          "Scroll to bottom scroll to bottom scroll "
 | 
						|
                          "to bottom scroll to bottom",
 | 
						|
                          -1);
 | 
						|
  g_free (spaces);
 | 
						|
 | 
						|
  /* Move the iterator to the beginning of line, so we don't scroll 
 | 
						|
   * in horizontal direction 
 | 
						|
   */
 | 
						|
  gtk_text_iter_set_line_offset (&iter, 0);
 | 
						|
  
 | 
						|
  /* and place the mark at iter. the mark will stay there after we
 | 
						|
   * insert some text at the end because it has right gravity.
 | 
						|
   */
 | 
						|
  mark = gtk_text_buffer_get_mark (buffer, "scroll");
 | 
						|
  gtk_text_buffer_move_mark (buffer, mark, &iter);
 | 
						|
  
 | 
						|
  /* Scroll the mark onscreen.
 | 
						|
   */
 | 
						|
  gtk_text_view_scroll_mark_onscreen (textview, mark);
 | 
						|
 | 
						|
  /* Shift text back if we got enough to the right.
 | 
						|
   */
 | 
						|
  if (count > 40)
 | 
						|
    count = 0;
 | 
						|
 | 
						|
  return TRUE;
 | 
						|
}
 | 
						|
 | 
						|
static guint
 | 
						|
setup_scroll (GtkTextView *textview,
 | 
						|
              gboolean     to_end)
 | 
						|
{
 | 
						|
  GtkTextBuffer *buffer;
 | 
						|
  GtkTextIter iter;
 | 
						|
 | 
						|
  buffer = gtk_text_view_get_buffer (textview);
 | 
						|
  gtk_text_buffer_get_end_iter (buffer, &iter);
 | 
						|
 | 
						|
  if (to_end)
 | 
						|
  {
 | 
						|
    /* If we want to scroll to the end, including horizontal scrolling,
 | 
						|
     * then we just create a mark with right gravity at the end of the 
 | 
						|
     * buffer. It will stay at the end unless explicitely moved with 
 | 
						|
     * gtk_text_buffer_move_mark.
 | 
						|
     */
 | 
						|
    gtk_text_buffer_create_mark (buffer, "end", &iter, FALSE);
 | 
						|
    
 | 
						|
    /* Add scrolling timeout. */
 | 
						|
    return g_timeout_add (50, (GSourceFunc) scroll_to_end, textview);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    /* If we want to scroll to the bottom, but not scroll horizontally, 
 | 
						|
     * then an end mark won't do the job. Just create a mark so we can 
 | 
						|
     * use it with gtk_text_view_scroll_mark_onscreen, we'll position it
 | 
						|
     * explicitely when needed. Use left gravity so the mark stays where 
 | 
						|
     * we put it after inserting new text.
 | 
						|
     */
 | 
						|
    gtk_text_buffer_create_mark (buffer, "scroll", &iter, TRUE);
 | 
						|
    
 | 
						|
    /* Add scrolling timeout. */
 | 
						|
    return g_timeout_add (100, (GSourceFunc) scroll_to_bottom, textview);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
remove_timeout (GtkWidget *window,
 | 
						|
                gpointer   timeout)
 | 
						|
{
 | 
						|
  g_source_remove (GPOINTER_TO_UINT (timeout));
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
create_text_view (GtkWidget *hbox,
 | 
						|
                  gboolean   to_end)
 | 
						|
{
 | 
						|
  GtkWidget *swindow;
 | 
						|
  GtkWidget *textview;
 | 
						|
  guint timeout;
 | 
						|
 | 
						|
  swindow = gtk_scrolled_window_new (NULL, NULL);
 | 
						|
  gtk_box_pack_start (GTK_BOX (hbox), swindow, TRUE, TRUE, 0);
 | 
						|
  textview = gtk_text_view_new ();
 | 
						|
  gtk_container_add (GTK_CONTAINER (swindow), textview);
 | 
						|
 | 
						|
  timeout = setup_scroll (GTK_TEXT_VIEW (textview), to_end);
 | 
						|
 | 
						|
  /* Remove the timeout in destroy handler, so we don't try to
 | 
						|
   * scroll destroyed widget. 
 | 
						|
   */
 | 
						|
  g_signal_connect (textview, "destroy",
 | 
						|
                    G_CALLBACK (remove_timeout),
 | 
						|
                    GUINT_TO_POINTER (timeout));
 | 
						|
}
 | 
						|
 | 
						|
GtkWidget *
 | 
						|
do_textscroll (GtkWidget *do_widget)
 | 
						|
{
 | 
						|
  static GtkWidget *window = NULL;
 | 
						|
 | 
						|
  if (!window)
 | 
						|
    {
 | 
						|
      GtkWidget *hbox;
 | 
						|
 | 
						|
      window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 | 
						|
      g_signal_connect (window, "destroy",
 | 
						|
			G_CALLBACK (gtk_widget_destroyed), &window);
 | 
						|
      gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
 | 
						|
      
 | 
						|
      hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
 | 
						|
      gtk_box_set_homogeneous (GTK_BOX (hbox), TRUE);
 | 
						|
      gtk_container_add (GTK_CONTAINER (window), hbox);
 | 
						|
 | 
						|
      create_text_view (hbox, TRUE);
 | 
						|
      create_text_view (hbox, FALSE);
 | 
						|
    }
 | 
						|
 | 
						|
  if (!gtk_widget_get_visible (window))
 | 
						|
      gtk_widget_show_all (window);
 | 
						|
  else
 | 
						|
      gtk_widget_destroy (window);
 | 
						|
 | 
						|
  return window;
 | 
						|
}
 |