[libgda] GdaBrowser: reworked tables index



commit d6451b7664656cd29a33a139751562f70827f683
Author: Vivien Malerba <malerba gnome-db org>
Date:   Sun Aug 16 15:16:57 2009 +0200

    GdaBrowser: reworked tables index
    
    * "cloud" view
    * search features

 tools/browser/Makefile.am                    |    4 +-
 tools/browser/popup-container.c              |  260 ++++++++++++
 tools/browser/popup-container.h              |   56 +++
 tools/browser/schema-browser/objects-index.c |  584 ++++++++++++++++++--------
 4 files changed, 722 insertions(+), 182 deletions(-)
---
diff --git a/tools/browser/Makefile.am b/tools/browser/Makefile.am
index d4ea7eb..71e26de 100644
--- a/tools/browser/Makefile.am
+++ b/tools/browser/Makefile.am
@@ -55,7 +55,9 @@ gda_browser_4_0_SOURCES=\
 	browser-connections-list.h \
 	main.c \
 	mgr-favorites.h \
-	mgr-favorites.c
+	mgr-favorites.c \
+	popup-container.h \
+	popup-container.c
 
 $(OBJECTS): marshal.c marshal.h
 
diff --git a/tools/browser/popup-container.c b/tools/browser/popup-container.c
new file mode 100644
index 0000000..50025e8
--- /dev/null
+++ b/tools/browser/popup-container.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2009 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include "popup-container.h"
+#include <gdk/gdkkeysyms.h>
+
+struct _PopupContainerPrivate {
+	PopupContainerPositionFunc position_func;
+};
+
+static void popup_container_class_init (PopupContainerClass *klass);
+static void popup_container_init       (PopupContainer *container,
+				       PopupContainerClass *klass);
+static void popup_container_dispose   (GObject *object);
+static void popup_container_show   (GtkWidget *widget);
+
+static GObjectClass *parent_class = NULL;
+
+/*
+ * PopupContainer class implementation
+ */
+
+static void
+popup_container_class_init (PopupContainerClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+	parent_class = g_type_class_peek_parent (klass);
+
+	object_class->dispose = popup_container_dispose;
+	widget_class->show = popup_container_show;
+}
+
+static gboolean
+delete_popup (GtkWidget *widget, PopupContainer *container)
+{
+        gtk_widget_hide (GTK_WIDGET (container));
+        gtk_grab_remove (GTK_WIDGET (container));
+        return TRUE;
+}
+
+static gboolean
+key_press_popup (GtkWidget *widget, GdkEventKey *event, PopupContainer *container)
+{
+        if (event->keyval != GDK_Escape)
+                return FALSE;
+
+        g_signal_stop_emission_by_name (widget, "key_press_event");
+        gtk_widget_hide (GTK_WIDGET (container));
+        gtk_grab_remove (GTK_WIDGET (container));
+        return TRUE;
+}
+
+static gboolean
+button_press_popup (GtkWidget *widget, GdkEventButton *event, PopupContainer *container)
+{
+        GtkWidget *child;
+
+        child = gtk_get_event_widget ((GdkEvent *) event);
+
+        /* We don't ask for button press events on the grab widget, so
+         *  if an event is reported directly to the grab widget, it must
+         *  be on a window outside the application (and thus we remove
+         *  the popup window). Otherwise, we check if the widget is a child
+         *  of the grab widget, and only remove the popup window if it
+         *  is not.
+         */
+        if (child != widget) {
+                while (child) {
+                        if (child == widget)
+                                return FALSE;
+                        child = child->parent;
+                }
+        }
+        gtk_widget_hide (GTK_WIDGET (container));
+	gtk_grab_remove (GTK_WIDGET (container));
+        return TRUE;
+}
+
+static void
+popup_container_init (PopupContainer *container, PopupContainerClass *klass)
+{
+	container->priv = g_new0 (PopupContainerPrivate, 1);
+	container->priv->position_func = NULL;
+
+	gtk_widget_set_events (GTK_WIDGET (container),
+			       gtk_widget_get_events (GTK_WIDGET (container)) | GDK_KEY_PRESS_MASK);
+	gtk_window_set_resizable (GTK_WINDOW (container), FALSE);
+	gtk_container_set_border_width (GTK_CONTAINER (container), 5);
+	g_signal_connect (G_OBJECT (container), "delete_event",
+			  G_CALLBACK (delete_popup), container);
+	g_signal_connect (G_OBJECT (container), "key_press_event",
+			  G_CALLBACK (key_press_popup), container);
+	g_signal_connect (G_OBJECT (container), "button_press_event",
+			  G_CALLBACK (button_press_popup), container);
+
+}
+
+/* FIXME:
+ *  - implement the show() virtual method with popup_grab_on_window()...
+ *  - implement the position_popup()
+ */
+
+static void
+popup_container_dispose (GObject *object)
+{
+	PopupContainer *container = (PopupContainer *) object;
+
+	/* free memory */
+	if (container->priv) {
+		g_free (container->priv);
+		container->priv = NULL;
+	}
+
+	parent_class->dispose (object);
+}
+
+static void
+default_position_func (PopupContainer *container, gint *out_x, gint *out_y)
+{
+	gdk_display_get_pointer (gdk_display_get_default (), NULL,
+				 out_x, out_y, NULL);
+}
+
+static gboolean
+popup_grab_on_window (GdkWindow *window, guint32 activate_time)
+{
+        if ((gdk_pointer_grab (window, TRUE,
+                               GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                               GDK_POINTER_MOTION_MASK,
+                               NULL, NULL, activate_time) == 0)) {
+                if (gdk_keyboard_grab (window, TRUE,
+                                       activate_time) == 0)
+                        return TRUE;
+                else {
+                        gdk_pointer_ungrab (activate_time);
+                        return FALSE;
+                }
+        }
+        return FALSE;
+}
+
+static void
+popup_container_show (GtkWidget *widget)
+{
+	PopupContainer *container = (PopupContainer *) widget;
+	gint x, y;
+
+	GTK_WIDGET_CLASS (parent_class)->show (widget);
+	if (container->priv->position_func)
+		container->priv->position_func (container, &x, &y);
+	else
+		default_position_func (container, &x, &y);
+	gtk_window_move (GTK_WINDOW (widget), x + 1, y + 1);
+	gtk_window_move (GTK_WINDOW (widget), x, y);
+
+	gtk_grab_add (widget);
+
+	GdkScreen *screen;
+        gint swidth, sheight;
+        gint root_x, root_y;
+        gint wwidth, wheight;
+        gboolean do_move = FALSE;
+        screen = gtk_window_get_screen (GTK_WINDOW (widget));
+        if (screen) {
+                swidth = gdk_screen_get_width (screen);
+                sheight = gdk_screen_get_height (screen);
+        }
+        else {
+                swidth = gdk_screen_width ();
+                sheight = gdk_screen_height ();
+        }
+        gtk_window_get_position (GTK_WINDOW (widget), &root_x, &root_y);
+        gtk_window_get_size (GTK_WINDOW (widget), &wwidth, &wheight);
+        if (root_x + wwidth > swidth) {
+                do_move = TRUE;
+                root_x = swidth - wwidth;
+        }
+        else if (root_x < 0) {
+                do_move = TRUE;
+                root_x = 0;
+        }
+	if (root_y + wheight > sheight) {
+                do_move = TRUE;
+                root_y = sheight - wheight;
+        }
+        else if (root_y < 0) {
+                do_move = TRUE;
+                root_y = 0;
+        }
+        if (do_move)
+                gtk_window_move (GTK_WINDOW (widget), root_x, root_y);
+
+	popup_grab_on_window (widget->window,
+                              gtk_get_current_event_time ());
+}
+
+GType
+popup_container_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		static const GTypeInfo info = {
+			sizeof (PopupContainerClass),
+			(GBaseInitFunc) NULL,
+			(GBaseFinalizeFunc) NULL,
+			(GClassInitFunc) popup_container_class_init,
+			NULL,
+			NULL,
+			sizeof (PopupContainer),
+			0,
+			(GInstanceInitFunc) popup_container_init
+		};
+		type = g_type_register_static (GTK_TYPE_WINDOW, "PopupContainer",
+					       &info, 0);
+	}
+	return type;
+}
+
+/**
+ * popup_container_new
+ *
+ *
+ *
+ * Returns:
+ */
+GtkWidget *
+popup_container_new (PopupContainerPositionFunc pos_func)
+{
+	PopupContainer *container;
+
+	container = POPUP_CONTAINER (g_object_new (POPUP_CONTAINER_TYPE, "type", GTK_WINDOW_POPUP,
+						   NULL));
+
+	container->priv->position_func = pos_func;
+	return (GtkWidget*) container;
+}
diff --git a/tools/browser/popup-container.h b/tools/browser/popup-container.h
new file mode 100644
index 0000000..ba917fc
--- /dev/null
+++ b/tools/browser/popup-container.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 The GNOME Foundation
+ *
+ * AUTHORS:
+ *      Vivien Malerba <malerba gnome-db org>
+ *
+ * This Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this Library; see the file COPYING.LIB.  If not,
+ * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __POPUP_CONTAINER_H__
+#define __POPUP_CONTAINER_H__
+
+#include <gtk/gtkwindow.h>
+
+G_BEGIN_DECLS
+
+#define POPUP_CONTAINER_TYPE            (popup_container_get_type())
+#define POPUP_CONTAINER(obj)            (G_TYPE_CHECK_INSTANCE_CAST (obj, POPUP_CONTAINER_TYPE, PopupContainer))
+#define POPUP_CONTAINER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST (klass, POPUP_CONTAINER_TYPE, PopupContainerClass))
+#define IS_POPUP_CONTAINER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE (obj, POPUP_CONTAINER_TYPE))
+#define IS_POPUP_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), POPUP_CONTAINER_TYPE))
+
+typedef struct _PopupContainer        PopupContainer;
+typedef struct _PopupContainerClass   PopupContainerClass;
+typedef struct _PopupContainerPrivate PopupContainerPrivate;
+
+typedef void (*PopupContainerPositionFunc) (PopupContainer *cont, gint *out_x, gint *out_y);
+
+struct _PopupContainer {
+	GtkWindow               parent;
+	PopupContainerPrivate  *priv;
+};
+
+struct _PopupContainerClass {
+	GtkWindowClass          parent_class;
+};
+
+GType                  popup_container_get_type (void) G_GNUC_CONST;
+GtkWidget             *popup_container_new      (PopupContainerPositionFunc pos_func);
+
+G_END_DECLS
+
+#endif
diff --git a/tools/browser/schema-browser/objects-index.c b/tools/browser/schema-browser/objects-index.c
index 0b9f342..05e6ca0 100644
--- a/tools/browser/schema-browser/objects-index.c
+++ b/tools/browser/schema-browser/objects-index.c
@@ -30,10 +30,16 @@
 #include "../support.h"
 #include "../cc-gray-bar.h"
 #include "marshal.h"
