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