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-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";
98 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), FALSE
);
102 userdir
= nadp_xdg_dirs_get_user_data_dir();
104 if( g_file_test( userdir
, G_FILE_TEST_IS_DIR
)){
105 able_to
= na_core_utils_dir_is_writable_path( userdir
);
107 } else if( g_mkdir_with_parents( userdir
, 0700 )){
108 g_warning( "%s: %s: %s", thisfn
, userdir
, g_strerror( errno
));
111 able_to
= na_core_utils_dir_is_writable_path( userdir
);
120 * This is implementation of NAIIOProvider::write_item method
123 nadp_iio_provider_write_item( const NAIIOProvider
*provider
, const NAObjectItem
*item
, GSList
**messages
)
125 static const gchar
*thisfn
= "nadp_iio_provider_write_item";
127 NadpDesktopFile
*ndf
;
136 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
138 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), ret
);
139 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), ret
);
141 if( na_object_is_readonly( item
)){
142 g_warning( "%s: item=%p is read-only", thisfn
, ( void * ) item
);
146 ndf
= ( NadpDesktopFile
* ) na_object_get_provider_data( item
);
148 /* write into the current key file and write it to current path */
150 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf
), ret
);
153 userdir
= nadp_xdg_dirs_get_user_data_dir();
154 subdirs
= na_core_utils_slist_from_split( NADP_DESKTOP_PROVIDER_SUBDIRS
, G_SEARCHPATH_SEPARATOR_S
);
155 fulldir
= g_build_filename( userdir
, ( gchar
* ) subdirs
->data
, NULL
);
158 if( !g_file_test( fulldir
, G_FILE_TEST_IS_DIR
)){
159 if( g_mkdir_with_parents( fulldir
, 0700 )){
160 g_warning( "%s: %s: %s", thisfn
, userdir
, g_strerror( errno
));
165 na_core_utils_slist_free( subdirs
);
168 id
= na_object_get_id( item
);
169 bname
= g_strdup_printf( "%s%s", id
, NADP_DESKTOP_FILE_SUFFIX
);
171 path
= g_build_filename( fulldir
, bname
, NULL
);
177 ndf
= nadp_desktop_file_new_for_write( path
);
178 na_object_set_provider_data( item
, ndf
);
179 g_object_weak_ref( G_OBJECT( item
), ( GWeakNotify
) desktop_weak_notify
, ndf
);
185 ret
= write_item( provider
, item
, ndf
, messages
);
192 * actually writes the item to the existing NadpDesktopFile
193 * as we have chosen to take advantage of data factory management system
194 * we do not need to enumerate each and every elementary data
196 * As we want keep comments between through multiple updates, we cannot
197 * just delete the .desktop file and recreate it as we are doing for GConf.
198 * Instead of that, we delete at end groups that have not been walked through
199 * -> as a side effect, we lose comments inside of these groups :(
202 write_item( const NAIIOProvider
*provider
, const NAObjectItem
*item
, NadpDesktopFile
*ndf
, GSList
**messages
)
204 static const gchar
*thisfn
= "nadp_iio_provider_write_item";
206 NadpDesktopProvider
*self
;
208 g_debug( "%s: provider=%p (%s), item=%p (%s), ndf=%p, messages=%p",
210 ( void * ) provider
, G_OBJECT_TYPE_NAME( provider
),
211 ( void * ) item
, G_OBJECT_TYPE_NAME( item
),
213 ( void * ) messages
);
215 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
217 g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider
), ret
);
218 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), ret
);
219 g_return_val_if_fail( NA_IS_IFACTORY_PROVIDER( provider
), ret
);
221 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), ret
);
222 g_return_val_if_fail( NA_IS_IFACTORY_OBJECT( item
), ret
);
224 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf
), ret
);
226 self
= NADP_DESKTOP_PROVIDER( provider
);
228 if( self
->private->dispose_has_run
){
229 return( NA_IIO_PROVIDER_CODE_NOT_WILLING_TO_RUN
);
232 ret
= NA_IIO_PROVIDER_CODE_OK
;
234 na_ifactory_provider_write_item( NA_IFACTORY_PROVIDER( provider
), ndf
, NA_IFACTORY_OBJECT( item
), messages
);
236 if( !nadp_desktop_file_write( ndf
)){
237 ret
= NA_IIO_PROVIDER_CODE_WRITE_ERROR
;
244 nadp_iio_provider_delete_item( const NAIIOProvider
*provider
, const NAObjectItem
*item
, GSList
**messages
)
246 static const gchar
*thisfn
= "nadp_iio_provider_delete_item";
248 NadpDesktopProvider
*self
;
249 NadpDesktopFile
*ndf
;
252 g_debug( "%s: provider=%p (%s), item=%p (%s), messages=%p",
254 ( void * ) provider
, G_OBJECT_TYPE_NAME( provider
),
255 ( void * ) item
, G_OBJECT_TYPE_NAME( item
),
256 ( void * ) messages
);
258 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
260 g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider
), ret
);
261 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), ret
);
262 g_return_val_if_fail( NA_IS_OBJECT_ITEM( item
), ret
);
264 self
= NADP_DESKTOP_PROVIDER( provider
);
266 if( self
->private->dispose_has_run
){
267 return( NA_IIO_PROVIDER_CODE_NOT_WILLING_TO_RUN
);
270 ndf
= ( NadpDesktopFile
* ) na_object_get_provider_data( item
);
273 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf
), ret
);
274 uri
= nadp_desktop_file_get_key_file_uri( ndf
);
275 if( nadp_utils_uri_delete( uri
)){
276 ret
= NA_IIO_PROVIDER_CODE_OK
;
281 g_warning( "%s: NadpDesktopFile is null", thisfn
);
282 ret
= NA_IIO_PROVIDER_CODE_OK
;
289 desktop_weak_notify( NadpDesktopFile
*ndf
, GObject
*item
)
291 static const gchar
*thisfn
= "nadp_writer_desktop_weak_notify";
293 g_debug( "%s: ndf=%p (%s), item=%p (%s)",
294 thisfn
, ( void * ) ndf
, G_OBJECT_TYPE_NAME( ndf
),
295 ( void * ) item
, G_OBJECT_TYPE_NAME( item
));
297 g_object_unref( ndf
);
301 * Implementation of NAIIOProvider::duplicate_data
302 * Add a ref on NadpDesktopFile data, so that unreffing origin object in NACT
303 * does not invalid duplicated pointer
306 nadp_iio_provider_duplicate_data( const NAIIOProvider
*provider
, NAObjectItem
*dest
, const NAObjectItem
*source
, GSList
**messages
)
308 static const gchar
*thisfn
= "nadp_iio_provider_duplicate_data";
310 NadpDesktopProvider
*self
;
311 NadpDesktopFile
*ndf
;
313 g_debug( "%s: provider=%p (%s), dest=%p (%s), source=%p (%s), messages=%p",
315 ( void * ) provider
, G_OBJECT_TYPE_NAME( provider
),
316 ( void * ) dest
, G_OBJECT_TYPE_NAME( dest
),
317 ( void * ) source
, G_OBJECT_TYPE_NAME( source
),
318 ( void * ) messages
);
320 ret
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
322 g_return_val_if_fail( NA_IS_IIO_PROVIDER( provider
), ret
);
323 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider
), ret
);
324 g_return_val_if_fail( NA_IS_OBJECT_ITEM( dest
), ret
);
325 g_return_val_if_fail( NA_IS_OBJECT_ITEM( source
), ret
);
327 self
= NADP_DESKTOP_PROVIDER( provider
);
329 if( self
->private->dispose_has_run
){
330 return( NA_IIO_PROVIDER_CODE_NOT_WILLING_TO_RUN
);
333 ndf
= ( NadpDesktopFile
* ) na_object_get_provider_data( source
);
334 g_return_val_if_fail( ndf
&& NADP_IS_DESKTOP_FILE( ndf
), ret
);
335 na_object_set_provider_data( dest
, g_object_ref( ndf
));
336 g_object_weak_ref( G_OBJECT( dest
), ( GWeakNotify
) desktop_weak_notify
, ndf
);
338 return( NA_IIO_PROVIDER_CODE_OK
);
342 * nadp_writer_iexporter_export_to_buffer:
343 * @instance: this #NAIExporter instance.
344 * @parms: a #NAIExporterBufferParms structure.
346 * Export the specified 'item' to a newly allocated buffer.
349 nadp_writer_iexporter_export_to_buffer( const NAIExporter
*instance
, NAIExporterBufferParms
*parms
)
351 static const gchar
*thisfn
= "nadp_writer_iexporter_export_to_buffer";
352 guint code
, write_code
;
355 NadpDesktopFile
*ndf
;
357 g_debug( "%s: instance=%p, parms=%p", thisfn
, ( void * ) instance
, ( void * ) parms
);
359 parms
->buffer
= NULL
;
360 code
= NA_IEXPORTER_CODE_OK
;
362 if( !parms
->exported
|| !NA_IS_OBJECT_ITEM( parms
->exported
)){
363 code
= NA_IEXPORTER_CODE_INVALID_ITEM
;
366 if( code
== NA_IEXPORTER_CODE_OK
){
367 fmt
= find_export_format_fn( parms
->format
);
370 code
= NA_IEXPORTER_CODE_INVALID_FORMAT
;
373 ndf
= nadp_desktop_file_new();
374 write_code
= na_ifactory_provider_write_item( NA_IFACTORY_PROVIDER( instance
), ndf
, NA_IFACTORY_OBJECT( parms
->exported
), &parms
->messages
);
376 if( write_code
!= NA_IIO_PROVIDER_CODE_OK
){
377 code
= NA_IEXPORTER_CODE_ERROR
;
380 key_file
= nadp_desktop_file_get_key_file( ndf
);
381 parms
->buffer
= g_key_file_to_data( key_file
, NULL
, NULL
);
384 g_object_unref( ndf
);
388 g_debug( "%s: returning code=%u", thisfn
, code
);
393 * nadp_writer_iexporter_export_to_file:
394 * @instance: this #NAIExporter instance.
395 * @parms: a #NAIExporterFileParms structure.
397 * Export the specified 'item' to a newly created file.
400 nadp_writer_iexporter_export_to_file( const NAIExporter
*instance
, NAIExporterFileParms
*parms
)
402 static const gchar
*thisfn
= "nadp_writer_iexporter_export_to_file";
403 guint code
, write_code
;
404 gchar
*id
, *folder_path
, *dest_path
;
406 NadpDesktopFile
*ndf
;
408 g_debug( "%s: instance=%p, parms=%p", thisfn
, ( void * ) instance
, ( void * ) parms
);
410 parms
->basename
= NULL
;
411 code
= NA_IEXPORTER_CODE_OK
;
413 if( !parms
->exported
|| !NA_IS_OBJECT_ITEM( parms
->exported
)){
414 code
= NA_IEXPORTER_CODE_INVALID_ITEM
;
417 if( code
== NA_IEXPORTER_CODE_OK
){
418 fmt
= find_export_format_fn( parms
->format
);
421 code
= NA_IEXPORTER_CODE_INVALID_FORMAT
;
424 id
= na_object_get_id( parms
->exported
);
425 parms
->basename
= g_strdup_printf( "%s%s", id
, NADP_DESKTOP_FILE_SUFFIX
);
428 folder_path
= g_filename_from_uri( parms
->folder
, NULL
, NULL
);
429 dest_path
= g_strdup_printf( "%s/%s", folder_path
, parms
->basename
);
430 g_free( folder_path
);
432 ndf
= nadp_desktop_file_new_for_write( dest_path
);
433 write_code
= na_ifactory_provider_write_item( NA_IFACTORY_PROVIDER( instance
), ndf
, NA_IFACTORY_OBJECT( parms
->exported
), &parms
->messages
);
435 if( write_code
!= NA_IIO_PROVIDER_CODE_OK
){
436 code
= NA_IEXPORTER_CODE_ERROR
;
438 } else if( !nadp_desktop_file_write( ndf
)){
439 code
= NA_IEXPORTER_CODE_UNABLE_TO_WRITE
;
443 g_object_unref( ndf
);
447 g_debug( "%s: returning code=%u", thisfn
, code
);
452 nadp_writer_ifactory_provider_write_start( const NAIFactoryProvider
*provider
, void *writer_data
,
453 const NAIFactoryObject
*object
, GSList
**messages
)
455 if( NA_IS_OBJECT_ITEM( object
)){
456 write_start_write_type( NADP_DESKTOP_FILE( writer_data
), NA_OBJECT_ITEM( object
));
459 return( NA_IIO_PROVIDER_CODE_OK
);
463 write_start_write_type( NadpDesktopFile
*ndp
, NAObjectItem
*item
)
465 nadp_desktop_file_set_string(
469 NA_IS_OBJECT_ACTION( item
) ? NADP_VALUE_TYPE_ACTION
: NADP_VALUE_TYPE_MENU
);
473 * when writing to .desktop file a profile which has both a path and parameters,
474 * then concatenate these two fields to the 'Exec' key
477 nadp_writer_ifactory_provider_write_data(
478 const NAIFactoryProvider
*provider
, void *writer_data
, const NAIFactoryObject
*object
,
479 const NADataBoxed
*boxed
, GSList
**messages
)
481 static const gchar
*thisfn
= "nadp_writer_ifactory_provider_write_data";
482 NadpDesktopFile
*ndf
;
484 const NADataDef
*def
;
493 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( writer_data
), NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
);
494 /*g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));*/
496 code
= NA_IIO_PROVIDER_CODE_OK
;
497 ndf
= NADP_DESKTOP_FILE( writer_data
);
498 def
= na_data_boxed_get_data_def( boxed
);
500 if( def
->desktop_entry
&& strlen( def
->desktop_entry
)){
502 if( NA_IS_OBJECT_PROFILE( object
)){
503 profile_id
= na_object_get_id( object
);
504 group_name
= g_strdup_printf( "%s %s", NADP_GROUP_PROFILE
, profile_id
);
505 g_free( profile_id
);
508 group_name
= g_strdup( NADP_GROUP_DESKTOP
);
511 if( !na_data_boxed_is_default( boxed
) || def
->write_if_default
){
515 case NA_DATA_TYPE_STRING
:
516 str_value
= na_boxed_get_string( NA_BOXED( boxed
));
518 if( !strcmp( def
->name
, NAFO_DATA_PATH
)){
519 parms
= na_object_get_parameters( object
);
520 tmp
= g_strdup_printf( "%s %s", str_value
, parms
);
526 nadp_desktop_file_set_string( ndf
, group_name
, def
->desktop_entry
, str_value
);
530 case NA_DATA_TYPE_LOCALE_STRING
:
531 str_value
= na_boxed_get_string( NA_BOXED( boxed
));
532 nadp_desktop_file_set_locale_string( ndf
, group_name
, def
->desktop_entry
, str_value
);
536 case NA_DATA_TYPE_BOOLEAN
:
537 bool_value
= GPOINTER_TO_UINT( na_boxed_get_as_void( NA_BOXED( boxed
)));
538 nadp_desktop_file_set_boolean( ndf
, group_name
, def
->desktop_entry
, bool_value
);
541 case NA_DATA_TYPE_STRING_LIST
:
542 slist_value
= ( GSList
* ) na_boxed_get_as_void( NA_BOXED( boxed
));
543 nadp_desktop_file_set_string_list( ndf
, group_name
, def
->desktop_entry
, slist_value
);
544 na_core_utils_slist_free( slist_value
);
547 case NA_DATA_TYPE_UINT
:
548 uint_value
= GPOINTER_TO_UINT( na_boxed_get_as_void( NA_BOXED( boxed
)));
549 nadp_desktop_file_set_uint( ndf
, group_name
, def
->desktop_entry
, uint_value
);
553 g_warning( "%s: unknown type=%u for %s", thisfn
, def
->type
, def
->name
);
554 code
= NA_IIO_PROVIDER_CODE_PROGRAM_ERROR
;
558 nadp_desktop_file_remove_key( ndf
, group_name
, def
->desktop_entry
);
561 g_free( group_name
);
568 nadp_writer_ifactory_provider_write_done( const NAIFactoryProvider
*provider
, void *writer_data
,
569 const NAIFactoryObject
*object
, GSList
**messages
)
571 if( NA_IS_OBJECT_ITEM( object
)){
572 write_done_write_subitems_list( NADP_DESKTOP_FILE( writer_data
), NA_OBJECT_ITEM( object
));
575 return( NA_IIO_PROVIDER_CODE_OK
);
579 write_done_write_subitems_list( NadpDesktopFile
*ndp
, NAObjectItem
*item
)
581 static const gchar
*thisfn
= "nadp_writer_write_done_write_subitems_list";
583 GSList
*profile_groups
, *ip
;
586 subitems
= na_object_get_items_slist( item
);
587 tmp
= g_strdup_printf( "%s (written subitems)", thisfn
);
588 na_core_utils_slist_dump( tmp
, subitems
);
591 nadp_desktop_file_set_string_list(
594 NA_IS_OBJECT_ACTION( item
) ? NADP_KEY_PROFILES
: NADP_KEY_ITEMS_LIST
,
597 profile_groups
= nadp_desktop_file_get_profiles( ndp
);
598 tmp
= g_strdup_printf( "%s (existing profiles)", thisfn
);
599 na_core_utils_slist_dump( tmp
, profile_groups
);
602 for( ip
= profile_groups
; ip
; ip
= ip
->next
){
603 if( na_core_utils_slist_count( subitems
, ( const gchar
* ) ip
->data
) == 0 ){
604 g_debug( "%s: deleting (removed) profile %s", thisfn
, ( const gchar
* ) ip
->data
);
605 nadp_desktop_file_remove_profile( ndp
, ( const gchar
* ) ip
->data
);
609 na_core_utils_slist_free( profile_groups
);
610 na_core_utils_slist_free( subitems
);
613 static ExportFormatFn
*
614 find_export_format_fn( GQuark format
)
616 ExportFormatFn
*found
;
620 i
= st_export_format_fn
;
622 while( i
->format
&& !found
){
623 if( g_quark_from_string( i
->format
) == format
){