nautilus-application.c 52.6 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
	g_object_unref (application->smclient);
332

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

	g_object_unref (application->unique_app);
339

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

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

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

356
	g_assert (NAUTILUS_IS_APPLICATION (application));
357

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

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

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

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

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

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

		ret = FALSE;
382

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

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

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

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

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

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

Alexander Larsson's avatar
Alexander Larsson committed
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 445
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);
}

446 447 448 449 450 451 452 453 454 455 456
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;
}

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

462 463 464
	/* initialize nautilus modules */
	nautilus_module_init ();

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

	/* 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);

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

501 502 503 504 505 506 507
static void
initialize_kde_trash_hack (void)
{
	char *desktop_dir, *desktop_uri, *kde_trash_dir;
	char *dir, *basename;

	desktop_uri = nautilus_get_desktop_directory_uri_no_create ();
Alexander Larsson's avatar
Alexander Larsson committed
508
	desktop_dir = g_filename_from_uri (desktop_uri, NULL, NULL);
509
	g_free (desktop_uri);
510

511 512 513 514
	if (g_file_test (desktop_dir, G_FILE_TEST_EXISTS)) {
		gboolean res;
		GKeyFile *keyfile;
		char **dirs;
515

516
		/* Look for trash directory */
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
		keyfile = g_key_file_new ();
		dirs = g_new0 (char *, 3);
		dirs[0] = g_build_filename (g_get_home_dir(), ".kde/share/config", NULL);
		dirs[1] = g_strdup ("/usr/share/config");
		dirs[2] = NULL;

		res = g_key_file_load_from_dirs (keyfile, "kdeglobals",
						 (const char **) dirs, NULL, 0, NULL);
		if (res) {
			kde_trash_dir = g_key_file_get_string (keyfile,
							       "Paths", "Trash",
							       NULL);
			if (kde_trash_dir != NULL) {
				basename = g_path_get_basename (kde_trash_dir);

				dir = g_build_filename (desktop_dir, basename, NULL);

				if (g_file_test (dir, G_FILE_TEST_IS_DIR)) {
					nautilus_set_kde_trash_name (basename);
				}

				g_free (basename);
				g_free (dir);
				g_free (kde_trash_dir);
			}
543 544
		}

545 546
		g_key_file_free (keyfile);
		g_strfreev (dirs);
547
	}
548

549 550 551
	g_free (desktop_dir);
}

552 553 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 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
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
608

609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
	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
626
{
627 628 629 630
	NautilusApplication *application;
	UniqueResponse res;
	char **uris;
	char *geometry;
Alexander Larsson's avatar
Alexander Larsson committed
631
	
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
	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
664 665
}

666
void
667
nautilus_application_startup (NautilusApplication *application,
668
			      gboolean kill_shell,
669
			      gboolean no_default_window,
670
			      gboolean no_desktop,
Alexander Larsson's avatar
Alexander Larsson committed
671
			      gboolean browser_window,
672
			      const char *geometry,
673
			      char **urls)
674
{
675 676
	UniqueMessageData *message;
	
677
	/* Check the user's ~/.nautilus directories and post warnings
678
	 * if there are problems.
679
	 */
680
	if (!kill_shell && !check_required_directories (application)) {
681 682
		return;
	}
683

684 685
	initialize_kde_trash_hack ();

686
	if (kill_shell) {
687 688 689 690 691
		if (unique_app_is_running (application->unique_app)) {
			unique_app_send_message (application->unique_app,
						 UNIQUE_CLOSE, NULL);
			
		}
692
	} else {
693
		/* If KDE desktop is running, then force no_desktop */
694
		if (is_kdesktop_present ()) {
695
			no_desktop = TRUE;
696
		}
697
		
698
		if (!no_desktop && eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
699 700 701 702 703 704 705 706 707 708 709
			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);			
710
		}
711 712
		
		/* Monitor the preference to show or hide the desktop */
713
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_SHOW_DESKTOP,
Darin Adler's avatar
Darin Adler committed
714 715 716
							  desktop_changed_callback,
							  application,
							  G_OBJECT (application));
717

718 719
		/* Monitor the preference to have the desktop */
		/* point to the Unix home folder */
720
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
Darin Adler's avatar
Darin Adler committed
721 722 723
							  desktop_location_changed_callback,
							  NULL,
							  G_OBJECT (application));
724

725
	  	/* Create the other windows. */
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
		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);
			}
744
		}
745

746 747
		/* Load session info if availible */
		nautilus_application_load_session (application);
748
	}
749 750
}

751 752 753 754 755 756 757 758 759 760 761 762 763

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)
{
764
	char selection_name[32];
765 766 767 768
	GdkAtom selection_atom;
	Window selection_owner;
	GtkWidget *selection_widget;

769
	g_snprintf (selection_name, sizeof (selection_name), "_NET_DESKTOP_MANAGER_S%d", screen);
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
	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;
}

818
static void
819
nautilus_application_create_desktop_windows (NautilusApplication *application)
820
{
821
	static gboolean create_in_progress = FALSE;
822 823
	GdkDisplay *display;
	NautilusDesktopWindow *window;
824
	GtkWidget *selection_widget;
825
	int screens, i;
826

827
	g_return_if_fail (nautilus_application_desktop_windows == NULL);
828
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
829

830 831 832 833 834 835
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

836 837 838 839
	display = gdk_display_get_default ();
	screens = gdk_display_get_n_screens (display);

	for (i = 0; i < screens; i++) {
840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
		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);
		}
