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