Update copyright message
[nautilus-actions.git] / src / core / na-object-item.c
bloba985cd4093a6105c90ce985617ed217fa59bf499
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 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 <stdlib.h>
36 #include <string.h>
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"
44 /* private class data
46 struct _NAObjectItemClassPrivate {
47 void *empty; /* so that gcc -pedantic is happy */
50 /* private instance data
52 struct _NAObjectItemPrivate {
53 gboolean dispose_has_run;
55 void *provider_data;
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
62 gboolean readonly;
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 );
79 GType
80 na_object_item_get_type( void )
82 static GType item_type = 0;
84 if( item_type == 0 ){
85 item_type = register_type();
88 return( item_type );
91 static GType
92 register_type( void )
94 static const gchar *thisfn = "na_object_item_register_type";
95 GType type;
97 static GTypeInfo info = {
98 sizeof( NAObjectItemClass ),
99 NULL,
100 NULL,
101 ( GClassInitFunc ) class_init,
102 NULL,
103 NULL,
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 );
113 return( type );
116 static void
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 );
144 static void
145 instance_init( GTypeInstance *instance, gpointer klass )
147 static const gchar *thisfn = "na_object_item_instance_init";
148 NAObjectItem *self;
150 g_return_if_fail( NA_IS_OBJECT_ITEM( instance ));
152 self = NA_OBJECT_ITEM( instance );
154 g_debug( "%s: instance=%p (%s), klass=%p",
155 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
157 self->private = g_new0( NAObjectItemPrivate, 1 );
160 static void
161 instance_dispose( GObject *object )
163 static const gchar *thisfn = "na_object_item_instance_dispose";
164 NAObjectItem *self;
165 GList *children;
167 g_return_if_fail( NA_IS_OBJECT_ITEM( object ));
169 self = NA_OBJECT_ITEM( object );
171 if( !self->private->dispose_has_run ){
173 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
175 self->private->dispose_has_run = TRUE;
177 children = na_object_get_items( self );
178 g_debug( "%s: children=%p (count=%d)", thisfn, ( void * ) children, g_list_length( children ));
179 na_object_set_items( self, NULL );
180 na_object_unref_items( children );
182 /* chain up to the parent class */
183 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
184 G_OBJECT_CLASS( st_parent_class )->dispose( object );
189 static void
190 instance_finalize( GObject *object )
192 NAObjectItem *self;
194 g_return_if_fail( NA_IS_OBJECT_ITEM( object ));
196 self = NA_OBJECT_ITEM( object );
198 g_free( self->private );
200 /* chain call to parent class */
201 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
202 G_OBJECT_CLASS( st_parent_class )->finalize( object );
206 static void
207 object_copy( NAObject *target, const NAObject *source, gboolean recursive )
209 static const gchar *thisfn = "na_object_item_object_copy";
210 void *provider;
212 g_return_if_fail( NA_IS_OBJECT_ITEM( target ));
213 g_return_if_fail( NA_IS_OBJECT_ITEM( source ));
215 if( !NA_OBJECT_ITEM( target )->private->dispose_has_run &&
216 !NA_OBJECT_ITEM( source )->private->dispose_has_run ){
218 if( recursive ){
219 copy_children( NA_OBJECT_ITEM( target ), NA_OBJECT_ITEM( source ));
222 provider = na_object_get_provider( source );
224 if( provider ){
225 if( !NA_IS_IO_PROVIDER( provider )){
226 g_warning( "%s: source=%p (%s), provider=%p is not a NAIOProvider",
227 thisfn,
228 ( void * ) source, G_OBJECT_TYPE_NAME( source ),
229 ( void * ) provider );
231 } else {
232 na_io_provider_duplicate_data( NA_IO_PROVIDER( provider ), NA_OBJECT_ITEM( target ), NA_OBJECT_ITEM( source ), NULL );
239 * new_parent is not relevant when allocating a new UUID for an action
240 * or a menu ; it may safely be left as NULL though there is no gain to
241 * check this
243 static gchar *
244 object_id_new_id( const NAObjectId *item, const NAObjectId *new_parent )
246 GList *children, *it;
247 uuid_t uuid;
248 gchar uuid_str[64];
249 gchar *new_uuid = NULL;
251 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), NULL );
253 if( !NA_OBJECT_ITEM( item )->private->dispose_has_run ){
255 /* recurse into NAObjectItems children
256 * i.e., if a menu, recurse into embedded actions
258 children = na_object_get_items( item );
259 for( it = children ; it ; it = it->next ){
260 na_object_set_new_id( it->data, new_parent );
263 uuid_generate( uuid );
264 uuid_unparse_lower( uuid, uuid_str );
265 new_uuid = g_strdup( uuid_str );
268 return( new_uuid );
272 * na_object_item_are_equal:
273 * @a: the first (original) #NAObjectItem instance.
274 * @b: the second #NAObjectItem instance.
276 * This function participates to the #na_iduplicable_check_status() stack,
277 * and is triggered after all comparable elementary data (in #NAIFactoryObject
278 * sense) have already been successfully compared.
280 * We have to deal here with the subitems: comparing children by their ids
281 * between @a and @b.
283 * Note that, when called from na_object_check_status, the status of children
284 * have already been checked, and so we should be able to rely on them.
286 * Returns: %TRUE if @a is equal to @b.
288 * Since: 2.30
290 gboolean
291 na_object_item_are_equal( const NAObjectItem *a, const NAObjectItem *b )
293 static const gchar *thisfn = "na_object_item_are_equal";
294 gboolean equal;
295 GList *a_children, *b_children, *it;
296 gchar *first_id, *second_id;
297 NAObjectId *first_obj, *second_obj;
298 gint first_pos, second_pos;
299 GList *second_list;
301 g_return_val_if_fail( NA_IS_OBJECT_ITEM( a ), FALSE );
302 g_return_val_if_fail( NA_IS_OBJECT_ITEM( b ), FALSE );
304 equal = FALSE;
306 if( !NA_OBJECT_ITEM( a )->private->dispose_has_run &&
307 !NA_OBJECT_ITEM( b )->private->dispose_has_run ){
309 equal = TRUE;
311 if( equal ){
312 a_children = na_object_get_items( a );
313 b_children = na_object_get_items( b );
314 equal = ( g_list_length( a_children ) == g_list_length( b_children ));
315 if( !equal ){
316 g_debug( "%s: %p (%s) not equal as g_list_length not equal",
317 thisfn, ( void * ) b, G_OBJECT_TYPE_NAME( b ));
318 g_debug( "a=%p children_count=%u", ( void * ) a, g_list_length( a_children ));
319 for( it = a_children ; it ; it = it->next ){
320 g_debug( "a_child=%p", ( void * ) it->data );
322 g_debug( "b=%p children_count=%u", ( void * ) b, g_list_length( b_children ));
323 for( it = b_children ; it ; it = it->next ){
324 g_debug( "b_child=%p", ( void * ) it->data );
329 if( equal ){
330 for( it = a_children ; it && equal ; it = it->next ){
331 first_id = na_object_get_id( it->data );
332 second_obj = na_object_get_item( b, first_id );
333 first_pos = -1;
334 second_pos = -1;
336 if( second_obj ){
337 first_pos = g_list_position( a_children, it );
338 second_list = g_list_find( b_children, second_obj );
339 second_pos = g_list_position( b_children, second_list );
341 if( first_pos != second_pos ){
342 equal = FALSE;
343 g_debug( "%s: %p (%s) not equal as child %s is at pos %u",
344 thisfn, ( void * ) b, G_OBJECT_TYPE_NAME( b ), first_id, second_pos );
347 } else {
348 equal = FALSE;
349 g_debug( "%s: %p (%s) not equal as child %s removed",
350 thisfn, ( void * ) b, G_OBJECT_TYPE_NAME( b ), first_id );
353 g_free( first_id );
357 if( equal ){
358 for( it = b_children ; it && equal ; it = it->next ){
359 second_id = na_object_get_id( it->data );
360 first_obj = na_object_get_item( a, second_id );
362 if( !first_obj ){
363 equal = FALSE;
364 g_debug( "%s: %p (%s) not equal as child %s added",
365 thisfn, ( void * ) b, G_OBJECT_TYPE_NAME( b ), second_id );
367 } else {
368 equal = !na_object_is_modified( it->data );
370 if( !equal ){
371 g_debug( "%s: %p (%s) not equal as child %s modified",
372 thisfn, ( void * ) b, G_OBJECT_TYPE_NAME( b ), second_id );
376 g_free( second_id );
380 /*g_debug( "na_object_item_object_are_equal: a=%p (%s), b=%p (%s), are_equal=%s",
381 ( void * ) a, G_OBJECT_TYPE_NAME( a ),
382 ( void * ) b, G_OBJECT_TYPE_NAME( b ),
383 equal ? "True":"False" );*/
386 return( equal );
390 * na_object_item_get_item:
391 * @item: the #NAObjectItem from which we want retrieve a subitem.
392 * @id: the id of the searched subitem.
394 * Returns: a pointer to the #NAObjectId -derived child with the required id.
396 * The returned #NAObjectId is owned by the @item object ; the
397 * caller should not try to g_free() nor g_object_unref() it.
399 * Since: 2.30
401 NAObjectId *
402 na_object_item_get_item( const NAObjectItem *item, const gchar *id )
404 GList *childs, *it;
405 NAObjectId *found = NULL;
406 NAObjectId *isub;
407 gchar *isubid;
409 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), NULL );
411 if( !item->private->dispose_has_run ){
413 childs = na_object_get_items( item );
414 for( it = childs ; it && !found ; it = it->next ){
415 isub = NA_OBJECT_ID( it->data );
416 isubid = na_object_get_id( isub );
417 if( !strcmp( id, isubid )){
418 found = isub;
420 g_free( isubid );
424 return( found );
428 * na_object_item_get_position:
429 * @item: this #NAObjectItem object.
430 * @child: a #NAObjectId -derived child.
432 * Returns: the position of @child in the subitems list of @item,
433 * starting from zero, or -1 if not found.
435 * Since: 2.30
437 gint
438 na_object_item_get_position( const NAObjectItem *item, const NAObjectId *child )
440 gint pos = -1;
441 GList *children;
443 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), pos );
444 g_return_val_if_fail( NA_IS_OBJECT_ID( child ), pos );
446 if( !child ){
447 return( pos );
450 if( !item->private->dispose_has_run ){
452 children = na_object_get_items( item );
453 pos = g_list_index( children, ( gconstpointer ) child );
456 return( pos );
460 * na_object_item_append_item:
461 * @item: the #NAObjectItem to which add the subitem.
462 * @child: a #NAObjectId to be added to list of subitems.
464 * Appends a new @child to the list of subitems of @item,
465 * and setup the parent pointer of the child to its new parent.
467 * Doesn't modify the reference count on @object.
469 * Since: 2.30
471 void
472 na_object_item_append_item( NAObjectItem *item, const NAObjectId *child )
474 GList *children;
476 g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
477 g_return_if_fail( NA_IS_OBJECT_ID( child ));
479 if( !item->private->dispose_has_run ){
481 children = na_object_get_items( item );
483 if( !g_list_find( children, ( gpointer ) child )){
485 children = g_list_append( children, ( gpointer ) child );
486 na_object_set_parent( child, item );
487 na_object_set_items( item, children );
493 * na_object_item_insert_at:
494 * @item: the #NAObjectItem in which add the subitem.
495 * @child: a #NAObjectId -derived to be inserted in the list of subitems.
496 * @pos: the position at which the @child should be inserted.
498 * Inserts a new @child in the list of subitems of @item.
500 * Doesn't modify the reference count on @child.
502 * Since: 2.30
504 void
505 na_object_item_insert_at( NAObjectItem *item, const NAObjectId *child, gint pos )
507 GList *children, *it;
508 gint i;
510 g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
511 g_return_if_fail( NA_IS_OBJECT_ID( child ));
513 if( !item->private->dispose_has_run ){
515 children = na_object_get_items( item );
516 if( pos == -1 || pos >= g_list_length( children )){
517 na_object_append_item( item, child );
519 } else {
520 i = 0;
521 for( it = children ; it && i <= pos ; it = it->next ){
522 if( i == pos ){
523 children = g_list_insert_before( children, it, ( gpointer ) child );
525 i += 1;
527 na_object_set_items( item, children );
533 * na_object_item_insert_item:
534 * @item: the #NAObjectItem to which add the subitem.
535 * @child: a #NAObjectId to be inserted in the list of subitems.
536 * @before: the #NAObjectId before which the @child should be inserted.
538 * Inserts a new @child in the list of subitems of @item.
540 * Doesn't modify the reference count on @child.
542 * Since: 2.30
544 void
545 na_object_item_insert_item( NAObjectItem *item, const NAObjectId *child, const NAObjectId *before )
547 GList *children;
548 GList *before_list;
550 g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
551 g_return_if_fail( NA_IS_OBJECT_ID( child ));
552 g_return_if_fail( !before || NA_IS_OBJECT_ID( before ));
554 if( !item->private->dispose_has_run ){
556 children = na_object_get_items( item );
557 if( !g_list_find( children, ( gpointer ) child )){
559 before_list = NULL;
561 if( before ){
562 before_list = g_list_find( children, ( gconstpointer ) before );
565 if( before_list ){
566 children = g_list_insert_before( children, before_list, ( gpointer ) child );
567 } else {
568 children = g_list_prepend( children, ( gpointer ) child );
571 na_object_set_items( item, children );
577 * na_object_item_remove_item:
578 * @item: the #NAObjectItem from which the subitem must be removed.
579 * @child: a #NAObjectId -derived to be removed from the list of subitems.
581 * Removes a @child from the list of subitems of @item.
583 * Doesn't modify the reference count on @child.
585 * Since: 2.30
587 void
588 na_object_item_remove_item( NAObjectItem *item, const NAObjectId *child )
590 GList *children;
592 g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
593 g_return_if_fail( NA_IS_OBJECT_ID( child ));
595 if( !item->private->dispose_has_run ){
597 children = na_object_get_items( item );
599 if( children ){
600 g_debug( "na_object_item_remove_item: removing %p (%s) from %p (%s)",
601 ( void * ) child, G_OBJECT_TYPE_NAME( child ),
602 ( void * ) item, G_OBJECT_TYPE_NAME( item ));
604 children = g_list_remove( children, ( gconstpointer ) child );
605 g_debug( "na_object_item_remove_item: after: children=%p, count=%u", ( void * ) children, g_list_length( children ));
606 na_object_set_items( item, children );
612 * na_object_item_get_items_count:
613 * @item: the #NAObjectItem from which we want a count of subitems.
615 * Returns: the count of subitems of @item.
617 * Since: 2.30
619 guint
620 na_object_item_get_items_count( const NAObjectItem *item )
622 guint count = 0;
623 GList *childs;
625 /*g_debug( "na_object_item_get_items_count: item=%p (%s)", ( void * ) item, G_OBJECT_TYPE_NAME( item ));*/
626 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), 0 );
628 if( !item->private->dispose_has_run ){
630 childs = na_object_get_items( item );
631 count = childs ? g_list_length( childs ) : 0;
634 return( count );
638 * na_object_item_count_items:
639 * @items: a list if #NAObject -derived to be counted.
640 * @menus: will be set to the count of menus.
641 * @actions: will be set to the count of actions.
642 * @profiles: will be set to the count of profiles.
643 * @recurse: whether to recursively count all items, or only those in
644 * level zero of the list.
646 * Returns: the count the numbers of items if the provided list.
648 * As this function is recursive, the counters should be initialized by
649 * the caller before calling it.
651 * Since: 2.30
653 void
654 na_object_item_count_items( GList *items, gint *menus, gint *actions, gint *profiles, gboolean recurse )
656 GList *it;
658 /*g_debug( "na_object_item_count_items: items=%p (count=%d), menus=%d, actions=%d, profiles=%d",
659 ( void * ) items, items ? g_list_length( items ) : 0, *menus, *actions, *profiles );*/
661 for( it = items ; it ; it = it->next ){
663 if( recurse ){
664 if( NA_IS_OBJECT_ITEM( it->data )){
665 GList *subitems = na_object_get_items( it->data );
666 na_object_count_items( subitems, menus, actions, profiles, recurse );
670 if( NA_IS_OBJECT_MENU( it->data )){
671 *menus += 1;
673 } else if( NA_IS_OBJECT_ACTION( it->data )){
674 *actions += 1;
676 } else if( NA_IS_OBJECT_PROFILE( it->data )){
677 *profiles += 1;
683 * na_object_item_unref_items:
684 * @items: a list of #NAObject -derived items.
686 * Unref only the first level the #NAObject of the list, freeing the list at last.
688 * This is rather only used by NAPivot.
690 * Since: 2.30
692 void
693 na_object_item_unref_items( GList *items )
695 g_list_foreach( items, ( GFunc ) g_object_unref, NULL );
696 g_list_free( items );
700 * na_object_item_unref_items_rec:
701 * @items: a list of #NAObject -derived items.
703 * Recursively unref the #NAObject's of the list, freeing the list at last.
705 * This is heavily used by NACT.
707 * Since: 2.30
709 void
710 na_object_item_unref_items_rec( GList *items )
712 GList *it;
714 for( it = items ; it ; it = it->next ){
715 na_object_unref( it->data );
718 g_list_free( items );
722 * na_object_item_deals_with_version:
723 * @item: this #NAObjectItem -derived object.
725 * Just after the @item has been readen from NAIFactoryProvider, setup
726 * the version. This is needed because some conversions may occur in
727 * this object.
729 * Note that there is only some 2.x versions where the version string
730 * was not systematically written. If @item has been readen from a
731 * .desktop file, then iversion is already set to (at least) 3.
733 * Since: 2.30
735 void
736 na_object_item_deals_with_version( NAObjectItem *item )
738 guint version_uint;
739 gchar *version_str;
741 g_return_if_fail( NA_IS_OBJECT_ITEM( item ));
743 if( !item->private->dispose_has_run ){
745 version_uint = na_object_get_iversion( item );
747 if( !version_uint ){
748 version_str = na_object_get_version( item );
750 if( !version_str || !strlen( version_str )){
751 g_free( version_str );
752 version_str = g_strdup( "2.0" );
755 version_uint = atoi( version_str );
756 na_object_set_iversion( item, version_uint );
758 g_free( version_str );
764 * na_object_item_rebuild_children_slist:
765 * @item: this #NAObjectItem -derived object.
767 * Rebuild the string list of children.
769 * Since: 2.30
771 void
772 na_object_item_rebuild_children_slist( NAObjectItem *item )
774 GSList *slist;
775 GList *subitems, *it;
776 gchar *id;
778 na_object_set_items_slist( item, NULL );
780 if( !item->private->dispose_has_run ){
782 subitems = na_object_get_items( item );
783 slist = NULL;
785 for( it = subitems ; it ; it = it->next ){
786 id = na_object_get_id( it->data );
787 slist = g_slist_prepend( slist, id );
789 slist = g_slist_reverse( slist );
791 na_object_set_items_slist( item, slist );
793 na_core_utils_slist_free( slist );
797 static void
798 copy_children( NAObjectItem *target, const NAObjectItem *source )
800 static const gchar *thisfn = "na_object_item_copy_children";
801 GList *tgt_children, *src_children, *ic;
802 NAObject *dup;
804 tgt_children = na_object_get_items( target );
805 if( tgt_children ){
806 g_warning( "%s: target_children=%p (count=%d)",
807 thisfn, ( void * ) tgt_children, g_list_length( tgt_children ));
808 g_return_if_fail( tgt_children == NULL );
811 src_children = na_object_get_items( source );
812 for( ic = src_children ; ic ; ic = ic->next ){
814 dup = ( NAObject * ) na_object_duplicate( ic->data );
815 na_object_set_parent( dup, target );
816 tgt_children = g_list_prepend( tgt_children, dup );
819 tgt_children = g_list_reverse( tgt_children );
820 na_object_set_items( target, tgt_children );