nautilus-application.c 30.7 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 <gtk/gtksignal.h>
49
#include <libgnome/gnome-config.h>
50
51
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
52
#include <libgnomeui/gnome-client.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
53
#include <libgnomeui/gnome-messagebox.h>
54
#include <libgnomeui/gnome-stock-icons.h>
55
#include <libgnomevfs/gnome-vfs-mime-handlers.h>
56
#include <libgnomevfs/gnome-vfs-ops.h>
Ramiro Estrugo's avatar
Ramiro Estrugo committed
57
#include <libgnomevfs/gnome-vfs-utils.h>
58
59
60
61
62
63
64
#include <libnautilus-private/nautilus-file-utilities.h>
#include <libnautilus-private/nautilus-global-preferences.h>
#include <libnautilus-private/nautilus-icon-factory.h>
#include <libnautilus-private/nautilus-metafile-factory.h>
#include <libnautilus-private/nautilus-sound.h>
#include <libnautilus-private/nautilus-undo-manager.h>
#include <libnautilus-private/nautilus-volume-monitor.h>
65
#include <libnautilus-private/nautilus-authn-manager.h>
66
#include <bonobo-activation/bonobo-activation.h>
67

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

72
#define FACTORY_IID	     "OAFIID:nautilus_factory:bd1e1862-92d7-4391-963e-37583f0daef3"
73
#define SEARCH_LIST_VIEW_IID "OAFIID:nautilus_file_manager_search_list_view:b186e381-198e-43cf-9c46-60b6bb35db0b"
74
#define SHELL_IID	     "OAFIID:nautilus_shell:cd5183b2-3913-4b74-9b8e-10528b0de08d"
75

76
77
78
79
80
81
/* 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;

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
static void     nautilus_application_init         (NautilusApplication      *application);
static void     nautilus_application_class_init   (NautilusApplicationClass *klass);
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
99
EEL_BONOBO_BOILERPLATE (NautilusApplication,
			nautilus_application,
			BONOBO_GENERIC_FACTORY_TYPE)
100
101

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

110
	if (strcmp (iid, NAUTILUS_ICON_VIEW_IID) == 0) {
111
		directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_icon_view_get_type (), NULL));
112
		object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
113
	} else if (strcmp (iid, NAUTILUS_DESKTOP_ICON_VIEW_IID) == 0) {
114
		directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_desktop_icon_view_get_type (), NULL));
115
		object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
116
#if GNOME2_CONVERSION_COMPLETE
117
	} else if (strcmp (iid, NAUTILUS_LIST_VIEW_IID) == 0) {
118
		directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_list_view_get_type (), NULL));
119
		object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
120
	} else if (strcmp (iid, SEARCH_LIST_VIEW_IID) == 0) {
121
		directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_search_list_view_get_type (), NULL));
122
		object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
123
#endif
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
		return CORBA_OBJECT_NIL;
Gene Z. Ragan's avatar
CVS:    
Gene Z. Ragan committed
131
	}
132
133

	return CORBA_Object_duplicate (bonobo_object_corba_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_init (NautilusApplication *application)
144
{
145
146
	/* Create an undo manager */
	application->undo_manager = nautilus_undo_manager_new ();
147

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

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

161
162
NautilusApplication *
nautilus_application_new (void)
163
{
164
	return NAUTILUS_APPLICATION (g_object_new (nautilus_application_get_type (), NULL));
165
166
167
}

static void
168
nautilus_application_finalize (GObject *object)
169
{
170
171
172
173
	NautilusApplication *application;

	application = NAUTILUS_APPLICATION (object);

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

178
	EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
179
}
180

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

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

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

210
	failed_count = eel_string_list_get_length (directories);
211

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

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

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

239
	eel_string_list_free (directories);
240
241

	return failed_count == 0;
242
243
}

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

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

	return uri_list;
}

