nautilus-application.c 51.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 4 5 6

/*
 *  Nautilus
 *
 *  Copyright (C) 1999, 2000 Red Hat, Inc.
7
 *  Copyright (C) 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 License
20
 *  along with this program; if not, write to the Free Software
21 22
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
23
 *  Authors: Elliot Lee <sopwith@redhat.com>,
24
 *           Darin Adler <darin@bentspoon.com>
25 26 27
 *
 */

28
#include <config.h>
29
#include "nautilus-application.h"
30

31
#include "file-manager/fm-desktop-icon-view.h"
32
#include "file-manager/fm-icon-view.h"
33
#include "file-manager/fm-list-view.h"
34
#include "file-manager/fm-tree-view.h"
Christian Neumair's avatar
Christian Neumair committed
35 36 37
#if ENABLE_EMPTY_VIEW
#include "file-manager/fm-empty-view.h"
#endif /* ENABLE_EMPTY_VIEW */
Alexander Larsson's avatar
Alexander Larsson committed
38 39
#include "nautilus-information-panel.h"
#include "nautilus-history-sidebar.h"
40
#include "nautilus-places-sidebar.h"
Alexander Larsson's avatar
Alexander Larsson committed
41 42 43
#include "nautilus-notes-viewer.h"
#include "nautilus-emblem-sidebar.h"
#include "nautilus-image-properties-page.h"
44 45 46
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Alexander Larsson's avatar
Alexander Larsson committed
47
#include <string.h>
48
#include "nautilus-desktop-window.h"
49
#include "nautilus-main.h"
50 51
#include "nautilus-spatial-window.h"
#include "nautilus-navigation-window.h"
52 53
#include "nautilus-window-slot.h"
#include "nautilus-navigation-window-slot.h"
54
#include "nautilus-window-bookmarks.h"
Alexander Larsson's avatar
Alexander Larsson committed
55
#include "libnautilus-private/nautilus-file-operations.h"
56
#include "nautilus-window-private.h"
57
#include "nautilus-window-manage-views.h"
58
#include <libxml/xmlsave.h>
59
#include <glib/gstdio.h>
60
#include <glib/gi18n.h>
61
#include <gio/gio.h>
62
#include <eel/eel-gtk-extensions.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
63 64
#include <eel/eel-gtk-macros.h>
#include <eel/eel-stock-dialogs.h>
65
#include <gdk/gdkx.h>
66
#include <gtk/gtk.h>
Alexander Larsson's avatar
 
Alexander Larsson committed
67
#include <libnautilus-private/nautilus-debug-log.h>
68 69
#include <libnautilus-private/nautilus-file-utilities.h>
#include <libnautilus-private/nautilus-global-preferences.h>
70
#include <libnautilus-private/nautilus-module.h>
71
#include <libnautilus-private/nautilus-undo-manager.h>
Alexander Larsson's avatar
Alexander Larsson committed
72
#include <libnautilus-private/nautilus-desktop-link-monitor.h>
73
#include <libnautilus-private/nautilus-directory-private.h>
Alexander Larsson's avatar
Alexander Larsson committed
74 75
#include <libnautilus-private/nautilus-signaller.h>
#include <libnautilus-extension/nautilus-menu-provider.h>
76
#include <libnautilus-private/nautilus-autorun.h>
Alexander Larsson's avatar
Alexander Larsson committed
77

78 79 80 81 82 83 84 85
enum
{
  COMMAND_0, /* unused: 0 is an invalid command */

  COMMAND_START_DESKTOP,
  COMMAND_STOP_DESKTOP,
  COMMAND_OPEN_BROWSER,
};
86

87 88 89 90
/* Needed for the is_kdesktop_present check */
#include <gdk/gdkx.h>
#include <X11/Xlib.h>

91 92 93 94 95
/* 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"
96

97 98
/* Keeps track of all the desktop windows. */
static GList *nautilus_application_desktop_windows;
99 100 101 102

/* Keeps track of all the nautilus windows. */
static GList *nautilus_application_window_list;

103 104 105
/* Keeps track of all the object windows */
static GList *nautilus_application_spatial_window_list;

