1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8; coding: utf-8 -*- */
2 /* project-tree.c
3  *
4  * Copyright (C) 2000  JP Rosevear
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  * Authors: JP Rosevear
22  *          Gustavo Giráldez <gustavo.giraldez@gmx.net>
23  */
24 
25 
26 
27 #ifdef HAVE_CONFIG_H
28 #include <config.h>
29 #endif
30 
31 #include <glib/gi18n.h>
32 #include <gdk/gdk.h>
33 #include <gtk/gtk.h>
34 #include <gio/gio.h>
35 #include <string.h>
36 
37 #include "tree-data.h"
38 #include "project-model.h"
39 #include "project-view.h"
40 #include "project-marshal.h"
41 #include "project-util.h"
42 
43 #define ICON_SIZE 16
44 
45 enum {
46 	NODE_SELECTED,
47 	NODE_LOADED,
48 	LAST_SIGNAL
49 };
50 
51 static guint signals [LAST_SIGNAL] = { 0 };
52 
53 /* Project model filter with drag and drop support
54  *---------------------------------------------------------------------------*/
55 
56 typedef struct {
57   GtkTreeModelFilter parent;
58 } PmProjectModelFilter;
59 
60 typedef struct {
61   GtkTreeModelFilterClass parent_class;
62 } PmProjectModelFilterClass;
63 
64 #define PM_TYPE_PROJECT_MODEL_FILTER	    (pm_project_model_filter_get_type ())
65 #define PM_PROJECT_MODEL_FILTER(obj)	    (G_TYPE_CHECK_INSTANCE_CAST ((obj), PM_TYPE_PROJECT_MODEL_FILTER, PmProjectModelFilter))
66 
67 
68 static void
pm_project_model_filter_class_init(PmProjectModelFilterClass * class)69 pm_project_model_filter_class_init (PmProjectModelFilterClass *class)
70 {
71 }
72 
73 static void
pm_project_model_filter_init(PmProjectModelFilter * model)74 pm_project_model_filter_init (PmProjectModelFilter *model)
75 {
76 }
77 
78 static gboolean
idrag_source_row_draggable(GtkTreeDragSource * drag_source,GtkTreePath * path)79 idrag_source_row_draggable (GtkTreeDragSource *drag_source, GtkTreePath *path)
80 {
81 	GtkTreeIter iter;
82 	GbfTreeData *data;
83 	gboolean retval = FALSE;
84 
85 	if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (drag_source), &iter, path))
86 		return FALSE;
87 
88 	gtk_tree_model_get (GTK_TREE_MODEL (drag_source), &iter,
89 			    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
90 			    -1);
91 
92 	if (data->is_shortcut) {
93 		/* shortcuts can be moved */
94 		retval = TRUE;
95 
96 	} else if (data->type == GBF_TREE_NODE_TARGET) {
97 		/* don't allow duplicate shortcuts */
98 		if (data->shortcut == NULL)
99 			retval = TRUE;
100 	}
101 
102 	return retval;
103 }
104 
105 static gboolean
idrag_source_drag_data_delete(GtkTreeDragSource * drag_source,GtkTreePath * path)106 idrag_source_drag_data_delete (GtkTreeDragSource *drag_source, GtkTreePath *path)
107 {
108 	return FALSE;
109 }
110 
111 static gboolean
idrag_dest_drag_data_received(GtkTreeDragDest * drag_dest,GtkTreePath * dest,GtkSelectionData * selection_data)112 idrag_dest_drag_data_received (GtkTreeDragDest  *drag_dest,
113 		    GtkTreePath      *dest,
114 		    GtkSelectionData *selection_data)
115 {
116 	GtkTreeModel *src_model = NULL;
117 	GtkTreePath *src_path = NULL;
118 	GtkTreeModel *project_model;
119 	gboolean retval = FALSE;
120 
121 	if (GTK_IS_TREE_MODEL_FILTER (drag_dest))
122 	{
123 		project_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (drag_dest));
124 	}
125 	else
126 	{
127 		project_model = GTK_TREE_MODEL (drag_dest);
128 	}
129 	g_return_val_if_fail (GBF_IS_PROJECT_MODEL (project_model), FALSE);
130 
131 	if (gtk_tree_get_row_drag_data (selection_data,
132 					&src_model,
133 					&src_path) &&
134 	    src_model == GTK_TREE_MODEL (project_model)) {
135 
136 		GtkTreeIter iter;
137 		GbfTreeData *data = NULL;
138 
139 		if (gtk_tree_model_get_iter (src_model, &iter, src_path)) {
140 			gtk_tree_model_get (src_model, &iter,
141 					    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
142 					    -1);
143 			if (data != NULL)
144 			{
145 				GtkTreePath *child_path;
146 
147 				child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (drag_dest), dest);
148 				if (data->type == GBF_TREE_NODE_SHORTCUT)
149 				{
150 					gbf_project_model_move_target_shortcut (GBF_PROJECT_MODEL (project_model),
151 					                                        &iter, data, child_path);
152 				}
153 				else
154 				{
155 					gbf_project_model_add_target_shortcut (GBF_PROJECT_MODEL (project_model),
156 						     	NULL, data, child_path, NULL);
157 				}
158 				gtk_tree_path_free (child_path);
159 				retval = TRUE;
160 			}
161 		}
162 	}
163 
164 	if (src_path)
165 		gtk_tree_path_free (src_path);
166 
167 	return retval;
168 }
169 
170 static gboolean
idrag_dest_row_drop_possible(GtkTreeDragDest * drag_dest,GtkTreePath * dest_path,GtkSelectionData * selection_data)171 idrag_dest_row_drop_possible (GtkTreeDragDest  *drag_dest,
172 		   GtkTreePath      *dest_path,
173 		   GtkSelectionData *selection_data)
174 {
175 	GtkTreeModel *src_model;
176 	GtkTreeModel *project_model;
177 	GtkTreePath *src_path;
178 	GtkTreeIter iter;
179 	gboolean retval = FALSE;
180 
181 	//g_return_val_if_fail (GBF_IS_PROJECT_MODEL (drag_dest), FALSE);
182 
183 	if (GTK_IS_TREE_MODEL_FILTER (drag_dest))
184 	{
185 		project_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (drag_dest));
186 	}
187 	else
188 	{
189 		project_model = GTK_TREE_MODEL (drag_dest);
190 	}
191 
192 	if (!gtk_tree_get_row_drag_data (selection_data,
193 					 &src_model,
194 					 &src_path))
195 	{
196 		return FALSE;
197 	}
198 
199 
200 	if (gtk_tree_model_get_iter (src_model, &iter, src_path))
201 	{
202 		GbfTreeData *data = NULL;
203 
204 		gtk_tree_model_get (src_model, &iter,
205 		    GBF_PROJECT_MODEL_COLUMN_DATA, &data, -1);
206 
207 		if (data != NULL)
208 		{
209 			/* can only drag to ourselves and only new toplevel nodes will
210 			 * be created */
211 			if (src_model == project_model &&
212 	    			gtk_tree_path_get_depth (dest_path) == 1)
213 			{
214 				if (data->type == GBF_TREE_NODE_SHORTCUT)
215 				{
216 					retval = TRUE;
217 				}
218 				else
219 				{
220 					GtkTreePath *root_path;
221 					GtkTreePath *child_path;
222 
223 					root_path = gbf_project_model_get_project_root (GBF_PROJECT_MODEL (project_model));
224 					child_path = gtk_tree_model_filter_convert_path_to_child_path (GTK_TREE_MODEL_FILTER (drag_dest), dest_path);
225 					retval = gtk_tree_path_compare (child_path, root_path) <= 0;
226 					gtk_tree_path_free (child_path);
227 					gtk_tree_path_free (root_path);
228 				}
229 			}
230 		}
231 	}
232 	gtk_tree_path_free (src_path);
233 
234 	return retval;
235 }
236 
237 static void
pm_project_model_filter_drag_source_iface_init(GtkTreeDragSourceIface * iface)238 pm_project_model_filter_drag_source_iface_init (GtkTreeDragSourceIface *iface)
239 {
240   iface->row_draggable = idrag_source_row_draggable;
241   iface->drag_data_delete = idrag_source_drag_data_delete;
242 }
243 
244 static void
pm_project_model_filter_drag_dest_iface_init(GtkTreeDragDestIface * iface)245 pm_project_model_filter_drag_dest_iface_init (GtkTreeDragDestIface *iface)
246 {
247   iface->drag_data_received = idrag_dest_drag_data_received;
248   iface->row_drop_possible = idrag_dest_row_drop_possible;
249 }
250 
251 static GType pm_project_model_filter_get_type (void);
252 
253 G_DEFINE_TYPE_WITH_CODE (PmProjectModelFilter,
254                          pm_project_model_filter,
255                          GTK_TYPE_TREE_MODEL_FILTER,
256                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
257                                                 pm_project_model_filter_drag_source_iface_init)
258                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST,
259                                                 pm_project_model_filter_drag_dest_iface_init));
260 
261 
262 static GtkTreeModel *
pm_project_model_filter_new(GtkTreeModel * child_model,GtkTreePath * root)263 pm_project_model_filter_new (GtkTreeModel          *child_model,
264                              GtkTreePath           *root)
265 {
266   PmProjectModelFilter *model;
267 
268   model = g_object_new (PM_TYPE_PROJECT_MODEL_FILTER,
269 			"child-model", child_model,
270 			"virtual-root", root,
271 			NULL);
272 
273   return GTK_TREE_MODEL (model);
274 }
275 
276 
277 
278 
279 
280 static void gbf_project_view_class_init    (GbfProjectViewClass *klass);
281 static void gbf_project_view_init          (GbfProjectView      *tree);
282 static void destroy                        (GtkWidget           *object);
283 
284 static void set_pixbuf                     (GtkCellLayout   	*layout,
285 					    GtkCellRenderer     *cell,
286 					    GtkTreeModel        *model,
287 					    GtkTreeIter         *iter,
288 					    gpointer             user_data);
289 static void set_text                       (GtkCellLayout   	*layout,
290 					    GtkCellRenderer     *cell,
291 					    GtkTreeModel        *model,
292 					    GtkTreeIter         *iter,
293 					    gpointer             user_data);
294 
295 
296 G_DEFINE_TYPE(GbfProjectView, gbf_project_view, GTK_TYPE_TREE_VIEW);
297 
298 static void
row_activated(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column)299 row_activated (GtkTreeView       *tree_view,
300 	       GtkTreePath       *path,
301 	       GtkTreeViewColumn *column)
302 {
303 	GtkTreeModel *model;
304 	GtkTreeIter iter;
305 	GbfTreeData *data;
306 	AnjutaProjectNode *node;
307 
308 	model = gtk_tree_view_get_model (tree_view);
309 
310 	gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
311 
312 	gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
313 			    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
314 			    -1);
315 
316 	node = gbf_tree_data_get_node (data);
317 	if (node)
318 	{
319 		switch (anjuta_project_node_get_node_type (node))
320 		{
321 		case ANJUTA_PROJECT_GROUP:
322 		case ANJUTA_PROJECT_ROOT:
323 		case ANJUTA_PROJECT_TARGET:
324 		case ANJUTA_PROJECT_MODULE:
325 		case ANJUTA_PROJECT_PACKAGE:
326 			if (!gtk_tree_view_row_expanded (tree_view, path))
327 			{
328 				gtk_tree_view_expand_row (tree_view, path, FALSE);
329 			}
330 			else
331 			{
332 				gtk_tree_view_collapse_row (tree_view, path);
333 			}
334 			break;
335 		default:
336 			g_signal_emit (G_OBJECT (tree_view),
337 			               signals [NODE_SELECTED], 0,
338 			               node);
339 			break;
340 		}
341 	}
342 }
343 
344 static void on_node_loaded (AnjutaPmProject *sender, AnjutaProjectNode *node, gboolean complete, GError *error, GbfProjectView *view);
345 
346 static void
dispose(GObject * object)347 dispose (GObject *object)
348 {
349 	GbfProjectView *view;
350 
351 	view = GBF_PROJECT_VIEW (object);
352 
353 	if (view->filter)
354 	{
355 		g_object_unref (G_OBJECT (view->filter));
356 		view->filter = NULL;
357 	}
358 	if (view->model)
359 	{
360 		AnjutaPmProject *old_project;
361 
362 		old_project = gbf_project_model_get_project (view->model);
363 		if (old_project != NULL)
364 		{
365 			g_signal_handlers_disconnect_by_func (old_project, G_CALLBACK (on_node_loaded), view);
366 		}
367 		g_object_unref (G_OBJECT (view->model));
368 		view->model = NULL;
369 	}
370 
371 	G_OBJECT_CLASS (gbf_project_view_parent_class)->dispose (object);
372 }
373 
374 static void
destroy(GtkWidget * object)375 destroy (GtkWidget *object)
376 {
377 	if (GTK_WIDGET_CLASS (gbf_project_view_parent_class)->destroy)
378 		(* GTK_WIDGET_CLASS (gbf_project_view_parent_class)->destroy) (object);
379 }
380 
381 static GdkPixbuf*
get_icon(GFile * file)382 get_icon (GFile *file)
383 {
384 	const gchar** icon_names;
385 	GtkIconInfo* icon_info;
386 	GIcon* icon;
387 	GdkPixbuf* pixbuf = NULL;
388 	GFileInfo* file_info;
389 	GError *error = NULL;
390 
391 	file_info = g_file_query_info (file,
392 				       G_FILE_ATTRIBUTE_STANDARD_ICON,
393 				       G_FILE_QUERY_INFO_NONE,
394 				       NULL,
395 				       &error);
396 
397 	if (file_info != NULL)
398 	{
399 		icon = g_file_info_get_icon(file_info);
400 		g_object_get (icon, "names", &icon_names, NULL);
401 		icon_info = gtk_icon_theme_choose_icon (gtk_icon_theme_get_default(),
402 							icon_names,
403 							ICON_SIZE,
404 							0);
405 		if (icon_info != NULL)
406 		{
407 			pixbuf = gtk_icon_info_load_icon (icon_info, NULL);
408 			gtk_icon_info_free(icon_info);
409 		}
410 		g_object_unref (file_info);
411 	}
412 
413 	if (pixbuf == NULL)
414 	{
415 		pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
416 						   GTK_STOCK_MISSING_IMAGE,
417 						   ICON_SIZE,
418 						   0,
419 						   NULL);
420 	}
421 
422 	return pixbuf;
423 }
424 
425 static void
set_pixbuf(GtkCellLayout * layout,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)426 set_pixbuf (GtkCellLayout *layout,
427 	    GtkCellRenderer *cell,
428 	    GtkTreeModel *model,
429 	    GtkTreeIter *iter,
430 	    gpointer user_data)
431 {
432 	GbfTreeData *data = NULL;
433 	GdkPixbuf *pixbuf = NULL;
434 
435 	gtk_tree_model_get (model, iter,
436 			    GBF_PROJECT_MODEL_COLUMN_DATA, &data, -1);
437 	g_return_if_fail (data != NULL);
438 	/* FIXME: segmentation fault with shortcut when corresponding
439 	 * data is removed before the shortcut, so data = NULL.
440 	 * Perhaps we can add a GtkTreeReference to the shortcut
441 	 * node to remove the shortcut when the node is destroyed */
442 
443 	if ((data->type == GBF_TREE_NODE_SHORTCUT) && (data->shortcut != NULL))
444  	{
445 		data = data->shortcut;
446 	}
447 	switch (data->type) {
448 		case GBF_TREE_NODE_SOURCE:
449 		{
450 			pixbuf = get_icon (data->source);
451 			break;
452 		}
453 		case GBF_TREE_NODE_ROOT:
454 			pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
455 							   GTK_STOCK_OPEN,
456 							   ICON_SIZE,
457 							   0,
458 							   NULL);
459 			break;
460 		case GBF_TREE_NODE_GROUP:
461 			pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
462 							   GTK_STOCK_DIRECTORY,
463 							   ICON_SIZE,
464 							   0,
465 							   NULL);
466 			break;
467 		case GBF_TREE_NODE_TARGET:
468 		{
469 			pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
470 							   GTK_STOCK_CONVERT,
471 							   ICON_SIZE,
472 							   0,
473 							   NULL);
474 			break;
475 		}
476 		case GBF_TREE_NODE_MODULE:
477 		{
478 			pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
479 							   GTK_STOCK_DND_MULTIPLE,
480 							   ICON_SIZE,
481 							   0,
482 							   NULL);
483 			break;
484 		}
485 		case GBF_TREE_NODE_PACKAGE:
486 		{
487 			pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default(),
488 							   GTK_STOCK_DND,
489 							   ICON_SIZE,
490 							   0,
491 							   NULL);
492 			break;
493 		}
494 		default:
495 			/* Can reach this if type = GBF_TREE_NODE_SHORTCUT. It
496 			 * means a shortcut with the original data removed */
497 			pixbuf = NULL;
498 	}
499 
500 	g_object_set (GTK_CELL_RENDERER (cell), "pixbuf", pixbuf, NULL);
501 	if (pixbuf)
502 		g_object_unref (pixbuf);
503 }
504 
505 static void
set_text(GtkCellLayout * layout,GtkCellRenderer * cell,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)506 set_text (GtkCellLayout *layout,
507 	  GtkCellRenderer *cell,
508 	  GtkTreeModel *model,
509 	  GtkTreeIter *iter,
510 	  gpointer user_data)
511 {
512 	GbfTreeData *data;
513 
514 	gtk_tree_model_get (model, iter, GBF_PROJECT_MODEL_COLUMN_DATA, &data, -1);
515 	/* data can be NULL just after gtk_tree_store_insert before
516 	calling gtk_tree_store_set */
517 	g_object_set (GTK_CELL_RENDERER (cell), "text",
518 		      data == NULL ? "" : data->name, NULL);
519 }
520 
521 static gboolean
search_equal_func(GtkTreeModel * model,gint column,const gchar * key,GtkTreeIter * iter,gpointer user_data)522 search_equal_func (GtkTreeModel *model, gint column,
523 		   const gchar *key, GtkTreeIter *iter,
524 		   gpointer user_data)
525 {
526 	GbfTreeData *data;
527 	gboolean ret = TRUE;
528 
529 	gtk_tree_model_get (model, iter, GBF_PROJECT_MODEL_COLUMN_DATA, &data, -1);
530 	if (strncmp (data->name, key, strlen (key)) == 0)
531 	    ret = FALSE;
532 	return ret;
533 }
534 
535 static gboolean
draw(GtkWidget * widget,cairo_t * cr)536 draw (GtkWidget *widget, cairo_t *cr)
537 {
538 	GtkTreeModel *view_model;
539 	GtkTreeModel *model = NULL;
540 	GtkTreeView *tree_view;
541 	gint event_handled = FALSE;
542 
543 	if (GTK_WIDGET_CLASS (gbf_project_view_parent_class)->draw != NULL)
544 		GTK_WIDGET_CLASS (gbf_project_view_parent_class)->draw (widget, cr);
545 
546 	tree_view = GTK_TREE_VIEW (widget);
547 	view_model = gtk_tree_view_get_model (tree_view);
548 	if (GTK_IS_TREE_MODEL_FILTER (view_model))
549 	{
550 		model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (view_model));
551 	}
552 	if (gtk_cairo_should_draw_window (cr, gtk_tree_view_get_bin_window (tree_view)) &&
553 	    model && GBF_IS_PROJECT_MODEL (model)) {
554 		GtkTreePath *root;
555 		GdkRectangle rect;
556 
557 		/* paint an horizontal ruler to separate the project
558 		 * tree from the target shortcuts */
559 
560 		root = gbf_project_model_get_project_root (GBF_PROJECT_MODEL (model));
561 		if (root) {
562 			if (view_model != model)
563 			{
564 				/* Convert path */
565 				GtkTreePath *child_path;
566 
567 				child_path = gtk_tree_model_filter_convert_child_path_to_path (GTK_TREE_MODEL_FILTER (view_model), root);
568 				gtk_tree_path_free (root);
569 				root = child_path;
570 			}
571 			gtk_tree_view_get_background_area (
572 				tree_view, root, gtk_tree_view_get_column (tree_view, 0), &rect);
573 			gtk_render_line (gtk_widget_get_style_context (widget),
574 			                 cr,
575 			                 rect.x, rect.y,
576 			                 rect.x + rect.width, rect.y);
577 			gtk_tree_path_free (root);
578 		}
579 	}
580 
581 	return event_handled;
582 }
583 
584 static void
gbf_project_view_class_init(GbfProjectViewClass * klass)585 gbf_project_view_class_init (GbfProjectViewClass *klass)
586 {
587 	GObjectClass     *g_object_class;
588 	GtkWidgetClass   *widget_class;
589 	GtkTreeViewClass *tree_view_class;
590 
591 	g_object_class = G_OBJECT_CLASS (klass);
592 	widget_class = GTK_WIDGET_CLASS (klass);
593 	tree_view_class = GTK_TREE_VIEW_CLASS (klass);
594 
595 	g_object_class->dispose = dispose;
596 	widget_class->destroy = destroy;
597 	widget_class->draw = draw;
598 	tree_view_class->row_activated = row_activated;
599 
600 	signals [NODE_SELECTED] =
601 		g_signal_new ("node-selected",
602 			      GBF_TYPE_PROJECT_VIEW,
603 			      G_SIGNAL_RUN_LAST,
604 			      G_STRUCT_OFFSET (GbfProjectViewClass,
605 					       node_selected),
606 			      NULL, NULL,
607 			      g_cclosure_marshal_VOID__POINTER,
608 			      G_TYPE_NONE, 1, G_TYPE_POINTER);
609 	signals[NODE_LOADED] =
610 		g_signal_new ("node-loaded",
611 				GBF_TYPE_PROJECT_VIEW,
612 				G_SIGNAL_RUN_LAST,
613 				G_STRUCT_OFFSET (GbfProjectViewClass,
614 				                 node_loaded),
615 				NULL, NULL,
616 				pm_cclosure_marshal_VOID__POINTER_BOOLEAN_BOXED,
617 				G_TYPE_NONE, 3,
618 				G_TYPE_POINTER,
619 				G_TYPE_BOOLEAN,
620 				G_TYPE_ERROR);
621 
622 }
623 
624 static gboolean
is_project_node_visible(GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)625 is_project_node_visible (GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
626 {
627 	GbfTreeData *data;
628 
629 	gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
630 		    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
631 		    -1);
632 
633 	return (data != NULL) && (gbf_tree_data_get_node (data) != NULL);
634 }
635 
636 static void
gbf_project_view_init(GbfProjectView * tree)637 gbf_project_view_init (GbfProjectView *tree)
638 {
639 	GtkTreeViewColumn *column;
640 	static GtkTargetEntry row_targets[] = {
641 		{ "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_WIDGET, 0 }
642 	};
643 
644 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree), FALSE);
645 	gtk_tree_view_set_enable_search (GTK_TREE_VIEW (tree), TRUE);
646 	gtk_tree_view_set_search_column (GTK_TREE_VIEW (tree), 0);
647 	gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (tree),
648 					     search_equal_func,
649 					     NULL, NULL);
650 
651 	gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (tree),
652 						GDK_BUTTON1_MASK,
653 						row_targets,
654 						G_N_ELEMENTS (row_targets),
655 						GDK_ACTION_MOVE);
656 	gtk_tree_view_enable_model_drag_dest (GTK_TREE_VIEW (tree),
657 					      row_targets,
658 					      G_N_ELEMENTS (row_targets),
659 					      GDK_ACTION_MOVE);
660 
661 	/* set renderer for files column. */
662 	column = gtk_tree_view_column_new ();
663 	pm_setup_project_renderer (GTK_CELL_LAYOUT (column));
664 	gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column);
665 
666 	/* Create model */
667 	tree->model = gbf_project_model_new (NULL);
668 	tree->filter = GTK_TREE_MODEL_FILTER (pm_project_model_filter_new (GTK_TREE_MODEL (tree->model), NULL));
669 	gtk_tree_model_filter_set_visible_func (tree->filter, is_project_node_visible, tree, NULL);
670 
671 	gtk_tree_view_set_model (GTK_TREE_VIEW (tree), GTK_TREE_MODEL (tree->filter));
672 }
673 
674 GtkWidget *
gbf_project_view_new(void)675 gbf_project_view_new (void)
676 {
677 	return GTK_WIDGET (g_object_new (GBF_TYPE_PROJECT_VIEW, NULL));
678 }
679 
680 AnjutaProjectNode *
gbf_project_view_find_selected(GbfProjectView * view,AnjutaProjectNodeType type)681 gbf_project_view_find_selected (GbfProjectView *view, AnjutaProjectNodeType type)
682 {
683 	AnjutaProjectNode *node = NULL;
684 	GbfTreeData *data;
685 
686 	g_return_val_if_fail (view != NULL, NULL);
687 	g_return_val_if_fail (GBF_IS_PROJECT_VIEW (view), NULL);
688 
689 	data = gbf_project_view_get_first_selected (view, NULL);
690 	if (data != NULL)
691 	{
692 
693 		node = gbf_tree_data_get_node (data);
694 
695 		/* walk up the hierarchy searching for a node of the given type */
696 		while ((node != NULL) && (type != ANJUTA_PROJECT_UNKNOWN) && (anjuta_project_node_get_node_type (node) != type))
697 		{
698 			node = anjuta_project_node_parent (node);
699 		}
700 	}
701 
702 	return node;
703 }
704 
705 AnjutaProjectNode *
gbf_project_view_find_selected_state(GtkTreeView * view,AnjutaProjectNodeState state)706 gbf_project_view_find_selected_state (GtkTreeView *view,
707                                       AnjutaProjectNodeState state)
708 {
709 	AnjutaProjectNode *node = NULL;
710 	GbfTreeData *data;
711 
712 	g_return_val_if_fail (view != NULL, NULL);
713 	g_return_val_if_fail (GBF_IS_PROJECT_VIEW (view), NULL);
714 
715 	data = gbf_project_view_get_first_selected (GBF_PROJECT_VIEW (view), NULL);
716 	if (data != NULL)
717 	{
718 
719 		node = gbf_tree_data_get_node (data);
720 
721 		/* walk up the hierarchy searching for a node of the given type */
722 		while ((node != NULL) && (state != 0) && !(anjuta_project_node_get_state (node) & state))
723 		{
724 			node = anjuta_project_node_parent (node);
725 		}
726 	}
727 
728 	return node;
729 }
730 
731 GbfTreeData *
gbf_project_view_get_first_selected(GbfProjectView * view,GtkTreeIter * selected)732 gbf_project_view_get_first_selected (GbfProjectView *view, GtkTreeIter* selected)
733 {
734 	GtkTreeSelection *selection;
735 	GbfTreeData *data = NULL;
736 	GtkTreeModel *model;
737 	GList *list;
738 
739 	g_return_val_if_fail (view != NULL, NULL);
740 	g_return_val_if_fail (GBF_IS_PROJECT_VIEW (view), NULL);
741 
742 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
743 	list = gtk_tree_selection_get_selected_rows(selection, &model);
744 	if (list != NULL)
745 	{
746 		GtkTreeIter iter;
747 
748 		if (gtk_tree_model_get_iter (model, &iter, list->data))
749 		{
750 			if (selected)
751 			{
752 				if (GTK_IS_TREE_MODEL_FILTER (model))
753 				{
754 					GtkTreeIter child_iter;
755 
756 					gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (model), &child_iter, &iter);
757 					*selected = child_iter;
758 				}
759 				else
760 				{
761 					*selected = iter;
762 				}
763 			}
764 
765 			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
766 				    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
767 			    	-1);
768 		}
769 		g_list_foreach (list, (GFunc)gtk_tree_path_free, NULL);
770 		g_list_free (list);
771 	}
772 
773 	return data;
774 }
775 
776 void
gbf_project_view_set_cursor_to_iter(GbfProjectView * view,GtkTreeIter * selected)777 gbf_project_view_set_cursor_to_iter (GbfProjectView *view,
778                                      GtkTreeIter *selected)
779 {
780 	GtkTreeIter view_iter;
781 
782 	if (pm_convert_project_iter_to_model_iter (GTK_TREE_MODEL (view->filter), &view_iter, selected))
783 	{
784 		GtkTreePath *path;
785 
786 		path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->filter), &view_iter);
787 		if (path)
788 		{
789 			gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), path);
790 
791 			gtk_tree_view_set_cursor (GTK_TREE_VIEW (view), path, NULL, FALSE);
792 			gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (view), path, NULL,
793 			                              TRUE, 0.5, 0.0);
794 			gtk_tree_path_free (path);
795 		}
796 	}
797 }
798 
799 
800 static void
on_each_get_data(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)801 on_each_get_data (GtkTreeModel *model,
802 			GtkTreePath *path,
803                         GtkTreeIter *iter,
804                         gpointer user_data)
805 {
806 	GList **selected = (GList **)user_data;
807 	GbfTreeData *data;
808 
809 	gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
810 		    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
811 		    -1);
812 
813 	*selected = g_list_prepend (*selected, data);
814 }
815 
816 GList *
gbf_project_view_get_all_selected(GbfProjectView * view)817 gbf_project_view_get_all_selected (GbfProjectView *view)
818 {
819 	GtkTreeSelection *selection;
820 	GList *selected = NULL;
821 
822 	g_return_val_if_fail (view != NULL, FALSE);
823 	g_return_val_if_fail (GBF_IS_PROJECT_VIEW (view), FALSE);
824 
825 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
826 	gtk_tree_selection_selected_foreach (selection, on_each_get_data, &selected);
827 
828 	return g_list_reverse (selected);
829 }
830 
831 static void
on_each_get_iter(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)832 on_each_get_iter (GtkTreeModel *model,
833 			GtkTreePath *path,
834                         GtkTreeIter *iter,
835                         gpointer user_data)
836 {
837 	GList **selected = (GList **)user_data;
838 
839 	*selected = g_list_prepend (*selected, gtk_tree_iter_copy (iter));
840 }
841 
842 GList *
gbf_project_view_get_all_selected_iter(GbfProjectView * view)843 gbf_project_view_get_all_selected_iter (GbfProjectView *view)
844 {
845 	GtkTreeSelection *selection;
846 	GList *selected = NULL;
847 
848 	g_return_val_if_fail (view != NULL, FALSE);
849 	g_return_val_if_fail (GBF_IS_PROJECT_VIEW (view), FALSE);
850 
851 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
852 	gtk_tree_selection_selected_foreach (selection, on_each_get_iter, &selected);
853 
854 	return g_list_reverse (selected);
855 }
856 
857 static void
gbf_project_view_update_shortcut(GbfProjectView * view,AnjutaProjectNode * parent)858 gbf_project_view_update_shortcut (GbfProjectView *view, AnjutaProjectNode *parent)
859 {
860 	GtkTreeIter child;
861 	gboolean valid;
862 
863 	/* Get all root node */
864 	valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (view->model), &child, NULL);
865 
866 	while (valid)
867 	{
868 		GbfTreeData *data;
869 		AnjutaProjectNode* old_node = NULL;
870 
871 		gtk_tree_model_get (GTK_TREE_MODEL (view->model), &child,
872 	  			 GBF_PROJECT_MODEL_COLUMN_DATA, &data,
873 	    			-1);
874 
875 		/* Shortcuts are always at the beginning */
876 		if (data->type != GBF_TREE_NODE_SHORTCUT) break;
877 
878 		old_node = gbf_tree_data_get_node (data);
879 		if (old_node == parent)
880 		{
881 			/* check children */
882 			gbf_project_view_update_tree (view, parent, &child);
883 		}
884 		valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (view->model), &child);
885 	}
886 }
887 
888 static gint
compare_node_name(gconstpointer a,gconstpointer b)889 compare_node_name (gconstpointer a, gconstpointer b)
890 {
891 	const AnjutaProjectNode *node = (const AnjutaProjectNode *)a;
892 	const gchar *name = (const gchar *)b;
893 
894 	return g_strcmp0 (anjuta_project_node_get_name (node), name);
895 }
896 
897 static GList *
list_visible_children(AnjutaProjectNode * parent)898 list_visible_children (AnjutaProjectNode *parent)
899 {
900  	AnjutaProjectNode *node;
901 	GList *list = NULL;
902 
903 	for (node = anjuta_project_node_first_child (parent); node != NULL; node = anjuta_project_node_next_sibling (node))
904 	{
905 		if (anjuta_project_node_get_full_type (node) & ANJUTA_PROJECT_FRAME) continue;
906 		if (anjuta_project_node_get_node_type (node) != ANJUTA_PROJECT_OBJECT)
907 		{
908 			list = g_list_prepend (list, node);
909 		}
910 		else
911 		{
912 			/* object node are hidden, get their children instead */
913 			GList *children = list_visible_children (node);
914 
915 			children = g_list_reverse (children);
916 			list = g_list_concat (children, list);
917 		}
918 	}
919 	list = g_list_reverse (list);
920 
921 	return list;
922 }
923 
924 void
gbf_project_view_update_tree(GbfProjectView * view,AnjutaProjectNode * parent,GtkTreeIter * iter)925 gbf_project_view_update_tree (GbfProjectView *view, AnjutaProjectNode *parent, GtkTreeIter *iter)
926 {
927 	GtkTreeIter child;
928 	GList *node;
929 	GList *nodes;
930 	GbfTreeData *data = NULL;
931 
932 	/* Get all new nodes */
933 	nodes = list_visible_children (parent);
934 
935 	/* walk the tree nodes */
936 	if (gtk_tree_model_iter_children (GTK_TREE_MODEL (view->model), &child, iter))
937 	{
938 		gboolean valid = TRUE;
939 
940 		while (valid) {
941 			data = NULL;
942 			AnjutaProjectNode* data_node = NULL;
943 
944 			/* Look for current node */
945 			gtk_tree_model_get (GTK_TREE_MODEL (view->model), &child,
946 				GBF_PROJECT_MODEL_COLUMN_DATA, &data,
947 				-1);
948 
949 			data_node = gbf_tree_data_get_node (data);
950 
951 			/* Skip shortcuts */
952 			if (data->type == GBF_TREE_NODE_SHORTCUT)
953 			{
954 				valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (view->model), &child);
955 				continue;
956 			}
957 
958 			if (data->type == GBF_TREE_NODE_UNKNOWN)
959 			{
960 				node = g_list_find_custom (nodes, data->name, compare_node_name);
961 				if (node != NULL)
962 				{
963 					GtkTreePath *path;
964 					GtkTreePath *child_path;
965 					GtkTreeModelFilter *filter;
966 					gboolean expanded;
967 					gboolean shortcut;
968 
969 					expanded = data->expanded;
970 					shortcut = data->has_shortcut;
971 					data_node = (AnjutaProjectNode *)node->data;
972 					gbf_tree_data_free (data);
973 					data = gbf_tree_data_new_node (data_node);
974 					gtk_tree_store_set (GTK_TREE_STORE (view->model), &child,
975 								GBF_PROJECT_MODEL_COLUMN_DATA, data,
976 								-1);
977 
978 					/* Node already exist, remove it from the list */
979 					nodes = g_list_delete_link (nodes, node);
980 
981 					/* Update shortcut */
982 					gbf_project_view_update_shortcut (view, data_node);
983 
984 					/* update recursively */
985 					gbf_project_view_update_tree (view, data_node, &child);
986 
987 					if (shortcut)
988 					{
989 						gboolean expanded;
990 						GtkTreeIter iter;
991 
992 						gbf_project_model_add_target_shortcut (view->model, &iter, data, NULL, &expanded);
993 						if (expanded)
994 						{
995 							/* Expand shortcut */
996 							filter = GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
997 							path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->model), &iter);
998 							child_path = gtk_tree_model_filter_convert_child_path_to_path (filter, path);
999 							gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), child_path);
1000 							gtk_tree_path_free (child_path);
1001 							gtk_tree_path_free (path);
1002 						}
1003 					}
1004 					data->expanded = expanded;
1005 					if (expanded)
1006 					{
1007 						filter = GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
1008 						path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->model), &child);
1009 						child_path = gtk_tree_model_filter_convert_child_path_to_path (filter, path);
1010 						gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), child_path);
1011 						expanded = gtk_tree_view_row_expanded (GTK_TREE_VIEW (view), child_path);
1012 						gtk_tree_path_free (child_path);
1013 						gtk_tree_path_free (path);
1014 					}
1015 				}
1016 				valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (view->model), &child);
1017 			}
1018 			else
1019 			{
1020 				node = g_list_find (nodes, data_node);
1021 				if (node != NULL)
1022 				{
1023 					/* Node already exist, remove it from the list */
1024 					nodes = g_list_delete_link (nodes, node);
1025 
1026 					/* Update shortcut */
1027 					gbf_project_view_update_shortcut (view, data_node);
1028 
1029 					/* update recursively */
1030 					gbf_project_view_update_tree (view, data_node, &child);
1031 
1032 					valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (view->model), &child);
1033 				}
1034 				else
1035 				{
1036 					/* Node has been deleted */
1037 					valid = gbf_project_model_remove (view->model, &child);
1038 				}
1039 			}
1040 		}
1041 	}
1042 
1043 	/* add the remaining sources, targets and groups */
1044 	for (node = nodes; node; node = node->next)
1045 	{
1046 		gbf_project_model_add_node (view->model, node->data, iter, 0);
1047 	}
1048 
1049 	g_list_free (nodes);
1050 
1051 	/* Expand parent, needed if the parent hasn't any children when it was created */
1052 	if (iter != NULL)
1053 	{
1054 		/* Check parent data */
1055 		gtk_tree_model_get (GTK_TREE_MODEL (view->model), iter,
1056 			GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1057 			-1);
1058 		if (data->expanded)
1059 		{
1060 			GtkTreePath *path;
1061 			GtkTreePath *child_path;
1062 			GtkTreeModelFilter *filter;
1063 
1064 			filter = GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
1065 			path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->model), iter);
1066 			child_path = gtk_tree_model_filter_convert_child_path_to_path (filter, path);
1067 			gtk_tree_view_expand_to_path (GTK_TREE_VIEW (view), child_path);
1068 			gtk_tree_path_free (child_path);
1069 			gtk_tree_path_free (path);
1070 			data->expanded = FALSE;
1071 		}
1072 	}
1073 }
1074 
1075 /* Shorcuts functions
1076  *---------------------------------------------------------------------------*/
1077 
1078 void
gbf_project_view_sort_shortcuts(GbfProjectView * view)1079 gbf_project_view_sort_shortcuts (GbfProjectView *view)
1080 {
1081 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (view->model),
1082 	                                      GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
1083 	                                      GTK_SORT_ASCENDING);
1084 	gbf_project_model_sort_shortcuts (view->model);
1085 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (view->model),
1086 	                                      GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
1087 	                                      GTK_SORT_ASCENDING);
1088 
1089 }
1090 
1091 GList *
gbf_project_view_get_shortcut_list(GbfProjectView * view)1092 gbf_project_view_get_shortcut_list (GbfProjectView *view)
1093 {
1094 	GList *list = NULL;
1095 	GtkTreeModel* model;
1096 	gboolean valid;
1097 	GtkTreeIter iter;
1098 
1099 	model = GTK_TREE_MODEL (view->model);
1100 	if (model != NULL)
1101 	{
1102 		for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, NULL);
1103 			valid == TRUE;
1104 			valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
1105 		{
1106 			GbfTreeData *data;
1107 			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
1108 				GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1109 				-1);
1110 
1111 			if ((data->type == GBF_TREE_NODE_SHORTCUT) && (data->shortcut != NULL))
1112 			{
1113 				GtkTreeIter iter;
1114 
1115 				if (gbf_project_model_find_tree_data (view->model, &iter, data->shortcut))
1116 				{
1117 					GString *str;
1118 					GtkTreeIter child;
1119 
1120 					str = g_string_new (NULL);
1121 					do
1122 					{
1123 						GbfTreeData *data;
1124 
1125 						child = iter;
1126 						gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
1127 								GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1128 								-1);
1129 
1130 						if (data->node != NULL)
1131 						{
1132 							if (str->len != 0) g_string_prepend (str, "//");
1133 							g_string_prepend (str, anjuta_project_node_get_name (data->node));
1134 						}
1135 					}
1136 					while (gtk_tree_model_iter_parent (model, &iter, &child));
1137 					list = g_list_prepend (list, str->str);
1138 					g_string_free (str, FALSE);
1139 				}
1140 			}
1141 		}
1142 		list = g_list_reverse (list);
1143 	}
1144 
1145 	return list;
1146 }
1147 
1148 static void
save_expanded_node(GtkTreeView * view,GtkTreePath * path,gpointer user_data)1149 save_expanded_node (GtkTreeView *view, GtkTreePath *path, gpointer user_data)
1150 {
1151 	GList **list = (GList **)user_data;
1152 	GtkTreeModel *model;
1153 	GtkTreeIter iter;
1154 
1155 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1156 
1157 	if (gtk_tree_model_get_iter (model, &iter, path))
1158 	{
1159 		GString *str;
1160 		GtkTreeIter child;
1161 
1162 		str = g_string_new (NULL);
1163 		do
1164 		{
1165 			GbfTreeData *data;
1166 
1167 			child = iter;
1168 			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
1169 				GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1170 				-1);
1171 
1172 			if (data->node != NULL)
1173 			{
1174 				if (str->len != 0) g_string_prepend (str, "//");
1175 				g_string_prepend (str, anjuta_project_node_get_name (data->node));
1176 			}
1177 		}
1178 		while (gtk_tree_model_iter_parent (model, &iter, &child));
1179 
1180 		*list = g_list_prepend (*list, str->str);
1181 		g_string_free (str, FALSE);
1182 	}
1183 }
1184 
1185 GList *
gbf_project_view_get_expanded_list(GbfProjectView * view)1186 gbf_project_view_get_expanded_list (GbfProjectView *view)
1187 {
1188 	GList *list = NULL;
1189 
1190 	gtk_tree_view_map_expanded_rows (GTK_TREE_VIEW (view), save_expanded_node, &list);
1191 	list = g_list_reverse (list);
1192 
1193 	return list;
1194 }
1195 
1196 void
gbf_project_view_remove_all_shortcut(GbfProjectView * view)1197 gbf_project_view_remove_all_shortcut (GbfProjectView* view)
1198 {
1199 	GtkTreeModel* model;
1200 	gboolean valid;
1201 	GtkTreeIter iter;
1202 
1203 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1204 
1205 	/* Remove all current shortcuts */
1206 	for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, NULL);
1207 		valid == TRUE;)
1208 	{
1209 		GbfTreeData *data;
1210 
1211 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
1212 		    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1213 	    	-1);
1214 
1215 		if (data->type == GBF_TREE_NODE_SHORTCUT)
1216 		{
1217 			valid = gbf_project_model_remove (GBF_PROJECT_MODEL (model), &iter);
1218 		}
1219 		else
1220 		{
1221 			/* No more shortcut */
1222 			break;
1223 		}
1224 	}
1225 }
1226 
1227 void
gbf_project_view_set_shortcut_list(GbfProjectView * view,GList * shortcuts)1228 gbf_project_view_set_shortcut_list (GbfProjectView *view, GList *shortcuts)
1229 {
1230 	GList *item;
1231 
1232 	gbf_project_model_set_default_shortcut (view->model, shortcuts == NULL);
1233 
1234 	for (item = g_list_first (shortcuts); item != NULL; item = g_list_next (item))
1235 	{
1236 		gchar *name = (gchar *)item->data;
1237 		gchar *end;
1238 		GtkTreeIter iter;
1239 		GtkTreeIter *parent = NULL;
1240 
1241 		do
1242 		{
1243 			end = strstr (name, "/" "/");   /* Avoid troubles with auto indent */
1244 			if (end != NULL) *end = '\0';
1245 			if (*name != '\0')
1246 			{
1247 				if (!gbf_project_model_find_child_name (view->model, &iter, parent, name))
1248 				{
1249 					GbfTreeData *data;
1250 
1251 					/* Create proxy node */
1252 					data = gbf_tree_data_new_proxy (name, FALSE);
1253 					gtk_tree_store_append (GTK_TREE_STORE (view->model), &iter, parent);
1254 					gtk_tree_store_set (GTK_TREE_STORE (view->model), &iter,
1255 							    GBF_PROJECT_MODEL_COLUMN_DATA, data,
1256 							    -1);
1257 					if (end == NULL)
1258 					{
1259 						data->has_shortcut = TRUE;
1260 
1261 						/* Create another proxy at root level to keep shortcut order */
1262 						data = gbf_tree_data_new_proxy (name, FALSE);
1263 						gtk_tree_store_append (GTK_TREE_STORE (view->model), &iter, NULL);
1264 						gtk_tree_store_set (GTK_TREE_STORE (view->model), &iter,
1265 								    GBF_PROJECT_MODEL_COLUMN_DATA, data,
1266 								    -1);
1267 					}
1268 				}
1269 				else
1270 				{
1271 					GbfTreeData *data;
1272 
1273 					gtk_tree_model_get (GTK_TREE_MODEL (view->model), &iter,
1274 			    			GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1275 		    				-1);
1276 					if (end == NULL) data->has_shortcut = TRUE;
1277 				}
1278 				parent = &iter;
1279 			}
1280 			if (end != NULL)
1281 			{
1282 				*end = '/';
1283 				name = end + 2;
1284 			}
1285 		}
1286 		while (end != NULL);
1287 	}
1288 
1289 	return;
1290 }
1291 
1292 void
gbf_project_view_set_expanded_list(GbfProjectView * view,GList * expand)1293 gbf_project_view_set_expanded_list (GbfProjectView *view, GList *expand)
1294 {
1295 	GList *item;
1296 
1297 	for (item = g_list_first (expand); item != NULL; item = g_list_next (item))
1298 	{
1299 		gchar *name = (gchar *)item->data;
1300 		gchar *end;
1301 		GtkTreeIter iter;
1302 		GtkTreeIter *parent = NULL;
1303 
1304 		do
1305 		{
1306 			end = strstr (name, "/" "/");   /* Avoid troubles with auto indent */
1307 			if (end != NULL) *end = '\0';
1308 			if (*name != '\0')
1309 			{
1310 				if (!gbf_project_model_find_child_name (view->model, &iter, parent, name))
1311 				{
1312 					GbfTreeData *data;
1313 
1314 					/* Create proxy node */
1315 					data = gbf_tree_data_new_proxy (name, TRUE);
1316 					gtk_tree_store_append (GTK_TREE_STORE (view->model), &iter, parent);
1317 					gtk_tree_store_set (GTK_TREE_STORE (view->model), &iter,
1318 							    GBF_PROJECT_MODEL_COLUMN_DATA, data,
1319 							    -1);
1320 				}
1321 				else
1322 				{
1323 					GbfTreeData *data;
1324 
1325 					gtk_tree_model_get (GTK_TREE_MODEL (view->model), &iter,
1326 			    			GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1327 		    				-1);
1328 					data->expanded = TRUE;
1329 				}
1330 				parent = &iter;
1331 			}
1332 			if (end != NULL)
1333 			{
1334 				*end = '/';
1335 				name = end + 2;
1336 			}
1337 		}
1338 		while (end != NULL);
1339 	}
1340 
1341 	return;
1342 }
1343 
1344 AnjutaProjectNode *
gbf_project_view_get_node_from_iter(GbfProjectView * view,GtkTreeIter * iter)1345 gbf_project_view_get_node_from_iter (GbfProjectView *view, GtkTreeIter *iter)
1346 {
1347 	return gbf_project_model_get_node (view->model, iter);
1348 }
1349 
1350 AnjutaProjectNode *
gbf_project_view_get_node_from_file(GbfProjectView * view,AnjutaProjectNodeType type,GFile * file)1351 gbf_project_view_get_node_from_file (GbfProjectView *view, AnjutaProjectNodeType type, GFile *file)
1352 {
1353 	GtkTreeIter iter;
1354 	AnjutaProjectNode *node = NULL;
1355 
1356 	if (gbf_project_model_find_file (view->model, &iter, NULL, gbf_tree_node_type_from_project (type), file))
1357 	{
1358 
1359 		node = gbf_project_model_get_node (view->model, &iter);
1360 	}
1361 
1362 	return node;
1363 }
1364 
1365 gboolean
gbf_project_view_remove_data(GbfProjectView * view,GbfTreeData * data,GError ** error)1366 gbf_project_view_remove_data (GbfProjectView *view, GbfTreeData *data, GError **error)
1367 {
1368 	GtkTreeIter iter;
1369 
1370 	if (gbf_project_model_find_tree_data (view->model, &iter, data))
1371 	{
1372 		gbf_project_model_remove (view->model, &iter);
1373 
1374 		return TRUE;
1375 	}
1376 	else
1377 	{
1378 		return FALSE;
1379 	}
1380 }
1381 
1382 static void
on_node_loaded(AnjutaPmProject * sender,AnjutaProjectNode * node,gboolean complete,GError * error,GbfProjectView * view)1383 on_node_loaded (AnjutaPmProject *sender, AnjutaProjectNode *node, gboolean complete, GError *error, GbfProjectView *view)
1384 {
1385 	if (error != NULL)
1386 	{
1387 		g_warning ("unable to load node");
1388 		g_signal_emit (G_OBJECT (view), NODE_LOADED, 0, NULL, complete, error);
1389 	}
1390 	else
1391 	{
1392 		GtkTreeIter iter;
1393 		gboolean found;
1394 
1395 		gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (view->model),
1396 		                                      GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
1397 		                                      GTK_SORT_ASCENDING);
1398 
1399 		found = gbf_project_model_find_node (view->model, &iter, NULL, node);
1400 		if (!found)
1401 		{
1402 			if (anjuta_project_node_parent (node) != NULL)
1403 			{
1404 				g_critical ("Unable to find node %s", anjuta_project_node_get_name (node));
1405 			}
1406 			else
1407 			{
1408 				GtkTreePath *path;
1409 				GtkTreePath *child_path;
1410 				GtkTreeModelFilter *filter;
1411 
1412 				if (!gbf_project_model_find_child_name (view->model, &iter, NULL, anjuta_project_node_get_name (node)))
1413 				{
1414 					gbf_project_model_add_node (view->model, node, NULL, 0);
1415 					gtk_tree_model_get_iter_first (GTK_TREE_MODEL (view->model), &iter);
1416 				}
1417 				else
1418 				{
1419 					GbfTreeData *data;
1420 					GbfTreeData *new_data;
1421 
1422 					/* Replace with new node */
1423 					gtk_tree_model_get (GTK_TREE_MODEL (view->model), &iter,
1424 						GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1425 						-1);
1426 					new_data = gbf_tree_data_new_node (node);
1427 					gtk_tree_store_set (GTK_TREE_STORE (view->model), &iter,
1428 								GBF_PROJECT_MODEL_COLUMN_DATA, new_data,
1429 								-1);
1430 					gbf_tree_data_free (data);
1431 					gbf_project_view_update_tree (view, node, &iter);
1432 				}
1433 
1434 				/* Expand root node */
1435 				path = gtk_tree_model_get_path (GTK_TREE_MODEL (view->model), &iter);
1436 				filter = GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
1437 				child_path = gtk_tree_model_filter_convert_child_path_to_path (filter, path);
1438 				if (child_path != NULL) gtk_tree_view_expand_row (GTK_TREE_VIEW (view), child_path, FALSE);
1439 				gtk_tree_path_free (child_path);
1440 				gtk_tree_path_free (path);
1441 			}
1442 		}
1443 		else
1444 		{
1445 			gbf_project_view_update_tree (view, node, &iter);
1446 		}
1447 		gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (view->model),
1448 		                                      GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
1449 		                                      GTK_SORT_ASCENDING);
1450 
1451 		g_signal_emit (G_OBJECT (view), signals[NODE_LOADED], 0, found ? &iter : NULL, complete, NULL);
1452 	}
1453 
1454 	if (complete)
1455 	{
1456 		// Add shortcut for all new primary targets
1457 		gbf_project_model_set_default_shortcut (view->model, TRUE);
1458 	}
1459 }
1460 
1461 
1462 void
gbf_project_view_set_project(GbfProjectView * view,AnjutaPmProject * project)1463 gbf_project_view_set_project (GbfProjectView *view, AnjutaPmProject *project)
1464 {
1465 	AnjutaPmProject *old_project;
1466 
1467 	old_project = gbf_project_model_get_project (view->model);
1468 	if (old_project != NULL)
1469 	{
1470 		g_signal_handlers_disconnect_by_func (old_project, G_CALLBACK (on_node_loaded), view);
1471 	}
1472 
1473 	g_signal_connect (project, "loaded", G_CALLBACK (on_node_loaded), view);
1474 
1475 	gbf_project_model_set_project (view->model, project);
1476 }
1477 
1478 void
gbf_project_view_set_parent_view(GbfProjectView * view,GbfProjectView * parent,GtkTreePath * root)1479 gbf_project_view_set_parent_view (GbfProjectView *view,
1480                                   GbfProjectView *parent,
1481                                   GtkTreePath *root)
1482 {
1483 
1484 	if (view->model != NULL) g_object_unref (view->model);
1485 	if (view->filter != NULL) g_object_unref (view->model);
1486 
1487 	view->model = g_object_ref (parent->model);
1488 	view->filter = GTK_TREE_MODEL_FILTER (pm_project_model_filter_new (GTK_TREE_MODEL (view->model), root));
1489 	gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL (view->filter));
1490 }
1491 
1492 void
gbf_project_view_set_visible_func(GbfProjectView * view,GtkTreeModelFilterVisibleFunc func,gpointer data,GDestroyNotify destroy)1493 gbf_project_view_set_visible_func (GbfProjectView *view,
1494                                    GtkTreeModelFilterVisibleFunc func,
1495                                    gpointer data,
1496                                    GDestroyNotify destroy)
1497 {
1498 	if (func == NULL)
1499 	{
1500 		gtk_tree_model_filter_set_visible_func (view->filter, is_project_node_visible, view, NULL);
1501 	}
1502 	else
1503 	{
1504 		gtk_tree_model_filter_set_visible_func (view->filter, func, data, destroy);
1505 	}
1506 	gtk_tree_model_filter_refilter (view->filter);
1507 }
1508 
1509 gboolean
gbf_project_view_find_file(GbfProjectView * view,GtkTreeIter * iter,GFile * file,GbfTreeNodeType type)1510 gbf_project_view_find_file (GbfProjectView *view, GtkTreeIter* iter, GFile *file, GbfTreeNodeType type)
1511 {
1512 	return gbf_project_model_find_file (view->model, iter, NULL, type, file);
1513 }
1514 
1515 GbfProjectModel *
gbf_project_view_get_model(GbfProjectView * view)1516 gbf_project_view_get_model (GbfProjectView *view)
1517 {
1518 	return view->model;
1519 }
1520 
1521 gboolean
gbf_project_view_get_project_root(GbfProjectView * view,GtkTreeIter * iter)1522 gbf_project_view_get_project_root (GbfProjectView *view, GtkTreeIter *iter)
1523 {
1524 	GtkTreeModel *model;
1525 	GtkTreeModel *view_model;
1526 	GtkTreePath *path;
1527 	gboolean ok = FALSE;
1528 
1529 	model = gtk_tree_view_get_model (GTK_TREE_VIEW (view));
1530 	view_model = model;
1531 	if (GTK_IS_TREE_MODEL_FILTER (model))
1532 	{
1533 		model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (view_model));
1534 	}
1535 
1536 	path = gbf_project_model_get_project_root (GBF_PROJECT_MODEL (model));
1537 	if (path)
1538 	{
1539 		ok = gtk_tree_model_get_iter (model, iter, path);
1540 		gtk_tree_path_free (path);
1541 	}
1542 
1543 	return ok;
1544 }
1545 
1546 /* Public functions
1547  *---------------------------------------------------------------------------*/
1548 
1549 GtkCellLayout *
pm_setup_project_renderer(GtkCellLayout * layout)1550 pm_setup_project_renderer (GtkCellLayout *layout)
1551 {
1552 	GtkCellRenderer *renderer;
1553 
1554 	renderer = gtk_cell_renderer_pixbuf_new ();
1555 	gtk_cell_layout_pack_start (layout, renderer, FALSE);
1556 	gtk_cell_layout_set_cell_data_func (layout, renderer, set_pixbuf, NULL, NULL);
1557 
1558 	renderer = gtk_cell_renderer_text_new ();
1559 	gtk_cell_layout_pack_start (layout, renderer, FALSE);
1560 	gtk_cell_layout_set_cell_data_func (layout, renderer, set_text, NULL, NULL);
1561 
1562 	return layout;
1563 }
1564 
1565 gboolean
pm_convert_project_iter_to_model_iter(GtkTreeModel * model,GtkTreeIter * model_iter,GtkTreeIter * project_iter)1566 pm_convert_project_iter_to_model_iter (GtkTreeModel *model,
1567                                        GtkTreeIter *model_iter,
1568                                        GtkTreeIter *project_iter)
1569 {
1570 	gboolean found = TRUE;
1571 
1572 	g_return_val_if_fail (GTK_IS_TREE_MODEL_FILTER (model), FALSE);
1573 
1574 	/* Check if we can find a direct correspondance */
1575 	if ((project_iter == NULL) || !gtk_tree_model_filter_convert_child_iter_to_iter (
1576 			GTK_TREE_MODEL_FILTER (model), model_iter, project_iter))
1577 	{
1578 		GtkTreeModel *project_model = gtk_tree_model_filter_get_model (GTK_TREE_MODEL_FILTER (model));
1579 
1580 		found = FALSE;
1581 
1582 		/* Check if it is a shortcut or a child of a shortcut */
1583 		if (project_iter != NULL)
1584 		{
1585 			GbfTreeData *data;
1586 
1587 			gtk_tree_model_get (project_model, project_iter,
1588 			                    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1589 			                    -1);
1590 
1591 			if ((data != NULL) && (data->node != NULL))
1592 			{
1593 				/* Select the corresponding node */
1594 				GtkTreePath *path;
1595 				GtkTreeIter root;
1596 				GtkTreeIter iter;
1597 				gboolean valid = FALSE;
1598 
1599 				path = gbf_project_model_get_project_root (GBF_PROJECT_MODEL (project_model));
1600 				if (path)
1601 				{
1602 					valid = gtk_tree_model_get_iter (project_model, &root, path);
1603 					gtk_tree_path_free (path);
1604 				}
1605 
1606 				if (valid && gbf_project_model_find_node (GBF_PROJECT_MODEL (project_model), &iter, &root, data->node))
1607 				{
1608 					found = gtk_tree_model_filter_convert_child_iter_to_iter (
1609 							GTK_TREE_MODEL_FILTER (model), model_iter, &iter);
1610 				}
1611 			}
1612 		}
1613 
1614 		/* Try to select root node */
1615 		if (!found)
1616 		{
1617 
1618 			GtkTreePath *root_path;
1619 
1620 			root_path = gbf_project_model_get_project_root (GBF_PROJECT_MODEL (project_model));
1621 			if (root_path)
1622 			{
1623 				GtkTreePath *path;
1624 				path = gtk_tree_model_filter_convert_child_path_to_path (
1625 							GTK_TREE_MODEL_FILTER (model), root_path);
1626 				if (path)
1627 				{
1628 					found = gtk_tree_model_get_iter	(model, model_iter, path);
1629 					gtk_tree_path_free (path);
1630 				}
1631 				gtk_tree_path_free (root_path);
1632 			}
1633 		}
1634 
1635 		/* Take the first node */
1636 		if (!found)
1637 		{
1638 			found = gtk_tree_model_get_iter_first (model, model_iter);
1639 		}
1640 	}
1641 
1642 	return found;
1643 }
1644 
1645