277
/* Find ~/.gnome-desktop/Trash and rename it to ~/.gnome-desktop/Trash.gmc
278
279
280
281
282
283
284
285
286
287
288
289
290
291
 * Only if it is a directory
 */
static void
migrate_gmc_trash (void)
{
	char *dp, *trash_dir, *dest;
	struct stat buf;

	dp = nautilus_get_desktop_directory ();
	trash_dir = g_strconcat (dp, "/", "Trash", NULL);
	dest = g_strconcat (dp, "/", "Trash.gmc", NULL);
	
	if (stat (trash_dir, &buf) == 0 && S_ISDIR (buf.st_mode)) {
		rename (trash_dir, dest);
292
#if GNOME2_CONVERSION_COMPLETE
293
		gnome_metadata_rename (trash_dir, dest);
294
#endif
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
	}
	
	g_free (dp);
	g_free (trash_dir);
	g_free (dest);
}

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

357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
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 ();
	desktop_link_file = nautilus_make_path (desktop_path,
						"starthere.desktop");

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

386
387
388
389
static void
finish_startup (NautilusApplication *application)
{
	/* initialize the sound machinery */
390
	nautilus_sound_init ();
391

392
	/* initialize URI authentication manager */
393
	nautilus_authentication_manager_init ();
394

395
396
397
398
399
	/* Make the desktop work with gmc and old Nautilus. */
	migrate_gmc_trash ();
	migrate_old_nautilus_files ();
}

400
void
401
nautilus_application_startup (NautilusApplication *application,
402
			      gboolean kill_shell,
403
			      gboolean restart_shell,
404
			      gboolean no_default_window,
405
			      gboolean no_desktop,
406
			      gboolean do_first_time_druid_check,
407
			      const char *geometry,
408
			      const char *urls[])
409
{
410
411
	CORBA_Environment ev;
	Nautilus_Shell shell;
412
	Bonobo_RegistrationResult result;
413
	const char *message, *detailed_message;
414
	GtkDialog *dialog;
415
	Nautilus_URIList *url_list;
416
	const CORBA_char *corba_geometry;
417
418
419
	int num_failures;

	num_failures = 0;
420

421
	/* Check the user's ~/.nautilus directories and post warnings
422
	 * if there are problems.
423
	 */
424
	if (!kill_shell && !check_required_directories (application)) {
425
426
		return;
	}
427

428
429
	/* Run the first time startup druid if needed. */
	if (do_first_time_druid_check && need_to_show_first_time_druid ()) {
430
431
432
433
434
435
		/* 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 ();
436
	}
437

438
439
	CORBA_exception_init (&ev);

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

		/* Get the shell object. */
		if (message == NULL) {
510
			shell = bonobo_activation_activate_from_id (SHELL_IID, 0, NULL, NULL);
511
512
513
514
515
516
517
			if (!CORBA_Object_is_nil (shell, &ev)) {
				break;
			}

			/* If we couldn't find ourselves it's a bad problem so
			 * we better stop looping.
			 */
518
			if (result == Bonobo_ACTIVATION_REG_SUCCESS) {
519
				/* FIXME bugzilla.gnome.org 42538: When can this happen? */
520
521
				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 "
522
523
						     "from Bonobo when attempting to locate the factory."
						     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
524
525
526
527
528
			} 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 "
529
530
							     "from Bonobo when attempting to locate the shell object. "
							     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
531
532
					
				}
533
534
535
			}
		}

536
		if (message != NULL) {
Ramiro Estrugo's avatar
Ramiro Estrugo committed
537
			dialog = eel_show_error_dialog_with_details (message, NULL, detailed_message, NULL);
538
539
			/* We need the main event loop so the user has a chance to see the dialog. */
			nautilus_main_event_loop_register (GTK_OBJECT (dialog));
540
			goto out;
541
542
543
		}
	}