106 107
static void     desktop_changed_callback          (gpointer                  user_data);
static void     desktop_location_changed_callback (gpointer                  user_data);
108 109 110 111 112 113 114 115 116
static void     mount_removed_callback            (GVolumeMonitor            *monitor,
						   GMount                    *mount,
						   NautilusApplication       *application);
static void     mount_added_callback              (GVolumeMonitor            *monitor,
						   GMount                    *mount,
						   NautilusApplication       *application);
static void     volume_added_callback              (GVolumeMonitor           *monitor,
						    GVolume                  *volume,
						    NautilusApplication      *application);
117 118 119 120 121
static void     drive_connected_callback           (GVolumeMonitor           *monitor,
						    GDrive                   *drive,
						    NautilusApplication      *application);
static void     drive_listen_for_eject_button      (GDrive *drive, 
						    NautilusApplication *application);
122
static gboolean is_kdesktop_present               (void);
123 124
static void     nautilus_application_load_session     (NautilusApplication *application); 
static char *   nautilus_application_get_session_data (void);
125

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
G_DEFINE_TYPE (NautilusApplication, nautilus_application, G_TYPE_OBJECT);

static gboolean
_unique_message_data_set_geometry_and_uris (UniqueMessageData  *message_data,
					    const char *geometry,
					    char **uris)
{
  GString *list;
  gint i;
  gchar *result;
  gsize length;

  list = g_string_new (NULL);
  if (geometry != NULL) {
	  g_string_append (list, geometry);
  }
  g_string_append (list, "\r\n");
  
  for (i = 0; uris != NULL && uris[i]; i++) {
	  g_string_append (list, uris[i]);
	  g_string_append (list, "\r\n");
  }

  result = g_convert (list->str, list->len,
                      "ASCII", "UTF-8",
                      NULL, &length, NULL);
  g_string_free (list, TRUE);
  
  if (result) {
	  unique_message_data_set (message_data, (guchar *) result, length);
	  g_free (result);
	  return TRUE;
  }
  
  return FALSE;
}
162

163 164 165
static gchar **
_unique_message_data_get_geometry_and_uris (UniqueMessageData *message_data,
					    char **geometry)
166
{
167
  gchar **result = NULL;
168

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
  *geometry = NULL;
  
  gchar *text, *newline, *uris;
  text = unique_message_data_get_text (message_data);
  if (text) {
	  newline = strchr (text, '\n');
	  if (newline) {
		  *geometry = g_strndup (text, newline-text);
		  uris = newline+1;
	  } else {
		  uris = text;
	  }
	  
	  result = g_uri_list_extract_uris (uris);
	  g_free (text);
  }
  
  return result;
}

189 190
GList *
nautilus_application_get_window_list (void)
191 192 193 194
{
	return nautilus_application_window_list;
}

195 196 197 198 199 200
GList *
nautilus_application_get_spatial_window_list (void)
{
	return nautilus_application_spatial_window_list;
}

201 202 203 204 205 206
unsigned int
nautilus_application_get_n_windows (void)
{
	return g_list_length (nautilus_application_window_list) +
	       g_list_length (nautilus_application_desktop_windows);
}
207

208 209 210 211 212
static void
startup_volume_mount_cb (GObject *source_object,
			 GAsyncResult *res,
			 gpointer user_data)
{
213
	g_volume_mount_finish (G_VOLUME (source_object), res, NULL);
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
}

static void
automount_all_volumes (NautilusApplication *application)
{
	GList *volumes, *l;
	GMount *mount;
	GVolume *volume;
		
	if (eel_preferences_get_boolean (NAUTILUS_PREFERENCES_MEDIA_AUTOMOUNT)) {
		/* automount all mountable volumes at start-up */
		volumes = g_volume_monitor_get_volumes (application->volume_monitor);
		for (l = volumes; l != NULL; l = l->next) {
			volume = l->data;
			
229 230
			if (!g_volume_should_automount (volume) ||
			    !g_volume_can_mount (volume)) {
231 232 233 234 235 236 237 238 239 240
				continue;
			}
			
			mount = g_volume_get_mount (volume);
			if (mount != NULL) {
				g_object_unref (mount);
				continue;
			}

			/* pass NULL as GMountOperation to avoid user interaction */
241
			g_volume_mount (volume, 0, NULL, NULL, startup_volume_mount_cb, NULL);
242 243 244 245 246 247
		}
		eel_g_object_list_free (volumes);
	}
	
}

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
static void
smclient_save_state_cb (EggSMClient   *client,
			GKeyFile      *state_file,
			NautilusApplication *application)
{
	char *data;

	data = nautilus_application_get_session_data ();
	if (data) {
		g_key_file_set_string (state_file,
				       "Nautilus",
				       "documents", 
				       data);
	}
	g_free (data);
}

