 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */

/*
 * Nautilus
 *
 * Copyright (C) 1999, 2000, 2001 Eazel, Inc.
 *
 * Nautilus 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.
 *
 * Nautilus 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 <andy@eazel.com>
 *
 */

#include <config.h>
#include "nautilus-information-panel.h"

#include "nautilus-sidebar-title.h"

#include <eel/eel-background.h>
#include <eel/eel-glib-extensions.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-gtk-macros.h>
#include <eel/eel-stock-dialogs.h>
#include <eel/eel-string.h>
#include <eel/eel-vfs-extensions.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <libnautilus-private/nautilus-dnd.h>
#include <libnautilus-private/nautilus-directory.h>
#include <libnautilus-private/nautilus-file-dnd.h>
#include <libnautilus-private/nautilus-file.h>
#include <libnautilus-private/nautilus-global-preferences.h>
#include <libnautilus-private/nautilus-keep-last-vertical-box.h>
#include <libnautilus-private/nautilus-metadata.h>
#include <libnautilus-private/nautilus-mime-actions.h>
#include <libnautilus-private/nautilus-program-choosing.h>
#include <libnautilus-private/nautilus-sidebar-provider.h>
#include <libnautilus-private/nautilus-module.h>

struct NautilusInformationPanelDetails {
	GtkVBox *container;
	NautilusWindowInfo *window;
	NautilusSidebarTitle *title;
	GtkHBox *button_box_centerer;
	GtkVBox *button_box;
	gboolean has_buttons;
	NautilusFile *file;
	guint file_changed_connection;
	gboolean background_connected;

	char *default_background_color;
	char *default_background_image;
	char *current_background_color;
	char *current_background_image;
};

/* button assignments */
#define CONTEXTUAL_MENU_BUTTON 3

static gboolean nautilus_information_panel_press_event           (GtkWidget                    *widget,
								  GdkEventButton               *event);
static void     nautilus_information_panel_finalize              (GObject                      *object);
static void     nautilus_information_panel_drag_data_received    (GtkWidget                    *widget,
								  GdkDragContext               *context,
								  int                           x,
								  int                           y,
								  GtkSelectionData             *selection_data,
								  guint                         info,
								  guint                         time);
static void     nautilus_information_panel_read_defaults         (NautilusInformationPanel     *information_panel);
static void     nautilus_information_panel_style_set             (GtkWidget                    *widget,
								  GtkStyle                     *previous_style);
static void     nautilus_information_panel_theme_changed         (gpointer                      user_data);
static void     nautilus_information_panel_update_appearance     (NautilusInformationPanel     *information_panel);
static void     nautilus_information_panel_update_buttons        (NautilusInformationPanel     *information_panel);
static void     background_metadata_changed_callback             (NautilusInformationPanel     *information_panel);
static void     nautilus_information_panel_iface_init            (NautilusSidebarIface         *iface);
static void     nautilus_information_panel_iface_init            (NautilusSidebarIface         *iface);
static void     sidebar_provider_iface_init                      (NautilusSidebarProviderIface *iface);
static GType    nautilus_information_panel_provider_get_type     (void);

enum {
	LOCATION_CHANGED,
	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

/* drag and drop definitions */

enum {
	TARGET_URI_LIST,
	TARGET_COLOR,
	TARGET_BGIMAGE,
	TARGET_KEYWORD,
	TARGET_BACKGROUND_RESET,
	TARGET_GNOME_URI_LIST
};

static const GtkTargetEntry target_table[] = {
	{ "text/uri-list",  0, TARGET_URI_LIST },
	{ "application/x-color", 0, TARGET_COLOR },
	{ "property/bgimage", 0, TARGET_BGIMAGE },
	{ "property/keyword", 0, TARGET_KEYWORD },
	{ "x-special/gnome-reset-background", 0, TARGET_BACKGROUND_RESET },	
	{ "x-special/gnome-icon-list",  0, TARGET_GNOME_URI_LIST }
};

typedef enum {
	NO_PART,
	BACKGROUND_PART,
	ICON_PART
} InformationPanelPart;

typedef struct {
        GObject parent;
} NautilusInformationPanelProvider;

typedef struct {
        GObjectClass parent;
} NautilusInformationPanelProviderClass;


G_DEFINE_TYPE_WITH_CODE (NautilusInformationPanel, nautilus_information_panel, EEL_TYPE_BACKGROUND_BOX,
			 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_SIDEBAR,
						nautilus_information_panel_iface_init));
/* for EEL_CALL_PARENT */
#define parent_class nautilus_information_panel_parent_class

G_DEFINE_TYPE_WITH_CODE (NautilusInformationPanelProvider, nautilus_information_panel_provider, G_TYPE_OBJECT,
			 G_IMPLEMENT_INTERFACE (NAUTILUS_TYPE_SIDEBAR_PROVIDER,
						sidebar_provider_iface_init));


