Define new 'pivot-prop-loadable' property
[nautilus-actions.git] / src / io-desktop / nadp-writer.c
blob8450e7e82a49ff781b0c22dc7d2935a482199a15
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;
97 GSList *messages;
99 g_return_val_if_fail( NADP_IS_DESKTOP_PROVIDER( provider ), FALSE );
101 able_to = FALSE;
102 messages = NULL;
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 ));
112 } else {
113 able_to = na_core_utils_dir_is_writable_path( userdir );
116 g_free( userdir );
118 return( able_to );
122 * This is implementation of NAIIOProvider::write_item method
124 guint
125 nadp_iio_provider_write_item( const NAIIOProvider *provider, const NAObjectItem *item, GSList **messages )
127 static const gchar *thisfn = "nadp_iio_provider_write_item";
128 guint ret;
129 NadpDesktopFile *ndf;
130 gchar *path;
131 gchar *userdir;
132 gchar *id;
133 gchar *bname;
134 GSList *subdirs;
135 gchar *fulldir;
136 gboolean dir_ok;
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 );
145 return( ret );
148 ndf = ( NadpDesktopFile * ) na_object_get_provider_data( item );
150 /* write into the current key file and write it to current path */
151 if( ndf ){
152 g_return_val_if_fail( NADP_IS_DESKTOP_FILE( ndf ), ret );
154 } else {
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 );
158 dir_ok = TRUE;
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 ));
163 dir_ok = FALSE;
166 g_free( userdir );
167 na_core_utils_slist_free( subdirs );
169 if( dir_ok ){
170 id = na_object_get_id( item );
171 bname = g_strdup_printf( "%s%s", id, NADP_DESKTOP_FILE_SUFFIX );
172 g_free( id );
173 path = g_build_filename( fulldir, bname, NULL );
174 g_free( bname );
176 g_free( fulldir );
178 if( dir_ok ){
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 );
182 g_free( path );
186 if( ndf ){
187 ret = write_item( provider, item, ndf, messages );
190 return( ret );
194 * actually writes the item to the existing NadpDesktopFile
195 * as we have chosen 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 :(
203 static guint
204 write_item( const NAIIOProvider *provider, const NAObjectItem *item, NadpDesktopFile *ndf, GSList **messages )
206 static const gchar *thisfn = "nadp_iio_provider_write_item";
207 guint ret;
208 NadpDesktopProvider *self;
210 g_debug( "%s: provider=%p (%s), item=%p (%s), ndf=%p, messages=%p",
211 thisfn,
212 ( void * ) provider, G_OBJECT_TYPE_NAME( provider ),
213 ( void * ) item, G_OBJECT_TYPE_NAME( item ),
214 ( void * ) ndf,
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;
242 return( ret );
245 guint
246 nadp_iio_provider_delete_item( const NAIIOProvider *provider, const NAObjectItem *item, GSList **messages )
248 static const gchar *thisfn = "nadp_iio_provider_delete_item";
249 guint ret;
250 NadpDesktopProvider *self;
251 NadpDesktopFile *ndf;
252 gchar *uri;
254 g_debug( "%s: provider=%p (%s), item=%p (%s), messages=%p",
255 thisfn,
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 );
274 if( ndf ){
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;
280 g_free( uri );
282 } else {
283 g_warning( "%s: NadpDesktopFile is null", thisfn );
284 ret = NA_IIO_PROVIDER_CODE_OK;
287 return( ret );
290 static void
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
307 guint
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";
311 guint ret;
312 NadpDesktopProvider *self;
313 NadpDesktopFile *ndf;
315 g_debug( "%s: provider=%p (%s), dest=%p (%s), source=%p (%s), messages=%p",
316 thisfn,
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.
350 guint
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;
355 ExportFormatFn *fmt;
356 GKeyFile *key_file;
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 );
371 if( !fmt ){
372 code = NA_IEXPORTER_CODE_INVALID_FORMAT;
374 } else {
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;
381 } else {
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 );
391 return( 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.
401 guint
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;
407 ExportFormatFn *fmt;
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 );
422 if( !fmt ){
423 code = NA_IEXPORTER_CODE_INVALID_FORMAT;
425 } else {
426 id = na_object_get_id( parms->exported );
427 parms->basename = g_strdup_printf( "%s%s", id, NADP_DESKTOP_FILE_SUFFIX );
428 g_free( id );
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;
444 g_free( dest_path );
445 g_object_unref( ndf );
449 g_debug( "%s: returning code=%u", thisfn, code );
450 return( code );
453 guint
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 );
464 static void
465 write_start_write_type( NadpDesktopFile *ndp, NAObjectItem *item )
467 nadp_desktop_file_set_string(
468 ndp,
469 NADP_GROUP_DESKTOP,
470 NADP_KEY_TYPE,
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
478 guint
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;
485 guint code;
486 const NADataDef *def;
487 gchar *profile_id;
488 gchar *group_name;
489 gchar *str_value;
490 gboolean bool_value;
491 GSList *slist_value;
492 guint uint_value;
493 gchar *parms, *tmp;
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 );
509 } else {
510 group_name = g_strdup( NADP_GROUP_DESKTOP );
513 if( !na_data_boxed_is_default( boxed ) || def->write_if_default ){
515 switch( def->type ){
517 case NA_DATA_TYPE_STRING:
518 str_value = na_boxed_get_string( NA_BOXED( 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 );
523 g_free( str_value );
524 g_free( parms );
525 str_value = tmp;
528 nadp_desktop_file_set_string( ndf, group_name, def->desktop_entry, str_value );
529 g_free( str_value );
530 break;
532 case NA_DATA_TYPE_LOCALE_STRING:
533 str_value = na_boxed_get_string( NA_BOXED( boxed ));
534 nadp_desktop_file_set_locale_string( ndf, group_name, def->desktop_entry, str_value );
535 g_free( str_value );
536 break;
538 case NA_DATA_TYPE_BOOLEAN:
539 bool_value = GPOINTER_TO_UINT( na_boxed_get_as_void( NA_BOXED( boxed )));
540 nadp_desktop_file_set_boolean( ndf, group_name, def->desktop_entry, bool_value );
541 break;
543 case NA_DATA_TYPE_STRING_LIST:
544 slist_value = ( GSList * ) na_boxed_get_as_void( NA_BOXED( boxed ));
545 nadp_desktop_file_set_string_list( ndf, group_name, def->desktop_entry, slist_value );
546 na_core_utils_slist_free( slist_value );
547 break;
549 case NA_DATA_TYPE_UINT:
550 uint_value = GPOINTER_TO_UINT( na_boxed_get_as_void( NA_BOXED( boxed )));
551 nadp_desktop_file_set_uint( ndf, group_name, def->desktop_entry, uint_value );
552 break;
554 default:
555 g_warning( "%s: unknown type=%u for %s", thisfn, def->type, def->name );
556 code = NA_IIO_PROVIDER_CODE_PROGRAM_ERROR;
559 } else {
560 nadp_desktop_file_remove_key( ndf, group_name, def->desktop_entry );
563 g_free( group_name );
566 return( code );
569 guint
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 );
580 static void
581 write_done_write_subitems_list( NadpDesktopFile *ndp, NAObjectItem *item )
583 static const gchar *thisfn = "nadp_writer_write_done_write_subitems_list";
584 GSList *subitems;
585 GSList *profile_groups, *ip;
586 gchar *tmp;
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 );
591 g_free( tmp );
593 nadp_desktop_file_set_string_list(
594 ndp,
595 NADP_GROUP_DESKTOP,
596 NA_IS_OBJECT_ACTION( item ) ? NADP_KEY_PROFILES : NADP_KEY_ITEMS_LIST,
597 subitems );
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 );
602 g_free( tmp );
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;
619 ExportFormatFn *i;
621 found = NULL;
622 i = st_export_format_fn;
624 while( i->format && !found ){
625 if( g_quark_from_string( i->format ) == format ){
626 found = i;
628 i++;
631 return( found );