1 /*
2  * Copyright (C) 2009 - 2011 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301, USA.
19  */
20 
21 #include <string.h>
22 #include <binreloc/gda-binreloc.h>
23 #include "gdaui-entry-text.h"
24 #include <libgda/gda-data-handler.h>
25 #include <libgda/gda-blob-op.h>
26 
27 #ifdef HAVE_GTKSOURCEVIEW
28   #ifdef GTK_DISABLE_SINGLE_INCLUDES
29     #undef GTK_DISABLE_SINGLE_INCLUDES
30   #endif
31 
32   #include <gtksourceview/gtksourceview.h>
33   #include <gtksourceview/gtksourcelanguagemanager.h>
34   #include <gtksourceview/gtksourcebuffer.h>
35   #include <gtksourceview/gtksourcestyleschememanager.h>
36   #include <gtksourceview/gtksourcestylescheme.h>
37 #endif
38 #define LANGUAGE_SQL "gda-sql"
39 
40 /*
41  * Main static functions
42  */
43 static void gdaui_entry_text_class_init (GdauiEntryTextClass * class);
44 static void gdaui_entry_text_init (GdauiEntryText * srv);
45 static void gdaui_entry_text_dispose (GObject   * object);
46 static void gdaui_entry_text_finalize (GObject   * object);
47 
48 /* virtual functions */
49 static GtkWidget *create_entry (GdauiEntryWrapper *mgwrap);
50 static void       real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value);
51 static GValue    *real_get_value (GdauiEntryWrapper *mgwrap);
52 static void       connect_signals(GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb);
53 static void       set_editable (GdauiEntryWrapper *mgwrap, gboolean editable);
54 
55 /* get a pointer to the parents to be able to call their destructor */
56 static GObjectClass  *parent_class = NULL;
57 
58 /* private structure */
59 struct _GdauiEntryTextPrivate
60 {
61 	GtkTextBuffer *buffer;
62 	GtkWidget     *view;
63 	gchar         *lang; /* for code colourisation */
64 	GtkWrapMode    wrapmode;
65 };
66 
67 
68 GType
gdaui_entry_text_get_type(void)69 gdaui_entry_text_get_type (void)
70 {
71 	static GType type = 0;
72 
73 	if (G_UNLIKELY (type == 0)) {
74 		static const GTypeInfo info = {
75 			sizeof (GdauiEntryTextClass),
76 			(GBaseInitFunc) NULL,
77 			(GBaseFinalizeFunc) NULL,
78 			(GClassInitFunc) gdaui_entry_text_class_init,
79 			NULL,
80 			NULL,
81 			sizeof (GdauiEntryText),
82 			0,
83 			(GInstanceInitFunc) gdaui_entry_text_init,
84 			0
85 		};
86 
87 		type = g_type_register_static (GDAUI_TYPE_ENTRY_WRAPPER, "GdauiEntryText", &info, 0);
88 	}
89 	return type;
90 }
91 
92 static void
gdaui_entry_text_class_init(GdauiEntryTextClass * class)93 gdaui_entry_text_class_init (GdauiEntryTextClass * class)
94 {
95 	GObjectClass   *object_class = G_OBJECT_CLASS (class);
96 
97 	parent_class = g_type_class_peek_parent (class);
98 
99 	object_class->dispose = gdaui_entry_text_dispose;
100 	object_class->finalize = gdaui_entry_text_finalize;
101 
102 	GDAUI_ENTRY_WRAPPER_CLASS (class)->create_entry = create_entry;
103 	GDAUI_ENTRY_WRAPPER_CLASS (class)->real_set_value = real_set_value;
104 	GDAUI_ENTRY_WRAPPER_CLASS (class)->real_get_value = real_get_value;
105 	GDAUI_ENTRY_WRAPPER_CLASS (class)->connect_signals = connect_signals;
106 	GDAUI_ENTRY_WRAPPER_CLASS (class)->set_editable = set_editable;
107 }
108 
109 static void
gdaui_entry_text_init(GdauiEntryText * gdaui_entry_text)110 gdaui_entry_text_init (GdauiEntryText *gdaui_entry_text)
111 {
112 	gdaui_entry_text->priv = g_new0 (GdauiEntryTextPrivate, 1);
113 	gdaui_entry_text->priv->buffer = NULL;
114 	gdaui_entry_text->priv->view = NULL;
115 	gdaui_entry_text->priv->wrapmode = GTK_WRAP_NONE;
116 	gtk_widget_set_vexpand (GTK_WIDGET (gdaui_entry_text), TRUE);
117 }
118 
119 /**
120  * gdaui_entry_text_new
121  * @dh: the data handler to be used by the new widget
122  * @type: the requested data type (compatible with @dh)
123  * @options: the options
124  *
125  * Creates a new widget which is mainly a GtkEntry
126  *
127  * Returns: the new widget
128  */
129 GtkWidget *
gdaui_entry_text_new(GdaDataHandler * dh,GType type,const gchar * options)130 gdaui_entry_text_new (GdaDataHandler *dh, GType type, const gchar *options)
131 {
132 	GObject *obj;
133 	GdauiEntryText *mgtxt;
134 
135 	g_return_val_if_fail (dh && GDA_IS_DATA_HANDLER (dh), NULL);
136 	g_return_val_if_fail (gda_data_handler_accepts_g_type (dh, type), NULL);
137 
138 	obj = g_object_new (GDAUI_TYPE_ENTRY_TEXT, "handler", dh, NULL);
139 	mgtxt = GDAUI_ENTRY_TEXT (obj);
140 	if (options && *options) {
141 
142                 GdaQuarkList *params;
143                 const gchar *str;
144 
145                 params = gda_quark_list_new_from_string (options);
146 #ifdef HAVE_GTKSOURCEVIEW
147                 str = gda_quark_list_find (params, "PROG_LANG");
148                 if (str)
149 			mgtxt->priv->lang = g_strdup (str);
150 #endif
151 		str = gda_quark_list_find (params, "WRAP_MODE");
152                 if (str) {
153 			if (*str == 'N')
154 				mgtxt->priv->wrapmode = GTK_WRAP_NONE;
155 			else if (*str == 'C')
156 				mgtxt->priv->wrapmode = GTK_WRAP_CHAR;
157 			else if (*str == 'W')
158 				mgtxt->priv->wrapmode = GTK_WRAP_WORD;
159 			else
160 				mgtxt->priv->wrapmode = GTK_WRAP_WORD_CHAR;
161 		}
162                 gda_quark_list_free (params);
163         }
164 
165 	gdaui_data_entry_set_value_type (GDAUI_DATA_ENTRY (mgtxt), type);
166 
167 	return GTK_WIDGET (obj);
168 }
169 
170 
171 static void
gdaui_entry_text_dispose(GObject * object)172 gdaui_entry_text_dispose (GObject   * object)
173 {
174 	GdauiEntryText *gdaui_entry_text;
175 
176 	g_return_if_fail (object != NULL);
177 	g_return_if_fail (GDAUI_IS_ENTRY_TEXT (object));
178 
179 	gdaui_entry_text = GDAUI_ENTRY_TEXT (object);
180 	if (gdaui_entry_text->priv) {
181 	}
182 
183 	/* parent class */
184 	parent_class->dispose (object);
185 }
186 
187 static void
gdaui_entry_text_finalize(GObject * object)188 gdaui_entry_text_finalize (GObject   * object)
189 {
190 	GdauiEntryText *gdaui_entry_text;
191 
192 	g_return_if_fail (object != NULL);
193 	g_return_if_fail (GDAUI_IS_ENTRY_TEXT (object));
194 
195 	gdaui_entry_text = GDAUI_ENTRY_TEXT (object);
196 	if (gdaui_entry_text->priv) {
197 		g_free (gdaui_entry_text->priv->lang);
198 
199 		g_free (gdaui_entry_text->priv);
200 		gdaui_entry_text->priv = NULL;
201 	}
202 
203 	/* parent class */
204 	parent_class->finalize (object);
205 }
206 
207 #ifdef HAVE_GTKSOURCEVIEW
208 static void
create_tags_for_sql(GtkTextBuffer * buffer,const gchar * language)209 create_tags_for_sql (GtkTextBuffer *buffer, const gchar *language)
210 {
211 	GtkSourceLanguageManager *mgr;
212 	GtkSourceLanguage *lang;
213 	gchar ** current_search_path;
214 	gint len;
215 	gchar ** search_path;
216 
217 	GtkSourceStyleSchemeManager* sch_mgr;
218 	GtkSourceStyleScheme *sch;
219 
220 	g_return_if_fail (language != NULL);
221 	g_return_if_fail (!strcmp (language, LANGUAGE_SQL));
222 	mgr = gtk_source_language_manager_new ();
223 
224 	/* alter search path */
225 	current_search_path = (gchar **) gtk_source_language_manager_get_search_path (mgr);
226 	len = g_strv_length (current_search_path);
227 	search_path = g_new0 (gchar*, len + 2);
228 	memcpy (search_path, current_search_path, sizeof (gchar*) * len);
229 	search_path [len] = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "language-specs", NULL);
230 	gtk_source_language_manager_set_search_path (mgr, search_path);
231 	g_free (search_path [len]);
232 	g_free (search_path);
233 
234 	lang = gtk_source_language_manager_get_language (mgr, "gda-sql");
235 
236 	if (!lang) {
237 		gchar *tmp;
238 		tmp = gda_gbr_get_file_path (GDA_DATA_DIR, LIBGDA_ABI_NAME, "language-spec", NULL);
239 		g_print ("Could not find the gda-sql.lang file in %s,\nusing the default SQL highlighting rules.\n",
240 			 tmp);
241 		g_free (tmp);
242 		lang = gtk_source_language_manager_get_language (mgr, "sql");
243 	}
244 	if (lang)
245 		gtk_source_buffer_set_language (GTK_SOURCE_BUFFER (buffer), lang);
246 
247 	g_object_unref (mgr);
248 
249 	sch_mgr = gtk_source_style_scheme_manager_get_default ();
250 	sch = gtk_source_style_scheme_manager_get_scheme (sch_mgr, "tango");
251 	if (sch)
252 		gtk_source_buffer_set_style_scheme (GTK_SOURCE_BUFFER (buffer), sch);
253 }
254 #endif
255 
256 
257 
258 static GtkWidget *
create_entry(GdauiEntryWrapper * mgwrap)259 create_entry (GdauiEntryWrapper *mgwrap)
260 {
261 	GdauiEntryText *mgtxt;
262 	GtkWidget *sw;
263 
264 	g_return_val_if_fail (mgwrap && GDAUI_IS_ENTRY_TEXT (mgwrap), NULL);
265 	mgtxt = GDAUI_ENTRY_TEXT (mgwrap);
266 	g_return_val_if_fail (mgtxt->priv, NULL);
267 
268 #ifdef HAVE_GTKSOURCEVIEW
269 	if (mgtxt->priv->lang) {
270 		GtkSourceBuffer *sbuf;
271 		mgtxt->priv->view = gtk_source_view_new ();
272 		sbuf = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (mgtxt->priv->view)));
273 
274 		GtkSourceLanguageManager *lm;
275 		GtkSourceLanguage *sl;
276 		lm = gtk_source_language_manager_get_default ();
277 		sl = gtk_source_language_manager_get_language (lm, mgtxt->priv->lang);
278 
279 		gtk_source_buffer_set_language (sbuf, sl);
280 		gtk_source_buffer_set_highlight_syntax (sbuf, TRUE);
281 		if (! strcmp (mgtxt->priv->lang, LANGUAGE_SQL))
282 			create_tags_for_sql (GTK_TEXT_BUFFER (sbuf), LANGUAGE_SQL);
283 	}
284 	else
285 		mgtxt->priv->view = gtk_text_view_new ();
286 #else
287 	mgtxt->priv->view = gtk_text_view_new ();
288 #endif
289 	mgtxt->priv->buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mgtxt->priv->view));
290 	gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (mgtxt->priv->view), mgtxt->priv->wrapmode);
291 	sw = gtk_scrolled_window_new (NULL, NULL);
292 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE);
293 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
294 					GTK_POLICY_AUTOMATIC,
295 					GTK_POLICY_AUTOMATIC);
296 	gtk_container_add (GTK_CONTAINER (sw), mgtxt->priv->view);
297 	gtk_widget_show (mgtxt->priv->view);
298 
299 	return sw;
300 }
301 
302 static void
real_set_value(GdauiEntryWrapper * mgwrap,const GValue * value)303 real_set_value (GdauiEntryWrapper *mgwrap, const GValue *value)
304 {
305 	GdauiEntryText *mgtxt;
306 
307 	g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_TEXT (mgwrap));
308 	mgtxt = GDAUI_ENTRY_TEXT (mgwrap);
309 	g_return_if_fail (mgtxt->priv);
310 
311 	gtk_text_buffer_set_text (mgtxt->priv->buffer, "", -1);
312 	if (value) {
313 		if (! gda_value_is_null ((GValue *) value)) {
314 			GdaDataHandler *dh;
315 			gchar *str;
316 			gboolean done = FALSE;
317 
318 			if (G_VALUE_TYPE (value) == GDA_TYPE_BLOB) {
319 				const GdaBlob *blob;
320 				GdaBinary *bin;
321 				blob = gda_value_get_blob (value);
322 				bin = (GdaBinary *) blob;
323 				if (blob->op &&
324 				    (bin->binary_length != gda_blob_op_get_length (blob->op)))
325                                         gda_blob_op_read_all (blob->op, (GdaBlob*) blob);
326 				if (g_utf8_validate ((gchar*) bin->data, bin->binary_length, NULL)) {
327 					gtk_text_buffer_set_text (mgtxt->priv->buffer, (gchar*) bin->data,
328 								  bin->binary_length);
329 					done = TRUE;
330 				}
331 			}
332 			else  if (G_VALUE_TYPE (value) == GDA_TYPE_BINARY) {
333 				const GdaBinary *bin;
334 				bin = gda_value_get_binary (value);
335 				if (g_utf8_validate ((gchar*) bin->data, bin->binary_length, NULL)) {
336 					gtk_text_buffer_set_text (mgtxt->priv->buffer, (gchar*) bin->data,
337 								  bin->binary_length);
338 					done = TRUE;
339 				}
340 			}
341 
342 			if (!done) {
343 				dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap));
344 				str = gda_data_handler_get_str_from_value (dh, value);
345 				if (str) {
346 					gtk_text_buffer_set_text (mgtxt->priv->buffer, str, -1);
347 					g_free (str);
348 				}
349 			}
350 		}
351 	}
352 }
353 
354 static GValue *
real_get_value(GdauiEntryWrapper * mgwrap)355 real_get_value (GdauiEntryWrapper *mgwrap)
356 {
357 	GValue *value;
358 	GdauiEntryText *mgtxt;
359 	GdaDataHandler *dh;
360 	gchar *str;
361 	GtkTextIter start, end;
362 
363 	g_return_val_if_fail (mgwrap && GDAUI_IS_ENTRY_TEXT (mgwrap), NULL);
364 	mgtxt = GDAUI_ENTRY_TEXT (mgwrap);
365 	g_return_val_if_fail (mgtxt->priv, NULL);
366 
367 	dh = gdaui_data_entry_get_handler (GDAUI_DATA_ENTRY (mgwrap));
368 	gtk_text_buffer_get_start_iter (mgtxt->priv->buffer, &start);
369 	gtk_text_buffer_get_end_iter (mgtxt->priv->buffer, &end);
370 	str = gtk_text_buffer_get_text (mgtxt->priv->buffer, &start, &end, FALSE);
371 	value = gda_data_handler_get_value_from_str (dh, str,
372 						     gdaui_data_entry_get_value_type (GDAUI_DATA_ENTRY (mgwrap)));
373 	g_free (str);
374 	if (!value) {
375 		/* in case the gda_data_handler_get_value_from_sql() returned an error because
376 		   the contents of the GtkEntry cannot be interpreted as a GValue */
377 		value = gda_value_new_null ();
378 	}
379 
380 	return value;
381 }
382 
383 typedef void (*Callback2) (gpointer, gpointer);
384 static gboolean
focus_out_cb(GtkWidget * widget,GdkEventFocus * event,GdauiEntryText * mgtxt)385 focus_out_cb (GtkWidget *widget, GdkEventFocus *event, GdauiEntryText *mgtxt)
386 {
387 	GCallback activate_cb;
388 	activate_cb = g_object_get_data (G_OBJECT (widget), "_activate_cb");
389 	g_assert (activate_cb);
390 	((Callback2)activate_cb) (widget, mgtxt);
391 
392 	return gtk_widget_event (GTK_WIDGET (mgtxt), (GdkEvent*) event);
393 }
394 
395 static void
connect_signals(GdauiEntryWrapper * mgwrap,GCallback modify_cb,GCallback activate_cb)396 connect_signals(GdauiEntryWrapper *mgwrap, GCallback modify_cb, GCallback activate_cb)
397 {
398 	GdauiEntryText *mgtxt;
399 
400 	g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_TEXT (mgwrap));
401 	mgtxt = GDAUI_ENTRY_TEXT (mgwrap);
402 	g_return_if_fail (mgtxt->priv);
403 
404 	g_object_set_data (G_OBJECT (mgtxt->priv->view), "_activate_cb", activate_cb);
405 	g_signal_connect (G_OBJECT (mgtxt->priv->buffer), "changed",
406 			  modify_cb, mgwrap);
407 	g_signal_connect (G_OBJECT (mgtxt->priv->view), "focus-out-event",
408 			  G_CALLBACK (focus_out_cb), mgtxt);
409 	/* FIXME: how does the user "activates" the GtkTextView widget ? */
410 }
411 
412 static void
set_editable(GdauiEntryWrapper * mgwrap,gboolean editable)413 set_editable (GdauiEntryWrapper *mgwrap, gboolean editable)
414 {
415 	GdauiEntryText *mgtxt;
416 
417 	g_return_if_fail (mgwrap && GDAUI_IS_ENTRY_TEXT (mgwrap));
418 	mgtxt = GDAUI_ENTRY_TEXT (mgwrap);
419 
420 	gtk_text_view_set_editable (GTK_TEXT_VIEW (mgtxt->priv->view), editable);
421 }
422