static void
smclient_quit_cb (EggSMClient   *client,
		  NautilusApplication *application)
{
	nautilus_main_event_loop_quit (TRUE);
}

272
static void
273
nautilus_application_init (NautilusApplication *application)
274
{
275 276
	/* Create an undo manager */
	application->undo_manager = nautilus_undo_manager_new ();
277

278 279 280 281 282
	application->unique_app = unique_app_new_with_commands ("org.gnome.Nautilus", NULL,
								"start_desktop", COMMAND_START_DESKTOP,
								"stop_desktop", COMMAND_STOP_DESKTOP,
								"open_browser", COMMAND_OPEN_BROWSER,
								NULL);
283 284 285 286 287 288 289 290 291 292

	
        application->smclient = egg_sm_client_get ();
        g_signal_connect (application->smclient, "save_state",
                          G_CALLBACK (smclient_save_state_cb),
                          application);
	g_signal_connect (application->smclient, "quit",
			  G_CALLBACK (smclient_quit_cb),
			  application);
	/* TODO: Should connect to quit_requested and block logout on active transfer? */
293
	
Alexander Larsson's avatar
Alexander Larsson committed
294 295 296 297
	/* register views */
	fm_icon_view_register ();
	fm_desktop_icon_view_register ();
	fm_list_view_register ();
298
	fm_compact_view_register ();
Christian Neumair's avatar
Christian Neumair committed
299 300 301
#if ENABLE_EMPTY_VIEW
	fm_empty_view_register ();
#endif /* ENABLE_EMPTY_VIEW */
Alexander Larsson's avatar
Alexander Larsson committed
302 303

	/* register sidebars */
304
	nautilus_places_sidebar_register ();
Alexander Larsson's avatar
Alexander Larsson committed
305 306 307 308 309 310 311 312
	nautilus_information_panel_register ();
	fm_tree_view_register ();
	nautilus_history_sidebar_register ();
	nautilus_notes_viewer_register (); /* also property page */
	nautilus_emblem_sidebar_register ();

	/* register property pages */
	nautilus_image_properties_page_register ();
313 314
}

315 316
NautilusApplication *
nautilus_application_new (void)
317
{
318
	return g_object_new (NAUTILUS_TYPE_APPLICATION, NULL);
319 320 321
}

static void
322
nautilus_application_finalize (GObject *object)
323
{
324 325 326 327
	NautilusApplication *application;

	application = NAUTILUS_APPLICATION (object);

328
	nautilus_bookmarks_exiting ();
329
	
Alexander Larsson's avatar
Alexander Larsson committed
330
	g_object_unref (application->undo_manager);
331

Alexander Larsson's avatar
Alexander Larsson committed
332 333 334 335
	if (application->volume_monitor) {
		g_object_unref (application->volume_monitor);
		application->volume_monitor = NULL;
	}
336 337

	g_object_unref (application->unique_app);
338

339 340 341 342 343
	if (application->automount_idle_id != 0) {
		g_source_remove (application->automount_idle_id);
		application->automount_idle_id = 0;
	}

344
        G_OBJECT_CLASS (nautilus_application_parent_class)->finalize (object);
345
}
346

