nautilus-information-panel.c 45.3 KB
Newer Older
1
 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2

3 4 5
/*
 * Nautilus
 *
6 7
 * Copyright (C) 1999, 2000 Eazel, Inc.
 *
8
 * Nautilus is free software; you can redistribute it and/or modify
9 10 11 12
 * 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.
 *
13
 * Nautilus is distributed in the hope that it will be useful,
14 15 16 17 18 19 20 21 22 23
 * 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>
 *
24 25 26
 */

/* This is the sidebar widget, which displays overview information
Darin Adler's avatar
Darin Adler committed
27
 * hosts individual panels for various views.
28 29
 */

30
#include <config.h>
31
#include "nautilus-sidebar.h"
32

33 34 35 36
#include "nautilus-link-set-window.h"
#include "nautilus-sidebar-tabs.h"
#include "nautilus-sidebar-title.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
37
#include <libgnomeui/gnome-uidefs.h>
38
#include <libgnomevfs/gnome-vfs-application-registry.h>
39
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
40
#include <libgnomevfs/gnome-vfs-types.h>
41
#include <libgnomevfs/gnome-vfs-uri.h>
42
#include <libgnomevfs/gnome-vfs-utils.h>
43 44 45
#include <libnautilus-extensions/nautilus-background.h>
#include <libnautilus-extensions/nautilus-directory.h>
#include <libnautilus-extensions/nautilus-file.h>
46
#include <libnautilus-extensions/nautilus-file-utilities.h>
47
#include <libnautilus-extensions/nautilus-glib-extensions.h>
48
#include <libnautilus-extensions/nautilus-global-preferences.h>
49 50
#include <libnautilus-extensions/nautilus-gtk-extensions.h>
#include <libnautilus-extensions/nautilus-gtk-macros.h>
51
#include <libnautilus-extensions/nautilus-keep-last-vertical-box.h>
52
#include <libnautilus-extensions/nautilus-metadata.h>
53 54
#include <libnautilus-extensions/nautilus-mime-actions.h>
#include <libnautilus-extensions/nautilus-preferences.h>
55
#include <libnautilus-extensions/nautilus-program-choosing.h>
56
#include <libnautilus-extensions/nautilus-stock-dialogs.h>
57
#include <libnautilus-extensions/nautilus-string.h>
58
#include <libnautilus-extensions/nautilus-theme.h>
59
#include <libnautilus-extensions/nautilus-view-identifier.h>
60 61 62
#include <liboaf/liboaf.h>
#include <math.h>
#include <parser.h>
63

Darin Adler's avatar
Darin Adler committed
64
struct NautilusSidebarDetails {
65
	GtkVBox *container;
66
	NautilusSidebarTitle *title;
67
	GtkNotebook *notebook;
68 69
	NautilusSidebarTabs *sidebar_tabs;
	NautilusSidebarTabs *title_tab;
70
	GtkHBox *button_box_centerer;
71 72
	GtkVBox *button_box;
	gboolean has_buttons;
73
	char *uri;
74 75
	char *default_background_color;
	char *default_background_image;
76
	int selected_index;
77
	NautilusDirectory *directory;
78
	gboolean background_connected;
79
	int old_width;
80 81
};

82 83
/* button assignments */
#define CONTEXTUAL_MENU_BUTTON 3
84

Darin Adler's avatar
Darin Adler committed
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
static void     nautilus_sidebar_initialize_class   (GtkObjectClass   *object_klass);
static void     nautilus_sidebar_initialize         (GtkObject        *object);
static gboolean nautilus_sidebar_press_event        (GtkWidget        *widget,
						     GdkEventButton   *event);
static gboolean nautilus_sidebar_leave_event        (GtkWidget        *widget,
						     GdkEventCrossing *event);
static gboolean nautilus_sidebar_motion_event       (GtkWidget        *widget,
						     GdkEventMotion   *event);
static void     nautilus_sidebar_destroy            (GtkObject        *object);
static void     nautilus_sidebar_drag_data_received (GtkWidget        *widget,
						     GdkDragContext   *context,
						     int               x,
						     int               y,
						     GtkSelectionData *selection_data,
						     guint             info,
						     guint             time);
101 102
static void	nautilus_sidebar_read_theme	    (NautilusSidebar *sidebar);

Darin Adler's avatar
Darin Adler committed
103 104
static void     nautilus_sidebar_size_allocate      (GtkWidget        *widget,
						     GtkAllocation    *allocation);
105
static void	nautilus_sidebar_theme_changed	    (gpointer user_data);
106
static void     nautilus_sidebar_update_appearance  (NautilusSidebar  *sidebar);
Darin Adler's avatar
Darin Adler committed
107 108 109
static void     nautilus_sidebar_update_buttons     (NautilusSidebar  *sidebar);
static void     add_command_buttons                 (NautilusSidebar  *sidebar,
						     GList            *application_list);
110

111
/* FIXME bugzilla.eazel.com 1245: hardwired sizes */
112 113
#define DEFAULT_TAB_COLOR "rgb:9999/9999/9999"

114
#define SIDEBAR_MINIMUM_WIDTH 1
115
#define SIDEBAR_MINIMUM_HEIGHT 400
116