+#include <gdk/gdkkeysyms.h>
+#include "../popup-container.h"
 
 struct _ObjectsIndexPrivate {
 	BrowserConnection *bcnc;
-	GtkWidget         *main;
+	GtkTextBuffer     *tbuffer;
+	GtkWidget         *tview;
+	GtkWidget         *popup_container;
+
+	gboolean hovering_over_link;
 };
 
 static void objects_index_class_init (ObjectsIndexClass *klass);
@@ -82,6 +88,16 @@ static void
 objects_index_init (ObjectsIndex *index, ObjectsIndexClass *klass)
 {
 	index->priv = g_new0 (ObjectsIndexPrivate, 1);
+	index->priv->tbuffer = gtk_text_buffer_new (NULL);
+	gtk_text_buffer_create_tag (index->priv->tbuffer, "section",
+                                    "weight", PANGO_WEIGHT_BOLD,
+                                    "foreground", "blue", NULL);
+	gtk_text_buffer_create_tag (index->priv->tbuffer, "size0",
+                                    "scale", PANGO_SCALE_MEDIUM, NULL);
+	gtk_text_buffer_create_tag (index->priv->tbuffer, "size1",
+                                    "scale", PANGO_SCALE_LARGE, NULL);
+	gtk_text_buffer_create_tag (index->priv->tbuffer, "size2",
+                                    "scale", PANGO_SCALE_LARGE, NULL);
 }
 
 static void
