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 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-object-api.h>
41 #include <api/na-ifactory-provider.h>
43 #include "nadp-desktop-file.h"
44 #include "nadp-desktop-provider.h"
45 #include "nadp-formats.h"
46 #include "nadp-keys.h"
47 #include "nadp-utils.h"
48 #include "nadp-writer.h"
49 #include "nadp-xdg-dirs.h"
51 /* the association between an export format and the functions
59 static ExportFormatFn st_export_format_fn
[] = {
61 { NADP_FORMAT_DESKTOP_V1
,
67 static guint
write_item( const NAIIOProvider
*provider
, const NAObjectItem
*item
, NadpDesktopFile
*ndf
, GSList
**messages
);
69 static void desktop_weak_notify( NadpDesktopFile
*ndf
, GObject
*item
);
71 static void write_start_write_type( NadpDesktopFile
*ndp
, NAObjectItem
*item
);
72 static void write_done_write_subitems_list( NadpDesktopFile
*ndp
, NAObjectItem
*item
);
74 static ExportFormatFn
*find_export_format_fn( GQuark format
);
77 * This is implementation of NAIIOProvider::is_willing_to_write method
80 nadp_iio_provider_is_willing_to_write( const NAIIOProvider
*provider
)
86 * NadpDesktopProvider is able to write if user data dir exists (or
87 * can be created) and is writable
89 * This is implementation of NAIIOProvider::is_able_to_write method
92 nadp_iio_provider_is_able_to_write( const NAIIOProvider
*provider
)
94 static const gchar
*thisfn
= "nadp_writer_iio_provider_is_able_to_write";
99 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), FALSE
);
104 userdir
= nadp_xdg_dirs_get_user_data_dir();
106 if( g_file_test( userdir
, G_FILE_TEST_IS_DIR
)){
107 able_to
= na_core_utils_dir_is_writable_path( userdir
);
109 } else if( g_mkdir_with_parents( userdir
, 0700 )){
110 g_warning( "%s: %s: %s", thisfn
, userdir
, g_strerror( errno
));
113 able_to
= na_core_utils_dir_is_writable_path( userdir
);
122 * This is implementation of NAIIOProvider::write_item method
125 nadp_iio_provider_write_item( const NAIIOProvider
*provider
, const NAObjectItem
*item
, GSList
**messages
)
127 static const gchar
*thisfn
= "nadp_iio_provider_write_item";
129 NadpDesktopFile
*ndf
;
138 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
140 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), ret
);
141 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), ret
);
143 if( na_object_is_readonly( item
)){
144 g_warning( "%s: item=%p is read-only", thisfn
, ( void * ) item
);
148 ndf
= ( NadpDesktopFile
* ) na_object_get_provider_data( item
);
150 /* write into the current key file and write it to current path */
152 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf
), ret
);
155 userdir
= nadp_xdg_dirs_get_user_data_dir();
156 subdirs
= na_core_utils_slist_from_split( NADP_DESKTOP_PROVIDER_SUBDIRS
, G_SEARCHPATH_SEPARATOR_S
);
157 fulldir
= g_build_filename( userdir
, ( gchar
* ) subdirs
->data
, NULL
);
160 if( !g_file_test( fulldir
, G_FILE_TEST_IS_DIR
)){
161 if( g_mkdir_with_parents( fulldir
, 0700 )){
162 g_warning( "%s: %s: %s", thisfn
, userdir
, g_strerror( errno
));
167 na_core_utils_slist_free( subdirs
);
170 id
= na_object_get_id( item
);
171 bname
= g_strdup_printf( "%s%s", id
, NADP_DESKTOP_FILE_SUFFIX
);
173 path
= g_build_filename( fulldir
, bname
, NULL
);
179 ndf
= nadp_desktop_file_new_for_write( path
);
180 na_object_set_provider_data( item
, ndf
);
181 g_object_weak_ref( G_OBJECT( item
), ( GWeakNotify
) desktop_weak_notify
, ndf
);
187 ret
= write_item( provider
, item
, ndf
, messages
);
194 * actually writes the item to the existing NadpDesktopFile
195 * as we have choosen to take advantage of data factory management system
196 * we do not need to enumerate each and every elementary data
198 * As we want keep comments between through multiple updates, we cannot
199 * just delete the .desktop file and recreate it as we are doing for GConf.
200 * Instead of that, we delete at end groups that have not been walked through
201 * -> as a side effect, we lose comments inside of these groups :(
204 write_item( const NAIIOProvider
*provider
, const NAObjectItem
*item
, NadpDesktopFile
*ndf
, GSList
**messages
)
206 static const gchar
*thisfn
= "nadp_iio_provider_write_item";
208 NadpDesktopProvider
*self
;
210 g_debug( "%s: provider=%p (%s), item=%p (%s), ndf=%p, messages=%p",
212 ( void * ) provider
, G_OBJECT_TYPE_NAME( provider
),
213 ( void * ) item
, G_OBJECT_TYPE_NAME( item
),
215 ( void * ) messages
);
217 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
219 g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider
), ret
);
220 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), ret
);
221 g_return_val_if_fail( NA_IS_IFACTORY_PROVIDER( provider
), ret
);
223 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), ret
);
224 g_return_val_if_fail( NA_IS_IFACTORY_OBJECT( item
), ret
);
226 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf
), ret
);
228 self
= NADP_DESKTOP_PROVIDER( provider
);
230 if( self
->private->dispose_has_run
){
231 return( NA_IIO_PROVIDER_CODE_NOT_WILLING_TO_RUN
);
234 ret
= NA_IIO_PROVIDER_CODE_OK
;
236 na_ifactory_provider_write_item( NA_IFACTORY_PROVIDER( provider
), ndf
, NA_IFACTORY_OBJECT( item
), messages
);
238 if( !nadp_desktop_file_write( ndf
)){
239 ret
= NA_IIO_PROVIDER_CODE_WRITE_ERROR
;
246 nadp_iio_provider_delete_item( const NAIIOProvider
*provider
, const NAObjectItem
*item
, GSList
**messages
)
248 static const gchar
*thisfn
= "nadp_iio_provider_delete_item";
250 NadpDesktopProvider
*self
;
251 NadpDesktopFile
*ndf
;
254 g_debug( "%s: provider=%p (%s), item=%p (%s), messages=%p",
256 ( void * ) provider
, G_OBJECT_TYPE_NAME( provider
),
257 ( void * ) item
, G_OBJECT_TYPE_NAME( item
),
258 ( void * ) messages
);
260 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
262 g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider
), ret
);
263 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), ret
);
264 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), ret
);
266 self
= NADP_DESKTOP_PROVIDER( provider
);
268 if( self
->private->dispose_has_run
){
269 return( NA_IIO_PROVIDER_CODE_NOT_WILLING_TO_RUN
);
272 ndf
= ( NadpDesktopFile
* ) na_object_get_provider_data( item
);
275 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf
), ret
);
276 uri
= nadp_desktop_file_get_key_file_uri( ndf
);
277 if( nadp_utils_uri_delete( uri
)){
278 ret
= NA_IIO_PROVIDER_CODE_OK
;
283 g_warning( "%s: NadpDesktopFile is null", thisfn
);
284 ret
= NA_IIO_PROVIDER_CODE_OK
;
291 desktop_weak_notify( NadpDesktopFile
*ndf
, GObject
*item
)
293 static const gchar
*thisfn
= "nadp_writer_desktop_weak_notify";
295 g_debug( "%s: ndf=%p (%s), item=%p (%s)",
296 thisfn
, ( void * ) ndf
, G_OBJECT_TYPE_NAME( ndf
),
297 ( void * ) item
, G_OBJECT_TYPE_NAME( item
));
299 g_object_unref( ndf
);
303 * Implementation of NAIIOProvider::duplicate_data
304 * Add a ref on NadpDesktopFile data, so that unreffing origin object in NACT
305 * does not invalid duplicated pointer
308 nadp_iio_provider_duplicate_data( const NAIIOProvider
*provider
, NAObjectItem
*dest
, const NAObjectItem
*source
, GSList
**messages
)
310 static const gchar
*thisfn
= "nadp_iio_provider_duplicate_data";
312 NadpDesktopProvider
*self
;
313 NadpDesktopFile
*ndf
;
315 g_debug( "%s: provider=%p (%s), dest=%p (%s), source=%p (%s), messages=%p",
317 ( void * ) provider
, G_OBJECT_TYPE_NAME( provider
),
318 ( void * ) dest
, G_OBJECT_TYPE_NAME( dest
),
319 ( void * ) source
, G_OBJECT_TYPE_NAME( source
),
320 ( void * ) messages
);
322 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
324 g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider
), ret
);
325 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), ret
);
326 g_return_val_if_fail( NA_IS_OBJECT_ITEM( dest
), ret
);
327 g_return_val_if_fail( NA_IS_OBJECT_ITEM( source
), ret
);
329 self
= NADP_DESKTOP_PROVIDER( provider
);
331 if( self
->private->dispose_has_run
){
332 return( NA_IIO_PROVIDER_CODE_NOT_WILLING_TO_RUN
);
335 ndf
= ( NadpDesktopFile
* ) na_object_get_provider_data( source
);
336 g_return_val_if_fail( ndf
&& NADP_IS_DESKTOP_FILE( ndf
), ret
);
337 na_object_set_provider_data( dest
, g_object_ref( ndf
));
338 g_object_weak_ref( G_OBJECT( dest
), ( GWeakNotify
) desktop_weak_notify
, ndf
);
340 return( NA_IIO_PROVIDER_CODE_OK
);
344 * nadp_writer_iexporter_export_to_buffer:
345 * @instance: this #NAIExporter instance.
346 * @parms: a #NAIExporterBufferParms structure.
348 * Export the specified 'item' to a newly allocated buffer.
351 nadp_writer_iexporter_export_to_buffer( const NAIExporter
*instance
, NAIExporterBufferParms
*parms
)
353 static const gchar
*thisfn
= "nadp_writer_iexporter_export_to_buffer";
354 guint code
, write_code
;
357 NadpDesktopFile
*ndf
;
359 g_debug( "%s: instance=%p, parms=%p", thisfn
, ( void * ) instance
, ( void * ) parms
);
361 parms
->buffer
= NULL
;
362 code
= NA_IEXPORTER_CODE_OK
;
364 if( !parms
->exported
|| !NA_IS_OBJECT_ITEM( parms
->exported
)){
365 code
= NA_IEXPORTER_CODE_INVALID_ITEM
;
368 if( code
== NA_IEXPORTER_CODE_OK
){
369 fmt
= find_export_format_fn( parms
->format
);
372 code
= NA_IEXPORTER_CODE_INVALID_FORMAT
;
375 ndf
= nadp_desktop_file_new();
376 write_code
= na_ifactory_provider_write_item( NA_IFACTORY_PROVIDER( instance
), ndf
, NA_IFACTORY_OBJECT( parms
->exported
), &parms
->messages
);
378 if( write_code
!= NA_IIO_PROVIDER_CODE_OK
){
379 code
= NA_IEXPORTER_CODE_ERROR
;
382 key_file
= nadp_desktop_file_get_key_file( ndf
);
383 parms
->buffer
= g_key_file_to_data( key_file
, NULL
, NULL
);
386 g_object_unref( ndf
);
390 g_debug( "%s: returning code=%u", thisfn
, code
);
395 * nadp_writer_iexporter_export_to_file:
396 * @instance: this #NAIExporter instance.
397 * @parms: a #NAIExporterFileParms structure.
399 * Export the specified 'item' to a newly created file.
402 nadp_writer_iexporter_export_to_file( const NAIExporter
*instance
, NAIExporterFileParms
*parms
)
404 static const gchar
*thisfn
= "nadp_writer_iexporter_export_to_file";
405 guint code
, write_code
;
406 gchar
*id
, *folder_path
, *dest_path
;
408 NadpDesktopFile
*ndf
;
410 g_debug( "%s: instance=%p, parms=%p", thisfn
, ( void * ) instance
, ( void * ) parms
);
412 parms
->basename
= NULL
;
413 code
= NA_IEXPORTER_CODE_OK
;
415 if( !parms
->exported
|| !NA_IS_OBJECT_ITEM( parms
->exported
)){
416 code
= NA_IEXPORTER_CODE_INVALID_ITEM
;
419 if( code
== NA_IEXPORTER_CODE_OK
){
420 fmt
= find_export_format_fn( parms
->format
);
423 code
= NA_IEXPORTER_CODE_INVALID_FORMAT
;
426 id
= na_object_get_id( parms
->exported
);
427 parms
->basename
= g_strdup_printf( "%s%s", id
, NADP_DESKTOP_FILE_SUFFIX
);
430 folder_path
= g_filename_from_uri( parms
->folder
, NULL
, NULL
);
431 dest_path
= g_strdup_printf( "%s/%s", folder_path
, parms
->basename
);
432 g_free( folder_path
);
434 ndf
= nadp_desktop_file_new_for_write( dest_path
);
435 write_code
= na_ifactory_provider_write_item( NA_IFACTORY_PROVIDER( instance
), ndf
, NA_IFACTORY_OBJECT( parms
->exported
), &parms
->messages
);
437 if( write_code
!= NA_IIO_PROVIDER_CODE_OK
){
438 code
= NA_IEXPORTER_CODE_ERROR
;
440 } else if( !nadp_desktop_file_write( ndf
)){
441 code
= NA_IEXPORTER_CODE_UNABLE_TO_WRITE
;
445 g_object_unref( ndf
);
449 g_debug( "%s: returning code=%u", thisfn
, code
);
454 nadp_writer_ifactory_provider_write_start( const NAIFactoryProvider
*provider
, void *writer_data
,
455 const NAIFactoryObject
*object
, GSList
**messages
)
457 if( NA_IS_OBJECT_ITEM( object
)){
458 write_start_write_type( NADP_DESKTOP_FILE( writer_data
), NA_OBJECT_ITEM( object
));
461 return( NA_IIO_PROVIDER_CODE_OK
);
465 write_start_write_type( NadpDesktopFile
*ndp
, NAObjectItem
*item
)
467 nadp_desktop_file_set_string(
471 NA_IS_OBJECT_ACTION( item
) ? NADP_VALUE_TYPE_ACTION
: NADP_VALUE_TYPE_MENU
);
475 * when writing to .desktop file a profile which has both a path and parameters,
476 * then concatenate these two fields to the 'Exec' key
479 nadp_writer_ifactory_provider_write_data(
480 const NAIFactoryProvider
*provider
, void *writer_data
, const NAIFactoryObject
*object
,
481 const NADataBoxed
*boxed
, GSList
**messages
)
483 static const gchar
*thisfn
= "nadp_writer_ifactory_provider_write_data";
484 NadpDesktopFile
*ndf
;
495 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( writer_data
), NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
);
496 /*g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));*/
498 code
= NA_IIO_PROVIDER_CODE_OK
;
499 ndf
= NADP_DESKTOP_FILE( writer_data
);
500 def
= na_data_boxed_get_data_def( boxed
);
502 if( def
->desktop_entry
&& strlen( def
->desktop_entry
)){
504 if( NA_IS_OBJECT_PROFILE( object
)){
505 profile_id
= na_object_get_id( object
);
506 group_name
= g_strdup_printf( "%s %s", NADP_GROUP_PROFILE
, profile_id
);
507 g_free( profile_id
);
510 group_name
= g_strdup( NADP_GROUP_DESKTOP
);
513 if( !na_data_boxed_is_default( boxed
) || def
->write_if_default
){
517 case NAFD_TYPE_STRING
:
518 str_value
= na_data_boxed_get_as_string( boxed
);
520 if( !strcmp( def
->name
, NAFO_DATA_PATH
)){
521 parms
= na_object_get_parameters( object
);
522 tmp
= g_strdup_printf( "%s %s", str_value
, parms
);
528 nadp_desktop_file_set_string( ndf
, group_name
, def
->desktop_entry
, str_value
);
532 case NAFD_TYPE_LOCALE_STRING
:
533 str_value
= na_data_boxed_get_as_string( boxed
);
534 nadp_desktop_file_set_locale_string( ndf
, group_name
, def
->desktop_entry
, str_value
);
538 case NAFD_TYPE_BOOLEAN
:
539 bool_value
= GPOINTER_TO_UINT( na_data_boxed_get_as_void( boxed
));
540 nadp_desktop_file_set_boolean( ndf
, group_name
, def
->desktop_entry
, bool_value
);
543 case NAFD_TYPE_STRING_LIST
:
544 slist_value
= ( GSList
* ) na_data_boxed_get_as_void( boxed
);
545 nadp_desktop_file_set_string_list( ndf
, group_name
, def
->desktop_entry
, slist_value
);
546 na_core_utils_slist_free( slist_value
);
550 uint_value
= GPOINTER_TO_UINT( na_data_boxed_get_as_void( boxed
));
551 nadp_desktop_file_set_uint( ndf
, group_name
, def
->desktop_entry
, uint_value
);
555 g_warning( "%s: unknown type=%u for %s", thisfn
, def
->type
, def
->name
);
556 code
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
560 nadp_desktop_file_remove_key( ndf
, group_name
, def
->desktop_entry
);
563 g_free( group_name
);
570 nadp_writer_ifactory_provider_write_done( const NAIFactoryProvider
*provider
, void *writer_data
,
571 const NAIFactoryObject
*object
, GSList
**messages
)
573 if( NA_IS_OBJECT_ITEM( object
)){
574 write_done_write_subitems_list( NADP_DESKTOP_FILE( writer_data
), NA_OBJECT_ITEM( object
));
577 return( NA_IIO_PROVIDER_CODE_OK
);
581 write_done_write_subitems_list( NadpDesktopFile
*ndp
, NAObjectItem
*item
)
583 static const gchar
*thisfn
= "nadp_writer_write_done_write_subitems_list";
585 GSList
*profile_groups
, *ip
;
588 subitems
= na_object_get_items_slist( item
);
589 tmp
= g_strdup_printf( "%s (written subitems)", thisfn
);
590 na_core_utils_slist_dump( tmp
, subitems
);
593 nadp_desktop_file_set_string_list(
596 NA_IS_OBJECT_ACTION( item
) ? NADP_KEY_PROFILES
: NADP_KEY_ITEMS_LIST
,
599 profile_groups
= nadp_desktop_file_get_profiles( ndp
);
600 tmp
= g_strdup_printf( "%s (existing profiles)", thisfn
);
601 na_core_utils_slist_dump( tmp
, profile_groups
);
604 for( ip
= profile_groups
; ip
; ip
= ip
->next
){
605 if( na_core_utils_slist_count( subitems
, ( const gchar
* ) ip
->data
) == 0 ){
606 g_debug( "%s: deleting (removed) profile %s", thisfn
, ( const gchar
* ) ip
->data
);
607 nadp_desktop_file_remove_profile( ndp
, ( const gchar
* ) ip
->data
);
611 na_core_utils_slist_free( profile_groups
);
612 na_core_utils_slist_free( subitems
);
615 static ExportFormatFn
*
616 find_export_format_fn( GQuark format
)
618 ExportFormatFn
*found
;
622 i
= st_export_format_fn
;
624 while( i
->format
&& !found
){
625 if( g_quark_from_string( i
->format
) == format
){