nautilus-spatial-window.c 58.8 KB
Newer Older
Darin Adler's avatar
Darin Adler committed
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 3 4 5

/*
 *  Nautilus
 *
Elliot Lee's avatar
Elliot Lee committed
6
 *  Copyright (C) 1999, 2000 Red Hat, Inc.
7
 *  Copyright (C) 1999, 2000, 2001 Eazel, Inc.
8
 *
9
 *  Nautilus is free software; you can redistribute it and/or
10 11 12 13
 *  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.
 *
14
 *  Nautilus is distributed in the hope that it will be useful,
15 16 17 18 19
 *  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
20
 *  License along with this program; if not, write to the Free
21 22
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
23 24
 *  Authors: Elliot Lee <sopwith@redhat.com>
 *  	     John Sullivan <sullivan@eazel.com>
25 26
 *
 */
27

28
/* nautilus-window.c: Implementation of the main window object */
29

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

33
#include "nautilus-application.h"
34
#include "nautilus-bookmarks-window.h"
35
#include "nautilus-main.h"
36
#include "nautilus-sidebar.h"
37
#include "nautilus-signaller.h"
38
#include "nautilus-switchable-navigation-bar.h"
39 40
#include "nautilus-window-manage-views.h"
#include "nautilus-zoom-control.h"
41
#include <X11/Xatom.h>
42
#include <bonobo/bonobo-exception.h>
43
#include <bonobo/bonobo-ui-util.h>
Darin Adler's avatar
Darin Adler committed
44
#include <eel/eel-debug.h>
45 46
#include <eel/eel-gdk-extensions.h>
#include <eel/eel-gdk-pixbuf-extensions.h>
47
#include <eel/eel-generous-bin.h>
48 49 50
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-gtk-macros.h>
#include <eel/eel-string.h>
51
#include <gdk-pixbuf/gdk-pixbuf.h>
52
#include <gdk/gdkx.h>
53
#include <gtk/gtkmain.h>
Mike Engber's avatar
Mike Engber committed
54
#include <gtk/gtkmenubar.h>
55
#include <gtk/gtkmenuitem.h>
56 57 58 59
#include <gtk/gtkoptionmenu.h>
#include <gtk/gtktogglebutton.h>
#include <gtk/gtkvbox.h>
#include <libgnome/gnome-i18n.h>
60
#include <libgnome/gnome-macros.h>
61
#include <libgnome/gnome-util.h>
62 63
#include <libgnomeui/gnome-messagebox.h>
#include <libgnomeui/gnome-uidefs.h>
64
#include <libgnomeui/gnome-window-icon.h>
65
#include <libgnomevfs/gnome-vfs-uri.h>
Darin Adler's avatar
Darin Adler committed
66
#include <libgnomevfs/gnome-vfs-utils.h>
67 68 69
#include <libnautilus-private/nautilus-bonobo-extensions.h>
#include <libnautilus-private/nautilus-drag-window.h>
#include <libnautilus-private/nautilus-file-utilities.h>
Alexander Larsson's avatar
Alexander Larsson committed
70
#include <libnautilus-private/nautilus-file-attributes.h>
71 72 73 74 75 76 77
#include <libnautilus-private/nautilus-global-preferences.h>
#include <libnautilus-private/nautilus-horizontal-splitter.h>
#include <libnautilus-private/nautilus-icon-factory.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-functions.h>
78
#include <libnautilus/nautilus-bonobo-ui.h>
79
#include <libnautilus/nautilus-clipboard.h>
80
#include <libnautilus/nautilus-undo.h>
81
#include <math.h>
82
#include <sys/time.h>
83

84
/* FIXME bugzilla.gnome.org 41243: 
85
 * We should use inheritance instead of these special cases
Darin Adler's avatar
Darin Adler committed
86 87 88
 * for the desktop window.
 */
#include "nautilus-desktop-window.h"
89

90 91
#define STATUS_BAR_CLEAR_TIMEOUT 10000	/* milliseconds */
#define MAX_HISTORY_ITEMS 50
92

93
/* dock items */
94
#define LOCATION_BAR_PATH	"/Location Bar"
95
#define TOOLBAR_PATH            "/Toolbar"
96
#define STATUS_BAR_PATH         "/status"
97
#define MENU_BAR_PATH           "/menu"
98

99
#define COMMAND_PREFIX                          "/commands/"
100
#define NAUTILUS_COMMAND_TOGGLE_FIND_MODE	"/commands/Toggle Find Mode"
101 102 103 104 105
#define NAUTILUS_COMMAND_VIEW_AS		"/commands/View as"

