nautilus-application.c 33.7 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-search-list-view.h"
35 36
#include "nautilus-desktop-window.h"
#include "nautilus-first-time-druid.h"
37
#include "nautilus-main.h"
38 39 40 41
#include "nautilus-shell-interface.h"
#include "nautilus-shell.h"
#include <bonobo/bonobo-main.h>
#include <bonobo/bonobo-object.h>
42
#include <dirent.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
43 44 45 46 47
#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>
48
#include <eel/eel-gtk-extensions.h>
49
#include <gdk/gdkx.h>
50
#include <gtk/gtksignal.h>
51
#include <libgnome/gnome-config.h>
52 53
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
54
#include <libgnomeui/gnome-client.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
55
#include <libgnomeui/gnome-messagebox.h>
56
#include <libgnomeui/gnome-stock-icons.h>
57
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
58
#include <libgnomevfs/gnome-vfs-ops.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
59
#include <libgnomevfs/gnome-vfs-utils.h>
60 61 62 63 64
#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>
65
#include <libnautilus-private/nautilus-bonobo-extensions.h>
66 67
#include <libnautilus-private/nautilus-undo-manager.h>
#include <libnautilus-private/nautilus-volume-monitor.h>
68
#include <libnautilus-private/nautilus-authn-manager.h>
69
#include <bonobo-activation/bonobo-activation.h>
70

71 72 73 74
/* Needed for the is_kdesktop_present check */
#include <gdk/gdkx.h>
#include <X11/Xlib.h>

75 76 77
#define FACTORY_IID	     "OAFIID:Nautilus_Factory"
#define SEARCH_LIST_VIEW_IID "OAFIID:Nautilus_File_Manager_Search_List_View"
#define SHELL_IID	     "OAFIID:Nautilus_Shell"
78

79 80 81 82 83 84
/* Keeps track of the one and only desktop window. */
static NautilusDesktopWindow *nautilus_application_desktop_window;

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

85 86 87 88 89 90 91 92 93 94 95 96 97
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_mounted_callback           (NautilusVolumeMonitor    *monitor,
						   NautilusVolume           *volume,
						   NautilusApplication      *application);
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);

98 99
BONOBO_CLASS_BOILERPLATE (NautilusApplication, nautilus_application,
			  BonoboGenericFactory, BONOBO_GENERIC_FACTORY_TYPE)
100 101

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

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

131
	return CORBA_Object_duplicate (BONOBO_OBJREF (object), ev);
132 133
}

134 135
GList *
nautilus_application_get_window_list (void)
136 137 138 139
{
	return nautilus_application_window_list;
}

140
static void
141
nautilus_application_instance_init (NautilusApplication *application)
142
{
143 144
	/* Create an undo manager */
	application->undo_manager = nautilus_undo_manager_new ();
145

146
	/* Watch for volume mounts so we can restore open windows */
147 148
	g_signal_connect_object (nautilus_volume_monitor_get (), "volume_mounted",
				 G_CALLBACK (volume_mounted_callback), application, 0);
149 150

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

155 156
NautilusApplication *
nautilus_application_new (void)
157
{
158 159 160 161 162 163 164 165 166
	NautilusApplication *application;

	application = g_object_new (NAUTILUS_TYPE_APPLICATION, NULL);
	
	bonobo_generic_factory_construct_noreg (BONOBO_GENERIC_FACTORY (application),
						FACTORY_IID,
						NULL);
	
	return application;
167 168 169
}

static void
170
nautilus_application_destroy (BonoboObject *object)
171
{
172 173 174 175
	NautilusApplication *application;

	application = NAUTILUS_APPLICATION (object);

176
	nautilus_bookmarks_exiting ();
177
	
178
	bonobo_object_unref (application->undo_manager);
179

180
	EEL_CALL_PARENT (BONOBO_OBJECT_CLASS, destroy, (object));
181
}
182

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

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

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

212
	failed_count = eel_string_list_get_length (directories);
213

214
	if (failed_count != 0) {
215
		directories_as_string = eel_string_list_as_string (directories, "\n", EEL_STRING_LIST_ALL_STRINGS);
216

217 218
		if (failed_count == 1) {
			dialog_title = g_strdup (_("Couldn't Create Required Folder"));
219 220 221
			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."),
222
							directories_as_string);
223 224
		} else {
			dialog_title = g_strdup (_("Couldn't Create Required Folders"));
225 226 227 228
			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."),
229
							directories_as_string);
230 231
		}
		
