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

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

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

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

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

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

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

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

84
85
86
87
88
89
90
91
92
93
94
95
96
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);

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

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

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

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

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

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

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

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

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

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

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

	application = NAUTILUS_APPLICATION (object);

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

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

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

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

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

211
	failed_count = eel_string_list_get_length (directories);
212

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

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

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

240
	eel_string_list_free (directories);
241
242

	return failed_count == 0;
243
244
}

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

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

	return uri_list;
}

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

		closedir (dir);

		g_free (new_desktop_dir);
	}

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

	g_free (old_desktop_dir);
}

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

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

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

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

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

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

	num_failures = 0;
396

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

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

414
415
	CORBA_exception_init (&ev);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	create_in_progress = TRUE;

592
	nautilus_application_desktop_window = nautilus_desktop_window_new (application);
593
594
595
596
597
	/* We realize it immediately so that the NAUTILUS_DESKTOP_WINDOW_ID
	   property is set so gnome-settings-daemon doesn't try to set the
	   background. And we do a gdk_flush() to be sure X gets it. */
	gtk_widget_realize (GTK_WIDGET (nautilus_application_desktop_window));
	gdk_flush ();
598
599

	create_in_progress = FALSE;
600
601
602
603
}

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

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

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

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

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

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

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

	return FALSE;
}

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

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

	return FALSE;
}

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

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

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

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

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

745
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
746

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

752
	return window;
753
}
754

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

765
766
767
768
769
770
771
/* 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);
772
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
773
774
775
776
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
777
778
779
780
781

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

Ramiro Estrugo's avatar
Ramiro Estrugo committed
784
785
786
787
788
789
/*
 * 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
790
 * presented is: ~/.nautilus/first-time-flag.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
791
792
 *
 * Another alternative could be to use preferences to store this flag
Darin Adler's avatar
Darin Adler committed
793
 * However, there because of bug 41229 this is not yet possible.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
794
795
 *
 * Also, for debugging purposes, it is convenient to have just one file
796
 * to kill in order to test the startup druid:
Ramiro Estrugo's avatar
Ramiro Estrugo committed
797
 *
798
 * rm -f ~/.nautilus/first-time-flag
Ramiro Estrugo's avatar
Ramiro Estrugo committed
799
800
801
802
803
804
805
806
807
808
809
 *
 * 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;
810
	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
811
812
	user_directory = nautilus_get_user_directory ();

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

817
818
819
820
	/* 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) {
821
822
		druid_flag_file_name = g_strconcat (user_directory, "/first-time-wizard-flag", NULL);
		unlink (druid_flag_file_name);
823
824
825
		g_free (druid_flag_file_name);
	}
	g_free (user_directory); 
Ramiro Estrugo's avatar
Ramiro Estrugo committed
826
827
	return result;
}
828
829
830
831
832

static void
volume_mounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
			 NautilusApplication *application)
{
833
834
	NautilusWindow *window;
	char *uri;
835

836
837
838
	if (volume == NULL || application == NULL) {
		return;
	}
839

840
	/* Open a window to the CD if the user has set that preference. */
841
	if (nautilus_volume_get_device_type (volume) == NAUTILUS_DEVICE_CDROM_DRIVE
842
		&& eel_gconf_get_boolean( "/apps/magicdev/do_fileman_window")) {
843
		window = nautilus_application_create_window (application);
844
		uri = gnome_vfs_get_uri_from_local_path (nautilus_volume_get_mount_path (volume));
845
846
847
		nautilus_window_go_to (window, uri);
		g_free (uri);
	}
848
849
}

850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
static gboolean
window_can_be_closed (NautilusWindow *window)
{
	if (!NAUTILUS_IS_DESKTOP_WINDOW (window)) {
		return TRUE;
	}
	
	return FALSE;
}

static gboolean
is_last_closable_window (NautilusWindow *window)
{
	GList *node, *window_list;
	
	window_list = nautilus_application_get_window_list ();
	
	for (node = window_list; node != NULL; node = node->next) {
		if (window != NAUTILUS_WINDOW (node->data) && window_can_be_closed (NAUTILUS_WINDOW (node->data))) {
			return FALSE;
		}
	}
	
	return TRUE;
}


877
878
879
880
881
882
883
884
/* Called whenever a volume is unmounted. Check and see if there are any windows open
 * displaying contents on the volume. If there are, close them.
 * It would also be cool to save open window and position info.
 */
