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 <uuid/uuid.h>
39 #include <api/na-core-utils.h>
40 #include <api/na-object-api.h>
42 #include "na-io-provider.h"
46 struct _NAObjectItemClassPrivate
{
47 void *empty
; /* so that gcc -pedantic is happy */
50 /* private instance data
52 struct _NAObjectItemPrivate
{
53 gboolean dispose_has_run
;
57 /* dynamically set when reading the item from the I/O storage
58 * subsystem; may be reset from FALSE to TRUE if a write operation
59 * has returned an error.
60 * defaults to FALSE for snew, not yet written to a provider, item
65 static NAObjectIdClass
*st_parent_class
= NULL
;
67 static GType
register_type( void );
68 static void class_init( NAObjectItemClass
*klass
);
69 static void instance_init( GTypeInstance
*instance
, gpointer klass
);
70 static void instance_dispose( GObject
*object
);
71 static void instance_finalize( GObject
*object
);
73 static void object_copy( NAObject
*target
, const NAObject
*source
, gboolean recursive
);
75 static gchar
*object_id_new_id( const NAObjectId
*item
, const NAObjectId
*new_parent
);
77 static void copy_children( NAObjectItem
*target
, const NAObjectItem
*source
);
80 na_object_item_get_type( void )
82 static GType item_type
= 0;
85 item_type
= register_type();
94 static const gchar
*thisfn
= "na_object_item_register_type";
97 static GTypeInfo info
= {
98 sizeof( NAObjectItemClass
),
101 ( GClassInitFunc
) class_init
,
104 sizeof( NAObjectItem
),
106 ( GInstanceInitFunc
) instance_init
109 g_debug( "%s", thisfn
);
111 type
= g_type_register_static( NA_OBJECT_ID_TYPE
, "NAObjectItem", &info
, 0 );
117 class_init( NAObjectItemClass
*klass
)
119 static const gchar
*thisfn
= "na_object_item_class_init";
120 GObjectClass
*object_class
;
121 NAObjectClass
*naobject_class
;
122 NAObjectIdClass
*naobjectid_class
;
124 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
126 st_parent_class
= g_type_class_peek_parent( klass
);
128 object_class
= G_OBJECT_CLASS( klass
);
129 object_class
->dispose
= instance_dispose
;
130 object_class
->finalize
= instance_finalize
;
132 naobject_class
= NA_OBJECT_CLASS( klass
);
133 naobject_class
->dump
= NULL
;
134 naobject_class
->copy
= object_copy
;
135 naobject_class
->are_equal
= NULL
;
136 naobject_class
->is_valid
= NULL
;
138 naobjectid_class
= NA_OBJECT_ID_CLASS( klass
);
139 naobjectid_class
->new_id
= object_id_new_id
;
141 klass
->private = g_new0( NAObjectItemClassPrivate
, 1 );
145 instance_init( GTypeInstance
*instance
, gpointer klass
)
149 g_return_if_fail( NA_IS_OBJECT_ITEM( instance
));
151 self
= NA_OBJECT_ITEM( instance
);
153 self
->private = g_new0( NAObjectItemPrivate
, 1 );
157 instance_dispose( GObject
*object
)
159 static const gchar
*thisfn
= "na_object_item_instance_dispose";
163 g_return_if_fail( NA_IS_OBJECT_ITEM( object
));
165 self
= NA_OBJECT_ITEM( object
);
167 if( !self
->private->dispose_has_run
){
169 self
->private->dispose_has_run
= TRUE
;
171 children
= na_object_get_items( self
);
172 g_debug( "%s: children=%p (count=%d)", thisfn
, ( void * ) children
, g_list_length( children
));
173 na_object_set_items( self
, NULL
);
174 na_object_unref_items( children
);
176 /* chain up to the parent class */
177 if( G_OBJECT_CLASS( st_parent_class
)->dispose
){
178 G_OBJECT_CLASS( st_parent_class
)->dispose( object
);
184 instance_finalize( GObject
*object
)
188 g_return_if_fail( NA_IS_OBJECT_ITEM( object
));
190 self
= NA_OBJECT_ITEM( object
);
192 g_free( self
->private );
194 /* chain call to parent class */
195 if( G_OBJECT_CLASS( st_parent_class
)->finalize
){
196 G_OBJECT_CLASS( st_parent_class
)->finalize( object
);
201 object_copy( NAObject
*target
, const NAObject
*source
, gboolean recursive
)
203 static const gchar
*thisfn
= "na_object_item_object_copy";
206 g_return_if_fail( NA_IS_OBJECT_ITEM( target
));
207 g_return_if_fail( NA_IS_OBJECT_ITEM( source
));
209 if( !NA_OBJECT_ITEM( target
)->private->dispose_has_run
&&
210 !NA_OBJECT_ITEM( source
)->private->dispose_has_run
){
213 copy_children( NA_OBJECT_ITEM( target
), NA_OBJECT_ITEM( source
));
216 provider
= na_object_get_provider( source
);
219 if( !NA_IS_IO_PROVIDER( provider
)){
220 g_warning( "%s: source=%p (%s), provider=%p is not a NAIOProvider",
222 ( void * ) source
, G_OBJECT_TYPE_NAME( source
),
223 ( void * ) provider
);
226 na_io_provider_duplicate_data( NA_IO_PROVIDER( provider
), NA_OBJECT_ITEM( target
), NA_OBJECT_ITEM( source
), NULL
);
233 * new_parent is not relevant when allocating a new identifier for an
234 * action or a menu ; it may safely be left as NULL though there is no
238 object_id_new_id( const NAObjectId
*item
, const NAObjectId
*new_parent
)
240 GList
*children
, *it
;
243 gchar
*new_uuid
= NULL
;
245 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), NULL
);
247 if( !NA_OBJECT_ITEM( item
)->private->dispose_has_run
){
249 /* recurse into NAObjectItems children
250 * i.e., if a menu, recurse into embedded actions
252 children
= na_object_get_items( item
);
253 for( it
= children
; it
; it
= it
->next
){
254 na_object_set_new_id( it
->data
, new_parent
);
257 uuid_generate( uuid
);
258 uuid_unparse_lower( uuid
, uuid_str
);
259 new_uuid
= g_strdup( uuid_str
);
266 * na_object_item_are_equal:
267 * @a: the first (original) #NAObjectItem instance.
268 * @b: the second #NAObjectItem instance.
270 * This function participates to the #na_iduplicable_check_status() stack,
271 * and is triggered after all comparable elementary data (in #NAIFactoryObject
272 * sense) have already been successfully compared.
274 * We have to deal here with the subitems: comparing children by their ids
277 * Note that, when called from na_object_check_status, the status of children
278 * have already been checked, and so we should be able to rely on them.
280 * Returns: %TRUE if @a is equal to @b.
285 na_object_item_are_equal( const NAObjectItem
*a
, const NAObjectItem
*b
)
287 static const gchar
*thisfn
= "na_object_item_are_equal";
289 GList
*a_children
, *b_children
, *it
;
290 gchar
*first_id
, *second_id
;
291 NAObjectId
*first_obj
, *second_obj
;
292 gint first_pos
, second_pos
;
295 g_return_val_if_fail( NA_IS_OBJECT_ITEM( a
), FALSE
);
296 g_return_val_if_fail( NA_IS_OBJECT_ITEM( b
), FALSE
);
300 if( !NA_OBJECT_ITEM( a
)->private->dispose_has_run
&&
301 !NA_OBJECT_ITEM( b
)->private->dispose_has_run
){
306 a_children
= na_object_get_items( a
);
307 b_children
= na_object_get_items( b
);
308 equal
= ( g_list_length( a_children
) == g_list_length( b_children
));
310 g_debug( "%s: %p (%s) not equal as g_list_length not equal",
311 thisfn
, ( void * ) b
, G_OBJECT_TYPE_NAME( b
));
312 g_debug( "a=%p children_count=%u", ( void * ) a
, g_list_length( a_children
));
313 for( it
= a_children
; it
; it
= it
->next
){
314 g_debug( "a_child=%p", ( void * ) it
->data
);
316 g_debug( "b=%p children_count=%u", ( void * ) b
, g_list_length( b_children
));
317 for( it
= b_children
; it
; it
= it
->next
){
318 g_debug( "b_child=%p", ( void * ) it
->data
);
324 for( it
= a_children
; it
&& equal
; it
= it
->next
){
325 first_id
= na_object_get_id( it
->data
);
326 second_obj
= na_object_get_item( b
, first_id
);
331 first_pos
= g_list_position( a_children
, it
);
332 second_list
= g_list_find( b_children
, second_obj
);
333 second_pos
= g_list_position( b_children
, second_list
);
335 if( first_pos
!= second_pos
){
337 g_debug( "%s: %p (%s) not equal as child %s is at pos %u",
338 thisfn
, ( void * ) b
, G_OBJECT_TYPE_NAME( b
), first_id
, second_pos
);
343 g_debug( "%s: %p (%s) not equal as child %s removed",
344 thisfn
, ( void * ) b
, G_OBJECT_TYPE_NAME( b
), first_id
);
352 for( it
= b_children
; it
&& equal
; it
= it
->next
){
353 second_id
= na_object_get_id( it
->data
);
354 first_obj
= na_object_get_item( a
, second_id
);
358 g_debug( "%s: %p (%s) not equal as child %s added",
359 thisfn
, ( void * ) b
, G_OBJECT_TYPE_NAME( b
), second_id
);
362 equal
= !na_object_is_modified( it
->data
);
365 g_debug( "%s: %p (%s) not equal as child %s modified",
366 thisfn
, ( void * ) b
, G_OBJECT_TYPE_NAME( b
), second_id
);
374 /*g_debug( "na_object_item_object_are_equal: a=%p (%s), b=%p (%s), are_equal=%s",
375 ( void * ) a, G_OBJECT_TYPE_NAME( a ),
376 ( void * ) b, G_OBJECT_TYPE_NAME( b ),
377 equal ? "True":"False" );*/
384 * na_object_item_get_item:
385 * @item: the #NAObjectItem from which we want retrieve a subitem.
386 * @id: the id of the searched subitem.
388 * Returns: a pointer to the #NAObjectId -derived child with the required id.
390 * The returned #NAObjectId is owned by the @item object ; the
391 * caller should not try to g_free() nor g_object_unref() it.
396 na_object_item_get_item( const NAObjectItem
*item
, const gchar
*id
)
399 NAObjectId
*found
= NULL
;
403 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), NULL
);
405 if( !item
->private->dispose_has_run
){
407 childs
= na_object_get_items( item
);
408 for( it
= childs
; it
&& !found
; it
= it
->next
){
409 isub
= NA_OBJECT_ID( it
->data
);
410 isubid
= na_object_get_id( isub
);
411 if( !strcmp( id
, isubid
)){
422 * na_object_item_get_position:
423 * @item: this #NAObjectItem object.
424 * @child: a #NAObjectId -derived child.
426 * Returns: the position of @child in the subitems list of @item,
427 * starting from zero, or -1 if not found.
432 na_object_item_get_position( const NAObjectItem
*item
, const NAObjectId
*child
)
437 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), pos
);
438 g_return_val_if_fail( NA_IS_OBJECT_ID( child
), pos
);
444 if( !item
->private->dispose_has_run
){
446 children
= na_object_get_items( item
);
447 pos
= g_list_index( children
, ( gconstpointer
) child
);
454 * na_object_item_append_item:
455 * @item: the #NAObjectItem to which add the subitem.
456 * @child: a #NAObjectId to be added to list of subitems.
458 * Appends a new @child to the list of subitems of @item,
459 * and setup the parent pointer of the child to its new parent.
461 * Doesn't modify the reference count on @object.
466 na_object_item_append_item( NAObjectItem
*item
, const NAObjectId
*child
)
470 g_return_if_fail( NA_IS_OBJECT_ITEM( item
));
471 g_return_if_fail( NA_IS_OBJECT_ID( child
));
473 if( !item
->private->dispose_has_run
){
475 children
= na_object_get_items( item
);
477 if( !g_list_find( children
, ( gpointer
) child
)){
479 children
= g_list_append( children
, ( gpointer
) child
);
480 na_object_set_parent( child
, item
);
481 na_object_set_items( item
, children
);
487 * na_object_item_insert_at:
488 * @item: the #NAObjectItem in which add the subitem.
489 * @child: a #NAObjectId -derived to be inserted in the list of subitems.
490 * @pos: the position at which the @child should be inserted.
492 * Inserts a new @child in the list of subitems of @item.
494 * Doesn't modify the reference count on @child.
499 na_object_item_insert_at( NAObjectItem
*item
, const NAObjectId
*child
, gint pos
)
501 GList
*children
, *it
;
504 g_return_if_fail( NA_IS_OBJECT_ITEM( item
));
505 g_return_if_fail( NA_IS_OBJECT_ID( child
));
507 if( !item
->private->dispose_has_run
){
509 children
= na_object_get_items( item
);
510 if( pos
== -1 || pos
>= g_list_length( children
)){
511 na_object_append_item( item
, child
);
515 for( it
= children
; it
&& i
<= pos
; it
= it
->next
){
517 children
= g_list_insert_before( children
, it
, ( gpointer
) child
);
521 na_object_set_items( item
, children
);
527 * na_object_item_insert_item:
528 * @item: the #NAObjectItem to which add the subitem.
529 * @child: a #NAObjectId to be inserted in the list of subitems.
530 * @before: the #NAObjectId before which the @child should be inserted.
532 * Inserts a new @child in the list of subitems of @item.
534 * Doesn't modify the reference count on @child.
539 na_object_item_insert_item( NAObjectItem
*item
, const NAObjectId
*child
, const NAObjectId
*before
)
544 g_return_if_fail( NA_IS_OBJECT_ITEM( item
));
545 g_return_if_fail( NA_IS_OBJECT_ID( child
));
546 g_return_if_fail( !before
|| NA_IS_OBJECT_ID( before
));
548 if( !item
->private->dispose_has_run
){
550 children
= na_object_get_items( item
);
551 if( !g_list_find( children
, ( gpointer
) child
)){
556 before_list
= g_list_find( children
, ( gconstpointer
) before
);
560 children
= g_list_insert_before( children
, before_list
, ( gpointer
) child
);
562 children
= g_list_prepend( children
, ( gpointer
) child
);
565 na_object_set_items( item
, children
);
571 * na_object_item_remove_item:
572 * @item: the #NAObjectItem from which the subitem must be removed.
573 * @child: a #NAObjectId -derived to be removed from the list of subitems.
575 * Removes a @child from the list of subitems of @item.
577 * Doesn't modify the reference count on @child.
582 na_object_item_remove_item( NAObjectItem
*item
, const NAObjectId
*child
)
586 g_return_if_fail( NA_IS_OBJECT_ITEM( item
));
587 g_return_if_fail( NA_IS_OBJECT_ID( child
));
589 if( !item
->private->dispose_has_run
){
591 children
= na_object_get_items( item
);
594 g_debug( "na_object_item_remove_item: removing %p (%s) from %p (%s)",
595 ( void * ) child
, G_OBJECT_TYPE_NAME( child
),
596 ( void * ) item
, G_OBJECT_TYPE_NAME( item
));
598 children
= g_list_remove( children
, ( gconstpointer
) child
);
599 g_debug( "na_object_item_remove_item: after: children=%p, count=%u", ( void * ) children
, g_list_length( children
));
600 na_object_set_items( item
, children
);
606 * na_object_item_get_items_count:
607 * @item: the #NAObjectItem from which we want a count of subitems.
609 * Returns: the count of subitems of @item.
614 na_object_item_get_items_count( const NAObjectItem
*item
)
619 /*g_debug( "na_object_item_get_items_count: item=%p (%s)", ( void * ) item, G_OBJECT_TYPE_NAME( item ));*/
620 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), 0 );
622 if( !item
->private->dispose_has_run
){
624 childs
= na_object_get_items( item
);
625 count
= childs
? g_list_length( childs
) : 0;
632 * na_object_item_count_items:
633 * @items: a list if #NAObject -derived to be counted.
634 * @menus: will be set to the count of menus.
635 * @actions: will be set to the count of actions.
636 * @profiles: will be set to the count of profiles.
637 * @recurse: whether to recursively count all items, or only those in
638 * level zero of the list.
640 * Returns: the count the numbers of items if the provided list.
642 * As this function is recursive, the counters should be initialized by
643 * the caller before calling it.
648 na_object_item_count_items( GList
*items
, gint
*menus
, gint
*actions
, gint
*profiles
, gboolean recurse
)
652 /*g_debug( "na_object_item_count_items: items=%p (count=%d), menus=%d, actions=%d, profiles=%d",
653 ( void * ) items, items ? g_list_length( items ) : 0, *menus, *actions, *profiles );*/
655 for( it
= items
; it
; it
= it
->next
){
658 if( NA_IS_OBJECT_ITEM( it
->data
)){
659 GList
*subitems
= na_object_get_items( it
->data
);
660 na_object_count_items( subitems
, menus
, actions
, profiles
, recurse
);
664 if( NA_IS_OBJECT_MENU( it
->data
)){
667 } else if( NA_IS_OBJECT_ACTION( it
->data
)){
670 } else if( NA_IS_OBJECT_PROFILE( it
->data
)){
677 * na_object_item_unref_items:
678 * @items: a list of #NAObject -derived items.
680 * Unref only the first level the #NAObject of the list, freeing the list at last.
682 * This is rather only used by NAPivot.
687 na_object_item_unref_items( GList
*items
)
689 g_list_foreach( items
, ( GFunc
) g_object_unref
, NULL
);
690 g_list_free( items
);
694 * na_object_item_unref_items_rec:
695 * @items: a list of #NAObject -derived items.
697 * Recursively unref the #NAObject's of the list, freeing the list at last.
699 * This is heavily used by NACT.
704 na_object_item_unref_items_rec( GList
*items
)
708 for( it
= items
; it
; it
= it
->next
){
709 na_object_unref( it
->data
);
712 g_list_free( items
);
716 * na_object_item_deals_with_version:
717 * @item: this #NAObjectItem -derived object.
719 * Just after the @item has been read from NAIFactoryProvider, setup
720 * the version. This is needed because some conversions may occur in
723 * Note that there is only some 2.x versions where the version string
724 * was not systematically written. If @item has been read from a
725 * .desktop file, then iversion is already set to (at least) 3.
730 na_object_item_deals_with_version( NAObjectItem
*item
)
735 g_return_if_fail( NA_IS_OBJECT_ITEM( item
));
737 if( !item
->private->dispose_has_run
){
739 version_uint
= na_object_get_iversion( item
);
742 version_str
= na_object_get_version( item
);
744 if( !version_str
|| !strlen( version_str
)){
745 g_free( version_str
);
746 version_str
= g_strdup( "2.0" );
749 version_uint
= atoi( version_str
);
750 na_object_set_iversion( item
, version_uint
);
752 g_free( version_str
);
758 * na_object_item_rebuild_children_slist:
759 * @item: this #NAObjectItem -derived object.
761 * Rebuild the string list of children.
766 na_object_item_rebuild_children_slist( NAObjectItem
*item
)
769 GList
*subitems
, *it
;
772 na_object_set_items_slist( item
, NULL
);
774 if( !item
->private->dispose_has_run
){
776 subitems
= na_object_get_items( item
);
779 for( it
= subitems
; it
; it
= it
->next
){
780 id
= na_object_get_id( it
->data
);
781 slist
= g_slist_prepend( slist
, id
);
783 slist
= g_slist_reverse( slist
);
785 na_object_set_items_slist( item
, slist
);
787 na_core_utils_slist_free( slist
);
792 copy_children( NAObjectItem
*target
, const NAObjectItem
*source
)
794 static const gchar
*thisfn
= "na_object_item_copy_children";
795 GList
*tgt_children
, *src_children
, *ic
;
798 tgt_children
= na_object_get_items( target
);
800 g_warning( "%s: target_children=%p (count=%d)",
801 thisfn
, ( void * ) tgt_children
, g_list_length( tgt_children
));
802 g_return_if_fail( tgt_children
== NULL
);
805 src_children
= na_object_get_items( source
);
806 for( ic
= src_children
; ic
; ic
= ic
->next
){
808 dup
= ( NAObject
* ) na_object_duplicate( ic
->data
);
809 na_object_set_parent( dup
, target
);
810 tgt_children
= g_list_prepend( tgt_children
, dup
);
813 tgt_children
= g_list_reverse( tgt_children
);
814 na_object_set_items( target
, tgt_children
);