nautilus-application.c 37.3 KB
Newer Older
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
/*
3
 * nautilus-application: main Nautilus application class.
4
 *
5 6 7
 * Copyright (C) 1999, 2000 Red Hat, Inc.
 * Copyright (C) 2000, 2001 Eazel, Inc.
 * Copyright (C) 2010, Cosimo Cecchi <cosimoc@gnome.org>
8
 *
9 10 11 12
 * 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.
13
 *
14 15 16 17
 * 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.
18
 *
19 20 21
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
22
 *
23 24 25
 * Authors: Elliot Lee <sopwith@redhat.com>,
 *          Darin Adler <darin@bentspoon.com>
 *          Cosimo Cecchi <cosimoc@gnome.org>
26 27 28
 *
 */

29
#include <config.h>
30

31
#include "nautilus-application.h"
32

Christian Neumair's avatar
Christian Neumair committed
33
#if ENABLE_EMPTY_VIEW
34
#include "nautilus-empty-view.h"
Christian Neumair's avatar
Christian Neumair committed
35
#endif /* ENABLE_EMPTY_VIEW */
36

37
#include "nautilus-desktop-icon-view.h"
38
#include "nautilus-desktop-window.h"
39
#include "nautilus-icon-view.h"
40
#include "nautilus-image-properties-page.h"
41
#include "nautilus-list-view.h"
42
#include "nautilus-navigation-window.h"
43
#include "nautilus-navigation-window-slot.h"
44
#include "nautilus-progress-ui-handler.h"
45
#include "nautilus-self-check-functions.h"
46
#include "nautilus-window-bookmarks.h"
47
#include "nautilus-window-manage-views.h"
48 49
#include "nautilus-window-private.h"
#include "nautilus-window-slot.h"
50

51
#include <libnautilus-private/nautilus-dbus-manager.h>
52 53
#include <libnautilus-private/nautilus-desktop-link-monitor.h>
#include <libnautilus-private/nautilus-directory-private.h>
54
#include <libnautilus-private/nautilus-file-utilities.h>
55
#include <libnautilus-private/nautilus-file-operations.h>
56
#include <libnautilus-private/nautilus-global-preferences.h>
57
#include <libnautilus-private/nautilus-lib-self-check-functions.h>
58
#include <libnautilus-private/nautilus-module.h>
Alexander Larsson's avatar
Alexander Larsson committed
59
#include <libnautilus-private/nautilus-signaller.h>
60
#include <libnautilus-private/nautilus-ui-utilities.h>
61
#include <libnautilus-private/nautilus-undo-manager.h>
Alexander Larsson's avatar
Alexander Larsson committed
62
#include <libnautilus-extension/nautilus-menu-provider.h>
63

64 65 66
#define DEBUG_FLAG NAUTILUS_DEBUG_APPLICATION
#include <libnautilus-private/nautilus-debug.h>

67 68 69
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
70 71 72 73 74 75
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-gtk-macros.h>
#include <eel/eel-stock-dialogs.h>
76
#include <libnotify/notify.h>
77 78
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
Alexander Larsson's avatar
Alexander Larsson committed
79

80 81 82 83 84
/* Keep window from shrinking down ridiculously small; numbers are somewhat arbitrary */
#define APPLICATION_WINDOW_MIN_WIDTH	300
#define APPLICATION_WINDOW_MIN_HEIGHT	100

#define START_STATE_CONFIG "start-state"
85

86 87
#define NAUTILUS_ACCEL_MAP_SAVE_DELAY 30

88 89
static NautilusApplication *singleton = NULL;

90 91
/* Keeps track of all the desktop windows. */
static GList *nautilus_application_desktop_windows;
92

93 94 95
/* The saving of the accelerator map was requested  */
static gboolean save_of_accel_map_requested = FALSE;

96
static void     desktop_changed_callback          (gpointer                  user_data);
97 98 99 100 101 102
static void     mount_removed_callback            (GVolumeMonitor            *monitor,
						   GMount                    *mount,
						   NautilusApplication       *application);
static void     mount_added_callback              (GVolumeMonitor            *monitor,
						   GMount                    *mount,
						   NautilusApplication       *application);
103

104
G_DEFINE_TYPE (NautilusApplication, nautilus_application, GTK_TYPE_APPLICATION);
105

