| 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 struct _GtkIMHtmlLink |
| |
92 { |
| |
93 GtkIMHtml *imhtml; |
| |
94 gchar *url; |
| |
95 GtkTextTag *tag; |
| |
96 }; |
| |
97 |
| 91 typedef struct _GtkIMHtmlProtocol |
98 typedef struct _GtkIMHtmlProtocol |
| 92 { |
99 { |
| 93 char *name; |
100 char *name; |
| 94 int length; |
101 int length; |
| 95 |
102 |
| 96 gboolean (*activate)(GtkIMHtml *imhtml, const char *text); |
103 gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link); |
| 97 gboolean (*context_menu)(GtkIMHtml *imhtml, const char *text, GtkWidget *menu); |
104 gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu); |
| 98 } GtkIMHtmlProtocol; |
105 } GtkIMHtmlProtocol; |
| 99 |
106 |
| 100 static gboolean |
107 static gboolean |
| 101 gtk_text_view_drag_motion (GtkWidget *widget, |
108 gtk_text_view_drag_motion (GtkWidget *widget, |
| 102 GdkDragContext *context, |
109 GdkDragContext *context, |
| 123 static void imhtml_toggle_underline(GtkIMHtml *imhtml); |
130 static void imhtml_toggle_underline(GtkIMHtml *imhtml); |
| 124 static void imhtml_font_grow(GtkIMHtml *imhtml); |
131 static void imhtml_font_grow(GtkIMHtml *imhtml); |
| 125 static void imhtml_font_shrink(GtkIMHtml *imhtml); |
132 static void imhtml_font_shrink(GtkIMHtml *imhtml); |
| 126 static void imhtml_clear_formatting(GtkIMHtml *imhtml); |
133 static void imhtml_clear_formatting(GtkIMHtml *imhtml); |
| 127 static int gtk_imhtml_is_protocol(const char *text); |
134 static int gtk_imhtml_is_protocol(const char *text); |
| |
135 static void gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag); |
| |
136 static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link); |
| 128 |
137 |
| 129 /* POINT_SIZE converts from AIM font sizes to a point size scale factor. */ |
138 /* POINT_SIZE converts from AIM font sizes to a point size scale factor. */ |
| 130 #define MAX_FONT_SIZE 7 |
139 #define MAX_FONT_SIZE 7 |
| 131 #define POINT_SIZE(x) (_point_sizes [MIN ((x > 0 ? x : 1), MAX_FONT_SIZE) - 1]) |
140 #define POINT_SIZE(x) (_point_sizes [MIN ((x > 0 ? x : 1), MAX_FONT_SIZE) - 1]) |
| 132 static const gdouble _point_sizes [] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736}; |
141 static const gdouble _point_sizes [] = { .85, .95, 1, 1.2, 1.44, 1.728, 2.0736}; |
| 1420 |
1429 |
| 1421 static void |
1430 static void |
| 1422 imhtml_url_clicked(GtkIMHtml *imhtml, const char *url) |
1431 imhtml_url_clicked(GtkIMHtml *imhtml, const char *url) |
| 1423 { |
1432 { |
| 1424 GtkIMHtmlProtocol *proto = imhtml_find_protocol(url); |
1433 GtkIMHtmlProtocol *proto = imhtml_find_protocol(url); |
| |
1434 GtkIMHtmlLink *link; |
| 1425 if (!proto) |
1435 if (!proto) |
| 1426 return; |
1436 return; |
| 1427 proto->activate(imhtml, url); /* XXX: Do something with the return value? */ |
1437 link = g_new0(GtkIMHtmlLink, 1); |
| |
1438 link->imhtml = g_object_ref(imhtml); |
| |
1439 link->url = g_strdup(url); |
| |
1440 proto->activate(imhtml, link); /* XXX: Do something with the return value? */ |
| |
1441 gtk_imhtml_link_destroy(link); |
| 1428 } |
1442 } |
| 1429 |
1443 |
| 1430 /* Boring GTK+ stuff */ |
1444 /* Boring GTK+ stuff */ |
| 1431 static void gtk_imhtml_class_init (GtkIMHtmlClass *klass) |
1445 static void gtk_imhtml_class_init (GtkIMHtmlClass *klass) |
| 1432 { |
1446 { |
| 1723 } |
1737 } |
| 1724 |
1738 |
| 1725 return imhtml_type; |
1739 return imhtml_type; |
| 1726 } |
1740 } |
| 1727 |
1741 |
| 1728 struct url_data { |
1742 static void gtk_imhtml_link_destroy(GtkIMHtmlLink *link) |
| 1729 GObject *object; |
1743 { |
| 1730 gchar *url; |
1744 if (link->imhtml) |
| 1731 GtkTextTag *tag; |
1745 g_object_unref(link->imhtml); |
| 1732 }; |
1746 if (link->tag) |
| 1733 |
1747 g_object_unref(link->tag); |
| 1734 static void url_data_destroy(gpointer mydata) |
1748 g_free(link->url); |
| 1735 { |
1749 g_free(link); |
| 1736 struct url_data *data = mydata; |
|
| 1737 g_object_unref(data->object); |
|
| 1738 g_object_unref(data->tag); |
|
| 1739 g_free(data->url); |
|
| 1740 g_free(data); |
|
| 1741 } |
|
| 1742 |
|
| 1743 static void url_open(GtkWidget *w, struct url_data *data) |
|
| 1744 { |
|
| 1745 if(!data) return; |
|
| 1746 g_signal_emit(data->object, signals[URL_CLICKED], 0, data->url); |
|
| 1747 g_object_set_data(G_OBJECT(data->tag), "visited", GINT_TO_POINTER(TRUE)); |
|
| 1748 gtk_imhtml_set_link_color(GTK_IMHTML(data->object), data->tag); |
|
| 1749 } |
|
| 1750 |
|
| 1751 static void url_copy(GtkWidget *w, gchar *url) { |
|
| 1752 GtkClipboard *clipboard; |
|
| 1753 |
|
| 1754 clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_PRIMARY); |
|
| 1755 gtk_clipboard_set_text(clipboard, url, -1); |
|
| 1756 |
|
| 1757 clipboard = gtk_widget_get_clipboard(w, GDK_SELECTION_CLIPBOARD); |
|
| 1758 gtk_clipboard_set_text(clipboard, url, -1); |
|
| 1759 } |
1750 } |
| 1760 |
1751 |
| 1761 /* The callback for an event on a link tag. */ |
1752 /* The callback for an event on a link tag. */ |
| 1762 static gboolean tag_event(GtkTextTag *tag, GObject *imhtml, GdkEvent *event, GtkTextIter *arg2, gpointer unused) |
1753 static gboolean tag_event(GtkTextTag *tag, GObject *imhtml, GdkEvent *event, GtkTextIter *arg2, gpointer unused) |
| 1763 { |
1754 { |
| 1769 GtkTextIter start, end; |
1760 GtkTextIter start, end; |
| 1770 /* we shouldn't open a URL if the user has selected something: */ |
1761 /* we shouldn't open a URL if the user has selected something: */ |
| 1771 if (gtk_text_buffer_get_selection_bounds( |
1762 if (gtk_text_buffer_get_selection_bounds( |
| 1772 gtk_text_iter_get_buffer(arg2), &start, &end)) |
1763 gtk_text_iter_get_buffer(arg2), &start, &end)) |
| 1773 return FALSE; |
1764 return FALSE; |
| 1774 |
1765 gtk_imhtml_activate_tag(GTK_IMHTML(imhtml), tag); |
| 1775 /* A link was clicked--we emit the "url_clicked" signal |
|
| 1776 * with the URL as the argument */ |
|
| 1777 g_object_ref(G_OBJECT(tag)); |
|
| 1778 g_signal_emit(imhtml, signals[URL_CLICKED], 0, g_object_get_data(G_OBJECT(tag), "link_url")); |
|
| 1779 g_object_unref(G_OBJECT(tag)); |
|
| 1780 g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE)); |
|
| 1781 gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag); |
|
| 1782 return FALSE; |
1766 return FALSE; |
| 1783 } else if(event_button->button == 3) { |
1767 } else if(event_button->button == 3) { |
| 1784 GtkWidget *img, *item, *menu; |
1768 GList *children; |
| |
1769 GtkWidget *menu; |
| 1785 GtkIMHtmlProtocol *proto; |
1770 GtkIMHtmlProtocol *proto; |
| 1786 struct url_data *tempdata = g_new(struct url_data, 1); |
1771 GtkIMHtmlLink *link = g_new(GtkIMHtmlLink, 1); |
| 1787 tempdata->object = g_object_ref(imhtml); |
1772 link->imhtml = g_object_ref(imhtml); |
| 1788 tempdata->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url")); |
1773 link->url = g_strdup(g_object_get_data(G_OBJECT(tag), "link_url")); |
| 1789 tempdata->tag = g_object_ref(tag); |
1774 link->tag = g_object_ref(tag); |
| 1790 |
1775 |
| 1791 /* Don't want the tooltip around if user right-clicked on link */ |
1776 /* Don't want the tooltip around if user right-clicked on link */ |
| 1792 if (GTK_IMHTML(imhtml)->tip_window) { |
1777 if (GTK_IMHTML(imhtml)->tip_window) { |
| 1793 gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window); |
1778 gtk_widget_destroy(GTK_IMHTML(imhtml)->tip_window); |
| 1794 GTK_IMHTML(imhtml)->tip_window = NULL; |
1779 GTK_IMHTML(imhtml)->tip_window = NULL; |
| 1800 if (GTK_IMHTML(imhtml)->editable) |
1785 if (GTK_IMHTML(imhtml)->editable) |
| 1801 gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->text_cursor); |
1786 gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->text_cursor); |
| 1802 else |
1787 else |
| 1803 gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor); |
1788 gdk_window_set_cursor(event_button->window, GTK_IMHTML(imhtml)->arrow_cursor); |
| 1804 menu = gtk_menu_new(); |
1789 menu = gtk_menu_new(); |
| 1805 g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", tempdata, url_data_destroy); |
1790 g_object_set_data_full(G_OBJECT(menu), "x-imhtml-url-data", link, |
| 1806 |
1791 (GDestroyNotify)gtk_imhtml_link_destroy); |
| 1807 proto = imhtml_find_protocol(tempdata->url); |
1792 |
| 1808 |
1793 proto = imhtml_find_protocol(link->url); |
| 1809 if (!strncmp(tempdata->url, "mailto:", 7)) |
1794 |
| 1810 { |
1795 if (proto && proto->context_menu) { |
| 1811 /* Copy Email Address */ |
1796 proto->context_menu(GTK_IMHTML(link->imhtml), link, menu); |
| 1812 img = gtk_image_new_from_stock(GTK_STOCK_COPY, |
1797 } |
| 1813 GTK_ICON_SIZE_MENU); |
1798 |
| 1814 item = gtk_image_menu_item_new_with_mnemonic( |
1799 children = gtk_container_get_children(GTK_CONTAINER(menu)); |
| 1815 _("_Copy Email Address")); |
1800 if (!children) { |
| 1816 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); |
1801 GtkWidget *item = gtk_menu_item_new_with_label(_("No actions available")); |
| 1817 g_signal_connect(G_OBJECT(item), "activate", |
1802 gtk_widget_show(item); |
| 1818 G_CALLBACK(url_copy), tempdata->url + 7); |
1803 gtk_widget_set_sensitive(item, FALSE); |
| 1819 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
1804 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
| 1820 } |
1805 } else { |
| 1821 else if (proto && proto->context_menu) |
1806 g_list_free(children); |
| 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 } |
|
| 1835 else |
|
| 1836 { |
|
| 1837 /* Open Link in Browser */ |
|
| 1838 img = gtk_image_new_from_stock(GTK_STOCK_JUMP_TO, |
|
| 1839 GTK_ICON_SIZE_MENU); |
|
| 1840 item = gtk_image_menu_item_new_with_mnemonic( |
|
| 1841 _("_Open Link")); |
|
| 1842 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); |
|
| 1843 g_signal_connect(G_OBJECT(item), "activate", |
|
| 1844 G_CALLBACK(url_open), tempdata); |
|
| 1845 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
|
| 1846 |
|
| 1847 /* Copy Link Location */ |
|
| 1848 img = gtk_image_new_from_stock(GTK_STOCK_COPY, |
|
| 1849 GTK_ICON_SIZE_MENU); |
|
| 1850 item = gtk_image_menu_item_new_with_mnemonic( |
|
| 1851 _("_Copy Link Location")); |
|
| 1852 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), img); |
|
| 1853 g_signal_connect(G_OBJECT(item), "activate", |
|
| 1854 G_CALLBACK(url_copy), tempdata->url); |
|
| 1855 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); |
|
| 1856 } |
1807 } |
| 1857 |
1808 |
| 1858 |
1809 |
| 1859 gtk_widget_show_all(menu); |
1810 gtk_widget_show_all(menu); |
| 1860 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, |
1811 gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, |
| 5795 g_object_unref(smiley->loader); |
5746 g_object_unref(smiley->loader); |
| 5796 g_free(smiley); |
5747 g_free(smiley); |
| 5797 } |
5748 } |
| 5798 |
5749 |
| 5799 gboolean gtk_imhtml_class_register_protocol(const char *name, |
5750 gboolean gtk_imhtml_class_register_protocol(const char *name, |
| 5800 gboolean (*activate)(GtkIMHtml *imhtml, const char *text), |
5751 gboolean (*activate)(GtkIMHtml *imhtml, GtkIMHtmlLink *link), |
| 5801 gboolean (*context_menu)(GtkIMHtml *imhtml, const char *text, GtkWidget *menu)) |
5752 gboolean (*context_menu)(GtkIMHtml *imhtml, GtkIMHtmlLink *link, GtkWidget *menu)) |
| 5802 { |
5753 { |
| 5803 GtkIMHtmlClass *klass; |
5754 GtkIMHtmlClass *klass; |
| 5804 GtkIMHtmlProtocol *proto; |
5755 GtkIMHtmlProtocol *proto; |
| 5805 |
5756 |
| 5806 g_return_val_if_fail(name, FALSE); |
5757 g_return_val_if_fail(name, FALSE); |
| 5826 klass->protocols = g_list_prepend(klass->protocols, proto); |
5777 klass->protocols = g_list_prepend(klass->protocols, proto); |
| 5827 |
5778 |
| 5828 return TRUE; |
5779 return TRUE; |
| 5829 } |
5780 } |
| 5830 |
5781 |
| |
5782 static void |
| |
5783 gtk_imhtml_activate_tag(GtkIMHtml *imhtml, GtkTextTag *tag) |
| |
5784 { |
| |
5785 /* A link was clicked--we emit the "url_clicked" signal |
| |
5786 * with the URL as the argument */ |
| |
5787 g_object_ref(G_OBJECT(tag)); |
| |
5788 g_signal_emit(imhtml, signals[URL_CLICKED], 0, g_object_get_data(G_OBJECT(tag), "link_url")); |
| |
5789 g_object_unref(G_OBJECT(tag)); |
| |
5790 g_object_set_data(G_OBJECT(tag), "visited", GINT_TO_POINTER(TRUE)); |
| |
5791 gtk_imhtml_set_link_color(GTK_IMHTML(imhtml), tag); |
| |
5792 } |
| |
5793 |
| |
5794 gboolean gtk_imhtml_link_activate(GtkIMHtmlLink *link) |
| |
5795 { |
| |
5796 g_return_val_if_fail(link, FALSE); |
| |
5797 |
| |
5798 if (link->tag) { |
| |
5799 gtk_imhtml_activate_tag(link->imhtml, link->tag); |
| |
5800 } else if (link->url) { |
| |
5801 g_signal_emit(link->imhtml, signals[URL_CLICKED], 0, link->url); |
| |
5802 } else |
| |
5803 return FALSE; |
| |
5804 return TRUE; |
| |
5805 } |
| |
5806 |
| |
5807 const char *gtk_imhtml_link_get_url(GtkIMHtmlLink *link) |
| |
5808 { |
| |
5809 return link->url; |
| |
5810 } |
| |
5811 |
| |
5812 const GtkTextTag * gtk_imhtml_link_get_text_tag(GtkIMHtmlLink *link) |
| |
5813 { |
| |
5814 return link->tag; |
| |
5815 } |
| |
5816 |