nautilus-application.c 42.2 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

James Willcox's avatar
James Willcox committed
32
#include "file-manager/fm-ditem-page.h"
33
#include "file-manager/fm-desktop-icon-view.h"
34
#include "file-manager/fm-icon-view.h"
35
#include "file-manager/fm-list-view.h"
36
#include "file-manager/fm-tree-view.h"
Alexander Larsson's avatar
Alexander Larsson committed
37 38
#include "nautilus-information-panel.h"
#include "nautilus-history-sidebar.h"
39
#include "nautilus-places-sidebar.h"
Alexander Larsson's avatar
Alexander Larsson committed
40 41 42
#include "nautilus-notes-viewer.h"
#include "nautilus-emblem-sidebar.h"
#include "nautilus-image-properties-page.h"
43 44 45
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
46 47
#include "nautilus-desktop-window.h"
#include "nautilus-first-time-druid.h"
48
#include "nautilus-main.h"
49 50
#include "nautilus-spatial-window.h"
#include "nautilus-navigation-window.h"
51 52
#include "nautilus-shell-interface.h"
#include "nautilus-shell.h"
53
#include "nautilus-window-bookmarks.h"
54
#include "nautilus-window-private.h"
55 56
#include <bonobo/bonobo-main.h>
#include <bonobo/bonobo-object.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
57 58 59 60
#include <eel/eel-gtk-macros.h>
#include <eel/eel-stock-dialogs.h>
#include <eel/eel-string-list.h>
#include <eel/eel-string.h>
61
#include <gdk/gdkx.h>
62
#include <gtk/gtkinvisible.h>
63
#include <gtk/gtksignal.h>
64
#include <gtk/gtkwindow.h>
65
#include <libgnome/gnome-config.h>
66
#include <glib/gi18n.h>
67
#include <libgnome/gnome-util.h>
68
#include <libgnomeui/gnome-authentication-manager.h>
69
#include <libgnomeui/gnome-client.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
70
#include <libgnomeui/gnome-messagebox.h>
71
#include <libgnomeui/gnome-stock-icons.h>
72
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
73
#include <libgnomevfs/gnome-vfs-ops.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
74
#include <libgnomevfs/gnome-vfs-utils.h>
Alexander Larsson's avatar
Alexander Larsson committed
75
#include <libgnomevfs/gnome-vfs-volume-monitor.h>
76 77 78 79
#include <libnautilus-private/nautilus-file-utilities.h>
#include <libnautilus-private/nautilus-global-preferences.h>
#include <libnautilus-private/nautilus-icon-factory.h>
#include <libnautilus-private/nautilus-metafile-factory.h>
80
#include <libnautilus-private/nautilus-module.h>
81
#include <libnautilus-private/nautilus-undo-manager.h>
Alexander Larsson's avatar
Alexander Larsson committed
82
#include <libnautilus-private/nautilus-desktop-link-monitor.h>
83
#include <libnautilus-private/nautilus-directory-private.h>
84
#include <bonobo-activation/bonobo-activation.h>
85
#ifdef HAVE_STARTUP_NOTIFICATION
86 87
#define SN_API_NOT_YET_FROZEN Yes_i_know_DO_IT
#include <libsn/sn-launchee.h>
88
#endif
89

90 91 92 93
/* Needed for the is_kdesktop_present check */
#include <gdk/gdkx.h>
#include <X11/Xlib.h>

94 95 96
#define FACTORY_IID	     "OAFIID:Nautilus_Factory"
#define SEARCH_LIST_VIEW_IID "OAFIID:Nautilus_File_Manager_Search_List_View"
#define SHELL_IID	     "OAFIID:Nautilus_Shell"
97
#define TREE_VIEW_IID         "OAFIID:Nautilus_File_Manager_Tree_View"
98

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

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

105 106 107
/* Keeps track of all the object windows */
static GList *nautilus_application_spatial_window_list;

108 109
static void     desktop_changed_callback          (gpointer                  user_data);
static void     desktop_location_changed_callback (gpointer                  user_data);
Alexander Larsson's avatar
Alexander Larsson committed
110 111
static void     volume_unmounted_callback         (GnomeVFSVolumeMonitor    *monitor,
						   GnomeVFSVolume           *volume,
112
						   NautilusApplication      *application);
