1 /* Text Widget/Hypertext
3 * Usually, tags modify the appearance of text in the view, e.g. making it
4 * bold or colored or underlined. But tags are not restricted to appearance.
5 * They can also affect the behavior of mouse and key presses, as this demo
10 #include <gdk/gdkkeysyms.h>
12 /* Inserts a piece of text into the buffer, giving it the usual
13 * appearance of a hyperlink in a web browser: blue and underlined.
14 * Additionally, attaches some data on the tag, to make it recognizable
18 insert_link (GtkTextBuffer *buffer,
25 tag = gtk_text_buffer_create_tag (buffer, NULL,
27 "underline", PANGO_UNDERLINE_SINGLE,
29 g_object_set_data (G_OBJECT (tag), "page", GINT_TO_POINTER (page));
30 gtk_text_buffer_insert_with_tags (buffer, iter, text, -1, tag, NULL);
33 /* Fills the buffer with text and interspersed links. In any real
34 * hypertext app, this method would parse a file to identify the links.
37 show_page (GtkTextBuffer *buffer,
42 gtk_text_buffer_set_text (buffer, "", 0);
43 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
46 gtk_text_buffer_insert (buffer, &iter, "Some text to show that simple ", -1);
47 insert_link (buffer, &iter, "hypertext", 3);
48 gtk_text_buffer_insert (buffer, &iter, " can easily be realized with ", -1);
49 insert_link (buffer, &iter, "tags", 2);
50 gtk_text_buffer_insert (buffer, &iter, ".", -1);
54 gtk_text_buffer_insert (buffer, &iter,
55 "A tag is an attribute that can be applied to some range of text. "
56 "For example, a tag might be called \"bold\" and make the text inside "
57 "the tag bold. However, the tag concept is more general than that; "
58 "tags don't have to affect appearance. They can instead affect the "
59 "behavior of mouse and key presses, \"lock\" a range of text so the "
60 "user can't edit it, or countless other things.\n", -1);
61 insert_link (buffer, &iter, "Go back", 1);
67 tag = gtk_text_buffer_create_tag (buffer, NULL,
68 "weight", PANGO_WEIGHT_BOLD,
70 gtk_text_buffer_insert_with_tags (buffer, &iter, "hypertext:\n", -1, tag, NULL);
71 gtk_text_buffer_insert (buffer, &iter,
72 "machine-readable text that is not sequential but is organized "
73 "so that related items of information are connected.\n", -1);
74 insert_link (buffer, &iter, "Go back", 1);
78 /* Looks at all tags covering the position of iter in the text view,
79 * and if one of them is a link, follow it by showing the page identified
80 * by the data attached to it.
83 follow_if_link (GtkWidget *text_view,
86 GSList *tags = NULL, *tagp = NULL;
88 tags = gtk_text_iter_get_tags (iter);
89 for (tagp = tags; tagp != NULL; tagp = tagp->next)
91 GtkTextTag *tag = tagp->data;
92 gint page = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "page"));
96 show_page (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)), page);
105 /* Links can be activated by pressing Enter.
108 key_press_event (GtkWidget *text_view,
112 GtkTextBuffer *buffer;
114 switch (event->keyval)
118 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
119 gtk_text_buffer_get_iter_at_mark (buffer, &iter,
120 gtk_text_buffer_get_insert (buffer));
121 follow_if_link (text_view, &iter);
131 /* Links can also be activated by clicking.
134 event_after (GtkWidget *text_view,
137 GtkTextIter start, end, iter;
138 GtkTextBuffer *buffer;
139 GdkEventButton *event;
142 if (ev->type != GDK_BUTTON_RELEASE)
145 event = (GdkEventButton *)ev;
147 if (event->button != 1)
150 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
152 /* we shouldn't follow a link if the user has selected something */
153 gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
154 if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
157 gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
158 GTK_TEXT_WINDOW_WIDGET,
159 event->x, event->y, &x, &y);
161 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
163 follow_if_link (text_view, &iter);
168 static gboolean hovering_over_link = FALSE;
169 static GdkCursor *hand_cursor = NULL;
170 static GdkCursor *regular_cursor = NULL;
172 /* Looks at all tags covering the position (x, y) in the text view,
173 * and if one of them is a link, change the cursor to the "hands" cursor
174 * typically used by web browsers.
177 set_cursor_if_appropriate (GtkTextView *text_view,
181 GSList *tags = NULL, *tagp = NULL;
183 gboolean hovering = FALSE;
185 gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
187 tags = gtk_text_iter_get_tags (&iter);
188 for (tagp = tags; tagp != NULL; tagp = tagp->next)
190 GtkTextTag *tag = tagp->data;
191 gint page = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "page"));
200 if (hovering != hovering_over_link)
202 hovering_over_link = hovering;
204 if (hovering_over_link)
205 gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), hand_cursor);
207 gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), regular_cursor);
214 /* Update the cursor image if the pointer moved.
217 motion_notify_event (GtkWidget *text_view,
218 GdkEventMotion *event)
222 gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
223 GTK_TEXT_WINDOW_WIDGET,
224 event->x, event->y, &x, &y);
226 set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y);
228 gdk_window_get_pointer (text_view->window, NULL, NULL, NULL);
232 /* Also update the cursor image if the window becomes visible
233 * (e.g. when a window covering it got iconified).
236 visibility_notify_event (GtkWidget *text_view,
237 GdkEventVisibility *event)
241 gdk_window_get_pointer (text_view->window, &wx, &wy, NULL);
243 gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
244 GTK_TEXT_WINDOW_WIDGET,
247 set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by);
253 do_hypertext (GtkWidget *do_widget)
255 static GtkWidget *window = NULL;
261 GtkTextBuffer *buffer;
263 hand_cursor = gdk_cursor_new (GDK_HAND2);
264 regular_cursor = gdk_cursor_new (GDK_XTERM);
266 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
267 gtk_window_set_screen (GTK_WINDOW (window),
268 gtk_widget_get_screen (do_widget));
269 gtk_window_set_default_size (GTK_WINDOW (window),
272 g_signal_connect (window, "destroy",
273 G_CALLBACK (gtk_widget_destroyed), &window);
275 gtk_window_set_title (GTK_WINDOW (window), "Hypertext");
276 gtk_container_set_border_width (GTK_CONTAINER (window), 0);
278 view = gtk_text_view_new ();
279 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD);
280 g_signal_connect (view, "key-press-event",
281 G_CALLBACK (key_press_event), NULL);
282 g_signal_connect (view, "event-after",
283 G_CALLBACK (event_after), NULL);
284 g_signal_connect (view, "motion-notify-event",
285 G_CALLBACK (motion_notify_event), NULL);
286 g_signal_connect (view, "visibility-notify-event",
287 G_CALLBACK (visibility_notify_event), NULL);
289 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
291 sw = gtk_scrolled_window_new (NULL, NULL);
292 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
293 GTK_POLICY_AUTOMATIC,
294 GTK_POLICY_AUTOMATIC);
295 gtk_container_add (GTK_CONTAINER (window), sw);
296 gtk_container_add (GTK_CONTAINER (sw), view);
298 show_page (buffer, 1);
300 gtk_widget_show_all (sw);
303 if (!gtk_widget_get_visible (window))
305 gtk_widget_show (window);
309 gtk_widget_destroy (window);