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