2 * Nautilus ObjectActions
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 <glib/gi18n.h>
40 #include <api/na-core-utils.h>
41 #include <api/na-iio-provider.h>
42 #include <api/na-object-api.h>
44 #include "na-factory-provider.h"
45 #include "na-factory-object.h"
49 struct _NAObjectActionClassPrivate
{
50 void *empty
; /* so that gcc -pedantic is happy */
53 /* private instance data
55 struct _NAObjectActionPrivate
{
56 gboolean dispose_has_run
;
59 /* i18n: default label for a new action */
60 #define NEW_NAUTILUS_ACTION N_( "New Nautilus action" )
62 extern NADataGroup action_data_groups
[]; /* defined in na-object-action-factory.c */
63 extern NADataDef data_def_action_v1
[]; /* defined in na-object-action-factory.c */
65 static NAObjectItemClass
*st_parent_class
= NULL
;
67 static GType
register_type( void );
68 static void class_init( NAObjectActionClass
*klass
);
69 static void instance_init( GTypeInstance
*instance
, gpointer klass
);
70 static void instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
);
71 static void instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
);
72 static void instance_dispose( GObject
*object
);
73 static void instance_finalize( GObject
*object
);
75 static void object_copy( NAObject
*target
, const NAObject
*source
, gboolean recursive
);
76 static gboolean
object_is_valid( const NAObject
*object
);
78 static void ifactory_object_iface_init( NAIFactoryObjectInterface
*iface
);
79 static guint
ifactory_object_get_version( const NAIFactoryObject
*instance
);
80 static NADataGroup
*ifactory_object_get_groups( const NAIFactoryObject
*instance
);
81 static gboolean
ifactory_object_are_equal( const NAIFactoryObject
*a
, const NAIFactoryObject
*b
);
82 static gboolean
ifactory_object_is_valid( const NAIFactoryObject
*object
);
83 static void ifactory_object_read_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*reader
, void *reader_data
, GSList
**messages
);
84 static guint
ifactory_object_write_start( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
85 static guint
ifactory_object_write_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
87 static void icontext_iface_init( NAIContextInterface
*iface
);
88 static gboolean
icontext_is_candidate( NAIContext
*object
, guint target
, GList
*selection
);
90 static gboolean
read_done_convert_v1_to_last( NAIFactoryObject
*instance
);
91 static void read_done_deals_with_toolbar_label( NAIFactoryObject
*instance
);
93 static guint
write_done_write_profiles( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
95 static gboolean
object_object_is_valid( const NAObjectAction
*action
);
96 static gboolean
is_valid_label( const NAObjectAction
*action
);
97 static gboolean
is_valid_toolbar_label( const NAObjectAction
*action
);
100 na_object_action_get_type( void )
102 static GType action_type
= 0;
104 if( action_type
== 0 ){
106 action_type
= register_type();
109 return( action_type
);
113 register_type( void )
115 static const gchar
*thisfn
= "na_object_action_register_type";
118 static GTypeInfo info
= {
119 sizeof( NAObjectActionClass
),
122 ( GClassInitFunc
) class_init
,
125 sizeof( NAObjectAction
),
127 ( GInstanceInitFunc
) instance_init
130 static const GInterfaceInfo icontext_iface_info
= {
131 ( GInterfaceInitFunc
) icontext_iface_init
,
136 static const GInterfaceInfo ifactory_object_iface_info
= {
137 ( GInterfaceInitFunc
) ifactory_object_iface_init
,
142 g_debug( "%s", thisfn
);
144 type
= g_type_register_static( NA_OBJECT_ITEM_TYPE
, "NAObjectAction", &info
, 0 );
146 g_type_add_interface_static( type
, NA_ICONTEXT_TYPE
, &icontext_iface_info
);
148 g_type_add_interface_static( type
, NA_IFACTORY_OBJECT_TYPE
, &ifactory_object_iface_info
);
154 class_init( NAObjectActionClass
*klass
)
156 static const gchar
*thisfn
= "na_object_action_class_init";
157 GObjectClass
*object_class
;
158 NAObjectClass
*naobject_class
;
160 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
162 st_parent_class
= g_type_class_peek_parent( klass
);
164 object_class
= G_OBJECT_CLASS( klass
);
165 object_class
->set_property
= instance_set_property
;
166 object_class
->get_property
= instance_get_property
;
167 object_class
->dispose
= instance_dispose
;
168 object_class
->finalize
= instance_finalize
;
170 naobject_class
= NA_OBJECT_CLASS( klass
);
171 naobject_class
->dump
= NULL
;
172 naobject_class
->copy
= object_copy
;
173 naobject_class
->are_equal
= NULL
;
174 naobject_class
->is_valid
= object_is_valid
;
176 klass
->private = g_new0( NAObjectActionClassPrivate
, 1 );
178 na_factory_object_define_properties( object_class
, action_data_groups
);
182 instance_init( GTypeInstance
*instance
, gpointer klass
)
184 static const gchar
*thisfn
= "na_object_action_instance_init";
185 NAObjectAction
*self
;
187 g_return_if_fail( NA_IS_OBJECT_ACTION( instance
));
189 self
= NA_OBJECT_ACTION( instance
);
191 g_debug( "%s: instance=%p (%s), klass=%p",
192 thisfn
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
), ( void * ) klass
);
194 self
->private = g_new0( NAObjectActionPrivate
, 1 );
198 instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
)
200 g_return_if_fail( NA_IS_OBJECT_ACTION( object
));
201 g_return_if_fail( NA_IS_IFACTORY_OBJECT( object
));
203 if( !NA_OBJECT_ACTION( object
)->private->dispose_has_run
){
205 na_factory_object_get_as_value( NA_IFACTORY_OBJECT( object
), g_quark_to_string( property_id
), value
);
210 instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
)
212 g_return_if_fail( NA_IS_OBJECT_ACTION( object
));
213 g_return_if_fail( NA_IS_IFACTORY_OBJECT( object
));
215 if( !NA_OBJECT_ACTION( object
)->private->dispose_has_run
){
217 na_factory_object_set_from_value( NA_IFACTORY_OBJECT( object
), g_quark_to_string( property_id
), value
);
222 instance_dispose( GObject
*object
)
224 static const gchar
*thisfn
= "na_object_action_instance_dispose";
225 NAObjectAction
*self
;
227 g_return_if_fail( NA_IS_OBJECT_ACTION( object
));
229 self
= NA_OBJECT_ACTION( object
);
231 if( !self
->private->dispose_has_run
){
233 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
235 self
->private->dispose_has_run
= TRUE
;
237 /* chain up to the parent class */
238 if( G_OBJECT_CLASS( st_parent_class
)->dispose
){
239 G_OBJECT_CLASS( st_parent_class
)->dispose( object
);
245 instance_finalize( GObject
*object
)
247 static const gchar
*thisfn
= "na_object_action_instance_finalize";
248 NAObjectAction
*self
;
250 g_return_if_fail( NA_IS_OBJECT_ACTION( object
));
252 self
= NA_OBJECT_ACTION( object
);
254 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
256 g_free( self
->private );
258 /* chain call to parent class */
259 if( G_OBJECT_CLASS( st_parent_class
)->finalize
){
260 G_OBJECT_CLASS( st_parent_class
)->finalize( object
);
265 object_copy( NAObject
*target
, const NAObject
*source
, gboolean recursive
)
267 g_return_if_fail( NA_IS_OBJECT_ACTION( target
));
268 g_return_if_fail( NA_IS_OBJECT_ACTION( source
));
270 if( !NA_OBJECT_ACTION( target
)->private->dispose_has_run
&&
271 !NA_OBJECT_ACTION( source
)->private->dispose_has_run
){
273 na_factory_object_copy( NA_IFACTORY_OBJECT( target
), NA_IFACTORY_OBJECT( source
));
278 object_is_valid( const NAObject
*object
)
280 g_return_val_if_fail( NA_IS_OBJECT_ACTION( object
), FALSE
);
282 return( object_object_is_valid( NA_OBJECT_ACTION( object
)));
286 ifactory_object_iface_init( NAIFactoryObjectInterface
*iface
)
288 static const gchar
*thisfn
= "na_object_action_ifactory_object_iface_init";
290 g_debug( "%s: iface=%p", thisfn
, ( void * ) iface
);
292 iface
->get_version
= ifactory_object_get_version
;
293 iface
->get_groups
= ifactory_object_get_groups
;
295 iface
->are_equal
= ifactory_object_are_equal
;
296 iface
->is_valid
= ifactory_object_is_valid
;
297 iface
->read_start
= NULL
;
298 iface
->read_done
= ifactory_object_read_done
;
299 iface
->write_start
= ifactory_object_write_start
;
300 iface
->write_done
= ifactory_object_write_done
;
304 ifactory_object_get_version( const NAIFactoryObject
*instance
)
310 ifactory_object_get_groups( const NAIFactoryObject
*instance
)
312 return( action_data_groups
);
316 ifactory_object_are_equal( const NAIFactoryObject
*a
, const NAIFactoryObject
*b
)
318 return( na_object_item_are_equal( NA_OBJECT_ITEM( a
), NA_OBJECT_ITEM( b
)));
322 ifactory_object_is_valid( const NAIFactoryObject
*object
)
324 g_return_val_if_fail( NA_IS_OBJECT_ACTION( object
), FALSE
);
326 return( object_object_is_valid( NA_OBJECT_ACTION( object
)));
330 * at this time, we don't yet have read the profiles as this will be
331 * done in ifactory_provider_read_done - we so just be able to deal with
332 * action-specific properties (not check for profiles consistency)
335 ifactory_object_read_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*reader
, void *reader_data
, GSList
**messages
)
339 g_debug( "na_object_action_ifactory_object_read_done: instance=%p", ( void * ) instance
);
341 na_object_item_deals_with_version( NA_OBJECT_ITEM( instance
));
343 /* may attach a new profile if we detect a pre-v2 action
344 * the v1_to_v2 conversion must be followed by a v2_to_v3 one
346 iversion
= na_object_get_iversion( instance
);
348 read_done_convert_v1_to_last( instance
);
351 /* deals with obsoleted data, i.e. data which may have been written in the past
352 * but are no long written by now - not relevant for a menu
354 read_done_deals_with_toolbar_label( instance
);
356 /* prepare the context after the reading
358 na_icontext_read_done( NA_ICONTEXT( instance
));
360 na_object_dump( instance
);
362 /* last, set other action defaults
364 na_factory_object_set_defaults( instance
);
368 ifactory_object_write_start( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
370 na_object_item_rebuild_children_slist( NA_OBJECT_ITEM( instance
));
372 return( NA_IIO_PROVIDER_CODE_OK
);
376 ifactory_object_write_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
380 g_return_val_if_fail( NA_IS_OBJECT_ACTION( instance
), NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
);
382 code
= write_done_write_profiles( instance
, writer
, writer_data
, messages
);
388 icontext_iface_init( NAIContextInterface
*iface
)
390 static const gchar
*thisfn
= "na_object_action_icontext_iface_init";
392 g_debug( "%s: iface=%p", thisfn
, ( void * ) iface
);
394 iface
->is_candidate
= icontext_is_candidate
;
398 icontext_is_candidate( NAIContext
*object
, guint target
, GList
*selection
)
404 * do we have a pre-v2 action ?
405 * it is be identified by an version = "1.x"
406 * any data found in data_def_action_v1 (defined in na-object-action-factory.c)
407 * is obsoleted and moved to a new profile
409 * actions read from .desktop already have iversion=3 (cf. desktop_read_start)
410 * and v1 actions may only come from xml or gconf
412 * returns TRUE if this actually was a v1 action which has been converted to v2
415 read_done_convert_v1_to_last( NAIFactoryObject
*instance
)
417 static const gchar
*thisfn
= "na_object_action_read_done_read_done_convert_v1_to_last";
422 NAObjectProfile
*profile
;
424 /* search for old data in the body: this is only possible before profiles
425 * because starting with contexts, iversion was written
428 def
= data_def_action_v1
;
431 boxed
= na_ifactory_object_get_data_boxed( instance
, def
->name
);
433 g_debug( "%s: boxed=%p (%s) marked to be moved from action body to profile",
434 thisfn
, ( void * ) boxed
, def
->name
);
435 to_move
= g_list_prepend( to_move
, boxed
);
444 /* now create a new profile
446 profile
= na_object_profile_new();
447 na_object_set_id( profile
, "profile-pre-v2" );
448 na_object_set_label( profile
, _( "Profile automatically created from pre-v2 action" ));
449 na_object_attach_profile( instance
, profile
);
452 for( ibox
= to_move
; ibox
; ibox
= ibox
->next
){
453 na_factory_object_move_boxed(
454 NA_IFACTORY_OBJECT( profile
), instance
, NA_DATA_BOXED( ibox
->data
));
458 na_factory_object_set_defaults( NA_IFACTORY_OBJECT( profile
));
459 na_object_profile_convert_v2_to_last( profile
);
464 * if toolbar-same-label is true, then ensure that this is actually true
467 read_done_deals_with_toolbar_label( NAIFactoryObject
*instance
)
469 gchar
*toolbar_label
;
473 toolbar_label
= na_object_get_toolbar_label( instance
);
474 action_label
= na_object_get_label( instance
);
476 if( !toolbar_label
|| !g_utf8_strlen( toolbar_label
, -1 )){
477 na_object_set_toolbar_label( instance
, action_label
);
478 na_object_set_toolbar_same_label( instance
, TRUE
);
481 same_label
= ( na_core_utils_str_collate( action_label
, toolbar_label
) == 0 );
482 na_object_set_toolbar_same_label( instance
, same_label
);
485 g_free( action_label
);
486 g_free( toolbar_label
);
490 * write the profiles of the action
491 * note that subitems string list has been rebuilt on write_start
494 write_done_write_profiles( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
496 static const gchar
*thisfn
= "na_object_action_write_done_write_profiles";
498 GSList
*children_slist
, *ic
;
499 NAObjectProfile
*profile
;
501 code
= NA_IIO_PROVIDER_CODE_OK
;
502 children_slist
= na_object_get_items_slist( instance
);
504 for( ic
= children_slist
; ic
&& code
== NA_IIO_PROVIDER_CODE_OK
; ic
= ic
->next
){
505 profile
= NA_OBJECT_PROFILE( na_object_get_item( instance
, ic
->data
));
508 code
= na_ifactory_provider_write_item( writer
, writer_data
, NA_IFACTORY_OBJECT( profile
), messages
);
511 g_warning( "%s: profile not found: %s", thisfn
, ( const gchar
* ) ic
->data
);
519 object_object_is_valid( const NAObjectAction
*action
)
522 GList
*profiles
, *ip
;
527 if( !action
->private->dispose_has_run
){
532 if( na_object_is_target_toolbar( action
)){
533 is_valid
= is_valid_toolbar_label( action
);
538 if( na_object_is_target_selection( action
)){
539 is_valid
= is_valid_label( action
);
545 profiles
= na_object_get_items( action
);
546 for( ip
= profiles
; ip
&& !valid_profiles
; ip
= ip
->next
){
547 if( na_object_is_valid( ip
->data
)){
551 is_valid
= ( valid_profiles
> 0 );
553 na_object_debug_invalid( action
, "no valid profile" );
562 is_valid_label( const NAObjectAction
*action
)
567 label
= na_object_get_label( action
);
568 is_valid
= ( label
&& g_utf8_strlen( label
, -1 ) > 0 );
572 na_object_debug_invalid( action
, "label" );
579 is_valid_toolbar_label( const NAObjectAction
*action
)
584 label
= na_object_get_toolbar_label( action
);
585 is_valid
= ( label
&& g_utf8_strlen( label
, -1 ) > 0 );
589 na_object_debug_invalid( action
, "toolbar-label" );
596 * na_object_action_new:
598 * Allocates a new #NAObjectAction object.
600 * The new #NAObjectAction object is initialized with suitable default values,
601 * but without any profile.
603 * Returns: the newly allocated #NAObjectAction object.
608 na_object_action_new( void )
610 NAObjectAction
*action
;
612 action
= g_object_new( NA_OBJECT_ACTION_TYPE
, NULL
);
618 * na_object_action_new_with_profile:
620 * Allocates a new #NAObjectAction object along with a default profile.
622 * Returns: the newly allocated #NAObjectAction action.
627 na_object_action_new_with_profile( void )
629 NAObjectAction
*action
;
630 NAObjectProfile
*profile
;
632 action
= na_object_action_new();
633 profile
= na_object_profile_new();
634 na_object_action_attach_profile( action
, profile
);
640 * na_object_action_new_with_defaults:
642 * Allocates a new #NAObjectAction object along with a default profile.
643 * These two objects have suitable default values.
645 * Returns: the newly allocated #NAObjectAction action.
650 na_object_action_new_with_defaults( void )
652 NAObjectAction
*action
;
653 NAObjectProfile
*profile
;
655 action
= na_object_action_new();
656 na_object_set_new_id( action
, NULL
);
657 na_object_set_label( action
, gettext( NEW_NAUTILUS_ACTION
));
658 na_object_set_toolbar_label( action
, gettext( NEW_NAUTILUS_ACTION
));
659 na_factory_object_set_defaults( NA_IFACTORY_OBJECT( action
));
661 profile
= na_object_profile_new_with_defaults();
662 na_object_action_attach_profile( action
, profile
);
668 * na_object_action_get_new_profile_name:
669 * @action: the #NAObjectAction object which will receive a new profile.
671 * Returns a name suitable as a new profile name.
673 * The search is made by iterating over the standard profile name
674 * prefix : basically, we increment a counter until finding a name
675 * which is not yet allocated. The provided name is so only suitable
676 * for the specified @action.
678 * When inserting a list of profiles in the action, we iter first for
679 * new names, before actually do the insertion. We so keep the last
680 * allocated name to avoid to allocate the same one twice.
682 * Returns: a newly allocated profile name, which should be g_free() by
688 na_object_action_get_new_profile_name( const NAObjectAction
*action
)
692 gchar
*candidate
= NULL
;
693 guint last_allocated
;
695 g_return_val_if_fail( NA_IS_OBJECT_ACTION( action
), NULL
);
697 if( !action
->private->dispose_has_run
){
699 last_allocated
= na_object_get_last_allocated( action
);
701 for( i
= last_allocated
+ 1 ; !ok
; ++i
){
703 candidate
= g_strdup_printf( "profile-%d", i
);
705 if( !na_object_get_item( action
, candidate
)){
707 na_object_set_last_allocated( action
, i
);
717 /*g_debug( "returning candidate=%s", candidate );*/
722 * na_object_action_attach_profile:
723 * @action: the #NAObjectAction action to which the profile will be attached.
724 * @profile: the #NAObjectProfile profile to be attached to @action.
726 * Adds a profile at the end of the list of profiles.
731 na_object_action_attach_profile( NAObjectAction
*action
, NAObjectProfile
*profile
)
733 g_return_if_fail( NA_IS_OBJECT_ACTION( action
));
734 g_return_if_fail( NA_IS_OBJECT_PROFILE( profile
));
736 if( !action
->private->dispose_has_run
){
738 na_object_append_item( action
, profile
);
739 na_object_set_parent( profile
, action
);
744 * na_object_action_set_last_version:
745 * @action: the #NAObjectAction action to update.
747 * Set the version number of the @action to the last one.
752 na_object_action_set_last_version( NAObjectAction
*action
)
754 g_return_if_fail( NA_IS_OBJECT_ACTION( action
));
756 if( !action
->private->dispose_has_run
){
758 na_object_set_version( action
, "2.0" );