117 118 119 120 121 122 123
enum {
	LOCATION_CHANGED,
	LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

124 125
/* drag and drop definitions */

126
enum {
127
	TARGET_URI_LIST,
128
	TARGET_COLOR,
129
	TARGET_BGIMAGE,
130
	TARGET_KEYWORD,
131
	TARGET_GNOME_URI_LIST
132 133
};

134
static GtkTargetEntry target_table[] = {
135
	{ "text/uri-list",  0, TARGET_URI_LIST },
136
	{ "application/x-color", 0, TARGET_COLOR },
137
	{ "property/bgimage", 0, TARGET_BGIMAGE },
138
	{ "property/keyword", 0, TARGET_KEYWORD },
139
	{ "x-special/gnome-icon-list",  0, TARGET_GNOME_URI_LIST }
140 141
};

142 143 144 145 146 147
typedef enum {
	NO_PART,
	BACKGROUND_PART,
	ICON_PART,
	TITLE_TAB_PART,
	TABS_PART
Darin Adler's avatar
Darin Adler committed
148
} SidebarPart;
149

150

Darin Adler's avatar
Darin Adler committed
151
NAUTILUS_DEFINE_CLASS_BOILERPLATE (NautilusSidebar, nautilus_sidebar, GTK_TYPE_EVENT_BOX)
152 153 154

/* initializing the class object by installing the operations we override */
static void
Darin Adler's avatar
Darin Adler committed
155
nautilus_sidebar_initialize_class (GtkObjectClass *object_klass)
156
{
157
	GtkWidgetClass *widget_class;
158
	
Darin Adler's avatar
Darin Adler committed
159
	NautilusSidebarClass *klass;
160

161
	widget_class = GTK_WIDGET_CLASS (object_klass);
Darin Adler's avatar
Darin Adler committed
162
	klass = NAUTILUS_SIDEBAR_CLASS (object_klass);
163

Darin Adler's avatar
Darin Adler committed
164
	object_klass->destroy = nautilus_sidebar_destroy;
165

Darin Adler's avatar
Darin Adler committed
166 167 168 169 170
	widget_class->drag_data_received  = nautilus_sidebar_drag_data_received;
	widget_class->motion_notify_event = nautilus_sidebar_motion_event;
	widget_class->leave_notify_event = nautilus_sidebar_leave_event;
	widget_class->button_press_event  = nautilus_sidebar_press_event;
	widget_class->size_allocate = nautilus_sidebar_size_allocate;
171 172 173 174 175 176

	/* add the "location changed" signal */
	signals[LOCATION_CHANGED]
		= gtk_signal_new ("location_changed",
			GTK_RUN_FIRST,
			object_klass->type,
Darin Adler's avatar
Darin Adler committed
177
			GTK_SIGNAL_OFFSET (NautilusSidebarClass,
178 179 180 181 182
				location_changed),
			gtk_marshal_NONE__STRING,
			GTK_TYPE_NONE, 1, GTK_TYPE_STRING);

	gtk_object_class_add_signals (object_klass, signals, LAST_SIGNAL);
183 184
}

185 186
/* utility routine to allocate the box the holds the command buttons */
static void
Darin Adler's avatar
Darin Adler committed
187
make_button_box (NautilusSidebar *sidebar)
188
{
Darin Adler's avatar
Darin Adler committed
189 190 191 192 193 194 195 196 197
	sidebar->details->button_box_centerer = GTK_HBOX (gtk_hbox_new (FALSE, 0));
	gtk_box_pack_start_defaults (GTK_BOX (sidebar->details->container),
			    	     GTK_WIDGET (sidebar->details->button_box_centerer));

	sidebar->details->button_box = GTK_VBOX (nautilus_keep_last_vertical_box_new (GNOME_PAD_SMALL));
	gtk_container_set_border_width (GTK_CONTAINER (sidebar->details->button_box), GNOME_PAD);				
	gtk_widget_show (GTK_WIDGET (sidebar->details->button_box));
	gtk_box_pack_start (GTK_BOX (sidebar->details->button_box_centerer),
			    GTK_WIDGET (sidebar->details->button_box),
198
			    TRUE, FALSE, 0);
Darin Adler's avatar
Darin Adler committed
199
	sidebar->details->has_buttons = FALSE;
200 201
}

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

204
static void
Darin Adler's avatar
Darin Adler committed
205
nautilus_sidebar_initialize (GtkObject *object)
206
{
Darin Adler's avatar
Darin Adler committed
207
	NautilusSidebar *sidebar;
208 209
	GtkWidget* widget;
	
Darin Adler's avatar
Darin Adler committed
210
	sidebar = NAUTILUS_SIDEBAR (object);
211
	widget = GTK_WIDGET (object);
212

Darin Adler's avatar
Darin Adler committed
213
	sidebar->details = g_new0 (NautilusSidebarDetails, 1);
214
	
215 216
	/* set the minimum size of the sidebar */
	gtk_widget_set_usize (widget, SIDEBAR_MINIMUM_WIDTH, SIDEBAR_MINIMUM_HEIGHT);
217 218 219

	/* load the default background from the current theme */
	nautilus_sidebar_read_theme(sidebar);
220 221 222

	/* enable mouse tracking */
	gtk_widget_add_events (GTK_WIDGET (sidebar), GDK_POINTER_MOTION_MASK);
223
	  	
224
	/* create the container box */
Darin Adler's avatar
Darin Adler committed
225 226 227 228 229
  	sidebar->details->container = GTK_VBOX (gtk_vbox_new (FALSE, 0));
	gtk_container_set_border_width (GTK_CONTAINER (sidebar->details->container), 0);				
	gtk_widget_show (GTK_WIDGET (sidebar->details->container));
	gtk_container_add (GTK_CONTAINER (sidebar),
			   GTK_WIDGET (sidebar->details->container));
230

231
	/* allocate and install the index title widget */ 
232
	sidebar->details->title = NAUTILUS_SIDEBAR_TITLE (nautilus_sidebar_title_new ());
Darin Adler's avatar
Darin Adler committed
233 234 235
	gtk_widget_show (GTK_WIDGET (sidebar->details->title));
	gtk_box_pack_start (GTK_BOX (sidebar->details->container),
			    GTK_WIDGET (sidebar->details->title),
236
			    FALSE, FALSE, GNOME_PAD);
237
	
238
	/* allocate the index tabs */
239
	sidebar->details->sidebar_tabs = NAUTILUS_SIDEBAR_TABS (nautilus_sidebar_tabs_new ());
Darin Adler's avatar
Darin Adler committed
240
	sidebar->details->selected_index = -1;
241

242
	/* also, allocate the title tab */
243 244
	sidebar->details->title_tab = NAUTILUS_SIDEBAR_TABS (nautilus_sidebar_tabs_new ());
	nautilus_sidebar_tabs_set_title_mode (sidebar->details->title_tab, TRUE);	
245
	
246
	gtk_widget_show (GTK_WIDGET (sidebar->details->sidebar_tabs));
Darin Adler's avatar
Darin Adler committed
247
	gtk_box_pack_end (GTK_BOX (sidebar->details->container),
248
			  GTK_WIDGET (sidebar->details->sidebar_tabs),
249
			  FALSE, FALSE, 0);
250

Darin Adler's avatar
Darin Adler committed
251
	sidebar->details->old_width = widget->allocation.width;
252
	
Darin Adler's avatar
Darin Adler committed
253 254 255 256
	/* allocate and install the panel tabs */
  	sidebar->details->notebook = GTK_NOTEBOOK (gtk_notebook_new ());
	gtk_object_ref (GTK_OBJECT (sidebar->details->notebook));
	gtk_object_sink (GTK_OBJECT (sidebar->details->notebook));
257
		
Darin Adler's avatar
Darin Adler committed
258
	gtk_notebook_set_show_tabs (sidebar->details->notebook, FALSE);
259 260
	
	/* allocate and install the command button container */
Darin Adler's avatar
Darin Adler committed
261
	make_button_box (sidebar);
262

263 264 265
	/* add a callback for when the theme changes */
	nautilus_preferences_add_callback (NAUTILUS_PREFERENCES_THEME, nautilus_sidebar_theme_changed, sidebar);	

266
	/* prepare ourselves to receive dropped objects */
Darin Adler's avatar
Darin Adler committed
267
	gtk_drag_dest_set (GTK_WIDGET (sidebar),
268
			   GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, 
269 270
			   target_table, NAUTILUS_N_ELEMENTS (target_table),
			   GDK_ACTION_COPY | GDK_ACTION_MOVE);
271 272 273
}

static void
Darin Adler's avatar
Darin Adler committed
274
nautilus_sidebar_destroy (GtkObject *object)
275
{
Darin Adler's avatar
Darin Adler committed
276
	NautilusSidebar *sidebar;
277

Darin Adler's avatar
Darin Adler committed
278
	sidebar = NAUTILUS_SIDEBAR (object);
279

Darin Adler's avatar
Darin Adler committed
280
	gtk_object_unref (GTK_OBJECT (sidebar->details->notebook));
281

Darin Adler's avatar
Darin Adler committed
282
	nautilus_directory_unref (sidebar->details->directory);
283

Darin Adler's avatar
Darin Adler committed
284
	g_free (sidebar->details->uri);
285 286 287
	g_free (sidebar->details->default_background_color);
	g_free (sidebar->details->default_background_image);
	
Darin Adler's avatar
Darin Adler committed
288
	g_free (sidebar->details);
289 290 291 292
	
	nautilus_preferences_remove_callback (NAUTILUS_PREFERENCES_THEME,
					      nautilus_sidebar_theme_changed,
					      sidebar);
293

294
	NAUTILUS_CALL_PARENT_CLASS (GTK_OBJECT_CLASS, destroy, (object));
295 296
}

297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
/* utility routines to test if sidebar panel is currently enabled */
static char *
nautilus_sidebar_get_sidebar_panel_key (const char *panel_iid)
{
	g_return_val_if_fail (panel_iid != NULL, NULL);

	return g_strdup_printf ("%s/%s", NAUTILUS_PREFERENCES_SIDEBAR_PANELS_NAMESPACE, panel_iid);
}

static gboolean
nautilus_sidebar_sidebar_panel_enabled (const char *panel_iid)
{
	gboolean enabled;
        gchar	 *key;

	key = nautilus_sidebar_get_sidebar_panel_key (panel_iid);
        enabled = nautilus_preferences_get_boolean (key, FALSE);

        g_free (key);
        return enabled;
}

319 320 321 322 323 324 325 326 327 328 329
/* callback to handle resetting the background */
static void
reset_background_callback(GtkWidget *menu_item, GtkWidget *sidebar)
{
	NautilusBackground *background;
	background = nautilus_get_widget_background(sidebar);
	if (background) { 
		nautilus_background_reset(background); 
	}
}

330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
/* callback for sidebar panel menu items to toggle their visibility */
static void
toggle_sidebar_panel(GtkWidget *widget, char *sidebar_id)
{
        gchar	 *key;

	key = nautilus_sidebar_get_sidebar_panel_key (sidebar_id);
	nautilus_preferences_set_boolean(key, !nautilus_preferences_get_boolean(key, FALSE));
	g_free(key); 
}

/* utility routine to add a menu item for each potential sidebar panel */

static void
nautilus_sidebar_add_panel_items(NautilusSidebar *sidebar, GtkWidget *menu)
{
	CORBA_Environment ev;
	const char *query;
        OAF_ServerInfoList *oaf_result;
	int i;
	gboolean enabled;
	GList *name_list;
	GtkWidget *menu_item;
	NautilusViewIdentifier *id;

	CORBA_exception_init (&ev);

	/* ask OAF for all of the sidebars panel */
	query = "nautilus:sidebar_panel_name.defined() AND repo_ids.has ('IDL:Bonobo/Control:1.0')";
	oaf_result = oaf_query (query, NULL, &ev);
	
	/* loop through the results, appending a new menu item for each unique sidebar panel */
	name_list = NULL;
        if (ev._major == CORBA_NO_EXCEPTION && oaf_result != NULL) {
		for (i = 0; i < oaf_result->_length; i++) {
			id = nautilus_view_identifier_new_from_sidebar_panel
				(&oaf_result->_buffer[i]);
			/* check to see if we've seen this one */
			if (g_list_find_custom (name_list, id->name, (GCompareFunc) strcmp) == NULL) {
				name_list = g_list_append (name_list, g_strdup (id->name));
			
				/* add a check menu item */
372 373 374 375 376
				menu_item = gtk_check_menu_item_new_with_label (id->name);
				enabled = nautilus_sidebar_sidebar_panel_enabled (id->iid);
				gtk_check_menu_item_set_show_toggle (GTK_CHECK_MENU_ITEM(menu_item), TRUE);
				gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), enabled);
				gtk_widget_show (menu_item);
377
				gtk_menu_append (GTK_MENU(menu), menu_item);
378 379 380 381
				gtk_signal_connect_full (GTK_OBJECT (menu_item), "activate",
							 GTK_SIGNAL_FUNC (toggle_sidebar_panel),
							 NULL, g_strdup(id ->iid), g_free,
							 FALSE, FALSE);
382 383 384 385 386 387 388 389 390 391 392 393 394
			}
			nautilus_view_identifier_free (id);
		}
	} 
	if (name_list != NULL)
		nautilus_g_list_free_deep(name_list);
		
	if (oaf_result != NULL) {
		CORBA_free (oaf_result);
	}
	
	CORBA_exception_free (&ev);
}
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416