106 107 108 109
struct _NautilusApplicationPriv {
	GVolumeMonitor *volume_monitor;
	GDBusProxy *ck_proxy;
	gboolean session_is_active;
110
	NautilusProgressUIHandler *progress_handler;
111 112 113 114

	gboolean initialized;
};

115 116
static gboolean
check_required_directories (NautilusApplication *application)
117
{
118 119
	char *user_directory;
	char *desktop_directory;
Alexander Larsson's avatar
Alexander Larsson committed
120 121 122
	GSList *directories;
	gboolean ret;

123
	g_assert (NAUTILUS_IS_APPLICATION (application));
124

Alexander Larsson's avatar
Alexander Larsson committed
125 126
	ret = TRUE;

127 128 129
	user_directory = nautilus_get_user_directory ();
	desktop_directory = nautilus_get_desktop_directory ();

Alexander Larsson's avatar
Alexander Larsson committed
130 131
	directories = NULL;

132
	if (!g_file_test (user_directory, G_FILE_TEST_IS_DIR)) {
Alexander Larsson's avatar
Alexander Larsson committed
133
		directories = g_slist_prepend (directories, user_directory);
134
	}
Alexander Larsson's avatar
Alexander Larsson committed
135

136
	if (!g_file_test (desktop_directory, G_FILE_TEST_IS_DIR)) {
Alexander Larsson's avatar
Alexander Larsson committed
137
		directories = g_slist_prepend (directories, desktop_directory);
138 139
	}

Alexander Larsson's avatar
Alexander Larsson committed
140 141 142 143 144 145 146 147 148
	if (directories != NULL) {
		int failed_count;
		GString *directories_as_string;
		GSList *l;
		char *error_string;
		const char *detail_string;
		GtkDialog *dialog;

		ret = FALSE;
149

Alexander Larsson's avatar
Alexander Larsson committed
150 151 152 153 154 155
		failed_count = g_slist_length (directories);

		directories_as_string = g_string_new ((const char *)directories->data);
		for (l = directories->next; l != NULL; l = l->next) {
			g_string_append_printf (directories_as_string, ", %s", (const char *)l->data);
		}
156

157
		if (failed_count == 1) {
158
			error_string = g_strdup_printf (_("Nautilus could not create the required folder \"%s\"."),
Alexander Larsson's avatar
Alexander Larsson committed
159
							directories_as_string->str);
160 161
			detail_string = _("Before running Nautilus, please create the following folder, or "
					  "set permissions such that Nautilus can create it.");
162
		} else {
163
			error_string = g_strdup_printf (_("Nautilus could not create the following required folders: "
Alexander Larsson's avatar
Alexander Larsson committed
164 165
							  "%s."), directories_as_string->str);
			detail_string = _("Before running Nautilus, please create these folders, or "
166
					  "set permissions such that Nautilus can create them.");
167
		}
Alexander Larsson's avatar
Alexander Larsson committed
168

169
		dialog = eel_show_error_dialog (error_string, detail_string, NULL);
170
		/* We need the main event loop so the user has a chance to see the dialog. */
171 172
		gtk_application_add_window (GTK_APPLICATION (application),
					    GTK_WINDOW (dialog));
173

Alexander Larsson's avatar
Alexander Larsson committed
174
		g_string_free (directories_as_string, TRUE);
175 176 177
		g_free (error_string);
	}

Alexander Larsson's avatar
Alexander Larsson committed
178 179 180
	g_slist_free (directories);
	g_free (user_directory);
	g_free (desktop_directory);
181

Alexander Larsson's avatar
Alexander Larsson committed
182
	return ret;
183 184
}

Alexander Larsson's avatar
Alexander Larsson committed
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
static void
menu_provider_items_updated_handler (NautilusMenuProvider *provider, GtkWidget* parent_window, gpointer data)
{

	g_signal_emit_by_name (nautilus_signaller_get_current (),
			       "popup_menu_changed");
}

static void
menu_provider_init_callback (void)
{
        GList *providers;
        GList *l;

        providers = nautilus_module_get_extensions_for_type (NAUTILUS_TYPE_MENU_PROVIDER);

        for (l = providers; l != NULL; l = l->next) {
                NautilusMenuProvider *provider = NAUTILUS_MENU_PROVIDER (l->data);

		g_signal_connect_after (G_OBJECT (provider), "items_updated",
                           (GCallback)menu_provider_items_updated_handler,
                           NULL);
        }

        nautilus_module_extension_list_free (providers);
}

