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 void object_dump( const NAObject
*object
);
77 static gboolean
object_is_valid( const NAObject
*object
);
79 static void ifactory_object_iface_init( NAIFactoryObjectInterface
*iface
);
80 static guint
ifactory_object_get_version( const NAIFactoryObject
*instance
);
81 static NADataGroup
*ifactory_object_get_groups( const NAIFactoryObject
*instance
);
82 static gboolean
ifactory_object_are_equal( const NAIFactoryObject
*a
, const NAIFactoryObject
*b
);
83 static gboolean
ifactory_object_is_valid( const NAIFactoryObject
*object
);
84 static void ifactory_object_read_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*reader
, void *reader_data
, GSList
**messages
);
85 static guint
ifactory_object_write_start( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
86 static guint
ifactory_object_write_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
88 static void icontext_iface_init( NAIContextInterface
*iface
);
89 static gboolean
icontext_is_candidate( NAIContext
*object
, guint target
, GList
*selection
);
91 static gboolean
read_done_convert_v1_to_last( NAIFactoryObject
*instance
);
92 static void read_done_deals_with_toolbar_label( NAIFactoryObject
*instance
);
94 static guint
write_done_write_profiles( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
96 static gboolean
object_object_is_valid( const NAObjectAction
*action
);
97 static gboolean
is_valid_label( const NAObjectAction
*action
);
98 static gboolean
is_valid_toolbar_label( const NAObjectAction
*action
);
101 na_object_action_get_type( void )
103 static GType action_type
= 0;
105 if( action_type
== 0 ){
107 action_type
= register_type();
110 return( action_type
);
114 register_type( void )
116 static const gchar
*thisfn
= "na_object_action_register_type";
119 static GTypeInfo info
= {
120 sizeof( NAObjectActionClass
),
123 ( GClassInitFunc
) class_init
,
126 sizeof( NAObjectAction
),
128 ( GInstanceInitFunc
) instance_init
131 static const GInterfaceInfo icontext_iface_info
= {
132 ( GInterfaceInitFunc
) icontext_iface_init
,
137 static const GInterfaceInfo ifactory_object_iface_info
= {
138 ( GInterfaceInitFunc
) ifactory_object_iface_init
,
143 g_debug( "%s", thisfn
);
145 type
= g_type_register_static( NA_OBJECT_ITEM_TYPE
, "NAObjectAction", &info
, 0 );
147 g_type_add_interface_static( type
, NA_ICONTEXT_TYPE
, &icontext_iface_info
);
149 g_type_add_interface_static( type
, NA_IFACTORY_OBJECT_TYPE
, &ifactory_object_iface_info
);
155 class_init( NAObjectActionClass
*klass
)
157 static const gchar
*thisfn
= "na_object_action_class_init";
158 GObjectClass
*object_class
;
159 NAObjectClass
*naobject_class
;
161 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
163 st_parent_class
= g_type_class_peek_parent( klass
);
165 object_class
= G_OBJECT_CLASS( klass
);
166 object_class
->set_property
= instance_set_property
;
167 object_class
->get_property
= instance_get_property
;
168 object_class
->dispose
= instance_dispose
;
169 object_class
->finalize
= instance_finalize
;
171 naobject_class
= NA_OBJECT_CLASS( klass
);
172 naobject_class
->copy
= object_copy
;
173 naobject_class
->dump
= object_dump
;
174 naobject_class
->are_equal
= NULL
;
175 naobject_class
->is_valid
= object_is_valid
;
177 klass
->private = g_new0( NAObjectActionClassPrivate
, 1 );
179 na_factory_object_define_properties( object_class
, action_data_groups
);
183 instance_init( GTypeInstance
*instance
, gpointer klass
)
185 static const gchar
*thisfn
= "na_object_action_instance_init";
186 NAObjectAction
*self
;
188 g_return_if_fail( NA_IS_OBJECT_ACTION( instance
));
190 self
= NA_OBJECT_ACTION( instance
);
192 g_debug( "%s: instance=%p (%s), klass=%p",
193 thisfn
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
), ( void * ) klass
);
195 self
->private = g_new0( NAObjectActionPrivate
, 1 );
199 instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
)
201 g_return_if_fail( NA_IS_OBJECT_ACTION( object
));
202 g_return_if_fail( NA_IS_IFACTORY_OBJECT( object
));
204 if( !NA_OBJECT_ACTION( object
)->private->dispose_has_run
){
206 na_factory_object_get_as_value( NA_IFACTORY_OBJECT( object
), g_quark_to_string( property_id
), value
);
211 instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
)
213 g_return_if_fail( NA_IS_OBJECT_ACTION( object
));
214 g_return_if_fail( NA_IS_IFACTORY_OBJECT( object
));
216 if( !NA_OBJECT_ACTION( object
)->private->dispose_has_run
){
218 na_factory_object_set_from_value( NA_IFACTORY_OBJECT( object
), g_quark_to_string( property_id
), value
);
223 instance_dispose( GObject
*object
)
225 static const gchar
*thisfn
= "na_object_action_instance_dispose";
226 NAObjectAction
*self
;
228 g_return_if_fail( NA_IS_OBJECT_ACTION( object
));
230 self
= NA_OBJECT_ACTION( object
);
232 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_dump( const NAObject
*object
)
280 static const char *thisfn
= "na_object_action_object_dump";
281 NAObjectAction
*self
;
283 g_return_if_fail( NA_IS_OBJECT_ACTION( object
));
285 self
= NA_OBJECT_ACTION( object
);
287 if( !self
->private->dispose_has_run
){
288 g_debug( "%s: object=%p (%s, ref_count=%d)", thisfn
,
289 ( void * ) object
, G_OBJECT_TYPE_NAME( object
), G_OBJECT( object
)->ref_count
);
291 /* chain up to the parent class */
292 if( NA_OBJECT_CLASS( st_parent_class
)->dump
){
293 NA_OBJECT_CLASS( st_parent_class
)->dump( object
);
296 g_debug( "+- end of dump" );
301 object_is_valid( const NAObject
*object
)
303 g_return_val_if_fail( NA_IS_OBJECT_ACTION( object
), FALSE
);
305 return( object_object_is_valid( NA_OBJECT_ACTION( object
)));
309 ifactory_object_iface_init( NAIFactoryObjectInterface
*iface
)
311 static const gchar
*thisfn
= "na_object_action_ifactory_object_iface_init";
313 g_debug( "%s: iface=%p", thisfn
, ( void * ) iface
);
315 iface
->get_version
= ifactory_object_get_version
;
316 iface
->get_groups
= ifactory_object_get_groups
;
318 iface
->are_equal
= ifactory_object_are_equal
;
319 iface
->is_valid
= ifactory_object_is_valid
;
320 iface
->read_start
= NULL
;
321 iface
->read_done
= ifactory_object_read_done
;
322 iface
->write_start
= ifactory_object_write_start
;
323 iface
->write_done
= ifactory_object_write_done
;
327 ifactory_object_get_version( const NAIFactoryObject
*instance
)
333 ifactory_object_get_groups( const NAIFactoryObject
*instance
)
335 return( action_data_groups
);
339 * @a is the original object
340 * @b is the current one
342 * Even if they have both the same children list, the current action is
343 * considered modified as soon as one of its profile is itself modified.
346 ifactory_object_are_equal( const NAIFactoryObject
*a
, const NAIFactoryObject
*b
)
350 if( na_object_item_are_equal( NA_OBJECT_ITEM( a
), NA_OBJECT_ITEM( b
))){
351 for( it
= na_object_get_items( b
) ; it
; it
= it
->next
){
352 if( na_object_is_modified( it
->data
)){
362 ifactory_object_is_valid( const NAIFactoryObject
*object
)
364 g_return_val_if_fail( NA_IS_OBJECT_ACTION( object
), FALSE
);
366 return( object_object_is_valid( NA_OBJECT_ACTION( object
)));
370 * at this time, we don't yet have read the profiles as this will be
371 * done in ifactory_provider_read_done - we so just be able to deal with
372 * action-specific properties (not check for profiles consistency)
375 ifactory_object_read_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*reader
, void *reader_data
, GSList
**messages
)
379 g_debug( "na_object_action_ifactory_object_read_done: instance=%p", ( void * ) instance
);
381 na_object_item_deals_with_version( NA_OBJECT_ITEM( instance
));
383 /* may attach a new profile if we detect a pre-v2 action
384 * the v1_to_v2 conversion must be followed by a v2_to_v3 one
386 iversion
= na_object_get_iversion( instance
);
388 read_done_convert_v1_to_last( instance
);
391 /* deals with obsoleted data, i.e. data which may have been written in the past
392 * but are no long written by now - not relevant for a menu
394 read_done_deals_with_toolbar_label( instance
);
396 /* prepare the context after the reading
398 na_icontext_read_done( NA_ICONTEXT( instance
));
400 na_object_dump( instance
);
402 /* last, set other action defaults
404 na_factory_object_set_defaults( instance
);
408 ifactory_object_write_start( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
410 na_object_item_rebuild_children_slist( NA_OBJECT_ITEM( instance
));
412 return( NA_IIO_PROVIDER_CODE_OK
);
416 ifactory_object_write_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
420 g_return_val_if_fail( NA_IS_OBJECT_ACTION( instance
), NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
);
422 code
= write_done_write_profiles( instance
, writer
, writer_data
, messages
);
428 icontext_iface_init( NAIContextInterface
*iface
)
430 static const gchar
*thisfn
= "na_object_action_icontext_iface_init";
432 g_debug( "%s: iface=%p", thisfn
, ( void * ) iface
);
434 iface
->is_candidate
= icontext_is_candidate
;
438 icontext_is_candidate( NAIContext
*object
, guint target
, GList
*selection
)
444 * do we have a pre-v2 action ?
445 * it is be identified by an version = "1.x"
446 * any data found in data_def_action_v1 (defined in na-object-action-factory.c)
447 * is obsoleted and moved to a new profile
449 * actions read from .desktop already have iversion=3 (cf. desktop_read_start)
450 * and v1 actions may only come from xml or gconf
452 * returns TRUE if this actually was a v1 action which has been converted to v2
455 read_done_convert_v1_to_last( NAIFactoryObject
*instance
)
457 static const gchar
*thisfn
= "na_object_action_read_done_read_done_convert_v1_to_last";
462 NAObjectProfile
*profile
;
464 /* search for old data in the body: this is only possible before profiles
465 * because starting with contexts, iversion was written
468 def
= data_def_action_v1
;
471 boxed
= na_ifactory_object_get_data_boxed( instance
, def
->name
);
473 g_debug( "%s: boxed=%p (%s) marked to be moved from action body to profile",
474 thisfn
, ( void * ) boxed
, def
->name
);
475 to_move
= g_list_prepend( to_move
, boxed
);
484 /* now create a new profile
486 profile
= na_object_profile_new();
487 na_object_set_id( profile
, "profile-pre-v2" );
488 na_object_set_label( profile
, _( "Profile automatically created from pre-v2 action" ));
489 na_object_attach_profile( instance
, profile
);
492 for( ibox
= to_move
; ibox
; ibox
= ibox
->next
){
493 na_factory_object_move_boxed(
494 NA_IFACTORY_OBJECT( profile
), instance
, NA_DATA_BOXED( ibox
->data
));
498 na_factory_object_set_defaults( NA_IFACTORY_OBJECT( profile
));
499 na_object_profile_convert_v2_to_last( profile
);
504 * if toolbar-same-label is true, then ensure that this is actually true
507 read_done_deals_with_toolbar_label( NAIFactoryObject
*instance
)
509 gchar
*toolbar_label
;
513 toolbar_label
= na_object_get_toolbar_label( instance
);
514 action_label
= na_object_get_label( instance
);
516 if( !toolbar_label
|| !g_utf8_strlen( toolbar_label
, -1 )){
517 na_object_set_toolbar_label( instance
, action_label
);
518 na_object_set_toolbar_same_label( instance
, TRUE
);
521 same_label
= ( na_core_utils_str_collate( action_label
, toolbar_label
) == 0 );
522 na_object_set_toolbar_same_label( instance
, same_label
);
525 g_free( action_label
);
526 g_free( toolbar_label
);
530 * write the profiles of the action
531 * note that subitems string list has been rebuilt on write_start
534 write_done_write_profiles( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
536 static const gchar
*thisfn
= "na_object_action_write_done_write_profiles";
538 GSList
*children_slist
, *ic
;
539 NAObjectProfile
*profile
;
541 code
= NA_IIO_PROVIDER_CODE_OK
;
542 children_slist
= na_object_get_items_slist( instance
);
544 for( ic
= children_slist
; ic
&& code
== NA_IIO_PROVIDER_CODE_OK
; ic
= ic
->next
){
545 profile
= NA_OBJECT_PROFILE( na_object_get_item( instance
, ic
->data
));
548 code
= na_ifactory_provider_write_item( writer
, writer_data
, NA_IFACTORY_OBJECT( profile
), messages
);
551 g_warning( "%s: profile not found: %s", thisfn
, ( const gchar
* ) ic
->data
);
559 object_object_is_valid( const NAObjectAction
*action
)
562 GList
*profiles
, *ip
;
567 if( !action
->private->dispose_has_run
){
572 if( na_object_is_target_toolbar( action
)){
573 is_valid
= is_valid_toolbar_label( action
);
578 if( na_object_is_target_selection( action
)){
579 is_valid
= is_valid_label( action
);
585 profiles
= na_object_get_items( action
);
586 for( ip
= profiles
; ip
&& !valid_profiles
; ip
= ip
->next
){
587 if( na_object_is_valid( ip
->data
)){
591 is_valid
= ( valid_profiles
> 0 );
593 na_object_debug_invalid( action
, "no valid profile" );
602 is_valid_label( const NAObjectAction
*action
)
607 label
= na_object_get_label( action
);
608 is_valid
= ( label
&& g_utf8_strlen( label
, -1 ) > 0 );
612 na_object_debug_invalid( action
, "label" );
619 is_valid_toolbar_label( const NAObjectAction
*action
)
624 label
= na_object_get_toolbar_label( action
);
625 is_valid
= ( label
&& g_utf8_strlen( label
, -1 ) > 0 );
629 na_object_debug_invalid( action
, "toolbar-label" );
636 * na_object_action_new:
638 * Allocates a new #NAObjectAction object.
640 * The new #NAObjectAction object is initialized with suitable default values,
641 * but without any profile.
643 * Returns: the newly allocated #NAObjectAction object.
648 na_object_action_new( void )
650 NAObjectAction
*action
;
652 action
= g_object_new( NA_OBJECT_ACTION_TYPE
, NULL
);
658 * na_object_action_new_with_profile:
660 * Allocates a new #NAObjectAction object along with a default profile.
662 * Returns: the newly allocated #NAObjectAction action.
667 na_object_action_new_with_profile( void )
669 NAObjectAction
*action
;
670 NAObjectProfile
*profile
;
672 action
= na_object_action_new();
673 profile
= na_object_profile_new();
674 na_object_action_attach_profile( action
, profile
);
680 * na_object_action_new_with_defaults:
682 * Allocates a new #NAObjectAction object along with a default profile.
683 * These two objects have suitable default values.
685 * Returns: the newly allocated #NAObjectAction action.
690 na_object_action_new_with_defaults( void )
692 NAObjectAction
*action
;
693 NAObjectProfile
*profile
;
695 action
= na_object_action_new();
696 na_object_set_new_id( action
, NULL
);
697 na_object_set_label( action
, gettext( NEW_NAUTILUS_ACTION
));
698 na_object_set_toolbar_label( action
, gettext( NEW_NAUTILUS_ACTION
));
699 na_factory_object_set_defaults( NA_IFACTORY_OBJECT( action
));
701 profile
= na_object_profile_new_with_defaults();
702 na_object_action_attach_profile( action
, profile
);
708 * na_object_action_get_new_profile_name:
709 * @action: the #NAObjectAction object which will receive a new profile.
711 * Returns a name suitable as a new profile name.
713 * The search is made by iterating over the standard profile name
714 * prefix : basically, we increment a counter until finding a name
715 * which is not yet allocated. The provided name is so only suitable
716 * for the specified @action.
718 * When inserting a list of profiles in the action, we iter first for
719 * new names, before actually do the insertion. We so keep the last
720 * allocated name to avoid to allocate the same one twice.
722 * Returns: a newly allocated profile name, which should be g_free() by
728 na_object_action_get_new_profile_name( const NAObjectAction
*action
)
732 gchar
*candidate
= NULL
;
733 guint last_allocated
;
735 g_return_val_if_fail( NA_IS_OBJECT_ACTION( action
), NULL
);
737 if( !action
->private->dispose_has_run
){
739 last_allocated
= na_object_get_last_allocated( action
);
741 for( i
= last_allocated
+ 1 ; !ok
; ++i
){
743 candidate
= g_strdup_printf( "profile-%d", i
);
745 if( !na_object_get_item( action
, candidate
)){
747 na_object_set_last_allocated( action
, i
);
757 /*g_debug( "returning candidate=%s", candidate );*/
762 * na_object_action_attach_profile:
763 * @action: the #NAObjectAction action to which the profile will be attached.
764 * @profile: the #NAObjectProfile profile to be attached to @action.
766 * Adds a profile at the end of the list of profiles.
771 na_object_action_attach_profile( NAObjectAction
*action
, NAObjectProfile
*profile
)
773 g_return_if_fail( NA_IS_OBJECT_ACTION( action
));
774 g_return_if_fail( NA_IS_OBJECT_PROFILE( profile
));
776 if( !action
->private->dispose_has_run
){
778 na_object_append_item( action
, profile
);
779 na_object_set_parent( profile
, action
);
784 * na_object_action_set_last_version:
785 * @action: the #NAObjectAction action to update.
787 * Set the version number of the @action to the last one.
792 na_object_action_set_last_version( NAObjectAction
*action
)
794 g_return_if_fail( NA_IS_OBJECT_ACTION( action
));
796 if( !action
->private->dispose_has_run
){
798 na_object_set_version( action
, "2.0" );