/* check to see if the background matches the default */
static gboolean
nautilus_sidebar_background_is_default (NautilusSidebar *sidebar)
{
	char *background_color, *background_image;
	gboolean is_default;
	
	background_color = nautilus_directory_get_metadata (sidebar->details->directory,
							    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR,
							    NULL);
	background_image = nautilus_directory_get_metadata (sidebar->details->directory,
							    NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE,
								    NULL);
	
	is_default = background_color == NULL && background_image == NULL;
	g_free (background_color);
	g_free (background_image);
	
	return is_default;
}

417 418 419 420 421 422 423 424 425
/* create the context menu */
GtkWidget *
nautilus_sidebar_create_context_menu (NautilusSidebar *sidebar)
{
	GtkWidget *menu, *menu_item;
	NautilusBackground *background;
	gboolean has_background;

	background = nautilus_get_widget_background (GTK_WIDGET(sidebar));
426
	has_background = background && !nautilus_sidebar_background_is_default (sidebar);
427 428 429 430 431 432 433 434 435 436
	
	menu = gtk_menu_new ();
	
	/* add the reset background item, possibly disabled */
	menu_item = gtk_menu_item_new_with_label (_("Reset Background"));
 	gtk_widget_show (menu_item);
	gtk_menu_append (GTK_MENU(menu), menu_item);
        gtk_widget_set_sensitive (menu_item, has_background);
	gtk_signal_connect (GTK_OBJECT (menu_item), "activate", reset_background_callback, sidebar);

437 438 439
	/* add a separator */
	menu_item = gtk_menu_item_new ();
	gtk_widget_show (menu_item);
440
	gtk_menu_append (GTK_MENU (menu), menu_item);
441 442 443
	
	/* add the sidebar panels */
	nautilus_sidebar_add_panel_items(sidebar, menu);
444 445 446
	return menu;
}

