RFC: GbfProjectView widget



Hi all,

The attached files are the implementation of a new widget for the
gnome-build module.  It's based on the ideas and work started by Dave
some time ago.

The widget, GbfProjectView, is intended to replace the current
GbfProjectTree and GbfTargetTree widgets.  In contrast with these two,
it presents a model of the targets in a gnome-build project (GbfProject)
in a hierarchical way.  This is, it has the same information as the
GbfTargetTree (targets and its sources), but they are arranged in a tree
(resembling the structure of the files in the filesystem).

Since the changes introduced by these patches are quite important, I'm
asking for the general opinion of the people in the list, and the
maintainers in particular, before committing, since the old widgets will
be replaced.

The reason for the new approach is two-fold:

1) The actual implementation of the automake backend generates targets
for all files in the project directory.  That is, not only programs and
libraries, but any file that participates in the package (scripts, built
sources, installed data files, etc.).  This behavior bloats the
GbfTargetTree, which was originally meant to be a quick list to access
the important files.  The new widget overcomes this difficulties by: a)
presenting the targets organized hierarchically, and b) by providing a
configurable shortcut area (before the normal tree of targets), where
the user can drag the targets she needs quick access to.

2) The new widget combines the good aspects of both the old widgets: the
GbfTargetTree provides visual information about how the files are
grouped to generate a given target, but since it's a non-organized flat
list, it becomes useless (a medium sized project can easily have more
than 20 targets); the GbfProjectTree presents files in an organized way,
but gives no information on what files generate which target (i.e.
program or library) and hence there's no easy way to modify such
relationship.

Besides these two practical (usability?) reasons, the new widget
actually works better than the old ones:

- Model updating is incremental (this could be done in the old widgets
too, but currently when they receive the "project-updated" signal, the
clear the model and re-read it completely)
- Add and remove source work in the patched project manager for anjuta2
(which of course can be done for the other widgets :-)
- Drag and drop interfaces are already implemented, and it should be
easy to extend them to, for example, add a source by dragging a file
from a nautilus window

So, what do you people think?  I think it's a much better approach at
presenting the project to the user and interacting with it, but someone
can prove me wrong, and I'm open to new ideas and suggestions.  If
people agree and once the maintainers give me green light to commit,
I'll continue to add functionality to the project manager.

Thanks,
Gustavo

? .tm_project.cache
? autom4te.cache
? stamp-h1
? src/controls/gbf-project-model.c
? src/controls/gbf-project-model.h
? src/controls/gbf-project-view.c
? src/controls/gbf-project-view.h
? src/controls/test-project-view
? src/controls/test-project-view.c
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/gnome-build/ChangeLog,v
retrieving revision 1.118
diff -u -r1.118 ChangeLog
--- ChangeLog	2 Jan 2003 22:38:22 -0000	1.118
+++ ChangeLog	6 Jan 2003 02:22:49 -0000
@@ -1,3 +1,27 @@
+2003-01-05  Gustavo Giraldez  <gustavo giraldez gmx net>
+
+	* src/backends/libgbf_am/gbf-am-project.c (uri_ensure_local),
+	(write_source_uri): Use new function uri_ensure_local to convert
+	full URIs (i.e. file:///...) to a local path.
+
+	* src/controls/default-icon.[ch]:
+	* src/controls/gbf-project-tree.[ch]:
+	* src/controls/gbf-target-tree.[ch]:
+	* src/controls/test-controls.c:
+	* src/controls/Makefile.am: Removed files obsoleted by
+	GbfProjectModel/View.
+
+	* src/controls/test-project-view.c: Test program for the widget.
+	* src/controls/gbf-project-model.[ch]: 
+	* src/controls/gbf-project-view.[ch]: New GbfProjectModel/View
+	widget to display gnome-build projects.
+	
+	* src/controls/gbf-tree-data.[ch]: Changed to the new model needs.
+
+	* src/lib/gbf-project.[ch]: GbfProjectGroup, GbfProjectTarget and
+	GbfProjectTargetSource are now GBoxed.  New copying and freeing
+	functions for these types.
+	
 2003-01-02  Gustavo Giraldez  <gustavo giraldez gmx net>
 
 	* src/controls/gbf-target-tree.c: 
Index: src/backends/libgbf_am/gbf-am-project.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/backends/libgbf_am/gbf-am-project.c,v
retrieving revision 1.34
diff -u -r1.34 gbf-am-project.c
--- src/backends/libgbf_am/gbf-am-project.c	2 Jan 2003 22:38:24 -0000	1.34
+++ src/backends/libgbf_am/gbf-am-project.c	6 Jan 2003 02:22:52 -0000
@@ -293,7 +293,8 @@
 /**
  * chroot_path:
  * @root: root path
- * @path: the path which must be inside @root for which the root-changed path is wanted
+ * @path: the path which must be inside @root for which the
+ * root-changed path is wanted
  * 
  * If @root is the parent of @path, returns an absolute path as if the
  * root of the hierarchy was @root.
@@ -320,7 +321,31 @@
 
 	return path + root_length;
 }
-	     
+
+/**
+ * uri_ensure_local:
+ * @uri: a string containing a URI
+ * 
+ * Ensure the given URI is on the local filesystem and strip the
+ * method part (i.e. file:///)
+ * 
+ * Return value: A newly allocated string with the local path, or NULL
+ * if the given @uri is not local
+ **/
+static gchar *
+uri_ensure_local (const gchar *uri)
+{
+	GnomeVFSURI *tmp_uri;
+	gchar *retval = NULL;
+	
+	tmp_uri = gnome_vfs_uri_new (uri);
+	if (gnome_vfs_uri_is_local (tmp_uri))
+		retval = gnome_vfs_uri_to_string (tmp_uri, GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD);
+	gnome_vfs_uri_unref (tmp_uri);
+
+	return retval;
+}
+
 static xmlNodePtr 
 write_source_uri (GbfAmProject *prj,
 		  xmlDocPtr     doc,
@@ -329,15 +354,20 @@
 {
 	xmlNodePtr src;
 	const gchar *filename;
+	gchar *local_uri;
 
+	local_uri = uri_ensure_local (uri);
+	if (!local_uri)
+		return NULL;
+	
 	src = xmlNewDocNode (doc, NULL, "source", NULL);
 	g_assert (prj->priv->project_dir);
 
-	/* FIXME: make this work with generalized URIs */
-	filename = chroot_path (prj->priv->project_dir, uri);
+	filename = chroot_path (prj->priv->project_dir, local_uri);
 	xmlSetProp (src, "uri", filename);
 	xmlAddChild (node, src);
-
+	g_free (local_uri);
+	
 	return src;
 }
 
Index: src/controls/Makefile.am
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/Makefile.am,v
retrieving revision 1.27
diff -u -r1.27 Makefile.am
--- src/controls/Makefile.am	29 Dec 2002 19:45:08 -0000	1.27
+++ src/controls/Makefile.am	6 Jan 2003 02:22:53 -0000
@@ -19,31 +19,34 @@
 libgbf_widgets_1_la_SOURCES =	\
 	libgbfmarshal.h		\
 	libgbfmarshal.c		\
-	default-icon.c          \
-	default-icon.h          \
-	gbf-project-tree.c	\
-	gbf-target-tree.c  	\
+	gbf-tree-data.h		\
 	gbf-tree-data.c		\
-	gbf-build-info.c
+	gbf-build-info.h	\
+	gbf-build-info.c	\
+	gbf-project-model.h	\
+	gbf-project-model.c	\
+	gbf-project-view.h	\
+	gbf-project-view.c
 
 libgbfincludedir = $(includedir)/gnome-build-1.0/gbf
 libgbfinclude_HEADERS = 		\
-	gbf-project-tree.h 		\
-	gbf-target-tree.h 		\
 	gbf-tree-data.h			\
 	gbf-build-info.h		\
-	gbf-widgets.h
+	gbf-widgets.h			\
+	gbf-project-model.h		\
+	gbf-project-view.h
+
 
 libgbf_widgets_1_la_LIBADD = 			\
 	$(top_builddir)/src/lib/libgbf-1.la	\
 	$(GBF_CONTROL_LIBS)
 
-noinst_PROGRAMS = test-controls
+noinst_PROGRAMS = test-project-view
 
-test_controls_SOURCES = 	\
-	test-controls.c	
+test_project_view_SOURCES = 	\
+	test-project-view.c
 
-test_controls_LDADD = 				\
+test_project_view_LDADD = 			\
 	$(top_builddir)/src/lib/libgbf-1.la	\
 	libgbf-widgets-1.la			\
 	$(GBF_CONTROL_LIBS)
Index: src/controls/gbf-tree-data.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/gbf-tree-data.c,v
retrieving revision 1.2
diff -u -r1.2 gbf-tree-data.c
--- src/controls/gbf-tree-data.c	23 Feb 2002 05:40:23 -0000	1.2
+++ src/controls/gbf-tree-data.c	6 Jan 2003 02:22:53 -0000
@@ -21,7 +21,9 @@
  * Author: JP Rosevear
  */
 
+#include <libgnomevfs/gnome-vfs-uri.h>
 #include "gbf-tree-data.h"
+#include "gbf-project.h"
 
 GType
 gbf_tree_data_get_type (void)
@@ -39,11 +41,43 @@
 GbfTreeData *
 gbf_tree_data_new (GbfTreeNodeType type, gpointer data) 
 {
+	GbfProjectGroup *group;
+	GbfProjectTarget *target;
+	GbfProjectTargetSource *source;
 	GbfTreeData *node = g_new0 (GbfTreeData, 1);
-
+	GnomeVFSURI *uri;
+	
 	node->type = type;
-	node->data = data;
-
+	switch (type) {
+		case GBF_TREE_NODE_STRING:
+			node->name = g_strdup (data);
+			break;
+
+		case GBF_TREE_NODE_GROUP:
+			group = data;
+			node->name = g_strdup (group->name);
+			node->id = g_strdup (group->id);
+			break;
+
+		case GBF_TREE_NODE_TARGET:
+			target = data;
+			node->name = g_strdup (target->name);
+			node->id = g_strdup (target->id);
+			node->mime_type = g_strdup (target->type);
+			break;
+
+		case GBF_TREE_NODE_TARGET_SOURCE:
+			source = data;
+			node->id = g_strdup (source->id);
+			node->uri = g_strdup (source->source_uri);
+
+			uri = gnome_vfs_uri_new (source->source_uri);
+			node->name = gnome_vfs_uri_extract_short_name (uri);
+			gnome_vfs_uri_unref (uri);
+			
+			break;
+	}
+	
 	return node;
 }
 
@@ -54,14 +88,23 @@
 
 	node = g_new (GbfTreeData, 1);
 	node->type = src->type;
-	node->data = src->data;
-  
+	node->name = g_strdup (src->name);
+	node->id = g_strdup (src->id);
+	node->uri = g_strdup (src->uri);
+	node->is_shortcut = src->is_shortcut;
+	node->mime_type = g_strdup (src->mime_type);
+	
 	return node;
 }
 
 void
 gbf_tree_data_free (GbfTreeData *node)
 {
-	//g_free (node->data);
-	g_free (node);
+	if (node) {
+		g_free (node->name);
+		g_free (node->id);
+		g_free (node->uri);
+		g_free (node->mime_type);
+		g_free (node);
+	}
 }
Index: src/controls/gbf-tree-data.h
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/gbf-tree-data.h,v
retrieving revision 1.3
diff -u -r1.3 gbf-tree-data.h
--- src/controls/gbf-tree-data.h	9 Apr 2002 21:42:29 -0000	1.3
+++ src/controls/gbf-tree-data.h	6 Jan 2003 02:22:53 -0000
@@ -33,16 +33,20 @@
 typedef struct _GbfTreeData GbfTreeData;
 
 typedef enum {
-	GBF_TREE_NODE_FOLDER,
+	GBF_TREE_NODE_STRING,
+	GBF_TREE_NODE_GROUP,
 	GBF_TREE_NODE_TARGET,
-	GBF_TREE_NODE_FILE,
-	GBF_TREE_NODE_STRING
+	GBF_TREE_NODE_TARGET_SOURCE,
 } GbfTreeNodeType;
 
 struct _GbfTreeData
 {
-	GbfTreeNodeType type;
-	gpointer data;
+	GbfTreeNodeType  type;
+	gchar           *name;
+	gchar           *id;
+	gchar           *uri;
+	gboolean         is_shortcut;
+	gchar           *mime_type;
 };
 
 GType        gbf_tree_data_get_type (void);
Index: src/controls/gbf-widgets.h
===================================================================
RCS file: /cvs/gnome/gnome-build/src/controls/gbf-widgets.h,v
retrieving revision 1.3
diff -u -r1.3 gbf-widgets.h
--- src/controls/gbf-widgets.h	23 Feb 2002 05:40:23 -0000	1.3
+++ src/controls/gbf-widgets.h	6 Jan 2003 02:22:53 -0000
@@ -24,7 +24,9 @@
 #ifndef __GBF_WIDGETS_H__
 #define __GBF_WIDGETS_H__
 
-#include "gbf-project-tree.h"
-#include "gbf-target-tree.h"
+#include <gbf/gbf-tree-data.h>
+#include <gbf/gbf-project-model.h>
+#include <gbf/gbf-project-view.h>
+#include <gbf/gbf-build-info.h>
 
 #endif
Index: src/lib/gbf-project.c
===================================================================
RCS file: /cvs/gnome/gnome-build/src/lib/gbf-project.c,v
retrieving revision 1.7
diff -u -r1.7 gbf-project.c
--- src/lib/gbf-project.c	28 Sep 2002 12:08:54 -0000	1.7
+++ src/lib/gbf-project.c	6 Jan 2003 02:22:54 -0000
@@ -392,3 +392,140 @@
 
 	return quark;
 }
