1 /*
2 * ao_openuri.c - this file is part of Addons, a Geany plugin
3 *
4 * Copyright 2009-2011 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 * $Id$
21 */
22
23 #include <glib-object.h>
24 #include <string.h>
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29 #include <geanyplugin.h>
30
31 #include "addons.h"
32 #include "ao_openuri.h"
33
34
35 typedef struct _AoOpenUriPrivate AoOpenUriPrivate;
36
37 #define AO_OPEN_URI_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj),\
38 AO_OPEN_URI_TYPE, AoOpenUriPrivate))
39
40 struct _AoOpenUri
41 {
42 GObject parent;
43 };
44
45 struct _AoOpenUriClass
46 {
47 GObjectClass parent_class;
48 };
49
50 struct _AoOpenUriPrivate
51 {
52 gboolean enable_openuri;
53 gchar *uri;
54
55 GtkWidget *menu_item_open;
56 GtkWidget *menu_item_copy;
57 GtkWidget *menu_item_sep;
58 };
59
60 enum
61 {
62 PROP_0,
63 PROP_ENABLE_OPENURI
64 };
65
66
67 static void ao_open_uri_finalize (GObject *object);
68 static void ao_open_uri_set_property (GObject *object, guint prop_id,
69 const GValue *value, GParamSpec *pspec);
70
71
G_DEFINE_TYPE(AoOpenUri,ao_open_uri,G_TYPE_OBJECT)72 G_DEFINE_TYPE(AoOpenUri, ao_open_uri, G_TYPE_OBJECT)
73
74
75
76 static void ao_open_uri_class_init(AoOpenUriClass *klass)
77 {
78 GObjectClass *g_object_class;
79
80 g_object_class = G_OBJECT_CLASS(klass);
81
82 g_object_class->finalize = ao_open_uri_finalize;
83 g_object_class->set_property = ao_open_uri_set_property;
84
85 g_type_class_add_private(klass, sizeof(AoOpenUriPrivate));
86
87 g_object_class_install_property(g_object_class,
88 PROP_ENABLE_OPENURI,
89 g_param_spec_boolean(
90 "enable-openuri",
91 "enable-openuri",
92 "Whether to show a menu item in the editor menu to open URIs",
93 FALSE,
94 G_PARAM_WRITABLE));
95 }
96
97
ao_open_uri_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)98 static void ao_open_uri_set_property(GObject *object, guint prop_id,
99 const GValue *value, GParamSpec *pspec)
100 {
101 AoOpenUriPrivate *priv = AO_OPEN_URI_GET_PRIVATE(object);
102
103 switch (prop_id)
104 {
105 case PROP_ENABLE_OPENURI:
106 priv->enable_openuri = g_value_get_boolean(value);
107 break;
108 default:
109 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
110 break;
111 }
112 }
113
114
ao_open_uri_finalize(GObject * object)115 static void ao_open_uri_finalize(GObject *object)
116 {
117 AoOpenUriPrivate *priv = AO_OPEN_URI_GET_PRIVATE(object);
118
119 g_free(priv->uri);
120 gtk_widget_destroy(priv->menu_item_open);
121 gtk_widget_destroy(priv->menu_item_copy);
122 gtk_widget_destroy(priv->menu_item_sep);
123
124 G_OBJECT_CLASS(ao_open_uri_parent_class)->finalize(object);
125 }
126
127
ao_menu_open_activate_cb(GtkMenuItem * item,AoOpenUri * self)128 static void ao_menu_open_activate_cb(GtkMenuItem *item, AoOpenUri *self)
129 {
130 AoOpenUriPrivate *priv = AO_OPEN_URI_GET_PRIVATE(self);
131
132 if (!EMPTY(priv->uri))
133 utils_open_browser(priv->uri);
134
135 }
136
137
ao_menu_copy_activate_cb(GtkMenuItem * item,AoOpenUri * self)138 static void ao_menu_copy_activate_cb(GtkMenuItem *item, AoOpenUri *self)
139 {
140 AoOpenUriPrivate *priv = AO_OPEN_URI_GET_PRIVATE(self);
141
142 if (!EMPTY(priv->uri))
143 gtk_clipboard_set_text(gtk_clipboard_get(gdk_atom_intern("CLIPBOARD", FALSE)), priv->uri, -1);
144 }
145
146
ao_find_icon_name(const gchar * request,const gchar * fallback)147 static const gchar *ao_find_icon_name(const gchar *request, const gchar *fallback)
148 {
149 GtkIconTheme *theme = gtk_icon_theme_get_default();
150
151 if (gtk_icon_theme_has_icon(theme, request))
152 return request;
153 else
154 return fallback;
155 }
156
157
ao_open_uri_init(AoOpenUri * self)158 static void ao_open_uri_init(AoOpenUri *self)
159 {
160 AoOpenUriPrivate *priv = AO_OPEN_URI_GET_PRIVATE(self);
161
162 priv->uri = NULL;
163
164 priv->menu_item_open = ao_image_menu_item_new(
165 ao_find_icon_name("text-html", GTK_STOCK_NEW), _("Open URI"));
166 gtk_container_add(GTK_CONTAINER(geany->main_widgets->editor_menu), priv->menu_item_open);
167 gtk_menu_reorder_child(GTK_MENU(geany->main_widgets->editor_menu), priv->menu_item_open, 0);
168 gtk_widget_hide(priv->menu_item_open);
169 g_signal_connect(priv->menu_item_open, "activate", G_CALLBACK(ao_menu_open_activate_cb), self);
170
171 priv->menu_item_copy = ao_image_menu_item_new(GTK_STOCK_COPY, _("Copy URI"));
172 gtk_container_add(GTK_CONTAINER(geany->main_widgets->editor_menu), priv->menu_item_copy);
173 gtk_menu_reorder_child(GTK_MENU(geany->main_widgets->editor_menu), priv->menu_item_copy, 1);
174 gtk_widget_hide(priv->menu_item_copy);
175 g_signal_connect(priv->menu_item_copy, "activate", G_CALLBACK(ao_menu_copy_activate_cb), self);
176
177 priv->menu_item_sep = gtk_separator_menu_item_new();
178 gtk_container_add(GTK_CONTAINER(geany->main_widgets->editor_menu), priv->menu_item_sep);
179 gtk_menu_reorder_child(GTK_MENU(geany->main_widgets->editor_menu), priv->menu_item_sep, 2);
180
181 }
182
183
ao_open_uri_new(gboolean enable)184 AoOpenUri *ao_open_uri_new(gboolean enable)
185 {
186 return g_object_new(AO_OPEN_URI_TYPE, "enable-openuri", enable, NULL);
187 }
188
189
ao_uri_is_link(const gchar * uri)190 static gboolean ao_uri_is_link(const gchar *uri)
191 {
192 gchar *dot;
193
194 g_return_val_if_fail (uri != NULL, FALSE);
195
196 if ((dot = strchr(uri, '.')) && *dot != '\0')
197 { /* we require two dots and don't allow any spaces (www.domain.tld)
198 * unless we get too many matches */
199 return (strchr(dot + 1, '.') && ! strchr(uri, ' '));
200 }
201 return FALSE;
202 }
203
204
205 /* based on g_uri_parse_scheme() but only checks for a scheme, doesn't return it */
ao_uri_has_scheme(const gchar * uri)206 static gboolean ao_uri_has_scheme(const gchar *uri)
207 {
208 const gchar *p;
209 gchar c;
210
211 g_return_val_if_fail (uri != NULL, FALSE);
212
213 p = uri;
214
215 if (! g_ascii_isalpha(*p))
216 return FALSE;
217
218 while (1)
219 {
220 c = *p++;
221
222 if (c == ':' && strncmp(p, "//", 2) == 0)
223 return TRUE;
224
225 if (! (g_ascii_isalnum(c) || c == '+' || c == '-' || c == '.'))
226 return FALSE;
227 }
228 return FALSE;
229 }
230
231
ao_open_uri_update_menu(AoOpenUri * openuri,GeanyDocument * doc,gint pos)232 void ao_open_uri_update_menu(AoOpenUri *openuri, GeanyDocument *doc, gint pos)
233 {
234 gchar *text;
235 AoOpenUriPrivate *priv;
236
237 g_return_if_fail(openuri != NULL);
238 g_return_if_fail(doc != NULL);
239
240 priv = AO_OPEN_URI_GET_PRIVATE(openuri);
241
242 if (! priv->enable_openuri)
243 return;
244
245 /* if we have a selection, prefer it over the current word */
246 if (sci_has_selection(doc->editor->sci))
247 text = sci_get_selection_contents(doc->editor->sci);
248 else
249 text = editor_get_word_at_pos(doc->editor, pos, GEANY_WORDCHARS"@.://-?&%#=~+,;");
250
251 /* TODO be more restrictive when handling selections as there are too many hits by now */
252 if (text != NULL && (ao_uri_has_scheme(text) || ao_uri_is_link(text)))
253 {
254 gsize len = strlen(text);
255 /* remove trailing dots and colons */
256 if (text[len - 1] == '.' || text[len - 1] == ':')
257 text[len - 1] = '\0';
258
259 setptr(priv->uri, text);
260
261 gtk_widget_show(priv->menu_item_open);
262 gtk_widget_show(priv->menu_item_copy);
263 gtk_widget_show(priv->menu_item_sep);
264 }
265 else
266 {
267 g_free(text);
268
269 gtk_widget_hide(priv->menu_item_open);
270 gtk_widget_hide(priv->menu_item_copy);
271 gtk_widget_hide(priv->menu_item_sep);
272 }
273 }
274
275
276