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

32
#include "file-manager/fm-desktop-icon-view.h"
33
#include "file-manager/fm-icon-view.h"
34
#include "file-manager/fm-list-view.h"
35
#include "file-manager/fm-search-list-view.h"
36 37 38
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
39 40
#include "nautilus-desktop-window.h"
#include "nautilus-first-time-druid.h"
41
#include "nautilus-main.h"
42 43 44 45
#include "nautilus-shell-interface.h"
#include "nautilus-shell.h"
#include <bonobo/bonobo-main.h>
#include <bonobo/bonobo-object.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
46 47 48 49 50
#include <eel/eel-gtk-macros.h>
#include <eel/eel-stock-dialogs.h>
#include <eel/eel-string-list.h>
#include <eel/eel-string.h>
#include <eel/eel-vfs-extensions.h>
51
#include <eel/eel-gtk-extensions.h>
52
#include <gdk/gdkx.h>
53
#include <gtk/gtkinvisible.h>
54
#include <gtk/gtksignal.h>
55
#include <libgnome/gnome-config.h>
56 57
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
58
#include <libgnomeui/gnome-authentication-manager.h>
59
#include <libgnomeui/gnome-client.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
60
#include <libgnomeui/gnome-messagebox.h>
61
#include <libgnomeui/gnome-stock-icons.h>
62
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
63
#include <libgnomevfs/gnome-vfs-ops.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
64
#include <libgnomevfs/gnome-vfs-utils.h>
65 66 67 68 69
#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>
#include <libnautilus-private/nautilus-sound.h>
70
#include <libnautilus-private/nautilus-bonobo-extensions.h>
71 72
#include <libnautilus-private/nautilus-undo-manager.h>
#include <libnautilus-private/nautilus-volume-monitor.h>
Alexander Larsson's avatar
Alexander Larsson committed
73
#include <libnautilus-private/nautilus-desktop-link-monitor.h>
74
#include <libnautilus-private/nautilus-directory-private.h>
75
#include <bonobo-activation/bonobo-activation.h>
76

77 78 79 80
/* Needed for the is_kdesktop_present check */
#include <gdk/gdkx.h>
#include <X11/Xlib.h>

81 82 83
#define FACTORY_IID	     "OAFIID:Nautilus_Factory"
#define SEARCH_LIST_VIEW_IID "OAFIID:Nautilus_File_Manager_Search_List_View"
#define SHELL_IID	     "OAFIID:Nautilus_Shell"
84

85 86
/* Keeps track of all the desktop windows. */
static GList *nautilus_application_desktop_windows;
87 88 89 90

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

91 92 93 94 95 96 97 98 99 100
static gboolean need_to_show_first_time_druid     (void);
static void     desktop_changed_callback          (gpointer                  user_data);
static void     desktop_location_changed_callback (gpointer                  user_data);
static void     volume_unmounted_callback         (NautilusVolumeMonitor    *monitor,
						   NautilusVolume           *volume,
						   NautilusApplication      *application);
static void     update_session                    (gpointer                  callback_data);
static void     init_session                      (void);
static gboolean is_kdesktop_present               (void);

101 102
BONOBO_CLASS_BOILERPLATE (NautilusApplication, nautilus_application,
			  BonoboGenericFactory, BONOBO_GENERIC_FACTORY_TYPE)
103 104

static CORBA_Object
105 106 107
create_object (PortableServer_Servant servant,
	       const CORBA_char *iid,
	       CORBA_Environment *ev)
108
{
109
	BonoboObject *object;
110
	FMDirectoryView *directory_view;
111
	NautilusApplication *application;
112

113
	if (strcmp (iid, NAUTILUS_ICON_VIEW_IID) == 0) {
114
		directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_icon_view_get_type (), NULL));
115
		object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
116
	} else if (strcmp (iid, NAUTILUS_DESKTOP_ICON_VIEW_IID) == 0) {
117
		directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_desktop_icon_view_get_type (), NULL));
118
		object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
119
	} else if (strcmp (iid, NAUTILUS_LIST_VIEW_IID) == 0) {
120
		directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_list_view_get_type (), NULL));
121
		object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
122
	} else if (strcmp (iid, SEARCH_LIST_VIEW_IID) == 0) {
123
		directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_search_list_view_get_type (), NULL));
124
		object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