static const char *
nautilus_information_panel_get_sidebar_id (NautilusSidebar *sidebar)
{
	return NAUTILUS_INFORMATION_PANEL_ID;
}

static char *
nautilus_information_panel_get_tab_label (NautilusSidebar *sidebar)
{
	return g_strdup (_("Information"));
}

static char *
nautilus_information_panel_get_tab_tooltip (NautilusSidebar *sidebar)
{
	return g_strdup (_("Show Information"));
}

static GdkPixbuf *
nautilus_information_panel_get_tab_icon (NautilusSidebar *sidebar)
{
	return NULL;
}

static void
nautilus_information_panel_is_visible_changed (NautilusSidebar *sidebar,
					       gboolean         is_visible)
{
	/* Do nothing */
}

static void
nautilus_information_panel_iface_init (NautilusSidebarIface *iface)
{
	iface->get_sidebar_id = nautilus_information_panel_get_sidebar_id;
	iface->get_tab_label = nautilus_information_panel_get_tab_label;
	iface->get_tab_tooltip = nautilus_information_panel_get_tab_tooltip;
	iface->get_tab_icon = nautilus_information_panel_get_tab_icon;
	iface->is_visible_changed = nautilus_information_panel_is_visible_changed;
}

/* initializing the class object by installing the operations we override */
static void
nautilus_information_panel_class_init (NautilusInformationPanelClass *klass)
{
	GtkWidgetClass *widget_class;
	GObjectClass *gobject_class;
	
	gobject_class = G_OBJECT_CLASS (klass);
	widget_class = GTK_WIDGET_CLASS (klass);
	
	gobject_class->finalize = nautilus_information_panel_finalize;

	widget_class->drag_data_received  = nautilus_information_panel_drag_data_received;
	widget_class->button_press_event  = nautilus_information_panel_press_event;
	widget_class->style_set = nautilus_information_panel_style_set;

	/* add the "location changed" signal */
	signals[LOCATION_CHANGED] = g_signal_new
		("location_changed",
		 G_TYPE_FROM_CLASS (klass),
		 G_SIGNAL_RUN_LAST,
		 G_STRUCT_OFFSET (NautilusInformationPanelClass,
				    location_changed),
		 NULL, NULL,
		 g_cclosure_marshal_VOID__STRING,
		 G_TYPE_NONE, 1, G_TYPE_STRING);
}

/* utility routine to allocate the box the holds the command buttons */
static void
make_button_box (NautilusInformationPanel *information_panel)
{
	information_panel->details->button_box_centerer = GTK_HBOX (gtk_hbox_new (FALSE, 0));
	gtk_box_pack_start (GTK_BOX (information_panel->details->container),
			    GTK_WIDGET (information_panel->details->button_box_centerer), TRUE, TRUE, 0);

	information_panel->details->button_box = GTK_VBOX (nautilus_keep_last_vertical_box_new (4));
	gtk_container_set_border_width (GTK_CONTAINER (information_panel->details->button_box), 8);				
	gtk_widget_show (GTK_WIDGET (information_panel->details->button_box));
	gtk_box_pack_start (GTK_BOX (information_panel->details->button_box_centerer),
			    GTK_WIDGET (information_panel->details->button_box),
			    TRUE, TRUE, 0);
	information_panel->details->has_buttons = FALSE;
}

/* initialize the instance's fields, create the necessary subviews, etc. */

static void
nautilus_information_panel_init (NautilusInformationPanel *information_panel)
{
	GtkWidget *widget;

	widget = GTK_WIDGET (information_panel);

	information_panel->details = g_new0 (NautilusInformationPanelDetails, 1);
	
	/* load the default background */
	nautilus_information_panel_read_defaults (information_panel);

	/* enable mouse tracking */
	gtk_widget_add_events (GTK_WIDGET (information_panel), GDK_POINTER_MOTION_MASK);
	  	
	/* create the container box */
  	information_panel->details->container = GTK_VBOX (gtk_vbox_new (FALSE, 0));
	gtk_container_set_border_width (GTK_CONTAINER (information_panel->details->container), 0);				
	gtk_widget_show (GTK_WIDGET (information_panel->details->container));
	gtk_container_add (GTK_CONTAINER (information_panel),
			   GTK_WIDGET (information_panel->details->container));

	/* allocate and install the index title widget */ 
	information_panel->details->title = NAUTILUS_SIDEBAR_TITLE (nautilus_sidebar_title_new ());
	gtk_widget_show (GTK_WIDGET (information_panel->details->title));
	gtk_box_pack_start (GTK_BOX (information_panel->details->container),
			    GTK_WIDGET (information_panel->details->title),
			    FALSE, FALSE, 8);
	
	/* allocate and install the command button container */
	make_button_box (information_panel);

	/* add a callback for when the theme changes */
	eel_preferences_add_callback (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_SET, nautilus_information_panel_theme_changed, information_panel);
	eel_preferences_add_callback (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_COLOR, nautilus_information_panel_theme_changed, information_panel);
	eel_preferences_add_callback (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_FILENAME, nautilus_information_panel_theme_changed, information_panel);

	/* prepare ourselves to receive dropped objects */
	gtk_drag_dest_set (GTK_WIDGET (information_panel),
			   GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, 
			   target_table, G_N_ELEMENTS (target_table),
			   GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_ASK);
}