+
+GbfProjectGroup *
+gbf_project_group_copy (GbfProjectGroup *group)
+{
+	GbfProjectGroup *new_group;
+	GList *l;
+	
+	new_group = g_new0 (GbfProjectGroup, 1);
+	new_group->id = g_strdup (group->id);
+	new_group->parent_id = g_strdup (group->parent_id);
+	new_group->name = g_strdup (group->name);
+	
+	for (l = group->groups; l; l = l->next)
+		new_group->groups = g_list_prepend (new_group->groups, g_strdup (l->data));
+	new_group->groups = g_list_reverse (new_group->groups);
+	
+	for (l = group->targets; l; l = l->next)
+		new_group->targets = g_list_prepend (new_group->targets, g_strdup (l->data));
+	new_group->targets = g_list_reverse (new_group->targets);
+
+	return new_group;
+}
+
+void
+gbf_project_group_free (GbfProjectGroup *group)
+{
+	g_free (group->id);
+	g_free (group->parent_id);
+	g_free (group->name);
+
+	while (group->groups) {
+		g_free (group->groups->data);
+		group->groups = g_list_delete_link (group->groups, group->groups);
+	}
+	
+	while (group->targets) {
+		g_free (group->targets->data);
+		group->targets = g_list_delete_link (group->targets, group->targets);
+	}
+	g_free (group);
+}
+
+GbfProjectTarget *
+gbf_project_target_copy (GbfProjectTarget *target)
+{
+	GbfProjectTarget *new_target;
+	GList *l;
+	
+	new_target = g_new0 (GbfProjectTarget, 1);
+	new_target->id = g_strdup (target->id);
+	new_target->group_id = g_strdup (target->group_id);
+	new_target->name = g_strdup (target->name);
+	new_target->type = g_strdup (target->type);
+	
+	for (l = target->sources; l; l = l->next)
+		new_target->sources = g_list_prepend (new_target->sources, g_strdup (l->data));
+	new_target->sources = g_list_reverse (new_target->sources);
+
+	return new_target;
+}
+
+void
+gbf_project_target_free (GbfProjectTarget *target)
+{
+	g_free (target->id);
+	g_free (target->group_id);
+	g_free (target->name);
+	g_free (target->type);
+
+	while (target->sources) {
+		g_free (target->sources->data);
+		target->sources = g_list_delete_link (target->sources, target->sources);
+	}
+
+	g_free (target);
+}
+
+GbfProjectTargetSource *
+gbf_project_target_source_copy (GbfProjectTargetSource *source)
+{
+	GbfProjectTargetSource *new_source;
+	
+	new_source = g_new0 (GbfProjectTargetSource, 1);
+	new_source->id = g_strdup (source->id);
+	new_source->target_id = g_strdup (source->target_id);
+	new_source->source_uri = g_strdup (source->source_uri);
+	
+	return new_source;
+}
+
+void
+gbf_project_target_source_free (GbfProjectTargetSource *source)
+{
+	g_free (source->id);
+	g_free (source->target_id);
+	g_free (source->source_uri);
+	g_free (source);
+}
+
+GType 
+gbf_project_group_get_type (void)
+{
+	static GType our_type = 0;
+  
+	if (our_type == 0)
+		our_type = g_boxed_type_register_static ("GbfProjectGroup",
+							 (GBoxedCopyFunc) gbf_project_group_copy,
+							 (GBoxedFreeFunc) gbf_project_group_free);
+	
+	return our_type;
+}
+
+GType 
+gbf_project_target_get_type (void)
+{
+	static GType our_type = 0;
+  
+	if (our_type == 0)
+		our_type = g_boxed_type_register_static ("GbfProjectTarget",
+							 (GBoxedCopyFunc) gbf_project_target_copy,
+							 (GBoxedFreeFunc) gbf_project_target_free);
+	
+	return our_type;
+}
+
+GType 
+gbf_project_target_source_get_type (void)
+{
+	static GType our_type = 0;
+  
+	if (our_type == 0)
+		our_type = g_boxed_type_register_static ("GbfProjectTargetSource",
+							 (GBoxedCopyFunc) gbf_project_target_source_copy,
+							 (GBoxedFreeFunc) gbf_project_target_source_free);
+	
+	return our_type;
+}
Index: src/lib/gbf-project.h
===================================================================
RCS file: /cvs/gnome/gnome-build/src/lib/gbf-project.h,v
retrieving revision 1.6
diff -u -r1.6 gbf-project.h
--- src/lib/gbf-project.h	28 Sep 2002 12:08:54 -0000	1.6
+++ src/lib/gbf-project.h	6 Jan 2003 02:22:54 -0000
@@ -34,6 +34,10 @@
 
 #define GBF_PROJECT_ERROR		(gbf_project_error_quark ())
 
+#define GBF_TYPE_PROJECT_GROUP          (gbf_project_group_get_type ())
+#define GBF_TYPE_PROJECT_TARGET         (gbf_project_target_get_type ())
+#define GBF_TYPE_PROJECT_TARGET_SOURCE  (gbf_project_target_source_get_type ())
+
 typedef struct _GbfProject		GbfProject;
 typedef struct _GbfProjectClass		GbfProjectClass;
 typedef struct _GbfProjectGroup		GbfProjectGroup;
@@ -208,6 +212,9 @@
 
 GQuark                  gbf_project_error_quark          (void);
 GType                   gbf_project_get_type             (void);
+GType                   gbf_project_group_get_type       (void);
+GType                   gbf_project_target_get_type      (void);
+GType                   gbf_project_target_source_get_type (void);
 void                    gbf_project_load                 (GbfProject    *project,
 							  const char    *path,
 							  GError       **error);
@@ -288,6 +295,18 @@
 							  const char    *type);
 GdkPixbuf *             gbf_project_icon_for_type        (GbfProject    *project,
 							  const char    *type);
