2 * Nautilus ObjectActions
3 * A file-manager extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009-2015 Pierre Wieser and others (see AUTHORS)
9 * FileManager-Actions 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 * FileManager-Actions 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 GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with FileManager-Actions; see the file COPYING. If not, see
21 * <http://www.gnu.org/licenses/>.
24 * Frederic Ruaudel <grumz@grumz.net>
25 * Rodrigo Moya <rodrigo@gnome-db.org>
26 * Pierre Wieser <pwieser@trychlos.org>
27 * ... and many others (see AUTHORS)
34 #include <glib/gi18n.h>
39 #include <api/fma-core-utils.h>
40 #include <api/fma-iio-provider.h>
41 #include <api/fma-object-api.h>
43 #include "fma-factory-provider.h"
44 #include "fma-factory-object.h"
48 struct _FMAObjectActionClassPrivate
{
49 void *empty
; /* so that gcc -pedantic is happy */
52 /* private instance data
54 struct _FMAObjectActionPrivate
{
55 gboolean dispose_has_run
;
58 /* i18n: default label for a new action */
59 #define NEW_FILEMANAGER_ACTION N_( "New file-manager action" )
61 extern FMADataGroup action_data_groups
[]; /* defined in fma-object-action-factory.c */
62 extern FMADataDef data_def_action_v1
[]; /* defined in fma-object-action-factory.c */
64 static FMAObjectItemClass
*st_parent_class
= NULL
;
66 static GType
register_type( void );
67 static void class_init( FMAObjectActionClass
*klass
);
68 static void instance_init( GTypeInstance
*instance
, gpointer klass
);
69 static void instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
);
70 static void instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
);
71 static void instance_dispose( GObject
*object
);
72 static void instance_finalize( GObject
*object
);
74 static void object_dump( const FMAObject
*object
);
75 static gboolean
object_are_equal( const FMAObject
*a
, const FMAObject
*b
);
76 static gboolean
object_is_valid( const FMAObject
*object
);
78 static void ifactory_object_iface_init( FMAIFactoryObjectInterface
*iface
, void *user_data
);
79 static guint
ifactory_object_get_version( const FMAIFactoryObject
*instance
);
80 static FMADataGroup
*ifactory_object_get_groups( const FMAIFactoryObject
*instance
);
81 static void ifactory_object_read_done( FMAIFactoryObject
*instance
, const FMAIFactoryProvider
*reader
, void *reader_data
, GSList
**messages
);
82 static guint
ifactory_object_write_start( FMAIFactoryObject
*instance
, const FMAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
83 static guint
ifactory_object_write_done( FMAIFactoryObject
*instance
, const FMAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
85 static void icontext_iface_init( FMAIContextInterface
*iface
, void *user_data
);
86 static gboolean
icontext_is_candidate( FMAIContext
*object
, guint target
, GList
*selection
);
88 static FMAObjectProfile
*read_done_convert_v1_to_v2( FMAIFactoryObject
*instance
);
89 static void read_done_deals_with_toolbar_label( FMAIFactoryObject
*instance
);
91 static guint
write_done_write_profiles( FMAIFactoryObject
*instance
, const FMAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
);
93 static gboolean
is_valid_label( const FMAObjectAction
*action
);
94 static gboolean
is_valid_toolbar_label( const FMAObjectAction
*action
);
97 fma_object_action_get_type( void )
99 static GType action_type
= 0;
101 if( action_type
== 0 ){
103 action_type
= register_type();
106 return( action_type
);
110 register_type( void )
112 static const gchar
*thisfn
= "fma_object_action_register_type";
115 static GTypeInfo info
= {
116 sizeof( FMAObjectActionClass
),
119 ( GClassInitFunc
) class_init
,
122 sizeof( FMAObjectAction
),
124 ( GInstanceInitFunc
) instance_init
127 static const GInterfaceInfo icontext_iface_info
= {
128 ( GInterfaceInitFunc
) icontext_iface_init
,
133 static const GInterfaceInfo ifactory_object_iface_info
= {
134 ( GInterfaceInitFunc
) ifactory_object_iface_init
,
139 g_debug( "%s", thisfn
);
141 type
= g_type_register_static( FMA_TYPE_OBJECT_ITEM
, "FMAObjectAction", &info
, 0 );
143 g_type_add_interface_static( type
, FMA_TYPE_ICONTEXT
, &icontext_iface_info
);
145 g_type_add_interface_static( type
, FMA_TYPE_IFACTORY_OBJECT
, &ifactory_object_iface_info
);
151 class_init( FMAObjectActionClass
*klass
)
153 static const gchar
*thisfn
= "fma_object_action_class_init";
154 GObjectClass
*object_class
;
155 FMAObjectClass
*naobject_class
;
157 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
159 st_parent_class
= g_type_class_peek_parent( klass
);
161 object_class
= G_OBJECT_CLASS( klass
);
162 object_class
->set_property
= instance_set_property
;
163 object_class
->get_property
= instance_get_property
;
164 object_class
->dispose
= instance_dispose
;
165 object_class
->finalize
= instance_finalize
;
167 naobject_class
= FMA_OBJECT_CLASS( klass
);
168 naobject_class
->dump
= object_dump
;
169 naobject_class
->are_equal
= object_are_equal
;
170 naobject_class
->is_valid
= object_is_valid
;
172 klass
->private = g_new0( FMAObjectActionClassPrivate
, 1 );
174 fma_factory_object_define_properties( object_class
, action_data_groups
);
178 instance_init( GTypeInstance
*instance
, gpointer klass
)
180 static const gchar
*thisfn
= "fma_object_action_instance_init";
181 FMAObjectAction
*self
;
183 g_return_if_fail( FMA_IS_OBJECT_ACTION( instance
));
185 self
= FMA_OBJECT_ACTION( instance
);
187 g_debug( "%s: instance=%p (%s), klass=%p",
188 thisfn
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
), ( void * ) klass
);
190 self
->private = g_new0( FMAObjectActionPrivate
, 1 );
194 instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
)
196 g_return_if_fail( FMA_IS_OBJECT_ACTION( object
));
197 g_return_if_fail( FMA_IS_IFACTORY_OBJECT( object
));
199 if( !FMA_OBJECT_ACTION( object
)->private->dispose_has_run
){
201 fma_factory_object_get_as_value( FMA_IFACTORY_OBJECT( object
), g_quark_to_string( property_id
), value
);
206 instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
)
208 g_return_if_fail( FMA_IS_OBJECT_ACTION( object
));
209 g_return_if_fail( FMA_IS_IFACTORY_OBJECT( object
));
211 if( !FMA_OBJECT_ACTION( object
)->private->dispose_has_run
){
213 fma_factory_object_set_from_value( FMA_IFACTORY_OBJECT( object
), g_quark_to_string( property_id
), value
);
218 instance_dispose( GObject
*object
)
220 static const gchar
*thisfn
= "fma_object_action_instance_dispose";
221 FMAObjectAction
*self
;
223 g_return_if_fail( FMA_IS_OBJECT_ACTION( object
));
225 self
= FMA_OBJECT_ACTION( object
);
227 if( !self
->private->dispose_has_run
){
228 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
230 self
->private->dispose_has_run
= TRUE
;
232 /* chain up to the parent class */
233 if( G_OBJECT_CLASS( st_parent_class
)->dispose
){
234 G_OBJECT_CLASS( st_parent_class
)->dispose( object
);
240 instance_finalize( GObject
*object
)
242 static const gchar
*thisfn
= "fma_object_action_instance_finalize";
243 FMAObjectAction
*self
;
245 g_return_if_fail( FMA_IS_OBJECT_ACTION( object
));
247 self
= FMA_OBJECT_ACTION( object
);
249 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
251 g_free( self
->private );
253 /* chain call to parent class */
254 if( G_OBJECT_CLASS( st_parent_class
)->finalize
){
255 G_OBJECT_CLASS( st_parent_class
)->finalize( object
);
260 object_dump( const FMAObject
*object
)
262 static const char *thisfn
= "fma_object_action_object_dump";
263 FMAObjectAction
*self
;
265 g_return_if_fail( FMA_IS_OBJECT_ACTION( object
));
267 self
= FMA_OBJECT_ACTION( object
);
269 if( !self
->private->dispose_has_run
){
270 g_debug( "%s: object=%p (%s, ref_count=%d)", thisfn
,
271 ( void * ) object
, G_OBJECT_TYPE_NAME( object
), G_OBJECT( object
)->ref_count
);
273 /* chain up to the parent class */
274 if( FMA_OBJECT_CLASS( st_parent_class
)->dump
){
275 FMA_OBJECT_CLASS( st_parent_class
)->dump( object
);
278 g_debug( "+- end of dump" );
283 * @a is the original object
284 * @b is the current one
286 * Even if they have both the same children list, the current action is
287 * considered modified as soon as one of its profile is itself modified.
290 object_are_equal( const FMAObject
*a
, const FMAObject
*b
)
292 static const gchar
*thisfn
= "fma_object_action_object_are_equal";
296 g_debug( "%s: a=%p, b=%p", thisfn
, ( void * ) a
, ( void * ) b
);
298 for( it
= fma_object_get_items( b
) ; it
; it
= it
->next
){
299 if( fma_object_is_modified( it
->data
)){
306 /* chain call to parent class */
307 if( FMA_OBJECT_CLASS( st_parent_class
)->are_equal
){
308 are_equal
&= FMA_OBJECT_CLASS( st_parent_class
)->are_equal( a
, b
);
315 object_is_valid( const FMAObject
*object
)
317 static const gchar
*thisfn
= "fma_object_action_object_is_valid";
319 FMAObjectAction
*action
;
321 g_return_val_if_fail( FMA_IS_OBJECT_ACTION( object
), FALSE
);
324 action
= FMA_OBJECT_ACTION( object
);
326 if( !action
->private->dispose_has_run
){
327 g_debug( "%s: action=%p (%s)", thisfn
, ( void * ) action
, G_OBJECT_TYPE_NAME( action
));
331 if( fma_object_is_target_toolbar( action
)){
332 is_valid
&= is_valid_toolbar_label( action
);
335 if( fma_object_is_target_selection( action
) || fma_object_is_target_location( action
)){
336 is_valid
&= is_valid_label( action
);
340 fma_object_debug_invalid( action
, "no valid profile" );
344 /* chain up to the parent class */
345 if( FMA_OBJECT_CLASS( st_parent_class
)->is_valid
){
346 is_valid
&= FMA_OBJECT_CLASS( st_parent_class
)->is_valid( object
);
353 ifactory_object_iface_init( FMAIFactoryObjectInterface
*iface
, void *user_data
)
355 static const gchar
*thisfn
= "fma_object_action_ifactory_object_iface_init";
357 g_debug( "%s: iface=%p, user_data=%p", thisfn
, ( void * ) iface
, ( void * ) user_data
);
359 iface
->get_version
= ifactory_object_get_version
;
360 iface
->get_groups
= ifactory_object_get_groups
;
361 iface
->read_done
= ifactory_object_read_done
;
362 iface
->write_start
= ifactory_object_write_start
;
363 iface
->write_done
= ifactory_object_write_done
;
367 ifactory_object_get_version( const FMAIFactoryObject
*instance
)
372 static FMADataGroup
*
373 ifactory_object_get_groups( const FMAIFactoryObject
*instance
)
375 return( action_data_groups
);
379 * at this time, we don't yet have read the profiles as this will be
380 * triggered by ifactory_provider_read_done - we so just are able to deal with
381 * action-specific properties (not to check for profiles consistency)
384 ifactory_object_read_done( FMAIFactoryObject
*instance
, const FMAIFactoryProvider
*reader
, void *reader_data
, GSList
**messages
)
387 FMAObjectProfile
*profile
;
389 g_debug( "fma_object_action_ifactory_object_read_done: instance=%p", ( void * ) instance
);
391 fma_object_item_deals_with_version( FMA_OBJECT_ITEM( instance
));
393 /* should attach a new profile if we detect a pre-v2 action
394 * the v1_to_v2 conversion must be followed by a v2_to_v3 one
396 iversion
= fma_object_get_iversion( instance
);
398 profile
= read_done_convert_v1_to_v2( instance
);
399 fma_object_profile_convert_v2_to_last( profile
);
402 /* deals with obsoleted data, i.e. data which may have been written in the past
403 * but are no long written by now - not relevant for a menu
405 read_done_deals_with_toolbar_label( instance
);
407 /* prepare the context after the reading
409 fma_icontext_read_done( FMA_ICONTEXT( instance
));
411 /* last, set action defaults
413 fma_factory_object_set_defaults( instance
);
417 ifactory_object_write_start( FMAIFactoryObject
*instance
, const FMAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
419 fma_object_item_rebuild_children_slist( FMA_OBJECT_ITEM( instance
));
421 return( IIO_PROVIDER_CODE_OK
);
425 ifactory_object_write_done( FMAIFactoryObject
*instance
, const FMAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
429 g_return_val_if_fail( FMA_IS_OBJECT_ACTION( instance
), IIO_PROVIDER_CODE_PROGRAM_ERROR
);
431 code
= write_done_write_profiles( instance
, writer
, writer_data
, messages
);
437 icontext_iface_init( FMAIContextInterface
*iface
, void *user_data
)
439 static const gchar
*thisfn
= "fma_object_action_icontext_iface_init";
441 g_debug( "%s: iface=%p, user_data=%p", thisfn
, ( void * ) iface
, ( void * ) user_data
);
443 iface
->is_candidate
= icontext_is_candidate
;
447 icontext_is_candidate( FMAIContext
*object
, guint target
, GList
*selection
)
453 * if we have a pre-v2 action
454 * any data found in data_def_action_v1 (defined in fma-object-action-factory.c)
455 * is obsoleted and should be moved to a new profile
457 * actions read from .desktop already have iversion=3 (cf. desktop_read_start)
458 * so v1 actions may only come from xml or gconf
460 * returns the newly defined profile
462 static FMAObjectProfile
*
463 read_done_convert_v1_to_v2( FMAIFactoryObject
*instance
)
465 static const gchar
*thisfn
= "fma_object_action_read_done_read_done_convert_v1_to_last";
470 FMAObjectProfile
*profile
;
472 /* search for old data in the body: this is only possible before profiles
473 * because starting with contexts, iversion was written
476 def
= data_def_action_v1
;
479 boxed
= fma_ifactory_object_get_data_boxed( instance
, def
->name
);
481 g_debug( "%s: boxed=%p (%s) marked to be moved from action body to profile",
482 thisfn
, ( void * ) boxed
, def
->name
);
483 to_move
= g_list_prepend( to_move
, boxed
);
488 /* now create a new profile
490 profile
= fma_object_profile_new();
491 fma_object_set_id( profile
, "profile-pre-v2" );
492 fma_object_set_label( profile
, _( "Profile automatically created from pre-v2 action" ));
493 fma_object_attach_profile( instance
, profile
);
495 for( ibox
= to_move
; ibox
; ibox
= ibox
->next
){
496 fma_factory_object_move_boxed(
497 FMA_IFACTORY_OBJECT( profile
), instance
, FMA_DATA_BOXED( ibox
->data
));
504 * if toolbar-same-label is true, then ensure that this is actually true
507 read_done_deals_with_toolbar_label( FMAIFactoryObject
*instance
)
509 gchar
*toolbar_label
;
513 toolbar_label
= fma_object_get_toolbar_label( instance
);
514 action_label
= fma_object_get_label( instance
);
516 if( !toolbar_label
|| !g_utf8_strlen( toolbar_label
, -1 )){
517 fma_object_set_toolbar_label( instance
, action_label
);
518 fma_object_set_toolbar_same_label( instance
, TRUE
);
521 same_label
= ( fma_core_utils_str_collate( action_label
, toolbar_label
) == 0 );
522 fma_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( FMAIFactoryObject
*instance
, const FMAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
536 static const gchar
*thisfn
= "fma_object_action_write_done_write_profiles";
538 GSList
*children_slist
, *ic
;
539 FMAObjectProfile
*profile
;
541 code
= IIO_PROVIDER_CODE_OK
;
542 children_slist
= fma_object_get_items_slist( instance
);
544 for( ic
= children_slist
; ic
&& code
== IIO_PROVIDER_CODE_OK
; ic
= ic
->next
){
545 profile
= FMA_OBJECT_PROFILE( fma_object_get_item( instance
, ic
->data
));
548 code
= fma_ifactory_provider_write_item( writer
, writer_data
, FMA_IFACTORY_OBJECT( profile
), messages
);
551 g_warning( "%s: profile not found: %s", thisfn
, ( const gchar
* ) ic
->data
);
559 is_valid_label( const FMAObjectAction
*action
)
564 label
= fma_object_get_label( action
);
565 is_valid
= ( label
&& g_utf8_strlen( label
, -1 ) > 0 );
569 fma_object_debug_invalid( action
, "label" );
576 is_valid_toolbar_label( const FMAObjectAction
*action
)
581 label
= fma_object_get_toolbar_label( action
);
582 is_valid
= ( label
&& g_utf8_strlen( label
, -1 ) > 0 );
586 fma_object_debug_invalid( action
, "toolbar-label" );
593 * fma_object_action_new:
595 * Allocates a new #FMAObjectAction object.
597 * The new #FMAObjectAction object is initialized with suitable default values,
598 * but without any profile.
600 * Returns: the newly allocated #FMAObjectAction object.
605 fma_object_action_new( void )
607 FMAObjectAction
*action
;
609 action
= g_object_new( FMA_TYPE_OBJECT_ACTION
, NULL
);
615 * fma_object_action_new_with_profile:
617 * Allocates a new #FMAObjectAction object along with a default profile.
619 * Returns: the newly allocated #FMAObjectAction action.
624 fma_object_action_new_with_profile( void )
626 FMAObjectAction
*action
;
627 FMAObjectProfile
*profile
;
629 action
= fma_object_action_new();
630 profile
= fma_object_profile_new();
631 fma_object_attach_profile( action
, profile
);
637 * fma_object_action_new_with_defaults:
639 * Allocates a new #FMAObjectAction object along with a default profile.
640 * These two objects have suitable default values.
642 * Returns: the newly allocated #FMAObjectAction action.
647 fma_object_action_new_with_defaults( void )
649 FMAObjectAction
*action
;
650 FMAObjectProfile
*profile
;
652 action
= fma_object_action_new();
653 fma_object_set_new_id( action
, NULL
);
654 fma_object_set_label( action
, gettext( NEW_FILEMANAGER_ACTION
));
655 fma_object_set_toolbar_label( action
, gettext( NEW_FILEMANAGER_ACTION
));
656 fma_factory_object_set_defaults( FMA_IFACTORY_OBJECT( action
));
658 profile
= fma_object_profile_new_with_defaults();
659 fma_object_attach_profile( action
, profile
);
665 * fma_object_action_get_new_profile_name:
666 * @action: the #FMAObjectAction object which will receive a new profile.
668 * Returns a name suitable as a new profile name.
670 * The search is made by iterating over the standard profile name
671 * prefix : basically, we increment a counter until finding a name
672 * which is not yet allocated. The provided name is so only suitable
673 * for the specified @action.
675 * When inserting a list of profiles in the action, we iter first for
676 * new names, before actually do the insertion. We so keep the last
677 * allocated name to avoid to allocate the same one twice.
679 * Returns: a newly allocated profile name, which should be g_free() by
685 fma_object_action_get_new_profile_name( const FMAObjectAction
*action
)
689 gchar
*candidate
= NULL
;
690 guint last_allocated
;
692 g_return_val_if_fail( FMA_IS_OBJECT_ACTION( action
), NULL
);
694 if( !action
->private->dispose_has_run
){
696 last_allocated
= fma_object_get_last_allocated( action
);
698 for( i
= last_allocated
+ 1 ; !ok
; ++i
){
700 candidate
= g_strdup_printf( "profile-%d", i
);
702 if( !fma_object_get_item( action
, candidate
)){
704 fma_object_set_last_allocated( action
, i
);
714 /*g_debug( "returning candidate=%s", candidate );*/
719 * fma_object_action_attach_profile:
720 * @action: the #FMAObjectAction action to which the profile will be attached.
721 * @profile: the #FMAObjectProfile profile to be attached to @action.
723 * Adds a profile at the end of the list of profiles.
728 fma_object_action_attach_profile( FMAObjectAction
*action
, FMAObjectProfile
*profile
)
730 g_return_if_fail( FMA_IS_OBJECT_ACTION( action
));
731 g_return_if_fail( FMA_IS_OBJECT_PROFILE( profile
));
733 if( !action
->private->dispose_has_run
){
735 fma_object_append_item( action
, profile
);
736 fma_object_set_parent( profile
, action
);
741 * fma_object_action_set_last_version:
742 * @action: the #FMAObjectAction action to update.
744 * Set the version number of the @action to the last one.
749 fma_object_action_set_last_version( FMAObjectAction
*action
)
751 g_return_if_fail( FMA_IS_OBJECT_ACTION( action
));
753 if( !action
->private->dispose_has_run
){
755 fma_object_set_version( action
, "2.0" );