static void
nautilus_information_panel_finalize (GObject *object)
{
	NautilusInformationPanel *information_panel;

	information_panel = NAUTILUS_INFORMATION_PANEL (object);

	if (information_panel->details->file != NULL) {
		nautilus_file_monitor_remove (information_panel->details->file, information_panel);
		nautilus_file_unref (information_panel->details->file);
	}
	
	g_free (information_panel->details->default_background_color);
	g_free (information_panel->details->default_background_image);
	g_free (information_panel->details->current_background_color);
	g_free (information_panel->details->current_background_image);
	g_free (information_panel->details);
		
	eel_preferences_remove_callback (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_SET,
					 nautilus_information_panel_theme_changed,
					 information_panel);
	eel_preferences_remove_callback (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_COLOR,
					 nautilus_information_panel_theme_changed,
					 information_panel);
	eel_preferences_remove_callback (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_FILENAME,
					 nautilus_information_panel_theme_changed,
					 information_panel);

	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
}

/* callback to handle resetting the background */
static void
reset_background_callback (GtkWidget *menu_item, GtkWidget *information_panel)
{
	EelBackground *background;

	background = eel_get_widget_background (information_panel);
	if (background != NULL) { 
		eel_background_reset (background); 
	}
}

static gboolean
information_panel_has_background (NautilusInformationPanel *information_panel)
{
	EelBackground *background;
	gboolean has_background;
	char *color;
	char *image;

	background = eel_get_widget_background (GTK_WIDGET(information_panel));

	color = eel_background_get_color (background);
	image = eel_background_get_image_uri (background);
	
	has_background = (color || image);

	return has_background;
}

/* create the context menu */
static GtkWidget *
nautilus_information_panel_create_context_menu (NautilusInformationPanel *information_panel)
{
	GtkWidget *menu, *menu_item;

	menu = gtk_menu_new ();
	gtk_menu_set_screen (GTK_MENU (menu),
			     gtk_widget_get_screen (GTK_WIDGET (information_panel)));
	
	/* add the reset background item, possibly disabled */
	menu_item = gtk_menu_item_new_with_mnemonic (_("Use _Default Background"));
 	gtk_widget_show (menu_item);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
        gtk_widget_set_sensitive (menu_item, information_panel_has_background (information_panel));
	g_signal_connect_object (menu_item, "activate",
				 G_CALLBACK (reset_background_callback), information_panel, 0);

	return menu;
}

/* set up the default backgrounds and images */
static void
nautilus_information_panel_read_defaults (NautilusInformationPanel *information_panel)
{
	gboolean background_set;
	char *background_color, *background_image;
	
	background_set = eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_SET);
	
	background_color = NULL;
	background_image = NULL;
	if (background_set) {
		background_color = eel_preferences_get (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_COLOR);
		background_image = eel_preferences_get (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_FILENAME);
	}	
	
	g_free (information_panel->details->default_background_color);
	information_panel->details->default_background_color = NULL;
	g_free (information_panel->details->default_background_image);
	information_panel->details->default_background_image = NULL;
			
	if (background_color && strlen (background_color)) {
		information_panel->details->default_background_color = g_strdup (background_color);
	}
			
	/* set up the default background image */
	
	if (background_image && strlen (background_image)) {
		information_panel->details->default_background_image = g_strdup (background_image);
	}

	g_free (background_color);
	g_free (background_image);
}

/* handler for handling theme changes */

static void
nautilus_information_panel_theme_changed (gpointer user_data)
{
	NautilusInformationPanel *information_panel;
	
	information_panel = NAUTILUS_INFORMATION_PANEL (user_data);
	nautilus_information_panel_read_defaults (information_panel);
	nautilus_information_panel_update_appearance (information_panel);
	gtk_widget_queue_draw (GTK_WIDGET (information_panel)) ;	
}

/* hit testing */

static InformationPanelPart
hit_test (NautilusInformationPanel *information_panel,
	  int x, int y)
{
	if (nautilus_sidebar_title_hit_test_icon (information_panel->details->title, x, y)) {
		return ICON_PART;
	}
	
	if (eel_point_in_widget (GTK_WIDGET (information_panel), x, y)) {
		return BACKGROUND_PART;
	}

	return NO_PART;
}