347 348
static gboolean
check_required_directories (NautilusApplication *application)
349
{
350 351
	char *user_directory;
	char *desktop_directory;
Alexander Larsson's avatar
Alexander Larsson committed
352 353 354
	GSList *directories;
	gboolean ret;

355
	g_assert (NAUTILUS_IS_APPLICATION (application));
356

Alexander Larsson's avatar
Alexander Larsson committed
357 358
	ret = TRUE;

359 360 361
	user_directory = nautilus_get_user_directory ();
	desktop_directory = nautilus_get_desktop_directory ();

Alexander Larsson's avatar
Alexander Larsson committed
362 363
	directories = NULL;

364
	if (!g_file_test (user_directory, G_FILE_TEST_IS_DIR)) {
Alexander Larsson's avatar
Alexander Larsson committed
365
		directories = g_slist_prepend (directories, user_directory);
366
	}
Alexander Larsson's avatar
Alexander Larsson committed
367

368
	if (!g_file_test (desktop_directory, G_FILE_TEST_IS_DIR)) {
Alexander Larsson's avatar
Alexander Larsson committed
369
		directories = g_slist_prepend (directories, desktop_directory);
370 371
	}

Alexander Larsson's avatar
Alexander Larsson committed
372 373 374 375 376 377 378 379 380
	if (directories != NULL) {
		int failed_count;
		GString *directories_as_string;
		GSList *l;
		char *error_string;
		const char *detail_string;
		GtkDialog *dialog;

		ret = FALSE;
381

Alexander Larsson's avatar
Alexander Larsson committed
382 383 384 385 386 387
		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);
		}
388

389
		if (failed_count == 1) {
390
			error_string = g_strdup_printf (_("Nautilus could not create the required folder \"%s\"."),
Alexander Larsson's avatar
Alexander Larsson committed
391
							directories_as_string->str);
392 393
			detail_string = _("Before running Nautilus, please create the following folder, or "
					  "set permissions such that Nautilus can create it.");
394
		} else {
395
			error_string = g_strdup_printf (_("Nautilus could not create the following required folders: "
Alexander Larsson's avatar
Alexander Larsson committed
396 397
							  "%s."), directories_as_string->str);
			detail_string = _("Before running Nautilus, please create these folders, or "
398
					  "set permissions such that Nautilus can create them.");
399
		}
Alexander Larsson's avatar
Alexander Larsson committed
400

401
		dialog = eel_show_error_dialog (error_string, detail_string, NULL);
402 403
		/* We need the main event loop so the user has a chance to see the dialog. */
		nautilus_main_event_loop_register (GTK_OBJECT (dialog));
404

Alexander Larsson's avatar
Alexander Larsson committed
405
		g_string_free (directories_as_string, TRUE);
406 407 408
		g_free (error_string);
	}

Alexander Larsson's avatar
Alexander Larsson committed
409 410 411
	g_slist_free (directories);
	g_free (user_directory);
	g_free (desktop_directory);
412

Alexander Larsson's avatar
Alexander Larsson committed
413
	return ret;
414 415
}

Alexander Larsson's avatar
Alexander Larsson committed
416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
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 *items;
        GList *providers;
        GList *l;

        providers = nautilus_module_get_extensions_for_type (NAUTILUS_TYPE_MENU_PROVIDER);
        items = NULL;

        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);
}

445 446 447 448 449 450 451 452 453 454 455
static gboolean
automount_all_volumes_idle_cb (gpointer data)
{
	NautilusApplication *application = NAUTILUS_APPLICATION (data);

	automount_all_volumes (application);

	application->automount_idle_id = 0;
	return FALSE;
}

456 457 458
static void
finish_startup (NautilusApplication *application)
{
459 460
	GList *drives;

461
	/* initialize nautilus modules */
462
	nautilus_module_setup ();
463

Alexander Larsson's avatar
Alexander Larsson committed
464 465
	/* attach menu-provider module callback */
	menu_provider_init_callback ();
466
	
Alexander Larsson's avatar
Alexander Larsson committed
467 468
	/* Initialize the desktop link monitor singleton */
	nautilus_desktop_link_monitor_get ();
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493

	/* Watch for mounts so we can restore open windows This used
	 * to be for showing new window on mount, but is not used
	 * anymore */

	/* Watch for unmounts so we can close open windows */
	/* TODO-gio: This should be using the UNMOUNTED feature of GFileMonitor instead */
	application->volume_monitor = g_volume_monitor_get ();
	g_signal_connect_object (application->volume_monitor, "mount_removed",
				 G_CALLBACK (mount_removed_callback), application, 0);
	g_signal_connect_object (application->volume_monitor, "mount_pre_unmount",
				 G_CALLBACK (mount_removed_callback), application, 0);
	g_signal_connect_object (application->volume_monitor, "mount_added",
				 G_CALLBACK (mount_added_callback), application, 0);
	g_signal_connect_object (application->volume_monitor, "volume_added",
				 G_CALLBACK (volume_added_callback), application, 0);
	g_signal_connect_object (application->volume_monitor, "drive_connected",
				 G_CALLBACK (drive_connected_callback), application, 0);

	/* listen for eject button presses */
	drives = g_volume_monitor_get_connected_drives (application->volume_monitor);
	g_list_foreach (drives, (GFunc) drive_listen_for_eject_button, application);
	g_list_foreach (drives, (GFunc) g_object_unref, NULL);
	g_list_free (drives);

494 495 496 497
	application->automount_idle_id = 
		g_idle_add_full (G_PRIORITY_LOW,
				 automount_all_volumes_idle_cb,
				 application, NULL);
498 499
}