Ramiro Estrugo's avatar
Ramiro Estrugo committed
232
		dialog = eel_show_error_dialog (error_string, dialog_title, NULL);
233 234
		/* We need the main event loop so the user has a chance to see the dialog. */
		nautilus_main_event_loop_register (GTK_OBJECT (dialog));
235

236
		g_free (directories_as_string);
237
		g_free (error_string);
238
		g_free (dialog_title);
239 240
	}

241
	eel_string_list_free (directories);
242 243

	return failed_count == 0;
244 245
}

246 247 248 249 250 251 252 253 254 255
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 *
256
nautilus_make_uri_list_from_shell_strv (const char * const *strv)
257 258 259
{
	int length, i;
	Nautilus_URIList *uri_list;
260
	char *translated_uri;
261 262 263 264 265 266 267 268

	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
269
		translated_uri = eel_make_uri_from_shell_arg (strv[i]);
270 271 272
		uri_list->_buffer[i] = CORBA_string_dup (translated_uri);
		g_free (translated_uri);
		translated_uri = NULL;
273 274 275 276 277 278
	}
	CORBA_sequence_set_release (uri_list, CORBA_TRUE);

	return uri_list;
}

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
static void
migrate_old_nautilus_files (void)
{
	char *new_desktop_dir, *np;
	char *old_desktop_dir, *op;
	char *old_desktop_dir_new_name;
	struct stat buf;
	DIR *dir;
	struct dirent *de;
	
	old_desktop_dir = g_strconcat (g_get_home_dir (), "/.nautilus/desktop", NULL);
	if (stat (old_desktop_dir, &buf) == -1) {
		g_free (old_desktop_dir);
		return;
	}
	if (!S_ISLNK (buf.st_mode)){
		dir = opendir (old_desktop_dir);
		if (dir == NULL) {
			g_free (old_desktop_dir);
			return;
		}
	
		new_desktop_dir = nautilus_get_desktop_directory ();
		
		while ((de = readdir (dir)) != NULL){
			if (de->d_name [0] == '.'){
				if (de->d_name [0] == 0)
					continue;
				
				if (de->d_name [1] == '.' && de->d_name [2] == 0)
					continue;
			}
	
			op = g_strconcat (old_desktop_dir, "/", de->d_name, NULL);
			np = g_strconcat (new_desktop_dir, "/", de->d_name, NULL);
	
			rename (op, np);
	
			g_free (op);
			g_free (np);
		}

		closedir (dir);

		g_free (new_desktop_dir);
	}

	/* In case we miss something */
	old_desktop_dir_new_name = g_strconcat (old_desktop_dir, "-old", NULL);
	rename (old_desktop_dir, old_desktop_dir_new_name);
	g_free (old_desktop_dir_new_name);

	g_free (old_desktop_dir);
}

334 335 336 337 338 339 340 341 342
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 ();
343 344 345
	desktop_link_file = g_build_filename (desktop_path,
					      "starthere.desktop",
					      NULL);
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363

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

364 365 366 367
static void
finish_startup (NautilusApplication *application)
{
	/* initialize the sound machinery */
368
	nautilus_sound_init ();
369

370
	/* initialize URI authentication manager */
371
	nautilus_authentication_manager_init ();
372

373
	/* Make the desktop work with old Nautilus. */
374 375 376
	migrate_old_nautilus_files ();
}

377
void
378
nautilus_application_startup (NautilusApplication *application,
379
			      gboolean kill_shell,
380
			      gboolean restart_shell,
381
			      gboolean no_default_window,
382
			      gboolean no_desktop,
383
			      gboolean do_first_time_druid_check,
384
			      const char *geometry,
385
			      const char *urls[])