212
static void
213 214
mark_desktop_files_trusted (void)
{
215
	char *do_once_file;
216 217 218 219 220 221
	GFile *f, *c;
	GFileEnumerator *e;
	GFileInfo *info;
	const char *name;
	int fd;
	
222 223
	do_once_file = g_build_filename (g_get_user_data_dir (),
					 ".converted-launchers", NULL);
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264

	if (g_file_test (do_once_file, G_FILE_TEST_EXISTS)) {
		goto out;
	}

	f = nautilus_get_desktop_location ();
	e = g_file_enumerate_children (f,
				       G_FILE_ATTRIBUTE_STANDARD_TYPE ","
				       G_FILE_ATTRIBUTE_STANDARD_NAME ","
				       G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE
				       ,
				       G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
				       NULL, NULL);
	if (e == NULL) {
		goto out2;
	}
	
	while ((info = g_file_enumerator_next_file (e, NULL, NULL)) != NULL) {
		name = g_file_info_get_name (info);
		
		if (g_str_has_suffix (name, ".desktop") &&
		    !g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE)) {
			c = g_file_get_child (f, name);
			nautilus_file_mark_desktop_file_trusted (c,
								 NULL, FALSE,
								 NULL, NULL);
			g_object_unref (c);
		}
		g_object_unref (info);
	}
	
	g_object_unref (e);
 out2:
	fd = g_creat (do_once_file, 0666);
	close (fd);
	
	g_object_unref (f);
 out:	
	g_free (do_once_file);
}

Christian Persch's avatar
Christian Persch committed
265 266 267
#define CK_NAME       "org.freedesktop.ConsoleKit"
#define CK_PATH       "/org/freedesktop/ConsoleKit"
#define CK_INTERFACE  "org.freedesktop.ConsoleKit"
268

269
static void
Christian Persch's avatar
Christian Persch committed
270
ck_session_proxy_signal_cb (GDBusProxy *proxy,
271 272 273 274
			    const char *sender_name,
			    const char *signal_name,
			    GVariant   *parameters,
			    gpointer    user_data)
275 276 277
{
	NautilusApplication *application = user_data;

278
	if (g_strcmp0 (signal_name, "ActiveChanged") == 0) {
279 280
		g_variant_get (parameters, "(b)", &application->priv->session_is_active);
		DEBUG ("ConsoleKit session is active %d", application->priv->session_is_active);
281
	}
282 283 284
}

static void
Christian Persch's avatar
Christian Persch committed
285 286 287
ck_call_is_active_cb (GDBusProxy   *proxy,
		      GAsyncResult *result,
		      gpointer      user_data)
288
{
Christian Persch's avatar
Christian Persch committed
289
	NautilusApplication *application = user_data;
290 291 292 293
	GVariant *variant;
	GError *error = NULL;

	variant = g_dbus_proxy_call_finish (proxy, result, &error);
294

295 296
	if (variant == NULL) {
		g_warning ("Error when calling IsActive(): %s\n", error->message);
297
		application->priv->session_is_active = TRUE;
298 299

		g_error_free (error);
300 301 302
		return;
	}

303 304
	g_variant_get (variant, "(b)", &application->priv->session_is_active);
	DEBUG ("ConsoleKit session is active %d", application->priv->session_is_active);
305 306

	g_variant_unref (variant);
307 308 309
}

static void
310 311 312
session_proxy_appeared (GObject       *source,
                        GAsyncResult *res,
                        gpointer      user_data)
313
{
Christian Persch's avatar
Christian Persch committed
314
	NautilusApplication *application = user_data;
315 316
        GDBusProxy *proxy;
	GError *error = NULL;
Christian Persch's avatar
Christian Persch committed
317

318
        proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
Christian Persch's avatar
Christian Persch committed
319

320 321 322 323
	if (error != NULL) {
		g_warning ("Failed to get the current CK session: %s", error->message);
		g_error_free (error);

324
		application->priv->session_is_active = TRUE;
325 326
		return;
	}
Christian Persch's avatar
Christian Persch committed
327

328 329 330
	g_signal_connect (proxy, "g-signal",
			  G_CALLBACK (ck_session_proxy_signal_cb),
			  application);
Christian Persch's avatar
Christian Persch committed
331

332 333 334 335 336 337 338
	g_dbus_proxy_call (proxy,
			   "IsActive",
			   g_variant_new ("()"),
			   G_DBUS_CALL_FLAGS_NONE,
			   -1,
			   NULL,
			   (GAsyncReadyCallback) ck_call_is_active_cb,
339 340
			   application);

341
        application->priv->ck_proxy = proxy;
Christian Persch's avatar
Christian Persch committed
342
}
343

