1 /*
2 * Copyright (C) 2009 - 2011 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 "objects-cloud.h"
26 #include <libgda-ui/gdaui-tree-store.h>
27 #include "../dnd.h"
28 #include "../support.h"
29 #include "../gdaui-bar.h"
30 #include "marshal.h"
31 #include <gdk/gdkkeysyms.h>
32 #include <libgda-ui/internal/popup-container.h>
33
34 struct _ObjectsCloudPrivate {
35 gboolean show_schemas;
36 ObjectsCloudObjType type;
37 GdaMetaStruct *mstruct;
38 GtkTextBuffer *tbuffer;
39 GtkWidget *tview;
40
41 gboolean hovering_over_link;
42 };
43
44 static void objects_cloud_class_init (ObjectsCloudClass *klass);
45 static void objects_cloud_init (ObjectsCloud *cloud,
46 ObjectsCloudClass *klass);
47 static void objects_cloud_dispose (GObject *object);
48
49 enum {
50 SELECTED,
51 LAST_SIGNAL
52 };
53
54 static guint objects_cloud_signals[LAST_SIGNAL] = { 0 };
55 static GObjectClass *parent_class = NULL;
56
57
58 /*
59 * ObjectsCloud class implementation
60 */
61
62 static void
objects_cloud_class_init(ObjectsCloudClass * klass)63 objects_cloud_class_init (ObjectsCloudClass *klass)
64 {
65 GObjectClass *object_class = G_OBJECT_CLASS (klass);
66
67 parent_class = g_type_class_peek_parent (klass);
68
69 /* signals */
70 objects_cloud_signals [SELECTED] =
71 g_signal_new ("selected",
72 G_TYPE_FROM_CLASS (object_class),
73 G_SIGNAL_RUN_FIRST,
74 G_STRUCT_OFFSET (ObjectsCloudClass, selected),
75 NULL, NULL,
76 _common_marshal_VOID__ENUM_STRING, G_TYPE_NONE,
77 2, G_TYPE_UINT, G_TYPE_STRING);
78 klass->selected = NULL;
79
80 object_class->dispose = objects_cloud_dispose;
81 }
82
83
84 static void
objects_cloud_init(ObjectsCloud * cloud,G_GNUC_UNUSED ObjectsCloudClass * klass)85 objects_cloud_init (ObjectsCloud *cloud, G_GNUC_UNUSED ObjectsCloudClass *klass)
86 {
87 cloud->priv = g_new0 (ObjectsCloudPrivate, 1);
88 cloud->priv->show_schemas = FALSE;
89 cloud->priv->tbuffer = gtk_text_buffer_new (NULL);
90
91 gtk_orientable_set_orientation (GTK_ORIENTABLE (cloud), GTK_ORIENTATION_VERTICAL);
92
93 gtk_text_buffer_create_tag (cloud->priv->tbuffer, "section",
94 "weight", PANGO_WEIGHT_BOLD,
95 "foreground", "blue", NULL);
96 }
97
98 static void
objects_cloud_dispose(GObject * object)99 objects_cloud_dispose (GObject *object)
100 {
101 ObjectsCloud *cloud = (ObjectsCloud *) object;
102
103 /* free memory */
104 if (cloud->priv) {
105 if (cloud->priv->mstruct)
106 g_object_unref (cloud->priv->mstruct);
107 g_object_unref (cloud->priv->tbuffer);
108 g_free (cloud->priv);
109 cloud->priv = NULL;
110 }
111
112 parent_class->dispose (object);
113 }
114
115 GType
objects_cloud_get_type(void)116 objects_cloud_get_type (void)
117 {
118 static GType type = 0;
119
120 if (G_UNLIKELY (type == 0)) {
121 static const GTypeInfo info = {
122 sizeof (ObjectsCloudClass),
123 (GBaseInitFunc) NULL,
124 (GBaseFinalizeFunc) NULL,
125 (GClassInitFunc) objects_cloud_class_init,
126 NULL,
127 NULL,
128 sizeof (ObjectsCloud),
129 0,
130 (GInstanceInitFunc) objects_cloud_init,
131 0
132 };
133 type = g_type_register_static (GTK_TYPE_BOX, "ObjectsCloud",
134 &info, 0);
135 }
136 return type;
137 }
138
139 static gint
dbo_sort(GdaMetaDbObject * dbo1,GdaMetaDbObject * dbo2)140 dbo_sort (GdaMetaDbObject *dbo1, GdaMetaDbObject *dbo2)
141 {
142 gint res;
143 res = g_strcmp0 (dbo1->obj_schema, dbo2->obj_schema);
144 if (res)
145 return - res;
146 return - g_strcmp0 (dbo1->obj_name, dbo2->obj_name);
147 }
148
149 typedef struct {
150 gchar *schema;
151 GtkTextMark *mark;
152 gint nb_tables;
153 } SchemaData;
154 static void add_to_schema_data (ObjectsCloud *cloud, SchemaData *sd, GdaMetaDbObject *dbo);
155 static void
update_display(ObjectsCloud * cloud)156 update_display (ObjectsCloud *cloud)
157 {
158 GSList *schemas = NULL; /* holds pointers to @SchemaData structures */
159 SchemaData *default_sd = NULL, *sd;
160
161 GtkTextBuffer *tbuffer;
162 GtkTextIter start, end;
163
164 /* clean all */
165 tbuffer = cloud->priv->tbuffer;
166 gtk_text_buffer_get_start_iter (tbuffer, &start);
167 gtk_text_buffer_get_end_iter (tbuffer, &end);
168 gtk_text_buffer_delete (tbuffer, &start, &end);
169
170 /* default SchemaData */
171 default_sd = g_new0 (SchemaData, 1);
172 default_sd->schema = NULL;
173 default_sd->mark = gtk_text_mark_new (NULL, TRUE);
174 default_sd->nb_tables = 0;
175 gtk_text_buffer_get_end_iter (tbuffer, &end);
176
177 gtk_text_buffer_get_end_iter (tbuffer, &end);
178 if (cloud->priv->show_schemas) {
179 gtk_text_buffer_insert_pixbuf (tbuffer, &end,
180 browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
181 gtk_text_buffer_insert (tbuffer, &end, " ", 1);
182 }
183 gtk_text_buffer_add_mark (tbuffer, default_sd->mark, &end);
184
185 /* using meta struct */
186 GdaMetaStruct *mstruct;
187 GSList *dbo_list, *list;
188 mstruct = cloud->priv->mstruct;
189 if (!mstruct) {
190 /* nothing to display */
191 goto out;
192 }
193 dbo_list = g_slist_reverse (gda_meta_struct_get_all_db_objects (mstruct));
194 list = g_slist_sort (dbo_list, (GCompareFunc) dbo_sort);
195 dbo_list = list;
196 for (list = dbo_list; list; list = list->next) {
197 GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
198 GSList *list;
199 gboolean is_default;
200
201 if (dbo->obj_type != GDA_META_DB_TABLE)
202 continue;
203 g_assert (dbo->obj_schema);
204
205 is_default = strcmp (dbo->obj_short_name, dbo->obj_full_name) ? TRUE : FALSE;
206 sd = NULL;
207 if (is_default) {
208 add_to_schema_data (cloud, default_sd, dbo);
209 }
210 else if (cloud->priv->show_schemas) {
211 for (list = schemas; list; list = list->next) {
212 if (!strcmp (((SchemaData *) list->data)->schema, dbo->obj_schema)) {
213 sd = (SchemaData *) list->data;
214 break;
215 }
216 }
217 if (!sd) {
218 sd = g_new0 (SchemaData, 1);
219 sd->schema = g_strdup (dbo->obj_schema);
220 sd->mark = gtk_text_mark_new (NULL, TRUE);
221 sd->nb_tables = 0;
222
223 gtk_text_buffer_get_end_iter (tbuffer, &end);
224 gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
225 gtk_text_buffer_insert_pixbuf (tbuffer, &end,
226 browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
227 gtk_text_buffer_insert (tbuffer, &end, " ", 1);
228 gtk_text_buffer_add_mark (tbuffer, sd->mark, &end);
229
230 schemas = g_slist_append (schemas, sd);
231 }
232
233 add_to_schema_data (cloud, sd, dbo);
234 }
235 }
236 g_slist_free (dbo_list);
237
238 out:
239 /* default_sd can't be NULL here */
240 schemas = g_slist_prepend (schemas, default_sd);
241
242 /* get rid of the SchemaData structures */
243 for (list = schemas; list; list = list->next) {
244 GtkTextIter iter;
245 gchar *str;
246 sd = (SchemaData*) list->data;
247
248 gtk_text_buffer_get_iter_at_mark (tbuffer, &iter, sd->mark);
249 if (sd == default_sd) {
250 if (sd->nb_tables > 0)
251 str = g_strdup_printf (ngettext ("%d table in current schema:",
252 "%d tables in current schema:", sd->nb_tables),
253 sd->nb_tables);
254 else
255 str = g_strdup (_("Tables in current schema:"));
256 }
257 else
258 str = g_strdup_printf (ngettext ("%d Table in schema '%s':",
259 "%d Tables in schema '%s':", sd->nb_tables),
260 sd->nb_tables, sd->schema);
261
262 gtk_text_buffer_insert_with_tags_by_name (tbuffer, &iter,
263 str, -1, "section", NULL);
264 g_free (str);
265 gtk_text_buffer_insert (tbuffer, &iter, "\n\n", 2);
266
267 if ((sd == default_sd) && (default_sd->nb_tables == 0))
268 gtk_text_buffer_insert (tbuffer, &iter, _("None"), -1);
269
270 g_free (sd->schema);
271 g_object_unref (sd->mark);
272 g_free (sd);
273 }
274 }
275
276 static void
add_to_schema_data(ObjectsCloud * cloud,SchemaData * sd,GdaMetaDbObject * dbo)277 add_to_schema_data (ObjectsCloud *cloud, SchemaData *sd, GdaMetaDbObject *dbo)
278 {
279 GtkTextTag *tag;
280 GtkTextIter iter;
281 gdouble scale = 1.0;
282
283 sd->nb_tables ++;
284
285 if (dbo->obj_type == GDA_META_DB_TABLE)
286 scale = 1.5 + g_slist_length (dbo->depend_list) / 5.;
287
288 gtk_text_buffer_get_iter_at_mark (cloud->priv->tbuffer, &iter, sd->mark);
289 tag = gtk_text_buffer_create_tag (cloud->priv->tbuffer, NULL,
290 "foreground", "#6161F2",
291 //"underline", PANGO_UNDERLINE_SINGLE,
292 "scale", scale,
293 NULL);
294 g_object_set_data_full (G_OBJECT (tag), "dbo_obj_schema", g_strdup (dbo->obj_schema), g_free);
295 g_object_set_data_full (G_OBJECT (tag), "dbo_obj_name", g_strdup (dbo->obj_name), g_free);
296 g_object_set_data_full (G_OBJECT (tag), "dbo_obj_short_name", g_strdup (dbo->obj_short_name), g_free);
297
298 gtk_text_buffer_insert_with_tags (cloud->priv->tbuffer, &iter, dbo->obj_name, -1,
299 tag, NULL);
300 gtk_text_buffer_insert (cloud->priv->tbuffer, &iter, " ", -1);
301 }
302
303 static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, ObjectsCloud *cloud);
304 static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, ObjectsCloud *cloud);
305 static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ObjectsCloud *cloud);
306 static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ObjectsCloud *cloud);
307
308 static void
text_tag_table_foreach_cb(GtkTextTag * tag,const gchar * find)309 text_tag_table_foreach_cb (GtkTextTag *tag, const gchar *find)
310 {
311 const gchar *name;
312
313 name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
314
315 if (!name)
316 return;
317
318 if (!*find) {
319 g_object_set (tag, "foreground", "#6161F2", NULL);
320 }
321 else {
322 gchar *ptr;
323 gchar *lcname, *lcfind;
324 lcname = g_utf8_strdown (name, -1);
325 lcfind = g_utf8_strdown (find, -1);
326
327 ptr = strstr (lcname, lcfind);
328 if (!ptr) {
329 /* string not present in name */
330 g_object_set (tag, "foreground", "#DBDBDB", NULL);
331 }
332 else if ((ptr == lcname) ||
333 ((*name == '"') && (ptr == lcname+1))) {
334 /* string present as start of name */
335 g_object_set (tag, "foreground", "#6161F2", NULL);
336 }
337 else {
338 /* string present in name but not at the start */
339 g_object_set (tag, "foreground", "#A0A0A0", NULL);
340 }
341
342 g_free (lcname);
343 g_free (lcfind);
344 }
345 }
346
347 void
objects_cloud_filter(ObjectsCloud * cloud,const gchar * filter)348 objects_cloud_filter (ObjectsCloud *cloud, const gchar *filter)
349 {
350 g_return_if_fail (IS_OBJECTS_CLOUD (cloud));
351
352 GtkTextTagTable *tags_table = gtk_text_buffer_get_tag_table (cloud->priv->tbuffer);
353
354 gtk_text_tag_table_foreach (tags_table, (GtkTextTagTableForeach) text_tag_table_foreach_cb,
355 (gpointer) filter);
356 }
357
358 /**
359 * objects_cloud_new
360 *
361 *
362 *
363 * Returns:
364 */
365 GtkWidget *
objects_cloud_new(GdaMetaStruct * mstruct,ObjectsCloudObjType type)366 objects_cloud_new (GdaMetaStruct *mstruct, ObjectsCloudObjType type)
367 {
368 ObjectsCloud *cloud;
369
370 g_return_val_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct), NULL);
371 cloud = OBJECTS_CLOUD (g_object_new (OBJECTS_CLOUD_TYPE, NULL));
372
373 if (mstruct)
374 cloud->priv->mstruct = g_object_ref (mstruct);
375 cloud->priv->type = type;
376
377 /* text contents */
378 GtkWidget *sw, *vbox;
379 sw = gtk_scrolled_window_new (NULL, NULL);
380 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
381 GTK_POLICY_AUTOMATIC);
382 gtk_box_pack_start (GTK_BOX (cloud), sw, TRUE, TRUE, 0);
383
384 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
385 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
386
387 cloud->priv->tview = gtk_text_view_new_with_buffer (cloud->priv->tbuffer);
388 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (cloud->priv->tview), GTK_WRAP_WORD);
389 gtk_text_view_set_editable (GTK_TEXT_VIEW (cloud->priv->tview), FALSE);
390 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (cloud->priv->tview), FALSE);
391 gtk_box_pack_start (GTK_BOX (vbox), cloud->priv->tview, TRUE, TRUE, 0);
392 gtk_widget_show_all (sw);
393
394 g_signal_connect (cloud->priv->tview, "key-press-event",
395 G_CALLBACK (key_press_event), cloud);
396 g_signal_connect (cloud->priv->tview, "event-after",
397 G_CALLBACK (event_after), cloud);
398 g_signal_connect (cloud->priv->tview, "motion-notify-event",
399 G_CALLBACK (motion_notify_event), cloud);
400 g_signal_connect (cloud->priv->tview, "visibility-notify-event",
401 G_CALLBACK (visibility_notify_event), cloud);
402
403 /* initial update */
404 update_display (cloud);
405
406 return (GtkWidget*) cloud;
407 }
408
409 /**
410 * objects_cloud_set_meta_struct
411 *
412 */
413 void
objects_cloud_set_meta_struct(ObjectsCloud * cloud,GdaMetaStruct * mstruct)414 objects_cloud_set_meta_struct (ObjectsCloud *cloud, GdaMetaStruct *mstruct)
415 {
416 g_return_if_fail (IS_OBJECTS_CLOUD (cloud));
417 g_return_if_fail (!mstruct || GDA_IS_META_STRUCT (mstruct));
418
419 if (cloud->priv->mstruct) {
420 g_object_unref (cloud->priv->mstruct);
421 cloud->priv->mstruct = NULL;
422 }
423 if (mstruct)
424 cloud->priv->mstruct = g_object_ref (mstruct);
425 update_display (cloud);
426 }
427
428 /**
429 * objects_cloud_show_schemas
430 */
431 void
objects_cloud_show_schemas(ObjectsCloud * cloud,gboolean show_schemas)432 objects_cloud_show_schemas (ObjectsCloud *cloud, gboolean show_schemas)
433 {
434 g_return_if_fail (IS_OBJECTS_CLOUD (cloud));
435
436 cloud->priv->show_schemas = show_schemas;
437 update_display (cloud);
438 }
439
440 static void
find_entry_changed_cb(GtkWidget * entry,ObjectsCloud * cloud)441 find_entry_changed_cb (GtkWidget *entry, ObjectsCloud *cloud)
442 {
443 gchar *find = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
444 objects_cloud_filter (cloud, find);
445 g_free (find);
446 }
447
448 /**
449 * objects_cloud_create_filter
450 */
451 GtkWidget *
objects_cloud_create_filter(ObjectsCloud * cloud)452 objects_cloud_create_filter (ObjectsCloud *cloud)
453 {
454 GtkWidget *hbox, *label, *wid;
455 g_return_val_if_fail (IS_OBJECTS_CLOUD (cloud), NULL);
456
457 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
458
459 label = gtk_label_new (_("Find:"));
460 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
461 wid = gtk_entry_new ();
462 g_signal_connect (wid, "changed",
463 G_CALLBACK (find_entry_changed_cb), cloud);
464 gtk_box_pack_start (GTK_BOX (hbox), wid, TRUE, TRUE, 0);
465 gtk_widget_show_all (hbox);
466 gtk_widget_hide (hbox);
467
468 return hbox;
469 }
470
471 static GdkCursor *hand_cursor = NULL;
472 static GdkCursor *regular_cursor = NULL;
473
474 /* Looks at all tags covering the position (x, y) in the text view,
475 * and if one of them is a link, change the cursor to the "hands" cursor
476 * typically used by web browsers.
477 */
478 static void
set_cursor_if_appropriate(GtkTextView * text_view,gint x,gint y,ObjectsCloud * cloud)479 set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ObjectsCloud *cloud)
480 {
481 GSList *tags = NULL, *tagp = NULL;
482 GtkTextIter iter;
483 gboolean hovering = FALSE;
484
485 gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
486
487 tags = gtk_text_iter_get_tags (&iter);
488 for (tagp = tags; tagp != NULL; tagp = tagp->next) {
489 GtkTextTag *tag = tagp->data;
490 gchar *table_name;
491
492 table_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
493 if (table_name) {
494 hovering = TRUE;
495 break;
496 }
497 }
498
499 if (hovering != cloud->priv->hovering_over_link) {
500 cloud->priv->hovering_over_link = hovering;
501
502 if (cloud->priv->hovering_over_link) {
503 if (! hand_cursor)
504 hand_cursor = gdk_cursor_new (GDK_HAND2);
505 gdk_window_set_cursor (gtk_text_view_get_window (text_view,
506 GTK_TEXT_WINDOW_TEXT),
507 hand_cursor);
508 }
509 else {
510 if (!regular_cursor)
511 regular_cursor = gdk_cursor_new (GDK_XTERM);
512 gdk_window_set_cursor (gtk_text_view_get_window (text_view,
513 GTK_TEXT_WINDOW_TEXT),
514 regular_cursor);
515 }
516 }
517
518 if (tags)
519 g_slist_free (tags);
520 }
521
522 /*
523 * Also update the cursor image if the window becomes visible
524 * (e.g. when a window covering it got iconified).
525 */
526 static gboolean
visibility_notify_event(GtkWidget * text_view,G_GNUC_UNUSED GdkEventVisibility * event,ObjectsCloud * cloud)527 visibility_notify_event (GtkWidget *text_view, G_GNUC_UNUSED GdkEventVisibility *event, ObjectsCloud *cloud)
528 {
529 gint wx, wy, bx, by;
530 GdkDeviceManager *manager;
531 GdkDevice *pointer;
532 manager = gdk_display_get_device_manager (gtk_widget_get_display (text_view));
533 pointer = gdk_device_manager_get_client_pointer (manager);
534 gdk_window_get_device_position (gtk_widget_get_window (text_view), pointer, &wx, &wy, NULL);
535
536 gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
537 GTK_TEXT_WINDOW_WIDGET,
538 wx, wy, &bx, &by);
539
540 set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by, cloud);
541
542 return FALSE;
543 }
544
545 /*
546 * Update the cursor image if the pointer moved.
547 */
548 static gboolean
motion_notify_event(GtkWidget * text_view,GdkEventMotion * event,ObjectsCloud * cloud)549 motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ObjectsCloud *cloud)
550 {
551 gint x, y;
552
553 gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
554 GTK_TEXT_WINDOW_WIDGET,
555 event->x, event->y, &x, &y);
556
557 set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y, cloud);
558 return FALSE;
559 }
560
561
562 /* Looks at all tags covering the position of iter in the text view,
563 * and if one of them is a link, follow it by showing the page identified
564 * by the data attached to it.
565 */
566 static void
follow_if_link(G_GNUC_UNUSED GtkWidget * text_view,GtkTextIter * iter,ObjectsCloud * cloud)567 follow_if_link (G_GNUC_UNUSED GtkWidget *text_view, GtkTextIter *iter, ObjectsCloud *cloud)
568 {
569 GSList *tags = NULL, *tagp = NULL;
570
571 tags = gtk_text_iter_get_tags (iter);
572 for (tagp = tags; tagp; tagp = tagp->next) {
573 GtkTextTag *tag = tagp->data;
574 const gchar *schema;
575
576 schema = g_object_get_data (G_OBJECT (tag), "dbo_obj_schema");
577 if (schema) {
578 const gchar *objects_name, *short_name;
579
580 objects_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
581 short_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_short_name");
582
583 if (objects_name && short_name) {
584 gchar *str, *tmp1, *tmp2, *tmp3;
585 tmp1 = gda_rfc1738_encode (schema);
586 tmp2 = gda_rfc1738_encode (objects_name);
587 tmp3 = gda_rfc1738_encode (short_name);
588 str = g_strdup_printf ("OBJ_TYPE=table;OBJ_SCHEMA=%s;OBJ_NAME=%s;OBJ_SHORT_NAME=%s",
589 tmp1, tmp2, tmp3);
590 g_free (tmp1);
591 g_free (tmp2);
592 g_free (tmp3);
593 g_signal_emit (cloud, objects_cloud_signals [SELECTED], 0,
594 cloud->priv->type, str);
595 g_free (str);
596 }
597 }
598 }
599
600 if (tags)
601 g_slist_free (tags);
602 }
603
604 /*
605 * Links can also be activated by clicking.
606 */
607 static gboolean
event_after(GtkWidget * text_view,GdkEvent * ev,ObjectsCloud * cloud)608 event_after (GtkWidget *text_view, GdkEvent *ev, ObjectsCloud *cloud)
609 {
610 GtkTextIter start, end, iter;
611 GtkTextBuffer *buffer;
612 GdkEventButton *event;
613 gint x, y;
614
615 if (ev->type != GDK_BUTTON_RELEASE)
616 return FALSE;
617
618 event = (GdkEventButton *)ev;
619
620 if (event->button != 1)
621 return FALSE;
622
623 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
624
625 /* we shouldn't follow a link if the user has selected something */
626 gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
627 if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
628 return FALSE;
629
630 gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view),
631 GTK_TEXT_WINDOW_WIDGET,
632 event->x, event->y, &x, &y);
633
634 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
635
636 follow_if_link (text_view, &iter, cloud);
637
638 return FALSE;
639 }
640
641 /*
642 * Links can be activated by pressing Enter.
643 */
644 static gboolean
key_press_event(GtkWidget * text_view,GdkEventKey * event,ObjectsCloud * cloud)645 key_press_event (GtkWidget *text_view, GdkEventKey *event, ObjectsCloud *cloud)
646 {
647 GtkTextIter iter;
648 GtkTextBuffer *buffer;
649
650 switch (event->keyval) {
651 case GDK_KEY_Return:
652 case GDK_KEY_KP_Enter:
653 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
654 gtk_text_buffer_get_iter_at_mark (buffer, &iter,
655 gtk_text_buffer_get_insert (buffer));
656 follow_if_link (text_view, &iter, cloud);
657 break;
658
659 default:
660 break;
661 }
662 return FALSE;
663 }
664