/* utility to test if a uri refers to a local image */
static gboolean
uri_is_local_image (const char *uri)
{
	GdkPixbuf *pixbuf;
	char *image_path;
	
	image_path = g_filename_from_uri (uri, NULL, NULL);
	if (image_path == NULL) {
		return FALSE;
	}

	pixbuf = gdk_pixbuf_new_from_file (image_path, NULL);
	g_free (image_path);
	
	if (pixbuf == NULL) {
		return FALSE;
	}
	g_object_unref (pixbuf);
	return TRUE;
}

static void
receive_dropped_uri_list (NautilusInformationPanel *information_panel,
			  GdkDragAction action,
			  int x, int y,
			  GtkSelectionData *selection_data)
{
	char **uris;
	gboolean exactly_one;
	GtkWindow *window;

	uris = g_uri_list_extract_uris ((gchar *) gtk_selection_data_get_data (selection_data));
	exactly_one = uris[0] != NULL && (uris[1] == NULL || uris[1][0] == '\0');
	window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (information_panel)));
	
	switch (hit_test (information_panel, x, y)) {
	case NO_PART:
	case BACKGROUND_PART:
		/* FIXME bugzilla.gnome.org 42507: Does this work for all images, or only background images?
		 * Other views handle background images differently from other URIs.
		 */
		if (exactly_one && uri_is_local_image (uris[0])) {
			if (action == GDK_ACTION_ASK) {
				action = nautilus_drag_drop_background_ask (GTK_WIDGET (information_panel), NAUTILUS_DND_ACTION_SET_AS_BACKGROUND | NAUTILUS_DND_ACTION_SET_AS_GLOBAL_BACKGROUND);
			}	

			if (action > 0) {
				eel_background_receive_dropped_background_image
					(eel_get_widget_background (GTK_WIDGET (information_panel)),
					 action,
					 uris[0]);
			}
		} else if (exactly_one) {
			g_signal_emit (information_panel,
					 signals[LOCATION_CHANGED], 0,
			 		 uris[0]);	
		}
		break;
	case ICON_PART:
		/* handle images dropped on the logo specially */
		
		if (!exactly_one) {
			eel_show_error_dialog (
				_("You cannot assign more than one custom icon at a time."),
				_("Please drag just one image to set a custom icon."), 
				window);
			break;
		}
		
		if (uri_is_local_image (uris[0])) {
			if (information_panel->details->file != NULL) {
				nautilus_file_set_metadata (information_panel->details->file,
							    NAUTILUS_METADATA_KEY_CUSTOM_ICON,
							    NULL,
							    uris[0]);
				nautilus_file_set_metadata (information_panel->details->file,
							    NAUTILUS_METADATA_KEY_ICON_SCALE,
							    NULL,
							    NULL);
			}
		} else {	
			GFile *f;

			f = g_file_new_for_uri (uris[0]);
			if (!g_file_is_native (f)) {
				eel_show_error_dialog (
					_("The file that you dropped is not local."),
					_("You can only use local images as custom icons."), 
					window);
			
			} else {
				eel_show_error_dialog (
					_("The file that you dropped is not an image."),
					_("You can only use images as custom icons."),
					window);
			}
			g_object_unref (f);
		}	
		break;
	}

	g_strfreev (uris);
}

static void
receive_dropped_color (NautilusInformationPanel *information_panel,
		       GdkDragAction action,
		       int x, int y,
		       GtkSelectionData *selection_data)
{
	guint16 *channels;
	char color_spec[8];

	if (gtk_selection_data_get_length (selection_data) != 8 ||
	    gtk_selection_data_get_format (selection_data) != 16) {
		g_warning ("received invalid color data");
		return;
	}
	
	channels = (guint16 *) gtk_selection_data_get_data (selection_data);
	g_snprintf (color_spec, sizeof (color_spec),
		    "#%02X%02X%02X", channels[0] >> 8, channels[1] >> 8, channels[2] >> 8);

	switch (hit_test (information_panel, x, y)) {
	case NO_PART:
		g_warning ("dropped color, but not on any part of information_panel");
		break;
	case ICON_PART:
	case BACKGROUND_PART:
		if (action == GDK_ACTION_ASK) {
			action = nautilus_drag_drop_background_ask (GTK_WIDGET (information_panel), NAUTILUS_DND_ACTION_SET_AS_BACKGROUND | NAUTILUS_DND_ACTION_SET_AS_GLOBAL_BACKGROUND);
		}	

		if (action > 0) {
			/* Let the background change based on the dropped color. */
			eel_background_receive_dropped_color
				(eel_get_widget_background (GTK_WIDGET (information_panel)),
				 GTK_WIDGET (information_panel), 
				 action, x, y, selection_data);
		}
		
		break;
	}
}

/* handle receiving a dropped keyword */

static void
receive_dropped_keyword (NautilusInformationPanel *information_panel,
			 int x, int y,
			 GtkSelectionData *selection_data)
{
	nautilus_drag_file_receive_dropped_keyword (information_panel->details->file,
						    gtk_selection_data_get_data (selection_data));
	
	/* regenerate the display */
	nautilus_information_panel_update_appearance (information_panel);  	
}