386
{
387 388
	CORBA_Environment ev;
	Nautilus_Shell shell;
389
	Bonobo_RegistrationResult result;
390
	const char *message, *detailed_message;
391
	GtkDialog *dialog;
392
	Nautilus_URIList *url_list;
393
	const CORBA_char *corba_geometry;
394 395 396
	int num_failures;

	num_failures = 0;
397

398
	/* Check the user's ~/.nautilus directories and post warnings
399
	 * if there are problems.
400
	 */
401
	if (!kill_shell && !check_required_directories (application)) {
402 403
		return;
	}
404

405 406
	/* Run the first time startup druid if needed. */
	if (do_first_time_druid_check && need_to_show_first_time_druid ()) {
407 408 409 410 411 412
		/* 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 ();
413
	}
414

415 416
	CORBA_exception_init (&ev);

417
	/* Start up the factory. */
418
	while (TRUE) {
419
		/* Try to register the file manager view factory. */
420
		result = nautilus_bonobo_activation_register_for_display
421
			(FACTORY_IID, BONOBO_OBJREF (application));
422

423
		switch (result) {
424 425
		case Bonobo_ACTIVATION_REG_SUCCESS:
			/* We are registered and all is right with the world. */
426
			finish_startup (application);
427
		case Bonobo_ACTIVATION_REG_ALREADY_ACTIVE:
428
			/* Another copy of nautilus already is running and registered. */
429
			message = NULL;
430
			detailed_message = NULL;
431
			break;
432
		case Bonobo_ACTIVATION_REG_NOT_LISTED:
433
			/* Can't register myself due to trouble locating the
434
			 * Nautilus_Shell.server file. This has happened when you
435
			 * launch Nautilus with an LD_LIBRARY_PATH that
436
			 * doesn't include the directory containing the oaf
437
			 * library. It could also happen if the
438
			 * Nautilus_Shell.server file was not present for some
439 440 441 442 443
			 * 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. "
444
				    "Running the command \"bonobo-slay\""
445 446 447
				    " from the console may fix the problem. If not,"
				    " you can try rebooting the computer or"
				    " installing Nautilus again.");
448
			/* FIXME bugzilla.gnome.org 42536: The guesses and stuff here are lame. */
449
			detailed_message = _("Nautilus can't be used now. "
450
					     "Running the command \"bonobo-slay\" "
451 452 453 454
					     "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. "
455
					     "One cause of this seems to be an LD_LIBRARY_PATH "
456
					     "that does not include the bonobo-activation library's directory. "
457
					     "Another possible cause would be bad install "
458
					     "with a missing Nautilus_Shell.server file.\n\n"
459
					     "Running \"bonobo-slay\" will kill all "
460
					     "Bonobo Activation and GConf processes, which may be needed by "
461
					     "other applications.\n\n"
462
					     "Sometimes killing bonobo-activation-server and gconfd fixes "
463
					     "the problem, but we don't know why.\n\n"
464
					     "We have also seen this error when a faulty "
465
					     "version of bonobo-activation was installed.");
466 467 468
			break;
		default:
			/* This should never happen. */
469
			g_warning ("bad error code from bonobo_activation_active_server_register");
470
		case Bonobo_ACTIVATION_REG_ERROR:
471
			/* Some misc. error (can never happen with current
472
			 * version of bonobo-activation). Show dialog and terminate the
473 474
			 * program.
			 */
475
			/* FIXME bugzilla.gnome.org 42537: Looks like this does happen with the
476
			 * current OAF. I guess I read the code wrong. Need to figure out when and make a
477 478
			 * good message.
			 */
479 480
			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 "
481
					     "from Bonobo when attempting to register the file manager view server.");
482 483
			break;
		}
484 485 486

		/* Get the shell object. */
		if (message == NULL) {
487
			shell = bonobo_activation_activate_from_id (SHELL_IID, 0, NULL, NULL);
488 489 490 491 492 493 494
			if (!CORBA_Object_is_nil (shell, &ev)) {
				break;
			}

			/* If we couldn't find ourselves it's a bad problem so
			 * we better stop looping.
			 */
495
			if (result == Bonobo_ACTIVATION_REG_SUCCESS) {
496
				/* FIXME bugzilla.gnome.org 42538: When can this happen? */
497 498
				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 "
499 500
						     "from Bonobo when attempting to locate the factory."
						     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
501 502 503 504 505
			} 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 "
506 507
							     "from Bonobo when attempting to locate the shell object. "
							     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
508 509
					
				}
510 511 512
			}
		}

513
		if (message != NULL) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
514
			dialog = eel_show_error_dialog_with_details (message, NULL, detailed_message, NULL);
515 516
			/* We need the main event loop so the user has a chance to see the dialog. */
			nautilus_main_event_loop_register (GTK_OBJECT (dialog));
517
			goto out;
518 519 520
		}
	}