#define NAUTILUS_MENU_PATH_EXTRA_VIEWER_PLACEHOLDER	"/menu/View/View Choices/Extra Viewer"
#define NAUTILUS_MENU_PATH_BEFORE_SHORT_LIST_SEPARATOR  "/menu/View/View Choices/Before Short List"
#define NAUTILUS_MENU_PATH_SHORT_LIST_PLACEHOLDER  	"/menu/View/View Choices/Short List"
106
#define NAUTILUS_MENU_PATH_AFTER_SHORT_LIST_SEPARATOR   "/menu/View/View Choices/After Short List"
107

Elliot Lee's avatar
Elliot Lee committed
108
enum {
Darin Adler's avatar
Darin Adler committed
109 110
	ARG_0,
	ARG_APP_ID,
111
	ARG_APP
Elliot Lee's avatar
Elliot Lee committed
112 113
};

114
static GList *history_list;
115

116 117 118 119 120 121
static void update_sidebar_panels_from_preferences (NautilusWindow *window);
static void sidebar_panels_changed_callback        (gpointer        user_data);
static void cancel_view_as_callback                (NautilusWindow *window);

GNOME_CLASS_BOILERPLATE (NautilusWindow, nautilus_window,
			 BonoboWindow, BONOBO_TYPE_WINDOW)
122

123 124 125 126 127 128
static void
set_up_default_icon_list (void)
{
	GList *icon_list;
	guint i;
	GdkPixbuf *pixbuf;
Anders Carlsson's avatar
Anders Carlsson committed
129
	char *path;
130
	const char *icon_filenames[] = { "nautilus-mini-logo.png", "nautilus-launch-icon.png" };
131

132
	icon_list = NULL;
133
	for (i = 0; i < G_N_ELEMENTS (icon_filenames); i++) {
Anders Carlsson's avatar
Anders Carlsson committed
134 135 136 137 138 139 140 141 142
		path = nautilus_pixmap_file (icon_filenames[i]);

		if (path == NULL) {
			continue;
		}
		
		pixbuf = gdk_pixbuf_new_from_file (path, NULL);
		g_free (path);
		
143 144
		if (pixbuf != NULL) {
			icon_list = g_list_prepend (icon_list, pixbuf);
145 146
		}
	}
147 148 149 150

	gtk_window_set_default_icon_list (icon_list);

	eel_g_list_free_deep_custom (icon_list, (GFunc) g_object_unref, NULL);
Elliot Lee's avatar
Elliot Lee committed
151 152
}

153
static void
154 155 156 157
add_sidebar_panel_callback (const char *name,
			    const char *iid,
			    const char *preference_key,
			    gpointer callback_data) 
158
{
159 160 161 162
	g_assert (name != NULL);
	g_assert (iid != NULL);
	g_assert (preference_key != NULL);
	g_assert (NAUTILUS_IS_WINDOW (callback_data));
163
	
164
	eel_preferences_add_callback_while_alive (preference_key,
165 166
						  sidebar_panels_changed_callback,
						  callback_data,
Darin Adler's avatar
Darin Adler committed
167
						  G_OBJECT (callback_data));
168 169
}

170
static void
171
nautilus_window_instance_init (NautilusWindow *window)
172
{
173 174
	window->details = g_new0 (NautilusWindowDetails, 1);

175
	/* CORBA and Bonobo setup, which must be done before the location bar setup */
176
	window->details->ui_container = bonobo_window_get_ui_container (BONOBO_WINDOW (window));
177
	bonobo_object_ref (window->details->ui_container);
178

179 180 181 182
	window->details->tooltips = gtk_tooltips_new ();
	g_object_ref (G_OBJECT (window->details->tooltips));
	gtk_object_sink (GTK_OBJECT (window->details->tooltips));

Anders Carlsson's avatar
Anders Carlsson committed
183 184 185
	/* Set initial window title */
	gtk_window_set_title (GTK_WINDOW (window), _("Nautilus"));

186 187 188 189 190 191 192 193 194
	window->details->shell_ui = bonobo_ui_component_new ("Nautilus Shell");
	bonobo_ui_component_set_container
		(window->details->shell_ui,
		 nautilus_window_get_ui_container (window),
		 NULL);

	/* Create a separate component so when we remove the status
	 * we don't loose the status bar
	 */
195
      	window->details->status_ui = bonobo_ui_component_new ("Status Component");  
196 197 198 199 200
	bonobo_ui_component_set_container
		(window->details->status_ui,
		 nautilus_window_get_ui_container (window),
		 NULL);

Darin Adler's avatar
Darin Adler committed
201
	gtk_quit_add_destroy (1, GTK_OBJECT (window));
202 203

	/* Keep track of changes in enabled state of sidebar panels */
204
	nautilus_sidebar_for_each_panel (add_sidebar_panel_callback, window);
205 206 207

	/* Keep the main event loop alive as long as the window exists */
	nautilus_main_event_loop_register (GTK_OBJECT (window));
208 209
}