447
/* create a new instance */
Darin Adler's avatar
Darin Adler committed
448 449
NautilusSidebar *
nautilus_sidebar_new (void)
450
{
451
	return NAUTILUS_SIDEBAR (gtk_widget_new (nautilus_sidebar_get_type (), NULL));
452 453
}

454 455 456 457 458
/* utility routine to handle mapping local file names to a uri */
static char*
map_local_data_file (char *file_name)
{
	char *temp_str;
459
	if (file_name && !nautilus_istr_has_prefix (file_name, "file://")) {
460 461 462 463 464 465 466

		if (nautilus_str_has_prefix (file_name, "./")) {
			temp_str = nautilus_theme_get_image_path (file_name + 2);
		} else {
			temp_str = g_strdup_printf ("%s/%s", NAUTILUS_DATADIR, file_name);
		}
		
467
		g_free (file_name);
468
		file_name = gnome_vfs_get_uri_from_local_path (temp_str);
469 470 471 472 473
		g_free (temp_str);
	}
	return file_name;
}

474 475 476 477
/* read the theme file and set up the default backgrounds and images accordingly */
static void
nautilus_sidebar_read_theme (NautilusSidebar *sidebar)
{
478
	char *background_color, *background_image;
479
	
480 481
	background_color = nautilus_theme_get_theme_data ("sidebar", NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR);
	background_image = nautilus_theme_get_theme_data ("sidebar", NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE);
482
	
483 484 485 486
	g_free(sidebar->details->default_background_color);
	sidebar->details->default_background_color = NULL;
	g_free(sidebar->details->default_background_image);
	sidebar->details->default_background_image = NULL;
487
			
488
	if (background_color && strlen (background_color)) {
489 490
		sidebar->details->default_background_color = g_strdup(background_color);
	}
491
			
492
	/* set up the default background image */
493
	
494 495
	background_image = map_local_data_file (background_image);
	if (background_image && strlen (background_image)) {
496
		sidebar->details->default_background_image = g_strdup(background_image);
497
	}
498 499 500

	g_free (background_color);
	g_free (background_image);
501 502 503 504 505 506 507 508 509
}