Alexander Larsson's avatar
Alexander Larsson committed
113 114 115
static void     volume_mounted_callback           (GnomeVFSVolumeMonitor    *monitor,
						   GnomeVFSVolume           *volume,
						   NautilusApplication      *application);
116 117 118 119
static void     update_session                    (gpointer                  callback_data);
static void     init_session                      (void);
static gboolean is_kdesktop_present               (void);

120
BONOBO_CLASS_BOILERPLATE (NautilusApplication, nautilus_application,
121
			  BonoboGenericFactory, BONOBO_TYPE_GENERIC_FACTORY)
122 123

static CORBA_Object
124 125 126
create_object (PortableServer_Servant servant,
	       const CORBA_char *iid,
	       CORBA_Environment *ev)
127
{
128 129
	BonoboObject *object;
	NautilusApplication *application;
130

Alexander Larsson's avatar
Alexander Larsson committed
131
	if (strcmp (iid, SHELL_IID) == 0) {
132
		application = NAUTILUS_APPLICATION (bonobo_object_from_servant (servant));
133
		object = BONOBO_OBJECT (nautilus_shell_new (application));
134 135
	} else if (strcmp (iid, METAFILE_FACTORY_IID) == 0) {
		object = BONOBO_OBJECT (nautilus_metafile_factory_get_instance ());
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
136
	} else {
137
		object = CORBA_OBJECT_NIL;
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
138
	}
139

140
	return CORBA_Object_duplicate (BONOBO_OBJREF (object), ev);
141 142
}

143 144
GList *
nautilus_application_get_window_list (void)
145 146 147 148
{
	return nautilus_application_window_list;
}

149 150 151 152 153 154
GList *
nautilus_application_get_spatial_window_list (void)
{
	return nautilus_application_spatial_window_list;
}

155 156 157 158 159 160
unsigned int
nautilus_application_get_n_windows (void)
{
	return g_list_length (nautilus_application_window_list) +
	       g_list_length (nautilus_application_desktop_windows);
}
161

162
static void
163
nautilus_application_instance_init (NautilusApplication *application)
164
{
165 166
	/* Create an undo manager */
	application->undo_manager = nautilus_undo_manager_new ();
167

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

	/* Watch for volume unmounts so we can close open windows */
Alexander Larsson's avatar
Alexander Larsson committed
173
	g_signal_connect_object (gnome_vfs_get_volume_monitor (), "volume_unmounted",
174
				 G_CALLBACK (volume_unmounted_callback), application, 0);
175 176
	g_signal_connect_object (gnome_vfs_get_volume_monitor (), "volume_pre_unmount",
				 G_CALLBACK (volume_unmounted_callback), application, 0);
Alexander Larsson's avatar
Alexander Larsson committed
177 178
	g_signal_connect_object (gnome_vfs_get_volume_monitor (), "volume_mounted",
				 G_CALLBACK (volume_mounted_callback), application, 0);
179

Alexander Larsson's avatar
Alexander Larsson committed
180 181 182 183 184 185
	/* register views */
	fm_icon_view_register ();
	fm_desktop_icon_view_register ();
	fm_list_view_register ();

	/* register sidebars */
186
	nautilus_places_sidebar_register ();
Alexander Larsson's avatar
Alexander Larsson committed
187 188 189 190 191 192 193 194
	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 ();
195 196
}

197 198
NautilusApplication *
nautilus_application_new (void)
199
{
200 201 202 203 204 205 206 207 208
	NautilusApplication *application;

	application = g_object_new (NAUTILUS_TYPE_APPLICATION, NULL);
	
	bonobo_generic_factory_construct_noreg (BONOBO_GENERIC_FACTORY (application),
						FACTORY_IID,
						NULL);
	
	return application;
209 210 211
}

static void
212
nautilus_application_destroy (BonoboObject *object)
213
{
214 215 216 217
	NautilusApplication *application;

	application = NAUTILUS_APPLICATION (object);

218
	nautilus_bookmarks_exiting ();
219
	
Alexander Larsson's avatar
Alexander Larsson committed
220
	g_object_unref (application->undo_manager);
221

222
	EEL_CALL_PARENT (BONOBO_OBJECT_CLASS, destroy, (object));
223
}
224