Christian Persch's avatar
Christian Persch committed
344 345
static void
ck_get_current_session_cb (GDBusConnection *connection,
346
			   GAsyncResult    *result,
Christian Persch's avatar
Christian Persch committed
347 348 349
			   gpointer         user_data)
{
	NautilusApplication *application = user_data;
350 351 352
	GVariant *variant;
	const char *session_path = NULL;
	GError *error = NULL;
353

354 355 356 357 358
	variant = g_dbus_connection_call_finish (connection, result, &error);

	if (variant == NULL) {
		g_warning ("Failed to get the current CK session: %s", error->message);
		g_error_free (error);
359

360
		application->priv->session_is_active = TRUE;
361 362 363
		return;
	}

364 365
	g_variant_get (variant, "(&o)", &session_path);

366 367
	DEBUG ("Found ConsoleKit session at path %s", session_path);

368 369 370 371 372 373 374 375 376
	g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
				  G_DBUS_PROXY_FLAGS_NONE,
				  NULL,
				  CK_NAME,
				  session_path,
				  CK_INTERFACE ".Session",
				  NULL,
				  session_proxy_appeared,
				  application);
377

378
	g_variant_unref (variant);
379 380 381 382 383
}

static void
do_initialize_consolekit (NautilusApplication *application)
{
384
	GDBusConnection *connection;
385

386 387 388
	connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);

	if (connection == NULL) {
389
		application->priv->session_is_active = TRUE;
390 391
		return;
	}
392

393 394 395 396 397 398
	g_dbus_connection_call (connection,
				CK_NAME,
				CK_PATH "/Manager",
				CK_INTERFACE ".Manager",
				"GetCurrentSession",
				g_variant_new ("()"),
399
				G_VARIANT_TYPE ("(o)"),
400 401 402 403 404 405 406
				G_DBUS_CALL_FLAGS_NONE,
				-1,
				NULL,
				(GAsyncReadyCallback) ck_get_current_session_cb,
				application);

	g_object_unref (connection);
407 408
}

409 410 411 412
static void
do_upgrades_once (NautilusApplication *application,
		  gboolean no_desktop)
{
413 414 415
	char *metafile_dir, *updated, *nautilus_dir, *xdg_dir;
	const gchar *message;
	int fd, res;
416

417 418 419 420
	if (!no_desktop) {
		mark_desktop_files_trusted ();
	}

421 422 423 424 425 426 427 428 429 430 431 432 433 434
	metafile_dir = g_build_filename (g_get_home_dir (),
					 ".nautilus/metafiles", NULL);
	if (g_file_test (metafile_dir, G_FILE_TEST_IS_DIR)) {
		updated = g_build_filename (metafile_dir, "migrated-to-gvfs", NULL);
		if (!g_file_test (updated, G_FILE_TEST_EXISTS)) {
			g_spawn_command_line_async (LIBEXECDIR"/nautilus-convert-metadata --quiet", NULL);
			fd = g_creat (updated, 0600);
			if (fd != -1) {
				close (fd);
			}
		}
		g_free (updated);
	}
	g_free (metafile_dir);
435 436 437 438 439 440 441

	nautilus_dir = g_build_filename (g_get_home_dir (),
					 ".nautilus", NULL);
	xdg_dir = nautilus_get_user_directory ();
	if (g_file_test (nautilus_dir, G_FILE_TEST_IS_DIR)) {
		/* test if we already attempted to migrate first */
		updated = g_build_filename (nautilus_dir, "DEPRECATED-DIRECTORY", NULL);
442
		message = _("Nautilus 3.0 deprecated this directory and tried migrating "
443 444 445 446 447 448 449 450 451 452
			    "this configuration to ~/.config/nautilus");
		if (!g_file_test (updated, G_FILE_TEST_EXISTS)) {
			/* rename() works fine if the destination directory is
			 * empty.
			 */
			res = g_rename (nautilus_dir, xdg_dir);

			if (res == -1) {
				fd = g_creat (updated, 0600);
				if (fd != -1) {
453
					res = write (fd, message, strlen (message));
454 455 456 457 458 459 460 461 462 463
					close (fd);
				}
			}
		}

		g_free (updated);
	}

	g_free (nautilus_dir);
	g_free (xdg_dir);
464
}
465 466 467 468