521 522
	if (kill_shell) {
		Nautilus_Shell_quit (shell, &ev);
523 524
	} else if (restart_shell) {
		Nautilus_Shell_restart (shell, &ev);
525
	} else {
526
		/* If KDE desktop is running, then force no_desktop */
527
		if (is_kdesktop_present ()) {
528
			no_desktop = TRUE;
529
		}
530
		
531
		if (!no_desktop && eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
532 533
			Nautilus_Shell_start_desktop (shell, &ev);
		}
534 535
		
		/* Monitor the preference to show or hide the desktop */
536
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_SHOW_DESKTOP,
Darin Adler's avatar
Darin Adler committed
537 538 539
							  desktop_changed_callback,
							  application,
							  G_OBJECT (application));
540

541 542
		/* Monitor the preference to have the desktop */
		/* point to the Unix home folder */
543
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
Darin Adler's avatar
Darin Adler committed
544 545 546
							  desktop_location_changed_callback,
							  NULL,
							  G_OBJECT (application));
547

548 549 550 551
		/* CORBA C mapping doesn't allow NULL to be passed
		   for string parameters */
		corba_geometry = (geometry != NULL) ? geometry : "";

552 553
	  	/* Create the other windows. */
		if (urls != NULL) {
554
			url_list = nautilus_make_uri_list_from_shell_strv (urls);
555
			Nautilus_Shell_open_windows (shell, url_list, corba_geometry, &ev);
556
			CORBA_free (url_list);
557
		} else if (!no_default_window) {
558
			Nautilus_Shell_open_default_window (shell, corba_geometry, &ev);
559
		}
560 561 562
		
		/* Add ourselves to the session */
		init_session ();
563
	}
564

565
	/* We're done with the shell now, so let it go. */
566 567 568 569 570
	/* 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.
	 */
571
	if (!(kill_shell || restart_shell)) {
572
		bonobo_object_release_unref (shell, NULL);
573
	}
574

575
 out:
576
	CORBA_exception_free (&ev);
577 578 579
}