static void
volume_unmounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
			   NautilusApplication *application)
{
885
	GList *window_list, *node, *close_list;
886
	NautilusWindow *window;
887
888
	char *uri;
	char *path;
889
890
891
892
		
	close_list = NULL;
	
	/* Check and see if any of the open windows are displaying contents from the unmounted volume */
893
	window_list = nautilus_application_get_window_list ();
894
	
895
896
	/* Construct a list of windows to be closed. Do not add the non-closable windows to the list. */
	for (node = window_list; node != NULL; node = node->next) {
897
		window = NAUTILUS_WINDOW (node->data);
898
		if (window != NULL && window_can_be_closed (window)) {
899
900
			uri = nautilus_window_get_location (window);
			path = gnome_vfs_get_local_path_from_uri (uri);
901
			if (eel_str_has_prefix (path, nautilus_volume_get_mount_path (volume))) {
902
				close_list = g_list_prepend (close_list, window);
903
			}
904
905
			g_free (path);
			g_free (uri);
906
907
		}
	}
908
909
		
	/* Handle the windows in the close list. */
910
	for (node = close_list; node != NULL; node = node->next) {
911
912
913
914
915
916
917
		window = NAUTILUS_WINDOW (node->data);
		if (is_last_closable_window (window)) {
			/* Don't close the last or only window. Try to redirect to the default home directory. */		 	
			nautilus_window_go_home (window);
		} else {
			nautilus_window_close (window);
		}
918
	}
919
		
920
	g_list_free (close_list);
921
922
}

923
924
925
926
927
928
929
930
931
932
933

static void
removed_from_session (GnomeClient *client, gpointer data)
{
	nautilus_main_event_loop_quit ();
}

static gint
save_session (GnomeClient *client, gint phase, GnomeSaveStyle save_style, gint shutdown,
	      GnomeInteractStyle interact_style, gint fast, gpointer data)
{
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
	NautilusWindow *window;
	GList *l;
	static char *clone_argv[] = { "nautilus", "--no-default-window" };
	char **restart_argv;
	int argc;
	int i;
	int num_windows;

	num_windows = g_list_length (nautilus_application_window_list);
	if (num_windows > 0) {
		argc = 1 + num_windows;
		i = 0;
		restart_argv = g_new (char *, argc);
		restart_argv[i++] = g_strdup ("nautilus");
		for (l = nautilus_application_window_list; l != NULL; l = l->next) {
			window = NAUTILUS_WINDOW (l->data);
			restart_argv[i++] = nautilus_window_get_location (window);
		}
		
		gnome_client_set_restart_command (client, argc, restart_argv);

		for (i = 0; i < argc; i++) {
			g_free (restart_argv[i]);
		}
		g_free (restart_argv);
	} else {
		gnome_client_set_restart_command (client, 
						  G_N_ELEMENTS (clone_argv), 
						  clone_argv);
	}
	
965
966
967
968
969
970
971
	return TRUE;
}

static void
set_session_restart (GnomeClient *client, gboolean restart)
{
	gnome_client_set_priority (client, 40);
972
973

	if (restart && g_getenv ("NAUTILUS_DEBUG") == NULL) {
974
		/* Don't respawn in debug mode */
975
		gnome_client_set_restart_style (client, GNOME_RESTART_IMMEDIATELY);
976
	} else {
977
		gnome_client_set_restart_style (client, GNOME_RESTART_IF_RUNNING);
978
	}
979
980
}

981
982
983
984
static void
update_session (gpointer callback_data)
{
	set_session_restart (callback_data,
985
			     eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ADD_TO_SESSION)
986
987
988
989
990
			     /* Only ever add ourselves to the session
			      * if we have a desktop window. Prevents the
			      * session thrashing that's seen otherwise
			      */
			     && nautilus_application_desktop_window != NULL);
991
992
}

993
994
995
996
997
998
999
static void
init_session (void)
{
	GnomeClient *client;

	client = gnome_master_client ();

1000
	g_signal_connect (client, "save_yourself",
1001
			  G_CALLBACK (save_session), NULL);
1002
	
1003
	g_signal_connect (client, "die",
1004
			  G_CALLBACK (removed_from_session), NULL);
1005
	
1006
	eel_preferences_add_callback
1007
1008
		(NAUTILUS_PREFERENCES_ADD_TO_SESSION,
		 update_session, client);
1009

1010
1011
	update_session (client);
}
1012

1013
1014
#ifdef UGLY_HACK_TO_DETECT_KDE

1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057