@@ -91,11 +107,13 @@ objects_index_dispose (GObject *object)
 
 	/* free memory */
 	if (index->priv) {
+		gtk_widget_destroy (index->priv->popup_container);
 		if (index->priv->bcnc) {
 			g_signal_handlers_disconnect_by_func (index->priv->bcnc,
 							      G_CALLBACK (meta_changed_cb), index);
 			g_object_unref (index->priv->bcnc);
 		}
+		g_object_unref (index->priv->tbuffer);
 		g_free (index->priv);
 		index->priv = NULL;
 	}
@@ -128,25 +146,40 @@ objects_index_get_type (void)
 
 typedef struct {
 	gchar *schema;
-	GtkWidget *container;
-	GtkWidget *objects;
-	gint left;
-	gint top;
-	gint nrows;
+	GtkTextMark *mark;
 } SchemaData;
 static void add_to_schema_data (ObjectsIndex *index, SchemaData *sd, GdaMetaDbObject *dbo);
 static void
 update_display (ObjectsIndex *index)
 {
-#define NCOLS 4
 	gchar *str;
 	GSList *schemas = NULL; /* holds pointers to @SchemaData structures */
 	SchemaData *default_sd = NULL, *sd;
+	
+	GtkTextBuffer *tbuffer;
+        GtkTextIter start, end;
+
+        /* clean all */
+        tbuffer = index->priv->tbuffer;
+        gtk_text_buffer_get_start_iter (tbuffer, &start);
+        gtk_text_buffer_get_end_iter (tbuffer, &end);
+        gtk_text_buffer_delete (tbuffer, &start, &end);
+
+	default_sd = g_new0 (SchemaData, 1);
+	default_sd->schema = NULL;
+	default_sd->mark = gtk_text_mark_new (NULL, TRUE);
+	gtk_text_buffer_get_end_iter (tbuffer, &end);
+	
+	gtk_text_buffer_get_end_iter (tbuffer, &end);
+	gtk_text_buffer_insert_pixbuf (tbuffer, &end,
+				       browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
+	gtk_text_buffer_insert (tbuffer, &end, " ", 1);
+	gtk_text_buffer_insert_with_tags_by_name (tbuffer, &end,
+						  _("Default tables:"), -1, "section", NULL);
+	gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
+
+	gtk_text_buffer_add_mark (tbuffer, default_sd->mark, &end);
 
-	if (index->priv->main) {
-		gtk_widget_destroy (index->priv->main);
-		index->priv->main = NULL;
-	}
 
 	GdaMetaStruct *mstruct;
 	GSList *dbo_list, *list;
@@ -155,7 +188,7 @@ update_display (ObjectsIndex *index)
 		/* not yet ready */
 		return;
 	}
-	dbo_list = gda_meta_struct_get_all_db_objects (mstruct);
+	dbo_list = g_slist_reverse (gda_meta_struct_get_all_db_objects (mstruct));
 	for (list = dbo_list; list; list = list->next) {
 		GdaMetaDbObject *dbo = GDA_META_DB_OBJECT (list->data);
 		GSList *list;
@@ -173,56 +206,24 @@ update_display (ObjectsIndex *index)
 				break;
 			}
 		}
