NadpDesktopFile: do not try to import empty or not DES-EMA files
[nautilus-actions.git] / src / io-desktop / nadp-writer.c
blob1f1be0a5a34ed2516648d72a20e53b8107c4c269
1 /*
2 * Nautilus-Actions
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 <errno.h>
36 #include <string.h>
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
53 typedef struct {
54 gchar *format;
55 void *foo;
57 ExportFormatFn;
59 static ExportFormatFn st_export_format_fn[] = {
61 { NADP_FORMAT_DESKTOP_V1,
62 NULL },
64 { NULL }
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
79 gboolean
80 nadp_iio_provider_is_willing_to_write( const NAIIOProvider *provider )
82 return( TRUE );
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
91 gboolean
92 nadp_iio_provider_is_able_to_write( const NAIIOProvider *provider )
94 static const gchar *thisfn = "nadp_writer_iio_provider_is_able_to_write";
95 gboolean able_to;
96 gchar *userdir;
98 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider ), FALSE );
100 able_to = 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 ));
110 } else {
111 able_to = na_core_utils_dir_is_writable_path( userdir );
114 g_free( userdir );
116 return( able_to );
120 * This is implementation of NAIIOProvider::write_item method
122 guint
123 nadp_iio_provider_write_item( const NAIIOProvider *provider, const NAObjectItem *item, GSList **messages )
125 static const gchar *thisfn = "nadp_iio_provider_write_item";
126 guint ret;
127 NadpDesktopFile *ndf;
128 gchar *path;
129 gchar *userdir;
130 gchar *id;
131 gchar *bname;
132 GSList *subdirs;
133 gchar *fulldir;
134 gboolean dir_ok;
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 );
143 return( ret );
146 ndf = ( NadpDesktopFile * ) na_object_get_provider_data( item );
148 /* write into the current key file and write it to current path */
149 if( ndf ){
150 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf ), ret );
152 } else {
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 );
156 dir_ok = TRUE;
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 ));
161 dir_ok = FALSE;
164 g_free( userdir );
165 na_core_utils_slist_free( subdirs );
167 if( dir_ok ){
168 id = na_object_get_id( item );
169 bname = g_strdup_printf( "%s%s", id, NADP_DESKTOP_FILE_SUFFIX );
170 g_free( id );
171 path = g_build_filename( fulldir, bname, NULL );
172 g_free( bname );
174 g_free( fulldir );
176 if( dir_ok ){
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 );
180 g_free( path );
184 if( ndf ){
185 ret = write_item( provider, item, ndf, messages );
188 return( ret );
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 :(
201 static guint
202 write_item( const NAIIOProvider *provider, const NAObjectItem *item, NadpDesktopFile *ndf, GSList **messages )
204 static const gchar *thisfn = "nadp_iio_provider_write_item";
205 guint ret;
206 NadpDesktopProvider *self;
208 g_debug( "%s: provider=%p (%s), item=%p (%s), ndf=%p, messages=%p",
209 thisfn,
210 ( void * ) provider, G_OBJECT_TYPE_NAME( provider ),
211 ( void * ) item, G_OBJECT_TYPE_NAME( item ),
212 ( void * ) ndf,
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;
240 return( ret );
243 guint
244 nadp_iio_provider_delete_item( const NAIIOProvider *provider, const NAObjectItem *item, GSList **messages )
246 static const gchar *thisfn = "nadp_iio_provider_delete_item";
247 guint ret;
248 NadpDesktopProvider *self;
249 NadpDesktopFile *ndf;
250 gchar *uri;
252 g_debug( "%s: provider=%p (%s), item=%p (%s), messages=%p",
253 thisfn,
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 );
272 if( ndf ){
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;
278 g_free( uri );
280 } else {
281 g_warning( "%s: NadpDesktopFile is null", thisfn );
282 ret = NA_IIO_PROVIDER_CODE_OK;
285 return( ret );
288 static void
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
305 guint
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";
309 guint ret;
310 NadpDesktopProvider *self;
311 NadpDesktopFile *ndf;
313 g_debug( "%s: provider=%p (%s), dest=%p (%s), source=%p (%s), messages=%p",
314 thisfn,
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.
348 guint
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;
353 ExportFormatFn *fmt;
354 GKeyFile *key_file;
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 );
369 if( !fmt ){
370 code = NA_IEXPORTER_CODE_INVALID_FORMAT;
372 } else {
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;
379 } else {
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 );
389 return( 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.
399 guint
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;
405 ExportFormatFn *fmt;
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 );
420 if( !fmt ){
421 code = NA_IEXPORTER_CODE_INVALID_FORMAT;
423 } else {
424 id = na_object_get_id( parms->exported );
425 parms->basename = g_strdup_printf( "%s%s", id, NADP_DESKTOP_FILE_SUFFIX );
426 g_free( id );
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;
442 g_free( dest_path );
443 g_object_unref( ndf );
447 g_debug( "%s: returning code=%u", thisfn, code );
448 return( code );
451 guint
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 );
462 static void
463 write_start_write_type( NadpDesktopFile *ndp, NAObjectItem *item )
465 nadp_desktop_file_set_string(
466 ndp,
467 NADP_GROUP_DESKTOP,
468 NADP_KEY_TYPE,
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
476 guint
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;
483 guint code;
484 const NADataDef *def;
485 gchar *profile_id;
486 gchar *group_name;
487 gchar *str_value;
488 gboolean bool_value;
489 GSList *slist_value;
490 guint uint_value;
491 gchar *parms, *tmp;
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 );
507 } else {
508 group_name = g_strdup( NADP_GROUP_DESKTOP );
511 if( !na_data_boxed_is_default( boxed ) || def->write_if_default ){
513 switch( def->type ){
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 );
521 g_free( str_value );
522 g_free( parms );
523 str_value = tmp;
526 nadp_desktop_file_set_string( ndf, group_name, def->desktop_entry, str_value );
527 g_free( str_value );
528 break;
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 );
533 g_free( str_value );
534 break;
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 );
539 break;
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 );
545 break;
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 );
550 break;
552 default:
553 g_warning( "%s: unknown type=%u for %s", thisfn, def->type, def->name );
554 code = NA_IIO_PROVIDER_CODE_PROGRAM_ERROR;
557 } else {
558 nadp_desktop_file_remove_key( ndf, group_name, def->desktop_entry );
561 g_free( group_name );
564 return( code );
567 guint
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 );
578 static void
579 write_done_write_subitems_list( NadpDesktopFile *ndp, NAObjectItem *item )
581 static const gchar *thisfn = "nadp_writer_write_done_write_subitems_list";
582 GSList *subitems;
583 GSList *profile_groups, *ip;
584 gchar *tmp;
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 );
589 g_free( tmp );
591 nadp_desktop_file_set_string_list(
592 ndp,
593 NADP_GROUP_DESKTOP,
594 NA_IS_OBJECT_ACTION( item ) ? NADP_KEY_PROFILES : NADP_KEY_ITEMS_LIST,
595 subitems );
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 );
600 g_free( tmp );
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;
617 ExportFormatFn *i;
619 found = NULL;
620 i = st_export_format_fn;
622 while( i->format && !found ){
623 if( g_quark_from_string( i->format ) == format ){
624 found = i;
626 i++;
629 return( found );