1 /*
2  * Copyright (C) 2009 - 2012 Vivien Malerba <malerba@gnome-db.org>
3  * Copyright (C) 2010 David King <davidk@openismus.com>
4  * Copyright (C) 2011 Murray Cumming <murrayc@murrayc.com>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (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 
21 #include <glib/gi18n-lib.h>
22 #include <string.h>
23 #include <gtk/gtk.h>
24 #include <libgda/gda-tree.h>
25 #include "query-favorite-selector.h"
26 #include "../mgr-favorites.h"
27 #include <libgda-ui/gdaui-tree-store.h>
28 #include "../dnd.h"
29 #include "../support.h"
30 #include "marshal.h"
31 #include "../gdaui-bar.h"
32 #include <gdk/gdkkeysyms.h>
33 #include <libgda-ui/internal/popup-container.h>
34 #include "query-editor.h"
35 
36 struct _QueryFavoriteSelectorPrivate {
37 	BrowserConnection *bcnc;
38 	GdaTree *tree;
39 	GtkWidget *treeview;
40 	guint idle_update_favorites;
41 
42 	GtkWidget *popup_menu;
43 	GtkWidget *popup_properties;
44 	GtkWidget *properties_name;
45 	GtkWidget *properties_action;
46 	GtkWidget *properties_text;
47 	gint       properties_id;
48 	gint       properties_position;
49 	guint      prop_save_timeout;
50 };
51 
52 static void query_favorite_selector_class_init (QueryFavoriteSelectorClass *klass);
53 static void query_favorite_selector_init       (QueryFavoriteSelector *tsel,
54 				       QueryFavoriteSelectorClass *klass);
55 static void query_favorite_selector_dispose   (GObject *object);
56 
57 static void favorites_changed_cb (ToolsFavorites *bfav, QueryFavoriteSelector *tsel);
58 
59 enum {
60 	SELECTION_CHANGED,
61 	LAST_SIGNAL
62 };
63 
64 static guint query_favorite_selector_signals[LAST_SIGNAL] = { 0 };
65 static GObjectClass *parent_class = NULL;
66 
67 /* columns of the resulting GtkTreeModel */
68 enum {
69 	COLUMN_POSITION = 0,
70 	COLUMN_ICON = 1,
71 	COLUMN_CONTENTS = 2,
72 	COLUMN_TYPE = 3,
73 	COLUMN_ID = 4,
74 	COLUMN_NAME = 5,
75 	COLUMN_SUMMARY = 6
76 };
77 
78 
79 /*
80  * QueryFavoriteSelector class implementation
81  */
82 
83 static void
query_favorite_selector_class_init(QueryFavoriteSelectorClass * klass)84 query_favorite_selector_class_init (QueryFavoriteSelectorClass *klass)
85 {
86 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
87 
88 	parent_class = g_type_class_peek_parent (klass);
89 
90 	/* signals */
91 	query_favorite_selector_signals [SELECTION_CHANGED] =
92                 g_signal_new ("selection-changed",
93                               G_TYPE_FROM_CLASS (object_class),
94                               G_SIGNAL_RUN_FIRST,
95                               G_STRUCT_OFFSET (QueryFavoriteSelectorClass, selection_changed),
96                               NULL, NULL,
97                               _qe_marshal_VOID__INT_ENUM_STRING, G_TYPE_NONE,
98                               3, G_TYPE_INT, G_TYPE_UINT, G_TYPE_STRING);
99 	klass->selection_changed = NULL;
100 
101 	object_class->dispose = query_favorite_selector_dispose;
102 }
103 
104 
105 static void
query_favorite_selector_init(QueryFavoriteSelector * tsel,G_GNUC_UNUSED QueryFavoriteSelectorClass * klass)106 query_favorite_selector_init (QueryFavoriteSelector *tsel, G_GNUC_UNUSED QueryFavoriteSelectorClass *klass)
107 {
108 	tsel->priv = g_new0 (QueryFavoriteSelectorPrivate, 1);
109 	tsel->priv->idle_update_favorites = 0;
110 	tsel->priv->prop_save_timeout = 0;
111 
112 	gtk_orientable_set_orientation (GTK_ORIENTABLE (tsel), GTK_ORIENTATION_VERTICAL);
113 }
114 
115 static void
query_favorite_selector_dispose(GObject * object)116 query_favorite_selector_dispose (GObject *object)
117 {
118 	QueryFavoriteSelector *tsel = (QueryFavoriteSelector *) object;
119 
120 	/* free memory */
121 	if (tsel->priv) {
122 		if (tsel->priv->idle_update_favorites != 0)
123 			g_source_remove (tsel->priv->idle_update_favorites);
124 		if (tsel->priv->tree)
125 			g_object_unref (tsel->priv->tree);
126 
127 		if (tsel->priv->bcnc) {
128 			g_signal_handlers_disconnect_by_func (browser_connection_get_favorites (tsel->priv->bcnc),
129 							      G_CALLBACK (favorites_changed_cb), tsel);
130 			g_object_unref (tsel->priv->bcnc);
131 		}
132 
133 		if (tsel->priv->popup_properties)
134 			gtk_widget_destroy (tsel->priv->popup_properties);
135 		if (tsel->priv->popup_menu)
136 			gtk_widget_destroy (tsel->priv->popup_menu);
137 		if (tsel->priv->prop_save_timeout)
138 			g_source_remove (tsel->priv->prop_save_timeout);
139 
140 		g_free (tsel->priv);
141 		tsel->priv = NULL;
142 	}
143 
144 	parent_class->dispose (object);
145 }
146 
147 GType
query_favorite_selector_get_type(void)148 query_favorite_selector_get_type (void)
149 {
150 	static GType type = 0;
151 
152 	if (G_UNLIKELY (type == 0)) {
153 		static const GTypeInfo info = {
154 			sizeof (QueryFavoriteSelectorClass),
155 			(GBaseInitFunc) NULL,
156 			(GBaseFinalizeFunc) NULL,
157 			(GClassInitFunc) query_favorite_selector_class_init,
158 			NULL,
159 			NULL,
160 			sizeof (QueryFavoriteSelector),
161 			0,
162 			(GInstanceInitFunc) query_favorite_selector_init,
163 			0
164 		};
165 		type = g_type_register_static (GTK_TYPE_BOX, "QueryFavoriteSelector",
166 					       &info, 0);
167 	}
168 	return type;
169 }
170 
171 static void
favorite_delete_selected(QueryFavoriteSelector * tsel)172 favorite_delete_selected (QueryFavoriteSelector *tsel)
173 {
174 	GtkTreeModel *model;
175 	GtkTreeSelection *select;
176 	GtkTreeIter iter;
177 
178 	select = gtk_tree_view_get_selection (GTK_TREE_VIEW (tsel->priv->treeview));
179 	if (gtk_tree_selection_get_selected (select, &model, &iter)) {
180 		ToolsFavorites *bfav;
181 		ToolsFavoritesAttributes fav;
182 		GError *lerror = NULL;
183 
184 		memset (&fav, 0, sizeof (ToolsFavoritesAttributes));
185 		gtk_tree_model_get (model, &iter,
186 				    COLUMN_ID, &(fav.id), -1);
187 		bfav = browser_connection_get_favorites (tsel->priv->bcnc);
188 		if (!gda_tools_favorites_delete (bfav, 0, &fav, NULL)) {
189 			browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*)tsel),
190 					    _("Could not remove favorite: %s"),
191 					    lerror && lerror->message ? lerror->message : _("No detail"));
192 			if (lerror)
193 				g_error_free (lerror);
194 		}
195 		else {
196 			/* remove any associated action */
197 			gint id;
198 			gchar *tmp;
199 			tmp = g_strdup_printf ("QUERY%d", fav.id);
200 			id = gda_tools_favorites_find (bfav, 0, tmp, &fav, NULL);
201 			if (id >= 0) {
202 				gda_tools_favorites_delete (bfav, 0, &fav, NULL);
203 				/*g_print ("ACTION_DELETED %d: %s\n", fav.id, tmp);*/
204 			}
205 			g_free (tmp);
206 		}
207 	}
208 }
209 
210 static gboolean
key_press_event_cb(G_GNUC_UNUSED GtkTreeView * treeview,GdkEventKey * event,QueryFavoriteSelector * tsel)211 key_press_event_cb (G_GNUC_UNUSED GtkTreeView *treeview, GdkEventKey *event,
212 		    QueryFavoriteSelector *tsel)
213 {
214 	if (event->keyval == GDK_KEY_Delete) {
215 		favorite_delete_selected (tsel);
216 		return TRUE;
217 	}
218 	return FALSE; /* not handled */
219 }
220 
221 
222 static void
selection_changed_cb(GtkTreeView * treeview,G_GNUC_UNUSED GtkTreePath * path,G_GNUC_UNUSED GtkTreeViewColumn * column,QueryFavoriteSelector * tsel)223 selection_changed_cb (GtkTreeView *treeview, G_GNUC_UNUSED GtkTreePath *path,
224 		      G_GNUC_UNUSED GtkTreeViewColumn *column, QueryFavoriteSelector *tsel)
225 {
226 	GtkTreeModel *model;
227 	GtkTreeSelection *select;
228 	GtkTreeIter iter;
229 
230 	select = gtk_tree_view_get_selection (treeview);
231 	if (gtk_tree_selection_get_selected (select, &model, &iter)) {
232 		gchar *str;
233 		guint type;
234 		gint fav_id;
235 		gtk_tree_model_get (model, &iter,
236 				    COLUMN_ID, &fav_id,
237 				    COLUMN_TYPE, &type,
238 				    COLUMN_CONTENTS, &str, -1);
239 		g_signal_emit (tsel, query_favorite_selector_signals [SELECTION_CHANGED], 0, fav_id, type, str);
240 		g_free (str);
241 	}
242 }
243 
244 static gboolean
prop_save_timeout(QueryFavoriteSelector * tsel)245 prop_save_timeout (QueryFavoriteSelector *tsel)
246 {
247 	ToolsFavorites *bfav;
248 	ToolsFavoritesAttributes fav;
249 	GError *error = NULL;
250 	gboolean allok, actiondel = TRUE;
251 
252 	bfav = browser_connection_get_favorites (tsel->priv->bcnc);
253 
254 	memset (&fav, 0, sizeof (ToolsFavoritesAttributes));
255 	fav.id = tsel->priv->properties_id;
256 	fav.type = GDA_TOOLS_FAVORITES_QUERIES;
257 	fav.name = (gchar*) gtk_entry_get_text (GTK_ENTRY (tsel->priv->properties_name));
258 	fav.descr = NULL;
259 	fav.contents = query_editor_get_all_text (QUERY_EDITOR (tsel->priv->properties_text));
260 
261 	allok = gda_tools_favorites_add (bfav, 0, &fav, ORDER_KEY_QUERIES,
262 				       tsel->priv->properties_position, &error);
263 	if (! allok) {
264 		browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tsel),
265 				    _("Could not add favorite: %s"),
266 				    error && error->message ? error->message : _("No detail"));
267 		if (error)
268 			g_error_free (error);
269 	}
270 	g_free (fav.contents);
271 
272 	if (allok && (fav.id >= 0)) {
273 		gboolean is_action;
274 		gint qid = fav.id;
275 		is_action = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (tsel->priv->properties_action));
276 		if (is_action) {
277 			fav.id = -1;
278 			fav.type = GDA_TOOLS_FAVORITES_ACTIONS;
279 			fav.name = (gchar*) gtk_entry_get_text (GTK_ENTRY (tsel->priv->properties_name));
280 			fav.descr = NULL;
281 			fav.contents = g_strdup_printf ("QUERY%d", qid);
282 			allok = gda_tools_favorites_add (bfav, 0, &fav, -1,
283 						       tsel->priv->properties_position, &error);
284 			if (! allok) {
285 				browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tsel),
286 						    _("Could not add action: %s"),
287 						    error && error->message ? error->message : _("No detail"));
288 				if (error)
289 					g_error_free (error);
290 			}
291 			else
292 				actiondel = FALSE;
293 			/*g_print ("ACTION_ADDED %d: %s\n", fav.id, fav.contents);*/
294 			g_free (fav.contents);
295 		}
296 	}
297 
298 	if (actiondel && (tsel->priv->properties_id >= 0)) {
299 		/* remove action */
300 		gint id;
301 		gchar *tmp;
302 		tmp = g_strdup_printf ("QUERY%d", tsel->priv->properties_id);
303 		id = gda_tools_favorites_find (bfav, 0, tmp, &fav, NULL);
304 		if (id >= 0) {
305 			gda_tools_favorites_delete (bfav, 0, &fav, NULL);
306 			/*g_print ("ACTION_DELETED %d: %s\n", fav.id, tmp);*/
307 		}
308 		g_free (tmp);
309 	}
310 
311 	tsel->priv->prop_save_timeout = 0;
312 	return FALSE; /* remove timeout */
313 }
314 
315 static void
property_changed_cb(G_GNUC_UNUSED GtkWidget * multiple,QueryFavoriteSelector * tsel)316 property_changed_cb (G_GNUC_UNUSED GtkWidget *multiple, QueryFavoriteSelector *tsel)
317 {
318 	if (tsel->priv->prop_save_timeout)
319 		g_source_remove (tsel->priv->prop_save_timeout);
320 	tsel->priv->prop_save_timeout = g_timeout_add (100, (GSourceFunc) prop_save_timeout, tsel);
321 }
322 
323 static void
properties_activated_cb(GtkMenuItem * mitem,QueryFavoriteSelector * tsel)324 properties_activated_cb (GtkMenuItem *mitem, QueryFavoriteSelector *tsel)
325 {
326 	if (! tsel->priv->popup_properties) {
327 		GtkWidget *pcont, *vbox, *hbox, *label, *entry, *text, *grid;
328 		gchar *str;
329 		gfloat align;
330 
331 		pcont = popup_container_new (GTK_WIDGET (mitem));
332 		vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
333 		gtk_container_add (GTK_CONTAINER (pcont), vbox);
334 
335 		label = gtk_label_new ("");
336 		str = g_strdup_printf ("<b>%s:</b>", _("Favorite's properties"));
337 		gtk_label_set_markup (GTK_LABEL (label), str);
338 		g_free (str);
339 		gtk_misc_get_alignment (GTK_MISC (label), NULL, &align);
340 		gtk_misc_set_alignment (GTK_MISC (label), 0., align);
341 		gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
342 
343 		hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); /* HIG */
344 		gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 5);
345 		label = gtk_label_new ("      ");
346 		gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
347 
348 		grid = gtk_grid_new ();
349 		gtk_box_pack_start (GTK_BOX (hbox), grid, TRUE, TRUE, 0);
350 
351 		label = gtk_label_new ("");
352 		str = g_strdup_printf ("<b>%s:</b>", _("Name"));
353 		gtk_label_set_markup (GTK_LABEL (label), str);
354 		g_free (str);
355 		gtk_misc_get_alignment (GTK_MISC (label), NULL, &align);
356 		gtk_misc_set_alignment (GTK_MISC (label), 0., align);
357 		gtk_grid_attach (GTK_GRID (grid), label, 0, 0, 1, 1);
358 
359 		label = gtk_label_new ("");
360 		str = g_strdup_printf ("<b>%s:</b>", _("SQL Code"));
361 		gtk_label_set_markup (GTK_LABEL (label), str);
362 		g_free (str);
363 		gtk_misc_set_alignment (GTK_MISC (label), 0., 0.);
364 		gtk_grid_attach (GTK_GRID (grid), label, 0, 1, 1, 1);
365 
366 		label = gtk_label_new ("");
367 		str = g_strdup_printf ("<b>%s:</b>", _("Is action"));
368 		gtk_label_set_markup (GTK_LABEL (label), str);
369 		g_free (str);
370 		gtk_widget_set_tooltip_text (label, _("Check this option to make this favorite an action\n"
371 						      "which can be proposed for execution from grids\n"
372 						      "containing data. The parameters required to execute\n"
373 						      "the query will be defined from the row selected in the grid"));
374 		gtk_misc_get_alignment (GTK_MISC (label), NULL, &align);
375 		gtk_misc_set_alignment (GTK_MISC (label), 0., align);
376 		gtk_grid_attach (GTK_GRID (grid), label, 0, 2, 1, 1);
377 
378 		entry = gtk_entry_new ();
379 		gtk_grid_attach (GTK_GRID (grid), entry, 1, 0, 1, 1);
380 		tsel->priv->properties_name = entry;
381 		g_signal_connect (entry, "changed",
382 				  G_CALLBACK (property_changed_cb), tsel);
383 
384 		text = query_editor_new ();
385 		query_editor_show_tooltip (QUERY_EDITOR (text), FALSE);
386 		gtk_widget_set_size_request (GTK_WIDGET (text), 400, 300);
387 		gtk_grid_attach (GTK_GRID (grid), text, 1, 1, 1, 1);
388 		tsel->priv->properties_text = text;
389 		g_signal_connect (text, "changed",
390 				  G_CALLBACK (property_changed_cb), tsel);
391 
392 		entry = gtk_check_button_new ();
393 		gtk_grid_attach (GTK_GRID (grid), entry, 1, 2, 1, 1);
394 		tsel->priv->properties_action = entry;
395 		g_signal_connect (entry, "toggled",
396 				  G_CALLBACK (property_changed_cb), tsel);
397 
398 		tsel->priv->popup_properties = pcont;
399 		gtk_widget_show_all (vbox);
400 	}
401 
402 	/* adjust contents */
403 	GtkTreeSelection *selection;
404 	GtkTreeModel *model;
405 	GtkTreeIter iter;
406 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tsel->priv->treeview));
407 	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
408 		gchar *name, *contents;
409 
410 		gtk_tree_model_get (model, &iter,
411 				    COLUMN_ID, &(tsel->priv->properties_id),
412 				    COLUMN_POSITION, &(tsel->priv->properties_position),
413 				    COLUMN_NAME, &name,
414 				    COLUMN_CONTENTS, &contents, -1);
415 
416 		g_signal_handlers_block_by_func (tsel->priv->properties_name,
417 						 G_CALLBACK (property_changed_cb), tsel);
418 		gtk_entry_set_text (GTK_ENTRY (tsel->priv->properties_name), name);
419 		g_signal_handlers_unblock_by_func (tsel->priv->properties_name,
420 						   G_CALLBACK (property_changed_cb), tsel);
421 		g_free (name);
422 
423 		g_signal_handlers_block_by_func (tsel->priv->properties_text,
424 						 G_CALLBACK (property_changed_cb), tsel);
425 		query_editor_set_text (QUERY_EDITOR (tsel->priv->properties_text), contents);
426 		g_signal_handlers_unblock_by_func (tsel->priv->properties_text,
427 						   G_CALLBACK (property_changed_cb), tsel);
428 		g_free (contents);
429 
430 		/* action, if any */
431 		g_signal_handlers_block_by_func (tsel->priv->properties_action,
432 						 G_CALLBACK (property_changed_cb), tsel);
433 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tsel->priv->properties_action), FALSE);
434 		if (tsel->priv->properties_id >= 0) {
435 			gint id;
436 			gchar *tmp;
437 			ToolsFavorites *bfav;
438 			ToolsFavoritesAttributes fav;
439 			bfav = browser_connection_get_favorites (tsel->priv->bcnc);
440 			tmp = g_strdup_printf ("QUERY%d", tsel->priv->properties_id);
441 			id = gda_tools_favorites_find (bfav, 0, tmp, &fav, NULL);
442 			if (id >= 0) {
443 				gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tsel->priv->properties_action),
444 							      TRUE);
445 				/*g_print ("ACTION_USED %d: %s\n", fav.id, tmp);*/
446 			}
447 			g_free (tmp);
448 		}
449 		g_signal_handlers_unblock_by_func (tsel->priv->properties_action,
450 						   G_CALLBACK (property_changed_cb), tsel);
451 
452 		gtk_widget_show (tsel->priv->popup_properties);
453 	}
454 }
455 
456 static void
delete_activated_cb(G_GNUC_UNUSED GtkMenuItem * mitem,QueryFavoriteSelector * tsel)457 delete_activated_cb (G_GNUC_UNUSED GtkMenuItem *mitem, QueryFavoriteSelector *tsel)
458 {
459 	favorite_delete_selected (tsel);
460 }
461 
462 static void
do_popup_menu(G_GNUC_UNUSED GtkWidget * widget,GdkEventButton * event,QueryFavoriteSelector * tsel)463 do_popup_menu (G_GNUC_UNUSED GtkWidget *widget, GdkEventButton *event, QueryFavoriteSelector *tsel)
464 {
465 	int button, event_time;
466 
467 	if (! tsel->priv->popup_menu) {
468 		GtkWidget *menu, *mitem;
469 
470 		menu = gtk_menu_new ();
471 		g_signal_connect (menu, "deactivate",
472 				  G_CALLBACK (gtk_widget_hide), NULL);
473 
474 		mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_PROPERTIES, NULL);
475 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
476 		gtk_widget_show (mitem);
477 		g_signal_connect (mitem, "activate",
478 				  G_CALLBACK (properties_activated_cb), tsel);
479 
480 		mitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_DELETE, NULL);
481 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), mitem);
482 		gtk_widget_show (mitem);
483 		g_signal_connect (mitem, "activate",
484 				  G_CALLBACK (delete_activated_cb), tsel);
485 
486 		tsel->priv->popup_menu = menu;
487 	}
488 
489 	if (event) {
490 		button = event->button;
491 		event_time = event->time;
492 	}
493 	else {
494 		button = 0;
495 		event_time = gtk_get_current_event_time ();
496 	}
497 
498 	gtk_menu_popup (GTK_MENU (tsel->priv->popup_menu), NULL, NULL, NULL, NULL,
499 			button, event_time);
500 }
501 
502 
503 static gboolean
popup_menu_cb(GtkWidget * widget,QueryFavoriteSelector * tsel)504 popup_menu_cb (GtkWidget *widget, QueryFavoriteSelector *tsel)
505 {
506 	do_popup_menu (widget, NULL, tsel);
507 	return TRUE;
508 }
509 
510 static gboolean
button_press_event_cb(GtkTreeView * treeview,GdkEventButton * event,QueryFavoriteSelector * tsel)511 button_press_event_cb (GtkTreeView *treeview, GdkEventButton *event, QueryFavoriteSelector *tsel)
512 {
513 	if (event->button == 3 && event->type == GDK_BUTTON_PRESS) {
514 		do_popup_menu ((GtkWidget*) treeview, event, tsel);
515 		return TRUE;
516 	}
517 
518 	return FALSE;
519 }
520 
521 static void cell_data_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
522 			    GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data);
523 static gboolean idle_update_favorites (QueryFavoriteSelector *tsel);
524 static gboolean tree_store_drag_drop_cb (GdauiTreeStore *store, const gchar *path,
525 					 GtkSelectionData *selection_data, QueryFavoriteSelector *tsel);
526 static gboolean tree_store_drag_can_drag_cb (GdauiTreeStore *store, const gchar *path,
527 					     QueryFavoriteSelector *tsel);
528 static gboolean tree_store_drag_get_cb (GdauiTreeStore *store, const gchar *path,
529 					GtkSelectionData *selection_data, QueryFavoriteSelector *tsel);
530 /**
531  * query_favorite_selector_new
532  *
533  * Returns: a new #GtkWidget
534  */
535 GtkWidget *
query_favorite_selector_new(BrowserConnection * bcnc)536 query_favorite_selector_new (BrowserConnection *bcnc)
537 {
538 	QueryFavoriteSelector *tsel;
539 	GdaTreeManager *manager;
540 
541 	g_return_val_if_fail (BROWSER_IS_CONNECTION (bcnc), NULL);
542 	tsel = QUERY_FAVORITE_SELECTOR (g_object_new (QUERY_FAVORITE_SELECTOR_TYPE, NULL));
543 
544 	tsel->priv->bcnc = g_object_ref (bcnc);
545 	g_signal_connect (browser_connection_get_favorites (tsel->priv->bcnc), "favorites-changed",
546 			  G_CALLBACK (favorites_changed_cb), tsel);
547 
548 	/* create tree managers */
549 	tsel->priv->tree = gda_tree_new ();
550 	manager = mgr_favorites_new (bcnc, GDA_TOOLS_FAVORITES_QUERIES, ORDER_KEY_QUERIES);
551         gda_tree_add_manager (tsel->priv->tree, manager);
552 	g_object_unref (manager);
553 
554 	/* update the tree's contents */
555 	if (! gda_tree_update_all (tsel->priv->tree, NULL)) {
556 		if (tsel->priv->idle_update_favorites == 0)
557 			tsel->priv->idle_update_favorites = g_idle_add ((GSourceFunc) idle_update_favorites, tsel);
558 	}
559 
560 	/* header */
561 	GtkWidget *label;
562 	gchar *str;
563 	str = g_strdup_printf ("<b>%s</b>", _("Favorites"));
564 	label = gdaui_bar_new (str);
565 	g_free (str);
566 	gdaui_bar_set_icon_from_pixbuf (GDAUI_BAR (label), browser_get_pixbuf_icon (BROWSER_ICON_BOOKMARK));
567         gtk_box_pack_start (GTK_BOX (tsel), label, FALSE, FALSE, 0);
568         gtk_widget_show (label);
569 
570 	/* tree model */
571 	GtkTreeModel *model;
572 	GtkWidget *treeview;
573 	GtkCellRenderer *renderer;
574 	GtkTreeViewColumn *column;
575 
576 	model = gdaui_tree_store_new (tsel->priv->tree, 7,
577 				      G_TYPE_INT, MGR_FAVORITES_POSITION_ATT_NAME,
578 				      G_TYPE_OBJECT, "icon",
579 				      G_TYPE_STRING, MGR_FAVORITES_CONTENTS_ATT_NAME,
580 				      G_TYPE_UINT, MGR_FAVORITES_TYPE_ATT_NAME,
581 				      G_TYPE_INT, MGR_FAVORITES_ID_ATT_NAME,
582 				      G_TYPE_STRING, MGR_FAVORITES_NAME_ATT_NAME,
583 				      G_TYPE_STRING, "summary");
584 	treeview = browser_make_tree_view (model);
585 	tsel->priv->treeview = treeview;
586 	g_object_unref (model);
587 
588 	/* icon */
589 	column = gtk_tree_view_column_new ();
590 
591 	renderer = gtk_cell_renderer_pixbuf_new ();
592 	gtk_tree_view_column_pack_start (column, renderer, FALSE);
593 	gtk_tree_view_column_add_attribute (column, renderer, "pixbuf", COLUMN_ICON);
594 	g_object_set ((GObject*) renderer, "yalign", 0., NULL);
595 
596 	/* text */
597 	renderer = gtk_cell_renderer_text_new ();
598 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
599 	gtk_tree_view_column_set_cell_data_func (column, renderer, (GtkTreeCellDataFunc) cell_data_func,
600 						 NULL, NULL);
601 	gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
602 	gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
603 
604 	/* scrolled window packing */
605 	GtkWidget *sw;
606 	sw = gtk_scrolled_window_new (NULL, NULL);
607 	gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
608 					     GTK_SHADOW_ETCHED_IN);
609 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
610 					GTK_POLICY_NEVER,
611 					GTK_POLICY_AUTOMATIC);
612 	gtk_container_add (GTK_CONTAINER (sw), treeview);
613 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
614 
615 	gtk_box_pack_start (GTK_BOX (tsel), sw, TRUE, TRUE, 0);
616 	gtk_widget_show_all (sw);
617 	g_signal_connect (G_OBJECT (treeview), "row-activated",
618 			  G_CALLBACK (selection_changed_cb), tsel);
619 	g_signal_connect (G_OBJECT (treeview), "key-press-event",
620 			  G_CALLBACK (key_press_event_cb), tsel);
621 	g_signal_connect (G_OBJECT (treeview), "popup-menu",
622 			  G_CALLBACK (popup_menu_cb), tsel);
623 	g_signal_connect (G_OBJECT (treeview), "button-press-event",
624 			  G_CALLBACK (button_press_event_cb), tsel);
625 
626 	/* DnD */
627 	gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (treeview), dbo_table, G_N_ELEMENTS (dbo_table),
628 					      GDK_ACTION_COPY);
629 	gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (treeview), GDK_BUTTON1_MASK,
630 						dbo_table, G_N_ELEMENTS (dbo_table),
631 						GDK_ACTION_COPY | GDK_ACTION_MOVE);
632 	g_signal_connect (model, "drag-drop",
633 			  G_CALLBACK (tree_store_drag_drop_cb), tsel);
634 	g_signal_connect (model, "drag-can-drag",
635 			  G_CALLBACK (tree_store_drag_can_drag_cb), tsel);
636 	g_signal_connect (model, "drag-get",
637 			  G_CALLBACK (tree_store_drag_get_cb), tsel);
638 
639 	return (GtkWidget*) tsel;
640 }
641 
642 static void
cell_data_func(G_GNUC_UNUSED GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,G_GNUC_UNUSED gpointer data)643 cell_data_func (G_GNUC_UNUSED GtkTreeViewColumn *tree_column, GtkCellRenderer *cell,
644 		GtkTreeModel *tree_model, GtkTreeIter *iter, G_GNUC_UNUSED gpointer data)
645 {
646 	gchar *name, *summary;
647 	gchar *markup, *tmp1, *tmp2;
648 
649 	gtk_tree_model_get (tree_model, iter,
650 			    COLUMN_NAME, &name, COLUMN_SUMMARY, &summary, -1);
651 	tmp1 = g_markup_printf_escaped ("%s", name);
652 	tmp2 = g_markup_printf_escaped ("%s", summary);
653 	g_free (name);
654 	g_free (summary);
655 
656 	markup = g_strdup_printf ("%s\n<small>%s</small>", tmp1, tmp2);
657 	g_free (tmp1);
658 	g_free (tmp2);
659 
660 	g_object_set ((GObject*) cell, "markup", markup, NULL);
661 	g_free (markup);
662 }
663 
664 
665 static gboolean
idle_update_favorites(QueryFavoriteSelector * tsel)666 idle_update_favorites (QueryFavoriteSelector *tsel)
667 {
668 	gboolean done;
669 	done = gda_tree_update_all (tsel->priv->tree, NULL);
670 	if (done)
671 		tsel->priv->idle_update_favorites = 0;
672 	else
673 		tsel->priv->idle_update_favorites = g_timeout_add_seconds (1, (GSourceFunc) idle_update_favorites,
674 									   tsel);
675 	return FALSE;
676 }
677 
678 static gboolean
tree_store_drag_drop_cb(G_GNUC_UNUSED GdauiTreeStore * store,const gchar * path,GtkSelectionData * selection_data,QueryFavoriteSelector * tsel)679 tree_store_drag_drop_cb (G_GNUC_UNUSED GdauiTreeStore *store, const gchar *path,
680 			 GtkSelectionData *selection_data, QueryFavoriteSelector *tsel)
681 {
682 	ToolsFavorites *bfav;
683 	ToolsFavoritesAttributes fav;
684 	GError *error = NULL;
685 	gint pos;
686 	gboolean retval = TRUE;
687 	gint id;
688 	bfav = browser_connection_get_favorites (tsel->priv->bcnc);
689 
690 	id = gda_tools_favorites_find (bfav, 0, (gchar*) gtk_selection_data_get_data (selection_data),
691 				     &fav, NULL);
692 	if (id < 0) {
693 		memset (&fav, 0, sizeof (ToolsFavoritesAttributes));
694 		fav.id = -1;
695 		fav.type = GDA_TOOLS_FAVORITES_QUERIES;
696 		fav.name = _("Unnamed query");
697 		fav.descr = NULL;
698 		fav.contents = (gchar*) gtk_selection_data_get_data (selection_data);
699 	}
700 
701 	pos = atoi (path);
702 	/*g_print ("%s() path => %s, pos: %d\n", __FUNCTION__, path, pos);*/
703 
704 	if (! gda_tools_favorites_add (bfav, 0, &fav, ORDER_KEY_QUERIES, pos, &error)) {
705 		browser_show_error ((GtkWindow*) gtk_widget_get_toplevel ((GtkWidget*) tsel),
706 				    _("Could not add favorite: %s"),
707 				    error && error->message ? error->message : _("No detail"));
708 		if (error)
709 			g_error_free (error);
710 		retval = FALSE;
711 	}
712 
713 	if (id >= 0)
714 		gda_tools_favorites_reset_attributes (&fav);
715 
716 	return retval;
717 }
718 
719 static gboolean
tree_store_drag_can_drag_cb(G_GNUC_UNUSED GdauiTreeStore * store,const gchar * path,QueryFavoriteSelector * tsel)720 tree_store_drag_can_drag_cb (G_GNUC_UNUSED GdauiTreeStore *store, const gchar *path,
721 			     QueryFavoriteSelector *tsel)
722 {
723 	GdaTreeNode *node;
724 	node = gda_tree_get_node (tsel->priv->tree, path, FALSE);
725 	if (node) {
726 		const GValue *cvalue;
727 		cvalue = gda_tree_node_get_node_attribute (node, "fav_contents");
728 		if (cvalue)
729 			return TRUE;
730 	}
731 	return FALSE;
732 }
733 
734 static gboolean
tree_store_drag_get_cb(G_GNUC_UNUSED GdauiTreeStore * store,const gchar * path,GtkSelectionData * selection_data,QueryFavoriteSelector * tsel)735 tree_store_drag_get_cb (G_GNUC_UNUSED GdauiTreeStore *store, const gchar *path,
736 			GtkSelectionData *selection_data,
737 			QueryFavoriteSelector *tsel)
738 {
739 	GdaTreeNode *node;
740 	node = gda_tree_get_node (tsel->priv->tree, path, FALSE);
741 	if (node) {
742 		const GValue *cvalue;
743 		cvalue = gda_tree_node_get_node_attribute (node, "fav_contents");
744 		if (cvalue) {
745 			const gchar *str;
746 			str = g_value_get_string (cvalue);
747 			gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data),
748 						8, (guchar*) str, strlen (str));
749 			return TRUE;
750 		}
751 	}
752 	return FALSE;
753 }
754 
755 static void
favorites_changed_cb(G_GNUC_UNUSED ToolsFavorites * bfav,QueryFavoriteSelector * tsel)756 favorites_changed_cb (G_GNUC_UNUSED ToolsFavorites *bfav, QueryFavoriteSelector *tsel)
757 {
758 	if (! gda_tree_update_all (tsel->priv->tree, NULL)) {
759 		if (tsel->priv->idle_update_favorites == 0)
760 			tsel->priv->idle_update_favorites = g_idle_add ((GSourceFunc) idle_update_favorites, tsel);
761 
762 	}
763 }
764