210 211 212 213
static gint
ui_idle_handler (gpointer data)
{
	NautilusWindow *window;
214
	gboolean old_updating_bonobo_state;
215 216 217

	window = data;

218 219 220
	g_assert (NAUTILUS_IS_WINDOW (window));
	g_object_ref (data);

221 222 223 224 225 226 227
	g_assert (window->details->ui_change_depth == 0);

	/* Simulate an extra freeze/thaw so that calling window_ui_freeze
	 * and thaw from within the idle handler doesn't try to remove it
	 * (the already running idle handler)
	 */
	window->details->ui_change_depth++;
228
	old_updating_bonobo_state = window->details->updating_bonobo_state;
229 230 231 232 233 234 235

	if (window->details->ui_pending_initialize_menus_part_2) {
		nautilus_window_initialize_menus_part_2 (window);
		window->details->ui_pending_initialize_menus_part_2 = FALSE;
	}

	if (window->details->ui_is_frozen) {
236
		window->details->updating_bonobo_state = TRUE;
237 238
		bonobo_ui_engine_thaw (bonobo_ui_container_get_engine (window->details->ui_container));
		window->details->ui_is_frozen = FALSE;
239
		window->details->updating_bonobo_state = old_updating_bonobo_state;
240 241 242 243 244
	}

	window->details->ui_change_depth--;

	window->details->ui_idle_id = 0;
245 246 247

	g_object_unref (data);

248 249 250 251 252 253 254
	return FALSE;
}

static inline void
ui_install_idle_handler (NautilusWindow *window)
{
	if (window->details->ui_idle_id == 0) {
255
		window->details->ui_idle_id = g_idle_add_full (G_PRIORITY_LOW, ui_idle_handler, window, NULL);
256 257 258 259 260 261 262
	}
}

static inline void
ui_remove_idle_handler (NautilusWindow *window)
{
	if (window->details->ui_idle_id != 0) {
263
		g_source_remove (window->details->ui_idle_id);
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
		window->details->ui_idle_id = 0;
	}
}

/* Register that BonoboUI changes are going to be made to WINDOW. The UI
 * won't actually be synchronised until some arbitrary date in the future.
 */
void
nautilus_window_ui_freeze (NautilusWindow *window)
{
	if (window->details->ui_change_depth == 0) {
		ui_remove_idle_handler (window);
	}

	if (!window->details->ui_is_frozen) {
		bonobo_ui_engine_freeze (bonobo_ui_container_get_engine (window->details->ui_container));
		window->details->ui_is_frozen = TRUE;
	}

	window->details->ui_change_depth++;
}

/* Register that the BonoboUI changes for WINDOW have finished. There _must_
 * be one and only one call to this function for every call to
 * starting_ui_change ()
 */
void
nautilus_window_ui_thaw (NautilusWindow *window)
{
	window->details->ui_change_depth--;

	g_assert (window->details->ui_change_depth >= 0);

	if (window->details->ui_change_depth == 0
	    && (window->details->ui_is_frozen
		|| window->details->ui_pending_initialize_menus_part_2)) {
		ui_install_idle_handler (window);
	}
}

/* Unconditionally synchronize the BonoboUI of WINDOW. */
static void
nautilus_window_ui_update (NautilusWindow *window)
{
	BonoboUIEngine *engine;
309
	gboolean old_updating_bonobo_state;
310 311

	engine = bonobo_ui_container_get_engine (window->details->ui_container);
312 313 314
	old_updating_bonobo_state = window->details->updating_bonobo_state;

	window->details->updating_bonobo_state = TRUE;
315 316 317 318 319 320 321 322 323 324 325 326 327
	if (window->details->ui_is_frozen) {
		bonobo_ui_engine_thaw (engine);
		if (window->details->ui_change_depth == 0) {
			window->details->ui_is_frozen = FALSE;
			if (!window->details->ui_pending_initialize_menus_part_2) {
				ui_remove_idle_handler (window);
			}
		} else {
			bonobo_ui_engine_freeze (engine);
		}
	} else {
		bonobo_ui_engine_update (engine);
	}
328
	window->details->updating_bonobo_state = old_updating_bonobo_state;
329 330
}

331
static gboolean
332
nautilus_window_clear_status (gpointer callback_data)
333
{
334 335 336
	NautilusWindow *window;

	window = NAUTILUS_WINDOW (callback_data);
337

338
	bonobo_ui_component_set_status (window->details->status_ui, NULL, NULL);
339
	window->status_bar_clear_id = 0;
340

Darin Adler's avatar
Darin Adler committed
341
	return FALSE;
342 343
}