static void  
nautilus_information_panel_drag_data_received (GtkWidget *widget, GdkDragContext *context,
					       int x, int y,
					       GtkSelectionData *selection_data,
					       guint info, guint time)
{
	NautilusInformationPanel *information_panel;
	EelBackground *background;

	g_return_if_fail (NAUTILUS_IS_INFORMATION_PANEL (widget));

	information_panel = NAUTILUS_INFORMATION_PANEL (widget);

	switch (info) {
	case TARGET_GNOME_URI_LIST:
	case TARGET_URI_LIST:
		receive_dropped_uri_list (information_panel,
					  gdk_drag_context_get_selected_action (context), x, y, selection_data);
		break;
	case TARGET_COLOR:
		receive_dropped_color (information_panel,
				       gdk_drag_context_get_selected_action (context), x, y, selection_data);
		break;
	case TARGET_BGIMAGE:
		if (hit_test (information_panel, x, y) == BACKGROUND_PART)
			receive_dropped_uri_list (information_panel,
						  gdk_drag_context_get_selected_action (context), x, y, selection_data);
		break;	
	case TARGET_BACKGROUND_RESET:
		background = eel_get_widget_background ( GTK_WIDGET (information_panel));
		if (background != NULL) { 
			eel_background_reset (background); 
		}
		break;
	case TARGET_KEYWORD:
		receive_dropped_keyword (information_panel, x, y, selection_data);
		break;
	default:
		g_warning ("unknown drop type");
	}
}

/* handle the context menu if necessary */
static gboolean
nautilus_information_panel_press_event (GtkWidget *widget, GdkEventButton *event)
{
	NautilusInformationPanel *information_panel;
	GtkWidget *menu;
		
	if (gtk_widget_get_window (widget) != event->window) {
		return FALSE;
	}

	information_panel = NAUTILUS_INFORMATION_PANEL (widget);

	/* handle the context menu */
	if (event->button == CONTEXTUAL_MENU_BUTTON) {
		menu = nautilus_information_panel_create_context_menu (information_panel);	
		eel_pop_up_context_menu (GTK_MENU(menu),
				      EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
				      EEL_DEFAULT_POPUP_MENU_DISPLACEMENT,
				      event);
	}	
	return TRUE;
}

static gboolean
value_different (const char *a, const char *b)
{
	if (!a && !b)
		return FALSE;

	if (!a || !b)
		return TRUE;

	return strcmp (a, b);
}

/* Handle the background changed signal by writing out the settings to metadata.
 */
static void
background_settings_changed_callback (EelBackground *background, GdkDragAction action, NautilusInformationPanel *information_panel)
{
	char *image;
	char *color;

	g_assert (EEL_IS_BACKGROUND (background));
	g_assert (NAUTILUS_IS_INFORMATION_PANEL (information_panel));

	if (information_panel->details->file == NULL) {
		return;
	}
	
	/* Block so we don't respond to our own metadata changes.
	 */
	g_signal_handlers_block_by_func (information_panel->details->file,
					 G_CALLBACK (background_metadata_changed_callback),
					 information_panel);

	color = eel_background_get_color (background);
	image = eel_background_get_image_uri (background);

	if (action != (GdkDragAction) NAUTILUS_DND_ACTION_SET_AS_BACKGROUND) {
		nautilus_file_set_metadata (information_panel->details->file,
					    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR,
					    NULL,
					    NULL);

		nautilus_file_set_metadata (information_panel->details->file,
					    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE,
					    NULL,
					    NULL);
		
		eel_preferences_set
			(NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_COLOR, color ? color : "");
		eel_preferences_set
			(NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_FILENAME, image ? image : "");
		eel_preferences_set_boolean
			(NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_SET, TRUE);
	} else {
		nautilus_file_set_metadata (information_panel->details->file,
					    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR,
					    NULL,
					    color);
		
		nautilus_file_set_metadata (information_panel->details->file,
					    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE,
					    NULL,
					    image);
	}

	if (value_different (information_panel->details->current_background_color, color)) {
		g_free (information_panel->details->current_background_color);
		information_panel->details->current_background_color = g_strdup (color);
	}
	
	if (value_different (information_panel->details->current_background_image, image)) {
		g_free (information_panel->details->current_background_image);
		information_panel->details->current_background_image = g_strdup (image);
	}

	g_free (color);
	g_free (image);

	g_signal_handlers_unblock_by_func (information_panel->details->file,
					   G_CALLBACK (background_metadata_changed_callback),
					   information_panel);
}

/* handle the background reset signal by writing out NULL to metadata and setting the backgrounds
   fields to their default values */
