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)
38 #include <api/na-core-utils.h>
39 #include <api/na-data-types.h>
40 #include <api/na-ifactory-object-data.h>
41 #include <api/na-ifactory-provider.h>
42 #include <api/na-object-api.h>
44 #include "nadp-desktop-provider.h"
45 #include "nadp-keys.h"
46 #include "nadp-reader.h"
47 #include "nadp-utils.h"
48 #include "nadp-xdg-dirs.h"
56 /* the structure passed as reader data to NAIFactoryObject
60 NAObjectAction
*action
;
64 static GList
*get_list_of_desktop_paths( NadpDesktopProvider
*provider
, GSList
**mesages
);
65 static void get_list_of_desktop_files( const NadpDesktopProvider
*provider
, GList
**files
, const gchar
*dir
, GSList
**messages
);
66 static gboolean
is_already_loaded( const NadpDesktopProvider
*provider
, GList
*files
, const gchar
*desktop_id
);
67 static GList
*desktop_path_from_id( const NadpDesktopProvider
*provider
, GList
*files
, const gchar
*dir
, const gchar
*id
);
68 static NAIFactoryObject
*item_from_desktop_path( const NadpDesktopProvider
*provider
, DesktopPath
*dps
, GSList
**messages
);
69 static NAIFactoryObject
*item_from_desktop_file( const NadpDesktopProvider
*provider
, NadpDesktopFile
*ndf
, GSList
**messages
);
70 static void desktop_weak_notify( NadpDesktopFile
*ndf
, GObject
*item
);
71 static void free_desktop_paths( GList
*paths
);
73 static void read_start_read_subitems_key( const NAIFactoryProvider
*provider
, NAObjectItem
*item
, NadpReaderData
*reader_data
, GSList
**messages
);
74 static void read_start_profile_attach_profile( const NAIFactoryProvider
*provider
, NAObjectProfile
*profile
, NadpReaderData
*reader_data
, GSList
**messages
);
76 static gboolean
read_done_item_is_writable( const NAIFactoryProvider
*provider
, NAObjectItem
*item
, NadpReaderData
*reader_data
, GSList
**messages
);
77 static void read_done_action_read_profiles( const NAIFactoryProvider
*provider
, NAObjectAction
*action
, NadpReaderData
*data
, GSList
**messages
);
78 static void read_done_action_load_profile( const NAIFactoryProvider
*provider
, NadpReaderData
*reader_data
, const gchar
*profile_id
, GSList
**messages
);
81 * Returns an unordered list of NAIFactoryObject-derived objects
83 * This is implementation of NAIIOProvider::read_items method
86 nadp_iio_provider_read_items( const NAIIOProvider
*provider
, GSList
**messages
)
88 static const gchar
*thisfn
= "nadp_iio_provider_read_items";
90 GList
*desktop_paths
, *ip
;
91 NAIFactoryObject
*item
;
93 g_debug( "%s: provider=%p (%s), messages=%p",
94 thisfn
, ( void * ) provider
, G_OBJECT_TYPE_NAME( provider
), ( void * ) messages
);
96 g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider
), NULL
);
99 nadp_desktop_provider_release_monitors( NADP_DESKTOP_PROVIDER( provider
));
101 desktop_paths
= get_list_of_desktop_paths( NADP_DESKTOP_PROVIDER( provider
), messages
);
102 for( ip
= desktop_paths
; ip
; ip
= ip
->next
){
104 item
= item_from_desktop_path( NADP_DESKTOP_PROVIDER( provider
), ( DesktopPath
* ) ip
->data
, messages
);
107 items
= g_list_prepend( items
, item
);
108 na_object_dump( item
);
112 free_desktop_paths( desktop_paths
);
114 g_debug( "%s: count=%d", thisfn
, g_list_length( items
));
119 * returns a list of DesktopPath items
121 * we get the ordered list of XDG_DATA_DIRS, and the ordered list of
122 * subdirs to add; then for each item of each list, we search for
123 * .desktop files in the resulted built path
125 * the returned list is so a list of DesktopPath struct, in
126 * the ordered of preference (most preferred first)
129 get_list_of_desktop_paths( NadpDesktopProvider
*provider
, GSList
**messages
)
132 GSList
*xdg_dirs
, *idir
;
133 GSList
*subdirs
, *isub
;
137 xdg_dirs
= nadp_xdg_dirs_get_data_dirs();
138 subdirs
= na_core_utils_slist_from_split( NADP_DESKTOP_PROVIDER_SUBDIRS
, G_SEARCHPATH_SEPARATOR_S
);
140 /* explore each directory from XDG_DATA_DIRS
142 for( idir
= xdg_dirs
; idir
; idir
= idir
->next
){
144 /* explore each N-A candidate subdirectory for each XDG dir
146 for( isub
= subdirs
; isub
; isub
= isub
->next
){
148 dir
= g_build_filename(( gchar
* ) idir
->data
, ( gchar
* ) isub
->data
, NULL
);
149 nadp_desktop_provider_add_monitor( provider
, dir
);
150 get_list_of_desktop_files( provider
, &files
, dir
, messages
);
155 na_core_utils_slist_free( subdirs
);
156 na_core_utils_slist_free( xdg_dirs
);
162 * scans the directory for .desktop files
163 * only adds to the list those which have not been yet loaded
166 get_list_of_desktop_files( const NadpDesktopProvider
*provider
, GList
**files
, const gchar
*dir
, GSList
**messages
)
168 static const gchar
*thisfn
= "nadp_reader_get_list_of_desktop_files";
174 g_debug( "%s: provider=%p, files=%p (count=%d), dir=%s, messages=%p",
175 thisfn
, ( void * ) provider
, ( void * ) files
, g_list_length( *files
), dir
, ( void * ) messages
);
180 /* do not warn when the directory just doesn't exist
182 if( g_file_test( dir
, G_FILE_TEST_IS_DIR
)){
183 dir_handle
= g_dir_open( dir
, 0, &error
);
185 g_warning( "%s: %s: %s", thisfn
, dir
, error
->message
);
186 g_error_free( error
);
187 goto close_dir_handle
;
190 g_debug( "%s: %s: directory doesn't exist", thisfn
, dir
);
194 while(( name
= g_dir_read_name( dir_handle
))){
195 if( g_str_has_suffix( name
, NADP_DESKTOP_FILE_SUFFIX
)){
196 desktop_id
= na_core_utils_str_remove_suffix( name
, NADP_DESKTOP_FILE_SUFFIX
);
197 if( !is_already_loaded( provider
, *files
, desktop_id
)){
198 *files
= desktop_path_from_id( provider
, *files
, dir
, desktop_id
);
200 g_free( desktop_id
);
207 g_dir_close( dir_handle
);
212 is_already_loaded( const NadpDesktopProvider
*provider
, GList
*files
, const gchar
*desktop_id
)
219 for( ip
= files
; ip
&& !found
; ip
= ip
->next
){
220 dps
= ( DesktopPath
* ) ip
->data
;
221 if( !g_ascii_strcasecmp( dps
->id
, desktop_id
)){
230 desktop_path_from_id( const NadpDesktopProvider
*provider
, GList
*files
, const gchar
*dir
, const gchar
*id
)
236 dps
= g_new0( DesktopPath
, 1 );
238 bname
= g_strdup_printf( "%s%s", id
, NADP_DESKTOP_FILE_SUFFIX
);
239 dps
->path
= g_build_filename( dir
, bname
, NULL
);
242 dps
->id
= g_strdup( id
);
244 list
= g_list_prepend( files
, dps
);
250 * Returns a newly allocated NAIFactoryObject-derived object, initialized
251 * from the .desktop file pointed to by DesktopPath struct
253 static NAIFactoryObject
*
254 item_from_desktop_path( const NadpDesktopProvider
*provider
, DesktopPath
*dps
, GSList
**messages
)
256 NadpDesktopFile
*ndf
;
258 ndf
= nadp_desktop_file_new_from_path( dps
->path
);
263 return( item_from_desktop_file( provider
, ndf
, messages
));
267 * Returns a newly allocated NAIFactoryObject-derived object, initialized
268 * from the .desktop file
270 static NAIFactoryObject
*
271 item_from_desktop_file( const NadpDesktopProvider
*provider
, NadpDesktopFile
*ndf
, GSList
**messages
)
273 static const gchar
*thisfn
= "nadp_reader_item_from_desktop_file";
274 NAIFactoryObject
*item
;
276 NadpReaderData
*reader_data
;
280 type
= nadp_desktop_file_get_file_type( ndf
);
282 if( !strcmp( type
, NADP_VALUE_TYPE_ACTION
)){
283 item
= NA_IFACTORY_OBJECT( na_object_action_new());
285 } else if( !strcmp( type
, NADP_VALUE_TYPE_MENU
)){
286 item
= NA_IFACTORY_OBJECT( na_object_menu_new());
289 g_warning( "%s: unknown type=%s", thisfn
, type
);
293 id
= nadp_desktop_file_get_id( ndf
);
294 na_object_set_id( item
, id
);
297 reader_data
= g_new0( NadpReaderData
, 1 );
298 reader_data
->ndf
= ndf
;
300 na_ifactory_provider_read_item( NA_IFACTORY_PROVIDER( provider
), reader_data
, item
, messages
);
302 na_object_set_provider_data( item
, ndf
);
303 g_object_weak_ref( G_OBJECT( item
), ( GWeakNotify
) desktop_weak_notify
, ndf
);
305 g_free( reader_data
);
312 desktop_weak_notify( NadpDesktopFile
*ndf
, GObject
*item
)
314 static const gchar
*thisfn
= "nadp_reader_desktop_weak_notify";
316 g_debug( "%s: ndf=%p (%s), item=%p (%s)",
317 thisfn
, ( void * ) ndf
, G_OBJECT_TYPE_NAME( ndf
),
318 ( void * ) item
, G_OBJECT_TYPE_NAME( item
));
320 g_object_unref( ndf
);
324 free_desktop_paths( GList
*paths
)
329 for( ip
= paths
; ip
; ip
= ip
->next
){
330 dps
= ( DesktopPath
* ) ip
->data
;
336 g_list_free( paths
);
340 * nadp_reader_iimporter_import_from_uri:
341 * @instance: the #NAIImporter provider.
342 * @parms: a #NAIImporterUriParms structure.
346 * Returns: the import operation code.
348 * As soon as we have a valid .desktop file, we are most probably willing
349 * to successfully import an action or a menu of it.
351 * GLib does not have any primitive to load a key file from an uri.
352 * So we have to load the file into memory, and then try to load the key
353 * file from the memory data.
356 nadp_reader_iimporter_import_from_uri( const NAIImporter
*instance
, NAIImporterImportFromUriParms
*parms
)
358 static const gchar
*thisfn
= "nadp_reader_iimporter_import_from_uri";
360 NadpDesktopFile
*ndf
;
361 NAIImporterManageImportModeParms manage_parms
;
363 g_debug( "%s: instance=%p, parms=%p", thisfn
, ( void * ) instance
, ( void * ) parms
);
365 g_return_val_if_fail( NA_IS_IIMPORTER( instance
), IMPORTER_CODE_PROGRAM_ERROR
);
366 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( instance
), IMPORTER_CODE_PROGRAM_ERROR
);
368 code
= IMPORTER_CODE_NOT_WILLING_TO
;
370 ndf
= nadp_desktop_file_new_from_uri( parms
->uri
);
372 parms
->exist
= FALSE
;
373 parms
->import_mode
= IMPORTER_MODE_NO_IMPORT
;
374 parms
->imported
= ( NAObjectItem
* ) item_from_desktop_file(
375 ( const NadpDesktopProvider
* ) NADP_DESKTOP_PROVIDER( instance
),
376 ndf
, &parms
->messages
);
378 if( parms
->imported
){
379 g_return_val_if_fail( NA_IS_OBJECT_ITEM( parms
->imported
), IMPORTER_CODE_NOT_WILLING_TO
);
381 /* remove the weak reference on desktop file set by 'item_from_desktop_file'
382 * as we must consider this #NAObjectItem as a new one
384 na_object_set_provider_data( parms
->imported
, NULL
);
385 g_object_weak_unref( G_OBJECT( parms
->imported
), ( GWeakNotify
) desktop_weak_notify
, ndf
);
386 g_object_unref( ndf
);
388 manage_parms
.version
= 1;
389 manage_parms
.imported
= parms
->imported
;
390 manage_parms
.check_fn
= parms
->check_fn
;
391 manage_parms
.check_fn_data
= parms
->check_fn_data
;
392 manage_parms
.ask_fn
= parms
->ask_fn
;
393 manage_parms
.ask_fn_data
= parms
->ask_fn_data
;
394 manage_parms
.asked_mode
= parms
->asked_mode
;
395 manage_parms
.messages
= parms
->messages
;
397 code
= na_iimporter_manage_import_mode( &manage_parms
);
399 parms
->exist
= manage_parms
.exist
;
400 parms
->import_mode
= manage_parms
.import_mode
;
401 parms
->messages
= manage_parms
.messages
;
404 if( code
!= IMPORTER_CODE_OK
){
405 if( parms
->imported
){
406 g_debug( "%s: unreffing imported item %p as na_iimporter_manage_import_mode didn't return IMPORTER_CODE_OK", thisfn
, parms
->imported
);
407 g_object_unref( parms
->imported
);
408 parms
->imported
= NULL
;
417 * at this time, the object has been allocated and its id has been set
418 * read here the subitems key, which may be 'Profiles' or 'ItemsList'
419 * depending of the exact class of the NAObjectItem
422 nadp_reader_ifactory_provider_read_start( const NAIFactoryProvider
*reader
, void *reader_data
, const NAIFactoryObject
*serializable
, GSList
**messages
)
424 static const gchar
*thisfn
= "nadp_reader_ifactory_provider_read_start";
426 g_return_if_fail( NA_IS_IFACTORY_PROVIDER( reader
));
427 g_return_if_fail( NADP_IS_DESKTOP_PROVIDER( reader
));
428 g_return_if_fail( NA_IS_IFACTORY_OBJECT( serializable
));
430 if( !NADP_DESKTOP_PROVIDER( reader
)->private->dispose_has_run
){
432 g_debug( "%s: reader=%p (%s), reader_data=%p, serializable=%p (%s), messages=%p",
434 ( void * ) reader
, G_OBJECT_TYPE_NAME( reader
),
435 ( void * ) reader_data
,
436 ( void * ) serializable
, G_OBJECT_TYPE_NAME( serializable
),
437 ( void * ) messages
);
439 if( NA_IS_OBJECT_ITEM( serializable
)){
440 read_start_read_subitems_key( reader
, NA_OBJECT_ITEM( serializable
), ( NadpReaderData
* ) reader_data
, messages
);
441 na_object_set_iversion( serializable
, 3 );
444 if( NA_IS_OBJECT_PROFILE( serializable
)){
445 read_start_profile_attach_profile( reader
, NA_OBJECT_PROFILE( serializable
), ( NadpReaderData
* ) reader_data
, messages
);
451 read_start_read_subitems_key( const NAIFactoryProvider
*provider
, NAObjectItem
*item
, NadpReaderData
*reader_data
, GSList
**messages
)
456 subitems
= nadp_desktop_file_get_string_list( reader_data
->ndf
,
458 NA_IS_OBJECT_ACTION( item
) ? NADP_KEY_PROFILES
: NADP_KEY_ITEMS_LIST
,
463 na_object_set_items_slist( item
, subitems
);
466 na_core_utils_slist_free( subitems
);
470 read_start_profile_attach_profile( const NAIFactoryProvider
*provider
, NAObjectProfile
*profile
, NadpReaderData
*reader_data
, GSList
**messages
)
472 na_object_attach_profile( reader_data
->action
, profile
);
476 * reading any data from a desktop file requires:
477 * - a NadpDesktopFile object which has been initialized with the .desktop file
478 * -> has been attached to the NAObjectItem in get_item() above
479 * - the data type (+ reading default value)
480 * - group and key names
482 * Returns: NULL if the key has not been found
483 * letting the caller deal with default values
486 nadp_reader_ifactory_provider_read_data( const NAIFactoryProvider
*reader
, void *reader_data
, const NAIFactoryObject
*object
, const NADataDef
*def
, GSList
**messages
)
488 static const gchar
*thisfn
= "nadp_reader_ifactory_provider_read_data";
499 g_return_val_if_fail( NA_IS_IFACTORY_PROVIDER( reader
), NULL
);
500 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( reader
), NULL
);
501 g_return_val_if_fail( NA_IS_IFACTORY_OBJECT( object
), NULL
);
505 if( !NADP_DESKTOP_PROVIDER( reader
)->private->dispose_has_run
){
507 nrd
= ( NadpReaderData
* ) reader_data
;
508 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( nrd
->ndf
), NULL
);
510 if( def
->desktop_entry
){
512 if( NA_IS_OBJECT_ITEM( object
)){
513 group
= g_strdup( NADP_GROUP_DESKTOP
);
516 g_return_val_if_fail( NA_IS_OBJECT_PROFILE( object
), NULL
);
517 id
= na_object_get_id( object
);
518 group
= g_strdup_printf( "%s %s", NADP_GROUP_PROFILE
, id
);
524 case NA_DATA_TYPE_LOCALE_STRING
:
525 str_value
= nadp_desktop_file_get_locale_string( nrd
->ndf
, group
, def
->desktop_entry
, &found
, def
->default_value
);
527 boxed
= na_data_boxed_new( def
);
528 na_boxed_set_from_void( NA_BOXED( boxed
), str_value
);
533 case NA_DATA_TYPE_STRING
:
534 str_value
= nadp_desktop_file_get_string( nrd
->ndf
, group
, def
->desktop_entry
, &found
, def
->default_value
);
536 boxed
= na_data_boxed_new( def
);
537 na_boxed_set_from_void( NA_BOXED( boxed
), str_value
);
542 case NA_DATA_TYPE_BOOLEAN
:
543 bool_value
= nadp_desktop_file_get_boolean( nrd
->ndf
, group
, def
->desktop_entry
, &found
, na_core_utils_boolean_from_string( def
->default_value
));
545 boxed
= na_data_boxed_new( def
);
546 na_boxed_set_from_void( NA_BOXED( boxed
), GUINT_TO_POINTER( bool_value
));
550 case NA_DATA_TYPE_STRING_LIST
:
551 slist_value
= nadp_desktop_file_get_string_list( nrd
->ndf
, group
, def
->desktop_entry
, &found
, def
->default_value
);
553 boxed
= na_data_boxed_new( def
);
554 na_boxed_set_from_void( NA_BOXED( boxed
), slist_value
);
556 na_core_utils_slist_free( slist_value
);
559 case NA_DATA_TYPE_UINT
:
560 uint_value
= nadp_desktop_file_get_uint( nrd
->ndf
, group
, def
->desktop_entry
, &found
, atoi( def
->default_value
));
562 boxed
= na_data_boxed_new( def
);
563 na_boxed_set_from_void( NA_BOXED( boxed
), GUINT_TO_POINTER( uint_value
));
568 msg
= g_strdup_printf( "%s: %d: invalid data type.", thisfn
, def
->type
);
569 g_warning( "%s", msg
);
570 *messages
= g_slist_append( *messages
, msg
);
581 * called when each NAIFactoryObject object has been read
584 nadp_reader_ifactory_provider_read_done( const NAIFactoryProvider
*reader
, void *reader_data
, const NAIFactoryObject
*serializable
, GSList
**messages
)
586 static const gchar
*thisfn
= "nadp_reader_ifactory_provider_read_done";
589 g_return_if_fail( NA_IS_IFACTORY_PROVIDER( reader
));
590 g_return_if_fail( NADP_IS_DESKTOP_PROVIDER( reader
));
591 g_return_if_fail( NA_IS_IFACTORY_OBJECT( serializable
));
593 if( !NADP_DESKTOP_PROVIDER( reader
)->private->dispose_has_run
){
595 g_debug( "%s: reader=%p (%s), reader_data=%p, serializable=%p (%s), messages=%p",
597 ( void * ) reader
, G_OBJECT_TYPE_NAME( reader
),
598 ( void * ) reader_data
,
599 ( void * ) serializable
, G_OBJECT_TYPE_NAME( serializable
),
600 ( void * ) messages
);
602 if( NA_IS_OBJECT_ITEM( serializable
)){
603 writable
= read_done_item_is_writable( reader
, NA_OBJECT_ITEM( serializable
), ( NadpReaderData
* ) reader_data
, messages
);
604 na_object_set_readonly( serializable
, !writable
);
607 if( NA_IS_OBJECT_ACTION( serializable
)){
608 read_done_action_read_profiles( reader
, NA_OBJECT_ACTION( serializable
), ( NadpReaderData
* ) reader_data
, messages
);
611 g_debug( "%s: quitting for %s at %p", thisfn
, G_OBJECT_TYPE_NAME( serializable
), ( void * ) serializable
);
616 read_done_item_is_writable( const NAIFactoryProvider
*provider
, NAObjectItem
*item
, NadpReaderData
*reader_data
, GSList
**messages
)
618 NadpDesktopFile
*ndf
;
622 ndf
= reader_data
->ndf
;
623 uri
= nadp_desktop_file_get_key_file_uri( ndf
);
624 writable
= nadp_utils_uri_is_writable( uri
);
631 * read and attach profiles in the specified order
632 * profiles which may exist in .desktop files, but are not referenced
633 * in the 'Profiles' string list are just ignored
636 read_done_action_read_profiles( const NAIFactoryProvider
*provider
, NAObjectAction
*action
, NadpReaderData
*reader_data
, GSList
**messages
)
643 reader_data
->action
= action
;
644 order
= na_object_get_items_slist( action
);
646 for( ip
= order
; ip
; ip
= ip
->next
){
647 profile_id
= ( gchar
* ) ip
->data
;
648 found
= na_object_get_item( action
, profile_id
);
650 read_done_action_load_profile( provider
, reader_data
, profile_id
, messages
);
654 na_core_utils_slist_free( order
);
658 read_done_action_load_profile( const NAIFactoryProvider
*provider
, NadpReaderData
*reader_data
, const gchar
*profile_id
, GSList
**messages
)
660 NAObjectProfile
*profile
;
662 g_debug( "nadp_reader_read_done_action_load_profile: loading profile=%s", profile_id
);
664 profile
= na_object_profile_new();
665 na_object_set_id( profile
, profile_id
);
667 na_ifactory_provider_read_item(
668 NA_IFACTORY_PROVIDER( provider
),
670 NA_IFACTORY_OBJECT( profile
),