-		if (is_default && !default_sd) {
-			GtkWidget *label;
-
-			default_sd = g_new0 (SchemaData, 1);
-			default_sd->schema = NULL;
-			default_sd->objects = gtk_table_new (2, NCOLS, FALSE);
-			default_sd->container = default_sd->objects;
-			
-			/*
-			str = g_strdup_printf ("<b>%s:</b>", _("Tables"));
-			label = gtk_label_new ("");
-			gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
-			gtk_label_set_markup (GTK_LABEL (label), str);
-			g_free (str);
-			gtk_table_attach (GTK_TABLE (default_sd->objects), label, 0, NCOLS, 0, 1,
-					  GTK_EXPAND | GTK_FILL, 0, 0, 0);
-			gtk_table_set_row_spacing (GTK_TABLE (default_sd->objects), 0, 5);
-			*/
-
-			default_sd->left = 0;
-			default_sd->top = 1;
-			default_sd->nrows = 1;
-		}
 		if (!sd) {
-			GtkWidget *hbox, *label, *icon;
-
 			sd = g_new0 (SchemaData, 1);
 			sd->schema = g_strdup (dbo->obj_schema);
-			sd->left = 0;
-			sd->top = 0;
-			sd->nrows = 1;
-			
-			sd->objects = gtk_table_new (2, NCOLS, FALSE);
+			sd->mark = gtk_text_mark_new (NULL, TRUE);
 			
-			str = g_strdup_printf (_("Tables in schema <b>'%s'</b>"), sd->schema);
-			label = gtk_label_new ("");
-			gtk_misc_set_alignment (GTK_MISC (label), 0., -1);
-			gtk_label_set_markup (GTK_LABEL (label), str);
+			gtk_text_buffer_get_end_iter (tbuffer, &end);
+			gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
+			gtk_text_buffer_insert_pixbuf (tbuffer, &end,
+						       browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
+			gtk_text_buffer_insert (tbuffer, &end, " ", 1);
+			str = g_strdup_printf (_("Tables in schema '%s':"), sd->schema);
+			gtk_text_buffer_insert_with_tags_by_name (tbuffer, &end,
+								  str, -1, "section", NULL);
 			g_free (str);
+			gtk_text_buffer_insert (tbuffer, &end, "\n\n", 2);
 
-			icon = gtk_image_new_from_pixbuf (browser_get_pixbuf_icon (BROWSER_ICON_SCHEMA));
-			hbox = gtk_hbox_new (FALSE, 0);
-			gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
-			gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 5);
-			gtk_widget_show_all (hbox);
-
-			sd->container = gtk_expander_new ("");
-			gtk_expander_set_label_widget (GTK_EXPANDER (sd->container), hbox);
+			gtk_text_buffer_add_mark (tbuffer, sd->mark, &end);
 
-			gtk_container_add (GTK_CONTAINER (sd->container), sd->objects);
 			schemas = g_slist_append (schemas, sd);
 		}
 
@@ -235,30 +236,14 @@ update_display (ObjectsIndex *index)
 	if (default_sd)
 		schemas = g_slist_prepend (schemas, default_sd);
 
-	GtkWidget *sw, *vbox;
-	sw = gtk_scrolled_window_new (NULL, NULL);
-	index->priv->main = sw;
-	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
-					GTK_POLICY_AUTOMATIC);
-	gtk_box_pack_start (GTK_BOX (index), sw, TRUE, TRUE, 0);
-	
-	vbox = gtk_vbox_new (FALSE, 0);
-	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
+	/* get rid of the SchemaData structures */
 	for (list = schemas; list; list = list->next) {
 		sd = (SchemaData*) list->data;
 
-		if (sd->nrows == 1) {
-			/* add some padding */
-			gint i;
-			for (i = sd->left; i < NCOLS; i++)
-				add_to_schema_data (index, sd, NULL);
-		}
-
-		gtk_box_pack_start (GTK_BOX (vbox), sd->container, FALSE, FALSE, 5);
 		g_free (sd->schema);
+		g_object_unref (sd->mark);
 		g_free (sd);
 	}
-	gtk_widget_show_all (sw);
 }
 
 static gchar *