344
void
345
nautilus_window_set_status (NautilusWindow *window, const char *text)
346
{
347 348 349
	if (window->status_bar_clear_id != 0) {
		g_source_remove (window->status_bar_clear_id);
	}
350

351
	if (text != NULL && text[0] != '\0') {
352
		bonobo_ui_component_set_status (window->details->status_ui, text, NULL);
Darin Adler's avatar
Darin Adler committed
353
		window->status_bar_clear_id = g_timeout_add
354
			(STATUS_BAR_CLEAR_TIMEOUT, nautilus_window_clear_status, window);
355
	} else {
356
		nautilus_window_clear_status (window);
357 358
		window->status_bar_clear_id = 0;
	}
359 360
}

361
void
362
nautilus_window_go_to (NautilusWindow *window, const char *uri)
363
{
364
	nautilus_window_open_location (window, uri);
365 366
}

367 368 369 370 371 372 373 374
char *
nautilus_window_get_location (NautilusWindow *window)
{
	g_return_val_if_fail (NAUTILUS_IS_WINDOW (window), NULL);

	return g_strdup (window->details->location);
}

375
static void
376 377
go_to_callback (GtkWidget *widget,
		const char *uri,
378 379 380 381 382 383 384 385 386 387 388
		NautilusWindow *window)
{
	g_assert (NAUTILUS_IS_WINDOW (window));

	nautilus_window_go_to (window, uri);
}

static void
navigation_bar_location_changed_callback (GtkWidget *widget,
					  const char *uri,
					  NautilusWindow *window)
389
{
390 391 392 393 394 395 396 397 398 399
	g_assert (NAUTILUS_IS_WINDOW (window));

	if (window->details->temporary_navigation_bar) {
		if (nautilus_window_location_bar_showing (window)) {
			nautilus_window_hide_location_bar (window);
		}
		window->details->temporary_navigation_bar = FALSE;
	}

	nautilus_window_go_to (window, uri);
400 401
}

402
static void
403
navigation_bar_mode_changed_callback (GtkWidget *widget,
404
				      NautilusSwitchableNavigationBarMode mode,
405
				      NautilusWindow *window)
406
{
407
	window->details->updating_bonobo_state = TRUE;
408

409
	g_assert (mode == NAUTILUS_SWITCHABLE_NAVIGATION_BAR_MODE_LOCATION 
410
		  || mode == NAUTILUS_SWITCHABLE_NAVIGATION_BAR_MODE_SEARCH);
411

412 413
	nautilus_window_ui_freeze (window);

414
	nautilus_bonobo_set_toggle_state (window->details->shell_ui,
415
					  NAUTILUS_COMMAND_TOGGLE_FIND_MODE,
416
					  mode == NAUTILUS_SWITCHABLE_NAVIGATION_BAR_MODE_SEARCH);
417
	
418
	window->details->updating_bonobo_state = FALSE;
419 420

	nautilus_window_ui_thaw (window);
421 422
}

423 424
void
nautilus_window_zoom_in (NautilusWindow *window)
425
{
Darin Adler's avatar
Darin Adler committed
426 427 428
	if (window->content_view != NULL) {
		nautilus_view_frame_zoom_in (window->content_view);
	}
429 430
}

431
void
432
nautilus_window_zoom_to_level (NautilusWindow *window, float level)
433 434
{
	if (window->content_view != NULL) {
435
		nautilus_view_frame_set_zoom_level (window->content_view, level);
436 437 438
	}
}

439 440
void
nautilus_window_zoom_out (NautilusWindow *window)
441
{
Darin Adler's avatar
Darin Adler committed
442 443 444
	if (window->content_view != NULL) {
		nautilus_view_frame_zoom_out (window->content_view);
	}
445 446
}

447 448
void
nautilus_window_zoom_to_fit (NautilusWindow *window)
449 450
{
	if (window->content_view != NULL) {
451
		nautilus_view_frame_zoom_to_fit (window->content_view);
452 453 454
	}
}

455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
/* Code should never force the window taller than this size.
 * (The user can still stretch the window taller if desired).
 */
static guint
get_max_forced_height (void)
{
	return (gdk_screen_height () * 90) / 100;
}

/* Code should never force the window wider than this size.
 * (The user can still stretch the window wider if desired).
 */
static guint
get_max_forced_width (void)
{
	return (gdk_screen_width () * 90) / 100;
}