500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 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
static void
open_window (NautilusApplication *application,
	     const char *startup_id,
	     const char *uri, const char *geometry, gboolean browser_window)
{
	GFile *location;
	NautilusWindow *window;

	if (browser_window ||
	    eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ALWAYS_USE_BROWSER)) {
		window = nautilus_application_create_navigation_window (application,
									startup_id,
									gdk_screen_get_default ());
		if (uri == NULL) {
			nautilus_window_go_home (window);
		} else {
			location = g_file_new_for_uri (uri);
			nautilus_window_go_to (window, location);
			g_object_unref (location);
		}
	} else {
		if (uri == NULL) {
			location = g_file_new_for_path (g_get_home_dir ());
		} else {
			location = g_file_new_for_uri (uri);
		}
		
		window = nautilus_application_present_spatial_window (application,
								      NULL,
								      startup_id,
								      location,
								      gdk_screen_get_default ());
		g_object_unref (location);
	}
	
	if (geometry != NULL && !GTK_WIDGET_VISIBLE (window)) {
		/* 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,
	      const char *geometry,
	      gboolean browser_window)
{
	guint i;
Alexander Larsson's avatar
Alexander Larsson committed
556

557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
	if (uris == NULL || uris[0] == NULL) {
		/* Open a window pointing at the default location. */
		open_window (application, startup_id, NULL, geometry, browser_window);
	} else {
		/* Open windows at each requested location. */
		for (i = 0; uris[i] != NULL; i++) {
			open_window (application, startup_id, uris[i], geometry, browser_window);
		}
	}
}

static UniqueResponse
message_received_cb (UniqueApp         *unique_app,
                     UniqueCommand      command,
                     UniqueMessageData *message,
                     guint              time_,
                     gpointer           user_data)
Alexander Larsson's avatar
Alexander Larsson committed
574
{
575 576 577 578
	NautilusApplication *application;
	UniqueResponse res;
	char **uris;
	char *geometry;
Alexander Larsson's avatar
Alexander Larsson committed
579
	
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
	application =  user_data;
	res = UNIQUE_RESPONSE_OK;
	
	switch (command) {
	case UNIQUE_CLOSE:
		res = UNIQUE_RESPONSE_OK;
		nautilus_main_event_loop_quit (TRUE);
		
		break;
	case UNIQUE_OPEN:
	case COMMAND_OPEN_BROWSER:
		uris = _unique_message_data_get_geometry_and_uris (message, &geometry);
		open_windows (application,
			      unique_message_data_get_startup_id (message),
			      uris,
			      geometry,
			      command == COMMAND_OPEN_BROWSER);
		g_strfreev (uris);
		g_free (geometry);
		break;
	case COMMAND_START_DESKTOP:
		nautilus_application_open_desktop (application);
		break;
	case COMMAND_STOP_DESKTOP:
		nautilus_application_close_desktop ();
		break;
	default:
		res = UNIQUE_RESPONSE_PASSTHROUGH;
		break;
	}
	
	return res;
Alexander Larsson's avatar
Alexander Larsson committed
612 613
}

614
void
615
nautilus_application_startup (NautilusApplication *application,
616
			      gboolean kill_shell,
617
			      gboolean no_default_window,
618
			      gboolean no_desktop,
Alexander Larsson's avatar
Alexander Larsson committed
619
			      gboolean browser_window,
620
			      const char *geometry,
621
			      char **urls)