static void
finish_startup (NautilusApplication *application,
		gboolean no_desktop)
469
{
470 471
	do_upgrades_once (application, no_desktop);
	
472
	/* initialize nautilus modules */
473
	nautilus_module_setup ();
474

Alexander Larsson's avatar
Alexander Larsson committed
475 476
	/* attach menu-provider module callback */
	menu_provider_init_callback ();
477
	
Alexander Larsson's avatar
Alexander Larsson committed
478 479
	/* Initialize the desktop link monitor singleton */
	nautilus_desktop_link_monitor_get ();
480

481 482 483
	/* Initialize the ConsoleKit listener for active session */
	do_initialize_consolekit (application);

484
	/* Initialize the UI handler singleton for file operations */
485
	notify_init (GETTEXT_PACKAGE);
486
	application->priv->progress_handler = nautilus_progress_ui_handler_new ();
487 488 489

	/* Watch for unmounts so we can close open windows */
	/* TODO-gio: This should be using the UNMOUNTED feature of GFileMonitor instead */
490 491
	application->priv->volume_monitor = g_volume_monitor_get ();
	g_signal_connect_object (application->priv->volume_monitor, "mount_removed",
492
				 G_CALLBACK (mount_removed_callback), application, 0);
493
	g_signal_connect_object (application->priv->volume_monitor, "mount_added",
494
				 G_CALLBACK (mount_added_callback), application, 0);
495 496
}

497 498 499
static void
open_window (NautilusApplication *application,
	     const char *startup_id,
500
	     const char *uri, GdkScreen *screen, const char *geometry)
501 502 503
{
	GFile *location;
	NautilusWindow *window;
504 505 506 507 508 509 510

	if (uri == NULL) {
		location = g_file_new_for_path (g_get_home_dir ());
	} else {
		location = g_file_new_for_uri (uri);
	}

511
	DEBUG ("Opening new window at uri %s", uri);
512

513 514 515
	window = nautilus_application_create_navigation_window (application,
								startup_id,
								screen);
516 517 518 519
	nautilus_window_go_to (window, location);

	g_object_unref (location);

520
	if (geometry != NULL && !gtk_widget_get_visible (GTK_WIDGET (window))) {
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
		/* never maximize windows opened from shell if a
		 * custom geometry has been requested.
		 */
		gtk_window_unmaximize (GTK_WINDOW (window));
		eel_gtk_window_set_initial_geometry_from_string (GTK_WINDOW (window),
								 geometry,
								 APPLICATION_WINDOW_MIN_WIDTH,
								 APPLICATION_WINDOW_MIN_HEIGHT,
								 FALSE);
	}
}

static void
open_windows (NautilusApplication *application,
	      const char *startup_id,
	      char **uris,
537
	      GdkScreen *screen,
538
	      const char *geometry)
539 540
{
	guint i;
Alexander Larsson's avatar
Alexander Larsson committed
541

542 543
	if (uris == NULL || uris[0] == NULL) {
		/* Open a window pointing at the default location. */
544
		open_window (application, startup_id, NULL, screen, geometry);
545 546 547
	} else {
		/* Open windows at each requested location. */
		for (i = 0; uris[i] != NULL; i++) {
548
			open_window (application, startup_id, uris[i], screen, geometry);
549 550 551 552
		}
	}
}

553
static gboolean 
554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
nautilus_application_save_accel_map (gpointer data)
{
	if (save_of_accel_map_requested) {
		char *accel_map_filename;
	 	accel_map_filename = nautilus_get_accel_map_file ();
	 	if (accel_map_filename) {
	 		gtk_accel_map_save (accel_map_filename);
	 		g_free (accel_map_filename);
	 	}
		save_of_accel_map_requested = FALSE;
	}

	return FALSE;
}