static void
580 581
nautilus_application_create_desktop_window (NautilusApplication *application)
{
582 583
	static gboolean create_in_progress = FALSE;

584 585
	g_return_if_fail (nautilus_application_desktop_window == NULL);
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
586

587 588 589 590 591 592
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

593
	nautilus_application_desktop_window = nautilus_desktop_window_new (application);
594 595 596 597 598
	/* 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 (nautilus_application_desktop_window));
	gdk_flush ();
599 600

	create_in_progress = FALSE;
601 602 603 604
}

void
nautilus_application_open_desktop (NautilusApplication *application)
605
{
606 607
	if (nautilus_application_desktop_window == NULL) {
		nautilus_application_create_desktop_window (application);
608 609 610 611 612 613
	}
}

void
nautilus_application_close_desktop (void)
{
614
	if (nautilus_application_desktop_window != NULL) {
615
		gtk_widget_destroy (GTK_WIDGET (nautilus_application_desktop_window));
616
		nautilus_application_desktop_window = NULL;
617
	}
618 619
}

620 621 622
void
nautilus_application_close_all_windows (void)
{
623
	while (nautilus_application_window_list != NULL) {
624 625 626 627
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

628
static void
629
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
630
{
631
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
632 633
}

634 635 636 637 638 639 640 641 642 643 644 645 646
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;
}				       

647
static gboolean
648
save_window_geometry_timeout (gpointer callback_data)
649 650 651 652 653 654
{
	NautilusWindow *window;
	
	window = NAUTILUS_WINDOW (callback_data);
	
	nautilus_window_save_geometry (window);
655 656

	window->save_geometry_timeout_id = 0;
657 658 659 660 661 662 663 664 665
	return FALSE;
}
 
static gboolean
nautilus_window_configure_event_callback (GtkWidget *widget,
						GdkEventConfigure *event,
						gpointer callback_data)
{
	NautilusWindow *window;
666
	char *geometry_string;
667 668 669
	
	window = NAUTILUS_WINDOW (widget);
	
670 671
	/* Only save the geometry if the user hasn't resized the window
	 * for half a second. Otherwise delay the callback another half second.
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
	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);
700 701 702 703 704 705 706 707 708 709 710 711 712 713
	}

	return FALSE;
}

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

714 715 716
	if (window->save_geometry_timeout_id != 0) {
		g_source_remove (window->save_geometry_timeout_id);
		window->save_geometry_timeout_id = 0;
717 718 719 720 721 722
		nautilus_window_save_geometry (window);
	}

	return FALSE;
}

723
NautilusWindow *
724
nautilus_application_create_window (NautilusApplication *application)
725
{
726
	NautilusWindow *window;
727 728

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
729
	
730
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
731
						  "app", application,
732
						  "app_id", "nautilus", NULL));
733
	
734 735
	g_signal_connect (window, "delete_event",
			  G_CALLBACK (nautilus_window_delete_event_callback), NULL);
736

737 738
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
739

740
	g_signal_connect (window, "configure_event",
Alexander Larsson's avatar
Alexander Larsson committed
741
			  G_CALLBACK (nautilus_window_configure_event_callback), NULL);
742 743

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

746
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
747

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

753
	return window;
754
}
755

756 757 758 759
/* callback for changing the directory the desktop points to */
static void
desktop_location_changed_callback (gpointer user_data)
{
760 761 762
	if (nautilus_application_desktop_window != NULL) {
		nautilus_desktop_window_update_directory
			(nautilus_application_desktop_window);
763
	}
764 765
}

766 767 768 769 770 771 772
/* callback for showing or hiding the desktop based on the user's preference */
static void
desktop_changed_callback (gpointer user_data)
{
	NautilusApplication *application;
	
	application = NAUTILUS_APPLICATION (user_data);
773
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
774 775 776 777
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
778 779 780 781 782

	/* Can't make this function just watch the preference
	 * itself changing since ordering is important
	 */
	update_session (gnome_master_client ());
783 784
}

Ramiro Estrugo's avatar
Ramiro Estrugo committed
785 786 787 788 789 790
/*
 * need_to_show_first_time_druid
 *
 * Determine whether Nautilus needs to show the first time druid.
 * 
 * Note that the flag file indicating whether the druid has been
791
 * presented is: ~/.nautilus/first-time-flag.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
792 793
 *
 * Another alternative could be to use preferences to store this flag
Darin Adler's avatar
Darin Adler committed
794
 * However, there because of bug 41229 this is not yet possible.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
795 796
 *
 * Also, for debugging purposes, it is convenient to have just one file
797
 * to kill in order to test the startup druid:
Ramiro Estrugo's avatar
Ramiro Estrugo committed
798
 *
799
 * rm -f ~/.nautilus/first-time-flag
Ramiro Estrugo's avatar
Ramiro Estrugo committed
800 801 802 803 804 805 806 807 808 809 810
 *
 * In order to accomplish the same thing with preferences, you would have
 * to either kill ALL your preferences or spend time digging in ~/.gconf
 * xml files finding the right one.
 */
static gboolean
need_to_show_first_time_druid (void)
{
	gboolean result;
	char *user_directory;
	char *druid_flag_file_name;
811
	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
812 813
	user_directory = nautilus_get_user_directory ();

814
	druid_flag_file_name = g_strconcat (user_directory, "/first-time-flag", NULL);
815
	result = !g_file_test (druid_flag_file_name, G_FILE_TEST_EXISTS);
Ramiro Estrugo's avatar
Ramiro Estrugo committed
816 817
	g_free (druid_flag_file_name);

818 819 820 821
	/* we changed the name of the flag for version 1.0, so we should
	 * check for and delete the old one, if the new one didn't exist 
	 */
	if (result) {
822 823
		druid_flag_file_name = g_strconcat (user_directory, "/first-time-wizard-flag", NULL);
		unlink (druid_flag_file_name);
824 825 826
		g_free (druid_flag_file_name);
	}
	g_free (user_directory); 
Ramiro Estrugo's avatar
Ramiro Estrugo committed
827 828
	return result;
}
829

830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
/* Apps like redhat-config-packages that are using the CD-ROM
 * directly, can grab ownership of the _NAUTILUS_DISABLE_MOUNT_WINDOW
 * selection to temporarily disable the new window behavior.
 */
static gboolean
check_mount_window_disabled (void)
{
        Atom selection_atom = gdk_x11_get_xatom_by_name ("_NAUTILUS_DISABLE_MOUNT_WINDOW");

        if (XGetSelectionOwner (GDK_DISPLAY(), selection_atom) != None)
                return TRUE;
	else
		return FALSE;
}

845 846 847 848
static void
volume_mounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
			 NautilusApplication *application)
{
849 850
	NautilusWindow *window;
	char *uri;
851

852 853 854
	if (volume == NULL || application == NULL) {
		return;
	}
855

856
	/* Open a window to the CD if the user has set that preference. */
857
	if (nautilus_volume_get_device_type (volume) == NAUTILUS_DEVICE_CDROM_DRIVE