622
{
623 624
	UniqueMessageData *message;
	
625
	/* Check the user's ~/.nautilus directories and post warnings
626
	 * if there are problems.
627
	 */
628
	if (!kill_shell && !check_required_directories (application)) {
629 630
		return;
	}
631

632
	if (kill_shell) {
633 634 635 636 637
		if (unique_app_is_running (application->unique_app)) {
			unique_app_send_message (application->unique_app,
						 UNIQUE_CLOSE, NULL);
			
		}
638
	} else {
639
		/* If KDE desktop is running, then force no_desktop */
640
		if (is_kdesktop_present ()) {
641
			no_desktop = TRUE;
642
		}
643
		
644
		if (!no_desktop && eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
645 646 647 648 649 650 651 652 653 654 655
			if (unique_app_is_running (application->unique_app)) {
				unique_app_send_message (application->unique_app,
							 COMMAND_START_DESKTOP, NULL);
			} else {
				nautilus_application_open_desktop (application);
			}
		}

		if (!unique_app_is_running (application->unique_app)) {
			finish_startup (application);
			g_signal_connect (application->unique_app, "message-received", G_CALLBACK (message_received_cb), application);			
656
		}
657 658
		
		/* Monitor the preference to show or hide the desktop */
659
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_SHOW_DESKTOP,
Darin Adler's avatar
Darin Adler committed
660 661 662
							  desktop_changed_callback,
							  application,
							  G_OBJECT (application));
663

664 665
		/* Monitor the preference to have the desktop */
		/* point to the Unix home folder */
666
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
Darin Adler's avatar
Darin Adler committed
667 668 669
							  desktop_location_changed_callback,
							  NULL,
							  G_OBJECT (application));
670

671
	  	/* Create the other windows. */
672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
		if (urls != NULL || !no_default_window) {
			if (unique_app_is_running (application->unique_app)) {
				message = unique_message_data_new ();
				_unique_message_data_set_geometry_and_uris (message, geometry, urls);
				if (browser_window) {
					unique_app_send_message (application->unique_app,
								 COMMAND_OPEN_BROWSER, message);
				} else {
					unique_app_send_message (application->unique_app,
								 UNIQUE_OPEN, message);
				}
				unique_message_data_free (message);				
			} else {
				open_windows (application, NULL,
					      urls,
					      geometry,
					      browser_window);
			}
690
		}
691

692 693
		/* Load session info if availible */
		nautilus_application_load_session (application);
694
	}
695 696
}

697 698 699 700 701 702 703 704 705 706 707 708 709

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)
{
710
	char selection_name[32];
711 712 713 714
	GdkAtom selection_atom;
	Window selection_owner;
	GtkWidget *selection_widget;

715
	g_snprintf (selection_name, sizeof (selection_name), "_NET_DESKTOP_MANAGER_S%d", screen);
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763
	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,
						 gdk_x11_get_server_time (selection_widget->window))) {
		
		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;
}

764
static void
765
nautilus_application_create_desktop_windows (NautilusApplication *application)
766
{
767
	static gboolean create_in_progress = FALSE;
768 769
	GdkDisplay *display;
	NautilusDesktopWindow *window;
770
	GtkWidget *selection_widget;
771
	int screens, i;
772

773
	g_return_if_fail (nautilus_application_desktop_windows == NULL);
774
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
775

776 777 778 779 780 781
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

782 783 784 785
	display = gdk_display_get_default ();
	screens = gdk_display_get_n_screens (display);

	for (i = 0; i < screens; i++) {
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
		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);
		}
807
	}
808 809

	create_in_progress = FALSE;
810 811 812 813
}

void
nautilus_application_open_desktop (NautilusApplication *application)
814
{
815 816
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
817 818 819 820 821 822
	}
}

void
nautilus_application_close_desktop (void)
{
823 824 825 826 827
	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;
828
	}
829 830
}

831
void
832
nautilus_application_close_all_navigation_windows (void)
833
{
834 835 836 837 838 839 840 841 842 843 844 845
	GList *list_copy;
	GList *l;
	
	list_copy = g_list_copy (nautilus_application_window_list);
	for (l = list_copy; l != NULL; l = l->next) {
		NautilusWindow *window;
		
		window = NAUTILUS_WINDOW (l->data);
		
		if (NAUTILUS_IS_NAVIGATION_WINDOW (window)) {
			nautilus_window_close (window);
		}
846
	}
847
	g_list_free (list_copy);
848 849
}

