Add a "Hypertext" demo.
This commit is contained in:
		| @ -21,7 +21,8 @@ demos =						\ | ||||
| 	sizegroup.c				\ | ||||
| 	stock_browser.c				\ | ||||
| 	textview.c				\ | ||||
| 	tree_store.c | ||||
| 	tree_store.c				\ | ||||
| 	hypertext.c | ||||
|  | ||||
| INCLUDES = \ | ||||
| 	-DDEMOCODEDIR="\"$(democodedir)\""		\ | ||||
|  | ||||
							
								
								
									
										315
									
								
								demos/gtk-demo/hypertext.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								demos/gtk-demo/hypertext.c
									
									
									
									
									
										Normal file
									
								
							| @ -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; | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Matthias Clasen
					Matthias Clasen