static void
set_initial_window_geometry (NautilusWindow *window)
{
	guint max_width_for_screen, max_height_for_screen;

	/* Don't let GTK determine the minimum size
	 * automatically. It will insist that the window be
	 * really wide based on some misguided notion about
	 * the content view area. Also, it might start the
	 * window wider (or taller) than the screen, which
	 * is evil. So we choose semi-arbitrary initial and
	 * minimum widths instead of letting GTK decide.
	 */
486 487 488 489 490 491 492 493
	/* FIXME - the above comment suggests that the size request
	 * of the content view area is wrong, probably because of
	 * another stupid set_usize someplace. If someone gets the
	 * content view area's size request right then we can
	 * probably remove this broken set_size_request() here.
	 * - hp@redhat.com
	 */
	
494 495 496
	max_width_for_screen = get_max_forced_width ();
	max_height_for_screen = get_max_forced_height ();

497 498 499 500 501
	gtk_widget_set_size_request (GTK_WIDGET (window), 
				     MIN (NAUTILUS_WINDOW_MIN_WIDTH, 
					  max_width_for_screen),
				     MIN (NAUTILUS_WINDOW_MIN_HEIGHT, 
					  max_height_for_screen));
502 503 504 505 506 507 508 509

	gtk_window_set_default_size (GTK_WINDOW (window), 
				     MIN (NAUTILUS_WINDOW_DEFAULT_WIDTH, 
				          max_width_for_screen), 
				     MIN (NAUTILUS_WINDOW_DEFAULT_HEIGHT, 
				          max_height_for_screen));
}

510 511 512 513 514 515 516 517 518 519 520 521
/* Add a dummy menu with a "View as ..." item when we first create the
 * view_as_option_menu -- without this the menu draws empty and shrunk,
 * once we populate it it grows and forces the toolbar and all the other
 * views to re-layout.
 */
static void
set_dummy_initial_view_as_menu (NautilusWindow *window)
{
	GtkWidget *new_menu;
	GtkWidget *menu_item;

	new_menu = gtk_menu_new ();
522
	menu_item = gtk_menu_item_new_with_label (_("View as..."));
523
       	gtk_widget_show (menu_item);
524
       	gtk_menu_shell_append (GTK_MENU_SHELL (new_menu), menu_item);
525 526 527 528 529

        gtk_option_menu_set_menu (GTK_OPTION_MENU (window->view_as_option_menu),
                                  new_menu);
}

530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
static void
nautilus_window_set_up_sidebar (NautilusWindow *window)
{
	window->sidebar = nautilus_sidebar_new ();

	if (window->details->location != NULL &&
	    window->details->title != NULL) {
		nautilus_sidebar_set_uri (window->sidebar,
					  window->details->location,
					  window->details->title);
	}
	
	g_signal_connect_object (window->sidebar, "location_changed",
				 G_CALLBACK (go_to_callback), window, 0);
	gtk_paned_pack1 (GTK_PANED (window->content_hbox),
			 GTK_WIDGET (window->sidebar),
			 FALSE, TRUE);
	
	nautilus_sidebar_setup_width (window->sidebar);

	/* Set up the sidebar panels. */
	update_sidebar_panels_from_preferences (window);

	gtk_widget_show (GTK_WIDGET (window->sidebar));
}

static void
nautilus_window_tear_down_sidebar (NautilusWindow *window)
{
	nautilus_window_set_sidebar_panels (window, NULL);
	gtk_widget_destroy (GTK_WIDGET (window->sidebar));
	window->sidebar = NULL;
}

