nautilus-application.c 35.1 KB
Newer Older
Gene Z. Ragan's avatar
CVS:  
Gene Z. Ragan committed
1
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 3 4 5 6

/*
 *  Nautilus
 *
 *  Copyright (C) 1999, 2000 Red Hat, Inc.
7
 *  Copyright (C) 2000, 2001 Eazel, Inc.
8
 *
9
 *  Nautilus is free software; you can redistribute it and/or
10 11 12 13
 *  modify it under the terms of the GNU General Public License as
 *  published by the Free Software Foundation; either version 2 of the
 *  License, or (at your option) any later version.
 *
14
 *  Nautilus is distributed in the hope that it will be useful,
15 16 17 18 19
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
20
 *  along with this program; if not, write to the Free Software
21 22
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
23
 *  Authors: Elliot Lee <sopwith@redhat.com>,
24
 *           Darin Adler <darin@bentspoon.com>
25 26 27
 *
 */

28
#include <config.h>
29
#include "nautilus-application.h"
30

31

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

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

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

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

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

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

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

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

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

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

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

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

148 149 150
	/* Watch for volume mounts so we can restore open windows
	 * This used to be for showing new window on mount, but is not
	 * used anymore */
151 152

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

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

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

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

	application = NAUTILUS_APPLICATION (object);

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

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

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

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

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

214
	failed_count = eel_string_list_get_length (directories);
215

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

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

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

243
	eel_string_list_free (directories);
244 245

	return failed_count == 0;
246 247
}

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

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

	return uri_list;
}

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

309 310 311 312 313
		fd = creat (migrated_file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
		if (fd >= 0) {
			close (fd);
		}
		
314
		eel_show_info_dialog (_("The location of the desktop directory has changed in GNOME 2.4. "
315 316 317 318 319 320
					"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);
321 322
}

323 324 325 326 327 328 329 330 331
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 ();
332 333 334
	desktop_link_file = g_build_filename (desktop_path,
					      "starthere.desktop",
					      NULL);
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

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

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

359
	/* initialize URI authentication manager */
360
	gnome_authentication_manager_init ();
361

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

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

369
void
370
nautilus_application_startup (NautilusApplication *application,
371
			      gboolean kill_shell,
372
			      gboolean restart_shell,
373
			      gboolean no_default_window,
374
			      gboolean no_desktop,
375
			      gboolean do_first_time_druid_check,
376
			      const char *geometry,
377
			      const char *urls[])
378
{
379 380
	CORBA_Environment ev;
	Nautilus_Shell shell;
381
	Bonobo_RegistrationResult result;
382
	const char *message, *detailed_message;
383
	GtkDialog *dialog;
384
	Nautilus_URIList *url_list;
385
	const CORBA_char *corba_geometry;
386 387 388
	int num_failures;

	num_failures = 0;
389

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

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

407 408
	CORBA_exception_init (&ev);

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

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

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

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

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

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

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

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

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

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

567
 out:
568
	CORBA_exception_free (&ev);
569 570
}

571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638

static void 
selection_get_cb (GtkWidget          *widget,
		  GtkSelectionData   *selection_data,
		  guint               info,
		  guint               time)
{
	/* No extra targets atm */
}

static GtkWidget *
get_desktop_manager_selection (GdkDisplay *display, int screen)
{
	char *selection_name;
	GdkAtom selection_atom;
	Window selection_owner;
	GtkWidget *selection_widget;

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

	selection_owner = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
					      gdk_x11_atom_to_xatom_for_display (display, 
										 selection_atom));
	if (selection_owner != None) {
		return NULL;
	}
	
	selection_widget = gtk_invisible_new_for_screen (gdk_display_get_screen (display, screen));
	/* We need this for gdk_x11_get_server_time() */
	gtk_widget_add_events (selection_widget, GDK_PROPERTY_CHANGE_MASK);

	if (gtk_selection_owner_set_for_display (display,
						 selection_widget,
						 selection_atom,
						 gdk_x11_get_server_time (selection_widget->window))) {
		
		g_signal_connect (selection_widget, "selection_get",
				  G_CALLBACK (selection_get_cb), NULL);
		return selection_widget;
	}

	gtk_widget_destroy (selection_widget);
	
	return NULL;
}

static void
desktop_unrealize_cb (GtkWidget        *widget,
		      GtkWidget        *selection_widget)
{
	gtk_widget_destroy (selection_widget);
}

static gboolean
selection_clear_event_cb (GtkWidget	        *widget,
			  GdkEventSelection     *event,
			  NautilusDesktopWindow *window)
{
	gtk_widget_destroy (GTK_WIDGET (window));
	
	nautilus_application_desktop_windows =
		g_list_remove (nautilus_application_desktop_windows, window);

	return TRUE;
}

639
static void
640
nautilus_application_create_desktop_windows (NautilusApplication *application)
641
{
642
	static gboolean create_in_progress = FALSE;
643 644
	GdkDisplay *display;
	NautilusDesktopWindow *window;
645
	GtkWidget *selection_widget;
646
	int screens, i;
647

648
	g_return_if_fail (nautilus_application_desktop_windows == NULL);
649
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
650

651 652 653 654 655 656
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

657 658 659 660
	display = gdk_display_get_default ();
	screens = gdk_display_get_n_screens (display);

	for (i = 0; i < screens; i++) {
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
		selection_widget = get_desktop_manager_selection (display, i);
		if (selection_widget != NULL) {
			window = nautilus_desktop_window_new (application,
							      gdk_display_get_screen (display, i));
			
			g_signal_connect (selection_widget, "selection_clear_event",
					  G_CALLBACK (selection_clear_event_cb), window);
			
			g_signal_connect (window, "unrealize",
					  G_CALLBACK (desktop_unrealize_cb), selection_widget);
			
			/* We realize it immediately so that the NAUTILUS_DESKTOP_WINDOW_ID
			   property is set so gnome-settings-daemon doesn't try to set the
			   background. And we do a gdk_flush() to be sure X gets it. */
			gtk_widget_realize (GTK_WIDGET (window));
			gdk_flush ();

			
			nautilus_application_desktop_windows =
				g_list_prepend (nautilus_application_desktop_windows, window);
		}
682
	}
683 684

	create_in_progress = FALSE;
685 686 687 688
}

void
nautilus_application_open_desktop (NautilusApplication *application)
689
{
690 691
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
692 693 694 695 696 697
	}
}

