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