1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /*
3  * Copyright (C) 2002 Dave Camp
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * Authors: Dave Camp <dave@ximian.com>
21  *          Gustavo Gir�ldez <gustavo.giraldez@gmx.net>
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <string.h>
29 #include <glib-object.h>
30 #include <gtk/gtk.h>
31 #include <glib/gi18n.h>
32 #include <gio/gio.h>
33 
34 
35 #include "project.h"
36 #include "project-util.h"
37 #include "project-model.h"
38 
39 
40 struct _GbfProjectModelPrivate {
41 	AnjutaPmProject      *proj;
42 	gulong               project_updated_handler;
43 
44 	GtkTreeRowReference *root;
45 	GtkTreeRowReference *root_group;
46 	GList               *shortcuts;
47 
48 	gboolean default_shortcut;	   /* Add shortcut for each primary node */
49 };
50 
51 enum {
52 	PROP_NONE,
53 	PROP_PROJECT
54 };
55 
56 
57 /* Function prototypes ------------- */
58 
59 static void     gbf_project_model_class_init         (GbfProjectModelClass   *klass);
60 static void     gbf_project_model_instance_init      (GbfProjectModel        *tree);
61 
62 static void     load_project                         (GbfProjectModel        *model,
63 						      AnjutaPmProject  *proj);
64 static void     insert_empty_node                    (GbfProjectModel        *model);
65 static void     unload_project                       (GbfProjectModel        *model);
66 
67 static gint     default_sort_func                    (GtkTreeModel           *model,
68 						      GtkTreeIter            *iter_a,
69 						      GtkTreeIter            *iter_b,
70 						      gpointer                user_data);
71 
72 static GtkTreeStoreClass *parent_class = NULL;
73 
74 
75 /* Implementation ---------------- */
76 
77 /* Helper functions */
78 
79 /* Type & interfaces initialization */
80 
81 static void
gbf_project_model_class_init_trampoline(gpointer klass,gpointer data)82 gbf_project_model_class_init_trampoline (gpointer klass,
83 					 gpointer data)
84 {
85 	parent_class = g_type_class_ref (GTK_TYPE_TREE_STORE);
86 	gbf_project_model_class_init (klass);
87 }
88 
89 GType
gbf_project_model_get_type(void)90 gbf_project_model_get_type (void)
91 {
92 	static GType object_type = 0;
93 	if (object_type == 0) {
94 		static const GTypeInfo object_info = {
95 		    sizeof (GbfProjectModelClass),
96 		    NULL,		/* base_init */
97 		    NULL,		/* base_finalize */
98 		    gbf_project_model_class_init_trampoline,
99 		    NULL,		/* class_finalize */
100 		    NULL,               /* class_data */
101 		    sizeof (GbfProjectModel),
102 		    0,                  /* n_preallocs */
103 		    (GInstanceInitFunc) gbf_project_model_instance_init
104 		};
105 
106 		object_type = g_type_register_static (
107 			GTK_TYPE_TREE_STORE, "GbfProjectModel",
108 			&object_info, 0);
109 	}
110 	return object_type;
111 }
112 
113 static void
get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)114 get_property (GObject    *object,
115 	      guint       prop_id,
116 	      GValue     *value,
117 	      GParamSpec *pspec)
118 {
119         GbfProjectModel *model = GBF_PROJECT_MODEL (object);
120 
121         switch (prop_id) {
122         case PROP_PROJECT:
123                 g_value_set_pointer (value, model->priv->proj);
124                 break;
125         default:
126                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
127                 break;
128         }
129 }
130 
131 static void
set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)132 set_property (GObject      *object,
133 	      guint         prop_id,
134 	      const GValue *value,
135 	      GParamSpec   *pspec)
136 {
137         GbfProjectModel *model = GBF_PROJECT_MODEL (object);
138 
139         switch (prop_id) {
140         case PROP_PROJECT:
141 		gbf_project_model_set_project (model, g_value_get_pointer (value));
142                 break;
143         default:
144                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
145                 break;
146         }
147 }
148 
149 static void
dispose(GObject * obj)150 dispose (GObject *obj)
151 {
152 	GbfProjectModel *model = GBF_PROJECT_MODEL (obj);
153 
154 	if (model->priv->proj) {
155 		unload_project (model);
156 	}
157 
158 	G_OBJECT_CLASS (parent_class)->finalize (obj);
159 }
160 
161 static void
finalize(GObject * obj)162 finalize (GObject *obj)
163 {
164 	GbfProjectModel *model = GBF_PROJECT_MODEL (obj);
165 
166 	g_free (model->priv);
167 
168 	G_OBJECT_CLASS (parent_class)->dispose (obj);
169 }
170 
171 static void
gbf_project_model_class_init(GbfProjectModelClass * klass)172 gbf_project_model_class_init (GbfProjectModelClass *klass)
173 {
174 	parent_class = g_type_class_peek_parent (klass);
175 
176 	G_OBJECT_CLASS (klass)->dispose = dispose;
177 	G_OBJECT_CLASS (klass)->finalize = finalize;
178 	G_OBJECT_CLASS (klass)->get_property = get_property;
179 	G_OBJECT_CLASS (klass)->set_property = set_property;
180 
181 	g_object_class_install_property
182                 (G_OBJECT_CLASS (klass), PROP_PROJECT,
183                  g_param_spec_pointer ("project",
184                                        _("Project"),
185                                        _("GbfProject Object"),
186                                        G_PARAM_READWRITE));
187 }
188 
189 static void
gbf_project_model_instance_init(GbfProjectModel * model)190 gbf_project_model_instance_init (GbfProjectModel *model)
191 {
192 	static GType types [GBF_PROJECT_MODEL_NUM_COLUMNS];
193 
194 	types [GBF_PROJECT_MODEL_COLUMN_DATA] = G_TYPE_POINTER;
195 
196 	gtk_tree_store_set_column_types (GTK_TREE_STORE (model),
197 					 GBF_PROJECT_MODEL_NUM_COLUMNS,
198 					 types);
199 
200 	model->priv = g_new0 (GbfProjectModelPrivate, 1);
201 	model->priv->default_shortcut = TRUE;
202 
203 	/* sorting function */
204 	gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (model),
205 						 default_sort_func,
206 						 NULL, NULL);
207 	gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model),
208 					      GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
209 					      GTK_SORT_ASCENDING);
210 
211 	insert_empty_node (model);
212 }
213 
214 /* Model data functions ------------ */
215 
216 /* Remove node without checking its shortcuts */
217 static gboolean
gbf_project_model_remove_children(GbfProjectModel * model,GtkTreeIter * iter)218 gbf_project_model_remove_children (GbfProjectModel *model, GtkTreeIter *iter)
219 {
220 	GtkTreeIter child;
221 	GbfTreeData *data;
222 	gboolean valid;
223 
224 	/* Free all children */
225 	valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &child, iter);
226 	while (valid)
227 	{
228 		valid = gbf_project_model_remove_children (model, &child);
229 
230 		/* Free children node */
231 		gtk_tree_model_get (GTK_TREE_MODEL (model), &child,
232 		   	 GBF_PROJECT_MODEL_COLUMN_DATA, &data,
233 		    	-1);
234 		valid = gtk_tree_store_remove (GTK_TREE_STORE (model), &child);
235 		if (data != NULL) gbf_tree_data_free (data);
236 	}
237 
238 	return valid;
239 }
240 
241 static gboolean
gbf_project_model_invalidate_children(GbfProjectModel * model,GtkTreeIter * iter)242 gbf_project_model_invalidate_children (GbfProjectModel *model, GtkTreeIter *iter)
243 {
244 	GtkTreeIter child;
245 	GbfTreeData *data;
246 	gboolean valid;
247 
248 	/* Mark all children as invalid */
249 	valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &child, iter);
250 	while (valid)
251 	{
252 		valid = gbf_project_model_invalidate_children (model, &child);
253 
254 		/* Invalidate children node */
255 		gtk_tree_model_get (GTK_TREE_MODEL (model), &child,
256 		   	 GBF_PROJECT_MODEL_COLUMN_DATA, &data,
257 		    	-1);
258 		gbf_tree_data_invalidate (data);
259 
260 		valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &child);
261 	}
262 
263 	return valid;
264 }
265 
266 static gboolean
gbf_project_model_remove_invalid_shortcut(GbfProjectModel * model,GtkTreeIter * iter)267 gbf_project_model_remove_invalid_shortcut (GbfProjectModel *model, GtkTreeIter *iter)
268 {
269 	GtkTreeIter child;
270 	gboolean valid;
271 	GbfTreeData *data;
272 
273 	/* Get all shortcut */
274 	valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &child, iter);
275 	while (valid)
276 	{
277 		gtk_tree_model_get (GTK_TREE_MODEL (model), &child,
278 	   		 GBF_PROJECT_MODEL_COLUMN_DATA, &data,
279 	    		-1);
280 		/* Shortcuts are always at the beginning */
281 		if (data->type != GBF_TREE_NODE_SHORTCUT) break;
282 
283 		if (data->shortcut->type == GBF_TREE_NODE_INVALID)
284 		{
285 			gbf_project_model_remove_children (model, &child);
286 			valid = gtk_tree_store_remove (GTK_TREE_STORE (model), &child);
287 			if (data != NULL) gbf_tree_data_free (data);
288 		}
289 		else
290 		{
291 			gbf_project_model_remove_invalid_shortcut (model, &child);
292 			valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &child);
293 		}
294 	}
295 
296 	return FALSE;
297 }
298 
299 /* Sort model
300  *---------------------------------------------------------------------------*/
301 
302 static gint
sort_by_name(GtkTreeModel * model,GtkTreeIter * iter_a,GtkTreeIter * iter_b,gpointer user_data)303 sort_by_name (GtkTreeModel *model,
304               GtkTreeIter  *iter_a,
305               GtkTreeIter  *iter_b,
306               gpointer      user_data)
307 {
308 	GbfTreeData *data_a, *data_b;
309 
310 	gtk_tree_model_get (model, iter_a,
311 			    GBF_PROJECT_MODEL_COLUMN_DATA, &data_a,
312 			    -1);
313 	gtk_tree_model_get (model, iter_b,
314 			    GBF_PROJECT_MODEL_COLUMN_DATA, &data_b,
315 			    -1);
316 
317 	return strcmp (data_a->name, data_b->name);
318 }
319 
320 
321 static void
gbf_project_model_merge(GtkTreeModel * model,GtkTreePath * begin,GtkTreePath * half,GtkTreePath * end,GtkTreeIterCompareFunc compare_func,gpointer user_data)322 gbf_project_model_merge (GtkTreeModel *model,
323                          GtkTreePath *begin,
324                          GtkTreePath *half,
325                          GtkTreePath *end,
326                          GtkTreeIterCompareFunc compare_func,
327                          gpointer user_data)
328 {
329 	GtkTreeIter right;
330 	GtkTreeIter left;
331 
332 	if (gtk_tree_model_get_iter (model, &left, begin) &&
333 	    gtk_tree_model_get_iter (model, &right, half))
334 	{
335 		gint depth;
336 		gint ll, lr;
337 
338 
339 		/* Get number of elements in both list */
340 		ll = (gtk_tree_path_get_indices_with_depth (half, &depth)[depth - 1]
341 		      - gtk_tree_path_get_indices_with_depth (begin, &depth)[depth - 1]);
342 		lr = (gtk_tree_path_get_indices_with_depth (end, &depth)[depth - 1]
343 		      - gtk_tree_path_get_indices_with_depth (half, &depth)[depth - 1]);
344 
345 		while (ll && lr)
346 		{
347 			if (compare_func (model, &left, &right, user_data) <= 0)
348 			{
349 				gtk_tree_model_iter_next (model, &left);
350 				ll--;
351 			}
352 			else
353 			{
354 				GtkTreeIter iter;
355 
356 				iter = right;
357 				gtk_tree_model_iter_next (model, &right);
358 				lr--;
359 				gtk_tree_store_move_before (GTK_TREE_STORE (model), &iter, &left);
360 			}
361 		}
362 	}
363 }
364 
365 /* sort using merge sort */
366 static void
gbf_project_model_sort(GtkTreeModel * model,GtkTreePath * begin,GtkTreePath * end,GtkTreeIterCompareFunc compare_func,gpointer user_data)367 gbf_project_model_sort (GtkTreeModel *model,
368                         GtkTreePath *begin,
369                         GtkTreePath *end,
370                         GtkTreeIterCompareFunc compare_func,
371                         gpointer user_data)
372 {
373 	GtkTreePath *half;
374 	gint depth;
375 
376 	/* Empty list are sorted */
377 	if (gtk_tree_path_compare (begin, end) >= 0)
378 	{
379 		return;
380 	}
381 
382 	/* Split the list in two */
383 	half = gtk_tree_path_copy (begin);
384 	gtk_tree_path_up (half);
385 	gtk_tree_path_append_index (half, (gtk_tree_path_get_indices_with_depth (begin, &depth)[depth -1] +
386 	                                   gtk_tree_path_get_indices_with_depth (end, &depth)[depth - 1]) / 2);
387 
388 	/* List with a single element are sorted too */
389 	if (gtk_tree_path_compare (begin, half) < 0)
390 	{
391 		gbf_project_model_sort (model, begin, half, compare_func, user_data);
392 		gbf_project_model_sort (model, half, end, compare_func, user_data);
393 		gbf_project_model_merge (model, begin, half, end, compare_func, user_data);
394 	}
395 
396 	gtk_tree_path_free (half);
397 }
398 
399 
400 /* Public function
401  *---------------------------------------------------------------------------*/
402 
403 gboolean
gbf_project_model_remove(GbfProjectModel * model,GtkTreeIter * iter)404 gbf_project_model_remove (GbfProjectModel *model, GtkTreeIter *iter)
405 {
406 	GtkTreeIter child;
407 	GbfTreeData *data;
408 	gboolean valid;
409 
410 	/* Check if node is not a shortcut. In this case we need to remove
411 	 * all shortcuts first. */
412 	gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
413 		    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
414 		    -1);
415 	if (data->type != GBF_TREE_NODE_SHORTCUT)
416 	{
417 		/* Mark all nodes those will be removed */
418 		gbf_project_model_invalidate_children (model, iter);
419 		gbf_tree_data_invalidate (data);
420 
421 		gbf_project_model_remove_invalid_shortcut (model, NULL);
422 	}
423 
424 	/* Free all children */
425 	valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &child, iter);
426 	while (valid)
427 	{
428 		valid = gbf_project_model_remove_children (model, &child);
429 	}
430 
431 	/* Free parent node */
432 	valid = gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
433 	if (data != NULL) gbf_tree_data_free (data);
434 
435 	return valid;
436 }
437 
438 
439 static void
gbf_project_model_clear(GbfProjectModel * model)440 gbf_project_model_clear (GbfProjectModel *model)
441 {
442 	GtkTreeIter child;
443 	gboolean valid;
444 
445 	valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &child, NULL);
446 	while (valid)
447 	{
448 		valid = gbf_project_model_remove (model, &child);
449 	}
450 }
451 
452 static gint
default_sort_func(GtkTreeModel * model,GtkTreeIter * iter_a,GtkTreeIter * iter_b,gpointer user_data)453 default_sort_func (GtkTreeModel *model,
454 		   GtkTreeIter  *iter_a,
455 		   GtkTreeIter  *iter_b,
456 		   gpointer      user_data)
457 {
458 	GbfTreeData *data_a, *data_b;
459 	gint retval = 0;
460 	gboolean unsorted_a, unsorted_b;
461 
462 	gtk_tree_model_get (model, iter_a,
463 			    GBF_PROJECT_MODEL_COLUMN_DATA, &data_a,
464 			    -1);
465 	gtk_tree_model_get (model, iter_b,
466 			    GBF_PROJECT_MODEL_COLUMN_DATA, &data_b,
467 			    -1);
468 
469 	unsorted_a = (data_a->type == GBF_TREE_NODE_SHORTCUT) || (data_a->type == GBF_TREE_NODE_UNKNOWN) || (data_a->is_shortcut);
470 	unsorted_b = (data_b->type == GBF_TREE_NODE_SHORTCUT) || (data_b->type == GBF_TREE_NODE_UNKNOWN) || (data_b->is_shortcut);
471 	if (unsorted_a && unsorted_b) {
472 		GtkTreeIter iter;
473 		gboolean valid;
474 
475 		/* special case: the order of shortcuts is
476 		 * user customizable */
477 		for (valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &iter);
478 		    valid == TRUE;
479 		    valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
480 		{
481 			GbfTreeData *data;
482 
483 			gtk_tree_model_get (model, &iter,
484 				    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
485 				    -1);
486 			if (data == data_a) {
487 				/* a comes first */
488 				retval = -1;
489 				break;
490 			}
491 			else if (data == data_b) {
492 				/* b comes first */
493 				retval = 1;
494 				break;
495 			}
496 		}
497 
498 	} else if (unsorted_a && !unsorted_b) {
499 		retval = -1;
500 
501 	} else if (!unsorted_a && unsorted_b) {
502 		retval = 1;
503 
504 	} else if (data_a->type == data_b->type) {
505 		retval = strcmp (data_a->name, data_b->name);
506 
507 	} else {
508 		/* assume a->b and check for the opposite cases */
509 		retval = -1;
510 		retval = data_a->type < data_b->type ? -1 : 1;
511 	}
512 
513 	return retval;
514 }
515 
516 void
gbf_project_model_add_target_shortcut(GbfProjectModel * model,GtkTreeIter * shortcut,GbfTreeData * target,GtkTreePath * before_path,gboolean * expanded)517 gbf_project_model_add_target_shortcut (GbfProjectModel *model,
518                      GtkTreeIter     *shortcut,
519 		     GbfTreeData     *target,
520 		     GtkTreePath     *before_path,
521                      gboolean	     *expanded)
522 {
523 	AnjutaProjectNode *node;
524 	GtkTreeIter iter, sibling;
525 	GtkTreePath *root_path;
526 	GbfTreeData *data;
527 	AnjutaProjectNode *parent;
528 	gboolean valid = FALSE;
529 
530 	if (!target)
531 		return;
532 
533 	if (expanded != NULL) *expanded = FALSE;
534 
535 	root_path = gbf_project_model_get_project_root (model);
536 	if ((before_path == NULL) && (target->type != GBF_TREE_NODE_SHORTCUT))
537 	{
538 		/* Check is a proxy node is not already existing. It is used to
539 		 * save the shortcut order */
540 
541 		for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, NULL);
542 			valid;
543 			valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
544 		{
545 			GbfTreeData *data;
546 
547 			/* Look for current node */
548 			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
549 				GBF_PROJECT_MODEL_COLUMN_DATA, &data,
550 				-1);
551 
552 			if (((data->type == GBF_TREE_NODE_UNKNOWN) || (data->type == GBF_TREE_NODE_SHORTCUT)) && (g_strcmp0 (target->name, data->name) == 0))
553 			{
554 				/* Find already existing node and replace it */
555 				if (expanded != NULL) *expanded = data->expanded;
556 				gbf_tree_data_free (data);
557 
558 				data = gbf_tree_data_new_shortcut (target);
559 				gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
560 				    GBF_PROJECT_MODEL_COLUMN_DATA, data,
561 				    -1);
562 				break;
563 			}
564 		}
565 	}
566 	if (!valid)
567 	{
568 		/* check before_path */
569 		if ((before_path == NULL) ||
570 		    gtk_tree_path_get_depth (before_path) > 1 ||
571 		    gtk_tree_path_compare (before_path, root_path) > 0)
572 		{
573 			before_path = root_path;
574 		}
575 
576 		/* get the tree iter for the row before which to insert the shortcut */
577 		if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &sibling, before_path)) {
578 			gtk_tree_path_free (root_path);
579 			return;
580 		}
581 
582 		if (target->type != GBF_TREE_NODE_SHORTCUT)
583 		{
584 			data = gbf_tree_data_new_shortcut (target);
585 		}
586 		else
587 		{
588 			data = target;
589 		}
590 		gtk_tree_store_insert_before (GTK_TREE_STORE (model), &iter, NULL, &sibling);
591 		gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
592 				    GBF_PROJECT_MODEL_COLUMN_DATA, data,
593 				    -1);
594 	}
595 
596 	/* add sources */
597 	parent = gbf_tree_data_get_node (target);
598 	for (node = anjuta_project_node_first_child (parent); node; node = anjuta_project_node_next_sibling (node))
599 		gbf_project_model_add_node (model, node, &iter, 0);
600 
601 	gtk_tree_path_free (root_path);
602 
603 	if (shortcut) *shortcut = iter;
604 }
605 
606 void
gbf_project_model_move_target_shortcut(GbfProjectModel * model,GtkTreeIter * iter,GbfTreeData * shortcut,GtkTreePath * before_path)607 gbf_project_model_move_target_shortcut (GbfProjectModel *model,
608 		     GtkTreeIter     *iter,
609     		     GbfTreeData     *shortcut,
610 		     GtkTreePath     *before_path)
611 {
612 	AnjutaProjectNode *node;
613 	GtkTreeIter sibling;
614 	GtkTreePath *root_path;
615 	GtkTreePath *src_path;
616 	AnjutaProjectNode *parent;
617 
618 	if (!shortcut)
619 		return;
620 
621 	root_path = gbf_project_model_get_project_root (model);
622 
623 	/* check before_path */
624 	if (!before_path ||
625 	    gtk_tree_path_get_depth (before_path) > 1)
626 	{
627 		/* Missing destination path, use root path */
628 		before_path = root_path;
629 	}
630 	else if (gtk_tree_path_compare (before_path, root_path) > 0)
631 	{
632 		/* Destination path outside shortcut are, remove shortcut */
633 		gbf_project_model_remove (model, iter);
634 		gtk_tree_path_free (root_path);
635 
636 		return;
637 	}
638 
639 	/* get the tree iter for the row before which to insert the shortcut */
640 	if (!gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &sibling, before_path)) {
641 		gtk_tree_path_free (root_path);
642 		return;
643 	}
644 
645 	src_path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
646 	if (gtk_tree_path_compare (src_path, before_path) != 0)
647 	{
648 		gtk_tree_store_remove (GTK_TREE_STORE (model), iter);
649 		gtk_tree_store_insert_before (GTK_TREE_STORE (model), iter, NULL, &sibling);
650 		gtk_tree_store_set (GTK_TREE_STORE (model), iter,
651 				    GBF_PROJECT_MODEL_COLUMN_DATA, shortcut,
652 				    -1);
653 
654 		/* add sources */
655 		parent = gbf_tree_data_get_node (shortcut->shortcut);
656 		for (node = anjuta_project_node_first_child (parent); node; node = anjuta_project_node_next_sibling (node))
657 			gbf_project_model_add_node (model, node, iter, 0);
658 
659 	}
660 
661 	gtk_tree_path_free (src_path);
662 	gtk_tree_path_free (root_path);
663 
664 }
665 
666 void
gbf_project_model_add_node(GbfProjectModel * model,AnjutaProjectNode * node,GtkTreeIter * parent,AnjutaProjectNodeType only_type)667 gbf_project_model_add_node (GbfProjectModel    	   *model,
668                             AnjutaProjectNode	   *node,
669                             GtkTreeIter            *parent,
670                             AnjutaProjectNodeType only_type)
671 {
672 	GtkTreeIter iter;
673 	GbfTreeData *data = NULL;
674 	AnjutaProjectNode *child;
675 	AnjutaProjectNodeType child_types[] = {ANJUTA_PROJECT_GROUP,
676 		ANJUTA_PROJECT_TARGET,
677 		ANJUTA_PROJECT_SOURCE,
678 		ANJUTA_PROJECT_MODULE,
679 		ANJUTA_PROJECT_PACKAGE,
680 		0};
681 	AnjutaProjectNodeType *type;
682 
683 	if (node == NULL) return;
684 
685 
686 	if (anjuta_project_node_get_full_type (node) & ANJUTA_PROJECT_FRAME) return;
687 
688 	if ((only_type == 0) || (anjuta_project_node_get_node_type (node) == only_type))
689 	{
690 		if (anjuta_project_node_get_node_type (node) != ANJUTA_PROJECT_OBJECT)
691 		{
692 			data = gbf_tree_data_new_node (node);
693 			gtk_tree_store_append (GTK_TREE_STORE (model), &iter, parent);
694 			gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
695 					    GBF_PROJECT_MODEL_COLUMN_DATA, data,
696 					    -1);
697 		}
698 		else
699 		{
700 			/* Hidden node */
701 			iter = *parent;
702 		}
703 
704 		/* add children */
705 		for (type = child_types; *type != 0; type++)
706 		{
707 			for (child = anjuta_project_node_first_child (node); child != NULL; child = anjuta_project_node_next_sibling (child))
708 			{
709 				gbf_project_model_add_node (model, child, &iter, *type);
710 			}
711 		}
712 
713 		/* Add shortcut if needed */
714 		if ((data != NULL) &&
715 	    		model->priv->default_shortcut &&
716 	    		(anjuta_project_node_get_node_type (node) == ANJUTA_PROJECT_TARGET) &&
717 	    		(anjuta_project_node_get_full_type (node) & ANJUTA_PROJECT_PRIMARY))
718 		{
719 			gbf_project_model_add_target_shortcut (model, NULL, data, NULL, NULL);
720 		}
721 	}
722 	else if (anjuta_project_node_get_node_type (node) == ANJUTA_PROJECT_OBJECT)
723 	{
724 		/* Add only children */
725 		for (child = anjuta_project_node_first_child (node); child != NULL; child = anjuta_project_node_next_sibling (child))
726 		{
727 			gbf_project_model_add_node (model, child, parent, only_type);
728 		}
729 	}
730 }
731 
732 static void
load_project(GbfProjectModel * model,AnjutaPmProject * proj)733 load_project (GbfProjectModel *model, AnjutaPmProject *proj)
734 {
735 	model->priv->proj = proj;
736 	g_object_ref (proj);
737 
738 	gbf_project_model_add_node (model, anjuta_pm_project_get_root (proj), NULL, 0);
739 }
740 
741 static void
insert_empty_node(GbfProjectModel * model)742 insert_empty_node (GbfProjectModel *model)
743 {
744 	GtkTreeIter iter;
745 	GbfTreeData *empty_node;
746 
747 	empty_node = gbf_tree_data_new_string (_("No project loaded"));
748 
749 	gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
750 	gtk_tree_store_set (GTK_TREE_STORE (model), &iter,
751 			    GBF_PROJECT_MODEL_COLUMN_DATA, empty_node,
752 			    -1);
753 }
754 
755 static void
unload_project(GbfProjectModel * model)756 unload_project (GbfProjectModel *model)
757 {
758 	if (model->priv->proj) {
759 		gtk_tree_row_reference_free (model->priv->root);
760 		model->priv->root = NULL;
761 
762 		gbf_project_model_clear (model);
763 
764 		g_list_free (model->priv->shortcuts);
765 		model->priv->shortcuts = NULL;
766 
767 		//g_signal_handler_disconnect (anjuta_pm_project_get_project (model->priv->proj),
768 		//			     model->priv->project_updated_handler);
769 		//model->priv->project_updated_handler = 0;
770 		model->priv->proj = NULL;
771 
772 		insert_empty_node (model);
773 	}
774 }
775 
776 static gboolean
recursive_find_tree_data(GtkTreeModel * model,GtkTreeIter * iter,GbfTreeData * data)777 recursive_find_tree_data (GtkTreeModel  *model,
778 		          GtkTreeIter   *iter,
779 		          GbfTreeData  	*data)
780 {
781 	GtkTreeIter tmp;
782 	gboolean retval = FALSE;
783 
784 	tmp = *iter;
785 
786 	do {
787 		GtkTreeIter child;
788 		GbfTreeData *tmp_data;
789 
790 		gtk_tree_model_get (model, &tmp,
791 				    GBF_PROJECT_MODEL_COLUMN_DATA, &tmp_data, -1);
792 		if (gbf_tree_data_equal (tmp_data, data))
793 		{
794 			*iter = tmp;
795 			retval = TRUE;
796 		}
797 
798 		if (gtk_tree_model_iter_children (model, &child, &tmp)) {
799 			if (recursive_find_tree_data (model, &child, data)) {
800 				*iter = child;
801 				retval = TRUE;
802 			}
803 		}
804 
805 	} while (!retval && gtk_tree_model_iter_next (model, &tmp));
806 
807 	return retval;
808 }
809 
810 gboolean
gbf_project_model_find_tree_data(GbfProjectModel * model,GtkTreeIter * iter,GbfTreeData * data)811 gbf_project_model_find_tree_data (GbfProjectModel 	*model,
812 			          GtkTreeIter     	*iter,
813 			          GbfTreeData  		*data)
814 {
815 	GtkTreeIter tmp_iter;
816 	gboolean retval = FALSE;
817 
818 	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (model), &tmp_iter)) {
819 		if (recursive_find_tree_data (GTK_TREE_MODEL (model), &tmp_iter, data)) {
820 			retval = TRUE;
821 			*iter = tmp_iter;
822 		}
823 	}
824 
825 	return retval;
826 }
827 
828 /* Can return shortcut node if exist */
829 gboolean
gbf_project_model_find_file(GbfProjectModel * model,GtkTreeIter * found,GtkTreeIter * parent,GbfTreeNodeType type,GFile * file)830 gbf_project_model_find_file (GbfProjectModel 	*model,
831     GtkTreeIter		*found,
832     GtkTreeIter		*parent,
833      GbfTreeNodeType type,
834     GFile		*file)
835 {
836 	GtkTreeIter iter;
837 	gboolean valid;
838 
839 	/* Search for direct children */
840 	for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, parent); valid == TRUE; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
841 	{
842 		GbfTreeData *data;
843 
844 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
845 		    GBF_PROJECT_MODEL_COLUMN_DATA, &data, -1);
846 
847 		if (gbf_tree_data_equal_file (data, type, file))
848 		{
849 			*found = iter;
850 			break;
851 		}
852 	}
853 
854 	/* Search for children of children */
855 	if (!valid)
856 	{
857 		for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, parent); valid == TRUE; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
858 		{
859 			if (gbf_project_model_find_file (model, found, &iter, type, file)) break;
860 		}
861 	}
862 
863 	return valid;
864 }
865 
866 gboolean
gbf_project_model_find_node(GbfProjectModel * model,GtkTreeIter * found,GtkTreeIter * parent,AnjutaProjectNode * node)867 gbf_project_model_find_node (GbfProjectModel 	*model,
868     GtkTreeIter		*found,
869     GtkTreeIter		*parent,
870     AnjutaProjectNode	*node)
871 {
872 	GtkTreeIter iter;
873 	gboolean valid;
874 
875 	/* Search for direct children */
876 	for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, parent); valid == TRUE; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
877 	{
878 		GbfTreeData *data;
879 
880 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
881 			GBF_PROJECT_MODEL_COLUMN_DATA, &data, -1);
882 
883 		if (node == gbf_tree_data_get_node (data))
884 		{
885 			*found = iter;
886 			break;
887 		}
888 	}
889 
890 	/* Search for children of children */
891 	if (!valid)
892 	{
893 		for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, parent); valid == TRUE; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
894 		{
895 			if (gbf_project_model_find_node (model, found, &iter, node)) break;
896 		}
897 	}
898 
899 	return valid;
900 }
901 
902 /* Can return shortcut node if exist */
903 gboolean
gbf_project_model_find_child_name(GbfProjectModel * model,GtkTreeIter * found,GtkTreeIter * parent,const gchar * name)904 gbf_project_model_find_child_name (GbfProjectModel 	*model,
905 	GtkTreeIter		*found,
906 	GtkTreeIter		*parent,
907         const gchar		*name)
908 {
909 	GtkTreeIter iter;
910 	gboolean valid;
911 
912 	/* Search for direct children only */
913 	for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, parent); valid == TRUE; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
914 	{
915 		GbfTreeData *data;
916 
917 		gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
918 		    GBF_PROJECT_MODEL_COLUMN_DATA, &data, -1);
919 
920 		if (gbf_tree_data_equal_name (data, name))
921 		{
922 			*found = iter;
923 			break;
924 		}
925 	}
926 
927 	return valid;
928 }
929 
930 GbfProjectModel *
gbf_project_model_new(AnjutaPmProject * project)931 gbf_project_model_new (AnjutaPmProject *project)
932 {
933 	return GBF_PROJECT_MODEL (g_object_new (GBF_TYPE_PROJECT_MODEL,
934 						"project", project,
935 						NULL));
936 }
937 
938 void
gbf_project_model_set_project(GbfProjectModel * model,AnjutaPmProject * project)939 gbf_project_model_set_project (GbfProjectModel *model, AnjutaPmProject *project)
940 {
941 	g_return_if_fail (model != NULL && GBF_IS_PROJECT_MODEL (model));
942 
943 	if (model->priv->proj != project)
944 	{
945 		//unload_project (model);
946 
947 		/* project can be NULL */
948 		if (project)
949 			load_project (model, project);
950 	}
951 }
952 
953 AnjutaPmProject *
gbf_project_model_get_project(GbfProjectModel * model)954 gbf_project_model_get_project (GbfProjectModel *model)
955 {
956 	g_return_val_if_fail (model != NULL && GBF_IS_PROJECT_MODEL (model), NULL);
957 
958 	return model->priv->proj;
959 }
960 
961 GtkTreePath *
gbf_project_model_get_project_root(GbfProjectModel * model)962 gbf_project_model_get_project_root (GbfProjectModel *model)
963 {
964 	GtkTreePath *path = NULL;
965 
966 	g_return_val_if_fail (GBF_IS_PROJECT_MODEL (model), NULL);
967 
968 	if (model->priv->root == NULL)
969 	{
970 		GtkTreeIter iter;
971 		gboolean valid;
972 
973 		/* Search root group */
974 		for (valid = gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, NULL); valid; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter))
975 		{
976 			GbfTreeData *data;
977 
978 			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
979 	   			 GBF_PROJECT_MODEL_COLUMN_DATA, &data,
980 		    		-1);
981 
982 			if (data->type == GBF_TREE_NODE_ROOT)
983 			{
984 				path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
985 				model->priv->root = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path);
986 			}
987 		}
988 	}
989 	else
990 	{
991 		path = gtk_tree_row_reference_get_path (model->priv->root);
992 	}
993 
994 	return path;
995 }
996 
997 AnjutaProjectNode *
gbf_project_model_get_node(GbfProjectModel * model,GtkTreeIter * iter)998 gbf_project_model_get_node (GbfProjectModel *model,
999                             GtkTreeIter     *iter)
1000 {
1001 	GbfTreeData *data = NULL;
1002 
1003 	gtk_tree_model_get (GTK_TREE_MODEL (model), iter,
1004 			    GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1005 			    -1);
1006 
1007 	return gbf_tree_data_get_node (data);
1008 }
1009 
1010 void
gbf_project_model_set_default_shortcut(GbfProjectModel * model,gboolean enable)1011 gbf_project_model_set_default_shortcut (GbfProjectModel *model,
1012                                         gboolean enable)
1013 {
1014 	model->priv->default_shortcut = enable;
1015 }
1016 
1017 void
gbf_project_model_sort_shortcuts(GbfProjectModel * model)1018 gbf_project_model_sort_shortcuts (GbfProjectModel *model)
1019 {
1020 	GtkTreeIter iter;
1021 
1022 	/* Get all shortcut */
1023 	if (gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, NULL))
1024 	{
1025 		GtkTreePath *begin;
1026 		GtkTreePath *end;
1027 		gboolean valid;
1028 
1029 		begin = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1030 		do
1031 		{
1032 			GbfTreeData *data;
1033 
1034 			gtk_tree_model_get (GTK_TREE_MODEL (model), &iter,
1035 	   			 GBF_PROJECT_MODEL_COLUMN_DATA, &data,
1036 		    		-1);
1037 
1038 			/* Shortcuts are always at the beginning */
1039 			if (data->type != GBF_TREE_NODE_SHORTCUT) break;
1040 
1041 			valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter);
1042 		}
1043 		while (valid);
1044 
1045 
1046 
1047 		end = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1048 		gbf_project_model_sort (GTK_TREE_MODEL (model), begin, end, sort_by_name, NULL);
1049 		gtk_tree_path_free (begin);
1050 		gtk_tree_path_free (end);
1051 	}
1052 }
1053