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>
38 #include <libnautilus-extension/nautilus-file-info.h>
40 #include <api/na-core-utils.h>
41 #include <api/na-iio-provider.h>
42 #include <api/na-ifactory-object.h>
43 #include <api/na-object-api.h>
45 #include "na-factory-provider.h"
46 #include "na-factory-object.h"
47 #include "na-selected-info.h"
48 #include "na-gnome-vfs-uri.h"
52 struct _NAObjectProfileClassPrivate
{
53 void *empty
; /* so that gcc -pedantic is happy */
56 /* private instance data
58 struct _NAObjectProfilePrivate
{
59 gboolean dispose_has_run
;
62 #define PROFILE_NAME_PREFIX "profile-"
64 extern NADataGroup profile_data_groups
[]; /* defined in na-item-profile-factory.c */
66 static NAObjectIdClass
*st_parent_class
= NULL
;
68 static GType
register_type( void );
69 static void class_init( NAObjectProfileClass
*klass
);
70 static void instance_init( GTypeInstance
*instance
, gpointer klass
);
71 static void instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
);
72 static void instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
);
73 static void instance_dispose( GObject
*object
);
74 static void instance_finalize( GObject
*object
);
76 static void object_copy( NAObject
*target
, const NAObject
*source
, gboolean recursive
);
77 static void object_dump( const NAObject
*object
);
78 static gboolean
object_is_valid( const NAObject
*object
);
80 static void ifactory_object_iface_init( NAIFactoryObjectInterface
*iface
);
81 static guint
ifactory_object_get_version( const NAIFactoryObject
*instance
);
82 static NADataGroup
*ifactory_object_get_groups( const NAIFactoryObject
*instance
);
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_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
convert_pre_v3_parameters( NAObjectProfile
*profile
);
91 static gboolean
convert_pre_v3_parameters_str( gchar
*str
);
92 static gboolean
convert_pre_v3_multiple( NAObjectProfile
*profile
);
93 static gboolean
convert_pre_v3_isfiledir( NAObjectProfile
*profile
);
94 static void read_done_ending( NAObjectProfile
*profile
);
95 static void split_path_parameters( NAObjectProfile
*profile
);
96 static gboolean
profile_is_valid( const NAObjectProfile
*profile
);
97 static gboolean
is_valid_path_parameters( const NAObjectProfile
*profile
);
99 static gchar
*object_id_new_id( const NAObjectId
*item
, const NAObjectId
*new_parent
);
102 na_object_profile_get_type( void )
104 static GType object_type
= 0;
107 object_type
= register_type();
110 return( object_type
);
114 register_type( void )
116 static const gchar
*thisfn
= "na_object_profile_register_type";
119 static GTypeInfo info
= {
120 sizeof( NAObjectProfileClass
),
121 ( GBaseInitFunc
) NULL
,
122 ( GBaseFinalizeFunc
) NULL
,
123 ( GClassInitFunc
) class_init
,
126 sizeof( NAObjectProfile
),
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_ID_TYPE
, "NAObjectProfile", &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( NAObjectProfileClass
*klass
)
157 static const gchar
*thisfn
= "na_object_profile_class_init";
158 GObjectClass
*object_class
;
159 NAObjectClass
*naobject_class
;
160 NAObjectIdClass
*naobjectid_class
;
162 g_debug( "%s: klass=%p", thisfn
, ( void * ) klass
);
164 st_parent_class
= g_type_class_peek_parent( klass
);
166 object_class
= G_OBJECT_CLASS( klass
);
167 object_class
->set_property
= instance_set_property
;
168 object_class
->get_property
= instance_get_property
;
169 object_class
->dispose
= instance_dispose
;
170 object_class
->finalize
= instance_finalize
;
172 naobject_class
= NA_OBJECT_CLASS( klass
);
173 naobject_class
->dump
= object_dump
;
174 naobject_class
->copy
= object_copy
;
175 naobject_class
->is_valid
= object_is_valid
;
177 naobjectid_class
= NA_OBJECT_ID_CLASS( klass
);
178 naobjectid_class
->new_id
= object_id_new_id
;
180 klass
->private = g_new0( NAObjectProfileClassPrivate
, 1 );
182 na_factory_object_define_properties( object_class
, profile_data_groups
);
186 instance_init( GTypeInstance
*instance
, gpointer klass
)
188 static const gchar
*thisfn
= "na_object_profile_instance_init";
189 NAObjectProfile
*self
;
191 g_return_if_fail( NA_IS_OBJECT_PROFILE( instance
));
193 self
= NA_OBJECT_PROFILE( instance
);
195 g_debug( "%s: instance=%p (%s), klass=%p",
196 thisfn
, ( void * ) instance
, G_OBJECT_TYPE_NAME( instance
), ( void * ) klass
);
198 self
->private = g_new0( NAObjectProfilePrivate
, 1 );
200 self
->private->dispose_has_run
= FALSE
;
204 instance_get_property( GObject
*object
, guint property_id
, GValue
*value
, GParamSpec
*spec
)
206 g_return_if_fail( NA_IS_OBJECT_PROFILE( object
));
207 g_return_if_fail( NA_IS_IFACTORY_OBJECT( object
));
209 if( !NA_OBJECT_PROFILE( object
)->private->dispose_has_run
){
211 na_factory_object_get_as_value( NA_IFACTORY_OBJECT( object
), g_quark_to_string( property_id
), value
);
216 instance_set_property( GObject
*object
, guint property_id
, const GValue
*value
, GParamSpec
*spec
)
218 g_return_if_fail( NA_IS_OBJECT_PROFILE( object
));
219 g_return_if_fail( NA_IS_IFACTORY_OBJECT( object
));
221 if( !NA_OBJECT_PROFILE( object
)->private->dispose_has_run
){
223 na_factory_object_set_from_value( NA_IFACTORY_OBJECT( object
), g_quark_to_string( property_id
), value
);
228 instance_dispose( GObject
*object
)
230 static const gchar
*thisfn
= "na_object_profile_instance_dispose";
231 NAObjectProfile
*self
;
233 g_return_if_fail( NA_IS_OBJECT_PROFILE( object
));
235 self
= NA_OBJECT_PROFILE( object
);
237 if( !self
->private->dispose_has_run
){
239 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
241 self
->private->dispose_has_run
= TRUE
;
243 /* chain up to the parent class */
244 if( G_OBJECT_CLASS( st_parent_class
)->dispose
){
245 G_OBJECT_CLASS( st_parent_class
)->dispose( object
);
251 instance_finalize( GObject
*object
)
253 static const gchar
*thisfn
= "na_object_profile_instance_finalize";
254 NAObjectProfile
*self
;
256 g_return_if_fail( NA_IS_OBJECT_PROFILE( object
));
258 self
= NA_OBJECT_PROFILE( object
);
260 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
262 g_free( self
->private );
264 /* chain call to parent class */
265 if( G_OBJECT_CLASS( st_parent_class
)->finalize
){
266 G_OBJECT_CLASS( st_parent_class
)->finalize( object
);
271 object_copy( NAObject
*target
, const NAObject
*source
, gboolean recursive
)
273 g_return_if_fail( NA_IS_OBJECT_PROFILE( target
));
274 g_return_if_fail( NA_IS_OBJECT_PROFILE( source
));
276 if( !NA_OBJECT_PROFILE( target
)->private->dispose_has_run
&&
277 !NA_OBJECT_PROFILE( source
)->private->dispose_has_run
){
279 na_factory_object_copy( NA_IFACTORY_OBJECT( target
), NA_IFACTORY_OBJECT( source
));
284 object_dump( const NAObject
*object
)
286 static const char *thisfn
= "na_object_profile_object_dump";
287 NAObjectProfile
*self
;
289 g_return_if_fail( NA_IS_OBJECT_PROFILE( object
));
291 self
= NA_OBJECT_PROFILE( object
);
293 if( !self
->private->dispose_has_run
){
294 g_debug( "%s: object=%p (%s, ref_count=%d)", thisfn
,
295 ( void * ) object
, G_OBJECT_TYPE_NAME( object
), G_OBJECT( object
)->ref_count
);
297 /* chain up to the parent class */
298 if( NA_OBJECT_CLASS( st_parent_class
)->dump
){
299 NA_OBJECT_CLASS( st_parent_class
)->dump( object
);
302 g_debug( "+- end of dump" );
307 object_is_valid( const NAObject
*object
)
309 g_return_val_if_fail( NA_IS_OBJECT_PROFILE( object
), FALSE
);
311 return( profile_is_valid( NA_OBJECT_PROFILE( object
)));
315 ifactory_object_iface_init( NAIFactoryObjectInterface
*iface
)
317 static const gchar
*thisfn
= "na_object_profile_ifactory_object_iface_init";
319 g_debug( "%s: iface=%p", thisfn
, ( void * ) iface
);
321 iface
->get_version
= ifactory_object_get_version
;
322 iface
->get_groups
= ifactory_object_get_groups
;
323 iface
->is_valid
= ifactory_object_is_valid
;
324 iface
->read_done
= ifactory_object_read_done
;
325 iface
->write_done
= ifactory_object_write_done
;
329 ifactory_object_get_version( const NAIFactoryObject
*instance
)
335 ifactory_object_get_groups( const NAIFactoryObject
*instance
)
337 return( profile_data_groups
);
341 ifactory_object_is_valid( const NAIFactoryObject
*object
)
343 static const gchar
*thisfn
= "na_object_profile_ifactory_object_is_valid";
345 g_return_val_if_fail( NA_IS_OBJECT_PROFILE( object
), FALSE
);
347 g_debug( "%s: object=%p (%s)", thisfn
, ( void * ) object
, G_OBJECT_TYPE_NAME( object
));
349 return( profile_is_valid( NA_OBJECT_PROFILE( object
)));
353 ifactory_object_read_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*reader
, void *reader_data
, GSList
**messages
)
355 static const gchar
*thisfn
= "na_object_profile_ifactory_object_read_done";
356 NAObjectAction
*action
;
359 g_debug( "%s: profile=%p", thisfn
, ( void * ) instance
);
361 /* converts pre-v3 data
363 action
= NA_OBJECT_ACTION( na_object_get_parent( instance
));
364 iversion
= na_object_get_iversion( action
);
365 g_debug( "%s: iversion=%d", thisfn
, iversion
);
368 na_object_profile_convert_v2_to_last( NA_OBJECT_PROFILE( instance
));
371 read_done_ending( NA_OBJECT_PROFILE( instance
));
375 ifactory_object_write_done( NAIFactoryObject
*instance
, const NAIFactoryProvider
*writer
, void *writer_data
, GSList
**messages
)
377 return( NA_IIO_PROVIDER_CODE_OK
);
381 icontext_iface_init( NAIContextInterface
*iface
)
383 static const gchar
*thisfn
= "na_object_profile_icontext_iface_init";
385 g_debug( "%s: iface=%p", thisfn
, ( void * ) iface
);
387 iface
->is_candidate
= icontext_is_candidate
;
391 icontext_is_candidate( NAIContext
*object
, guint target
, GList
*selection
)
397 * starting wih v3, parameters are relabeled
398 * pre-v3 parameters post-v3 parameters
399 * ---------------------------- -----------------------------------
400 * %b: (first) basename (was %f)
401 * %B: list of basenames (was %m)
403 * %d: (first) base directory ................... (unchanged)
404 * %D: list of base dir (new)
405 * %f: (first) filename -> %b %f: (first) pathname (new)
406 * %F: list of pathnames (was %M)
407 * %h: (first) hostname ................... (unchanged)
408 * %m: list of basenames -> %B %m: (first) mimetype (new)
409 * %M: list of pathnames -> %F %M: list of mimetypes (new)
410 * %n: (first) username (was %U)
411 * %p: (first) port number ................... (unchanged)
412 * %R: list of URIs -> %U - (removed)
413 * %s: (first) scheme ................... (unchanged)
414 * %u: (first) URI ................... (unchanged)
415 * %U: (first) username -> %n %U: list of URIs (was %R)
416 * %w: (first) basename w/o ext. (new)
417 * %W: list of basenames w/o ext. (new)
418 * %x: (first) extension (new)
419 * %X: list of extensions (new)
420 * %%: % ................... (unchanged)
423 * - substitute %f with %b, and as a special case %d/%f -> %f
424 * - substitute %m with %B
425 * - substitute %M with %F
426 * - substitute %R with %U
427 * - substitute %U with %n
429 * Note that pre-v3 items only have parameters in the command and path fields.
430 * Are only located in 'profile' objects.
431 * Are only found in GConf or XML providers, as .desktop files have been
432 * simultaneously introduced.
434 * As a recall of the dynamics of the reading when loading an action:
435 * - na_object_action_read_done: set action defaults
436 * - nagp_reader_read_done: read profiles
437 * > nagp_reader_read_start: attach profile to its parent
438 * > na_object_profile_read_done: convert old parameters
440 * So, when converting v2 to v3 parameters in a v2 profile,
441 * action already has its default values (including iversion=3)
444 convert_pre_v3_parameters( NAObjectProfile
*profile
)
446 static const gchar
*thisfn
= "na_object_profile_convert_pre_v3_parameters";
447 gboolean path_changed
, parms_changed
;
450 gchar
*path
= na_object_get_path( profile
);
451 before
= g_strdup( path
);
452 path_changed
= convert_pre_v3_parameters_str( path
);
454 na_object_set_path( profile
, path
);
455 g_debug( "%s: path=%s changed to %s", thisfn
, before
, path
);
460 gchar
*parms
= na_object_get_parameters( profile
);
461 before
= g_strdup( parms
);
462 parms_changed
= convert_pre_v3_parameters_str( parms
);
464 na_object_set_parameters( profile
, parms
);
465 g_debug( "%s: parameters=%s changed to %s", thisfn
, before
, parms
);
470 return( path_changed
|| parms_changed
);
474 convert_pre_v3_parameters_str( gchar
*str
)
480 while( iter
!= NULL
&&
481 strlen( iter
) > 0 &&
482 ( iter
= g_strstr_len( iter
, strlen( iter
), "%" )) != NULL
){
486 /* as a special optimization case, "%d/%f" parameters
487 * may be favourably converted to just "%f" instead of "%d/%b"
490 if( !strncmp( iter
, "%d/%f", 5 )){
491 strncpy( iter
, iter
+3, strlen( iter
));
496 /* %f (first filename) becomes %b
503 /* %m (list of basenames) becomes %B
510 /* %M (list of filenames) becomes %F
517 /* %R (list of URIs) becomes %U
524 /* %U ((first) username) becomes %n
539 * default changes from accept_multiple=false
540 * to selection_count>0
543 convert_pre_v3_multiple( NAObjectProfile
*profile
)
545 static const gchar
*thisfn
= "na_object_profile_convert_pre_v3_multiple";
546 gboolean accept_multiple
;
547 gchar
*selection_count
;
549 accept_multiple
= na_object_is_multiple( profile
);
550 selection_count
= g_strdup( accept_multiple
? ">0" : "=1" );
551 na_object_set_selection_count( profile
, selection_count
);
552 g_debug( "%s: accept_multiple=%s changed to selection_count= %s",
553 thisfn
, accept_multiple
? "True":"False", selection_count
);
554 g_free( selection_count
);
560 * we may have file=true and dir=false -> only files -> all/allfiles
561 * file=false and dir=true -> only dirs -> inode/directory
562 * file=true and dir=true -> both files and dirs -> all/all
564 * we try to replace this with the corresponding mimetype, but only if
565 * current mimetype is '*' (or * / * or all/all).
567 * note that inode/directory is actually the mimetype provided by Nautilus;
568 * contrarily all/allfiles mimetype has to be checked separately.
571 convert_pre_v3_isfiledir( NAObjectProfile
*profile
)
573 static const gchar
*thisfn
= "na_object_profile_convert_pre_v3_isfiledir";
574 gboolean is_all_mimetypes
;
576 gboolean isfile
, isdir
;
579 gchar
*before_str
, *after_str
;
583 na_object_check_mimetypes( profile
);
584 is_all_mimetypes
= na_object_get_all_mimetypes( profile
);
585 g_debug( "%s: is_all_mimetypes=%s", thisfn
, is_all_mimetypes
? "True":"False" );
587 if( is_all_mimetypes
){
590 before_list
= na_object_get_mimetypes( profile
);
592 isfile
= na_object_is_file( profile
);
593 isdir
= na_object_is_dir( profile
);
597 /* both file and dir -> do not modify mimetypes
603 mimetypes
= g_slist_prepend( NULL
, g_strdup( "all/allfiles" ));
609 mimetypes
= g_slist_prepend( NULL
, g_strdup( "inode/directory" ));
611 /* not files nor dir: this is an invalid case -> do not modify
614 g_warning( "%s: is_dir=False, is_file=False is invalid", thisfn
);
620 na_object_set_mimetypes( profile
, mimetypes
);
622 before_str
= na_core_utils_slist_join_at_end( before_list
, ";" );
623 after_str
= na_core_utils_slist_join_at_end( mimetypes
, ";" );
624 g_debug( "%s; mimetypes=[%s] changed to [%s]", thisfn
, before_str
, after_str
);
626 g_free( before_str
);
629 na_core_utils_slist_free( mimetypes
);
630 na_core_utils_slist_free( before_list
);
637 read_done_ending( NAObjectProfile
*profile
)
639 /* split path+parameters
640 * not done in io-desktop because some actions may have all arguments in path
642 split_path_parameters( profile
);
644 /* prepare the context after the reading
646 na_icontext_read_done( NA_ICONTEXT( profile
));
648 /* last, set other action defaults
650 na_factory_object_set_defaults( NA_IFACTORY_OBJECT( profile
));
654 split_path_parameters( NAObjectProfile
*profile
)
656 gchar
*path
, *parameters
;
659 path
= na_object_get_path( profile
);
660 parameters
= na_object_get_parameters( profile
);
661 exec
= g_strstrip( g_strdup_printf( "%s %s", path
? path
: "", parameters
? parameters
: "" ));
662 g_free( parameters
);
665 na_core_utils_str_split_first_word( exec
, &path
, ¶meters
);
668 na_object_set_path( profile
, path
);
669 na_object_set_parameters( profile
, parameters
);
670 g_free( parameters
);
675 profile_is_valid( const NAObjectProfile
*profile
)
681 if( !profile
->private->dispose_has_run
){
684 is_valid_path_parameters( profile
) &&
685 na_icontext_is_valid( NA_ICONTEXT( profile
));
692 * historical behavior was to not check path nor parameters at all
693 * 2.29.x serie, and up to 2.30.0, have tried to check an actual executable path
694 * but most of already actions only used a command, relying on the PATH env variable
695 * so, starting with 2.30.1, we only check for non empty path+parameters
698 is_valid_path_parameters( const NAObjectProfile
*profile
)
701 gchar
*path
, *parameters
;
704 path
= na_object_get_path( profile
);
705 parameters
= na_object_get_parameters( profile
);
707 command
= g_strdup_printf( "%s %s", path
, parameters
);
708 g_strstrip( command
);
710 valid
= g_utf8_strlen( command
, -1 ) > 0;
713 g_free( parameters
);
717 na_object_debug_invalid( profile
, "command" );
724 * new_parent is specifically set to be able to allocate a new id for
725 * the current profile into the target parent
728 object_id_new_id( const NAObjectId
*item
, const NAObjectId
*new_parent
)
732 g_return_val_if_fail( NA_IS_OBJECT_PROFILE( item
), NULL
);
733 g_return_val_if_fail( !new_parent
|| NA_IS_OBJECT_ACTION( new_parent
), NULL
);
735 if( !NA_OBJECT_PROFILE( item
)->private->dispose_has_run
){
738 id
= na_object_action_get_new_profile_name( NA_OBJECT_ACTION( new_parent
));
746 * na_object_profile_new:
748 * Allocates a new profile.
750 * Returns: the newly allocated #NAObjectProfile profile.
755 na_object_profile_new( void )
757 NAObjectProfile
*profile
;
759 profile
= g_object_new( NA_OBJECT_PROFILE_TYPE
, NULL
);
765 * na_object_profile_new_with_defaults:
767 * Allocates a new profile, and set default values.
769 * Returns: the newly allocated #NAObjectProfile profile.
774 na_object_profile_new_with_defaults( void )
776 NAObjectProfile
*profile
= na_object_profile_new();
777 na_object_set_id( profile
, "profile-zero" );
778 /* i18n: label for the default profile */
779 na_object_set_label( profile
, _( "Default profile" ));
780 na_factory_object_set_defaults( NA_IFACTORY_OBJECT( profile
));
786 * na_object_profile_convert_v2_to_last:
787 * @profile: the #NAObjectProfile profile to be converted.
789 * Converts to v3 a @profile which has just been created from a pre-v2 action.
794 na_object_profile_convert_v2_to_last( NAObjectProfile
*profile
)
796 NAObjectAction
*action
;
798 gboolean parms_converted
, multiple_converted
, isfiledir_converted
;
800 g_return_if_fail( NA_IS_OBJECT_PROFILE( profile
));
802 action
= NA_OBJECT_ACTION( na_object_get_parent( profile
));
803 iversion
= na_object_get_iversion( action
);
804 g_return_if_fail( iversion
< 3 );
806 parms_converted
= convert_pre_v3_parameters( profile
);
807 multiple_converted
= convert_pre_v3_multiple( profile
);
808 isfiledir_converted
= convert_pre_v3_isfiledir( profile
);
810 if( parms_converted
|| multiple_converted
|| isfiledir_converted
){
811 na_object_set_iversion( action
, 3 );
814 read_done_ending( profile
);