/* handler for handling theme changes */

static void
nautilus_sidebar_theme_changed (gpointer user_data)
{
	NautilusSidebar *sidebar;
	
510 511 512 513
	sidebar = NAUTILUS_SIDEBAR (user_data);
	nautilus_sidebar_read_theme (sidebar);
	nautilus_sidebar_update_appearance (sidebar);
	gtk_widget_queue_draw (GTK_WIDGET (sidebar)) ;	
514 515 516 517
}

/* hit testing */

Darin Adler's avatar
Darin Adler committed
518 519
static SidebarPart
hit_test (NautilusSidebar *sidebar,
520 521
	  int x, int y)
{
522
	if (nautilus_point_in_widget (GTK_WIDGET (sidebar->details->sidebar_tabs), x, y)) {
523 524 525
		return TABS_PART;
	}
	
Darin Adler's avatar
Darin Adler committed
526
	if (nautilus_point_in_widget (GTK_WIDGET (sidebar->details->title_tab), x, y)) {
527 528 529
		return TITLE_TAB_PART;
	}
	
530
	if (nautilus_sidebar_title_hit_test_icon (sidebar->details->title, x, y)) {
531 532 533
		return ICON_PART;
	}
	
Darin Adler's avatar
Darin Adler committed
534
	if (nautilus_point_in_widget (GTK_WIDGET (sidebar), x, y)) {
535 536 537 538 539 540
		return BACKGROUND_PART;
	}

	return NO_PART;
}

541
/* utility to test if a uri refers to a local image */
542 543 544 545
static gboolean
uri_is_local_image (const char *uri)
{
	GdkPixbuf *pixbuf;
546
	char *image_path;
547
	
548
	if (nautilus_is_remote_uri (uri)) {
549 550
		return FALSE;
	}
551
	
552 553 554 555 556
	image_path = gnome_vfs_get_local_path_from_uri (uri);
	if (image_path == NULL) {
		return FALSE;
	}

557 558 559
	pixbuf = gdk_pixbuf_new_from_file (image_path);
	g_free (image_path);
	
560 561 562 563 564 565 566 567
	if (pixbuf == NULL) {
		return FALSE;
	}
	gdk_pixbuf_unref (pixbuf);
	return TRUE;
}

static void
Darin Adler's avatar
Darin Adler committed
568
receive_dropped_uri_list (NautilusSidebar *sidebar,
569 570 571 572 573 574
			  int x, int y,
			  GtkSelectionData *selection_data)
{
	char **uris;
	gboolean exactly_one;
	NautilusFile *file;
575 576
	GtkWindow *window;
	
577 578
	uris = g_strsplit (selection_data->data, "\r\n", 0);
	exactly_one = uris[0] != NULL && uris[1] == NULL;
579 580
	window = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (sidebar)));
	
Darin Adler's avatar
Darin Adler committed
581
	switch (hit_test (sidebar, x, y)) {
582 583
	case NO_PART:
	case BACKGROUND_PART:
584
		/* FIXME bugzilla.eazel.com 2507: Does this work for all images, or only background images?
585 586
		 * Other views handle background images differently from other URIs.
		 */
587
		if (exactly_one && uri_is_local_image (uris[0])) {
588
			nautilus_background_receive_dropped_background_image
589 590
				(nautilus_get_widget_background (GTK_WIDGET (sidebar)),
				 uris[0]);
591
		} else if (exactly_one) {
Darin Adler's avatar
Darin Adler committed
592
			gtk_signal_emit (GTK_OBJECT (sidebar),
593 594 595
					 signals[LOCATION_CHANGED],
			 		 uris[0]);	
		}
596
		break;
597 598 599 600 601
	case TABS_PART:
	case TITLE_TAB_PART:
		break;
	case ICON_PART:
		/* handle images dropped on the logo specially */
602 603
		
		if (!exactly_one) {
604 605 606
			nautilus_error_dialog (
				_("You can't assign more than one custom icon at a time! "
				  "Please drag just one image to set a custom icon."), 
607
				_("More Than One Image"),
608
				window);
609 610 611 612
			break;
		}
		
		if (uri_is_local_image (uris[0])) {
Darin Adler's avatar
Darin Adler committed
613
			file = nautilus_file_get (sidebar->details->uri);
614
						
615 616
			if (file != NULL) {
				nautilus_file_set_metadata (file,
617
							    NAUTILUS_METADATA_KEY_CUSTOM_ICON,
618 619
							    NULL,
							    uris[0]);
620 621 622 623
				nautilus_file_set_metadata (file,
							    NAUTILUS_METADATA_KEY_ICON_SCALE,
							    NULL,
							    NULL);
624 625
				nautilus_file_unref (file);
			}
626 627
		} else {	
			if (nautilus_is_remote_uri (uris[0])) {
628 629 630
				nautilus_error_dialog (
					_("The file that you dropped is not local.  "
					  "You can only use local images as custom icons."), 
631
					_("Local Images Only"),
632
					window);
633 634
			
			} else {
635 636 637
				nautilus_error_dialog (
					_("The file that you dropped is not an image.  "
					  "You can only use local images as custom icons."),
638
					_("Images Only"),
639
					window);
640 641
			}
		}	
642 643 644 645 646 647 648
		break;
	}

	g_strfreev (uris);
}

