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

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

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

31

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

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

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

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

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

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

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

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

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

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

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

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

	application = NAUTILUS_APPLICATION (object);

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

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

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

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

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

217
	failed_count = eel_string_list_get_length (directories);
218

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

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

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

246
	eel_string_list_free (directories);
247 248

	return failed_count == 0;
249 250
}

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

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

	return uri_list;
}

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

312 313 314 315 316 317 318 319 320 321 322 323
		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);
324 325
}

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

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

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

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

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

	/* Initialize the desktop link monitor singleton */
	nautilus_desktop_link_monitor_get ();
370 371
}

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

	num_failures = 0;
392

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

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

410 411
	CORBA_exception_init (&ev);

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

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

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

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

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

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

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

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

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

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

570
 out:
571
	CORBA_exception_free (&ev);
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 639 640 641

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

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

651
	g_return_if_fail (nautilus_application_desktop_windows == NULL);
652
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
653

654 655 656 657 658 659
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

660 661 662 663
	display = gdk_display_get_default ();
	screens = gdk_display_get_n_screens (display);

	for (i = 0; i < screens; i++) {
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
		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);
		}
685
	}
686 687

	create_in_progress = FALSE;
688 689 690 691
}

void
nautilus_application_open_desktop (NautilusApplication *application)
692
{
693 694
	if (nautilus_application_desktop_windows == NULL) {
		nautilus_application_create_desktop_windows (application);
695 696 697 698 699 700
	}
}

void
nautilus_application_close_desktop (void)
{
701 702 703 704 705
	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;
706
	}
707 708
}

709 710 711
void
nautilus_application_close_all_windows (void)
{
712
	while (nautilus_application_window_list != NULL) {
713 714 715 716
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

717
static void
718
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
719
{
720
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
721 722
}

723 724 725 726 727 728 729 730 731 732 733 734 735
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;
}				       

736
static gboolean
737
save_window_geometry_timeout (gpointer callback_data)
738 739 740 741 742 743
{
	NautilusWindow *window;
	
	window = NAUTILUS_WINDOW (callback_data);
	
	nautilus_window_save_geometry (window);
744 745

	window->save_geometry_timeout_id = 0;
746 747 748 749 750 751 752 753 754
	return FALSE;
}
 
static gboolean
nautilus_window_configure_event_callback (GtkWidget *widget,
						GdkEventConfigure *event,
						gpointer callback_data)
{
	NautilusWindow *window;
755
	char *geometry_string;
756 757 758
	
	window = NAUTILUS_WINDOW (widget);
	
759 760
	/* Only save the geometry if the user hasn't resized the window
	 * for half a second. Otherwise delay the callback another half second.
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 786 787 788
	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);
789 790 791 792 793 794 795 796 797 798 799 800 801 802
	}

	return FALSE;
}

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

803 804 805
	if (window->save_geometry_timeout_id != 0) {
		g_source_remove (window->save_geometry_timeout_id);
		window->save_geometry_timeout_id = 0;
806 807 808 809 810 811
		nautilus_window_save_geometry (window);
	}

	return FALSE;
}

812
NautilusWindow *
813 814
nautilus_application_create_window (NautilusApplication *application,
				    GdkScreen           *screen)
815
{
816
	NautilusWindow *window;
817 818

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
819
	
820
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
821
						  "app", application,
822 823 824 825
						  "app_id", "nautilus",
						  "screen", screen,
						  NULL));

826 827
	g_signal_connect (window, "delete_event",
			  G_CALLBACK (nautilus_window_delete_event_callback), NULL);
828

829 830
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
831

832
	g_signal_connect (window, "configure_event",
Alexander Larsson's avatar
Alexander Larsson committed
833
			  G_CALLBACK (nautilus_window_configure_event_callback), NULL);
834 835

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

838
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
839

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

845
	return window;
846
}
847

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

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