Define new 'pivot-prop-loadable' property
[nautilus-actions.git] / src / core / na-pivot.c
blob49d96907a85c5edeed42c79350f43405f1b1f4c3
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-core-utils.h>
38 #include <api/na-timeout.h>
40 #include "na-io-provider.h"
41 #include "na-iprefs.h"
42 #include "na-module.h"
43 #include "na-pivot.h"
45 /* private class data
47 struct _NAPivotClassPrivate {
48 void *empty; /* so that gcc -pedantic is happy */
51 /* private instance data
53 struct _NAPivotPrivate {
54 gboolean dispose_has_run;
56 guint loadable_set;
58 /* dynamically loaded modules (extension plugins)
60 GList *modules;
62 /* configuration tree of actions and menus
64 GList *tree;
66 /* timeout to manage i/o providers 'item-changed' burst
68 NATimeout change_timeout;
70 /* the preferences management
72 NASettings *settings;
75 /* NAPivot properties
77 enum {
78 PRIVOT_PROP_0,
80 PIVOT_PROP_LOADABLE_ID,
81 PIVOT_PROP_TREE_ID,
83 /* count of properties */
84 PIVOT_PROP_N
87 /* signals
89 enum {
90 ITEMS_CHANGED,
91 LAST_SIGNAL
94 static GObjectClass *st_parent_class = NULL;
95 static gint st_burst_timeout = 100; /* burst timeout in msec */
96 static gint st_signals[ LAST_SIGNAL ] = { 0 };
98 static GType register_type( void );
99 static void class_init( NAPivotClass *klass );
100 static void instance_init( GTypeInstance *instance, gpointer klass );
101 static void instance_constructed( GObject *object );
102 static void instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
103 static void instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
104 static void instance_dispose( GObject *object );
105 static void instance_finalize( GObject *object );
107 static NAObjectItem *get_item_from_tree( const NAPivot *pivot, GList *tree, const gchar *id );
109 /* NAIIOProvider management */
110 static void on_items_changed_timeout( NAPivot *pivot );
112 GType
113 na_pivot_get_type( void )
115 static GType object_type = 0;
117 if( !object_type ){
118 object_type = register_type();
121 return( object_type );
124 static GType
125 register_type( void )
127 static const gchar *thisfn = "na_pivot_register_type";
128 GType type;
130 static GTypeInfo info = {
131 sizeof( NAPivotClass ),
132 ( GBaseInitFunc ) NULL,
133 ( GBaseFinalizeFunc ) NULL,
134 ( GClassInitFunc ) class_init,
135 NULL,
136 NULL,
137 sizeof( NAPivot ),
139 ( GInstanceInitFunc ) instance_init
142 g_debug( "%s", thisfn );
144 type = g_type_register_static( G_TYPE_OBJECT, "NAPivot", &info, 0 );
146 return( type );
149 static void
150 class_init( NAPivotClass *klass )
152 static const gchar *thisfn = "na_pivot_class_init";
153 GObjectClass *object_class;
155 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
157 st_parent_class = g_type_class_peek_parent( klass );
159 object_class = G_OBJECT_CLASS( klass );
160 object_class->constructed = instance_constructed;
161 object_class->set_property = instance_set_property;
162 object_class->get_property = instance_get_property;
163 object_class->dispose = instance_dispose;
164 object_class->finalize = instance_finalize;
166 g_object_class_install_property( object_class, PIVOT_PROP_LOADABLE_ID,
167 g_param_spec_uint(
168 PIVOT_PROP_LOADABLE,
169 "Loadable set",
170 "The set of loadble items",
171 0, 255, 0,
172 G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
174 g_object_class_install_property( object_class, PIVOT_PROP_TREE_ID,
175 g_param_spec_pointer(
176 PIVOT_PROP_TREE,
177 "Items tree",
178 "Hierarchical tree of items",
179 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
181 klass->private = g_new0( NAPivotClassPrivate, 1 );
184 * NAPivot::pivot-items-changed:
186 * This signal is sent by NAPivot at the end of a burst of modifications
187 * as signaled by i/o providers.
189 * The signal is registered without any default handler.
191 st_signals[ ITEMS_CHANGED ] = g_signal_new(
192 PIVOT_SIGNAL_ITEMS_CHANGED,
193 NA_PIVOT_TYPE,
194 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
195 0, /* class offset */
196 NULL, /* accumulator */
197 NULL, /* accumulator data */
198 g_cclosure_marshal_VOID__VOID,
199 G_TYPE_NONE,
200 0 );
203 static void
204 instance_init( GTypeInstance *instance, gpointer klass )
206 static const gchar *thisfn = "na_pivot_instance_init";
207 NAPivot *self;
209 g_return_if_fail( NA_IS_PIVOT( instance ));
211 g_debug( "%s: instance=%p (%s), klass=%p",
212 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
214 self = NA_PIVOT( instance );
216 self->private = g_new0( NAPivotPrivate, 1 );
218 self->private->dispose_has_run = FALSE;
219 self->private->loadable_set = PIVOT_LOAD_NONE;
220 self->private->modules = NULL;
221 self->private->tree = NULL;
223 /* initialize timeout parameters for 'item-changed' handler
225 self->private->change_timeout.timeout = st_burst_timeout;
226 self->private->change_timeout.handler = ( NATimeoutFunc ) on_items_changed_timeout;
227 self->private->change_timeout.user_data = self;
228 self->private->change_timeout.source_id = 0;
231 static void
232 instance_constructed( GObject *object )
234 static const gchar *thisfn = "na_pivot_instance_constructed";
235 NAPivot *self;
237 g_return_if_fail( NA_IS_PIVOT( object ));
238 self = NA_PIVOT( object );
240 if( !self->private->dispose_has_run ){
242 g_debug( "%s: object=%p", thisfn, ( void * ) object );
244 self->private->modules = na_module_load_modules();
245 self->private->settings = na_settings_new();
247 /* force class initialization and io-factory registration
249 g_object_unref( na_object_action_new_with_profile());
250 g_object_unref( na_object_menu_new());
252 /* chain up to the parent class */
253 if( G_OBJECT_CLASS( st_parent_class )->constructed ){
254 G_OBJECT_CLASS( st_parent_class )->constructed( object );
259 static void
260 instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec )
262 NAPivot *self;
264 g_return_if_fail( NA_IS_PIVOT( object ));
265 self = NA_PIVOT( object );
267 if( !self->private->dispose_has_run ){
269 switch( property_id ){
270 case PIVOT_PROP_LOADABLE_ID:
271 g_value_set_uint( value, self->private->loadable_set );
272 break;
274 case PIVOT_PROP_TREE_ID:
275 g_value_set_pointer( value, self->private->tree );
276 break;
278 default:
279 G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
280 break;
285 static void
286 instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec )
288 NAPivot *self;
290 g_return_if_fail( NA_IS_PIVOT( object ));
291 self = NA_PIVOT( object );
293 if( !self->private->dispose_has_run ){
295 switch( property_id ){
296 case PIVOT_PROP_LOADABLE_ID:
297 self->private->loadable_set = g_value_get_uint( value );
298 break;
300 case PIVOT_PROP_TREE_ID:
301 self->private->tree = g_value_get_pointer( value );
302 break;
304 default:
305 G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
306 break;
311 static void
312 instance_dispose( GObject *object )
314 static const gchar *thisfn = "na_pivot_instance_dispose";
315 NAPivot *self;
317 g_return_if_fail( NA_IS_PIVOT( object ));
319 self = NA_PIVOT( object );
321 if( !self->private->dispose_has_run ){
323 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
325 self->private->dispose_has_run = TRUE;
327 /* release modules */
328 na_module_release_modules( self->private->modules );
329 self->private->modules = NULL;
331 /* release item tree */
332 g_debug( "%s: tree=%p (count=%u)", thisfn,
333 ( void * ) self->private->tree, g_list_length( self->private->tree ));
334 na_object_dump_tree( self->private->tree );
335 self->private->tree = na_object_free_items( self->private->tree );
337 /* release the settings */
338 g_object_unref( self->private->settings );
340 /* release the I/O Provider object list */
341 na_io_provider_unref_io_providers_list();
343 /* chain up to the parent class */
344 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
345 G_OBJECT_CLASS( st_parent_class )->dispose( object );
350 static void
351 instance_finalize( GObject *object )
353 static const gchar *thisfn = "na_pivot_instance_finalize";
354 NAPivot *self;
356 g_return_if_fail( NA_IS_PIVOT( object ));
358 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
360 self = NA_PIVOT( object );
362 g_free( self->private );
364 /* chain call to parent class */
365 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
366 G_OBJECT_CLASS( st_parent_class )->finalize( object );
371 * na_pivot_new:
373 * This object takes care of all items/actions/menus/providers/settings
374 * management which is required to correctly handle file manager context
375 * menus.
377 * When this object is instantiated, it automatically takes care of:
378 * - loading Nautilus-Actions dynamic modules;
379 * - initializing the preferences monitoring.
381 * Actual loading of items from i/o providers is delayed until a call
382 * to call to na_pivot_load_items() function, so that the caller is able
383 * to set its own needed #NAPivot properties (e.g. the loadable set of
384 * items).
386 * Only one #NAPivot object should be instantiated for a running application.
388 * Returns: a newly allocated #NAPivot object which should be g_object_unref()
389 * by the caller at the end of the application.
391 NAPivot *
392 na_pivot_new( void )
394 static const gchar *thisfn = "na_pivot_new";
395 NAPivot *pivot;
397 g_debug( "%s", thisfn );
399 pivot = g_object_new( NA_PIVOT_TYPE, NULL );
401 return( pivot );
405 * na_pivot_dump:
406 * @pivot: the #NAPivot object do be dumped.
408 * Dumps the content of a #NAPivot object.
410 void
411 na_pivot_dump( const NAPivot *pivot )
413 static const gchar *thisfn = "na_pivot_dump";
414 GList *it;
415 int i;
417 if( !pivot->private->dispose_has_run ){
419 g_debug( "%s: loadable_set=%d", thisfn, pivot->private->loadable_set );
420 g_debug( "%s: modules=%p (%d elts)", thisfn, ( void * ) pivot->private->modules, g_list_length( pivot->private->modules ));
421 g_debug( "%s: tree=%p (%d elts)", thisfn, ( void * ) pivot->private->tree, g_list_length( pivot->private->tree ));
422 /*g_debug( "%s: monitors=%p (%d elts)", thisfn, ( void * ) pivot->private->monitors, g_list_length( pivot->private->monitors ));*/
424 for( it = pivot->private->tree, i = 0 ; it ; it = it->next ){
425 g_debug( "%s: [%d]: %p", thisfn, i++, it->data );
431 * na_pivot_get_providers:
432 * @pivot: this #NAPivot instance.
433 * @type: the type of searched interface.
434 * For now, we only have NA_IIO_PROVIDER_TYPE interfaces.
436 * Returns: a newly allocated list of providers of the required interface.
438 * This function is called by interfaces API in order to find the
439 * list of providers registered for their own given interface.
441 * The returned list should be release by calling na_pivot_free_providers().
443 GList *
444 na_pivot_get_providers( const NAPivot *pivot, GType type )
446 static const gchar *thisfn = "na_pivot_get_providers";
447 GList *list = NULL;
449 g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
451 if( !pivot->private->dispose_has_run ){
453 g_debug( "%s: pivot=%p, type=%lu (%s)", thisfn, ( void * ) pivot, ( unsigned long ) type, g_type_name( type ));
455 list = na_module_get_extensions_for_type( pivot->private->modules, type );
456 g_debug( "%s: list=%p, count=%d", thisfn, ( void * ) list, list ? g_list_length( list ) : 0 );
459 return( list );
463 * na_pivot_free_providers:
464 * @providers: a list of providers.
466 * Frees a list of providers as returned from na_pivot_get_providers().
468 void
469 na_pivot_free_providers( GList *providers )
471 static const gchar *thisfn = "na_pivot_free_providers";
473 g_debug( "%s: providers=%p", thisfn, ( void * ) providers );
475 na_module_free_extensions_list( providers );
479 * na_pivot_get_item:
480 * @pivot: this #NAPivot instance.
481 * @id: the required item identifier.
483 * Returns the specified item, action or menu.
485 * Returns: the required #NAObjectItem-derived object, or %NULL if not
486 * found.
488 * The returned pointer is owned by #NAPivot, and should not be
489 * g_free() nor g_object_unref() by the caller.
491 NAObjectItem *
492 na_pivot_get_item( const NAPivot *pivot, const gchar *id )
494 NAObjectItem *object = NULL;
496 g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
498 if( !pivot->private->dispose_has_run ){
500 if( !id || !strlen( id )){
501 return( NULL );
504 object = get_item_from_tree( pivot, pivot->private->tree, id );
507 return( object );
510 static NAObjectItem *
511 get_item_from_tree( const NAPivot *pivot, GList *tree, const gchar *id )
513 GList *subitems, *ia;
514 NAObjectItem *found = NULL;
516 for( ia = tree ; ia && !found ; ia = ia->next ){
518 gchar *i_id = na_object_get_id( NA_OBJECT( ia->data ));
520 if( !g_ascii_strcasecmp( id, i_id )){
521 found = NA_OBJECT_ITEM( ia->data );
524 if( !found && NA_IS_OBJECT_ITEM( ia->data )){
525 subitems = na_object_get_items( ia->data );
526 found = get_item_from_tree( pivot, subitems, id );
530 return( found );
534 * na_pivot_get_items:
535 * @pivot: this #NAPivot instance.
537 * Returns: the current configuration tree.
539 * The returned list is owned by this #NAPivot object, and should not
540 * be g_free(), nor g_object_unref() by the caller.
542 GList *
543 na_pivot_get_items( const NAPivot *pivot )
545 GList *tree;
547 g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
549 tree = NULL;
551 if( !pivot->private->dispose_has_run ){
553 tree = pivot->private->tree;
556 return( tree );
560 * na_pivot_load_items:
561 * @pivot: this #NAPivot instance.
563 * Loads the hierarchical list of items from I/O providers.
565 void
566 na_pivot_load_items( NAPivot *pivot )
568 static const gchar *thisfn = "na_pivot_load_items";
569 GSList *messages, *im;
571 g_return_if_fail( NA_IS_PIVOT( pivot ));
573 if( !pivot->private->dispose_has_run ){
575 g_debug( "%s: pivot=%p", thisfn, ( void * ) pivot );
577 messages = NULL;
578 na_object_free_items( pivot->private->tree );
579 pivot->private->tree = na_io_provider_load_items( pivot, pivot->private->loadable_set, &messages );
581 for( im = messages ; im ; im = im->next ){
582 g_warning( "%s: %s", thisfn, ( const gchar * ) im->data );
585 na_core_utils_slist_free( messages );
590 * na_pivot_set_new_items:
591 * @pivot: this #NAPivot instance.
592 * @tree: the new tree of items.
594 * Replace the current list with this one, acquiring the full ownership
595 * of the provided @tree.
597 void
598 na_pivot_set_new_items( NAPivot *pivot, GList *items )
600 static const gchar *thisfn = "na_pivot_set_new_items";
602 g_return_if_fail( NA_IS_PIVOT( pivot ));
604 if( !pivot->private->dispose_has_run ){
606 g_debug( "%s: pivot=%p, items=%p (count=%d)",
607 thisfn, ( void * ) pivot, ( void * ) items, items ? g_list_length( items ) : 0 );
609 na_object_free_items( pivot->private->tree );
610 pivot->private->tree = items;
615 * na_pivot_on_item_changed_handler:
616 * @provider: the #NAIIOProvider which has emitted the signal.
617 * @pivot: this #NAPivot instance.
619 * This handler is trigerred by #NAIIOProvider providers when an action
620 * is changed in their underlying storage subsystems.
622 * The NAIIOProvider is supposed to have itself already summarized
623 * a minima its own burst of notifications.
625 * We don't care of updating our internal list with each and every
626 * atomic modification; instead we wait for the end of notifications
627 * serie, and then signal our consumers.
629 void
630 na_pivot_on_item_changed_handler( NAIIOProvider *provider, NAPivot *pivot )
632 static const gchar *thisfn = "na_pivot_on_item_changed_handler";
634 g_return_if_fail( NA_IS_IIO_PROVIDER( provider ));
635 g_return_if_fail( NA_IS_PIVOT( pivot ));
637 if( !pivot->private->dispose_has_run ){
638 g_debug( "%s: provider=%p, pivot=%p", thisfn, ( void * ) provider, ( void * ) pivot );
640 na_timeout_event( &pivot->private->change_timeout );
645 * this callback is triggered after having received a first 'item-changed' event,
646 * and having received no more event during a 'st_burst_timeout' period; we can
647 * so suppose that the burst if modification events is terminated
648 * this is up to NAPivot to send now its summarized signal
650 static void
651 on_items_changed_timeout( NAPivot *pivot )
653 static const gchar *thisfn = "na_pivot_on_items_changed_timeout";
655 g_return_if_fail( NA_IS_PIVOT( pivot ));
657 g_debug( "%s: emitting %s signal", thisfn, PIVOT_SIGNAL_ITEMS_CHANGED );
658 g_signal_emit_by_name(( gpointer ) pivot, PIVOT_SIGNAL_ITEMS_CHANGED );
662 * na_pivot_get_settings:
663 * @pivot: this #NAPivot instance.
665 * Returns: a pointer (not a reference) to the common #NASettings object.
666 * This pointer is owned by @pivot, and should not be released by the caller.
668 * Since: 3.1.0
670 NASettings *
671 na_pivot_get_settings( const NAPivot *pivot )
673 NASettings *settings;
675 g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
677 settings = NULL;
679 if( !pivot->private->dispose_has_run ){
681 settings = pivot->private->settings;
684 return( settings );
688 * na_pivot_set_loadable:
689 * @pivot: this #NAPivot instance.
690 * @loadable: the population of items to be loaded.
692 * Sets the loadable set.
694 void
695 na_pivot_set_loadable( NAPivot *pivot, guint loadable )
697 g_return_if_fail( NA_IS_PIVOT( pivot ));
699 if( !pivot->private->dispose_has_run ){
701 pivot->private->loadable_set = loadable;