@@ -283,131 +268,120 @@ double_underscores (const gchar *str)
 	return out;
 }
 
-static void objects_button_clicked_cb (GtkWidget *button, ObjectsIndex *index);
-static void source_drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
-				     GtkSelectionData *selection_data,
-				     guint info, guint time, ObjectsIndex *index);
-
 static void
 add_to_schema_data (ObjectsIndex *index, SchemaData *sd, GdaMetaDbObject *dbo)
 {
-	GtkWidget *wid, *icon;
-	
-	wid = gtk_button_new ();
-	gtk_button_set_relief (GTK_BUTTON (wid), GTK_RELIEF_NONE);
-	gtk_button_set_focus_on_click (GTK_BUTTON (wid), FALSE);
-	if (dbo) {
-		icon = gtk_image_new_from_pixbuf (browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
-		gtk_button_set_alignment (GTK_BUTTON (wid), 0., -1);
-		gtk_button_set_image (GTK_BUTTON (wid), icon);
-
-		gchar *str;
-		str = double_underscores (dbo->obj_name);
-		gtk_button_set_label (GTK_BUTTON (wid), str);
-		g_free (str);
-
-		g_object_set_data_full (G_OBJECT (wid), "dbo_obj_schema", g_strdup (dbo->obj_schema), g_free);
-		g_object_set_data_full (G_OBJECT (wid), "dbo_obj_name", g_strdup (dbo->obj_name), g_free);
-		g_object_set_data_full (G_OBJECT (wid), "dbo_obj_short_name", g_strdup (dbo->obj_short_name), g_free);
-		g_signal_connect (wid, "clicked",
-				  G_CALLBACK (objects_button_clicked_cb), index);
-
-		/* setup DnD */
-		gtk_drag_source_set (wid, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
-				     dbo_table, G_N_ELEMENTS (dbo_table), GDK_ACTION_COPY);
-		gtk_drag_source_set_icon_pixbuf (wid, browser_get_pixbuf_icon (BROWSER_ICON_TABLE));
-		g_signal_connect (wid, "drag-data-get",
-				  G_CALLBACK (source_drag_data_get_cb), index);
-	}
-	else {
-		gtk_button_set_label (GTK_BUTTON (wid), "             ");
-		gtk_widget_set_sensitive (wid, FALSE);
-	}
+	GtkTextTag *tag;
+	GtkTextIter iter;
+	gdouble scale = 1.0;
 
-	gtk_button_set_use_underline (GTK_BUTTON (wid), FALSE);
-	if (! sd->schema && dbo) {
-		gchar *str;
-		str = g_strdup_printf (_("Objects in the '%s' schema"), dbo->obj_schema);
-		gtk_widget_set_tooltip_text (wid, str);
-		g_free (str);
-	}
-	gtk_table_attach (GTK_TABLE (sd->objects), wid, sd->left, sd->left + 1, sd->top, sd->top +1,
-			  GTK_EXPAND | GTK_FILL, 0, 5, 0);
-	sd->left ++;
-	if (sd->left >= NCOLS) {
-		sd->left = 0;
-		sd->top ++;
-		sd->nrows ++;
+	if (dbo->obj_type == GDA_META_DB_TABLE) {
+		scale = 1.0 + g_slist_length (dbo->depend_list) / 5.;
 	}
+
+	gtk_text_buffer_get_iter_at_mark (index->priv->tbuffer, &iter, sd->mark);
+	tag = gtk_text_buffer_create_tag (index->priv->tbuffer, NULL, 
+					  "foreground", "#6161F2", 
+					  //"underline", PANGO_UNDERLINE_SINGLE,
+					  "scale", scale,
+					  NULL);
+	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_schema", g_strdup (dbo->obj_schema), g_free);
+	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_name", g_strdup (dbo->obj_name), g_free);
+	g_object_set_data_full (G_OBJECT (tag), "dbo_obj_short_name", g_strdup (dbo->obj_short_name), g_free);
+
+	gtk_text_buffer_insert_with_tags (index->priv->tbuffer, &iter, dbo->obj_name, -1,
+					  tag, NULL);
+	gtk_text_buffer_insert (index->priv->tbuffer, &iter, " ", -1);
 }
 
+static void popup_position (PopupContainer *container, gint *out_x, gint *out_y);
+static gboolean key_press_event (GtkWidget *text_view, GdkEventKey *event, ObjectsIndex *index);
+static gboolean event_after (GtkWidget *text_view, GdkEvent *ev, ObjectsIndex *index);
+static gboolean motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ObjectsIndex *index);
+static gboolean visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ObjectsIndex *index);
+
 static void