static void
Darin Adler's avatar
Darin Adler committed
649
receive_dropped_color (NautilusSidebar *sidebar,
650 651 652
		       int x, int y,
		       GtkSelectionData *selection_data)
{
653
	guint16 *channels;
654
	char *color_spec;
655 656 657 658 659 660 661 662 663

	if (selection_data->length != 8 || selection_data->format != 16) {
		g_warning ("received invalid color data");
		return;
	}
	
	channels = (guint16 *) selection_data->data;
	color_spec = g_strdup_printf ("rgb:%04hX/%04hX/%04hX", channels[0], channels[1], channels[2]);

Darin Adler's avatar
Darin Adler committed
664
	switch (hit_test (sidebar, x, y)) {
665 666 667 668 669
	case NO_PART:
		g_warning ("dropped color, but not on any part of sidebar");
		break;
	case TABS_PART:
		/* color dropped on main tabs */
670 671
		nautilus_sidebar_tabs_receive_dropped_color
			(sidebar->details->sidebar_tabs,
672
			 x, y, selection_data);
673
		
Darin Adler's avatar
Darin Adler committed
674 675 676 677 678
		nautilus_directory_set_metadata
			(sidebar->details->directory,
			 NAUTILUS_METADATA_KEY_SIDEBAR_TAB_COLOR,
			 DEFAULT_TAB_COLOR,
			 color_spec);
679
		
680 681 682
		break;
	case TITLE_TAB_PART:
		/* color dropped on title tab */
683
		nautilus_sidebar_tabs_receive_dropped_color
Darin Adler's avatar
Darin Adler committed
684
			(sidebar->details->title_tab,
685
			 x, y, selection_data);
686
		
Darin Adler's avatar
Darin Adler committed
687 688 689 690 691
		nautilus_directory_set_metadata
			(sidebar->details->directory,
			 NAUTILUS_METADATA_KEY_SIDEBAR_TITLE_TAB_COLOR,
			 DEFAULT_TAB_COLOR,
			 color_spec);
692 693 694 695 696
		break;
	case ICON_PART:
	case BACKGROUND_PART:
		/* Let the background change based on the dropped color. */
		nautilus_background_receive_dropped_color
Darin Adler's avatar
Darin Adler committed
697 698
			(nautilus_get_widget_background (GTK_WIDGET (sidebar)),
			 GTK_WIDGET (sidebar), x, y, selection_data);
699 700
		
		/* regenerate the display */
701
		nautilus_sidebar_update_appearance (sidebar);  	
702
		
703 704
		break;
	}
705
	g_free(color_spec);
706
}
707

708 709 710
/* handle receiving a dropped keyword */

static void
Darin Adler's avatar
Darin Adler committed
711
receive_dropped_keyword (NautilusSidebar *sidebar,
712 713
			 int x, int y,
			 GtkSelectionData *selection_data)
714 715 716
{
	NautilusFile *file;
	GList *keywords, *word;
717

718
	/* FIXME bugzilla.eazel.com 2509: This is a cut and paste copy of code that's in the icon dnd code. */
719
			
720 721
	/* OK, now we've got the keyword, so add it to the metadata */

722 723 724 725
	/* FIXME bugzilla.eazel.com 866: Can't expect to read the
	 * keywords list instantly here. We might need to read the
	 * metafile first.
	 */
Darin Adler's avatar
Darin Adler committed
726
	file = nautilus_file_get (sidebar->details->uri);
727
	if (file == NULL)
728
		return;
729

730 731
	/* Check and see if it's already there. */
	keywords = nautilus_file_get_keywords (file);
732
	word = g_list_find_custom (keywords, selection_data->data, (GCompareFunc) strcmp);
733
	if (word == NULL) {
734
		keywords = g_list_append (keywords, g_strdup (selection_data->data));
735
	} else {
736
		keywords = g_list_remove_link (keywords, word);
737
		g_free (word->data);
738
		g_list_free_1 (word);
739
	}
740 741

	nautilus_file_set_keywords (file, keywords);
742
	nautilus_file_unref (file);
743 744
	
	/* regenerate the display */
745
	nautilus_sidebar_update_appearance (sidebar);  	
746 747
}

748
static void  
Darin Adler's avatar
Darin Adler committed
749
nautilus_sidebar_drag_data_received (GtkWidget *widget, GdkDragContext *context,
750
					 int x, int y,
751 752
					 GtkSelectionData *selection_data,
					 guint info, guint time)
