7d4be655bfd894cb3a86106687cdbbf95396de23
[rodin/chimara.git] / libchimara / hyperlink.c
1 #include "hyperlink.h"
2 #include "chimara-glk-private.h"
3
4 extern GPrivate *glk_data_key;
5
6 /**
7  * glk_set_hyperlink:
8  * @linkval: Set to nonzero to initiate hyperlink mode. Set to zero to disengage.
9  *
10  * Use this function to create hyperlinks in a textbuffer. It sets the current stream
11  * to hyperlink mode, after which text will become a hyperlink until hyperlink mode
12  * is turned off. If the current stream does not write to a textbuffer window, this function
13  * does nothing.
14  *
15  * You can request hyperlink events with glk_request_hyperlink_event() to react
16  * to clicks on the link.
17  */
18 void 
19 glk_set_hyperlink(glui32 linkval)
20 {
21         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
22         g_return_if_fail(glk_data->current_stream != NULL);
23         glk_set_hyperlink_stream(glk_data->current_stream, linkval);
24 }
25
26 /**
27  * glk_set_hyperlink_stream:
28  * @str: The stream to set the hyperlink mode on.
29  * @linkval: Set to nonzero to initiate hyperlink mode. Set to zero to disengage.
30  *
31  * Use this function to create hyperlinks in a textbuffer. It sets a stream to a textbuffer
32  * window to hyperlink mode, after which text will become a hyperlink until hyperlink mode
33  * is turned off. Calling this function on a stream that does not write to a textbuffer does
34  * nothing.
35  *
36  * You can request hyperlink events with glk_request_hyperlink_event() to react
37  * to clicks on the link.
38  */
39 void 
40 glk_set_hyperlink_stream(strid_t str, glui32 linkval)
41 {
42         g_return_if_fail(str != NULL);
43         g_return_if_fail(str->window != NULL);
44         g_return_if_fail(str->window->type == wintype_TextBuffer);
45
46         flush_window_buffer(str->window);
47
48         if(linkval == 0) {
49                 /* Turn off hyperlink mode */
50                 str->hyperlink_mode = FALSE;
51                 str->window->current_hyperlink = NULL;
52                 return;
53         }
54
55         /* Check whether a tag with the needed value already exists */
56         hyperlink_t *new_hyperlink = g_hash_table_lookup(str->window->hyperlinks, &linkval);
57         if(new_hyperlink == NULL) {
58                 /* Create a new hyperlink with the requested value */
59                 new_hyperlink = g_new0(struct hyperlink, 1);
60                 new_hyperlink->value = linkval;
61                 new_hyperlink->tag = gtk_text_tag_new(NULL);
62                 new_hyperlink->event_handler = g_signal_connect( new_hyperlink->tag, "event", G_CALLBACK(on_hyperlink_clicked), new_hyperlink );
63                 g_signal_handler_block(new_hyperlink->tag, new_hyperlink->event_handler);
64                 new_hyperlink->window = str->window;
65
66                 /* Add the new tag to the tag table of the textbuffer */
67                 GtkTextBuffer *textbuffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(str->window->widget) );
68                 GtkTextTagTable *tags = gtk_text_buffer_get_tag_table(textbuffer);
69                 gtk_text_tag_table_add(tags, new_hyperlink->tag);
70
71                 printf("inserting link %d\n", linkval);
72
73                 gint *linkval_pointer = g_new0(gint, 1);
74                 *linkval_pointer = linkval;
75                 g_hash_table_insert(str->window->hyperlinks, linkval_pointer, new_hyperlink);
76         }
77
78         str->hyperlink_mode = TRUE;
79         str->window->current_hyperlink = new_hyperlink;
80 }
81
82 /* Internal function used to iterate over all the hyperlinks, unblocking the event handler */
83 void
84 hyperlink_unblock_event_handler(gpointer key, gpointer value, gpointer user_data)
85 {
86         hyperlink_t *link = (hyperlink_t *) value;
87         g_signal_handler_unblock(link->tag, link->event_handler);
88         printf("unblocking link %d\n", link->value);
89 }
90
91 /* Internal function used to iterate over all the hyperlinks, blocking the event handler */
92 void
93 hyperlink_block_event_handler(gpointer key, gpointer value, gpointer user_data)
94 {
95         hyperlink_t *link = (hyperlink_t *) value;
96         g_signal_handler_block(link->tag, link->event_handler);
97 }
98
99 void 
100 glk_request_hyperlink_event(winid_t win)
101 {
102         VALID_WINDOW(win, return);
103         g_return_if_fail(win != NULL);
104         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
105
106         g_hash_table_foreach(win->hyperlinks, hyperlink_unblock_event_handler, NULL);
107
108 }
109
110 void 
111 glk_cancel_hyperlink_event(winid_t win)
112 {
113         VALID_WINDOW(win, return);
114         g_return_if_fail(win != NULL);
115         g_return_if_fail(win->type != wintype_TextBuffer || win->type != wintype_TextGrid);
116
117         g_hash_table_foreach(win->hyperlinks, hyperlink_block_event_handler, NULL);
118 }
119
120 gboolean
121 on_hyperlink_clicked(GtkTextTag *tag, GObject *object, GdkEvent *event, GtkTextIter *iter, hyperlink_t *link)
122 {
123         ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(link->window->widget, CHIMARA_TYPE_GLK));
124         g_assert(glk);
125
126         if(event->type == GDK_BUTTON_PRESS) {
127                 event_throw(glk, evtype_Hyperlink, link->window, link->value, 0);
128         }
129
130         return FALSE;
131 }