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 <gtk/gtk.h>
35 #include <string.h>
36
37 #include <api/na-object-api.h>
38
39 #include <core/na-exporter.h>
40 #include <core/na-export-format.h>
41
42 #include "nact-application.h"
43 #include "nact-export-ask.h"
44 #include "nact-tree-model.h"
45 #include "nact-clipboard.h"
46
47 /* private class data
48 */
49 struct _NactClipboardClassPrivate {
50 void *empty; /* so that gcc -pedantic is happy */
51 };
52
53 /* private instance data
54 */
55 typedef struct {
56 guint target;
57 gchar *folder;
58 GList *rows;
59 gboolean copy;
60 }
61 NactClipboardDndData;
62
63 typedef struct {
64 GList *items;
65 gint mode;
66 guint nb_actions;
67 guint nb_profiles;
68 guint nb_menus;
69 }
70 PrimaryData;
71
72 struct _NactClipboardPrivate {
73 gboolean dispose_has_run;
74 BaseWindow *window;
75 GtkClipboard *dnd;
76 GtkClipboard *primary;
77 PrimaryData *primary_data;
78 gboolean primary_got;
79 };
80
81 #define NACT_CLIPBOARD_ATOM gdk_atom_intern( "_NACT_CLIPBOARD", FALSE )
82 #define NACT_CLIPBOARD_NACT_ATOM gdk_atom_intern( "ClipboardNautilusActions", FALSE )
83
84 enum {
85 NACT_CLIPBOARD_FORMAT_NACT = 0,
86 NACT_CLIPBOARD_FORMAT_APPLICATION_XML,
87 NACT_CLIPBOARD_FORMAT_TEXT_PLAIN
88 };
89
90 /* clipboard formats
91 * - a special ClipboardNautilusAction format for internal move/copy
92 * and also used by drag and drop operations
93 * - a XdndDirectSave, suitable for exporting to a file manager
94 * (note that Nautilus recognized the "XdndDirectSave0" format as XDS
95 * protocol)
96 * - a text (xml) format, to go to clipboard or a text editor
97 */
98 static GtkTargetEntry clipboard_formats[] = {
99 { "ClipboardNautilusActions", 0, NACT_CLIPBOARD_FORMAT_NACT },
100 { "application/xml", 0, NACT_CLIPBOARD_FORMAT_APPLICATION_XML },
101 { "text/plain", 0, NACT_CLIPBOARD_FORMAT_TEXT_PLAIN },
102 };
103
104 static GObjectClass *st_parent_class = NULL;
105
106 static GType register_type( void );
107 static void class_init( NactClipboardClass *klass );
108 static void instance_init( GTypeInstance *instance, gpointer klass );
109 static void instance_dispose( GObject *application );
110 static void instance_finalize( GObject *application );
111
112 static void get_from_dnd_clipboard_callback( GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, guchar *data );
113 static void clear_dnd_clipboard_callback( GtkClipboard *clipboard, NactClipboardDndData *data );
114 static gchar *export_rows( NactClipboard *clipboard, GList *rows, const gchar *dest_folder );
115 static gchar *export_objects( NactClipboard *clipboard, GList *objects );
116 static gchar *export_row_object( NactClipboard *clipboard, NAObject *object, const gchar *dest_folder, GList **exported, gboolean first );
117
118 static void get_from_primary_clipboard_callback( GtkClipboard *gtk_clipboard, GtkSelectionData *selection_data, guint info, NactClipboard *clipboard );
119 static void clear_primary_clipboard( NactClipboard *clipboard );
120 static void clear_primary_clipboard_callback( GtkClipboard *gtk_clipboard, NactClipboard *clipboard );
121 static void dump_primary_clipboard( NactClipboard *clipboard );
122
123 static gchar *clipboard_mode_to_string( gint mode );
124
125 GType
nact_clipboard_get_type(void)126 nact_clipboard_get_type( void )
127 {
128 static GType type = 0;
129
130 if( !type ){
131 type = register_type();
132 }
133
134 return( type );
135 }
136
137 static GType
register_type(void)138 register_type( void )
139 {
140 static const gchar *thisfn = "nact_clipboard_register_type";
141 GType type;
142
143 static GTypeInfo info = {
144 sizeof( NactClipboardClass ),
145 ( GBaseInitFunc ) NULL,
146 ( GBaseFinalizeFunc ) NULL,
147 ( GClassInitFunc ) class_init,
148 NULL,
149 NULL,
150 sizeof( NactClipboard ),
151 0,
152 ( GInstanceInitFunc ) instance_init
153 };
154
155 g_debug( "%s", thisfn );
156
157 type = g_type_register_static( G_TYPE_OBJECT, "NactClipboard", &info, 0 );
158
159 return( type );
160 }
161
162 static void
class_init(NactClipboardClass * klass)163 class_init( NactClipboardClass *klass )
164 {
165 static const gchar *thisfn = "nact_clipboard_class_init";
166 GObjectClass *object_class;
167
168 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
169
170 st_parent_class = g_type_class_peek_parent( klass );
171
172 object_class = G_OBJECT_CLASS( klass );
173 object_class->dispose = instance_dispose;
174 object_class->finalize = instance_finalize;
175
176 klass->private = g_new0( NactClipboardClassPrivate, 1 );
177 }
178
179 static void
instance_init(GTypeInstance * instance,gpointer klass)180 instance_init( GTypeInstance *instance, gpointer klass )
181 {
182 static const gchar *thisfn = "nact_clipboard_instance_init";
183 NactClipboard *self;
184 GdkDisplay *display;
185
186 g_return_if_fail( NACT_IS_CLIPBOARD( instance ));
187
188 g_debug( "%s: instance=%p (%s), klass=%p",
189 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
190
191 self = NACT_CLIPBOARD( instance );
192
193 self->private = g_new0( NactClipboardPrivate, 1 );
194
195 self->private->dispose_has_run = FALSE;
196
197 display = gdk_display_get_default();
198 self->private->dnd = gtk_clipboard_get_for_display( display, NACT_CLIPBOARD_ATOM );
199 self->private->primary = gtk_clipboard_get_for_display( display, GDK_SELECTION_CLIPBOARD );
200 self->private->primary_data = NULL;
201 }
202
203 static void
instance_dispose(GObject * object)204 instance_dispose( GObject *object )
205 {
206 static const gchar *thisfn = "nact_clipboard_instance_dispose";
207 NactClipboard *self;
208
209 g_return_if_fail( NACT_IS_CLIPBOARD( object ));
210
211 self = NACT_CLIPBOARD( object );
212
213 if( !self->private->dispose_has_run ){
214
215 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
216
217 self->private->dispose_has_run = TRUE;
218
219 gtk_clipboard_clear( self->private->dnd );
220 gtk_clipboard_clear( self->private->primary );
221
222 /* chain up to the parent class */
223 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
224 G_OBJECT_CLASS( st_parent_class )->dispose( object );
225 }
226 }
227 }
228
229 static void
instance_finalize(GObject * instance)230 instance_finalize( GObject *instance )
231 {
232 static const gchar *thisfn = "nact_clipboard_instance_finalize";
233 NactClipboard *self;
234
235 g_return_if_fail( NACT_IS_CLIPBOARD( instance ));
236
237 g_debug( "%s: instance=%p (%s)", thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ));
238
239 self = NACT_CLIPBOARD( instance );
240
241 if( self->private->primary_data ){
242 clear_primary_clipboard( self );
243 g_free( self->private->primary_data );
244 }
245
246 g_free( self->private );
247
248 /* chain call to parent class */
249 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
250 G_OBJECT_CLASS( st_parent_class )->finalize( instance );
251 }
252 }
253
254 /**
255 * nact_clipboard_new:
256 *
257 * Returns: a new #NactClipboard object.
258 */
259 NactClipboard *
nact_clipboard_new(BaseWindow * window)260 nact_clipboard_new( BaseWindow *window )
261 {
262 NactClipboard *clipboard;
263
264 g_return_val_if_fail( BASE_IS_WINDOW( window ), NULL );
265
266 clipboard = g_object_new( NACT_TYPE_CLIPBOARD, NULL );
267
268 clipboard->private->window = window;
269
270 return( clipboard );
271 }
272
273 /**
274 * nact_clipboard_dnd_set:
275 * @clipboard: this #NactClipboard instance.
276 * @rows: the list of row references of dragged items.
277 * @folder: the URI of the target folder if any (XDS protocol to outside).
278 * @copy_data: %TRUE if data is to be copied, %FALSE else
279 * (only relevant when drag and drop occurs inside of the tree view).
280 *
281 * Set the selected items into our dnd clipboard.
282 */
283 void
nact_clipboard_dnd_set(NactClipboard * clipboard,guint target,GList * rows,const gchar * folder,gboolean copy_data)284 nact_clipboard_dnd_set( NactClipboard *clipboard, guint target, GList *rows, const gchar *folder, gboolean copy_data )
285 {
286 static const gchar *thisfn = "nact_clipboard_dnd_set";
287 NactClipboardDndData *data;
288 GList *it;
289
290 g_return_if_fail( NACT_IS_CLIPBOARD( clipboard ));
291 g_return_if_fail( rows && g_list_length( rows ));
292
293 if( !clipboard->private->dispose_has_run ){
294
295 data = g_new0( NactClipboardDndData, 1 );
296
297 data->target = target;
298 data->folder = g_strdup( folder );
299 data->rows = NULL;
300 data->copy = copy_data;
301
302 for( it = rows ; it ; it = it->next ){
303 data->rows = g_list_append(
304 data->rows,
305 gtk_tree_row_reference_copy(( GtkTreeRowReference * ) it->data ));
306 }
307
308 gtk_clipboard_set_with_data( clipboard->private->dnd,
309 clipboard_formats, G_N_ELEMENTS( clipboard_formats ),
310 ( GtkClipboardGetFunc ) get_from_dnd_clipboard_callback,
311 ( GtkClipboardClearFunc ) clear_dnd_clipboard_callback,
312 data );
313
314 g_debug( "%s: clipboard=%p, data=%p", thisfn, ( void * ) clipboard, ( void * ) data );
315 }
316 }
317
318 /**
319 * nact_clipboard_dnd_get_data:
320 * @clipboard: this #NactClipboard instance.
321 * @copy_data: will be set to the original value of the drag and drop.
322 *
323 * Returns the list of rows references privously stored.
324 *
325 * The returned list should be gtk_tree_row_reference_free() by the
326 * caller.
327 */
328 GList *
nact_clipboard_dnd_get_data(NactClipboard * clipboard,gboolean * copy_data)329 nact_clipboard_dnd_get_data( NactClipboard *clipboard, gboolean *copy_data )
330 {
331 static const gchar *thisfn = "nact_clipboard_dnd_get_data";
332 GList *rows = NULL;
333 GtkSelectionData *selection;
334 NactClipboardDndData *data;
335 GList *it;
336
337 g_debug( "%s: clipboard=%p", thisfn, ( void * ) clipboard );
338 g_return_val_if_fail( NACT_IS_CLIPBOARD( clipboard ), NULL );
339
340 if( copy_data ){
341 *copy_data = FALSE;
342 }
343
344 if( !clipboard->private->dispose_has_run ){
345
346 selection = gtk_clipboard_wait_for_contents( clipboard->private->dnd, NACT_CLIPBOARD_NACT_ATOM );
347 if( selection ){
348 data = ( NactClipboardDndData * ) gtk_selection_data_get_data( selection );
349
350 if( data->target == NACT_XCHANGE_FORMAT_NACT ){
351 for( it = data->rows ; it ; it = it->next ){
352 rows = g_list_append( rows,
353 gtk_tree_row_reference_copy(( GtkTreeRowReference * ) it->data ));
354 }
355 *copy_data = data->copy;
356 }
357 }
358 gtk_selection_data_free( selection );
359 }
360
361 return( rows );
362 }
363
364 /*
365 * Get text/plain from selected actions.
366 *
367 * This is called when we drop or paste a selection onto an application
368 * willing to deal with Xdnd protocol, for text/plain or application/xml
369 * mime types.
370 *
371 * Selected items may include menus, actions and profiles.
372 * For now, we only exports actions (and profiles) as XML files.
373 *
374 * FIXME: na_xml_writer_get_xml_buffer() returns a valid XML document,
375 * which includes the <?xml ...?> header. Concatenating several valid
376 * XML documents doesn't provide a valid global XML doc, because of
377 * the presence of several <?xml ?> headers inside.
378 */
379 gchar *
nact_clipboard_dnd_get_text(NactClipboard * clipboard,GList * rows)380 nact_clipboard_dnd_get_text( NactClipboard *clipboard, GList *rows )
381 {
382 static const gchar *thisfn = "nact_clipboard_dnd_get_text";
383 gchar *buffer;
384
385 g_return_val_if_fail( NACT_IS_CLIPBOARD( clipboard ), NULL );
386
387 g_debug( "%s: clipboard=%p, rows=%p (count=%u)",
388 thisfn, ( void * ) clipboard, ( void * ) rows, g_list_length( rows ));
389
390 buffer = NULL;
391
392 if( !clipboard->private->dispose_has_run ){
393
394 buffer = export_rows( clipboard, rows, NULL );
395 g_debug( "%s: returning buffer=%p (length=%lu)", thisfn, ( void * ) buffer, g_utf8_strlen( buffer, -1 ));
396 }
397
398 return( buffer );
399 }
400
401 /**
402 * nact_clipboard_dnd_drag_end:
403 * @clipboard: this #NactClipboard instance.
404 *
405 * On drag-end, exports the objects if needed.
406 */
407 void
nact_clipboard_dnd_drag_end(NactClipboard * clipboard)408 nact_clipboard_dnd_drag_end( NactClipboard *clipboard )
409 {
410 static const gchar *thisfn = "nact_clipboard_dnd_drag_end";
411 GtkSelectionData *selection;
412 NactClipboardDndData *data;
413 gchar *buffer;
414
415 g_debug( "%s: clipboard=%p", thisfn, ( void * ) clipboard );
416 g_return_if_fail( NACT_IS_CLIPBOARD( clipboard ));
417
418 if( !clipboard->private->dispose_has_run ){
419
420 selection = gtk_clipboard_wait_for_contents( clipboard->private->dnd, NACT_CLIPBOARD_NACT_ATOM );
421 g_debug( "%s: selection=%p", thisfn, ( void * ) selection );
422
423 if( selection ){
424 data = ( NactClipboardDndData * ) gtk_selection_data_get_data( selection );
425 g_debug( "%s: data=%p (NactClipboardDndData)", thisfn, ( void * ) data );
426
427 if( data->target == NACT_XCHANGE_FORMAT_XDS ){
428 g_debug( "%s: folder=%s", thisfn, data->folder );
429 buffer = export_rows( clipboard, data->rows, data->folder );
430 g_free( buffer );
431 }
432
433 gtk_selection_data_free( selection );
434 }
435 }
436 }
437
438 /**
439 * nact_clipboard_dnd_clear:
440 * @clipboard: this #NactClipboard instance.
441 *
442 * Clears the drag-and-drop clipboard.
443 *
444 * At least called on drag-begin.
445 */
446 void
nact_clipboard_dnd_clear(NactClipboard * clipboard)447 nact_clipboard_dnd_clear( NactClipboard *clipboard )
448 {
449 g_debug( "nact_clipboard_dnd_clear: clipboard=%p", ( void * ) clipboard );
450
451 gtk_clipboard_clear( clipboard->private->dnd );
452 }
453
454 static void
get_from_dnd_clipboard_callback(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,guchar * data)455 get_from_dnd_clipboard_callback( GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, guchar *data )
456 {
457 static const gchar *thisfn = "nact_clipboard_get_from_dnd_clipboard_callback";
458 GdkAtom selection_data_target;
459
460 selection_data_target = gtk_selection_data_get_target( selection_data );
461
462 g_debug( "%s: clipboard=%p, selection_data=%p, target=%s, info=%d, data=%p",
463 thisfn, ( void * ) clipboard,
464 ( void * ) selection_data, gdk_atom_name( selection_data_target ), info, ( void * ) data );
465
466 gtk_selection_data_set( selection_data,
467 selection_data_target, 8, data, sizeof( NactClipboardDndData ));
468 }
469
470 static void
clear_dnd_clipboard_callback(GtkClipboard * clipboard,NactClipboardDndData * data)471 clear_dnd_clipboard_callback( GtkClipboard *clipboard, NactClipboardDndData *data )
472 {
473 static const gchar *thisfn = "nact_clipboard_clear_dnd_clipboard_callback";
474
475 g_debug( "%s: clipboard=%p, data=%p", thisfn, ( void * ) clipboard, ( void * ) data );
476
477 g_free( data->folder );
478 g_list_foreach( data->rows, ( GFunc ) gtk_tree_row_reference_free, NULL );
479 g_list_free( data->rows );
480 g_free( data );
481 }
482
483 /*
484 * returns a buffer which contains all exported items if dest_folder is null
485 * else export items as files to target directory, returning an empty string
486 */
487 static gchar *
export_rows(NactClipboard * clipboard,GList * rows,const gchar * dest_folder)488 export_rows( NactClipboard *clipboard, GList *rows, const gchar *dest_folder )
489 {
490 static const gchar *thisfn = "nact_clipboard_export_rows";
491 GString *data;
492 GtkTreeModel *model;
493 GList *exported, *irow;
494 GtkTreePath *path;
495 GtkTreeIter iter;
496 NAObject *object;
497 gchar *buffer;
498 gboolean first;
499
500 g_debug( "%s: clipboard=%p, rows=%p (count=%d), dest_folder=%s",
501 thisfn, ( void * ) clipboard, ( void * ) rows, g_list_length( rows ), dest_folder );
502
503 first = TRUE;
504 buffer = NULL;
505 exported = NULL;
506 data = g_string_new( "" );
507 model = gtk_tree_row_reference_get_model(( GtkTreeRowReference * ) rows->data );
508
509 for( irow = rows ; irow ; irow = irow->next ){
510 path = gtk_tree_row_reference_get_path(( GtkTreeRowReference * ) irow->data );
511 if( path ){
512 gtk_tree_model_get_iter( model, &iter, path );
513 gtk_tree_path_free( path );
514 gtk_tree_model_get( model, &iter, TREE_COLUMN_NAOBJECT, &object, -1 );
515 buffer = export_row_object( clipboard, object, dest_folder, &exported, first );
516 if( buffer && strlen( buffer )){
517 data = g_string_append( data, buffer );
518 g_free( buffer );
519 }
520 g_object_unref( object );
521 }
522 first = FALSE;
523 }
524
525 g_list_free( exported );
526 return( g_string_free( data, FALSE ));
527 }
528
529 static gchar *
export_objects(NactClipboard * clipboard,GList * objects)530 export_objects( NactClipboard *clipboard, GList *objects )
531 {
532 gchar *buffer;
533 GString *data;
534 GList *exported;
535 GList *iobj;
536 NAObject *object;
537 gboolean first;
538
539 first = TRUE;
540 buffer = NULL;
541 exported = NULL;
542 data = g_string_new( "" );
543
544 for( iobj = objects ; iobj ; iobj = iobj->next ){
545 object = NA_OBJECT( iobj->data );
546 buffer = export_row_object( clipboard, object, NULL, &exported, first );
547 if( buffer && strlen( buffer )){
548 data = g_string_append( data, buffer );
549 g_free( buffer );
550 }
551 g_object_unref( object );
552 first = FALSE;
553 }
554
555 g_list_free( exported );
556 return( g_string_free( data, FALSE ));
557 }
558
559 /*
560 * export to a buffer if dest_folder is null, and returns this buffer
561 * else export to a new file in the target directory (returning an empty string)
562 *
563 * exported maintains a list of exported items, so that the same item is not
564 * exported twice
565 */
566 static gchar *
export_row_object(NactClipboard * clipboard,NAObject * object,const gchar * dest_folder,GList ** exported,gboolean first)567 export_row_object( NactClipboard *clipboard, NAObject *object, const gchar *dest_folder, GList **exported, gboolean first )
568 {
569 static const gchar *thisfn = "nact_clipboard_export_row_object";
570 GList *subitems, *isub;
571 NactApplication *application;
572 NAUpdater *updater;
573 NAObjectItem *item;
574 gchar *item_label;
575 gint index;
576 GString *data;
577 gchar *buffer;
578 gchar *format;
579 gchar *fname;
580 GSList *msgs;
581
582 data = g_string_new( "" );
583
584 /* if we have a menu, first export the subitems
585 */
586 if( NA_IS_OBJECT_MENU( object )){
587 subitems = na_object_get_items( object );
588
589 for( isub = subitems ; isub ; isub = isub->next ){
590 buffer = export_row_object( clipboard, isub->data, dest_folder, exported, first );
591 if( buffer && strlen( buffer )){
592 data = g_string_append( data, buffer );
593 g_free( buffer );
594 }
595 first = FALSE;
596 }
597 }
598
599 /* only export NAObjectItem type
600 * here, object may be a menu, an action or a profile
601 */
602 msgs = NULL;
603 item = ( NAObjectItem * ) object;
604 if( NA_IS_OBJECT_PROFILE( object )){
605 item = NA_OBJECT_ITEM( na_object_get_parent( object ));
606 }
607
608 application = NACT_APPLICATION( base_window_get_application( clipboard->private->window ));
609 updater = nact_application_get_updater( application );
610
611 index = g_list_index( *exported, ( gconstpointer ) item );
612 if( index == -1 ){
613
614 item_label = na_object_get_label( item );
615 g_debug( "%s: exporting %s", thisfn, item_label );
616 g_free( item_label );
617
618 *exported = g_list_prepend( *exported, ( gpointer ) item );
619 format = na_settings_get_string( NA_IPREFS_EXPORT_PREFERRED_FORMAT, NULL, NULL );
620 g_return_val_if_fail( format && strlen( format ), NULL );
621
622 if( !strcmp( format, EXPORTER_FORMAT_ASK )){
623 g_free( format );
624 format = nact_export_ask_user( clipboard->private->window, item, first );
625 g_return_val_if_fail( format && strlen( format ), NULL );
626 }
627
628 if( strcmp( format, EXPORTER_FORMAT_NOEXPORT ) != 0 ){
629 if( dest_folder ){
630 fname = na_exporter_to_file( NA_PIVOT( updater ), item, dest_folder, format, &msgs );
631 g_free( fname );
632
633 } else {
634 buffer = na_exporter_to_buffer( NA_PIVOT( updater ), item, format, &msgs );
635 if( buffer && strlen( buffer )){
636 data = g_string_append( data, buffer );
637 g_free( buffer );
638 }
639 }
640 }
641
642 g_free( format );
643 }
644
645 return( g_string_free( data, FALSE ));
646 }
647
648 /**
649 * nact_clipboard_primary_set:
650 * @clipboard: this #NactClipboard object.
651 * @items: a list of #NAObject items
652 * @mode: where do these items come from ?
653 * Or what is the operation which has led the items to the clipboard?
654 *
655 * Installs a copy of provided items in the clipboard.
656 *
657 * Rationale: when cutting an item to the clipboard, the next paste
658 * will keep its same original id, and it is safe because this is
659 * actually what we want when we cut/paste.
660 *
661 * Contrarily, when we copy/paste, we are waiting for a new element
662 * which has the same characteristics that the previous one ; we so
663 * have to renumber actions/menus items when copying into the clipboard.
664 *
665 * Note that we use NAIDuplicable interface without actually taking care
666 * of what is origin or so, as origin will be reinitialized when getting
667 * data out of the clipboard.
668 */
669 void
nact_clipboard_primary_set(NactClipboard * clipboard,GList * items,gint mode)670 nact_clipboard_primary_set( NactClipboard *clipboard, GList *items, gint mode )
671 {
672 static const gchar *thisfn = "nact_clipboard_primary_set";
673 PrimaryData *user_data;
674 GList *it;
675
676 g_debug( "%s: clipboard=%p, items=%p (count=%d), mode=%d",
677 thisfn, ( void * ) clipboard, ( void * ) items, g_list_length( items ), mode );
678 g_return_if_fail( NACT_IS_CLIPBOARD( clipboard ));
679
680 if( !clipboard->private->dispose_has_run ){
681
682 user_data = clipboard->private->primary_data;
683
684 if( user_data == NULL ){
685 user_data = g_new0( PrimaryData, 1 );
686 clipboard->private->primary_data = user_data;
687 g_debug( "%s: allocating PrimaryData=%p", thisfn, ( void * ) user_data );
688
689 } else {
690 clear_primary_clipboard( clipboard );
691 }
692
693 na_object_item_count_items( items,
694 ( gint * ) &user_data->nb_menus,
695 ( gint * ) &user_data->nb_actions,
696 ( gint * ) &user_data->nb_profiles,
697 FALSE );
698
699 for( it = items ; it ; it = it->next ){
700 user_data->items =
701 g_list_prepend( user_data->items, na_object_duplicate( it->data, DUPLICATE_REC ));
702 }
703 user_data->items = g_list_reverse( user_data->items );
704
705 user_data->mode = mode;
706
707 gtk_clipboard_set_with_data( clipboard->private->primary,
708 clipboard_formats, G_N_ELEMENTS( clipboard_formats ),
709 ( GtkClipboardGetFunc ) get_from_primary_clipboard_callback,
710 ( GtkClipboardClearFunc ) clear_primary_clipboard_callback,
711 clipboard );
712
713 clipboard->private->primary_got = FALSE;
714 }
715 }
716
717 /**
718 * nact_clipboard_primary_get:
719 * @clipboard: this #NactClipboard object.
720 *
721 * Returns: a copy of the list of items previously referenced in the
722 * internal clipboard.
723 *
724 * We allocate a new id for items in order to be ready to paste another
725 * time.
726 */
727 GList *
nact_clipboard_primary_get(NactClipboard * clipboard,gboolean * relabel)728 nact_clipboard_primary_get( NactClipboard *clipboard, gboolean *relabel )
729 {
730 static const gchar *thisfn = "nact_clipboard_primary_get";
731 GList *items = NULL;
732 GtkSelectionData *selection;
733 PrimaryData *user_data;
734 GList *it;
735 NAObject *obj;
736
737 g_debug( "%s: clipboard=%p", thisfn, ( void * ) clipboard );
738 g_return_val_if_fail( NACT_IS_CLIPBOARD( clipboard ), NULL );
739 g_return_val_if_fail( relabel, NULL );
740
741 if( !clipboard->private->dispose_has_run ){
742
743 selection = gtk_clipboard_wait_for_contents( clipboard->private->primary, NACT_CLIPBOARD_NACT_ATOM );
744
745 if( selection ){
746 user_data = ( PrimaryData * ) gtk_selection_data_get_data( selection );
747 g_debug( "%s: retrieving PrimaryData=%p", thisfn, ( void * ) user_data );
748
749 if( user_data ){
750 for( it = user_data->items ; it ; it = it->next ){
751 obj = NA_OBJECT( na_object_duplicate( it->data, DUPLICATE_REC ));
752 na_object_set_origin( obj, NULL );
753 items = g_list_prepend( items, obj );
754 }
755 items = g_list_reverse( items );
756
757 *relabel = (( user_data->mode == CLIPBOARD_MODE_CUT && clipboard->private->primary_got ) ||
758 user_data->mode == CLIPBOARD_MODE_COPY );
759
760 clipboard->private->primary_got = TRUE;
761 }
762
763 gtk_selection_data_free( selection );
764 }
765 }
766
767 return( items );
768 }
769
770 /**
771 * nact_clipboard_primary_counts:
772 * @clipboard: this #NactClipboard object.
773 *
774 * Returns some counters on content of primary clipboard.
775 */
776 void
nact_clipboard_primary_counts(NactClipboard * clipboard,guint * actions,guint * profiles,guint * menus)777 nact_clipboard_primary_counts( NactClipboard *clipboard, guint *actions, guint *profiles, guint *menus )
778 {
779 PrimaryData *user_data;
780
781 g_return_if_fail( NACT_IS_CLIPBOARD( clipboard ));
782 g_return_if_fail( actions && profiles && menus );
783
784 if( !clipboard->private->dispose_has_run ){
785
786 *actions = 0;
787 *profiles = 0;
788 *menus = 0;
789
790 user_data = clipboard->private->primary_data;
791 if( user_data ){
792
793 *actions = user_data->nb_actions;
794 *profiles = user_data->nb_profiles;
795 *menus = user_data->nb_menus;
796 }
797 }
798 }
799
800 static void
get_from_primary_clipboard_callback(GtkClipboard * gtk_clipboard,GtkSelectionData * selection_data,guint info,NactClipboard * clipboard)801 get_from_primary_clipboard_callback( GtkClipboard *gtk_clipboard, GtkSelectionData *selection_data, guint info, NactClipboard *clipboard )
802 {
803 static const gchar *thisfn = "nact_clipboard_get_from_primary_clipboard_callback";
804 PrimaryData *user_data;
805 gchar *buffer;
806 GdkAtom selection_data_target;
807
808 selection_data_target = gtk_selection_data_get_target( selection_data );
809
810 g_debug( "%s: gtk_clipboard=%p, selection_data=%p, target=%s, info=%d, clipboard=%p",
811 thisfn, ( void * ) gtk_clipboard,
812 ( void * ) selection_data, gdk_atom_name( selection_data_target ), info, ( void * ) clipboard );
813
814 user_data = clipboard->private->primary_data;
815
816 if( info == NACT_CLIPBOARD_FORMAT_TEXT_PLAIN ){
817 buffer = export_objects( clipboard, user_data->items );
818 gtk_selection_data_set( selection_data,
819 selection_data_target, 8, ( const guchar * ) buffer, strlen( buffer ));
820 g_free( buffer );
821
822 } else {
823 gtk_selection_data_set( selection_data,
824 selection_data_target, 8, ( const guchar * ) user_data, sizeof( PrimaryData ));
825 }
826 }
827
828 static void
clear_primary_clipboard(NactClipboard * clipboard)829 clear_primary_clipboard( NactClipboard *clipboard )
830 {
831 static const gchar *thisfn = "nact_clipboard_clear_primary_clipboard";
832 PrimaryData *user_data;
833
834 g_debug( "%s: clipboard=%p", thisfn, ( void * ) clipboard );
835
836 user_data = clipboard->private->primary_data;
837 g_return_if_fail( user_data != NULL );
838
839 g_list_foreach( user_data->items, ( GFunc ) g_object_unref, NULL );
840 g_list_free( user_data->items );
841 user_data->items = NULL;
842 user_data->nb_menus = 0;
843 user_data->nb_actions = 0;
844 user_data->nb_profiles = 0;
845
846 clipboard->private->primary_got = FALSE;
847 }
848
849 static void
clear_primary_clipboard_callback(GtkClipboard * gtk_clipboard,NactClipboard * clipboard)850 clear_primary_clipboard_callback( GtkClipboard *gtk_clipboard, NactClipboard *clipboard )
851 {
852 }
853
854 /**
855 * nact_clipboard_dump:
856 * @clipboard: this #NactClipboard instance.
857 *
858 * Dumps the content of the primary clipboard.
859 */
860 void
nact_clipboard_dump(NactClipboard * clipboard)861 nact_clipboard_dump( NactClipboard *clipboard )
862 {
863 static const gchar *thisfn = "nact_clipboard_dump";
864
865 g_return_if_fail( NACT_IS_CLIPBOARD( clipboard ));
866
867 if( !clipboard->private->dispose_has_run ){
868
869 g_debug( "%s: window=%p (%s)", thisfn, ( void * ) clipboard->private->window, G_OBJECT_TYPE_NAME( clipboard->private->window ));
870 g_debug( "%s: dnd=%p", thisfn, ( void * ) clipboard->private->dnd );
871 g_debug( "%s: primary=%p", thisfn, ( void * ) clipboard->private->primary );
872 g_debug( "%s: primary_data=%p", thisfn, ( void * ) clipboard->private->primary_data );
873
874 if( clipboard->private->primary_data ){
875 dump_primary_clipboard( clipboard );
876 }
877 }
878 }
879
880 static void
dump_primary_clipboard(NactClipboard * clipboard)881 dump_primary_clipboard( NactClipboard *clipboard )
882 {
883 static const gchar *thisfn = "nact_clipboard_dump_primary";
884 PrimaryData *user_data;
885 gchar *mode;
886 GList *it;
887
888 g_return_if_fail( NACT_IS_CLIPBOARD( clipboard ));
889
890 if( !clipboard->private->dispose_has_run ){
891
892 user_data = clipboard->private->primary_data;
893
894 if( user_data ){
895 g_debug( "%s: user_data->nb_actions=%d", thisfn, user_data->nb_actions );
896 g_debug( "%s: user_data->nb_profiles=%d", thisfn, user_data->nb_profiles );
897 g_debug( "%s: user_data->nb_menus=%d", thisfn, user_data->nb_menus );
898 g_debug( "%s: user_data->items=%p (count=%d)",
899 thisfn,
900 ( void * ) user_data->items,
901 user_data->items ? g_list_length( user_data->items ) : 0 );
902 mode = clipboard_mode_to_string( user_data->mode );
903 g_debug( "%s: user_data->mode=%d (%s)", thisfn, user_data->mode, mode );
904 g_free( mode );
905 for( it = user_data->items ; it ; it = it->next ){
906 na_object_object_dump( NA_OBJECT( it->data ));
907 }
908 }
909
910 g_debug( "%s: clipboard->private->primary_got=%s", thisfn, clipboard->private->primary_got ? "True":"False" );
911 }
912 }
913
914 static gchar *
clipboard_mode_to_string(gint mode)915 clipboard_mode_to_string( gint mode )
916 {
917 gchar *mode_str;
918
919 switch( mode ){
920 case CLIPBOARD_MODE_CUT:
921 mode_str = g_strdup( "CutMode" );
922 break;
923
924 case CLIPBOARD_MODE_COPY:
925 mode_str = g_strdup( "CopyMode" );
926 break;
927
928 default:
929 mode_str = g_strdup( "unknown mode" );
930 break;
931 }
932
933 return( mode_str );
934 }
935