/*
* nautilus-application: main Nautilus application class.
*
* Copyright (C) 1999, 2000 Red Hat, Inc.
* Copyright (C) 2000, 2001 Eazel, Inc.
* Copyright (C) 2010, Cosimo Cecchi <cosimoc@gnome.org>
*
* Nautilus is free software; you can redistribute it and/or
* 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.
*
* Nautilus is distributed in the hope that it will be useful,
* 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
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Authors: Elliot Lee <sopwith@redhat.com>,
* Darin Adler <darin@bentspoon.com>
* Cosimo Cecchi <cosimoc@gnome.org>
*
*/
#include <config.h>
#include "nautilus-application.h"
#include "nautilus-dbus-manager.h"
#include "nautilus-file-undo-manager.h"
#include "nautilus-freedesktop-dbus.h"
#include "nautilus-previewer.h"
#include "nautilus-progress-persistence-handler.h"
#include "nautilus-self-check-functions.h"
#include "nautilus-shell-search-provider.h"
#include "nautilus-window.h"
#include "nautilus-window-slot.h"
#include "nautilus-preferences-window.h"
#include "nautilus-tag-manager.h"
#include "nautilus-directory-private.h"
#include "nautilus-file-utilities.h"
#include "nautilus-file-operations.h"
#include "nautilus-global-preferences.h"
#include "nautilus-lib-self-check-functions.h"
#include "nautilus-module.h"
#include "nautilus-profile.h"
#include "nautilus-signaller.h"
#include "nautilus-ui-utilities.h"
#include <nautilus-extension.h>
#define DEBUG_FLAG NAUTILUS_DEBUG_APPLICATION
#include "nautilus-debug.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-stock-dialogs.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
typedef struct
{
NautilusProgressPersistenceHandler *progress_handler;
NautilusDBusManager *dbus_manager;
NautilusFreedesktopDBus *fdb_manager;
NautilusBookmarkList *bookmark_list;
NautilusShellSearchProvider *search_provider;
GList *windows;
GHashTable *notifications;
NautilusFileUndoManager *undo_manager;
NautilusTagManager *tag_manager;
GCancellable *tag_manager_cancellable;
} NautilusApplicationPrivate;
G_DEFINE_TYPE_WITH_PRIVATE (NautilusApplication, nautilus_application, GTK_TYPE_APPLICATION);
void
nautilus_application_set_accelerator (GApplication *app,
const gchar *action_name,
const gchar *accel)
{
const gchar *vaccels[] =
{
accel,
NULL
};
gtk_application_set_accels_for_action (GTK_APPLICATION (app), action_name, vaccels);
}
void
nautilus_application_set_accelerators (GApplication *app,
const gchar *action_name,
const gchar **accels)
{
gtk_application_set_accels_for_action (GTK_APPLICATION (app), action_name, accels);
}
GList *
nautilus_application_get_windows (NautilusApplication *self)
{
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
return priv->windows;
}
NautilusBookmarkList *
nautilus_application_get_bookmarks (NautilusApplication *self)
{
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
if (!priv->bookmark_list)
{
priv->bookmark_list = nautilus_bookmark_list_new ();
}
return priv->bookmark_list;
}
static gboolean
check_required_directories (NautilusApplication *self)
{
char *user_directory;
GSList *directories;
gboolean ret;
g_assert (NAUTILUS_IS_APPLICATION (self));
nautilus_profile_start (NULL);
ret = TRUE;
user_directory = nautilus_get_user_directory ();
directories = NULL;
if (!g_file_test (user_directory, G_FILE_TEST_IS_DIR))
{
directories = g_slist_prepend (directories, user_directory);
}
if (directories != NULL)
{
int failed_count;
GString *directories_as_string;
GSList *l;
char *error_string;
g_autofree char *detail_string = NULL;
GtkDialog *dialog;
ret = FALSE;
failed_count = g_slist_length (directories);
directories_as_string = g_string_new ((const char *) directories->data);
for (l = directories->next; l != NULL; l = l->next)
{
g_string_append_printf (directories_as_string, ", %s", (const char *) l->data);
}
error_string = _("Oops! Something went wrong.");
if (failed_count == 1)
{
detail_string = g_strdup_printf (_("Unable to create a required folder. "
"Please create the following folder, or "
"set permissions such that it can be created:\n%s"),
directories_as_string->str);
}
else
{
detail_string = g_strdup_printf (_("Unable to create required folders. "
"Please create the following folders, or "
"set permissions such that they can be created:\n%s"),
directories_as_string->str);
}
dialog = eel_show_error_dialog (error_string, detail_string, NULL);
/* We need the main event loop so the user has a chance to see the dialog. */
gtk_application_add_window (GTK_APPLICATION (self),
GTK_WINDOW (dialog));
g_string_free (directories_as_string, TRUE);
}
g_slist_free (directories);
g_free (user_directory);
nautilus_profile_end (NULL);
return ret;
}
static void
menu_provider_items_updated_handler (NautilusMenuProvider *provider,
GtkWidget *parent_window,
gpointer data)
{
g_signal_emit_by_name (nautilus_signaller_get_current (),
"popup-menu-changed");
}
static void
menu_provider_init_callback (void)
{
GList *providers;
GList *l;
providers = nautilus_module_get_extensions_for_type (NAUTILUS_TYPE_MENU_PROVIDER);
for (l = providers; l != NULL; l = l->next)
{
NautilusMenuProvider *provider = NAUTILUS_MENU_PROVIDER (l->data);
g_signal_connect_after (G_OBJECT (provider), "items-updated",
(GCallback) menu_provider_items_updated_handler,
NULL);
}
nautilus_module_extension_list_free (providers);
}
NautilusWindow *
nautilus_application_create_window (NautilusApplication *self,
GdkScreen *screen)
{
NautilusWindow *window;
gboolean maximized;
g_autoptr (GVariant) default_size = NULL;
gint default_width = 0;
gint default_height = 0;
const gchar *application_id;
g_return_val_if_fail (NAUTILUS_IS_APPLICATION (self), NULL);
nautilus_profile_start (NULL);
window = nautilus_window_new (screen);
maximized = g_settings_get_boolean
(nautilus_window_state, NAUTILUS_WINDOW_STATE_MAXIMIZED);
if (maximized)
{
gtk_window_maximize (GTK_WINDOW (window));
}
else
{
gtk_window_unmaximize (GTK_WINDOW (window));
}
default_size = g_settings_get_value (nautilus_window_state,
NAUTILUS_WINDOW_STATE_INITIAL_SIZE);
g_variant_get (default_size, "(ii)", &default_width, &default_height);
gtk_window_set_default_size (GTK_WINDOW (window),
MAX (NAUTILUS_WINDOW_MIN_WIDTH, default_width),
MAX (NAUTILUS_WINDOW_MIN_HEIGHT, default_height));
application_id = g_application_get_application_id (G_APPLICATION (self));
if (g_strcmp0 (application_id, "org.gnome.NautilusDevel") == 0 ||
g_strcmp0 (application_id, "org.gnome.NautilusStableFlatpak") == 0)
{
GtkStyleContext *style_context;
style_context = gtk_widget_get_style_context (GTK_WIDGET (window));
gtk_style_context_add_class (style_context, "devel");
}
DEBUG ("Creating a new navigation window");
nautilus_profile_end (NULL);
return window;
}
static NautilusWindowSlot *
get_window_slot_for_location (NautilusApplication *self,
GFile *location)
{
NautilusApplicationPrivate *priv;
NautilusWindowSlot *slot;
NautilusWindow *window;
NautilusFile *file;
GList *l, *sl;
priv = nautilus_application_get_instance_private (self);
slot = NULL;
file = nautilus_file_get (location);
if (!nautilus_file_is_directory (file) && !nautilus_file_is_other_locations (file) &&
g_file_has_parent (location, NULL))
{
location = g_file_get_parent (location);
}
else
{
g_object_ref (location);
}
for (l = priv->windows; l != NULL; l = l->next)
{
window = l->data;
for (sl = nautilus_window_get_slots (window); sl; sl = sl->next)
{
NautilusWindowSlot *current = sl->data;
GFile *slot_location = nautilus_window_slot_get_location (current);
if (slot_location && g_file_equal (slot_location, location))
{
slot = current;
break;
}
}
if (slot)
{
break;
}
}
nautilus_file_unref (file);
g_object_unref (location);
return slot;
}
static void
new_window_show_callback (GtkWidget *widget,
gpointer user_data)
{
NautilusWindow *window;
window = NAUTILUS_WINDOW (user_data);
nautilus_window_close (window);
g_signal_handlers_disconnect_by_func (widget,
G_CALLBACK (new_window_show_callback),
user_data);
}
void
nautilus_application_open_location_full (NautilusApplication *self,
GFile *location,
NautilusWindowOpenFlags flags,
GList *selection,
NautilusWindow *target_window,
NautilusWindowSlot *target_slot)
{
NAUTILUS_APPLICATION_CLASS (G_OBJECT_GET_CLASS (self))->open_location_full (self,
location,
flags,
selection,
target_window,
target_slot);
}
static void
real_open_location_full (NautilusApplication *self,
GFile *location,
NautilusWindowOpenFlags flags,
GList *selection,
NautilusWindow *target_window,
NautilusWindowSlot *target_slot)
{
NautilusWindowSlot *active_slot = NULL;
NautilusWindow *active_window;
GFile *old_location = NULL;
char *old_uri, *new_uri;
gboolean use_same;
GdkScreen *screen;
use_same = TRUE;
/* FIXME: We are having problems on getting the current focused window with
* gtk_application_get_active_window, see https://bugzilla.gnome.org/show_bug.cgi?id=756499
* so what we do is never rely on this on the callers, but would be cool to
* make it work withouth explicitly setting the active window on the callers. */
active_window = NAUTILUS_WINDOW (gtk_application_get_active_window (GTK_APPLICATION (self)));
/* There is no active window if the application is run with
* --gapplication-service
*/
if (active_window)
{
active_slot = nautilus_window_get_active_slot (active_window);
/* Just for debug.*/
old_location = nautilus_window_slot_get_location (active_slot);
}
/* this happens at startup */
if (old_location == NULL)
{
old_uri = g_strdup ("(none)");
}
else
{
old_uri = g_file_get_uri (old_location);
}
new_uri = g_file_get_uri (location);
DEBUG ("Application opening location, old: %s, new: %s", old_uri, new_uri);
nautilus_profile_start ("Application opening location, old: %s, new: %s", old_uri, new_uri);
g_free (old_uri);
g_free (new_uri);
/* end debug */
/* In case a target slot is provided, we can use it's associated window.
* In case a target window were given as well, we give preference to the
* slot we target at */
if (target_slot != NULL)
{
target_window = nautilus_window_slot_get_window (target_slot);
}
g_assert (!((flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW) != 0 &&
(flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_TAB) != 0));
/* and if the flags specify so, this is overridden */
if ((flags & NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW) != 0)
{
use_same = FALSE;
}
/* now get/create the window */
if (use_same)
{
if (!target_window)
{
if (!target_slot)
{
target_window = active_window;
}
else
{
target_window = nautilus_window_slot_get_window (target_slot);
}
}
}
else
{
screen = active_window != NULL ?
gtk_window_get_screen (GTK_WINDOW (active_window)) :
gdk_screen_get_default ();
target_window = nautilus_application_create_window (self, screen);
/* Whatever the caller says, the slot won't be the same */
target_slot = NULL;
}
g_assert (target_window != NULL);
/* close the current window if the flags say so */
if ((flags & NAUTILUS_WINDOW_OPEN_FLAG_CLOSE_BEHIND) != 0)
{
if (gtk_widget_get_visible (GTK_WIDGET (target_window)))
{
nautilus_window_close (active_window);
}
else
{
g_signal_connect_object (target_window,
"show",
G_CALLBACK (new_window_show_callback),
active_window,
G_CONNECT_AFTER);
}
}
/* Application is the one that manages windows, so this flag shouldn't use
* it anymore by any client */
flags &= ~NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW;
nautilus_window_open_location_full (target_window, location, flags, selection, target_slot);
}
static NautilusWindow *
open_window (NautilusApplication *self,
GFile *location)
{
NautilusWindow *window;
nautilus_profile_start (NULL);
window = nautilus_application_create_window (self, gdk_screen_get_default ());
if (location != NULL)
{
nautilus_application_open_location_full (self, location, 0, NULL, window, NULL);
}
else
{
GFile *home;
home = g_file_new_for_path (g_get_home_dir ());
nautilus_application_open_location_full (self, home, 0, NULL, window, NULL);
g_object_unref (home);
}
nautilus_profile_end (NULL);
return window;
}
void
nautilus_application_open_location (NautilusApplication *self,
GFile *location,
GFile *selection,
const char *startup_id)
{
NautilusWindow *window;
NautilusWindowSlot *slot;
GList *sel_list = NULL;
nautilus_profile_start (NULL);
if (selection != NULL)
{
sel_list = g_list_prepend (sel_list, nautilus_file_get (selection));
}
slot = get_window_slot_for_location (self, location);
if (!slot)
{
window = nautilus_application_create_window (self, gdk_screen_get_default ());
}
else
{
window = nautilus_window_slot_get_window (slot);
}
nautilus_application_open_location_full (self, location, 0, sel_list, window, slot);
if (sel_list != NULL)
{
nautilus_file_list_free (sel_list);
}
nautilus_profile_end (NULL);
}
/* Note: when launched from command line we do not reach this method
* since we manually handle the command line parameters in order to
* parse --version, --check, etc.
* However this method is called when open () is called via dbus, for
* instance when gtk_uri_open () is called from outside.
*/
static void
nautilus_application_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *hint)
{
NautilusApplication *self = NAUTILUS_APPLICATION (app);
gboolean force_new = (g_strcmp0 (hint, "new-window") == 0);
NautilusWindowSlot *slot = NULL;
GFile *file;
gint idx;
DEBUG ("Open called on the GApplication instance; %d files", n_files);
/* Open windows at each requested location. */
for (idx = 0; idx < n_files; idx++)
{
file = files[idx];
if (!force_new)
{
slot = get_window_slot_for_location (self, file);
}
if (!slot)
{
open_window (self, file);
}
else
{
/* We open the location again to update any possible selection */
nautilus_application_open_location_full (NAUTILUS_APPLICATION (app), file, 0, NULL, NULL, slot);
}
}
}
static void
nautilus_application_finalize (GObject *object)
{
NautilusApplication *self;
NautilusApplicationPrivate *priv;
self = NAUTILUS_APPLICATION (object);
priv = nautilus_application_get_instance_private (self);
g_clear_object (&priv->progress_handler);
g_clear_object (&priv->bookmark_list);
g_clear_object (&priv->fdb_manager);
g_list_free (priv->windows);
g_hash_table_destroy (priv->notifications);
g_clear_object (&priv->undo_manager);
g_clear_object (&priv->tag_manager);
g_cancellable_cancel (priv->tag_manager_cancellable);
g_clear_object (&priv->tag_manager_cancellable);
G_OBJECT_CLASS (nautilus_application_parent_class)->finalize (object);
}
static gboolean
do_cmdline_sanity_checks (NautilusApplication *self,
GVariantDict *options)
{
gboolean retval = FALSE;
if (g_variant_dict_contains (options, "check") &&
(g_variant_dict_contains (options, G_OPTION_REMAINING) ||
g_variant_dict_contains (options, "quit")))
{
g_printerr ("%s\n",
_("--check cannot be used with other options."));
goto out;
}
if (g_variant_dict_contains (options, "quit") &&
g_variant_dict_contains (options, G_OPTION_REMAINING))
{
g_printerr ("%s\n",
_("--quit cannot be used with URIs."));
goto out;
}
if (g_variant_dict_contains (options, "select") &&
!g_variant_dict_contains (options, G_OPTION_REMAINING))
{
g_printerr ("%s\n",
_("--select must be used with at least an URI."));
goto out;
}
retval = TRUE;
out:
return retval;
}
static int
do_perform_self_checks (void)
{
#ifndef NAUTILUS_OMIT_SELF_CHECK
gtk_init (NULL, NULL);
nautilus_profile_start (NULL);
/* Run the checks (each twice) for nautilus and libnautilus-private. */
nautilus_run_self_checks ();
nautilus_run_lib_self_checks ();
eel_exit_if_self_checks_failed ();
nautilus_run_self_checks ();
nautilus_run_lib_self_checks ();
eel_exit_if_self_checks_failed ();
nautilus_profile_end (NULL);
#endif
return EXIT_SUCCESS;
}
static void
nautilus_application_select (NautilusApplication *self,
GFile **files,
gint len)
{
int i;
GFile *file;
GFile *parent;
for (i = 0; i < len; i++)
{
file = files[i];
parent = g_file_get_parent (file);
if (parent != NULL)
{
nautilus_application_open_location (self, parent, file, NULL);
g_object_unref (parent);
}
else
{
nautilus_application_open_location (self, file, NULL, NULL);
}
}
}
static void
action_new_window (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
NautilusApplication *application;
g_autoptr (GFile) home = NULL;
application = NAUTILUS_APPLICATION (user_data);
home = g_file_new_for_path (g_get_home_dir ());
nautilus_application_open_location_full (application, home,
NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW,
NULL, NULL, NULL);
}
static void
action_clone_window (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
NautilusWindowSlot *active_slot = NULL;
NautilusWindow *active_window = NULL;
GtkApplication *application = user_data;
g_autoptr (GFile) location = NULL;
NautilusView *current_view;
active_window = NAUTILUS_WINDOW (gtk_application_get_active_window (application));
active_slot = nautilus_window_get_active_slot (active_window);
current_view = nautilus_window_slot_get_current_view (active_slot);
if (current_view != NULL &&
nautilus_view_is_searching (current_view))
{
location = g_file_new_for_path (g_get_home_dir ());
}
else
{
location = nautilus_window_slot_get_location (active_slot);
}
nautilus_application_open_location_full (NAUTILUS_APPLICATION (application), location,
NAUTILUS_WINDOW_OPEN_FLAG_NEW_WINDOW, NULL, NULL, NULL);
}
static void
action_preferences (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkApplication *application = user_data;
nautilus_preferences_window_show (gtk_application_get_active_window (application));
}
static void
action_about (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkApplication *application = user_data;
nautilus_window_show_about_dialog (NAUTILUS_WINDOW (gtk_application_get_active_window (application)));
}
static void
action_help (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkWindow *window;
GtkWidget *dialog;
GtkApplication *application = user_data;
GError *error = NULL;
window = gtk_application_get_active_window (application);
gtk_show_uri_on_window (window, "help:gnome-help/files",
gtk_get_current_event_time (), &error);
if (error)
{
dialog = gtk_message_dialog_new (window ? GTK_WINDOW (window) : NULL,
GTK_DIALOG_MODAL,
GTK_MESSAGE_ERROR,
GTK_BUTTONS_OK,
_("There was an error displaying help: \n%s"),
error->message);
g_signal_connect (G_OBJECT (dialog), "response",
G_CALLBACK (gtk_widget_destroy),
NULL);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
gtk_widget_show (dialog);
g_error_free (error);
}
}
static void
action_kill (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
GtkApplication *application = user_data;
/* we have been asked to force quit */
g_application_quit (G_APPLICATION (application));
}
static void
action_quit (GSimpleAction *action,
GVariant *parameter,
gpointer user_data)
{
NautilusApplication *self = NAUTILUS_APPLICATION (user_data);
GList *windows, *l;
windows = nautilus_application_get_windows (self);
/* make a copy, since the original list will be modified when destroying
* a window, making this list invalid */
windows = g_list_copy (windows);
for (l = windows; l != NULL; l = l->next)
{
nautilus_window_close (l->data);
}
g_list_free (windows);
}
static void
action_show_hide_sidebar (GObject *object,
GParamSpec *pspec,
gpointer *user_data)
{
GList *window, *windows;
GVariant *state = g_action_get_state (G_ACTION (object));
windows = gtk_application_get_windows (GTK_APPLICATION (user_data));
for (window = windows; window != NULL; window = window->next)
{
if (g_variant_get_boolean (state))
{
nautilus_window_show_sidebar (window->data);
}
else
{
nautilus_window_hide_sidebar (window->data);
}
}
}
static void
action_show_help_overlay (GSimpleAction *action,
GVariant *state,
gpointer user_data)
{
GtkApplication *application = user_data;
GtkWindow *window = gtk_application_get_active_window (application);
g_action_group_activate_action (G_ACTION_GROUP (window), "show-help-overlay", NULL);
}
static gboolean
variant_get_mapping (GValue *value,
GVariant *variant,
gpointer user_data)
{
g_value_set_variant (value, variant);
return TRUE;
}
static GVariant *
variant_set_mapping (const GValue *value,
const GVariantType *expected_type,
gpointer user_data)
{
return g_value_get_variant (value);
}
static GActionEntry app_entries[] =
{
{ "new-window", action_new_window, NULL, NULL, NULL },
{ "clone-window", action_clone_window, NULL, NULL, NULL },
{ "preferences", action_preferences, NULL, NULL, NULL },
{ "show-hide-sidebar", NULL, NULL, "true", NULL },
{ "about", action_about, NULL, NULL, NULL },
{ "help", action_help, NULL, NULL, NULL },
{ "quit", action_quit, NULL, NULL, NULL },
{ "kill", action_kill, NULL, NULL, NULL },
{ "show-help-overlay", action_show_help_overlay, NULL, NULL, NULL },
};
static void
nautilus_init_application_actions (NautilusApplication *app)
{
const gchar *debug_no_app_menu;
GAction *sidebar_action;
g_action_map_add_action_entries (G_ACTION_MAP (app),
app_entries, G_N_ELEMENTS (app_entries),
app);
debug_no_app_menu = g_getenv ("NAUTILUS_DEBUG_NO_APP_MENU");
if (debug_no_app_menu)
{
DEBUG ("Disabling app menu GtkSetting as requested...");
g_object_set (gtk_settings_get_default (),
"gtk-shell-shows-app-menu", FALSE,
NULL);
}
sidebar_action = g_action_map_lookup_action (G_ACTION_MAP (app),
"show-hide-sidebar");
g_signal_connect (sidebar_action,
"notify::state",
G_CALLBACK (action_show_hide_sidebar),
app);
g_settings_bind_with_mapping (nautilus_window_state,
NAUTILUS_WINDOW_STATE_START_WITH_SIDEBAR,
sidebar_action,
"state",
G_SETTINGS_BIND_DEFAULT,
variant_get_mapping,
variant_set_mapping,
NULL, NULL);
nautilus_application_set_accelerator (G_APPLICATION (app), "app.show-hide-sidebar", "F9");
}
static void
nautilus_application_activate (GApplication *app)
{
GFile **files;
DEBUG ("Calling activate");
files = g_malloc0 (2 * sizeof (GFile *));
files[0] = g_file_new_for_path (g_get_home_dir ());
nautilus_application_open (app, files, 1, NULL);
g_object_unref (files[0]);
g_free (files);
}
static gint
nautilus_application_handle_file_args (NautilusApplication *self,
GVariantDict *options)
{
GFile **files;
GFile *file;
gint idx, len;
const gchar * const *remaining = NULL;
GPtrArray *file_array;
g_variant_dict_lookup (options, G_OPTION_REMAINING, "^a&s", &remaining);
/* Convert args to GFiles */
file_array = g_ptr_array_new_full (0, g_object_unref);
if (remaining)
{
for (idx = 0; remaining[idx] != NULL; idx++)
{
gchar *cwd;
g_variant_dict_lookup (options, "cwd", "s", &cwd);
if (cwd == NULL)
{
file = g_file_new_for_commandline_arg (remaining[idx]);
}
else
{
file = g_file_new_for_commandline_arg_and_cwd (remaining[idx], cwd);
g_free (cwd);
}
g_ptr_array_add (file_array, file);
}
}
else if (g_variant_dict_contains (options, "new-window"))
{
file = g_file_new_for_path (g_get_home_dir ());
g_ptr_array_add (file_array, file);
}
else
{
g_ptr_array_unref (file_array);
/* No command line options or files, just activate the application */
nautilus_application_activate (G_APPLICATION (self));
return EXIT_SUCCESS;
}
len = file_array->len;
files = (GFile **) file_array->pdata;
if (g_variant_dict_contains (options, "select"))
{
nautilus_application_select (self, files, len);
}
else
{
/* Create new windows */
nautilus_application_open (G_APPLICATION (self), files, len,
g_variant_dict_contains (options, "new-window") ? "new-window" : "");
}
g_ptr_array_unref (file_array);
return EXIT_SUCCESS;
}
static gint
nautilus_application_command_line (GApplication *application,
GApplicationCommandLine *command_line)
{
NautilusApplication *self = NAUTILUS_APPLICATION (application);
gint retval = -1;
GVariantDict *options;
nautilus_profile_start (NULL);
options = g_application_command_line_get_options_dict (command_line);
if (g_variant_dict_contains (options, "version"))
{
g_application_command_line_print (command_line,
"GNOME nautilus " PACKAGE_VERSION "\n");
retval = EXIT_SUCCESS;
goto out;
}
if (!do_cmdline_sanity_checks (self, options))
{
retval = EXIT_FAILURE;
goto out;
}
if (g_variant_dict_contains (options, "check"))
{
retval = do_perform_self_checks ();
goto out;
}
if (g_variant_dict_contains (options, "quit"))
{
DEBUG ("Killing application, as requested");
g_action_group_activate_action (G_ACTION_GROUP (application),
"kill", NULL);
goto out;
}
retval = nautilus_application_handle_file_args (self, options);
out:
nautilus_profile_end (NULL);
return retval;
}
static void
nautilus_application_init (NautilusApplication *self)
{
static const GOptionEntry options[] =
{
#ifndef NAUTILUS_OMIT_SELF_CHECK
{ "check", 'c', 0, G_OPTION_ARG_NONE, NULL,
N_("Perform a quick set of self-check tests."), NULL },
#endif
/* dummy, only for compatibility reasons */
{ "browser", '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, NULL,
NULL, NULL },
/* ditto */
{ "geometry", 'g', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_STRING, NULL,
N_("Create the initial window with the given geometry."), N_("GEOMETRY") },
{ "version", '\0', 0, G_OPTION_ARG_NONE, NULL,
N_("Show the version of the program."), NULL },
{ "new-window", 'w', 0, G_OPTION_ARG_NONE, NULL,
N_("Always open a new window for browsing specified URIs"), NULL },
{ "no-default-window", 'n', 0, G_OPTION_ARG_NONE, NULL,
N_("Only create windows for explicitly specified URIs."), NULL },
{ "quit", 'q', 0, G_OPTION_ARG_NONE, NULL,
N_("Quit Nautilus."), NULL },
{ "select", 's', 0, G_OPTION_ARG_NONE, NULL,
N_("Select specified URI in parent folder."), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, NULL, NULL, N_("[URIā¦]") },
{ NULL }
};
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
priv->notifications = g_hash_table_new_full (g_str_hash,
g_str_equal,
g_free,
NULL);
priv->undo_manager = nautilus_file_undo_manager_new ();
priv->tag_manager_cancellable = g_cancellable_new ();
priv->tag_manager = nautilus_tag_manager_get ();
nautilus_tag_manager_set_cancellable (priv->tag_manager,
priv->tag_manager_cancellable);
g_application_add_main_option_entries (G_APPLICATION (self), options);
nautilus_ensure_extension_points ();
nautilus_ensure_extension_builtins ();
}
static void
theme_changed (GtkSettings *settings)
{
static GtkCssProvider *provider = NULL;
static GtkCssProvider *permanent_provider = NULL;
gchar *theme;
GdkScreen *screen;
GFile *file;
g_object_get (settings, "gtk-theme-name", &theme, NULL);
screen = gdk_screen_get_default ();
/* CSS that themes can override */
if (g_str_equal (theme, "Adwaita") || g_str_equal (theme, "Adwaita-dark"))
{
if (provider == NULL)
{
provider = gtk_css_provider_new ();
file = g_file_new_for_uri ("resource:///org/gnome/nautilus/css/Adwaita.css");
gtk_css_provider_load_from_file (provider, file, NULL);
g_object_unref (file);
}
gtk_style_context_add_provider_for_screen (screen,
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
else if (provider != NULL)
{
gtk_style_context_remove_provider_for_screen (screen,
GTK_STYLE_PROVIDER (provider));
g_clear_object (&provider);
}
/* CSS we want to always load for any theme */
if (permanent_provider == NULL)
{
permanent_provider = gtk_css_provider_new ();
file = g_file_new_for_uri ("resource:///org/gnome/nautilus/css/nautilus.css");
gtk_css_provider_load_from_file (permanent_provider, file, NULL);
/* The behavior of two style providers with the same priority is
* undefined and gtk happens to prefer the provider that got added last.
* Use a higher priority here to avoid this problem.
*/
gtk_style_context_add_provider_for_screen (screen,
GTK_STYLE_PROVIDER (permanent_provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1);
g_object_unref (file);
}
g_free (theme);
}
static void
setup_theme_extensions (void)
{
GtkSettings *settings;
/* Set up a handler to load our custom css for Adwaita.
* See https://bugzilla.gnome.org/show_bug.cgi?id=732959
* for a more automatic solution that is still under discussion.
*/
settings = gtk_settings_get_default ();
g_signal_connect (settings, "notify::gtk-theme-name", G_CALLBACK (theme_changed), NULL);
theme_changed (settings);
}
NautilusApplication *
nautilus_application_get_default (void)
{
NautilusApplication *self;
self = NAUTILUS_APPLICATION (g_application_get_default ());
return self;
}
void
nautilus_application_send_notification (NautilusApplication *self,
const gchar *notification_id,
GNotification *notification)
{
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
g_hash_table_add (priv->notifications, g_strdup (notification_id));
g_application_send_notification (G_APPLICATION (self), notification_id, notification);
}
void
nautilus_application_withdraw_notification (NautilusApplication *self,
const gchar *notification_id)
{
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
if (!g_hash_table_contains (priv->notifications, notification_id))
{
return;
}
g_hash_table_remove (priv->notifications, notification_id);
g_application_withdraw_notification (G_APPLICATION (self), notification_id);
}
static void
on_application_shutdown (GApplication *application,
gpointer user_data)
{
NautilusApplication *self = NAUTILUS_APPLICATION (application);
NautilusApplicationPrivate *priv;
GList *notification_ids;
GList *l;
gchar *notification_id;
priv = nautilus_application_get_instance_private (self);
notification_ids = g_hash_table_get_keys (priv->notifications);
for (l = notification_ids; l != NULL; l = l->next)
{
notification_id = l->data;
g_application_withdraw_notification (application, notification_id);
}
g_list_free (notification_ids);
nautilus_icon_info_clear_caches ();
}
void
nautilus_application_startup_common (NautilusApplication *self)
{
NautilusApplicationPrivate *priv;
nautilus_profile_start (NULL);
priv = nautilus_application_get_instance_private (self);
g_application_set_resource_base_path (G_APPLICATION (self), "/org/gnome/nautilus");
/* chain up to the GTK+ implementation early, so gtk_init()
* is called for us.
*/
G_APPLICATION_CLASS (nautilus_application_parent_class)->startup (G_APPLICATION (self));
gtk_window_set_default_icon_name (APPLICATION_ID);
setup_theme_extensions ();
/* initialize preferences and create the global GSettings objects */
nautilus_global_preferences_init ();
/* initialize nautilus modules */
nautilus_profile_start ("Modules");
nautilus_module_setup ();
nautilus_profile_end ("Modules");
/* attach menu-provider module callback */
menu_provider_init_callback ();
/* Initialize the UI handler singleton for file operations */
priv->progress_handler = nautilus_progress_persistence_handler_new (G_OBJECT (self));
/* Check the user's .nautilus directories and post warnings
* if there are problems.
*/
check_required_directories (self);
nautilus_init_application_actions (self);
nautilus_profile_end (NULL);
g_signal_connect (self, "shutdown", G_CALLBACK (on_application_shutdown), NULL);
}
static void
nautilus_application_startup (GApplication *app)
{
NautilusApplication *self = NAUTILUS_APPLICATION (app);
NautilusApplicationPrivate *priv;
nautilus_profile_start (NULL);
priv = nautilus_application_get_instance_private (self);
/* create DBus manager */
priv->fdb_manager = nautilus_freedesktop_dbus_new ();
nautilus_application_startup_common (self);
nautilus_profile_end (NULL);
}
static gboolean
nautilus_application_dbus_register (GApplication *app,
GDBusConnection *connection,
const gchar *object_path,
GError **error)
{
NautilusApplication *self = NAUTILUS_APPLICATION (app);
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
priv->dbus_manager = nautilus_dbus_manager_new ();
if (!nautilus_dbus_manager_register (priv->dbus_manager, connection, error))
{
return FALSE;
}
priv->search_provider = nautilus_shell_search_provider_new ();
if (!nautilus_shell_search_provider_register (priv->search_provider, connection, error))
{
return FALSE;
}
return TRUE;
}
static void
nautilus_application_dbus_unregister (GApplication *app,
GDBusConnection *connection,
const gchar *object_path)
{
NautilusApplication *self = NAUTILUS_APPLICATION (app);
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
if (priv->dbus_manager)
{
nautilus_dbus_manager_unregister (priv->dbus_manager);
g_clear_object (&priv->dbus_manager);
}
if (priv->search_provider)
{
nautilus_shell_search_provider_unregister (priv->search_provider);
g_clear_object (&priv->search_provider);
}
}
static void
update_dbus_opened_locations (NautilusApplication *self)
{
NautilusApplicationPrivate *priv;
gint i;
GList *l, *sl;
GList *locations = NULL;
gsize locations_size = 0;
gchar **locations_array;
NautilusWindow *window;
GFile *location;
g_return_if_fail (NAUTILUS_IS_APPLICATION (self));
priv = nautilus_application_get_instance_private (self);
/* Children of nautilus application could not handle the dbus, so don't
* do anything in that case */
if (!priv->fdb_manager)
{
return;
}
for (l = priv->windows; l != NULL; l = l->next)
{
window = l->data;
for (sl = nautilus_window_get_slots (window); sl; sl = sl->next)
{
NautilusWindowSlot *slot = sl->data;
location = nautilus_window_slot_get_location (slot);
if (location != NULL)
{
gchar *uri = g_file_get_uri (location);
GList *found = g_list_find_custom (locations, uri, (GCompareFunc) g_strcmp0);
if (!found)
{
locations = g_list_prepend (locations, uri);
++locations_size;
}
else
{
g_free (uri);
}
}
}
}
locations_array = g_new (gchar *, locations_size + 1);
for (i = 0, l = locations; l; l = l->next, ++i)
{
/* We reuse the locations string locations saved on list */
locations_array[i] = l->data;
}
locations_array[locations_size] = NULL;
nautilus_freedesktop_dbus_set_open_locations (priv->fdb_manager,
(const gchar **) locations_array);
g_free (locations_array);
g_list_free_full (locations, g_free);
}
static void
on_slot_location_changed (NautilusWindowSlot *slot,
GParamSpec *pspec,
NautilusApplication *self)
{
update_dbus_opened_locations (self);
}
static void
on_slot_added (NautilusWindow *window,
NautilusWindowSlot *slot,
NautilusApplication *self)
{
if (nautilus_window_slot_get_location (slot))
{
update_dbus_opened_locations (self);
}
g_signal_connect (slot, "notify::location", G_CALLBACK (on_slot_location_changed), self);
}
static void
on_slot_removed (NautilusWindow *window,
NautilusWindowSlot *slot,
NautilusApplication *self)
{
update_dbus_opened_locations (self);
g_signal_handlers_disconnect_by_func (slot, on_slot_location_changed, self);
}
static void
nautilus_application_window_added (GtkApplication *app,
GtkWindow *window)
{
NautilusApplication *self = NAUTILUS_APPLICATION (app);
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
GTK_APPLICATION_CLASS (nautilus_application_parent_class)->window_added (app, window);
if (NAUTILUS_IS_WINDOW (window))
{
priv->windows = g_list_prepend (priv->windows, window);
g_signal_connect (window, "slot-added", G_CALLBACK (on_slot_added), app);
g_signal_connect (window, "slot-removed", G_CALLBACK (on_slot_removed), app);
}
}
static void
nautilus_application_window_removed (GtkApplication *app,
GtkWindow *window)
{
NautilusApplication *self = NAUTILUS_APPLICATION (app);
NautilusApplicationPrivate *priv;
priv = nautilus_application_get_instance_private (self);
GTK_APPLICATION_CLASS (nautilus_application_parent_class)->window_removed (app, window);
if (NAUTILUS_IS_WINDOW (window))
{
priv->windows = g_list_remove_all (priv->windows, window);
g_signal_handlers_disconnect_by_func (window, on_slot_added, app);
g_signal_handlers_disconnect_by_func (window, on_slot_removed, app);
}
/* if this was the last window, close the previewer */
if (g_list_length (priv->windows) == 0)
{
nautilus_previewer_call_close ();
nautilus_progress_persistence_handler_make_persistent (priv->progress_handler);
}
}
/* Manage the local instance command line options. This is only necessary to
* resolve correctly relative paths, since if the main instance resolve them in
* open(), it will do it with its current cwd, which may not be correct for the
* non main GApplication instance */
static gint
nautilus_application_handle_local_options (GApplication *app,
GVariantDict *options)
{
gchar *cwd;
cwd = g_get_current_dir ();
g_variant_dict_insert (options, "cwd", "s", cwd);
g_free (cwd);
return -1;
}
static void
nautilus_application_class_init (NautilusApplicationClass *class)
{
GObjectClass *object_class;
GApplicationClass *application_class;
GtkApplicationClass *gtkapp_class;
object_class = G_OBJECT_CLASS (class);
object_class->finalize = nautilus_application_finalize;
application_class = G_APPLICATION_CLASS (class);
application_class->startup = nautilus_application_startup;
application_class->activate = nautilus_application_activate;
application_class->dbus_register = nautilus_application_dbus_register;
application_class->dbus_unregister = nautilus_application_dbus_unregister;
application_class->open = nautilus_application_open;
application_class->command_line = nautilus_application_command_line;
application_class->handle_local_options = nautilus_application_handle_local_options;
class->open_location_full = real_open_location_full;
gtkapp_class = GTK_APPLICATION_CLASS (class);
gtkapp_class->window_added = nautilus_application_window_added;
gtkapp_class->window_removed = nautilus_application_window_removed;
}
NautilusApplication *
nautilus_application_new (void)
{
return g_object_new (NAUTILUS_TYPE_APPLICATION,
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_HANDLES_COMMAND_LINE | G_APPLICATION_HANDLES_OPEN,
"inactivity-timeout", 12000,
NULL);
}
void
nautilus_application_search (NautilusApplication *self,
const gchar *uri,
const gchar *text)
{
NautilusWindow *window;
GFile *location;
location = g_file_new_for_uri (uri);
window = open_window (self, location);
nautilus_window_search (window, text);
g_object_unref (location);
}