1 /*
2  * Nautilus-Actions
3  * A Nautilus extension which offers configurable context menu actions.
4  *
5  * Copyright (C) 2005 The GNOME Foundation
6  * Copyright (C) 2006-2008 Frederic Ruaudel and others (see AUTHORS)
7  * Copyright (C) 2009-2014 Pierre Wieser and others (see AUTHORS)
8  *
9  * Nautilus-Actions 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.
13  *
14  * Nautilus-Actions 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 GNU
17  * General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with Nautilus-Actions; see the file COPYING. If not, see
21  * <http://www.gnu.org/licenses/>.
22  *
23  * Authors:
24  *   Frederic Ruaudel <grumz@grumz.net>
25  *   Rodrigo Moya <rodrigo@gnome-db.org>
26  *   Pierre Wieser <pwieser@trychlos.org>
27  *   ... and many others (see AUTHORS)
28  */
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #include <glib/gi18n.h>
35 #include <gtk/gtk.h>
36 #include <string.h>
37 
38 #include "na-exporter.h"
39 #include "na-export-format.h"
40 #include "na-settings.h"
41 
42 typedef struct {
43 	const gchar *format;				/* export format saved in user's preferences */
44 	const gchar *label;					/* short label */
45 	const gchar *description;			/* full description */
46 	const gchar *image;					/* associated image */
47 }
48 	NAExporterFormatStr;
49 
50 static NAExporterFormatStr st_format_ask = {
51 
52 		EXPORTER_FORMAT_ASK,
53 		N_( "_Ask me" ),
54 		N_( "You will be asked for the format to choose each time an item " \
55 			"is about to be exported." ),
56 		"export-format-ask.png"
57 };
58 
59 /* i18n: NAIExporter is an interface name, do not even try to translate */
60 #define NO_IMPLEMENTATION_MSG			N_( "No NAIExporter implementation found for '%s' format." )
61 
62 static GList *exporter_get_formats( const NAIExporter *exporter );
63 static void   exporter_free_formats( const NAIExporter *exporter, GList * str_list );
64 static gchar *exporter_get_name( const NAIExporter *exporter );
65 static void   on_pixbuf_finalized( gpointer user_data, GObject *pixbuf );
66 
67 /*
68  * na_exporter_get_formats:
69  * @pivot: the #NAPivot instance.
70  *
71  * Returns: a list of #NAExportFormat objects, each of them addressing an
72  * available export format, i.e. a format provided by a module which
73  * implement the #NAIExporter interface.
74  *
75  * The returned list should later be na_exporter_free_formats() by the caller.
76  */
77 GList *
na_exporter_get_formats(const NAPivot * pivot)78 na_exporter_get_formats( const NAPivot *pivot )
79 {
80 	GList *iexporters, *imod;
81 	GList *formats;
82 	GList *str_list, *is;
83 	NAExportFormat *format;
84 
85 	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
86 
87 	formats = NULL;
88 	iexporters = na_pivot_get_providers( pivot, NA_TYPE_IEXPORTER );
89 
90 	for( imod = iexporters ; imod ; imod = imod->next ){
91 		str_list = exporter_get_formats( NA_IEXPORTER( imod->data ));
92 
93 		for( is = str_list ; is ; is = is->next ){
94 			format = na_export_format_new(( NAIExporterFormatv2 * ) is->data );
95 			formats = g_list_prepend( formats, format );
96 		}
97 
98 		exporter_free_formats( NA_IEXPORTER( imod->data ), str_list );
99 	}
100 
101 	na_pivot_free_providers( iexporters );
102 
103 	return( formats );
104 }
105 
106 /*
107  * Returns a GList of NAIExporterFormatv2 structures which describes
108  * the export formats provided by the exporter
109  * If the provider only implements the v1 interface, we dynamically
110  * allocate a new structure and convert the v1 to the v2.
111  */
112 static GList *
exporter_get_formats(const NAIExporter * exporter)113 exporter_get_formats( const NAIExporter *exporter )
114 {
115 	GList *str_list;
116 	guint version;
117 
118 	str_list = NULL;
119 
120 	version = 1;
121 	if( NA_IEXPORTER_GET_INTERFACE( exporter )->get_version ){
122 		version = NA_IEXPORTER_GET_INTERFACE( exporter )->get_version( exporter );
123 	}
124 
125 	if( NA_IEXPORTER_GET_INTERFACE( exporter )->get_formats ){
126 		if( version == 1 ){
127 #ifdef NA_ENABLE_DEPRECATED
128 			const NAIExporterFormat *strv1;
129 			strv1 = NA_IEXPORTER_GET_INTERFACE( exporter )->get_formats( exporter );
130 			while( strv1->format ){
131 				NAIExporterFormatv2 *strv2 = g_new0( NAIExporterFormatv2, 1 );
132 				strv2->version = 2;
133 				strv2->provider = ( NAIExporter * ) exporter;
134 				strv2->format = strv1->format;
135 				strv2->label = strv1->label;
136 				strv2->description = strv1->description;
137 				strv2->pixbuf = NULL;
138 				str_list = g_list_prepend( str_list, strv2 );
139 				strv1++;
140 			}
141 #else
142 			;
143 #endif
144 		} else {
145 			str_list = NA_IEXPORTER_GET_INTERFACE( exporter )->get_formats( exporter );
146 		}
147 	}
148 
149 	return( str_list );
150 }
151 
152 /*
153  * Free the list returned by exporter_get_formats() for this provider
154  */
155 static void
exporter_free_formats(const NAIExporter * exporter,GList * str_list)156 exporter_free_formats( const NAIExporter *exporter, GList *str_list )
157 {
158 	guint version;
159 
160 	version = 1;
161 	if( NA_IEXPORTER_GET_INTERFACE( exporter )->get_version ){
162 		version = NA_IEXPORTER_GET_INTERFACE( exporter )->get_version( exporter );
163 	}
164 
165 	if( version == 1 ){
166 		g_list_foreach( str_list, ( GFunc ) g_free, NULL );
167 		g_list_free( str_list );
168 
169 	} else {
170 		g_return_if_fail( NA_IEXPORTER_GET_INTERFACE( exporter )->free_formats );
171 		NA_IEXPORTER_GET_INTERFACE( exporter )->free_formats( exporter, str_list );
172 	}
173 }
174 
175 /*
176  * na_exporter_free_formats:
177  * @formats: a list of available export formats, as returned by
178  *  na_exporter_get_formats().
179  *
180  * Release the @formats #GList.
181  */
182 void
na_exporter_free_formats(GList * formats)183 na_exporter_free_formats( GList *formats )
184 {
185 	static const gchar *thisfn = "na_exporter_free_formats";
186 
187 	g_debug( "%s: formats=%p (count=%d)", thisfn, ( void * ) formats, g_list_length( formats ));
188 
189 	g_list_foreach( formats, ( GFunc ) g_object_unref, NULL );
190 	g_list_free( formats );
191 }
192 
193 /*
194  * na_exporter_get_ask_option:
195  *
196  * Returns the 'Ask me' option.
197  *
198  * Since: 3.2
199  */
200 NAIOption *
na_exporter_get_ask_option(void)201 na_exporter_get_ask_option( void )
202 {
203 	static const gchar *thisfn = "na_exporter_get_ask_option";
204 	NAIExporterFormatv2 *str;
205 	gint width, height;
206 	gchar *fname;
207 	NAExportFormat *format;
208 
209 	if( !gtk_icon_size_lookup( GTK_ICON_SIZE_DIALOG, &width, &height )){
210 		width = height = 48;
211 	}
212 
213 	str = g_new0( NAIExporterFormatv2, 1 );
214 	str->version = 2;
215 	str->provider = NULL;
216 	str->format = g_strdup( st_format_ask.format );
217 	str->label = g_strdup( gettext( st_format_ask.label ));
218 	str->description = g_strdup( gettext( st_format_ask.description ));
219 	if( st_format_ask.image ){
220 		fname = g_strdup_printf( "%s/%s", PKGEXPORTFORMATDIR, st_format_ask.image );
221 		str->pixbuf = gdk_pixbuf_new_from_file_at_size( fname, width, height, NULL );
222 		g_free( fname );
223 		if( str->pixbuf ){
224 			g_debug( "%s: allocating pixbuf at %p", thisfn, str->pixbuf );
225 			g_object_weak_ref( G_OBJECT( str->pixbuf ), ( GWeakNotify ) on_pixbuf_finalized, NULL );
226 		}
227 	}
228 
229 	format = na_export_format_new( str );
230 
231 	if( str->pixbuf ){
232 		g_object_unref( str->pixbuf );
233 	}
234 	g_free( str->description );
235 	g_free( str->label );
236 	g_free( str->format );
237 	g_free( str );
238 
239 	return( NA_IOPTION( format ));
240 }
241 
242 static void
on_pixbuf_finalized(gpointer user_data,GObject * pixbuf)243 on_pixbuf_finalized( gpointer user_data /* ==NULL */, GObject *pixbuf )
244 {
245 	g_debug( "na_exporter_on_pixbuf_finalized: pixbuf=%p", ( void * ) pixbuf );
246 }
247 
248 /*
249  * na_exporter_to_buffer:
250  * @pivot: the #NAPivot pivot for the running application.
251  * @item: a #NAObjectItem-derived object.
252  * @format: the target format identifier.
253  * @messages: a pointer to a #GSList list of strings; the provider
254  *  may append messages to this list, but shouldn't reinitialize it.
255  *
256  * Exports the specified @item in the required @format.
257  *
258  * Returns: the output buffer, as a newly allocated string which should
259  * be g_free() by the caller, or %NULL if an error has been detected.
260  */
261 gchar *
na_exporter_to_buffer(const NAPivot * pivot,const NAObjectItem * item,const gchar * format,GSList ** messages)262 na_exporter_to_buffer( const NAPivot *pivot,
263 		const NAObjectItem *item, const gchar *format, GSList **messages )
264 {
265 	static const gchar *thisfn = "na_exporter_to_buffer";
266 	gchar *buffer;
267 	NAIExporterBufferParmsv2 parms;
268 	NAIExporter *exporter;
269 	gchar *name;
270 	gchar *msg;
271 
272 	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
273 	g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), NULL );
274 
275 	buffer = NULL;
276 
277 	g_debug( "%s: pivot=%p, item=%p (%s), format=%s, messages=%p",
278 			thisfn,
279 			( void * ) pivot,
280 			( void * ) item, G_OBJECT_TYPE_NAME( item ),
281 			format,
282 			( void * ) messages );
283 
284 	exporter = na_exporter_find_for_format( pivot, format );
285 	g_debug( "%s: exporter=%p (%s)", thisfn, ( void * ) exporter, G_OBJECT_TYPE_NAME( exporter ));
286 
287 	if( exporter ){
288 		parms.version = 2;
289 		parms.exported = ( NAObjectItem * ) item;
290 		parms.format = g_strdup( format );
291 		parms.buffer = NULL;
292 		parms.messages = messages ? *messages : NULL;
293 
294 		if( NA_IEXPORTER_GET_INTERFACE( exporter )->to_buffer ){
295 			NA_IEXPORTER_GET_INTERFACE( exporter )->to_buffer( exporter, &parms );
296 
297 			if( parms.buffer ){
298 				buffer = parms.buffer;
299 			}
300 
301 		} else {
302 			name = exporter_get_name( exporter );
303 			/* i18n: NAIExporter is an interface name, do not even try to translate */
304 			msg = g_strdup_printf( _( "%s NAIExporter doesn't implement 'to_buffer' interface." ), name );
305 			*messages = g_slist_append( *messages, msg );
306 			g_free( name );
307 		}
308 
309 		g_free( parms.format );
310 
311 	} else {
312 		msg = g_strdup_printf( NO_IMPLEMENTATION_MSG, format );
313 		*messages = g_slist_append( *messages, msg );
314 	}
315 
316 	return( buffer );
317 }
318 
319 /*
320  * na_exporter_to_file:
321  * @pivot: the #NAPivot pivot for the running application.
322  * @item: a #NAObjectItem-derived object.
323  * @folder_uri: the URI of the target folder.
324  * @format: the target format identifier.
325  * @messages: a pointer to a #GSList list of strings; the provider
326  *  may append messages to this list, but shouldn't reinitialize it.
327  *
328  * Exports the specified @item to the target @uri in the required @format.
329  *
330  * Returns: the URI of the exported file, as a newly allocated string which
331  * should be g_free() by the caller, or %NULL if an error has been detected.
332  */
333 gchar *
na_exporter_to_file(const NAPivot * pivot,const NAObjectItem * item,const gchar * folder_uri,const gchar * format,GSList ** messages)334 na_exporter_to_file( const NAPivot *pivot,
335 		const NAObjectItem *item, const gchar *folder_uri, const gchar *format, GSList **messages )
336 {
337 	static const gchar *thisfn = "na_exporter_to_file";
338 	gchar *export_uri;
339 	NAIExporterFileParmsv2 parms;
340 	NAIExporter *exporter;
341 	gchar *msg;
342 	gchar *name;
343 
344 	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
345 	g_return_val_if_fail( NA_IS_OBJECT_ITEM( item ), NULL );
346 
347 	export_uri = NULL;
348 
349 	g_debug( "%s: pivot=%p, item=%p (%s), folder_uri=%s, format=%s, messages=%p",
350 			thisfn,
351 			( void * ) pivot,
352 			( void * ) item, G_OBJECT_TYPE_NAME( item ),
353 			folder_uri,
354 			format,
355 			( void * ) messages );
356 
357 	exporter = na_exporter_find_for_format( pivot, format );
358 
359 	if( exporter ){
360 		parms.version = 2;
361 		parms.exported = ( NAObjectItem * ) item;
362 		parms.folder = ( gchar * ) folder_uri;
363 		parms.format = g_strdup( format );
364 		parms.basename = NULL;
365 		parms.messages = messages ? *messages : NULL;
366 
367 		if( NA_IEXPORTER_GET_INTERFACE( exporter )->to_file ){
368 			NA_IEXPORTER_GET_INTERFACE( exporter )->to_file( exporter, &parms );
369 
370 			if( parms.basename ){
371 				export_uri = g_strdup_printf( "%s%s%s", folder_uri, G_DIR_SEPARATOR_S, parms.basename );
372 			}
373 
374 		} else {
375 			name = exporter_get_name( exporter );
376 			/* i18n: NAIExporter is an interface name, do not even try to translate */
377 			msg = g_strdup_printf( _( "%s NAIExporter doesn't implement 'to_file' interface." ), name );
378 			*messages = g_slist_append( *messages, msg );
379 			g_free( name );
380 		}
381 
382 		g_free( parms.format );
383 
384 	} else {
385 		msg = g_strdup_printf( NO_IMPLEMENTATION_MSG, format );
386 		*messages = g_slist_append( *messages, msg );
387 	}
388 
389 	return( export_uri );
390 }
391 
392 static gchar *
exporter_get_name(const NAIExporter * exporter)393 exporter_get_name( const NAIExporter *exporter )
394 {
395 	gchar *name;
396 
397 	name = NULL;
398 
399 	if( NA_IEXPORTER_GET_INTERFACE( exporter )->get_name ){
400 		name = NA_IEXPORTER_GET_INTERFACE( exporter )->get_name( exporter );
401 	}
402 
403 	return( name );
404 }
405 
406 /**
407  * na_exporter_find_for_format:
408  * @pivot: the #NAPivot instance.
409  * @format: the string identifier of the searched format.
410  *
411  * Returns: the #NAIExporter instance which provides the @format export
412  * format. The returned instance is owned by @pivot, and should not be
413  * released by the caller.
414  */
415 NAIExporter *
na_exporter_find_for_format(const NAPivot * pivot,const gchar * format)416 na_exporter_find_for_format( const NAPivot *pivot, const gchar *format )
417 {
418 	NAIExporter *exporter;
419 	GList *formats, *ifmt;
420 	gchar *id;
421 	NAExportFormat *export_format;
422 
423 	g_return_val_if_fail( NA_IS_PIVOT( pivot ), NULL );
424 
425 	exporter = NULL;
426 	formats = na_exporter_get_formats( pivot );
427 
428 	for( ifmt = formats ; ifmt && !exporter ; ifmt = ifmt->next ){
429 
430 		export_format = NA_EXPORT_FORMAT( ifmt->data );
431 		id = na_ioption_get_id( NA_IOPTION( export_format ));
432 		if( !strcmp( id, format )){
433 			exporter = na_export_format_get_provider( NA_EXPORT_FORMAT( ifmt->data ));
434 		}
435 		g_free( id );
436 	}
437 
438 	na_exporter_free_formats( formats );
439 
440 	return( exporter );
441 }
442