225 226
static gboolean
check_required_directories (NautilusApplication *application)
227
{
228 229 230 231 232
	char *user_directory;
	char *desktop_directory;
	EelStringList *directories;
	char *directories_as_string;
	char *error_string;
233
	char *detail_string;
234
	GtkDialog *dialog;
235
	int failed_count;
236
	
237
	g_assert (NAUTILUS_IS_APPLICATION (application));
238 239 240 241

	user_directory = nautilus_get_user_directory ();
	desktop_directory = nautilus_get_desktop_directory ();

242
	directories = eel_string_list_new (TRUE);
243
	
244
	if (!g_file_test (user_directory, G_FILE_TEST_IS_DIR)) {
245
		eel_string_list_insert (directories, user_directory);
246
	}
247
	g_free (user_directory);	    
248
	    
249
	if (!g_file_test (desktop_directory, G_FILE_TEST_IS_DIR)) {
250
		eel_string_list_insert (directories, desktop_directory);
251
	}
252
	g_free (desktop_directory);
253

254
	failed_count = eel_string_list_get_length (directories);
255

256
	if (failed_count != 0) {
257
		directories_as_string = eel_string_list_as_string (directories, ", ", EEL_STRING_LIST_ALL_STRINGS);
258

259
		if (failed_count == 1) {
260
			error_string = g_strdup_printf (_("Nautilus could not create the required folder \"%s\"."),
261
							directories_as_string);
262 263
			detail_string = _("Before running Nautilus, please create the following folder, or "
					  "set permissions such that Nautilus can create it.");
264
		} else {
265 266 267 268
			error_string = g_strdup_printf (_("Nautilus could not create the following required folders: "
							  "%s."), directories_as_string);
  			detail_string = _("Before running Nautilus, please create these folders, or "
					  "set permissions such that Nautilus can create them.");
269 270
		}
		
271
		dialog = eel_show_error_dialog (error_string, detail_string, NULL);
272 273
		/* We need the main event loop so the user has a chance to see the dialog. */
		nautilus_main_event_loop_register (GTK_OBJECT (dialog));
274

275
		g_free (directories_as_string);
276 277 278
		g_free (error_string);
	}

279
	eel_string_list_free (directories);
280 281

	return failed_count == 0;
282 283
}

284
static Nautilus_URIList *
285
nautilus_make_uri_list_from_shell_strv (const char * const *strv)
286 287 288
{
	int length, i;
	Nautilus_URIList *uri_list;
289
	char *translated_uri;
290

291
	length = g_strv_length ((char **) strv);
292 293 294 295 296 297

	uri_list = Nautilus_URIList__alloc ();
	uri_list->_maximum = length;
	uri_list->_length = length;
	uri_list->_buffer = CORBA_sequence_Nautilus_URI_allocbuf (length);
	for (i = 0; i < length; i++) {
298
		translated_uri = gnome_vfs_make_uri_from_shell_arg (strv[i]);
299 300 301
		uri_list->_buffer[i] = CORBA_string_dup (translated_uri);
		g_free (translated_uri);
		translated_uri = NULL;
302 303 304 305 306 307
	}
	CORBA_sequence_set_release (uri_list, CORBA_TRUE);

	return uri_list;
}