-objects_button_clicked_cb (GtkWidget *button, ObjectsIndex *index)
+popup_position (PopupContainer *container, gint *out_x, gint *out_y)
 {
-	const gchar *schema;
-	const gchar *objects_name, *short_name;
-	gchar *str, *tmp1, *tmp2, *tmp3;
+	GtkWidget *button;
+	button = g_object_get_data (G_OBJECT (container), "button");
 
-	schema = g_object_get_data (G_OBJECT (button), "dbo_obj_schema");
-	objects_name = g_object_get_data (G_OBJECT (button), "dbo_obj_name");
-	short_name = g_object_get_data (G_OBJECT (button), "dbo_obj_short_name");
-	
-	tmp1 = gda_rfc1738_encode (schema);
-	tmp2 = gda_rfc1738_encode (objects_name);
-	tmp3 = gda_rfc1738_encode (short_name);
-	str = g_strdup_printf ("OBJ_TYPE=table;OBJ_SCHEMA=%s;OBJ_NAME=%s;OBJ_SHORT_NAME=%s", tmp1, tmp2, tmp3);
-	g_free (tmp1);
-	g_free (tmp2);
-	g_free (tmp3);
-	g_signal_emit (index, objects_index_signals [SELECTION_CHANGED], 0, BROWSER_FAVORITES_TABLES, str);
-	g_free (str);
+	gint x, y;
+        gint bwidth, bheight;
+        GtkRequisition req;
+
+        gtk_widget_size_request (button, &req);
+
+        gdk_window_get_origin (button->window, &x, &y);
+
+        x += button->allocation.x;
+        y += button->allocation.y;
+        bwidth = button->allocation.width;
+        bheight = button->allocation.height;
+
+        x += bwidth - req.width;
+        y += bheight;
+
+        if (x < 0)
+                x = 0;
+
+        if (y < 0)
+                y = 0;
+
+	*out_x = x;
+	*out_y = y;
 }
 
 static void
-source_drag_data_get_cb (GtkWidget *widget, GdkDragContext *context,
-			 GtkSelectionData *selection_data,
-			 guint info, guint time, ObjectsIndex *index)
+text_tag_table_foreach_cb (GtkTextTag *tag, const gchar *find)
 {
-	const gchar *schema;
-	const gchar *objects_name;
-	const gchar *objects_short_name;
-
-	schema = g_object_get_data (G_OBJECT (widget), "dbo_obj_schema");
-	objects_name = g_object_get_data (G_OBJECT (widget), "dbo_obj_name");
-	objects_short_name = g_object_get_data (G_OBJECT (widget), "dbo_obj_short_name");
-
-	switch (info) {
-	case TARGET_KEY_VALUE: {
-		GString *string;
-		gchar *tmp;
-		string = g_string_new ("OBJ_TYPE=table");
-		tmp = gda_rfc1738_encode (schema);
-		g_string_append_printf (string, ";OBJ_SCHEMA=%s", tmp);
-		g_free (tmp);
-		tmp = gda_rfc1738_encode (objects_name);
-		g_string_append_printf (string, ";OBJ_NAME=%s", tmp);
-		g_free (tmp);
-		tmp = gda_rfc1738_encode (objects_short_name);
-		g_string_append_printf (string, ";OBJ_SHORT_NAME=%s", tmp);
-		g_free (tmp);
-		gtk_selection_data_set (selection_data, selection_data->target, 8, string->str, strlen (string->str));
-		g_string_free (string, TRUE);
-		break;
-	}
-	default:
-	case TARGET_PLAIN: {
-		gchar *str;
-		str = g_strdup_printf ("%s.%s", schema, objects_name);
-		gtk_selection_data_set_text (selection_data, str, -1);
-		g_free (str);
-		break;
+	const gchar *name;
+
+	name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
+
+	if (!name)
+		return;
+	
+	if (!*find) {
+		g_object_set (tag, "foreground", "#6161F2", NULL);
 	}
-	case TARGET_ROOTWIN:
-		TO_IMPLEMENT; /* dropping on the Root Window => create a file */
-		break;
+	else {
+		gchar *ptr;
+		gchar *lcname, *lcfind;
+		lcname = g_utf8_strdown (name, -1);
+		lcfind = g_utf8_strdown (find, -1);
+
+		ptr = strstr (lcname, lcfind);
+		if (!ptr) {
+			/* string not present in name */
+			g_object_set (tag, "foreground", "#DBDBDB", NULL);
+		}
+		else if ((ptr == lcname) ||
+			 ((*name == '"') && (ptr == lcname+1))) {
+			/* string present as start of name */
+			g_object_set (tag, "foreground", "#6161F2", NULL);
+		}
+		else {
+			/* string present in name but not at the start */
+			g_object_set (tag, "foreground", "#A0A0A0", NULL);
+		}
+
+		g_free (lcname);
+		g_free (lcfind);
 	}
 }
 
+static void
+find_entry_changed_cb (GtkWidget *entry, ObjectsIndex *index)
+{
+	gchar *find = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
+	GtkTextTagTable *tags_table = gtk_text_buffer_get_tag_table (index->priv->tbuffer);
+
+	gtk_text_tag_table_foreach (tags_table, (GtkTextTagTableForeach) text_tag_table_foreach_cb,
+				    find);
+	g_free (find);
+}
+
 /**
  * objects_index_new
  *
@@ -428,15 +402,70 @@ objects_index_new (BrowserConnection *bcnc)
 			  G_CALLBACK (meta_changed_cb), index);
 
 	/* header */
+	GtkWidget *hbox, *wid;
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (index), hbox, FALSE, FALSE, 0);
+
 	GtkWidget *label;
 	gchar *str;
 
 	str = g_strdup_printf ("<b>%s</b>", _("Tables' index"));
 	label = cc_gray_bar_new (str);
 	g_free (str);
-        gtk_box_pack_start (GTK_BOX (index), label, FALSE, FALSE, 0);
+        gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
         gtk_widget_show (label);
 
+	wid = gtk_button_new ();
+	label = gtk_image_new_from_stock (GTK_STOCK_FIND, GTK_ICON_SIZE_BUTTON);
+	gtk_container_add (GTK_CONTAINER (wid), label);
+	gtk_box_pack_start (GTK_BOX (hbox), wid, FALSE, FALSE, 0);
+	g_object_set (G_OBJECT (wid), "label", NULL, NULL);
+	
+	GtkWidget *popup;
+	popup = popup_container_new (popup_position);
+	index->priv->popup_container = popup;
+	g_signal_connect_swapped (wid, "clicked",
+				  G_CALLBACK (gtk_widget_show), popup);
+	g_object_set_data (G_OBJECT (popup), "button", wid);
+
+	hbox = gtk_hbox_new (FALSE, 0);
+	gtk_container_add (GTK_CONTAINER (popup), hbox);
+
+	label = gtk_label_new (_("Find:"));
+	gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+	wid = gtk_entry_new ();
+	g_signal_connect (wid, "changed",
+			  G_CALLBACK (find_entry_changed_cb), index);
+	gtk_box_pack_start (GTK_BOX (hbox), wid, TRUE, TRUE, 0);
+	gtk_widget_show_all (hbox);
+
+	/* text contents */
+	GtkWidget *sw, *vbox;
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
+					GTK_POLICY_AUTOMATIC);
+	gtk_box_pack_start (GTK_BOX (index), sw, TRUE, TRUE, 0);
+	
+	vbox = gtk_vbox_new (FALSE, 0);
+	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), vbox);
+
+	index->priv->tview = gtk_text_view_new_with_buffer (index->priv->tbuffer);
+	gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (index->priv->tview), GTK_WRAP_WORD);
+	gtk_text_view_set_editable (GTK_TEXT_VIEW (index->priv->tview), FALSE);
+	//gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (index->priv->tview), FALSE);
+	gtk_box_pack_start (GTK_BOX (vbox), index->priv->tview, TRUE, TRUE, 0);
+	gtk_widget_show_all (sw);
+
+	g_signal_connect (index->priv->tview, "key-press-event", 
+			  G_CALLBACK (key_press_event), index);
+	g_signal_connect (index->priv->tview, "event-after", 
+			  G_CALLBACK (event_after), index);
+	g_signal_connect (index->priv->tview, "motion-notify-event", 
+			  G_CALLBACK (motion_notify_event), index);
+	g_signal_connect (index->priv->tview, "visibility-notify-event", 
+			  G_CALLBACK (visibility_notify_event), index);
+
+	/* initial update */
 	update_display (index);
 
 	return (GtkWidget*) index;