static void
background_reset_callback (EelBackground *background, NautilusInformationPanel *information_panel)
{
	char *color;
	char *image;
	g_assert (EEL_IS_BACKGROUND (background));
	g_assert (NAUTILUS_IS_INFORMATION_PANEL (information_panel));

	if (information_panel->details->file == NULL) {
		return;
	}

	/* Block so we don't respond to our own metadata changes.
	 */
	g_signal_handlers_block_by_func (information_panel->details->file,
					 G_CALLBACK (background_metadata_changed_callback),
					 information_panel);

	color = nautilus_file_get_metadata (information_panel->details->file,
				    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR,
				    NULL);

	image = nautilus_file_get_metadata (information_panel->details->file,
				    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE,
				    NULL);
	if (color || image) {
		nautilus_file_set_metadata (information_panel->details->file,
					    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR,
					    NULL,
					    NULL);
		
		nautilus_file_set_metadata (information_panel->details->file,
					    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE,
					    NULL,
					    NULL);
	} else {
		eel_preferences_set_boolean (NAUTILUS_PREFERENCES_SIDE_PANE_BACKGROUND_SET, FALSE);
	}

	g_signal_handlers_unblock_by_func (information_panel->details->file,
					   G_CALLBACK (background_metadata_changed_callback),
					   information_panel);

	/* Force a read from the metadata to set the defaults
	 */
	background_metadata_changed_callback (information_panel);
}

static GtkWindow *
nautilus_information_panel_get_window (NautilusInformationPanel *information_panel)
{
	GtkWidget *result;

	result = gtk_widget_get_ancestor (GTK_WIDGET (information_panel), GTK_TYPE_WINDOW);

	return result == NULL ? NULL : GTK_WINDOW (result);
}

static void
command_button_callback (GtkWidget *button, GAppInfo *application)
{
	NautilusInformationPanel *information_panel;
	GList files;
	
	information_panel = NAUTILUS_INFORMATION_PANEL (g_object_get_data (G_OBJECT (button), "user_data"));

	files.next = NULL;
	files.prev = NULL;
	files.data = information_panel->details->file;
	nautilus_launch_application (application, &files,
				     nautilus_information_panel_get_window (information_panel));	
}

/* interpret commands for buttons specified by metadata. Handle some built-in ones explicitly, or fork
   a shell to handle general ones */
/* for now, we don't have any of these */
static void
metadata_button_callback (GtkWidget *button, const char *command_str)
{
	NautilusInformationPanel *information_panel;
		
	information_panel = NAUTILUS_INFORMATION_PANEL (g_object_get_data (G_OBJECT (button), "user_data"));
}

/* utility routine that allocates the command buttons from the command list */

static void
add_command_button (NautilusInformationPanel *information_panel, GAppInfo *application)
{
	char *temp_str;
	GtkWidget *temp_button, *label;

	/* There's always at least the "Open with..." button */
	information_panel->details->has_buttons = TRUE;

	temp_str = g_strdup_printf (_("Open With %s"), g_app_info_get_display_name (application));
        temp_button = gtk_button_new_with_label (temp_str);
	label = gtk_bin_get_child (GTK_BIN (temp_button));
	gtk_label_set_ellipsize (GTK_LABEL (label), PANGO_ELLIPSIZE_START);
	g_free (temp_str);
	gtk_box_pack_start (GTK_BOX (information_panel->details->button_box), 
			    temp_button, 
			    FALSE, FALSE, 
			    0);

	g_signal_connect_data (temp_button,
			       "clicked",
			       G_CALLBACK (command_button_callback),
			       g_object_ref (application),
			       (GClosureNotify)g_object_unref,
			       0);

	g_object_set_data (G_OBJECT (temp_button), "user_data", information_panel);
	
	gtk_widget_show (temp_button);
}

/* utility to construct command buttons for the information_panel from the passed in metadata string */

static void
add_buttons_from_metadata (NautilusInformationPanel *information_panel, const char *button_data)
{
	char **terms;
	char *current_term, *temp_str;
	char *button_name, *command_string;
	const char *term;
	int index;
	GtkWidget *temp_button;
	
	/* split the button specification into a set of terms */	
	button_name = NULL;
	terms = g_strsplit (button_data, ";", 0);	
	
	/* for each term, either create a button or attach a property to one */
	for (index = 0; (term = terms[index]) != NULL; index++) {
		current_term = g_strdup (term);
		temp_str = strchr (current_term, '=');
		if (temp_str) {
			*temp_str = '\0';
			if (!g_ascii_strcasecmp (current_term, "button")) {
				button_name = g_strdup (temp_str + 1);
			} else if (!g_ascii_strcasecmp (current_term, "script")) {
			        if (button_name != NULL) {
			        	temp_button = gtk_button_new_with_label (button_name);		    
					gtk_box_pack_start (GTK_BOX (information_panel->details->button_box), 
							    temp_button, 
							    FALSE, FALSE, 
							    0);
					information_panel->details->has_buttons = TRUE;
					command_string = g_strdup (temp_str + 1);
					g_free (button_name);

					g_signal_connect_data (temp_button,
							       "clicked",
							       G_CALLBACK (metadata_button_callback),
							       command_string,
							       (GClosureNotify)g_free,
							       0);

		                	g_object_set_data (G_OBJECT (temp_button), "user_data", information_panel);
					
					gtk_widget_show (temp_button);			
				}
			}
		}
		g_free(current_term);
	}
	g_strfreev (terms);
}