850
static NautilusSpatialWindow *
Alexander Larsson's avatar
Alexander Larsson committed
851
nautilus_application_get_existing_spatial_window (GFile *location)
852
{
853
	GList *l;
854
	NautilusWindowSlot *slot;
Alexander Larsson's avatar
Alexander Larsson committed
855

856 857
	for (l = nautilus_application_get_spatial_window_list ();
	     l != NULL; l = l->next) {
Alexander Larsson's avatar
Alexander Larsson committed
858 859
		GFile *window_location;

860 861
		slot = NAUTILUS_WINDOW (l->data)->details->active_slot;
		window_location = slot->location;
Alexander Larsson's avatar
Alexander Larsson committed
862 863 864 865
		if (window_location != NULL) {
			if (g_file_equal (location, window_location)) {
				return NAUTILUS_SPATIAL_WINDOW (l->data);
			}
866 867 868
		}
	}
	return NULL;
869 870
}

871 872
static NautilusSpatialWindow *
find_parent_spatial_window (NautilusSpatialWindow *window)
873
{
874 875
	NautilusFile *file;
	NautilusFile *parent_file;
876
	NautilusWindowSlot *slot;
Alexander Larsson's avatar
Alexander Larsson committed
877
	GFile *location;
878

879 880 881
	slot = NAUTILUS_WINDOW (window)->details->active_slot;

	location = slot->location;
Alexander Larsson's avatar
Alexander Larsson committed
882 883 884
	if (location == NULL) {
		return NULL;
	}
885
	file = nautilus_file_get (location);
886

887 888 889
	if (!file) {
		return NULL;
	}
Alexander Larsson's avatar
Alexander Larsson committed
890

891 892 893 894 895
	parent_file = nautilus_file_get_parent (file);
	nautilus_file_unref (file);
	while (parent_file) {
		NautilusSpatialWindow *parent_window;

Alexander Larsson's avatar
Alexander Larsson committed
896
		location = nautilus_file_get_location (parent_file);
897
		parent_window = nautilus_application_get_existing_spatial_window (location);
Alexander Larsson's avatar
Alexander Larsson committed
898 899
		g_object_unref (location);

900 901 902 903 904 905 906 907
		/* Stop at the desktop directory if it's not explicitely opened
		 * in a spatial window of its own.
		 */
		if (nautilus_file_is_desktop_directory (parent_file) && !parent_window) {
			nautilus_file_unref (parent_file);
			return NULL;
		}

908 909 910
		if (parent_window) {
			nautilus_file_unref (parent_file);
			return parent_window;
911
		}
912 913 914 915 916 917 918 919 920
		file = parent_file;
		parent_file = nautilus_file_get_parent (file);
		nautilus_file_unref (file);
	}

	return NULL;
}

void
921
nautilus_application_close_parent_windows (NautilusSpatialWindow *window)
922 923
{
	NautilusSpatialWindow *parent_window;
924
	NautilusSpatialWindow *new_parent_window;
925

926
	g_return_if_fail (NAUTILUS_IS_SPATIAL_WINDOW (window));
927

928 929 930
	parent_window = find_parent_spatial_window (window);
	
	while (parent_window) {
931 932 933 934
		
		new_parent_window = find_parent_spatial_window (parent_window);
		nautilus_window_close (NAUTILUS_WINDOW (parent_window));
		parent_window = new_parent_window;
935
	}
936
}
937

938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956
void
nautilus_application_close_all_spatial_windows (void)
{
	GList *list_copy;
	GList *l;
	
	list_copy = g_list_copy (nautilus_application_spatial_window_list);
	for (l = list_copy; l != NULL; l = l->next) {
		NautilusWindow *window;
		
		window = NAUTILUS_WINDOW (l->data);
		
		if (NAUTILUS_IS_SPATIAL_WINDOW (window)) {
			nautilus_window_close (window);
		}
	}
	g_list_free (list_copy);
}

957 958 959 960
static void
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
{
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
961 962 963
}

static gboolean
964 965 966
nautilus_window_delete_event_callback (GtkWidget *widget,
				       GdkEvent *event,
				       gpointer user_data)
967 968
{
	NautilusWindow *window;
969

970
	window = NAUTILUS_WINDOW (widget);
971
	nautilus_window_close (window);
972

973 974
	return TRUE;
}				       
975 976


