summaryrefslogtreecommitdiff
path: root/tools/glade/glade/tree.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/glade/glade/tree.c')
-rw-r--r--tools/glade/glade/tree.c471
1 files changed, 471 insertions, 0 deletions
diff --git a/tools/glade/glade/tree.c b/tools/glade/glade/tree.c
new file mode 100644
index 00000000..8f643e35
--- /dev/null
+++ b/tools/glade/glade/tree.c
@@ -0,0 +1,471 @@
+/* Gtk+ User Interface Builder
+ * Copyright (C) 1998 Damon Chaplin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "gladeconfig.h"
+
+#include <string.h>
+
+#include <gtk/gtkcellrendererpixbuf.h>
+#include <gtk/gtkcellrenderertext.h>
+#include <gtk/gtktreeselection.h>
+#include <gtk/gtktreestore.h>
+#include <gtk/gtktreeview.h>
+#include <gtk/gtkscrolledwindow.h>
+#include <gtk/gtkwindow.h>
+
+
+#include "gbwidget.h"
+#include "editor.h"
+#include "tree.h"
+#include "utils.h"
+#include "glade_project_window.h"
+
+/* This key is used to store a pointer from each widget to its corresponding
+ node in the widget tree. */
+#define GLADE_TREE_NODE_KEY "GLADE_TREE_NODE_KEY"
+
+enum {
+ COLUMN_ICON,
+ COLUMN_TEXT,
+ COLUMN_OBJECT
+};
+
+GtkWidget *win_tree = NULL;
+static GtkWidget *widget_tree = NULL;
+static GtkTreeStore *tree_store = NULL;
+static GtkTreeSelection *tree_selection = NULL;
+
+
+static void tree_on_selection_changed (GtkTreeSelection *selection,
+ gpointer data);
+static gint tree_on_button_press (GtkWidget * tree,
+ GdkEventButton * event,
+ gpointer widget);
+
+
+
+/* This creates the widget tree window, with just the root tree. */
+void
+tree_init ()
+{
+ GtkWidget *scrolled_win;
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *icon_rend, *text_rend;
+
+ win_tree = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_move (GTK_WINDOW (win_tree), 530, 0);
+ gtk_widget_set_name (win_tree, "GladeWidgetTree");
+ gtk_signal_connect (GTK_OBJECT (win_tree), "delete_event",
+ GTK_SIGNAL_FUNC (tree_hide), NULL);
+ gtk_signal_connect_after (GTK_OBJECT (win_tree), "hide",
+ GTK_SIGNAL_FUNC (glade_project_window_uncheck_widget_tree_menu_item),
+ NULL);
+ gtk_window_set_title (GTK_WINDOW (win_tree), _("Widget Tree"));
+ gtk_container_set_border_width (GTK_CONTAINER (win_tree), 0);
+ gtk_window_set_wmclass (GTK_WINDOW (win_tree), "widget_tree", "Glade");
+ gtk_window_add_accel_group (GTK_WINDOW (win_tree),
+ glade_get_global_accel_group ());
+
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add (GTK_CONTAINER (win_tree), scrolled_win);
+ gtk_widget_show (scrolled_win);
+
+ tree_store = gtk_tree_store_new (3, GDK_TYPE_PIXBUF, G_TYPE_STRING,
+ G_TYPE_POINTER);
+ widget_tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
+ gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget_tree), FALSE);
+ gtk_container_add (GTK_CONTAINER (scrolled_win), widget_tree);
+ gtk_widget_set_usize (widget_tree, 250, 320);
+ gtk_widget_show (widget_tree);
+
+ tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget_tree));
+ gtk_tree_selection_set_mode (GTK_TREE_SELECTION (tree_selection),
+ GTK_SELECTION_SINGLE);
+
+ col = gtk_tree_view_column_new ();
+
+ icon_rend = gtk_cell_renderer_pixbuf_new ();
+ text_rend = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (col, icon_rend, FALSE);
+ gtk_tree_view_column_pack_start (col, text_rend, TRUE);
+ gtk_tree_view_column_set_attributes (col, icon_rend,
+ "pixbuf", COLUMN_ICON,
+ NULL);
+ gtk_tree_view_column_set_attributes (col, text_rend,
+ "text", COLUMN_TEXT,
+ NULL);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (widget_tree), col);
+
+ gtk_signal_connect (GTK_OBJECT (widget_tree), "button_press_event",
+ GTK_SIGNAL_FUNC (tree_on_button_press), NULL);
+ g_signal_connect (G_OBJECT (tree_selection), "changed",
+ GTK_SIGNAL_FUNC (tree_on_selection_changed), NULL);
+}
+
+/* This shows the widget tree window. */
+void
+tree_show (GtkWidget * widget, gpointer data)
+{
+ gtk_widget_show (win_tree);
+ gdk_window_show (GTK_WIDGET (win_tree)->window);
+ gdk_window_raise (GTK_WIDGET (win_tree)->window);
+}
+
+/* This hides the widget tree window. */
+gint
+tree_hide (GtkWidget * widget, gpointer data)
+{
+ gtk_widget_hide (win_tree);
+ return TRUE;
+}
+
+
+/* This adds a widget to the tree (if it is a widget that we are interested in,
+ * i.e. a GbWidget). The widget must have already been added to the interface,
+ * so we can determine its parent. It also recursively adds any children. */
+void
+tree_add_widget (GtkWidget * widget)
+{
+ /* We only add it to the tree if it is a GbWidget and it hasn't already been
+ added. */
+ if (GB_IS_GB_WIDGET (widget))
+ {
+ if (!gtk_object_get_data (GTK_OBJECT (widget), GLADE_TREE_NODE_KEY))
+ {
+ GbWidget *gbwidget;
+ GtkWidget *parent;
+ GNode *parent_node = NULL;
+ GtkTreeIter iter, tmp_iter = { 0 }, *parent_iter = NULL;
+
+ gbwidget = gb_widget_lookup (widget);
+ g_return_if_fail (gbwidget != NULL);
+
+ parent = glade_util_get_parent (widget);
+ if (parent)
+ parent_node = gtk_object_get_data (GTK_OBJECT (parent),
+ GLADE_TREE_NODE_KEY);
+ if (parent_node)
+ {
+ /* I'm not sure if this is a bad hack. */
+ tmp_iter.stamp = tree_store->stamp;
+ tmp_iter.user_data = parent_node;
+ parent_iter = &tmp_iter;
+ }
+
+ /* Create the pixbuf for the icon, if necessary. */
+ if (gbwidget->pixbuf == NULL && gbwidget->pixmap_struct)
+ {
+ gbwidget->pixbuf = gdk_pixbuf_new_from_xpm_data ((const char**) gbwidget->pixmap_struct);
+ }
+
+ gtk_tree_store_append (tree_store, &iter, parent_iter);
+ gtk_tree_store_set (tree_store, &iter,
+ COLUMN_ICON, gbwidget->pixbuf,
+ COLUMN_TEXT, gtk_widget_get_name (widget),
+ COLUMN_OBJECT, widget,
+ -1);
+
+ /* Save a pointer to the GNode inside the widget. */
+ gtk_object_set_data (GTK_OBJECT (widget), GLADE_TREE_NODE_KEY,
+ iter.user_data);
+ }
+ else
+ {
+ /* Update the name if necessary. This is needed because we may have
+ added this widget before (if it was created by its parent
+ automatically), but we have only just loaded its name. */
+ tree_rename_widget (widget, gtk_widget_get_name (widget));
+ }
+ }
+
+ /* Now add any children. */
+ gb_widget_children_foreach (widget, (GtkCallback) tree_add_widget, NULL);
+}
+
+
+/* Recursively removes any pointers to tree GNodes in the widgets. */
+void
+tree_remove_widget_cb (GtkWidget * widget)
+{
+#if 0
+ g_print ("Removing node key from widget: %s\n", gtk_widget_get_name (widget));
+#endif
+ gtk_object_remove_data (GTK_OBJECT (widget), GLADE_TREE_NODE_KEY);
+ gb_widget_children_foreach (widget, (GtkCallback) tree_remove_widget_cb,
+ NULL);
+}
+
+
+void
+tree_remove_widget (GtkWidget * widget)
+{
+ GNode *node;
+ GtkTreeIter iter = { 0 };
+
+#if 0
+ g_print ("In tree_remove_widget: %s\n", gtk_widget_get_name (widget));
+#endif
+
+ node = gtk_object_get_data (GTK_OBJECT (widget), GLADE_TREE_NODE_KEY);
+ if (node == NULL)
+ return;
+
+ /* I'm not sure if this is a bad hack. */
+ iter.stamp = tree_store->stamp;
+ iter.user_data = node;
+
+#if 0
+ g_print ("Removing widget: %s\n", gtk_widget_get_name (widget));
+#endif
+
+ gtk_object_remove_data (GTK_OBJECT (widget), GLADE_TREE_NODE_KEY);
+
+ /* Remove the GNode's stored in any child widgets, as these GNodes are also
+ about to be removed. */
+ gb_widget_children_foreach (widget, (GtkCallback) tree_remove_widget_cb,
+ NULL);
+
+ gtk_tree_store_remove (tree_store, &iter);
+}
+
+
+void
+tree_clear (void)
+{
+ gtk_tree_store_clear (tree_store);
+}
+
+
+void
+tree_freeze (void)
+{
+#if 0
+ /* I don't think GtkTreeView has a corresponding function. */
+ gtk_clist_freeze (GTK_CLIST (widget_tree));
+#endif
+}
+
+
+void
+tree_thaw (void)
+{
+#if 0
+ /* I don't think GtkTreeView has a corresponding function. */
+ gtk_clist_thaw (GTK_CLIST (widget_tree));
+#endif
+}
+
+
+void
+tree_rename_widget (GtkWidget * widget, const gchar * name)
+{
+ GNode *node;
+ GtkTreeIter iter = { 0 };
+ gchar *old_name;
+
+ node = gtk_object_get_data (GTK_OBJECT (widget), GLADE_TREE_NODE_KEY);
+ if (node == NULL)
+ return;
+
+ /* I'm not sure if this is a bad hack. */
+ iter.stamp = tree_store->stamp;
+ iter.user_data = node;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (tree_store), &iter,
+ COLUMN_TEXT, &old_name, -1);
+
+ /* Only update it if the name has really changed. */
+ if (strcmp (old_name, name))
+ {
+#if 0
+ g_print ("Changing name from: %s to: %s\n", old_name, name);
+#endif
+ gtk_tree_store_set (tree_store, &iter, COLUMN_TEXT, name, -1);
+ }
+}
+
+
+/* This is used when inserting an alignment or event box above a widget. */
+void
+tree_insert_widget_parent (GtkWidget * parent, GtkWidget * widget)
+{
+ /* Remove the widget if it is currently in the tree. */
+ tree_remove_widget (widget);
+
+ /* Now add the parent. Since widgets are added recursively, the widget
+ will get added back as well. */
+ tree_add_widget (parent);
+}
+
+
+/* Selects a node and makes sure it is visible on the tree */
+static void
+tree_select_iter (GtkTreeIter *iter)
+{
+ GtkTreePath *path;
+ GList *ancestors = NULL, *elem;
+
+ /* Make sure all ancestors are expanded. This makes the GUI easier to use,
+ but is also necessary to be able to select the widget in the GtkTreeView.
+ We have to expand the rows from the top down, so first we step up the
+ tree pushing them onto a list. */
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_store), iter);
+ for (;;)
+ {
+ if (!gtk_tree_path_up (path))
+ break;
+
+ ancestors = g_list_prepend (ancestors, path);
+ path = gtk_tree_path_copy (path);
+ }
+ gtk_tree_path_free (path);
+
+ for (elem = ancestors; elem; elem = elem->next)
+ {
+ GtkTreePath *tmp_path = elem->data;
+
+ if (!gtk_tree_view_row_expanded (GTK_TREE_VIEW (widget_tree), tmp_path))
+ gtk_tree_view_expand_row (GTK_TREE_VIEW (widget_tree), tmp_path,
+ FALSE);
+ gtk_tree_path_free (tmp_path);
+ }
+ g_list_free (ancestors);
+
+ /* Select the row. */
+ gtk_tree_selection_select_iter (tree_selection, iter);
+
+ /* Make sure the row is visible. */
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (tree_store), iter);
+ gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (widget_tree), path, NULL,
+ TRUE, 0, 0);
+ gtk_tree_path_free (path);
+}
+
+
+static void
+tree_get_selection_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ GList **selection)
+{
+ GObject *object;
+
+ gtk_tree_model_get (model, iter, COLUMN_OBJECT, &object, -1);
+ if (object)
+ *selection = g_list_prepend (*selection, object);
+}
+
+
+/* Returns a list of selected widgets. g_list_free() it. */
+static GList*
+tree_get_selection (void)
+{
+ GList *selection = NULL;
+
+ /* Create a GList of all the widgets selected in the tree. */
+ gtk_tree_selection_selected_foreach (tree_selection,
+ (GtkTreeSelectionForeachFunc) tree_get_selection_cb,
+ &selection);
+ return selection;
+}
+
+
+void
+tree_select_widget (GtkWidget * widget, gboolean select)
+{
+ GNode *node;
+ GtkTreeIter iter = { 0 };
+ GList *selection;
+ gboolean selected = FALSE;
+
+ if (!GB_IS_GB_WIDGET (widget))
+ return;
+
+ node = gtk_object_get_data (GTK_OBJECT (widget), GLADE_TREE_NODE_KEY);
+ if (node == NULL)
+ return;
+
+ /* I'm not sure if this is a bad hack. */
+ iter.stamp = tree_store->stamp;
+ iter.user_data = node;
+
+ /* Check if the item is currently selected. */
+ selection = tree_get_selection ();
+ if (g_list_find (selection, widget))
+ selected = TRUE;
+ g_list_free (selection);
+
+ if (select && !selected)
+ tree_select_iter (&iter);
+ else if (!select && selected)
+ gtk_tree_selection_unselect_iter (tree_selection, &iter);
+}
+
+
+/* We set the widget selection in the GUI to match the selection in the tree.
+ */
+static void
+tree_on_selection_changed (GtkTreeSelection *selection, gpointer data)
+{
+ /* Note that we may get callbacks as widgets are selected or deselected in
+ the GUI, but we will check if the tree needs updating and it shouldn't
+ so we won't get into infinite loops. */
+ editor_set_selection (tree_get_selection ());
+}
+
+
+/* This is called when a button is pressed in the tree. If it was the
+ right mouse button we show the popup menu for the widget. */
+static gint
+tree_on_button_press (GtkWidget *tree,
+ GdkEventButton *event,
+ gpointer data)
+{
+ GtkTreePath *path;
+ GtkTreeIter iter;
+
+ if (event->button != 3
+ || event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (tree)))
+ return FALSE;
+
+ if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (tree),
+ event->x, event->y,
+ &path, NULL, NULL, NULL))
+ return FALSE;
+
+ if (gtk_tree_model_get_iter (GTK_TREE_MODEL (tree_store), &iter, path))
+ {
+ GtkWidget *widget;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (tree_store), &iter,
+ COLUMN_OBJECT, &widget,
+ -1);
+
+ if (widget)
+ gb_widget_show_popup_menu (GTK_WIDGET (widget), event);
+
+ gtk_tree_path_free (path);
+ }
+
+ return TRUE;
+}
+
+