Permalink
Fetching contributors…
Cannot retrieve contributors at this time
2452 lines (2024 sloc) 71.1 KB
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
* Caja
*
* Copyright (C) 1999, 2000 Red Hat, Inc.
* Copyright (C) 2000, 2001 Eazel, Inc.
*
* Caja 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.
*
* Caja 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, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Authors: Elliot Lee <sopwith@redhat.com>,
* Darin Adler <darin@bentspoon.com>,
* Cosimo Cecchi <cosimoc@gnome.org>
*
*/
#include <config.h>
#include "caja-application.h"
#include "file-manager/fm-desktop-icon-view.h"
#include "file-manager/fm-icon-view.h"
#include "file-manager/fm-list-view.h"
#include "file-manager/fm-tree-view.h"
#if ENABLE_EMPTY_VIEW
#include "file-manager/fm-empty-view.h"
#endif /* ENABLE_EMPTY_VIEW */
#include "caja-information-panel.h"
#include "caja-history-sidebar.h"
#include "caja-places-sidebar.h"
#include "caja-self-check-functions.h"
#include "caja-notes-viewer.h"
#include "caja-emblem-sidebar.h"
#include "caja-image-properties-page.h"
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include "caja-desktop-window.h"
#include "caja-spatial-window.h"
#include "caja-navigation-window.h"
#include "caja-window-slot.h"
#include "caja-navigation-window-slot.h"
#include "caja-window-bookmarks.h"
#include "libcaja-private/caja-file-operations.h"
#include "caja-window-private.h"
#include "caja-window-manage-views.h"
#include "caja-freedesktop-dbus.h"
#include <libxml/xmlsave.h>
#include <glib/gstdio.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
#include <eel/eel-gtk-extensions.h>
#include <eel/eel-gtk-macros.h>
#include <eel/eel-stock-dialogs.h>
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#include <libnotify/notify.h>
#include <libcaja-private/caja-debug-log.h>
#include <libcaja-private/caja-file-utilities.h>
#include <libcaja-private/caja-global-preferences.h>
#include <libcaja-private/caja-lib-self-check-functions.h>
#include <libcaja-private/caja-extensions.h>
#include <libcaja-private/caja-module.h>
#include <libcaja-private/caja-desktop-link-monitor.h>
#include <libcaja-private/caja-directory-private.h>
#include <libcaja-private/caja-signaller.h>
#include <libcaja-extension/caja-menu-provider.h>
#include <libcaja-private/caja-autorun.h>
#define MATE_DESKTOP_USE_UNSTABLE_API
#include <libmate-desktop/mate-bg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/* Keep window from shrinking down ridiculously small; numbers are somewhat arbitrary */
#define APPLICATION_WINDOW_MIN_WIDTH 300
#define APPLICATION_WINDOW_MIN_HEIGHT 100
#define CAJA_ACCEL_MAP_SAVE_DELAY 30
/* Keeps track of all the desktop windows. */
static GList *caja_application_desktop_windows;
/* Keeps track of all the object windows */
static GList *caja_application_spatial_window_list;
/* The saving of the accelerator map was requested */
static gboolean save_of_accel_map_requested = FALSE;
/* File Manager DBus Interface */
static CajaFreedesktopDBus *fdb_manager = NULL;
static char * caja_application_get_session_data (CajaApplication *self);
void caja_application_quit (CajaApplication *self);
G_DEFINE_TYPE (CajaApplication, caja_application, GTK_TYPE_APPLICATION);
struct _CajaApplicationPriv {
GVolumeMonitor *volume_monitor;
gboolean no_desktop;
gboolean force_desktop;
gboolean autostart;
gchar *geometry;
};
GList *
caja_application_get_spatial_window_list (void)
{
return caja_application_spatial_window_list;
}
static void
startup_volume_mount_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
g_volume_mount_finish (G_VOLUME (source_object), res, NULL);
}
static void
automount_all_volumes (CajaApplication *application)
{
GList *volumes, *l;
GMount *mount;
GVolume *volume;
if (g_settings_get_boolean (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTOMOUNT))
{
/* automount all mountable volumes at start-up */
volumes = g_volume_monitor_get_volumes (application->priv->volume_monitor);
for (l = volumes; l != NULL; l = l->next)
{
volume = l->data;
if (!g_volume_should_automount (volume) ||
!g_volume_can_mount (volume))
{
continue;
}
mount = g_volume_get_mount (volume);
if (mount != NULL)
{
g_object_unref (mount);
continue;
}
/* pass NULL as GMountOperation to avoid user interaction */
g_volume_mount (volume, 0, NULL, NULL, startup_volume_mount_cb, NULL);
}
g_list_free_full (volumes, g_object_unref);
}
}
static void
smclient_save_state_cb (EggSMClient *client,
GKeyFile *state_file,
CajaApplication *application)
{
char *data;
data = caja_application_get_session_data (application);
if (data != NULL)
{
g_key_file_set_string (state_file,
"Caja",
"documents",
data);
}
g_free (data);
}
static void
smclient_quit_cb (EggSMClient *client,
CajaApplication *application)
{
caja_application_quit (application);
}
static void
caja_application_smclient_initialize (CajaApplication *self)
{
g_signal_connect (self->smclient, "save_state",
G_CALLBACK (smclient_save_state_cb),
self);
g_signal_connect (self->smclient, "quit",
G_CALLBACK (smclient_quit_cb),
self);
/* TODO: Should connect to quit_requested and block logout on active transfer? */
}
void
caja_application_smclient_startup (CajaApplication *self)
{
g_assert (self->smclient == NULL);
self->smclient = egg_sm_client_get ();
}
static void
caja_empty_callback_to_ensure_read() {
/*do nothing, just exist to suppress runtime error*/
}
static void
open_window (CajaApplication *application,
GFile *location, GdkScreen *screen, const char *geometry, gboolean browser_window)
{
CajaApplication *self = CAJA_APPLICATION (application);
CajaWindow *window;
gchar *uri;
uri = g_file_get_uri (location);
g_debug ("Opening new window at uri %s", uri);
/*monitor the preference to use browser or spatial windows */
/*connect before trying to read or this preference won't be read by root or after change*/
g_signal_connect_swapped(caja_preferences, "changed::"CAJA_PREFERENCES_ALWAYS_USE_BROWSER,
G_CALLBACK (caja_empty_callback_to_ensure_read),
self);
if (browser_window ||g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
window = caja_application_create_navigation_window (application,
screen);
}
else {
window = caja_application_get_spatial_window (application,
NULL,
NULL,
location,
screen,
NULL);
}
caja_window_go_to (window, location);
if (geometry != NULL && !gtk_widget_get_visible (GTK_WIDGET (window))) {
/* never maximize windows opened from shell if a
* custom geometry has been requested.
*/
gtk_window_unmaximize (GTK_WINDOW (window));
eel_gtk_window_set_initial_geometry_from_string (GTK_WINDOW (window),
geometry,
APPLICATION_WINDOW_MIN_WIDTH,
APPLICATION_WINDOW_MIN_HEIGHT,
FALSE);
}
g_free (uri);
}
static void
open_windows (CajaApplication *application,
GFile **files,
GdkScreen *screen,
const char *geometry,
guint len,
gboolean browser_window)
{
guint i;
if (files == NULL || files[0] == NULL) {
/* Open a window pointing at the default location. */
open_window (application, NULL, screen, geometry, browser_window );
} else {
/* Open windows at each requested location. */
i = 0;
while (i < len ){
open_window (application, files[i], screen, geometry, browser_window);
i++ ;
}
}
}
static void
caja_application_open (GApplication *app,
GFile **files,
gint n_files,
const gchar *options)
{
CajaApplication *self = CAJA_APPLICATION (app);
gboolean browser_window = FALSE;
const gchar *geometry = NULL;
const char splitter = '=';
g_debug ("Open called on the GApplication instance; %d files", n_files);
/*Check if local command line passed --browser or --geometry */
if (strcmp(options,"") != 0 ){
if (g_str_match_string ("browser",
options,
FALSE) == TRUE){
browser_window = TRUE;
geometry = strchr(options, splitter);
}
else {
geometry = options;
}
/*Reset this or 3ed and later invocations will use same
*geometry even if the user has resized open window
*/
self->priv->geometry = NULL;
}
open_windows (self, files,
gdk_screen_get_default (),
geometry,
n_files,
browser_window);
}
void
caja_application_open_location (CajaApplication *application,
GFile *location,
GFile *selection,
const char *startup_id)
{
CajaWindow *window;
GList *sel_list = NULL;
window = caja_application_create_navigation_window (application, gdk_screen_get_default ());
if (selection != NULL) {
sel_list = g_list_prepend (NULL, g_object_ref (selection));
}
caja_window_slot_open_location_full (caja_window_get_active_slot (window), location,
0, CAJA_WINDOW_OPEN_FLAG_NEW_WINDOW, sel_list, NULL, NULL);
if (sel_list != NULL) {
caja_file_list_free (sel_list);
}
}
void
caja_application_quit (CajaApplication *self)
{
GApplication *app = G_APPLICATION (self);
GList *windows;
windows = gtk_application_get_windows (GTK_APPLICATION (app));
g_list_foreach (windows, (GFunc) gtk_widget_destroy, NULL);
/* we have been asked to force quit */
g_application_quit (G_APPLICATION (self));
}
static void
caja_application_init (CajaApplication *application)
{
GSimpleAction *action;
application->priv =
G_TYPE_INSTANCE_GET_PRIVATE (application, CAJA_TYPE_APPLICATION,
CajaApplicationPriv);
action = g_simple_action_new ("quit", NULL);
g_action_map_add_action (G_ACTION_MAP (application), G_ACTION (action));
g_signal_connect_swapped (action, "activate",
G_CALLBACK (caja_application_quit), application);
g_object_unref (action);
}
static void
caja_application_finalize (GObject *object)
{
CajaApplication *application;
application = CAJA_APPLICATION (object);
caja_bookmarks_exiting ();
if (application->volume_monitor)
{
g_object_unref (application->priv->volume_monitor);
application->priv->volume_monitor = NULL;
}
g_free (application->priv->geometry);
if (application->ss_watch_id > 0)
{
g_bus_unwatch_name (application->ss_watch_id);
}
if (application->volume_queue != NULL)
{
g_list_free_full (application->volume_queue, g_object_unref);
application->volume_queue = NULL;
}
if (application->automount_idle_id != 0)
{
g_source_remove (application->automount_idle_id);
application->automount_idle_id = 0;
}
if (fdb_manager != NULL)
{
g_object_unref (fdb_manager);
fdb_manager = NULL;
}
if (application->ss_proxy != NULL)
{
g_object_unref (application->ss_proxy);
application->ss_proxy = NULL;
}
notify_uninit ();
G_OBJECT_CLASS (caja_application_parent_class)->finalize (object);
}
static gboolean
check_required_directories (CajaApplication *application)
{
char *user_directory;
char *desktop_directory;
GSList *directories;
gboolean ret;
g_assert (CAJA_IS_APPLICATION (application));
ret = TRUE;
user_directory = caja_get_user_directory ();
desktop_directory = caja_get_desktop_directory ();
directories = NULL;
if (!g_file_test (user_directory, G_FILE_TEST_IS_DIR))
{
directories = g_slist_prepend (directories, user_directory);
}
if (!g_file_test (desktop_directory, G_FILE_TEST_IS_DIR))
{
directories = g_slist_prepend (directories, desktop_directory);
}
if (directories != NULL)
{
int failed_count;
GString *directories_as_string;
GSList *l;
char *error_string;
const char *detail_string;
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);
}
if (failed_count == 1)
{
error_string = g_strdup_printf (_("Caja could not create the required folder \"%s\"."),
directories_as_string->str);
detail_string = _("Before running Caja, please create the following folder, or "
"set permissions such that Caja can create it.");
}
else
{
error_string = g_strdup_printf (_("Caja could not create the following required folders: "
"%s."), directories_as_string->str);
detail_string = _("Before running Caja, please create these folders, or "
"set permissions such that Caja can create them.");
}
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 (application),
GTK_WINDOW (dialog));
g_string_free (directories_as_string, TRUE);
g_free (error_string);
}
g_slist_free (directories);
g_free (user_directory);
g_free (desktop_directory);
return ret;
}
static void
menu_provider_items_updated_handler (CajaMenuProvider *provider, GtkWidget* parent_window, gpointer data)
{
g_signal_emit_by_name (caja_signaller_get_current (),
"popup_menu_changed");
}
static void
menu_provider_init_callback (void)
{
GList *providers;
GList *l;
providers = caja_extensions_get_for_type (CAJA_TYPE_MENU_PROVIDER);
for (l = providers; l != NULL; l = l->next)
{
CajaMenuProvider *provider = CAJA_MENU_PROVIDER (l->data);
g_signal_connect_after (G_OBJECT (provider), "items_updated",
(GCallback)menu_provider_items_updated_handler,
NULL);
}
caja_module_extension_list_free (providers);
}
static gboolean
automount_all_volumes_idle_cb (gpointer data)
{
CajaApplication *application = CAJA_APPLICATION (data);
automount_all_volumes (application);
application->automount_idle_id = 0;
return FALSE;
}
static void
check_volume_queue (CajaApplication *application)
{
GList *l, *next;
GVolume *volume;
l = application->volume_queue;
if (application->screensaver_active)
{
return;
}
while (l != NULL) {
volume = l->data;
next = l->next;
caja_file_operations_mount_volume (NULL, volume, TRUE);
application->volume_queue =
g_list_remove (application->volume_queue, volume);
g_object_unref (volume);
l = next;
}
application->volume_queue = NULL;
}
#define SCREENSAVER_NAME "org.mate.ScreenSaver"
#define SCREENSAVER_PATH "/org/mate/ScreenSaver"
#define SCREENSAVER_INTERFACE "org.mate.ScreenSaver"
static void
screensaver_signal_callback (GDBusProxy *proxy,
const gchar *sender_name,
const gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
CajaApplication *application = user_data;
if (g_strcmp0 (signal_name, "ActiveChanged") == 0)
{
g_variant_get (parameters, "(b)", &application->screensaver_active);
g_debug ("Screensaver active changed to %d", application->screensaver_active);
check_volume_queue (application);
}
}
static void
screensaver_get_active_ready_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
CajaApplication *application = user_data;
GDBusProxy *proxy = application->ss_proxy;
GVariant *result;
GError *error = NULL;
result = g_dbus_proxy_call_finish (proxy,
res,
&error);
if (error != NULL) {
g_warning ("Can't call GetActive() on the ScreenSaver object: %s",
error->message);
g_error_free (error);
return;
}
g_variant_get (result, "(b)", &application->screensaver_active);
g_variant_unref (result);
g_debug ("Screensaver GetActive() returned %d", application->screensaver_active);
}
static void
screensaver_proxy_ready_cb (GObject *source,
GAsyncResult *res,
gpointer user_data)
{
CajaApplication *application = user_data;
GError *error = NULL;
GDBusProxy *ss_proxy;
ss_proxy = g_dbus_proxy_new_finish (res, &error);
if (error != NULL)
{
g_warning ("Can't get proxy for the ScreenSaver object: %s",
error->message);
g_error_free (error);
return;
}
g_debug ("ScreenSaver proxy ready");
application->ss_proxy = ss_proxy;
g_signal_connect (ss_proxy, "g-signal",
G_CALLBACK (screensaver_signal_callback), application);
g_dbus_proxy_call (ss_proxy,
"GetActive",
NULL,
G_DBUS_CALL_FLAGS_NO_AUTO_START,
-1,
NULL,
screensaver_get_active_ready_cb,
application);
}
static void
screensaver_appeared_callback (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
CajaApplication *application = user_data;
g_debug ("ScreenSaver name appeared");
application->screensaver_active = FALSE;
g_dbus_proxy_new (connection,
G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
NULL,
name,
SCREENSAVER_PATH,
SCREENSAVER_INTERFACE,
NULL,
screensaver_proxy_ready_cb,
application);
}
static void
screensaver_vanished_callback (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
CajaApplication *application = user_data;
g_debug ("ScreenSaver name vanished");
application->screensaver_active = FALSE;
if (application->ss_proxy != NULL)
{
g_object_unref (application->ss_proxy);
application->ss_proxy = NULL;
}
/* in this case force a clear of the volume queue, without
* mounting them.
*/
if (application->volume_queue != NULL)
{
g_list_free_full (application->volume_queue, g_object_unref);
application->volume_queue = NULL;
}
}
static void
do_initialize_screensaver (CajaApplication *application)
{
application->ss_watch_id =
g_bus_watch_name (G_BUS_TYPE_SESSION,
SCREENSAVER_NAME,
G_BUS_NAME_WATCHER_FLAGS_NONE,
screensaver_appeared_callback,
screensaver_vanished_callback,
application,
NULL);
}
static void
selection_get_cb (GtkWidget *widget,
GtkSelectionData *selection_data,
guint info,
guint time)
{
/* No extra targets atm */
}
static GtkWidget *
get_desktop_manager_selection (GdkDisplay *display)
{
char selection_name[32];
GdkAtom selection_atom;
Window selection_owner;
GtkWidget *selection_widget;
g_snprintf (selection_name, sizeof (selection_name), "_NET_DESKTOP_MANAGER_S0");
selection_atom = gdk_atom_intern (selection_name, FALSE);
selection_owner = XGetSelectionOwner (GDK_DISPLAY_XDISPLAY (display),
gdk_x11_atom_to_xatom_for_display (display,
selection_atom));
if (selection_owner != None)
{
return NULL;
}
selection_widget = gtk_invisible_new_for_screen (gdk_display_get_default_screen (display));
/* We need this for gdk_x11_get_server_time() */
gtk_widget_add_events (selection_widget, GDK_PROPERTY_CHANGE_MASK);
if (gtk_selection_owner_set_for_display (display,
selection_widget,
selection_atom,
gdk_x11_get_server_time (gtk_widget_get_window (selection_widget))))
{
g_signal_connect (selection_widget, "selection_get",
G_CALLBACK (selection_get_cb), NULL);
return selection_widget;
}
gtk_widget_destroy (selection_widget);
return NULL;
}
static void
desktop_unrealize_cb (GtkWidget *widget,
GtkWidget *selection_widget)
{
gtk_widget_destroy (selection_widget);
}
static gboolean
selection_clear_event_cb (GtkWidget *widget,
GdkEventSelection *event,
CajaDesktopWindow *window)
{
gtk_widget_destroy (GTK_WIDGET (window));
caja_application_desktop_windows =
g_list_remove (caja_application_desktop_windows, window);
return TRUE;
}
static void
caja_application_create_desktop_windows (CajaApplication *application)
{
GdkDisplay *display;
CajaDesktopWindow *window;
GtkWidget *selection_widget;
g_return_if_fail (caja_application_desktop_windows == NULL);
g_return_if_fail (CAJA_IS_APPLICATION (application));
display = gdk_display_get_default ();
selection_widget = get_desktop_manager_selection (display);
if (selection_widget != NULL)
{
window = caja_desktop_window_new (application, gdk_display_get_default_screen (display));
g_signal_connect (selection_widget, "selection_clear_event",
G_CALLBACK (selection_clear_event_cb), window);
g_signal_connect (window, "unrealize",
G_CALLBACK (desktop_unrealize_cb), selection_widget);
/* We realize it immediately so that the CAJA_DESKTOP_WINDOW_ID
property is set so mate-settings-daemon doesn't try to set the
background. And we do a gdk_flush() to be sure X gets it. */
gtk_widget_realize (GTK_WIDGET (window));
gdk_flush ();
caja_application_desktop_windows =
g_list_prepend (caja_application_desktop_windows, window);
gtk_application_add_window (GTK_APPLICATION (application),
GTK_WINDOW (window));
}
}
void
caja_application_open_desktop (CajaApplication *application)
{
if (caja_application_desktop_windows == NULL)
{
caja_application_create_desktop_windows (application);
}
}
static void
caja_application_close_desktop (void)
{
if (caja_application_desktop_windows != NULL)
{
g_list_free_full (caja_application_desktop_windows, (GDestroyNotify) gtk_widget_destroy);
caja_application_desktop_windows = NULL;
}
}
void
caja_application_close_all_navigation_windows (CajaApplication *self)
{
GList *list_copy;
GList *l;
list_copy = g_list_copy (gtk_application_get_windows (GTK_APPLICATION (self)));
/* First hide all window to get the feeling of quick response */
for (l = list_copy; l != NULL; l = l->next)
{
CajaWindow *window;
window = CAJA_WINDOW (l->data);
if (CAJA_IS_NAVIGATION_WINDOW (window))
{
gtk_widget_hide (GTK_WIDGET (window));
}
}
for (l = list_copy; l != NULL; l = l->next)
{
CajaWindow *window;
window = CAJA_WINDOW (l->data);
if (CAJA_IS_NAVIGATION_WINDOW (window))
{
caja_window_close (window);
}
}
g_list_free (list_copy);
}
static CajaSpatialWindow *
caja_application_get_existing_spatial_window (GFile *location)
{
GList *l;
CajaWindowSlot *slot;
GFile *window_location;
for (l = caja_application_get_spatial_window_list ();
l != NULL; l = l->next) {
slot = CAJA_WINDOW (l->data)->details->active_pane->active_slot;
window_location = slot->pending_location;
if (window_location == NULL) {
window_location = slot->location;
}
if (window_location != NULL) {
if (g_file_equal (location, window_location)) {
return CAJA_SPATIAL_WINDOW (l->data);
}
}
}
return NULL;
}
static CajaSpatialWindow *
find_parent_spatial_window (CajaSpatialWindow *window)
{
CajaFile *file;
CajaFile *parent_file;
CajaWindowSlot *slot;
GFile *location;
slot = CAJA_WINDOW (window)->details->active_pane->active_slot;
location = slot->location;
if (location == NULL)
{
return NULL;
}
file = caja_file_get (location);
if (!file)
{
return NULL;
}
parent_file = caja_file_get_parent (file);
caja_file_unref (file);
while (parent_file)
{
CajaSpatialWindow *parent_window;
location = caja_file_get_location (parent_file);
parent_window = caja_application_get_existing_spatial_window (location);
g_object_unref (location);
/* Stop at the desktop directory if it's not explicitely opened
* in a spatial window of its own.
*/
if (caja_file_is_desktop_directory (parent_file) && !parent_window)
{
caja_file_unref (parent_file);
return NULL;
}
if (parent_window)
{
caja_file_unref (parent_file);
return parent_window;
}
file = parent_file;
parent_file = caja_file_get_parent (file);
caja_file_unref (file);
}
return NULL;
}
void
caja_application_close_parent_windows (CajaSpatialWindow *window)
{
CajaSpatialWindow *parent_window;
CajaSpatialWindow *new_parent_window;
g_return_if_fail (CAJA_IS_SPATIAL_WINDOW (window));
parent_window = find_parent_spatial_window (window);
while (parent_window)
{
new_parent_window = find_parent_spatial_window (parent_window);
caja_window_close (CAJA_WINDOW (parent_window));
parent_window = new_parent_window;
}
}
void
caja_application_close_all_spatial_windows (void)
{
GList *list_copy;
GList *l;
list_copy = g_list_copy (caja_application_spatial_window_list);
/* First hide all window to get the feeling of quick response */
for (l = list_copy; l != NULL; l = l->next)
{
CajaWindow *window;
window = CAJA_WINDOW (l->data);
if (CAJA_IS_SPATIAL_WINDOW (window))
{
gtk_widget_hide (GTK_WIDGET (window));
}
}
for (l = list_copy; l != NULL; l = l->next)
{
CajaWindow *window;
window = CAJA_WINDOW (l->data);
if (CAJA_IS_SPATIAL_WINDOW (window))
{
caja_window_close (window);
}
}
g_list_free (list_copy);
}
static gboolean
caja_window_delete_event_callback (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
{
CajaWindow *window;
window = CAJA_WINDOW (widget);
caja_window_close (window);
return TRUE;
}
static CajaWindow *
create_window (CajaApplication *application,
GType window_type,
GdkScreen *screen)
{
CajaWindow *window;
g_return_val_if_fail (CAJA_IS_APPLICATION (application), NULL);
window = CAJA_WINDOW (gtk_widget_new (window_type,
"app", application,
"screen", screen,
NULL));
g_signal_connect_data (window, "delete_event",
G_CALLBACK (caja_window_delete_event_callback), NULL, NULL,
G_CONNECT_AFTER);
gtk_application_add_window (GTK_APPLICATION (application),
GTK_WINDOW (window));
/* Do not yet show the window. It will be shown later on if it can
* successfully display its initial URI. Otherwise it will be destroyed
* without ever having seen the light of day.
*/
return window;
}
static void
spatial_window_destroyed_callback (void *user_data, GObject *window)
{
caja_application_spatial_window_list = g_list_remove (caja_application_spatial_window_list, window);
}
CajaWindow *
caja_application_get_spatial_window (CajaApplication *application,
CajaWindow *requesting_window,
const char *startup_id,
GFile *location,
GdkScreen *screen,
gboolean *existing)
{
CajaWindow *window;
gchar *uri;
g_return_val_if_fail (CAJA_IS_APPLICATION (application), NULL);
window = CAJA_WINDOW
(caja_application_get_existing_spatial_window (location));
if (window != NULL) {
if (existing != NULL) {
*existing = TRUE;
}
return window;
}
if (existing != NULL) {
*existing = FALSE;
}
window = create_window (application, CAJA_TYPE_SPATIAL_WINDOW, screen);
if (requesting_window)
{
/* Center the window over the requesting window by default */
int orig_x, orig_y, orig_width, orig_height;
int new_x, new_y, new_width, new_height;
gtk_window_get_position (GTK_WINDOW (requesting_window),
&orig_x, &orig_y);
gtk_window_get_size (GTK_WINDOW (requesting_window),
&orig_width, &orig_height);
gtk_window_get_default_size (GTK_WINDOW (window),
&new_width, &new_height);
new_x = orig_x + (orig_width - new_width) / 2;
new_y = orig_y + (orig_height - new_height) / 2;
if (orig_width - new_width < 10)
{
new_x += 10;
new_y += 10;
}
gtk_window_move (GTK_WINDOW (window), new_x, new_y);
}
caja_application_spatial_window_list = g_list_prepend (caja_application_spatial_window_list, window);
g_object_weak_ref (G_OBJECT (window),
spatial_window_destroyed_callback, NULL);
uri = g_file_get_uri (location);
caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
"present NEW spatial window=%p: %s",
window, uri);
g_free (uri);
return window;
}
CajaWindow *
caja_application_create_navigation_window (CajaApplication *application,
GdkScreen *screen)
{
CajaWindow *window;
char *geometry_string;
gboolean maximized;
g_return_val_if_fail (CAJA_IS_APPLICATION (application), NULL);
window = create_window (application, CAJA_TYPE_NAVIGATION_WINDOW, screen);
maximized = g_settings_get_boolean (caja_window_state,
CAJA_WINDOW_STATE_MAXIMIZED);
if (maximized)
{
gtk_window_maximize (GTK_WINDOW (window));
}
else
{
gtk_window_unmaximize (GTK_WINDOW (window));
}
geometry_string = g_settings_get_string (caja_window_state,
CAJA_WINDOW_STATE_GEOMETRY);
if (geometry_string != NULL &&
geometry_string[0] != 0)
{
eel_gtk_window_set_initial_geometry_from_string
(GTK_WINDOW (window),
geometry_string,
CAJA_NAVIGATION_WINDOW_MIN_WIDTH,
CAJA_NAVIGATION_WINDOW_MIN_HEIGHT,
TRUE);
}
g_free (geometry_string);
caja_debug_log (FALSE, CAJA_DEBUG_LOG_DOMAIN_USER,
"create new navigation window=%p",
window);
return window;
}
/* callback for showing or hiding the desktop based on the user's preference */
static void
desktop_changed_callback (gpointer user_data)
{
CajaApplication *application;
application = CAJA_APPLICATION (user_data);
if (g_settings_get_boolean (mate_background_preferences, MATE_BG_KEY_SHOW_DESKTOP))
{
caja_application_open_desktop (application);
}
else
{
caja_application_close_desktop ();
}
}
static gboolean
window_can_be_closed (CajaWindow *window)
{
if (!CAJA_IS_DESKTOP_WINDOW (window))
{
return TRUE;
}
return FALSE;
}
static void
check_screen_lock_and_mount (CajaApplication *application,
GVolume *volume)
{
if (application->screensaver_active)
{
/* queue the volume, to mount it after the screensaver state changed */
g_debug ("Queuing volume %p", volume);
application->volume_queue = g_list_prepend (application->volume_queue,
g_object_ref (volume));
} else {
/* mount it immediately */
caja_file_operations_mount_volume (NULL, volume, TRUE);
}
}
static void
volume_removed_callback (GVolumeMonitor *monitor,
GVolume *volume,
CajaApplication *application)
{
g_debug ("Volume %p removed, removing from the queue", volume);
/* clear it from the queue, if present */
application->volume_queue =
g_list_remove (application->volume_queue, volume);
}
static void
volume_added_callback (GVolumeMonitor *monitor,
GVolume *volume,
CajaApplication *application)
{
if (g_settings_get_boolean (caja_media_preferences, CAJA_PREFERENCES_MEDIA_AUTOMOUNT) &&
g_volume_should_automount (volume) &&
g_volume_can_mount (volume))
{
check_screen_lock_and_mount (application, volume);
}
else
{
/* Allow caja_autorun() to run. When the mount is later
* added programmatically (i.e. for a blank CD),
* caja_autorun() will be called by mount_added_callback(). */
caja_allow_autorun_for_volume (volume);
caja_allow_autorun_for_volume_finish (volume);
}
}
static void
drive_eject_cb (GObject *source_object,
GAsyncResult *res,
gpointer user_data)
{
GError *error;
char *primary;
char *name;
error = NULL;
if (!g_drive_eject_with_operation_finish (G_DRIVE (source_object), res, &error))
{
if (error->code != G_IO_ERROR_FAILED_HANDLED)
{
name = g_drive_get_name (G_DRIVE (source_object));
primary = g_strdup_printf (_("Unable to eject %s"), name);
g_free (name);
eel_show_error_dialog (primary,
error->message,
NULL);
g_free (primary);
}
g_error_free (error);
}
}
static void
drive_eject_button_pressed (GDrive *drive,
CajaApplication *application)
{
GMountOperation *mount_op;
mount_op = gtk_mount_operation_new (NULL);
g_drive_eject_with_operation (drive, 0, mount_op, NULL, drive_eject_cb, NULL);
g_object_unref (mount_op);
}
static void
drive_listen_for_eject_button (GDrive *drive, CajaApplication *application)
{
g_signal_connect (drive,
"eject-button",
G_CALLBACK (drive_eject_button_pressed),
application);
}
static void
drive_connected_callback (GVolumeMonitor *monitor,
GDrive *drive,
CajaApplication *application)
{
drive_listen_for_eject_button (drive, application);
}
static void
autorun_show_window (GMount *mount, gpointer user_data)
{
GFile *location;
CajaApplication *application = user_data;
CajaWindow *window;
gboolean existing;
location = g_mount_get_root (mount);
existing = FALSE;
/* There should probably be an easier way to do this */
if (g_settings_get_boolean (caja_preferences, CAJA_PREFERENCES_ALWAYS_USE_BROWSER)) {
window = caja_application_create_navigation_window (application,
gdk_screen_get_default ());
}
else
{
window = caja_application_get_spatial_window (application,
NULL,
NULL,
location,
gdk_screen_get_default (),
NULL);
}
caja_window_go_to (window, location);
g_object_unref (location);
}
static void
mount_added_callback (GVolumeMonitor *monitor,
GMount *mount,
CajaApplication *application)
{
CajaDirectory *directory;
GFile *root;
gchar *uri;
root = g_mount_get_root (mount);
uri = g_file_get_uri (root);
g_debug ("Added mount at uri %s", uri);
g_free (uri);
directory = caja_directory_get_existing (root);
g_object_unref (root);
if (directory != NULL) {
caja_directory_force_reload (directory);
caja_directory_unref (directory);
}
caja_autorun (mount, autorun_show_window, application);
}
static CajaWindowSlot *
get_first_navigation_slot (GList *slot_list)
{
GList *l;
for (l = slot_list; l != NULL; l = l->next)
{
if (CAJA_IS_NAVIGATION_WINDOW_SLOT (l->data))
{
return l->data;
}
}
return NULL;
}
/* We redirect some slots and close others */
static gboolean
should_close_slot_with_mount (CajaWindow *window,
CajaWindowSlot *slot,
GMount *mount)
{
if (CAJA_IS_SPATIAL_WINDOW (window))
{
return TRUE;
}
return caja_navigation_window_slot_should_close_with_mount (CAJA_NAVIGATION_WINDOW_SLOT (slot),
mount);
}
/* Called whenever a mount is unmounted. Check and see if there are
* any windows open displaying contents on the mount. If there are,
* close them. It would also be cool to save open window and position
* info.
*
* This is also called on pre_unmount.
*/
static void
mount_removed_callback (GVolumeMonitor *monitor,
GMount *mount,
CajaApplication *application)
{
GList *window_list, *node, *close_list;
CajaWindow *window;
CajaWindowSlot *slot;
CajaWindowSlot *force_no_close_slot;
GFile *root, *computer;
gboolean unclosed_slot;
close_list = NULL;
force_no_close_slot = NULL;
unclosed_slot = FALSE;
/* Check and see if any of the open windows are displaying contents from the unmounted mount */
window_list = gtk_application_get_windows (GTK_APPLICATION (application));
root = g_mount_get_root (mount);
/* 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)
{
window = CAJA_WINDOW (node->data);
if (window != NULL && window_can_be_closed (window))
{
GList *l;
GList *lp;
GFile *location;
for (lp = window->details->panes; lp != NULL; lp = lp->next)
{
CajaWindowPane *pane;
pane = (CajaWindowPane*) lp->data;
for (l = pane->slots; l != NULL; l = l->next)
{
slot = l->data;
location = slot->location;
if (g_file_has_prefix (location, root) ||
g_file_equal (location, root))
{
close_list = g_list_prepend (close_list, slot);
if (!should_close_slot_with_mount (window, slot, mount))
{
/* We'll be redirecting this, not closing */
unclosed_slot = TRUE;
}
}
else
{
unclosed_slot = TRUE;
}
} /* for all slots */
} /* for all panes */
}
}
if (caja_application_desktop_windows == NULL &&
!unclosed_slot)
{
/* We are trying to close all open slots. Keep one navigation slot open. */
force_no_close_slot = get_first_navigation_slot (close_list);
}
/* Handle the windows in the close list. */
for (node = close_list; node != NULL; node = node->next)
{
slot = node->data;
window = slot->pane->window;
if (should_close_slot_with_mount (window, slot, mount) &&
slot != force_no_close_slot)
{
caja_window_slot_close (slot);
}
else
{
computer = g_file_new_for_uri ("computer:///");
caja_window_slot_go_to (slot, computer, FALSE);
g_object_unref(computer);
}
}
g_list_free (close_list);
}
static char *
icon_to_string (GIcon *icon)
{
const char * const *names;
GFile *file;
if (icon == NULL)
{
return NULL;
}
else if (G_IS_THEMED_ICON (icon))
{
names = g_themed_icon_get_names (G_THEMED_ICON (icon));
return g_strjoinv (":", (char **)names);
}
else if (G_IS_FILE_ICON (icon))
{
file = g_file_icon_get_file (G_FILE_ICON (icon));
return g_file_get_path (file);
}
return NULL;
}
static GIcon *
icon_from_string (const char *string)
{
GFile *file;
GIcon *icon;
gchar **names;
if (g_path_is_absolute (string))
{
file = g_file_new_for_path (string);
icon = g_file_icon_new (file);
g_object_unref (file);
return icon;
}
else
{
names = g_strsplit (string, ":", 0);
icon = g_themed_icon_new_from_names (names, -1);
g_strfreev (names);
return icon;
}
return NULL;
}
static char *
caja_application_get_session_data (CajaApplication *self)
{
xmlDocPtr doc;
xmlNodePtr root_node, history_node;
GList *l, *window_list;
char *data;
unsigned n_processed;
xmlSaveCtxtPtr ctx;
xmlBufferPtr buffer;
doc = xmlNewDoc ("1.0");
root_node = xmlNewNode (NULL, "session");
xmlDocSetRootElement (doc, root_node);
history_node = xmlNewChild (root_node, NULL, "history", NULL);
n_processed = 0;
for (l = caja_get_history_list (); l != NULL; l = l->next) {
CajaBookmark *bookmark;
xmlNodePtr bookmark_node;
GIcon *icon;
char *tmp;
bookmark = l->data;
bookmark_node = xmlNewChild (history_node, NULL, "bookmark", NULL);
tmp = caja_bookmark_get_name (bookmark);
xmlNewProp (bookmark_node, "name", tmp);
g_free (tmp);
icon = caja_bookmark_get_icon (bookmark);
tmp = g_icon_to_string (icon);
g_object_unref (icon);
if (tmp) {
xmlNewProp (bookmark_node, "icon", tmp);
g_free (tmp);
}
tmp = caja_bookmark_get_uri (bookmark);
xmlNewProp (bookmark_node, "uri", tmp);
g_free (tmp);
if (caja_bookmark_get_has_custom_name (bookmark)) {
xmlNewProp (bookmark_node, "has_custom_name", "TRUE");
}
if (++n_processed > 50) { /* prevent history list from growing arbitrarily large. */
break;
}
}
window_list = gtk_application_get_windows (GTK_APPLICATION (self));
for (l = window_list; l != NULL; l = l->next) {
xmlNodePtr win_node, slot_node;
CajaWindow *window;
CajaWindowSlot *slot, *active_slot;
GList *slots, *m;
char *tmp;
window = l->data;
slots = caja_window_get_slots (window);
active_slot = caja_window_get_active_slot (window);
/* store one slot as window location. Otherwise
* older Caja versions will bail when reading the file. */
tmp = caja_window_slot_get_location_uri (active_slot);
if (eel_uri_is_desktop (tmp)) {
g_list_free (slots);
g_free (tmp);
continue;
}
win_node = xmlNewChild (root_node, NULL, "window", NULL);
xmlNewProp (win_node, "location", tmp);
g_free (tmp);
xmlNewProp (win_node, "type", CAJA_IS_NAVIGATION_WINDOW (window) ? "navigation" : "spatial");
if (CAJA_IS_NAVIGATION_WINDOW (window)) { /* spatial windows store their state as file metadata */
GdkWindow *gdk_window;
tmp = eel_gtk_window_get_geometry_string (GTK_WINDOW (window));
xmlNewProp (win_node, "geometry", tmp);
g_free (tmp);
gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
if (gdk_window &&
gdk_window_get_state (gdk_window) & GDK_WINDOW_STATE_MAXIMIZED) {
xmlNewProp (win_node, "maximized", "TRUE");
}
if (gdk_window &&
gdk_window_get_state (gdk_window) & GDK_WINDOW_STATE_STICKY) {
xmlNewProp (win_node, "sticky", "TRUE");
}
if (gdk_window &&
gdk_window_get_state (gdk_window) & GDK_WINDOW_STATE_ABOVE) {
xmlNewProp (win_node, "keep-above", "TRUE");
}
}
for (m = slots; m != NULL; m = m->next) {
slot = CAJA_WINDOW_SLOT (m->data);
slot_node = xmlNewChild (win_node, NULL, "slot", NULL);
tmp = caja_window_slot_get_location_uri (slot);
xmlNewProp (slot_node, "location", tmp);
g_free (tmp);
if (slot == active_slot) {
xmlNewProp (slot_node, "active", "TRUE");
}
}
g_list_free (slots);
}
buffer = xmlBufferCreate ();
xmlIndentTreeOutput = 1;
ctx = xmlSaveToBuffer (buffer, "UTF-8", XML_SAVE_FORMAT);
if (xmlSaveDoc (ctx, doc) < 0 ||
xmlSaveFlush (ctx) < 0) {
g_message ("failed to save session");
}
xmlSaveClose(ctx);
data = g_strndup (buffer->content, buffer->use);
xmlBufferFree (buffer);
xmlFreeDoc (doc);
return data;
}
static void
caja_application_load_session (CajaApplication *application)
{
xmlDocPtr doc;
gboolean bail;
xmlNodePtr root_node;
GKeyFile *state_file;
char *data;
caja_application_smclient_initialize (application);
if (!egg_sm_client_is_resumed (application->smclient))
{
return;
}
state_file = egg_sm_client_get_state_file (application->smclient);
if (!state_file)
{
return;
}
data = g_key_file_get_string (state_file,
"Caja",
"documents",
NULL);
if (data == NULL)
{
return;
}
bail = TRUE;
doc = xmlReadMemory (data, strlen (data), NULL, "UTF-8", 0);
if (doc != NULL && (root_node = xmlDocGetRootElement (doc)) != NULL)
{
xmlNodePtr node;
bail = FALSE;
for (node = root_node->children; node != NULL; node = node->next)
{
if (g_strcmp0 (node->name, "text") == 0)
{
continue;
}
else if (g_strcmp0 (node->name, "history") == 0)
{
xmlNodePtr bookmark_node;
gboolean emit_change;
emit_change = FALSE;
for (bookmark_node = node->children; bookmark_node != NULL; bookmark_node = bookmark_node->next)
{
if (g_strcmp0 (bookmark_node->name, "text") == 0)
{
continue;
}
else if (g_strcmp0 (bookmark_node->name, "bookmark") == 0)
{
xmlChar *name, *icon_str, *uri;
gboolean has_custom_name;
GIcon *icon;
GFile *location;
uri = xmlGetProp (bookmark_node, "uri");
name = xmlGetProp (bookmark_node, "name");
has_custom_name = xmlHasProp (bookmark_node, "has_custom_name") ? TRUE : FALSE;
icon_str = xmlGetProp (bookmark_node, "icon");
icon = NULL;
if (icon_str)
{
icon = g_icon_new_for_string (icon_str, NULL);
}
location = g_file_new_for_uri (uri);
emit_change |= caja_add_to_history_list_no_notify (location, name, has_custom_name, icon);
g_object_unref (location);
if (icon)
{
g_object_unref (icon);
}
xmlFree (name);
xmlFree (uri);
xmlFree (icon_str);
}
else
{
g_message ("unexpected bookmark node %s while parsing session data", bookmark_node->name);
bail = TRUE;
continue;
}
}
if (emit_change)
{
caja_send_history_list_changed ();
}
}
else if (g_strcmp0 (node->name, "window") == 0)
{
CajaWindow *window;
xmlChar *type, *location_uri, *slot_uri;
xmlNodePtr slot_node;
GFile *location;
int i;
type = xmlGetProp (node, "type");
if (type == NULL)
{
g_message ("empty type node while parsing session data");
bail = TRUE;
continue;
}
location_uri = xmlGetProp (node, "location");
if (location_uri == NULL)
{
g_message ("empty location node while parsing session data");
bail = TRUE;
xmlFree (type);
continue;
}
if (g_strcmp0 (type, "navigation") == 0)
{
xmlChar *geometry;
window = caja_application_create_navigation_window (application, gdk_screen_get_default ());
geometry = xmlGetProp (node, "geometry");
if (geometry != NULL)
{
eel_gtk_window_set_initial_geometry_from_string
(GTK_WINDOW (window),
geometry,
CAJA_NAVIGATION_WINDOW_MIN_WIDTH,
CAJA_NAVIGATION_WINDOW_MIN_HEIGHT,
FALSE);
}
xmlFree (geometry);
if (xmlHasProp (node, "maximized"))
{
gtk_window_maximize (GTK_WINDOW (window));
}
else
{
gtk_window_unmaximize (GTK_WINDOW (window));
}
if (xmlHasProp (node, "sticky"))
{
gtk_window_stick (GTK_WINDOW (window));
}
else
{
gtk_window_unstick (GTK_WINDOW (window));
}
if (xmlHasProp (node, "keep-above"))
{
gtk_window_set_keep_above (GTK_WINDOW (window), TRUE);
}
else
{
gtk_window_set_keep_above (GTK_WINDOW (window), FALSE);
}
for (i = 0, slot_node = node->children; slot_node != NULL; slot_node = slot_node->next)
{
if (g_strcmp0 (slot_node->name, "slot") == 0)
{
slot_uri = xmlGetProp (slot_node, "location");
if (slot_uri != NULL)
{
CajaWindowSlot *slot;
if (i == 0)
{
slot = window->details->active_pane->active_slot;
}
else
{
slot = caja_window_open_slot (window->details->active_pane, CAJA_WINDOW_OPEN_SLOT_APPEND);
}
location = g_file_new_for_uri (slot_uri);
caja_window_slot_open_location (slot, location, FALSE);
if (xmlHasProp (slot_node, "active"))
{
caja_window_set_active_slot (slot->pane->window, slot);
}
i++;
}
xmlFree (slot_uri);
}
}
if (i == 0)
{
/* This may be an old session file */
location = g_file_new_for_uri (location_uri);
caja_window_slot_open_location (window->details->active_pane->active_slot, location, FALSE);
g_object_unref (location);
}
}
else if (g_strcmp0 (type, "spatial") == 0)
{
location = g_file_new_for_uri (location_uri);
window = caja_application_get_spatial_window (application, NULL, NULL,
location, gdk_screen_get_default (),
NULL);
caja_window_go_to (window, location);
g_object_unref (location);
}
else
{
g_message ("unknown window type \"%s\" while parsing session data", type);
bail = TRUE;
}
xmlFree (type);
xmlFree (location_uri);
}
else
{
g_message ("unexpected node %s while parsing session data", node->name);
bail = TRUE;
continue;
}
}
}
if (doc != NULL)
{
xmlFreeDoc (doc);
}
g_free (data);
if (bail)
{
g_message ("failed to load session");
}
}
static gboolean
do_cmdline_sanity_checks (CajaApplication *self,
gboolean perform_self_check,
gboolean version,
gboolean kill_shell,
gchar **remaining)
{
gboolean retval = FALSE;
if (perform_self_check && (remaining != NULL || kill_shell)) {
g_printerr ("%s\n",
_("--check cannot be used with other options."));
goto out;
}
if (kill_shell && remaining != NULL) {
g_printerr ("%s\n",
_("--quit cannot be used with URIs."));
goto out;
}
if (self->priv->geometry != NULL &&
remaining != NULL && remaining[0] != NULL && remaining[1] != NULL) {
g_printerr ("%s\n",
_("--geometry cannot be used with more than one URI."));
goto out;
}
retval = TRUE;
out:
return retval;
}
static void
do_perform_self_checks (gint *exit_status)
{
#ifndef CAJA_OMIT_SELF_CHECK
/* Run the checks (each twice) for caja and libcaja-private. */
caja_run_self_checks ();
caja_run_lib_self_checks ();
eel_exit_if_self_checks_failed ();
caja_run_self_checks ();
caja_run_lib_self_checks ();
eel_exit_if_self_checks_failed ();
#endif
*exit_status = EXIT_SUCCESS;
}
static gboolean
running_in_mate (void)
{
return (g_strcmp0 (g_getenv ("XDG_CURRENT_DESKTOP"), "MATE") == 0)
|| (g_strcmp0 (g_getenv ("XDG_SESSION_DESKTOP"), "MATE") == 0)
|| (g_strcmp0 (g_getenv ("DESKTOP_SESSION"), "MATE") == 0);
}
static gboolean
running_as_root (void)
{
return geteuid () == 0;
}
static gboolean
caja_application_local_command_line (GApplication *application,
gchar ***arguments,
gint *exit_status)
{
gboolean perform_self_check = FALSE;
gboolean version = FALSE;
gboolean browser_window = FALSE;
gboolean kill_shell = FALSE;
const gchar *autostart_id;
gboolean no_default_window = FALSE;
gchar **remaining = NULL;
CajaApplication *self = CAJA_APPLICATION (application);
/*First set these FALSE */
self->priv->force_desktop = FALSE;
self->priv->no_desktop = FALSE;
const GOptionEntry options[] = {
#ifndef CAJA_OMIT_SELF_CHECK
{ "check", 'c', 0, G_OPTION_ARG_NONE, &perform_self_check,
N_("Perform a quick set of self-check tests."), NULL },
#endif
{ "version", '\0', 0, G_OPTION_ARG_NONE, &version,
N_("Show the version of the program."), NULL },
{ "geometry", 'g', 0, G_OPTION_ARG_STRING, &self->priv->geometry,
N_("Create the initial window with the given geometry."), N_("GEOMETRY") },
{ "no-default-window", 'n', 0, G_OPTION_ARG_NONE, &no_default_window,
N_("Only create windows for explicitly specified URIs."), NULL },
{ "no-desktop", '\0', 0, G_OPTION_ARG_NONE, &self->priv->no_desktop,
N_("Do not manage the desktop (ignore the preference set in the preferences dialog)."), NULL },
{ "force-desktop", '\0', 0, G_OPTION_ARG_NONE, &self->priv->force_desktop,
N_("Manage the desktop regardless of set preferences or environment (on new startup only)"), NULL },
{ "browser", '\0', 0, G_OPTION_ARG_NONE, &browser_window,
N_("Open a browser window."), NULL },
{ "quit", 'q', 0, G_OPTION_ARG_NONE, &kill_shell,
N_("Quit Caja."), NULL },
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining, NULL, N_("[URI...]") },
{ NULL }
};
GOptionContext *context;
GError *error = NULL;
gint argc = 0;
gchar **argv = NULL;
*exit_status = EXIT_SUCCESS;
context = g_option_context_new (_("\n\nBrowse the file system with the file manager"));
g_option_context_add_main_entries (context, options, NULL);
g_option_context_add_group (context, gtk_get_option_group (TRUE));
g_option_context_add_group (context, egg_sm_client_get_option_group ());
/* we need to do this here, as parsing the EggSMClient option context,
* unsets this variable.
*/
autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
if (autostart_id != NULL && *autostart_id != '\0') {
no_default_window = TRUE;
self->priv->autostart = TRUE;
}
argv = *arguments;
argc = g_strv_length (argv);
if (!g_option_context_parse (context, &argc, &argv, &error)) {
g_printerr ("Could not parse arguments: %s\n", error->message);
g_error_free (error);
*exit_status = EXIT_FAILURE;
goto out;
}
if (version) {
g_print ("MATE caja " PACKAGE_VERSION "\n");
goto out;
}
if (!do_cmdline_sanity_checks (self, perform_self_check,
version, kill_shell, remaining)) {
*exit_status = EXIT_FAILURE;
goto out;
}
if (perform_self_check) {
do_perform_self_checks (exit_status);
goto out;
}
g_debug ("Parsing local command line, no_default_window %d, quit %d, "
"self checks %d, no_desktop %d",
no_default_window, kill_shell, perform_self_check, self->priv->no_desktop);
g_application_register (application, NULL, &error);
if (error != NULL) {
g_printerr ("Could not register the application: %s\n", error->message);
g_error_free (error);
*exit_status = EXIT_FAILURE;
goto out;
}
if (kill_shell) {
g_debug ("Killing application, as requested");
g_action_group_activate_action (G_ACTION_GROUP (application),
"quit", NULL);
goto out;
}
/* Initialize and load session info if available */
/* Load session if and only if autostarted */
/* This avoids errors on command line invocation */
if (autostart_id != NULL ) {
caja_application_load_session (self);
}
GFile **files;
gint idx, len;
len = 0;
files = NULL;
/* Convert args to GFiles */
if (remaining != NULL) {
GFile *file;
GPtrArray *file_array;
file_array = g_ptr_array_new ();
for (idx = 0; remaining[idx] != NULL; idx++) {
file = g_file_new_for_commandline_arg (remaining[idx]);
if (file != NULL) {
g_ptr_array_add (file_array, file);
}
}
len = file_array->len;
files = (GFile **) g_ptr_array_free (file_array, FALSE);
g_strfreev (remaining);
}
if (files == NULL && !no_default_window) {
files = g_malloc0 (2 * sizeof (GFile *));
len = 1;
files[0] = g_file_new_for_path (g_get_home_dir ());
files[1] = NULL;
}
/*Set up geometry and --browser options */
/*Invoke "Open" to create new windows */
if (browser_window == TRUE && self->priv->geometry == NULL){
if (len > 0) {
g_application_open (application, files, len, "browser");
}
}
else if (browser_window == FALSE && self->priv->geometry != NULL){
if (len > 0) {
g_application_open (application, files, len, self->priv->geometry);
}
}
else if (browser_window == TRUE && self->priv->geometry != NULL){
if (len > 0) {
g_application_open (application, files, len, (g_strconcat("browser","=",
self->priv->geometry, NULL)));
}
}
else {
if (len > 0) {
g_application_open (application, files, len, "");
}
}
for (idx = 0; idx < len; idx++) {
g_object_unref (files[idx]);
}
g_free (files);
out:
g_option_context_free (context);
return TRUE;
}
static void
init_icons_and_styles (void)
{
GtkCssProvider *provider;
GError *error = NULL;
/* add our custom CSS provider */
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_path (provider,
CAJA_DATADIR G_DIR_SEPARATOR_S "caja.css", &error);
if (error != NULL) {
g_warning ("Can't parse Caja' CSS custom description: %s\n", error->message);
g_error_free (error);
} else {
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_THEME);
}
/* add our desktop CSS provider, ensures the desktop background does not get covered */
provider = gtk_css_provider_new ();
#if GTK_CHECK_VERSION (3, 16, 0)
gtk_css_provider_load_from_path (provider, CAJA_DATADIR G_DIR_SEPARATOR_S "caja-desktop.css", &error);
#else
gtk_css_provider_load_from_path (provider, CAJA_DATADIR G_DIR_SEPARATOR_S "caja-desktop-3.14.css", &error);
#endif
if (error != NULL) {
g_warning ("Can't parse Caja' CSS custom description: %s\n", error->message);
g_error_free (error);
} else {
gtk_style_context_add_provider_for_screen (gdk_screen_get_default (),
GTK_STYLE_PROVIDER (provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
g_object_unref (provider);
/* initialize search path for custom icons */
gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
CAJA_DATADIR G_DIR_SEPARATOR_S "icons");
}
static void
init_desktop (CajaApplication *self)
{
/* Initialize the desktop link monitor singleton */
caja_desktop_link_monitor_get ();
if (!self->priv->no_desktop &&
!g_settings_get_boolean (mate_background_preferences,
MATE_BG_KEY_SHOW_DESKTOP)) {
self->priv->no_desktop = TRUE;
}
if (running_as_root () || !running_in_mate ())
{
/* do not manage desktop when running as root or on other desktops unless forced */
self->priv->no_desktop = TRUE;
}
if (!self->priv->no_desktop || self->priv->force_desktop) {
caja_application_open_desktop (self);
}
/* Monitor the preference to show or hide the desktop */
g_signal_connect_swapped (mate_background_preferences, "changed::" MATE_BG_KEY_SHOW_DESKTOP,
G_CALLBACK (desktop_changed_callback),
self);
}
static gboolean
caja_application_save_accel_map (gpointer data)
{
if (save_of_accel_map_requested) {
char *accel_map_filename;
accel_map_filename = caja_get_accel_map_file ();
if (accel_map_filename) {
gtk_accel_map_save (accel_map_filename);
g_free (accel_map_filename);
}
save_of_accel_map_requested = FALSE;
}
return FALSE;
}
static void
queue_accel_map_save_callback (GtkAccelMap *object, gchar *accel_path,
guint accel_key, GdkModifierType accel_mods,
gpointer user_data)
{
if (!save_of_accel_map_requested) {
save_of_accel_map_requested = TRUE;
g_timeout_add_seconds (CAJA_ACCEL_MAP_SAVE_DELAY,
caja_application_save_accel_map, NULL);
}
}
static void
init_gtk_accels (void)
{
char *accel_map_filename;
/* load accelerator map, and register save callback */
accel_map_filename = caja_get_accel_map_file ();
if (accel_map_filename) {
gtk_accel_map_load (accel_map_filename);
g_free (accel_map_filename);
}
g_signal_connect (gtk_accel_map_get (), "changed",
G_CALLBACK (queue_accel_map_save_callback), NULL);
}
static void
caja_application_startup (GApplication *app)
{
GList *drives;
CajaApplication *application;
CajaApplication *self = CAJA_APPLICATION (app);
GApplication *instance;
gboolean exit_with_last_window;
exit_with_last_window = TRUE;
/* chain up to the GTK+ implementation early, so gtk_init()
* is called for us.
*/
G_APPLICATION_CLASS (caja_application_parent_class)->startup (app);
/* Start the File Manager DBus Interface */
fdb_manager = caja_freedesktop_dbus_new (self);
/* Initialize preferences. This is needed so that proper
* defaults are available before any preference peeking
* happens.
*/
caja_global_preferences_init ();
/* initialize the session manager client */
caja_application_smclient_startup (self);
/* register views */
fm_icon_view_register ();
fm_desktop_icon_view_register ();
fm_list_view_register ();
fm_compact_view_register ();
#if ENABLE_EMPTY_VIEW
fm_empty_view_register ();
#endif /* ENABLE_EMPTY_VIEW */
/* register sidebars */
caja_places_sidebar_register ();
caja_information_panel_register ();
fm_tree_view_register ();
caja_history_sidebar_register ();
caja_notes_viewer_register (); /* also property page */
caja_emblem_sidebar_register ();
/* register property pages */
caja_image_properties_page_register ();
/* initialize theming */
init_icons_and_styles ();
init_gtk_accels ();
/* initialize caja modules */
caja_module_setup ();
/* attach menu-provider module callback */
menu_provider_init_callback ();
/* Initialize notifications for eject operations */
notify_init (GETTEXT_PACKAGE);
/* Watch for unmounts so we can close open windows */
/* TODO-gio: This should be using the UNMOUNTED feature of GFileMonitor instead */
self->priv->volume_monitor = g_volume_monitor_get ();
g_signal_connect_object ( self->priv->volume_monitor, "mount_removed",
G_CALLBACK (mount_removed_callback), self, 0);
g_signal_connect_object ( self->priv->volume_monitor, "mount_pre_unmount",
G_CALLBACK (mount_removed_callback), self, 0);
g_signal_connect_object ( self->priv->volume_monitor, "mount_added",
G_CALLBACK (mount_added_callback), self, 0);
g_signal_connect_object ( self->priv->volume_monitor, "volume_added",
G_CALLBACK (volume_added_callback), self, 0);
g_signal_connect_object ( self->priv->volume_monitor, "volume_removed",
G_CALLBACK (volume_removed_callback), self, 0);
g_signal_connect_object ( self->priv->volume_monitor, "drive_connected",
G_CALLBACK (drive_connected_callback), self, 0);
/* listen for eject button presses */
drives = g_volume_monitor_get_connected_drives ( self->priv->volume_monitor);
self->automount_idle_id =
g_idle_add_full (G_PRIORITY_LOW,
automount_all_volumes_idle_cb,
self, NULL);
/* Check the user's ~/.caja directories and post warnings
* if there are problems.
*/
check_required_directories (self);
init_desktop (self);
/* exit_with_last_window is already set to TRUE, and we need to keep that value
* on other desktops, running from the command line, or when running caja as root.
* Otherwise, we read the value from the configuration.
*/
if (running_in_mate () && !running_as_root())
{
exit_with_last_window = g_settings_get_boolean (caja_preferences,
CAJA_PREFERENCES_EXIT_WITH_LAST_WINDOW);
/*Keep this inside the running as mate/not as root block */
/*So other desktop don't get unkillable caja instances holding open */
instance = g_application_get_default ();
if (exit_with_last_window == FALSE){
g_application_hold (G_APPLICATION (instance));
}
}
}
static void
caja_application_quit_mainloop (GApplication *app)
{
caja_icon_info_clear_caches ();
caja_application_save_accel_map (NULL);
G_APPLICATION_CLASS (caja_application_parent_class)->quit_mainloop (app);
}
static void
caja_application_class_init (CajaApplicationClass *class)
{
GObjectClass *object_class;
GApplicationClass *application_class;
object_class = G_OBJECT_CLASS (class);
object_class->finalize = caja_application_finalize;
application_class = G_APPLICATION_CLASS (class);
application_class->startup = caja_application_startup;
application_class->quit_mainloop = caja_application_quit_mainloop;
application_class->open = caja_application_open;
application_class->local_command_line = caja_application_local_command_line;
g_type_class_add_private (class, sizeof (CajaApplicationPriv));
}
CajaApplication *
caja_application_new (void)
{
return g_object_new (CAJA_TYPE_APPLICATION,
"application-id", "org.mate.Caja",
"register-session", TRUE,
"flags", G_APPLICATION_HANDLES_OPEN,
NULL);
}