nautilus-3.6.3/src/nautilus-desktop-item-properties.c

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 }