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.
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)
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"
47 struct _NAPivotClassPrivate
{
48 void *empty
; /* so that gcc -pedantic is happy */
51 /* private instance data
53 struct _NAPivotPrivate
{
54 gboolean dispose_has_run
;
58 /* dynamically loaded modules (extension plugins)
62 /* configuration tree of actions and menus
66 /* timeout to manage i/o providers 'item-changed' burst
68 NATimeout change_timeout
;
70 /* the preferences management
80 PIVOT_PROP_LOADABLE_ID
,
83 /* count of properties */
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
);
113 na_pivot_get_type( void )
115 static GType object_type
= 0;
118 object_type
= register_type();
121 return( object_type
);
125 register_type( void )
127 static const gchar
*thisfn
= "na_pivot_register_type";
130 static GTypeInfo info
= {
131 sizeof( NAPivotClass
),
132 ( GBaseInitFunc
) NULL
,
133 ( GBaseFinalizeFunc
) NULL
,
134 ( GClassInitFunc
) class_init
,
139 ( GInstanceInitFunc
) instance_init
142 g_debug( "%s", thisfn
);
144 type
= g_type_register_static( G_TYPE_OBJECT
, "NAPivot", &info
, 0 );
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
,
170 "The set of loadble items",
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(
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
,
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
,
204 instance_init( GTypeInstance
*instance
, gpointer klass
)
206 static const gchar
*thisfn
= "na_pivot_instance_init";
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;
232 instance_constructed( GObject
*object
)
234 static const gchar
*thisfn
= "na_pivot_instance_constructed";
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
);
260 instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
)
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
);
274 case PIVOT_PROP_TREE_ID
:
275 g_value_set_pointer( value
, self
->private->tree
);
279 G_OBJECT_WARN_INVALID_PROPERTY_ID( object
, property_id
, spec
);
286 instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
)
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
);
300 case PIVOT_PROP_TREE_ID
:
301 self
->private->tree
= g_value_get_pointer( value
);
305 G_OBJECT_WARN_INVALID_PROPERTY_ID( object
, property_id
, spec
);
312 instance_dispose( GObject
*object
)
314 static const gchar
*thisfn
= "na_pivot_instance_dispose";
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
);
351 instance_finalize( GObject
*object
)
353 static const gchar
*thisfn
= "na_pivot_instance_finalize";
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
);
373 * This object takes care of all items/actions/menus/providers/settings
374 * management which is required to correctly handle file manager context
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
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.
394 static const gchar
*thisfn
= "na_pivot_new";
397 g_debug( "%s", thisfn
);
399 pivot
= g_object_new( NA_PIVOT_TYPE
, NULL
);
406 * @pivot: the #NAPivot object do be dumped.
408 * Dumps the content of a #NAPivot object.
411 na_pivot_dump( const NAPivot
*pivot
)
413 static const gchar
*thisfn
= "na_pivot_dump";
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().
444 na_pivot_get_providers( const NAPivot
*pivot
, GType type
)
446 static const gchar
*thisfn
= "na_pivot_get_providers";
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 );
463 * na_pivot_free_providers:
464 * @providers: a list of providers.
466 * Frees a list of providers as returned from na_pivot_get_providers().
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
);
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
488 * The returned pointer is owned by #NAPivot, and should not be
489 * g_free() nor g_object_unref() by the caller.
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
)){
504 object
= get_item_from_tree( pivot
, pivot
->private->tree
, id
);
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
);
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.
543 na_pivot_get_items( const NAPivot
*pivot
)
547 g_return_val_if_fail( NA_IS_PIVOT( pivot
), NULL
);
551 if( !pivot
->private->dispose_has_run
){
553 tree
= pivot
->private->tree
;
560 * na_pivot_load_items:
561 * @pivot: this #NAPivot instance.
563 * Loads the hierarchical list of items from I/O providers.
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
);
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.
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.
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
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.
671 na_pivot_get_settings( const NAPivot
*pivot
)
673 NASettings
*settings
;
675 g_return_val_if_fail( NA_IS_PIVOT( pivot
), NULL
);
679 if( !pivot
->private->dispose_has_run
){
681 settings
= pivot
->private->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.
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
;