source: trunk/third/nautilus/src/nautilus-application.c @ 18663

Revision 18663, 34.2 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18662, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3/*
4 *  Nautilus
5 *
6 *  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 *  modify it under the terms of the GNU General Public License as
11 *  published by the Free Software Foundation; either version 2 of the
12 *  License, or (at your option) any later version.
13 *
14 *  Nautilus is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 *  General Public License for more details.
18 *
19 *  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 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
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#include "nautilus-desktop-window.h"
36#include "nautilus-first-time-druid.h"
37#include "nautilus-main.h"
38#include "nautilus-shell-interface.h"
39#include "nautilus-shell.h"
40#include <bonobo/bonobo-main.h>
41#include <bonobo/bonobo-object.h>
42#include <dirent.h>
43#include <eel/eel-gtk-macros.h>
44#include <eel/eel-stock-dialogs.h>
45#include <eel/eel-string-list.h>
46#include <eel/eel-string.h>
47#include <eel/eel-vfs-extensions.h>
48#include <eel/eel-gtk-extensions.h>
49#include <gdk/gdkx.h>
50#include <gtk/gtksignal.h>
51#include <libgnome/gnome-config.h>
52#include <libgnome/gnome-i18n.h>
53#include <libgnome/gnome-util.h>
54#include <libgnomeui/gnome-client.h>
55#include <libgnomeui/gnome-messagebox.h>
56#include <libgnomeui/gnome-stock-icons.h>
57#include <libgnomevfs/gnome-vfs-mime-handlers.h>
58#include <libgnomevfs/gnome-vfs-ops.h>
59#include <libgnomevfs/gnome-vfs-utils.h>
60#include <libnautilus-private/nautilus-file-utilities.h>
61#include <libnautilus-private/nautilus-global-preferences.h>
62#include <libnautilus-private/nautilus-icon-factory.h>
63#include <libnautilus-private/nautilus-metafile-factory.h>
64#include <libnautilus-private/nautilus-sound.h>
65#include <libnautilus-private/nautilus-bonobo-extensions.h>
66#include <libnautilus-private/nautilus-undo-manager.h>
67#include <libnautilus-private/nautilus-volume-monitor.h>
68#include <libnautilus-private/nautilus-authn-manager.h>
69#include <bonobo-activation/bonobo-activation.h>
70
71/* Needed for the is_kdesktop_present check */
72#include <gdk/gdkx.h>
73#include <X11/Xlib.h>
74
75#define FACTORY_IID          "OAFIID:Nautilus_Factory"
76#define SEARCH_LIST_VIEW_IID "OAFIID:Nautilus_File_Manager_Search_List_View"
77#define SHELL_IID            "OAFIID:Nautilus_Shell"
78
79/* Keeps track of all the desktop windows. */
80static GList *nautilus_application_desktop_windows;
81
82/* Keeps track of all the nautilus windows. */
83static GList *nautilus_application_window_list;
84
85static gboolean need_to_show_first_time_druid     (void);
86static void     desktop_changed_callback          (gpointer                  user_data);
87static void     desktop_location_changed_callback (gpointer                  user_data);
88static void     volume_mounted_callback           (NautilusVolumeMonitor    *monitor,
89                                                   NautilusVolume           *volume,
90                                                   NautilusApplication      *application);
91static void     volume_unmounted_callback         (NautilusVolumeMonitor    *monitor,
92                                                   NautilusVolume           *volume,
93                                                   NautilusApplication      *application);
94static void     update_session                    (gpointer                  callback_data);
95static void     init_session                      (void);
96static gboolean is_kdesktop_present               (void);
97
98BONOBO_CLASS_BOILERPLATE (NautilusApplication, nautilus_application,
99                          BonoboGenericFactory, BONOBO_GENERIC_FACTORY_TYPE)
100
101static CORBA_Object
102create_object (PortableServer_Servant servant,
103               const CORBA_char *iid,
104               CORBA_Environment *ev)
105{
106        BonoboObject *object;
107        FMDirectoryView *directory_view;
108        NautilusApplication *application;
109
110        if (strcmp (iid, NAUTILUS_ICON_VIEW_IID) == 0) {
111                directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_icon_view_get_type (), NULL));
112                object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
113        } else if (strcmp (iid, NAUTILUS_DESKTOP_ICON_VIEW_IID) == 0) {
114                directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_desktop_icon_view_get_type (), NULL));
115                object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
116        } else if (strcmp (iid, NAUTILUS_LIST_VIEW_IID) == 0) {
117                directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_list_view_get_type (), NULL));
118                object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
119        } else if (strcmp (iid, SEARCH_LIST_VIEW_IID) == 0) {
120                directory_view = FM_DIRECTORY_VIEW (g_object_new (fm_search_list_view_get_type (), NULL));
121                object = BONOBO_OBJECT (fm_directory_view_get_nautilus_view (directory_view));
122        } else if (strcmp (iid, SHELL_IID) == 0) {
123                application = NAUTILUS_APPLICATION (bonobo_object_from_servant (servant));
124                object = BONOBO_OBJECT (nautilus_shell_new (application));
125        } else if (strcmp (iid, METAFILE_FACTORY_IID) == 0) {
126                object = BONOBO_OBJECT (nautilus_metafile_factory_get_instance ());
127        } else {
128                object = CORBA_OBJECT_NIL;
129        }
130
131        return CORBA_Object_duplicate (BONOBO_OBJREF (object), ev);
132}
133
134GList *
135nautilus_application_get_window_list (void)
136{
137        return nautilus_application_window_list;
138}
139
140static void
141nautilus_application_instance_init (NautilusApplication *application)
142{
143        /* Create an undo manager */
144        application->undo_manager = nautilus_undo_manager_new ();
145
146        /* Watch for volume mounts so we can restore open windows */
147        g_signal_connect_object (nautilus_volume_monitor_get (), "volume_mounted",
148                                 G_CALLBACK (volume_mounted_callback), application, 0);
149
150        /* Watch for volume unmounts so we can close open windows */
151        g_signal_connect_object (nautilus_volume_monitor_get (), "volume_unmounted",
152                                 G_CALLBACK (volume_unmounted_callback), application, 0);
153}
154
155NautilusApplication *
156nautilus_application_new (void)
157{
158        NautilusApplication *application;
159
160        application = g_object_new (NAUTILUS_TYPE_APPLICATION, NULL);
161       
162        bonobo_generic_factory_construct_noreg (BONOBO_GENERIC_FACTORY (application),
163                                                FACTORY_IID,
164                                                NULL);
165       
166        return application;
167}
168
169static void
170nautilus_application_destroy (BonoboObject *object)
171{
172        NautilusApplication *application;
173
174        application = NAUTILUS_APPLICATION (object);
175
176        nautilus_bookmarks_exiting ();
177       
178        bonobo_object_unref (application->undo_manager);
179
180        EEL_CALL_PARENT (BONOBO_OBJECT_CLASS, destroy, (object));
181}
182
183static gboolean
184check_required_directories (NautilusApplication *application)
185{
186        char *user_directory;
187        char *desktop_directory;
188        EelStringList *directories;
189        char *directories_as_string;
190        char *error_string;
191        char *dialog_title;
192        GtkDialog *dialog;
193        int failed_count;
194       
195        g_assert (NAUTILUS_IS_APPLICATION (application));
196
197        user_directory = nautilus_get_user_directory ();
198        desktop_directory = nautilus_get_desktop_directory ();
199
200        directories = eel_string_list_new (TRUE);
201       
202        if (!g_file_test (user_directory, G_FILE_TEST_IS_DIR)) {
203                eel_string_list_insert (directories, user_directory);
204        }
205        g_free (user_directory);           
206           
207        if (!g_file_test (desktop_directory, G_FILE_TEST_IS_DIR)) {
208                eel_string_list_insert (directories, desktop_directory);
209        }
210        g_free (desktop_directory);
211
212        failed_count = eel_string_list_get_length (directories);
213
214        if (failed_count != 0) {
215                directories_as_string = eel_string_list_as_string (directories, "\n", EEL_STRING_LIST_ALL_STRINGS);
216
217                if (failed_count == 1) {
218                        dialog_title = g_strdup (_("Couldn't Create Required Folder"));
219                        error_string = g_strdup_printf (_("Nautilus could not create the required folder \"%s\". "
220                                                          "Before running Nautilus, please create this folder, or "
221                                                          "set permissions such that Nautilus can create it."),
222                                                        directories_as_string);
223                } else {
224                        dialog_title = g_strdup (_("Couldn't Create Required Folders"));
225                        error_string = g_strdup_printf (_("Nautilus could not create the following required folders:\n\n"
226                                                          "%s\n\n"
227                                                          "Before running Nautilus, please create these folders, or "
228                                                          "set permissions such that Nautilus can create them."),
229                                                        directories_as_string);
230                }
231               
232                dialog = eel_show_error_dialog (error_string, dialog_title, NULL);
233                /* We need the main event loop so the user has a chance to see the dialog. */
234                nautilus_main_event_loop_register (GTK_OBJECT (dialog));
235
236                g_free (directories_as_string);
237                g_free (error_string);
238                g_free (dialog_title);
239        }
240
241        eel_string_list_free (directories);
242
243        return failed_count == 0;
244}
245
246static int
247nautilus_strv_length (const char * const *strv)
248{
249        const char * const *p;
250
251        for (p = strv; *p != NULL; p++) { }
252        return p - strv;
253}
254
255static Nautilus_URIList *
256nautilus_make_uri_list_from_shell_strv (const char * const *strv)
257{
258        int length, i;
259        Nautilus_URIList *uri_list;
260        char *translated_uri;
261
262        length = nautilus_strv_length (strv);
263
264        uri_list = Nautilus_URIList__alloc ();
265        uri_list->_maximum = length;
266        uri_list->_length = length;
267        uri_list->_buffer = CORBA_sequence_Nautilus_URI_allocbuf (length);
268        for (i = 0; i < length; i++) {
269                translated_uri = eel_make_uri_from_shell_arg (strv[i]);
270                uri_list->_buffer[i] = CORBA_string_dup (translated_uri);
271                g_free (translated_uri);
272                translated_uri = NULL;
273        }
274        CORBA_sequence_set_release (uri_list, CORBA_TRUE);
275
276        return uri_list;
277}
278
279static void
280migrate_old_nautilus_files (void)
281{
282        char *new_desktop_dir, *np;
283        char *old_desktop_dir, *op;
284        char *old_desktop_dir_new_name;
285        struct stat buf;
286        DIR *dir;
287        struct dirent *de;
288       
289        old_desktop_dir = g_strconcat (g_get_home_dir (), "/.nautilus/desktop", NULL);
290        if (stat (old_desktop_dir, &buf) == -1) {
291                g_free (old_desktop_dir);
292                return;
293        }
294        if (!S_ISLNK (buf.st_mode)){
295                dir = opendir (old_desktop_dir);
296                if (dir == NULL) {
297                        g_free (old_desktop_dir);
298                        return;
299                }
300       
301                new_desktop_dir = nautilus_get_desktop_directory ();
302               
303                while ((de = readdir (dir)) != NULL){
304                        if (de->d_name [0] == '.'){
305                                if (de->d_name [0] == 0)
306                                        continue;
307                               
308                                if (de->d_name [1] == '.' && de->d_name [2] == 0)
309                                        continue;
310                        }
311       
312                        op = g_strconcat (old_desktop_dir, "/", de->d_name, NULL);
313                        np = g_strconcat (new_desktop_dir, "/", de->d_name, NULL);
314       
315                        rename (op, np);
316       
317                        g_free (op);
318                        g_free (np);
319                }
320
321                closedir (dir);
322
323                g_free (new_desktop_dir);
324        }
325
326        /* In case we miss something */
327        old_desktop_dir_new_name = g_strconcat (old_desktop_dir, "-old", NULL);
328        rename (old_desktop_dir, old_desktop_dir_new_name);
329        g_free (old_desktop_dir_new_name);
330
331        g_free (old_desktop_dir);
332}
333
334static gint
335create_starthere_link_callback (gpointer data)
336{
337        char *desktop_path;
338        char *desktop_link_file;
339        char *cmd;
340       
341        /* Create default services icon on the desktop */
342        desktop_path = nautilus_get_desktop_directory ();
343        desktop_link_file = g_build_filename (desktop_path,
344                                              "starthere.desktop",
345                                              NULL);
346
347        cmd = g_strconcat ("/bin/cp ",
348                           NAUTILUS_DATADIR,
349                           "/starthere-link.desktop ",
350                           desktop_link_file,
351                           NULL);
352
353        if (system (cmd) != 0) {
354                g_warning ("Failed to execute command '%s'\n", cmd);
355        }
356       
357        g_free (desktop_path);
358        g_free (desktop_link_file);
359        g_free (cmd);
360       
361        return FALSE;
362}
363
364static void
365finish_startup (NautilusApplication *application)
366{
367        /* 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        migrate_old_nautilus_files ();
375}
376
377void
378nautilus_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        CORBA_Environment ev;
388        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        int num_failures;
395
396        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                return;
403        }
404
405        /* Run the first time startup druid if needed. */
406        if (do_first_time_druid_check && need_to_show_first_time_druid ()) {
407                /* Do this at idle time, once nautilus has initialized
408                 * itself. Otherwise we may spawn a second nautilus
409                 * process when looking for a metadata factory..
410                 */
411                g_idle_add (create_starthere_link_callback, NULL);
412                nautilus_set_first_time_file_flag ();
413        }
414
415        CORBA_exception_init (&ev);
416
417        /* Start up the factory. */
418        while (TRUE) {
419                /* Try to register the file manager view factory. */
420                result = nautilus_bonobo_activation_register_for_display
421                        (FACTORY_IID, BONOBO_OBJREF (application));
422
423                switch (result) {
424                case Bonobo_ACTIVATION_REG_SUCCESS:
425                        /* 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                         * reason. Sometimes killing oafd and gconfd fixes
440                         * this problem but we don't exactly understand why,
441                         * since neither of the above causes explain it.
442                         */
443                        message = _("Nautilus can't be used now. "
444                                    "Running the command \"bonobo-slay\""
445                                    " from the console may fix the problem. If not,"
446                                    " you can try rebooting the computer or"
447                                    " 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                                             "from the console may fix the problem. If not, "
452                                             "you can try rebooting the computer or "
453                                             "installing Nautilus again.\n\n"
454                                             "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                        break;
467                default:
468                        /* 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                         * program.
474                         */
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                         * good message.
478                         */
479                        message = _("Nautilus can't be used now, due to an unexpected error.");
480                        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                        break;
483                }
484
485                /* Get the shell object. */
486                if (message == NULL) {
487                        shell = bonobo_activation_activate_from_id (SHELL_IID, 0, NULL, NULL);
488                        if (!CORBA_Object_is_nil (shell, &ev)) {
489                                break;
490                        }
491
492                        /* If we couldn't find ourselves it's a bad problem so
493                         * we better stop looping.
494                         */
495                        if (result == Bonobo_ACTIVATION_REG_SUCCESS) {
496                                /* FIXME bugzilla.gnome.org 42538: When can this happen? */
497                                message = _("Nautilus can't be used now, due to an unexpected error.");
498                                detailed_message = _("Nautilus can't be used now, due to an unexpected error "
499                                                     "from Bonobo when attempting to locate the factory."
500                                                     "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
501                        } else {
502                                num_failures++;
503                                if (num_failures > 20) {
504                                        message = _("Nautilus can't be used now, due to an unexpected error.");
505                                        detailed_message = _("Nautilus can't be used now, due to an unexpected error "
506                                                             "from Bonobo when attempting to locate the shell object. "
507                                                             "Killing bonobo-activation-server and restarting Nautilus may help fix the problem.");
508                                       
509                                }
510                        }
511                }
512
513                if (message != NULL) {
514                        dialog = eel_show_error_dialog_with_details (message, NULL, detailed_message, NULL);
515                        /* We need the main event loop so the user has a chance to see the dialog. */
516                        nautilus_main_event_loop_register (GTK_OBJECT (dialog));
517                        goto out;
518                }
519        }
520
521        if (kill_shell) {
522                Nautilus_Shell_quit (shell, &ev);
523        } else if (restart_shell) {
524                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                        Nautilus_Shell_start_desktop (shell, &ev);
533                }
534               
535                /* Monitor the preference to show or hide the desktop */
536                eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_SHOW_DESKTOP,
537                                                          desktop_changed_callback,
538                                                          application,
539                                                          G_OBJECT (application));
540
541                /* Monitor the preference to have the desktop */
542                /* point to the Unix home folder */
543                eel_preferences_add_callback_while_alive (NAUTILUS_PREFERENCES_DESKTOP_IS_HOME_DIR,
544                                                          desktop_location_changed_callback,
545                                                          NULL,
546                                                          G_OBJECT (application));
547
548                /* CORBA C mapping doesn't allow NULL to be passed
549                   for string parameters */
550                corba_geometry = (geometry != NULL) ? geometry : "";
551
552                /* Create the other windows. */
553                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                /* Add ourselves to the session */
562                init_session ();
563        }
564
565        /* We're done with the shell now, so let it go. */
566        /* HACK: Don't bother releasing the shell in the case where we
567         * just told it to quit -- that just leads to hangs and does
568         * no good. We could probably fix this in some fancier way if
569         * we could figure out a better lifetime rule.
570         */
571        if (!(kill_shell || restart_shell)) {
572                bonobo_object_release_unref (shell, NULL);
573        }
574
575 out:
576        CORBA_exception_free (&ev);
577}
578
579static void
580nautilus_application_create_desktop_windows (NautilusApplication *application)
581{
582        static gboolean create_in_progress = FALSE;
583        GdkDisplay *display;
584        NautilusDesktopWindow *window;
585        int screens, i;
586
587        g_return_if_fail (nautilus_application_desktop_windows == NULL);
588        g_return_if_fail (NAUTILUS_IS_APPLICATION (application));
589
590        if (create_in_progress) {
591                return;
592        }
593
594        create_in_progress = TRUE;
595
596        display = gdk_display_get_default ();
597        screens = gdk_display_get_n_screens (display);
598
599        for (i = 0; i < screens; i++) {
600                window = nautilus_desktop_window_new (application,
601                                                      gdk_display_get_screen (display, i));
602                /* We realize it immediately so that the NAUTILUS_DESKTOP_WINDOW_ID
603                   property is set so gnome-settings-daemon doesn't try to set the
604                   background. And we do a gdk_flush() to be sure X gets it. */
605                gtk_widget_realize (GTK_WIDGET (window));
606                gdk_flush ();
607
608                nautilus_application_desktop_windows =
609                        g_list_prepend (nautilus_application_desktop_windows, window);
610        }
611
612        create_in_progress = FALSE;
613}
614
615void
616nautilus_application_open_desktop (NautilusApplication *application)
617{
618        if (nautilus_application_desktop_windows == NULL) {
619                nautilus_application_create_desktop_windows (application);
620        }
621}
622
623void
624nautilus_application_close_desktop (void)
625{
626        if (nautilus_application_desktop_windows != NULL) {
627                g_list_foreach (nautilus_application_desktop_windows,
628                                (GFunc) gtk_widget_destroy, NULL);
629                g_list_free (nautilus_application_desktop_windows);
630                nautilus_application_desktop_windows = NULL;
631        }
632}
633
634void
635nautilus_application_close_all_windows (void)
636{
637        while (nautilus_application_window_list != NULL) {
638                nautilus_window_close (NAUTILUS_WINDOW (nautilus_application_window_list->data));
639        }
640}
641
642static void
643nautilus_application_destroyed_window (GtkObject *object, NautilusApplication *application)
644{
645        nautilus_application_window_list = g_list_remove (nautilus_application_window_list, object);
646}
647
648static gboolean
649nautilus_window_delete_event_callback (GtkWidget *widget,
650                                       GdkEvent *event,
651                                       gpointer user_data)
652{
653        NautilusWindow *window;
654
655        window = NAUTILUS_WINDOW (widget);
656        nautilus_window_close (window);
657
658        return TRUE;
659}                                     
660
661static gboolean
662save_window_geometry_timeout (gpointer callback_data)
663{
664        NautilusWindow *window;
665       
666        window = NAUTILUS_WINDOW (callback_data);
667       
668        nautilus_window_save_geometry (window);
669
670        window->save_geometry_timeout_id = 0;
671        return FALSE;
672}
673 
674static gboolean
675nautilus_window_configure_event_callback (GtkWidget *widget,
676                                                GdkEventConfigure *event,
677                                                gpointer callback_data)
678{
679        NautilusWindow *window;
680        char *geometry_string;
681       
682        window = NAUTILUS_WINDOW (widget);
683       
684        /* Only save the geometry if the user hasn't resized the window
685         * for half a second. Otherwise delay the callback another half second.
686         */
687        if (window->save_geometry_timeout_id != 0) {
688                g_source_remove (window->save_geometry_timeout_id);     
689        }
690        if (GTK_WIDGET_VISIBLE (GTK_WIDGET (window)) && !NAUTILUS_IS_DESKTOP_WINDOW (window)) {
691               
692                geometry_string = eel_gtk_window_get_geometry_string (GTK_WINDOW (window));
693       
694                /* If the last geometry is NULL the window must have just
695                 * been shown. No need to save geometry to disk since it
696                 * must be the same.
697                 */
698                if (window->last_geometry == NULL) {
699                        window->last_geometry = geometry_string;
700                        return FALSE;
701                }
702       
703                /* Don't save geometry if it's the same as before. */
704                if (!strcmp (window->last_geometry, geometry_string)) {
705                        g_free (geometry_string);
706                        return FALSE;
707                }
708
709                g_free (window->last_geometry);
710                window->last_geometry = geometry_string;
711
712                window->save_geometry_timeout_id =
713                                g_timeout_add (500, save_window_geometry_timeout, window);
714        }
715
716        return FALSE;
717}
718
719static gboolean
720nautilus_window_unrealize_event_callback (GtkWidget *widget,
721                                                GdkEvent *event,
722                                                gpointer callback_data)
723{
724        NautilusWindow *window;
725       
726        window = NAUTILUS_WINDOW (widget);
727
728        if (window->save_geometry_timeout_id != 0) {
729                g_source_remove (window->save_geometry_timeout_id);
730                window->save_geometry_timeout_id = 0;
731                nautilus_window_save_geometry (window);
732        }
733
734        return FALSE;
735}
736
737NautilusWindow *
738nautilus_application_create_window (NautilusApplication *application,
739                                    GdkScreen           *screen)
740{
741        NautilusWindow *window;
742
743        g_return_val_if_fail (NAUTILUS_IS_APPLICATION (application), NULL);
744       
745        window = NAUTILUS_WINDOW (gtk_widget_new (nautilus_window_get_type (),
746                                                  "app", application,
747                                                  "app_id", "nautilus",
748                                                  "screen", screen,
749                                                  NULL));
750
751        g_signal_connect (window, "delete_event",
752                          G_CALLBACK (nautilus_window_delete_event_callback), NULL);
753
754        g_signal_connect_object (window, "destroy",
755                                 G_CALLBACK (nautilus_application_destroyed_window), application, 0);
756
757        g_signal_connect (window, "configure_event",
758                          G_CALLBACK (nautilus_window_configure_event_callback), NULL);
759
760        g_signal_connect (window, "unrealize",
761                          G_CALLBACK (nautilus_window_unrealize_event_callback), NULL);
762
763        nautilus_application_window_list = g_list_prepend (nautilus_application_window_list, window);
764
765        /* Do not yet show the window. It will be shown later on if it can
766         * successfully display its initial URI. Otherwise it will be destroyed
767         * without ever having seen the light of day.
768         */
769
770        return window;
771}
772
773/* callback for changing the directory the desktop points to */
774static void
775desktop_location_changed_callback (gpointer user_data)
776{
777        if (nautilus_application_desktop_windows != NULL) {
778                g_list_foreach (nautilus_application_desktop_windows,
779                                (GFunc) nautilus_desktop_window_update_directory, NULL);
780        }
781}
782
783/* callback for showing or hiding the desktop based on the user's preference */
784static void
785desktop_changed_callback (gpointer user_data)
786{
787        NautilusApplication *application;
788       
789        application = NAUTILUS_APPLICATION (user_data);
790        if ( eel_preferences_get_boolean (NAUTILUS_PREFERENCES_SHOW_DESKTOP)) {
791                nautilus_application_open_desktop (application);
792        } else {
793                nautilus_application_close_desktop ();
794        }
795
796        /* Can't make this function just watch the preference
797         * itself changing since ordering is important
798         */
799        update_session (gnome_master_client ());
800}
801
802/*
803 * need_to_show_first_time_druid
804 *
805 * Determine whether Nautilus needs to show the first time druid.
806 *
807 * Note that the flag file indicating whether the druid has been
808 * presented is: ~/.nautilus/first-time-flag.
809 *
810 * Another alternative could be to use preferences to store this flag
811 * However, there because of bug 41229 this is not yet possible.
812 *
813 * Also, for debugging purposes, it is convenient to have just one file
814 * to kill in order to test the startup druid:
815 *
816 * rm -f ~/.nautilus/first-time-flag
817 *
818 * In order to accomplish the same thing with preferences, you would have
819 * to either kill ALL your preferences or spend time digging in ~/.gconf
820 * xml files finding the right one.
821 */
822static gboolean
823need_to_show_first_time_druid (void)
824{
825        gboolean result;
826        char *user_directory;
827        char *druid_flag_file_name;
828       
829        user_directory = nautilus_get_user_directory ();
830
831        druid_flag_file_name = g_strconcat (user_directory, "/first-time-flag", NULL);
832        result = !g_file_test (druid_flag_file_name, G_FILE_TEST_EXISTS);
833        g_free (druid_flag_file_name);
834
835        /* we changed the name of the flag for version 1.0, so we should
836         * check for and delete the old one, if the new one didn't exist
837         */
838        if (result) {
839                druid_flag_file_name = g_strconcat (user_directory, "/first-time-wizard-flag", NULL);
840                unlink (druid_flag_file_name);
841                g_free (druid_flag_file_name);
842        }
843        g_free (user_directory);
844        return result;
845}
846
847/* Apps like redhat-config-packages that are using the CD-ROM
848 * directly, can grab ownership of the _NAUTILUS_DISABLE_MOUNT_WINDOW
849 * selection to temporarily disable the new window behavior.
850 */
851static gboolean
852check_mount_window_disabled (void)
853{
854        Atom selection_atom = gdk_x11_get_xatom_by_name ("_NAUTILUS_DISABLE_MOUNT_WINDOW");
855
856        if (XGetSelectionOwner (GDK_DISPLAY(), selection_atom) != None)
857                return TRUE;
858        else
859                return FALSE;
860}
861
862static void
863volume_mounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
864                         NautilusApplication *application)
865{
866        NautilusWindow *window;
867        char *uri;
868
869        if (volume == NULL || application == NULL) {
870                return;
871        }
872
873        /* Open a window to the CD if the user has set that preference. */
874        if (nautilus_volume_get_device_type (volume) == NAUTILUS_DEVICE_CDROM_DRIVE
875                && eel_gconf_get_boolean( "/apps/magicdev/do_fileman_window")
876                && !check_mount_window_disabled ()) {
877                window = nautilus_application_create_window (application, gdk_screen_get_default ());
878                uri = gnome_vfs_get_uri_from_local_path (nautilus_volume_get_mount_path (volume));
879                nautilus_window_go_to (window, uri);
880                g_free (uri);
881        }
882}
883
884static gboolean
885window_can_be_closed (NautilusWindow *window)
886{
887        if (!NAUTILUS_IS_DESKTOP_WINDOW (window)) {
888                return TRUE;
889        }
890       
891        return FALSE;
892}
893
894static gboolean
895is_last_closable_window (NautilusWindow *window)
896{
897        GList *node, *window_list;
898       
899        window_list = nautilus_application_get_window_list ();
900       
901        for (node = window_list; node != NULL; node = node->next) {
902                if (window != NAUTILUS_WINDOW (node->data) && window_can_be_closed (NAUTILUS_WINDOW (node->data))) {
903                        return FALSE;
904                }
905        }
906       
907        return TRUE;
908}
909
910
911/* Called whenever a volume is unmounted. Check and see if there are any windows open
912 * displaying contents on the volume. If there are, close them.
913 * It would also be cool to save open window and position info.
914 */
915static void
916volume_unmounted_callback (NautilusVolumeMonitor *monitor, NautilusVolume *volume,
917                           NautilusApplication *application)
918{
919        GList *window_list, *node, *close_list;
920        NautilusWindow *window;
921        char *uri;
922        char *path;
923               
924        close_list = NULL;
925       
926        /* Check and see if any of the open windows are displaying contents from the unmounted volume */
927        window_list = nautilus_application_get_window_list ();
928       
929        /* Construct a list of windows to be closed. Do not add the non-closable windows to the list. */
930        for (node = window_list; node != NULL; node = node->next) {
931                window = NAUTILUS_WINDOW (node->data);
932                if (window != NULL && window_can_be_closed (window)) {
933                        uri = nautilus_window_get_location (window);
934                        path = gnome_vfs_get_local_path_from_uri (uri);
935                        if (eel_str_has_prefix (path, nautilus_volume_get_mount_path (volume))) {
936                                close_list = g_list_prepend (close_list, window);
937                        }
938                        g_free (path);
939                        g_free (uri);
940                }
941        }
942               
943        /* Handle the windows in the close list. */
944        for (node = close_list; node != NULL; node = node->next) {
945                window = NAUTILUS_WINDOW (node->data);
946                if (is_last_closable_window (window)) {
947                        /* Don't close the last or only window. Try to redirect to the default home directory. */                       
948                        nautilus_window_go_home (window);
949                } else {
950                        nautilus_window_close (window);
951                }
952        }
953               
954        g_list_free (close_list);
955}
956
957
958static void
959removed_from_session (GnomeClient *client, gpointer data)
960{
961        nautilus_main_event_loop_quit ();
962}
963
964static gint
965save_session (GnomeClient *client, gint phase, GnomeSaveStyle save_style, gint shutdown,
966              GnomeInteractStyle interact_style, gint fast, gpointer data)
967{
968        NautilusWindow *window;
969        GList *l;
970        static char *clone_argv[] = { "nautilus", "--no-default-window" };
971        char **restart_argv;
972        int argc;
973        int i;
974        int num_windows;
975
976        num_windows = g_list_length (nautilus_application_window_list);
977        if (num_windows > 0) {
978                argc = 1 + num_windows;
979                i = 0;
980                restart_argv = g_new (char *, argc);
981                restart_argv[i++] = g_strdup ("nautilus");
982                for (l = nautilus_application_window_list; l != NULL; l = l->next) {
983                        window = NAUTILUS_WINDOW (l->data);
984                        restart_argv[i++] = nautilus_window_get_location (window);
985                }
986               
987                gnome_client_set_restart_command (client, argc, restart_argv);
988
989                for (i = 0; i < argc; i++) {
990                        g_free (restart_argv[i]);
991                }
992                g_free (restart_argv);
993        } else {
994                gnome_client_set_restart_command (client,
995                                                  G_N_ELEMENTS (clone_argv),
996                                                  clone_argv);
997        }
998       
999        return TRUE;
1000}
1001
1002static void
1003set_session_restart (GnomeClient *client, gboolean restart)
1004{
1005        gnome_client_set_priority (client, 40);
1006
1007        if (restart && g_getenv ("NAUTILUS_DEBUG") == NULL) {
1008                /* Don't respawn in debug mode */
1009                gnome_client_set_restart_style (client, GNOME_RESTART_IMMEDIATELY);
1010        } else {
1011                gnome_client_set_restart_style (client, GNOME_RESTART_IF_RUNNING);
1012        }
1013}
1014
1015static void
1016update_session (gpointer callback_data)
1017{
1018        set_session_restart (callback_data,
1019                             eel_preferences_get_boolean (NAUTILUS_PREFERENCES_ADD_TO_SESSION)
1020                             /* Only ever add ourselves to the session
1021                              * if we have a desktop window. Prevents the
1022                              * session thrashing that's seen otherwise
1023                              */
1024                             && nautilus_application_desktop_windows != NULL);
1025}
1026
1027static void
1028init_session (void)
1029{
1030        GnomeClient *client;
1031
1032        client = gnome_master_client ();
1033
1034        g_signal_connect (client, "save_yourself",
1035                          G_CALLBACK (save_session), NULL);
1036       
1037        g_signal_connect (client, "die",
1038                          G_CALLBACK (removed_from_session), NULL);
1039       
1040        eel_preferences_add_callback
1041                (NAUTILUS_PREFERENCES_ADD_TO_SESSION,
1042                 update_session, client);
1043
1044        update_session (client);
1045}
1046
1047#ifdef UGLY_HACK_TO_DETECT_KDE
1048
1049static gboolean
1050get_self_typed_prop (Window      xwindow,
1051                     Atom        atom,
1052                     gulong     *val)
1053
1054        Atom type;
1055        int format;
1056        gulong nitems;
1057        gulong bytes_after;
1058        gulong *num;
1059        int err;
1060 
1061        gdk_error_trap_push ();
1062        type = None;
1063        XGetWindowProperty (gdk_display,
1064                            xwindow,
1065                            atom,
1066                            0, G_MAXLONG,
1067                            False, atom, &type, &format, &nitems,
1068                            &bytes_after, (guchar **)&num); 
1069
1070        err = gdk_error_trap_pop ();
1071        if (err != Success) {
1072                return FALSE;
1073        }
1074 
1075        if (type != atom) {
1076                return FALSE;
1077        }
1078
1079        if (val)
1080                *val = *num;
1081 
1082        XFree (num);
1083
1084        return TRUE;
1085}
1086
1087static gboolean
1088has_wm_state (Window xwindow)
1089{
1090        return get_self_typed_prop (xwindow,
1091                                    XInternAtom (gdk_display, "WM_STATE", False),
1092                                    NULL);
1093}
1094
1095static gboolean
1096look_for_kdesktop_recursive (Window xwindow)
1097{
1098 
1099        Window ignored1, ignored2;
1100        Window *children;
1101        unsigned int n_children;
1102        unsigned int i;
1103        gboolean retval;
1104 
1105        /* If WM_STATE is set, this is a managed client, so look
1106         * for the class hint and end recursion. Otherwise,
1107         * this is probably just a WM frame, so keep recursing.
1108         */
1109        if (has_wm_state (xwindow)) {     
1110                XClassHint ch;
1111     
1112                gdk_error_trap_push ();
1113                ch.res_name = NULL;
1114                ch.res_class = NULL;
1115     
1116                XGetClassHint (gdk_display, xwindow, &ch);
1117     
1118                gdk_error_trap_pop ();
1119     
1120                if (ch.res_name)
1121                        XFree (ch.res_name);
1122     
1123                if (ch.res_class) {
1124                        if (strcmp (ch.res_class, "kdesktop") == 0) {
1125                                XFree (ch.res_class);
1126                                return TRUE;
1127                        }
1128                        else
1129                                XFree (ch.res_class);
1130                }
1131
1132                return FALSE;
1133        }
1134 
1135        retval = FALSE;
1136 
1137        gdk_error_trap_push ();
1138 
1139        XQueryTree (gdk_display,
1140                    xwindow,
1141                    &ignored1, &ignored2, &children, &n_children);
1142
1143        if (gdk_error_trap_pop ()) {
1144                return FALSE;
1145        }
1146
1147        i = 0;
1148        while (i < n_children) {
1149                if (look_for_kdesktop_recursive (children[i])) {
1150                        retval = TRUE;
1151                        break;
1152                }
1153     
1154                ++i;
1155        }
1156 
1157        if (children)
1158                XFree (children);
1159
1160        return retval;
1161}
1162#endif /* UGLY_HACK_TO_DETECT_KDE */
1163
1164static gboolean
1165is_kdesktop_present (void)
1166{
1167#ifdef UGLY_HACK_TO_DETECT_KDE
1168        /* FIXME this is a pretty lame hack, should be replaced
1169         * eventually with e.g. a requirement that desktop managers
1170         * support a manager selection, ICCCM sec 2.8
1171         */
1172        return look_for_kdesktop_recursive (GDK_ROOT_WINDOW ());
1173#else
1174        return FALSE;
1175#endif
1176}
1177
1178static void
1179nautilus_application_class_init (NautilusApplicationClass *class)
1180{
1181        BONOBO_OBJECT_CLASS (class)->destroy = nautilus_application_destroy;
1182        BONOBO_GENERIC_FACTORY_CLASS (class)->epv.createObject = create_object;
1183}
Note: See TracBrowser for help on using the repository browser.