static void 
queue_accel_map_save_callback (GtkAccelMap *object, gchar *accel_path,
		guint accel_key, GdkModifierType accel_mods,
		gpointer user_data)
{
	if (!save_of_accel_map_requested) {
		save_of_accel_map_requested = TRUE;
		g_timeout_add_seconds (NAUTILUS_ACCEL_MAP_SAVE_DELAY, 
				nautilus_application_save_accel_map, NULL);
	}
}

582 583 584 585 586 587 588 589 590 591 592 593
static void 
selection_get_cb (GtkWidget          *widget,
		  GtkSelectionData   *selection_data,
		  guint               info,
		  guint               time)
{
	/* No extra targets atm */
}

static GtkWidget *
get_desktop_manager_selection (GdkDisplay *display, int screen)
{
594
	char selection_name[32];
595 596 597 598
	GdkAtom selection_atom;
	Window selection_owner;
	GtkWidget *selection_widget;

599
	g_snprintf (selection_name, sizeof (selection_name), "_NET_DESKTOP_MANAGER_S%d", screen);
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
	selection_atom = gdk_atom_intern (selection_name, FALSE);

	selection_owner = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
					      gdk_x11_atom_to_xatom_for_display (display, 
										 selection_atom));
	if (selection_owner != None) {
		return NULL;
	}
	
	selection_widget = gtk_invisible_new_for_screen (gdk_display_get_screen (display, screen));
	/* We need this for gdk_x11_get_server_time() */
	gtk_widget_add_events (selection_widget, GDK_PROPERTY_CHANGE_MASK);

	if (gtk_selection_owner_set_for_display (display,
						 selection_widget,
						 selection_atom,
616
						 gdk_x11_get_server_time (gtk_widget_get_window (selection_widget)))) {
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647
		
		g_signal_connect (selection_widget, "selection_get",
				  G_CALLBACK (selection_get_cb), NULL);
		return selection_widget;
	}

	gtk_widget_destroy (selection_widget);
	
	return NULL;
}

static void
desktop_unrealize_cb (GtkWidget        *widget,
		      GtkWidget        *selection_widget)
{
	gtk_widget_destroy (selection_widget);
}

static gboolean
selection_clear_event_cb (GtkWidget	        *widget,
			  GdkEventSelection     *event,
			  NautilusDesktopWindow *window)
{
	gtk_widget_destroy (GTK_WIDGET (window));
	
	nautilus_application_desktop_windows =
		g_list_remove (nautilus_application_desktop_windows, window);

	return TRUE;
}

648
static void
649
nautilus_application_create_desktop_windows (NautilusApplication *application)
650
{
651 652
	GdkDisplay *display;
	NautilusDesktopWindow *window;
653
	GtkWidget *selection_widget;
654
	int screens, i;
655

656 657 658 659
	display = gdk_display_get_default ();
	screens = gdk_display_get_n_screens (display);

	for (i = 0; i < screens; i++) {
660 661 662

		DEBUG ("Creating a desktop window for screen %d", i);
		
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
		selection_widget = get_desktop_manager_selection (display, i);
		if (selection_widget != NULL) {
			window = nautilus_desktop_window_new (application,
							      gdk_display_get_screen (display, i));
			
			g_signal_connect (selection_widget, "selection_clear_event",
					  G_CALLBACK (selection_clear_event_cb), window);
			
			g_signal_connect (window, "unrealize",
					  G_CALLBACK (desktop_unrealize_cb), selection_widget);
			
			/* We realize it immediately so that the NAUTILUS_DESKTOP_WINDOW_ID
			   property is set so gnome-settings-daemon doesn't try to set the
			   background. And we do a gdk_flush() to be sure X gets it. */
			gtk_widget_realize (GTK_WIDGET (window));
			gdk_flush ();

			nautilus_application_desktop_windows =
				g_list_prepend (nautilus_application_desktop_windows, window);
682

683 684
			gtk_application_add_window (GTK_APPLICATION (application),
						    GTK_WINDOW (window));
685
		}
686
	}
687 688
}

689
static void
690
nautilus_application_open_desktop (NautilusApplication *application)
691
{
692 693
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
694 695 696
	}
}

697
static void
698 699
nautilus_application_close_desktop (void)
{
700 701 702 703 704
	if (nautilus_application_desktop_windows != NULL) {
		g_list_foreach (nautilus_application_desktop_windows,
				(GFunc) gtk_widget_destroy, NULL);
		g_list_free (nautilus_application_desktop_windows);
		nautilus_application_desktop_windows = NULL;
705
	}
706 707
}