Elliot Lee's avatar
Elliot Lee committed
564
static void
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
565
nautilus_window_constructed (NautilusWindow *window)
Elliot Lee's avatar
Elliot Lee committed
566
{
Darin Adler's avatar
Darin Adler committed
567
	GtkWidget *location_bar_box;
568
	GtkWidget *view_as_menu_vbox;
569
	BonoboControl *location_bar_wrapper;
570
	
571 572
	nautilus_window_ui_freeze (window);

573
	/* Load the user interface from the XML file. */
574 575 576
	bonobo_ui_util_set_ui (window->details->shell_ui,
			       DATADIR,
			       "nautilus-shell-ui.xml",
577
			       "nautilus", NULL);
578
	
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
579
	/* set up location bar */
Darin Adler's avatar
Darin Adler committed
580
	location_bar_box = gtk_hbox_new (FALSE, GNOME_PAD);
581
	gtk_container_set_border_width (GTK_CONTAINER (location_bar_box), GNOME_PAD_SMALL);
Darin Adler's avatar
Darin Adler committed
582
	
583
	window->navigation_bar = nautilus_switchable_navigation_bar_new (window);
584 585
	gtk_widget_show (GTK_WIDGET (window->navigation_bar));

586 587 588 589
	g_signal_connect_object (window->navigation_bar, "location_changed",
				 G_CALLBACK (navigation_bar_location_changed_callback), window, 0);
	g_signal_connect_object (window->navigation_bar, "mode_changed",
				 G_CALLBACK (navigation_bar_mode_changed_callback), window, 0);
590 591

	gtk_box_pack_start (GTK_BOX (location_bar_box), window->navigation_bar,
Darin Adler's avatar
Darin Adler committed
592
			    TRUE, TRUE, GNOME_PAD_SMALL);
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
593

594 595 596 597 598 599 600
	/* Option menu for content view types; it's empty here, filled in when a uri is set.
	 * Pack it into vbox so it doesn't grow vertically when location bar does. 
	 */
	view_as_menu_vbox = gtk_vbox_new (FALSE, GNOME_PAD_SMALL);
	gtk_widget_show (view_as_menu_vbox);
	gtk_box_pack_end (GTK_BOX (location_bar_box), view_as_menu_vbox, FALSE, FALSE, 0);
	
601
	window->view_as_option_menu = gtk_option_menu_new ();
602 603
	gtk_box_pack_end (GTK_BOX (view_as_menu_vbox), window->view_as_option_menu, TRUE, FALSE, 0);
	gtk_widget_show (window->view_as_option_menu);
604 605 606 607
 	/* Make sure there is something in the option menu to prevent it from
 	 * growing later.
 	 */
	set_dummy_initial_view_as_menu (window);
Darin Adler's avatar
Darin Adler committed
608
	
609 610 611
	/* Allocate the zoom control and place on the right next to the menu.
	 * It gets shown later, if the view-frame contains something zoomable.
	 */
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
612
	window->zoom_control = nautilus_zoom_control_new ();
613 614 615 616 617 618 619 620 621 622 623 624
	g_signal_connect_object (window->zoom_control, "zoom_in",
				 G_CALLBACK (nautilus_window_zoom_in),
				 window, G_CONNECT_SWAPPED);
	g_signal_connect_object (window->zoom_control, "zoom_out",
				 G_CALLBACK (nautilus_window_zoom_out),
				 window, G_CONNECT_SWAPPED);
	g_signal_connect_object (window->zoom_control, "zoom_to_level",
				 G_CALLBACK (nautilus_window_zoom_to_level),
				 window, G_CONNECT_SWAPPED);
	g_signal_connect_object (window->zoom_control, "zoom_to_fit",
				 G_CALLBACK (nautilus_window_zoom_to_fit),
				 window, G_CONNECT_SWAPPED);
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
625 626
	gtk_box_pack_end (GTK_BOX (location_bar_box), window->zoom_control, FALSE, FALSE, 0);
	
627
	gtk_widget_show (location_bar_box);
Darin Adler's avatar
Darin Adler committed
628
	
629
	/* FIXME bugzilla.gnome.org 41243: 
630
	 * We should use inheritance instead of these special cases
Darin Adler's avatar
Darin Adler committed
631 632 633
	 * for the desktop window.
	 */
        if (NAUTILUS_IS_DESKTOP_WINDOW (window)) {
634
		window->content_hbox = gtk_widget_new (EEL_TYPE_GENEROUS_BIN, NULL);
Darin Adler's avatar
Darin Adler committed
635
	} else {
636 637
		set_initial_window_geometry (window);
	
638
		window->content_hbox = nautilus_horizontal_splitter_new ();
639

640 641 642
		if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_START_WITH_SIDEBAR)) {
			nautilus_window_set_up_sidebar (window);
		}
Darin Adler's avatar
Darin Adler committed
643
	}
644 645 646

	gtk_widget_show (window->content_hbox);
	bonobo_window_set_contents (BONOBO_WINDOW (window), window->content_hbox);
Darin Adler's avatar
Darin Adler committed
647
	
648 649
	bonobo_ui_component_freeze (window->details->shell_ui, NULL);

650
	/* FIXME bugzilla.gnome.org 41243: 
651 652 653
	 * We should use inheritance instead of these special cases
	 * for the desktop window.
	 */
654 655 656 657
	if (NAUTILUS_IS_DESKTOP_WINDOW (window)) {
		nautilus_bonobo_set_hidden (window->details->shell_ui,
					    LOCATION_BAR_PATH, TRUE);
		nautilus_bonobo_set_hidden (window->details->shell_ui,
658
					    TOOLBAR_PATH, TRUE);
659 660 661 662 663
		nautilus_bonobo_set_hidden (window->details->shell_ui,
					    STATUS_BAR_PATH, TRUE);
		nautilus_bonobo_set_hidden (window->details->shell_ui,
					    MENU_BAR_PATH, TRUE);
	}
664 665 666 667 668

	/* Wrap the location bar in a control and set it up. */
	location_bar_wrapper = bonobo_control_new (location_bar_box);
	bonobo_ui_component_object_set (window->details->shell_ui,
					"/Location Bar/Wrapper",
669
					BONOBO_OBJREF (location_bar_wrapper),
670
					NULL);
