nautilus-application.c 30 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
#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>
63
#include <libnautilus-private/nautilus-bonobo-extensions.h>
64
65
#include <libnautilus-private/nautilus-undo-manager.h>
#include <libnautilus-private/nautilus-volume-monitor.h>
66
#include <libnautilus-private/nautilus-authn-manager.h>
67
#include <bonobo-activation/bonobo-activation.h>
68

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	application = NAUTILUS_APPLICATION (object);

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

178
	EEL_CALL_PARENT (BONOBO_OBJECT_CLASS, destroy, (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
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
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);
}

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
357
358
359
360
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;
}

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

367
	/* initialize URI authentication manager */
368
	nautilus_authentication_manager_init ();
369

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

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

	num_failures = 0;
394

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

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

412
413
	CORBA_exception_init (&ev);

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

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

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

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

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

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

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

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

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

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

572
 out:
573
	CORBA_exception_free (&ev);
574
575
576
}

static void
577
578
nautilus_application_create_desktop_window (NautilusApplication *application)
{
579
580
	g_return_if_fail (nautilus_application_desktop_window == NULL);
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
581

582
583
	nautilus_application_desktop_window = nautilus_desktop_window_new (application);
	gtk_widget_show (GTK_WIDGET (nautilus_application_desktop_window));
584
585
586
587
}

void
nautilus_application_open_desktop (NautilusApplication *application)
588
{
589
590
	if (nautilus_application_desktop_window == NULL) {
		nautilus_application_create_desktop_window (application);
591
592
593
594
595
596
	}
}

void
nautilus_application_close_desktop (void)
{
597
	if (nautilus_application_desktop_window != NULL) {
598
		gtk_widget_destroy (GTK_WIDGET (nautilus_application_desktop_window));
599
		nautilus_application_desktop_window = NULL;
600
	}
601
602
}

603
604
605
void
nautilus_application_close_all_windows (void)
{
606
	while (nautilus_application_window_list != NULL) {
607
608
609
610
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

611
static void
612
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
613
{
614
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
615
616
}

617
618
619
620
621
622
623
624
625
626
627
628
629
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;
}				       

630
NautilusWindow *
631
nautilus_application_create_window (NautilusApplication *application)
632
{
633
	NautilusWindow *window;
634
635

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
636
	
637
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
638
						  "app", application,
639
						  "app_id", "nautilus", NULL));
640
	
641
642
	g_signal_connect (window, "delete_event",
			  G_CALLBACK (nautilus_window_delete_event_callback), NULL);
643

644
645
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
646

647
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
648

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

654
	return window;
655
}
656

657
658
659
660
/* callback for changing the directory the desktop points to */
static void
desktop_location_changed_callback (gpointer user_data)
{
661
662
663
	if (nautilus_application_desktop_window != NULL) {
		nautilus_desktop_window_update_directory
			(nautilus_application_desktop_window);
664
	}
665
666
}

667
668
669
670
671
672
673
/* 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);
674
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
675
676
677
678
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
679
680
681
682
683

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

Ramiro Estrugo's avatar
Ramiro Estrugo committed
686
687
688
689
690
691
/*
 * 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
692
 * presented is: ~/.nautilus/first-time-flag.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
693
694
 *
 * Another alternative could be to use preferences to store this flag
Darin Adler's avatar
Darin Adler committed
695
 * However, there because of bug 41229 this is not yet possible.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
696
697
 *
 * Also, for debugging purposes, it is convenient to have just one file
698
 * to kill in order to test the startup druid:
Ramiro Estrugo's avatar
Ramiro Estrugo committed
699
 *
700
 * rm -f ~/.nautilus/first-time-flag
Ramiro Estrugo's avatar
Ramiro Estrugo committed
701
702
703
704
705
706
707
708
709
710
711
 *
 * 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;
712
	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
713
714
	user_directory = nautilus_get_user_directory ();

715
	druid_flag_file_name = g_strconcat (user_directory, "/first-time-flag", NULL);
716
	result = !g_file_test (druid_flag_file_name, G_FILE_TEST_EXISTS);
Ramiro Estrugo's avatar
Ramiro Estrugo committed
717
718
	g_free (druid_flag_file_name);

719
720
721
722
	/* 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) {
723
724
		druid_flag_file_name = g_strconcat (user_directory, "/first-time-wizard-flag", NULL);
		unlink (druid_flag_file_name);
725
726
727
		g_free (druid_flag_file_name);
	}
	g_free (user_directory); 
Ramiro Estrugo's avatar
Ramiro Estrugo committed
728
729
	return result;
}
730
731
732
733
734

static void
volume_mounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
			 NautilusApplication *application)
{
735
736
737
738
739
740
741
742
	NautilusWindow *window;
	char *uri;
	
	if (volume == NULL || application == NULL) {
		return;
	}
	
	/* Open a window to the CD if the user has set that preference. */