753
{
Darin Adler's avatar
Darin Adler committed
754
	NautilusSidebar *sidebar;
755

Darin Adler's avatar
Darin Adler committed
756
	g_return_if_fail (NAUTILUS_IS_SIDEBAR (widget));
757

Darin Adler's avatar
Darin Adler committed
758
	sidebar = NAUTILUS_SIDEBAR (widget);
759 760 761 762

	switch (info) {
	case TARGET_GNOME_URI_LIST:
	case TARGET_URI_LIST:
Darin Adler's avatar
Darin Adler committed
763
		receive_dropped_uri_list (sidebar, x, y, selection_data);
764 765
		break;
	case TARGET_COLOR:
Darin Adler's avatar
Darin Adler committed
766
		receive_dropped_color (sidebar, x, y, selection_data);
767
		break;
768
	case TARGET_BGIMAGE:
Darin Adler's avatar
Darin Adler committed
769 770
		if (hit_test (sidebar, x, y) == BACKGROUND_PART)
			receive_dropped_uri_list (sidebar, x, y, selection_data);
771
		break;	
772
	case TARGET_KEYWORD:
Darin Adler's avatar
Darin Adler committed
773
		receive_dropped_keyword(sidebar, x, y, selection_data);
774
		break;
775 776 777
	default:
		g_warning ("unknown drop type");
	}
778 779
}

Darin Adler's avatar
Darin Adler committed
780
/* add a new panel to the sidebar */
781
void
Darin Adler's avatar
Darin Adler committed
782
nautilus_sidebar_add_panel (NautilusSidebar *sidebar, NautilusViewFrame *panel)
783
{
784
	GtkWidget *label;
Darin Adler's avatar
Darin Adler committed
785
	char *description;
786
	int page_num;
787
	
Darin Adler's avatar
Darin Adler committed
788 789
	g_return_if_fail (NAUTILUS_IS_SIDEBAR (sidebar));
	g_return_if_fail (NAUTILUS_IS_VIEW_FRAME (panel));
790
	
Darin Adler's avatar
Darin Adler committed
791
	description = nautilus_view_frame_get_label (panel);
792

793
	label = gtk_label_new (description);
Darin Adler's avatar
Darin Adler committed
794

795 796
	gtk_widget_show (label);
	
Darin Adler's avatar
Darin Adler committed
797 798 799 800
	gtk_notebook_append_page (GTK_NOTEBOOK (sidebar->details->notebook),
				  GTK_WIDGET (panel), label);
	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (sidebar->details->notebook),
					  GTK_WIDGET (panel));
801

802
	/* tell the index tabs about it */
803
	nautilus_sidebar_tabs_add_view (sidebar->details->sidebar_tabs,
Darin Adler's avatar
Darin Adler committed
804
				      description, GTK_WIDGET (panel), page_num);
805
	
806 807
	g_free (description);

Darin Adler's avatar
Darin Adler committed
808
	gtk_widget_show (GTK_WIDGET (panel));
809 810
}

Darin Adler's avatar
Darin Adler committed
811
/* remove the passed-in panel from the sidebar */
812
void
Darin Adler's avatar
Darin Adler committed
813 814
nautilus_sidebar_remove_panel (NautilusSidebar *sidebar,
				       NautilusViewFrame *panel)
815
{
816
	int page_num;
817
	char *description;
818
	
Darin Adler's avatar
Darin Adler committed
819 820
	page_num = gtk_notebook_page_num (GTK_NOTEBOOK (sidebar->details->notebook),
					  GTK_WIDGET (panel));
821 822 823 824 825 826


	/* FIXME bugzilla.eazel.com 1840: 
	 * This g_return_if_fail gets hit in cases where the sidebar panel
	 * fails when loading, which causes the panel's tab to be left behind.
	 */
827
	g_return_if_fail (page_num >= 0);
828

Darin Adler's avatar
Darin Adler committed
829
	gtk_notebook_remove_page (GTK_NOTEBOOK (sidebar->details->notebook),
830
				  page_num);
831

Darin Adler's avatar
Darin Adler committed
832
	description = nautilus_view_frame_get_label (panel);
833

Darin Adler's avatar
Darin Adler committed
834
	/* Remove the tab associated with this panel */
835
	nautilus_sidebar_tabs_remove_view (sidebar->details->sidebar_tabs, description);
836 837

	g_free (description);
838 839
}

Darin Adler's avatar
Darin Adler committed
840
/* utility to activate the panel corresponding to the passed in index  */
841
static void
Darin Adler's avatar
Darin Adler committed
842
nautilus_sidebar_activate_panel (NautilusSidebar *sidebar, int which_view)
843
{
844
	char *title;
845 846
	GtkNotebook *notebook;

Darin Adler's avatar
Darin Adler committed
847 848
	notebook = sidebar->details->notebook;
	if (sidebar->details->selected_index < 0) {
849 850
		gtk_widget_show (GTK_WIDGET (notebook));
		if (GTK_WIDGET (notebook)->parent == NULL) {
Darin Adler's avatar
Darin Adler committed
851
			gtk_box_pack_end (GTK_BOX (sidebar->details->container),
852
					  GTK_WIDGET (notebook),
853
					  TRUE, TRUE, 0);
854
		}
855
		
Darin Adler's avatar
Darin Adler committed
856 857 858 859
		gtk_widget_show (GTK_WIDGET (sidebar->details->title_tab));
		if (GTK_WIDGET (sidebar->details->title_tab)->parent == NULL) {
			gtk_box_pack_end (GTK_BOX (sidebar->details->container),
					  GTK_WIDGET (sidebar->details->title_tab),
860 861
					  FALSE, FALSE, 0);
		}
862 863
	}
	
Darin Adler's avatar
Darin Adler committed
864
	sidebar->details->selected_index = which_view;
865
	title = nautilus_sidebar_tabs_get_title_from_index (sidebar->details->sidebar_tabs,
866
							  which_view);
867 868
	nautilus_sidebar_tabs_set_title (sidebar->details->title_tab, title);
	nautilus_sidebar_tabs_prelight_tab (sidebar->details->title_tab, -1);
869
    
870
	g_free (title);
871
	
872
	/* hide the buttons, since they look confusing when partially overlapped */
873 874
	gtk_widget_hide (GTK_WIDGET (sidebar->details->button_box_centerer));
	gtk_widget_hide (GTK_WIDGET (sidebar->details->title));
875
	
876
	gtk_notebook_set_page (notebook, which_view);
877 878
}