+
+/* functions for copying/freeing data structures */
+
+GbfProjectGroup        *gbf_project_group_copy         (GbfProjectGroup        *group);
+void                    gbf_project_group_free         (GbfProjectGroup        *group);
+
+GbfProjectTarget       *gbf_project_target_copy        (GbfProjectTarget       *target);
+void                    gbf_project_target_free        (GbfProjectTarget       *target);
+
+GbfProjectTargetSource *gbf_project_target_source_copy (GbfProjectTargetSource *source);
+void                    gbf_project_target_source_free (GbfProjectTargetSource *source);
+
 
 
 
Index: ChangeLog
===================================================================
RCS file: /cvs/gnome/anjuta2/ChangeLog,v
retrieving revision 1.240
diff -u -r1.240 ChangeLog
--- ChangeLog	30 Dec 2002 11:02:00 -0000	1.240
+++ ChangeLog	6 Jan 2003 02:25:17 -0000
@@ -1,3 +1,11 @@
+2003-01-05  Gustavo Giraldez  <gustavo giraldez gmx net>
+
+	* plugins/project-manager/project-tool.c (set_build),
+	(get_selected_target_node), (project_add_source),
+	(project_remove_source), (init_project_view), (shell_set):
+	Replaced GbfProjectTree and GbfTargetTree by GbfProjectModel/View.
+	Implemented remove_source.
+
 2002-12-30  Jeroen Zwartepoorte  <jeroen xs4all nl>
 
 	* plugins/gnome-project-types/anjuta-gnome-application-create.py: Fix
Index: plugins/project-manager/project-tool.c
===================================================================
RCS file: /cvs/gnome/anjuta2/plugins/project-manager/project-tool.c,v
retrieving revision 1.36
diff -u -r1.36 project-tool.c
--- plugins/project-manager/project-tool.c	29 Dec 2002 19:17:05 -0000	1.36
+++ plugins/project-manager/project-tool.c	6 Jan 2003 02:25:18 -0000
@@ -25,8 +25,9 @@
 #include <gconf/gconf-client.h>
 #include <gdl/gdl.h>
 #include <gbf/gbf-backend.h>
-#include <gbf/gbf-project-tree.h>
-#include <gbf/gbf-target-tree.h>
+#include <gbf/gbf-project-model.h>
+#include <gbf/gbf-project-view.h>
+#include <gbf/gbf-tree-data.h>
 #include <gbf/gbf-build-info.h>
 #include <libanjuta/libanjuta.h>
 #include <libgnomevfs/gnome-vfs.h>
@@ -40,13 +41,15 @@
 
 	AnjutaProjectManager *manager;
 
-	GtkWidget *file_tree;
-	GtkWidget *target_tree;
+	GtkWidget *project_view;
+	GbfProjectModel *project_model;
+
 	GtkWidget *build_info;
 
 	GbfProject *project;
 	GbfProjectTarget *current_target;
-
+	gchar *project_root;
+	
 	GdlRecent *recent;
 } ProjectTool;
 
@@ -79,6 +82,7 @@
 					   "CurrentTarget",
 					   NULL);
 
+		gbf_project_target_free (tool->current_target);
 		tool->current_target = NULL;
 	}
 
@@ -139,6 +143,8 @@
 	g_return_if_fail (tool != NULL);
 	g_return_if_fail (ANJUTA_IS_TOOL (tool));
 
+	set_current_target (proj_tool, NULL);
+		
 	/* Close any open project first. */
 	if (proj_tool->project) {
 		anjuta_shell_remove_value (tool->shell, 
@@ -146,6 +152,9 @@
 					   NULL);
 		g_object_unref (proj_tool->project);
 		proj_tool->project = NULL;
+
+		g_free (proj_tool->project_root);
+		proj_tool->project_root = NULL;
 	}
 
 	if (path != NULL) {
@@ -195,16 +204,17 @@
 				  NULL);
 
 		proj_tool->project = project;
-	}
+		/* save root for future reference */
+		proj_tool->project_root = g_strdup (path);
 
-	/* Connect to build signals. */
-	g_signal_connect (G_OBJECT (project), "build_start",
-			  G_CALLBACK (build_start_cb), proj_tool);
-	g_signal_connect (G_OBJECT (project), "build_stop",
-			  G_CALLBACK (build_stop_cb), proj_tool);
+		/* Connect to build signals. */
+		g_signal_connect (G_OBJECT (project), "build_start",
+				  G_CALLBACK (build_start_cb), proj_tool);
+		g_signal_connect (G_OBJECT (project), "build_stop",
+				  G_CALLBACK (build_stop_cb), proj_tool);
+	}
 
-	g_object_set (G_OBJECT (proj_tool->file_tree), "project", project, NULL);
-	g_object_set (G_OBJECT (proj_tool->target_tree), "project", project, NULL);
+	g_object_set (G_OBJECT (proj_tool->project_model), "project", project, NULL);
 	g_object_set (G_OBJECT (proj_tool->build_info), "project", project, NULL);
 
 	/* Update GUI. */
@@ -323,74 +333,139 @@
 	set_build ((ProjectTool*)tool, NULL);
 }
 
+static GbfTreeData *
+get_selected_target_node (GtkTreeView *view)
+{
+	GbfTreeData *data = NULL;
+	GtkTreeSelection *selection;
+	GtkTreeModel *model;
+	GtkTreeIter iter, iter2;
+
+	g_return_val_if_fail (GBF_IS_PROJECT_VIEW (view), NULL);
+	
+	selection = gtk_tree_view_get_selection (view);
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gtk_tree_model_get (model, &iter,
+				    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
+				    -1);
+		if (data->type != GBF_TREE_NODE_TARGET) {
+			if (data->type == GBF_TREE_NODE_TARGET_SOURCE &&
+			    gtk_tree_model_iter_parent (model, &iter2, &iter)) {
+				gbf_tree_data_free (data);
+				gtk_tree_model_get (model, &iter2,
+						    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
+						    -1);
+			} else {
+				gbf_tree_data_free (data);
+				data = NULL;
+			}
+		}
+	}
+
+	return data;
+}
+
+static GtkWindow *
+try_get_toplevel_widget (GtkWidget *widget)
+{
+	GtkWidget *toplevel;
+
+	if (!widget)
+		return NULL;
+
+	toplevel = gtk_widget_get_toplevel (widget);
+	if (GTK_WIDGET_TOPLEVEL (toplevel))
+		return GTK_WINDOW (toplevel);
+
+	return NULL;
+}
+	
 static void
-project_add_source (GtkWidget *widget, gpointer data)
+project_add_source (GtkWidget *widget, gpointer user_data)
 {
-	AnjutaTool *tool = data;
-	ProjectTool *proj_tool = (ProjectTool*) data;
+	AnjutaTool *tool = user_data;
+	ProjectTool *proj_tool = user_data;
 	GbfProject *project = proj_tool->project;
-	char *uri;
+	gchar *uri, *selected_uri;
+	gchar *title;
 	const char *source;
 	GError *err = NULL;
+	GbfTreeData *data;
 
+	data = get_selected_target_node (GTK_TREE_VIEW (proj_tool->project_view));
+	if (!data) {
+		anjuta_dialog_error (_("No target has been selected"));
+		return;
+	}
+	
+	title = g_strdup_printf (_("Add source to target %s"), data->name);
 	uri = anjuta_get_current_uri (tool);
-	g_print ("Adding: %s\n", uri);
-
-	if (uri != NULL) {
-		source = gbf_project_add_source (project,
-						 proj_tool->current_target->id,
-						 uri,
-						 &err);
+	if (uri) {
+		GnomeVFSURI *tmp_uri;
+		
+		tmp_uri = gnome_vfs_uri_new (uri);
+		g_free (uri);
+		uri = gnome_vfs_uri_extract_dirname (tmp_uri);
+		gnome_vfs_uri_unref (tmp_uri);
+	}
+	selected_uri = bonobo_file_selector_open (try_get_toplevel_widget (proj_tool->project_view),
+						  FALSE, title, NULL,
+						  uri ? uri : proj_tool->project_root);
+	g_free (title);
+	g_free (uri);
+	
+	if (selected_uri != NULL) {
+		source = gbf_project_add_source (project, data->id, selected_uri, &err);
 		if (err) {
-			g_print ("error adding source: %s\n", uri);
+			gchar *msg;
+			msg = g_strdup_printf ("Error adding source: %s", err->message);
+			anjuta_dialog_error (msg);
+			g_free (msg);
 			g_error_free (err);
 		}
 	}
 
-	g_free (uri);
+	g_free (selected_uri);
+	gbf_tree_data_free (data);
 }
 
 static void
-project_remove_source (GtkWidget *widget, gpointer data) 
+project_remove_source (GtkWidget *widget, gpointer user_data) 
 {
-#if 0
-	AnjutaTool *tool = ANJUTA_TOOL (data);
-	GbfProjectClient *prj = g_object_get_data (G_OBJECT (tool), "project");
-	GbfProjectClientResult res;
-	char *uri;
-	
-	uri = anjuta_get_current_filename (tool);
-	g_print ("Removing: %s\n", uri);
+	ProjectTool *tool = (ProjectTool *) user_data;
+	GbfTreeData *data;
+	GtkTreeSelection *selection;
+	GtkTreeModel *model;
+	GtkTreeIter iter;
 	
-	if (uri != NULL) {
-		GBF_TargetList *tl;
-		GBF_Source source;
-
-		source.uri = CORBA_string_dup (uri);
-
-		res = gbf_project_client_get_targets_of_source (prj, &source, &tl);
-		if (res != GBF_PROJECT_CLIENT_OK) {
-			anjuta_dialog_error ("Source is not in the project");			
-			return;
-		}
-		
-		switch (tl->_length) {
-		case 0:
-			anjuta_dialog_error ("Source is not in the project");
-			break;
-		case 1:
-			res = gbf_project_client_remove_target_source (prj, &tl->_buffer[0], &source);
-			if (res != GBF_PROJECT_CLIENT_OK)
-				anjuta_dialog_error ("Unable to remove source.");
-			break;
-		default:
-			anjuta_dialog_error ("Source is in more than one target and"
-					     "this is not currently handled.");
+	/* examine the project view to get the currently selected source */
+	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (tool->project_view));
+	if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+		gtk_tree_model_get (model, &iter,
+				    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
+				    -1);
+		if (data->type == GBF_TREE_NODE_TARGET_SOURCE) {
+			GError *err = NULL;
+			
+			gbf_project_remove_source (tool->project, data->id, &err);
+			if (err) {
+				gchar *msg;
+				msg = g_strdup_printf ("Error removing source: %s", err->message);
+				anjuta_dialog_error (msg);
+				g_free (msg);
+				g_error_free (err);
+			}
+			
+		} else {
+			anjuta_dialog_error ("No source file is selected");
+			
 		}
-	}
+		gbf_tree_data_free (data);
 
-	g_free (uri);
-#endif
+	} else {
+		anjuta_dialog_error ("Nothing selected");
+
+	}
 }
 
 static void