977 978 979
static NautilusWindow *
create_window (NautilusApplication *application,
	       GType window_type,
980
	       const char *startup_id,
981
	       GdkScreen *screen)
982
{
983
	NautilusWindow *window;
984
	
985
	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
986
	
987
	window = NAUTILUS_WINDOW (gtk_widget_new (window_type,
988
						  "app", application,
989 990
						  "screen", screen,
						  NULL));
Alexander Larsson's avatar
Alexander Larsson committed
991 992
	/* Must be called after construction finished */
	nautilus_window_constructed (window);
993

994 995 996 997 998
	unique_app_watch_window (application->unique_app, GTK_WINDOW (window));
	if (startup_id) {
		gtk_window_set_startup_id (GTK_WINDOW (window), startup_id);
	}
	
999 1000 1001
	g_signal_connect_data (window, "delete_event",
			       G_CALLBACK (nautilus_window_delete_event_callback), NULL, NULL,
			       G_CONNECT_AFTER);
1002

1003 1004
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
1005

1006
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
1007

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

1013
	return window;
1014
}
1015

1016 1017 1018 1019 1020 1021 1022 1023 1024
static void
spatial_window_destroyed_callback (void *user_data, GObject *window)
{
	nautilus_application_spatial_window_list = g_list_remove (nautilus_application_spatial_window_list, window);
		
}

NautilusWindow *
nautilus_application_present_spatial_window (NautilusApplication *application,
1025
					     NautilusWindow      *requesting_window,
1026
					     const char          *startup_id,
Alexander Larsson's avatar
Alexander Larsson committed
1027
					     GFile               *location,
1028
					     GdkScreen           *screen)
1029 1030 1031
{
	return nautilus_application_present_spatial_window_with_selection (application,
									   requesting_window,
1032
									   startup_id,
1033 1034 1035 1036 1037 1038 1039
									   location,
									   NULL,
									   screen);
}

NautilusWindow *
nautilus_application_present_spatial_window_with_selection (NautilusApplication *application,
Alexander Larsson's avatar
Alexander Larsson committed
1040
							    NautilusWindow      *requesting_window,
1041
							    const char          *startup_id,
Alexander Larsson's avatar
Alexander Larsson committed
1042
							    GFile               *location,
1043
							    GList		*new_selection,
Alexander Larsson's avatar
Alexander Larsson committed
1044
							    GdkScreen           *screen)
1045 1046 1047
{
	NautilusWindow *window;
	GList *l;
Alexander Larsson's avatar
Alexander Larsson committed
1048
	char *uri;
1049 1050

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
Alexander Larsson's avatar
Alexander Larsson committed
1051
	
1052 1053 1054
	for (l = nautilus_application_get_spatial_window_list ();
	     l != NULL; l = l->next) {
		NautilusWindow *existing_window;
1055
		NautilusWindowSlot *slot;
Alexander Larsson's avatar
Alexander Larsson committed
1056
		GFile *existing_location;
1057

1058
		existing_window = NAUTILUS_WINDOW (l->data);
1059 1060
		slot = existing_window->details->active_slot;
		existing_location = slot->pending_location;
Alexander Larsson's avatar
Alexander Larsson committed
1061
		
1062
		if (existing_location == NULL) {
1063
			existing_location = slot->location;
1064
		}
Alexander Larsson's avatar
Alexander Larsson committed
1065 1066
		
		if (g_file_equal (existing_location, location)) {
1067
			gtk_window_present (GTK_WINDOW (existing_window));
1068
			if (new_selection &&
1069 1070
			    slot->content_view != NULL) {
				nautilus_view_set_selection (slot->content_view, new_selection);
1071
			}
Alexander Larsson's avatar
 
Alexander Larsson committed
1072

Alexander Larsson's avatar
Alexander Larsson committed
1073
			uri = g_file_get_uri (location);
Alexander Larsson's avatar
 
Alexander Larsson committed
1074 1075
			nautilus_debug_log (FALSE, NAUTILUS_DEBUG_LOG_DOMAIN_USER,
					    "present EXISTING spatial window=%p: %s",
Alexander Larsson's avatar
Alexander Larsson committed
1076 1077
					    existing_window, uri);
			g_free (uri);
1078