diff options
author | Perberos <[email protected]> | 2011-12-01 22:24:23 -0300 |
---|---|---|
committer | Perberos <[email protected]> | 2011-12-01 22:24:23 -0300 |
commit | 0e004c696b0e68b2cff37a4c3315b022a35eaf43 (patch) | |
tree | 43261e815529cb9518ed7be37af13b846af8b26b /src/caja-window.c | |
download | caja-0e004c696b0e68b2cff37a4c3315b022a35eaf43.tar.bz2 caja-0e004c696b0e68b2cff37a4c3315b022a35eaf43.tar.xz |
moving from https://github.com/perberos/mate-desktop-environment
Diffstat (limited to 'src/caja-window.c')
-rw-r--r-- | src/caja-window.c | 2164 |
1 files changed, 2164 insertions, 0 deletions
diff --git a/src/caja-window.c b/src/caja-window.c new file mode 100644 index 00000000..72575f63 --- /dev/null +++ b/src/caja-window.c @@ -0,0 +1,2164 @@ +/* + * Caja + * + * Copyright (C) 1999, 2000, 2004 Red Hat, Inc. + * Copyright (C) 1999, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Authors: Elliot Lee <[email protected]> + * John Sullivan <[email protected]> + * Alexander Larsson <[email protected]> + */ + +/* caja-window.c: Implementation of the main window object */ + +#include <config.h> +#include "caja-window-private.h" + +#include "caja-actions.h" +#include "caja-application.h" +#include "caja-bookmarks-window.h" +#include "caja-information-panel.h" +#include "caja-main.h" +#include "caja-window-manage-views.h" +#include "caja-window-bookmarks.h" +#include "caja-window-slot.h" +#include "caja-navigation-window-slot.h" +#include "caja-zoom-control.h" +#include "caja-search-bar.h" +#include "caja-navigation-window-pane.h" +#include <eel/eel-debug.h> +#include <eel/eel-marshal.h> +#include <eel/eel-gtk-macros.h> +#include <eel/eel-string.h> +#include <gdk-pixbuf/gdk-pixbuf.h> +#include <gdk/gdkx.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#ifdef HAVE_X11_XF86KEYSYM_H +#include <X11/XF86keysym.h> +#endif +#include <libcaja-private/caja-file-utilities.h> +#include <libcaja-private/caja-file-attributes.h> +#include <libcaja-private/caja-global-preferences.h> +#include <libcaja-private/caja-horizontal-splitter.h> +#include <libcaja-private/caja-metadata.h> +#include <libcaja-private/caja-marshal.h> +#include <libcaja-private/caja-mime-actions.h> +#include <libcaja-private/caja-program-choosing.h> +#include <libcaja-private/caja-view-factory.h> +#include <libcaja-private/caja-clipboard.h> +#include <libcaja-private/caja-undo.h> +#include <libcaja-private/caja-search-directory.h> +#include <libcaja-private/caja-signaller.h> +#include <math.h> +#include <sys/time.h> + +#define MAX_HISTORY_ITEMS 50 + +#define EXTRA_VIEW_WIDGETS_BACKGROUND "#a7c6e1" + +#define SIDE_PANE_MINIMUM_WIDTH 1 +#define SIDE_PANE_MINIMUM_HEIGHT 400 + +/* dock items */ + +#define CAJA_MENU_PATH_EXTRA_VIEWER_PLACEHOLDER "/MenuBar/View/View Choices/Extra Viewer" +#define CAJA_MENU_PATH_SHORT_LIST_PLACEHOLDER "/MenuBar/View/View Choices/Short List" +#define CAJA_MENU_PATH_AFTER_SHORT_LIST_SEPARATOR "/MenuBar/View/View Choices/After Short List" + +enum { + ARG_0, + ARG_APP +}; + +enum { + GO_UP, + RELOAD, + PROMPT_FOR_LOCATION, + ZOOM_CHANGED, + VIEW_AS_CHANGED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct +{ + CajaWindow *window; + char *id; +} ActivateViewData; + +static void cancel_view_as_callback (CajaWindowSlot *slot); +static void caja_window_info_iface_init (CajaWindowInfoIface *iface); +static void action_view_as_callback (GtkAction *action, + ActivateViewData *data); + +static GList *history_list; + +G_DEFINE_TYPE_WITH_CODE (CajaWindow, caja_window, GTK_TYPE_WINDOW, + G_IMPLEMENT_INTERFACE (CAJA_TYPE_WINDOW_INFO, + caja_window_info_iface_init)); + +static const struct +{ + unsigned int keyval; + const char *action; +} extra_window_keybindings [] = +{ +#ifdef HAVE_X11_XF86KEYSYM_H + { XF86XK_AddFavorite, CAJA_ACTION_ADD_BOOKMARK }, + { XF86XK_Favorites, CAJA_ACTION_EDIT_BOOKMARKS }, + { XF86XK_Go, CAJA_ACTION_GO_TO_LOCATION }, + /* TODO?{ XF86XK_History, CAJA_ACTION_HISTORY }, */ + { XF86XK_HomePage, CAJA_ACTION_GO_HOME }, + { XF86XK_OpenURL, CAJA_ACTION_GO_TO_LOCATION }, + { XF86XK_Refresh, CAJA_ACTION_RELOAD }, + { XF86XK_Reload, CAJA_ACTION_RELOAD }, + { XF86XK_Search, CAJA_ACTION_SEARCH }, + { XF86XK_Start, CAJA_ACTION_GO_HOME }, + { XF86XK_Stop, CAJA_ACTION_STOP }, + { XF86XK_ZoomIn, CAJA_ACTION_ZOOM_IN }, + { XF86XK_ZoomOut, CAJA_ACTION_ZOOM_OUT } +#endif +}; + +static void +caja_window_init (CajaWindow *window) +{ + GtkWidget *table; + GtkWidget *menu; + GtkWidget *statusbar; + + window->details = G_TYPE_INSTANCE_GET_PRIVATE (window, CAJA_TYPE_WINDOW, CajaWindowDetails); + + window->details->panes = NULL; + window->details->active_pane = NULL; + + window->details->show_hidden_files_mode = CAJA_WINDOW_SHOW_HIDDEN_FILES_DEFAULT; + + /* Remove Top border on GtkStatusBar */ + gtk_rc_parse_string ( + "style \"statusbar-no-border\"\n" + "{\n" + " GtkStatusbar::shadow_type = GTK_SHADOW_NONE\n" + "}\n" + "widget \"*.statusbar-noborder\" style \"statusbar-no-border\""); + + /* Set initial window title */ + gtk_window_set_title (GTK_WINDOW (window), _("Caja")); + + table = gtk_table_new (1, 6, FALSE); + window->details->table = table; + gtk_widget_show (table); + gtk_container_add (GTK_CONTAINER (window), table); + + statusbar = gtk_statusbar_new (); + gtk_widget_set_name (statusbar, "statusbar-noborder"); + window->details->statusbar = statusbar; + window->details->help_message_cid = gtk_statusbar_get_context_id + (GTK_STATUSBAR (statusbar), "help_message"); + /* Statusbar is packed in the subclasses */ + + caja_window_initialize_menus (window); + + menu = gtk_ui_manager_get_widget (window->details->ui_manager, "/MenuBar"); + window->details->menubar = menu; + gtk_widget_show (menu); + gtk_table_attach (GTK_TABLE (table), + menu, + /* X direction */ /* Y direction */ + 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, + 0, 0); + + /* Register to menu provider extension signal managing menu updates */ + g_signal_connect_object (caja_signaller_get_current (), "popup_menu_changed", + G_CALLBACK (caja_window_load_extension_menus), window, G_CONNECT_SWAPPED); + + gtk_quit_add_destroy (1, GTK_OBJECT (window)); + + /* Keep the main event loop alive as long as the window exists */ + caja_main_event_loop_register (GTK_OBJECT (window)); +} + +/* Unconditionally synchronize the GtkUIManager of WINDOW. */ +static void +caja_window_ui_update (CajaWindow *window) +{ + g_assert (CAJA_IS_WINDOW (window)); + + gtk_ui_manager_ensure_update (window->details->ui_manager); +} + +static void +caja_window_push_status (CajaWindow *window, + const char *text) +{ + g_return_if_fail (CAJA_IS_WINDOW (window)); + + /* clear any previous message, underflow is allowed */ + gtk_statusbar_pop (GTK_STATUSBAR (window->details->statusbar), 0); + + if (text != NULL && text[0] != '\0') + { + gtk_statusbar_push (GTK_STATUSBAR (window->details->statusbar), 0, text); + } +} + +void +caja_window_sync_status (CajaWindow *window) +{ + CajaWindowSlot *slot; + + slot = window->details->active_pane->active_slot; + caja_window_push_status (window, slot->status_text); +} + +void +caja_window_go_to (CajaWindow *window, GFile *location) +{ + g_return_if_fail (CAJA_IS_WINDOW (window)); + + caja_window_slot_go_to (window->details->active_pane->active_slot, location, FALSE); +} + +void +caja_window_go_to_with_selection (CajaWindow *window, GFile *location, GList *new_selection) +{ + g_return_if_fail (CAJA_IS_WINDOW (window)); + + caja_window_slot_go_to_with_selection (window->details->active_pane->active_slot, location, new_selection); +} + +static gboolean +caja_window_go_up_signal (CajaWindow *window, gboolean close_behind) +{ + caja_window_go_up (window, close_behind, FALSE); + return TRUE; +} + +void +caja_window_go_up (CajaWindow *window, gboolean close_behind, gboolean new_tab) +{ + CajaWindowSlot *slot; + GFile *parent; + GList *selection; + CajaWindowOpenFlags flags; + + g_assert (CAJA_IS_WINDOW (window)); + + slot = window->details->active_pane->active_slot; + + if (slot->location == NULL) + { + return; + } + + parent = g_file_get_parent (slot->location); + + if (parent == NULL) + { + return; + } + + selection = g_list_prepend (NULL, g_object_ref (slot->location)); + + flags = 0; + if (close_behind) + { + flags |= CAJA_WINDOW_OPEN_FLAG_CLOSE_BEHIND; + } + if (new_tab) + { + flags |= CAJA_WINDOW_OPEN_FLAG_NEW_TAB; + } + + caja_window_slot_open_location_full (slot, parent, + CAJA_WINDOW_OPEN_ACCORDING_TO_MODE, + flags, + selection); + + g_object_unref (parent); + + eel_g_object_list_free (selection); +} + +static void +real_set_allow_up (CajaWindow *window, + gboolean allow) +{ + GtkAction *action; + + g_assert (CAJA_IS_WINDOW (window)); + + action = gtk_action_group_get_action (window->details->main_action_group, + CAJA_ACTION_UP); + gtk_action_set_sensitive (action, allow); + action = gtk_action_group_get_action (window->details->main_action_group, + CAJA_ACTION_UP_ACCEL); + gtk_action_set_sensitive (action, allow); +} + +void +caja_window_allow_up (CajaWindow *window, gboolean allow) +{ + g_return_if_fail (CAJA_IS_WINDOW (window)); + + EEL_CALL_METHOD (CAJA_WINDOW_CLASS, window, + set_allow_up, (window, allow)); +} + +static void +update_cursor (CajaWindow *window) +{ + CajaWindowSlot *slot; + GdkCursor *cursor; + + slot = window->details->active_pane->active_slot; + + if (slot->allow_stop) + { + cursor = gdk_cursor_new (GDK_WATCH); + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), cursor); + gdk_cursor_unref (cursor); + } + else + { + gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (window)), NULL); + } +} + +void +caja_window_sync_allow_stop (CajaWindow *window, + CajaWindowSlot *slot) +{ + GtkAction *action; + gboolean allow_stop; + + g_assert (CAJA_IS_WINDOW (window)); + + action = gtk_action_group_get_action (window->details->main_action_group, + CAJA_ACTION_STOP); + allow_stop = gtk_action_get_sensitive (action); + + if (slot != window->details->active_pane->active_slot || + allow_stop != slot->allow_stop) + { + if (slot == window->details->active_pane->active_slot) + { + gtk_action_set_sensitive (action, slot->allow_stop); + } + + if (gtk_widget_get_realized (GTK_WIDGET (window))) + { + update_cursor (window); + } + + EEL_CALL_METHOD (CAJA_WINDOW_CLASS, window, + sync_allow_stop, (window, slot)); + } +} + +void +caja_window_allow_reload (CajaWindow *window, gboolean allow) +{ + GtkAction *action; + + g_return_if_fail (CAJA_IS_WINDOW (window)); + + action = gtk_action_group_get_action (window->details->main_action_group, + CAJA_ACTION_RELOAD); + gtk_action_set_sensitive (action, allow); +} + +void +caja_window_go_home (CajaWindow *window) +{ + g_return_if_fail (CAJA_IS_WINDOW (window)); + + caja_window_slot_go_home (window->details->active_pane->active_slot, FALSE); +} + +void +caja_window_prompt_for_location (CajaWindow *window, + const char *initial) +{ + g_return_if_fail (CAJA_IS_WINDOW (window)); + + EEL_CALL_METHOD (CAJA_WINDOW_CLASS, window, + prompt_for_location, (window, initial)); +} + +static char * +caja_window_get_location_uri (CajaWindow *window) +{ + CajaWindowSlot *slot; + + g_assert (CAJA_IS_WINDOW (window)); + + slot = window->details->active_pane->active_slot; + + if (slot->location) + { + return g_file_get_uri (slot->location); + } + return NULL; +} + +void +caja_window_zoom_in (CajaWindow *window) +{ + g_assert (window != NULL); + + caja_window_pane_zoom_in (window->details->active_pane); +} + +void +caja_window_zoom_to_level (CajaWindow *window, + CajaZoomLevel level) +{ + g_assert (window != NULL); + + caja_window_pane_zoom_to_level (window->details->active_pane, level); +} + +void +caja_window_zoom_out (CajaWindow *window) +{ + g_assert (window != NULL); + + caja_window_pane_zoom_out (window->details->active_pane); +} + +void +caja_window_zoom_to_default (CajaWindow *window) +{ + g_assert (window != NULL); + + caja_window_pane_zoom_to_default (window->details->active_pane); +} + +/* Code should never force the window taller than this size. + * (The user can still stretch the window taller if desired). + */ +static guint +get_max_forced_height (GdkScreen *screen) +{ + return (gdk_screen_get_height (screen) * 90) / 100; +} + +/* Code should never force the window wider than this size. + * (The user can still stretch the window wider if desired). + */ +static guint +get_max_forced_width (GdkScreen *screen) +{ + return (gdk_screen_get_width (screen) * 90) / 100; +} + +/* This must be called when construction of CajaWindow is finished, + * since it depends on the type of the argument, which isn't decided at + * construction time. + */ +static void +caja_window_set_initial_window_geometry (CajaWindow *window) +{ + GdkScreen *screen; + guint max_width_for_screen, max_height_for_screen, min_width, min_height; + guint default_width, default_height; + + screen = gtk_window_get_screen (GTK_WINDOW (window)); + + /* Don't let GTK determine the minimum size + * automatically. It will insist that the window be + * really wide based on some misguided notion about + * the content view area. Also, it might start the + * window wider (or taller) than the screen, which + * is evil. So we choose semi-arbitrary initial and + * minimum widths instead of letting GTK decide. + */ + /* FIXME - the above comment suggests that the size request + * of the content view area is wrong, probably because of + * another stupid set_usize someplace. If someone gets the + * content view area's size request right then we can + * probably remove this broken set_size_request() here. + * - [email protected] + */ + + max_width_for_screen = get_max_forced_width (screen); + max_height_for_screen = get_max_forced_height (screen); + + EEL_CALL_METHOD (CAJA_WINDOW_CLASS, window, + get_min_size, (window, &min_width, &min_height)); + + gtk_widget_set_size_request (GTK_WIDGET (window), + MIN (min_width, + max_width_for_screen), + MIN (min_height, + max_height_for_screen)); + + EEL_CALL_METHOD (CAJA_WINDOW_CLASS, window, + get_default_size, (window, &default_width, &default_height)); + + gtk_window_set_default_size (GTK_WINDOW (window), + MIN (default_width, + max_width_for_screen), + MIN (default_height, + max_height_for_screen)); +} + +static void +caja_window_constructed (GObject *self) +{ + CajaWindow *window; + + window = CAJA_WINDOW (self); + + caja_window_initialize_bookmarks_menu (window); + caja_window_set_initial_window_geometry (window); + caja_undo_manager_attach (window->application->undo_manager, G_OBJECT (window)); +} + +static void +caja_window_set_property (GObject *object, + guint arg_id, + const GValue *value, + GParamSpec *pspec) +{ + CajaWindow *window; + + window = CAJA_WINDOW (object); + + switch (arg_id) + { + case ARG_APP: + window->application = CAJA_APPLICATION (g_value_get_object (value)); + break; + } +} + +static void +caja_window_get_property (GObject *object, + guint arg_id, + GValue *value, + GParamSpec *pspec) +{ + switch (arg_id) + { + case ARG_APP: + g_value_set_object (value, CAJA_WINDOW (object)->application); + break; + } +} + +static void +free_stored_viewers (CajaWindow *window) +{ + eel_g_list_free_deep_custom (window->details->short_list_viewers, + (GFunc) g_free, + NULL); + window->details->short_list_viewers = NULL; + g_free (window->details->extra_viewer); + window->details->extra_viewer = NULL; +} + +static void +caja_window_destroy (GtkObject *object) +{ + CajaWindow *window; + GList *panes_copy; + + window = CAJA_WINDOW (object); + + /* close all panes safely */ + panes_copy = g_list_copy (window->details->panes); + g_list_foreach (panes_copy, (GFunc) caja_window_close_pane, NULL); + g_list_free (panes_copy); + + /* the panes list should now be empty */ + g_assert (window->details->panes == NULL); + g_assert (window->details->active_pane == NULL); + + GTK_OBJECT_CLASS (caja_window_parent_class)->destroy (object); +} + +static void +caja_window_finalize (GObject *object) +{ + CajaWindow *window; + + window = CAJA_WINDOW (object); + + caja_window_remove_trash_monitor_callback (window); + free_stored_viewers (window); + + if (window->details->bookmark_list != NULL) + { + g_object_unref (window->details->bookmark_list); + } + + /* caja_window_close() should have run */ + g_assert (window->details->panes == NULL); + + g_object_unref (window->details->ui_manager); + + G_OBJECT_CLASS (caja_window_parent_class)->finalize (object); +} + +static GObject * +caja_window_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_params) +{ + GObject *object; + CajaWindow *window; + CajaWindowSlot *slot; + + object = (* G_OBJECT_CLASS (caja_window_parent_class)->constructor) (type, + n_construct_properties, + construct_params); + + window = CAJA_WINDOW (object); + + slot = caja_window_open_slot (window->details->active_pane, 0); + caja_window_set_active_slot (window, slot); + + return object; +} + +void +caja_window_show_window (CajaWindow *window) +{ + CajaWindowSlot *slot; + CajaWindowPane *pane; + GList *l, *walk; + + for (walk = window->details->panes; walk; walk = walk->next) + { + pane = walk->data; + for (l = pane->slots; l != NULL; l = l->next) + { + slot = l->data; + + caja_window_slot_update_title (slot); + caja_window_slot_update_icon (slot); + } + } + + gtk_widget_show (GTK_WIDGET (window)); + + slot = window->details->active_pane->active_slot; + + if (slot->viewed_file) + { + if (CAJA_IS_SPATIAL_WINDOW (window)) + { + caja_file_set_has_open_window (slot->viewed_file, TRUE); + } + } +} + +static void +caja_window_view_visible (CajaWindow *window, + CajaView *view) +{ + CajaWindowSlot *slot; + CajaWindowPane *pane; + GList *l, *walk; + + g_return_if_fail (CAJA_IS_WINDOW (window)); + + slot = caja_window_get_slot_for_view (window, view); + + /* Ensure we got the right active state for newly added panes */ + caja_window_slot_is_in_active_pane (slot, slot->pane->is_active); + + if (slot->visible) + { + return; + } + + slot->visible = TRUE; + + pane = slot->pane; + + if (pane->visible) + { + return; + } + + /* Look for other non-visible slots */ + for (l = pane->slots; l != NULL; l = l->next) + { + slot = l->data; + + if (!slot->visible) + { + return; + } + } + + /* None, this pane is visible */ + caja_window_pane_show (pane); + + /* Look for other non-visible panes */ + for (walk = window->details->panes; walk; walk = walk->next) + { + pane = walk->data; + + if (!pane->visible) + { + return; + } + } + + caja_window_pane_grab_focus (window->details->active_pane); + + /* All slots and panes visible, show window */ + caja_window_show_window (window); +} + +void +caja_window_close (CajaWindow *window) +{ + g_return_if_fail (CAJA_IS_WINDOW (window)); + + EEL_CALL_METHOD (CAJA_WINDOW_CLASS, window, + close, (window)); + + gtk_widget_destroy (GTK_WIDGET (window)); +} + +CajaWindowSlot * +caja_window_open_slot (CajaWindowPane *pane, + CajaWindowOpenSlotFlags flags) +{ + CajaWindowSlot *slot; + + g_assert (CAJA_IS_WINDOW_PANE (pane)); + g_assert (CAJA_IS_WINDOW (pane->window)); + + slot = EEL_CALL_METHOD_WITH_RETURN_VALUE (CAJA_WINDOW_CLASS, pane->window, + open_slot, (pane, flags)); + + g_assert (CAJA_IS_WINDOW_SLOT (slot)); + g_assert (pane->window == slot->pane->window); + + pane->slots = g_list_append (pane->slots, slot); + + return slot; +} + +void +caja_window_close_pane (CajaWindowPane *pane) +{ + CajaWindow *window; + + g_assert (CAJA_IS_WINDOW_PANE (pane)); + g_assert (CAJA_IS_WINDOW (pane->window)); + g_assert (g_list_find (pane->window->details->panes, pane) != NULL); + + while (pane->slots != NULL) + { + CajaWindowSlot *slot = pane->slots->data; + + caja_window_close_slot (slot); + } + + window = pane->window; + + /* If the pane was active, set it to NULL. The caller is responsible + * for setting a new active pane with caja_window_pane_switch_to() + * if it wants to continue using the window. */ + if (window->details->active_pane == pane) + { + window->details->active_pane = NULL; + } + + window->details->panes = g_list_remove (window->details->panes, pane); + + g_object_unref (pane); +} + +static void +real_close_slot (CajaWindowPane *pane, + CajaWindowSlot *slot) +{ + caja_window_manage_views_close_slot (pane, slot); + cancel_view_as_callback (slot); +} + +void +caja_window_close_slot (CajaWindowSlot *slot) +{ + CajaWindowPane *pane; + + g_assert (CAJA_IS_WINDOW_SLOT (slot)); + g_assert (CAJA_IS_WINDOW_PANE(slot->pane)); + g_assert (g_list_find (slot->pane->slots, slot) != NULL); + + /* save pane because slot is not valid anymore after this call */ + pane = slot->pane; + + EEL_CALL_METHOD (CAJA_WINDOW_CLASS, slot->pane->window, + close_slot, (slot->pane, slot)); + + g_object_run_dispose (G_OBJECT (slot)); + slot->pane = NULL; + g_object_unref (slot); + pane->slots = g_list_remove (pane->slots, slot); + pane->active_slots = g_list_remove (pane->active_slots, slot); + +} + +CajaWindowPane* +caja_window_get_active_pane (CajaWindow *window) +{ + g_assert (CAJA_IS_WINDOW (window)); + return window->details->active_pane; +} + +static void +real_set_active_pane (CajaWindow *window, CajaWindowPane *new_pane) +{ + /* make old pane inactive, and new one active. + * Currently active pane may be NULL (after init). */ + if (window->details->active_pane && + window->details->active_pane != new_pane) + { + caja_window_pane_set_active (new_pane->window->details->active_pane, FALSE); + } + caja_window_pane_set_active (new_pane, TRUE); + + window->details->active_pane = new_pane; +} + +/* Make the given pane the active pane of its associated window. This + * always implies making the containing active slot the active slot of + * the window. */ +void +caja_window_set_active_pane (CajaWindow *window, + CajaWindowPane *new_pane) +{ + g_assert (CAJA_IS_WINDOW_PANE (new_pane)); + if (new_pane->active_slot) + { + caja_window_set_active_slot (window, new_pane->active_slot); + } + else if (new_pane != window->details->active_pane) + { + real_set_active_pane (window, new_pane); + } +} + +/* Make both, the given slot the active slot and its corresponding + * pane the active pane of the associated window. + * new_slot may be NULL. */ +void +caja_window_set_active_slot (CajaWindow *window, CajaWindowSlot *new_slot) +{ + CajaWindowSlot *old_slot; + + g_assert (CAJA_IS_WINDOW (window)); + + if (new_slot) + { + g_assert (CAJA_IS_WINDOW_SLOT (new_slot)); + g_assert (CAJA_IS_WINDOW_PANE (new_slot->pane)); + g_assert (window == new_slot->pane->window); + g_assert (g_list_find (new_slot->pane->slots, new_slot) != NULL); + } + + if (window->details->active_pane != NULL) + { + old_slot = window->details->active_pane->active_slot; + } + else + { + old_slot = NULL; + } + + if (old_slot == new_slot) + { + return; + } + + /* make old slot inactive if it exists (may be NULL after init, for example) */ + if (old_slot != NULL) + { + /* inform window */ + if (old_slot->content_view != NULL) + { + caja_window_slot_disconnect_content_view (old_slot, old_slot->content_view); + } + + /* inform slot & view */ + g_signal_emit_by_name (old_slot, "inactive"); + } + + /* deal with panes */ + if (new_slot && + new_slot->pane != window->details->active_pane) + { + real_set_active_pane (window, new_slot->pane); + } + + window->details->active_pane->active_slot = new_slot; + + /* make new slot active, if it exists */ + if (new_slot) + { + window->details->active_pane->active_slots = + g_list_remove (window->details->active_pane->active_slots, new_slot); + window->details->active_pane->active_slots = + g_list_prepend (window->details->active_pane->active_slots, new_slot); + + /* inform sidebar panels */ + caja_window_report_location_change (window); + /* TODO decide whether "selection-changed" should be emitted */ + + if (new_slot->content_view != NULL) + { + /* inform window */ + caja_window_slot_connect_content_view (new_slot, new_slot->content_view); + } + + /* inform slot & view */ + g_signal_emit_by_name (new_slot, "active"); + } +} + +void +caja_window_slot_close (CajaWindowSlot *slot) +{ + caja_window_pane_slot_close (slot->pane, slot); +} + +static void +caja_window_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GdkScreen *screen; + guint max_width; + guint max_height; + + g_assert (CAJA_IS_WINDOW (widget)); + g_assert (requisition != NULL); + + GTK_WIDGET_CLASS (caja_window_parent_class)->size_request (widget, requisition); + + screen = gtk_window_get_screen (GTK_WINDOW (widget)); + + /* Limit the requisition to be within 90% of the available screen + * real state. + * + * This way the user will have a fighting chance of getting + * control of their window back if for whatever reason one of the + * window's descendants decide they want to be 4000 pixels wide. + * + * Note that the user can still make the window really huge by hand. + * + * Bugs in components or other widgets that cause such huge geometries + * to be requested, should still be fixed. This code is here only to + * prevent the extremely frustrating consequence of such bugs. + */ + max_width = get_max_forced_width (screen); + max_height = get_max_forced_height (screen); + + if (requisition->width > (int) max_width) + { + requisition->width = max_width; + } + + if (requisition->height > (int) max_height) + { + requisition->height = max_height; + } +} + +static void +caja_window_realize (GtkWidget *widget) +{ + GTK_WIDGET_CLASS (caja_window_parent_class)->realize (widget); + update_cursor (CAJA_WINDOW (widget)); +} + +static gboolean +caja_window_key_press_event (GtkWidget *widget, + GdkEventKey *event) +{ + CajaWindow *window; + int i; + + window = CAJA_WINDOW (widget); + + for (i = 0; i < G_N_ELEMENTS (extra_window_keybindings); i++) + { + if (extra_window_keybindings[i].keyval == event->keyval) + { + const GList *action_groups; + GtkAction *action; + + action = NULL; + + action_groups = gtk_ui_manager_get_action_groups (window->details->ui_manager); + while (action_groups != NULL && action == NULL) + { + action = gtk_action_group_get_action (action_groups->data, extra_window_keybindings[i].action); + action_groups = action_groups->next; + } + + g_assert (action != NULL); + if (gtk_action_is_sensitive (action)) + { + gtk_action_activate (action); + return TRUE; + } + + break; + } + } + + return GTK_WIDGET_CLASS (caja_window_parent_class)->key_press_event (widget, event); +} + +/* + * Main API + */ + +static void +free_activate_view_data (gpointer data) +{ + ActivateViewData *activate_data; + + activate_data = data; + + g_free (activate_data->id); + + g_slice_free (ActivateViewData, activate_data); +} + +static void +action_view_as_callback (GtkAction *action, + ActivateViewData *data) +{ + CajaWindow *window; + CajaWindowSlot *slot; + + window = data->window; + + if (gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action))) + { + slot = window->details->active_pane->active_slot; + caja_window_slot_set_content_view (slot, + data->id); + } +} + +static GtkRadioAction * +add_view_as_menu_item (CajaWindow *window, + const char *placeholder_path, + const char *identifier, + int index, /* extra_viewer is always index 0 */ + guint merge_id) +{ + const CajaViewInfo *info; + GtkRadioAction *action; + char action_name[32]; + ActivateViewData *data; + + char accel[32]; + char accel_path[48]; + unsigned int accel_keyval; + + info = caja_view_factory_lookup (identifier); + + g_snprintf (action_name, sizeof (action_name), "view_as_%d", index); + action = gtk_radio_action_new (action_name, + _(info->view_menu_label_with_mnemonic), + _(info->display_location_label), + NULL, + 0); + + if (index >= 1 && index <= 9) + { + g_snprintf (accel, sizeof (accel), "%d", index); + g_snprintf (accel_path, sizeof (accel_path), "<Caja-Window>/%s", action_name); + + accel_keyval = gdk_keyval_from_name (accel); + g_assert (accel_keyval != GDK_VoidSymbol); + + gtk_accel_map_add_entry (accel_path, accel_keyval, GDK_CONTROL_MASK); + gtk_action_set_accel_path (GTK_ACTION (action), accel_path); + } + + if (window->details->view_as_radio_action != NULL) + { + gtk_radio_action_set_group (action, + gtk_radio_action_get_group (window->details->view_as_radio_action)); + } + else if (index != 0) + { + /* Index 0 is the extra view, and we don't want to use that here, + as it can get deleted/changed later */ + window->details->view_as_radio_action = action; + } + + data = g_slice_new (ActivateViewData); + data->window = window; + data->id = g_strdup (identifier); + g_signal_connect_data (action, "activate", + G_CALLBACK (action_view_as_callback), + data, (GClosureNotify) free_activate_view_data, 0); + + gtk_action_group_add_action (window->details->view_as_action_group, + GTK_ACTION (action)); + g_object_unref (action); + + gtk_ui_manager_add_ui (window->details->ui_manager, + merge_id, + placeholder_path, + action_name, + action_name, + GTK_UI_MANAGER_MENUITEM, + FALSE); + + return action; /* return value owned by group */ +} + +/* Make a special first item in the "View as" option menu that represents + * the current content view. This should only be called if the current + * content view isn't already in the "View as" option menu. + */ +static void +update_extra_viewer_in_view_as_menus (CajaWindow *window, + const char *id) +{ + gboolean had_extra_viewer; + + had_extra_viewer = window->details->extra_viewer != NULL; + + if (id == NULL) + { + if (!had_extra_viewer) + { + return; + } + } + else + { + if (had_extra_viewer + && strcmp (window->details->extra_viewer, id) == 0) + { + return; + } + } + g_free (window->details->extra_viewer); + window->details->extra_viewer = g_strdup (id); + + if (window->details->extra_viewer_merge_id != 0) + { + gtk_ui_manager_remove_ui (window->details->ui_manager, + window->details->extra_viewer_merge_id); + window->details->extra_viewer_merge_id = 0; + } + + if (window->details->extra_viewer_radio_action != NULL) + { + gtk_action_group_remove_action (window->details->view_as_action_group, + GTK_ACTION (window->details->extra_viewer_radio_action)); + window->details->extra_viewer_radio_action = NULL; + } + + if (id != NULL) + { + window->details->extra_viewer_merge_id = gtk_ui_manager_new_merge_id (window->details->ui_manager); + window->details->extra_viewer_radio_action = + add_view_as_menu_item (window, + CAJA_MENU_PATH_EXTRA_VIEWER_PLACEHOLDER, + window->details->extra_viewer, + 0, + window->details->extra_viewer_merge_id); + } +} + +static void +remove_extra_viewer_in_view_as_menus (CajaWindow *window) +{ + update_extra_viewer_in_view_as_menus (window, NULL); +} + +static void +replace_extra_viewer_in_view_as_menus (CajaWindow *window) +{ + CajaWindowSlot *slot; + const char *id; + + slot = window->details->active_pane->active_slot; + + id = caja_window_slot_get_content_view_id (slot); + update_extra_viewer_in_view_as_menus (window, id); +} + +/** + * caja_window_synch_view_as_menus: + * + * Set the visible item of the "View as" option menu and + * the marked "View as" item in the View menu to + * match the current content view. + * + * @window: The CajaWindow whose "View as" option menu should be synched. + */ +static void +caja_window_synch_view_as_menus (CajaWindow *window) +{ + CajaWindowSlot *slot; + int index; + char action_name[32]; + GList *node; + GtkAction *action; + + g_assert (CAJA_IS_WINDOW (window)); + + slot = window->details->active_pane->active_slot; + + if (slot->content_view == NULL) + { + return; + } + for (node = window->details->short_list_viewers, index = 1; + node != NULL; + node = node->next, ++index) + { + if (caja_window_slot_content_view_matches_iid (slot, (char *)node->data)) + { + break; + } + } + if (node == NULL) + { + replace_extra_viewer_in_view_as_menus (window); + index = 0; + } + else + { + remove_extra_viewer_in_view_as_menus (window); + } + + g_snprintf (action_name, sizeof (action_name), "view_as_%d", index); + action = gtk_action_group_get_action (window->details->view_as_action_group, + action_name); + + /* Don't trigger the action callback when we're synchronizing */ + g_signal_handlers_block_matched (action, + G_SIGNAL_MATCH_FUNC, + 0, 0, + NULL, + action_view_as_callback, + NULL); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), TRUE); + g_signal_handlers_unblock_matched (action, + G_SIGNAL_MATCH_FUNC, + 0, 0, + NULL, + action_view_as_callback, + NULL); +} + +static void +refresh_stored_viewers (CajaWindow *window) +{ + CajaWindowSlot *slot; + GList *viewers; + char *uri, *mimetype; + + slot = window->details->active_pane->active_slot; + + uri = caja_file_get_uri (slot->viewed_file); + mimetype = caja_file_get_mime_type (slot->viewed_file); + viewers = caja_view_factory_get_views_for_uri (uri, + caja_file_get_file_type (slot->viewed_file), + mimetype); + g_free (uri); + g_free (mimetype); + + free_stored_viewers (window); + window->details->short_list_viewers = viewers; +} + +static void +load_view_as_menu (CajaWindow *window) +{ + CajaWindowSlot *slot; + GList *node; + int index; + guint merge_id; + + slot = window->details->active_pane->active_slot; + + if (window->details->short_list_merge_id != 0) + { + gtk_ui_manager_remove_ui (window->details->ui_manager, + window->details->short_list_merge_id); + window->details->short_list_merge_id = 0; + } + if (window->details->extra_viewer_merge_id != 0) + { + gtk_ui_manager_remove_ui (window->details->ui_manager, + window->details->extra_viewer_merge_id); + window->details->extra_viewer_merge_id = 0; + window->details->extra_viewer_radio_action = NULL; + } + if (window->details->view_as_action_group != NULL) + { + gtk_ui_manager_remove_action_group (window->details->ui_manager, + window->details->view_as_action_group); + window->details->view_as_action_group = NULL; + } + + + refresh_stored_viewers (window); + + merge_id = gtk_ui_manager_new_merge_id (window->details->ui_manager); + window->details->short_list_merge_id = merge_id; + window->details->view_as_action_group = gtk_action_group_new ("ViewAsGroup"); + gtk_action_group_set_translation_domain (window->details->view_as_action_group, GETTEXT_PACKAGE); + window->details->view_as_radio_action = NULL; + + /* Add a menu item for each view in the preferred list for this location. */ + /* Start on 1, because extra_viewer gets index 0 */ + for (node = window->details->short_list_viewers, index = 1; + node != NULL; + node = node->next, ++index) + { + /* Menu item in View menu. */ + add_view_as_menu_item (window, + CAJA_MENU_PATH_SHORT_LIST_PLACEHOLDER, + node->data, + index, + merge_id); + } + gtk_ui_manager_insert_action_group (window->details->ui_manager, + window->details->view_as_action_group, + -1); + g_object_unref (window->details->view_as_action_group); /* owned by ui_manager */ + + caja_window_synch_view_as_menus (window); + + g_signal_emit (window, signals[VIEW_AS_CHANGED], 0); + +} + +static void +load_view_as_menus_callback (CajaFile *file, + gpointer callback_data) +{ + CajaWindow *window; + CajaWindowSlot *slot; + + slot = callback_data; + window = CAJA_WINDOW (slot->pane->window); + + if (slot == window->details->active_pane->active_slot) + { + load_view_as_menu (window); + } +} + +static void +cancel_view_as_callback (CajaWindowSlot *slot) +{ + caja_file_cancel_call_when_ready (slot->viewed_file, + load_view_as_menus_callback, + slot); +} + +void +caja_window_load_view_as_menus (CajaWindow *window) +{ + CajaWindowSlot *slot; + CajaFileAttributes attributes; + + g_return_if_fail (CAJA_IS_WINDOW (window)); + + attributes = caja_mime_actions_get_required_file_attributes (); + + slot = window->details->active_pane->active_slot; + + cancel_view_as_callback (slot); + caja_file_call_when_ready (slot->viewed_file, + attributes, + load_view_as_menus_callback, + slot); +} + +void +caja_window_display_error (CajaWindow *window, const char *error_msg) +{ + GtkWidget *dialog; + + g_return_if_fail (CAJA_IS_WINDOW (window)); + + dialog = gtk_message_dialog_new (GTK_WINDOW (window), 0, GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, error_msg, NULL); + gtk_widget_show (dialog); +} + +static char * +real_get_title (CajaWindow *window) +{ + g_assert (CAJA_IS_WINDOW (window)); + + return caja_window_slot_get_title (window->details->active_pane->active_slot); +} + +static void +real_sync_title (CajaWindow *window, + CajaWindowSlot *slot) +{ + char *copy; + + if (slot == window->details->active_pane->active_slot) + { + copy = g_strdup (slot->title); + g_signal_emit_by_name (window, "title_changed", + slot->title); + g_free (copy); + } +} + +void +caja_window_sync_title (CajaWindow *window, + CajaWindowSlot *slot) +{ + EEL_CALL_METHOD (CAJA_WINDOW_CLASS, window, + sync_title, (window, slot)); +} + +void +caja_window_sync_zoom_widgets (CajaWindow *window) +{ + CajaWindowSlot *slot; + CajaView *view; + GtkAction *action; + gboolean supports_zooming; + gboolean can_zoom, can_zoom_in, can_zoom_out; + CajaZoomLevel zoom_level; + + slot = window->details->active_pane->active_slot; + view = slot->content_view; + + if (view != NULL) + { + supports_zooming = caja_view_supports_zooming (view); + zoom_level = caja_view_get_zoom_level (view); + can_zoom = supports_zooming && + zoom_level >= CAJA_ZOOM_LEVEL_SMALLEST && + zoom_level <= CAJA_ZOOM_LEVEL_LARGEST; + can_zoom_in = can_zoom && caja_view_can_zoom_in (view); + can_zoom_out = can_zoom && caja_view_can_zoom_out (view); + } + else + { + zoom_level = CAJA_ZOOM_LEVEL_STANDARD; + supports_zooming = FALSE; + can_zoom = FALSE; + can_zoom_in = FALSE; + can_zoom_out = FALSE; + } + + action = gtk_action_group_get_action (window->details->main_action_group, + CAJA_ACTION_ZOOM_IN); + gtk_action_set_visible (action, supports_zooming); + gtk_action_set_sensitive (action, can_zoom_in); + + action = gtk_action_group_get_action (window->details->main_action_group, + CAJA_ACTION_ZOOM_OUT); + gtk_action_set_visible (action, supports_zooming); + gtk_action_set_sensitive (action, can_zoom_out); + + action = gtk_action_group_get_action (window->details->main_action_group, + CAJA_ACTION_ZOOM_NORMAL); + gtk_action_set_visible (action, supports_zooming); + gtk_action_set_sensitive (action, can_zoom); + + g_signal_emit (window, signals[ZOOM_CHANGED], 0, + zoom_level, supports_zooming, can_zoom, + can_zoom_in, can_zoom_out); +} + +static void +zoom_level_changed_callback (CajaView *view, + CajaWindow *window) +{ + g_assert (CAJA_IS_WINDOW (window)); + + /* This is called each time the component in + * the active slot successfully completed + * a zooming operation. + */ + caja_window_sync_zoom_widgets (window); +} + + +/* These are called + * A) when switching the view within the active slot + * B) when switching the active slot + * C) when closing the active slot (disconnect) +*/ +void +caja_window_connect_content_view (CajaWindow *window, + CajaView *view) +{ + CajaWindowSlot *slot; + + g_assert (CAJA_IS_WINDOW (window)); + g_assert (CAJA_IS_VIEW (view)); + + slot = caja_window_get_slot_for_view (window, view); + g_assert (slot == caja_window_get_active_slot (window)); + + g_signal_connect (view, "zoom-level-changed", + G_CALLBACK (zoom_level_changed_callback), + window); + + /* Update displayed view in menu. Only do this if we're not switching + * locations though, because if we are switching locations we'll + * install a whole new set of views in the menu later (the current + * views in the menu are for the old location). + */ + if (slot->pending_location == NULL) + { + caja_window_load_view_as_menus (window); + } + + caja_view_grab_focus (view); +} + +void +caja_window_disconnect_content_view (CajaWindow *window, + CajaView *view) +{ + CajaWindowSlot *slot; + + g_assert (CAJA_IS_WINDOW (window)); + g_assert (CAJA_IS_VIEW (view)); + + slot = caja_window_get_slot_for_view (window, view); + g_assert (slot == caja_window_get_active_slot (window)); + + g_signal_handlers_disconnect_by_func (view, G_CALLBACK (zoom_level_changed_callback), window); +} + +/** + * caja_window_show: + * @widget: GtkWidget + * + * Call parent and then show/hide window items + * base on user prefs. + */ +static void +caja_window_show (GtkWidget *widget) +{ + CajaWindow *window; + + window = CAJA_WINDOW (widget); + + GTK_WIDGET_CLASS (caja_window_parent_class)->show (widget); + + caja_window_ui_update (window); +} + +GtkUIManager * +caja_window_get_ui_manager (CajaWindow *window) +{ + g_return_val_if_fail (CAJA_IS_WINDOW (window), NULL); + + return window->details->ui_manager; +} + +CajaWindowPane * +caja_window_get_next_pane (CajaWindow *window) +{ + CajaWindowPane *next_pane; + GList *node; + + /* return NULL if there is only one pane */ + if (!window->details->panes || !window->details->panes->next) + { + return NULL; + } + + /* get next pane in the (wrapped around) list */ + node = g_list_find (window->details->panes, window->details->active_pane); + g_return_val_if_fail (node, NULL); + if (node->next) + { + next_pane = node->next->data; + } + else + { + next_pane = window->details->panes->data; + } + + return next_pane; +} + + +void +caja_window_slot_set_viewed_file (CajaWindowSlot *slot, + CajaFile *file) +{ + CajaWindow *window; + CajaFileAttributes attributes; + + if (slot->viewed_file == file) + { + return; + } + + caja_file_ref (file); + + cancel_view_as_callback (slot); + + if (slot->viewed_file != NULL) + { + window = slot->pane->window; + + if (CAJA_IS_SPATIAL_WINDOW (window)) + { + caja_file_set_has_open_window (slot->viewed_file, + FALSE); + } + caja_file_monitor_remove (slot->viewed_file, + slot); + } + + if (file != NULL) + { + attributes = + CAJA_FILE_ATTRIBUTE_INFO | + CAJA_FILE_ATTRIBUTE_LINK_INFO; + caja_file_monitor_add (file, slot, attributes); + } + + caja_file_unref (slot->viewed_file); + slot->viewed_file = file; +} + +void +caja_send_history_list_changed (void) +{ + g_signal_emit_by_name (caja_signaller_get_current (), + "history_list_changed"); +} + +static void +free_history_list (void) +{ + eel_g_object_list_free (history_list); + history_list = NULL; +} + +/* Remove the this URI from the history list. + * Do not sent out a change notice. + * We pass in a bookmark for convenience. + */ +static void +remove_from_history_list (CajaBookmark *bookmark) +{ + GList *node; + + /* Compare only the uris here. Comparing the names also is not + * necessary and can cause problems due to the asynchronous + * nature of when the title of the window is set. + */ + node = g_list_find_custom (history_list, + bookmark, + caja_bookmark_compare_uris); + + /* Remove any older entry for this same item. There can be at most 1. */ + if (node != NULL) + { + history_list = g_list_remove_link (history_list, node); + g_object_unref (node->data); + g_list_free_1 (node); + } +} + +gboolean +caja_add_bookmark_to_history_list (CajaBookmark *bookmark) +{ + /* Note that the history is shared amongst all windows so + * this is not a CajaNavigationWindow function. Perhaps it belongs + * in its own file. + */ + int i; + GList *l, *next; + static gboolean free_history_list_is_set_up; + + g_assert (CAJA_IS_BOOKMARK (bookmark)); + + if (!free_history_list_is_set_up) + { + eel_debug_call_at_shutdown (free_history_list); + free_history_list_is_set_up = TRUE; + } + + /* g_warning ("Add to history list '%s' '%s'", + caja_bookmark_get_name (bookmark), + caja_bookmark_get_uri (bookmark)); */ + + if (!history_list || + caja_bookmark_compare_uris (history_list->data, bookmark)) + { + g_object_ref (bookmark); + remove_from_history_list (bookmark); + history_list = g_list_prepend (history_list, bookmark); + + for (i = 0, l = history_list; l; l = next) + { + next = l->next; + + if (i++ >= MAX_HISTORY_ITEMS) + { + g_object_unref (l->data); + history_list = g_list_delete_link (history_list, l); + } + } + + return TRUE; + } + + return FALSE; +} + +void +caja_remove_from_history_list_no_notify (GFile *location) +{ + CajaBookmark *bookmark; + + bookmark = caja_bookmark_new (location, "", FALSE, NULL); + remove_from_history_list (bookmark); + g_object_unref (bookmark); +} + +gboolean +caja_add_to_history_list_no_notify (GFile *location, + const char *name, + gboolean has_custom_name, + GIcon *icon) +{ + CajaBookmark *bookmark; + gboolean ret; + + bookmark = caja_bookmark_new (location, name, has_custom_name, icon); + ret = caja_add_bookmark_to_history_list (bookmark); + g_object_unref (bookmark); + + return ret; +} + +CajaWindowSlot * +caja_window_get_slot_for_view (CajaWindow *window, + CajaView *view) +{ + CajaWindowSlot *slot; + GList *l, *walk; + + for (walk = window->details->panes; walk; walk = walk->next) + { + CajaWindowPane *pane = walk->data; + + for (l = pane->slots; l != NULL; l = l->next) + { + slot = l->data; + if (slot->content_view == view || + slot->new_content_view == view) + { + return slot; + } + } + } + + return NULL; +} + +void +caja_forget_history (void) +{ + CajaWindowSlot *slot; + CajaNavigationWindowSlot *navigation_slot; + GList *window_node, *l, *walk; + + /* Clear out each window's back & forward lists. Also, remove + * each window's current location bookmark from history list + * so it doesn't get clobbered. + */ + for (window_node = caja_application_get_window_list (); + window_node != NULL; + window_node = window_node->next) + { + + if (CAJA_IS_NAVIGATION_WINDOW (window_node->data)) + { + CajaNavigationWindow *window; + + window = CAJA_NAVIGATION_WINDOW (window_node->data); + + for (walk = CAJA_WINDOW (window_node->data)->details->panes; walk; walk = walk->next) + { + CajaWindowPane *pane = walk->data; + for (l = pane->slots; l != NULL; l = l->next) + { + navigation_slot = l->data; + + caja_navigation_window_slot_clear_back_list (navigation_slot); + caja_navigation_window_slot_clear_forward_list (navigation_slot); + } + } + + caja_navigation_window_allow_back (window, FALSE); + caja_navigation_window_allow_forward (window, FALSE); + } + + for (walk = CAJA_WINDOW (window_node->data)->details->panes; walk; walk = walk->next) + { + CajaWindowPane *pane = walk->data; + for (l = pane->slots; l != NULL; l = l->next) + { + slot = l->data; + history_list = g_list_remove (history_list, + slot->current_location_bookmark); + } + } + } + + /* Clobber history list. */ + free_history_list (); + + /* Re-add each window's current location to history list. */ + for (window_node = caja_application_get_window_list (); + window_node != NULL; + window_node = window_node->next) + { + CajaWindow *window; + CajaWindowSlot *slot; + GList *l; + + window = CAJA_WINDOW (window_node->data); + for (walk = window->details->panes; walk; walk = walk->next) + { + CajaWindowPane *pane = walk->data; + for (l = pane->slots; l != NULL; l = l->next) + { + slot = CAJA_WINDOW_SLOT (l->data); + caja_window_slot_add_current_location_to_history_list (slot); + } + } + } +} + +GList * +caja_get_history_list (void) +{ + return history_list; +} + +static GList * +caja_window_get_history (CajaWindow *window) +{ + return eel_g_object_list_copy (history_list); +} + + +static CajaWindowType +caja_window_get_window_type (CajaWindow *window) +{ + g_assert (CAJA_IS_WINDOW (window)); + + return CAJA_WINDOW_GET_CLASS (window)->window_type; +} + +static int +caja_window_get_selection_count (CajaWindow *window) +{ + CajaWindowSlot *slot; + + g_assert (CAJA_IS_WINDOW (window)); + + slot = window->details->active_pane->active_slot; + + if (slot->content_view != NULL) + { + return caja_view_get_selection_count (slot->content_view); + } + + return 0; +} + +static GList * +caja_window_get_selection (CajaWindow *window) +{ + CajaWindowSlot *slot; + + g_assert (CAJA_IS_WINDOW (window)); + + slot = window->details->active_pane->active_slot; + + if (slot->content_view != NULL) + { + return caja_view_get_selection (slot->content_view); + } + return NULL; +} + +static CajaWindowShowHiddenFilesMode +caja_window_get_hidden_files_mode (CajaWindowInfo *window) +{ + return window->details->show_hidden_files_mode; +} + +static void +caja_window_set_hidden_files_mode (CajaWindowInfo *window, + CajaWindowShowHiddenFilesMode mode) +{ + window->details->show_hidden_files_mode = mode; + + g_signal_emit_by_name (window, "hidden_files_mode_changed"); +} + +static gboolean +caja_window_get_initiated_unmount (CajaWindowInfo *window) +{ + return window->details->initiated_unmount; +} + +static void +caja_window_set_initiated_unmount (CajaWindowInfo *window, + gboolean initiated_unmount) +{ + window->details->initiated_unmount = initiated_unmount; +} + +static char * +caja_window_get_cached_title (CajaWindow *window) +{ + CajaWindowSlot *slot; + + g_assert (CAJA_IS_WINDOW (window)); + + slot = window->details->active_pane->active_slot; + + return g_strdup (slot->title); +} + +CajaWindowSlot * +caja_window_get_active_slot (CajaWindow *window) +{ + g_assert (CAJA_IS_WINDOW (window)); + + return window->details->active_pane->active_slot; +} + +CajaWindowSlot * +caja_window_get_extra_slot (CajaWindow *window) +{ + CajaWindowPane *extra_pane; + GList *node; + + g_assert (CAJA_IS_WINDOW (window)); + + + /* return NULL if there is only one pane */ + if (window->details->panes == NULL || + window->details->panes->next == NULL) + { + return NULL; + } + + /* get next pane in the (wrapped around) list */ + node = g_list_find (window->details->panes, + window->details->active_pane); + g_return_val_if_fail (node, FALSE); + + if (node->next) + { + extra_pane = node->next->data; + } + else + { + extra_pane = window->details->panes->data; + } + + return extra_pane->active_slot; +} + +GList * +caja_window_get_slots (CajaWindow *window) +{ + GList *walk,*list; + + g_assert (CAJA_IS_WINDOW (window)); + + list = NULL; + for (walk = window->details->panes; walk; walk = walk->next) + { + CajaWindowPane *pane = walk->data; + list = g_list_concat (list, g_list_copy(pane->slots)); + } + return list; +} + +static void +caja_window_info_iface_init (CajaWindowInfoIface *iface) +{ + iface->report_load_underway = caja_window_report_load_underway; + iface->report_load_complete = caja_window_report_load_complete; + iface->report_selection_changed = caja_window_report_selection_changed; + iface->report_view_failed = caja_window_report_view_failed; + iface->view_visible = caja_window_view_visible; + iface->close_window = caja_window_close; + iface->push_status = caja_window_push_status; + iface->get_window_type = caja_window_get_window_type; + iface->get_title = caja_window_get_cached_title; + iface->get_history = caja_window_get_history; + iface->get_current_location = caja_window_get_location_uri; + iface->get_ui_manager = caja_window_get_ui_manager; + iface->get_selection_count = caja_window_get_selection_count; + iface->get_selection = caja_window_get_selection; + iface->get_hidden_files_mode = caja_window_get_hidden_files_mode; + iface->set_hidden_files_mode = caja_window_set_hidden_files_mode; + iface->get_active_slot = caja_window_get_active_slot; + iface->get_extra_slot = caja_window_get_extra_slot; + iface->get_initiated_unmount = caja_window_get_initiated_unmount; + iface->set_initiated_unmount = caja_window_set_initiated_unmount; +} + +static void +caja_window_class_init (CajaWindowClass *class) +{ + GtkBindingSet *binding_set; + + G_OBJECT_CLASS (class)->finalize = caja_window_finalize; + G_OBJECT_CLASS (class)->constructor = caja_window_constructor; + G_OBJECT_CLASS (class)->constructed = caja_window_constructed; + G_OBJECT_CLASS (class)->get_property = caja_window_get_property; + G_OBJECT_CLASS (class)->set_property = caja_window_set_property; + GTK_OBJECT_CLASS (class)->destroy = caja_window_destroy; + GTK_WIDGET_CLASS (class)->show = caja_window_show; + GTK_WIDGET_CLASS (class)->size_request = caja_window_size_request; + GTK_WIDGET_CLASS (class)->realize = caja_window_realize; + GTK_WIDGET_CLASS (class)->key_press_event = caja_window_key_press_event; + class->get_title = real_get_title; + class->sync_title = real_sync_title; + class->set_allow_up = real_set_allow_up; + class->close_slot = real_close_slot; + + g_object_class_install_property (G_OBJECT_CLASS (class), + ARG_APP, + g_param_spec_object ("app", + "Application", + "The CajaApplication associated with this window.", + CAJA_TYPE_APPLICATION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); + + signals[GO_UP] = + g_signal_new ("go_up", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (CajaWindowClass, go_up), + g_signal_accumulator_true_handled, NULL, + eel_marshal_BOOLEAN__BOOLEAN, + G_TYPE_BOOLEAN, 1, G_TYPE_BOOLEAN); + signals[RELOAD] = + g_signal_new ("reload", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (CajaWindowClass, reload), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[PROMPT_FOR_LOCATION] = + g_signal_new ("prompt-for-location", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (CajaWindowClass, prompt_for_location), + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + signals[ZOOM_CHANGED] = + g_signal_new ("zoom-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + caja_marshal_VOID__INT_BOOLEAN_BOOLEAN_BOOLEAN_BOOLEAN, + G_TYPE_NONE, 5, + G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); + signals[VIEW_AS_CHANGED] = + g_signal_new ("view-as-changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + binding_set = gtk_binding_set_by_class (class); + gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0, + "go_up", 1, + G_TYPE_BOOLEAN, FALSE); + gtk_binding_entry_add_signal (binding_set, GDK_F5, 0, + "reload", 0); + gtk_binding_entry_add_signal (binding_set, GDK_slash, 0, + "prompt-for-location", 1, + G_TYPE_STRING, "/"); + + class->reload = caja_window_reload; + class->go_up = caja_window_go_up_signal; + + /* Allow to set the colors of the extra view widgets */ + gtk_rc_parse_string ("\n" + " style \"caja-extra-view-widgets-style-internal\"\n" + " {\n" + " bg[NORMAL] = \"" EXTRA_VIEW_WIDGETS_BACKGROUND "\"\n" + " }\n" + "\n" + " widget \"*.caja-extra-view-widget\" style:rc \"caja-extra-view-widgets-style-internal\" \n" + "\n"); + + g_type_class_add_private (G_OBJECT_CLASS (class), sizeof (CajaWindowDetails)); +} + +/** + * caja_window_has_menubar_and_statusbar: + * @window: A #CajaWindow + * + * Queries whether the window should have a menubar and statusbar, based on the + * window_type from its class structure. + * + * Return value: TRUE if the window should have a menubar and statusbar; FALSE + * otherwise. + **/ +gboolean +caja_window_has_menubar_and_statusbar (CajaWindow *window) +{ + return (caja_window_get_window_type (window) != CAJA_WINDOW_DESKTOP); +} |