Update copyright message
[nautilus-actions.git] / src / nact / nact-tree-model.c
blob3df8e318923aaca8d913c5c9d1ad9e7038c965c3
1 /*
2 * Nautilus Actions
3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
9 * This Program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * This Program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
24 * Authors:
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
35 #include <string.h>
37 #include <api/na-object-api.h>
39 #include <core/na-iprefs.h>
41 #include "nact-application.h"
42 #include "nact-clipboard.h"
43 #include "nact-gtk-utils.h"
44 #include "nact-iactions-list.h"
45 #include "nact-tree-model.h"
46 #include "nact-tree-model-dnd.h"
47 #include "nact-tree-model-priv.h"
49 /* private class data
51 struct NactTreeModelClassPrivate {
52 void *empty; /* so that gcc -pedantic is happy */
55 /* search for an object, setting the iter if found
57 typedef struct {
58 GtkTreeModel *store;
59 const NAObject *object;
60 gboolean found;
61 GtkTreeIter *iter;
63 ntmSearchStruct;
65 /* search for an object identified by its id, setting the iter if found
67 typedef struct {
68 GtkTreeModel *store;
69 gchar *id;
70 gboolean found;
71 GtkTreeIter *iter;
73 ntmSearchIdStruct;
75 /* dump the content of the tree
77 typedef struct {
78 gchar *fname;
79 gchar *prefix;
81 ntmDumpStruct;
83 /* drop formats, as defined in nact-tree-model-dnd.c
85 extern GtkTargetEntry tree_model_dnd_dest_formats[];
86 extern guint tree_model_dnd_dest_formats_count;
88 #define TREE_MODEL_ORDER_MODE "nact-tree-model-order-mode"
90 static GtkTreeModelFilterClass *st_parent_class = NULL;
92 static GType register_type( void );
93 static void class_init( NactTreeModelClass *klass );
94 static void imulti_drag_source_init( EggTreeMultiDragSourceIface *iface );
95 static void idrag_dest_init( GtkTreeDragDestIface *iface );
96 static void instance_init( GTypeInstance *instance, gpointer klass );
97 static void instance_dispose( GObject *application );
98 static void instance_finalize( GObject *application );
100 static NactTreeModel *tree_model_new( BaseWindow *window, GtkTreeView *treeview );
102 static void append_item( GtkTreeStore *model, GtkTreeView *treeview, GtkTreeIter *parent, GtkTreeIter *iter, const NAObject *object );
103 static void display_item( GtkTreeStore *model, GtkTreeView *treeview, GtkTreeIter *iter, const NAObject *object );
104 static gboolean dump_store( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmDumpStruct *ntm );
105 static void fill_tree_store( GtkTreeStore *model, GtkTreeView *treeview, NAObject *object, gboolean only_actions, GtkTreeIter *parent );
106 static gboolean filter_visible( GtkTreeModel *store, GtkTreeIter *iter, NactTreeModel *model );
107 static void iter_on_store( NactTreeModel *model, GtkTreeModel *store, GtkTreeIter *parent, FnIterOnStore fn, gpointer user_data );
108 static gboolean iter_on_store_item( NactTreeModel *model, GtkTreeModel *store, GtkTreeIter *iter, FnIterOnStore fn, gpointer user_data );
109 static void remove_if_exists( NactTreeModel *model, GtkTreeModel *store, const NAObject *object );
110 static gboolean remove_items( GtkTreeStore *store, GtkTreeIter *iter );
111 static gboolean search_for_object( NactTreeModel *model, GtkTreeModel *store, const NAObject *object, GtkTreeIter *iter );
112 static gboolean search_for_object_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmSearchStruct *ntm );
113 static gboolean search_for_object_id( NactTreeModel *model, GtkTreeModel *store, const NAObject *object, GtkTreeIter *iter );
114 static gboolean search_for_object_id_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmSearchIdStruct *ntm );
115 static gint sort_actions_list( GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data );
117 GType
118 nact_tree_model_get_type( void )
120 static GType model_type = 0;
122 if( !model_type ){
123 model_type = register_type();
126 return( model_type );
129 static GType
130 register_type (void)
132 static const gchar *thisfn = "nact_tree_model_register_type";
133 GType type;
135 static const GTypeInfo info = {
136 sizeof( NactTreeModelClass ),
137 NULL, /* base_init */
138 NULL, /* base_finalize */
139 ( GClassInitFunc ) class_init,
140 NULL, /* class_finalize */
141 NULL, /* class_data */
142 sizeof( NactTreeModel ),
144 ( GInstanceInitFunc ) instance_init
147 static const GInterfaceInfo imulti_drag_source_info = {
148 ( GInterfaceInitFunc ) imulti_drag_source_init,
149 NULL,
150 NULL
153 static const GInterfaceInfo idrag_dest_info = {
154 ( GInterfaceInitFunc ) idrag_dest_init,
155 NULL,
156 NULL
159 g_debug( "%s", thisfn );
161 type = g_type_register_static( GTK_TYPE_TREE_MODEL_FILTER, "NactTreeModel", &info, 0 );
163 g_type_add_interface_static( type, EGG_TYPE_TREE_MULTI_DRAG_SOURCE, &imulti_drag_source_info );
165 g_type_add_interface_static( type, GTK_TYPE_TREE_DRAG_DEST, &idrag_dest_info );
167 return( type );
170 static void
171 class_init( NactTreeModelClass *klass )
173 static const gchar *thisfn = "nact_tree_model_class_init";
174 GObjectClass *object_class;
176 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
178 st_parent_class = g_type_class_peek_parent( klass );
180 object_class = G_OBJECT_CLASS( klass );
181 object_class->dispose = instance_dispose;
182 object_class->finalize = instance_finalize;
184 klass->private = g_new0( NactTreeModelClassPrivate, 1 );
187 static void
188 imulti_drag_source_init( EggTreeMultiDragSourceIface *iface )
190 static const gchar *thisfn = "nact_tree_model_imulti_drag_source_init";
192 g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
194 iface->row_draggable = nact_tree_model_dnd_imulti_drag_source_row_draggable;
195 iface->drag_data_get = nact_tree_model_dnd_imulti_drag_source_drag_data_get;
196 iface->drag_data_delete = nact_tree_model_dnd_imulti_drag_source_drag_data_delete;
197 iface->get_target_list = nact_tree_model_dnd_imulti_drag_source_get_format_list;
198 iface->free_target_list = NULL;
199 iface->get_drag_actions = nact_tree_model_dnd_imulti_drag_source_get_drag_actions;
202 static void
203 idrag_dest_init( GtkTreeDragDestIface *iface )
205 static const gchar *thisfn = "nact_tree_model_idrag_dest_init";
207 g_debug( "%s: iface=%p", thisfn, ( void * ) iface );
209 iface->drag_data_received = nact_tree_model_dnd_idrag_dest_drag_data_received;
210 iface->row_drop_possible = nact_tree_model_dnd_idrag_dest_row_drop_possible;
213 static void
214 instance_init( GTypeInstance *instance, gpointer klass )
216 static const gchar *thisfn = "nact_tree_model_instance_init";
217 NactTreeModel *self;
219 g_debug( "%s: instance=%p (%s), klass=%p",
220 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
221 g_return_if_fail( NACT_IS_TREE_MODEL( instance ));
222 self = NACT_TREE_MODEL( instance );
224 self->private = g_new0( NactTreeModelPrivate, 1 );
226 self->private->dispose_has_run = FALSE;
229 static void
230 instance_dispose( GObject *object )
232 static const gchar *thisfn = "nact_tree_model_instance_dispose";
233 NactTreeModel *self;
235 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
236 g_return_if_fail( NACT_IS_TREE_MODEL( object ));
237 self = NACT_TREE_MODEL( object );
239 if( !self->private->dispose_has_run ){
241 self->private->dispose_has_run = TRUE;
243 g_object_unref( self->private->clipboard );
245 /* chain up to the parent class */
246 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
247 G_OBJECT_CLASS( st_parent_class )->dispose( object );
252 static void
253 instance_finalize( GObject *object )
255 static const gchar *thisfn = "nact_tree_model_instance_finalize";
256 NactTreeModel *self;
258 g_debug( "%s: object=%p", thisfn, ( void * ) object );
259 g_return_if_fail( NACT_IS_TREE_MODEL( object ));
260 self = NACT_TREE_MODEL( object );
262 g_free( self->private );
264 /* chain call to parent class */
265 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
266 G_OBJECT_CLASS( st_parent_class )->finalize( object );
271 * tree_model_new:
272 * @window: a #BaseWindow window which must implement #NactIActionsList
273 * interface.
274 * @treeview: the #GtkTreeView widget.
276 * Creates a new #NactTreeModel model.
278 * This function should be called at widget initial load time. Is is so
279 * too soon to make any assumption about sorting in the tree view.
281 static NactTreeModel *
282 tree_model_new( BaseWindow *window, GtkTreeView *treeview )
284 static const gchar *thisfn = "nact_tree_model_new";
285 GtkTreeStore *ts_model;
286 NactTreeModel *model;
287 NactApplication *application;
288 NAUpdater *updater;
289 gint order_mode;
291 g_debug( "%s: window=%p, treeview=%p", thisfn, ( void * ) window, ( void * ) treeview );
292 g_return_val_if_fail( BASE_IS_WINDOW( window ), NULL );
293 g_return_val_if_fail( NACT_IS_IACTIONS_LIST( window ), NULL );
294 g_return_val_if_fail( GTK_IS_TREE_VIEW( treeview ), NULL );
296 ts_model = gtk_tree_store_new(
297 IACTIONS_LIST_N_COLUMN, GDK_TYPE_PIXBUF, G_TYPE_STRING, NA_OBJECT_TYPE );
299 /* create the filter model
301 model = g_object_new( NACT_TREE_MODEL_TYPE, "child-model", ts_model, NULL );
303 gtk_tree_model_filter_set_visible_func(
304 GTK_TREE_MODEL_FILTER( model ), ( GtkTreeModelFilterVisibleFunc ) filter_visible, model, NULL );
306 /* initialize the sortable interface
308 application = NACT_APPLICATION( base_window_get_application( window ));
309 updater = nact_application_get_updater( application );
310 order_mode = na_iprefs_get_order_mode( NA_IPREFS( updater ));
311 nact_tree_model_display_order_change( model, order_mode );
313 model->private->window = window;
314 model->private->treeview = treeview;
315 model->private->clipboard = nact_clipboard_new( window );
317 return( model );
321 * nact_tree_model_initial_load:
322 * @window: the #BaseWindow window.
323 * @widget: the #GtkTreeView which will implement the #NactTreeModel.
325 * Creates a #NactTreeModel, and attaches it to the treeview.
327 * Please note that we cannot make any assumption here whether the
328 * treeview, and so the tree model, must or not implement the drag and
329 * drop interfaces.
330 * This is because #NactIActionsList::on_initial_load() initializes these
331 * properties to %FALSE. The actual values will be set by the main
332 * program between #NactIActionsList::on_initial_load() returns and call
333 * to #NactIActionsList::on_runtime_init().
335 void
336 nact_tree_model_initial_load( BaseWindow *window, GtkTreeView *treeview )
338 static const gchar *thisfn = "nact_tree_model_initial_load";
339 NactTreeModel *model;
341 g_debug( "%s: window=%p, treeview=%p", thisfn, ( void * ) window, ( void * ) treeview );
342 g_return_if_fail( BASE_IS_WINDOW( window ));
343 g_return_if_fail( NACT_IS_IACTIONS_LIST( window ));
344 g_return_if_fail( GTK_IS_TREE_VIEW( treeview ));
346 model = tree_model_new( window, treeview );
348 gtk_tree_view_set_model( treeview, GTK_TREE_MODEL( model ));
350 g_object_unref( model );
354 * nact_tree_model_runtime_init:
355 * @model: this #NactTreeModel instance.
356 * @have_dnd: whether the tree model must implement drag and drop
357 * interfaces.
359 * Initializes the tree model.
361 * We use drag and drop:
362 * - inside of treeview, for duplicating items, or moving items between
363 * menus
364 * - from treeview to the outside world (e.g. Nautilus) to export actions
365 * - from outside world (e.g. Nautilus) to import actions
367 void
368 nact_tree_model_runtime_init( NactTreeModel *model, gboolean have_dnd )
370 static const gchar *thisfn = "nact_tree_model_runtime_init";
372 g_debug( "%s: model=%p, have_dnd=%s", thisfn, ( void * ) model, have_dnd ? "True":"False" );
373 g_return_if_fail( NACT_IS_TREE_MODEL( model ));
375 if( !model->private->dispose_has_run ){
377 if( have_dnd ){
379 egg_tree_multi_drag_add_drag_support( EGG_TREE_MULTI_DRAG_SOURCE( model ), model->private->treeview );
381 gtk_tree_view_enable_model_drag_dest(
382 model->private->treeview,
383 tree_model_dnd_dest_formats,
384 tree_model_dnd_dest_formats_count,
385 nact_tree_model_dnd_imulti_drag_source_get_drag_actions( EGG_TREE_MULTI_DRAG_SOURCE( model )));
387 base_window_signal_connect(
388 BASE_WINDOW( model->private->window ),
389 G_OBJECT( model->private->treeview ),
390 "drag-begin",
391 G_CALLBACK( nact_tree_model_dnd_on_drag_begin ));
393 /* connect: implies that we have to do all hard stuff
394 * connect_after: no more triggers drag-drop signal
396 /*base_window_signal_connect_after(
397 BASE_WINDOW( model->private->window ),
398 G_OBJECT( model->private->treeview ),
399 "drag-motion",
400 G_CALLBACK( on_drag_motion ));*/
402 /*base_window_signal_connect(
403 BASE_WINDOW( model->private->window ),
404 G_OBJECT( model->private->treeview ),
405 "drag-drop",
406 G_CALLBACK( on_drag_drop ));*/
408 base_window_signal_connect(
409 BASE_WINDOW( model->private->window ),
410 G_OBJECT( model->private->treeview ),
411 "drag-end",
412 G_CALLBACK( nact_tree_model_dnd_on_drag_end ));
417 void
418 nact_tree_model_dispose( NactTreeModel *model )
420 static const gchar *thisfn = "nact_tree_model_dispose";
421 GtkTreeStore *ts_model;
423 g_debug( "%s: model=%p", thisfn, ( void * ) model );
424 g_return_if_fail( NACT_IS_TREE_MODEL( model ));
426 if( !model->private->dispose_has_run ){
428 nact_tree_model_dump( model );
430 ts_model = GTK_TREE_STORE( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model )));
432 gtk_tree_store_clear( ts_model );
434 g_debug( "%s: end of tree store clear", thisfn );
439 * nact_tree_model_display:
440 * @model: this #NactTreeModel instance.
441 * @object: the object whose display is to be refreshed.
443 * Refresh the display of a #NAObject.
445 void
446 nact_tree_model_display( NactTreeModel *model, NAObject *object )
448 static const gchar *thisfn = "nact_tree_model_display";
449 GtkTreeStore *store;
450 GtkTreeIter iter;
451 GtkTreePath *path;
453 g_return_if_fail( NACT_IS_TREE_MODEL( model ));
455 g_debug( "%s: model=%p (%s), object=%p (%s)",
456 thisfn,
457 ( void * ) model, G_OBJECT_TYPE_NAME( model ),
458 ( void * ) object, G_OBJECT_TYPE_NAME( object ));
460 if( !model->private->dispose_has_run ){
462 store = GTK_TREE_STORE( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model )));
464 if( search_for_object( model, GTK_TREE_MODEL( store ), object, &iter )){
465 display_item( store, model->private->treeview, &iter, object );
466 path = gtk_tree_model_get_path( GTK_TREE_MODEL( store ), &iter );
467 gtk_tree_model_row_changed( GTK_TREE_MODEL( store ), path, &iter );
468 gtk_tree_path_free( path );
471 /*gtk_tree_model_filter_refilter( GTK_TREE_MODEL_FILTER( model ));*/
476 * nact_tree_model_display_order_change:
477 * @model: this #NactTreeModel.
478 * @order_mode: the new order mode.
480 * Setup the new order mode.
482 void
483 nact_tree_model_display_order_change( NactTreeModel *model, gint order_mode )
485 GtkTreeStore *store;
487 g_return_if_fail( NACT_IS_TREE_MODEL( model ));
489 if( !model->private->dispose_has_run ){
491 store = GTK_TREE_STORE( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model )));
492 g_object_set_data( G_OBJECT( store ), TREE_MODEL_ORDER_MODE, GINT_TO_POINTER( order_mode ));
494 switch( order_mode ){
496 case IPREFS_ORDER_ALPHA_ASCENDING:
498 gtk_tree_sortable_set_sort_column_id(
499 GTK_TREE_SORTABLE( store ),
500 IACTIONS_LIST_LABEL_COLUMN,
501 GTK_SORT_ASCENDING );
503 gtk_tree_sortable_set_sort_func(
504 GTK_TREE_SORTABLE( store ),
505 IACTIONS_LIST_LABEL_COLUMN,
506 ( GtkTreeIterCompareFunc ) sort_actions_list,
507 NULL,
508 NULL );
509 break;
511 case IPREFS_ORDER_ALPHA_DESCENDING:
513 gtk_tree_sortable_set_sort_column_id(
514 GTK_TREE_SORTABLE( store ),
515 IACTIONS_LIST_LABEL_COLUMN,
516 GTK_SORT_DESCENDING );
518 gtk_tree_sortable_set_sort_func(
519 GTK_TREE_SORTABLE( store ),
520 IACTIONS_LIST_LABEL_COLUMN,
521 ( GtkTreeIterCompareFunc ) sort_actions_list,
522 NULL,
523 NULL );
524 break;
526 case IPREFS_ORDER_MANUAL:
527 default:
529 gtk_tree_sortable_set_sort_column_id(
530 GTK_TREE_SORTABLE( store ),
531 GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
532 0 );
533 break;
539 * nact_tree_model_dump:
540 * @model: this #NactTreeModel instance.
542 * Briefly dumps the content of the tree.
544 void
545 nact_tree_model_dump( NactTreeModel *model )
547 static const gchar *thisfn = "nact_tree_model_dump";
548 GtkTreeStore *store;
549 ntmDumpStruct *ntm;
551 g_return_if_fail( NACT_IS_TREE_MODEL( model ));
553 if( !model->private->dispose_has_run ){
555 store = GTK_TREE_STORE( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model )));
557 g_debug( "%s: %s at %p, %s at %p", thisfn,
558 G_OBJECT_TYPE_NAME( model ), ( void * ) model, G_OBJECT_TYPE_NAME( store ), ( void * ) store );
560 ntm = g_new0( ntmDumpStruct, 1 );
561 ntm->fname = g_strdup( thisfn );
562 ntm->prefix = g_strdup( "" );
564 nact_tree_model_iter( model, ( FnIterOnStore ) dump_store, ntm );
566 g_free( ntm->prefix );
567 g_free( ntm->fname );
568 g_free( ntm );
573 * nact_tree_model_fill:
574 * @model: this #NactTreeModel instance.
575 * @ŧreeview: the #GtkTreeView widget.
576 * @items: this list of items, usually from #NAPivot, which will be used
577 * to fill up the tree store.
578 * @are_profiles_displayed: whether to show profiles (in edition mode),
579 * or not (in export mode).
581 * Fill up the tree store with specified items.
583 * We enter with the GSList owned by NAPivot which contains the ordered
584 * list of level-zero items. We want have a duplicate of this list in
585 * tree store, so that we are able to freely edit it.
587 void
588 nact_tree_model_fill( NactTreeModel *model, GList *items, gboolean are_profiles_displayed )
590 static const gchar *thisfn = "nact_tree_model_fill";
591 GtkTreeStore *ts_model;
592 GList *it;
593 NAObject *duplicate;
595 g_return_if_fail( NACT_IS_TREE_MODEL( model ));
597 g_debug( "%s: model=%p, items=%p (%d items), are_profiles_displayed=%s",
598 thisfn,
599 ( void * ) model,
600 ( void * ) items, g_list_length( items ),
601 are_profiles_displayed ? "True":"False" );
603 if( !model->private->dispose_has_run ){
605 model->private->are_profiles_displayed = are_profiles_displayed;
606 ts_model = GTK_TREE_STORE( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model )));
607 gtk_tree_store_clear( ts_model );
609 for( it = items ; it ; it = it->next ){
610 duplicate = ( NAObject * ) na_object_duplicate( it->data );
611 na_object_check_status( duplicate );
612 fill_tree_store( ts_model, model->private->treeview, duplicate, are_profiles_displayed, NULL );
613 na_object_unref( duplicate );
619 * nact_tree_model_insert:
620 * @model: this #NactTreeModel instance.
621 * @object: a #NAObject-derived object to be inserted.
622 * @path: the #GtkTreePath which specifies the insertion path.
623 * @parent: set to the parent or the object itself.
625 * Insert a new row at the given position.
627 * Gtk API uses to returns iter ; but at least when inserting a new
628 * profile in an action, we may have store_iter_path="0:1" (good), but
629 * iter_path="0:0" (bad) - so we'd rather return a string path.
631 * Returns: the actual insertion path, which may be different from the
632 * asked insertion path if tree is sorted.
634 GtkTreePath *
635 nact_tree_model_insert( NactTreeModel *model, const NAObject *object, GtkTreePath *path, NAObject **parent )
637 static const gchar *thisfn = "nact_tree_model_insert";
638 GtkTreeModel *store;
639 gchar *path_str;
640 GtkTreeIter iter;
641 GtkTreeIter parent_iter;
642 GtkTreePath *parent_path;
643 GtkTreePath *inserted_path;
644 NAObject *parent_obj;
645 gboolean has_parent;
646 GtkTreeIter sibling_iter;
647 NAObject *sibling_obj;
648 gboolean has_sibling;
650 path_str = gtk_tree_path_to_string( path );
651 g_debug( "%s: model=%p, object=%p (%s, ref_count=%d), path=%p (%s), parent=%p",
652 thisfn,
653 ( void * ) model,
654 ( void * ) object, G_OBJECT_TYPE_NAME( object ), G_OBJECT( object )->ref_count,
655 ( void * ) path, path_str, ( void * ) parent );
656 g_free( path_str );
657 g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), NULL );
658 g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
660 inserted_path = NULL;
662 if( !model->private->dispose_has_run ){
664 store = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model ));
665 has_parent = FALSE;
666 parent_obj = NULL;
667 sibling_obj = NULL;
669 remove_if_exists( model, store, object );
671 /* may be FALSE when store is empty */
672 has_sibling = gtk_tree_model_get_iter( store, &sibling_iter, path );
673 if( has_sibling ){
674 gtk_tree_model_get( store, &sibling_iter, IACTIONS_LIST_NAOBJECT_COLUMN, &sibling_obj, -1 );
675 g_object_unref( sibling_obj );
677 g_debug( "%s: has_sibling=%s, sibling_obj=%p", thisfn, has_sibling ? "True":"False", ( void * ) sibling_obj );
679 if( gtk_tree_path_get_depth( path ) > 1 ){
681 has_parent = TRUE;
682 parent_path = gtk_tree_path_copy( path );
683 gtk_tree_path_up( parent_path );
684 gtk_tree_model_get_iter( store, &parent_iter, parent_path );
685 gtk_tree_path_free( parent_path );
687 gtk_tree_model_get( store, &parent_iter, IACTIONS_LIST_NAOBJECT_COLUMN, &parent_obj, -1 );
688 g_object_unref( parent_obj );
690 if( parent && !*parent ){
691 *parent = parent_obj;
694 if( has_sibling ){
695 na_object_insert_item( parent_obj, object, sibling_obj );
696 } else {
697 na_object_append_item( parent_obj, object );
700 na_object_set_parent( object, parent_obj );
702 g_debug( "%s: has_parent=%s, parent_obj=%p", thisfn, has_parent ? "True":"False", ( void * ) parent_obj );
704 gtk_tree_store_insert_before(
705 GTK_TREE_STORE( store ), &iter,
706 has_parent ? &parent_iter : NULL,
707 has_sibling ? &sibling_iter : NULL );
708 gtk_tree_store_set( GTK_TREE_STORE( store ), &iter, IACTIONS_LIST_NAOBJECT_COLUMN, object, -1 );
709 display_item( GTK_TREE_STORE( store ), model->private->treeview, &iter, object );
711 inserted_path = gtk_tree_model_get_path( store, &iter );
714 return( inserted_path );
718 * nact_tree_model_insert_into:
719 * @model:
720 * @object:
721 * @path:
722 * @parent:
724 GtkTreePath *
725 nact_tree_model_insert_into( NactTreeModel *model, const NAObject *object, GtkTreePath *path, NAObject **parent )
727 static const gchar *thisfn = "nact_tree_model_insert_into";
728 GtkTreeModel *store;
729 GtkTreeIter iter;
730 GtkTreeIter parent_iter;
731 GtkTreePath *new_path;
732 gchar *path_str;
734 path_str = gtk_tree_path_to_string( path );
735 g_debug( "%s: model=%p, object=%p (%s, ref_count=%d), path=%p (%s), parent=%p",
736 thisfn,
737 ( void * ) model,
738 ( void * ) object, G_OBJECT_TYPE_NAME( object ), G_OBJECT( object )->ref_count,
739 ( void * ) path, path_str, ( void * ) parent );
740 g_free( path_str );
741 g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), NULL );
742 g_return_val_if_fail( NA_IS_OBJECT( object ), NULL );
744 new_path = NULL;
746 if( !model->private->dispose_has_run ){
748 store = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model ));
750 if( !gtk_tree_model_get_iter( store, &parent_iter, path )){
751 path_str = gtk_tree_path_to_string( path );
752 g_warning( "%s: unable to get iter at path %s", thisfn, path_str );
753 g_free( path_str );
754 return( NULL );
756 gtk_tree_model_get( store, &parent_iter, IACTIONS_LIST_NAOBJECT_COLUMN, parent, -1 );
757 g_object_unref( *parent );
759 na_object_insert_item( *parent, object, NULL );
760 na_object_set_parent( object, *parent );
762 gtk_tree_store_insert_after( GTK_TREE_STORE( store ), &iter, &parent_iter, NULL );
763 gtk_tree_store_set( GTK_TREE_STORE( store ), &iter, IACTIONS_LIST_NAOBJECT_COLUMN, object, -1 );
764 display_item( GTK_TREE_STORE( store ), model->private->treeview, &iter, object );
766 new_path = gtk_tree_model_get_path( store, &iter );
769 return( new_path );
773 * nact_tree_model_iter:
774 * @model: this #NactTreeModel instance.
776 void
777 nact_tree_model_iter( NactTreeModel *model, FnIterOnStore fn, gpointer user_data )
779 GtkTreeStore *store;
781 g_return_if_fail( NACT_IS_TREE_MODEL( model ));
783 if( !model->private->dispose_has_run ){
785 store = GTK_TREE_STORE( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model )));
786 iter_on_store( model, GTK_TREE_MODEL( store ), NULL, fn, user_data );
791 * nact_tree_model_object_at_path:
792 * @model: this #NactTreeModel instance.
793 * @path: the #GtkTreePath to be searched.
795 * Returns: the #NAObject at the given @path if any, or NULL.
797 * The reference count of the object is not modified.
799 NAObject *
800 nact_tree_model_object_at_path( NactTreeModel *model, GtkTreePath *path )
802 NAObject *object;
803 GtkTreeModel *store;
804 GtkTreeIter iter;
806 g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), NULL );
808 object = NULL;
810 if( !model->private->dispose_has_run ){
812 store = gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model ));
814 if( gtk_tree_model_get_iter( store, &iter, path )){
815 gtk_tree_model_get( store, &iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
816 g_object_unref( object );
820 return( object );
824 * nact_tree_model_remove:
825 * @model: this #NactTreeModel instance.
826 * @object: the #NAObject to be deleted.
828 * Recursively deletes the specified object.
830 * Returns: a path which may be suitable for the next selection.
832 GtkTreePath *
833 nact_tree_model_remove( NactTreeModel *model, NAObject *object )
835 static const gchar *thisfn = "nact_tree_model_remove";
836 GtkTreeIter iter;
837 GtkTreeStore *store;
838 NAObjectItem *parent;
839 GtkTreePath *path = NULL;
841 g_debug( "%s: model=%p, object=%p (%s)",
842 thisfn, ( void * ) model, ( void * ) object, object ? G_OBJECT_TYPE_NAME( object ) : "(null)" );
843 g_return_val_if_fail( NACT_IS_TREE_MODEL( model ), NULL );
845 if( !model->private->dispose_has_run ){
847 store = GTK_TREE_STORE( gtk_tree_model_filter_get_model( GTK_TREE_MODEL_FILTER( model )));
849 if( search_for_object( model, GTK_TREE_MODEL( store ), object, &iter )){
850 parent = na_object_get_parent( object );
851 g_debug( "%s: object=%p, parent=%p", thisfn, ( void * ) object, ( void * ) parent );
852 if( parent ){
853 na_object_remove_item( parent, object );
854 na_object_check_status_up( parent );
856 path = gtk_tree_model_get_path( GTK_TREE_MODEL( store ), &iter );
857 remove_items( store, &iter );
861 return( path );
864 static void
865 append_item( GtkTreeStore *model, GtkTreeView *treeview, GtkTreeIter *parent, GtkTreeIter *iter, const NAObject *object )
867 /*g_debug( "nact_tree_model_append_item: object=%p (ref_count=%d), parent=%p",
868 ( void * ) object, G_OBJECT( object )->ref_count, ( void * ) parent );*/
870 gtk_tree_store_append( model, iter, parent );
871 gtk_tree_store_set( model, iter, IACTIONS_LIST_NAOBJECT_COLUMN, object, -1 );
872 display_item( model, treeview, iter, object );
875 static void
876 display_item( GtkTreeStore *model, GtkTreeView *treeview, GtkTreeIter *iter, const NAObject *object )
878 gchar *label = na_object_get_label( object );
879 gtk_tree_store_set( model, iter, IACTIONS_LIST_LABEL_COLUMN, label, -1 );
880 g_free( label );
882 if( NA_IS_OBJECT_ITEM( object )){
883 gchar *icon_name = na_object_get_icon( object );
884 GdkPixbuf *icon = nact_gtk_utils_get_pixbuf( icon_name, GTK_WIDGET( treeview ), GTK_ICON_SIZE_MENU );
885 gtk_tree_store_set( model, iter, IACTIONS_LIST_ICON_COLUMN, icon, -1 );
886 g_object_unref( icon );
890 static gboolean
891 dump_store( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmDumpStruct *ntm )
893 gint depth;
894 gint i;
895 GString *prefix;
896 gchar *id, *label;
897 NAObjectItem *origin;
899 depth = gtk_tree_path_get_depth( path );
900 prefix = g_string_new( ntm->prefix );
901 for( i=1 ; i<depth ; ++i ){
902 g_string_append_printf( prefix, " " );
905 id = na_object_get_id( object );
906 label = na_object_get_label( object );
907 origin = ( NAObjectItem * ) na_object_get_origin( object );
908 g_debug( "%s: %s%s at %p (ref_count=%d) \"[%s] %s\" origin=%p (%s)",
909 ntm->fname, prefix->str,
910 G_OBJECT_TYPE_NAME( object ), ( void * ) object, G_OBJECT( object )->ref_count, id, label,
911 ( void * ) origin, origin ? G_OBJECT_TYPE_NAME( object ) : "null" );
912 g_free( label );
913 g_free( id );
915 g_string_free( prefix, TRUE );
917 /* don't stop iteration */
918 return( FALSE );
921 static void
922 fill_tree_store( GtkTreeStore *model, GtkTreeView *treeview,
923 NAObject *object, gboolean are_profiles_displayed, GtkTreeIter *parent )
925 static const gchar *thisfn = "nact_tree_model_fill_tree_store";
926 GList *subitems, *it;
927 GtkTreeIter iter;
929 g_debug( "%s entering: object=%p (%s, ref_count=%d)", thisfn,
930 ( void * ) object, G_OBJECT_TYPE_NAME( object ), G_OBJECT( object )->ref_count );
932 /* an action or a menu
934 if( NA_IS_OBJECT_ITEM( object )){
935 append_item( model, treeview, parent, &iter, object );
936 subitems = na_object_get_items( object );
937 for( it = subitems ; it ; it = it->next ){
938 fill_tree_store( model, treeview, it->data, are_profiles_displayed, &iter );
941 } else {
942 g_return_if_fail( NA_IS_OBJECT_PROFILE( object ));
943 append_item( model, treeview, parent, &iter, object );
946 /*g_debug( "%s quitting: object=%p (%s, ref_count=%d)", thisfn,
947 ( void * ) object, G_OBJECT_TYPE_NAME( object ), G_OBJECT( object )->ref_count );*/
951 * only display profiles when we are in edition mode
953 static gboolean
954 filter_visible( GtkTreeModel *store, GtkTreeIter *iter, NactTreeModel *model )
956 /*static const gchar *thisfn = "nact_tree_model_filter_visible";*/
957 NAObject *object;
958 NAObjectAction *action;
959 gboolean are_profiles_displayed;
960 gint count;
962 /*g_debug( "%s: model=%p, iter=%p, window=%p", thisfn, ( void * ) model, ( void * ) iter, ( void * ) window );*/
963 /*g_debug( "%s at %p", G_OBJECT_TYPE_NAME( model ), ( void * ) model );*/
964 /* is a GtkTreeStore */
966 gtk_tree_model_get( store, iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
968 if( object ){
969 g_object_unref( object );
970 /*na_object_dump( object );*/
972 /* an action or a menu
974 if( NA_IS_OBJECT_ITEM( object )){
975 return( TRUE );
978 g_return_val_if_fail( NA_IS_OBJECT_PROFILE( object ), FALSE );
979 are_profiles_displayed = NACT_TREE_MODEL( model )->private->are_profiles_displayed;
981 if( !are_profiles_displayed ){
982 return( FALSE );
985 action = NA_OBJECT_ACTION( na_object_get_parent( object ));
986 count = na_object_get_items_count( action );
987 /*g_debug( "action=%p: count=%d", ( void * ) action, count );*/
988 /*return( TRUE );*/
989 return( count > 1 );
992 return( FALSE );
995 static void
996 iter_on_store( NactTreeModel *model, GtkTreeModel *store, GtkTreeIter *parent, FnIterOnStore fn, gpointer user_data )
998 GtkTreeIter iter;
999 gboolean stop;
1001 if( gtk_tree_model_iter_children( store, &iter, parent )){
1002 stop = iter_on_store_item( model, store, &iter, fn, user_data );
1003 while( !stop && gtk_tree_model_iter_next( store, &iter )){
1004 stop = iter_on_store_item( model, store, &iter, fn, user_data );
1009 static gboolean
1010 iter_on_store_item( NactTreeModel *model, GtkTreeModel *store, GtkTreeIter *iter, FnIterOnStore fn, gpointer user_data )
1012 NAObject *object;
1013 GtkTreePath *path;
1014 gboolean stop;
1016 gtk_tree_model_get( store, iter, IACTIONS_LIST_NAOBJECT_COLUMN, &object, -1 );
1017 /* unreffing as soon as we got the pointer so that the ref count is
1018 * unchanged in dump_store
1020 g_object_unref( object );
1021 g_debug( "nact_tree_model_iter_on_store_item: object=%p (%s, ref_count=%d)",
1022 ( void * ) object, G_OBJECT_TYPE_NAME( object ), G_OBJECT( object )->ref_count );
1024 path = gtk_tree_model_get_path( store, iter );
1026 stop = ( *fn )( model, path, object, user_data );
1028 gtk_tree_path_free( path );
1030 if( !stop ){
1031 iter_on_store( model, store, iter, fn, user_data );
1034 return( stop );
1038 * if the object, identified by its uuid, already exists, then remove it first
1040 static void
1041 remove_if_exists( NactTreeModel *model, GtkTreeModel *store, const NAObject *object )
1043 GtkTreeIter iter;
1045 if( NA_IS_OBJECT_ITEM( object )){
1046 if( search_for_object_id( model, store, object, &iter )){
1047 g_debug( "nact_tree_model_remove_if_exists: removing %s %p",
1048 G_OBJECT_TYPE_NAME( object ), ( void * ) object );
1049 gtk_tree_store_remove( GTK_TREE_STORE( store ), &iter );
1055 * recursively remove child items starting with iter
1056 * returns TRUE if iter is always valid after the remove
1058 static gboolean
1059 remove_items( GtkTreeStore *store, GtkTreeIter *iter )
1061 GtkTreeIter child;
1062 gboolean valid;
1064 while( gtk_tree_model_iter_children( GTK_TREE_MODEL( store ), &child, iter )){
1065 remove_items( store, &child );
1067 valid = gtk_tree_store_remove( store, iter );
1069 return( valid );
1072 static gboolean
1073 search_for_object( NactTreeModel *model, GtkTreeModel *store, const NAObject *object, GtkTreeIter *result_iter )
1075 gboolean found = FALSE;
1076 ntmSearchStruct *ntm;
1077 GtkTreeIter iter;
1079 ntm = g_new0( ntmSearchStruct, 1 );
1080 ntm->store = store;
1081 ntm->object = object;
1082 ntm->found = FALSE;
1083 ntm->iter = &iter;
1085 iter_on_store( model, store, NULL, ( FnIterOnStore ) search_for_object_iter, ntm );
1087 if( ntm->found ){
1088 found = TRUE;
1089 memcpy( result_iter, ntm->iter, sizeof( GtkTreeIter ));
1092 g_free( ntm );
1093 return( found );
1096 static gboolean
1097 search_for_object_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmSearchStruct *ntm )
1099 if( object == ntm->object ){
1100 if( gtk_tree_model_get_iter( ntm->store, ntm->iter, path )){
1101 ntm->found = TRUE;
1105 /* stop iteration when found */
1106 return( ntm->found );
1109 static gboolean
1110 search_for_object_id( NactTreeModel *model, GtkTreeModel *store, const NAObject *object, GtkTreeIter *result_iter )
1112 gboolean found = FALSE;
1113 ntmSearchIdStruct *ntm;
1114 GtkTreeIter iter;
1116 ntm = g_new0( ntmSearchIdStruct, 1 );
1117 ntm->store = store;
1118 ntm->id = na_object_get_id( object );
1119 ntm->found = FALSE;
1120 ntm->iter = &iter;
1122 iter_on_store( model, store, NULL, ( FnIterOnStore ) search_for_object_id_iter, ntm );
1124 if( ntm->found ){
1125 found = TRUE;
1126 memcpy( result_iter, ntm->iter, sizeof( GtkTreeIter ));
1129 g_free( ntm->id );
1130 g_free( ntm );
1131 return( found );
1134 static gboolean
1135 search_for_object_id_iter( NactTreeModel *model, GtkTreePath *path, NAObject *object, ntmSearchIdStruct *ntm )
1137 gchar *id;
1139 id = na_object_get_id( object );
1141 if( !g_ascii_strcasecmp( id, ntm->id )){
1142 if( gtk_tree_model_get_iter( ntm->store, ntm->iter, path )){
1143 ntm->found = TRUE;
1147 g_free( id );
1149 /* stop iteration when found */
1150 return( ntm->found );
1153 static gint
1154 sort_actions_list( GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data )
1156 /*static const gchar *thisfn = "nact_tree_model_sort_actions_list";*/
1157 NAObjectId *obj_a, *obj_b;
1158 gint ret;
1160 /*g_debug( "%s: model=%p, a=%p, b=%p, window=%p", thisfn, ( void * ) model, ( void * ) a, ( void * ) b, ( void * ) window );*/
1162 gtk_tree_model_get( model, a, IACTIONS_LIST_NAOBJECT_COLUMN, &obj_a, -1 );
1163 gtk_tree_model_get( model, b, IACTIONS_LIST_NAOBJECT_COLUMN, &obj_b, -1 );
1165 g_object_unref( obj_b );
1166 g_object_unref( obj_a );
1168 if( NA_IS_OBJECT_PROFILE( obj_a )){
1169 ret = 0;
1170 } else {
1171 ret = na_object_sort_alpha_asc( obj_a, obj_b );
1174 /*g_debug( "%s: ret=%d", thisfn, ret );*/
1175 return( ret );