1 /* nautilus-clipboard.c
2  *
3  * Nautilus Clipboard support.  For now, routines to support component cut
4  * and paste.
5  *
6  * Copyright (C) 1999, 2000  Free Software Foundaton
7  * Copyright (C) 2000, 2001  Eazel, Inc.
8  * Copyright (C) 2016 Carlos Soriano <csoriano@gnome.org>
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
22  *
23  * Authors: Rebecca Schulman <rebecka@eazel.com>,
24  *          Darin Adler <darin@bentspoon.com>
25  */
26 
27 #include <config.h>
28 #include "nautilus-clipboard.h"
29 #include "nautilus-file-utilities.h"
30 #include "nautilus-file.h"
31 
32 #include <glib/gi18n.h>
33 #include <gtk/gtk.h>
34 #include <string.h>
35 
36 static GdkAtom copied_files_atom;
37 
38 typedef struct
39 {
40     gboolean cut;
41     GList *files;
42 } ClipboardInfo;
43 
44 static GList *
convert_lines_to_str_list(char ** lines)45 convert_lines_to_str_list (char **lines)
46 {
47     int i;
48     GList *result;
49 
50     if (lines[0] == NULL)
51     {
52         return NULL;
53     }
54 
55     result = NULL;
56     for (i = 0; lines[i] != NULL; i++)
57     {
58         result = g_list_prepend (result, g_strdup (lines[i]));
59     }
60     return g_list_reverse (result);
61 }
62 
63 static char *
convert_file_list_to_string(ClipboardInfo * info,gboolean format_for_text,gsize * len)64 convert_file_list_to_string (ClipboardInfo *info,
65                              gboolean       format_for_text,
66                              gsize         *len)
67 {
68     GString *uris;
69     char *uri, *tmp;
70     GFile *f;
71     guint i;
72     GList *l;
73 
74     if (format_for_text)
75     {
76         uris = g_string_new (NULL);
77     }
78     else
79     {
80         uris = g_string_new (info->cut ? "cut" : "copy");
81     }
82 
83     for (i = 0, l = info->files; l != NULL; l = l->next, i++)
84     {
85         uri = nautilus_file_get_uri (l->data);
86 
87         if (format_for_text)
88         {
89             f = g_file_new_for_uri (uri);
90             tmp = g_file_get_parse_name (f);
91             g_object_unref (f);
92 
93             if (tmp != NULL)
94             {
95                 g_string_append (uris, tmp);
96                 g_free (tmp);
97             }
98             else
99             {
100                 g_string_append (uris, uri);
101             }
102 
103             /* skip newline for last element */
104             if (i + 1 < g_list_length (info->files))
105             {
106                 g_string_append_c (uris, '\n');
107             }
108         }
109         else
110         {
111             g_string_append_c (uris, '\n');
112             g_string_append (uris, uri);
113         }
114 
115         g_free (uri);
116     }
117 
118     *len = uris->len;
119     return g_string_free (uris, FALSE);
120 }
121 
122 static GList *
get_item_list_from_selection_data(GtkSelectionData * selection_data)123 get_item_list_from_selection_data (GtkSelectionData *selection_data)
124 {
125     GList *items;
126     char **lines;
127 
128     if (gtk_selection_data_get_data_type (selection_data) != copied_files_atom
129         || gtk_selection_data_get_length (selection_data) <= 0)
130     {
131         items = NULL;
132     }
133     else
134     {
135         gchar *data;
136         /* Not sure why it's legal to assume there's an extra byte
137          * past the end of the selection data that it's safe to write
138          * to. But gtk_editable_selection_received does this, so I
139          * think it is OK.
140          */
141         data = (gchar *) gtk_selection_data_get_data (selection_data);
142         data[gtk_selection_data_get_length (selection_data)] = '\0';
143         lines = g_strsplit (data, "\n", 0);
144         items = convert_lines_to_str_list (lines);
145         g_strfreev (lines);
146     }
147 
148     return items;
149 }
150 
151 GList *
nautilus_clipboard_get_uri_list_from_selection_data(GtkSelectionData * selection_data)152 nautilus_clipboard_get_uri_list_from_selection_data (GtkSelectionData *selection_data)
153 {
154     GList *items;
155 
156     items = get_item_list_from_selection_data (selection_data);
157     if (items)
158     {
159         /* Line 0 is "cut" or "copy", so uris start at line 1. */
160         items = g_list_remove (items, items->data);
161     }
162 
163     return items;
164 }
165 
166 GtkClipboard *
nautilus_clipboard_get(GtkWidget * widget)167 nautilus_clipboard_get (GtkWidget *widget)
168 {
169     return gtk_clipboard_get_for_display (gtk_widget_get_display (GTK_WIDGET (widget)),
170                                           GDK_SELECTION_CLIPBOARD);
171 }
172 
173 void
nautilus_clipboard_clear_if_colliding_uris(GtkWidget * widget,const GList * item_uris)174 nautilus_clipboard_clear_if_colliding_uris (GtkWidget   *widget,
175                                             const GList *item_uris)
176 {
177     GtkSelectionData *data;
178     GList *clipboard_item_uris, *l;
179     gboolean collision;
180 
181     collision = FALSE;
182     data = gtk_clipboard_wait_for_contents (nautilus_clipboard_get (widget),
183                                             copied_files_atom);
184     if (data == NULL)
185     {
186         return;
187     }
188 
189     clipboard_item_uris = nautilus_clipboard_get_uri_list_from_selection_data (data);
190 
191     for (l = (GList *) item_uris; l; l = l->next)
192     {
193         if (g_list_find_custom ((GList *) clipboard_item_uris, l->data,
194                                 (GCompareFunc) g_strcmp0))
195         {
196             collision = TRUE;
197             break;
198         }
199     }
200 
201     if (collision)
202     {
203         gtk_clipboard_clear (nautilus_clipboard_get (widget));
204     }
205 
206     if (clipboard_item_uris)
207     {
208         g_list_free_full (clipboard_item_uris, g_free);
209     }
210 }
211 
212 gboolean
nautilus_clipboard_is_cut_from_selection_data(GtkSelectionData * selection_data)213 nautilus_clipboard_is_cut_from_selection_data (GtkSelectionData *selection_data)
214 {
215     GList *items;
216     gboolean is_cut_from_selection_data;
217 
218     items = get_item_list_from_selection_data (selection_data);
219     is_cut_from_selection_data = items != NULL &&
220                                  g_strcmp0 ((gchar *) items->data, "cut") == 0;
221 
222     g_list_free_full (items, g_free);
223 
224     return is_cut_from_selection_data;
225 }
226 
227 static void
on_get_clipboard(GtkClipboard * clipboard,GtkSelectionData * selection_data,guint info,gpointer user_data)228 on_get_clipboard (GtkClipboard     *clipboard,
229                   GtkSelectionData *selection_data,
230                   guint             info,
231                   gpointer          user_data)
232 {
233     char **uris;
234     GList *l;
235     int i;
236     ClipboardInfo *clipboard_info;
237     GdkAtom target;
238 
239     clipboard_info = (ClipboardInfo *) user_data;
240 
241     target = gtk_selection_data_get_target (selection_data);
242 
243     if (gtk_targets_include_uri (&target, 1))
244     {
245         uris = g_malloc ((g_list_length (clipboard_info->files) + 1) * sizeof (char *));
246         i = 0;
247 
248         for (l = clipboard_info->files; l != NULL; l = l->next)
249         {
250             uris[i] = nautilus_file_get_uri (l->data);
251             i++;
252         }
253 
254         uris[i] = NULL;
255 
256         gtk_selection_data_set_uris (selection_data, uris);
257 
258         g_strfreev (uris);
259     }
260     else if (gtk_targets_include_text (&target, 1))
261     {
262         char *str;
263         gsize len;
264 
265         str = convert_file_list_to_string (clipboard_info, TRUE, &len);
266         gtk_selection_data_set_text (selection_data, str, len);
267         g_free (str);
268     }
269     else if (target == copied_files_atom)
270     {
271         char *str;
272         gsize len;
273 
274         str = convert_file_list_to_string (clipboard_info, FALSE, &len);
275         gtk_selection_data_set (selection_data, copied_files_atom, 8, (guchar *) str, len);
276         g_free (str);
277     }
278 }
279 
280 static void
on_clear_clipboard(GtkClipboard * clipboard,gpointer user_data)281 on_clear_clipboard (GtkClipboard *clipboard,
282                     gpointer      user_data)
283 {
284     ClipboardInfo *clipboard_info = (ClipboardInfo *) user_data;
285 
286     nautilus_file_list_free (clipboard_info->files);
287 
288     g_free (clipboard_info);
289 }
290 
291 void
nautilus_clipboard_prepare_for_files(GtkClipboard * clipboard,GList * files,gboolean cut)292 nautilus_clipboard_prepare_for_files (GtkClipboard *clipboard,
293                                       GList        *files,
294                                       gboolean      cut)
295 {
296     GtkTargetList *target_list;
297     GtkTargetEntry *targets;
298     int n_targets;
299     ClipboardInfo *clipboard_info;
300 
301     clipboard_info = g_new (ClipboardInfo, 1);
302     clipboard_info->cut = cut;
303     clipboard_info->files = nautilus_file_list_copy (files);
304 
305     target_list = gtk_target_list_new (NULL, 0);
306     gtk_target_list_add (target_list, copied_files_atom, 0, 0);
307     gtk_target_list_add_uri_targets (target_list, 0);
308     gtk_target_list_add_text_targets (target_list, 0);
309 
310     targets = gtk_target_table_new_from_list (target_list, &n_targets);
311     gtk_target_list_unref (target_list);
312 
313     gtk_clipboard_set_with_data (clipboard,
314                                  targets, n_targets,
315                                  on_get_clipboard, on_clear_clipboard,
316                                  clipboard_info);
317     gtk_target_table_free (targets, n_targets);
318 }
319 
320 GdkAtom
nautilus_clipboard_get_atom(void)321 nautilus_clipboard_get_atom (void)
322 {
323     if (!copied_files_atom)
324     {
325         copied_files_atom = gdk_atom_intern_static_string ("x-special/gnome-copied-files");
326     }
327 
328     return copied_files_atom;
329 }
330