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

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

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

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

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

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

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

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

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

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

	application = g_object_new (NAUTILUS_TYPE_APPLICATION, NULL);
	
	bonobo_generic_factory_construct_noreg (BONOBO_GENERIC_FACTORY (application),
						FACTORY_IID,
						NULL);
	
	return application;
168
169
170
}

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

	application = NAUTILUS_APPLICATION (object);

177
	nautilus_bookmarks_exiting ();
178
	
179
180
	bonobo_object_unref (BONOBO_OBJECT (application->undo_manager));

181
	EEL_CALL_PARENT (BONOBO_OBJECT_CLASS, destroy, (object));
182
}
183

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

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

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

213
	failed_count = eel_string_list_get_length (directories);
214

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

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

237
		g_free (directories_as_string);
238
		g_free (error_string);
239
		g_free (dialog_title);
240
241
	}

242
	eel_string_list_free (directories);
243
244

	return failed_count == 0;
245
246
}

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

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

	return uri_list;
}

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
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);
}

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
361
362
363
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;
}

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

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

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

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

	num_failures = 0;
397

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

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

415
416
	CORBA_exception_init (&ev);

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

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

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

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

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

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

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

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

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

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

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

static void
580
581
nautilus_application_create_desktop_window (NautilusApplication *application)
{
582
583
	g_return_if_fail (nautilus_application_desktop_window == NULL);
	g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
584

585
586
	nautilus_application_desktop_window = nautilus_desktop_window_new (application);
	gtk_widget_show (GTK_WIDGET (nautilus_application_desktop_window));
587
588
589
590
}

void
nautilus_application_open_desktop (NautilusApplication *application)
591
{
592
593
	if (nautilus_application_desktop_window == NULL) {
		nautilus_application_create_desktop_window (application);
594
595
596
597
598
599
	}
}

void
nautilus_application_close_desktop (void)
{
600
	if (nautilus_application_desktop_window != NULL) {
601
		gtk_widget_destroy (GTK_WIDGET (nautilus_application_desktop_window));
602
		nautilus_application_desktop_window = NULL;
603
	}
604
605
}

606
607
608
void
nautilus_application_close_all_windows (void)
{
609
	while (nautilus_application_window_list != NULL) {
610
611
612
613
		nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
	}
}

614
static void
615
nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
616
{
617
	nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
618
619
}

620
621
622
623
624
625
626
627
628
629
630
631
632
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;
}				       

633
NautilusWindow *
634
nautilus_application_create_window (NautilusApplication *application)
635
{
636
	NautilusWindow *window;
637
638

	g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
639
	
640
	window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
Darin Adler's avatar
Darin Adler committed
641
						  "app", G_OBJECT (application),
642
						  "app_id", "nautilus", NULL));
643
	
644
	g_signal_connect (window, 
645
			    "delete_event", G_CALLBACK (nautilus_window_delete_event_callback),
646
647
                    	    NULL);

648
	g_signal_connect (window,
649
			    "destroy", G_CALLBACK (nautilus_application_destroyed_window),
650
651
			    application);

652
	nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
653

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

659
	return window;
660
}
661

662
663
664
665
/* callback for changing the directory the desktop points to */
static void
desktop_location_changed_callback (gpointer user_data)
{
666
667
668
	if (nautilus_application_desktop_window != NULL) {
		nautilus_desktop_window_update_directory
			(nautilus_application_desktop_window);
669
	}
670
671
}

672
673
674
675
676
677
678
/* 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);
679
	if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
680
681
682
683
		nautilus_application_open_desktop (application);
	} else {
		nautilus_application_close_desktop ();
	}
684
685
686
687
688

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

Ramiro Estrugo's avatar
Ramiro Estrugo committed
691
692
693
694
695
696
/*
 * 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
697
 * presented is: ~/.nautilus/first-time-flag.
Ramiro Estrugo's avatar
Ramiro Estrugo committed
698
699
700
701
702
 *
 * 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
703
 * to kill in order to test the startup druid:
Ramiro Estrugo's avatar
Ramiro Estrugo committed
704
 *
705
 * rm -f ~/.nautilus/first-time-flag
Ramiro Estrugo's avatar
Ramiro Estrugo committed
706
707
708
709
710
711
712
713
714
715
716
 *
 * 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;
717
	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
718
719
	user_directory = nautilus_get_user_directory ();

720
	druid_flag_file_name = g_strconcat (user_directory, "/first-time-flag", NULL);
721
	result = !g_file_exists (druid_flag_file_name);	
Ramiro Estrugo's avatar
Ramiro Estrugo committed
722
723
	g_free (druid_flag_file_name);

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

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

757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
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;
}


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

830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846

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

	gnome_client_set_restart_command (client, 2, restart_argv);
850
	gnome_client_set_priority (client, 40);
851
852

	if (restart && g_getenv ("NAUTILUS_DEBUG") == NULL) {
853
		/* Don't respawn in debug mode */
854
		gnome_client_set_restart_style (client, GNOME_RESTART_IMMEDIATELY);
855
	} else {
856
		gnome_client_set_restart_style (client, GNOME_RESTART_NEVER);
857
	}
858
859
}

860
861
862
863
static void
update_session (gpointer callback_data)
{
	set_session_restart (callback_data,
864
			     eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ADD_TO_SESSION)
865
866
867
868
869
			     /* 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);
870
871
}

872
873
874
875
876
877
878
static void
init_session (void)
{
	GnomeClient *client;

	client = gnome_master_client ();

879
	g_signal_connect (client, "save_yourself",
880
			    (GtkSignalFunc) save_session,
881
			    NULL);
882
	
883
	g_signal_connect (client, "die",
884
885
			    (GtkSignalFunc) removed_from_session,
			    NULL);
886
	
887
	eel_preferences_add_callback
888
889
		(NAUTILUS_PREFERENCES_ADD_TO_SESSION,
		 update_session, client);
890

891
892
	update_session (client);
}
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
1011
1012
1013
1014
1015
1016
1017

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 ());
}
1018
1019
1020
1021

static void
nautilus_application_class_init (NautilusApplicationClass *klass)
{
1022
	BONOBO_OBJECT_CLASS (klass)->destroy = nautilus_application_destroy;
1023
1024
1025
	
	BONOBO_GENERIC_FACTORY_CLASS (klass)->epv.createObject = create_object;
}