README: add deprecation notice
[nautilus-actions.git] / src / core / fma-object-action.c
blob07d1f00ba885eb29ecb1d10f5e2c96403d0aa697
1 /*
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/>.
23 * Authors:
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)
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
34 #include <glib/gi18n.h>
35 #include <libintl.h>
36 #include <string.h>
37 #include <stdlib.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"
46 /* private class data
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 );
96 GType
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 );
109 static GType
110 register_type( void )
112 static const gchar *thisfn = "fma_object_action_register_type";
113 GType type;
115 static GTypeInfo info = {
116 sizeof( FMAObjectActionClass ),
117 NULL,
118 NULL,
119 ( GClassInitFunc ) class_init,
120 NULL,
121 NULL,
122 sizeof( FMAObjectAction ),
124 ( GInstanceInitFunc ) instance_init
127 static const GInterfaceInfo icontext_iface_info = {
128 ( GInterfaceInitFunc ) icontext_iface_init,
129 NULL,
130 NULL
133 static const GInterfaceInfo ifactory_object_iface_info = {
134 ( GInterfaceInitFunc ) ifactory_object_iface_init,
135 NULL,
136 NULL
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 );
147 return( type );
150 static void
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 );
177 static void
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 );
193 static void
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 );
205 static void
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 );
217 static void
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 );
239 static void
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 );
259 static void
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.
289 static gboolean
290 object_are_equal( const FMAObject *a, const FMAObject *b )
292 static const gchar *thisfn = "fma_object_action_object_are_equal";
293 GList *it;
294 gboolean 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 )){
300 return( FALSE );
304 are_equal = TRUE;
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 );
311 return( are_equal );
314 static gboolean
315 object_is_valid( const FMAObject *object )
317 static const gchar *thisfn = "fma_object_action_object_is_valid";
318 gboolean is_valid;
319 FMAObjectAction *action;
321 g_return_val_if_fail( FMA_IS_OBJECT_ACTION( object ), FALSE );
323 is_valid = 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 ));
329 is_valid = TRUE;
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 );
339 if( !is_valid ){
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 );
349 return( is_valid );
352 static void
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;
366 static guint
367 ifactory_object_get_version( const FMAIFactoryObject *instance )
369 return( 1 );
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)
383 static void
384 ifactory_object_read_done( FMAIFactoryObject *instance, const FMAIFactoryProvider *reader, void *reader_data, GSList **messages )
386 guint iversion;
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 );
397 if( iversion < 2 ){
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 );
416 static guint
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 );
424 static guint
425 ifactory_object_write_done( FMAIFactoryObject *instance, const FMAIFactoryProvider *writer, void *writer_data, GSList **messages )
427 guint code;
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 );
433 return( code );
436 static void
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;
446 static gboolean
447 icontext_is_candidate( FMAIContext *object, guint target, GList *selection )
449 return( TRUE );
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";
466 GList *to_move;
467 FMADataDef *def;
468 FMADataBoxed *boxed;
469 GList *ibox;
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
475 to_move = NULL;
476 def = data_def_action_v1;
478 while( def->name ){
479 boxed = fma_ifactory_object_get_data_boxed( instance, def->name );
480 if( boxed ){
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 );
485 def++;
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 ));
500 return( profile );
504 * if toolbar-same-label is true, then ensure that this is actually true
506 static void
507 read_done_deals_with_toolbar_label( FMAIFactoryObject *instance )
509 gchar *toolbar_label;
510 gchar *action_label;
511 gboolean same_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 );
520 } else {
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
533 static guint
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";
537 guint code;
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 ));
547 if( profile ){
548 code = fma_ifactory_provider_write_item( writer, writer_data, FMA_IFACTORY_OBJECT( profile ), messages );
550 } else {
551 g_warning( "%s: profile not found: %s", thisfn, ( const gchar * ) ic->data );
555 return( code );
558 static gboolean
559 is_valid_label( const FMAObjectAction *action )
561 gboolean is_valid;
562 gchar *label;
564 label = fma_object_get_label( action );
565 is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
566 g_free( label );
568 if( !is_valid ){
569 fma_object_debug_invalid( action, "label" );
572 return( is_valid );
575 static gboolean
576 is_valid_toolbar_label( const FMAObjectAction *action )
578 gboolean is_valid;
579 gchar *label;
581 label = fma_object_get_toolbar_label( action );
582 is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
583 g_free( label );
585 if( !is_valid ){
586 fma_object_debug_invalid( action, "toolbar-label" );
589 return( is_valid );
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.
602 * Since: 2.30
604 FMAObjectAction *
605 fma_object_action_new( void )
607 FMAObjectAction *action;
609 action = g_object_new( FMA_TYPE_OBJECT_ACTION, NULL );
611 return( action );
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.
621 * Since: 2.30
623 FMAObjectAction *
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 );
633 return( action );
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.
644 * Since: 2.30
646 FMAObjectAction *
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 );
661 return( action );
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
680 * the caller.
682 * Since: 2.30
684 gchar *
685 fma_object_action_get_new_profile_name( const FMAObjectAction *action )
687 int i;
688 gboolean ok = FALSE;
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 ){
699 g_free( candidate );
700 candidate = g_strdup_printf( "profile-%d", i );
702 if( !fma_object_get_item( action, candidate )){
703 ok = TRUE;
704 fma_object_set_last_allocated( action, i );
708 if( !ok ){
709 g_free( candidate );
710 candidate = NULL;
714 /*g_debug( "returning candidate=%s", candidate );*/
715 return( 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.
725 * Since: 2.30
727 void
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.
746 * Since: 2.30
748 void
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" );