nautilus-application.c 34.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
#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
/* Keeps track of all the desktop windows. */
static GList *nautilus_application_desktop_windows;
81 82 83 84

/* 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
nautilus_application_create_desktop_windows (NautilusApplication *application)
581
{
582
	static gboolean create_in_progress = FALSE;
583 584 585
	GdkDisplay *display;
	NautilusDesktopWindow *window;
	int screens, i;
586

587
	g_return_if_fail (nautilus_application_desktop_windows == NULL);
588
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
589

590 591 592 593 594 595
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
	display = gdk_display_get_default ();
	screens = gdk_display_get_n_screens (display);

	for (i = 0; i < screens; i++) {
		window = nautilus_desktop_window_new (application,
						      gdk_display_get_screen (display, i));
		/* 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);
	}
611 612

	create_in_progress = FALSE;
613 614 615 616
}

void
nautilus_application_open_desktop (NautilusApplication *application)
617
{
618 619
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
620 621 622 623 624 625
	}
}

void
nautilus_application_close_desktop (void)
{
626 627 628 629 630
	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;
631
	}
632 633
}

634 635 636
void
nautilus_application_close_all_windows (void)
{
637
	while (nautilus_application_window_list != NULL) {
638 639 640 641
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

642
static void
643
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
644
{
645
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
646 647
}

648 649 650 651 652 653 654 655 656 657 658 659 660
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;
}				       

661
static gboolean
662
save_window_geometry_timeout (gpointer callback_data)
663 664 665 666 667 668
{
	NautilusWindow *window;
	
	window = NAUTILUS_WINDOW (callback_data);
	
	nautilus_window_save_geometry (window);
669 670

	window->save_geometry_timeout_id = 0;
671 672 673 674 675 676 677 678 679
	return FALSE;
}
 
static gboolean
nautilus_window_configure_event_callback (GtkWidget *widget,
						GdkEventConfigure *event,
						gpointer callback_data)
{
	NautilusWindow *window;
680
	char *geometry_string;
681 682 683
	
	window = NAUTILUS_WINDOW (widget);
	
684 685
	/* Only save the geometry if the user hasn't resized the window
	 * for half a second. Otherwise delay the callback another half second.
686
	 */
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
	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);
714 715 716 717 718 719 720 721 722 723 724 725 726 727
	}

	return FALSE;
}

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

728 729 730
	if (window->save_geometry_timeout_id != 0) {
		g_source_remove (window->save_geometry_timeout_id);
		window->save_geometry_timeout_id = 0;
731 732 733 734 735 736
		nautilus_window_save_geometry (window);
	}

	return FALSE;
}

737
NautilusWindow *
738 739
nautilus_application_create_window (NautilusApplication *application,
				    GdkScreen           *screen)
740
{
741
	NautilusWindow *window;
742 743

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
744
	
745
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
746
						  "app", application,
747 748 749 750
						  "app_id", "nautilus",
						  "screen", screen,
						  NULL));

751 752
	g_signal_connect (window, "delete_event",
			  G_CALLBACK (nautilus_window_delete_event_callback), NULL);
753

754 755
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
756

757
	g_signal_connect (window, "configure_event",
Alexander Larsson's avatar
Alexander Larsson committed
758
			  G_CALLBACK (nautilus_window_configure_event_callback), NULL);
759 760

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

763
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
764

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

770
	return window;
771
}
772

773 774 775 776
/* callback for changing the directory the desktop points to */
static void
desktop_location_changed_callback (gpointer user_data)
{
777 778 779
	if (nautilus_application_desktop_windows != NULL) {
		g_list_foreach (nautilus_application_desktop_windows,
				(GFunc) nautilus_desktop_window_update_directory, NULL);
780
	}
781 782
}

783 784 785 786 787 788 789
/* 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);
790
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
791 792 793 794
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
795 796 797 798 799

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

Ramiro Estrugo's avatar
Ramiro Estrugo committed
802 803 804 805 806 807
/*
 * 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
808
 * presented is: ~/.nautilus/first-time-flag.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
809 810
 *
 * Another alternative could be to use preferences to store this flag
Darin Adler's avatar
Darin Adler committed
811
 * However, there because of bug 41229 this is not yet possible.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
812 813
 *
 * Also, for debugging purposes, it is convenient to have just one file
814
 * to kill in order to test the startup druid:
Ramiro Estrugo's avatar
Ramiro Estrugo committed
815
 *
816
 * rm -f ~/.nautilus/first-time-flag
Ramiro Estrugo's avatar
Ramiro Estrugo committed
817 818 819 820 821 822 823 824 825 826 827
 *
 * 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;
828
	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
829 830
	user_directory = nautilus_get_user_directory ();

831
	druid_flag_file_name = g_strconcat (user_directory, "/first-time-flag", NULL);
832
	result = !g_file_test (druid_flag_file_name, G_FILE_TEST_EXISTS);
Ramiro Estrugo's avatar
Ramiro Estrugo committed
833 834
	g_free (druid_flag_file_name);

835 836 837 838
	/* 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) {
839 840
		druid_flag_file_name = g_strconcat (user_directory, "/first-time-wizard-flag", NULL);
		unlink (druid_flag_file_name);
841 842 843
		g_free (druid_flag_file_name);
	}
	g_free (user_directory); 
Ramiro Estrugo's avatar
Ramiro Estrugo committed
844 845
	return result;
}
846

847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
/* 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;
}

862 863 864 865
static void
volume_mounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
			 NautilusApplication *application)
{
866 867
	NautilusWindow *window;
	char *uri;
868

Gene Z. Ragan's avatar