pidgin/gtkimhtml.c

branch
imhtml.customlinks
changeset 24674
36bf974a7d78
parent 24588
b2c89babe134
child 24675
9b6a40a095fb
equal deleted inserted replaced
24672:3e12ba88e3b9 24674:36bf974a7d78
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

mercurial