671
	bonobo_ui_component_thaw (window->details->shell_ui, NULL);
672
	bonobo_object_unref (location_bar_wrapper);
673

674
	/* initalize the menus and toolbars */
675
	nautilus_window_initialize_menus_part_1 (window);
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
676
	nautilus_window_initialize_toolbars (window);
677

678 679
	/* We'll do the second part later (bookmarks and go menus) */
	window->details->ui_pending_initialize_menus_part_2 = TRUE;
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
680 681
	
	/* Set initial sensitivity of some buttons & menu items 
Darin Adler's avatar
Darin Adler committed
682 683 684 685 686
	 * now that they're all created.
	 */
	nautilus_window_allow_back (window, FALSE);
	nautilus_window_allow_forward (window, FALSE);
	nautilus_window_allow_stop (window, FALSE);
687

Darin Adler's avatar
Darin Adler committed
688
	/* Set up undo manager */
689
	nautilus_undo_manager_attach (window->application->undo_manager, G_OBJECT (window));	
690

691 692
	/* Register that things may be dragged from this window */
	nautilus_drag_window_register (GTK_WINDOW (window));
693 694

	nautilus_window_ui_thaw (window);
Elliot Lee's avatar
Elliot Lee committed
695 696 697
}

static void
698 699 700 701
nautilus_window_set_property (GObject *object,
			      guint arg_id,
			      const GValue *value,
			      GParamSpec *pspec)
Elliot Lee's avatar
Elliot Lee committed
702
{
703
	char *old_name;
704 705 706
	NautilusWindow *window;

	window = NAUTILUS_WINDOW (object);
Darin Adler's avatar
Darin Adler committed
707
	
708
	switch (arg_id) {
Darin Adler's avatar
Darin Adler committed
709
	case ARG_APP_ID:
710
		if (g_value_get_string (value) == NULL) {
Darin Adler's avatar
Darin Adler committed
711 712
			return;
		}
713
		old_name = bonobo_window_get_name (BONOBO_WINDOW (window));
714
		bonobo_window_set_name (BONOBO_WINDOW (window), g_value_get_string (value));
715 716 717 718 719 720
		/* This hack of using the time when the name first
		 * goes non-NULL to be window-constructed time is
		 * completely lame. But it works, so for now we leave
		 * it alone.
		 */
		if (old_name == NULL) {
721
			nautilus_window_constructed (window);
722 723
		}
		g_free (old_name);
Darin Adler's avatar
Darin Adler committed
724 725
		break;
	case ARG_APP:
726
		window->application = NAUTILUS_APPLICATION (g_value_get_object (value));
Darin Adler's avatar
Darin Adler committed
727 728
		break;
	}
Elliot Lee's avatar
Elliot Lee committed
729 730 731
}

static void
732 733 734 735
nautilus_window_get_property (GObject *object,
			      guint arg_id,
			      GValue *value,
			      GParamSpec *pspec)
Elliot Lee's avatar
Elliot Lee committed
736
{
737
	switch (arg_id) {
Darin Adler's avatar
Darin Adler committed
738
	case ARG_APP_ID:
739 740 741
		g_value_set_string_take_ownership (
			value,
			bonobo_window_get_name (BONOBO_WINDOW (object)));
Darin Adler's avatar
Darin Adler committed
742 743
		break;
	case ARG_APP:
744
		g_value_set_object (value, NAUTILUS_WINDOW (object)->application);
Darin Adler's avatar
Darin Adler committed
745 746
		break;
	}
Elliot Lee's avatar
Elliot Lee committed
747 748
}

749 750 751 752 753 754 755 756 757 758 759
static void
free_stored_viewers (NautilusWindow *window)
{
	eel_g_list_free_deep_custom (window->details->short_list_viewers, 
				     (GFunc) nautilus_view_identifier_free, 
				     NULL);
	window->details->short_list_viewers = NULL;
	nautilus_view_identifier_free (window->details->extra_viewer);
	window->details->extra_viewer = NULL;
}

760 761
static void
nautilus_window_unrealize (GtkWidget *widget)
762
{
763
	NautilusWindow *window;
764
	
765 766
	window = NAUTILUS_WINDOW (widget);

767 768 769 770
	if (window->details->throbber_property_bag != CORBA_OBJECT_NIL) {
		bonobo_object_release_unref (window->details->throbber_property_bag, NULL);
		window->details->throbber_property_bag = CORBA_OBJECT_NIL;
	}
771

772 773 774
	if (window->details->throbber_listener != CORBA_OBJECT_NIL) {
		CORBA_Object_release (window->details->throbber_listener, NULL);
		window->details->throbber_listener = CORBA_OBJECT_NIL;
775 776
	}

777
	GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
778 779
}

