summaryrefslogtreecommitdiff
path: root/demos/gtk-demo/textscroll.c
blob: 7da926dfb5a0727d9d5063c4e9fbf9a9031085d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/* Text View/Automatic Scrolling
 * #Keywords: GtkTextView, GtkScrolledWindow
 *
 * 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>

/* Scroll to the end of the buffer.
 */
static gboolean
scroll_to_end (GtkTextView *textview)
{
  GtkTextBuffer *buffer;
  GtkTextIter iter;
  GtkTextMark *mark;
  char *spaces;
  char *text;
  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);
  text = g_strdup_printf ("Scroll to end scroll to end scroll "
                          "to end scroll to end %d", count);
  gtk_text_buffer_insert (buffer, &iter, text, -1);
  g_free (spaces);
  g_free (text);

  /* 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 G_SOURCE_CONTINUE;
}

/* Scroll to the bottom of the buffer.
 */
static gboolean
scroll_to_bottom (GtkTextView *textview)
{
  GtkTextBuffer *buffer;
  GtkTextIter iter;
  GtkTextMark *mark;
  char *spaces;
  char *text;
  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);
  text = g_strdup_printf ("Scroll to bottom scroll to bottom scroll "
                          "to bottom scroll to bottom %d", count);
  gtk_text_buffer_insert (buffer, &iter, text, -1);
  g_free (spaces);
  g_free (text);

  /* 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 left 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 G_SOURCE_CONTINUE;
}

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 explicitly 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
       * explicitly 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 ();
  gtk_box_append (GTK_BOX (hbox), swindow);
  textview = gtk_text_view_new ();
  gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (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_set_title (GTK_WINDOW (window), "Automatic Scrolling");
      g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&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_window_set_child (GTK_WINDOW (window), hbox);

      create_text_view (hbox, TRUE);
      create_text_view (hbox, FALSE);
    }

  if (!gtk_widget_get_visible (window))
    gtk_widget_set_visible (window, TRUE);
  else
    gtk_window_destroy (GTK_WINDOW (window));

  return window;
}