[dia] Reimplementation of the Diagram Tree with GtkTree(Model|View)



commit 131eea01ceb400268ef0b6662cfce4c53a4b936c
Author: Hans Breuer <hans breuer org>
Date:   Fri Apr 24 19:58:00 2009 +0200

    Reimplementation of the Diagram Tree with GtkTree(Model|View)
    
    It already has a bunch of fancy new features:
     - nicer look
     - can search for object name by simply typing it
     - multiple selection (works across layers!)
     - allows to transfer the selection to the diagram
     - tooltips for additional information
    
    Currently missing:
     - reaction to the diagram changes (i.e. auto update)
     - filtering by object type (or anything else)
     - sorting by name or type (or size?)
     - serializing it's position
     - looking into groups
     - in-place editing (e.g. moving objects between layers)
     - enough functionality to also replace the layer dialog
       - move layer up or down
       - add/remove layer
       - hide/show layer
       - rename layer
       - maybe hide toe objects for layer editing (or just view defaults)
---
 app/Makefile.am          |    6 +
 app/diagram_tree_model.c |  377 +++++++++++++++++++++++++++++++++
 app/diagram_tree_model.h |   28 +++
 app/diagram_tree_view.c  |  514 ++++++++++++++++++++++++++++++++++++++++++++++
 app/makefile.msc         |    5 +-
 app/menus.c              |    4 +-
 6 files changed, 932 insertions(+), 2 deletions(-)

diff --git a/app/Makefile.am b/app/Makefile.am
index 0e84710..6f18451 100644
--- a/app/Makefile.am
+++ b/app/Makefile.am
@@ -181,12 +181,18 @@ dia_core_files = \
 	recent_files.c \
 	diagram_tree.h \
 	diagram_tree.c \
+	\
 	diagram_tree_window.h \
 	diagram_tree_window.c \
 	diagram_tree_menu.h \
 	diagram_tree_menu.c \
 	diagram_tree_menu_callbacks.h \
 	diagram_tree_menu_callbacks.c \
+	\
+	diagram_tree_model.c \
+	diagram_tree_model.h \
+	diagram_tree_view.c \
+	\
 	sheets.c \
 	sheets.h \
 	sheets_dialog.c \