308 309 310
static void
migrate_old_nautilus_files (void)
{
311 312 313 314 315 316
	char *new_desktop_dir;
	char *old_desktop_dir;
	char *migrated_file;
	char *link_name;
	char *link_path;
	int fd;
317
	
318
	old_desktop_dir = nautilus_get_gmc_desktop_directory ();
319 320
	if (!g_file_test (old_desktop_dir, G_FILE_TEST_IS_DIR) ||
	    g_file_test (old_desktop_dir, G_FILE_TEST_IS_SYMLINK)) {
321 322 323
		g_free (old_desktop_dir);
		return;
	}
324 325 326
	migrated_file = g_build_filename (old_desktop_dir, ".migrated", NULL);
	if (!g_file_test (migrated_file, G_FILE_TEST_EXISTS)) {
		link_name = g_filename_from_utf8 (_("Link To Old Desktop"), -1, NULL, NULL, NULL);
327
		new_desktop_dir = nautilus_get_desktop_directory ();
328
		link_path = g_build_filename (new_desktop_dir, link_name, NULL);
329
	
330 331 332 333
		
		symlink ("../.gnome-desktop", link_path);
		
		g_free (link_name);
334
		g_free (new_desktop_dir);
335
		g_free (link_path);
336

337 338 339 340 341
		fd = creat (migrated_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
		if (fd >= 0) {
			close (fd);
		}
		
342 343 344
		eel_show_info_dialog (_("A link called \"Link To Old Desktop\" has been created on the desktop."),
				      _("The location of the desktop directory has changed in GNOME 2.4. "
					"You can open the link and move over the files you want, then delete the link."),
345 346
				      NULL);
	}
347
	g_free (old_desktop_dir);
348
	g_free (migrated_file);
349 350
}

351 352 353
static void
finish_startup (NautilusApplication *application)
{
354 355 356
	/* initialize nautilus modules */
	nautilus_module_init ();

James Willcox's avatar
James Willcox committed
357
	nautilus_module_add_type (FM_TYPE_DITEM_PAGE);
358
	
359
	/* initialize URI authentication manager */
360
	gnome_authentication_manager_init ();
361

362
	/* Make the desktop work with old Nautilus. */
363
	migrate_old_nautilus_files ();
Alexander Larsson's avatar
Alexander Larsson committed
364 365 366

	/* Initialize the desktop link monitor singleton */
	nautilus_desktop_link_monitor_get ();
367 368
}

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
static void
initialize_kde_trash_hack (void)
{
	char *trash_dir;
	char *desktop_dir, *desktop_uri, *kde_trash_dir;
	char *dir, *basename;
	char *kde_conf_file;
	char *key;
	gboolean def;
	
	trash_dir = NULL;

	desktop_uri = nautilus_get_desktop_directory_uri_no_create ();
	desktop_dir = gnome_vfs_get_local_path_from_uri (desktop_uri);
	g_free (desktop_uri);
	
	if (g_file_test (desktop_dir, G_FILE_TEST_EXISTS)) {
		/* Look for trash directory */
		kde_conf_file = g_build_filename (g_get_home_dir(), ".kde/share/config/kdeglobals", NULL);
		key = g_strconcat ("=", kde_conf_file, "=/Paths/Trash", NULL);
		kde_trash_dir = gnome_config_get_string_with_default (key, &def);
		gnome_config_drop_file (kde_conf_file);
		g_free (kde_conf_file);
		g_free (key);

394 395 396 397 398 399 400 401
		if (kde_trash_dir == NULL) {
			kde_conf_file = "/usr/share/config/kdeglobals";
			key = g_strconcat ("=", kde_conf_file, "=/Paths/Trash", NULL);
			kde_trash_dir = gnome_config_get_string_with_default (key, &def);
			gnome_config_drop_file (kde_conf_file);
			g_free (key);
		}

402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
		if (kde_trash_dir != NULL) {
			basename = g_path_get_basename (kde_trash_dir);
			g_free (kde_trash_dir);
			
			dir = g_build_filename (desktop_dir, basename, NULL);

			if (g_file_test (dir, G_FILE_TEST_IS_DIR)) {
				trash_dir = g_strdup (basename);
			} 
			g_free (basename);
			g_free (dir);
		} 

		if (trash_dir != NULL) {
			nautilus_set_kde_trash_name (trash_dir);
		}

		g_free (trash_dir);
	}
	g_free (desktop_dir);
}

Alexander Larsson's avatar
Alexander Larsson committed
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440

static Bonobo_RegistrationResult
nautilus_bonobo_activation_register_for_display (const char    *iid,
						 Bonobo_Unknown ref)
{
	const char *display_name;
	GSList *reg_env ;
	Bonobo_RegistrationResult result;
	
	display_name = gdk_display_get_name (gdk_display_get_default());
	reg_env = bonobo_activation_registration_env_set (NULL,
							  "DISPLAY", display_name);
	result = bonobo_activation_register_active_server (iid, ref, reg_env);
	bonobo_activation_registration_env_free (reg_env);
	return result;
}

441
void
442
nautilus_application_startup (NautilusApplication *application,
443
			      gboolean kill_shell,
444
			      gboolean restart_shell,
445
			      gboolean no_default_window,
446
			      gboolean no_desktop,
447
			      gboolean do_first_time_druid_check,
Alexander Larsson's avatar
Alexander Larsson committed
448
			      gboolean browser_window,
449
			      const char *startup_id,
450
			      const char *geometry,
451
			      const char *urls[])
452
{
453 454
	CORBA_Environment ev;
	Nautilus_Shell shell;
455
	Bonobo_RegistrationResult result;
456
	const char *message, *detailed_message;
457
	GtkDialog *dialog;
458
	Nautilus_URIList *url_list;
459
	const CORBA_char *corba_startup_id;
460
	const CORBA_char *corba_geometry;
461 462 463
	int num_failures;

	num_failures = 0;
464

465
	/* Check the user's ~/.nautilus directories and post warnings
466
	 * if there are problems.
467
	 */
468
	if (!kill_shell && !check_required_directories (application)) {
469 470
		return;
	}
471

472 473
	initialize_kde_trash_hack ();

474 475
	CORBA_exception_init (&ev);

476
	/* Start up the factory. */
477
	while (TRUE) {
478
		/* Try to register the file manager view factory. */
479
		result = nautilus_bonobo_activation_register_for_display
480
			(FACTORY_IID, BONOBO_OBJREF (application));
481

482
		switch (result) {
483 484
		case Bonobo_ACTIVATION_REG_SUCCESS:
			/* We are registered and all is right with the world. */
485
			finish_startup (application);
486
		case Bonobo_ACTIVATION_REG_ALREADY_ACTIVE:
487
			/* Another copy of nautilus already is running and registered. */
488
			message = NULL;
489
			detailed_message = NULL;
490
			break;
491
		case Bonobo_ACTIVATION_REG_NOT_LISTED:
492
			/* Can't register myself due to trouble locating the
493
			 * Nautilus_Shell.server file. This has happened when you
494
			 * launch Nautilus with an LD_LIBRARY_PATH that
495
			 * doesn't include the directory containing the oaf
496
			 * library. It could also happen if the
497
			 * Nautilus_Shell.server file was not present for some
498 499 500 501 502
			 * reason. Sometimes killing oafd and gconfd fixes
			 * this problem but we don't exactly understand why,
			 * since neither of the above causes explain it.
			 */
			message = _("Nautilus can't be used now. "
503
				    "Running the command \"bonobo-slay\""
504 505 506
				    " from the console may fix the problem. If not,"
				    " you can try rebooting the computer or"
				    " installing Nautilus again.");
507
			/* FIXME bugzilla.gnome.org 42536: The guesses and stuff here are lame. */
508
			detailed_message = _("Nautilus can't be used now. "
509
					     "Running the command \"bonobo-slay\" "
510 511 512 513
					     "from the console may fix the problem. If not, "
					     "you can try rebooting the computer or "
					     "installing Nautilus again.\n\n"
					     "Bonobo couldn't locate the Nautilus_shell.server file. "
514
					     "One cause of this seems to be an LD_LIBRARY_PATH "
515
					     "that does not include the bonobo-activation library's directory. "
516
					     "Another possible cause would be bad install "
517
					     "with a missing Nautilus_Shell.server file.\n\n"
518
					     "Running \"bonobo-slay\" will kill all "
519
					     "Bonobo Activation and GConf processes, which may be needed by "
520
					     "other applications.\n\n"
521
					     "Sometimes killing bonobo-activation-server and gconfd fixes "
522
					     "the problem, but we don't know why.\n\n"
523
					     "We have also seen this error when a faulty "
524
					     "version of bonobo-activation was installed.");
525 526 527
			break;
		default:
			/* This should never happen. */
528
			g_warning ("bad error code from bonobo_activation_active_server_register");
529
		case Bonobo_ACTIVATION_REG_ERROR:
530
			/* Some misc. error (can never happen with current
531
			 * version of bonobo-activation). Show dialog and terminate the
532 533
			 * program.
			 */
534
			/* FIXME bugzilla.gnome.org 42537: Looks like this does happen with the
535
			 * current OAF. I guess I read the code wrong. Need to figure out when and make a
536 537
			 * good message.
			 */
538 539
			message = _("Nautilus can't be used now, due to an unexpected error.");
			detailed_message = _("Nautilus can't be used now, due to an unexpected error "
540
					     "from Bonobo when attempting to register the file manager view server.");
541 542
			break;
		}
543 544 545

		/* Get the shell object. */
		if (message == NULL) {
546
			shell = bonobo_activation_activate_from_id (SHELL_IID, 0, NULL, NULL);
547 548 549 550 551 552 553
			if (!CORBA_Object_is_nil (shell, &ev)) {
				break;
			}

			/* If we couldn't find ourselves it's a bad problem so
			 * we better stop looping.
			 */
554
			if (result == Bonobo_ACTIVATION_REG_SUCCESS) {
555
				/* FIXME bugzilla.gnome.org 42538: When can this happen? */
556 557
				message = _("Nautilus can't be used now, due to an unexpected error.");
				detailed_message = _("Nautilus can't be used now, due to an unexpected error "
558 559
						     "from Bonobo when attempting to locate the factory."
						     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
560 561 562 563 564
			} else {
				num_failures++;
				if (num_failures > 20) {
					message = _("Nautilus can't be used now, due to an unexpected error.");
					detailed_message = _("Nautilus can't be used now, due to an unexpected error "
565 566
							     "from Bonobo when attempting to locate the shell object. "
							     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
567 568
					
				}
569 570 571
			}
		}

572
		if (message != NULL) {
573
			dialog = eel_show_error_dialog_with_details (message, NULL, detailed_message, NULL);
574 575
			/* We need the main event loop so the user has a chance to see the dialog. */
			nautilus_main_event_loop_register (GTK_OBJECT (dialog));
576
			goto out;
577 578 579
		}
	}

580 581
	if (kill_shell) {
		Nautilus_Shell_quit (shell, &ev);
582 583
	} else if (restart_shell) {
		Nautilus_Shell_restart (shell, &ev);
584
	} else {
585
		/* If KDE desktop is running, then force no_desktop */
586
		if (is_kdesktop_present ()) {
587
			no_desktop = TRUE;
588
		}
589
		
590
		if (!no_desktop && eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
591 592
			Nautilus_Shell_start_desktop (shell, &ev);
		}
593 594
		
		/* Monitor the preference to show or hide the desktop */
595
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_SHOW_DESKTOP,
Darin Adler's avatar
Darin Adler committed
596 597 598
							  desktop_changed_callback,
							  application,
							  G_OBJECT (application));
599

600 601
		/* Monitor the preference to have the desktop */
		/* point to the Unix home folder */
602
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
Darin Adler's avatar
Darin Adler committed
603 604 605
							  desktop_location_changed_callback,
							  NULL,
							  G_OBJECT (application));
606

607 608
		/* CORBA C mapping doesn't allow NULL to be passed
		   for string parameters */
609 610
		corba_geometry   = (geometry   != NULL) ? geometry   : "";
		corba_startup_id = (startup_id != NULL) ? startup_id : "";
611

612 613
	  	/* Create the other windows. */
		if (urls != NULL) {
614
			url_list = nautilus_make_uri_list_from_shell_strv (urls);
615
			Nautilus_Shell_open_windows (shell, url_list, corba_startup_id, corba_geometry, browser_window, &ev);
616
			CORBA_free (url_list);
617
		} else if (!no_default_window) {
618
			Nautilus_Shell_open_default_window (shell, corba_startup_id, corba_geometry, browser_window, &ev);
619
		}
620 621 622
		
		/* Add ourselves to the session */
		init_session ();
623
	}
624

625
	/* We're done with the shell now, so let it go. */
626 627 628 629 630
	/* HACK: Don't bother releasing the shell in the case where we
	 * just told it to quit -- that just leads to hangs and does
	 * no good. We could probably fix this in some fancier way if
	 * we could figure out a better lifetime rule.
	 */
631
	if (!(kill_shell || restart_shell)) {
632
		bonobo_object_release_unref (shell, NULL);
633
	}
634

635
 out:
636
	CORBA_exception_free (&ev);
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 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706

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)
{
	char *selection_name;
	GdkAtom selection_atom;
	Window selection_owner;
	GtkWidget *selection_widget;

	selection_name = g_strdup_printf ("_NET_DESKTOP_MANAGER_S%d", screen);
	selection_atom = gdk_atom_intern (selection_name, FALSE);
	g_free (selection_name);

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

707
static void
708
nautilus_application_create_desktop_windows (NautilusApplication *application)
709
{
710
	static gboolean create_in_progress = FALSE;
711 712
	GdkDisplay *display;
	NautilusDesktopWindow *window;
713
	GtkWidget *selection_widget;
714
	int screens, i;
715

716
	g_return_if_fail (nautilus_application_desktop_windows == NULL);
717
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
718

719 720 721 722 723 724
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

725 726 727 728
	display = gdk_display_get_default ();
	screens = gdk_display_get_n_screens (display);

	for (i = 0; i < screens; i++) {
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
		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);
		}
750
	}
751 752

	create_in_progress = FALSE;
753 754 755 756
}