125
	} else if (strcmp (iid, SHELL_IID) == 0) {
126
		application = NAUTILUS_APPLICATION (bonobo_object_from_servant (servant));
127
		object = BONOBO_OBJECT (nautilus_shell_new (application));
128 129
	} 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
130
	} else {
131
		object = CORBA_OBJECT_NIL;
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
132
	}
133

134
	return CORBA_Object_duplicate (BONOBO_OBJREF (object), ev);
135 136
}

137 138
GList *
nautilus_application_get_window_list (void)
139 140 141 142
{
	return nautilus_application_window_list;
}

143
static void
144
nautilus_application_instance_init (NautilusApplication *application)
145
{
146 147
	/* Create an undo manager */
	application->undo_manager = nautilus_undo_manager_new ();
148

149 150 151
	/* 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 */
152 153

	/* Watch for volume unmounts so we can close open windows */
154 155
	g_signal_connect_object (nautilus_volume_monitor_get (), "volume_unmounted",
				 G_CALLBACK (volume_unmounted_callback), application, 0);
156 157
}

158 159
NautilusApplication *
nautilus_application_new (void)
160
{
161 162 163 164 165 166 167 168 169
	NautilusApplication *application;

	application = g_object_new (NAUTILUS_TYPE_APPLICATION, NULL);
	
	bonobo_generic_factory_construct_noreg (BONOBO_GENERIC_FACTORY (application),
						FACTORY_IID,
						NULL);
	
	return application;
170 171 172
}

static void
173
nautilus_application_destroy (BonoboObject *object)
174
{
175 176 177 178
	NautilusApplication *application;

	application = NAUTILUS_APPLICATION (object);

179
	nautilus_bookmarks_exiting ();
180
	
181
	bonobo_object_unref (application->undo_manager);
182

183
	EEL_CALL_PARENT (BONOBO_OBJECT_CLASS, destroy, (object));
184
}
185

186 187
static gboolean
check_required_directories (NautilusApplication *application)
188
{
189 190 191 192 193 194
	char *user_directory;
	char *desktop_directory;
	EelStringList *directories;
	char *directories_as_string;
	char *error_string;
	char *dialog_title;
195
	GtkDialog *dialog;
196
	int failed_count;
197
	
198
	g_assert (NAUTILUS_IS_APPLICATION (application));
199 200 201 202

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

203
	directories = eel_string_list_new (TRUE);
204
	
205
	if (!g_file_test (user_directory, G_FILE_TEST_IS_DIR)) {
206
		eel_string_list_insert (directories, user_directory);
207
	}
208
	g_free (user_directory);	    
209
	    
210
	if (!g_file_test (desktop_directory, G_FILE_TEST_IS_DIR)) {
211
		eel_string_list_insert (directories, desktop_directory);
212
	}
213
	g_free (desktop_directory);
214

215
	failed_count = eel_string_list_get_length (directories);
216

217
	if (failed_count != 0) {
218
		directories_as_string = eel_string_list_as_string (directories, "\n", EEL_STRING_LIST_ALL_STRINGS);
219

220 221
		if (failed_count == 1) {
			dialog_title = g_strdup (_("Couldn't Create Required Folder"));
222 223 224
			error_string = g_strdup_printf (_("Nautilus could not create the required folder \"%s\". "
							  "Before running Nautilus, please create this folder, or "
							  "set permissions such that Nautilus can create it."),
225
							directories_as_string);
226 227
		} else {
			dialog_title = g_strdup (_("Couldn't Create Required Folders"));
228 229 230 231
			error_string = g_strdup_printf (_("Nautilus could not create the following required folders:\n\n"
							  "%s\n\n"
							  "Before running Nautilus, please create these folders, or "
							  "set permissions such that Nautilus can create them."),
232
							directories_as_string);
233 234
		}
		
Ramiro Estrugo's avatar
Ramiro Estrugo committed
235
		dialog = eel_show_error_dialog (error_string, dialog_title, NULL);
236 237
		/* We need the main event loop so the user has a chance to see the dialog. */
		nautilus_main_event_loop_register (GTK_OBJECT (dialog));
238

239
		g_free (directories_as_string);
240
		g_free (error_string);
241
		g_free (dialog_title);
242 243
	}

244
	eel_string_list_free (directories);
245 246

	return failed_count == 0;
247 248
}

249 250 251 252 253 254 255 256 257 258
static int
nautilus_strv_length (const char * const *strv)
{
	const char * const *p;

	for (p = strv; *p != NULL; p++) { }
	return p - strv;
}