@@ -474,30 +549,11 @@
 };
 
 static void
-file_selected_cb (GtkWidget  *widget,
-		  const char *filename,
+uri_activated_cb (GtkWidget  *widget,
+		  const char *uri,
 		  gpointer    user_data)
 {
-	open_file (user_data, filename, 0);
-}
-
-static void
-init_project_tree (AnjutaTool *tool)
-{
-	ProjectTool *proj_tool = (ProjectTool *)tool;
-
-	proj_tool->file_tree = gbf_project_tree_new ();
-	gtk_widget_show (proj_tool->file_tree);
-
-	anjuta_shell_add_widget (tool->shell, proj_tool->file_tree,
-				 "ProjectFileTree",
-				 _("Files"),
-				 NULL);
-
-	g_signal_connect (G_OBJECT (proj_tool->file_tree),
-			  "file_selected",
-			  G_CALLBACK (file_selected_cb),
-			  tool);
+	open_file (user_data, uri, 0);
 }
 
 static void
@@ -521,27 +577,34 @@
 }
 
 static void
-init_target_tree (AnjutaTool *tool)
+init_project_view (AnjutaTool *tool)
 {
+	GtkWidget *scrolled_window;
 	ProjectTool *proj_tool = (ProjectTool *)tool;
 
-	proj_tool->target_tree = gbf_target_tree_new ();
-	gtk_widget_show (proj_tool->target_tree);
+	proj_tool->project_model = gbf_project_model_new ();
+	proj_tool->project_view = gbf_project_view_new ();
+	gtk_tree_view_set_model (GTK_TREE_VIEW (proj_tool->project_view),
+				 GTK_TREE_MODEL (proj_tool->project_model));
+	gtk_widget_show (proj_tool->project_view);
+
+        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+                                        GTK_POLICY_AUTOMATIC,
+                                        GTK_POLICY_AUTOMATIC);
+        gtk_container_add (GTK_CONTAINER (scrolled_window), proj_tool->project_view);
+        gtk_widget_show (scrolled_window);
+
+	g_signal_connect (proj_tool->project_view, "uri_activated",
+			  G_CALLBACK (uri_activated_cb), tool);
+	g_signal_connect (proj_tool->project_view, "target_selected",
+			  G_CALLBACK (target_selected_cb), tool);
 
 	anjuta_shell_add_widget (tool->shell,
-				 proj_tool->target_tree,
-				 "ProjectTargetTree",
-				 _("Targets"),
+				 scrolled_window,
+				 "ProjectView",
+				 _("Project"),
 				 NULL);
-
-	g_signal_connect (G_OBJECT (proj_tool->target_tree),
-			  "file_selected",
-			  G_CALLBACK (file_selected_cb),
-			  tool);
-	g_signal_connect (G_OBJECT (proj_tool->target_tree),
-			  "target_selected",
-			  G_CALLBACK (target_selected_cb),
-			  tool);
 }
 
 static void
@@ -599,8 +662,7 @@
 			      "anjuta-project-manager.xml",
 			      verbs, tool);
 	
-	init_project_tree (tool);
-	init_target_tree (tool);
+	init_project_view (tool);
 	init_build_info (tool);
 	
 	/* Create GdlRecent object for projects history. */
@@ -627,18 +689,14 @@
 	AnjutaTool *tool = ANJUTA_TOOL (obj);
 	ProjectTool *proj_tool = (ProjectTool*)obj;
 
-	if (proj_tool->file_tree) {
-		anjuta_shell_remove_value (tool->shell,
-					   "ProjectFileTree",
-					   NULL);
-		proj_tool->file_tree = NULL;
-	}
+	if (proj_tool->project_view) {
+		g_object_unref (proj_tool->project_model);
+		proj_tool->project_model = NULL;
 
-	if (proj_tool->target_tree) {
-		anjuta_shell_remove_value (tool->shell, 
-					   "ProjectTargetTree",
+		anjuta_shell_remove_value (tool->shell,
+					   "ProjectView",
 					   NULL);
-		proj_tool->target_tree = NULL;
+		proj_tool->project_view = NULL;
 	}
 
 	if (proj_tool->build_info) {
/* 
 * Copyright (C) 2002 Dave Camp
 *
 * 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. 
 * 
 * Authors: Dave Camp <dave ximian com>
 *          Gustavo Giráldez <gustavo giraldez gmx net>
 */

#include <config.h>

#include <string.h>
#include <bonobo/bonobo-i18n.h>
#include <glib-object.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreestore.h>
#include <gtk/gtktreednd.h>
#include <libgnomevfs/gnome-vfs-uri.h>

#include "gbf-project.h"
#include "gbf-tree-data.h"
#include "gbf-project-model.h"


struct _GbfProjectModelPrivate {
	GbfProject          *proj;
	gulong               project_updated_handler;
	
	GtkTreeRowReference *root_row;
	GList               *shortcuts;

	GbfTreeData         *empty_node;
};

enum {
	PROP_NONE,
	PROP_PROJECT
};


/* Function prototypes ------------- */

static void     gbf_project_model_class_init         (GbfProjectModelClass   *klass);
static void     gbf_project_model_instance_init      (GbfProjectModel        *tree);
static void     gbf_project_model_drag_source_init   (GtkTreeDragSourceIface *iface);
static void     gbf_project_model_drag_dest_init     (GtkTreeDragDestIface   *iface);

static void     load_project                         (GbfProjectModel        *model,
						      GbfProject             *proj);
static void     insert_empty_node                    (GbfProjectModel        *model);
static void     unload_project                       (GbfProjectModel        *model);

static gint     default_sort_func                    (GtkTreeModel           *model,
						      GtkTreeIter            *iter_a,
						      GtkTreeIter            *iter_b,
						      gpointer                user_data);

/* DND functions */

static gboolean row_draggable                        (GtkTreeDragSource      *drag_source,
						      GtkTreePath            *path);

static gboolean drag_data_delete                     (GtkTreeDragSource      *drag_source,
						      GtkTreePath            *path);

static gboolean drag_data_received                   (GtkTreeDragDest        *drag_dest,
						      GtkTreePath            *dest,
						      GtkSelectionData       *selection_data);

static gboolean row_drop_possible                    (GtkTreeDragDest        *drag_dest,
						      GtkTreePath            *dest_path,
						      GtkSelectionData       *selection_data);


static GtkTreeStoreClass *parent_class = NULL;


/* Implementation ---------------- */

/* Type & interfaces initialization */

static void
gbf_project_model_drag_source_init (GtkTreeDragSourceIface *iface)
{
	iface->row_draggable = row_draggable;
	iface->drag_data_delete = drag_data_delete;
}

static void
gbf_project_model_drag_dest_init (GtkTreeDragDestIface *iface)
{
	iface->drag_data_received = drag_data_received;
	iface->row_drop_possible = row_drop_possible;
}

static void
gbf_project_model_class_init_trampoline (gpointer klass,
					 gpointer data)
{
	parent_class = g_type_class_ref (GTK_TYPE_TREE_STORE);
	gbf_project_model_class_init (klass);
}

GType
gbf_project_model_get_type (void)
{
	static GType object_type = 0;
	if (object_type == 0) {
		static const GTypeInfo object_info = {
		    sizeof (GbfProjectModelClass),
		    NULL,		/* base_init */
		    NULL,		/* base_finalize */
		    gbf_project_model_class_init_trampoline,
		    NULL,		/* class_finalize */
		    NULL,               /* class_data */
		    sizeof (GbfProjectModel),
		    0,                  /* n_preallocs */
		    (GInstanceInitFunc) gbf_project_model_instance_init
		};
		static const GInterfaceInfo drag_source_info = {
			(GInterfaceInitFunc) gbf_project_model_drag_source_init,
			NULL,
			NULL
		};
		static const GInterfaceInfo drag_dest_info = {
			(GInterfaceInitFunc) gbf_project_model_drag_dest_init,
			NULL,
			NULL
		};

		object_type = g_type_register_static (
			GTK_TYPE_TREE_STORE, "GbfProjectModel",
			&object_info, 0);

		g_type_add_interface_static (object_type,
					     GTK_TYPE_TREE_DRAG_SOURCE,
					     &drag_source_info);

		g_type_add_interface_static (object_type,
					     GTK_TYPE_TREE_DRAG_DEST,
					     &drag_dest_info);
	}
	return object_type;
}

static void 
get_property (GObject    *object,
	      guint       prop_id,
	      GValue     *value,
	      GParamSpec *pspec)
{
        GbfProjectModel *model = GBF_PROJECT_MODEL (object);
        
        switch (prop_id) {
        case PROP_PROJECT:
                g_value_set_pointer (value, model->priv->proj);
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }
}

static void 
set_property (GObject      *object,
	      guint         prop_id,
	      const GValue *value,
	      GParamSpec   *pspec)
{
        GbfProjectModel *model = GBF_PROJECT_MODEL (object);

        switch (prop_id) {
        case PROP_PROJECT:
                if (model->priv->proj) {
                        unload_project (model);
                }

                if (g_value_get_pointer (value)) {
                        load_project (model, g_value_get_pointer (value));
                }
                break;
        default:
                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                break;
        }   
}

static void
dispose (GObject *obj)
{
	GbfProjectModel *model = GBF_PROJECT_MODEL (obj);

	if (model->priv->empty_node) {
		gbf_tree_data_free (model->priv->empty_node);
		model->priv->empty_node = NULL;
	}
	
	if (model->priv->proj) {
		unload_project (model);
	}
}

static void
finalize (GObject *obj)
{
	GbfProjectModel *model = GBF_PROJECT_MODEL (obj);

	g_free (model->priv);
}

static void 
gbf_project_model_class_init (GbfProjectModelClass *klass)
{
	parent_class = g_type_class_peek_parent (klass);

	G_OBJECT_CLASS (klass)->dispose = dispose;
	G_OBJECT_CLASS (klass)->finalize = finalize;
	G_OBJECT_CLASS (klass)->get_property = get_property;
	G_OBJECT_CLASS (klass)->set_property = set_property;

	g_object_class_install_property 
                (G_OBJECT_CLASS (klass), PROP_PROJECT,
                 g_param_spec_pointer ("project", 
                                       _("Project"),
                                       _("GbfProject Object"),
                                       G_PARAM_READWRITE));
}

static void
gbf_project_model_instance_init (GbfProjectModel *model)
{
	static GType types [GBF_PROJECT_MODEL_NUM_COLUMNS];

	types [GBF_PROJECT_MODEL_COLUMN_DATA] = GBF_TYPE_TREE_DATA;

	gtk_tree_store_set_column_types (GTK_TREE_STORE (model),
					 GBF_PROJECT_MODEL_NUM_COLUMNS,
					 types);

	model->priv = g_new0 (GbfProjectModelPrivate, 1);

	model->priv->empty_node = gbf_tree_data_new (GBF_TREE_NODE_STRING,
						     _("No project loaded"));
	/* sorting function */
	gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (model),
						 default_sort_func,
						 NULL, NULL);
	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
					      GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
					      GTK_SORT_ASCENDING);

	insert_empty_node (model);
}

/* Model data functions ------------ */

static gint 
default_sort_func (GtkTreeModel *model,
		   GtkTreeIter  *iter_a,
		   GtkTreeIter  *iter_b,
		   gpointer      user_data)
{
	GbfTreeData *data_a, *data_b;
	gint retval = 0;
	
	gtk_tree_model_get (model, iter_a,
			    GBF_PROJECT_MODEL_COLUMN_DATA, &data_a,
			    -1);
	gtk_tree_model_get (model, iter_b,
			    GBF_PROJECT_MODEL_COLUMN_DATA, &data_b,
			    -1);

	if (data_a->is_shortcut && data_b->is_shortcut) {
		GList *l;
		
		/* special case: the order of shortcuts is
		 * user customizable */
		for (l = GBF_PROJECT_MODEL (model)->priv->shortcuts; l; l = l->next) {
			if (!strcmp (l->data, data_a->id)) {
				/* a comes first */
				retval = -1;
				break;
			}
			else if (!strcmp (l->data, data_b->id)) {
				/* b comes first */
				retval = 1;
				break;
			}
		}
		
	} else if (data_a->is_shortcut && !data_b->is_shortcut) {
		retval = -1;
	 
	} else if (!data_a->is_shortcut && data_b->is_shortcut) {
		retval = 1;
	 
	} else if (data_a->type == data_b->type) {
		retval = strcmp (data_a->name, data_b->name);

	} else {
		/* assume a->b and check for the opposite cases */
		retval = -1;
		if (data_a->type == GBF_TREE_NODE_TARGET &&
		    data_b->type == GBF_TREE_NODE_GROUP) {
			retval = 1;
		}
	}
	
	gbf_tree_data_free (data_a);
	gbf_tree_data_free (data_b);
	
	return retval;
}


static void 
add_source (GbfProjectModel *model,
	    const gchar     *source_id,
	    GtkTreeIter     *parent)
{
	GbfProjectTargetSource *source;
	GtkTreeIter iter;
	GbfTreeData *data;
	
	source = gbf_project_get_source (model->priv->proj, source_id, NULL);
	if (!source)
		return;
	
	data = gbf_tree_data_new (GBF_TREE_NODE_TARGET_SOURCE, source);
	gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 
			    GBF_PROJECT_MODEL_COLUMN_DATA, data,
			    -1);
	gbf_tree_data_free (data);

	gbf_project_target_source_free (source);
}

