No issues found
1 /*
2 * fm-ditem-page.c: Desktop item editing support
3 *
4 * Copyright (C) 2004 James Willcox
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * Authors: James Willcox <jwillcox@gnome.org>
21 *
22 */
23
24 #include <config.h>
25
26 #include "nautilus-desktop-item-properties.h"
27
28 #include <string.h>
29
30 #include <eel/eel-glib-extensions.h>
31 #include <gtk/gtk.h>
32 #include <glib/gi18n.h>
33 #include <libnautilus-extension/nautilus-extension-types.h>
34 #include <libnautilus-extension/nautilus-file-info.h>
35 #include <libnautilus-private/nautilus-file.h>
36 #include <libnautilus-private/nautilus-file-attributes.h>
37
38 #define MAIN_GROUP "Desktop Entry"
39
40 typedef struct ItemEntry {
41 const char *field;
42 const char *description;
43 char *current_value;
44 gboolean localized;
45 gboolean filename;
46 } ItemEntry;
47
48 enum {
49 TARGET_URI_LIST
50 };
51
52 static const GtkTargetEntry target_table[] = {
53 { "text/uri-list", 0, TARGET_URI_LIST }
54 };
55
56 static gboolean
57 _g_key_file_load_from_gfile (GKeyFile *key_file,
58 GFile *file,
59 GKeyFileFlags flags,
60 GError **error)
61 {
62 char *data;
63 gsize len;
64 gboolean res;
65
66 if (!g_file_load_contents (file, NULL, &data, &len, NULL, error)) {
67 return FALSE;
68 }
69
70 res = g_key_file_load_from_data (key_file, data, len, flags, error);
71
72 g_free (data);
73
74 return res;
75 }
76
77 static gboolean
78 _g_key_file_save_to_uri (GKeyFile *key_file,
79 const char *uri,
80 GError **error)
81 {
82 GFile *file;
83 char *data;
84 gsize len;
85
86 data = g_key_file_to_data (key_file, &len, error);
87 if (data == NULL) {
88 return FALSE;
89 }
90 file = g_file_new_for_uri (uri);
91 if (!g_file_replace_contents (file,
92 data, len,
93 NULL, FALSE,
94 G_FILE_CREATE_NONE,
95 NULL, NULL, error)) {
96 g_object_unref (file);
97 g_free (data);
98 return FALSE;
99 }
100 g_object_unref (file);
101 g_free (data);
102 return TRUE;
103 }
104
105 static GKeyFile *
106 _g_key_file_new_from_file (GFile *file,
107 GKeyFileFlags flags,
108 GError **error)
109 {
110 GKeyFile *key_file;
111
112 key_file = g_key_file_new ();
113 if (!_g_key_file_load_from_gfile (key_file, file, flags, error)) {
114 g_key_file_free (key_file);
115 key_file = NULL;
116 }
117 return key_file;
118 }
119
120 static GKeyFile *
121 _g_key_file_new_from_uri (const char *uri,
122 GKeyFileFlags flags,
123 GError **error)
124 {
125 GKeyFile *key_file;
126 GFile *file;
127
128 file = g_file_new_for_uri (uri);
129 key_file = _g_key_file_new_from_file (file, flags, error);
130 g_object_unref (file);
131 return key_file;
132 }
133
134 static ItemEntry *
135 item_entry_new (const char *field,
136 const char *description,
137 gboolean localized,
138 gboolean filename)
139 {
140 ItemEntry *entry;
141
142 entry = g_new0 (ItemEntry, 1);
143 entry->field = field;
144 entry->description = description;
145 entry->localized = localized;
146 entry->filename = filename;
147
148 return entry;
149 }
150
151 static void
152 item_entry_free (ItemEntry *entry)
153 {
154 g_free (entry->current_value);
155 g_free (entry);
156 }
157
158 static void
159 nautilus_desktop_item_properties_url_drag_data_received (GtkWidget *widget, GdkDragContext *context,
160 int x, int y,
161 GtkSelectionData *selection_data,
162 guint info, guint time,
163 GtkEntry *entry)
164 {
165 char **uris;
166 gboolean exactly_one;
167 char *path;
168
169 uris = g_strsplit ((gchar *) gtk_selection_data_get_data (selection_data), "\r\n", 0);
170 exactly_one = uris[0] != NULL && (uris[1] == NULL || uris[1][0] == '\0');
171
172 if (!exactly_one) {
173 g_strfreev (uris);
174 return;
175 }
176
177 path = g_filename_from_uri (uris[0], NULL, NULL);
178 if (path != NULL) {
179 gtk_entry_set_text (entry, path);
180 g_free (path);
181 } else {
182 gtk_entry_set_text (entry, uris[0]);
183 }
184
185 g_strfreev (uris);
186 }
187
188 static void
189 nautilus_desktop_item_properties_exec_drag_data_received (GtkWidget *widget, GdkDragContext *context,
190 int x, int y,
191 GtkSelectionData *selection_data,
192 guint info, guint time,
193 GtkEntry *entry)
194 {
195 char **uris;
196 gboolean exactly_one;
197 NautilusFile *file;
198 GKeyFile *key_file;
199 char *uri, *type, *exec;
200
201 uris = g_strsplit ((gchar *) gtk_selection_data_get_data (selection_data), "\r\n", 0);
202 exactly_one = uris[0] != NULL && (uris[1] == NULL || uris[1][0] == '\0');
203
204 if (!exactly_one) {
205 g_strfreev (uris);
206 return;
207 }
208
209 file = nautilus_file_get_by_uri (uris[0]);
210
211 g_return_if_fail (file != NULL);
212
213 uri = nautilus_file_get_uri (file);
214 if (nautilus_file_is_mime_type (file, "application/x-desktop")) {
215 key_file = _g_key_file_new_from_uri (uri, G_KEY_FILE_NONE, NULL);
216 if (key_file != NULL) {
217 type = g_key_file_get_string (key_file, MAIN_GROUP, "Type", NULL);
218 if (type != NULL && strcmp (type, "Application") == 0) {
219 exec = g_key_file_get_string (key_file, MAIN_GROUP, "Exec", NULL);
220 if (exec != NULL) {
221 g_free (uri);
222 uri = exec;
223 }
224 }
225 g_free (type);
226 g_key_file_free (key_file);
227 }
228 }
229 gtk_entry_set_text (entry,
230 uri?uri:"");
231 gtk_widget_grab_focus (GTK_WIDGET (entry));
232
233 g_free (uri);
234
235 nautilus_file_unref (file);
236
237 g_strfreev (uris);
238 }
239
240 static void
241 save_entry (GtkEntry *entry, GKeyFile *key_file, const char *uri)
242 {
243 GError *error;
244 ItemEntry *item_entry;
245 const char *val;
246 gchar **languages;
247
248 item_entry = g_object_get_data (G_OBJECT (entry), "item_entry");
249 val = gtk_entry_get_text (entry);
250
251 if (strcmp (val, item_entry->current_value) == 0) {
252 return; /* No actual change, don't update file */
253 }
254
255 g_free (item_entry->current_value);
256 item_entry->current_value = g_strdup (val);
257
258 if (item_entry->localized) {
259 languages = (gchar **) g_get_language_names ();
260 g_key_file_set_locale_string (key_file, MAIN_GROUP, item_entry->field, languages[0], val);
261 } else {
262 g_key_file_set_string (key_file, MAIN_GROUP, item_entry->field, val);
263 }
264
265 error = NULL;
266
267 if (!_g_key_file_save_to_uri (key_file, uri, &error)) {
268 g_warning ("%s", error->message);
269 g_error_free (error);
270 }
271 }
272
273 static void
274 entry_activate_cb (GtkWidget *entry,
275 GtkWidget *container)
276 {
277 const char *uri;
278 GKeyFile *key_file;
279
280 uri = g_object_get_data (G_OBJECT (container), "uri");
281 key_file = g_object_get_data (G_OBJECT (container), "keyfile");
282 save_entry (GTK_ENTRY (entry), key_file, uri);
283 }
284
285 static gboolean
286 entry_focus_out_cb (GtkWidget *entry,
287 GdkEventFocus *event,
288 GtkWidget *container)
289 {
290 const char *uri;
291 GKeyFile *key_file;
292
293 uri = g_object_get_data (G_OBJECT (container), "uri");
294 key_file = g_object_get_data (G_OBJECT (container), "keyfile");
295 save_entry (GTK_ENTRY (entry), key_file, uri);
296 return FALSE;
297 }
298
299 static GtkWidget *
300 build_grid (GtkWidget *container,
301 GKeyFile *key_file,
302 GtkSizeGroup *label_size_group,
303 GList *entries)
304 {
305 GtkWidget *grid;
306 GtkWidget *label;
307 GtkWidget *entry;
308 GList *l;
309 char *val;
310
311 grid = gtk_grid_new ();
312 gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL);
313 gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
314 gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
315
316 for (l = entries; l; l = l->next) {
317 ItemEntry *item_entry = (ItemEntry *)l->data;
318 char *label_text;
319
320 label_text = g_strdup_printf ("%s:", item_entry->description);
321 label = gtk_label_new (label_text);
322 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
323 g_free (label_text);
324 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
325 gtk_size_group_add_widget (label_size_group, label);
326
327 entry = gtk_entry_new ();
328 gtk_widget_set_hexpand (entry, TRUE);
329
330 if (item_entry->localized) {
331 val = g_key_file_get_locale_string (key_file,
332 MAIN_GROUP,
333 item_entry->field,
334 NULL, NULL);
335 } else {
336 val = g_key_file_get_string (key_file,
337 MAIN_GROUP,
338 item_entry->field,
339 NULL);
340 }
341
342 item_entry->current_value = g_strdup (val?val:"");
343 gtk_entry_set_text (GTK_ENTRY (entry), item_entry->current_value);
344 g_free (val);
345
346 gtk_container_add (GTK_CONTAINER (grid), label);
347 gtk_grid_attach_next_to (GTK_GRID (grid), entry, label,
348 GTK_POS_RIGHT, 1, 1);
349
350 g_signal_connect (entry, "activate",
351 G_CALLBACK (entry_activate_cb),
352 container);
353 g_signal_connect (entry, "focus_out_event",
354 G_CALLBACK (entry_focus_out_cb),
355 container);
356
357 g_object_set_data_full (G_OBJECT (entry), "item_entry", item_entry,
358 (GDestroyNotify)item_entry_free);
359
360 if (item_entry->filename) {
361 gtk_drag_dest_set (GTK_WIDGET (entry),
362 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
363 target_table, G_N_ELEMENTS (target_table),
364 GDK_ACTION_COPY | GDK_ACTION_MOVE);
365
366 g_signal_connect (entry, "drag_data_received",
367 G_CALLBACK (nautilus_desktop_item_properties_url_drag_data_received),
368 entry);
369 } else if (strcmp (item_entry->field, "Exec") == 0) {
370 gtk_drag_dest_set (GTK_WIDGET (entry),
371 GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
372 target_table, G_N_ELEMENTS (target_table),
373 GDK_ACTION_COPY | GDK_ACTION_MOVE);
374
375 g_signal_connect (entry, "drag_data_received",
376 G_CALLBACK (nautilus_desktop_item_properties_exec_drag_data_received),
377 entry);
378 }
379 }
380
381 /* append dummy row */
382 label = gtk_label_new ("");
383 gtk_container_add (GTK_CONTAINER (grid), label);
384 gtk_size_group_add_widget (label_size_group, label);
385
386 gtk_widget_show_all (grid);
387 return grid;
388 }
389
390 static void
391 create_page (GKeyFile *key_file, GtkWidget *box)
392 {
393 GtkWidget *grid;
394 GList *entries;
395 GtkSizeGroup *label_size_group;
396 char *type;
397
398 entries = NULL;
399
400 type = g_key_file_get_string (key_file, MAIN_GROUP, "Type", NULL);
401
402 if (g_strcmp0 (type, "Link") == 0) {
403 entries = g_list_prepend (entries,
404 item_entry_new ("Comment",
405 _("Comment"), TRUE, FALSE));
406 entries = g_list_prepend (entries,
407 item_entry_new ("URL",
408 _("URL"), FALSE, TRUE));
409 entries = g_list_prepend (entries,
410 item_entry_new ("GenericName",
411 _("Description"), TRUE, FALSE));
412 } else if (g_strcmp0 (type, "Application") == 0) {
413 entries = g_list_prepend (entries,
414 item_entry_new ("Comment",
415 _("Comment"), TRUE, FALSE));
416 entries = g_list_prepend (entries,
417 item_entry_new ("Exec",
418 _("Command"), FALSE, FALSE));
419 entries = g_list_prepend (entries,
420 item_entry_new ("GenericName",
421 _("Description"), TRUE, FALSE));
422 } else {
423 /* we only handle launchers and links */
424
425 /* ensure that we build an empty table with a dummy row at the end */
426 goto build_table;
427 }
428 g_free (type);
429
430 build_table:
431 label_size_group = g_object_get_data (G_OBJECT (box), "label-size-group");
432
433 grid = build_grid (box, key_file, label_size_group, entries);
434 g_list_free (entries);
435
436 gtk_box_pack_start (GTK_BOX (box), grid, FALSE, TRUE, 0);
437 gtk_widget_show_all (GTK_WIDGET (box));
438 }
439
440
441 static void
442 ditem_read_cb (GObject *source_object,
443 GAsyncResult *res,
444 gpointer user_data)
445 {
446 GKeyFile *key_file;
447 GtkWidget *box;
448 gsize file_size;
449 char *file_contents;
450
451 box = GTK_WIDGET (user_data);
452
453 if (g_file_load_contents_finish (G_FILE (source_object),
454 res,
455 &file_contents, &file_size,
456 NULL, NULL)) {
457 key_file = g_key_file_new ();
458 g_object_set_data_full (G_OBJECT (box), "keyfile", key_file, (GDestroyNotify)g_key_file_free);
459 if (g_key_file_load_from_data (key_file, file_contents, file_size, 0, NULL)) {
460 create_page (key_file, box);
461 }
462 g_free (file_contents);
463
464 }
465 g_object_unref (box);
466 }
467
468 static void
469 nautilus_desktop_item_properties_create_begin (const char *uri,
470 GtkWidget *box)
471 {
472 GFile *location;
473
474 location = g_file_new_for_uri (uri);
475 g_object_set_data_full (G_OBJECT (box), "uri", g_strdup (uri), g_free);
476 g_file_load_contents_async (location, NULL, ditem_read_cb, g_object_ref (box));
477 g_object_unref (location);
478 }
479
480 GtkWidget *
481 nautilus_desktop_item_properties_make_box (GtkSizeGroup *label_size_group,
482 GList *files)
483 {
484 NautilusFileInfo *info;
485 char *uri;
486 GtkWidget *box;
487
488 g_assert (nautilus_desktop_item_properties_should_show (files));
489
490 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
491 g_object_set_data_full (G_OBJECT (box), "label-size-group",
492 label_size_group, (GDestroyNotify) g_object_unref);
493
494 info = NAUTILUS_FILE_INFO (files->data);
495
496 uri = nautilus_file_info_get_uri (info);
497 nautilus_desktop_item_properties_create_begin (uri, box);
498 g_free (uri);
499
500 return box;
501 }
502
503 gboolean
504 nautilus_desktop_item_properties_should_show (GList *files)
505 {
506 NautilusFileInfo *info;
507
508 if (!files || files->next) {
509 return FALSE;
510 }
511
512 info = NAUTILUS_FILE_INFO (files->data);
513
514 if (!nautilus_file_info_is_mime_type (info, "application/x-desktop")) {
515 return FALSE;
516 }
517
518 return TRUE;
519 }