static Nautilus_URIList *
259
nautilus_make_uri_list_from_shell_strv (const char * const *strv)
260 261 262
{
	int length, i;
	Nautilus_URIList *uri_list;
263
	char *translated_uri;
264 265 266 267 268 269 270 271

	length = nautilus_strv_length (strv);

	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++) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
272
		translated_uri = eel_make_uri_from_shell_arg (strv[i]);
273 274 275
		uri_list->_buffer[i] = CORBA_string_dup (translated_uri);
		g_free (translated_uri);
		translated_uri = NULL;
276 277 278 279 280 281
	}
	CORBA_sequence_set_release (uri_list, CORBA_TRUE);

	return uri_list;
}

282 283 284
static void
migrate_old_nautilus_files (void)
{
285 286 287 288 289 290
	char *new_desktop_dir;
	char *old_desktop_dir;
	char *migrated_file;
	char *link_name;
	char *link_path;
	int fd;
291
	
292
	old_desktop_dir = nautilus_get_gmc_desktop_directory ();
293 294
	if (!g_file_test (old_desktop_dir, G_FILE_TEST_IS_DIR) ||
	    g_file_test (old_desktop_dir, G_FILE_TEST_IS_SYMLINK)) {
295 296 297
		g_free (old_desktop_dir);
		return;
	}
298 299 300
	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);
301
		new_desktop_dir = nautilus_get_desktop_directory ();
302
		link_path = g_build_filename (new_desktop_dir, link_name, NULL);
303
	
304 305 306 307
		
		symlink ("../.gnome-desktop", link_path);
		
		g_free (link_name);
308
		g_free (new_desktop_dir);
309
		g_free (link_path);
310

311 312 313 314 315
		fd = creat (migrated_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
		if (fd >= 0) {
			close (fd);
		}
		
316
		eel_show_info_dialog (_("The location of the desktop directory has changed in GNOME 2.4. "
317 318 319 320 321 322
					"A link called \"Link To Old Desktop\" has been created on the desktop. "
					"You can open this to move over the files you want, then delete the link."),
				      _("Migrated old desktop"),
				      NULL);
	}
	g_free (migrated_file);
323 324
}

325 326 327 328 329 330 331 332 333
static gint
create_starthere_link_callback (gpointer data)
{
	char *desktop_path;
	char *desktop_link_file;
	char *cmd;
	
	/* Create default services icon on the desktop */
	desktop_path = nautilus_get_desktop_directory ();
334 335 336
	desktop_link_file = g_build_filename (desktop_path,
					      "starthere.desktop",
					      NULL);
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354

	cmd = g_strconcat ("/bin/cp ",
			   NAUTILUS_DATADIR,
			   "/starthere-link.desktop ",
			   desktop_link_file,
			   NULL);

	if (system (cmd) != 0) {
		g_warning ("Failed to execute command '%s'\n", cmd);
	}
	
	g_free (desktop_path);
	g_free (desktop_link_file);
	g_free (cmd);
	
	return FALSE;
}

355 356 357 358
static void
finish_startup (NautilusApplication *application)
{
	/* initialize the sound machinery */
359
	nautilus_sound_init ();
360

361
	/* initialize URI authentication manager */
362
	gnome_authentication_manager_init ();
363

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

	/* Initialize the desktop link monitor singleton */
	nautilus_desktop_link_monitor_get ();
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 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
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);

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

418
void
419
nautilus_application_startup (NautilusApplication *application,
420
			      gboolean kill_shell,
421
			      gboolean restart_shell,
422
			      gboolean no_default_window,
423
			      gboolean no_desktop,
424
			      gboolean do_first_time_druid_check,
425
			      const char *geometry,
426
			      const char *urls[])