/*
 * nautilus_information_panel_update_buttons:
 * 
 * Update the list of program-launching buttons based on the current uri.
 */
static void
nautilus_information_panel_update_buttons (NautilusInformationPanel *information_panel)
{
	char *button_data;
	GAppInfo *default_app;
	
	/* dispose of any existing buttons */
	if (information_panel->details->has_buttons) {
		gtk_container_remove (GTK_CONTAINER (information_panel->details->container),
				      GTK_WIDGET (information_panel->details->button_box_centerer)); 
		make_button_box (information_panel);
	}

	/* create buttons from file metadata if necessary */
	button_data = nautilus_file_get_metadata (information_panel->details->file,
						  NAUTILUS_METADATA_KEY_SIDEBAR_BUTTONS,
						  NULL);
	if (button_data) {
		add_buttons_from_metadata (information_panel, button_data);
		g_free(button_data);
	}

	/* Make a button for the default application */
	if (nautilus_mime_has_any_applications_for_file (information_panel->details->file) &&
	    !nautilus_file_is_directory (information_panel->details->file)) {
		default_app = 
			nautilus_mime_get_default_application_for_file (information_panel->details->file);
		add_command_button (information_panel, default_app);
		g_object_unref (default_app);
	}

	gtk_widget_show (GTK_WIDGET (information_panel->details->button_box_centerer));
}

static void
nautilus_information_panel_update_appearance (NautilusInformationPanel *information_panel)
{
	EelBackground *background;
	char *background_color;
	char *background_image;

	g_return_if_fail (NAUTILUS_IS_INFORMATION_PANEL (information_panel));
	
	/* Connect the background changed signal to code that writes the color. */
	background = eel_get_widget_background (GTK_WIDGET (information_panel));
	if (!information_panel->details->background_connected) {
		information_panel->details->background_connected = TRUE;
		g_signal_connect_object (background,"settings_changed",
					 G_CALLBACK (background_settings_changed_callback), information_panel, 0);
		g_signal_connect_object (background, "reset",
					 G_CALLBACK (background_reset_callback), information_panel, 0);
	}
	
	/* Set up the background color and image from the metadata. */
	background_color = nautilus_file_get_metadata (information_panel->details->file,
						       NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR,
						       NULL);
	background_image = nautilus_file_get_metadata (information_panel->details->file,
						       NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE,
						       NULL);

	if (background_color == NULL && background_image == NULL) {
		background_color = g_strdup (information_panel->details->default_background_color);
		background_image = g_strdup (information_panel->details->default_background_image);
	}
		
	/* Block so we don't write these settings out in response to our set calls below */
	g_signal_handlers_block_by_func (background,
					 G_CALLBACK (background_settings_changed_callback),
					 information_panel);

	if (value_different (information_panel->details->current_background_color, background_color) ||
	    value_different (information_panel->details->current_background_image, background_image)) {
		
		g_free (information_panel->details->current_background_color);
		information_panel->details->current_background_color = g_strdup (background_color);
		g_free (information_panel->details->current_background_image);
		information_panel->details->current_background_image = g_strdup (background_image);

		eel_background_set_image_uri (background, background_image);
		eel_background_set_color (background, background_color);

		nautilus_sidebar_title_select_text_color
			(information_panel->details->title, background,
			 !information_panel_has_background (information_panel));
	}

	g_free (background_color);
	g_free (background_image);	

	g_signal_handlers_unblock_by_func (background,
					   G_CALLBACK (background_settings_changed_callback),
					   information_panel);
}

static void
background_metadata_changed_callback (NautilusInformationPanel *information_panel)
{
	NautilusFileAttributes attributes;
	gboolean ready;

	attributes = nautilus_mime_actions_get_required_file_attributes ();
	ready = nautilus_file_check_if_ready (information_panel->details->file, attributes);

	if (ready) {
		nautilus_information_panel_update_appearance (information_panel);
		
		/* set up the command buttons */
		nautilus_information_panel_update_buttons (information_panel);
	}
}

/* here is the key routine that populates the information_panel with the appropriate information when the uri changes */