Darin Adler's avatar
Darin Adler committed
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796
static void
nautilus_window_destroy (GtkObject *object)
{
	NautilusWindow *window;
	
	window = NAUTILUS_WINDOW (object);

	nautilus_window_manage_views_destroy (window);

	eel_g_object_list_free (window->sidebar_panels);
	window->sidebar_panels = NULL;

	if (window->content_view != NULL) {
		gtk_object_destroy (GTK_OBJECT (window->content_view));
		window->content_view = NULL;
	}

797 798 799 800 801
	if (window->details->tooltips) {
		g_object_unref (G_OBJECT (window->details->tooltips));
		window->details->tooltips = NULL;
	}

Darin Adler's avatar
Darin Adler committed
802 803 804
	GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

805 806
static void
nautilus_window_finalize (GObject *object)
807 808 809
{
	NautilusWindow *window;
	
810 811
	window = NAUTILUS_WINDOW (object);

812
	nautilus_window_manage_views_finalize (window);
813

814 815 816
	nautilus_window_set_viewed_file (window, NULL);
	nautilus_window_remove_bookmarks_menu_callback (window);
	nautilus_window_remove_go_menu_callback (window);
817

818 819 820
	if (window->details->ui_idle_id != 0) {
		gtk_idle_remove (window->details->ui_idle_id);
	}
821

822 823 824 825
	if (window->details->shell_ui != NULL) {
		bonobo_ui_component_unset_container (window->details->shell_ui, NULL);
		bonobo_object_unref (window->details->shell_ui);
	}
826

827 828 829 830 831
	if (window->details->status_ui != NULL) {
		bonobo_ui_component_unset_container (window->details->status_ui, NULL);
		bonobo_object_unref (window->details->status_ui);
	}

832
	nautilus_file_unref (window->details->viewed_file);
833

834
	free_stored_viewers (window);
835

836 837 838
	g_free (window->details->location);
	eel_g_list_free_deep (window->details->selection);
	eel_g_list_free_deep (window->details->pending_selection);
839

840 841
	nautilus_window_clear_back_list (window);
	nautilus_window_clear_forward_list (window);
842

843 844 845 846 847 848
	if (window->current_location_bookmark != NULL) {
		g_object_unref (window->current_location_bookmark);
	}
	if (window->last_location_bookmark != NULL) {
		g_object_unref (window->last_location_bookmark);
	}
849

850 851 852
	if (window->status_bar_clear_id != 0) {
		g_source_remove (window->status_bar_clear_id);
	}
853

854
	bonobo_object_unref (window->details->ui_container);
855

856 857
	if (window->details->location_change_at_idle_id != 0) {
		gtk_idle_remove (window->details->location_change_at_idle_id);
Michael Meeks's avatar
Michael Meeks committed
858
	}
859

860 861 862 863 864
	g_free (window->details->title);
	
	g_free (window->details);

	G_OBJECT_CLASS (parent_class)->finalize (object);
865 866
}

867
void
868 869 870 871
nautilus_window_save_geometry (NautilusWindow *window)
{
	char *geometry_string;

872
	g_assert (NAUTILUS_IS_WINDOW (window));
873

874
	geometry_string = eel_gtk_window_get_geometry_string (GTK_WINDOW (window));
875

876 877 878 879
	nautilus_file_set_metadata (window->details->viewed_file,
				    NAUTILUS_METADATA_KEY_WINDOW_GEOMETRY,
				    NULL,
				    geometry_string);
880 881 882
	g_free (geometry_string);
}

883 884 885
void
nautilus_window_close (NautilusWindow *window)
{
886
	g_return_if_fail (NAUTILUS_IS_WINDOW (window));
887

888 889 890
	gtk_widget_destroy (GTK_WIDGET (window));
}

891 892 893 894 895 896 897
static void
nautilus_window_size_request (GtkWidget		*widget,
			      GtkRequisition	*requisition)
{
	guint max_width;
	guint max_height;

898 899
	g_assert (NAUTILUS_IS_WINDOW (widget));
	g_assert (requisition != NULL);
900

901
	GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
902 903 904 905 906 907 908 909 910 911 912 913 914 915

	/* Limit the requisition to be within 90% of the available screen 
	 * real state.
	 *
	 * This way the user will have a fighting chance of getting
	 * control of their window back if for whatever reason one of the
	 * window's descendants decide they want to be 4000 pixels wide.
	 *
	 * Note that the user can still make the window really huge by hand.
	 *
	 * Bugs in components or other widgets that cause such huge geometries
	 * to be requested, should still be fixed.  This code is here only to 
	 * prevent the extremely frustrating consequence of such bugs.
	 */