861
	}
862 863

	create_in_progress = FALSE;
864 865 866 867
}

void
nautilus_application_open_desktop (NautilusApplication *application)
868
{
869 870
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
871 872 873 874 875 876
	}
}

void
nautilus_application_close_desktop (void)
{
877 878 879 880 881
	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;
882
	}
883 884
}

885
void
886
nautilus_application_close_all_navigation_windows (void)
887
{
888 889 890 891 892 893 894 895 896 897 898 899
	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);
		}
900
	}
901
	g_list_free (list_copy);
902 903
}

904
static NautilusSpatialWindow *
Alexander Larsson's avatar
Alexander Larsson committed
905
nautilus_application_get_existing_spatial_window (GFile *location)
906
{
907
	GList *l;
908
	NautilusWindowSlot *slot;
Alexander Larsson's avatar
Alexander Larsson committed
909

910 911
	for (l = nautilus_application_get_spatial_window_list ();
	     l != NULL; l = l->next) {
Alexander Larsson's avatar
Alexander Larsson committed
912 913
		GFile *window_location;

914 915
		slot = NAUTILUS_WINDOW (l->data)->details->active_slot;
		window_location = slot->location;
Alexander Larsson's avatar
Alexander Larsson committed
916 917 918 919
		if (window_location != NULL) {
			if (g_file_equal (location, window_location)) {
				return NAUTILUS_SPATIAL_WINDOW (l->data);
			}
920 921 922
		}
	}
	return NULL;
923 924
}

925 926
static NautilusSpatialWindow *
find_parent_spatial_window (NautilusSpatialWindow *window)
927
{
928 929
	NautilusFile *file;
	NautilusFile *parent_file;
930
	NautilusWindowSlot *slot;
Alexander Larsson's avatar
Alexander Larsson committed
931
	GFile *location;
932

933 934 935
	slot = NAUTILUS_WINDOW (window)->details->active_slot;

	location = slot->location;
Alexander Larsson's avatar
Alexander Larsson committed
936 937 938
	if (location == NULL) {
		return NULL;
	}
939
	file = nautilus_file_get (location);
940

941 942 943
	if (!file) {
		return NULL;
	}
Alexander Larsson's avatar
Alexander Larsson committed
944

945 946 947 948 949
	parent_file = nautilus_file_get_parent (file);
	nautilus_file_unref (file);
	while (parent_file) {
		NautilusSpatialWindow *parent_window;

Alexander Larsson's avatar
Alexander Larsson committed
950
		location = nautilus_file_get_location (parent_file);
951
		parent_window = nautilus_application_get_existing_spatial_window (location);
Alexander Larsson's avatar
Alexander Larsson committed
952 953
		g_object_unref (location);

954 955 956 957 958 959 960 961
		/* 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;
		}

962 963 964
		if (parent_window) {
			nautilus_file_unref (parent_file);
			return parent_window;
965
		}
966 967 968 969 970 971 972 973 974
		file = parent_file;
		parent_file = nautilus_file_get_parent (file);
		nautilus_file_unref (file);
	}

	return NULL;
}

void
975
nautilus_application_close_parent_windows (NautilusSpatialWindow *window)
976 977
{
	NautilusSpatialWindow *parent_window;
978
	NautilusSpatialWindow *new_parent_window;
979

980
	g_return_if_fail (NAUTILUS_IS_SPATIAL_WINDOW (window));
981

982 983 984
	parent_window = find_parent_spatial_window (window);
	
	while (parent_window) {
985 986 987 988
		
		new_parent_window = find_parent_spatial_window (parent_window);
		nautilus_window_close (NAUTILUS_WINDOW (parent_window));
		parent_window = new_parent_window;
989
	}
990
}
991

992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
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);
}

1011 1012 1013 1014
static void
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
{
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
1015 1016 1017
}

static gboolean
1018 1019 1020
nautilus_window_delete_event_callback (GtkWidget *widget,
				       GdkEvent *event,
				       gpointer user_data)
1021 1022
{
	NautilusWindow *window;
1023

1024
	window = NAUTILUS_WINDOW (widget);
1025
	nautilus_window_close (window);
1026

1027 1028
	return TRUE;
}				       
1029 1030


1031 1032 1033
static NautilusWindow *
create_window (NautilusApplication *application,
	       GType window_type,
1034
	       const char *startup_id,
1035
	       GdkScreen *screen)
1036
{
1037
	NautilusWindow *window;
1038
	
1039
	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
1040
	
1041
	window = NAUTILUS_WINDOW (gtk_widget_new (window_type,
1042
						  "app", application,
1043 1044
						  "screen", screen,
						  NULL));
Alexander Larsson's avatar
Alexander Larsson committed
1045 1046
	/* Must be called after construction finished */
	nautilus_window_constructed (window);
1047

1048 1049 1050 1051 1052
	unique_app_watch_window (application->unique_app, GTK_WINDOW (window));
	if (startup_id) {
		gtk_window_set_startup_id (GTK_WINDOW (window), startup_id);
	}
	
1053 1054 1055
	g_signal_connect_data (window, "delete_event",
			       G_CALLBACK (nautilus_window_delete_event_callback), NULL, NULL,
			       G_CONNECT_AFTER);
1056

1057 1058
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
1059

1060
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
1061

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