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
gboolean hovering_over_link = FALSE;
169
GdkCursor *hand_cursor = NULL;
170
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;
182
GtkTextBuffer *buffer;
184
gboolean hovering = FALSE;
186
buffer = gtk_text_view_get_buffer (text_view);
188
gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
190
tags = gtk_text_iter_get_tags (&iter);
191
for (tagp = tags; tagp != NULL; tagp = tagp->next)
193
GtkTextTag *tag = tagp->data;
194
gint page = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "page"));
203
if (hovering != hovering_over_link)
205
hovering_over_link = hovering;
207
if (hovering_over_link)
208
gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), hand_cursor);
210
gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), regular_cursor);
217
/* Update the cursor image if the pointer moved.
220
motion_notify_event (GtkWidget *text_view,
221
GdkEventMotion *event)
225
gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
226
GTK_TEXT_WINDOW_WIDGET,
227
event->x, event->y, &x, &y);
229
set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y);
231
gdk_window_get_pointer (text_view->window, NULL, NULL, NULL);
235
/* Also update the cursor image if the window becomes visible
236
* (e.g. when a window covering it got iconified).
239
visibility_notify_event (GtkWidget *text_view,
240
GdkEventVisibility *event)
244
gdk_window_get_pointer (text_view->window, &wx, &wy, NULL);
246
gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
247
GTK_TEXT_WINDOW_WIDGET,
250
set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by);
256
do_hypertext (GtkWidget *do_widget)
258
static GtkWidget *window = NULL;
264
GtkTextBuffer *buffer;
266
hand_cursor = gdk_cursor_new (GDK_HAND2);
267
regular_cursor = gdk_cursor_new (GDK_XTERM);
269
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
270
gtk_window_set_screen (GTK_WINDOW (window),
271
gtk_widget_get_screen (do_widget));
272
gtk_window_set_default_size (GTK_WINDOW (window),
275
g_signal_connect (window, "destroy",
276
G_CALLBACK (gtk_widget_destroyed), &window);
278
gtk_window_set_title (GTK_WINDOW (window), "Hypertext");
279
gtk_container_set_border_width (GTK_CONTAINER (window), 0);
281
view = gtk_text_view_new ();
282
gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD);
283
g_signal_connect (view, "key-press-event",
284
G_CALLBACK (key_press_event), NULL);
285
g_signal_connect (view, "event-after",
286
G_CALLBACK (event_after), NULL);
287
g_signal_connect (view, "motion-notify-event",
288
G_CALLBACK (motion_notify_event), NULL);
289
g_signal_connect (view, "visibility-notify-event",
290
G_CALLBACK (visibility_notify_event), NULL);
292
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
294
sw = gtk_scrolled_window_new (NULL, NULL);
295
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
296
GTK_POLICY_AUTOMATIC,
297
GTK_POLICY_AUTOMATIC);
298
gtk_container_add (GTK_CONTAINER (window), sw);
299
gtk_container_add (GTK_CONTAINER (sw), view);
301
show_page (buffer, 1);
303
gtk_widget_show_all (sw);
306
if (!GTK_WIDGET_VISIBLE (window))
308
gtk_widget_show (window);
312
gtk_widget_destroy (window);