void
nautilus_application_open_desktop (NautilusApplication *application)
757
{
758 759
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
760 761 762 763 764 765
	}
}

void
nautilus_application_close_desktop (void)
{
766 767 768 769 770
	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;
771
	}
772 773
}

774
void
775
nautilus_application_close_all_navigation_windows (void)
776
{
777 778 779 780 781 782 783 784 785 786 787 788
	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);
		}
789
	}
790
	g_list_free (list_copy);
791 792
}

793 794
static NautilusSpatialWindow *
nautilus_application_get_existing_spatial_window (const char *location)
795
{
796 797 798 799 800 801 802
	GList *l;
	
	for (l = nautilus_application_get_spatial_window_list ();
	     l != NULL; l = l->next) {
		char *window_location;
		
		window_location = nautilus_window_get_location (NAUTILUS_WINDOW (l->data));
803 804
		if (window_location != NULL &&
		    strcmp (location, window_location) == 0) {
805 806 807 808 809 810
			g_free (window_location);
			return NAUTILUS_SPATIAL_WINDOW (l->data);
		}
		g_free (window_location);
	}
	return NULL;
811 812
}

813 814
static NautilusSpatialWindow *
find_parent_spatial_window (NautilusSpatialWindow *window)
815
{
816 817 818 819
	NautilusFile *file;
	NautilusFile *parent_file;
	char *location;
	char *desktop_directory;
820

821
	location = nautilus_window_get_location (NAUTILUS_WINDOW (window));
Alexander Larsson's avatar
Alexander Larsson committed
822 823 824
	if (location == NULL) {
		return NULL;
	}
825 826
	file = nautilus_file_get (location);
	g_free (location);
827

828 829 830
	if (!file) {
		return NULL;
	}
831
	
832
	desktop_directory = nautilus_get_desktop_directory_uri ();	
833
	
834 835 836 837
	parent_file = nautilus_file_get_parent (file);
	nautilus_file_unref (file);
	while (parent_file) {
		NautilusSpatialWindow *parent_window;
838
		
839 840 841 842 843 844 845 846 847
		location = nautilus_file_get_uri (parent_file);

		/* Stop at the desktop directory, as this is the
		 * conceptual root of the spatial windows */
		if (!strcmp (location, desktop_directory)) {
			g_free (location);
			g_free (desktop_directory);
			nautilus_file_unref (parent_file);
			return NULL;
848
		}
849 850 851 852 853 854 855

		parent_window = nautilus_application_get_existing_spatial_window (location);
		g_free (location);
		
		if (parent_window) {
			nautilus_file_unref (parent_file);
			return parent_window;
856
		}
857 858 859 860 861 862 863 864 865 866
		file = parent_file;
		parent_file = nautilus_file_get_parent (file);
		nautilus_file_unref (file);
	}
	g_free (desktop_directory);

	return NULL;
}

void
867
nautilus_application_close_paren