static GtkTreePath *
find_shortcut (GbfProjectModel *model, const gchar *target_id)
{
	GList *l;
	gint i;
	
	for (l = model->priv->shortcuts, i = 0; l; l = l->next, i++) {
		if (!strcmp (target_id, l->data))
			return gtk_tree_path_new_from_indices (i, -1);
	}
	return NULL;
}

static void
remove_shortcut (GbfProjectModel *model, const gchar *target_id)
{
	GList *l;
	
	for (l = model->priv->shortcuts; l; l = l->next) {
		if (!strcmp (target_id, l->data)) {
			g_free (l->data);
			model->priv->shortcuts = g_list_delete_link (
				model->priv->shortcuts, l);
			break;
		}
	}
}

static void 
add_target_shortcut (GbfProjectModel *model,
		     const gchar     *target_id,
		     GtkTreePath     *before_path)
{
	GList *l;
	GtkTreeIter iter, sibling;
	GbfProjectTarget *target;
	GtkTreePath *root_path, *old_path;
	gint *path_indices, i;
	GbfTreeData *data;
	
	target = gbf_project_get_target (model->priv->proj, target_id, NULL);
	if (!target)
		return;

	root_path = gtk_tree_row_reference_get_path (model->priv->root_row);
	/* check before_path */
	if (!before_path ||
	    gtk_tree_path_get_depth (before_path) > 1 ||
	    gtk_tree_path_compare (before_path, root_path) > 0) {
		before_path = root_path;
	}
		
	/* get the tree iter for the row before which to insert the shortcut */
	if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &sibling, before_path)) {
		gtk_tree_path_free (root_path);
		return;
	}
	
	path_indices = gtk_tree_path_get_indices (before_path);
	i = path_indices [0];

	/* remove the old shortcut to make sorting actually work */
	old_path = find_shortcut (model, target_id);
	if (old_path) {
		remove_shortcut (model, target_id);
		if (gtk_tree_path_compare (old_path, before_path) < 0) {
			/* adjust shortcut insert position if the old
			 * index was before the new site */
			i--;
		}
		gtk_tree_path_free (old_path);
	}
			
	/* add entry to the shortcut list */
	model->priv->shortcuts = g_list_insert (model->priv->shortcuts,
						g_strdup (target->id), i);
	
	data = gbf_tree_data_new (GBF_TREE_NODE_TARGET, target);
	data->is_shortcut = TRUE;
	gtk_tree_store_insert_before (GTK_TREE_STORE (model), &iter, NULL, &sibling);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 
			    GBF_PROJECT_MODEL_COLUMN_DATA, data,
			    -1);
	gbf_tree_data_free (data);
	
	/* add sources */
	for (l = target->sources; l; l = l->next)
		add_source (model, l->data, &iter);

	gtk_tree_path_free (root_path);
	gbf_project_target_free (target);
}

static void 
add_target (GbfProjectModel *model,
	    const gchar     *target_id,
	    GtkTreeIter     *parent)
{
	GbfProjectTarget *target;
	GList *l;
	GtkTreeIter iter;	
	GbfTreeData *data;
	
	target = gbf_project_get_target (model->priv->proj, target_id, NULL);
	if (!target)
		return;
	
	data = gbf_tree_data_new (GBF_TREE_NODE_TARGET, target);
	gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 
			    GBF_PROJECT_MODEL_COLUMN_DATA, data,
			    -1);
	gbf_tree_data_free (data);
	
	/* add sources */
	for (l = target->sources; l; l = l->next)
		add_source (model, l->data, &iter);

	/* add a shortcut to the target if the target's type is a primary */
	/* FIXME: this shouldn't be here.  We would rather provide a
	 * set of public functions to add/remove shortcuts to save
	 * this information in the project metadata (when that's
	 * implemented) */
	if (!strcmp (target->type, "program") ||
	    !strcmp (target->type, "shared_lib") ||
	    !strcmp (target->type, "static_lib")) {
		add_target_shortcut (model, target->id, NULL);
	}

	gbf_project_target_free (target);
}

static void
update_target (GbfProjectModel *model, const gchar *target_id, GtkTreeIter *iter)
{
	GtkTreeModel *tree_model;
	GbfProjectTarget *target;
	GtkTreeIter child;
	GList *node;
	
	tree_model = GTK_TREE_MODEL (model);
	target = gbf_project_get_target (model->priv->proj, target_id, NULL);
	if (!target)
		return;
	
	/* update target data here */
	
	/* walk the tree target */
	if (gtk_tree_model_iter_children (tree_model, &child, iter)) {
		GbfTreeData *data;
		gboolean valid = TRUE;
		
		while (valid) {
			gtk_tree_model_get (tree_model, &child,
					    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
					    -1);

			/* find the iterating id in the target's sources */
			if (data->id) {
				node = g_list_find_custom (target->sources,
							   data->id, (GCompareFunc) strcmp);
				if (node) {
					target->sources = g_list_delete_link (target->sources, node);
					valid = gtk_tree_model_iter_next (tree_model, &child);
				} else {
					valid = gtk_tree_store_remove (GTK_TREE_STORE (model), &child);
				}
				gbf_tree_data_free (data);
			}
		}
	}

	/* add the remaining sources */
	for (node = target->sources; node; node = node->next)
		add_source (model, node->data, iter);
	
	gbf_project_target_free (target);
}