Darin Adler's avatar
Darin Adler committed
879
/* utility to deactivate the active panel */
880
static void
Darin Adler's avatar
Darin Adler committed
881
nautilus_sidebar_deactivate_panel(NautilusSidebar *sidebar)
882
{
Darin Adler's avatar
Darin Adler committed
883 884 885
	if (sidebar->details->selected_index >= 0) {
		gtk_widget_hide (GTK_WIDGET (sidebar->details->notebook));
		gtk_widget_hide (GTK_WIDGET (sidebar->details->title_tab));
886 887
	}
	
888 889
	gtk_widget_show (GTK_WIDGET (sidebar->details->button_box_centerer));
	gtk_widget_show (GTK_WIDGET (sidebar->details->title));
Darin Adler's avatar
Darin Adler committed
890
	sidebar->details->selected_index = -1;
891
	nautilus_sidebar_tabs_select_tab (sidebar->details->sidebar_tabs, -1);
892 893
}

894 895
/* handle mouse motion events by passing it to the tabs if necessary for pre-lighting */
static gboolean
Darin Adler's avatar
Darin Adler committed
896
nautilus_sidebar_motion_event (GtkWidget *widget, GdkEventMotion *event)
897
{
898
	int x, y;
899 900
	int which_tab;
	int title_top, title_bottom;
Darin Adler's avatar
Darin Adler committed
901
	NautilusSidebar *sidebar;
902
	NautilusSidebarTabs *sidebar_tabs, *title_tab;
903

Darin Adler's avatar
Darin Adler committed
904
	sidebar = NAUTILUS_SIDEBAR (widget);
905 906 907

	gtk_widget_get_pointer(widget, &x, &y);
	
908
	/* if the motion is in the main tabs, tell them about it */
909 910 911 912
	sidebar_tabs = sidebar->details->sidebar_tabs;
	if (y >= GTK_WIDGET (sidebar_tabs)->allocation.y) {
		which_tab = nautilus_sidebar_tabs_hit_test (sidebar_tabs, x, y);
		nautilus_sidebar_tabs_prelight_tab (sidebar_tabs, which_tab);
913 914 915
	} else
		nautilus_sidebar_tabs_prelight_tab (sidebar_tabs, -1);
	
916 917

	/* also handle prelighting in the title tab if necessary */
Darin Adler's avatar
Darin Adler committed
918 919
	if (sidebar->details->selected_index >= 0) {
		title_tab = sidebar->details->title_tab;
920 921 922
		title_top = GTK_WIDGET (title_tab)->allocation.y;
		title_bottom = title_top + GTK_WIDGET (title_tab)->allocation.height;
		if (y >= title_top && y < title_bottom) {
923
			which_tab = nautilus_sidebar_tabs_hit_test (title_tab, x, y);
924 925
		} else {
			which_tab = -1;
926
		}
927
		nautilus_sidebar_tabs_prelight_tab (title_tab, which_tab);
928 929 930 931 932 933 934 935
	}

	return TRUE;
}

/* handle the leave event by turning off the preliting */

static gboolean
Darin Adler's avatar
Darin Adler committed
936
nautilus_sidebar_leave_event (GtkWidget *widget, GdkEventCrossing *event)
937
{
Darin Adler's avatar
Darin Adler committed
938
	NautilusSidebar *sidebar;
939
	NautilusSidebarTabs *sidebar_tabs;
940

Darin Adler's avatar
Darin Adler committed
941
	sidebar = NAUTILUS_SIDEBAR (widget);
942 943
	sidebar_tabs = sidebar->details->sidebar_tabs; 
	nautilus_sidebar_tabs_prelight_tab (sidebar_tabs, -1);
944 945 946 947

	return TRUE;
}

948 949 950
/* hit-test the index tabs and activate if necessary */

static gboolean
Darin Adler's avatar
Darin Adler committed
951
nautilus_sidebar_press_event (GtkWidget *widget, GdkEventButton *event)
952
{
953
	int title_top, title_bottom;
954
	GtkWidget *menu;
Darin Adler's avatar
Darin Adler committed
955
	NautilusSidebar *sidebar;
956 957
	NautilusSidebarTabs *sidebar_tabs;
	NautilusSidebarTabs *title_tab;
958 959
	int rounded_y;
	int which_tab;
960
		
961 962 963 964
	if (widget->window != event->window) {
		return FALSE;
	}

Darin Adler's avatar
Darin Adler committed
965
	sidebar = NAUTILUS_SIDEBAR (widget);
966 967 968 969 970 971

	/* handle the context menu */
	if (event->button == CONTEXTUAL_MENU_BUTTON) {
		menu = nautilus_sidebar_create_context_menu (sidebar);	
		nautilus_pop_up_context_menu (GTK_MENU(menu),
				      NAUTILUS_DEFAULT_POPUP_MENU_DISPLACEMENT,
972 973
				      NAUTILUS_DEFAULT_POPUP_MENU_DISPLACEMENT,
				      event->button);
974 975 976
		return TRUE;
	}
	
977
	sidebar_tabs = sidebar->details->sidebar_tabs;
Darin Adler's avatar
Darin Adler committed
978
	title_tab = sidebar->details->title_tab;