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)
35 #include <api/na-core-utils.h>
36 #include <api/na-gconf-utils.h>
37 #include <api/na-object-api.h>
39 #include "na-io-provider.h"
40 #include "na-settings.h"
41 #include "na-updater.h"
45 struct _NAUpdaterClassPrivate
{
46 void *empty
; /* so that gcc -pedantic is happy */
49 /* private instance data
51 struct _NAUpdaterPrivate
{
52 gboolean dispose_has_run
;
53 gboolean are_preferences_locked
;
54 gboolean is_level_zero_writable
;
57 static NAPivotClass
*st_parent_class
= NULL
;
59 static GType
register_type( void );
60 static void class_init( NAUpdaterClass
*klass
);
61 static void instance_init( GTypeInstance
*instance
, gpointer klass
);
62 static void instance_dispose( GObject
*object
);
63 static void instance_finalize( GObject
*object
);
65 static gboolean
are_preferences_locked( const NAUpdater
*updater
);
66 static gboolean
is_level_zero_writable( const NAUpdater
*updater
);
67 static void set_writability_status( NAObjectItem
*item
, const NAUpdater
*updater
);
70 na_updater_get_type( void )
72 static GType object_type
= 0;
75 object_type
= register_type();
78 return( object_type
);
84 static const gchar
*thisfn
= "na_updater_register_type";
87 static GTypeInfo info
= {
88 sizeof( NAUpdaterClass
),
89 ( GBaseInitFunc
) NULL
,
90 ( GBaseFinalizeFunc
) NULL
,
91 ( GClassInitFunc
) class_init
,
96 ( GInstanceInitFunc
) instance_init
99 g_debug( "%s", thisfn
);
101 type
= g_type_register_static( NA_PIVOT_TYPE
, "NAUpdater", &info
, 0 );
107 class_init( NAUpdaterClass
*klass
)
109 static const gchar
*thisfn
= "na_updater_class_init";
110 GObjectClass
*object_class
;
112 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
114 st_parent_class
= g_type_class_peek_parent( klass
);
116 object_class
= G_OBJECT_CLASS( klass
);
117 object_class
->dispose
= instance_dispose
;
118 object_class
->finalize
= instance_finalize
;
120 klass
->private = g_new0( NAUpdaterClassPrivate
, 1 );
124 instance_init( GTypeInstance
*instance
, gpointer klass
)
126 static const gchar
*thisfn
= "na_updater_instance_init";
129 g_return_if_fail( NA_IS_UPDATER( instance
));
131 g_debug( "%s: instance=%p (%s), klass=%p",
132 thisfn
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
), ( void * ) klass
);
134 self
= NA_UPDATER( instance
);
136 self
->private = g_new0( NAUpdaterPrivate
, 1 );
138 self
->private->dispose_has_run
= FALSE
;
142 instance_dispose( GObject
*object
)
144 static const gchar
*thisfn
= "na_updater_instance_dispose";
147 g_return_if_fail( NA_IS_UPDATER( object
));
149 self
= NA_UPDATER( object
);
151 if( !self
->private->dispose_has_run
){
153 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
155 self
->private->dispose_has_run
= TRUE
;
157 /* chain up to the parent class */
158 if( G_OBJECT_CLASS( st_parent_class
)->dispose
){
159 G_OBJECT_CLASS( st_parent_class
)->dispose( object
);
165 instance_finalize( GObject
*object
)
167 static const gchar
*thisfn
= "na_updater_instance_finalize";
170 g_return_if_fail( NA_IS_UPDATER( object
));
172 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
174 self
= NA_UPDATER( object
);
176 g_free( self
->private );
178 /* chain call to parent class */
179 if( G_OBJECT_CLASS( st_parent_class
)->finalize
){
180 G_OBJECT_CLASS( st_parent_class
)->finalize( object
);
187 * Returns: a newly allocated #NAUpdater object.
190 na_updater_new( void )
192 static const gchar
*thisfn
= "na_updater_new";
195 g_debug( "%s", thisfn
);
197 updater
= g_object_new( NA_UPDATER_TYPE
, NULL
);
199 updater
->private->are_preferences_locked
= are_preferences_locked( updater
);
200 updater
->private->is_level_zero_writable
= is_level_zero_writable( updater
);
206 are_preferences_locked( const NAUpdater
*updater
)
210 NASettings
*settings
;
212 settings
= na_pivot_get_settings( NA_PIVOT( updater
));
213 are_locked
= na_settings_get_boolean( settings
, NA_IPREFS_ADMIN_PREFERENCES_LOCKED
, NULL
, &mandatory
);
215 return( are_locked
&& mandatory
);
219 is_level_zero_writable( const NAUpdater
*updater
)
224 level_zero
= na_settings_get_string_list(
225 na_pivot_get_settings( NA_PIVOT( updater
)), NA_IPREFS_ITEMS_LEVEL_ZERO_ORDER
, NULL
, &mandatory
);
227 na_core_utils_slist_free( level_zero
);
229 return( !mandatory
);
233 * na_updater_are_preferences_locked:
234 * @updater: the #NAUpdater application object.
236 * Returns: %TRUE if preferences have been globally locked down by an
237 * admin, %FALSE else.
240 na_updater_are_preferences_locked( const NAUpdater
*updater
)
244 g_return_val_if_fail( NA_IS_UPDATER( updater
), TRUE
);
248 if( !updater
->private->dispose_has_run
){
250 are_locked
= updater
->private->are_preferences_locked
;
253 return( are_locked
);
257 * na_updater_is_level_zero_writable:
258 * @updater: the #NAUpdater application object.
260 * As of 3.1.0, level-zero is written as a user preference.
262 * This function considers that the level_zero is writable if it is not
263 * a mandatory preference.
264 * Whether preferences themselves are or not globally locked is not
265 * considered here (as imho, level zero is not really and semantically
266 * part of user preferences).
268 * This function only considers the case of the level zero itself.
269 * It does not take into account whether the i/o provider (if any)
270 * is writable, or if the item iself is not read only.
272 * Returns: %TRUE if we are able to update the level-zero list of items,
276 na_updater_is_level_zero_writable( const NAUpdater
*updater
)
278 gboolean is_writable
;
280 g_return_val_if_fail( NA_IS_UPDATER( updater
), FALSE
);
284 if( !updater
->private->dispose_has_run
){
286 is_writable
= updater
->private->is_level_zero_writable
;
289 return( is_writable
);
293 * na_updater_check_item_writability_status:
294 * @updater: this #NAUpdater object.
295 * @item: the #NAObjectItem to be written.
296 * @reason: the reason for why @item may not be writable.
298 * Returns: %TRUE: if @item is actually writable, given the current
299 * status of its provider, %FALSE else.
301 * For an item be actually writable:
302 * - the item must not be itself in a read-only store, which has been
303 * checked when first reading it
304 * - the provider must be willing (resp. able) to write
305 * - the provider must not has been locked by the admin, nor by the user
307 * If the item does not have a parent, the the level zero must be writable.
310 na_updater_check_item_writability_status( const NAUpdater
*updater
, const NAObjectItem
*item
, guint
*reason
)
313 NAIOProvider
*provider
;
314 NAObjectItem
*parent
;
316 g_return_val_if_fail( NA_IS_UPDATER( updater
), FALSE
);
317 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), FALSE
);
321 *reason
= NA_IIO_PROVIDER_STATUS_UNDETERMINED
;
324 if( !updater
->private->dispose_has_run
){
328 *reason
= NA_IIO_PROVIDER_STATUS_WRITABLE
;
331 /* Writability status of the item has been determined at load time
332 * (cf. e.g. io-desktop/nadp-reader.c:read_done_item_is_writable()).
333 * Though I'm plenty conscious that this status is subject to many
334 * changes during the life of the item (e.g. by modifying permissions
335 * on the underlying store), it is just more efficient to not reevaluate
336 * this status each time we need it, and enough for our needs..
339 if( na_object_is_readonly( item
)){
342 *reason
= NA_IIO_PROVIDER_STATUS_ITEM_READONLY
;
348 provider
= na_object_get_provider( item
);
350 writable
= na_io_provider_is_finally_writable( provider
, reason
);
352 /* the get_writable_provider() api already takes care of above checks
355 provider
= na_io_provider_find_writable_io_provider( NA_PIVOT( updater
));
359 *reason
= NA_IIO_PROVIDER_STATUS_NO_PROVIDER_FOUND
;
365 /* if needed, the level zero must be writable
368 parent
= ( NAObjectItem
* ) na_object_get_parent( item
);
370 if( updater
->private->is_level_zero_writable
){
373 *reason
= NA_IIO_PROVIDER_STATUS_LEVEL_ZERO
;
384 * na_updater_append_item:
385 * @updater: this #NAUpdater object.
386 * @item: a #NAObjectItem-derived object to be appended to the tree.
388 * Append a new item at the end of the global tree.
391 na_updater_append_item( NAUpdater
*updater
, NAObjectItem
*item
)
395 g_return_if_fail( NA_IS_UPDATER( updater
));
396 g_return_if_fail( NA_IS_OBJECT_ITEM( item
));
398 if( !updater
->private->dispose_has_run
){
400 g_object_get( G_OBJECT( updater
), PIVOT_PROP_TREE
, &tree
, NULL
);
401 tree
= g_list_append( tree
, item
);
402 g_object_set( G_OBJECT( updater
), PIVOT_PROP_TREE
, tree
, NULL
);
407 * na_updater_insert_item:
408 * @updater: this #NAUpdater object.
409 * @item: a #NAObjectItem-derived object to be inserted in the tree.
410 * @parent_id: the id of the parent, or %NULL.
411 * @pos: the position in the children of the parent, starting at zero, or -1.
413 * Insert a new item in the global tree.
416 na_updater_insert_item( NAUpdater
*updater
, NAObjectItem
*item
, const gchar
*parent_id
, gint pos
)
419 NAObjectItem
*parent
;
421 g_return_if_fail( NA_IS_UPDATER( updater
));
422 g_return_if_fail( NA_IS_OBJECT_ITEM( item
));
424 if( !updater
->private->dispose_has_run
){
427 g_object_get( G_OBJECT( updater
), PIVOT_PROP_TREE
, &tree
, NULL
);
430 parent
= na_pivot_get_item( NA_PIVOT( updater
), parent_id
);
434 na_object_insert_at( parent
, item
, pos
);
437 tree
= g_list_append( tree
, item
);
438 g_object_set( G_OBJECT( updater
), PIVOT_PROP_TREE
, tree
, NULL
);
444 * na_updater_remove_item:
445 * @updater: this #NAPivot instance.
446 * @item: the #NAObjectItem to be removed from the list.
448 * Removes a #NAObjectItem from the hierarchical tree. Does not delete it.
451 na_updater_remove_item( NAUpdater
*updater
, NAObject
*item
)
454 NAObjectItem
*parent
;
456 g_return_if_fail( NA_IS_PIVOT( updater
));
458 if( !updater
->private->dispose_has_run
){
460 g_debug( "na_updater_remove_item: updater=%p, item=%p (%s)",
462 ( void * ) item
, G_IS_OBJECT( item
) ? G_OBJECT_TYPE_NAME( item
) : "(null)" );
464 parent
= na_object_get_parent( item
);
466 tree
= na_object_get_items( parent
);
467 tree
= g_list_remove( tree
, ( gconstpointer
) item
);
468 na_object_set_items( parent
, tree
);
471 g_object_get( G_OBJECT( updater
), PIVOT_PROP_TREE
, &tree
, NULL
);
472 tree
= g_list_remove( tree
, ( gconstpointer
) item
);
473 g_object_set( G_OBJECT( updater
), PIVOT_PROP_TREE
, tree
, NULL
);
479 * na_updater_should_pasted_be_relabeled:
480 * @updater: this #NAUpdater instance.
481 * @object: the considered #NAObject-derived object.
483 * Whether the specified object should be relabeled when pasted ?
485 * Returns: %TRUE if the object should be relabeled, %FALSE else.
488 na_updater_should_pasted_be_relabeled( const NAUpdater
*updater
, const NAObject
*item
)
490 static const gchar
*thisfn
= "na_updater_should_pasted_be_relabeled";
492 NASettings
*settings
;
494 settings
= na_pivot_get_settings( NA_PIVOT( updater
));
496 if( NA_IS_OBJECT_MENU( item
)){
497 relabel
= na_settings_get_boolean( settings
, NA_IPREFS_RELABEL_DUPLICATE_MENU
, NULL
, NULL
);
499 } else if( NA_IS_OBJECT_ACTION( item
)){
500 relabel
= na_settings_get_boolean( settings
, NA_IPREFS_RELABEL_DUPLICATE_ACTION
, NULL
, NULL
);
502 } else if( NA_IS_OBJECT_PROFILE( item
)){
503 relabel
= na_settings_get_boolean( settings
, NA_IPREFS_RELABEL_DUPLICATE_PROFILE
, NULL
, NULL
);
506 g_warning( "%s: unknown item type at %p", thisfn
, ( void * ) item
);
507 g_return_val_if_reached( FALSE
);
514 * na_updater_load_items:
515 * @updater: this #NAUpdater instance.
517 * Loads the items, updating simultaneously their writability status.
519 * Returns: a pointer (not a ref) on the loaded tree.
524 na_updater_load_items( NAUpdater
*updater
)
526 static const gchar
*thisfn
= "na_updater_load_items";
529 g_return_val_if_fail( NA_IS_UPDATER( updater
), NULL
);
533 if( !updater
->private->dispose_has_run
){
534 g_debug( "%s: updater=%p (%s)", thisfn
, ( void * ) updater
, G_OBJECT_TYPE_NAME( updater
));
536 na_pivot_load_items( NA_PIVOT( updater
));
537 tree
= na_pivot_get_items( NA_PIVOT( updater
));
538 g_list_foreach( tree
, ( GFunc
) set_writability_status
, ( gpointer
) updater
);
545 set_writability_status( NAObjectItem
*item
, const NAUpdater
*updater
)
551 writable
= na_updater_check_item_writability_status( updater
, item
, &reason
);
552 na_object_set_writability_status( item
, writable
, reason
);
554 if( NA_IS_OBJECT_MENU( item
)){
555 children
= na_object_get_items( item
);
556 g_list_foreach( children
, ( GFunc
) set_writability_status
, ( gpointer
) updater
);
561 * na_updater_write_item:
562 * @updater: this #NAUpdater instance.
563 * @item: a #NAObjectItem to be written down to the storage subsystem.
564 * @messages: the I/O provider can allocate and store here its error
567 * Writes an item (an action or a menu).
569 * Returns: the #NAIIOProvider return code.
572 na_updater_write_item( const NAUpdater
*updater
, NAObjectItem
*item
, GSList
**messages
)
576 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
578 g_return_val_if_fail( NA_IS_UPDATER( updater
), ret
);
579 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), ret
);
580 g_return_val_if_fail( messages
, ret
);
582 if( !updater
->private->dispose_has_run
){
584 NAIOProvider
*provider
= na_object_get_provider( item
);
587 provider
= na_io_provider_find_writable_io_provider( NA_PIVOT( updater
));
588 g_return_val_if_fail( provider
, NA_IIO_PROVIDER_STATUS_NO_PROVIDER_FOUND
);
592 ret
= na_io_provider_write_item( provider
, item
, messages
);
600 * na_updater_delete_item:
601 * @updater: this #NAUpdater instance.
602 * @item: the #NAObjectItem to be deleted from the storage subsystem.
603 * @messages: the I/O provider can allocate and store here its error
606 * Deletes an item, action or menu, from the I/O storage subsystem.
608 * Returns: the #NAIIOProvider return code.
610 * Note that a new item, not already written to an I/O subsystem,
611 * doesn't have any attached provider. We so do nothing and return OK...
614 na_updater_delete_item( const NAUpdater
*updater
, const NAObjectItem
*item
, GSList
**messages
)
618 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
620 g_return_val_if_fail( NA_IS_UPDATER( updater
), ret
);
621 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), ret
);
622 g_return_val_if_fail( messages
, ret
);
624 if( !updater
->private->dispose_has_run
){
626 NAIOProvider
*provider
= na_object_get_provider( item
);
627 g_return_val_if_fail( provider
, ret
);
629 ret
= na_io_provider_delete_item( provider
, item
, messages
);