static void 
add_target_group (GbfProjectModel *model,
		  const gchar     *group_id,
		  GtkTreeIter     *parent)
{
	GbfProjectGroup *group;
	GtkTreeIter iter;
	GList *l;
	GbfTreeData *data;

	group = gbf_project_get_group (model->priv->proj, group_id, NULL);
	if (!group)
		return;
	
	data = gbf_tree_data_new (GBF_TREE_NODE_GROUP, group);
	gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 
			    GBF_PROJECT_MODEL_COLUMN_DATA, data,
			    -1);
	gbf_tree_data_free (data);

	/* create root reference */
	if (parent == NULL) {
		GtkTreePath *root_path;

		root_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
		model->priv->root_row = gtk_tree_row_reference_new (
			GTK_TREE_MODEL (model), root_path);
		gtk_tree_path_free (root_path);
	}

	/* add groups ... */
	for (l = group->groups; l; l = l->next)
		add_target_group (model, l->data, &iter);

	/* ... and targets */
	for (l = group->targets; l; l = l->next)
		add_target (model, l->data, &iter);

	gbf_project_group_free (group);
}

static void
update_group (GbfProjectModel *model, const gchar *group_id, GtkTreeIter *iter)
{
	GtkTreeModel *tree_model;
	GbfProjectGroup *group;
	GtkTreeIter child;
	GList *node;
	
	tree_model = GTK_TREE_MODEL (model);
	group = gbf_project_get_group (model->priv->proj, group_id, NULL);
	if (!group)
		return;
	
	/* update group data. nothing to do here */

	/* walk the tree group */
	if (gtk_tree_model_iter_children (tree_model, &child, iter)) {
		GbfTreeData *data;
		gboolean valid = TRUE;
		
		while (valid) {
			gboolean remove_child = FALSE;
			
			gtk_tree_model_get (tree_model, &child,
					    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
					    -1);

			/* find the iterating id in the group's children */
			if (data->type == GBF_TREE_NODE_GROUP) {
				node = g_list_find_custom (group->groups, data->id, (GCompareFunc) strcmp);
				if (node) {
					group->groups = g_list_delete_link (group->groups, node);
					/* update recursively */
					update_group (model, data->id, &child);
				} else {
					remove_child = TRUE;
				}

			} else if (data->type == GBF_TREE_NODE_TARGET) {
				GtkTreePath *shortcut;

				node = g_list_find_custom (group->targets, data->id, (GCompareFunc) strcmp);
				if (node) {
					group->targets = g_list_delete_link (group->targets, node);
					/* update recursively */
					update_target (model, data->id, &child);
				} else {
					remove_child = TRUE;
				}

				/* remove or update the shortcut if it previously existed */
				shortcut = find_shortcut (model, data->id);
				if (shortcut) {
					GtkTreeIter tmp;
					
					if (remove_child)
						remove_shortcut (model, data->id);

					if (gtk_tree_model_get_iter (tree_model, &tmp, shortcut)) {
						if (remove_child)
							gtk_tree_store_remove (GTK_TREE_STORE (model), &tmp);
						else
							update_target (model, data->id, &tmp);
					}
					gtk_tree_path_free (shortcut);
				}
			}
			
			gbf_tree_data_free (data);
			if (remove_child)
				valid = gtk_tree_store_remove (GTK_TREE_STORE (model), &child);
			else
				valid = gtk_tree_model_iter_next (tree_model, &child);
		};
	}

	/* add the remaining targets and groups */
	for (node = group->groups; node; node = node->next)
		add_target_group (model, node->data, iter);

	for (node = group->targets; node; node = node->next)
		add_target (model, node->data, iter);
	
	gbf_project_group_free (group);
}

static void
project_updated_cb (GbfProject *project, GbfProjectModel *model)
{
	GtkTreePath *path;
	GtkTreeIter iter;

	path = gtk_tree_row_reference_get_path (model->priv->root_row);
	if (path && gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path))
		update_group (model, "/", &iter);
	else
		add_target_group (model, "/", NULL);
			
	if (path)
		gtk_tree_path_free (path);
}

static void
load_project (GbfProjectModel *model, GbfProject *proj)
{
	model->priv->proj = proj;
	g_object_ref (proj);

	/* to get rid of the empty node */
	gtk_tree_store_clear (GTK_TREE_STORE (model));

	add_target_group (model, "/", NULL);

	model->priv->project_updated_handler =
		g_signal_connect (model->priv->proj, "project-updated",
				  (GCallback) project_updated_cb, model);
}

static void 
insert_empty_node (GbfProjectModel *model)
{
	GtkTreeIter iter;

	gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
	gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
			    GBF_PROJECT_MODEL_COLUMN_DATA, model->priv->empty_node,
			    -1);
}

static void
unload_project (GbfProjectModel *model)
{
	if (model->priv->proj) {
		gtk_tree_row_reference_free (model->priv->root_row);
		model->priv->root_row = NULL;

		gtk_tree_store_clear (GTK_TREE_STORE (model));

		g_list_foreach (model->priv->shortcuts, (GFunc) g_free, NULL);
		g_list_free (model->priv->shortcuts);
		model->priv->shortcuts = NULL;
		
		g_signal_handler_disconnect (model->priv->proj,
					     model->priv->project_updated_handler);
		model->priv->project_updated_handler = 0;
		g_object_unref (model->priv->proj);
		model->priv->proj = NULL;

		insert_empty_node (model);
	}
}

GbfProjectModel *
gbf_project_model_new (void)
{
	return GBF_PROJECT_MODEL (g_object_new (GBF_TYPE_PROJECT_MODEL, NULL));
}

GtkTreePath *
gbf_project_model_get_project_root (GbfProjectModel *model)
{
	GtkTreePath *path = NULL;
	
	g_return_val_if_fail (GBF_IS_PROJECT_MODEL (model), NULL);

	if (model->priv->root_row)
		path = gtk_tree_row_reference_get_path (model->priv->root_row);
	
	return path;
}


/* DND stuff ------------- */

static gboolean
row_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path)
{
	GtkTreeIter iter;
	GbfTreeData *data;
	gboolean retval = FALSE;
	
	if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source), &iter, path))
		return FALSE;

	gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter,
			    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
			    -1);

	if (data->is_shortcut) {
		/* shortcuts can be moved */
		retval = TRUE;

	} else if (data->type == GBF_TREE_NODE_TARGET) {
		GtkTreePath *found;
		
		/* don't allow duplicate shortcuts */
		found = find_shortcut (GBF_PROJECT_MODEL (drag_source), data->id);
		if (!found)
			retval = TRUE;
		else
			gtk_tree_path_free (found);
	}
	gbf_tree_data_free (data);

	return retval;
}

static gboolean 
drag_data_delete (GtkTreeDragSource *drag_source, GtkTreePath *path)
{
	GtkTreeIter iter;
	gboolean retval = FALSE;
	
	if (gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source),
				     &iter, path)) {
		GbfTreeData *data;

		gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter,
				    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
				    -1);

		if (data->is_shortcut) {
			gtk_tree_store_remove (GTK_TREE_STORE (drag_source), &iter);
			retval = TRUE;
		}
		gbf_tree_data_free (data);
	}
	
	return retval;
}

static gboolean
drag_data_received (GtkTreeDragDest  *drag_dest,
		    GtkTreePath      *dest,
		    GtkSelectionData *selection_data)
{
	GtkTreeModel *src_model = NULL;
	GtkTreePath *src_path = NULL;
	gboolean retval = FALSE;

	g_return_val_if_fail (GBF_IS_PROJECT_MODEL (drag_dest), FALSE);

	if (gtk_tree_get_row_drag_data (selection_data,
					&src_model,
					&src_path) &&
	    src_model == GTK_TREE_MODEL (drag_dest)) {

		GtkTreeIter iter;
		GbfTreeData *data = NULL;
		
		if (gtk_tree_model_get_iter (src_model, &iter, src_path)) {
			gtk_tree_model_get (src_model, &iter,
					    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
					    -1);
			if (data && data->id && data->type == GBF_TREE_NODE_TARGET) {
				add_target_shortcut (GBF_PROJECT_MODEL (drag_dest),
						     data->id, dest);
				retval = TRUE;
			}
			gbf_tree_data_free (data);
		}
	}

	if (src_path)
		gtk_tree_path_free (src_path);

	return retval;
}

static gboolean
row_drop_possible (GtkTreeDragDest  *drag_dest,
		   GtkTreePath      *dest_path,
		   GtkSelectionData *selection_data)
{
	GtkTreePath *root_path;
	GbfProjectModel *model;
	gboolean retval = FALSE;
	GtkTreeModel *src_model = NULL;
  
	g_return_val_if_fail (GBF_IS_PROJECT_MODEL (drag_dest), FALSE);

	model = GBF_PROJECT_MODEL (drag_dest);

	if (!gtk_tree_get_row_drag_data (selection_data,
					 &src_model,
					 NULL))
		return FALSE;
    
	/* can only drag to ourselves and only new toplevel nodes will
	 * be created */
	if (src_model == GTK_TREE_MODEL (drag_dest) &&
	    gtk_tree_path_get_depth (dest_path) == 1) {
		root_path = gtk_tree_row_reference_get_path (model->priv->root_row);
		if (gtk_tree_path_compare (dest_path, root_path) <= 0) {
			retval = TRUE;
		}
		gtk_tree_path_free (root_path);
	}

	return retval;
}
/*  -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * 
 * Copyright (C) 2002 Dave Camp
 *
 * 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. 
 * 
 * Author: Dave Camp <dave ximian com> 
 */

#ifndef GBF_PROJEcT_MODEL_H
#define GBF_PROJECT_MODEL_H

#include <glib-object.h>
#include <gtk/gtktreestore.h>