static void
nautilus_information_panel_set_uri (NautilusInformationPanel *information_panel, 
				    const char* new_uri,
				    const char* initial_title)
{       
	NautilusFile *file;
	NautilusFileAttributes attributes;

	g_return_if_fail (NAUTILUS_IS_INFORMATION_PANEL (information_panel));
	g_return_if_fail (new_uri != NULL);
	g_return_if_fail (initial_title != NULL);

	/* there's nothing to do if the uri is the same as the current one */ 
	if (information_panel->details->file != NULL &&
	    nautilus_file_matches_uri (information_panel->details->file, new_uri)) {
		return;
	}

	if (information_panel->details->file != NULL) {
		g_signal_handler_disconnect (information_panel->details->file, 
					     information_panel->details->file_changed_connection);
		nautilus_file_monitor_remove (information_panel->details->file, information_panel);
	}

	file = nautilus_file_get_by_uri (new_uri);

	nautilus_file_unref (information_panel->details->file);
	information_panel->details->file = file;
	
	information_panel->details->file_changed_connection =
		g_signal_connect_object (information_panel->details->file, "changed",
					 G_CALLBACK (background_metadata_changed_callback),
					 information_panel, G_CONNECT_SWAPPED);

	attributes = nautilus_mime_actions_get_required_file_attributes ();
	nautilus_file_monitor_add (information_panel->details->file, information_panel, attributes);

	background_metadata_changed_callback (information_panel);

	/* tell the title widget about it */
	nautilus_sidebar_title_set_file (information_panel->details->title,
					 information_panel->details->file,
					 initial_title);
}

static void
title_changed_callback (NautilusWindowInfo *window,
			char               *new_title,
			NautilusInformationPanel *panel)
{       
	nautilus_sidebar_title_set_text (panel->details->title,
					 new_title);
}

/* ::style_set handler for the information_panel */
static void
nautilus_information_panel_style_set (GtkWidget *widget, GtkStyle *previous_style)
{
	NautilusInformationPanel *information_panel;

	information_panel = NAUTILUS_INFORMATION_PANEL (widget);

	nautilus_information_panel_theme_changed (information_panel);
}

static void
loading_uri_callback (NautilusWindowInfo *window,
		      char               *uri,
		      NautilusInformationPanel *panel)
{
	NautilusWindowSlotInfo *slot;
	char *title;

	slot = nautilus_window_info_get_active_slot (window);

	title = nautilus_window_slot_info_get_title (slot);
	nautilus_information_panel_set_uri (panel,
					    uri,
					    title);
	g_free (title);
}

static void
selection_changed_callback (NautilusWindowInfo *window,
			    NautilusInformationPanel *panel)
{
	int selection_count;
	GList *selection;
	GFile *selected;
	NautilusFile *file;
	char *uri, *name;

	selection = nautilus_window_info_get_selection (window);
	selection_count = g_list_length (selection);

	if (selection_count == 1) {
		selection = nautilus_window_info_get_selection (window);
		selected = selection->data;

		/* this should never fail here, as we're displaying the file */
		file = nautilus_file_get_existing (selected);
		uri = nautilus_file_get_uri (file);
		name = nautilus_file_get_display_name (file);

		nautilus_file_unref (file);
	} else {
		uri = nautilus_window_info_get_current_location (window);
		name = nautilus_window_info_get_title (window);
	}

	nautilus_information_panel_set_uri (panel, uri, name);

	eel_g_object_list_unref (selection);
	g_free (uri);
	g_free (name);
}

static void
nautilus_information_panel_set_parent_window (NautilusInformationPanel *panel,
					      NautilusWindowInfo *window)
{
	gpointer slot;
	char *title, *location;

	panel->details->window = window;
	
	g_signal_connect_object (window, "loading_uri",
				 G_CALLBACK (loading_uri_callback), panel, 0);
	g_signal_connect_object (window, "title_changed",
				 G_CALLBACK (title_changed_callback), panel, 0);
	g_signal_connect_object (window, "selection-changed",
				 G_CALLBACK (selection_changed_callback), panel, 0);

	slot = nautilus_window_info_get_active_slot (window);
	
	title = nautilus_window_slot_info_get_title (slot);
	location = nautilus_window_slot_info_get_current_location (slot);
	nautilus_information_panel_set_uri (panel, 
					    location,
					    title);
	g_free (location);
	g_free (title);
}

static NautilusSidebar *
nautilus_information_panel_create (NautilusSidebarProvider *provider,
				   NautilusWindowInfo *window)
{
	NautilusInformationPanel *panel;
	
	panel = g_object_new (nautilus_information_panel_get_type (), NULL);
	nautilus_information_panel_set_parent_window (panel, window);
	g_object_ref_sink (panel);

	return NAUTILUS_SIDEBAR (panel);
}

static void 
sidebar_provider_iface_init (NautilusSidebarProviderIface *iface)
{
	iface->create = nautilus_information_panel_create;
}

static void
nautilus_information_panel_provider_init (NautilusInformationPanelProvider *sidebar)
{
}

static void
nautilus_information_panel_provider_class_init (NautilusInformationPanelProviderClass *class)
{
}

void
nautilus_information_panel_register (void)
{
        nautilus_module_add_type (nautilus_information_panel_provider_get_type ());
}

