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