Fixed typo in string
[nautilus-actions.git] / src / core / na-pivot.c
blob0f2bd4f5ed112421280a0a73022bf4d82a9a1603
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, 2012 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-module.h"
42 #include "na-pivot.h"
44 /* private class data
46 struct _NAPivotClassPrivate {
47 void *empty; /* so that gcc -pedantic is happy */
50 /* private instance data
52 struct _NAPivotPrivate {
53 gboolean dispose_has_run;
55 guint loadable_set;
57 /* dynamically loaded modules (extension plugins)
59 GList *modules;
61 /* configuration tree of actions and menus
63 GList *tree;
65 /* timeout to manage i/o providers 'item-changed' burst
67 NATimeout change_timeout;
70 /* NAPivot properties
72 enum {
73 PRIVOT_PROP_0,
75 PIVOT_PROP_LOADABLE_ID,
76 PIVOT_PROP_TREE_ID,
78 /* count of properties */
79 PIVOT_PROP_N
82 /* signals
84 enum {
85 ITEMS_CHANGED,
86 LAST_SIGNAL
89 static GObjectClass *st_parent_class = NULL;
90 static gint st_burst_timeout = 100; /* burst timeout in msec */
91 static gint st_signals[ LAST_SIGNAL ] = { 0 };
93 static GType register_type( void );
94 static void class_init( NAPivotClass *klass );
95 static void instance_init( GTypeInstance *instance, gpointer klass );
96 static void instance_constructed( GObject *object );
97 static void instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec );
98 static void instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec );
99 static void instance_dispose( GObject *object );
100 static void instance_finalize( GObject *object );
102 static NAObjectItem *get_item_from_tree( const NAPivot *pivot, GList *tree, const gchar *id );
104 /* NAIIOProvider management */
105 static void on_items_changed_timeout( NAPivot *pivot );
107 GType
108 na_pivot_get_type( void )
110 static GType object_type = 0;
112 if( !object_type ){
113 object_type = register_type();
116 return( object_type );
119 static GType
120 register_type( void )
122 static const gchar *thisfn = "na_pivot_register_type";
123 GType type;
125 static GTypeInfo info = {
126 sizeof( NAPivotClass ),
127 ( GBaseInitFunc ) NULL,
128 ( GBaseFinalizeFunc ) NULL,
129 ( GClassInitFunc ) class_init,
130 NULL,
131 NULL,
132 sizeof( NAPivot ),
134 ( GInstanceInitFunc ) instance_init
137 g_debug( "%s", thisfn );
139 type = g_type_register_static( G_TYPE_OBJECT, "NAPivot", &info, 0 );
141 return( type );
144 static void
145 class_init( NAPivotClass *klass )
147 static const gchar *thisfn = "na_pivot_class_init";
148 GObjectClass *object_class;
150 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
152 st_parent_class = g_type_class_peek_parent( klass );
154 object_class = G_OBJECT_CLASS( klass );
155 object_class->constructed = instance_constructed;
156 object_class->set_property = instance_set_property;
157 object_class->get_property = instance_get_property;
158 object_class->dispose = instance_dispose;
159 object_class->finalize = instance_finalize;
161 g_object_class_install_property( object_class, PIVOT_PROP_LOADABLE_ID,
162 g_param_spec_uint(
163 PIVOT_PROP_LOADABLE,
164 "Loadable set",
165 "The set of loadble items",
166 0, 255, 0,
167 G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
169 g_object_class_install_property( object_class, PIVOT_PROP_TREE_ID,
170 g_param_spec_pointer(
171 PIVOT_PROP_TREE,
172 "Items tree",
173 "Hierarchical tree of items",
174 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE ));
176 klass->private = g_new0( NAPivotClassPrivate, 1 );
179 * NAPivot::pivot-items-changed:
181 * This signal is sent by NAPivot at the end of a burst of modifications
182 * as signaled by i/o providers.
184 * The signal is registered without any default handler.
186 st_signals[ ITEMS_CHANGED ] = g_signal_new(
187 PIVOT_SIGNAL_ITEMS_CHANGED,
188 NA_TYPE_PIVOT,
189 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
190 0, /* class offset */
191 NULL, /* accumulator */
192 NULL, /* accumulator data */
193 g_cclosure_marshal_VOID__VOID,
194 G_TYPE_NONE,
195 0 );
198 static void
199 instance_init( GTypeInstance *instance, gpointer klass )
201 static const gchar *thisfn = "na_pivot_instance_init";
202 NAPivot *self;
204 g_return_if_fail( NA_IS_PIVOT( instance ));
206 g_debug( "%s: instance=%p (%s), klass=%p",
207 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
209 self = NA_PIVOT( instance );
211 self->private = g_new0( NAPivotPrivate, 1 );
213 self->private->dispose_has_run = FALSE;
214 self->private->loadable_set = PIVOT_LOAD_NONE;
215 self->private->modules = NULL;
216 self->private->tree = NULL;
218 /* initialize timeout parameters for 'item-changed' handler
220 self->private->change_timeout.timeout = st_burst_timeout;
221 self->private->change_timeout.handler = ( NATimeoutFunc ) on_items_changed_timeout;
222 self->private->change_timeout.user_data = self;
223 self->private->change_timeout.source_id = 0;
226 static void
227 instance_constructed( GObject *object )
229 static const gchar *thisfn = "na_pivot_instance_constructed";
230 NAPivotPrivate *priv;
232 g_return_if_fail( NA_IS_PIVOT( object ));
234 priv = NA_PIVOT( object )->private;
236 if( !priv->dispose_has_run ){
238 /* chain up to the parent class */
239 if( G_OBJECT_CLASS( st_parent_class )->constructed ){
240 G_OBJECT_CLASS( st_parent_class )->constructed( object );
243 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
245 priv->modules = na_module_load_modules();
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());
254 static void
255 instance_get_property( GObject *object, guint property_id, GValue *value, GParamSpec *spec )
257 NAPivot *self;
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 );
267 break;
269 case PIVOT_PROP_TREE_ID:
270 g_value_set_pointer( value, self->private->tree );
271 break;
273 default:
274 G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
275 break;
280 static void
281 instance_set_property( GObject *object, guint property_id, const GValue *value, GParamSpec *spec )
283 NAPivot *self;
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 );
293 break;
295 case PIVOT_PROP_TREE_ID:
296 self->private->tree = g_value_get_pointer( value );
297 break;
299 default:
300 G_OBJECT_WARN_INVALID_PROPERTY_ID( object, property_id, spec );
301 break;
306 static void
307 instance_dispose( GObject *object )
309 static const gchar *thisfn = "na_pivot_instance_dispose";
310 NAPivot *self;
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 */
333 na_settings_free();
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 );
345 static void
346 instance_finalize( GObject *object )
348 static const gchar *thisfn = "na_pivot_instance_finalize";
349 NAPivot *self;
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 );
366 * na_pivot_new:
368 * This object takes care of all items/actions/menus/providers/settings
369 * management which is required to correctly handle file manager context
370 * menus.
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
379 * items).
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.
386 NAPivot *
387 na_pivot_new( void )
389 static const gchar *thisfn = "na_pivot_new";
390 NAPivot *pivot;
392 g_debug( "%s", thisfn );
394 pivot = g_object_new( NA_TYPE_PIVOT, NULL );
396 return( pivot );
400 * na_pivot_dump:
401 * @pivot: the #NAPivot object do be dumped.
403 * Dumps the content of a #NAPivot object.
405 void
406 na_pivot_dump( const NAPivot *pivot )
408 static const gchar *thisfn = "na_pivot_dump";
409 GList *it;
410 int i;
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_TYPE_IIO_PROVIDER 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().
438 GList *
439 na_pivot_get_providers( const NAPivot *pivot, GType type )
441 static const gchar *thisfn = "na_pivot_get_providers";
442 GList *list = NULL;
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 );
454 return( list );
458 * na_pivot_free_providers:
459 * @providers: a list of providers.
461 * Frees a list of providers as returned from na_pivot_get_providers().
463 void
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 );
474 * na_pivot_get_item:
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
481 * found.
483 * The returned pointer is owned by #NAPivot, and should not be
484 * g_free() nor g_object_unref() by the caller.
486 NAObjectItem *
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 )){
496 return( NULL );
499 object = get_item_from_tree( pivot, pivot->private->tree, id );
502 return( object );
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 );
525 return( found );
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.
537 GList *
538 na_pivot_get_items( const NAPivot *pivot )
540 GList *tree;
542 g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
544 tree = NULL;
546 if( !pivot->private->dispose_has_run ){
548 tree = pivot->private->tree;
551 return( tree );
555 * na_pivot_load_items:
556 * @pivot: this #NAPivot instance.
558 * Loads the hierarchical list of items from I/O providers.
560 void
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 );
572 messages = NULL;
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.
592 void
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.
624 void
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
645 static void
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.
663 void
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;