| 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 |