427
{
428 429
	CORBA_Environment ev;
	Nautilus_Shell shell;
430
	Bonobo_RegistrationResult result;
431
	const char *message, *detailed_message;
432
	GtkDialog *dialog;
433
	Nautilus_URIList *url_list;
434
	const CORBA_char *corba_geometry;
435 436 437
	int num_failures;

	num_failures = 0;
438

439
	/* Check the user's ~/.nautilus directories and post warnings
440
	 * if there are problems.
441
	 */
442
	if (!kill_shell && !check_required_directories (application)) {
443 444
		return;
	}
445

446 447
	/* Run the first time startup druid if needed. */
	if (do_first_time_druid_check && need_to_show_first_time_druid ()) {
448 449 450 451 452 453
		/* Do this at idle time, once nautilus has initialized
		 * itself. Otherwise we may spawn a second nautilus
		 * process when looking for a metadata factory..
		 */
		g_idle_add (create_starthere_link_callback, NULL);
		nautilus_set_first_time_file_flag ();
454
	}
455

456 457
	initialize_kde_trash_hack ();

458 459
	CORBA_exception_init (&ev);

460
	/* Start up the factory. */
461
	while (TRUE) {
462
		/* Try to register the file manager view factory. */
463
		result = nautilus_bonobo_activation_register_for_display
464
			(FACTORY_IID, BONOBO_OBJREF (application));
465

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

		/* Get the shell object. */
		if (message == NULL) {
530
			shell = bonobo_activation_activate_from_id (SHELL_IID, 0, NULL, NULL);
531 532 533 534 535 536 537
			if (!CORBA_Object_is_nil (shell, &ev)) {
				break;
			}

			/* If we couldn't find ourselves it's a bad problem so
			 * we better stop looping.
			 */
538
			if (result == Bonobo_ACTIVATION_REG_SUCCESS) {
539
				/* FIXME bugzilla.gnome.org 42538: When can this happen? */
540 541
				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 "
542 543
						     "from Bonobo when attempting to locate the factory."
						     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
544 545 546 547 548
			} 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 "
549 550
							     "from Bonobo when attempting to locate the shell object. "
							     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
551 552
					
				}
553 554 555
			}
		}

556
		if (message != NULL) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
557
			dialog = eel_show_error_dialog_with_details (message, NULL, detailed_message, NULL);
558 559
			/* We need the main event loop so the user has a chance to see the dialog. */
			nautilus_main_event_loop_register (GTK_OBJECT (dialog));
560
			goto out;
561 562 563
		}
	}

564 565
	if (kill_shell) {
		Nautilus_Shell_quit (shell, &ev);
566 567
	} else if (restart_shell) {
		Nautilus_Shell_restart (shell, &ev);
568
	} else {
569
		/* If KDE desktop is running, then force no_desktop */
570
		if (is_kdesktop_present ()) {
571
			no_desktop = TRUE;
572
		}
573
		
574
		if (!no_desktop && eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
575 576
			Nautilus_Shell_start_desktop (shell, &ev);
		}
577 578
		
		/* Monitor the preference to show or hide the desktop */
579
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_SHOW_DESKTOP,
Darin Adler's avatar
Darin Adler committed
580 581 582
							  desktop_changed_callback,
							  application,
							  G_OBJECT (application));
583

584 585
		/* Monitor the preference to have the desktop */
		/* point to the Unix home folder */
586
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
Darin Adler's avatar
Darin Adler committed
587 588 589
							  desktop_location_changed_callback,
							  NULL,
							  G_OBJECT (application));
590

591 592 593 594
		/* CORBA C mapping doesn't allow NULL to be passed
		   for string parameters */
		corba_geometry = (geometry != NULL) ? geometry : "";

595 596
	  	/* Create the other windows. */
		if (urls != NULL) {
597
			url_list = nautilus_make_uri_list_from_shell_strv (urls);
598
			Nautilus_Shell_open_windows (shell, url_list, corba_geometry, &ev);
599
			CORBA_free (url_list);
600
		} else if (!no_default_window) {
601
			Nautilus_Shell_open_default_window (shell, corba_geometry, &ev);
602
		}
603 604 605
		
		/* Add ourselves to the session */
		init_session ();
606
	}
607

608
	/* We're done with the shell now, so let it go. */
609 610 611 612 613
	/* 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.
	 */
614
	if (!(kill_shell || restart_shell)) {
615
		bonobo_object_release_unref (shell, NULL);
616
	}
617

618
 out:
619
	CORBA_exception_free (&ev);
620 621
}

622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 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

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

690
static void
691
nautilus_application_create_desktop_windows (NautilusApplication *application)
692
{
693
	static gboolean create_in_progress = FALSE;
694 695
	GdkDisplay *display;
	NautilusDesktopWindow *window;
696
	GtkWidget *selection_widget;
697
	int screens, i;
698

699
	g_return_if_fail (nautilus_application_desktop_windows == NULL);
700
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
701

702 703 704 705 706 707
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

708 709 710 711
	display = gdk_display_get_default ();
	screens = gdk_display_get_n_screens (display);

	for (i = 0; i < screens; i++) {
712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
		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);
		}
