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
74
75
#define FACTORY_IID	     "OAFIID:Nautilus_Factory"
#define SEARCH_LIST_VIEW_IID "OAFIID:Nautilus_File_Manager_Search_List_View"
#define SHELL_IID	     "OAFIID:Nautilus_Shell"
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
	static gboolean create_in_progress = FALSE;

581
582
	g_return_if_fail (nautilus_application_desktop_window == NULL);
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
583

584
585
586
587
588
589
	if (create_in_progress) {
		return;
	}

	create_in_progress = TRUE;

590
	nautilus_application_desktop_window = nautilus_desktop_window_new (application);
591
592
593

	create_in_progress = FALSE;

594
	gtk_widget_show (GTK_WIDGET (nautilus_application_desktop_window));
595
596
597
598
}

void
nautilus_application_open_desktop (NautilusApplication *application)
599
{
600
601
	if (nautilus_application_desktop_window == NULL) {
		nautilus_application_create_desktop_window (application);
602
603
604
605
606
607
	}
}

void
nautilus_application_close_desktop (void)
{
608
	if (nautilus_application_desktop_window != NULL) {
609
		gtk_widget_destroy (GTK_WIDGET (nautilus_application_desktop_window));
610
		nautilus_application_desktop_window = NULL;
611
	}
612
613
}

614
615
616
void
nautilus_application_close_all_windows (void)
{
617
	while (nautilus_application_window_list != NULL) {
618
619
620
621
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

622
static void
623
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
624
{
625
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
626
627
}

628
629
630
631
632
633
634
635
636
637
638
639
640
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;
}				       

641
NautilusWindow *
642
nautilus_application_create_window (NautilusApplication *application)
643
{
644
	NautilusWindow *window;
645
646

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
647
	
648
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
649
						  "app", application,
650
						  "app_id", "nautilus", NULL));
651
	
652
653
	g_signal_connect (window, "delete_event",
			  G_CALLBACK (nautilus_window_delete_event_callback), NULL);
654

655
656
	g_signal_connect_object (window, "destroy",
				 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
657

658
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
659

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

665
	return window;
666
}
667

668
669
670
671
/* callback for changing the directory the desktop points to */
static void
desktop_location_changed_callback (gpointer user_data)
{
672
673
674
	if (nautilus_application_desktop_window != NULL) {
		nautilus_desktop_window_update_directory
			(nautilus_application_desktop_window);
675
	}
676
677
}

678
679
680
681
682
683
684
/* 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);
685
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
686
687
688
689
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
690
691
692
693
694

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

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

726
	druid_flag_file_name = g_strconcat (user_directory, "/first-time-flag", NULL);
727
	result = !g_file_test (druid_flag_file_name, G_FILE_TEST_EXISTS);
Ramiro Estrugo's avatar
Ramiro Estrugo committed
728
729
	g_free (druid_flag_file_name);

730
731
732
733
	/* 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) {
734
735
		druid_flag_file_name = g_strconcat (user_directory, "/first-time-wizard-flag", NULL);
		unlink (druid_flag_file_name);
736
737
738
		g_free (druid_flag_file_name);
	}
	g_free (user_directory); 
Ramiro Estrugo's avatar
Ramiro Estrugo committed
739
740
	return result;
}
741
742
743
744
745

static void
volume_mounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
			 NautilusApplication *application)
{
746
747
748
749
750
751
752
753
	NautilusWindow *window;
	char *uri;
	
	if (volume == NULL || application == NULL) {
		return;
	}
	
	/* Open a window to the CD if the user has set that preference. */
754
	if (nautilus_volume_get_device_type (volume) == NAUTILUS_DEVICE_CDROM_DRIVE
755
756
		&& gnome_config_get_bool ("/magicdev/Options/do_fileman_window=true")) {		
		window = nautilus_application_create_window (application);
757
		uri = gnome_vfs_get_uri_from_local_path (nautilus_volume_get_mount_path (volume));
758
759
760
		nautilus_window_go_to (window, uri);
		g_free (uri);
	}
761
762
}

763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
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;
}


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

836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852

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

	gnome_client_set_restart_command (client, 2, restart_argv);
856
	gnome_client_set_priority (client, 40);
857
858

	if (restart && g_getenv ("NAUTILUS_DEBUG") == NULL) {
859
		/* Don't respawn in debug mode */
860
		gnome_client_set_restart_style (client, GNOME_RESTART_IMMEDIATELY);
861
	} else {
862
		gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
863
	}
864
865
}

866
867
868
869
static void
update_session (gpointer callback_data)
{
	set_session_restart (callback_data,
870
			     eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ADD_TO_SESSION)
871
872
873
874
875
			     /* 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);
876
877
}

878
879
880
881
882
883
884
static void
init_session (void)
{
	GnomeClient *client;

	client = gnome_master_client ();

885
	g_signal_connect (client, "save_yourself",
886
			  G_CALLBACK (save_session), NULL);
887
	
888
	g_signal_connect (client, "die",
889
			  G_CALLBACK (removed_from_session), NULL);
890
	
891
	eel_preferences_add_callback
892
893
		(NAUTILUS_PREFERENCES_ADD_TO_SESSION,
		 update_session, client);
894

895
896
	update_session (client);
}
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
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021

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 ());
}
1022
1023

static void
1024
nautilus_application_class_init (NautilusApplicationClass *class)
1025
{
1026
1027
	BONOBO_OBJECT_CLASS (class)->destroy = nautilus_application_destroy;
	BONOBO_GENERIC_FACTORY_CLASS (class)->epv.createObject = create_object;
1028
}