nautilus-application.c 34.4 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/gtksignal.h>
54
#include <libgnome/gnome-config.h>
55 56
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
57
#include <libgnomeui/gnome-client.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
58
#include <libgnomeui/gnome-messagebox.h>
59
#include <libgnomeui/gnome-stock-icons.h>
60
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
61
#include <libgnomevfs/gnome-vfs-ops.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
62
#include <libgnomevfs/gnome-vfs-utils.h>
63 64 65 66 67
#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>
68
#include <libnautilus-private/nautilus-bonobo-extensions.h>
69 70
#include <libnautilus-private/nautilus-undo-manager.h>
#include <libnautilus-private/nautilus-volume-monitor.h>
71
#include <libnautilus-private/nautilus-authn-manager.h>
Alexander Larsson's avatar
Alexander Larsson committed
72
#include <libnautilus-private/nautilus-desktop-link-monitor.h>
73
#include <bonobo-activation/bonobo-activation.h>
74

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

79 80 81
#define FACTORY_IID	     "OAFIID:Nautilus_Factory"
#define SEARCH_LIST_VIEW_IID "OAFIID:Nautilus_File_Manager_Search_List_View"
#define SHELL_IID	     "OAFIID:Nautilus_Shell"
82

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

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

89 90 91 92 93 94 95 96 97 98 99 100 101
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);

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

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

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

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

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

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

150
	/* Watch for volume mounts so we can restore open windows */
151 152
	g_signal_connect_object (nautilus_volume_monitor_get (), "volume_mounted",
				 G_CALLBACK (volume_mounted_callback), application, 0);
153 154

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

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

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

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

	application = NAUTILUS_APPLICATION (object);

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

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

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

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

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

216
	failed_count = eel_string_list_get_length (directories);
217

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

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

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

245
	eel_string_list_free (directories);
246 247

	return failed_count == 0;
248 249
}

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

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

	return uri_list;
}

283 284 285
static void
migrate_old_nautilus_files (void)
{
286 287 288 289 290 291
	char *new_desktop_dir;
	char *old_desktop_dir;
	char *migrated_file;
	char *link_name;
	char *link_path;
	int fd;
292
	
293 294
	old_desktop_dir = nautilus_get_gmc_desktop_directory ();
	if (!g_file_test (old_desktop_dir, G_FILE_TEST_IS_DIR)) {
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 316 317 318 319 320 321 322
		fd = creat (migrated_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
		if (fd >= 0) {
			close (fd);
		}
		
		eel_show_info_dialog (_("The location of the desktop directory has changed in Gnome 2.4. "
					"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
	nautilus_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
void
372
nautilus_application_startup (NautilusApplication *application,
373
			      gboolean kill_shell,
374
			      gboolean restart_shell,
375
			      gboolean no_default_window,
376
			      gboolean no_desktop,
377
			      gboolean do_first_time_druid_check,
378
			      const char *geometry,
379
			      const char *urls[])
380
{
381 382
	CORBA_Environment ev;
	Nautilus_Shell shell;
383
	Bonobo_RegistrationResult result;
384
	const char *message, *detailed_message;
385
	GtkDialog *dialog;
386
	Nautilus_URIList *url_list;
387
	const CORBA_char *corba_geometry;
388 389 390
	int num_failures;

	num_failures = 0;
391

392
	/* Check the user's ~/.nautilus directories and post warnings
393
	 * if there are problems.
394
	 */
395
	if (!kill_shell && !check_required_directories (application)) {
396 397
		return;
	}
398

399 400
	/* Run the first time startup druid if needed. */
	if (do_first_time_druid_check && need_to_show_first_time_druid ()) {
401 402 403 404 405 406
		/* 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 ();
407
	}
408

409 410
	CORBA_exception_init (&ev);

411
	/* Start up the factory. */
412
	while (TRUE) {
413
		/* Try to register the file manager view factory. */
414
		result = nautilus_bonobo_activation_register_for_display
415
			(FACTORY_IID, BONOBO_OBJREF (application));
416

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

		/* Get the shell object. */
		if (message == NULL) {
481
			shell = bonobo_activation_activate_from_id (SHELL_IID, 0, NULL, NULL);
482 483 484 485 486 487 488
			if (!CORBA_Object_is_nil (shell, &ev)) {
				break;
			}

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

507
		if (message != NULL) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
508
			dialog = eel_show_error_dialog_with_details (message, NULL, detailed_message, NULL);
509 510
			/* We need the main event loop so the user has a chance to see the dialog. */
			nautilus_main_event_loop_register (GTK_OBJECT (dialog));
511
			goto out;
512 513 514
		}
	}

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

535 536
		/* Monitor the preference to have the desktop */
		/* point to the Unix home folder */
537
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
Darin Adler's avatar
Darin Adler committed
538 539 540
							  desktop_location_changed_callback,
							  NULL,
							  G_OBJECT (application));
541

542 543 544 545
		/* CORBA C mapping doesn't allow NULL to be passed
		   for string parameters */
		corba_geometry = (geometry != NULL) ? geometry : "";

546 547
	  	/* Create the other windows. */
		if (urls != NULL) {
548
			url_list = nautilus_make_uri_list_from_shell_strv (urls);
549
			Nautilus_Shell_open_windows (shell, url_list, corba_geometry, &ev);
550
			CORBA_free (url_list);
551
		} else if (!no_default_window) {
552
			Nautilus_Shell_open_default_window (shell, corba_geometry, &ev);
553
		}
554 555 556
		
		/* Add ourselves to the session */
		init_session ();
557
	}
558

559
	/* We're done with the shell now, so let it go. */
560 561 562 563 564
	/* 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.
	 */
565
	if (!(kill_shell || restart_shell)) {
566
		bonobo_object_release_unref (shell, NULL);
567
	}
568

569
 out:
570
	CORBA_exception_free (&ev);
571 572 573
}

static void
574
nautilus_application_create_desktop_windows (NautilusApplication *application)
575
{
576
	static gboolean create_in_progress = FALSE;
577 578 579
	GdkDisplay *display;
	NautilusDesktopWindow *window;
	int screens, i;
580

581
	g_return_if_fail (nautilus_application_desktop_windows == NULL);
582
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
583

584 585 586 587 588 589
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

590 591 592 593 594 595 596 597 598 599 600 601 602 603 604
	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);
	}
605 606

	create_in_progress = FALSE;
607 608 609 610
}

void
nautilus_application_open_desktop (NautilusApplication *application)
611
{
612 613
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
614 615 616 617 618 619
	}
}

void
nautilus_application_close_desktop (void)
{
620 621 622 623 624
	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;
625
	}
626 627
}