733
	}
734 735

	create_in_progress = FALSE;
736 737 738 739
}

void
nautilus_application_open_desktop (NautilusApplication *application)
740
{
741 742
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
743 744 745 746 747 748
	}
}

void
nautilus_application_close_desktop (void)
{
749 750 751 752 753
	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;
754
	}
755 756
}

757 758 759
void
nautilus_application_close_all_windows (void)
{
760
	while (nautilus_application_window_list != NULL) {
761 762 763 764
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

765
static void
766
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
767
{
768
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
769 770
}

771 772 773 774 775 776 777 778 779 780 781 782 783
static gboolean
nautilus_window_delete_event_callback (GtkWidget *widget,
				       GdkEvent *event,
				       gpointer user_data)
{
	NautilusWindow *window;

	window = NAUTILUS_WINDOW (widget);
	nautilus_window_close (window);

	return TRUE;
}				       

784
static gboolean
785
save_window_geometry_timeout (gpointer callback_data)
786 787 788 789 790 791
{
	NautilusWindow *window;
	
	window = NAUTILUS_WINDOW (callback_data);
	
	nautilus_window_save_geometry (window);
792 793

	window->save_geometry_timeout_id = 0;
794 795 796 797 798 799 800 801 802
	return FALSE;
}
 
static gboolean
nautilus_window_configure_event_callback (GtkWidget *widget,
						GdkEventConfigure *event,
						gpointer callback_data)
{
	NautilusWindow *window;
803
	char *geometry_string;
804 805 806
	
	window = NAUTILUS_WINDOW (widget);
	
807 808
	/* Only save the geometry if the user hasn't resized the window
	 * for half a second. Otherwise delay the callback another half second.
809
	 */
810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
	if (window->save_geometry_timeout_id != 0) {
		g_source_remove (window->save_geometry_timeout_id);	
	}
	if (GTK_WIDGET_VISIBLE (GTK_WIDGET (window)) && !NAUTILUS_IS_DESKTOP_WINDOW (window)) {
		
		geometry_string = eel_gtk_window_get_geometry_string (GTK_WINDOW (window));
	
		/* If the last geometry is NULL the window must have just
		 * been shown. No need to save geometry to disk since it
		 * must be the same.
		 */
		if (window->last_geometry == NULL) {
			window->last_geometry = geometry_string;
			return FALSE;
		}
	
		/* Don't save geometry if it's the same as before. */
		if (!strcmp (window->last_geometry, geometry_string)) {
			g_free (geometry_string);
			return FALSE;
		}

		g_free (window->last_geometry);
		window->last_geometry = geometry_string;

		window->save_geometry_timeout_id = 
				g_timeout_add (500, save_window_geometry_timeout, window);
837 838 839 840 841 842 843 844 845 846 847 848 849 850
	}

	return FALSE;
}

static gboolean
nautilus_window_unrealize_event_callback (GtkWidget *widget,
						GdkEvent *event,
						gpointer callback_data)
{
	NautilusWindow *window;
	
	window = NAUTILUS_WINDOW (widget);

851 852 853
	if (window->save_geometry_timeout_id != 0) {
		g_source_remove (window->save_geometry_timeout_id);
		window->save_geometry_timeout_id = 0;
854 855 856 857 858 859
		nautilus_window_save_geometry (window);
	}

	return FALSE;
}

860
NautilusWindow *
861 862
nautilus_application_create_window (NautilusApplication *application,
				    GdkScreen           *screen)
863
{
864
	NautilusWindow *window;
865 866

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
867
	
868
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
869
						  "app", application,
870 871 872 873
						  "app_id", "nautilus",
						  "screen", screen,
						  NULL));

874 875
	g_signal_connect (window, "delete_event",
			  G_CALLBACK (nautilus_window_delete_event_callback), NULL);
876

877 878
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
879

880
	g_signal_connect (window, "configure_event",
Alexander Larsson's avatar
Alexander Larsson committed
881
			  G_CALLBACK (nautilus_window_configure_event_callback), NULL);
882 883

	g_signal_connect (window, "unrealize",
Alexander Larsson's avatar
Alexander Larsson committed
884
			  G_CALLBACK (nautilus_window_unrealize_event_callback), NULL);
885

886
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
887

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