708
void
709
nautilus_application_close_all_navigation_windows (NautilusApplication *self)
710
{
711 712 713
	GList *list_copy;
	GList *l;
	
714
	list_copy = g_list_copy (gtk_application_get_windows (GTK_APPLICATION (self)));
715 716 717 718 719
	/* First hide all window to get the feeling of quick response */
	for (l = list_copy; l != NULL; l = l->next) {
		NautilusWindow *window;
		
		window = NAUTILUS_WINDOW (l->data);
720
		gtk_widget_hide (GTK_WIDGET (window));
721 722
	}

723 724 725 726
	for (l = list_copy; l != NULL; l = l->next) {
		NautilusWindow *window;
		
		window = NAUTILUS_WINDOW (l->data);
727
		nautilus_window_close (window);
728
	}
729
	g_list_free (list_copy);
730 731
}

732
static gboolean
733 734 735
nautilus_window_delete_event_callback (GtkWidget *widget,
				       GdkEvent *event,
				       gpointer user_data)
736 737
{
	NautilusWindow *window;
738

739
	window = NAUTILUS_WINDOW (widget);
740
	nautilus_window_close (window);
741

742 743
	return TRUE;
}				       
744 745


746 747 748
static NautilusWindow *
create_window (NautilusApplication *application,
	       GType window_type,
749
	       const char *startup_id,
750
	       GdkScreen *screen)
751
{
752
	NautilusWindow *window;
753
	
754
	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
755
	
756
	window = NAUTILUS_WINDOW (gtk_widget_new (window_type,
757
						  "app", application,
758 759 760
						  "screen", screen,
						  NULL));

761 762 763 764
	if (startup_id) {
		gtk_window_set_startup_id (GTK_WINDOW (window), startup_id);
	}
	
765 766 767
	g_signal_connect_data (window, "delete_event",
			       G_CALLBACK (nautilus_window_delete_event_callback), NULL, NULL,
			       G_CONNECT_AFTER);
768

769 770
	gtk_application_add_window (GTK_APPLICATION (application),
				    GTK_WINDOW (window));
771

Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
772
	/* Do not yet show the window. It will be shown later on if it can
773 774 775
	 * successfully display its initial URI. Otherwise it will be destroyed
	 * without ever having seen the light of day.
	 */
776

777
	return window;
778
}
779

780
static gboolean
781 782
another_navigation_window_already_showing (NautilusApplication *application,
					   NautilusWindow *the_window)
783 784 785
{
	GList *list, *item;
	
786
	list = gtk_application_get_windows (GTK_APPLICATION (application));
787
	for (item = list; item != NULL; item = item->next) {
788
		if (item->data != the_window) {
789 790 791 792 793 794 795
			return TRUE;
		}
	}
	
	return FALSE;
}

796 797
NautilusWindow *
nautilus_application_create_navigation_window (NautilusApplication *application,
798
					       const char          *startup_id,
799 800 801
					       GdkScreen           *screen)
{
	NautilusWindow *window;
802 803
	char *geometry_string;
	gboolean maximized;
804 805

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
806

807
	window = create_window (application, NAUTILUS_TYPE_NAVIGATION_WINDOW, startup_id, screen);
808

809 810
	maximized = g_settings_get_boolean
		(nautilus_window_state, NAUTILUS_WINDOW_STATE_MAXIMIZED);
811 812 813 814 815 816
	if (maximized) {
		gtk_window_maximize (GTK_WINDOW (window));
	} else {
		gtk_window_unmaximize (GTK_WINDOW (window));
	}

817 818
	geometry_string = g_settings_get_string
		(nautilus_window_state, NAUTILUS_WINDOW_STATE_GEOMETRY);
819 820 821 822 823 824 825 826 827
	if (geometry_string != NULL &&
	    geometry_string[0] != 0) {
		/* Ignore saved window position if a window with the same
		 * location is already showing. That way the two windows
		 * wont appear at the exact same location on the screen.
		 */
		eel_gtk_window_set_initial_geometry_from_string 
			(GTK_WINDOW (window), 
			 geometry_string,
828 829
			 NAUTILUS_NAVIGATION_WINDOW_MIN_WIDTH,
			 NAUTILUS_NAVIGATION_WINDOW_MIN_HEIGHT,
830
			 another_navigation_window_already_showing (application, window));
831 832 833
	}
	g_free (geometry_string);

834 835
	DEBUG ("Creating a new navigation window");
	
836 837 838
	return window;
}