#define GBF_TYPE_PROJECT_MODEL            (gbf_project_model_get_type ())
#define GBF_PROJECT_MODEL(obj)	          (GTK_CHECK_CAST ((obj), GBF_TYPE_PROJECT_MODEL, GbfProjectModel))
#define GBF_PROJECT_MODEL_CLASS(klass)    (GTK_CHECK_CLASS_CAST ((klass), GBF_TYPE_PROJECT_MODEL, GbfProjectModelClass))
#define GBF_IS_PROJECT_MODEL(obj)	      (GTK_CHECK_TYPE ((obj), GBF_TYPE_PROJECT_MODEL))
#define GBF_IS_PROJECT_MODEL_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GBF_TYPE_PROJECT_MODEL))

typedef struct _GbfProjectModel        GbfProjectModel;
typedef struct _GbfProjectModelClass   GbfProjectModelClass;
typedef struct _GbfProjectModelPrivate GbfProjectModelPrivate;

enum {
	GBF_PROJECT_MODEL_COLUMN_DATA,
	GBF_PROJECT_MODEL_NUM_COLUMNS
};

struct _GbfProjectModel {
	GtkTreeStore parent;
	GbfProjectModelPrivate *priv;
};

struct _GbfProjectModelClass {
	GtkTreeStoreClass parent_class;
};

GType            gbf_project_model_get_type          (void); 

GbfProjectModel *gbf_project_model_new               (void);

GtkTreePath     *gbf_project_model_get_project_root  (GbfProjectModel *model);

#endif
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gbf_project-tree.c
 *
 * Copyright (C) 2000  JP Rosevear
 *
 * 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.
 *
 * Authors: JP Rosevear
 *          Gustavo Giráldez <gustavo giraldez gmx net>
 */

#include <gdk/gdk.h>
#include <gtk/gtkcellrendererpixbuf.h>
#include <gtk/gtkcellrenderertext.h>
#include <libgnomevfs/gnome-vfs-types.h>
#include <libgnomevfs/gnome-vfs-uri.h>
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-macros.h>
#include <string.h>
#include <gdl/gdl-icons.h>
#include "gbf-tree-data.h"
#include "gbf-project.h"
#include "gbf-project-model.h"
#include "gbf-project-view.h"

struct _GbfProjectViewPrivate {	
	GdlIcons *icons;
};

enum {
	URI_ACTIVATED,
	TARGET_SELECTED,
	LAST_SIGNAL
};

static guint signals [LAST_SIGNAL] = { 0 };

static void gbf_project_view_class_init    (GbfProjectViewClass *klass);
static void gbf_project_view_instance_init (GbfProjectView      *tree);
static void destroy                        (GtkObject           *object);

static void set_pixbuf                     (GtkTreeViewColumn   *tree_column,
					    GtkCellRenderer     *cell,
					    GtkTreeModel        *model,
					    GtkTreeIter         *iter,
					    gpointer             data);
static void set_text                       (GtkTreeViewColumn   *tree_column,
					    GtkCellRenderer     *cell,
					    GtkTreeModel        *model,
					    GtkTreeIter         *iter,
					    gpointer             data);


GNOME_CLASS_BOILERPLATE(GbfProjectView, gbf_project_view,
			GtkTreeView, GTK_TYPE_TREE_VIEW);


static void
row_activated (GtkTreeView       *tree_view,
	       GtkTreePath       *path,
	       GtkTreeViewColumn *column)
{
	GtkTreeModel *model;
	GtkTreeIter iter;
	GbfTreeData *data;
	
	model = gtk_tree_view_get_model (tree_view);

	gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);

	gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
			    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
			    -1);

	if (data->uri) {
		g_signal_emit (G_OBJECT (tree_view), 
			       signals [URI_ACTIVATED], 0,
			       data->uri);
	}

	if (data->type == GBF_TREE_NODE_TARGET) {
		g_signal_emit (G_OBJECT (tree_view),
			       signals [TARGET_SELECTED], 0,
			       data->id);
	}
	
	gbf_tree_data_free (data);
}

static void
destroy (GtkObject *object)
{
	GbfProjectView *tree;
	GbfProjectViewPrivate *priv;
	
	tree = GBF_PROJECT_VIEW (object);
	priv = tree->priv;
	
	if (priv) {
		g_object_unref (priv->icons);
		g_free (priv);
		tree->priv = NULL;
	}
	
	if (GTK_OBJECT_CLASS (parent_class)->destroy)
		(* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}

static void
set_pixbuf (GtkTreeViewColumn *tree_column,
	    GtkCellRenderer   *cell,
	    GtkTreeModel      *model,
	    GtkTreeIter       *iter,
	    gpointer           user_data)
{
	GbfProjectView *view = GBF_PROJECT_VIEW (user_data);
	GbfTreeData *data;
	GdkPixbuf *pixbuf;
	
	gtk_tree_model_get (model, iter, GBF_PROJECT_MODEL_COLUMN_DATA, &data, -1);

	switch (data->type) {
		case GBF_TREE_NODE_TARGET_SOURCE:
			pixbuf = gdl_icons_get_uri_icon (view->priv->icons, data->uri);
			break;
		case GBF_TREE_NODE_GROUP:
			pixbuf = gdl_icons_get_folder_icon (view->priv->icons);
			break;
		case GBF_TREE_NODE_TARGET: 
		{
			GbfProject *project;

			g_object_get (G_OBJECT (model), "project", &project, NULL);
			pixbuf = gbf_project_icon_for_type (project, data->mime_type);
			
#if 0
			/* FIXME: make GdlIcons understand about
			 * target types (which are stored in the
			 * mime_type field) */
			pixbuf = gdl_icons_get_mime_icon (view->priv->icons,
							  data->mime_type);
#endif
			break;
		}
		default:
			pixbuf = NULL;
	}

	g_object_set (GTK_CELL_RENDERER (cell), "pixbuf", pixbuf, NULL);
	if (pixbuf)
		g_object_unref (pixbuf);

	gbf_tree_data_free (data);
}

static void
set_text (GtkTreeViewColumn *tree_column,
	  GtkCellRenderer   *cell,
	  GtkTreeModel      *model,
	  GtkTreeIter       *iter,
	  gpointer           user_data)
{
	GbfTreeData *data;
  
	gtk_tree_model_get (model, iter, 0, &data, -1);
	g_object_set (GTK_CELL_RENDERER (cell), "text", 
		      data->name, NULL);
	gbf_tree_data_free (data);
}

static gint
expose_event (GtkWidget *widget, GdkEventExpose *ev)
{
	GtkTreeModel *model;
	GtkTreeView *tree_view;
	gint event_handled = FALSE;
	
	event_handled = GNOME_CALL_PARENT_WITH_DEFAULT (
		GTK_WIDGET_CLASS, expose_event, (widget, ev), event_handled);

	tree_view = GTK_TREE_VIEW (widget);
	model = gtk_tree_view_get_model (tree_view);
	if (ev->window == gtk_tree_view_get_bin_window (tree_view) &&
	    model && GBF_IS_PROJECT_MODEL (model)) {
		GtkTreePath *root;
		GdkRectangle rect;
		
		/* paint an horizontal ruler to separate the project
		 * tree from the target shortcuts */
		
		root = gbf_project_model_get_project_root (GBF_PROJECT_MODEL (model));
		if (root) {
			gtk_tree_view_get_background_area (
				tree_view, root, gtk_tree_view_get_column (tree_view, 0), &rect);
			gtk_paint_hline (widget->style,
					 ev->window,
					 GTK_WIDGET_STATE (widget),
					 &ev->area,
					 widget,
					 "",
					 rect.x, rect.x + rect.width,
					 rect.y);
			gtk_tree_path_free (root);
		}
	}
	
	return event_handled;
}

static void 
gbf_project_view_class_init (GbfProjectViewClass *klass)
{
	GObjectClass     *g_object_class;
	GtkObjectClass   *object_class;
	GtkWidgetClass   *widget_class;
	GtkTreeViewClass *tree_view_class;

	g_object_class = G_OBJECT_CLASS (klass);
	object_class = (GtkObjectClass *) klass;
	widget_class = GTK_WIDGET_CLASS (klass);
	tree_view_class = GTK_TREE_VIEW_CLASS (klass);

	parent_class = g_type_class_peek_parent (klass);

	object_class->destroy = destroy;
	widget_class->expose_event = expose_event;
	tree_view_class->row_activated = row_activated;

	signals [URI_ACTIVATED] = 
		g_signal_new ("uri_activated",
			      GBF_TYPE_PROJECT_VIEW,
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (GbfProjectViewClass,
					       uri_activated),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__STRING,
			      G_TYPE_NONE, 1, G_TYPE_STRING);

	signals [TARGET_SELECTED] = 
		g_signal_new ("target_selected",
			      GBF_TYPE_PROJECT_VIEW,
			      G_SIGNAL_RUN_LAST,
			      G_STRUCT_OFFSET (GbfProjectViewClass,
					       target_selected),
			      NULL, NULL,
			      g_cclosure_marshal_VOID__STRING,
			      G_TYPE_NONE, 1, G_TYPE_STRING);
}

static void 
gbf_project_view_instance_init (GbfProjectView *tree)
{
	GbfProjectViewPrivate *priv;
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;
	static GtkTargetEntry row_targets[] = {
		{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
	};

	priv = g_new0 (GbfProjectViewPrivate, 1);
	tree->priv = priv;

	priv->icons = gdl_icons_new (24, 16.0);
    
	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE);

	gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree),
						GDK_BUTTON1_MASK,
						row_targets,
						G_N_ELEMENTS (row_targets),
						GDK_ACTION_MOVE);
	gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (tree),
					      row_targets,
					      G_N_ELEMENTS (row_targets),
					      GDK_ACTION_MOVE);
      
	/* set renderer for files column. */        
	column = gtk_tree_view_column_new ();
	renderer = gtk_cell_renderer_pixbuf_new ();
	gtk_tree_view_column_pack_start (column, renderer, FALSE);
	gtk_tree_view_column_set_cell_data_func (column, renderer, set_pixbuf, tree, NULL);
	
	renderer = gtk_cell_renderer_text_new ();
	gtk_tree_view_column_pack_start (column, renderer, FALSE);
	gtk_tree_view_column_set_cell_data_func (column, renderer, set_text, tree, NULL);

	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
}

