Mercurial > pidgin.yaz
comparison pidgin/gtkimhtml.c @ 24396:38a2f78f80a7
Allow plugins to specify custom link types to the GtkIMHtml widget.
Currently, the custom link types are added for all GtkIMHtml widgets. If
we wanted, it should be possible to add custom links to particular
widgets only too. If everything looks OK, I might merge this in before
2.6.0
author | Sadrul Habib Chowdhury <imadil@gmail.com> |
---|---|
date | Sun, 16 Nov 2008 09:58:48 +0000 |
parents | 9ea84135db24 |
children | 16811be2253f |
comparison
equal
deleted
inserted
replaced
24394:fae699fece1f | 24396:38a2f78f80a7 |
---|---|
86 struct im_image_data { | 86 struct im_image_data { |
87 int id; | 87 int id; |
88 GtkTextMark *mark; | 88 GtkTextMark *mark; |
89 }; | 89 }; |
90 | 90 |
91 typedef struct _GtkIMHtmlProtocol | |
92 { | |
93 char *name; | |
94 int length; | |
95 | |
96 gboolean (*activate)(GtkIMHtml *imhtml, const char *text); | |
97 gboolean (*context_menu)(GtkIMHtml *imhtml, const char *text, GtkWidget *menu); | |
98 } GtkIMHtmlProtocol; | |
99 | |
91 static gboolean | 100 static gboolean |
92 gtk_text_view_drag_motion (GtkWidget *widget, | 101 gtk_text_view_drag_motion (GtkWidget *widget, |
93 GdkDragContext *context, | 102 GdkDragContext *context, |
94 gint x, | 103 gint x, |
95 gint y, | 104 gint y, |
113 static void imhtml_toggle_strike(GtkIMHtml *imhtml); | 122 static void imhtml_toggle_strike(GtkIMHtml *imhtml); |
114 static void imhtml_toggle_underline(GtkIMHtml *imhtml); | 123 static void imhtml_toggle_underline(GtkIMHtml *imhtml); |
115 static void imhtml_font_grow(GtkIMHtml *imhtml); | 124 static void imhtml_font_grow(GtkIMHtml *imhtml); |
116 static void imhtml_font_shrink(GtkIMHtml *imhtml); | 125 static void imhtml_font_shrink(GtkIMHtml *imhtml); |
117 static void imhtml_clear_formatting(GtkIMHtml *imhtml); | 126 static void imhtml_clear_formatting(GtkIMHtml *imhtml); |
127 static int gtk_imhtml_is_protocol(const char *text); | |
118 | 128 |
119 /* POINT_SIZE converts from AIM font sizes to a point size scale factor. */ | 129 /* POINT_SIZE converts from AIM font sizes to a point size scale factor. */ |
120 #define MAX_FONT_SIZE 7 | 130 #define MAX_FONT_SIZE 7 |
121 #define POINT_SIZE(x) (_point_sizes [MIN ((x > 0 ? x : 1), MAX_FONT_SIZE) - 1]) | 131 #define POINT_SIZE(x) (_point_sizes [MIN ((x > 0 ? x : 1), MAX_FONT_SIZE) - 1]) |
122 static const gdouble _point_sizes [] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736}; | 132 static const gdouble _point_sizes [] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736}; |
1387 g_free(imhtml->protocol_name); | 1397 g_free(imhtml->protocol_name); |
1388 g_free(imhtml->search_string); | 1398 g_free(imhtml->search_string); |
1389 g_object_unref(imhtml->undo_manager); | 1399 g_object_unref(imhtml->undo_manager); |
1390 G_OBJECT_CLASS(parent_class)->finalize (object); | 1400 G_OBJECT_CLASS(parent_class)->finalize (object); |
1391 | 1401 |
1402 } | |
1403 | |
1404 static GtkIMHtmlProtocol * | |
1405 imhtml_find_protocol(const char *url) | |
1406 { | |
1407 GtkIMHtmlClass *klass; | |
1408 GList *iter; | |
1409 GtkIMHtmlProtocol *proto = NULL; | |
1410 | |
1411 klass = g_type_class_ref(GTK_TYPE_IMHTML); | |
1412 for (iter = klass->protocols; iter; iter = iter->next) { | |
1413 proto = iter->data; | |
1414 if (g_ascii_strncasecmp(url, proto->name, proto->length) == 0) { | |
1415 return proto; | |
1416 } | |
1417 } | |
1418 return NULL; | |
1419 } | |
1420 | |
1421 static void | |
1422 imhtml_url_clicked(GtkIMHtml *imhtml, const char *url) | |
1423 { | |
1424 GtkIMHtmlProtocol *proto = imhtml_find_protocol(url); | |
1425 if (!proto) | |
1426 return; | |
1427 proto->activate(imhtml, url); /* XXX: Do something with the return value? */ | |
1392 } | 1428 } |
1393 | 1429 |
1394 /* Boring GTK+ stuff */ | 1430 /* Boring GTK+ stuff */ |
1395 static void gtk_imhtml_class_init (GtkIMHtmlClass *klass) | 1431 static void gtk_imhtml_class_init (GtkIMHtmlClass *klass) |
1396 { | 1432 { |
1473 | 1509 |
1474 | 1510 |
1475 klass->toggle_format = imhtml_toggle_format; | 1511 klass->toggle_format = imhtml_toggle_format; |
1476 klass->message_send = imhtml_message_send; | 1512 klass->message_send = imhtml_message_send; |
1477 klass->clear_format = imhtml_clear_formatting; | 1513 klass->clear_format = imhtml_clear_formatting; |
1514 klass->url_clicked = imhtml_url_clicked; | |
1478 klass->undo = gtk_imhtml_undo; | 1515 klass->undo = gtk_imhtml_undo; |
1479 klass->redo = gtk_imhtml_redo; | 1516 klass->redo = gtk_imhtml_redo; |
1480 | 1517 |
1481 gobject_class->finalize = gtk_imhtml_finalize; | 1518 gobject_class->finalize = gtk_imhtml_finalize; |
1482 widget_class->drag_motion = gtk_text_view_drag_motion; | 1519 widget_class->drag_motion = gtk_text_view_drag_motion; |
1743 g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE)); | 1780 g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE)); |
1744 gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag); | 1781 gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag); |
1745 return FALSE; | 1782 return FALSE; |
1746 } else if(event_button->button == 3) { | 1783 } else if(event_button->button == 3) { |
1747 GtkWidget *img, *item, *menu; | 1784 GtkWidget *img, *item, *menu; |
1785 GtkIMHtmlProtocol *proto; | |
1748 struct url_data *tempdata = g_new(struct url_data, 1); | 1786 struct url_data *tempdata = g_new(struct url_data, 1); |
1749 tempdata->object = g_object_ref(imhtml); | 1787 tempdata->object = g_object_ref(imhtml); |
1750 tempdata->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url")); | 1788 tempdata->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url")); |
1751 tempdata->tag = g_object_ref(tag); | 1789 tempdata->tag = g_object_ref(tag); |
1752 | 1790 |
1764 else | 1802 else |
1765 gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor); | 1803 gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor); |
1766 menu = gtk_menu_new(); | 1804 menu = gtk_menu_new(); |
1767 g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", tempdata, url_data_destroy); | 1805 g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", tempdata, url_data_destroy); |
1768 | 1806 |
1769 /* buttons and such */ | 1807 proto = imhtml_find_protocol(tempdata->url); |
1770 | 1808 |
1771 if (!strncmp(tempdata->url, "mailto:", 7)) | 1809 if (!strncmp(tempdata->url, "mailto:", 7)) |
1772 { | 1810 { |
1773 /* Copy Email Address */ | 1811 /* Copy Email Address */ |
1774 img = gtk_image_new_from_stock(GTK_STOCK_COPY, | 1812 img = gtk_image_new_from_stock(GTK_STOCK_COPY, |
1778 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); | 1816 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); |
1779 g_signal_connect(G_OBJECT(item), "activate", | 1817 g_signal_connect(G_OBJECT(item), "activate", |
1780 G_CALLBACK(url_copy), tempdata->url + 7); | 1818 G_CALLBACK(url_copy), tempdata->url + 7); |
1781 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); | 1819 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
1782 } | 1820 } |
1821 else if (proto && proto->context_menu) | |
1822 { | |
1823 GList *children; | |
1824 proto->context_menu(GTK_IMHTML(tempdata->object), tempdata->url, menu); | |
1825 children = gtk_container_get_children(GTK_CONTAINER(menu)); | |
1826 if (!children) { | |
1827 item = gtk_menu_item_new_with_label(_("No actions available")); | |
1828 gtk_widget_show(item); | |
1829 gtk_widget_set_sensitive(item, FALSE); | |
1830 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); | |
1831 } else { | |
1832 g_list_free(children); | |
1833 } | |
1834 } | |
1783 else | 1835 else |
1784 { | 1836 { |
1785 /* Open Link in Browser */ | 1837 /* Open Link in Browser */ |
1786 img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, | 1838 img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, |
1787 GTK_ICON_SIZE_MENU); | 1839 GTK_ICON_SIZE_MENU); |
1882 /* TODO: Is it really ok to change sd->data...? */ | 1934 /* TODO: Is it really ok to change sd->data...? */ |
1883 purple_str_strip_char((char *)sd->data, '\r'); | 1935 purple_str_strip_char((char *)sd->data, '\r'); |
1884 | 1936 |
1885 links = g_strsplit((char *)sd->data, "\n", 0); | 1937 links = g_strsplit((char *)sd->data, "\n", 0); |
1886 while((link = links[i]) != NULL){ | 1938 while((link = links[i]) != NULL){ |
1887 if(purple_str_has_prefix(link, "http://") || | 1939 if (gtk_imhtml_is_protocol(link)) { |
1888 purple_str_has_prefix(link, "https://") || | |
1889 purple_str_has_prefix(link, "ftp://")) | |
1890 { | |
1891 gchar *label; | 1940 gchar *label; |
1892 | 1941 |
1893 if(links[i + 1]) | 1942 if(links[i + 1]) |
1894 i++; | 1943 i++; |
1895 | 1944 |
1896 label = links[i]; | 1945 label = links[i]; |
1897 | 1946 |
1898 gtk_imhtml_insert_link(imhtml, mark, link, label); | 1947 gtk_imhtml_insert_link(imhtml, mark, link, label); |
1899 } else if (link=='\0') { | 1948 } else if (*link == '\0') { |
1900 /* Ignore blank lines */ | 1949 /* Ignore blank lines */ |
1901 } else { | 1950 } else { |
1902 /* Special reasons, aka images being put in via other tag, etc. */ | 1951 /* Special reasons, aka images being put in via other tag, etc. */ |
1903 /* ... don't pretend we handled it if we didn't */ | 1952 /* ... don't pretend we handled it if we didn't */ |
1904 gtk_drag_finish(dc, FALSE, FALSE, t); | 1953 gtk_drag_finish(dc, FALSE, FALSE, t); |
2380 g_free(val); | 2429 g_free(val); |
2381 | 2430 |
2382 return g_string_free(ret, FALSE); | 2431 return g_string_free(ret, FALSE); |
2383 } | 2432 } |
2384 | 2433 |
2385 static const char *accepted_protocols[] = { | |
2386 "http://", | |
2387 "https://", | |
2388 "ftp://" | |
2389 }; | |
2390 | |
2391 static const int accepted_protocols_size = 3; | |
2392 | |
2393 /* returns if the beginning of the text is a protocol. If it is the protocol, returns the length so | 2434 /* returns if the beginning of the text is a protocol. If it is the protocol, returns the length so |
2394 the caller knows how long the protocol string is. */ | 2435 the caller knows how long the protocol string is. */ |
2395 static int gtk_imhtml_is_protocol(const char *text) | 2436 static int gtk_imhtml_is_protocol(const char *text) |
2396 { | 2437 { |
2397 gint i; | 2438 GtkIMHtmlProtocol *proto = imhtml_find_protocol(text); |
2398 | 2439 return proto ? proto->length : 0; |
2399 for(i=0; i<accepted_protocols_size; i++){ | |
2400 if( g_ascii_strncasecmp(text, accepted_protocols[i], strlen(accepted_protocols[i])) == 0 ){ | |
2401 return strlen(accepted_protocols[i]); | |
2402 } | |
2403 } | |
2404 return 0; | |
2405 } | 2440 } |
2406 | 2441 |
2407 /* | 2442 /* |
2408 <KingAnt> marv: The two IM image functions in oscar are purple_odc_send_im and purple_odc_incoming | 2443 <KingAnt> marv: The two IM image functions in oscar are purple_odc_send_im and purple_odc_incoming |
2409 | 2444 |
3318 } | 3353 } |
3319 c++; | 3354 c++; |
3320 pos++; | 3355 pos++; |
3321 } else if ((len_protocol = gtk_imhtml_is_protocol(c)) > 0){ | 3356 } else if ((len_protocol = gtk_imhtml_is_protocol(c)) > 0){ |
3322 br = FALSE; | 3357 br = FALSE; |
3358 if (wpos > 0) { | |
3359 gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos); | |
3360 ws[0] = '\0'; | |
3361 wpos = 0; | |
3362 } | |
3323 while(len_protocol--){ | 3363 while(len_protocol--){ |
3324 /* Skip the next len_protocol characters, but make sure they're | 3364 /* Skip the next len_protocol characters, but make sure they're |
3325 copied into the ws array. | 3365 copied into the ws array. |
3326 */ | 3366 */ |
3327 ws [wpos++] = *c++; | 3367 ws [wpos++] = *c++; |
3328 pos++; | 3368 pos++; |
3369 } | |
3370 if (!imhtml->edit.link) { | |
3371 while (*c && *c != ' ') { | |
3372 ws [wpos++] = *c++; | |
3373 pos++; | |
3374 } | |
3375 ws[wpos] = '\0'; | |
3376 gtk_imhtml_toggle_link(imhtml, ws); | |
3377 gtk_text_buffer_insert(imhtml->text_buffer, iter, ws, wpos); | |
3378 ws[0] = '\0'; wpos = 0; | |
3379 gtk_imhtml_toggle_link(imhtml, NULL); | |
3329 } | 3380 } |
3330 } else if (*c) { | 3381 } else if (*c) { |
3331 br = FALSE; | 3382 br = FALSE; |
3332 ws [wpos++] = *c++; | 3383 ws [wpos++] = *c++; |
3333 pos++; | 3384 pos++; |
5743 if (smiley->loader) | 5794 if (smiley->loader) |
5744 g_object_unref(smiley->loader); | 5795 g_object_unref(smiley->loader); |
5745 g_free(smiley); | 5796 g_free(smiley); |
5746 } | 5797 } |
5747 | 5798 |
5799 gboolean gtk_imhtml_class_register_protocol(const char *name, | |
5800 gboolean (*activate)(GtkIMHtml *imhtml, const char *text), | |
5801 gboolean (*context_menu)(GtkIMHtml *imhtml, const char *text, GtkWidget *menu)) | |
5802 { | |
5803 GtkIMHtmlClass *klass; | |
5804 GtkIMHtmlProtocol *proto; | |
5805 | |
5806 g_return_val_if_fail(name, FALSE); | |
5807 | |
5808 klass = g_type_class_ref(GTK_TYPE_IMHTML); | |
5809 g_return_val_if_fail(klass, FALSE); | |
5810 | |
5811 if ((proto = imhtml_find_protocol(name))) { | |
5812 g_return_val_if_fail(!activate, FALSE); | |
5813 g_free(proto->name); | |
5814 g_free(proto); | |
5815 klass->protocols = g_list_remove(klass->protocols, proto); | |
5816 return TRUE; | |
5817 } else { | |
5818 g_return_val_if_fail(activate, FALSE); | |
5819 } | |
5820 | |
5821 proto = g_new0(GtkIMHtmlProtocol, 1); | |
5822 proto->name = g_strdup(name); | |
5823 proto->length = strlen(name); | |
5824 proto->activate = activate; | |
5825 proto->context_menu = context_menu; | |
5826 klass->protocols = g_list_prepend(klass->protocols, proto); | |
5827 | |
5828 return TRUE; | |
5829 } | |
5830 |