summaryrefslogtreecommitdiff
path: root/src/caja-property-browser.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/caja-property-browser.c')
-rw-r--r--src/caja-property-browser.c2492
1 files changed, 2492 insertions, 0 deletions
diff --git a/src/caja-property-browser.c b/src/caja-property-browser.c
new file mode 100644
index 00000000..aa7b29f2
--- /dev/null
+++ b/src/caja-property-browser.c
@@ -0,0 +1,2492 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+
+/*
+ * Caja
+ *
+ * Copyright (C) 2000 Eazel, Inc.
+ *
+ * Caja is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Caja is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Andy Hertzfeld <[email protected]>
+ */
+
+/* This is the implementation of the property browser window, which
+ * gives the user access to an extensible palette of properties which
+ * can be dropped on various elements of the user interface to
+ * customize them
+ */
+
+#include <config.h>
+#include <math.h>
+#include "caja-property-browser.h"
+
+#include <eel/eel-gdk-extensions.h>
+#include <eel/eel-gdk-pixbuf-extensions.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-mate-extensions.h>
+#include <eel/eel-gtk-extensions.h>
+#include <eel/eel-gtk-macros.h>
+#include <eel/eel-image-table.h>
+#include <eel/eel-labeled-image.h>
+#include <eel/eel-stock-dialogs.h>
+#include <eel/eel-string.h>
+#include <eel/eel-vfs-extensions.h>
+#include <eel/eel-xml-extensions.h>
+#include <libxml/parser.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <libcaja-private/caja-customization-data.h>
+#include <libcaja-private/caja-directory.h>
+#include <libcaja-private/caja-emblem-utils.h>
+#include <libcaja-private/caja-file-utilities.h>
+#include <libcaja-private/caja-file.h>
+#include <libcaja-private/caja-global-preferences.h>
+#include <libcaja-private/caja-metadata.h>
+#include <libcaja-private/caja-signaller.h>
+#include <atk/atkrelationset.h>
+
+/* property types */
+
+typedef enum
+{
+ CAJA_PROPERTY_NONE,
+ CAJA_PROPERTY_PATTERN,
+ CAJA_PROPERTY_COLOR,
+ CAJA_PROPERTY_EMBLEM
+} CajaPropertyType;
+
+struct CajaPropertyBrowserDetails
+{
+ GtkHBox *container;
+
+ GtkWidget *content_container;
+ GtkWidget *content_frame;
+ GtkWidget *content_table;
+
+ GtkWidget *category_container;
+ GtkWidget *category_box;
+
+ GtkWidget *title_box;
+ GtkWidget *title_label;
+ GtkWidget *help_label;
+
+ GtkWidget *bottom_box;
+
+ GtkWidget *add_button;
+ GtkWidget *add_button_image;
+ GtkWidget *remove_button;
+ GtkWidget *remove_button_image;
+
+ GtkWidget *patterns_dialog;
+ GtkWidget *colors_dialog;
+ GtkWidget *emblems_dialog;
+
+ GtkWidget *keyword;
+ GtkWidget *emblem_image;
+ GtkWidget *image_button;
+
+ GtkWidget *color_picker;
+ GtkWidget *color_name;
+
+ GList *keywords;
+
+ char *path;
+ char *category;
+ char *dragged_file;
+ char *drag_type;
+ char *image_path;
+ char *filename;
+
+ CajaPropertyType category_type;
+
+ int category_position;
+
+ GdkPixbuf *property_chit;
+
+ gboolean remove_mode;
+ gboolean keep_around;
+ gboolean has_local;
+};
+
+static void caja_property_browser_class_init (GtkObjectClass *object_klass);
+static void caja_property_browser_init (GtkObject *object);
+static void caja_property_browser_destroy (GtkObject *object);
+static void caja_property_browser_update_contents (CajaPropertyBrowser *property_browser);
+static void caja_property_browser_set_category (CajaPropertyBrowser *property_browser,
+ const char *new_category);
+static void caja_property_browser_set_dragged_file (CajaPropertyBrowser *property_browser,
+ const char *dragged_file_name);
+static void caja_property_browser_set_drag_type (CajaPropertyBrowser *property_browser,
+ const char *new_drag_type);
+static void add_new_button_callback (GtkWidget *widget,
+ CajaPropertyBrowser *property_browser);
+static void cancel_remove_mode (CajaPropertyBrowser *property_browser);
+static void done_button_callback (GtkWidget *widget,
+ GtkWidget *property_browser);
+static void help_button_callback (GtkWidget *widget,
+ GtkWidget *property_browser);
+static void remove_button_callback (GtkWidget *widget,
+ CajaPropertyBrowser *property_browser);
+static gboolean caja_property_browser_delete_event_callback (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data);
+static void caja_property_browser_hide_callback (GtkWidget *widget,
+ gpointer user_data);
+static void caja_property_browser_drag_end (GtkWidget *widget,
+ GdkDragContext *context);
+static void caja_property_browser_drag_begin (GtkWidget *widget,
+ GdkDragContext *context);
+static void caja_property_browser_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint32 time);
+static void caja_property_browser_theme_changed (gpointer user_data);
+static void emit_emblems_changed_signal (void);
+static void emblems_changed_callback (GObject *signaller,
+ CajaPropertyBrowser *property_browser);
+
+/* misc utilities */
+static void element_clicked_callback (GtkWidget *image_table,
+ GtkWidget *child,
+ const EelImageTableEvent *event,
+ gpointer callback_data);
+
+static GdkPixbuf * make_drag_image (CajaPropertyBrowser *property_browser,
+ const char *file_name);
+static GdkPixbuf * make_color_drag_image (CajaPropertyBrowser *property_browser,
+ const char *color_spec,
+ gboolean trim_edges);
+
+
+#define BROWSER_CATEGORIES_FILE_NAME "browser.xml"
+
+#define PROPERTY_BROWSER_WIDTH 540
+#define PROPERTY_BROWSER_HEIGHT 340
+#define MAX_EMBLEM_HEIGHT 52
+#define STANDARD_BUTTON_IMAGE_HEIGHT 42
+
+#define MAX_ICON_WIDTH 63
+#define MAX_ICON_HEIGHT 63
+#define COLOR_SQUARE_SIZE 48
+
+#define LABELED_IMAGE_SPACING 2
+#define IMAGE_TABLE_X_SPACING 6
+#define IMAGE_TABLE_Y_SPACING 4
+
+#define ERASE_OBJECT_NAME "erase.png"
+
+enum
+{
+ PROPERTY_TYPE
+};
+
+static GtkTargetEntry drag_types[] =
+{
+ { "text/uri-list", 0, PROPERTY_TYPE }
+};
+
+
+EEL_CLASS_BOILERPLATE (CajaPropertyBrowser,
+ caja_property_browser,
+ GTK_TYPE_WINDOW)
+
+/* initializing the class object by installing the operations we override */
+static void
+caja_property_browser_class_init (GtkObjectClass *object_klass)
+{
+ CajaPropertyBrowserClass *klass;
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (object_klass);
+
+ klass = CAJA_PROPERTY_BROWSER_CLASS (object_klass);
+
+ object_klass->destroy = caja_property_browser_destroy;
+ widget_class->drag_begin = caja_property_browser_drag_begin;
+ widget_class->drag_data_get = caja_property_browser_drag_data_get;
+ widget_class->drag_end = caja_property_browser_drag_end;
+}
+
+/* initialize the instance's fields, create the necessary subviews, etc. */
+
+static void
+caja_property_browser_init (GtkObject *object)
+{
+ CajaPropertyBrowser *property_browser;
+ GtkWidget *widget, *temp_box, *temp_hbox, *temp_frame, *vbox;
+ GtkWidget *temp_button;
+ GtkWidget *viewport;
+ char *temp_str;
+
+ property_browser = CAJA_PROPERTY_BROWSER (object);
+ widget = GTK_WIDGET (object);
+
+ property_browser->details = g_new0 (CajaPropertyBrowserDetails, 1);
+
+ property_browser->details->category = g_strdup ("patterns");
+ property_browser->details->category_type = CAJA_PROPERTY_PATTERN;
+
+ /* load the chit frame */
+ temp_str = caja_pixmap_file ("chit_frame.png");
+ if (temp_str != NULL)
+ {
+ property_browser->details->property_chit = gdk_pixbuf_new_from_file (temp_str, NULL);
+ }
+ g_free (temp_str);
+
+ /* set the initial size of the property browser */
+ gtk_window_set_default_size (GTK_WINDOW (property_browser),
+ PROPERTY_BROWSER_WIDTH,
+ PROPERTY_BROWSER_HEIGHT);
+
+ /* set the title and standard close accelerator */
+ gtk_window_set_title (GTK_WINDOW (widget), _("Backgrounds and Emblems"));
+ gtk_window_set_wmclass (GTK_WINDOW (widget), "property_browser", "Caja");
+ gtk_window_set_type_hint (GTK_WINDOW (widget), GDK_WINDOW_TYPE_HINT_DIALOG);
+ eel_gtk_window_set_up_close_accelerator (GTK_WINDOW (widget));
+
+ /* create the main vbox. */
+ vbox = gtk_vbox_new (FALSE, 12);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
+ gtk_widget_show (vbox);
+ gtk_container_add (GTK_CONTAINER (property_browser), vbox);
+
+ /* create the container box */
+ property_browser->details->container = GTK_HBOX (gtk_hbox_new (FALSE, 6));
+ gtk_widget_show (GTK_WIDGET (property_browser->details->container));
+ gtk_box_pack_start (GTK_BOX (vbox),
+ GTK_WIDGET (property_browser->details->container),
+ TRUE, TRUE, 0);
+
+ /* make the category container */
+ property_browser->details->category_container = gtk_scrolled_window_new (NULL, NULL);
+ property_browser->details->category_position = -1;
+
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_widget_show (viewport);
+ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_NONE);
+
+ gtk_box_pack_start (GTK_BOX (property_browser->details->container),
+ property_browser->details->category_container, FALSE, FALSE, 0);
+ gtk_widget_show (property_browser->details->category_container);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (property_browser->details->category_container),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+ /* allocate a table to hold the category selector */
+ property_browser->details->category_box = gtk_vbox_new (FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(viewport), property_browser->details->category_box);
+ gtk_container_add (GTK_CONTAINER (property_browser->details->category_container), viewport);
+ gtk_widget_show (GTK_WIDGET (property_browser->details->category_box));
+
+ /* make the content container vbox */
+ property_browser->details->content_container = gtk_vbox_new (FALSE, 6);
+ gtk_widget_show (property_browser->details->content_container);
+ gtk_box_pack_start (GTK_BOX (property_browser->details->container),
+ property_browser->details->content_container,
+ TRUE, TRUE, 0);
+
+ /* create the title box */
+ property_browser->details->title_box = gtk_event_box_new();
+
+ gtk_widget_show(property_browser->details->title_box);
+ gtk_box_pack_start (GTK_BOX(property_browser->details->content_container),
+ property_browser->details->title_box,
+ FALSE, FALSE, 0);
+
+ temp_frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(temp_frame), GTK_SHADOW_NONE);
+ gtk_widget_show(temp_frame);
+ gtk_container_add(GTK_CONTAINER(property_browser->details->title_box), temp_frame);
+
+ temp_hbox = gtk_hbox_new(FALSE, 0);
+ gtk_widget_show(temp_hbox);
+
+ gtk_container_add(GTK_CONTAINER(temp_frame), temp_hbox);
+
+ /* add the title label */
+ property_browser->details->title_label = gtk_label_new ("");
+ eel_gtk_label_set_scale (GTK_LABEL (property_browser->details->title_label), PANGO_SCALE_X_LARGE);
+ eel_gtk_label_make_bold (GTK_LABEL (property_browser->details->title_label));
+
+ gtk_widget_show(property_browser->details->title_label);
+ gtk_box_pack_start (GTK_BOX(temp_hbox), property_browser->details->title_label, FALSE, FALSE, 0);
+
+ /* add the help label */
+ property_browser->details->help_label = gtk_label_new ("");
+ gtk_widget_show(property_browser->details->help_label);
+ gtk_box_pack_end (GTK_BOX (temp_hbox), property_browser->details->help_label, FALSE, FALSE, 0);
+
+ /* add the bottom box to hold the command buttons */
+ temp_box = gtk_event_box_new();
+ gtk_widget_show(temp_box);
+
+ property_browser->details->bottom_box = gtk_hbox_new (FALSE, 6);
+ gtk_widget_show (property_browser->details->bottom_box);
+
+ gtk_box_pack_end (GTK_BOX (vbox), temp_box, FALSE, FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (temp_box), property_browser->details->bottom_box);
+
+ /* create the "help" button */
+ temp_button = gtk_button_new_from_stock (GTK_STOCK_HELP);
+
+ gtk_widget_show (temp_button);
+ gtk_box_pack_start (GTK_BOX (property_browser->details->bottom_box), temp_button, FALSE, FALSE, 0);
+ g_signal_connect_object (temp_button, "clicked", G_CALLBACK (help_button_callback), property_browser, 0);
+
+ /* create the "done" button */
+ temp_button = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
+ gtk_widget_set_can_default (temp_button, TRUE);
+
+ gtk_widget_show (temp_button);
+ gtk_box_pack_end (GTK_BOX (property_browser->details->bottom_box), temp_button, FALSE, FALSE, 0);
+ gtk_widget_grab_default (temp_button);
+ gtk_widget_grab_focus (temp_button);
+ g_signal_connect_object (temp_button, "clicked", G_CALLBACK (done_button_callback), property_browser, 0);
+
+ /* create the "remove" button */
+ property_browser->details->remove_button = gtk_button_new_with_mnemonic (_("_Remove..."));
+
+ property_browser->details->remove_button_image = gtk_image_new_from_stock (GTK_STOCK_REMOVE, GTK_ICON_SIZE_BUTTON);
+ gtk_button_set_image (GTK_BUTTON (property_browser->details->remove_button),
+ property_browser->details->remove_button_image);
+ gtk_widget_show_all (property_browser->details->remove_button);
+
+ gtk_box_pack_end (GTK_BOX (property_browser->details->bottom_box),
+ property_browser->details->remove_button, FALSE, FALSE, 0);
+
+ g_signal_connect_object (property_browser->details->remove_button, "clicked",
+ G_CALLBACK (remove_button_callback), property_browser, 0);
+
+ /* now create the "add new" button */
+ property_browser->details->add_button = gtk_button_new_with_mnemonic (_("Add new..."));
+
+ property_browser->details->add_button_image = gtk_image_new_from_stock (GTK_STOCK_ADD, GTK_ICON_SIZE_BUTTON);
+ gtk_button_set_image (GTK_BUTTON (property_browser->details->add_button),
+ property_browser->details->add_button_image);
+ gtk_widget_show_all (property_browser->details->add_button);
+
+ gtk_box_pack_end (GTK_BOX(property_browser->details->bottom_box),
+ property_browser->details->add_button, FALSE, FALSE, 0);
+
+ g_signal_connect_object (property_browser->details->add_button, "clicked",
+ G_CALLBACK (add_new_button_callback), property_browser, 0);
+
+
+ /* now create the actual content, with the category pane and the content frame */
+
+ /* the actual contents are created when necessary */
+ property_browser->details->content_frame = NULL;
+
+ /* add a callback for when the theme changes */
+ eel_preferences_add_callback (CAJA_PREFERENCES_THEME,
+ caja_property_browser_theme_changed,
+ property_browser);
+
+ g_signal_connect (property_browser, "delete_event",
+ G_CALLBACK (caja_property_browser_delete_event_callback), NULL);
+ g_signal_connect (property_browser, "hide",
+ G_CALLBACK (caja_property_browser_hide_callback), NULL);
+
+ g_signal_connect_object (caja_signaller_get_current (),
+ "emblems_changed",
+ G_CALLBACK (emblems_changed_callback), property_browser, 0);
+
+ /* initially, display the top level */
+ caja_property_browser_set_path(property_browser, BROWSER_CATEGORIES_FILE_NAME);
+}
+
+/* Destroy the three dialogs for adding patterns/colors/emblems if any of them
+ exist. */
+static void
+caja_property_browser_destroy_dialogs (CajaPropertyBrowser *property_browser)
+{
+ if (property_browser->details->patterns_dialog)
+ {
+ gtk_widget_destroy (property_browser->details->patterns_dialog);
+ property_browser->details->patterns_dialog = NULL;
+ }
+ if (property_browser->details->colors_dialog)
+ {
+ gtk_widget_destroy (property_browser->details->colors_dialog);
+ property_browser->details->colors_dialog = NULL;
+ }
+ if (property_browser->details->emblems_dialog)
+ {
+ gtk_widget_destroy (property_browser->details->emblems_dialog);
+ property_browser->details->emblems_dialog = NULL;
+ }
+}
+
+static void
+caja_property_browser_destroy (GtkObject *object)
+{
+ CajaPropertyBrowser *property_browser;
+
+
+ property_browser = CAJA_PROPERTY_BROWSER (object);
+
+ caja_property_browser_destroy_dialogs (property_browser);
+
+ g_free (property_browser->details->path);
+ g_free (property_browser->details->category);
+ g_free (property_browser->details->dragged_file);
+ g_free (property_browser->details->drag_type);
+
+ eel_g_list_free_deep (property_browser->details->keywords);
+
+ if (property_browser->details->property_chit)
+ {
+ g_object_unref (property_browser->details->property_chit);
+ }
+
+ g_free (property_browser->details);
+
+ eel_preferences_remove_callback (CAJA_PREFERENCES_THEME,
+ caja_property_browser_theme_changed,
+ property_browser);
+
+ EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
+}
+
+/* create a new instance */
+CajaPropertyBrowser *
+caja_property_browser_new (GdkScreen *screen)
+{
+ CajaPropertyBrowser *browser;
+
+ browser = CAJA_PROPERTY_BROWSER
+ (gtk_widget_new (caja_property_browser_get_type (), NULL));
+
+ gtk_window_set_screen (GTK_WINDOW (browser), screen);
+ gtk_widget_show (GTK_WIDGET(browser));
+
+ return browser;
+}
+
+/* show the main property browser */
+
+void
+caja_property_browser_show (GdkScreen *screen)
+{
+ static GtkWindow *browser = NULL;
+
+ if (browser == NULL)
+ {
+ browser = GTK_WINDOW (caja_property_browser_new (screen));
+ g_object_add_weak_pointer (G_OBJECT (browser),
+ (gpointer *) &browser);
+ }
+ else
+ {
+ gtk_window_set_screen (browser, screen);
+ gtk_window_present (browser);
+ }
+}
+
+static gboolean
+caja_property_browser_delete_event_callback (GtkWidget *widget,
+ GdkEvent *event,
+ gpointer user_data)
+{
+ /* Hide but don't destroy */
+ gtk_widget_hide(widget);
+ return TRUE;
+}
+
+static void
+caja_property_browser_hide_callback (GtkWidget *widget,
+ gpointer user_data)
+{
+ CajaPropertyBrowser *property_browser;
+
+ property_browser = CAJA_PROPERTY_BROWSER (widget);
+
+ cancel_remove_mode (property_browser);
+
+ /* Destroy the 3 dialogs to add new patterns/colors/emblems. */
+ caja_property_browser_destroy_dialogs (property_browser);
+}
+
+/* remember the name of the dragged file */
+static void
+caja_property_browser_set_dragged_file (CajaPropertyBrowser *property_browser,
+ const char *dragged_file_name)
+{
+ g_free (property_browser->details->dragged_file);
+ property_browser->details->dragged_file = g_strdup (dragged_file_name);
+}
+
+/* remember the drag type */
+static void
+caja_property_browser_set_drag_type (CajaPropertyBrowser *property_browser,
+ const char *new_drag_type)
+{
+ g_free (property_browser->details->drag_type);
+ property_browser->details->drag_type = g_strdup (new_drag_type);
+}
+
+static void
+caja_property_browser_drag_begin (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ CajaPropertyBrowser *property_browser;
+ GtkWidget *child;
+ GdkPixbuf *pixbuf;
+ int x_delta, y_delta;
+ char *element_name;
+
+ property_browser = CAJA_PROPERTY_BROWSER (widget);
+
+ child = g_object_steal_data (G_OBJECT (property_browser), "dragged-image");
+ g_return_if_fail (child != NULL);
+
+ element_name = g_object_get_data (G_OBJECT (child), "property-name");
+ g_return_if_fail (child != NULL);
+
+ /* compute the offsets for dragging */
+ if (strcmp (drag_types[0].target, "application/x-color") != 0)
+ {
+ /* it's not a color, so, for now, it must be an image */
+ /* fiddle with the category to handle the "reset" case properly */
+ char * save_category = property_browser->details->category;
+ if (eel_strcmp (property_browser->details->category, "colors") == 0)
+ {
+ property_browser->details->category = "patterns";
+ }
+ pixbuf = make_drag_image (property_browser, element_name);
+ property_browser->details->category = save_category;
+ }
+ else
+ {
+ pixbuf = make_color_drag_image (property_browser, element_name, TRUE);
+ }
+
+ /* set the pixmap and mask for dragging */
+ if (pixbuf != NULL)
+ {
+ x_delta = gdk_pixbuf_get_width (pixbuf) / 2;
+ y_delta = gdk_pixbuf_get_height (pixbuf) / 2;
+
+ gtk_drag_set_icon_pixbuf
+ (context,
+ pixbuf,
+ x_delta, y_delta);
+ g_object_unref (pixbuf);
+ }
+
+}
+
+
+/* drag and drop data get handler */
+
+static void
+caja_property_browser_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint32 time)
+{
+ char *image_file_name, *image_file_uri;
+ gboolean is_reset;
+ CajaPropertyBrowser *property_browser = CAJA_PROPERTY_BROWSER(widget);
+ GdkAtom target;
+
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (context != NULL);
+
+ target = gtk_selection_data_get_target (selection_data);
+
+ switch (info)
+ {
+ case PROPERTY_TYPE:
+ /* formulate the drag data based on the drag type. Eventually, we will
+ probably select the behavior from properties in the category xml definition,
+ but for now we hardwire it to the drag_type */
+
+ is_reset = FALSE;
+ if (strcmp (property_browser->details->drag_type,
+ "property/keyword") == 0)
+ {
+ char *keyword_str = eel_filename_strip_extension (property_browser->details->dragged_file);
+ gtk_selection_data_set (selection_data, target, 8, keyword_str, strlen (keyword_str));
+ g_free (keyword_str);
+ return;
+ }
+ else if (strcmp (property_browser->details->drag_type,
+ "application/x-color") == 0)
+ {
+ GdkColor color;
+ guint16 colorArray[4];
+
+ /* handle the "reset" case as an image */
+ if (eel_strcmp (property_browser->details->dragged_file, RESET_IMAGE_NAME) != 0)
+ {
+ gdk_color_parse (property_browser->details->dragged_file, &color);
+
+ colorArray[0] = color.red;
+ colorArray[1] = color.green;
+ colorArray[2] = color.blue;
+ colorArray[3] = 0xffff;
+
+ gtk_selection_data_set(selection_data,
+ target, 16, (const char *) &colorArray[0], 8);
+ return;
+ }
+ else
+ {
+ is_reset = TRUE;
+ }
+
+ }
+
+ image_file_name = g_strdup_printf ("%s/%s/%s",
+ CAJA_DATADIR,
+ is_reset ? "patterns" : property_browser->details->category,
+ property_browser->details->dragged_file);
+
+ if (!g_file_test (image_file_name, G_FILE_TEST_EXISTS))
+ {
+ char *user_directory;
+ g_free (image_file_name);
+
+ user_directory = caja_get_user_directory ();
+ image_file_name = g_strdup_printf ("%s/%s/%s",
+ user_directory,
+ property_browser->details->category,
+ property_browser->details->dragged_file);
+
+ g_free (user_directory);
+ }
+
+ image_file_uri = g_filename_to_uri (image_file_name, NULL, NULL);
+ gtk_selection_data_set (selection_data, target, 8, image_file_uri, strlen (image_file_uri));
+ g_free (image_file_name);
+ g_free (image_file_uri);
+
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/* drag and drop end handler, where we destroy ourselves, since the transaction is complete */
+
+static void
+caja_property_browser_drag_end (GtkWidget *widget, GdkDragContext *context)
+{
+ CajaPropertyBrowser *property_browser = CAJA_PROPERTY_BROWSER(widget);
+ if (!property_browser->details->keep_around)
+ {
+ gtk_widget_hide (GTK_WIDGET (widget));
+ }
+}
+
+/* utility routine to check if the passed-in uri is an image file */
+static gboolean
+ensure_file_is_image (GFile *file)
+{
+ GFileInfo *info;
+ const char *mime_type;
+ gboolean ret;
+
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, 0, NULL, NULL);
+ if (info == NULL)
+ {
+ return FALSE;
+ }
+
+ mime_type = g_file_info_get_content_type (info);
+ if (mime_type == NULL)
+ {
+ return FALSE;
+ }
+
+ ret = (g_content_type_is_a (mime_type, "image/*") &&
+ !g_content_type_equals (mime_type, "image/svg") &&
+ !g_content_type_equals (mime_type, "image/svg+xml"));
+
+ g_object_unref (info);
+
+ return ret;
+}
+
+/* create the appropriate pixbuf for the passed in file */
+
+static GdkPixbuf *
+make_drag_image (CajaPropertyBrowser *property_browser, const char* file_name)
+{
+ GdkPixbuf *pixbuf, *orig_pixbuf;
+ char *image_file_name;
+ char *icon_name;
+ gboolean is_reset;
+ CajaIconInfo *info;
+
+ if (property_browser->details->category_type == CAJA_PROPERTY_EMBLEM)
+ {
+ if (strcmp (file_name, "erase") == 0)
+ {
+ pixbuf = NULL;
+
+ image_file_name = caja_pixmap_file (ERASE_OBJECT_NAME);
+ if (image_file_name != NULL)
+ {
+ pixbuf = gdk_pixbuf_new_from_file (image_file_name, NULL);
+ }
+ g_free (image_file_name);
+ }
+ else
+ {
+ icon_name = caja_emblem_get_icon_name_from_keyword (file_name);
+ info = caja_icon_info_lookup_from_name (icon_name, CAJA_ICON_SIZE_STANDARD);
+ pixbuf = caja_icon_info_get_pixbuf_at_size (info, CAJA_ICON_SIZE_STANDARD);
+ g_object_unref (info);
+ g_free (icon_name);
+ }
+ return pixbuf;
+ }
+
+ image_file_name = g_strdup_printf ("%s/%s/%s",
+ CAJA_DATADIR,
+ property_browser->details->category,
+ file_name);
+
+ if (!g_file_test (image_file_name, G_FILE_TEST_EXISTS))
+ {
+ char *user_directory;
+ g_free (image_file_name);
+
+ user_directory = caja_get_user_directory ();
+
+ image_file_name = g_strdup_printf ("%s/%s/%s",
+ user_directory,
+ property_browser->details->category,
+ file_name);
+
+ g_free (user_directory);
+ }
+
+ orig_pixbuf = gdk_pixbuf_new_from_file_at_scale (image_file_name,
+ MAX_ICON_WIDTH, MAX_ICON_HEIGHT,
+ TRUE,
+ NULL);
+
+ g_free (image_file_name);
+
+ if (orig_pixbuf == NULL)
+ {
+ return NULL;
+ }
+
+ is_reset = eel_strcmp (file_name, RESET_IMAGE_NAME) == 0;
+
+ if (strcmp (property_browser->details->category, "patterns") == 0 &&
+ property_browser->details->property_chit != NULL)
+ {
+ pixbuf = caja_customization_make_pattern_chit (orig_pixbuf, property_browser->details->property_chit, TRUE, is_reset);
+ }
+ else
+ {
+ pixbuf = eel_gdk_pixbuf_scale_down_to_fit (orig_pixbuf, MAX_ICON_WIDTH, MAX_ICON_HEIGHT);
+ }
+
+ g_object_unref (orig_pixbuf);
+
+ return pixbuf;
+}
+
+
+/* create a pixbuf and fill it with a color */
+
+static GdkPixbuf*
+make_color_drag_image (CajaPropertyBrowser *property_browser, const char *color_spec, gboolean trim_edges)
+{
+ GdkPixbuf *color_square;
+ GdkPixbuf *ret;
+ int row, col, stride;
+ char *pixels, *row_pixels;
+ GdkColor color;
+
+ color_square = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, COLOR_SQUARE_SIZE, COLOR_SQUARE_SIZE);
+
+ gdk_color_parse (color_spec, &color);
+ color.red >>= 8;
+ color.green >>= 8;
+ color.blue >>= 8;
+
+ pixels = gdk_pixbuf_get_pixels (color_square);
+ stride = gdk_pixbuf_get_rowstride (color_square);
+
+ /* loop through and set each pixel */
+ for (row = 0; row < COLOR_SQUARE_SIZE; row++)
+ {
+ row_pixels = (pixels + (row * stride));
+ for (col = 0; col < COLOR_SQUARE_SIZE; col++)
+ {
+ *row_pixels++ = color.red;
+ *row_pixels++ = color.green;
+ *row_pixels++ = color.blue;
+ *row_pixels++ = 255;
+ }
+ }
+
+ g_assert (color_square != NULL);
+
+ if (property_browser->details->property_chit != NULL)
+ {
+ ret = caja_customization_make_pattern_chit (color_square,
+ property_browser->details->property_chit,
+ trim_edges, FALSE);
+ g_object_unref (color_square);
+ }
+ else
+ {
+ ret = color_square;
+ }
+
+ return ret;
+}
+
+/* this callback handles button presses on the category widget. It maintains the active state */
+
+static void
+category_toggled_callback (GtkWidget *widget, char *category_name)
+{
+ CajaPropertyBrowser *property_browser;
+
+ property_browser = CAJA_PROPERTY_BROWSER (g_object_get_data (G_OBJECT (widget), "user_data"));
+
+ /* exit remove mode when the user switches categories, since there might be nothing to remove
+ in the new category */
+ property_browser->details->remove_mode = FALSE;
+
+ caja_property_browser_set_category (property_browser, category_name);
+}
+
+static xmlDocPtr
+read_browser_xml (CajaPropertyBrowser *property_browser)
+{
+ char *path;
+ xmlDocPtr document;
+
+ path = caja_get_data_file_path (property_browser->details->path);
+ if (path == NULL)
+ {
+ return NULL;
+ }
+ document = xmlParseFile (path);
+ g_free (path);
+ return document;
+}
+
+static void
+write_browser_xml (CajaPropertyBrowser *property_browser,
+ xmlDocPtr document)
+{
+ char *user_directory, *path;
+
+ user_directory = caja_get_user_directory ();
+ path = g_build_filename (user_directory, property_browser->details->path, NULL);
+ g_free (user_directory);
+ xmlSaveFile (path, document);
+ g_free (path);
+}
+
+static xmlNodePtr
+get_color_category (xmlDocPtr document)
+{
+ return eel_xml_get_root_child_by_name_and_property (document, "category", "name", "colors");
+}
+
+/* routines to remove specific category types. First, handle colors */
+
+static void
+remove_color (CajaPropertyBrowser *property_browser, const char* color_name)
+{
+ /* load the local xml file to remove the color */
+ xmlDocPtr document;
+ xmlNodePtr cur_node, color_node;
+ gboolean match;
+ char *name;
+
+ document = read_browser_xml (property_browser);
+ if (document == NULL)
+ {
+ return;
+ }
+
+ /* find the colors category */
+ cur_node = get_color_category (document);
+ if (cur_node != NULL)
+ {
+ /* loop through the colors to find one that matches */
+ for (color_node = eel_xml_get_children (cur_node);
+ color_node != NULL;
+ color_node = color_node->next)
+ {
+
+ if (color_node->type != XML_ELEMENT_NODE)
+ {
+ continue;
+ }
+
+ name = xmlGetProp (color_node, "name");
+ match = name != NULL
+ && strcmp (name, color_name) == 0;
+ xmlFree (name);
+
+ if (match)
+ {
+ xmlUnlinkNode (color_node);
+ xmlFreeNode (color_node);
+ write_browser_xml (property_browser, document);
+ break;
+ }
+ }
+ }
+
+ xmlFreeDoc (document);
+}
+
+/* remove the pattern matching the passed in name */
+
+static void
+remove_pattern(CajaPropertyBrowser *property_browser, const char* pattern_name)
+{
+ char *pattern_path;
+ char *user_directory;
+
+ user_directory = caja_get_user_directory ();
+
+ /* build the pathname of the pattern */
+ pattern_path = g_build_filename (user_directory,
+ "patterns",
+ pattern_name,
+ NULL);
+ g_free (user_directory);
+
+ /* delete the pattern from the pattern directory */
+ if (g_unlink (pattern_path) != 0)
+ {
+ char *message = g_strdup_printf (_("Sorry, but pattern %s could not be deleted."), pattern_name);
+ char *detail = _("Check that you have permission to delete the pattern.");
+ eel_show_error_dialog (message, detail, GTK_WINDOW (property_browser));
+ g_free (message);
+ }
+
+ g_free (pattern_path);
+}
+
+/* remove the emblem matching the passed in name */
+
+static void
+remove_emblem (CajaPropertyBrowser *property_browser, const char* emblem_name)
+{
+ /* delete the emblem from the emblem directory */
+ if (caja_emblem_remove_emblem (emblem_name) == FALSE)
+ {
+ char *message = g_strdup_printf (_("Sorry, but emblem %s could not be deleted."), emblem_name);
+ char *detail = _("Check that you have permission to delete the emblem.");
+ eel_show_error_dialog (message, detail, GTK_WINDOW (property_browser));
+ g_free (message);
+ }
+ else
+ {
+ emit_emblems_changed_signal ();
+ }
+}
+
+/* handle removing the passed in element */
+
+static void
+caja_property_browser_remove_element (CajaPropertyBrowser *property_browser, EelLabeledImage *child)
+{
+ const char *element_name;
+ char *color_name;
+
+ element_name = g_object_get_data (G_OBJECT (child), "property-name");
+
+ /* lookup category and get mode, then case out and handle the modes */
+ switch (property_browser->details->category_type)
+ {
+ case CAJA_PROPERTY_PATTERN:
+ remove_pattern (property_browser, element_name);
+ break;
+ case CAJA_PROPERTY_COLOR:
+ color_name = eel_labeled_image_get_text (child);
+ remove_color (property_browser, color_name);
+ g_free (color_name);
+ break;
+ case CAJA_PROPERTY_EMBLEM:
+ remove_emblem (property_browser, element_name);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+update_preview_cb (GtkFileChooser *fc,
+ GtkImage *preview)
+{
+ char *filename;
+ GdkPixbuf *pixbuf;
+
+ filename = gtk_file_chooser_get_preview_filename (fc);
+ if (filename)
+ {
+ pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
+
+ gtk_file_chooser_set_preview_widget_active (fc, pixbuf != NULL);
+
+ if (pixbuf)
+ {
+ gtk_image_set_from_pixbuf (preview, pixbuf);
+ g_object_unref (pixbuf);
+ }
+
+ g_free (filename);
+ }
+}
+
+static void
+icon_button_clicked_cb (GtkButton *b,
+ CajaPropertyBrowser *browser)
+{
+ GtkWidget *dialog;
+ GtkFileFilter *filter;
+ GtkWidget *preview;
+ int res;
+
+ dialog = gtk_file_chooser_dialog_new (_("Select an Image File for the New Emblem"),
+ GTK_WINDOW (browser),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
+ DATADIR "/pixmaps");
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_add_pixbuf_formats (filter);
+ gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ preview = gtk_image_new ();
+ gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog),
+ preview);
+ g_signal_connect (dialog, "update-preview",
+ G_CALLBACK (update_preview_cb), preview);
+
+ res = gtk_dialog_run (GTK_DIALOG (dialog));
+
+ if (res == GTK_RESPONSE_ACCEPT)
+ {
+ /* update the image */
+ g_free (browser->details->filename);
+ browser->details->filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ gtk_image_set_from_file (GTK_IMAGE (browser->details->image_button), browser->details->filename);
+ }
+
+ gtk_widget_destroy (dialog);
+}
+
+/* here's where we create the emblem dialog */
+static GtkWidget*
+caja_emblem_dialog_new (CajaPropertyBrowser *property_browser)
+{
+ GtkWidget *widget;
+ GtkWidget *button;
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *table = gtk_table_new(2, 2, FALSE);
+
+ dialog = gtk_dialog_new_with_buttons (_("Create a New Emblem"),
+ GTK_WINDOW (property_browser), 0,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ /* install the table in the dialog */
+ gtk_container_set_border_width (GTK_CONTAINER (table), 5);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 12);
+ gtk_widget_show (table);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+ gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 2);
+ gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), table, TRUE, TRUE, 0);
+ gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+ /* make the keyword label and field */
+
+ widget = gtk_label_new_with_mnemonic(_("_Keyword:"));
+ gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+ gtk_widget_show(widget);
+ gtk_table_attach(GTK_TABLE(table), widget, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ property_browser->details->keyword = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (property_browser->details->keyword), TRUE);
+ gtk_entry_set_max_length (GTK_ENTRY (property_browser->details->keyword), 24);
+ gtk_widget_show(property_browser->details->keyword);
+ gtk_table_attach(GTK_TABLE(table), property_browser->details->keyword, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_grab_focus(property_browser->details->keyword);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget),
+ GTK_WIDGET (property_browser->details->keyword));
+
+ /* default image is the generic emblem */
+ g_free (property_browser->details->image_path);
+ property_browser->details->image_path = g_build_filename (CAJA_PIXMAPDIR, "emblems.png", NULL);
+
+ /* set up a file chooser to pick the image file */
+ label = gtk_label_new_with_mnemonic (_("_Image:"));
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+ gtk_widget_show (label);
+ gtk_table_attach (GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+
+ widget = gtk_hbox_new (FALSE, 0);
+ gtk_widget_show (widget);
+
+ button = gtk_button_new ();
+ property_browser->details->image_button = gtk_image_new_from_file (property_browser->details->image_path);
+ gtk_button_set_image (GTK_BUTTON (button), property_browser->details->image_button);
+ g_signal_connect (button, "clicked", G_CALLBACK (icon_button_clicked_cb),
+ property_browser);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (label), button);
+
+ gtk_widget_show (button);
+ gtk_table_attach (GTK_TABLE (table), widget, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_box_pack_start (GTK_BOX (widget), button, FALSE, FALSE, 0);
+
+ return dialog;
+}
+
+/* create the color selection dialog */
+
+static GtkWidget*
+caja_color_selection_dialog_new (CajaPropertyBrowser *property_browser)
+{
+ GtkWidget *widget;
+ GtkWidget *dialog;
+ GtkWidget *table = gtk_table_new(2, 2, FALSE);
+
+ dialog = gtk_dialog_new_with_buttons (_("Create a New Color:"),
+ GTK_WINDOW (property_browser), 0,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+
+ /* install the table in the dialog */
+
+ gtk_widget_show (table);
+ gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), table, TRUE, TRUE, 0);
+ gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_OK);
+
+ /* make the name label and field */
+
+ widget = gtk_label_new_with_mnemonic(_("Color _name:"));
+ gtk_widget_show(widget);
+ gtk_table_attach(GTK_TABLE(table), widget, 0, 1, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+
+ property_browser->details->color_name = gtk_entry_new ();
+ gtk_entry_set_activates_default (GTK_ENTRY (property_browser->details->color_name), TRUE);
+ gtk_entry_set_max_length (GTK_ENTRY (property_browser->details->color_name), 24);
+ gtk_widget_grab_focus (property_browser->details->color_name);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), property_browser->details->color_name);
+ gtk_widget_show(property_browser->details->color_name);
+ gtk_table_attach(GTK_TABLE(table), property_browser->details->color_name, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
+ gtk_widget_grab_focus(property_browser->details->color_name);
+
+ /* default image is the generic emblem */
+ g_free(property_browser->details->image_path);
+
+ widget = gtk_label_new_with_mnemonic(_("Color _value:"));
+ gtk_widget_show(widget);
+ gtk_table_attach(GTK_TABLE(table), widget, 0, 1, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+
+ property_browser->details->color_picker = gtk_color_button_new ();
+ gtk_widget_show (property_browser->details->color_picker);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), property_browser->details->color_picker);
+
+ gtk_table_attach(GTK_TABLE(table), property_browser->details->color_picker, 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
+
+ return dialog;
+}
+
+/* add the newly selected file to the browser images */
+static void
+add_pattern_to_browser (GtkDialog *dialog, gint response_id, gpointer *data)
+{
+ char *directory_path, *destination_name;
+ char *basename;
+ char *user_directory;
+ GFile *dest, *selected;
+
+ CajaPropertyBrowser *property_browser = CAJA_PROPERTY_BROWSER (data);
+
+ if (response_id != GTK_RESPONSE_ACCEPT)
+ {
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ return;
+ }
+
+ selected = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
+
+ /* don't allow the user to change the reset image */
+ basename = g_file_get_basename (selected);
+ if (basename && eel_strcmp (basename, RESET_IMAGE_NAME) == 0)
+ {
+ eel_show_error_dialog (_("Sorry, but you cannot replace the reset image."),
+ _("Reset is a special image that cannot be deleted."),
+ NULL);
+ g_object_unref (selected);
+ g_free (basename);
+ return;
+ }
+
+
+ user_directory = caja_get_user_directory ();
+
+ /* copy the image file to the patterns directory */
+ directory_path = g_build_filename (user_directory, "patterns", NULL);
+ g_free (user_directory);
+ destination_name = g_build_filename (directory_path, basename, NULL);
+
+ /* make the directory if it doesn't exist */
+ if (!g_file_test (directory_path, G_FILE_TEST_EXISTS))
+ {
+ g_mkdir_with_parents (directory_path, 0775);
+ }
+
+ dest = g_file_new_for_path (destination_name);
+
+ g_free (destination_name);
+ g_free (directory_path);
+
+ if (!g_file_copy (selected, dest,
+ 0,
+ NULL, NULL, NULL, NULL))
+ {
+ char *message = g_strdup_printf (_("Sorry, but the pattern %s could not be installed."), basename);
+ eel_show_error_dialog (message, NULL, GTK_WINDOW (property_browser));
+ g_free (message);
+ }
+ g_object_unref (selected);
+ g_object_unref (dest);
+ g_free (basename);
+
+
+ /* update the property browser's contents to show the new one */
+ caja_property_browser_update_contents (property_browser);
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+}
+
+/* here's where we initiate adding a new pattern by putting up a file selector */
+
+static void
+add_new_pattern (CajaPropertyBrowser *property_browser)
+{
+ GtkWidget *dialog;
+ GtkFileFilter *filter;
+ GtkWidget *preview;
+
+ if (property_browser->details->patterns_dialog)
+ {
+ gtk_window_present (GTK_WINDOW (property_browser->details->patterns_dialog));
+ }
+ else
+ {
+ property_browser->details->patterns_dialog = dialog =
+ gtk_file_chooser_dialog_new (_("Select an Image File to Add as a Pattern"),
+ GTK_WINDOW (property_browser),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
+ DATADIR "/caja/patterns/");
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_add_pixbuf_formats (filter);
+ gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
+
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (dialog), FALSE);
+
+ preview = gtk_image_new ();
+ gtk_file_chooser_set_preview_widget (GTK_FILE_CHOOSER (dialog),
+ preview);
+ g_signal_connect (dialog, "update-preview",
+ G_CALLBACK (update_preview_cb), preview);
+
+ g_signal_connect (dialog, "response",
+ G_CALLBACK (add_pattern_to_browser),
+ property_browser);
+
+ gtk_widget_show (GTK_WIDGET (dialog));
+
+ if (property_browser->details->patterns_dialog)
+ eel_add_weak_pointer (&property_browser->details->patterns_dialog);
+ }
+}
+
+/* here's where we add the passed in color to the file that defines the colors */
+
+static void
+add_color_to_file (CajaPropertyBrowser *property_browser, const char *color_spec, const char *color_name)
+{
+ xmlNodePtr cur_node, new_color_node, children_node;
+ xmlDocPtr document;
+ xmlChar *child_color_name;
+ gboolean color_name_exists = FALSE;
+
+ document = read_browser_xml (property_browser);
+ if (document == NULL)
+ {
+ return;
+ }
+
+ /* find the colors category */
+ cur_node = get_color_category (document);
+ if (cur_node != NULL)
+ {
+ /* check if theres already a color whith that name */
+ children_node = cur_node->xmlChildrenNode;
+ while (children_node != NULL)
+ {
+ child_color_name = xmlGetProp (children_node, "name");
+ if (xmlStrcmp (color_name, child_color_name) == 0)
+ {
+ color_name_exists = TRUE;
+ xmlFree (child_color_name);
+ break;
+ }
+ xmlFree (child_color_name);
+
+ children_node = children_node->next;
+ }
+
+ /* add a new color node */
+ if (!color_name_exists)
+ {
+ new_color_node = xmlNewChild (cur_node, NULL, "color", NULL);
+ xmlNodeSetContent (new_color_node, color_spec);
+ xmlSetProp (new_color_node, "local", "1");
+ xmlSetProp (new_color_node, "name", color_name);
+
+ write_browser_xml (property_browser, document);
+ }
+ else
+ {
+ eel_show_error_dialog (_("The color cannot be installed."),
+ _("Sorry, but you must specify an unused color name for the new color."),
+ GTK_WINDOW (property_browser));
+ }
+ }
+
+ xmlFreeDoc (document);
+}
+
+/* handle the OK button being pushed on the color selection dialog */
+static void
+add_color_to_browser (GtkWidget *widget, gint which_button, gpointer *data)
+{
+ char * color_spec;
+ const char *color_name;
+ char *stripped_color_name;
+
+ CajaPropertyBrowser *property_browser = CAJA_PROPERTY_BROWSER (data);
+
+ if (which_button == GTK_RESPONSE_OK)
+ {
+ GdkColor color;
+
+ gtk_color_button_get_color (GTK_COLOR_BUTTON (property_browser->details->color_picker), &color);
+ color_spec = gdk_color_to_string (&color);
+
+ color_name = gtk_entry_get_text (GTK_ENTRY (property_browser->details->color_name));
+ stripped_color_name = g_strstrip (g_strdup (color_name));
+ if (strlen (stripped_color_name) == 0)
+ {
+ eel_show_error_dialog (_("The color cannot be installed."),
+ _("Sorry, but you must specify a non-blank name for the new color."),
+ GTK_WINDOW (property_browser));
+
+ }
+ else
+ {
+ add_color_to_file (property_browser, color_spec, stripped_color_name);
+ caja_property_browser_update_contents(property_browser);
+ }
+ g_free (stripped_color_name);
+ g_free (color_spec);
+ }
+
+ gtk_widget_destroy(property_browser->details->colors_dialog);
+ property_browser->details->colors_dialog = NULL;
+}
+
+/* create the color selection dialog, pre-set with the color that was just selected */
+static void
+show_color_selection_window (GtkWidget *widget, gpointer *data)
+{
+ GdkColor color;
+ CajaPropertyBrowser *property_browser = CAJA_PROPERTY_BROWSER(data);
+
+ gtk_color_selection_get_current_color (GTK_COLOR_SELECTION
+ (gtk_color_selection_dialog_get_color_selection (GTK_COLOR_SELECTION_DIALOG (property_browser->details->colors_dialog))),
+ &color);
+ gtk_widget_destroy (property_browser->details->colors_dialog);
+
+ /* allocate a new color selection dialog */
+ property_browser->details->colors_dialog = caja_color_selection_dialog_new (property_browser);
+
+ /* set the color to the one picked by the selector */
+ gtk_color_button_set_color (GTK_COLOR_BUTTON (property_browser->details->color_picker), &color);
+
+ /* connect the signals to the new dialog */
+
+ eel_add_weak_pointer (&property_browser->details->colors_dialog);
+
+ g_signal_connect_object (property_browser->details->colors_dialog, "response",
+ G_CALLBACK (add_color_to_browser), property_browser, 0);
+ gtk_window_set_position (GTK_WINDOW (property_browser->details->colors_dialog), GTK_WIN_POS_MOUSE);
+ gtk_widget_show (GTK_WIDGET(property_browser->details->colors_dialog));
+}
+
+
+/* here's the routine to add a new color, by putting up a color selector */
+
+static void
+add_new_color (CajaPropertyBrowser *property_browser)
+{
+ if (property_browser->details->colors_dialog)
+ {
+ gtk_window_present (GTK_WINDOW (property_browser->details->colors_dialog));
+ }
+ else
+ {
+ GtkColorSelectionDialog *color_dialog;
+ GtkWidget *ok_button, *cancel_button, *help_button;
+
+ property_browser->details->colors_dialog = gtk_color_selection_dialog_new (_("Select a Color to Add"));
+ color_dialog = GTK_COLOR_SELECTION_DIALOG (property_browser->details->colors_dialog);
+
+ eel_add_weak_pointer (&property_browser->details->colors_dialog);
+
+ g_object_get (color_dialog, "ok-button", &ok_button,
+ "cancel-button", &cancel_button,
+ "help-button", &help_button, NULL);
+
+ g_signal_connect_object (ok_button, "clicked",
+ G_CALLBACK (show_color_selection_window), property_browser, 0);
+ g_signal_connect_object (cancel_button, "clicked",
+ G_CALLBACK (gtk_widget_destroy), color_dialog, G_CONNECT_SWAPPED);
+ gtk_widget_hide (help_button);
+
+ gtk_window_set_position (GTK_WINDOW (color_dialog), GTK_WIN_POS_MOUSE);
+ gtk_widget_show (GTK_WIDGET(color_dialog));
+ }
+}
+
+/* here's where we handle clicks in the emblem dialog buttons */
+static void
+emblem_dialog_clicked (GtkWidget *dialog, int which_button, CajaPropertyBrowser *property_browser)
+{
+ const char *new_keyword;
+ char *stripped_keyword;
+ char *emblem_path;
+ GFile *emblem_file;
+ GdkPixbuf *pixbuf;
+
+ if (which_button == GTK_RESPONSE_OK)
+ {
+
+ /* update the image path from the file entry */
+ if (property_browser->details->filename)
+ {
+ emblem_path = property_browser->details->filename;
+ emblem_file = g_file_new_for_path (emblem_path);
+ if (ensure_file_is_image (emblem_file))
+ {
+ g_free (property_browser->details->image_path);
+ property_browser->details->image_path = emblem_path;
+ }
+ else
+ {
+ char *message = g_strdup_printf
+ (_("Sorry, but \"%s\" is not a usable image file."), emblem_path);
+ eel_show_error_dialog (_("The file is not an image."), message, GTK_WINDOW (property_browser));
+ g_free (message);
+ g_free (emblem_path);
+ emblem_path = NULL;
+ g_object_unref (emblem_file);
+ return;
+ }
+ g_object_unref (emblem_file);
+ }
+
+ emblem_file = g_file_new_for_path (property_browser->details->image_path);
+ pixbuf = caja_emblem_load_pixbuf_for_emblem (emblem_file);
+ g_object_unref (emblem_file);
+
+ if (pixbuf == NULL)
+ {
+ char *message = g_strdup_printf
+ (_("Sorry, but \"%s\" is not a usable image file."), property_browser->details->image_path);
+ eel_show_error_dialog (_("The file is not an image."), message, GTK_WINDOW (property_browser));
+ g_free (message);
+ }
+
+ new_keyword = gtk_entry_get_text(GTK_ENTRY(property_browser->details->keyword));
+ if (new_keyword == NULL)
+ {
+ stripped_keyword = NULL;
+ }
+ else
+ {
+ stripped_keyword = g_strstrip (g_strdup (new_keyword));
+ }
+
+
+ caja_emblem_install_custom_emblem (pixbuf,
+ stripped_keyword,
+ stripped_keyword,
+ GTK_WINDOW (property_browser));
+ if (pixbuf != NULL)
+ g_object_unref (pixbuf);
+
+ caja_emblem_refresh_list ();
+
+ emit_emblems_changed_signal ();
+
+ g_free (stripped_keyword);
+ }
+
+ gtk_widget_destroy (dialog);
+
+ property_browser->details->keyword = NULL;
+ property_browser->details->emblem_image = NULL;
+ property_browser->details->filename = NULL;
+}
+
+/* here's the routine to add a new emblem, by putting up an emblem dialog */
+
+static void
+add_new_emblem (CajaPropertyBrowser *property_browser)
+{
+ if (property_browser->details->emblems_dialog)
+ {
+ gtk_window_present (GTK_WINDOW (property_browser->details->emblems_dialog));
+ }
+ else
+ {
+ property_browser->details->emblems_dialog = caja_emblem_dialog_new (property_browser);
+
+ eel_add_weak_pointer (&property_browser->details->emblems_dialog);
+
+ g_signal_connect_object (property_browser->details->emblems_dialog, "response",
+ G_CALLBACK (emblem_dialog_clicked), property_browser, 0);
+ gtk_window_set_position (GTK_WINDOW (property_browser->details->emblems_dialog), GTK_WIN_POS_MOUSE);
+ gtk_widget_show (GTK_WIDGET(property_browser->details->emblems_dialog));
+ }
+}
+
+/* cancelremove mode */
+static void
+cancel_remove_mode (CajaPropertyBrowser *property_browser)
+{
+ if (property_browser->details->remove_mode)
+ {
+ property_browser->details->remove_mode = FALSE;
+ caja_property_browser_update_contents(property_browser);
+ gtk_widget_show (property_browser->details->help_label);
+ }
+}
+
+/* handle the add_new button */
+
+static void
+add_new_button_callback(GtkWidget *widget, CajaPropertyBrowser *property_browser)
+{
+ /* handle remove mode, where we act as a cancel button */
+ if (property_browser->details->remove_mode)
+ {
+ cancel_remove_mode (property_browser);
+ return;
+ }
+
+ switch (property_browser->details->category_type)
+ {
+ case CAJA_PROPERTY_PATTERN:
+ add_new_pattern (property_browser);
+ break;
+ case CAJA_PROPERTY_COLOR:
+ add_new_color (property_browser);
+ break;
+ case CAJA_PROPERTY_EMBLEM:
+ add_new_emblem (property_browser);
+ break;
+ default:
+ break;
+ }
+}
+
+/* handle the "done" button */
+static void
+done_button_callback (GtkWidget *widget, GtkWidget *property_browser)
+{
+ cancel_remove_mode (CAJA_PROPERTY_BROWSER (property_browser));
+ gtk_widget_hide (property_browser);
+}
+
+/* handle the "help" button */
+static void
+help_button_callback (GtkWidget *widget, GtkWidget *property_browser)
+{
+ GError *error = NULL;
+ GtkWidget *dialog;
+
+ gtk_show_uri (gtk_widget_get_screen (property_browser),
+ "ghelp:user-guide#goscaja-50",
+ gtk_get_current_event_time (), &error);
+
+ if (error)
+ {
+ dialog = gtk_message_dialog_new (GTK_WINDOW (property_browser),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("There was an error displaying help: \n%s"),
+ error->message);
+
+ g_signal_connect (G_OBJECT (dialog),
+ "response", G_CALLBACK (gtk_widget_destroy),
+ NULL);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_widget_show (dialog);
+ g_error_free (error);
+ }
+}
+
+/* handle the "remove" button */
+static void
+remove_button_callback(GtkWidget *widget, CajaPropertyBrowser *property_browser)
+{
+ if (property_browser->details->remove_mode)
+ {
+ return;
+ }
+
+ property_browser->details->remove_mode = TRUE;
+ gtk_widget_hide (property_browser->details->help_label);
+ caja_property_browser_update_contents(property_browser);
+}
+
+/* this callback handles clicks on the image or color based content content elements */
+
+static void
+element_clicked_callback (GtkWidget *image_table,
+ GtkWidget *child,
+ const EelImageTableEvent *event,
+ gpointer callback_data)
+{
+ CajaPropertyBrowser *property_browser;
+ GtkTargetList *target_list;
+ GdkDragContext *context;
+ const char *element_name;
+ GdkDragAction action;
+
+ g_return_if_fail (EEL_IS_IMAGE_TABLE (image_table));
+ g_return_if_fail (EEL_IS_LABELED_IMAGE (child));
+ g_return_if_fail (event != NULL);
+ g_return_if_fail (CAJA_IS_PROPERTY_BROWSER (callback_data));
+ g_return_if_fail (g_object_get_data (G_OBJECT (child), "property-name") != NULL);
+
+ element_name = g_object_get_data (G_OBJECT (child), "property-name");
+ property_browser = CAJA_PROPERTY_BROWSER (callback_data);
+
+ /* handle remove mode by removing the element */
+ if (property_browser->details->remove_mode)
+ {
+ caja_property_browser_remove_element (property_browser, EEL_LABELED_IMAGE (child));
+ property_browser->details->remove_mode = FALSE;
+ caja_property_browser_update_contents (property_browser);
+ gtk_widget_show (property_browser->details->help_label);
+ return;
+ }
+
+ /* set up the drag and drop type corresponding to the category */
+ drag_types[0].target = property_browser->details->drag_type;
+
+ /* treat the reset property in the colors section specially */
+ if (eel_strcmp (element_name, RESET_IMAGE_NAME) == 0)
+ {
+ drag_types[0].target = "x-special/mate-reset-background";
+ }
+
+ target_list = gtk_target_list_new (drag_types, G_N_ELEMENTS (drag_types));
+ caja_property_browser_set_dragged_file(property_browser, element_name);
+ action = event->button == 3 ? GDK_ACTION_ASK : GDK_ACTION_MOVE | GDK_ACTION_COPY;
+
+ g_object_set_data (G_OBJECT (property_browser), "dragged-image", child);
+
+ context = gtk_drag_begin (GTK_WIDGET (property_browser),
+ target_list,
+ GDK_ACTION_ASK | GDK_ACTION_MOVE | GDK_ACTION_COPY,
+ event->button,
+ event->event);
+ gtk_target_list_unref (target_list);
+
+ /* optionally (if the shift key is down) hide the property browser - it will later be destroyed when the drag ends */
+ property_browser->details->keep_around = (event->state & GDK_SHIFT_MASK) == 0;
+ if (! property_browser->details->keep_around)
+ {
+ gtk_widget_hide (GTK_WIDGET (property_browser));
+ }
+}
+
+static void
+labeled_image_configure (EelLabeledImage *labeled_image)
+{
+ g_return_if_fail (EEL_IS_LABELED_IMAGE (labeled_image));
+
+ eel_labeled_image_set_spacing (labeled_image, LABELED_IMAGE_SPACING);
+}
+
+/* Make a color tile for a property */
+static GtkWidget *
+labeled_image_new (const char *text,
+ GdkPixbuf *pixbuf,
+ const char *property_name,
+ double scale_factor)
+{
+ GtkWidget *labeled_image;
+
+ labeled_image = eel_labeled_image_new (text, pixbuf);
+ labeled_image_configure (EEL_LABELED_IMAGE (labeled_image));
+
+ if (property_name != NULL)
+ {
+ g_object_set_data_full (G_OBJECT (labeled_image),
+ "property-name",
+ g_strdup (property_name),
+ g_free);
+ }
+
+ return labeled_image;
+}
+
+static void
+make_properties_from_directories (CajaPropertyBrowser *property_browser)
+{
+ CajaCustomizationData *customization_data;
+ char *object_name;
+ char *object_label;
+ GdkPixbuf *object_pixbuf;
+ EelImageTable *image_table;
+ GtkWidget *reset_object = NULL;
+ GList *icons, *l;
+ char *icon_name;
+ char *keyword;
+ GtkWidget *property_image;
+ GtkWidget *blank;
+ guint num_images;
+ char *path;
+ CajaIconInfo *info;
+
+ g_return_if_fail (CAJA_IS_PROPERTY_BROWSER (property_browser));
+ g_return_if_fail (EEL_IS_IMAGE_TABLE (property_browser->details->content_table));
+
+ image_table = EEL_IMAGE_TABLE (property_browser->details->content_table);
+
+ if (property_browser->details->category_type == CAJA_PROPERTY_EMBLEM)
+ {
+ eel_g_list_free_deep (property_browser->details->keywords);
+ property_browser->details->keywords = NULL;
+
+ icons = caja_emblem_list_available ();
+
+ property_browser->details->has_local = FALSE;
+ l = icons;
+ while (l != NULL)
+ {
+ icon_name = (char *)l->data;
+ l = l->next;
+
+ if (!caja_emblem_should_show_in_list (icon_name))
+ {
+ continue;
+ }
+
+ object_name = caja_emblem_get_keyword_from_icon_name (icon_name);
+ if (caja_emblem_can_remove_emblem (object_name))
+ {
+ property_browser->details->has_local = TRUE;
+ }
+ else if (property_browser->details->remove_mode)
+ {
+ g_free (object_name);
+ continue;
+ }
+ info = caja_icon_info_lookup_from_name (icon_name, CAJA_ICON_SIZE_STANDARD);
+ object_pixbuf = caja_icon_info_get_pixbuf_at_size (info, CAJA_ICON_SIZE_STANDARD);
+ object_label = g_strdup (caja_icon_info_get_display_name (info));
+ g_object_unref (info);
+
+ if (object_label == NULL)
+ {
+ object_label = g_strdup (object_name);
+ }
+
+ property_image = labeled_image_new (object_label, object_pixbuf, object_name, PANGO_SCALE_LARGE);
+ eel_labeled_image_set_fixed_image_height (EEL_LABELED_IMAGE (property_image), MAX_EMBLEM_HEIGHT);
+
+ keyword = eel_filename_strip_extension (object_name);
+ property_browser->details->keywords = g_list_prepend (property_browser->details->keywords,
+ keyword);
+
+ gtk_container_add (GTK_CONTAINER (image_table), property_image);
+ gtk_widget_show (property_image);
+
+ g_free (object_name);
+ g_free (object_label);
+ if (object_pixbuf != NULL)
+ {
+ g_object_unref (object_pixbuf);
+ }
+ }
+ eel_g_list_free_deep (icons);
+ }
+ else
+ {
+ customization_data = caja_customization_data_new (property_browser->details->category,
+ !property_browser->details->remove_mode,
+ MAX_ICON_WIDTH,
+ MAX_ICON_HEIGHT);
+ if (customization_data == NULL)
+ {
+ return;
+ }
+
+ /* interate through the set of objects and display each */
+ while (caja_customization_data_get_next_element_for_display (customization_data,
+ &object_name,
+ &object_pixbuf,
+ &object_label))
+ {
+
+ property_image = labeled_image_new (object_label, object_pixbuf, object_name, PANGO_SCALE_LARGE);
+
+ gtk_container_add (GTK_CONTAINER (image_table), property_image);
+ gtk_widget_show (property_image);
+
+ /* Keep track of ERASE objects to place them prominently later */
+ if (property_browser->details->category_type == CAJA_PROPERTY_PATTERN
+ && !eel_strcmp (object_name, RESET_IMAGE_NAME))
+ {
+ g_assert (reset_object == NULL);
+ reset_object = property_image;
+ }
+
+ gtk_widget_show (property_image);
+
+ g_free (object_name);
+ g_free (object_label);
+ if (object_pixbuf != NULL)
+ {
+ g_object_unref (object_pixbuf);
+ }
+ }
+
+ property_browser->details->has_local = caja_customization_data_private_data_was_displayed (customization_data);
+ caja_customization_data_destroy (customization_data);
+ }
+
+ /*
+ * We place ERASE objects (for emblems) at the end with a blank in between.
+ */
+ if (property_browser->details->category_type == CAJA_PROPERTY_EMBLEM)
+ {
+ blank = eel_image_table_add_empty_image (image_table);
+ labeled_image_configure (EEL_LABELED_IMAGE (blank));
+
+
+ num_images = eel_wrap_table_get_num_children (EEL_WRAP_TABLE (image_table));
+ g_assert (num_images > 0);
+ eel_wrap_table_reorder_child (EEL_WRAP_TABLE (image_table),
+ blank,
+ num_images - 1);
+
+ gtk_widget_show (blank);
+
+ object_pixbuf = NULL;
+
+ path = caja_pixmap_file (ERASE_OBJECT_NAME);
+ if (path != NULL)
+ {
+ object_pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+ }
+ g_free (path);
+ property_image = labeled_image_new (_("Erase"), object_pixbuf, "erase", PANGO_SCALE_LARGE);
+ eel_labeled_image_set_fixed_image_height (EEL_LABELED_IMAGE (property_image), MAX_EMBLEM_HEIGHT);
+
+ gtk_container_add (GTK_CONTAINER (image_table), property_image);
+ gtk_widget_show (property_image);
+
+ eel_wrap_table_reorder_child (EEL_WRAP_TABLE (image_table),
+ property_image, -1);
+
+ if (object_pixbuf != NULL)
+ {
+ g_object_unref (object_pixbuf);
+ }
+ }
+
+ /*
+ * We place RESET objects (for colors and patterns) at the beginning.
+ */
+ if (reset_object != NULL)
+ {
+ g_assert (EEL_IS_LABELED_IMAGE (reset_object));
+ eel_wrap_table_reorder_child (EEL_WRAP_TABLE (image_table),
+ reset_object,
+ 0);
+ }
+
+}
+
+/* utility routine to add a reset property in the first position */
+static void
+add_reset_property (CajaPropertyBrowser *property_browser)
+{
+ char *reset_image_file_name;
+ GtkWidget *reset_image;
+ GdkPixbuf *reset_pixbuf, *reset_chit;
+
+ reset_chit = NULL;
+
+ reset_image_file_name = g_strdup_printf ("%s/%s/%s", CAJA_DATADIR, "patterns", RESET_IMAGE_NAME);
+ reset_pixbuf = gdk_pixbuf_new_from_file (reset_image_file_name, NULL);
+ if (reset_pixbuf != NULL && property_browser->details->property_chit != NULL)
+ {
+ reset_chit = caja_customization_make_pattern_chit (reset_pixbuf, property_browser->details->property_chit, FALSE, TRUE);
+ }
+
+ g_free (reset_image_file_name);
+
+ reset_image = labeled_image_new (_("Reset"), reset_chit != NULL ? reset_chit : reset_pixbuf, RESET_IMAGE_NAME, PANGO_SCALE_MEDIUM);
+ gtk_container_add (GTK_CONTAINER (property_browser->details->content_table), reset_image);
+ eel_wrap_table_reorder_child (EEL_WRAP_TABLE (property_browser->details->content_table),
+ reset_image,
+ 0);
+ gtk_widget_show (reset_image);
+
+ if (reset_pixbuf != NULL)
+ {
+ g_object_unref (reset_pixbuf);
+ }
+
+ if (reset_chit != NULL)
+ {
+ g_object_unref (reset_chit);
+ }
+}
+
+/* generate properties from the children of the passed in node */
+/* for now, we just handle color nodes */
+
+static void
+make_properties_from_xml_node (CajaPropertyBrowser *property_browser,
+ xmlNodePtr node)
+{
+ xmlNodePtr child_node;
+ GdkPixbuf *pixbuf;
+ GtkWidget *new_property;
+ char *deleted, *local, *color, *name;
+
+ gboolean local_only = property_browser->details->remove_mode;
+
+ /* add a reset property in the first slot */
+ if (!property_browser->details->remove_mode)
+ {
+ add_reset_property (property_browser);
+ }
+
+ property_browser->details->has_local = FALSE;
+
+ for (child_node = eel_xml_get_children (node);
+ child_node != NULL;
+ child_node = child_node->next)
+ {
+
+ if (child_node->type != XML_ELEMENT_NODE)
+ {
+ continue;
+ }
+
+ /* We used to mark colors that were removed with the "deleted" attribute.
+ * To prevent these colors from suddenly showing up now, this legacy remains.
+ */
+ deleted = xmlGetProp (child_node, "deleted");
+ local = xmlGetProp (child_node, "local");
+
+ if (deleted == NULL && (!local_only || local != NULL))
+ {
+ if (local != NULL)
+ {
+ property_browser->details->has_local = TRUE;
+ }
+
+ color = xmlNodeGetContent (child_node);
+ name = eel_xml_get_property_translated (child_node, "name");
+
+ /* make the image from the color spec */
+ pixbuf = make_color_drag_image (property_browser, color, FALSE);
+
+ /* make the tile from the pixmap and name */
+ new_property = labeled_image_new (name, pixbuf, color, PANGO_SCALE_LARGE);
+
+ gtk_container_add (GTK_CONTAINER (property_browser->details->content_table), new_property);
+ gtk_widget_show (new_property);
+
+ g_object_unref (pixbuf);
+ xmlFree (color);
+ xmlFree (name);
+ }
+
+ xmlFree (local);
+ xmlFree (deleted);
+ }
+}
+
+/* handle theme changes by updating the browser contents */
+
+static void
+caja_property_browser_theme_changed (gpointer user_data)
+{
+ CajaPropertyBrowser *property_browser;
+
+ property_browser = CAJA_PROPERTY_BROWSER(user_data);
+ caja_property_browser_update_contents (property_browser);
+}
+
+/* make_category generates widgets corresponding all of the objects in the passed in directory */
+static void
+make_category(CajaPropertyBrowser *property_browser, const char* path, const char* mode, xmlNodePtr node, const char *description)
+{
+
+ /* set up the description in the help label */
+ gtk_label_set_text (GTK_LABEL (property_browser->details->help_label), description);
+
+ /* case out on the mode */
+ if (strcmp (mode, "directory") == 0)
+ make_properties_from_directories (property_browser);
+ else if (strcmp (mode, "inline") == 0)
+ make_properties_from_xml_node (property_browser, node);
+
+}
+
+/* Create a category button */
+static GtkWidget *
+property_browser_category_button_new (const char *display_name,
+ const char *image)
+{
+ GtkWidget *button;
+ char *file_name;
+
+ g_return_val_if_fail (display_name != NULL, NULL);
+ g_return_val_if_fail (image != NULL, NULL);
+
+ file_name = caja_pixmap_file (image);
+ if (file_name != NULL)
+ {
+ button = eel_labeled_image_radio_button_new_from_file_name (display_name, file_name);
+ }
+ else
+ {
+ button = eel_labeled_image_radio_button_new (display_name, NULL);
+ }
+
+ gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (button), FALSE);
+
+ /* We also want all of the buttons to be the same height */
+ eel_labeled_image_set_fixed_image_height (EEL_LABELED_IMAGE (gtk_bin_get_child (GTK_BIN (button))), STANDARD_BUTTON_IMAGE_HEIGHT);
+
+ g_free (file_name);
+
+ return button;
+}
+
+/* this is a utility routine to generate a category link widget and install it in the browser */
+static void
+make_category_link (CajaPropertyBrowser *property_browser,
+ const char *name,
+ const char *display_name,
+ const char *image,
+ GtkRadioButton **group)
+{
+ GtkWidget *button;
+
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (image != NULL);
+ g_return_if_fail (display_name != NULL);
+ g_return_if_fail (CAJA_IS_PROPERTY_BROWSER (property_browser));
+
+ button = property_browser_category_button_new (display_name, image);
+ gtk_widget_show (button);
+
+ if (*group)
+ {
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (button),
+ gtk_radio_button_get_group (*group));
+ }
+ else
+ {
+ *group = GTK_RADIO_BUTTON (button);
+ }
+
+ /* if the button represents the current category, highlight it */
+ if (property_browser->details->category &&
+ strcmp (property_browser->details->category, name) == 0)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), TRUE);
+
+ /* Place it in the category box */
+ gtk_box_pack_start (GTK_BOX (property_browser->details->category_box),
+ button, FALSE, FALSE, 0);
+
+ property_browser->details->category_position += 1;
+
+ /* add a signal to handle clicks */
+ g_object_set_data (G_OBJECT(button), "user_data", property_browser);
+ g_signal_connect_data
+ (button, "toggled",
+ G_CALLBACK (category_toggled_callback),
+ g_strdup (name), (GClosureNotify) g_free, 0);
+}
+
+/* update_contents populates the property browser with information specified by the path and other state variables */
+void
+caja_property_browser_update_contents (CajaPropertyBrowser *property_browser)
+{
+ xmlNodePtr cur_node;
+ xmlDocPtr document;
+ GtkWidget *viewport;
+ GtkRadioButton *group;
+ gboolean got_categories;
+ char *name, *image, *type, *description, *display_name, *path, *mode;
+ const char *text;
+
+ /* load the xml document corresponding to the path and selection */
+ document = read_browser_xml (property_browser);
+ if (document == NULL)
+ {
+ return;
+ }
+
+ /* remove the existing content box, if any, and allocate a new one */
+ if (property_browser->details->content_frame)
+ {
+ gtk_widget_destroy(property_browser->details->content_frame);
+ }
+
+ /* allocate a new container, with a scrollwindow and viewport */
+ property_browser->details->content_frame = gtk_scrolled_window_new (NULL, NULL);
+ viewport = gtk_viewport_new (NULL, NULL);
+ gtk_widget_show(viewport);
+ gtk_viewport_set_shadow_type(GTK_VIEWPORT(viewport), GTK_SHADOW_IN);
+ gtk_container_add (GTK_CONTAINER (property_browser->details->content_container), property_browser->details->content_frame);
+ gtk_widget_show (property_browser->details->content_frame);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (property_browser->details->content_frame),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+
+ /* allocate a table to hold the content widgets */
+ property_browser->details->content_table = eel_image_table_new (TRUE);
+ eel_wrap_table_set_x_spacing (EEL_WRAP_TABLE (property_browser->details->content_table),
+ IMAGE_TABLE_X_SPACING);
+ eel_wrap_table_set_y_spacing (EEL_WRAP_TABLE (property_browser->details->content_table),
+ IMAGE_TABLE_Y_SPACING);
+
+ g_signal_connect_object (property_browser->details->content_table, "child_pressed",
+ G_CALLBACK (element_clicked_callback), property_browser, 0);
+
+ gtk_container_add(GTK_CONTAINER(viewport), property_browser->details->content_table);
+ gtk_container_add (GTK_CONTAINER (property_browser->details->content_frame), viewport);
+ gtk_widget_show (GTK_WIDGET (property_browser->details->content_table));
+
+ /* iterate through the xml file to generate the widgets */
+ got_categories = property_browser->details->category_position >= 0;
+ if (!got_categories)
+ {
+ property_browser->details->category_position = 0;
+ }
+
+ group = NULL;
+ for (cur_node = eel_xml_get_children (xmlDocGetRootElement (document));
+ cur_node != NULL;
+ cur_node = cur_node->next)
+ {
+
+ if (cur_node->type != XML_ELEMENT_NODE)
+ {
+ continue;
+ }
+
+ if (strcmp (cur_node->name, "category") == 0)
+ {
+ name = xmlGetProp (cur_node, "name");
+
+ if (property_browser->details->category != NULL
+ && strcmp (property_browser->details->category, name) == 0)
+ {
+ path = xmlGetProp (cur_node, "path");
+ mode = xmlGetProp (cur_node, "mode");
+ description = eel_xml_get_property_translated (cur_node, "description");
+ type = xmlGetProp (cur_node, "type");
+
+ make_category (property_browser,
+ path,
+ mode,
+ cur_node,
+ description);
+ caja_property_browser_set_drag_type (property_browser, type);
+
+ xmlFree (path);
+ xmlFree (mode);
+ xmlFree (description);
+ xmlFree (type);
+ }
+
+ if (!got_categories)
+ {
+ display_name = eel_xml_get_property_translated (cur_node, "display_name");
+ image = xmlGetProp (cur_node, "image");
+
+ make_category_link (property_browser,
+ name,
+ display_name,
+ image,
+ &group);
+
+ xmlFree (display_name);
+ xmlFree (image);
+ }
+
+ xmlFree (name);
+ }
+ }
+
+ /* release the xml document and we're done */
+ xmlFreeDoc (document);
+
+ /* update the title and button */
+
+ if (property_browser->details->category == NULL)
+ {
+ gtk_label_set_text (GTK_LABEL (property_browser->details->title_label), _("Select a Category:"));
+ gtk_widget_hide(property_browser->details->add_button);
+ gtk_widget_hide(property_browser->details->remove_button);
+
+ }
+ else
+ {
+ char *label_text;
+ char *stock_id;
+ if (property_browser->details->remove_mode)
+ {
+ stock_id = GTK_STOCK_CANCEL;
+ text = _("C_ancel Remove");
+ }
+ else
+ {
+ stock_id = GTK_STOCK_ADD;
+ /* FIXME: Using spaces to add padding is not good design. */
+ switch (property_browser->details->category_type)
+ {
+ case CAJA_PROPERTY_PATTERN:
+ text = _("_Add a New Pattern...");
+ break;
+ case CAJA_PROPERTY_COLOR:
+ text = _("_Add a New Color...");
+ break;
+ case CAJA_PROPERTY_EMBLEM:
+ text = _("_Add a New Emblem...");
+ break;
+ default:
+ text = NULL;
+ break;
+ }
+ }
+
+ /* enable the "add new" button and update it's name and icon */
+ gtk_image_set_from_stock (GTK_IMAGE(property_browser->details->add_button_image), stock_id,
+ GTK_ICON_SIZE_BUTTON);
+
+ if (text != NULL)
+ {
+ gtk_button_set_label (GTK_BUTTON (property_browser->details->add_button), text);
+
+ }
+ gtk_widget_show (property_browser->details->add_button);
+
+
+ if (property_browser->details->remove_mode)
+ {
+
+ switch (property_browser->details->category_type)
+ {
+ case CAJA_PROPERTY_PATTERN:
+ label_text = g_strdup (_("Click on a pattern to remove it"));
+ break;
+ case CAJA_PROPERTY_COLOR:
+ label_text = g_strdup (_("Click on a color to remove it"));
+ break;
+ case CAJA_PROPERTY_EMBLEM:
+ label_text = g_strdup (_("Click on an emblem to remove it"));
+ break;
+ default:
+ label_text = NULL;
+ break;
+ }
+ }
+ else
+ {
+ switch (property_browser->details->category_type)
+ {
+ case CAJA_PROPERTY_PATTERN:
+ label_text = g_strdup (_("Patterns:"));
+ break;
+ case CAJA_PROPERTY_COLOR:
+ label_text = g_strdup (_("Colors:"));
+ break;
+ case CAJA_PROPERTY_EMBLEM:
+ label_text = g_strdup (_("Emblems:"));
+ break;
+ default:
+ label_text = NULL;
+ break;
+ }
+ }
+
+ if (label_text)
+ {
+ gtk_label_set_text_with_mnemonic
+ (GTK_LABEL (property_browser->details->title_label), label_text);
+ }
+ g_free(label_text);
+
+ /* enable the remove button (if necessary) and update its name */
+
+ /* case out instead of substituting to provide flexibilty for other languages */
+ /* FIXME: Using spaces to add padding is not good design. */
+ switch (property_browser->details->category_type)
+ {
+ case CAJA_PROPERTY_PATTERN:
+ text = _("_Remove a Pattern...");
+ break;
+ case CAJA_PROPERTY_COLOR:
+ text = _("_Remove a Color...");
+ break;
+ case CAJA_PROPERTY_EMBLEM:
+ text = _("_Remove an Emblem...");
+ break;
+ default:
+ text = NULL;
+ break;
+ }
+
+ if (property_browser->details->remove_mode
+ || !property_browser->details->has_local)
+ gtk_widget_hide(property_browser->details->remove_button);
+ else
+ gtk_widget_show(property_browser->details->remove_button);
+ if (text != NULL)
+ {
+ gtk_button_set_label (GTK_BUTTON (property_browser->details->remove_button), text);
+ }
+ }
+}
+
+/* set the category and regenerate contents as necessary */
+
+static void
+caja_property_browser_set_category (CajaPropertyBrowser *property_browser,
+ const char *new_category)
+{
+ /* there's nothing to do if the category is the same as the current one */
+ if (eel_strcmp (property_browser->details->category, new_category) == 0)
+ {
+ return;
+ }
+
+ g_free (property_browser->details->category);
+ property_browser->details->category = g_strdup (new_category);
+
+ /* set up the property type enum */
+ if (eel_strcmp (new_category, "patterns") == 0)
+ {
+ property_browser->details->category_type = CAJA_PROPERTY_PATTERN;
+ }
+ else if (eel_strcmp (new_category, "colors") == 0)
+ {
+ property_browser->details->category_type = CAJA_PROPERTY_COLOR;
+ }
+ else if (eel_strcmp (new_category, "emblems") == 0)
+ {
+ property_browser->details->category_type = CAJA_PROPERTY_EMBLEM;
+ }
+ else
+ {
+ property_browser->details->category_type = CAJA_PROPERTY_NONE;
+ }
+
+ /* populate the per-uri box with the info */
+ caja_property_browser_update_contents (property_browser);
+}
+
+
+/* here is the routine that populates the property browser with the appropriate information
+ when the path changes */
+
+void
+caja_property_browser_set_path (CajaPropertyBrowser *property_browser,
+ const char *new_path)
+{
+ /* there's nothing to do if the uri is the same as the current one */
+ if (eel_strcmp (property_browser->details->path, new_path) == 0)
+ {
+ return;
+ }
+
+ g_free (property_browser->details->path);
+ property_browser->details->path = g_strdup (new_path);
+
+ /* populate the per-uri box with the info */
+ caja_property_browser_update_contents (property_browser);
+}
+
+static void
+emblems_changed_callback (GObject *signaller,
+ CajaPropertyBrowser *property_browser)
+{
+ caja_property_browser_update_contents (property_browser);
+}
+
+
+static void
+emit_emblems_changed_signal (void)
+{
+ g_signal_emit_by_name (caja_signaller_get_current (), "emblems_changed");
+}