544
545
	if (kill_shell) {
		Nautilus_Shell_quit (shell, &ev);
546
547
	} else if (restart_shell) {
		Nautilus_Shell_restart (shell, &ev);
548
	} else {
549
		/* If KDE desktop is running, then force no_desktop */
550
		if (is_kdesktop_present ()) {
551
			no_desktop = TRUE;
552
		}
553
		
554
		if (!no_desktop && eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
555
556
			Nautilus_Shell_start_desktop (shell, &ev);
		}
557
558
		
		/* Monitor the preference to show or hide the desktop */
559
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_SHOW_DESKTOP,
Darin Adler's avatar
Darin Adler committed
560
561
562
							  desktop_changed_callback,
							  application,
							  G_OBJECT (application));
563

564
565
		/* Monitor the preference to have the desktop */
		/* point to the Unix home folder */
566
		eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
Darin Adler's avatar
Darin Adler committed
567
568
569
							  desktop_location_changed_callback,
							  NULL,
							  G_OBJECT (application));
570

571
572
573
574
		/* CORBA C mapping doesn't allow NULL to be passed
		   for string parameters */
		corba_geometry = (geometry != NULL) ? geometry : "";

575
576
	  	/* Create the other windows. */
		if (urls != NULL) {
577
			url_list = nautilus_make_uri_list_from_shell_strv (urls);
578
			Nautilus_Shell_open_windows (shell, url_list, corba_geometry, &ev);
579
			CORBA_free (url_list);
580
		} else if (!no_default_window) {
581
			Nautilus_Shell_open_default_window (shell, corba_geometry, &ev);
582
		}
583
584
585
		
		/* Add ourselves to the session */
		init_session ();
586
	}
587

588
	/* We're done with the shell now, so let it go. */
589
590
591
592
593
	/* 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.
	 */
594
	if (!(kill_shell || restart_shell)) {
595
		bonobo_object_release_unref (shell, NULL);
596
	}
597

598
 out:
599
	CORBA_exception_free (&ev);
600
601
602
}

static void
603
604
nautilus_application_create_desktop_window (NautilusApplication *application)
{
605
606
	g_return_if_fail (nautilus_application_desktop_window == NULL);
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
607

608
609
	nautilus_application_desktop_window = nautilus_desktop_window_new (application);
	gtk_widget_show (GTK_WIDGET (nautilus_application_desktop_window));
610
611
612
613
}

void
nautilus_application_open_desktop (NautilusApplication *application)
614
{
615
616
	if (nautilus_application_desktop_window == NULL) {
		nautilus_application_create_desktop_window (application);
617
618
619
620
621
622
	}
}

void
nautilus_application_close_desktop (void)
{
623
	if (nautilus_application_desktop_window != NULL) {
624
		gtk_widget_destroy (GTK_WIDGET (nautilus_application_desktop_window));
625
		nautilus_application_desktop_window = NULL;
626
	}
627
628
}

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

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

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

656
NautilusWindow *
657
nautilus_application_create_window (NautilusApplication *application)
658
{
659
	NautilusWindow *window;
660
661

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
662
	
663
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
Darin Adler's avatar
Darin Adler committed
664
						  "app", G_OBJECT (application),
665
						  "app_id", "nautilus", NULL));
666
	
667
	g_signal_connect (G_OBJECT (window), 
668
			    "delete_event", G_CALLBACK (nautilus_window_delete_event_callback),
669
670
                    	    NULL);

671
	g_signal_connect (G_OBJECT (window),
672
			    "destroy", G_CALLBACK (nautilus_application_destroyed_window),
673
674
			    application);

675
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
676

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

682
	return window;
683
}
684

685
686
687
688
/* callback for changing the directory the desktop points to */
static void
desktop_location_changed_callback (gpointer user_data)
{
689
690
691
	if (nautilus_application_desktop_window != NULL) {
		nautilus_desktop_window_update_directory
			(nautilus_application_desktop_window);
692
	}
693
694
}

