2008-05-05 Paolo Borelli <pborelli@katamail.com>
[nautilus.git] / src / nautilus-property-browser.c
blob0564659707c68f5cb2c3b855b5008b0bc588fdf7
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /*
4 * Nautilus
6 * Copyright (C) 2000 Eazel, Inc.
8 * Nautilus is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * Nautilus is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Author: Andy Hertzfeld <andy@eazel.com>
25 /* This is the implementation of the property browser window, which
26 * gives the user access to an extensible palette of properties which
27 * can be dropped on various elements of the user interface to
28 * customize them
31 #include <config.h>
32 #include <math.h>
33 #include "nautilus-property-browser.h"
35 #include <eel/eel-gdk-extensions.h>
36 #include <eel/eel-gdk-pixbuf-extensions.h>
37 #include <eel/eel-glib-extensions.h>
38 #include <eel/eel-gnome-extensions.h>
39 #include <eel/eel-gtk-extensions.h>
40 #include <eel/eel-gtk-macros.h>
41 #include <eel/eel-image-table.h>
42 #include <eel/eel-labeled-image.h>
43 #include <eel/eel-stock-dialogs.h>
44 #include <eel/eel-string.h>
45 #include <eel/eel-xml-extensions.h>
46 #include <librsvg/rsvg.h>
47 #include <libxml/parser.h>
48 #include <gtk/gtkalignment.h>
49 #include <gtk/gtkcolorseldialog.h>
50 #include <gtk/gtkmessagedialog.h>
51 #include <gtk/gtkdnd.h>
52 #include <gtk/gtkentry.h>
53 #include <gtk/gtkeventbox.h>
54 #include <gtk/gtkfilesel.h>
55 #include <gtk/gtkframe.h>
56 #include <gtk/gtkhbox.h>
57 #include <gtk/gtkimage.h>
58 #include <gtk/gtklabel.h>
59 #include <gtk/gtkscrolledwindow.h>
60 #include <gtk/gtkselection.h>
61 #include <gtk/gtksignal.h>
62 #include <gtk/gtkstock.h>
63 #include <gtk/gtktable.h>
64 #include <gtk/gtktogglebutton.h>
65 #include <gtk/gtkradiobutton.h>
66 #include <gtk/gtkvbox.h>
67 #include <gtk/gtkviewport.h>
68 #include <glib/gi18n.h>
69 #include <glib/gstdio.h>
70 #include <gio/gio.h>
71 #include <libgnome/gnome-util.h>
72 #include <libgnome/gnome-help.h>
73 #include <libgnomeui/gnome-color-picker.h>
74 #include <libgnomeui/gnome-icon-entry.h>
75 #include <libgnomeui/gnome-help.h>
76 #include <libgnomeui/gnome-stock-icons.h>
77 #include <libgnomeui/gnome-uidefs.h>
78 #include <libnautilus-private/nautilus-customization-data.h>
79 #include <libnautilus-private/nautilus-directory.h>
80 #include <libnautilus-private/nautilus-emblem-utils.h>
81 #include <libnautilus-private/nautilus-file-utilities.h>
82 #include <libnautilus-private/nautilus-file.h>
83 #include <libnautilus-private/nautilus-global-preferences.h>
84 #include <libnautilus-private/nautilus-metadata.h>
85 #include <libnautilus-private/nautilus-signaller.h>
86 #include <atk/atkrelationset.h>
88 /* property types */
90 typedef enum {
91 NAUTILUS_PROPERTY_NONE,
92 NAUTILUS_PROPERTY_PATTERN,
93 NAUTILUS_PROPERTY_COLOR,
94 NAUTILUS_PROPERTY_EMBLEM
95 } NautilusPropertyType;
97 struct NautilusPropertyBrowserDetails {
98 GtkHBox *container;
100 GtkWidget *content_container;
101 GtkWidget *content_frame;
102 GtkWidget *content_table;
104 GtkWidget *category_container;
105 GtkWidget *category_box;
107 GtkWidget *title_box;
108 GtkWidget *title_label;
109 GtkWidget *help_label;
111 GtkWidget *bottom_box;
113 GtkWidget *add_button;
114 GtkWidget *add_button_label;
115 GtkWidget *add_button_image;
116 GtkWidget *remove_button;
117 GtkWidget *remove_button_label;
118 GtkWidget *remove_button_image;
120 GtkWidget *patterns_dialog;
121 GtkWidget *colors_dialog;
122 GtkWidget *emblems_dialog;
124 GtkWidget *keyword;
125 GtkWidget *emblem_image;
126 GtkWidget *file_entry;
128 GtkWidget *color_picker;
129 GtkWidget *color_name;
131 GList *keywords;
133 char *path;
134 char *category;
135 char *dragged_file;
136 char *drag_type;
137 char *image_path;
139 NautilusPropertyType category_type;
141 int category_position;
143 GdkPixbuf *property_chit;
145 gboolean remove_mode;
146 gboolean keep_around;
147 gboolean has_local;
150 static void nautilus_property_browser_class_init (GtkObjectClass *object_klass);
151 static void nautilus_property_browser_init (GtkObject *object);
152 static void nautilus_property_browser_destroy (GtkObject *object);
153 static void nautilus_property_browser_update_contents (NautilusPropertyBrowser *property_browser);
154 static void nautilus_property_browser_set_category (NautilusPropertyBrowser *property_browser,
155 const char *new_category);
156 static void nautilus_property_browser_set_dragged_file (NautilusPropertyBrowser *property_browser,
157 const char *dragged_file_name);
158 static void nautilus_property_browser_set_drag_type (NautilusPropertyBrowser *property_browser,
159 const char *new_drag_type);
160 static void add_new_button_callback (GtkWidget *widget,
161 NautilusPropertyBrowser *property_browser);
162 static void cancel_remove_mode (NautilusPropertyBrowser *property_browser);
163 static void done_button_callback (GtkWidget *widget,
164 GtkWidget *property_browser);
165 static void help_button_callback (GtkWidget *widget,
166 GtkWidget *property_browser);
167 static void remove_button_callback (GtkWidget *widget,
168 NautilusPropertyBrowser *property_browser);
169 static gboolean nautilus_property_browser_delete_event_callback (GtkWidget *widget,
170 GdkEvent *event,
171 gpointer user_data);
172 static void nautilus_property_browser_hide_callback (GtkWidget *widget,
173 gpointer user_data);
174 static void nautilus_property_browser_drag_end (GtkWidget *widget,
175 GdkDragContext *context);
176 static void nautilus_property_browser_drag_begin (GtkWidget *widget,
177 GdkDragContext *context);
178 static void nautilus_property_browser_drag_data_get (GtkWidget *widget,
179 GdkDragContext *context,
180 GtkSelectionData *selection_data,
181 guint info,
182 guint32 time);
183 static void nautilus_property_browser_theme_changed (gpointer user_data);
184 static void emit_emblems_changed_signal (void);
185 static void emblems_changed_callback (GObject *signaller,
186 NautilusPropertyBrowser *property_browser);
188 /* misc utilities */
189 static char * strip_extension (const char *string_to_strip);
190 static void element_clicked_callback (GtkWidget *image_table,
191 GtkWidget *child,
192 const EelImageTableEvent *event,
193 gpointer callback_data);
195 static GdkPixbuf * make_drag_image (NautilusPropertyBrowser *property_browser,
196 const char *file_name);
197 static GdkPixbuf * make_color_drag_image (NautilusPropertyBrowser *property_browser,
198 const char *color_spec,
199 gboolean trim_edges);
202 #define BROWSER_CATEGORIES_FILE_NAME "browser.xml"
204 #define PROPERTY_BROWSER_WIDTH 540
205 #define PROPERTY_BROWSER_HEIGHT 340
206 #define MAX_EMBLEM_HEIGHT 52
207 #define STANDARD_BUTTON_IMAGE_HEIGHT 42
209 #define MAX_ICON_WIDTH 63
210 #define MAX_ICON_HEIGHT 63
211 #define COLOR_SQUARE_SIZE 48
213 #define LABELED_IMAGE_SPACING 2
214 #define IMAGE_TABLE_X_SPACING 6
215 #define IMAGE_TABLE_Y_SPACING 4
217 #define ERASE_OBJECT_NAME "erase.png"
219 enum {
220 PROPERTY_TYPE
223 static GtkTargetEntry drag_types[] = {
224 { "text/uri-list", 0, PROPERTY_TYPE }
228 EEL_CLASS_BOILERPLATE (NautilusPropertyBrowser,
229 nautilus_property_browser,
230 GTK_TYPE_WINDOW)
232 /* initializing the class object by installing the operations we override */
233 static void
234 nautilus_property_browser_class_init (GtkObjectClass *object_klass)
236 NautilusPropertyBrowserClass *klass;
237 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (object_klass);
239 klass = NAUTILUS_PROPERTY_BROWSER_CLASS (object_klass);
241 object_klass->destroy = nautilus_property_browser_destroy;
242 widget_class->drag_begin = nautilus_property_browser_drag_begin;
243 widget_class->drag_data_get = nautilus_property_browser_drag_data_get;
244 widget_class->drag_end = nautilus_property_browser_drag_end;
247 /* initialize the instance's fields, create the necessary subviews, etc. */
249 static void
250 nautilus_property_browser_init (GtkObject *object)
252 NautilusPropertyBrowser *property_browser;
253 GtkWidget *widget, *temp_box, *temp_hbox, *temp_frame, *vbox;
254 GtkWidget *temp_button, *align;
255 GtkWidget *viewport;
256 char *temp_str;
258 property_browser = NAUTILUS_PROPERTY_BROWSER (object);
259 widget = GTK_WIDGET (object);
261 property_browser->details = g_new0 (NautilusPropertyBrowserDetails, 1);
263 property_browser->details->category = g_strdup ("patterns");
264 property_browser->details->category_type = NAUTILUS_PROPERTY_PATTERN;
266 /* load the chit frame */
267 temp_str = nautilus_pixmap_file ("chit_frame.png");
268 if (temp_str != NULL) {
269 property_browser->details->property_chit = gdk_pixbuf_new_from_file (temp_str, NULL);
271 g_free (temp_str);
273 /* set the initial size of the property browser */
274 gtk_window_set_default_size (GTK_WINDOW (property_browser),
275 PROPERTY_BROWSER_WIDTH,
276 PROPERTY_BROWSER_HEIGHT);
278 /* set the title and standard close accelerator */
279 gtk_window_set_title (GTK_WINDOW (widget), _("Backgrounds and Emblems"));
280 gtk_window_set_wmclass (GTK_WINDOW (widget), "property_browser", "Nautilus");
281 eel_gtk_window_set_up_close_accelerator (GTK_WINDOW (widget));
283 /* create the main vbox. */
284 vbox = gtk_vbox_new (FALSE, 12);
285 gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
286 gtk_widget_show (vbox);
287 gtk_container_add (GTK_CONTAINER (property_browser), vbox);
289 /* create the container box */
290 property_browser->details->container = GTK_HBOX (gtk_hbox_new (FALSE, 6));
291 gtk_widget_show (GTK_WIDGET (property_browser->details->container));
292 gtk_box_pack_start (GTK_BOX (vbox),
293 GTK_WIDGET (property_browser->details->container),
294 TRUE, TRUE, 0);
296 /* make the category container */
297 property_browser->details->category_container = gtk_scrolled_window_new (NULL, NULL);
298 property_browser->details->category_position = -1;
300 viewport = gtk_viewport_new (NULL, NULL);
301 gtk_widget_show (viewport);
302 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
304 gtk_box_pack_start (GTK_BOX (property_browser->details->container),
305 property_browser->details->category_container, FALSE, FALSE, 0);
306 gtk_widget_show (property_browser->details->category_container);
307 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (property_browser->details->category_container),
308 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
310 /* allocate a table to hold the category selector */
311 property_browser->details->category_box = gtk_vbox_new (FALSE, 6);
312 gtk_container_add(GTK_CONTAINER(viewport), property_browser->details->category_box);
313 gtk_container_add (GTK_CONTAINER (property_browser->details->category_container), viewport);
314 gtk_widget_show (GTK_WIDGET (property_browser->details->category_box));
316 /* make the content container vbox */
317 property_browser->details->content_container = gtk_vbox_new (FALSE, 6);
318 gtk_widget_show (property_browser->details->content_container);
319 gtk_box_pack_start (GTK_BOX (property_browser->details->container),
320 property_browser->details->content_container,
321 TRUE, TRUE, 0);
323 /* create the title box */
324 property_browser->details->title_box = gtk_event_box_new();
326 gtk_widget_show(property_browser->details->title_box);
327 gtk_box_pack_start (GTK_BOX(property_browser->details->content_container),
328 property_browser->details->title_box,
329 FALSE, FALSE, 0);
331 temp_frame = gtk_frame_new(NULL);
332 gtk_frame_set_shadow_type(GTK_FRAME(temp_frame), GTK_SHADOW_NONE);
333 gtk_widget_show(temp_frame);
334 gtk_container_add(GTK_CONTAINER(property_browser->details->title_box), temp_frame);
336 temp_hbox = gtk_hbox_new(FALSE, 0);
337 gtk_widget_show(temp_hbox);
339 gtk_container_add(GTK_CONTAINER(temp_frame), temp_hbox);
341 /* add the title label */
342 property_browser->details->title_label = gtk_label_new ("");
343 eel_gtk_label_set_scale (GTK_LABEL (property_browser->details->title_label), PANGO_SCALE_X_LARGE);
344 eel_gtk_label_make_bold (GTK_LABEL (property_browser->details->title_label));
346 gtk_widget_show(property_browser->details->title_label);
347 gtk_box_pack_start (GTK_BOX(temp_hbox), property_browser->details->title_label, FALSE, FALSE, 0);
349 /* add the help label */
350 property_browser->details->help_label = gtk_label_new ("");
351 gtk_widget_show(property_browser->details->help_label);
352 gtk_box_pack_end (GTK_BOX (temp_hbox), property_browser->details->help_label, FALSE, FALSE, 0);
354 /* add the bottom box to hold the command buttons */
355 temp_box = gtk_event_box_new();
356 gtk_widget_show(temp_box);
358 temp_frame = gtk_frame_new(NULL);
359 gtk_frame_set_shadow_type(GTK_FRAME(temp_frame), GTK_SHADOW_NONE);
360 gtk_widget_show(temp_frame);
361 gtk_container_add(GTK_CONTAINER(temp_box), temp_frame);
363 property_browser->details->bottom_box = gtk_hbox_new (FALSE, 6);
364 gtk_widget_show (property_browser->details->bottom_box);
366 gtk_box_pack_end (GTK_BOX (vbox), temp_box, FALSE, FALSE, 0);
367 gtk_container_add (GTK_CONTAINER (temp_frame), property_browser->details->bottom_box);
369 /* create the "help" button */
370 temp_button = gtk_button_new_from_stock (GTK_STOCK_HELP);
372 gtk_widget_show (temp_button);
373 gtk_box_pack_start (GTK_BOX (property_browser->details->bottom_box), temp_button, FALSE, FALSE, 0);
374 g_signal_connect_object (temp_button, "clicked", G_CALLBACK (help_button_callback), property_browser, 0);
376 /* create the "done" button */
377 temp_button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
378 GTK_WIDGET_SET_FLAGS (temp_button, GTK_CAN_DEFAULT);
380 gtk_widget_show (temp_button);
381 gtk_box_pack_end (GTK_BOX (property_browser->details->bottom_box), temp_button, FALSE, FALSE, 0);
382 gtk_widget_grab_default (temp_button);
383 gtk_widget_grab_focus (temp_button);
384 g_signal_connect_object (temp_button, "clicked", G_CALLBACK (done_button_callback), property_browser, 0);
386 /* create the "remove" button */
387 property_browser->details->remove_button = gtk_button_new();
388 property_browser->details->remove_button_label = gtk_label_new_with_mnemonic (_("_Remove..."));
389 gtk_label_set_mnemonic_widget (GTK_LABEL (property_browser->details->remove_button_label),
390 GTK_WIDGET (property_browser->details->remove_button));
391 property_browser->details->remove_button_image = gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_BUTTON);
392 temp_hbox = gtk_hbox_new (FALSE, 2);
393 align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
394 gtk_box_pack_start (GTK_BOX (temp_hbox), property_browser->details->remove_button_image, FALSE, FALSE, 0);
395 gtk_box_pack_end (GTK_BOX (temp_hbox), property_browser->details->remove_button_label, FALSE, FALSE, 0);
396 gtk_container_add (GTK_CONTAINER (property_browser->details->remove_button), align);
397 gtk_container_add (GTK_CONTAINER (align), temp_hbox);
398 gtk_widget_show_all (property_browser->details->remove_button);
400 gtk_box_pack_end (GTK_BOX (property_browser->details->bottom_box),
401 property_browser->details->remove_button, FALSE, FALSE, 0);
403 g_signal_connect_object (property_browser->details->remove_button, "clicked",
404 G_CALLBACK (remove_button_callback), property_browser, 0);
406 /* now create the "add new" button */
407 property_browser->details->add_button = gtk_button_new ();
408 property_browser->details->add_button_label = gtk_label_new_with_mnemonic (_("_Add new..."));
409 gtk_label_set_mnemonic_widget (GTK_LABEL (property_browser->details->add_button_label),
410 GTK_WIDGET (property_browser->details->add_button));
412 property_browser->details->add_button_image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
413 temp_hbox = gtk_hbox_new (FALSE, 2);
414 align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
415 gtk_box_pack_start (GTK_BOX (temp_hbox), property_browser->details->add_button_image, FALSE, FALSE, 0);
416 gtk_box_pack_end (GTK_BOX (temp_hbox), property_browser->details->add_button_label, FALSE, FALSE, 0);
417 gtk_container_add (GTK_CONTAINER (property_browser->details->add_button), align);
418 gtk_container_add (GTK_CONTAINER (align), temp_hbox);
419 gtk_widget_show_all (property_browser->details->add_button);
421 gtk_box_pack_end (GTK_BOX(property_browser->details->bottom_box),
422 property_browser->details->add_button, FALSE, FALSE, 0);
424 g_signal_connect_object (property_browser->details->add_button, "clicked",
425 G_CALLBACK (add_new_button_callback), property_browser, 0);
428 /* now create the actual content, with the category pane and the content frame */
430 /* the actual contents are created when necessary */
431 property_browser->details->content_frame = NULL;
433 /* add a callback for when the theme changes */
434 eel_preferences_add_callback (NAUTILUS_PREFERENCES_THEME,
435 nautilus_property_browser_theme_changed,
436 property_browser);
438 g_signal_connect (property_browser, "delete_event",
439 G_CALLBACK (nautilus_property_browser_delete_event_callback), NULL);
440 g_signal_connect (property_browser, "hide",
441 G_CALLBACK (nautilus_property_browser_hide_callback), NULL);
443 g_signal_connect_object (nautilus_signaller_get_current (),
444 "emblems_changed",
445 G_CALLBACK (emblems_changed_callback), property_browser, 0);
447 /* initially, display the top level */
448 nautilus_property_browser_set_path(property_browser, BROWSER_CATEGORIES_FILE_NAME);
451 /* Destroy the three dialogs for adding patterns/colors/emblems if any of them
452 exist. */
453 static void
454 nautilus_property_browser_destroy_dialogs (NautilusPropertyBrowser *property_browser)
456 if (property_browser->details->patterns_dialog) {
457 gtk_widget_destroy (property_browser->details->patterns_dialog);
458 property_browser->details->patterns_dialog = NULL;
460 if (property_browser->details->colors_dialog) {
461 gtk_widget_destroy (property_browser->details->colors_dialog);
462 property_browser->details->colors_dialog = NULL;
464 if (property_browser->details->emblems_dialog) {
465 gtk_widget_destroy (property_browser->details->emblems_dialog);
466 property_browser->details->emblems_dialog = NULL;
470 static void
471 nautilus_property_browser_destroy (GtkObject *object)
473 NautilusPropertyBrowser *property_browser;
476 property_browser = NAUTILUS_PROPERTY_BROWSER (object);
478 nautilus_property_browser_destroy_dialogs (property_browser);
480 g_free (property_browser->details->path);
481 g_free (property_browser->details->category);
482 g_free (property_browser->details->dragged_file);
483 g_free (property_browser->details->drag_type);
485 eel_g_list_free_deep (property_browser->details->keywords);
487 if (property_browser->details->property_chit) {
488 g_object_unref (property_browser->details->property_chit);
491 g_free (property_browser->details);
493 eel_preferences_remove_callback (NAUTILUS_PREFERENCES_THEME,
494 nautilus_property_browser_theme_changed,
495 property_browser);
497 EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
500 /* create a new instance */
501 NautilusPropertyBrowser *
502 nautilus_property_browser_new (GdkScreen *screen)
504 NautilusPropertyBrowser *browser;
506 browser = NAUTILUS_PROPERTY_BROWSER
507 (gtk_widget_new (nautilus_property_browser_get_type (), NULL));
509 gtk_window_set_screen (GTK_WINDOW (browser), screen);
510 gtk_widget_show (GTK_WIDGET(browser));
512 return browser;
515 /* show the main property browser */
517 void
518 nautilus_property_browser_show (GdkScreen *screen)
520 static GtkWindow *browser = NULL;
522 if (browser == NULL) {
523 browser = GTK_WINDOW (nautilus_property_browser_new (screen));
524 g_object_add_weak_pointer (G_OBJECT (browser),
525 (gpointer *) &browser);
526 } else {
527 gtk_window_set_screen (browser, screen);
528 gtk_window_present (browser);
532 static gboolean
533 nautilus_property_browser_delete_event_callback (GtkWidget *widget,
534 GdkEvent *event,
535 gpointer user_data)
537 /* Hide but don't destroy */
538 gtk_widget_hide(widget);
539 return TRUE;
542 static void
543 nautilus_property_browser_hide_callback (GtkWidget *widget,
544 gpointer user_data)
546 NautilusPropertyBrowser *property_browser;
548 property_browser = NAUTILUS_PROPERTY_BROWSER (widget);
550 cancel_remove_mode (property_browser);
552 /* Destroy the 3 dialogs to add new patterns/colors/emblems. */
553 nautilus_property_browser_destroy_dialogs (property_browser);
556 /* remember the name of the dragged file */
557 static void
558 nautilus_property_browser_set_dragged_file (NautilusPropertyBrowser *property_browser,
559 const char *dragged_file_name)
561 g_free (property_browser->details->dragged_file);
562 property_browser->details->dragged_file = g_strdup (dragged_file_name);
565 /* remember the drag type */
566 static void
567 nautilus_property_browser_set_drag_type (NautilusPropertyBrowser *property_browser,
568 const char *new_drag_type)
570 g_free (property_browser->details->drag_type);
571 property_browser->details->drag_type = g_strdup (new_drag_type);
574 static void
575 nautilus_property_browser_drag_begin (GtkWidget *widget,
576 GdkDragContext *context)
578 NautilusPropertyBrowser *property_browser;
579 GtkWidget *child;
580 GdkPixbuf *pixbuf;
581 int x_delta, y_delta;
582 char *element_name;
584 property_browser = NAUTILUS_PROPERTY_BROWSER (widget);
586 child = g_object_steal_data (G_OBJECT (property_browser), "dragged-image");
587 g_return_if_fail (child != NULL);
589 element_name = g_object_get_data (G_OBJECT (child), "property-name");
590 g_return_if_fail (child != NULL);
592 /* compute the offsets for dragging */
593 if (strcmp (drag_types[0].target, "application/x-color") != 0) {
594 /* it's not a color, so, for now, it must be an image */
595 /* fiddle with the category to handle the "reset" case properly */
596 char * save_category = property_browser->details->category;
597 if (eel_strcmp (property_browser->details->category, "colors") == 0) {
598 property_browser->details->category = "patterns";
600 pixbuf = make_drag_image (property_browser, element_name);
601 property_browser->details->category = save_category;
602 } else {
603 pixbuf = make_color_drag_image (property_browser, element_name, TRUE);
606 /* set the pixmap and mask for dragging */
607 if (pixbuf != NULL) {
608 x_delta = gdk_pixbuf_get_width (pixbuf) / 2;
609 y_delta = gdk_pixbuf_get_height (pixbuf) / 2;
611 gtk_drag_set_icon_pixbuf
612 (context,
613 pixbuf,
614 x_delta, y_delta);
615 g_object_unref (pixbuf);
621 /* drag and drop data get handler */
623 static void
624 nautilus_property_browser_drag_data_get (GtkWidget *widget,
625 GdkDragContext *context,
626 GtkSelectionData *selection_data,
627 guint info,
628 guint32 time)
630 char *image_file_name, *image_file_uri;
631 gboolean is_reset;
632 NautilusPropertyBrowser *property_browser = NAUTILUS_PROPERTY_BROWSER(widget);
634 g_return_if_fail (widget != NULL);
635 g_return_if_fail (context != NULL);
637 switch (info) {
638 case PROPERTY_TYPE:
639 /* formulate the drag data based on the drag type. Eventually, we will
640 probably select the behavior from properties in the category xml definition,
641 but for now we hardwire it to the drag_type */
643 is_reset = FALSE;
644 if (strcmp (property_browser->details->drag_type,
645 "property/keyword") == 0) {
646 char* keyword_str = strip_extension(property_browser->details->dragged_file);
647 gtk_selection_data_set(selection_data, selection_data->target, 8, keyword_str, strlen(keyword_str));
648 g_free(keyword_str);
649 return;
651 else if (strcmp (property_browser->details->drag_type,
652 "application/x-color") == 0) {
653 GdkColor color;
654 guint16 colorArray[4];
656 /* handle the "reset" case as an image */
657 if (eel_strcmp (property_browser->details->dragged_file, RESET_IMAGE_NAME) != 0) {
658 eel_gdk_color_parse (property_browser->details->dragged_file, &color);
660 colorArray[0] = color.red;
661 colorArray[1] = color.green;
662 colorArray[2] = color.blue;
663 colorArray[3] = 0xffff;
665 gtk_selection_data_set(selection_data,
666 selection_data->target, 16, (const char *) &colorArray[0], 8);
667 return;
668 } else {
669 is_reset = TRUE;
674 image_file_name = g_strdup_printf ("%s/%s/%s",
675 NAUTILUS_DATADIR,
676 is_reset ? "patterns" : property_browser->details->category,
677 property_browser->details->dragged_file);
679 if (!g_file_test (image_file_name, G_FILE_TEST_EXISTS)) {
680 char *user_directory;
681 g_free (image_file_name);
683 user_directory = nautilus_get_user_directory ();
684 image_file_name = g_strdup_printf ("%s/%s/%s",
685 user_directory,
686 property_browser->details->category,
687 property_browser->details->dragged_file);
689 g_free (user_directory);
692 image_file_uri = g_filename_to_uri (image_file_name, NULL, NULL);
693 gtk_selection_data_set (selection_data, selection_data->target, 8, image_file_uri, strlen (image_file_uri));
694 g_free (image_file_name);
695 g_free (image_file_uri);
697 break;
698 default:
699 g_assert_not_reached ();
703 /* drag and drop end handler, where we destroy ourselves, since the transaction is complete */
705 static void
706 nautilus_property_browser_drag_end (GtkWidget *widget, GdkDragContext *context)
708 NautilusPropertyBrowser *property_browser = NAUTILUS_PROPERTY_BROWSER(widget);
709 if (!property_browser->details->keep_around) {
710 gtk_widget_hide (GTK_WIDGET (widget));
714 /* utility routine to check if the passed-in uri is an image file */
715 static gboolean
716 ensure_file_is_image (GFile *file)
718 GFileInfo *info;
719 const char *mime_type;
720 gboolean ret;
722 info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, NULL, NULL);
723 if (info == NULL) {
724 return FALSE;
727 mime_type = g_file_info_get_content_type (info);
728 if (mime_type == NULL) {
729 return FALSE;
732 ret = (g_content_type_is_a (mime_type, "image/*") &&
733 !g_content_type_equals (mime_type, "image/svg") &&
734 !g_content_type_equals (mime_type, "image/svg+xml"));
736 g_object_unref (info);
738 return ret;
741 /* create the appropriate pixbuf for the passed in file */
743 static GdkPixbuf *
744 make_drag_image (NautilusPropertyBrowser *property_browser, const char* file_name)
746 GdkPixbuf *pixbuf, *orig_pixbuf;
747 char *image_file_name;
748 char *icon_name;
749 gboolean is_reset;
750 NautilusIconInfo *info;
752 if (property_browser->details->category_type == NAUTILUS_PROPERTY_EMBLEM) {
753 if (strcmp (file_name, "erase") == 0) {
754 pixbuf = NULL;
756 image_file_name = nautilus_pixmap_file (ERASE_OBJECT_NAME);
757 if (image_file_name != NULL) {
758 pixbuf = gdk_pixbuf_new_from_file (image_file_name, NULL);
760 g_free (image_file_name);
761 } else {
762 icon_name = nautilus_emblem_get_icon_name_from_keyword (file_name);
763 info = nautilus_icon_info_lookup_from_name (file_name, NAUTILUS_ICON_SIZE_STANDARD);
764 pixbuf = nautilus_icon_info_get_pixbuf_at_size (info, NAUTILUS_ICON_SIZE_STANDARD);
765 g_object_unref (info);
766 g_free (icon_name);
768 return pixbuf;
771 image_file_name = g_strdup_printf ("%s/%s/%s",
772 NAUTILUS_DATADIR,
773 property_browser->details->category,
774 file_name);
776 if (!g_file_test (image_file_name, G_FILE_TEST_EXISTS)) {
777 char *user_directory;
778 g_free (image_file_name);
780 user_directory = nautilus_get_user_directory ();
782 image_file_name = g_strdup_printf ("%s/%s/%s",
783 user_directory,
784 property_browser->details->category,
785 file_name);
787 g_free (user_directory);
790 orig_pixbuf = gdk_pixbuf_new_from_file (image_file_name, NULL);
792 if (orig_pixbuf == NULL) {
793 orig_pixbuf = rsvg_pixbuf_from_file_at_max_size (image_file_name,
794 MAX_ICON_WIDTH, MAX_ICON_HEIGHT,
795 NULL);
798 g_free (image_file_name);
800 if (orig_pixbuf == NULL) {
801 return NULL;
804 is_reset = eel_strcmp (file_name, RESET_IMAGE_NAME) == 0;
806 if (strcmp (property_browser->details->category, "patterns") == 0 &&
807 property_browser->details->property_chit != NULL) {
808 pixbuf = nautilus_customization_make_pattern_chit (orig_pixbuf, property_browser->details->property_chit, TRUE, is_reset);
809 } else {
810 pixbuf = eel_gdk_pixbuf_scale_down_to_fit (orig_pixbuf, MAX_ICON_WIDTH, MAX_ICON_HEIGHT);
813 gdk_pixbuf_unref (orig_pixbuf);
815 return pixbuf;
819 /* create a pixbuf and fill it with a color */
821 static GdkPixbuf*
822 make_color_drag_image (NautilusPropertyBrowser *property_browser, const char *color_spec, gboolean trim_edges)
824 GdkPixbuf *color_square;
825 GdkPixbuf *ret;
826 int row, col, stride;
827 char *pixels, *row_pixels;
828 GdkColor color;
830 color_square = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, COLOR_SQUARE_SIZE, COLOR_SQUARE_SIZE);
832 eel_gdk_color_parse (color_spec, &color);
833 color.red >>= 8;
834 color.green >>= 8;
835 color.blue >>= 8;
837 pixels = gdk_pixbuf_get_pixels (color_square);
838 stride = gdk_pixbuf_get_rowstride (color_square);
840 /* loop through and set each pixel */
841 for (row = 0; row < COLOR_SQUARE_SIZE; row++) {
842 row_pixels = (pixels + (row * stride));
843 for (col = 0; col < COLOR_SQUARE_SIZE; col++) {
844 *row_pixels++ = color.red;
845 *row_pixels++ = color.green;
846 *row_pixels++ = color.blue;
847 *row_pixels++ = 255;
851 g_assert (color_square != NULL);
853 if (property_browser->details->property_chit != NULL) {
854 ret = nautilus_customization_make_pattern_chit (color_square,
855 property_browser->details->property_chit,
856 trim_edges, FALSE);
857 gdk_pixbuf_unref (color_square);
858 } else {
859 ret = color_square;
862 return ret;
865 /* this callback handles button presses on the category widget. It maintains the active state */
867 static void
868 category_clicked_callback (GtkWidget *widget, char *category_name)
870 NautilusPropertyBrowser *property_browser;
872 property_browser = NAUTILUS_PROPERTY_BROWSER (g_object_get_data (G_OBJECT (widget), "user_data"));
874 /* exit remove mode when the user switches categories, since there might be nothing to remove
875 in the new category */
876 property_browser->details->remove_mode = FALSE;
878 nautilus_property_browser_set_category (property_browser, category_name);
881 static xmlDocPtr
882 read_browser_xml (NautilusPropertyBrowser *property_browser)
884 char *path;
885 xmlDocPtr document;
887 path = nautilus_get_data_file_path (property_browser->details->path);
888 if (path == NULL) {
889 return NULL;
891 document = xmlParseFile (path);
892 g_free (path);
893 return document;
896 static void
897 write_browser_xml (NautilusPropertyBrowser *property_browser,
898 xmlDocPtr document)
900 char *user_directory, *path;
902 user_directory = nautilus_get_user_directory ();
903 path = g_build_filename (user_directory, property_browser->details->path, NULL);
904 g_free (user_directory);
905 xmlSaveFile (path, document);
906 g_free (path);
909 static xmlNodePtr
910 get_color_category (xmlDocPtr document)
912 return eel_xml_get_root_child_by_name_and_property (document, "category", "name", "colors");
915 /* routines to remove specific category types. First, handle colors */
917 static void
918 remove_color (NautilusPropertyBrowser *property_browser, const char* color_name)
920 /* load the local xml file to remove the color */
921 xmlDocPtr document;
922 xmlNodePtr cur_node, color_node;
923 gboolean match;
924 char *name;
926 document = read_browser_xml (property_browser);
927 if (document == NULL) {
928 return;
931 /* find the colors category */
932 cur_node = get_color_category (document);
933 if (cur_node != NULL) {
934 /* loop through the colors to find one that matches */
935 for (color_node = eel_xml_get_children (cur_node);
936 color_node != NULL;
937 color_node = color_node->next) {
939 if (color_node->type != XML_ELEMENT_NODE) {
940 continue;
943 name = xmlGetProp (color_node, "name");
944 match = name != NULL
945 && strcmp (name, color_name) == 0;
946 xmlFree (name);
948 if (match) {
949 xmlUnlinkNode (color_node);
950 xmlFreeNode (color_node);
951 write_browser_xml (property_browser, document);
952 break;
957 xmlFreeDoc (document);
960 /* remove the pattern matching the passed in name */
962 static void
963 remove_pattern(NautilusPropertyBrowser *property_browser, const char* pattern_name)
965 char *pattern_path;
966 char *user_directory;
968 user_directory = nautilus_get_user_directory ();
970 /* build the pathname of the pattern */
971 pattern_path = g_build_filename (user_directory,
972 "patterns",
973 pattern_name,
974 NULL);
975 g_free (user_directory);
977 /* delete the pattern from the pattern directory */
978 if (g_unlink (pattern_path) != 0) {
979 char *message = g_strdup_printf (_("Sorry, but pattern %s could not be deleted."), pattern_name);
980 char *detail = _("Check that you have permission to delete the pattern.");
981 eel_show_error_dialog (message, detail, GTK_WINDOW (property_browser));
982 g_free (message);
985 g_free (pattern_path);
988 /* remove the emblem matching the passed in name */
990 static void
991 remove_emblem (NautilusPropertyBrowser *property_browser, const char* emblem_name)
993 /* build the pathname of the emblem */
994 char *emblem_path;
995 char *user_directory;
997 user_directory = nautilus_get_user_directory ();
999 emblem_path = g_build_filename (user_directory,
1000 "emblems",
1001 emblem_name,
1002 NULL);
1004 g_free (user_directory);
1006 /* delete the emblem from the emblem directory */
1007 if (g_unlink (emblem_path) != 0) {
1008 char *message = g_strdup_printf (_("Sorry, but emblem %s could not be deleted."), emblem_name);
1009 char *detail = _("Check that you have permission to delete the emblem.");
1010 eel_show_error_dialog (message, detail, GTK_WINDOW (property_browser));
1011 g_free (message);
1012 } else {
1013 emit_emblems_changed_signal ();
1015 g_free (emblem_path);
1018 /* handle removing the passed in element */
1020 static void
1021 nautilus_property_browser_remove_element (NautilusPropertyBrowser *property_browser, EelLabeledImage *child)
1023 const char *element_name;
1024 char *color_name;
1026 element_name = g_object_get_data (G_OBJECT (child), "property-name");
1028 /* lookup category and get mode, then case out and handle the modes */
1029 switch (property_browser->details->category_type) {
1030 case NAUTILUS_PROPERTY_PATTERN:
1031 remove_pattern (property_browser, element_name);
1032 break;
1033 case NAUTILUS_PROPERTY_COLOR:
1034 color_name = eel_labeled_image_get_text (child);
1035 remove_color (property_browser, color_name);
1036 g_free (color_name);
1037 break;
1038 case NAUTILUS_PROPERTY_EMBLEM:
1039 remove_emblem (property_browser, element_name);
1040 break;
1041 default:
1042 break;
1046 /* here's where we create the emblem dialog */
1047 static GtkWidget*
1048 nautilus_emblem_dialog_new (NautilusPropertyBrowser *property_browser)
1050 GtkWidget *hbox;
1051 GtkWidget *widget;
1052 GtkWidget *dialog;
1053 GtkWidget *table = gtk_table_new(2, 2, FALSE);
1055 dialog = gtk_dialog_new_with_buttons (_("Create a New Emblem"),
1056 GTK_WINDOW (property_browser), 0,
1057 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1058 GTK_STOCK_OK, GTK_RESPONSE_OK,
1059 NULL);
1061 /* install the table in the dialog */
1062 gtk_container_set_border_width (GTK_CONTAINER (table), 5);
1063 gtk_table_set_row_spacings (GTK_TABLE (table), 6);
1064 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
1065 gtk_widget_show (table);
1067 gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
1068 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
1069 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog)->vbox), 2);
1070 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1071 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1072 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), table, TRUE, TRUE, 0);
1073 gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_OK);
1075 /* make the keyword label and field */
1077 widget = gtk_label_new_with_mnemonic(_("_Keyword:"));
1078 gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
1079 gtk_widget_show(widget);
1080 gtk_table_attach(GTK_TABLE(table), widget, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
1082 property_browser->details->keyword = gtk_entry_new ();
1083 gtk_entry_set_activates_default (GTK_ENTRY (property_browser->details->keyword), TRUE);
1084 gtk_entry_set_max_length (GTK_ENTRY (property_browser->details->keyword), 24);
1085 gtk_widget_show(property_browser->details->keyword);
1086 gtk_table_attach(GTK_TABLE(table), property_browser->details->keyword, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
1087 gtk_widget_grab_focus(property_browser->details->keyword);
1088 gtk_label_set_mnemonic_widget (GTK_LABEL (widget),
1089 GTK_WIDGET (property_browser->details->keyword));
1091 /* default image is the generic emblem */
1092 g_free(property_browser->details->image_path);
1093 property_browser->details->image_path = g_build_filename (NAUTILUS_PIXMAPDIR, "emblems.png", NULL);
1095 /* set up a gnome icon entry to pick the image file */
1096 widget = gtk_label_new_with_mnemonic (_("_Image:"));
1097 gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
1098 gtk_widget_show(widget);
1099 gtk_table_attach(GTK_TABLE(table), widget, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
1101 hbox = gtk_hbox_new (FALSE, 0);
1102 gtk_widget_show (hbox);
1104 property_browser->details->file_entry = gnome_icon_entry_new (NULL, _("Select an Image File for the New Emblem"));
1105 gnome_icon_entry_set_pixmap_subdir (GNOME_ICON_ENTRY(property_browser->details->file_entry),
1106 DATADIR "/pixmaps");
1107 gnome_icon_entry_set_filename (GNOME_ICON_ENTRY(property_browser->details->file_entry),
1108 property_browser->details->image_path);
1109 gtk_label_set_mnemonic_widget (GTK_LABEL (widget),
1110 GTK_WIDGET (property_browser->details->file_entry));
1112 gtk_widget_show(property_browser->details->file_entry);
1113 gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
1114 gtk_box_pack_start (GTK_BOX (hbox), GTK_WIDGET (property_browser->details->file_entry), FALSE, FALSE, 0);
1116 gnome_icon_entry_set_filename (GNOME_ICON_ENTRY (property_browser->details->file_entry), property_browser->details->image_path);
1117 return dialog;
1120 /* create the color selection dialog */
1122 static GtkWidget*
1123 nautilus_color_selection_dialog_new (NautilusPropertyBrowser *property_browser)
1125 GtkWidget *widget;
1126 GtkWidget *dialog;
1127 GtkWidget *table = gtk_table_new(2, 2, FALSE);
1129 dialog = gtk_dialog_new_with_buttons (_("Create a New Color:"),
1130 GTK_WINDOW (property_browser), 0,
1131 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1132 GTK_STOCK_OK, GTK_RESPONSE_OK,
1133 NULL);
1135 /* install the table in the dialog */
1137 gtk_widget_show (table);
1138 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0);
1139 gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_OK);
1141 /* make the name label and field */
1143 widget = gtk_label_new_with_mnemonic(_("Color _name:"));
1144 gtk_widget_show(widget);
1145 gtk_table_attach(GTK_TABLE(table), widget, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
1147 property_browser->details->color_name = gtk_entry_new ();
1148 gtk_entry_set_activates_default (GTK_ENTRY (property_browser->details->color_name), TRUE);
1149 gtk_entry_set_max_length (GTK_ENTRY (property_browser->details->color_name), 24);
1150 gtk_widget_grab_focus (property_browser->details->color_name);
1151 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), property_browser->details->color_name);
1152 gtk_widget_show(property_browser->details->color_name);
1153 gtk_table_attach(GTK_TABLE(table), property_browser->details->color_name, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
1154 gtk_widget_grab_focus(property_browser->details->color_name);
1156 /* default image is the generic emblem */
1157 g_free(property_browser->details->image_path);
1159 widget = gtk_label_new_with_mnemonic(_("Color _value:"));
1160 gtk_widget_show(widget);
1161 gtk_table_attach(GTK_TABLE(table), widget, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
1163 /* set up a gnome file entry to pick the image file */
1164 property_browser->details->color_picker = gnome_color_picker_new ();
1165 gtk_widget_show (property_browser->details->color_picker);
1166 gtk_label_set_mnemonic_widget (GTK_LABEL (widget), property_browser->details->color_picker);
1168 gtk_table_attach(GTK_TABLE(table), property_browser->details->color_picker, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
1171 return dialog;
1174 /* add the newly selected file to the browser images */
1175 static void
1176 add_pattern_to_browser (const char *path_name, gpointer *data)
1178 char *directory_path, *source_file_name, *destination_name;
1179 char *path_uri, *basename;
1180 char *user_directory;
1181 GFile *src, *dest;
1183 NautilusPropertyBrowser *property_browser = NAUTILUS_PROPERTY_BROWSER (data);
1185 /* make sure that it's a valid path */
1186 if (path_name == NULL || path_name[0] != '/') {
1187 char *message;
1188 char *detail;
1189 if (path_name != NULL) {
1190 message = g_strdup_printf (_("Sorry, but \"%s\" is not a valid file name."), path_name);
1191 detail = _("Please check the spelling and try again.");
1192 } else {
1193 message = g_strdup (_("Sorry, but you did not supply a valid file name."));
1194 detail = _("Please try again.");
1196 eel_show_error_dialog (message, detail, GTK_WINDOW (property_browser));
1197 g_free (message);
1198 return;
1201 /* fetch the mime type and make sure that the file is an image */
1202 path_uri = g_filename_to_uri (path_name, NULL, NULL);
1204 /* don't allow the user to change the reset image */
1205 basename = g_path_get_basename (path_name);
1206 if (basename && eel_strcmp (basename, RESET_IMAGE_NAME) == 0) {
1207 eel_show_error_dialog (_("Sorry, but you cannot replace the reset image."),
1208 _("Reset is a special image that cannot be deleted."),
1209 NULL);
1210 g_free (path_uri);
1211 g_free (basename);
1212 return;
1215 g_free (path_uri);
1216 g_free (basename);
1218 user_directory = nautilus_get_user_directory ();
1220 /* copy the image file to the patterns directory */
1221 directory_path = g_build_filename (user_directory, "patterns", NULL);
1222 g_free (user_directory);
1223 source_file_name = strrchr (path_name, '/');
1224 destination_name = g_build_filename (directory_path, source_file_name + 1, NULL);
1226 /* make the directory if it doesn't exist */
1227 if (!g_file_test (directory_path, G_FILE_TEST_EXISTS)) {
1228 g_mkdir_with_parents (directory_path, 0775);
1231 g_free (directory_path);
1233 src = g_file_new_for_path (path_name);
1234 dest = g_file_new_for_path (destination_name);
1235 if (!g_file_copy (src, dest,
1237 NULL, NULL, NULL, NULL)) {
1238 char *message = g_strdup_printf (_("Sorry, but the pattern %s could not be installed."), path_name);
1239 eel_show_error_dialog (message, NULL, GTK_WINDOW (property_browser));
1240 g_free (message);
1242 g_object_unref (src);
1243 g_object_unref (dest);
1245 g_free (destination_name);
1247 /* update the property browser's contents to show the new one */
1248 nautilus_property_browser_update_contents (property_browser);
1251 /* here's where we initiate adding a new pattern by putting up a file selector */
1253 static void
1254 add_new_pattern (NautilusPropertyBrowser *property_browser)
1256 if (property_browser->details->patterns_dialog) {
1257 gtk_window_present (GTK_WINDOW (property_browser->details->patterns_dialog));
1258 } else {
1259 property_browser->details->patterns_dialog =
1260 eel_gnome_icon_selector_new (_("Select an Image File to Add as a Pattern"),
1261 DATADIR "/nautilus/patterns/",
1262 GTK_WINDOW (property_browser),
1263 (EelIconSelectionFunction) add_pattern_to_browser,
1264 property_browser);
1266 if (property_browser->details->patterns_dialog)
1267 eel_add_weak_pointer (&property_browser->details->patterns_dialog);
1271 /* here's where we add the passed in color to the file that defines the colors */
1273 static void
1274 add_color_to_file (NautilusPropertyBrowser *property_browser, const char *color_spec, const char *color_name)
1276 xmlNodePtr cur_node, new_color_node, children_node;
1277 xmlDocPtr document;
1278 xmlChar *child_color_name;
1279 gboolean color_name_exists = FALSE;
1281 document = read_browser_xml (property_browser);
1282 if (document == NULL) {
1283 return;
1286 /* find the colors category */
1287 cur_node = get_color_category (document);
1288 if (cur_node != NULL) {
1289 /* check if theres already a color whith that name */
1290 children_node = cur_node->xmlChildrenNode;
1291 while (children_node != NULL) {
1292 child_color_name = xmlGetProp (children_node, "name");
1293 if (xmlStrcmp (color_name, child_color_name) == 0) {
1294 color_name_exists = TRUE;
1295 xmlFree (child_color_name);
1296 break;
1298 xmlFree (child_color_name);
1300 children_node = children_node->next;
1303 /* add a new color node */
1304 if (!color_name_exists) {
1305 new_color_node = xmlNewChild (cur_node, NULL, "color", NULL);
1306 xmlNodeSetContent (new_color_node, color_spec);
1307 xmlSetProp (new_color_node, "local", "1");
1308 xmlSetProp (new_color_node, "name", color_name);
1310 write_browser_xml (property_browser, document);
1311 } else {
1312 eel_show_error_dialog (_("The color cannot be installed."),
1313 _("Sorry, but you must specify an unused color name for the new color."),
1314 GTK_WINDOW (property_browser));
1318 xmlFreeDoc (document);
1321 /* handle the OK button being pushed on the color selection dialog */
1322 static void
1323 add_color_to_browser (GtkWidget *widget, gint which_button, gpointer *data)
1325 char color_spec[8];
1326 const char *color_name;
1327 char *stripped_color_name;
1329 gdouble color[4];
1330 NautilusPropertyBrowser *property_browser = NAUTILUS_PROPERTY_BROWSER (data);
1332 if (which_button == GTK_RESPONSE_OK) {
1333 gnome_color_picker_get_d (GNOME_COLOR_PICKER (property_browser->details->color_picker), &color[0], &color[1], &color[2], &color[3]);
1334 g_snprintf (color_spec, sizeof (color_spec),
1335 "#%02X%02X%02X",
1336 (guint) (color[0] * 255.0 + 0.5),
1337 (guint) (color[1] * 255.0 + 0.5),
1338 (guint) (color[2] * 255.0 + 0.5));
1340 color_name = gtk_entry_get_text (GTK_ENTRY (property_browser->details->color_name));
1341 stripped_color_name = g_strstrip (g_strdup (color_name));
1342 if (strlen (stripped_color_name) == 0) {
1343 eel_show_error_dialog (_("The color cannot be installed."),
1344 _("Sorry, but you must specify a non-blank name for the new color."),
1345 GTK_WINDOW (property_browser));
1347 } else {
1348 add_color_to_file (property_browser, color_spec, stripped_color_name);
1349 nautilus_property_browser_update_contents(property_browser);
1351 g_free (stripped_color_name);
1354 gtk_widget_destroy(property_browser->details->colors_dialog);
1355 property_browser->details->colors_dialog = NULL;
1358 /* create the color selection dialog, pre-set with the color that was just selected */
1359 static void
1360 show_color_selection_window (GtkWidget *widget, gpointer *data)
1362 GdkColor color;
1363 NautilusPropertyBrowser *property_browser = NAUTILUS_PROPERTY_BROWSER(data);
1365 gtk_color_selection_get_current_color (GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG (property_browser->details->colors_dialog)->colorsel), &color);
1366 gtk_widget_destroy (property_browser->details->colors_dialog);
1368 /* allocate a new color selection dialog */
1369 property_browser->details->colors_dialog = nautilus_color_selection_dialog_new (property_browser);
1371 /* set the color to the one picked by the selector */
1372 gnome_color_picker_set_i16 (GNOME_COLOR_PICKER (property_browser->details->color_picker), color.red, color.green, color.blue, 1.0);
1374 /* connect the signals to the new dialog */
1376 eel_add_weak_pointer (&property_browser->details->colors_dialog);
1378 g_signal_connect_object (property_browser->details->colors_dialog, "response",
1379 G_CALLBACK (add_color_to_browser), property_browser, 0);
1380 gtk_window_set_position (GTK_WINDOW (property_browser->details->colors_dialog), GTK_WIN_POS_MOUSE);
1381 gtk_widget_show (GTK_WIDGET(property_browser->details->colors_dialog));
1386 /* here's the routine to add a new color, by putting up a color selector */
1388 static void
1389 add_new_color (NautilusPropertyBrowser *property_browser)
1391 if (property_browser->details->colors_dialog) {
1392 gtk_window_present (GTK_WINDOW (property_browser->details->colors_dialog));
1393 } else {
1394 GtkColorSelectionDialog *color_dialog;
1396 property_browser->details->colors_dialog = gtk_color_selection_dialog_new (_("Select a Color to Add"));
1397 color_dialog = GTK_COLOR_SELECTION_DIALOG (property_browser->details->colors_dialog);
1399 eel_add_weak_pointer (&property_browser->details->colors_dialog);
1401 g_signal_connect_object (color_dialog->ok_button, "clicked",
1402 G_CALLBACK (show_color_selection_window), property_browser, 0);
1403 g_signal_connect_object (color_dialog->cancel_button, "clicked",
1404 G_CALLBACK (gtk_widget_destroy), color_dialog, G_CONNECT_SWAPPED);
1405 gtk_widget_hide(color_dialog->help_button);
1407 gtk_window_set_position (GTK_WINDOW (color_dialog), GTK_WIN_POS_MOUSE);
1408 gtk_widget_show (GTK_WIDGET(color_dialog));
1412 /* here's where we handle clicks in the emblem dialog buttons */
1413 static void
1414 emblem_dialog_clicked (GtkWidget *dialog, int which_button, NautilusPropertyBrowser *property_browser)
1416 const char *new_keyword;
1417 char *stripped_keyword;
1418 char *emblem_path;
1419 GFile *emblem_file;
1420 GdkPixbuf *pixbuf;
1422 if (which_button == GTK_RESPONSE_OK) {
1424 /* update the image path from the file entry */
1425 if (property_browser->details->file_entry) {
1426 emblem_path = gnome_icon_entry_get_filename (GNOME_ICON_ENTRY (property_browser->details->file_entry));
1427 if (emblem_path) {
1428 emblem_file = g_file_new_for_path (emblem_path);
1429 if (ensure_file_is_image (emblem_file)) {
1430 g_free (property_browser->details->image_path);
1431 property_browser->details->image_path = emblem_path;
1432 } else {
1433 char *message = g_strdup_printf
1434 (_("Sorry, but \"%s\" is not a usable image file."), emblem_path);
1435 eel_show_error_dialog (_("The file is not an image."), message, GTK_WINDOW (property_browser));
1436 g_free (message);
1437 g_free (emblem_path);
1438 g_object_unref (emblem_file);
1439 return;
1441 g_object_unref (emblem_file);
1445 emblem_file = g_file_new_for_path (property_browser->details->image_path);
1446 pixbuf = nautilus_emblem_load_pixbuf_for_emblem (emblem_file);
1447 g_object_unref (emblem_file);
1449 if (pixbuf == NULL) {
1450 char *message = g_strdup_printf
1451 (_("Sorry, but \"%s\" is not a usable image file."), property_browser->details->image_path);
1452 eel_show_error_dialog (_("The file is not an image."), message, GTK_WINDOW (property_browser));
1453 g_free (message);
1456 new_keyword = gtk_entry_get_text(GTK_ENTRY(property_browser->details->keyword));
1457 if (new_keyword == NULL) {
1458 stripped_keyword = NULL;
1459 } else {
1460 stripped_keyword = g_strstrip (g_strdup (new_keyword));
1464 nautilus_emblem_install_custom_emblem (pixbuf,
1465 stripped_keyword,
1466 stripped_keyword,
1467 GTK_WINDOW (property_browser));
1468 if (pixbuf != NULL)
1469 g_object_unref (pixbuf);
1471 nautilus_emblem_refresh_list ();
1473 emit_emblems_changed_signal ();
1475 g_free (stripped_keyword);
1478 gtk_widget_destroy (dialog);
1480 property_browser->details->keyword = NULL;
1481 property_browser->details->emblem_image = NULL;
1482 property_browser->details->file_entry = NULL;
1485 /* here's the routine to add a new emblem, by putting up an emblem dialog */
1487 static void
1488 add_new_emblem (NautilusPropertyBrowser *property_browser)
1490 if (property_browser->details->emblems_dialog) {
1491 gtk_window_present (GTK_WINDOW (property_browser->details->emblems_dialog));
1492 } else {
1493 property_browser->details->emblems_dialog = nautilus_emblem_dialog_new (property_browser);
1495 eel_add_weak_pointer (&property_browser->details->emblems_dialog);
1497 g_signal_connect_object (property_browser->details->emblems_dialog, "response",
1498 G_CALLBACK (emblem_dialog_clicked), property_browser, 0);
1499 gtk_window_set_position (GTK_WINDOW (property_browser->details->emblems_dialog), GTK_WIN_POS_MOUSE);
1500 gtk_widget_show (GTK_WIDGET(property_browser->details->emblems_dialog));
1504 /* cancelremove mode */
1505 static void
1506 cancel_remove_mode (NautilusPropertyBrowser *property_browser)
1508 if (property_browser->details->remove_mode) {
1509 property_browser->details->remove_mode = FALSE;
1510 nautilus_property_browser_update_contents(property_browser);
1511 gtk_widget_show (property_browser->details->help_label);
1515 /* handle the add_new button */
1517 static void
1518 add_new_button_callback(GtkWidget *widget, NautilusPropertyBrowser *property_browser)
1520 /* handle remove mode, where we act as a cancel button */
1521 if (property_browser->details->remove_mode) {
1522 cancel_remove_mode (property_browser);
1523 return;
1526 switch (property_browser->details->category_type) {
1527 case NAUTILUS_PROPERTY_PATTERN:
1528 add_new_pattern (property_browser);
1529 break;
1530 case NAUTILUS_PROPERTY_COLOR:
1531 add_new_color (property_browser);
1532 break;
1533 case NAUTILUS_PROPERTY_EMBLEM:
1534 add_new_emblem (property_browser);
1535 break;
1536 default:
1537 break;
1541 /* handle the "done" button */
1542 static void
1543 done_button_callback (GtkWidget *widget, GtkWidget *property_browser)
1545 cancel_remove_mode (NAUTILUS_PROPERTY_BROWSER (property_browser));
1546 gtk_widget_hide (property_browser);
1549 /* handle the "help" button */
1550 static void
1551 help_button_callback (GtkWidget *widget, GtkWidget *property_browser)
1553 GError *error = NULL;
1554 GtkWidget *dialog;
1556 gnome_help_display_desktop_on_screen (
1557 NULL, "user-guide", "user-guide.xml", "gosnautilus-50",
1558 gtk_window_get_screen (GTK_WINDOW (property_browser)), &error);
1560 if (error) {
1561 dialog = gtk_message_dialog_new (GTK_WINDOW (property_browser),
1562 GTK_DIALOG_DESTROY_WITH_PARENT,
1563 GTK_MESSAGE_ERROR,
1564 GTK_BUTTONS_OK,
1565 _("There was an error displaying help: \n%s"),
1566 error->message);
1568 g_signal_connect (G_OBJECT (dialog),
1569 "response", G_CALLBACK (gtk_widget_destroy),
1570 NULL);
1571 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1572 gtk_widget_show (dialog);
1573 g_error_free (error);
1577 /* handle the "remove" button */
1578 static void
1579 remove_button_callback(GtkWidget *widget, NautilusPropertyBrowser *property_browser)
1581 if (property_browser->details->remove_mode) {
1582 return;
1585 property_browser->details->remove_mode = TRUE;
1586 gtk_widget_hide (property_browser->details->help_label);
1587 nautilus_property_browser_update_contents(property_browser);
1590 /* this callback handles clicks on the image or color based content content elements */
1592 static void
1593 element_clicked_callback (GtkWidget *image_table,
1594 GtkWidget *child,
1595 const EelImageTableEvent *event,
1596 gpointer callback_data)
1598 NautilusPropertyBrowser *property_browser;
1599 GtkTargetList *target_list;
1600 GdkDragContext *context;
1601 const char *element_name;
1602 GdkDragAction action;
1604 g_return_if_fail (EEL_IS_IMAGE_TABLE (image_table));
1605 g_return_if_fail (EEL_IS_LABELED_IMAGE (child));
1606 g_return_if_fail (event != NULL);
1607 g_return_if_fail (NAUTILUS_IS_PROPERTY_BROWSER (callback_data));
1608 g_return_if_fail (g_object_get_data (G_OBJECT (child), "property-name") != NULL);
1610 element_name = g_object_get_data (G_OBJECT (child), "property-name");
1611 property_browser = NAUTILUS_PROPERTY_BROWSER (callback_data);
1613 /* handle remove mode by removing the element */
1614 if (property_browser->details->remove_mode) {
1615 nautilus_property_browser_remove_element (property_browser, EEL_LABELED_IMAGE (child));
1616 property_browser->details->remove_mode = FALSE;
1617 nautilus_property_browser_update_contents (property_browser);
1618 gtk_widget_show (property_browser->details->help_label);
1619 return;
1622 /* set up the drag and drop type corresponding to the category */
1623 drag_types[0].target = property_browser->details->drag_type;
1625 /* treat the reset property in the colors section specially */
1626 if (eel_strcmp (element_name, RESET_IMAGE_NAME) == 0) {
1627 drag_types[0].target = "x-special/gnome-reset-background";
1630 target_list = gtk_target_list_new (drag_types, G_N_ELEMENTS (drag_types));
1631 nautilus_property_browser_set_dragged_file(property_browser, element_name);
1632 action = event->button == 3 ? GDK_ACTION_ASK : GDK_ACTION_MOVE | GDK_ACTION_COPY;
1634 g_object_set_data (G_OBJECT (property_browser), "dragged-image", child);
1636 context = gtk_drag_begin (GTK_WIDGET (property_browser),
1637 target_list,
1638 GDK_ACTION_ASK | GDK_ACTION_MOVE | GDK_ACTION_COPY,
1639 event->button,
1640 event->event);
1641 gtk_target_list_unref (target_list);
1643 /* optionally (if the shift key is down) hide the property browser - it will later be destroyed when the drag ends */
1644 property_browser->details->keep_around = (event->state & GDK_SHIFT_MASK) == 0;
1645 if (! property_browser->details->keep_around) {
1646 gtk_widget_hide (GTK_WIDGET (property_browser));
1651 /* utility routine to strip the extension from the passed in string */
1652 static char*
1653 strip_extension (const char* string_to_strip)
1655 char *result_str, *temp_str;
1656 if (string_to_strip == NULL)
1657 return NULL;
1659 result_str = g_strdup(string_to_strip);
1660 temp_str = strrchr(result_str, '.');
1661 if (temp_str)
1662 *temp_str = '\0';
1663 return result_str;
1666 static void
1667 labeled_image_configure (EelLabeledImage *labeled_image)
1669 g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
1671 eel_labeled_image_set_spacing (labeled_image, LABELED_IMAGE_SPACING);
1674 /* Make a color tile for a property */
1675 static GtkWidget *
1676 labeled_image_new (const char *text,
1677 GdkPixbuf *pixbuf,
1678 const char *property_name,
1679 double scale_factor)
1681 GtkWidget *labeled_image;
1683 labeled_image = eel_labeled_image_new (text, pixbuf);
1684 labeled_image_configure (EEL_LABELED_IMAGE (labeled_image));
1686 if (property_name != NULL) {
1687 g_object_set_data_full (G_OBJECT (labeled_image),
1688 "property-name",
1689 g_strdup (property_name),
1690 g_free);
1693 return labeled_image;
1696 static void
1697 make_properties_from_directories (NautilusPropertyBrowser *property_browser)
1699 NautilusCustomizationData *customization_data;
1700 char *object_name;
1701 char *object_label;
1702 GdkPixbuf *object_pixbuf;
1703 EelImageTable *image_table;
1704 GtkWidget *reset_object = NULL;
1705 GList *icons, *l;
1706 char *icon_name;
1707 char *keyword;
1708 char *extension;
1709 GtkWidget *property_image;
1710 GtkWidget *blank;
1711 guint num_images;
1712 char *path;
1713 NautilusIconInfo *info;
1715 g_return_if_fail (NAUTILUS_IS_PROPERTY_BROWSER (property_browser));
1716 g_return_if_fail (EEL_IS_IMAGE_TABLE (property_browser->details->content_table));
1718 image_table = EEL_IMAGE_TABLE (property_browser->details->content_table);
1720 if (property_browser->details->category_type == NAUTILUS_PROPERTY_EMBLEM) {
1721 eel_g_list_free_deep (property_browser->details->keywords);
1722 property_browser->details->keywords = NULL;
1724 icons = nautilus_emblem_list_available ();
1726 l = icons;
1727 while (l != NULL) {
1728 icon_name = (char *)l->data;
1729 l = l->next;
1731 if (!nautilus_emblem_should_show_in_list (icon_name)) {
1732 continue;
1734 object_name = nautilus_emblem_get_keyword_from_icon_name (icon_name);
1735 info = nautilus_icon_info_lookup_from_name (icon_name, NAUTILUS_ICON_SIZE_STANDARD);
1736 object_pixbuf = nautilus_icon_info_get_pixbuf_at_size (info, NAUTILUS_ICON_SIZE_STANDARD);
1737 object_label = g_strdup (nautilus_icon_info_get_display_name (info));
1738 g_object_unref (info);
1740 if (object_label == NULL) {
1741 object_label = g_strdup (object_name);
1744 property_image = labeled_image_new (object_label, object_pixbuf, object_name, PANGO_SCALE_LARGE);
1745 eel_labeled_image_set_fixed_image_height (EEL_LABELED_IMAGE (property_image), MAX_EMBLEM_HEIGHT);
1747 keyword = g_strdup (object_name);
1748 extension = strchr (keyword, '.');
1749 if (extension) {
1750 *extension = '\0';
1752 property_browser->details->keywords = g_list_prepend (property_browser->details->keywords,
1753 keyword);
1755 gtk_container_add (GTK_CONTAINER (image_table), property_image);
1756 gtk_widget_show (property_image);
1758 g_free (object_name);
1759 g_free (object_label);
1760 if (object_pixbuf != NULL) {
1761 g_object_unref (object_pixbuf);
1764 eel_g_list_free_deep (icons);
1765 property_browser->details->has_local = FALSE;
1766 } else {
1767 customization_data = nautilus_customization_data_new (property_browser->details->category,
1768 !property_browser->details->remove_mode,
1769 MAX_ICON_WIDTH,
1770 MAX_ICON_HEIGHT);
1771 if (customization_data == NULL) {
1772 return;
1775 /* interate through the set of objects and display each */
1776 while (nautilus_customization_data_get_next_element_for_display (customization_data,
1777 &object_name,
1778 &object_pixbuf,
1779 &object_label)) {
1781 property_image = labeled_image_new (object_label, object_pixbuf, object_name, PANGO_SCALE_LARGE);
1783 gtk_container_add (GTK_CONTAINER (image_table), property_image);
1784 gtk_widget_show (property_image);
1786 /* Keep track of ERASE objects to place them prominently later */
1787 if (property_browser->details->category_type == NAUTILUS_PROPERTY_PATTERN
1788 && !eel_strcmp (object_name, RESET_IMAGE_NAME)) {
1789 g_assert (reset_object == NULL);
1790 reset_object = property_image;
1793 gtk_widget_show (property_image);
1795 g_free (object_name);
1796 g_free (object_label);
1797 if (object_pixbuf != NULL) {
1798 g_object_unref (object_pixbuf);
1802 property_browser->details->has_local = nautilus_customization_data_private_data_was_displayed (customization_data);
1803 nautilus_customization_data_destroy (customization_data);
1807 * We place ERASE objects (for emblems) at the end with a blank in between.
1809 if (property_browser->details->category_type == NAUTILUS_PROPERTY_EMBLEM) {
1810 blank = eel_image_table_add_empty_image (image_table);
1811 labeled_image_configure (EEL_LABELED_IMAGE (blank));
1814 num_images = eel_wrap_table_get_num_children (EEL_WRAP_TABLE (image_table));
1815 g_assert (num_images > 0);
1816 eel_wrap_table_reorder_child (EEL_WRAP_TABLE (image_table),
1817 blank,
1818 num_images - 1);
1820 gtk_widget_show (blank);
1822 object_pixbuf = NULL;
1824 path = nautilus_pixmap_file (ERASE_OBJECT_NAME);
1825 if (path != NULL) {
1826 object_pixbuf = gdk_pixbuf_new_from_file (path, NULL);
1828 g_free (path);
1829 property_image = labeled_image_new (_("Erase"), object_pixbuf, "erase", PANGO_SCALE_LARGE);
1830 eel_labeled_image_set_fixed_image_height (EEL_LABELED_IMAGE (property_image), MAX_EMBLEM_HEIGHT);
1832 gtk_container_add (GTK_CONTAINER (image_table), property_image);
1833 gtk_widget_show (property_image);
1835 eel_wrap_table_reorder_child (EEL_WRAP_TABLE (image_table),
1836 property_image, -1);
1838 if (object_pixbuf != NULL) {
1839 g_object_unref (object_pixbuf);
1844 * We place RESET objects (for colors and patterns) at the beginning.
1846 if (reset_object != NULL) {
1847 g_assert (EEL_IS_LABELED_IMAGE (reset_object));
1848 eel_wrap_table_reorder_child (EEL_WRAP_TABLE (image_table),
1849 reset_object,
1855 /* utility routine to add a reset property in the first position */
1856 static void
1857 add_reset_property (NautilusPropertyBrowser *property_browser)
1859 char *reset_image_file_name;
1860 GtkWidget *reset_image;
1861 GdkPixbuf *reset_pixbuf, *reset_chit;
1863 reset_chit = NULL;
1865 reset_image_file_name = g_strdup_printf ("%s/%s/%s", NAUTILUS_DATADIR, "patterns", RESET_IMAGE_NAME);
1866 reset_pixbuf = gdk_pixbuf_new_from_file (reset_image_file_name, NULL);
1867 if (reset_pixbuf != NULL && property_browser->details->property_chit != NULL) {
1868 reset_chit = nautilus_customization_make_pattern_chit (reset_pixbuf, property_browser->details->property_chit, FALSE, TRUE);
1871 g_free (reset_image_file_name);
1873 reset_image = labeled_image_new (_("Reset"), reset_chit != NULL ? reset_chit : reset_pixbuf, RESET_IMAGE_NAME, PANGO_SCALE_MEDIUM);
1874 gtk_container_add (GTK_CONTAINER (property_browser->details->content_table), reset_image);
1875 eel_wrap_table_reorder_child (EEL_WRAP_TABLE (property_browser->details->content_table),
1876 reset_image,
1878 gtk_widget_show (reset_image);
1880 if (reset_pixbuf != NULL) {
1881 g_object_unref (reset_pixbuf);
1884 if (reset_chit != NULL) {
1885 g_object_unref (reset_chit);
1889 /* generate properties from the children of the passed in node */
1890 /* for now, we just handle color nodes */
1892 static void
1893 make_properties_from_xml_node (NautilusPropertyBrowser *property_browser,
1894 xmlNodePtr node)
1896 xmlNodePtr child_node;
1897 GdkPixbuf *pixbuf;
1898 GtkWidget *new_property;
1899 char *deleted, *local, *color, *name;
1901 gboolean local_only = property_browser->details->remove_mode;
1903 /* add a reset property in the first slot */
1904 if (!property_browser->details->remove_mode) {
1905 add_reset_property (property_browser);
1908 property_browser->details->has_local = FALSE;
1910 for (child_node = eel_xml_get_children (node);
1911 child_node != NULL;
1912 child_node = child_node->next) {
1914 if (child_node->type != XML_ELEMENT_NODE) {
1915 continue;
1918 /* We used to mark colors that were removed with the "deleted" attribute.
1919 * To prevent these colors from suddenly showing up now, this legacy remains.
1921 deleted = xmlGetProp (child_node, "deleted");
1922 local = xmlGetProp (child_node, "local");
1924 if (deleted == NULL && (!local_only || local != NULL)) {
1925 if (local != NULL) {
1926 property_browser->details->has_local = TRUE;
1929 color = xmlNodeGetContent (child_node);
1930 name = eel_xml_get_property_translated (child_node, "name");
1932 /* make the image from the color spec */
1933 pixbuf = make_color_drag_image (property_browser, color, FALSE);
1935 /* make the tile from the pixmap and name */
1936 new_property = labeled_image_new (name, pixbuf, color, PANGO_SCALE_LARGE);
1938 gtk_container_add (GTK_CONTAINER (property_browser->details->content_table), new_property);
1939 gtk_widget_show (new_property);
1941 g_object_unref (pixbuf);
1942 xmlFree (color);
1943 xmlFree (name);
1946 xmlFree (local);
1947 xmlFree (deleted);
1951 /* handle theme changes by updating the browser contents */
1953 static void
1954 nautilus_property_browser_theme_changed (gpointer user_data)
1956 NautilusPropertyBrowser *property_browser;
1958 property_browser = NAUTILUS_PROPERTY_BROWSER(user_data);
1959 nautilus_property_browser_update_contents (property_browser);
1962 /* make_category generates widgets corresponding all of the objects in the passed in directory */
1963 static void
1964 make_category(NautilusPropertyBrowser *property_browser, const char* path, const char* mode, xmlNodePtr node, const char *description)
1967 /* set up the description in the help label */
1968 gtk_label_set_text (GTK_LABEL (property_browser->details->help_label), description);
1970 /* case out on the mode */
1971 if (strcmp (mode, "directory") == 0)
1972 make_properties_from_directories (property_browser);
1973 else if (strcmp (mode, "inline") == 0)
1974 make_properties_from_xml_node (property_browser, node);
1978 /* Create a category button */
1979 static GtkWidget *
1980 property_browser_category_button_new (const char *display_name,
1981 const char *image)
1983 GtkWidget *button;
1984 char *file_name;
1986 g_return_val_if_fail (display_name != NULL, NULL);
1987 g_return_val_if_fail (image != NULL, NULL);
1989 file_name = nautilus_pixmap_file (image);
1990 if (file_name != NULL) {
1991 button = eel_labeled_image_radio_button_new_from_file_name (display_name, file_name);
1992 } else {
1993 button = eel_labeled_image_radio_button_new (display_name, NULL);
1996 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
1998 /* We also want all of the buttons to be the same height */
1999 eel_labeled_image_set_fixed_image_height (EEL_LABELED_IMAGE (GTK_BIN (button)->child), STANDARD_BUTTON_IMAGE_HEIGHT);
2001 g_free (file_name);
2003 return button;
2006 /* this is a utility routine to generate a category link widget and install it in the browser */
2007 static void
2008 make_category_link (NautilusPropertyBrowser *property_browser,
2009 const char *name,
2010 const char *display_name,
2011 const char *image,
2012 GtkRadioButton **group)
2014 GtkWidget *button;
2016 g_return_if_fail (name != NULL);
2017 g_return_if_fail (image != NULL);
2018 g_return_if_fail (display_name != NULL);
2019 g_return_if_fail (NAUTILUS_IS_PROPERTY_BROWSER (property_browser));
2021 button = property_browser_category_button_new (display_name, image);
2022 gtk_widget_show (button);
2024 if (*group) {
2025 gtk_radio_button_set_group (GTK_RADIO_BUTTON (button),
2026 gtk_radio_button_get_group (*group));
2027 } else {
2028 *group = GTK_RADIO_BUTTON (button);
2031 /* if the button represents the current category, highlight it */
2032 if (property_browser->details->category &&
2033 strcmp (property_browser->details->category, name) == 0)
2034 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
2036 /* Place it in the category box */
2037 gtk_box_pack_start (GTK_BOX (property_browser->details->category_box),
2038 button, FALSE, FALSE, 0);
2040 property_browser->details->category_position += 1;
2042 /* add a signal to handle clicks */
2043 g_object_set_data (G_OBJECT(button), "user_data", property_browser);
2044 g_signal_connect_data
2045 (button, "clicked",
2046 G_CALLBACK (category_clicked_callback),
2047 g_strdup (name), (GClosureNotify) g_free, 0);
2050 /* update_contents populates the property browser with information specified by the path and other state variables */
2051 void
2052 nautilus_property_browser_update_contents (NautilusPropertyBrowser *property_browser)
2054 xmlNodePtr cur_node;
2055 xmlDocPtr document;
2056 GtkWidget *viewport;
2057 GtkRadioButton *group;
2058 gboolean got_categories;
2059 char *name, *image, *type, *description, *display_name, *path, *mode;
2060 const char *text;
2062 /* load the xml document corresponding to the path and selection */
2063 document = read_browser_xml (property_browser);
2064 if (document == NULL) {
2065 return;
2068 /* remove the existing content box, if any, and allocate a new one */
2069 if (property_browser->details->content_frame) {
2070 gtk_widget_destroy(property_browser->details->content_frame);
2073 /* allocate a new container, with a scrollwindow and viewport */
2074 property_browser->details->content_frame = gtk_scrolled_window_new (NULL, NULL);
2075 viewport = gtk_viewport_new (NULL, NULL);
2076 gtk_widget_show(viewport);
2077 gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_IN);
2078 gtk_container_add (GTK_CONTAINER (property_browser->details->content_container), property_browser->details->content_frame);
2079 gtk_widget_show (property_browser->details->content_frame);
2080 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (property_browser->details->content_frame),
2081 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
2083 /* allocate a table to hold the content widgets */
2084 property_browser->details->content_table = eel_image_table_new (TRUE);
2085 eel_wrap_table_set_x_spacing (EEL_WRAP_TABLE (property_browser->details->content_table),
2086 IMAGE_TABLE_X_SPACING);
2087 eel_wrap_table_set_y_spacing (EEL_WRAP_TABLE (property_browser->details->content_table),
2088 IMAGE_TABLE_Y_SPACING);
2090 g_signal_connect_object (property_browser->details->content_table, "child_pressed",
2091 G_CALLBACK (element_clicked_callback), property_browser, 0);
2093 gtk_container_add(GTK_CONTAINER(viewport), property_browser->details->content_table);
2094 gtk_container_add (GTK_CONTAINER (property_browser->details->content_frame), viewport);
2095 gtk_widget_show (GTK_WIDGET (property_browser->details->content_table));
2097 /* iterate through the xml file to generate the widgets */
2098 got_categories = property_browser->details->category_position >= 0;
2099 if (!got_categories) {
2100 property_browser->details->category_position = 0;
2103 group = NULL;
2104 for (cur_node = eel_xml_get_children (xmlDocGetRootElement (document));
2105 cur_node != NULL;
2106 cur_node = cur_node->next) {
2108 if (cur_node->type != XML_ELEMENT_NODE) {
2109 continue;
2112 if (strcmp (cur_node->name, "category") == 0) {
2113 name = xmlGetProp (cur_node, "name");
2115 if (property_browser->details->category != NULL
2116 && strcmp (property_browser->details->category, name) == 0) {
2117 path = xmlGetProp (cur_node, "path");
2118 mode = xmlGetProp (cur_node, "mode");
2119 description = eel_xml_get_property_translated (cur_node, "description");
2120 type = xmlGetProp (cur_node, "type");
2122 make_category (property_browser,
2123 path,
2124 mode,
2125 cur_node,
2126 description);
2127 nautilus_property_browser_set_drag_type (property_browser, type);
2129 xmlFree (path);
2130 xmlFree (mode);
2131 xmlFree (description);
2132 xmlFree (type);
2135 if (!got_categories) {
2136 display_name = eel_xml_get_property_translated (cur_node, "display_name");
2137 image = xmlGetProp (cur_node, "image");
2139 make_category_link (property_browser,
2140 name,
2141 display_name,
2142 image,
2143 &group);
2145 xmlFree (display_name);
2146 xmlFree (image);
2149 xmlFree (name);
2153 /* release the xml document and we're done */
2154 xmlFreeDoc (document);
2156 /* update the title and button */
2158 if (property_browser->details->category == NULL) {
2159 gtk_label_set_text (GTK_LABEL (property_browser->details->title_label), _("Select a Category:"));
2160 gtk_widget_hide(property_browser->details->add_button);
2161 gtk_widget_hide(property_browser->details->remove_button);
2163 } else {
2164 char *label_text;
2165 char *stock_id;
2166 if (property_browser->details->remove_mode) {
2167 stock_id = GTK_STOCK_CANCEL;
2168 text = _("C_ancel Remove");
2169 } else {
2170 stock_id = GTK_STOCK_ADD;
2171 /* FIXME: Using spaces to add padding is not good design. */
2172 switch (property_browser->details->category_type) {
2173 case NAUTILUS_PROPERTY_PATTERN:
2174 text = _("_Add a New Pattern...");
2175 break;
2176 case NAUTILUS_PROPERTY_COLOR:
2177 text = _("_Add a New Color...");
2178 break;
2179 case NAUTILUS_PROPERTY_EMBLEM:
2180 text = _("_Add a New Emblem...");
2181 break;
2182 default:
2183 text = NULL;
2184 break;
2188 /* enable the "add new" button and update it's name and icon */
2189 gtk_image_set_from_stock (GTK_IMAGE(property_browser->details->add_button_image), stock_id,
2190 GTK_ICON_SIZE_BUTTON);
2192 if (text != NULL) {
2193 gtk_label_set_text_with_mnemonic (GTK_LABEL(property_browser->details->add_button_label), text);
2196 gtk_widget_show (property_browser->details->add_button);
2199 if (property_browser->details->remove_mode) {
2201 switch (property_browser->details->category_type) {
2202 case NAUTILUS_PROPERTY_PATTERN:
2203 label_text = g_strdup (_("Click on a pattern to remove it"));
2204 break;
2205 case NAUTILUS_PROPERTY_COLOR:
2206 label_text = g_strdup (_("Click on a color to remove it"));
2207 break;
2208 case NAUTILUS_PROPERTY_EMBLEM:
2209 label_text = g_strdup (_("Click on an emblem to remove it"));
2210 break;
2211 default:
2212 label_text = NULL;
2213 break;
2215 } else {
2216 switch (property_browser->details->category_type) {
2217 case NAUTILUS_PROPERTY_PATTERN:
2218 label_text = g_strdup (_("Patterns:"));
2219 break;
2220 case NAUTILUS_PROPERTY_COLOR:
2221 label_text = g_strdup (_("Colors:"));
2222 break;
2223 case NAUTILUS_PROPERTY_EMBLEM:
2224 label_text = g_strdup (_("Emblems:"));
2225 break;
2226 default:
2227 label_text = NULL;
2228 break;
2232 if (label_text) {
2233 gtk_label_set_text_with_mnemonic
2234 (GTK_LABEL (property_browser->details->title_label), label_text);
2236 g_free(label_text);
2238 /* enable the remove button (if necessary) and update its name */
2240 /* case out instead of substituting to provide flexibilty for other languages */
2241 /* FIXME: Using spaces to add padding is not good design. */
2242 switch (property_browser->details->category_type) {
2243 case NAUTILUS_PROPERTY_PATTERN:
2244 text = _("_Remove a Pattern...");
2245 break;
2246 case NAUTILUS_PROPERTY_COLOR:
2247 text = _("_Remove a Color...");
2248 break;
2249 case NAUTILUS_PROPERTY_EMBLEM:
2250 text = _("_Remove an Emblem...");
2251 break;
2252 default:
2253 text = NULL;
2254 break;
2257 if (property_browser->details->remove_mode
2258 || !property_browser->details->has_local)
2259 gtk_widget_hide(property_browser->details->remove_button);
2260 else
2261 gtk_widget_show(property_browser->details->remove_button);
2262 if (text != NULL) {
2263 gtk_label_set_text_with_mnemonic (GTK_LABEL(property_browser->details->remove_button_label), text);
2268 /* set the category and regenerate contents as necessary */
2270 static void
2271 nautilus_property_browser_set_category (NautilusPropertyBrowser *property_browser,
2272 const char *new_category)
2274 /* there's nothing to do if the category is the same as the current one */
2275 if (eel_strcmp (property_browser->details->category, new_category) == 0) {
2276 return;
2279 g_free (property_browser->details->category);
2280 property_browser->details->category = g_strdup (new_category);
2282 /* set up the property type enum */
2283 if (eel_strcmp (new_category, "patterns") == 0) {
2284 property_browser->details->category_type = NAUTILUS_PROPERTY_PATTERN;
2285 } else if (eel_strcmp (new_category, "colors") == 0) {
2286 property_browser->details->category_type = NAUTILUS_PROPERTY_COLOR;
2287 } else if (eel_strcmp (new_category, "emblems") == 0) {
2288 property_browser->details->category_type = NAUTILUS_PROPERTY_EMBLEM;
2289 } else {
2290 property_browser->details->category_type = NAUTILUS_PROPERTY_NONE;
2293 /* populate the per-uri box with the info */
2294 nautilus_property_browser_update_contents (property_browser);
2298 /* here is the routine that populates the property browser with the appropriate information
2299 when the path changes */
2301 void
2302 nautilus_property_browser_set_path (NautilusPropertyBrowser *property_browser,
2303 const char *new_path)
2305 /* there's nothing to do if the uri is the same as the current one */
2306 if (eel_strcmp (property_browser->details->path, new_path) == 0) {
2307 return;
2310 g_free (property_browser->details->path);
2311 property_browser->details->path = g_strdup (new_path);
2313 /* populate the per-uri box with the info */
2314 nautilus_property_browser_update_contents (property_browser);
2317 static void
2318 emblems_changed_callback (GObject *signaller,
2319 NautilusPropertyBrowser *property_browser)
2321 nautilus_property_browser_update_contents (property_browser);
2325 static void
2326 emit_emblems_changed_signal (void)
2328 g_signal_emit_by_name (nautilus_signaller_get_current (), "emblems_changed");