Define new 'pivot-prop-loadable' property
[nautilus-actions.git] / src / core / na-object-action.c
blob2e6984926c98aa928f30331619582439705d3d59
1 /*
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.
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 <glib/gi18n.h>
36 #include <libintl.h>
37 #include <string.h>
38 #include <stdlib.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"
47 /* private class data
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 );
100 GType
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 );
113 static GType
114 register_type( void )
116 static const gchar *thisfn = "na_object_action_register_type";
117 GType type;
119 static GTypeInfo info = {
120 sizeof( NAObjectActionClass ),
121 NULL,
122 NULL,
123 ( GClassInitFunc ) class_init,
124 NULL,
125 NULL,
126 sizeof( NAObjectAction ),
128 ( GInstanceInitFunc ) instance_init
131 static const GInterfaceInfo icontext_iface_info = {
132 ( GInterfaceInitFunc ) icontext_iface_init,
133 NULL,
134 NULL
137 static const GInterfaceInfo ifactory_object_iface_info = {
138 ( GInterfaceInitFunc ) ifactory_object_iface_init,
139 NULL,
140 NULL
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 );
151 return( type );
154 static void
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 );
182 static void
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 );
198 static void
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 );
210 static void
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 );
222 static void
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 );
244 static void
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 );
264 static void
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 ));
277 static void
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" );
300 static gboolean
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 )));
308 static void
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;
317 iface->copy = NULL;
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;
326 static guint
327 ifactory_object_get_version( const NAIFactoryObject *instance )
329 return( 1 );
332 static NADataGroup *
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.
345 static gboolean
346 ifactory_object_are_equal( const NAIFactoryObject *a, const NAIFactoryObject *b )
348 GList *it;
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 )){
353 return( FALSE );
358 return( TRUE );
361 static gboolean
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)
374 static void
375 ifactory_object_read_done( NAIFactoryObject *instance, const NAIFactoryProvider *reader, void *reader_data, GSList **messages )
377 guint iversion;
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 );
387 if( iversion < 2 ){
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 );
407 static guint
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 );
415 static guint
416 ifactory_object_write_done( NAIFactoryObject *instance, const NAIFactoryProvider *writer, void *writer_data, GSList **messages )
418 guint code;
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 );
424 return( code );
427 static void
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;
437 static gboolean
438 icontext_is_candidate( NAIContext *object, guint target, GList *selection )
440 return( TRUE );
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
454 static gboolean
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";
458 GList *to_move;
459 NADataDef *def;
460 NADataBoxed *boxed;
461 GList *ibox;
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
467 to_move = NULL;
468 def = data_def_action_v1;
470 while( def->name ){
471 boxed = na_ifactory_object_get_data_boxed( instance , def->name );
472 if( boxed ){
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 );
477 def++;
480 if( !to_move ){
481 return( FALSE );
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 );
491 if( to_move ){
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 );
500 return( TRUE );
504 * if toolbar-same-label is true, then ensure that this is actually true
506 static void
507 read_done_deals_with_toolbar_label( NAIFactoryObject *instance )
509 gchar *toolbar_label;
510 gchar *action_label;
511 gboolean same_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 );
520 } else {
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
533 static guint
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";
537 guint code;
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 ));
547 if( profile ){
548 code = na_ifactory_provider_write_item( writer, writer_data, NA_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 object_object_is_valid( const NAObjectAction *action )
561 gboolean is_valid;
562 GList *profiles, *ip;
563 gint valid_profiles;
565 is_valid = FALSE;
567 if( !action->private->dispose_has_run ){
569 is_valid = TRUE;
571 if( is_valid ){
572 if( na_object_is_target_toolbar( action )){
573 is_valid = is_valid_toolbar_label( action );
577 if( is_valid ){
578 if( na_object_is_target_selection( action )){
579 is_valid = is_valid_label( action );
583 if( is_valid ){
584 valid_profiles = 0;
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 )){
588 valid_profiles += 1;
591 is_valid = ( valid_profiles > 0 );
592 if( !is_valid ){
593 na_object_debug_invalid( action, "no valid profile" );
598 return( is_valid );
601 static gboolean
602 is_valid_label( const NAObjectAction *action )
604 gboolean is_valid;
605 gchar *label;
607 label = na_object_get_label( action );
608 is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
609 g_free( label );
611 if( !is_valid ){
612 na_object_debug_invalid( action, "label" );
615 return( is_valid );
618 static gboolean
619 is_valid_toolbar_label( const NAObjectAction *action )
621 gboolean is_valid;
622 gchar *label;
624 label = na_object_get_toolbar_label( action );
625 is_valid = ( label && g_utf8_strlen( label, -1 ) > 0 );
626 g_free( label );
628 if( !is_valid ){
629 na_object_debug_invalid( action, "toolbar-label" );
632 return( is_valid );
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.
645 * Since: 2.30
647 NAObjectAction *
648 na_object_action_new( void )
650 NAObjectAction *action;
652 action = g_object_new( NA_OBJECT_ACTION_TYPE, NULL );
654 return( action );
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.
664 * Since: 2.30
666 NAObjectAction *
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 );
676 return( action );
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.
687 * Since: 2.30
689 NAObjectAction *
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 );
704 return( action );
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
723 * the caller.
725 * Since: 2.30
727 gchar *
728 na_object_action_get_new_profile_name( const NAObjectAction *action )
730 int i;
731 gboolean ok = FALSE;
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 ){
742 g_free( candidate );
743 candidate = g_strdup_printf( "profile-%d", i );
745 if( !na_object_get_item( action, candidate )){
746 ok = TRUE;
747 na_object_set_last_allocated( action, i );
751 if( !ok ){
752 g_free( candidate );
753 candidate = NULL;
757 /*g_debug( "returning candidate=%s", candidate );*/
758 return( 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.
768 * Since: 2.30
770 void
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.
789 * Since: 2.30
791 void
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" );