695
696
697
698
699
700
701
/* 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);
702
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
703
704
705
706
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
707
708
709
710
711

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

Ramiro Estrugo's avatar
Ramiro Estrugo committed
714
715
716
717
718
719
/*
 * 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
720
 * presented is: ~/.nautilus/first-time-flag.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
721
722
723
724
725
 *
 * Another alternative could be to use preferences to store this flag
 * However, there because of bug 1229 this is not yet possible.
 *
 * Also, for debugging purposes, it is convenient to have just one file
726
 * to kill in order to test the startup druid:
Ramiro Estrugo's avatar
Ramiro Estrugo committed
727
 *
728
 * rm -f ~/.nautilus/first-time-flag
Ramiro Estrugo's avatar
Ramiro Estrugo committed
729
730
731
732
733
734
735
736
737
738
739
 *
 * 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;
740
	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
741
742
	user_directory = nautilus_get_user_directory ();

743
	druid_flag_file_name = g_strconcat (user_directory, "/first-time-flag", NULL);
744
	result = !g_file_exists (druid_flag_file_name);	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
745
746
	g_free (druid_flag_file_name);

747
748
749
750
	/* 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) {
751
752
		druid_flag_file_name = g_strconcat (user_directory, "/first-time-wizard-flag", NULL);
		unlink (druid_flag_file_name);
753
754
755
		g_free (druid_flag_file_name);
	}
	g_free (user_directory); 
Ramiro Estrugo's avatar
Ramiro Estrugo committed
756
757
	return result;
}
758
759
760
761
762

static void
volume_mounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
			 NautilusApplication *application)
{
763
764
765
766
767
768
769
770
	NautilusWindow *window;
	char *uri;
	
	if (volume == NULL || application == NULL) {
		return;
	}
	
	/* Open a window to the CD if the user has set that preference. */
771
	if (nautilus_volume_get_device_type (volume) == NAUTILUS_DEVICE_CDROM_DRIVE
772
773
		&& gnome_config_get_bool ("/magicdev/Options/do_fileman_window=true")) {		
		window = nautilus_application_create_window (application);
774
		uri = gnome_vfs_get_uri_from_local_path (nautilus_volume_get_mount_path (volume));
775
776
777
		nautilus_window_go_to (window, uri);
		g_free (uri);
	}
778
779
}

780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
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;
}


807
808
809
810
811
812
813
814
/* 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)
{
815
	GList *window_list, *node, *close_list;
816
	NautilusWindow *window;
817
818
	char *uri;
	char *path;
819
820
821
822
		
	close_list = NULL;
	
	/* Check and see if any of the open windows are displaying contents from the unmounted volume */
823
	window_list = nautilus_application_get_window_list ();
824
	
825
826
	/* 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) {
827
		window = NAUTILUS_WINDOW (node->data);
828
		if (window != NULL && window_can_be_closed (window)) {
829
830
			uri = nautilus_window_get_location (window);
			path = gnome_vfs_get_local_path_from_uri (uri);
831
			if (eel_str_has_prefix (path, nautilus_volume_get_mount_path (volume))) {
832
				close_list = g_list_prepend (close_list, window);
833
			}
834
835
			g_free (path);
			g_free (uri);
836
837
		}
	}
838
839
		
	/* Handle the windows in the close list. */
840
	for (node = close_list; node != NULL; node = node->next) {
841
842
843
844
845
846
847
		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);
		}
848
	}
849
		
850
	g_list_free (close_list);
851
852
}

853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869

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)
{
	return TRUE;
}

static void
set_session_restart (GnomeClient *client, gboolean restart)
{
870
871
872
	static char *restart_argv[] = { "nautilus", "--no-default-window", 0 };

	gnome_client_set_restart_command (client, 2, restart_argv);
873
	gnome_client_set_priority (client, 40);
874
875

	if (restart && g_getenv ("NAUTILUS_DEBUG") == NULL) {
876
		/* Don't respawn in debug mode */
877
		gnome_client_set_restart_style (client, GNOME_RESTART_IMMEDIATELY);
878
	} else {
879
		gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
880
	}
881
882
}