void
nautilus_application_close_desktop (void)
{
698 699 700 701 702
	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;
703
	}
704 705
}

706 707 708
void
nautilus_application_close_all_windows (void)
{
709
	while (nautilus_application_window_list != NULL) {
710 711 712 713
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

714
static void
715
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
716
{
717
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
718 719
}

720 721 722 723 724 725 726 727 728 729 730 731 732
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;
}				       

733
static gboolean
734
save_window_geometry_timeout (gpointer callback_data)
735 736 737 738 739 740
{
	NautilusWindow *window;
	
	window = NAUTILUS_WINDOW (callback_data);
	
	nautilus_window_save_geometry (window);
741 742

	window->save_geometry_timeout_id = 0;
743 744 745 746 747 748 749 750 751
	return FALSE;
}
 
static gboolean
nautilus_window_configure_event_callback (GtkWidget *widget,
						GdkEventConfigure *event,
						gpointer callback_data)
{
	NautilusWindow *window;
752
	char *geometry_string;
753 754 755
	
	window = NAUTILUS_WINDOW (widget);
	
756 757
	/* Only save the geometry if the user hasn't resized the window
	 * for half a second. Otherwise delay the callback another half second.
758
	 */
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
	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);
786 787 788 789 790 791 792 793 794 795 796 797 798 799
	}

	return FALSE;
}

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

800 801 802
	if (window->save_geometry_timeout_id != 0) {
		g_source_remove (window->save_geometry_timeout_id);
		window->save_geometry_timeout_id = 0;
803 804 805 806 807 808
		nautilus_window_save_geometry (window);
	}

	return FALSE;
}

809
NautilusWindow *
810 811
nautilus_application_create_window (NautilusApplication *application,
				    GdkScreen           *screen)
812
{
813
	NautilusWindow *window;
814 815

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
816
	
817
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
818
						  "app", application,
819 820 821 822
						  "app_id", "nautilus",
						  "screen", screen,
						  NULL));

823 824
	g_signal_connect (window, "delete_event",
			  G_CALLBACK (nautilus_window_delete_event_callback), NULL);
825

826 827
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
828

829
	g_signal_connect (window, "configure_event",
Alexander Larsson's avatar
Alexander Larsson committed
830
			  G_CALLBACK (nautilus_window_configure_event_callback), NULL);
831 832

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

835
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
836

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

842
	return window;
843
}
844

845 846 847 848
/* callback for changing the directory the desktop points to */
static void
desktop_location_changed_callback (gpointer user_data)
{
849 850 851
	if (nautilus_application_desktop_windows != NULL) {
		g_list_foreach (nautilus_application_desktop_windows,
				(GFunc) nautilus_desktop_window_update_directory, NULL);
852
	}
853 854
}

855 856 857 858 859 860 861
/* 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);
862
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
863 864 865 866
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}