4
* Copyright (c) 2005-2006 Benedikt Meurer <benny@xfce.org>
5
* Copyright (c) 2009 Jannis Pohlmann <jannis@xfce.org>
7
* This program is free software; you can redistribute it and/or modify it
8
* under the terms of the GNU General Public License as published by the Free
9
* Software Foundation; either version 2 of the License, or (at your option)
12
* This program is distributed in the hope that it will be useful, but WITHOUT
13
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17
* You should have received a copy of the GNU General Public License along with
18
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19
* Place, Suite 330, Boston, MA 02111-1307 USA
33
#include "marlin-clipboard-manager.h"
35
#include <glib/gi18n.h>
36
#include "eel-stock-dialogs.h"
38
/*#include <thunar/thunar-application.h>
39
#include <thunar/thunar-clipboard-manager.h>
40
#include <thunar/thunar-dialogs.h>
41
#include <thunar/thunar-gobject-extensions.h>
42
#include <thunar/thunar-private.h>*/
59
TARGET_GNOME_COPIED_FILES,
65
static void marlin_clipboard_manager_finalize (GObject *object);
66
static void marlin_clipboard_manager_get_property (GObject *object,
70
static void marlin_clipboard_manager_file_destroyed (GOFFile *file,
71
MarlinClipboardManager *manager);
72
static void marlin_clipboard_manager_owner_changed (GtkClipboard *clipboard,
73
GdkEventOwnerChange *event,
74
MarlinClipboardManager *manager);
75
static void marlin_clipboard_manager_contents_received (GtkClipboard *clipboard,
76
GtkSelectionData *selection_data,
78
static void marlin_clipboard_manager_targets_received (GtkClipboard *clipboard,
79
GtkSelectionData *selection_data,
81
static void marlin_clipboard_manager_get_callback (GtkClipboard *clipboard,
82
GtkSelectionData *selection_data,
85
static void marlin_clipboard_manager_clear_callback (GtkClipboard *clipboard,
87
static void marlin_clipboard_manager_transfer_files (MarlinClipboardManager *manager,
93
struct _MarlinClipboardManagerClass
95
GObjectClass __parent__;
97
void (*changed) (MarlinClipboardManager *manager);
100
struct _MarlinClipboardManager
104
GtkClipboard *clipboard;
106
GdkAtom x_special_gnome_copied_files;
108
gboolean files_cutted;
114
MarlinClipboardManager *manager;
117
GCallback *new_files_closure;
118
} MarlinClipboardPasteRequest;
122
static const GtkTargetEntry clipboard_targets[] =
124
{ "x-special/gnome-copied-files", 0, TARGET_GNOME_COPIED_FILES },
125
{ "UTF8_STRING", 0, TARGET_UTF8_STRING }
128
static GQuark marlin_clipboard_manager_quark = 0;
129
static guint manager_signals[LAST_SIGNAL];
133
G_DEFINE_TYPE (MarlinClipboardManager, marlin_clipboard_manager, G_TYPE_OBJECT)
138
marlin_clipboard_manager_class_init (MarlinClipboardManagerClass *klass)
140
GObjectClass *gobject_class;
142
gobject_class = G_OBJECT_CLASS (klass);
143
gobject_class->finalize = marlin_clipboard_manager_finalize;
144
gobject_class->get_property = marlin_clipboard_manager_get_property;
147
* MarlinClipboardManager:can-paste:
149
* This property tells whether the current clipboard content of
150
* this #MarlinClipboardManager can be pasted into a folder
151
* displayed by a #MarlinView.
153
g_object_class_install_property (gobject_class,
155
g_param_spec_boolean ("can-paste", "can-paste", "can-paste",
159
// EXO_PARAM_READABLE));
162
* MarlinClipboardManager::changed:
163
* @manager : a #MarlinClipboardManager.
165
* This signal is emitted whenever the contents of the
166
* clipboard associated with @manager changes.
168
manager_signals[CHANGED] =
169
g_signal_new ("changed",
170
G_TYPE_FROM_CLASS (klass),
172
G_STRUCT_OFFSET (MarlinClipboardManagerClass, changed),
174
g_cclosure_marshal_VOID__VOID,
181
marlin_clipboard_manager_init (MarlinClipboardManager *manager)
183
manager->x_special_gnome_copied_files = gdk_atom_intern_static_string ("x-special/gnome-copied-files");
189
marlin_clipboard_manager_finalize (GObject *object)
191
MarlinClipboardManager *manager = MARLIN_CLIPBOARD_MANAGER (object);
194
/* release any pending files */
195
for (lp = manager->files; lp != NULL; lp = lp->next)
197
g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), marlin_clipboard_manager_file_destroyed, manager);
198
g_object_unref (G_OBJECT (lp->data));
200
g_list_free (manager->files);
202
/* disconnect from the clipboard */
203
g_signal_handlers_disconnect_by_func (G_OBJECT (manager->clipboard), marlin_clipboard_manager_owner_changed, manager);
204
g_object_set_qdata (G_OBJECT (manager->clipboard), marlin_clipboard_manager_quark, NULL);
205
g_object_unref (G_OBJECT (manager->clipboard));
207
(*G_OBJECT_CLASS (marlin_clipboard_manager_parent_class)->finalize) (object);
213
marlin_clipboard_manager_get_property (GObject *object,
218
MarlinClipboardManager *manager = MARLIN_CLIPBOARD_MANAGER (object);
223
g_value_set_boolean (value, marlin_clipboard_manager_get_can_paste (manager));
227
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
235
marlin_clipboard_manager_file_destroyed (GOFFile *file,
236
MarlinClipboardManager *manager)
238
g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager));
239
g_return_if_fail (g_list_find (manager->files, file) != NULL);
241
/* remove the file from our list */
242
manager->files = g_list_remove (manager->files, file);
244
/* disconnect from the file */
245
g_signal_handlers_disconnect_by_func (G_OBJECT (file), marlin_clipboard_manager_file_destroyed, manager);
246
g_object_unref (G_OBJECT (file));
252
marlin_clipboard_manager_owner_changed (GtkClipboard *clipboard,
253
GdkEventOwnerChange *event,
254
MarlinClipboardManager *manager)
256
g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
257
g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager));
258
g_return_if_fail (manager->clipboard == clipboard);
260
/* need to take a reference on the manager, because the clipboards
261
* "targets received callback" mechanism is not cancellable.
263
g_object_ref (G_OBJECT (manager));
265
/* request the list of supported targets from the new owner */
266
gtk_clipboard_request_contents (clipboard, gdk_atom_intern_static_string ("TARGETS"),
267
marlin_clipboard_manager_targets_received, manager);
271
convert_lines_to_gfile_list (char **lines)
276
if (lines[0] == NULL) {
281
for (i=0; lines[i] != NULL; i++) {
282
result = g_list_prepend (result, g_file_new_for_uri (lines[i]));
284
return g_list_reverse (result);
288
marlin_clipboard_manager_contents_received (GtkClipboard *clipboard,
289
GtkSelectionData *selection_data,
292
MarlinClipboardPasteRequest *request = user_data;
293
MarlinClipboardManager *manager = MARLIN_CLIPBOARD_MANAGER (request->manager);
294
gboolean path_copy = TRUE;
295
GList *file_list = NULL;
299
/* check whether the retrieval worked */
300
if (G_LIKELY (gtk_selection_data_get_length (selection_data) > 0))
302
/* be sure the selection data is zero-terminated */
303
data = (gchar *) gtk_selection_data_get_data (selection_data);
304
data[gtk_selection_data_get_length (selection_data)] = '\0';
306
/* check whether to copy or move */
307
if (g_ascii_strncasecmp (data, "copy", 4) == 0)
312
else if (g_ascii_strncasecmp (data, "cut", 3) == 0)
318
/* get uris list from selection_data */
319
lines = g_strsplit (data, "\n", 0);
320
file_list = convert_lines_to_gfile_list (lines);
324
/* perform the action if possible */
325
if (G_LIKELY (file_list != NULL))
327
if (G_LIKELY (path_copy))
329
marlin_file_operations_copy_move (file_list,
331
request->target_file,
334
request->new_files_closure,
337
marlin_file_operations_copy_move (file_list,
339
request->target_file,
342
request->new_files_closure,
346
g_list_free_full (file_list, g_object_unref);
348
/* clear the clipboard if it contained "cutted data"
349
* (gtk_clipboard_clear takes care of not clearing
350
* the selection if we don't own it)
352
if (G_UNLIKELY (!path_copy))
353
gtk_clipboard_clear (manager->clipboard);
355
/* check the contents of the clipboard again if either the Xserver or
356
* our GTK+ version doesn't support the XFixes extension */
357
if (!gdk_display_supports_selection_notification (gtk_clipboard_get_display (manager->clipboard)))
359
marlin_clipboard_manager_owner_changed (manager->clipboard, NULL, manager);
364
/* tell the user that we cannot paste */
365
marlin_dialogs_show_error (request->widget, NULL, _("There is nothing on the clipboard to paste"));
368
/* free the request */
369
if (G_LIKELY (request->widget != NULL))
370
g_object_remove_weak_pointer (G_OBJECT (request->widget), (gpointer) &request->widget);
372
g_object_unref (G_OBJECT (request->manager));
373
g_object_unref (request->target_file);
374
g_slice_free (MarlinClipboardPasteRequest, request);
380
marlin_clipboard_manager_targets_received (GtkClipboard *clipboard,
381
GtkSelectionData *selection_data,
384
MarlinClipboardManager *manager = MARLIN_CLIPBOARD_MANAGER (user_data);
389
g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
390
g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager));
391
g_return_if_fail (manager->clipboard == clipboard);
393
/* reset the "can-paste" state */
394
manager->can_paste = FALSE;
396
/* check the list of targets provided by the owner */
397
if (gtk_selection_data_get_targets (selection_data, &targets, &n_targets))
399
for (n = 0; n < n_targets; ++n)
400
if (targets[n] == manager->x_special_gnome_copied_files)
402
manager->can_paste = TRUE;
409
/* notify listeners that we have a new clipboard state */
410
g_signal_emit (manager, manager_signals[CHANGED], 0);
411
g_object_notify (G_OBJECT (manager), "can-paste");
413
/* drop the reference taken for the callback */
414
g_object_unref (manager);
418
marlin_clipboard_file_list_to_string (MarlinClipboardManager *manager,
419
gboolean format_for_text,
428
if (format_for_text) {
429
uris = g_string_new (NULL);
431
uris = g_string_new (manager->files_cutted ? "cut" : "copy");
434
for (i = 0, l = manager->files; l != NULL; l = l->next, i++) {
435
uri = g_file_get_uri(GOF_FILE(l->data)->location);
437
if (format_for_text) {
438
f = g_file_new_for_uri (uri);
439
tmp = g_file_get_parse_name (f);
443
g_string_append (uris, tmp);
446
g_string_append (uris, uri);
449
/* skip newline for last element */
450
if (i + 1 < g_list_length (manager->files)) {
451
g_string_append_c (uris, '\n');
454
g_string_append_c (uris, '\n');
455
g_string_append (uris, uri);
462
return g_string_free (uris, FALSE);
466
marlin_clipboard_manager_get_callback (GtkClipboard *clipboard,
467
GtkSelectionData *selection_data,
471
MarlinClipboardManager *manager = MARLIN_CLIPBOARD_MANAGER (user_data);
475
g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
476
g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager));
477
g_return_if_fail (manager->clipboard == clipboard);
481
case TARGET_GNOME_COPIED_FILES:
482
str = marlin_clipboard_file_list_to_string (manager, FALSE, &len);
483
gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *) str, len);
487
case TARGET_UTF8_STRING:
488
str = marlin_clipboard_file_list_to_string (manager, TRUE, &len);
489
gtk_selection_data_set_text (selection_data, str, len);
490
//gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *) string_list, strlen (string_list));
495
g_assert_not_reached ();
502
marlin_clipboard_manager_clear_callback (GtkClipboard *clipboard,
505
MarlinClipboardManager *manager = MARLIN_CLIPBOARD_MANAGER (user_data);
508
g_return_if_fail (GTK_IS_CLIPBOARD (clipboard));
509
g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager));
510
g_return_if_fail (manager->clipboard == clipboard);
512
/* release the pending files */
513
for (lp = manager->files; lp != NULL; lp = lp->next)
515
g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), marlin_clipboard_manager_file_destroyed, manager);
516
g_object_unref (G_OBJECT (lp->data));
518
g_list_free (manager->files);
519
manager->files = NULL;
525
marlin_clipboard_manager_transfer_files (MarlinClipboardManager *manager,
532
/* release any pending files */
533
for (lp = manager->files; lp != NULL; lp = lp->next)
535
g_signal_handlers_disconnect_by_func (G_OBJECT (lp->data), marlin_clipboard_manager_file_destroyed, manager);
536
g_object_unref (G_OBJECT (lp->data));
538
g_list_free (manager->files);
540
/* remember the transfer operation */
541
manager->files_cutted = !copy;
543
/* setup the new file list */
544
for (lp = files, manager->files = NULL; lp != NULL; lp = lp->next)
546
file = g_object_ref (G_OBJECT (lp->data));
547
manager->files = g_list_prepend (manager->files, file);
548
g_signal_connect (G_OBJECT (file), "destroy", G_CALLBACK (marlin_clipboard_manager_file_destroyed), manager);
551
/* acquire the CLIPBOARD ownership */
552
gtk_clipboard_set_with_owner (manager->clipboard, clipboard_targets,
553
G_N_ELEMENTS (clipboard_targets),
554
marlin_clipboard_manager_get_callback,
555
marlin_clipboard_manager_clear_callback,
558
/* Need to fake a "owner-change" event here if the Xserver doesn't support clipboard notification */
559
if (!gdk_display_supports_selection_notification (gtk_clipboard_get_display (manager->clipboard)))
560
marlin_clipboard_manager_owner_changed (manager->clipboard, NULL, manager);
566
* marlin_clipboard_manager_new_get_for_display:
567
* @display : a #GdkDisplay.
569
* Determines the #MarlinClipboardManager that is used to manage
570
* the clipboard on the given @display.
572
* The caller is responsible for freeing the returned object
573
* using g_object_unref() when it's no longer needed.
575
* Return value: the #MarlinClipboardManager for @display.
577
MarlinClipboardManager*
578
marlin_clipboard_manager_new_get_for_display (GdkDisplay *display)
580
MarlinClipboardManager *manager;
581
GtkClipboard *clipboard;
583
g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL);
585
/* generate the quark on-demand */
586
if (G_UNLIKELY (marlin_clipboard_manager_quark == 0))
587
marlin_clipboard_manager_quark = g_quark_from_static_string ("marlin-clipboard-manager");
589
/* figure out the clipboard for the given display */
590
clipboard = gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
592
/* check if a clipboard manager exists */
593
manager = g_object_get_qdata (G_OBJECT (clipboard), marlin_clipboard_manager_quark);
594
if (G_LIKELY (manager != NULL))
596
g_object_ref (G_OBJECT (manager));
600
/* allocate a new manager */
601
manager = g_object_new (MARLIN_TYPE_CLIPBOARD_MANAGER, NULL);
602
manager->clipboard = g_object_ref (G_OBJECT (clipboard));
603
g_object_set_qdata (G_OBJECT (clipboard), marlin_clipboard_manager_quark, manager);
605
/* listen for the "owner-change" signal on the clipboard */
606
g_signal_connect (G_OBJECT (manager->clipboard), "owner-change",
607
G_CALLBACK (marlin_clipboard_manager_owner_changed), manager);
615
* marlin_clipboard_manager_get_can_paste:
616
* @manager : a #MarlinClipboardManager.
618
* Tells whether the contents of the clipboard represented
619
* by @manager can be pasted into a folder.
621
* Return value: %TRUE if the contents of the clipboard
622
* represented by @manager can be pasted
626
marlin_clipboard_manager_get_can_paste (MarlinClipboardManager *manager)
628
g_return_val_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager), FALSE);
629
return manager->can_paste;
635
* marlin_clipboard_manager_has_cutted_file:
636
* @manager : a #MarlinClipboardManager.
637
* @file : a #GOFFile.
639
* Checks whether @file was cutted to the given @manager earlier.
641
* Return value: %TRUE if @file is on the cutted list of @manager.
644
marlin_clipboard_manager_has_cutted_file (MarlinClipboardManager *manager,
647
g_return_val_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager), FALSE);
648
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
650
return (manager->files_cutted && g_list_find (manager->files, file) != NULL);
654
marlin_clipboard_manager_has_file (MarlinClipboardManager *manager,
657
g_return_val_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager), FALSE);
658
g_return_val_if_fail (GOF_IS_FILE (file), FALSE);
660
return (g_list_find (manager->files, file) != NULL);
664
marlin_clipboard_manager_count_files (MarlinClipboardManager *manager)
666
g_return_val_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager), 0);
668
return g_list_length (manager->files);
672
* marlin_clipboard_manager_copy_files:
673
* @manager : a #MarlinClipboardManager.
674
* @files : a list of #GOFFile<!---->s.
676
* Sets the clipboard represented by @manager to
677
* contain the @files and marks them to be copied
678
* when the user pastes from the clipboard.
681
marlin_clipboard_manager_copy_files (MarlinClipboardManager *manager,
684
g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager));
685
marlin_clipboard_manager_transfer_files (manager, TRUE, files);
691
* marlin_clipboard_manager_cut_files:
692
* @manager : a #MarlinClipboardManager.
693
* @files : a list of #GOFFile<!---->s.
695
* Sets the clipboard represented by @manager to
696
* contain the @files and marks them to be moved
697
* when the user pastes from the clipboard.
700
marlin_clipboard_manager_cut_files (MarlinClipboardManager *manager,
703
g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager));
704
marlin_clipboard_manager_transfer_files (manager, FALSE, files);
710
* marlin_clipboard_manager_paste_files:
711
* @manager : a #MarlinClipboardManager.
712
* @target_file : the #GFile of the folder to which the contents on the clipboard
714
* @widget : a #GtkWidget, on which to perform the paste or %NULL if no widget is
716
* @new_files_closure : a #GClosure to connect to the job's "new-files" signal,
717
* which will be emitted when the job finishes with the
718
* list of #GFile<!---->s created by the job, or
719
* %NULL if you're not interested in the signal.
721
* Pastes the contents from the clipboard associated with @manager to the directory
722
* referenced by @target_file.
725
marlin_clipboard_manager_paste_files (MarlinClipboardManager *manager,
728
GCallback *new_files_closure)
730
MarlinClipboardPasteRequest *request;
731
g_return_if_fail (MARLIN_IS_CLIPBOARD_MANAGER (manager));
732
g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
734
/* prepare the paste request */
735
request = g_slice_new0 (MarlinClipboardPasteRequest);
736
request->manager = g_object_ref (G_OBJECT (manager));
737
request->target_file = g_object_ref (target_file);
738
request->widget = widget;
740
/* take a reference on the closure (if any) */
741
if (G_LIKELY (new_files_closure != NULL))
743
request->new_files_closure = new_files_closure;
746
/* get notified when the widget is destroyed prior to
747
* completing the clipboard contents retrieval
749
if (G_LIKELY (request->widget != NULL))
750
g_object_add_weak_pointer (G_OBJECT (request->widget), (gpointer) &request->widget);
752
/* schedule the request */
753
gtk_clipboard_request_contents (manager->clipboard, manager->x_special_gnome_copied_files,
754
marlin_clipboard_manager_contents_received, request);