]> Pileus Git - ~andy/gtk/blob - demos/gtk-demo/textscroll.c
Deprecate widget flag: GTK_WIDGET_VISIBLE
[~andy/gtk] / demos / gtk-demo / textscroll.c
1 /* Text Widget/Automatic scrolling
2  *
3  * This example demonstrates how to use the gravity of 
4  * GtkTextMarks to keep a text view scrolled to the bottom
5  * while appending text.
6  */
7
8 #include <gtk/gtk.h>
9 #include "demo-common.h"
10
11 /* Scroll to the end of the buffer.
12  */
13 static gboolean
14 scroll_to_end (GtkTextView *textview)
15 {
16   GtkTextBuffer *buffer;
17   GtkTextIter iter;
18   GtkTextMark *mark;
19   char *spaces;
20   static int count;
21
22   buffer = gtk_text_view_get_buffer (textview);
23   
24   /* Get "end" mark. It's located at the end of buffer because 
25    * of right gravity
26    */
27   mark = gtk_text_buffer_get_mark (buffer, "end");
28   gtk_text_buffer_get_iter_at_mark (buffer, &iter, mark);
29
30   /* and insert some text at its position, the iter will be 
31    * revalidated after insertion to point to the end of inserted text
32    */
33   spaces = g_strnfill (count++, ' ');
34   gtk_text_buffer_insert (buffer, &iter, "\n", -1);
35   gtk_text_buffer_insert (buffer, &iter, spaces, -1);
36   gtk_text_buffer_insert (buffer, &iter,
37                           "Scroll to end scroll to end scroll "
38                           "to end scroll to end ",
39                           -1);
40   g_free (spaces);
41
42   /* Now scroll the end mark onscreen.
43    */
44   gtk_text_view_scroll_mark_onscreen (textview, mark);
45
46   /* Emulate typewriter behavior, shift to the left if we 
47    * are far enough to the right.
48    */
49   if (count > 150)
50     count = 0;
51
52   return TRUE;
53 }
54
55 /* Scroll to the bottom of the buffer.
56  */
57 static gboolean
58 scroll_to_bottom (GtkTextView *textview)
59 {
60   GtkTextBuffer *buffer;
61   GtkTextIter iter;
62   GtkTextMark *mark;
63   char *spaces;
64   static int count;
65
66   buffer = gtk_text_view_get_buffer (textview);
67   
68   /* Get end iterator */
69   gtk_text_buffer_get_end_iter (buffer, &iter);
70
71   /* and insert some text at it, the iter will be revalidated
72    * after insertion to point to the end of inserted text
73    */
74   spaces = g_strnfill (count++, ' ');
75   gtk_text_buffer_insert (buffer, &iter, "\n", -1);
76   gtk_text_buffer_insert (buffer, &iter, spaces, -1);
77   gtk_text_buffer_insert (buffer, &iter,
78                           "Scroll to bottom scroll to bottom scroll "
79                           "to bottom scroll to bottom",
80                           -1);
81   g_free (spaces);
82
83   /* Move the iterator to the beginning of line, so we don't scroll 
84    * in horizontal direction 
85    */
86   gtk_text_iter_set_line_offset (&iter, 0);
87   
88   /* and place the mark at iter. the mark will stay there after we
89    * insert some text at the end because it has right gravity.
90    */
91   mark = gtk_text_buffer_get_mark (buffer, "scroll");
92   gtk_text_buffer_move_mark (buffer, mark, &iter);
93   
94   /* Scroll the mark onscreen.
95    */
96   gtk_text_view_scroll_mark_onscreen (textview, mark);
97
98   /* Shift text back if we got enough to the right.
99    */
100   if (count > 40)
101     count = 0;
102
103   return TRUE;
104 }
105
106 static guint
107 setup_scroll (GtkTextView *textview,
108               gboolean     to_end)
109 {
110   GtkTextBuffer *buffer;
111   GtkTextIter iter;
112
113   buffer = gtk_text_view_get_buffer (textview);
114   gtk_text_buffer_get_end_iter (buffer, &iter);
115
116   if (to_end)
117   {
118     /* If we want to scroll to the end, including horizontal scrolling,
119      * then we just create a mark with right gravity at the end of the 
120      * buffer. It will stay at the end unless explicitely moved with 
121      * gtk_text_buffer_move_mark.
122      */
123     gtk_text_buffer_create_mark (buffer, "end", &iter, FALSE);
124     
125     /* Add scrolling timeout. */
126     return g_timeout_add (50, (GSourceFunc) scroll_to_end, textview);
127   }
128   else
129   {
130     /* If we want to scroll to the bottom, but not scroll horizontally, 
131      * then an end mark won't do the job. Just create a mark so we can 
132      * use it with gtk_text_view_scroll_mark_onscreen, we'll position it
133      * explicitely when needed. Use left gravity so the mark stays where 
134      * we put it after inserting new text.
135      */
136     gtk_text_buffer_create_mark (buffer, "scroll", &iter, TRUE);
137     
138     /* Add scrolling timeout. */
139     return g_timeout_add (100, (GSourceFunc) scroll_to_bottom, textview);
140   }
141 }
142
143 static void
144 remove_timeout (GtkWidget *window,
145                 gpointer   timeout)
146 {
147   g_source_remove (GPOINTER_TO_UINT (timeout));
148 }
149
150 static void
151 create_text_view (GtkWidget *hbox,
152                   gboolean   to_end)
153 {
154   GtkWidget *swindow;
155   GtkWidget *textview;
156   guint timeout;
157
158   swindow = gtk_scrolled_window_new (NULL, NULL);
159   gtk_box_pack_start (GTK_BOX (hbox), swindow, TRUE, TRUE, 0);
160   textview = gtk_text_view_new ();
161   gtk_container_add (GTK_CONTAINER (swindow), textview);
162
163   timeout = setup_scroll (GTK_TEXT_VIEW (textview), to_end);
164
165   /* Remove the timeout in destroy handler, so we don't try to
166    * scroll destroyed widget. 
167    */
168   g_signal_connect (textview, "destroy",
169                     G_CALLBACK (remove_timeout),
170                     GUINT_TO_POINTER (timeout));
171 }
172
173 GtkWidget *
174 do_textscroll (GtkWidget *do_widget)
175 {
176   static GtkWidget *window = NULL;
177
178   if (!window)
179     {
180       GtkWidget *hbox;
181
182       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
183       g_signal_connect (window, "destroy",
184                         G_CALLBACK (gtk_widget_destroyed), &window);
185       gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
186       
187       hbox = gtk_hbox_new (TRUE, 6);
188       gtk_container_add (GTK_CONTAINER (window), hbox);
189
190       create_text_view (hbox, TRUE);
191       create_text_view (hbox, FALSE);
192     }
193
194   if (!gtk_widget_get_visible (window))
195       gtk_widget_show_all (window);
196   else
197       gtk_widget_destroy (window);
198
199   return window;
200 }