GtkWidget *
gbf_project_view_new (void)
{
	return GTK_WIDGET (g_object_new (GBF_TYPE_PROJECT_VIEW, NULL));
}
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gbf-project-view.h
 *
 * Copyright (C) 2000-2002  JP Rosevear
 * Copyright (C) 2002  Dave Camp
 *
 * 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.
 */

#ifndef _GBF_PROJECT_TREE_H_
#define _GBF_PROJECT_TREE_H_

#include <gtk/gtktreeview.h>

G_BEGIN_DECLS

#define GBF_TYPE_PROJECT_VIEW		  (gbf_project_view_get_type ())
#define GBF_PROJECT_VIEW(obj)		  (GTK_CHECK_CAST ((obj), GBF_TYPE_PROJECT_VIEW, GbfProjectView))
#define GBF_PROJECT_VIEW_CLASS(klass)	  (GTK_CHECK_CLASS_CAST ((klass), GBF_TYPE_PROJECT_VIEW, GbfProjectViewClass))
#define GBF_IS_PROJECT_VIEW(obj)	  (GTK_CHECK_TYPE ((obj), GBF_TYPE_PROJECT_VIEW))
#define GBF_IS_PROJECT_VIEW_CLASS(klass)  (GTK_CHECK_CLASS_TYPE ((obj), GBF_TYPE_PROJECT_VIEW))

typedef struct _GbfProjectView        GbfProjectView;
typedef struct _GbfProjectViewPrivate GbfProjectViewPrivate;
typedef struct _GbfProjectViewClass   GbfProjectViewClass;


struct _GbfProjectView {
	GtkTreeView parent;

	GbfProjectViewPrivate *priv;
};

struct _GbfProjectViewClass {
	GtkTreeViewClass parent_class;

	void (* uri_activated)    (GbfProjectView *project_view,
				   const char     *uri);

	void (* target_selected)  (GbfProjectView *project_view,
				   const gchar    *target_id);
};

GType                       gbf_project_view_get_type         (void);
GtkWidget                  *gbf_project_view_new              (void);

G_END_DECLS

#endif /* _GBF_PROJECT_VIEW_H_ */
#include <config.h>
#include <gnome.h>
#include <string.h>
#include <libbonoboui.h>
#include <libgnomevfs/gnome-vfs-uri.h>
#include "gbf-backend.h"
#include "gbf-project-view.h"
#include "gbf-project-model.h"

#define TEST_CONTROLS_UI_XML "test-controls.xml"

static GtkWidget *app;
static GbfProject *proj = NULL;
static GtkWidget *view;
static GbfProjectModel *model;


static void open_cb      (GtkWidget *widget,
                          gpointer   user_data,
                          char      *cname);
static void open_project (GtkWidget *widget,
                          gpointer   user_data);
static void close_cb     (GtkWidget *widget,
			  gpointer   user_data,
			  char      *cname);
static void exit_cb      (GtkWidget *widget,
                          gpointer   user_data,
                          char      *cname);

static BonoboUIVerb verbs[] = {
	BONOBO_UI_UNSAFE_VERB ("FileOpen", open_cb),
	BONOBO_UI_UNSAFE_VERB ("FileClose", close_cb),
	BONOBO_UI_UNSAFE_VERB ("FileExit", exit_cb),
	BONOBO_UI_VERB_END
};

static void
open_cb (GtkWidget *widget, 
         gpointer   user_data, 
         char      *cname)
{
	GtkWidget *file_sel;

	file_sel = gtk_file_selection_new (_("Open Project"));

	g_signal_connect (GTK_FILE_SELECTION (file_sel)->ok_button,
			  "clicked",
			  G_CALLBACK (open_project),
			  file_sel);

	g_signal_connect_swapped (GTK_FILE_SELECTION (file_sel)->ok_button, 
				  "clicked", G_CALLBACK (gtk_widget_destroy),
				  file_sel);
	g_signal_connect_swapped (GTK_FILE_SELECTION (file_sel)->cancel_button, 
				  "clicked", G_CALLBACK (gtk_widget_destroy),
				  file_sel);

	gtk_widget_show (file_sel);
}

static void
open_project (GtkWidget *widget,
	      gpointer   user_data)
{
	GtkFileSelection *file_sel;
	const gchar *filename;
	GnomeVFSURI *vfs_uri;
	gchar *dirname;
	GSList *l;
	GbfBackend *backend = NULL;

	file_sel = GTK_FILE_SELECTION (user_data);    
	filename = gtk_file_selection_get_filename (file_sel);

	if (!filename)
		return;

	vfs_uri = gnome_vfs_uri_new (filename);
	dirname = gnome_vfs_uri_extract_dirname (vfs_uri);
	gnome_vfs_uri_unref (vfs_uri);

	if (proj != NULL)
		g_object_unref (proj);

	g_print ("initializing gbf backend...\n");
	gbf_backend_init ();

	for (l = gbf_backend_get_backends (); l; l = l->next) {
		backend = l->data;
		if (!strcmp (backend->id, "gbf-am:GbfAmProject"))
			break;
		backend = NULL;
	}

	if (!backend) {
		g_print ("no automake/autoconf backend available\n");
		return;
	}

	g_print ("creating new gbf-am project\n");
	proj = gbf_backend_new_project (backend->id);
	if (!proj) {
		g_print ("project creation failed\n");
		return;
	}

	g_print ("loading project %s\n\n", dirname);
	/* FIXME: use the error parameter to determine if the project
	 * was loaded successfully */
	gbf_project_load (proj, dirname, NULL);
	g_free (dirname);
	
	g_object_set (G_OBJECT (model), "project", proj, NULL);
}

static void
close_cb (GtkWidget *widget,
	  gpointer   user_data,
	  char      *cname)
{
	if (proj) {
 		g_object_unref (proj);
		proj = NULL;
		g_object_set (G_OBJECT (model), "project", NULL, NULL);
	}
}

static void 
exit_cb (GtkWidget *widget, 
         gpointer   user_data, 
         char      *cname)
{
	gtk_widget_destroy (GTK_WIDGET (user_data));

	bonobo_main_quit ();
}

static void
window_destroyed (GtkWidget *window,
		  gpointer   user_data)
{
    BonoboUIComponent *ui_component;

    ui_component = g_object_get_data (G_OBJECT (window), "ui_component");
    if (ui_component) {
	bonobo_ui_component_unset_container (ui_component, NULL);
	bonobo_object_unref (ui_component);
	g_object_set_data (G_OBJECT (window), "ui_component", NULL);
    }
}

static void
uri_activated_cb (GtkWidget *widget, gchar *uri, gpointer user_data)
{
        g_message ("URI activated: %s", uri);
}

static BonoboWindow *
create_window (void)
{
	BonoboWindow       *win;
	BonoboUIContainer  *ui_container;
	BonoboUIComponent  *ui_component;
	GtkWidget          *scrolled_window;
	
	win = BONOBO_WINDOW (bonobo_window_new (GETTEXT_PACKAGE, _("Test Project View")));

	/* Create Container: */
	ui_container = bonobo_window_get_ui_container (win);

	/* This determines where the UI configuration info. will be stored */
	bonobo_ui_engine_config_set_path (bonobo_window_get_ui_engine (win),
					  "/apps/gnome-build/test-project/UIConfig/kvps");

	/* Create a UI component with which to communicate with the window */
	ui_component = bonobo_ui_component_new_default ();
	g_object_set_data (G_OBJECT (win), "ui_component", ui_component);
	
	/* Associate the BonoboUIComponent with the container */
	bonobo_ui_component_set_container (ui_component, 
					   BONOBO_OBJREF (ui_container), 
					   NULL);

	/* NB. this creates a relative file name from the current dir,
	 * in production you want to pass the application's datadir
	 * see Makefile.am to see how HELLO_SRCDIR gets set. */
	bonobo_ui_util_set_ui (ui_component, "", /* data dir */
	                       CONTROLS_SRCDIR TEST_CONTROLS_UI_XML,
	                       "test-controls", NULL);

	/* Associate our verb -> callback mapping with the BonoboWindow */
	/* All the callback's user_data pointers will be set to 'win' */
	bonobo_ui_component_add_verb_list_with_data (ui_component, verbs, win);

        /* create model & view and bind them */
        model = gbf_project_model_new ();
        view = gbf_project_view_new ();
        gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (model));
        g_object_unref (model);
        g_signal_connect (view, "uri-activated",
                          G_CALLBACK (uri_activated_cb), NULL);

        scrolled_window = gtk_scrolled_window_new (NULL, NULL);
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                                        GTK_POLICY_AUTOMATIC,
                                        GTK_POLICY_AUTOMATIC);
        gtk_container_add (GTK_CONTAINER (scrolled_window), view);
        gtk_widget_show (view);
        
        bonobo_window_set_contents (win, scrolled_window);
        gtk_widget_show (scrolled_window);

	g_signal_connect (win, "destroy", G_CALLBACK (window_destroyed), NULL);
	g_signal_connect (win, "delete_event", G_CALLBACK (bonobo_main_quit), NULL);

	gtk_window_set_default_size (GTK_WINDOW (win), 500, 500);
    
	return win;
}

int
main (int argc, char *argv[])
{
	gnome_program_init ("test-controls", VERSION, LIBGNOMEUI_MODULE, 
			    argc, argv, GNOME_PARAM_POPT_TABLE, 
			    bonobo_activation_popt_options, NULL);

	if (!bonobo_init (&argc, argv))
		g_error (_("Can't initialize bonobo!"));

	app = GTK_WIDGET (create_window ());
    
	gtk_widget_show_all (app);

	bonobo_main ();

	if (proj)
		g_object_unref (proj);

	bonobo_ui_debug_shutdown ();

	return 0;
}


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