diff --git a/app/diagram_tree_model.c b/app/diagram_tree_model.c
new file mode 100644
index 0000000..e8af9a0
--- /dev/null
+++ b/app/diagram_tree_model.c
@@ -0,0 +1,377 @@
+/* Dia -- a diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diagram_tree.c : a tree showing open diagrams
+ * Copyright (C) 2001 Jose A Ortega Ruiz
+ *
+ * complete rewrite to get rid of deprecated widgets
+ * Copyright (C) 2009 Hans Breuer
+ * 
+ * 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 "config.h"
+
+#include <gtk/gtk.h>
+#include "diagram.h"
+#include "object.h"
+
+#include "diagram_tree_model.h"
+
+/* accessing iter fileds by name by */
+#define NODE_DIAGRAM(it) ((DiagramData*)(it->user_data))
+#define NODE_LAYER(it) ((Layer*)(it->user_data2))
+#define NODE_OBJECT(it) ((DiaObject*)(it->user_data3))
+typedef struct _DiagramTreeModelClass
+{
+  GObjectClass parent_class;
+} DiagramTreeModelClass;
+typedef struct _DiagramTreeModel
+{
+  GObject parent;
+  /* no need to store anything */
+} DiagramTreeModel;
+static void
+_dtm_class_init (DiagramTreeModelClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+}
+static void
+_dtm_init (DiagramTreeModel *dtm)
+{
+}
+
+#define DIAGRAM_TREE_MODEL (_dtm_get_type ())
+
+static void _dtm_iface_init (GtkTreeModelIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (DiagramTreeModel, _dtm, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
+						_dtm_iface_init))
+
+static GtkTreeModelFlags
+_get_flags (GtkTreeModel *tree_model)
+{
+  return GTK_TREE_MODEL_ITERS_PERSIST;
+}
+static gint
+_dtm_get_n_columns (GtkTreeModel *tree_model)
+{
+  return NUM_COLUMNS;
+}
+static GType
+_dtm_get_column_type (GtkTreeModel *tree_model,
+		      gint          index)
+{
+  g_return_val_if_fail (index >= DIAGRAM_COLUMN || index < NUM_COLUMNS, G_TYPE_NONE);
+  
+  switch (index) {
+  case DIAGRAM_COLUMN :
+    return DIA_TYPE_DIAGRAM;
+  case NAME_COLUMN :
+    return G_TYPE_STRING;
+  case LAYER_COLUMN :
+  case OBJECT_COLUMN :
+    /* TODO: these should be ported to GObject ... */
+    return G_TYPE_POINTER;
+  default :
+    g_assert_not_reached ();
+    return G_TYPE_NONE;
+  }
+}
+static gboolean
+_dtm_get_iter (GtkTreeModel *tree_model,
+	       GtkTreeIter  *iter,
+	       GtkTreePath  *path)
+{
+  /* copy&paste from Gtk */
+  GtkTreeIter parent;
+  gint *indices;
+  gint depth, i;
+  
+  indices = gtk_tree_path_get_indices (path);
+  depth = gtk_tree_path_get_depth (path);
+
+  g_return_val_if_fail (depth > 0, FALSE);
+  
+  if (!gtk_tree_model_iter_nth_child (tree_model, iter, NULL, indices[0]))
+    return FALSE;
+
+  for (i = 1; i < depth; i++)
+    {
+      parent = *iter;
+      if (!gtk_tree_model_iter_nth_child (tree_model, iter, &parent, indices[i]))
+	return FALSE;
+    }
+
+  return TRUE;
+}
+static GtkTreePath *
+_dtm_get_path (GtkTreeModel *tree_model,
+	       GtkTreeIter  *iter)
+{
+  GtkTreePath *result;
+
+  if (!NODE_DIAGRAM(iter) && !NODE_LAYER(iter) && !NODE_OBJECT(iter)) {
+    /* the root path */
+    return gtk_tree_path_new_first ();
+  }
+
+  result = gtk_tree_path_new ();
+
+  if (NODE_DIAGRAM(iter)) {
+    GList *list = dia_open_diagrams();
+    gtk_tree_path_append_index (result, g_list_index (list, NODE_DIAGRAM(iter)));
+  }
+  if (NODE_LAYER(iter)) {
+    g_return_val_if_fail (NODE_DIAGRAM(iter) == layer_get_parent_diagram (NODE_LAYER(iter)), NULL);
+    gtk_tree_path_append_index (result, data_layer_get_index (NODE_DIAGRAM(iter), NODE_LAYER(iter)));
+  }
+  if (NODE_OBJECT(iter)) {
+    g_return_val_if_fail (NODE_LAYER(iter) == dia_object_get_parent_layer (NODE_OBJECT(iter)), NULL);
+    gtk_tree_path_append_index (result, layer_object_get_index (NODE_LAYER(iter), NODE_OBJECT(iter)));
+  }
+
+  return result;
+}
+
+static gint
+_dtm_iter_n_children (GtkTreeModel *tree_model,
+		      GtkTreeIter  *iter)
+{
+  if (!iter)
+    return g_list_length (dia_open_diagrams());
+
+  if (NODE_OBJECT(iter)) {
+    /* TODO: dive into objects (groups?)? */
+    return 0;
+  } else if (NODE_LAYER(iter)) {
+    if (!NODE_LAYER(iter))
+      return 0;
+    return layer_object_count (NODE_LAYER(iter));
+  } else if (NODE_DIAGRAM(iter)) {
+     if (!NODE_DIAGRAM(iter))
+       return 0;
+     return data_layer_count (NODE_DIAGRAM(iter));
+  }
+  return 0;
+}
+static void
+_dtm_get_value (GtkTreeModel *tree_model,
+		GtkTreeIter  *iter,
+		gint          column,
+		GValue       *value)
+{
+
+  switch (column) {
+  case DIAGRAM_COLUMN :
+    g_value_init (value, G_TYPE_OBJECT);
+    g_value_set_object (value, g_object_ref(NODE_DIAGRAM(iter)));
+    break;
+  case LAYER_COLUMN :
+    g_value_init (value, G_TYPE_POINTER);
+    g_value_set_pointer (value, NODE_LAYER(iter));
+    break;
+  case OBJECT_COLUMN :
+    g_value_init (value, G_TYPE_POINTER);
+    g_value_set_pointer (value, NODE_OBJECT(iter));
+    break;
+  case NAME_COLUMN :
+    g_value_init (value, G_TYPE_STRING);
+    /* deduce the requested name from the iter */
+    if (NODE_OBJECT(iter))
+      g_value_set_string (value, object_get_displayname (NODE_OBJECT (iter)));
+    else if (NODE_LAYER(iter))
+      g_value_set_string (value, layer_get_name (NODE_LAYER (iter)));
+    else if (NODE_DIAGRAM(iter))
+      g_value_set_string (value, diagram_get_name (DIA_DIAGRAM(NODE_DIAGRAM(iter))));
+    else /* warn on it? */
+      g_value_set_string (value, NULL);
+    break;
+  default :
+    g_assert_not_reached ();
+  }
+}
+static gboolean
+_dtm_iter_next (GtkTreeModel *tree_model,
+		GtkTreeIter  *iter)
+{
+  int i;
+  if (NODE_OBJECT(iter)) {
+    if (!NODE_LAYER(iter))
+      return FALSE;
+    i = layer_object_get_index (NODE_LAYER(iter), NODE_OBJECT(iter));
+    ++i;
+    NODE_OBJECT(iter) = layer_object_get_nth(NODE_LAYER(iter), i);
+    return NODE_OBJECT(iter) != NULL;
+  } else if (NODE_LAYER(iter)) {
+    if (!NODE_DIAGRAM(iter))
+      return FALSE;
+    i = data_layer_get_index (NODE_DIAGRAM(iter), NODE_LAYER(iter));
+    ++i;
+    NODE_LAYER(iter) = data_layer_get_nth(NODE_DIAGRAM(iter), i);
+    return NODE_LAYER(iter) != NULL;
+  } else if (NODE_DIAGRAM(iter)) {
+    GList *list = dia_open_diagrams();
+    i = g_list_index (list, NODE_DIAGRAM(iter));
+    ++i;
+    list = g_list_nth (list, i);
+    NODE_DIAGRAM(iter) = list ? list->data : NULL;
+    return NODE_DIAGRAM(iter) != NULL;
+  } else {
+    /* empy iter? */
+    GList *list = dia_open_diagrams();
+    NODE_DIAGRAM(iter) = list ? list->data : NULL;
+    return NODE_DIAGRAM(iter) != NULL;
+  }
+  return FALSE;
+}
+static gboolean
+_dtm_iter_children (GtkTreeModel *tree_model,
+		    GtkTreeIter  *iter,
+		    GtkTreeIter  *parent)
+{
+  if (parent) {
+    if (NODE_OBJECT(parent))
+      return FALSE;
+    else if (NODE_LAYER(parent)) {
+      NODE_OBJECT(iter) = layer_object_get_nth(NODE_LAYER(parent), 0);
+      if (NODE_OBJECT(iter)) {
+        NODE_LAYER(iter) = dia_object_get_parent_layer(NODE_OBJECT(iter));
+	NODE_DIAGRAM(iter) = layer_get_parent_diagram (NODE_LAYER(iter));
+	return TRUE;
+      }
+    } else if (NODE_DIAGRAM(parent)) {
+      NODE_LAYER(iter) = data_layer_get_nth(NODE_DIAGRAM(parent), 0);
+      if (NODE_LAYER(iter)) {
+	NODE_DIAGRAM(iter) = layer_get_parent_diagram (NODE_LAYER(iter));
+	NODE_OBJECT(iter) = NULL;
+	return TRUE;
+      }
+    } else {
+      /* deliver root's children */
+      parent = NULL;
+    }
+  }
+  if (!parent) {
+    /* the first diagram */
+    GList *list = dia_open_diagrams();
+    NODE_DIAGRAM(iter) = list ? list->data : NULL;
+    NODE_LAYER(iter) = NULL;
+    NODE_OBJECT(iter) = NULL;
+    return NODE_DIAGRAM(iter) != NULL;
+  }
+}
+static gboolean
+_dtm_iter_has_child (GtkTreeModel *tree_model,
+		     GtkTreeIter  *iter)
+{
+  return _dtm_iter_n_children (tree_model, iter) > 0;
+}
+static gboolean
+_dtm_iter_nth_child (GtkTreeModel *tree_model,
+		     GtkTreeIter  *iter,
+		     GtkTreeIter  *parent,
+		     gint          n)
+{
+  if (parent) {
+    *iter = *parent;
+    if (NODE_OBJECT(parent)) {
+      return FALSE;
+    } else if (NODE_LAYER(parent)) {
+      NODE_OBJECT(iter) = layer_object_get_nth(NODE_LAYER(iter), n);
+      return NODE_OBJECT(iter) != NULL;
+    } else if (NODE_DIAGRAM(parent)) {
+      NODE_LAYER(iter) = data_layer_get_nth(NODE_DIAGRAM(iter), n);
+      return NODE_LAYER(iter) != NULL;
+    }
+  }
+  if (!parent || !NODE_DIAGRAM(parent)) {
+    /* the nth diagram */
+    GList *list = dia_open_diagrams();
+    list = g_list_nth(list, n);
+    NODE_DIAGRAM(iter) = list ? list->data : NULL;
+    NODE_LAYER(iter) = NULL;
+    NODE_OBJECT(iter) = NULL;
+    return NODE_DIAGRAM(iter) != NULL;
+  }
+  return FALSE;
+}
+static gboolean
+_dtm_iter_parent (GtkTreeModel *tree_model,
+		  GtkTreeIter  *iter,
+		  GtkTreeIter  *child)
+{
+  *iter = *child;
+  if (!NODE_DIAGRAM(child))
+    return FALSE;
+  if (NODE_OBJECT(child))
+    NODE_OBJECT(iter) = NULL;
+  else if (NODE_LAYER(child))
+    NODE_LAYER(iter) = NULL;
+  else if (NODE_DIAGRAM(child))
+    NODE_DIAGRAM(iter) = NULL;
+  else
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+_dtm_ref_node (GtkTreeModel *tree_model,
+	       GtkTreeIter  *iter)
+{
+  /* fixme: ref-counting? */
+}
+static void
+_dtm_unref_node (GtkTreeModel *tree_model,
+	     GtkTreeIter  *iter)
+{
+  /* fixme: ref-counting? */
+}
+static void
+_dtm_iface_init (GtkTreeModelIface *iface)
+{
+  iface->get_flags = _get_flags;
+  iface->get_n_columns = _dtm_get_n_columns;
+  iface->get_column_type = _dtm_get_column_type;
+  iface->get_iter = _dtm_get_iter;
+  iface->get_path = _dtm_get_path;
+  iface->get_value = _dtm_get_value;
+  iface->iter_next = _dtm_iter_next;
+  iface->iter_children = _dtm_iter_children;
+  iface->iter_has_child = _dtm_iter_has_child;
+  iface->iter_n_children = _dtm_iter_n_children;
+  iface->iter_nth_child = _dtm_iter_nth_child;
+  iface->iter_parent = _dtm_iter_parent;
+
+  iface->ref_node = _dtm_ref_node;
+  iface->unref_node = _dtm_unref_node;
+
+  /*todo?*/
+#if 0
+  row_changed;
+  row_inserted;
+  row_has_child_toggled;
+  row_deleted;
+  rows_reordered;
+#endif
+}
+
+GtkTreeModel *
+diagram_tree_model_new (void)
+{
+  return g_object_new (DIAGRAM_TREE_MODEL, NULL);
+}
diff --git a/app/diagram_tree_model.h b/app/diagram_tree_model.h
new file mode 100644
index 0000000..4e67056
--- /dev/null
+++ b/app/diagram_tree_model.h
@@ -0,0 +1,28 @@
+#ifndef DIAGRAM_TREE_MODEL_H
+#define DIAGRAM_TREE_MODEL_H
+/*  Root/
+ *    Diagram/
+ *      Layer/
+ *        Object/  object or group with objects?
+ *          (Meta-)Properties/ ?
+ *
+ * But for a tree the concept of column and hierachy are ortogonal, 
+ * e.g. we can have a name column (something we can provide a value for)
+ * for all of the objects. 
+ * We may also want to build another heirachy by connected? Or maybe
+ * not because they could be circular.
+ *
+ * Another idea: split models, e.g. one for the application list of diagrams,
+ * one for a single diagram and one for meta/properties of an object.
+ */
+typedef enum {
+  DIAGRAM_COLUMN, /*!< conceptionally the Diagram although it is called DiagramData */
+  LAYER_COLUMN, /*!< not a gobject yet, but a pointer */
+  OBJECT_COLUMN, /*!< not a gobject yet, but a pointer */
+  NAME_COLUMN, /*!< the name of the 'row' be it diagram/layer/object */
+  NUM_COLUMNS /*! must be last - total number */
+} DiaNodeType;
+
+GtkTreeModel *diagram_tree_model_new (void);
+
+#endif
\ No newline at end of file
diff --git a/app/diagram_tree_view.c b/app/diagram_tree_view.c
new file mode 100644
index 0000000..139d313
--- /dev/null
+++ b/app/diagram_tree_view.c
@@ -0,0 +1,514 @@
+/* Dia -- a diagram creation/manipulation program
+ * Copyright (C) 1998 Alexander Larsson
+ *
+ * diagram_tree.c : a tree showing open diagrams
+ * Copyright (C) 2001 Jose A Ortega Ruiz
+ *
+ * diagram_tree_view.c : complete rewrite to get rid of deprecated widgets
+ * Copyright (C) 2009 Hans Breuer
+ * 
+ * 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 "config.h"
+
+#include <string.h>
+
+#include <gtk/gtk.h>
+
+#include <lib/object.h>
+
+#include "diagram_tree_model.h"
+
+#include "dia-app-icons.h" /* for dia_group_icon */
+#include <lib/group.h> /* IS_GROUP */
+#include "display.h"
+#include "properties-dialog.h" /* object_list_properties_show */
+#include "dia-props.h" /* diagram_properties_show */
+
+typedef struct _DiagramTreeView DiagramTreeView;
+struct _DiagramTreeView {
+  GtkTreeView parent_instance;
+  
+  GtkUIManager *ui_manager;
+  GtkMenu      *popup;
+};
+
+typedef struct _DiagramTreeViewClass DiagramTreeViewClass;
+struct _DiagramTreeViewClass {
+  GtkTreeViewClass parent_class;
+};
+
+#define DIAGRAM_TREE_VIEW_TYPE (_dtv_get_type ())
+#define DIAGRAM_TREE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), DIAGRAM_TREE_VIEW_TYPE, DiagramTreeView))
+
+#if 0
+//future
+G_DEFINE_TYPE_WITH_CODE (DiagramTreeView, _dtv, GTK_TYPE_TREE_VIEW,
+			 G_IMPLEMENT_INTERFACE (DIAGRAM_TYPE_SEARCHABLE,
+						_dtv_init))
+#else
+G_DEFINE_TYPE (DiagramTreeView, _dtv, GTK_TYPE_TREE_VIEW);
+#endif
+
+static gboolean
+_dtv_button_press (GtkWidget      *widget,
+		   GdkEventButton *event)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GtkTreePath      *path = NULL;
+  GtkTreeIter       iter;
+  DiaObject        *object;
+  Diagram          *diagram;
+
+  if (event->button == 3) {
+    selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+    model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+
+    if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), 
+					event->x, event->y,
+                                        &path, NULL, NULL, NULL)) {
+      return TRUE;
+    }
+
+    if (gtk_tree_selection_count_selected_rows (gtk_tree_view_get_selection (GTK_TREE_VIEW (widget))) < 2) {
+      gtk_tree_selection_unselect_all (selection);
+      gtk_tree_selection_select_path (selection, path);
+
+      /* FIXME: for multiple selection we should check all object selected being compatible */
+      gtk_tree_model_get_iter (model, &iter, path);
+      gtk_tree_model_get (model, &iter, OBJECT_COLUMN, &object, -1);
+      gtk_tree_path_free (path);
+    }
+
+    gtk_menu_popup (DIAGRAM_TREE_VIEW(widget)->popup, NULL, NULL, NULL, NULL, event->button, event->time);
+
+  } else {
+    GTK_WIDGET_CLASS (_dtv_parent_class)->button_press_event (widget, event);
+
+    if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), 
+					event->x, event->y,
+                                        &path, NULL, NULL, NULL)) {
+      model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+      gtk_tree_model_get_iter (model, &iter, path);
+      gtk_tree_model_get (model, &iter, OBJECT_COLUMN, &object, -1);
+      gtk_tree_model_get (model, &iter, DIAGRAM_COLUMN, &diagram, -1);
+
+      if (object && diagram && ddisplay_active_diagram() == diagram) {
+	if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) /* double-click 'locates' */
+	  ddisplay_present_object (ddisplay_active(), object);
+      }
+      if (diagram)
+	g_object_unref(diagram);
+      gtk_tree_path_free (path);
+    }
+  }
+
+  return TRUE;
+}
+
+static gboolean
+_dtv_query_tooltip (GtkWidget  *widget,
+                    gint        x,
+                    gint        y,
+                    gboolean    keyboard_mode,
+                    GtkTooltip *tooltip)
+{
+  int                    bin_x, bin_y;
+  GtkTreePath           *path = NULL;
+  GtkTreeViewColumn     *column;
+  GtkTreeIter            iter;
+  GtkTreeModel          *model;
+  GdkRectangle           cell_area;
+  GString               *markup;
+  gboolean               had_info = FALSE;
+
+  gtk_tree_view_convert_widget_to_bin_window_coords (
+	GTK_TREE_VIEW (widget), x, y, &bin_x, &bin_y);
+
+  if (gtk_tree_view_get_path_at_pos
+	(GTK_TREE_VIEW (widget), bin_x, bin_y, &path, &column, NULL, NULL)) {
+
+    model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+    if (gtk_tree_model_get_iter (model, &iter, path)) {
+      /* show some useful  information */
+      Diagram   *diagram;
+      Layer     *layer;
+      DiaObject *object;
+
+
+      gtk_tree_model_get (model, &iter, DIAGRAM_COLUMN, &diagram, -1);
+      gtk_tree_model_get (model, &iter, LAYER_COLUMN, &layer, -1);
+      gtk_tree_model_get (model, &iter, OBJECT_COLUMN, &object, -1);
+
+      markup = g_string_new (NULL);
+
+      if (diagram) {
+        g_string_append_printf (markup, "<b>%s</b>: %s\n", _("Diagram"), diagram->filename);
+        g_object_unref (diagram);
+      }
+      if (layer) {
+        gchar *name = layer_get_name (layer);
+        g_string_append_printf (markup, "<b>%s</b>: %s\n", _("Layer"), name);
+        g_free (name);
+      } else if (diagram) {
+	g_string_append_printf (markup, "%d %s", 
+			        data_layer_count (DIA_DIAGRAM_DATA(diagram)), _("Layer(s)"));
+      }
+      if (object) {
+        g_string_append_printf (markup, "<b>%s</b>: %s\n", _("Type"), object->type->name);
+        g_string_append_printf (markup, "<b>%s</b>: %g,%g\n", _("Position"), 
+			        object->position.x, object->position.y);
+	g_string_append_printf (markup, "%d %s", 
+	                        g_list_length (object->children), _("Children"));
+        /* and some dia_object_get_meta ? */
+      } else if (layer) {
+	g_string_append_printf (markup, "%d %s", 
+	                        layer_object_count (layer), _("Object(s)"));
+      }
+
+      if (markup->len > 0) {
+        gtk_tree_view_get_cell_area (GTK_TREE_VIEW (widget), path, column, &cell_area);
+
+        gtk_tree_view_convert_bin_window_to_widget_coords
+			(GTK_TREE_VIEW (widget), cell_area.x, cell_area.y,
+			 &cell_area.x, &cell_area.y);
+
+        gtk_tooltip_set_tip_area (tooltip, &cell_area);
+        gtk_tooltip_set_markup (tooltip, markup->str);
+	had_info = TRUE;
+      }
+
+      g_string_free (markup, TRUE);
+    }
+    gtk_tree_path_free (path);
+    if (had_info)
+      return TRUE;
+  }
+
+  return GTK_WIDGET_CLASS (_dtv_parent_class)->query_tooltip 
+						(widget, x, y, keyboard_mode, tooltip);
+}
+
+static void
+_dtv_row_activated (GtkTreeView       *view,
+		    GtkTreePath       *path,
+		    GtkTreeViewColumn *column)
+{
+  GtkTreeModel *model;
+  GtkTreeIter   iter;
+  DiaObject    *object;
+
+  model = gtk_tree_view_get_model (view);
+
+  if (gtk_tree_model_get_iter (model, &iter, path)) {
+    gtk_tree_model_get (model, &iter, OBJECT_COLUMN, &object, -1);
+
+    //g_signal_emit (view, signals[REVISION_ACTIVATED], 0, object);
+
+  }
+
+  if (GTK_TREE_VIEW_CLASS (_dtv_parent_class)->row_activated)
+    GTK_TREE_VIEW_CLASS (_dtv_parent_class)->row_activated (view, path, column);
+}
+
+static void
+_dtv_class_init (DiagramTreeViewClass *klass)
+{
+  GObjectClass     *object_class    = G_OBJECT_CLASS (klass);
+  GtkWidgetClass   *widget_class    = GTK_WIDGET_CLASS (klass);
+  GtkTreeViewClass *tree_view_class = GTK_TREE_VIEW_CLASS (klass);
+
+  widget_class->button_press_event  = _dtv_button_press;
+  widget_class->query_tooltip       = _dtv_query_tooltip;
+
+  tree_view_class->row_activated    = _dtv_row_activated;
+}
+
+/* given the model and the path, construct the icon */
+static void
+_dtv_cell_pixbuf_func (GtkCellLayout   *layout,
+		       GtkCellRenderer *cell,
+		       GtkTreeModel    *tree_model,
+		       GtkTreeIter     *iter,
+		       gpointer         data)
+{
+  DiaObject *object;
+  GdkPixbuf *pixbuf = NULL;
+
+  gtk_tree_model_get (tree_model, iter, OBJECT_COLUMN, &object, -1);
+  if (object) {
+    if (object->type->pixmap != NULL) {
+      if (strncmp((char *)object->type->pixmap, "GdkP", 4) == 0)
+        pixbuf = gdk_pixbuf_new_from_inline(-1, (guint8*)object->type->pixmap, TRUE, NULL);
+      else /* must be an XPM */
+        pixbuf = gdk_pixbuf_new_from_xpm_data(object->type->pixmap);
+    } else if (object->type->pixmap_file != NULL) {
+      GError *error = NULL;
+      pixbuf = gdk_pixbuf_new_from_file (object->type->pixmap_file, &error);
+      if (error) {
+        g_warning ("%s", error->message);
+        g_error_free (error);
+      }
+    } else if (IS_GROUP(object)) {
+      pixbuf = gdk_pixbuf_new_from_inline(-1, dia_group_icon, TRUE, NULL);
+    }
+  } else {
+#if 0 /* these icons are not that useful */
+    Layer *layer;
+    gtk_tree_model_get (tree_model, iter, LAYER_COLUMN, &layer, -1);
+    if (layer)
+      pixbuf = gdk_pixbuf_new_from_inline(-1, dia_layers, TRUE, NULL);
+    else /* must be diagram */
+      pixbuf = gdk_pixbuf_new_from_inline(-1, dia_diagram_icon, TRUE, NULL);
+#endif
+  }
+
+  g_object_set (cell, "pixbuf", pixbuf, NULL);
+  if (pixbuf)
+    g_object_unref (pixbuf);
+}
+
+static void
+_dtv_select_items (GtkAction *action,
+                  DiagramTreeView *dtv)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GList            *rows, *r;
+  gboolean          once = TRUE;
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dtv));
+  rows = gtk_tree_selection_get_selected_rows (selection, &model);
+  r = rows;
+  while (r) {
+    GtkTreeIter     iter;
+
+    if (gtk_tree_model_get_iter (model, &iter, r->data)) {
+      Diagram   *diagram;
+      Layer     *layer;
+      DiaObject *object;
+
+      gtk_tree_model_get (model, &iter, DIAGRAM_COLUMN, &diagram, -1);
+      gtk_tree_model_get (model, &iter, LAYER_COLUMN, &layer, -1);
+      gtk_tree_model_get (model, &iter, OBJECT_COLUMN, &object, -1);
+
+      if (once) { /* destroy previous selection in first iteration */
+	diagram_remove_all_selected(diagram, TRUE);
+	once = FALSE;
+      }
+
+      if (layer) /* fixme: layer dialog update missing */
+        data_set_active_layer (DIA_DIAGRAM_DATA(diagram), layer);
+      if (object)
+        diagram_select (diagram, object);
+      if (diagram) {
+        diagram_add_update_all (diagram);
+        diagram_flush(diagram);
+        g_object_unref (diagram);
+      }
+    }
+    r = g_list_next (r);
+  }
+  g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
+  g_list_free (rows);
+}
+static void
+_dtv_locate_item (GtkAction *action,
+                  DiagramTreeView *dtv)
+{
+  /* FIXME: implement */
+}
+static void
+_dtv_showprops_item (GtkAction *action,
+                     DiagramTreeView *dtv)
+{
+  GList *selected = NULL;
+  GtkTreeSelection *selection;
+  GtkTreeModel     *model;
+  GList            *rows, *r;
+  Diagram          *diagram = NULL;
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dtv));
+  rows = gtk_tree_selection_get_selected_rows (selection, &model);
+  r = rows;
+  while (r) {
+    GtkTreeIter     iter;
+
+    if (gtk_tree_model_get_iter (model, &iter, r->data)) {
+      DiaObject *object;
+      Diagram   *current_diagram;
+
+      gtk_tree_model_get (model, &iter, DIAGRAM_COLUMN, &current_diagram, -1);
+      gtk_tree_model_get (model, &iter, OBJECT_COLUMN, &object, -1);
+      if (object)
+        selected = g_list_append (selected, object);
+      if (!diagram) /* keep the reference */
+        diagram = current_diagram;
+      else if (diagram == current_diagram) /* still the same */
+        g_object_unref (current_diagram);
+      else {
+        g_object_unref (current_diagram);
+	break; /* can only handle one diagram's objects */
+      } 
+    }
+    r = g_list_next (r);
+  }
+
+  if (diagram && selected)
+    object_list_properties_show (diagram, selected);
+  else if (diagram)
+    diagram_properties_show(diagram);
+  if (diagram)
+    g_object_unref(diagram);
+
+  g_list_foreach (rows, (GFunc) gtk_tree_path_free, NULL);
+  g_list_free (rows);
+}
+/* create the popup menu */
+static void
+_dtv_create_popup_menu (DiagramTreeView *dtv)
+{
+  static GtkActionEntry menu_items [] = {
+    { "Select",  NULL, N_("Select"), NULL, NULL, G_CALLBACK (_dtv_select_items) },
+    { "Locate",  NULL, N_("Locate"), NULL, NULL, G_CALLBACK (_dtv_locate_item) },
+    { "Properties", NULL, N_("Properties"), NULL, NULL, G_CALLBACK(_dtv_showprops_item) }
+  };
+  static const gchar *ui_description =
+    "<ui>"
+    "  <popup name='PopupMenu'>"
+    "    <menuitem action='Select'/>"
+    "    <menuitem action='Locate'/>"
+    "    <menuitem action='Properties'/>"
+    "  </popup>"
+    "</ui>";
+  GtkActionGroup *action_group;
+
+  g_return_if_fail (dtv->popup == NULL && dtv->ui_manager == NULL);
+
+  action_group = gtk_action_group_new ("PopupActions");
+  gtk_action_group_set_translation_domain (action_group, NULL); /* FIXME? */
+  gtk_action_group_add_actions (action_group, menu_items,
+				G_N_ELEMENTS (menu_items), dtv);
+
+  dtv->ui_manager = gtk_ui_manager_new ();
+  gtk_ui_manager_insert_action_group (dtv->ui_manager, action_group, 0);
+  if (gtk_ui_manager_add_ui_from_string (dtv->ui_manager, ui_description, -1, NULL))
+    dtv->popup = GTK_MENU(gtk_ui_manager_get_widget (dtv->ui_manager, "/ui/PopupMenu"));
+}
+
+static void
+_dtv_init (DiagramTreeView *dtv)
+{
+  GtkTreeViewColumn *column;
+  GtkCellRenderer   *cell;
+  gint               font_size;
+
+  /* connect the model with the view */
+  gtk_tree_view_set_model (GTK_TREE_VIEW (dtv), diagram_tree_model_new ());
+
+  gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (dtv), TRUE);
+  /* the tree requires reading across rows (semantic hint) */
+#if 0 /* stripe style */
+  gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (dtv), TRUE);
+#endif
+
+  font_size = pango_font_description_get_size (GTK_WIDGET (dtv)->style->font_desc);
+  font_size = PANGO_PIXELS (font_size);
+
+  /* first colum: name of diagram/layer/object - here is the tree */
+  cell = gtk_cell_renderer_text_new ();
+  gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (cell), 1);
+  g_object_set (cell, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Name"));
+  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
+  gtk_tree_view_column_set_min_width (column, font_size * 10);
+  gtk_tree_view_column_set_expand (column, TRUE);
+  gtk_tree_view_column_set_resizable (column, TRUE);
+  
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), cell, TRUE);
+  gtk_tree_view_column_add_attribute (column, cell, "text", NAME_COLUMN);
+  gtk_tree_view_insert_column (GTK_TREE_VIEW (dtv), column, -1);
+
+  /* this is enough for simple name search (just typing) */
+  gtk_tree_view_set_search_column (GTK_TREE_VIEW (dtv), NAME_COLUMN);
+
+  /* type column - show the type icon */
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Type"));
+  /* must have fixed size, too */
+  gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED);
+  /* without it gets zero size - not very useful! */
+  gtk_tree_view_column_set_min_width (column, font_size * 4);
+  gtk_tree_view_column_set_resizable (column, TRUE);
+  cell = gtk_cell_renderer_pixbuf_new ();
+  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), cell, TRUE);
+  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), cell,
+				      _dtv_cell_pixbuf_func, dtv, NULL);
+  gtk_tree_view_insert_column (GTK_TREE_VIEW (dtv), column, -1);
+
+  /*  TODO: other fancy stuff */
+
+  {
+    GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dtv));
+
+    gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
+    gtk_tree_view_set_rubber_banding (GTK_TREE_VIEW (dtv), TRUE);
+  }
+  gtk_widget_set_has_tooltip (GTK_WIDGET (dtv), TRUE);
+
+
+  _dtv_create_popup_menu (dtv);
+}
+
+GtkWidget *
+diagram_tree_view_new (void)
+{
+  return g_object_new (DIAGRAM_TREE_VIEW_TYPE, NULL);
+}
+
+/* should go to it's own file, just for testing */
+void
+diagram_tree_show (void)
+{
+  static GtkWidget *window = NULL;
+
+  if (!window) {
+    GtkWidget *sw;
+    GtkWidget *dtv;
+
+    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title (GTK_WINDOW (window), _("Diagram Tree"));
+
+    sw = gtk_scrolled_window_new (NULL, NULL);
+    gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN);
+    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+    gtk_container_add (GTK_CONTAINER (window), sw);
+    gtk_window_set_default_size (GTK_WINDOW (window), 300, 600);
+
+    dtv = diagram_tree_view_new ();
+    gtk_container_add (GTK_CONTAINER (sw), dtv);
+    /* expand all rows after the treeview widget has been realized */
+    g_signal_connect (dtv, "realize",
+		      G_CALLBACK (gtk_tree_view_expand_all), NULL);
+
+    if (!GTK_WIDGET_VISIBLE (window))
+      gtk_widget_show_all (window);
+  }
+  gtk_window_present (GTK_WINDOW(window));
+}
diff --git a/app/makefile.msc b/app/makefile.msc
index ca6b773..1c9fa77 100644
--- a/app/makefile.msc
+++ b/app/makefile.msc
@@ -74,7 +74,10 @@ OBJECTS = \
 	textedit.obj \
 	textedit_tool.obj \
 	tool.obj \
-	undo.obj
+	undo.obj \
+	\
+	diagram_tree_model.obj \
+	diagram_tree_view.obj \
 
 ICON_PNG_PAIRS = \
 	dia_connectable_icon pixmaps\connectable.png \
diff --git a/app/menus.c b/app/menus.c
index d884f80..81bd30b 100644
--- a/app/menus.c
+++ b/app/menus.c
@@ -94,10 +94,12 @@ static const GtkActionEntry toolbox_entries[] =
     { "FilePlugins", NULL, N_("Plugins..."), NULL, NULL, G_CALLBACK (file_plugins_callback) }
 };
 
+extern void diagram_tree_show(void);
 /* Toggle-Actions for toolbox menu */
 static const GtkToggleActionEntry toolbox_toggle_entries[] = 
 {
-    { "FileTree", NULL, N_("_Diagram tree..."), "F8", NULL, G_CALLBACK (diagtree_show_callback) }
+    { "FileTree", NULL, N_("_Diagram tree..."), "F8", NULL, G_CALLBACK (diagtree_show_callback) },
+    { "DiagramTree", NULL, N_("_Diagram tree"), NULL, NULL, G_CALLBACK (diagram_tree_show) }
 };
 
 /* Toggle-Actions for toolbox menu */



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