839 840 841 842 843
/* callback for showing or hiding the desktop based on the user's preference */
static void
desktop_changed_callback (gpointer user_data)
{
	NautilusApplication *application;
844

845
	application = NAUTILUS_APPLICATION (user_data);
846
	if (g_settings_get_boolean (gnome_background_preferences, NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
847 848 849 850 851 852
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
}

853 854 855 856 857 858 859 860 861 862
static gboolean
window_can_be_closed (NautilusWindow *window)
{
	if (!NAUTILUS_IS_DESKTOP_WINDOW (window)) {
		return TRUE;
	}
	
	return FALSE;
}

Alexander Larsson's avatar
Alexander Larsson committed
863
static void
864 865 866
mount_added_callback (GVolumeMonitor *monitor,
		      GMount *mount,
		      NautilusApplication *application)
Alexander Larsson's avatar
Alexander Larsson committed
867 868
{
	NautilusDirectory *directory;
Alexander Larsson's avatar
Alexander Larsson committed
869
	GFile *root;
870
	gchar *uri;
871

872
	if (!application->priv->session_is_active) {
873 874
		return;
	}
Alexander Larsson's avatar
Alexander Larsson committed
875
		
876
	root = g_mount_get_root (mount);
877 878 879 880 881
	uri = g_file_get_uri (root);

	DEBUG ("Added mount at uri %s", uri);
	g_free (uri);
	
Alexander Larsson's avatar
Alexander Larsson committed
882 883
	directory = nautilus_directory_get_existing (root);
	g_object_unref (root);
Alexander Larsson's avatar
Alexander Larsson committed
884 885 886 887 888 889
	if (directory != NULL) {
		nautilus_directory_force_reload (directory);
		nautilus_directory_unref (directory);
	}
}

890 891 892 893 894 895 896 897 898 899 900 901 902 903
static NautilusWindowSlot *
get_first_navigation_slot (GList *slot_list)
{
	GList *l;

	for (l = slot_list; l != NULL; l = l->next) {
		if (NAUTILUS_IS_NAVIGATION_WINDOW_SLOT (l->data)) {
			return l->data;
		}
	}

	return NULL;
}

904 905 906 907 908 909 910 911 912 913
/* We redirect some slots and close others */
static gboolean
should_close_slot_with_mount (NautilusWindow *window,
			      NautilusWindowSlot *slot,
			      GMount *mount)
{
	return nautilus_navigation_window_slot_should_close_with_mount (NAUTILUS_NAVIGATION_WINDOW_SLOT (slot),
									mount);
}

914 915 916 917
/* Called whenever a mount is unmounted. Check and see if there are
 * any windows open displaying contents on the mount. If there are,
 * close them.  It would also be cool to save open window and position
 * info.
918 919
 */
static void
920 921 922
mount_removed_callback (GVolumeMonitor *monitor,
			GMount *mount,
			NautilusApplication *application)
923
{
924
	GList *window_list, *node, *close_list;
925
	NautilusWindow *window;
926
	NautilusWindowSlot *slot;
927
	NautilusWindowSlot *force_no_close_slot;
Luke Symes's avatar
Luke Symes committed
928
	GFile *root, *computer;
929
	gboolean unclosed_slot;
930
	gchar *uri;
Alexander Larsson's avatar
Alexander Larsson committed
931

932
	close_list = NULL;
933
	force_no_close_slot = NULL;
934 935
	unclosed_slot = FALSE;

936
	/* Check and see if any of the open windows are displaying contents from the unmounted mount */
937
	window_list = gtk_application_get_windows (GTK_APPLICATION (application));
Alexander Larsson's avatar
Alexander Larsson committed
938

939
	root = g_mount_get_root (mount);
940 941 942 943 944
	uri = g_file_get_uri (root);

	DEBUG ("Removed mount at uri %s", uri);
	g_free (uri);

945 946
	/* Construct a list of windows to be closed. Do not add the non-closable windows to the list. */
	for (node = window_list; node != NULL; node = node->next) {
947
		window = NAUTILUS_WINDOW (node->data);
948
		if (window != NULL && window_can_be_closed (window)) {
949
			GList *l;
950 951 952 953 954 955 956 957 958