743
	if (nautilus_volume_get_device_type (volume) == NAUTILUS_DEVICE_CDROM_DRIVE
744
745
		&& gnome_config_get_bool ("/magicdev/Options/do_fileman_window=true")) {		
		window = nautilus_application_create_window (application);
746
		uri = gnome_vfs_get_uri_from_local_path (nautilus_volume_get_mount_path (volume));
747
748
749
		nautilus_window_go_to (window, uri);
		g_free (uri);
	}
750
751
}

752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
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;
}


779
780
781
782
783
784
785
786
/* 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)
{
787
	GList *window_list, *node, *close_list;
788
	NautilusWindow *window;
789
790
	char *uri;
	char *path;
791
792
793
794
		
	close_list = NULL;
	
	/* Check and see if any of the open windows are displaying contents from the unmounted volume */
795
	window_list = nautilus_application_get_window_list ();
796
	
797
798
	/* 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) {
799
		window = NAUTILUS_WINDOW (node->data);
800
		if (window != NULL && window_can_be_closed (window)) {
801
802
			uri = nautilus_window_get_location (window);
			path = gnome_vfs_get_local_path_from_uri (uri);
803
			if (eel_str_has_prefix (path, nautilus_volume_get_mount_path (volume))) {
804
				close_list = g_list_prepend (close_list, window);
805
			}
806
807
			g_free (path);
			g_free (uri);
808
809
		}
	}
810
811
		
	/* Handle the windows in the close list. */
812
	for (node = close_list; node != NULL; node = node->next) {
813
814
815
816
817
818
819
		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);
		}
820
	}
821
		
822
	g_list_free (close_list);
823
824
}

825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841

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)
{
842
843
844
	static char *restart_argv[] = { "nautilus", "--no-default-window", 0 };

	gnome_client_set_restart_command (client, 2, restart_argv);
845
	gnome_client_set_priority (client, 40);
846
847

	if (restart && g_getenv ("NAUTILUS_DEBUG") == NULL) {
848
		/* Don't respawn in debug mode */
849
		gnome_client_set_restart_style (client, GNOME_RESTART_IMMEDIATELY);
850
	} else {
851
		gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
852
	}
853
854
}

855
856
857
858
static void
update_session (gpointer callback_data)
{
	set_session_restart (callback_data,
859
			     eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ADD_TO_SESSION)
860
861
862
863
864
			     /* 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);
865
866
}

867
868
869
870
871
872
873
static void
init_session (void)
{
	GnomeClient *client;

	client = gnome_master_client ();

874
	g_signal_connect (client, "save_yourself",
875
			  G_CALLBACK (save_session), NULL);
876
	
877
	g_signal_connect (client, "die",
878
			  G_CALLBACK (removed_from_session), NULL);
879
	
880
	eel_preferences_add_callback
881
882
		(NAUTILUS_PREFERENCES_ADD_TO_SESSION,
		 update_session, client);
883

884
885
	update_session (client);
}
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
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

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 ());
}
1011
1012

static void
1013
nautilus_application_class_init (NautilusApplicationClass *class)
1014
{
1015
1016
	BONOBO_OBJECT_CLASS (class)->destroy = nautilus_application_destroy;
	BONOBO_GENERIC_FACTORY_CLASS (class)->epv.createObject = create_object;
1017
}