home *** CD-ROM | disk | FTP | other *** search
/ Freelog 65 / Freelog065.iso / BAS / Bureautique / Gnumeric / gnumeric-1.3.92-rc1.exe / hypertext.c < prev    next >
C/C++ Source or Header  |  2004-10-15  |  9KB  |  320 lines

  1. /* Text Widget/Hypertext
  2.  *
  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 
  6.  * shows.
  7.  */
  8.  
  9. #include <config.h>
  10. #include <gtk/gtk.h>
  11. #include <gdk/gdkkeysyms.h>
  12.  
  13. /* Inserts a piece of text into the buffer, giving it the usual
  14.  * appearance of a hyperlink in a web browser: blue and underlined.
  15.  * Additionally, attaches some data on the tag, to make it recognizable
  16.  * as a link. 
  17.  */
  18. static void 
  19. insert_link (GtkTextBuffer *buffer, 
  20.          GtkTextIter   *iter, 
  21.          gchar         *text, 
  22.          gint           page)
  23. {
  24.   GtkTextTag *tag;
  25.   
  26.   tag = gtk_text_buffer_create_tag (buffer, NULL, 
  27.                     "foreground", "blue", 
  28.                     "underline", PANGO_UNDERLINE_SINGLE, 
  29.                     NULL);
  30.   g_object_set_data (G_OBJECT (tag), "page", GINT_TO_POINTER (page));
  31.   gtk_text_buffer_insert_with_tags (buffer, iter, text, -1, tag, NULL);
  32. }
  33.  
  34. /* Fills the buffer with text and interspersed links. In any real
  35.  * hypertext app, this method would parse a file to identify the links.
  36.  */
  37. static void
  38. show_page (GtkTextBuffer *buffer, 
  39.        gint           page)
  40. {
  41.   GtkTextIter iter;
  42.  
  43.   gtk_text_buffer_set_text (buffer, "", 0);
  44.   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
  45.   if (page == 1)
  46.     {
  47.       gtk_text_buffer_insert (buffer, &iter, "Some text to show that simple ", -1);
  48.       insert_link (buffer, &iter, "hypertext", 3);
  49.       gtk_text_buffer_insert (buffer, &iter, " can easily be realized with ", -1);
  50.       insert_link (buffer, &iter, "tags", 2);
  51.       gtk_text_buffer_insert (buffer, &iter, ".", -1);
  52.     }
  53.   else if (page == 2)
  54.     {
  55.       gtk_text_buffer_insert (buffer, &iter, 
  56.                   "A tag is an attribute that can be applied to some range of text. "
  57.                   "For example, a tag might be called \"bold\" and make the text inside "
  58.                   "the tag bold. However, the tag concept is more general than that; "
  59.                   "tags don't have to affect appearance. They can instead affect the "
  60.                   "behavior of mouse and key presses, \"lock\" a range of text so the "
  61.                   "user can't edit it, or countless other things.\n", -1);
  62.       insert_link (buffer, &iter, "Go back", 1);
  63.     }
  64.   else if (page == 3) 
  65.     {
  66.       GtkTextTag *tag;
  67.   
  68.       tag = gtk_text_buffer_create_tag (buffer, NULL, 
  69.                     "weight", PANGO_WEIGHT_BOLD, 
  70.                     NULL);
  71.       gtk_text_buffer_insert_with_tags (buffer, &iter, "hypertext:\n", -1, tag, NULL);
  72.       gtk_text_buffer_insert (buffer, &iter, 
  73.                   "machine-readable text that is not sequential but is organized "
  74.                   "so that related items of information are connected.\n", -1);
  75.       insert_link (buffer, &iter, "Go back", 1);
  76.     }
  77. }
  78.  
  79. /* Looks at all tags covering the position of iter in the text view, 
  80.  * and if one of them is a link, follow it by showing the page identified
  81.  * by the data attached to it.
  82.  */
  83. static void
  84. follow_if_link (GtkWidget   *text_view, 
  85.         GtkTextIter *iter)
  86. {
  87.   GSList *tags = NULL, *tagp = NULL;
  88.  
  89.   tags = gtk_text_iter_get_tags (iter);
  90.   for (tagp = tags;  tagp != NULL;  tagp = tagp->next)
  91.     {
  92.       GtkTextTag *tag = tagp->data;
  93.       gint page = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "page"));
  94.  
  95.       if (page != 0)
  96.         {
  97.       show_page (gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view)), page);
  98.       break;
  99.         }
  100.     }
  101.  
  102.   if (tags) 
  103.     g_slist_free (tags);
  104. }
  105.  
  106. /* Links can be activated by pressing Enter.
  107.  */
  108. static gboolean
  109. key_press_event (GtkWidget *text_view,
  110.          GdkEventKey *event)
  111. {
  112.   GtkTextIter iter;
  113.   GtkTextBuffer *buffer;
  114.  
  115.   switch (event->keyval)
  116.     {
  117.       case GDK_Return: 
  118.       case GDK_KP_Enter:
  119.         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
  120.         gtk_text_buffer_get_iter_at_mark (buffer, &iter, 
  121.                                           gtk_text_buffer_get_insert (buffer));
  122.         follow_if_link (text_view, &iter);
  123.         break;
  124.  
  125.       default:
  126.         break;
  127.     }
  128.  
  129.   return FALSE;
  130. }
  131.  
  132. /* Links can also be activated by clicking.
  133.  */
  134. static gboolean
  135. event_after (GtkWidget *text_view,
  136.          GdkEvent  *ev)
  137. {
  138.   GtkTextIter start, end, iter;
  139.   GtkTextBuffer *buffer;
  140.   GdkEventButton *event;
  141.   gint x, y;
  142.  
  143.   if (ev->type != GDK_BUTTON_RELEASE)
  144.     return FALSE;
  145.  
  146.   event = (GdkEventButton *)ev;
  147.  
  148.   if (event->button != 1)
  149.     return FALSE;
  150.  
  151.   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
  152.  
  153.   /* we shouldn't follow a link if the user has selected something */
  154.   gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
  155.   if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
  156.     return FALSE;
  157.  
  158.   gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
  159.                                          GTK_TEXT_WINDOW_WIDGET,
  160.                                          event->x, event->y, &x, &y);
  161.  
  162.   gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
  163.  
  164.   follow_if_link (text_view, &iter);
  165.  
  166.   return FALSE;
  167. }
  168.  
  169. gboolean hovering_over_link = FALSE;
  170. GdkCursor *hand_cursor = NULL;
  171. GdkCursor *regular_cursor = NULL;
  172.  
  173. /* Looks at all tags covering the position (x, y) in the text view, 
  174.  * and if one of them is a link, change the cursor to the "hands" cursor
  175.  * typically used by web browsers.
  176.  */
  177. static void
  178. set_cursor_if_appropriate (GtkTextView    *text_view,
  179.                            gint            x,
  180.                            gint            y)
  181. {
  182.   GSList *tags = NULL, *tagp = NULL;
  183.   GtkTextBuffer *buffer;
  184.   GtkTextIter iter;
  185.   gboolean hovering = FALSE;
  186.  
  187.   buffer = gtk_text_view_get_buffer (text_view);
  188.  
  189.   gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
  190.   
  191.   tags = gtk_text_iter_get_tags (&iter);
  192.   for (tagp = tags;  tagp != NULL;  tagp = tagp->next)
  193.     {
  194.       GtkTextTag *tag = tagp->data;
  195.       gint page = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tag), "page"));
  196.  
  197.       if (page != 0) 
  198.         {
  199.           hovering = TRUE;
  200.           break;
  201.         }
  202.     }
  203.  
  204.   if (hovering != hovering_over_link)
  205.     {
  206.       hovering_over_link = hovering;
  207.  
  208.       if (hovering_over_link)
  209.         gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), hand_cursor);
  210.       else
  211.         gdk_window_set_cursor (gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT), regular_cursor);
  212.     }
  213.  
  214.   if (tags) 
  215.     g_slist_free (tags);
  216. }
  217.  
  218. /* Update the cursor image if the pointer moved. 
  219.  */
  220. static gboolean
  221. motion_notify_event (GtkWidget      *text_view,
  222.              GdkEventMotion *event)
  223. {
  224.   gint x, y;
  225.  
  226.   gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
  227.                                          GTK_TEXT_WINDOW_WIDGET,
  228.                                          event->x, event->y, &x, &y);
  229.  
  230.   set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y);
  231.  
  232.   gdk_window_get_pointer (text_view->window, NULL, NULL, NULL);
  233.   return FALSE;
  234. }
  235.  
  236. /* Also update the cursor image if the window becomes visible
  237.  * (e.g. when a window covering it got iconified).
  238.  */
  239. static gboolean
  240. visibility_notify_event (GtkWidget          *text_view,
  241.              GdkEventVisibility *event)
  242. {
  243.   gint wx, wy, bx, by;
  244.   
  245.   gdk_window_get_pointer (text_view->window, &wx, &wy, NULL);
  246.   
  247.   gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
  248.                                          GTK_TEXT_WINDOW_WIDGET,
  249.                                          wx, wy, &bx, &by);
  250.  
  251.   set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by);
  252.  
  253.   return FALSE;
  254. }
  255.  
  256. GtkWidget *
  257. do_hypertext (GtkWidget *do_widget)
  258. {
  259.   static GtkWidget *window = NULL;
  260.  
  261.   if (!window)
  262.     {
  263.       GtkWidget *view;
  264.       GtkWidget *sw;
  265.       GtkTextBuffer *buffer;
  266.  
  267.       hand_cursor = gdk_cursor_new (GDK_HAND2);
  268.       regular_cursor = gdk_cursor_new (GDK_XTERM);
  269.       
  270.       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  271.       gtk_window_set_screen (GTK_WINDOW (window),
  272.                  gtk_widget_get_screen (do_widget));
  273.       gtk_window_set_default_size (GTK_WINDOW (window),
  274.                    450, 450);
  275.       
  276.       g_signal_connect (window, "destroy",
  277.             G_CALLBACK (gtk_widget_destroyed), &window);
  278.  
  279.       gtk_window_set_title (GTK_WINDOW (window), "Hypertext");
  280.       gtk_container_set_border_width (GTK_CONTAINER (window), 0);
  281.  
  282.       view = gtk_text_view_new ();
  283.       gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view), GTK_WRAP_WORD);
  284.       g_signal_connect (G_OBJECT (view), "key-press-event", 
  285.             G_CALLBACK (key_press_event), NULL);
  286.       g_signal_connect (G_OBJECT (view), "event-after", 
  287.             G_CALLBACK (event_after), NULL);
  288.       g_signal_connect (G_OBJECT (view), "motion-notify-event", 
  289.             G_CALLBACK (motion_notify_event), NULL);
  290.       g_signal_connect (G_OBJECT (view), "visibility-notify-event", 
  291.             G_CALLBACK (visibility_notify_event), NULL);
  292.  
  293.       buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view));
  294.       
  295.       sw = gtk_scrolled_window_new (NULL, NULL);
  296.       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
  297.                       GTK_POLICY_AUTOMATIC,
  298.                       GTK_POLICY_AUTOMATIC);
  299.       gtk_container_add (GTK_CONTAINER (window), sw);
  300.       gtk_container_add (GTK_CONTAINER (sw), view);
  301.  
  302.       show_page (buffer, 1);
  303.  
  304.       gtk_widget_show_all (sw);
  305.     }
  306.  
  307.   if (!GTK_WIDGET_VISIBLE (window))
  308.     {
  309.       gtk_widget_show (window);
  310.     }
  311.   else
  312.     {
  313.       gtk_widget_destroy (window);
  314.       window = NULL;
  315.     }
  316.  
  317.   return window;
  318. }
  319.  
  320.