883
884
885
886
static void
update_session (gpointer callback_data)
{
	set_session_restart (callback_data,
887
			     eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ADD_TO_SESSION)
888
889
890
891
892
			     /* 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);
893
894
}

895
896
897
898
899
900
901
static void
init_session (void)
{
	GnomeClient *client;

	client = gnome_master_client ();

902
	g_signal_connect (G_OBJECT (client), "save_yourself",
903
			    (GtkSignalFunc) save_session,
904
			    NULL);
905
	
906
	g_signal_connect (G_OBJECT (client), "die",
907
908
			    (GtkSignalFunc) removed_from_session,
			    NULL);
909
	
910
	eel_preferences_add_callback
911
912
		(NAUTILUS_PREFERENCES_ADD_TO_SESSION,
		 update_session, client);
913

914
915
	update_session (client);
}
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
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
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
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

static gboolean
get_self_typed_prop (Window      xwindow,
                     Atom        atom,
                     gulong     *val)
{  
	Atom type;
	int format;
	gulong nitems;
	gulong bytes_after;
	gulong *num;
	int err;
  
	gdk_error_trap_push ();
	type = None;
	XGetWindowProperty (gdk_display,
			    xwindow,
			    atom,
			    0, G_MAXLONG,
			    False, atom, &type, &format, &nitems,
			    &bytes_after, (guchar **)&num);  

	err = gdk_error_trap_pop ();
	if (err != Success) {
		return FALSE;
	}
  
	if (type != atom) {
		return FALSE;
	}

	if (val)
		*val = *num;
  
	XFree (num);

	return TRUE;
}

static gboolean
has_wm_state (Window xwindow)
{
	return get_self_typed_prop (xwindow,
				    XInternAtom (gdk_display, "WM_STATE", False),
				    NULL);
}

static gboolean
look_for_kdesktop_recursive (Window xwindow)
{
  
	Window ignored1, ignored2;
	Window *children;
	unsigned int n_children;
	unsigned int i;
	gboolean retval;
  
	/* If WM_STATE is set, this is a managed client, so look
	 * for the class hint and end recursion. Otherwise,
	 * this is probably just a WM frame, so keep recursing.
	 */
	if (has_wm_state (xwindow)) {      
		XClassHint ch;
      
		gdk_error_trap_push ();
		ch.res_name = NULL;
		ch.res_class = NULL;
      
		XGetClassHint (gdk_display, xwindow, &ch);
      
		gdk_error_trap_pop ();
      
		if (ch.res_name)
			XFree (ch.res_name);
      
		if (ch.res_class) {
			if (strcmp (ch.res_class, "kdesktop") == 0) {
				XFree (ch.res_class);
				return TRUE;
			}
			else
				XFree (ch.res_class);
		}

		return FALSE;
	}
  
	retval = FALSE;
  
	gdk_error_trap_push ();
  
	XQueryTree (gdk_display,
		    xwindow,
		    &ignored1, &ignored2, &children, &n_children);

	if (gdk_error_trap_pop ()) {
		return FALSE;
	}

	i = 0;
	while (i < n_children) {
		if (look_for_kdesktop_recursive (children[i])) {
			retval = TRUE;
			break;
		}
      
		++i;
	}
  
	if (children)
		XFree (children);

	return retval;
}

static gboolean
is_kdesktop_present (void)
{
	/* FIXME this is a pretty lame hack, should be replaced
	 * eventually with e.g. a requirement that desktop managers
	 * support a manager selection, ICCCM sec 2.8
	 */

	return look_for_kdesktop_recursive (GDK_ROOT_WINDOW ());
}
1041
1042
1043
1044
1045
1046
1047
1048

static void
nautilus_application_class_init (NautilusApplicationClass *klass)
{
	G_OBJECT_CLASS (klass)->finalize = nautilus_application_finalize;
	
	BONOBO_GENERIC_FACTORY_CLASS (klass)->epv.createObject = create_object;
}