summaryrefslogtreecommitdiff
path: root/demos
diff options
context:
space:
mode:
authorMatthias Clasen <matthiasc@src.gnome.org>2003-08-05 19:16:01 +0000
committerMatthias Clasen <matthiasc@src.gnome.org>2003-08-05 19:16:01 +0000
commitfae5833c8925fe635f5a7454e224bf6f977fd593 (patch)
tree173838149ae4317e05032b0ae60ce5eaa5932e23 /demos
parent7ef86d232b1fc27065c3c142c239945a685a7ade (diff)
downloadgtk+-fae5833c8925fe635f5a7454e224bf6f977fd593.tar.gz
Add a "Hypertext" demo.
Diffstat (limited to 'demos')
-rw-r--r--demos/gtk-demo/Makefile.am3
-rw-r--r--demos/gtk-demo/hypertext.c315
-rw-r--r--demos/gtk-demo/textview.c2
3 files changed, 318 insertions, 2 deletions
diff --git a/demos/gtk-demo/Makefile.am b/demos/gtk-demo/Makefile.am
index c12164382b..f04ec82bb6 100644
--- a/demos/gtk-demo/Makefile.am
+++ b/demos/gtk-demo/Makefile.am
@@ -21,7 +21,8 @@ demos = \
sizegroup.c \
stock_browser.c \
textview.c \
- tree_store.c
+ tree_store.c \
+ hypertext.c
INCLUDES = \
-DDEMOCODEDIR="\"$(democodedir)\"" \
diff --git a/demos/gtk-demo/hypertext.c b/demos/gtk-demo/hypertext.c
new file mode 100644
index 0000000000..b346a1f6dc
--- /dev/null
+++ b/demos/gtk-demo/hypertext.c
@@ -0,0 +1,315 @@
+/* Text Widget/Hypertext
+ *
+ * Usually, tags modify the appearance of text in the view, e.g. making it bold or colored or underlined.
+ * But tags are not restricted to appearance. They can also affect the behavior of mouse and key presses,
+ * as this demo shows.
+ */
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+/* Inserts a piece of text into the buffer, giving it the usual
+ * appearance of a hyperlink in a web browser: blue and underlined.
+ * Additionally, attaches some data on the tag, to make it recognizable
+ * as a link.
+ */
+static void
+insert_link (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ gchar *text,
+ gint page)
+{
+ GtkTextTag *tag;
+
+ tag = gtk_text_buffer_create_tag (buffer, NULL,
+ "foreground", "blue",
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+ g_object_set_data (G_OBJECT (tag), "page", GINT_TO_POINTER (page));
+ gtk_text_buffer_insert_with_tags (buffer, iter, text, -1, tag, NULL);
+}
+
+/* Fills the buffer with text and interspersed links. In any real
+ * hypertext app, this method would parse a file to identify the links.
+ */
+static void
+show_page (GtkTextBuffer *buffer,
+ gint page)
+{
+ GtkTextIter iter;
+
+ gtk_text_buffer_set_text (buffer, "", 0);
+ gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
+ if (page == 1)
+ {
+ gtk_text_buffer_insert (buffer, &iter, "Some text to show that simple ", -1);
+ insert_link (buffer, &iter, "hypertext", 3);
+ gtk_text_buffer_insert (buffer, &iter, " can easily be realized with ", -1);
+ insert_link (buffer, &iter, "tags", 2);
+ gtk_text_buffer_insert (buffer, &iter, ".", -1);
+ }
+ else if (page == 2)
+ {
+ gtk_text_buffer_insert (buffer, &iter,
+ "A tag is an attribute that can be applied to some range of text. "
+ "For example, a tag might be called \"bold\" and make the text inside "
+ "the tag bold. However, the tag concept is more general than that; "
+ "tags don't have to affect appearance. They can instead affect the "
+ "behavior of mouse and key presses, \"lock\" a range of text so the "
+ "user can't edit it, or countless other things.\n", -1);
+ insert_link (buffer, &iter, "Go back", 1);
+ }
+ else if (page == 3)
+ {
+ GtkTextTag *tag;
+
+ tag = gtk_text_buffer_create_tag (buffer, NULL,
+ "weight", PANGO_WEIGHT_BOLD,
+ NULL);
+ gtk_text_buffer_insert_with_tags (buffer, &iter, "hypertext:\n", -1, tag, NULL);
+ gtk_text_buffer_insert (buffer, &iter,
+ "machine-readable text that is not sequential but is organized "
+ "so that related items of information are connected.\n", -1);
+ insert_link (buffer, &iter, "Go back", 1);
+ }
+}
+
+/* Looks at all tags covering the position of iter in the text view,
+ * and if one of them is a link, follow it by showing the page identified
+ * by the data attached to it.
+ */
+static void
+follow_if_link (GtkWidget *text_view,
+ GtkTextIter *iter)
+{
+ GSList *tags = NULL, *tagp = NULL;
+
+ tags = gtk_text_iter_get_tags (iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next)
+ {
+ GtkTextTag *tag = tagp->data;
+ gint page = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "page"));
+
+ if (page != 0)
+ {
+ show_page (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)), page);
+ break;
+ }
+ }
+
+ if (tags)
+ g_slist_free (tags);
+}
+
+/* Links can be activated by pressing Enter.
+ */
+static gboolean
+key_press_event (GtkWidget *text_view,
+ GdkEventKey *event)
+{
+ GtkTextIter iter;
+ GtkTextBuffer *buffer;
+
+ switch (event->keyval)
+ {
+ case GDK_Return:
+ case GDK_KP_Enter:
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter,
+ gtk_text_buffer_get_insert (buffer));
+ follow_if_link (text_view, &iter);
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+/* Links can also be activated by clicking.
+ */
+static gboolean
+event_after (GtkWidget *text_view,
+ GdkEvent *ev)
+{
+ GtkTextIter start, end, iter;
+ GtkTextBuffer *buffer;
+ GdkEventButton *event;
+ gint x, y;
+
+ if (ev->type != GDK_BUTTON_RELEASE)
+ return FALSE;
+
+ event = (GdkEventButton *)ev;
+
+ if (event->button != 1)
+ return FALSE;
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+
+ /* we shouldn't follow a link if the user has selected something */
+ gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+ if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
+ return FALSE;
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ event->x, event->y, &x, &y);
+
+ gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
+
+ follow_if_link (text_view, &iter);
+
+ return FALSE;
+}
+
+gboolean hovering_over_link = FALSE;
+GdkCursor *hand_cursor = NULL;
+GdkCursor *regular_cursor = NULL;
+
+/* Looks at all tags covering the position (x, y) in the text view,
+ * and if one of them is a link, change the cursor to the "hands" cursor
+ * typically used by web browsers.
+ */
+static void
+set_cursor_if_appropriate (GtkTextView *text_view,
+ gint x,
+ gint y)
+{
+ GSList *tags = NULL, *tagp = NULL;
+ GtkTextBuffer *buffer;
+ GtkTextIter iter;
+ gboolean hovering = FALSE;
+
+ buffer = gtk_text_view_get_buffer (text_view);
+
+ gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+
+ tags = gtk_text_iter_get_tags (&iter);
+ for (tagp = tags; tagp != NULL; tagp = tagp->next)
+ {
+ GtkTextTag *tag = tagp->data;
+ gint page = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "page"));
+
+ if (page != 0)
+ {
+ hovering = TRUE;
+ break;
+ }
+ }
+
+ if (hovering != hovering_over_link)
+ {
+ hovering_over_link = hovering;
+
+ if (hovering_over_link)
+ gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), hand_cursor);
+ else
+ gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), regular_cursor);
+ }
+
+ if (tags)
+ g_slist_free (tags);
+}
+
+/* Update the cursor image if the pointer moved.
+ */
+static gboolean
+motion_notify_event (GtkWidget *text_view,
+ GdkEventMotion *event)
+{
+ gint x, y;
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ event->x, event->y, &x, &y);
+
+ set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y);
+
+ gdk_window_get_pointer (text_view->window, NULL, NULL, NULL);
+ return FALSE;
+}
+
+/* Also update the cursor image if the window becomes visible
+ * (e.g. when a window covering it got iconified).
+ */
+static gboolean
+visibility_notify_event (GtkWidget *text_view,
+ GdkEventVisibility *event)
+{
+ gint wx, wy, bx, by;
+
+ gdk_window_get_pointer (text_view->window, &wx, &wy, NULL);
+
+ gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
+ GTK_TEXT_WINDOW_WIDGET,
+ wx, wy, &bx, &by);
+
+ set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by);
+
+ return FALSE;
+}
+
+GtkWidget *
+do_hypertext (void)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkWidget *view;
+ GtkWidget *sw;
+ GtkTextBuffer *buffer;
+
+ hand_cursor = gdk_cursor_new (GDK_HAND2);
+ regular_cursor = gdk_cursor_new (GDK_XTERM);
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_default_size (GTK_WINDOW (window),
+ 450, 450);
+
+ g_signal_connect (window, "destroy",
+ G_CALLBACK (gtk_widget_destroyed), &window);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Hypertext");
+ gtk_container_set_border_width (GTK_CONTAINER (window), 0);
+
+ view = gtk_text_view_new ();
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD);
+ g_signal_connect (G_OBJECT (view), "key-press-event",
+ G_CALLBACK (key_press_event), NULL);
+ g_signal_connect (G_OBJECT (view), "event-after",
+ G_CALLBACK (event_after), NULL);
+ g_signal_connect (G_OBJECT (view), "motion-notify-event",
+ G_CALLBACK (motion_notify_event), NULL);
+ g_signal_connect (G_OBJECT (view), "visibility-notify-event",
+ G_CALLBACK (visibility_notify_event), NULL);
+
+ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
+
+ sw = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (window), sw);
+ gtk_container_add (GTK_CONTAINER (sw), view);
+
+ show_page (buffer, 1);
+
+ gtk_widget_show_all (sw);
+ }
+
+ if (!GTK_WIDGET_VISIBLE (window))
+ {
+ gtk_widget_show (window);
+ }
+ else
+ {
+ gtk_widget_destroy (window);
+ window = NULL;
+ }
+
+ return window;
+}
+
diff --git a/demos/gtk-demo/textview.c b/demos/gtk-demo/textview.c
index 0b870ee734..fbec120b4b 100644
--- a/demos/gtk-demo/textview.c
+++ b/demos/gtk-demo/textview.c
@@ -1,4 +1,4 @@
-/* Text Widget
+/* Text Widget/Multiple Views
*
* The GtkTextView widget displays a GtkTextBuffer. One GtkTextBuffer
* can be displayed by multiple GtkTextViews. This demo has two views