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
;
76 PIVOT_PROP_LOADABLE_ID
,
79 /* count of properties */
90 static GObjectClass
*st_parent_class
= NULL
;
91 static gint st_burst_timeout
= 100; /* burst timeout in msec */
92 static gint st_signals
[ LAST_SIGNAL
] = { 0 };
94 static GType
register_type( void );
95 static void class_init( NAPivotClass
*klass
);
96 static void instance_init( GTypeInstance
*instance
, gpointer klass
);
97 static void instance_constructed( GObject
*object
);
98 static void instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
);
99 static void instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
);
100 static void instance_dispose( GObject
*object
);
101 static void instance_finalize( GObject
*object
);
103 static NAObjectItem
*get_item_from_tree( const NAPivot
*pivot
, GList
*tree
, const gchar
*id
);
105 /* NAIIOProvider management */
106 static void on_items_changed_timeout( NAPivot
*pivot
);
109 na_pivot_get_type( void )
111 static GType object_type
= 0;
114 object_type
= register_type();
117 return( object_type
);
121 register_type( void )
123 static const gchar
*thisfn
= "na_pivot_register_type";
126 static GTypeInfo info
= {
127 sizeof( NAPivotClass
),
128 ( GBaseInitFunc
) NULL
,
129 ( GBaseFinalizeFunc
) NULL
,
130 ( GClassInitFunc
) class_init
,
135 ( GInstanceInitFunc
) instance_init
138 g_debug( "%s", thisfn
);
140 type
= g_type_register_static( G_TYPE_OBJECT
, "NAPivot", &info
, 0 );
146 class_init( NAPivotClass
*klass
)
148 static const gchar
*thisfn
= "na_pivot_class_init";
149 GObjectClass
*object_class
;
151 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
153 st_parent_class
= g_type_class_peek_parent( klass
);
155 object_class
= G_OBJECT_CLASS( klass
);
156 object_class
->constructed
= instance_constructed
;
157 object_class
->set_property
= instance_set_property
;
158 object_class
->get_property
= instance_get_property
;
159 object_class
->dispose
= instance_dispose
;
160 object_class
->finalize
= instance_finalize
;
162 g_object_class_install_property( object_class
, PIVOT_PROP_LOADABLE_ID
,
166 "The set of loadble items",
168 G_PARAM_CONSTRUCT
| G_PARAM_STATIC_STRINGS
| G_PARAM_READWRITE
));
170 g_object_class_install_property( object_class
, PIVOT_PROP_TREE_ID
,
171 g_param_spec_pointer(
174 "Hierarchical tree of items",
175 G_PARAM_STATIC_STRINGS
| G_PARAM_READWRITE
));
177 klass
->private = g_new0( NAPivotClassPrivate
, 1 );
180 * NAPivot::pivot-items-changed:
182 * This signal is sent by NAPivot at the end of a burst of modifications
183 * as signaled by i/o providers.
185 * The signal is registered without any default handler.
187 st_signals
[ ITEMS_CHANGED
] = g_signal_new(
188 PIVOT_SIGNAL_ITEMS_CHANGED
,
190 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
191 0, /* class offset */
192 NULL
, /* accumulator */
193 NULL
, /* accumulator data */
194 g_cclosure_marshal_VOID__VOID
,
200 instance_init( GTypeInstance
*instance
, gpointer klass
)
202 static const gchar
*thisfn
= "na_pivot_instance_init";
205 g_return_if_fail( NA_IS_PIVOT( instance
));
207 g_debug( "%s: instance=%p (%s), klass=%p",
208 thisfn
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
), ( void * ) klass
);
210 self
= NA_PIVOT( instance
);
212 self
->private = g_new0( NAPivotPrivate
, 1 );
214 self
->private->dispose_has_run
= FALSE
;
215 self
->private->loadable_set
= PIVOT_LOAD_NONE
;
216 self
->private->modules
= NULL
;
217 self
->private->tree
= NULL
;
219 /* initialize timeout parameters for 'item-changed' handler
221 self
->private->change_timeout
.timeout
= st_burst_timeout
;
222 self
->private->change_timeout
.handler
= ( NATimeoutFunc
) on_items_changed_timeout
;
223 self
->private->change_timeout
.user_data
= self
;
224 self
->private->change_timeout
.source_id
= 0;
228 instance_constructed( GObject
*object
)
230 static const gchar
*thisfn
= "na_pivot_instance_constructed";
233 g_return_if_fail( NA_IS_PIVOT( object
));
234 self
= NA_PIVOT( object
);
236 if( !self
->private->dispose_has_run
){
238 g_debug( "%s: object=%p", thisfn
, ( void * ) object
);
240 self
->private->modules
= na_module_load_modules();
242 /* force class initialization and io-factory registration
244 g_object_unref( na_object_action_new_with_profile());
245 g_object_unref( na_object_menu_new());
247 /* chain up to the parent class */
248 if( G_OBJECT_CLASS( st_parent_class
)->constructed
){
249 G_OBJECT_CLASS( st_parent_class
)->constructed( object
);
255 instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
)
259 g_return_if_fail( NA_IS_PIVOT( object
));
260 self
= NA_PIVOT( object
);
262 if( !self
->private->dispose_has_run
){
264 switch( property_id
){
265 case PIVOT_PROP_LOADABLE_ID
:
266 g_value_set_uint( value
, self
->private->loadable_set
);
269 case PIVOT_PROP_TREE_ID
:
270 g_value_set_pointer( value
, self
->private->tree
);
274 G_OBJECT_WARN_INVALID_PROPERTY_ID( object
, property_id
, spec
);
281 instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
)
285 g_return_if_fail( NA_IS_PIVOT( object
));
286 self
= NA_PIVOT( object
);
288 if( !self
->private->dispose_has_run
){
290 switch( property_id
){
291 case PIVOT_PROP_LOADABLE_ID
:
292 self
->private->loadable_set
= g_value_get_uint( value
);
295 case PIVOT_PROP_TREE_ID
:
296 self
->private->tree
= g_value_get_pointer( value
);
300 G_OBJECT_WARN_INVALID_PROPERTY_ID( object
, property_id
, spec
);
307 instance_dispose( GObject
*object
)
309 static const gchar
*thisfn
= "na_pivot_instance_dispose";
312 g_return_if_fail( NA_IS_PIVOT( object
));
314 self
= NA_PIVOT( object
);
316 if( !self
->private->dispose_has_run
){
318 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
320 self
->private->dispose_has_run
= TRUE
;
322 /* release modules */
323 na_module_release_modules( self
->private->modules
);
324 self
->private->modules
= NULL
;
326 /* release item tree */
327 g_debug( "%s: tree=%p (count=%u)", thisfn
,
328 ( void * ) self
->private->tree
, g_list_length( self
->private->tree
));
329 na_object_dump_tree( self
->private->tree
);
330 self
->private->tree
= na_object_free_items( self
->private->tree
);
332 /* release the settings */
335 /* release the I/O Provider object list */
336 na_io_provider_unref_io_providers_list();
338 /* chain up to the parent class */
339 if( G_OBJECT_CLASS( st_parent_class
)->dispose
){
340 G_OBJECT_CLASS( st_parent_class
)->dispose( object
);
346 instance_finalize( GObject
*object
)
348 static const gchar
*thisfn
= "na_pivot_instance_finalize";
351 g_return_if_fail( NA_IS_PIVOT( object
));
353 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
355 self
= NA_PIVOT( object
);
357 g_free( self
->private );
359 /* chain call to parent class */
360 if( G_OBJECT_CLASS( st_parent_class
)->finalize
){
361 G_OBJECT_CLASS( st_parent_class
)->finalize( object
);
368 * This object takes care of all items/actions/menus/providers/settings
369 * management which is required to correctly handle file manager context
372 * When this object is instantiated, it automatically takes care of:
373 * - loading Nautilus-Actions dynamic modules;
374 * - initializing the preferences monitoring.
376 * Actual loading of items from i/o providers is delayed until a call
377 * to call to na_pivot_load_items() function, so that the caller is able
378 * to set its own needed #NAPivot properties (e.g. the loadable set of
381 * Only one #NAPivot object should be instantiated for a running application.
383 * Returns: a newly allocated #NAPivot object which should be g_object_unref()
384 * by the caller at the end of the application.
389 static const gchar
*thisfn
= "na_pivot_new";
392 g_debug( "%s", thisfn
);
394 pivot
= g_object_new( NA_PIVOT_TYPE
, NULL
);
401 * @pivot: the #NAPivot object do be dumped.
403 * Dumps the content of a #NAPivot object.
406 na_pivot_dump( const NAPivot
*pivot
)
408 static const gchar
*thisfn
= "na_pivot_dump";
412 if( !pivot
->private->dispose_has_run
){
414 g_debug( "%s: loadable_set=%d", thisfn
, pivot
->private->loadable_set
);
415 g_debug( "%s: modules=%p (%d elts)", thisfn
, ( void * ) pivot
->private->modules
, g_list_length( pivot
->private->modules
));
416 g_debug( "%s: tree=%p (%d elts)", thisfn
, ( void * ) pivot
->private->tree
, g_list_length( pivot
->private->tree
));
417 /*g_debug( "%s: monitors=%p (%d elts)", thisfn, ( void * ) pivot->private->monitors, g_list_length( pivot->private->monitors ));*/
419 for( it
= pivot
->private->tree
, i
= 0 ; it
; it
= it
->next
){
420 g_debug( "%s: [%d]: %p", thisfn
, i
++, it
->data
);
426 * na_pivot_get_providers:
427 * @pivot: this #NAPivot instance.
428 * @type: the type of searched interface.
429 * For now, we only have NA_IIO_PROVIDER_TYPE interfaces.
431 * Returns: a newly allocated list of providers of the required interface.
433 * This function is called by interfaces API in order to find the
434 * list of providers registered for their own given interface.
436 * The returned list should be release by calling na_pivot_free_providers().
439 na_pivot_get_providers( const NAPivot
*pivot
, GType type
)
441 static const gchar
*thisfn
= "na_pivot_get_providers";
444 g_return_val_if_fail( NA_IS_PIVOT( pivot
), NULL
);
446 if( !pivot
->private->dispose_has_run
){
448 g_debug( "%s: pivot=%p, type=%lu (%s)", thisfn
, ( void * ) pivot
, ( unsigned long ) type
, g_type_name( type
));
450 list
= na_module_get_extensions_for_type( pivot
->private->modules
, type
);
451 g_debug( "%s: list=%p, count=%d", thisfn
, ( void * ) list
, list
? g_list_length( list
) : 0 );
458 * na_pivot_free_providers:
459 * @providers: a list of providers.
461 * Frees a list of providers as returned from na_pivot_get_providers().
464 na_pivot_free_providers( GList
*providers
)
466 static const gchar
*thisfn
= "na_pivot_free_providers";
468 g_debug( "%s: providers=%p", thisfn
, ( void * ) providers
);
470 na_module_free_extensions_list( providers
);
475 * @pivot: this #NAPivot instance.
476 * @id: the required item identifier.
478 * Returns the specified item, action or menu.
480 * Returns: the required #NAObjectItem-derived object, or %NULL if not
483 * The returned pointer is owned by #NAPivot, and should not be
484 * g_free() nor g_object_unref() by the caller.
487 na_pivot_get_item( const NAPivot
*pivot
, const gchar
*id
)
489 NAObjectItem
*object
= NULL
;
491 g_return_val_if_fail( NA_IS_PIVOT( pivot
), NULL
);
493 if( !pivot
->private->dispose_has_run
){
495 if( !id
|| !strlen( id
)){
499 object
= get_item_from_tree( pivot
, pivot
->private->tree
, id
);
505 static NAObjectItem
*
506 get_item_from_tree( const NAPivot
*pivot
, GList
*tree
, const gchar
*id
)
508 GList
*subitems
, *ia
;
509 NAObjectItem
*found
= NULL
;
511 for( ia
= tree
; ia
&& !found
; ia
= ia
->next
){
513 gchar
*i_id
= na_object_get_id( NA_OBJECT( ia
->data
));
515 if( !g_ascii_strcasecmp( id
, i_id
)){
516 found
= NA_OBJECT_ITEM( ia
->data
);
519 if( !found
&& NA_IS_OBJECT_ITEM( ia
->data
)){
520 subitems
= na_object_get_items( ia
->data
);
521 found
= get_item_from_tree( pivot
, subitems
, id
);
529 * na_pivot_get_items:
530 * @pivot: this #NAPivot instance.
532 * Returns: the current configuration tree.
534 * The returned list is owned by this #NAPivot object, and should not
535 * be g_free(), nor g_object_unref() by the caller.
538 na_pivot_get_items( const NAPivot
*pivot
)
542 g_return_val_if_fail( NA_IS_PIVOT( pivot
), NULL
);
546 if( !pivot
->private->dispose_has_run
){
548 tree
= pivot
->private->tree
;
555 * na_pivot_load_items:
556 * @pivot: this #NAPivot instance.
558 * Loads the hierarchical list of items from I/O providers.
561 na_pivot_load_items( NAPivot
*pivot
)
563 static const gchar
*thisfn
= "na_pivot_load_items";
564 GSList
*messages
, *im
;
566 g_return_if_fail( NA_IS_PIVOT( pivot
));
568 if( !pivot
->private->dispose_has_run
){
570 g_debug( "%s: pivot=%p", thisfn
, ( void * ) pivot
);
573 na_object_free_items( pivot
->private->tree
);
574 pivot
->private->tree
= na_io_provider_load_items( pivot
, pivot
->private->loadable_set
, &messages
);
576 for( im
= messages
; im
; im
= im
->next
){
577 g_warning( "%s: %s", thisfn
, ( const gchar
* ) im
->data
);
580 na_core_utils_slist_free( messages
);
585 * na_pivot_set_new_items:
586 * @pivot: this #NAPivot instance.
587 * @tree: the new tree of items.
589 * Replace the current list with this one, acquiring the full ownership
590 * of the provided @tree.
593 na_pivot_set_new_items( NAPivot
*pivot
, GList
*items
)
595 static const gchar
*thisfn
= "na_pivot_set_new_items";
597 g_return_if_fail( NA_IS_PIVOT( pivot
));
599 if( !pivot
->private->dispose_has_run
){
601 g_debug( "%s: pivot=%p, items=%p (count=%d)",
602 thisfn
, ( void * ) pivot
, ( void * ) items
, items
? g_list_length( items
) : 0 );
604 na_object_free_items( pivot
->private->tree
);
605 pivot
->private->tree
= items
;
610 * na_pivot_on_item_changed_handler:
611 * @provider: the #NAIIOProvider which has emitted the signal.
612 * @pivot: this #NAPivot instance.
614 * This handler is trigerred by #NAIIOProvider providers when an action
615 * is changed in their underlying storage subsystems.
617 * The NAIIOProvider is supposed to have itself already summarized
618 * a minima its own burst of notifications.
620 * We don't care of updating our internal list with each and every
621 * atomic modification; instead we wait for the end of notifications
622 * serie, and then signal our consumers.
625 na_pivot_on_item_changed_handler( NAIIOProvider
*provider
, NAPivot
*pivot
)
627 static const gchar
*thisfn
= "na_pivot_on_item_changed_handler";
629 g_return_if_fail( NA_IS_IIO_PROVIDER( provider
));
630 g_return_if_fail( NA_IS_PIVOT( pivot
));
632 if( !pivot
->private->dispose_has_run
){
633 g_debug( "%s: provider=%p, pivot=%p", thisfn
, ( void * ) provider
, ( void * ) pivot
);
635 na_timeout_event( &pivot
->private->change_timeout
);
640 * this callback is triggered after having received a first 'item-changed' event,
641 * and having received no more event during a 'st_burst_timeout' period; we can
642 * so suppose that the burst if modification events is terminated
643 * this is up to NAPivot to send now its summarized signal
646 on_items_changed_timeout( NAPivot
*pivot
)
648 static const gchar
*thisfn
= "na_pivot_on_items_changed_timeout";
650 g_return_if_fail( NA_IS_PIVOT( pivot
));
652 g_debug( "%s: emitting %s signal", thisfn
, PIVOT_SIGNAL_ITEMS_CHANGED
);
653 g_signal_emit_by_name(( gpointer
) pivot
, PIVOT_SIGNAL_ITEMS_CHANGED
);
657 * na_pivot_set_loadable:
658 * @pivot: this #NAPivot instance.
659 * @loadable: the population of items to be loaded.
661 * Sets the loadable set.
664 na_pivot_set_loadable( NAPivot
*pivot
, guint loadable
)
666 g_return_if_fail( NA_IS_PIVOT( pivot
));
668 if( !pivot
->private->dispose_has_run
){
670 pivot
->private->loadable_set
= loadable
;