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

source code of gtk/demos/gtk-demo/textscroll.c