@@ -447,3 +476,196 @@ meta_changed_cb (BrowserConnection *bcnc, GdaMetaStruct *mstruct, ObjectsIndex *
 {
 	update_display (index);
 }
+
+static GdkCursor *hand_cursor = NULL;
+static GdkCursor *regular_cursor = NULL;
+
+/* Looks at all tags covering the position (x, y) in the text view, 
+ * and if one of them is a link, change the cursor to the "hands" cursor
+ * typically used by web browsers.
+ */
+static void
+set_cursor_if_appropriate (GtkTextView *text_view, gint x, gint y, ObjectsIndex *index)
+{
+	GSList *tags = NULL, *tagp = NULL;
+	GtkTextIter iter;
+	gboolean hovering = FALSE;
+	
+	gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
+	
+	tags = gtk_text_iter_get_tags (&iter);
+	for (tagp = tags;  tagp != NULL;  tagp = tagp->next) {
+		GtkTextTag *tag = tagp->data;
+		gchar *table_name;
+		
+		table_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
+		if (table_name) {
+			hovering = TRUE;
+			break;
+		}
+	}
+	
+	if (hovering != index->priv->hovering_over_link) {
+		index->priv->hovering_over_link = hovering;
+		
+		if (index->priv->hovering_over_link) {
+			if (! hand_cursor)
+				hand_cursor = gdk_cursor_new (GDK_HAND2);
+			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
+									 GTK_TEXT_WINDOW_TEXT),
+					       hand_cursor);
+		}
+		else {
+			if (!regular_cursor)
+				regular_cursor = gdk_cursor_new (GDK_XTERM);
+			gdk_window_set_cursor (gtk_text_view_get_window (text_view,
+									 GTK_TEXT_WINDOW_TEXT),
+					       regular_cursor);
+		}
+	}
+	
+	if (tags) 
+		g_slist_free (tags);
+}
+
+/* 
+ * Also update the cursor image if the window becomes visible
+ * (e.g. when a window covering it got iconified).
+ */
+static gboolean
+visibility_notify_event (GtkWidget *text_view, GdkEventVisibility *event, ObjectsIndex *index)
+{
+	gint wx, wy, bx, by;
+	
+	gdk_window_get_pointer (text_view->window, &wx, &wy, NULL);
+	
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
+					       GTK_TEXT_WINDOW_WIDGET,
+					       wx, wy, &bx, &by);
+	
+	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), bx, by, index);
+	
+	return FALSE;
+}
+
+/*
+ * Update the cursor image if the pointer moved. 
+ */
+static gboolean
+motion_notify_event (GtkWidget *text_view, GdkEventMotion *event, ObjectsIndex *index)
+{
+	gint x, y;
+	
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
+					       GTK_TEXT_WINDOW_WIDGET,
+					       event->x, event->y, &x, &y);
+	
+	set_cursor_if_appropriate (GTK_TEXT_VIEW (text_view), x, y, index);
+	
+	gdk_window_get_pointer (text_view->window, NULL, NULL, NULL);
+	return FALSE;
+}
+
+
+/* Looks at all tags covering the position of iter in the text view, 
+ * and if one of them is a link, follow it by showing the page identified
+ * by the data attached to it.
+ */
+static void
+follow_if_link (GtkWidget *text_view, GtkTextIter *iter, ObjectsIndex *index)
+{
+	GSList *tags = NULL, *tagp = NULL;
+	
+	tags = gtk_text_iter_get_tags (iter);
+	for (tagp = tags;  tagp;  tagp = tagp->next) {
+		GtkTextTag *tag = tagp->data;
+		const gchar *schema;
+		
+		schema = g_object_get_data (G_OBJECT (tag), "dbo_obj_schema");
+		if (schema) {
+			const gchar *objects_name, *short_name;
+		
+			objects_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_name");
+			short_name = g_object_get_data (G_OBJECT (tag), "dbo_obj_short_name");
+			
+			if (objects_name && short_name) {
+				gchar *str, *tmp1, *tmp2, *tmp3;
+				tmp1 = gda_rfc1738_encode (schema);
+				tmp2 = gda_rfc1738_encode (objects_name);
+				tmp3 = gda_rfc1738_encode (short_name);
+				str = g_strdup_printf ("OBJ_TYPE=table;OBJ_SCHEMA=%s;OBJ_NAME=%s;OBJ_SHORT_NAME=%s",
+						       tmp1, tmp2, tmp3);
+				g_free (tmp1);
+				g_free (tmp2);
+				g_free (tmp3);
+				g_signal_emit (index, objects_index_signals [SELECTION_CHANGED], 0,
+					       BROWSER_FAVORITES_TABLES, str);
+				g_free (str);
+			}
+		}
+        }
+
+	if (tags) 
+		g_slist_free (tags);
+}
+
+/*
+ * Links can also be activated by clicking.
+ */
+static gboolean
+event_after (GtkWidget *text_view, GdkEvent *ev, ObjectsIndex *index)
+{
+	GtkTextIter start, end, iter;
+	GtkTextBuffer *buffer;
+	GdkEventButton *event;
+	gint x, y;
+	
+	if (ev->type != GDK_BUTTON_RELEASE)
+		return FALSE;
+	
+	event = (GdkEventButton *)ev;
+	
+	if (event->button != 1)
+		return FALSE;
+	
+	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+	
+	/* we shouldn't follow a link if the user has selected something */
+	gtk_text_buffer_get_selection_bounds (buffer, &start, &end);
+	if (gtk_text_iter_get_offset (&start) != gtk_text_iter_get_offset (&end))
+		return FALSE;
+	
+	gtk_text_view_window_to_buffer_coords (GTK_TEXT_VIEW (text_view), 
+					       GTK_TEXT_WINDOW_WIDGET,
+					       event->x, event->y, &x, &y);
+	
+	gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW (text_view), &iter, x, y);
+	
+	follow_if_link (text_view, &iter, index);
+	
+	return FALSE;
+}
+
+/* 
+ * Links can be activated by pressing Enter.
+ */
+static gboolean
+key_press_event (GtkWidget *text_view, GdkEventKey *event, ObjectsIndex *index)
+{
+	GtkTextIter iter;
+	GtkTextBuffer *buffer;
+	
+	switch (event->keyval) {
+	case GDK_Return: 
+	case GDK_KP_Enter:
+		buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
+		gtk_text_buffer_get_iter_at_mark (buffer, &iter, 
+						  gtk_text_buffer_get_insert (buffer));
+		follow_if_link (text_view, &iter, index);
+		break;
+		
+	default:
+		break;
+	}
+	return FALSE;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]