628 629 630
void
nautilus_application_close_all_windows (void)
{
631
	while (nautilus_application_window_list != NULL) {
632 633 634 635
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

636
static void
637
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
638
{
639
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
640 641
}

642 643 644 645 646 647 648 649 650 651 652 653 654
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;
}				       

655
static gboolean
656
save_window_geometry_timeout (gpointer callback_data)
657 658 659 660 661 662
{
	NautilusWindow *window;
	
	window = NAUTILUS_WINDOW (callback_data);
	
	nautilus_window_save_geometry (window);
663 664

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

	return FALSE;
}

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

722 723 724
	if (window->save_geometry_timeout_id != 0) {
		g_source_remove (window->save_geometry_timeout_id);
		window->save_geometry_timeout_id = 0;
725 726 727 728 729 730
		nautilus_window_save_geometry (window);
	}

	return FALSE;
}

731
NautilusWindow *
732 733
nautilus_application_create_window (NautilusApplication *application,
				    GdkScreen           *screen)
734
{
735
	NautilusWindow *window;
736 737

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
738
	
739
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
740
						  "app", application,
741 742 743 744
						  "app_id", "nautilus",
						  "screen", screen,
						  NULL));

745 746
	g_signal_connect (window, "delete_event",
			  G_CALLBACK (nautilus_window_delete_event_callback), NULL);
747

748 749
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
750

751
	g_signal_connect (window, "configure_event",
Alexander Larsson's avatar
Alexander Larsson committed
752
			  G_CALLBACK (nautilus_window_configure_event_callback), NULL);
753 754

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

757
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
758

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

764
	return window;
765
}
766

767 768 769 770
/* callback for changing the directory the desktop points to */
static void
desktop_location_changed_callback (gpointer user_data)
{
771 772 773
	if (nautilus_application_desktop_windows != NULL) {
		g_list_foreach (nautilus_application_desktop_windows,
				(GFunc) nautilus_desktop_window_update_directory, NULL);
774
	}
775 776
}

777 778 779 780 781 782 783
/* 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);
784
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
785 786 787 788
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
789 790 791 792 793

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

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

825
	druid_flag_file_name = g_strconcat (user_directory, "/first-time-flag", NULL);
826
	result = !g_file_test (druid_flag_file_name, G_FILE_TEST_EXISTS);
Ramiro Estrugo's avatar
Ramiro Estrugo committed
827 828
	g_free (druid_flag_file_name);

829 830 831 832
	/* 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) {