diff options
author | Perberos <[email protected]> | 2011-11-09 22:53:33 -0300 |
---|---|---|
committer | Perberos <[email protected]> | 2011-11-09 22:53:33 -0300 |
commit | 70438138096a47b2505ac55634cd94947ce378b6 (patch) | |
tree | e45e49dda10a71616466500a4ab65d1c54b5f6c1 /src/fr-window.c | |
download | engrampa-70438138096a47b2505ac55634cd94947ce378b6.tar.bz2 engrampa-70438138096a47b2505ac55634cd94947ce378b6.tar.xz |
initial
Diffstat (limited to 'src/fr-window.c')
-rw-r--r-- | src/fr-window.c | 8855 |
1 files changed, 8855 insertions, 0 deletions
diff --git a/src/fr-window.c b/src/fr-window.c new file mode 100644 index 0000000..41407c6 --- /dev/null +++ b/src/fr-window.c @@ -0,0 +1,8855 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * File-Roller + * + * Copyright (C) 2007 Free Software Foundation, Inc. + * + * This program 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. + * + * This program 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. + * + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + */ + +#include <config.h> +#include <math.h> +#include <string.h> + +#include <glib/gi18n.h> +#include <gio/gio.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#include "actions.h" +#include "dlg-batch-add.h" +#include "dlg-delete.h" +#include "dlg-extract.h" +#include "dlg-open-with.h" +#include "dlg-ask-password.h" +#include "dlg-package-installer.h" +#include "dlg-update.h" +#include "eggtreemultidnd.h" +#include "fr-marshal.h" +#include "fr-list-model.h" +#include "fr-archive.h" +#include "fr-error.h" +#include "fr-stock.h" +#include "fr-window.h" +#include "file-data.h" +#include "file-utils.h" +#include "glib-utils.h" +#include "main.h" +#include "gtk-utils.h" +#include "mateconf-utils.h" +#include "open-file.h" +#include "typedefs.h" +#include "ui.h" + + +#define LAST_OUTPUT_DIALOG_NAME "last-output" +#define MAX_HISTORY_LEN 5 +#define ACTIVITY_DELAY 100 +#define ACTIVITY_PULSE_STEP (0.033) +#define MAX_MESSAGE_LENGTH 50 + +#define PROGRESS_DIALOG_DEFAULT_WIDTH 400 +#define PROGRESS_TIMEOUT_MSECS 5000 +#define PROGRESS_BAR_HEIGHT 10 +#undef LOG_PROGRESS + +#define HIDE_PROGRESS_TIMEOUT_MSECS 500 +#define DEFAULT_NAME_COLUMN_WIDTH 250 +#define OTHER_COLUMNS_WIDTH 100 +#define RECENT_ITEM_MAX_WIDTH 25 + +#define DEF_WIN_WIDTH 600 +#define DEF_WIN_HEIGHT 480 +#define DEF_SIDEBAR_WIDTH 200 + +#define FILE_LIST_ICON_SIZE GTK_ICON_SIZE_LARGE_TOOLBAR +#define DIR_TREE_ICON_SIZE GTK_ICON_SIZE_MENU + +#define BAD_CHARS "/\\*" + +static GHashTable *pixbuf_hash = NULL; +static GHashTable *tree_pixbuf_hash = NULL; +static GtkIconTheme *icon_theme = NULL; +static int file_list_icon_size = 0; +static int dir_tree_icon_size = 0; + +#define XDS_FILENAME "xds.txt" +#define MAX_XDS_ATOM_VAL_LEN 4096 +#define XDS_ATOM gdk_atom_intern ("XdndDirectSave0", FALSE) +#define TEXT_ATOM gdk_atom_intern ("text/plain", FALSE) +#define OCTET_ATOM gdk_atom_intern ("application/octet-stream", FALSE) +#define XFR_ATOM gdk_atom_intern ("XdndFileRoller0", FALSE) + +#define FR_CLIPBOARD (gdk_atom_intern_static_string ("_FILE_ROLLER_SPECIAL_CLIPBOARD")) +#define FR_SPECIAL_URI_LIST (gdk_atom_intern_static_string ("application/file-roller-uri-list")) + +static GtkTargetEntry clipboard_targets[] = { + { "application/file-roller-uri-list", 0, 1 } +}; + +static GtkTargetEntry target_table[] = { + { "XdndFileRoller0", 0, 0 }, + { "text/uri-list", 0, 1 }, +}; + +static GtkTargetEntry folder_tree_targets[] = { + { "XdndFileRoller0", 0, 0 }, + { "XdndDirectSave0", 0, 2 } +}; + + +typedef struct { + FrBatchActionType type; + void * data; + GFreeFunc free_func; +} FRBatchAction; + + +typedef struct { + guint converting : 1; + char *temp_dir; + FrArchive *new_archive; + char *password; + gboolean encrypt_header; + guint volume_size; + char *new_file; +} FRConvertData; + + +typedef enum { + FR_CLIPBOARD_OP_CUT, + FR_CLIPBOARD_OP_COPY +} FRClipboardOp; + + +typedef struct { + GList *file_list; + char *extract_to_dir; + char *base_dir; + gboolean skip_older; + gboolean overwrite; + gboolean junk_paths; + char *password; + gboolean extract_here; +} ExtractData; + + +typedef enum { + FR_WINDOW_AREA_MENUBAR, + FR_WINDOW_AREA_TOOLBAR, + FR_WINDOW_AREA_LOCATIONBAR, + FR_WINDOW_AREA_CONTENTS, + FR_WINDOW_AREA_FILTERBAR, + FR_WINDOW_AREA_STATUSBAR, +} FrWindowArea; + + +typedef enum { + DIALOG_RESPONSE_NONE = 1, + DIALOG_RESPONSE_OPEN_ARCHIVE, + DIALOG_RESPONSE_OPEN_DESTINATION_FOLDER, + DIALOG_RESPONSE_QUIT +} DialogResponse; + + +/* -- FrClipboardData -- */ + + +typedef struct { + int refs; + char *archive_filename; + char *archive_password; + FRClipboardOp op; + char *base_dir; + GList *files; + char *tmp_dir; + char *current_dir; +} FrClipboardData; + + +static FrClipboardData* +fr_clipboard_data_new (void) +{ + FrClipboardData *data; + + data = g_new0 (FrClipboardData, 1); + data->refs = 1; + + return data; +} + + +static FrClipboardData * +fr_clipboard_data_ref (FrClipboardData *clipboard_data) +{ + clipboard_data->refs++; + return clipboard_data; +} + + +static void +fr_clipboard_data_unref (FrClipboardData *clipboard_data) +{ + if (clipboard_data == NULL) + return; + if (--clipboard_data->refs > 0) + return; + + g_free (clipboard_data->archive_filename); + g_free (clipboard_data->archive_password); + g_free (clipboard_data->base_dir); + g_free (clipboard_data->tmp_dir); + g_free (clipboard_data->current_dir); + g_list_foreach (clipboard_data->files, (GFunc) g_free, NULL); + g_list_free (clipboard_data->files); + g_free (clipboard_data); +} + + +static void +fr_clipboard_data_set_password (FrClipboardData *clipboard_data, + const char *password) +{ + if (clipboard_data->archive_password != password) + g_free (clipboard_data->archive_password); + if (password != NULL) + clipboard_data->archive_password = g_strdup (password); +} + + +/**/ + +enum { + ARCHIVE_LOADED, + LAST_SIGNAL +}; + +static GtkWindowClass *parent_class = NULL; +static guint fr_window_signals[LAST_SIGNAL] = { 0 }; + +struct _FrWindowPrivateData { + GtkWidget *layout; + GtkWidget *contents; + GtkWidget *list_view; + GtkListStore *list_store; + GtkWidget *tree_view; + GtkTreeStore *tree_store; + GtkWidget *toolbar; + GtkWidget *statusbar; + GtkWidget *progress_bar; + GtkWidget *location_bar; + GtkWidget *location_entry; + GtkWidget *location_label; + GtkWidget *filter_bar; + GtkWidget *filter_entry; + GtkWidget *paned; + GtkWidget *sidepane; + GtkTreePath *tree_hover_path; + GtkTreePath *list_hover_path; + GtkTreeViewColumn *filename_column; + + gboolean filter_mode; + gint current_view_length; + + guint help_message_cid; + guint list_info_cid; + guint progress_cid; + char * last_status_message; + + GtkWidget * up_arrows[5]; + GtkWidget * down_arrows[5]; + + FrAction action; + gboolean archive_present; + gboolean archive_new; /* A new archive has been created + * but it doesn't contain any + * file yet. The real file will + * be created only when the user + * adds some file to the + * archive.*/ + + char * archive_uri; + char * open_default_dir; /* default directory to be used + * in the Open dialog. */ + char * add_default_dir; /* default directory to be used + * in the Add dialog. */ + char * extract_default_dir; /* default directory to be used + * in the Extract dialog. */ + gboolean freeze_default_dir; + gboolean asked_for_password; + gboolean ask_to_open_destination_after_extraction; + gboolean destroy_with_error_dialog; + + FRBatchAction current_batch_action; + + gboolean give_focus_to_the_list; + gboolean single_click; + GtkTreePath *path_clicked; + + FrWindowSortMethod sort_method; + GtkSortType sort_type; + + char * last_location; + + gboolean view_folders; + FrWindowListMode list_mode; + FrWindowListMode last_list_mode; + GList * history; + GList * history_current; + char * password; + char * password_for_paste; + gboolean encrypt_header; + FrCompression compression; + guint volume_size; + + guint activity_timeout_handle; /* activity timeout + * handle. */ + gint activity_ref; /* when > 0 some activity + * is present. */ + + guint update_timeout_handle; /* update file list + * timeout handle. */ + + FRConvertData convert_data; + + gboolean stoppable; + gboolean closing; + + FrClipboardData *clipboard_data; + FrClipboardData *copy_data; + + FrArchive *copy_from_archive; + + GtkActionGroup *actions; + + GtkRecentManager *recent_manager; + GtkWidget *recent_chooser_menu; + GtkWidget *recent_chooser_toolbar; + + GtkWidget *file_popup_menu; + GtkWidget *folder_popup_menu; + GtkWidget *sidebar_folder_popup_menu; + GtkWidget *mitem_recents_menu; + GtkWidget *recent_toolbar_menu; + GtkAction *open_action; + + /* dragged files data */ + + char *drag_destination_folder; + char *drag_base_dir; + GError *drag_error; + GList *drag_file_list; /* the list of files we are + * dragging*/ + + /* progress dialog data */ + + GtkWidget *progress_dialog; + GtkWidget *pd_action; + GtkWidget *pd_archive; + GtkWidget *pd_message; + GtkWidget *pd_progress_bar; + GtkWidget *pd_cancel_button; + GtkWidget *pd_close_button; + GtkWidget *pd_open_archive_button; + GtkWidget *pd_open_destination_button; + GtkWidget *pd_quit_button; + gboolean progress_pulse; + guint progress_timeout; /* Timeout to display the progress dialog. */ + guint hide_progress_timeout; /* Timeout to hide the progress dialog. */ + FrAction pd_last_action; + char *pd_last_archive; + char *working_archive; + + /* update dialog data */ + + gpointer update_dialog; + GList *open_files; + + /* batch mode data */ + + gboolean batch_mode; /* whether we are in a non interactive + * mode. */ + GList *batch_action_list; /* FRBatchAction * elements */ + GList *batch_action; /* current action. */ + + /* misc */ + + guint cnxn_id[MATECONF_NOTIFICATIONS]; + + gulong theme_changed_handler_id; + gboolean non_interactive; + char *extract_here_dir; + gboolean extract_interact_use_default_dir; + gboolean update_dropped_files; + gboolean batch_adding_one_file; + + GtkWindow *load_error_parent_window; + gboolean showing_error_dialog; + GtkWindow *error_dialog_parent; +}; + + +/* -- fr_window_free_private_data -- */ + + +static void +fr_window_remove_notifications (FrWindow *window) +{ + int i; + + for (i = 0; i < MATECONF_NOTIFICATIONS; i++) + if (window->priv->cnxn_id[i] != -1) + eel_mateconf_notification_remove (window->priv->cnxn_id[i]); +} + + +static void +fr_window_free_batch_data (FrWindow *window) +{ + GList *scan; + + for (scan = window->priv->batch_action_list; scan; scan = scan->next) { + FRBatchAction *adata = scan->data; + + if ((adata->data != NULL) && (adata->free_func != NULL)) + (*adata->free_func) (adata->data); + g_free (adata); + } + + g_list_free (window->priv->batch_action_list); + window->priv->batch_action_list = NULL; + window->priv->batch_action = NULL; +} + + +static void +gh_unref_pixbuf (gpointer key, + gpointer value, + gpointer user_data) +{ + g_object_unref (value); +} + + +static void +fr_window_clipboard_remove_file_list (FrWindow *window, + GList *file_list) +{ + GList *scan1; + + if (window->priv->copy_data == NULL) + return; + + if (file_list == NULL) { + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + return; + } + + for (scan1 = file_list; scan1; scan1 = scan1->next) { + const char *name1 = scan1->data; + GList *scan2; + + for (scan2 = window->priv->copy_data->files; scan2;) { + const char *name2 = scan2->data; + + if (strcmp (name1, name2) == 0) { + GList *tmp = scan2->next; + window->priv->copy_data->files = g_list_remove_link (window->priv->copy_data->files, scan2); + g_free (scan2->data); + g_list_free (scan2); + scan2 = tmp; + } + else + scan2 = scan2->next; + } + } + + if (window->priv->copy_data->files == NULL) { + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + } +} + + +static void +fr_window_history_clear (FrWindow *window) +{ + if (window->priv->history != NULL) + path_list_free (window->priv->history); + window->priv->history = NULL; + window->priv->history_current = NULL; + g_free (window->priv->last_location); + window->priv->last_location = NULL; +} + + +static void +fr_window_free_open_files (FrWindow *window) +{ + GList *scan; + + for (scan = window->priv->open_files; scan; scan = scan->next) { + OpenFile *file = scan->data; + + if (file->monitor != NULL) + g_file_monitor_cancel (file->monitor); + open_file_free (file); + } + g_list_free (window->priv->open_files); + window->priv->open_files = NULL; +} + + +static void +fr_window_convert_data_free (FrWindow *window, + gboolean all) +{ + if (all) { + g_free (window->priv->convert_data.new_file); + window->priv->convert_data.new_file = NULL; + } + + window->priv->convert_data.converting = FALSE; + + if (window->priv->convert_data.temp_dir != NULL) { + g_free (window->priv->convert_data.temp_dir); + window->priv->convert_data.temp_dir = NULL; + } + + if (window->priv->convert_data.new_archive != NULL) { + g_object_unref (window->priv->convert_data.new_archive); + window->priv->convert_data.new_archive = NULL; + } + + if (window->priv->convert_data.password != NULL) { + g_free (window->priv->convert_data.password); + window->priv->convert_data.password = NULL; + } +} + + +static void +fr_window_free_private_data (FrWindow *window) +{ + FrWindowPrivateData *priv = window->priv; + + if (priv->update_timeout_handle != 0) { + g_source_remove (priv->update_timeout_handle); + priv->update_timeout_handle = 0; + } + + fr_window_remove_notifications (window); + + if (priv->open_action != NULL) { + g_object_unref (priv->open_action); + priv->open_action = NULL; + } + + if (priv->recent_toolbar_menu != NULL) { + gtk_widget_destroy (priv->recent_toolbar_menu); + priv->recent_toolbar_menu = NULL; + } + + while (priv->activity_ref > 0) + fr_window_stop_activity_mode (window); + + if (priv->progress_timeout != 0) { + g_source_remove (priv->progress_timeout); + priv->progress_timeout = 0; + } + + if (priv->hide_progress_timeout != 0) { + g_source_remove (priv->hide_progress_timeout); + priv->hide_progress_timeout = 0; + } + + if (priv->theme_changed_handler_id != 0) + g_signal_handler_disconnect (icon_theme, priv->theme_changed_handler_id); + + fr_window_history_clear (window); + + g_free (priv->open_default_dir); + g_free (priv->add_default_dir); + g_free (priv->extract_default_dir); + g_free (priv->archive_uri); + + g_free (priv->password); + g_free (priv->password_for_paste); + + g_object_unref (priv->list_store); + + if (window->priv->clipboard_data != NULL) { + fr_clipboard_data_unref (window->priv->clipboard_data); + window->priv->clipboard_data = NULL; + } + if (window->priv->copy_data != NULL) { + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + } + if (priv->copy_from_archive != NULL) { + g_object_unref (priv->copy_from_archive); + priv->copy_from_archive = NULL; + } + + fr_window_free_open_files (window); + + fr_window_convert_data_free (window, TRUE); + + g_clear_error (&priv->drag_error); + path_list_free (priv->drag_file_list); + priv->drag_file_list = NULL; + + if (priv->file_popup_menu != NULL) { + gtk_widget_destroy (priv->file_popup_menu); + priv->file_popup_menu = NULL; + } + + if (priv->folder_popup_menu != NULL) { + gtk_widget_destroy (priv->folder_popup_menu); + priv->folder_popup_menu = NULL; + } + + if (priv->sidebar_folder_popup_menu != NULL) { + gtk_widget_destroy (priv->sidebar_folder_popup_menu); + priv->sidebar_folder_popup_menu = NULL; + } + + g_free (window->priv->last_location); + + fr_window_free_batch_data (window); + fr_window_reset_current_batch_action (window); + + g_free (priv->pd_last_archive); + g_free (priv->extract_here_dir); + g_free (priv->last_status_message); + + preferences_set_sort_method (priv->sort_method); + preferences_set_sort_type (priv->sort_type); + preferences_set_list_mode (priv->last_list_mode); +} + + +static void +fr_window_finalize (GObject *object) +{ + FrWindow *window = FR_WINDOW (object); + + fr_window_free_open_files (window); + + if (window->archive != NULL) { + g_object_unref (window->archive); + window->archive = NULL; + } + + if (window->priv != NULL) { + fr_window_free_private_data (window); + g_free (window->priv); + window->priv = NULL; + } + + WindowList = g_list_remove (WindowList, window); + + G_OBJECT_CLASS (parent_class)->finalize (object); + + if (WindowList == NULL) { + if (pixbuf_hash != NULL) { + g_hash_table_foreach (pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (pixbuf_hash); + } + if (tree_pixbuf_hash != NULL) { + g_hash_table_foreach (tree_pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (tree_pixbuf_hash); + } + + gtk_main_quit (); + } +} + + +static gboolean +close__step2 (gpointer data) +{ + gtk_widget_destroy (GTK_WIDGET (data)); + return FALSE; +} + + +void +fr_window_close (FrWindow *window) +{ + if (window->priv->activity_ref > 0) + return; + + window->priv->closing = TRUE; + + if (gtk_widget_get_realized (GTK_WIDGET (window))) { + int width, height; + + #if GTK_CHECK_VERSION(3, 0, 0) + width = gdk_window_get_width(gtk_widget_get_window(GTK_WIDGET(window))); + height = gdk_window_get_height(gtk_widget_get_window(GTK_WIDGET(window))); + #else + gdk_drawable_get_size(gtk_widget_get_window(GTK_WIDGET(window)), &width, &height); + #endif + + eel_mateconf_set_integer (PREF_UI_WINDOW_WIDTH, width); + eel_mateconf_set_integer (PREF_UI_WINDOW_HEIGHT, height); + + width = gtk_paned_get_position (GTK_PANED (window->priv->paned)); + if (width > 0) + eel_mateconf_set_integer (PREF_UI_SIDEBAR_WIDTH, width); + + width = gtk_tree_view_column_get_width (window->priv->filename_column); + if (width > 0) + eel_mateconf_set_integer (PREF_NAME_COLUMN_WIDTH, width); + } + + g_idle_add (close__step2, window); +} + + +static void +fr_window_class_init (FrWindowClass *class) +{ + GObjectClass *gobject_class; + GtkWidgetClass *widget_class; + + parent_class = g_type_class_peek_parent (class); + + fr_window_signals[ARCHIVE_LOADED] = + g_signal_new ("archive-loaded", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (FrWindowClass, archive_loaded), + NULL, NULL, + fr_marshal_VOID__BOOLEAN, + G_TYPE_NONE, 1, + G_TYPE_BOOLEAN); + + gobject_class = (GObjectClass*) class; + gobject_class->finalize = fr_window_finalize; + + widget_class = (GtkWidgetClass*) class; +} + + +static void fr_window_update_paste_command_sensitivity (FrWindow *, GtkClipboard *); + + +static void +clipboard_owner_change_cb (GtkClipboard *clipboard, + GdkEvent *event, + gpointer user_data) +{ + fr_window_update_paste_command_sensitivity ((FrWindow *) user_data, clipboard); +} + + +static void +fr_window_realized (GtkWidget *window, + gpointer *data) +{ + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (window, FR_CLIPBOARD); + g_signal_connect (clipboard, + "owner_change", + G_CALLBACK (clipboard_owner_change_cb), + window); +} + + +static void +fr_window_unrealized (GtkWidget *window, + gpointer *data) +{ + GtkClipboard *clipboard; + + clipboard = gtk_widget_get_clipboard (window, FR_CLIPBOARD); + g_signal_handlers_disconnect_by_func (clipboard, + G_CALLBACK (clipboard_owner_change_cb), + window); +} + + +static void +fr_window_init (FrWindow *window) +{ + window->priv = g_new0 (FrWindowPrivateData, 1); + window->priv->update_dropped_files = FALSE; + window->priv->filter_mode = FALSE; + + g_signal_connect (window, + "realize", + G_CALLBACK (fr_window_realized), + NULL); + g_signal_connect (window, + "unrealize", + G_CALLBACK (fr_window_unrealized), + NULL); + + WindowList = g_list_prepend (WindowList, window); +} + + +GType +fr_window_get_type (void) +{ + static GType type = 0; + + if (! type) { + GTypeInfo type_info = { + sizeof (FrWindowClass), + NULL, + NULL, + (GClassInitFunc) fr_window_class_init, + NULL, + NULL, + sizeof (FrWindow), + 0, + (GInstanceInitFunc) fr_window_init + }; + + type = g_type_register_static (GTK_TYPE_WINDOW, + "FrWindow", + &type_info, + 0); + } + + return type; +} + + +/* -- window history -- */ + + +#if 0 +static void +fr_window_history_print (FrWindow *window) +{ + GList *list; + + debug (DEBUG_INFO, "history:\n"); + for (list = window->priv->history; list; list = list->next) + g_print ("\t%s %s\n", + (char*) list->data, + (list == window->priv->history_current)? "<-": ""); + g_print ("\n"); +} +#endif + + +static void +fr_window_history_add (FrWindow *window, + const char *path) +{ + if ((window->priv->history != NULL) && (window->priv->history_current != NULL)) { + if (strcmp (window->priv->history_current->data, path) == 0) + return; + + /* Add locations visited using the back button to the history + * list. */ + if (window->priv->history != window->priv->history_current) { + GList *scan = window->priv->history->next; + while (scan != window->priv->history_current->next) { + window->priv->history = g_list_prepend (window->priv->history, g_strdup (scan->data)); + scan = scan->next; + } + } + } + + window->priv->history = g_list_prepend (window->priv->history, g_strdup (path)); + window->priv->history_current = window->priv->history; +} + + +static void +fr_window_history_pop (FrWindow *window) +{ + GList *first; + + if (window->priv->history == NULL) + return; + + first = window->priv->history; + window->priv->history = g_list_remove_link (window->priv->history, first); + if (window->priv->history_current == first) + window->priv->history_current = window->priv->history; + g_free (first->data); + g_list_free (first); +} + + +/* -- window_update_file_list -- */ + + +static GPtrArray * +fr_window_get_current_dir_list (FrWindow *window) +{ + GPtrArray *files; + int i; + + files = g_ptr_array_sized_new (128); + + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + + if (fdata->list_name == NULL) + continue; + g_ptr_array_add (files, fdata); + } + + return files; +} + + +static gint +sort_by_name (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + + return strcasecmp (fdata1->list_name, fdata2->list_name); +} + + +static gint +sort_by_size (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + else if (file_data_is_dir (fdata1) && file_data_is_dir (fdata2)) { + if (fdata1->dir_size > fdata2->dir_size) + return 1; + else + return -1; + } + + if (fdata1->size == fdata2->size) + return sort_by_name (ptr1, ptr2); + else if (fdata1->size > fdata2->size) + return 1; + else + return -1; +} + + +static gint +sort_by_type (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + int result; + const char *desc1, *desc2; + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + else if (file_data_is_dir (fdata1) && file_data_is_dir (fdata2)) + return sort_by_name (ptr1, ptr2); + + desc1 = g_content_type_get_description (fdata1->content_type); + desc2 = g_content_type_get_description (fdata2->content_type); + + result = strcasecmp (desc1, desc2); + if (result == 0) + return sort_by_name (ptr1, ptr2); + else + return result; +} + + +static gint +sort_by_time (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + else if (file_data_is_dir (fdata1) && file_data_is_dir (fdata2)) + return sort_by_name (ptr1, ptr2); + + if (fdata1->modified == fdata2->modified) + return sort_by_name (ptr1, ptr2); + else if (fdata1->modified > fdata2->modified) + return 1; + else + return -1; +} + + +static gint +sort_by_path (gconstpointer ptr1, + gconstpointer ptr2) +{ + FileData *fdata1 = *((FileData **) ptr1); + FileData *fdata2 = *((FileData **) ptr2); + int result; + + if (file_data_is_dir (fdata1) != file_data_is_dir (fdata2)) { + if (file_data_is_dir (fdata1)) + return -1; + else + return 1; + } + else if (file_data_is_dir (fdata1) && file_data_is_dir (fdata2)) + return sort_by_name (ptr1, ptr2); + + /* 2 files */ + + result = strcasecmp (fdata1->path, fdata2->path); + if (result == 0) + return sort_by_name (ptr1, ptr2); + else + return result; +} + + +static guint64 +get_dir_size (FrWindow *window, + const char *current_dir, + const char *name) +{ + guint64 size; + char *dirname; + int dirname_l; + int i; + + dirname = g_strconcat (current_dir, name, "/", NULL); + dirname_l = strlen (dirname); + + size = 0; + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fd = g_ptr_array_index (window->archive->command->files, i); + + if (strncmp (dirname, fd->full_path, dirname_l) == 0) + size += fd->size; + } + + g_free (dirname); + + return size; +} + + +static gboolean +file_data_respects_filter (FrWindow *window, + FileData *fdata) +{ + const char *filter; + + filter = gtk_entry_get_text (GTK_ENTRY (window->priv->filter_entry)); + if ((fdata == NULL) || (filter == NULL) || (*filter == '\0')) + return TRUE; + + if (fdata->dir || (fdata->name == NULL)) + return FALSE; + + return strncasecmp (fdata->name, filter, strlen (filter)) == 0; +} + + +static gboolean +compute_file_list_name (FrWindow *window, + FileData *fdata, + const char *current_dir, + int current_dir_len, + GHashTable *names_hash, + gboolean *different_name) +{ + register char *scan, *end; + + *different_name = FALSE; + + if (! file_data_respects_filter (window, fdata)) + return FALSE; + + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) { + fdata->list_name = g_strdup (fdata->name); + if (fdata->dir) + fdata->dir_size = 0; + return FALSE; + } + + if (strncmp (fdata->full_path, current_dir, current_dir_len) != 0) { + *different_name = TRUE; + return FALSE; + } + + if (strlen (fdata->full_path) == current_dir_len) + return FALSE; + + scan = fdata->full_path + current_dir_len; + end = strchr (scan, '/'); + if ((end == NULL) && ! fdata->dir) { /* file */ + fdata->list_name = g_strdup (scan); + } + else { /* folder */ + char *dir_name; + + if (end != NULL) + dir_name = g_strndup (scan, end - scan); + else + dir_name = g_strdup (scan); + + /* avoid to insert duplicated folders */ + if (g_hash_table_lookup (names_hash, dir_name) != NULL) { + g_free (dir_name); + return FALSE; + } + g_hash_table_insert (names_hash, dir_name, GINT_TO_POINTER (1)); + + if ((end != NULL) && (*(end + 1) != '\0')) + fdata->list_dir = TRUE; + fdata->list_name = dir_name; + fdata->dir_size = get_dir_size (window, current_dir, dir_name); + } + + return TRUE; +} + + +static void +fr_window_compute_list_names (FrWindow *window, + GPtrArray *files) +{ + const char *current_dir; + int current_dir_len; + GHashTable *names_hash; + int i; + gboolean visible_list_started = FALSE; + gboolean visible_list_completed = FALSE; + gboolean different_name; + + current_dir = fr_window_get_current_location (window); + current_dir_len = strlen (current_dir); + names_hash = g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; i < files->len; i++) { + FileData *fdata = g_ptr_array_index (files, i); + + g_free (fdata->list_name); + fdata->list_name = NULL; + fdata->list_dir = FALSE; + + /* the files array is sorted by path, when the visible list + * is started and we find a path that doesn't match the + * current_dir path, the following files can't match + * the current_dir path. */ + + if (visible_list_completed) + continue; + + if (compute_file_list_name (window, fdata, current_dir, current_dir_len, names_hash, &different_name)) { + visible_list_started = TRUE; + } + else if (visible_list_started && different_name) + visible_list_completed = TRUE; + } + + g_hash_table_destroy (names_hash); +} + + +static gboolean +fr_window_dir_exists_in_archive (FrWindow *window, + const char *dir_name) +{ + int dir_name_len; + int i; + + if (dir_name == NULL) + return FALSE; + + dir_name_len = strlen (dir_name); + if (dir_name_len == 0) + return TRUE; + + if (strcmp (dir_name, "/") == 0) + return TRUE; + + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + + if (strncmp (dir_name, fdata->full_path, dir_name_len) == 0) { + return TRUE; + } + else if (fdata->dir + && (fdata->full_path[strlen (fdata->full_path)] != '/') + && (strncmp (dir_name, fdata->full_path, dir_name_len - 1) == 0)) + { + return TRUE; + } + } + + return FALSE; +} + + +static char * +get_parent_dir (const char *current_dir) +{ + char *dir; + char *new_dir; + char *retval; + + if (current_dir == NULL) + return NULL; + if (strcmp (current_dir, "/") == 0) + return g_strdup ("/"); + + dir = g_strdup (current_dir); + dir[strlen (dir) - 1] = 0; + new_dir = remove_level_from_path (dir); + g_free (dir); + + if (new_dir[strlen (new_dir) - 1] == '/') + retval = new_dir; + else { + retval = g_strconcat (new_dir, "/", NULL); + g_free (new_dir); + } + + return retval; +} + + +static void fr_window_update_statusbar_list_info (FrWindow *window); + + +static GdkPixbuf * +get_mime_type_icon (const char *mime_type) +{ + GdkPixbuf *pixbuf = NULL; + + pixbuf = g_hash_table_lookup (tree_pixbuf_hash, mime_type); + if (pixbuf != NULL) { + g_object_ref (G_OBJECT (pixbuf)); + return pixbuf; + } + + pixbuf = get_mime_type_pixbuf (mime_type, file_list_icon_size, icon_theme); + if (pixbuf == NULL) + return NULL; + + pixbuf = gdk_pixbuf_copy (pixbuf); + g_hash_table_insert (tree_pixbuf_hash, (gpointer) mime_type, pixbuf); + g_object_ref (G_OBJECT (pixbuf)); + + return pixbuf; +} + + +static GdkPixbuf * +get_icon (GtkWidget *widget, + FileData *fdata) +{ + GdkPixbuf *pixbuf = NULL; + const char *content_type; + GIcon *icon; + + if (file_data_is_dir (fdata)) + content_type = MIME_TYPE_DIRECTORY; + else + content_type = fdata->content_type; + + /* look in the hash table. */ + + pixbuf = g_hash_table_lookup (pixbuf_hash, content_type); + if (pixbuf != NULL) { + g_object_ref (G_OBJECT (pixbuf)); + return pixbuf; + } + + icon = g_content_type_get_icon (content_type); + pixbuf = get_icon_pixbuf (icon, file_list_icon_size, icon_theme); + g_object_unref (icon); + + if (pixbuf == NULL) + return NULL; + + pixbuf = gdk_pixbuf_copy (pixbuf); + g_hash_table_insert (pixbuf_hash, (gpointer) content_type, pixbuf); + g_object_ref (G_OBJECT (pixbuf)); + + return pixbuf; +} + + +static GdkPixbuf * +get_emblem (GtkWidget *widget, + FileData *fdata) +{ + GdkPixbuf *pixbuf = NULL; + + if (! fdata->encrypted) + return NULL; + + /* encrypted */ + + pixbuf = g_hash_table_lookup (pixbuf_hash, "emblem-nowrite"); + if (pixbuf != NULL) { + g_object_ref (G_OBJECT (pixbuf)); + return pixbuf; + } + + pixbuf = gtk_icon_theme_load_icon (icon_theme, + "emblem-nowrite", + file_list_icon_size, + 0, + NULL); + if (pixbuf == NULL) + return NULL; + + pixbuf = gdk_pixbuf_copy (pixbuf); + g_hash_table_insert (pixbuf_hash, (gpointer) "emblem-nowrite", pixbuf); + g_object_ref (G_OBJECT (pixbuf)); + + return pixbuf; +} + + +static int +get_column_from_sort_method (FrWindowSortMethod sort_method) +{ + switch (sort_method) { + case FR_WINDOW_SORT_BY_NAME: return COLUMN_NAME; + case FR_WINDOW_SORT_BY_SIZE: return COLUMN_SIZE; + case FR_WINDOW_SORT_BY_TYPE: return COLUMN_TYPE; + case FR_WINDOW_SORT_BY_TIME: return COLUMN_TIME; + case FR_WINDOW_SORT_BY_PATH: return COLUMN_PATH; + default: + break; + } + + return COLUMN_NAME; +} + + +static int +get_sort_method_from_column (int column_id) +{ + switch (column_id) { + case COLUMN_NAME: return FR_WINDOW_SORT_BY_NAME; + case COLUMN_SIZE: return FR_WINDOW_SORT_BY_SIZE; + case COLUMN_TYPE: return FR_WINDOW_SORT_BY_TYPE; + case COLUMN_TIME: return FR_WINDOW_SORT_BY_TIME; + case COLUMN_PATH: return FR_WINDOW_SORT_BY_PATH; + default: + break; + } + + return FR_WINDOW_SORT_BY_NAME; +} + + +static void +add_selected_from_list_view (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **list = data; + FileData *fdata; + + gtk_tree_model_get (model, iter, + COLUMN_FILE_DATA, &fdata, + -1); + *list = g_list_prepend (*list, fdata); +} + + +static void +add_selected_from_tree_view (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **list = data; + char *dir_path; + + gtk_tree_model_get (model, iter, + TREE_COLUMN_PATH, &dir_path, + -1); + *list = g_list_prepend (*list, dir_path); +} + + +static void +add_selected_fd (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GList **list = data; + FileData *fdata; + + gtk_tree_model_get (model, iter, + COLUMN_FILE_DATA, &fdata, + -1); + if (! fdata->list_dir) + *list = g_list_prepend (*list, fdata); +} + + +static GList * +get_selection_as_fd (FrWindow *window) +{ + GtkTreeSelection *selection; + GList *list = NULL; + + if (! gtk_widget_get_realized (window->priv->list_view)) + return NULL; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return NULL; + gtk_tree_selection_selected_foreach (selection, add_selected_fd, &list); + + return list; +} + + +static void +fr_window_update_statusbar_list_info (FrWindow *window) +{ + char *info, *archive_info, *selected_info; + char *size_txt, *sel_size_txt; + int tot_n, sel_n; + goffset tot_size, sel_size; + GList *scan; + + if (window == NULL) + return; + + if ((window->archive == NULL) || (window->archive->command == NULL)) { + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), window->priv->list_info_cid); + return; + } + + tot_n = 0; + tot_size = 0; + + if (window->priv->archive_present) { + GPtrArray *files = fr_window_get_current_dir_list (window); + int i; + + for (i = 0; i < files->len; i++) { + FileData *fd = g_ptr_array_index (files, i); + + tot_n++; + if (! file_data_is_dir (fd)) + tot_size += fd->size; + else + tot_size += fd->dir_size; + } + g_ptr_array_free (files, TRUE); + } + + sel_n = 0; + sel_size = 0; + + if (window->priv->archive_present) { + GList *selection = get_selection_as_fd (window); + + for (scan = selection; scan; scan = scan->next) { + FileData *fd = scan->data; + + sel_n++; + if (! file_data_is_dir (fd)) + sel_size += fd->size; + } + g_list_free (selection); + } + + size_txt = g_format_size_for_display (tot_size); + sel_size_txt = g_format_size_for_display (sel_size); + + if (tot_n == 0) + archive_info = g_strdup (""); + else + archive_info = g_strdup_printf (ngettext ("%d object (%s)", "%d objects (%s)", tot_n), tot_n, size_txt); + + if (sel_n == 0) + selected_info = g_strdup (""); + else + selected_info = g_strdup_printf (ngettext ("%d object selected (%s)", "%d objects selected (%s)", sel_n), sel_n, sel_size_txt); + + info = g_strconcat (archive_info, + ((sel_n == 0) ? NULL : ", "), + selected_info, + NULL); + + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), window->priv->list_info_cid, info); + + g_free (size_txt); + g_free (sel_size_txt); + g_free (archive_info); + g_free (selected_info); + g_free (info); +} + + +static void +fr_window_populate_file_list (FrWindow *window, + GPtrArray *files) +{ + int i; + + gtk_list_store_clear (window->priv->list_store); + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), + GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, + GTK_SORT_ASCENDING); + + for (i = 0; i < files->len; i++) { + FileData *fdata = g_ptr_array_index (files, i); + GtkTreeIter iter; + GdkPixbuf *icon, *emblem; + char *utf8_name; + + if (fdata->list_name == NULL) + continue; + + gtk_list_store_append (window->priv->list_store, &iter); + + icon = get_icon (GTK_WIDGET (window), fdata); + utf8_name = g_filename_display_name (fdata->list_name); + emblem = get_emblem (GTK_WIDGET (window), fdata); + + if (file_data_is_dir (fdata)) { + char *utf8_path; + char *tmp; + char *s_size; + char *s_time; + + if (fdata->list_dir) + tmp = remove_ending_separator (fr_window_get_current_location (window)); + + else + tmp = remove_level_from_path (fdata->path); + utf8_path = g_filename_display_name (tmp); + g_free (tmp); + + s_size = g_format_size_for_display (fdata->dir_size); + + if (fdata->list_dir) + s_time = g_strdup (""); + else + s_time = get_time_string (fdata->modified); + + gtk_list_store_set (window->priv->list_store, &iter, + COLUMN_FILE_DATA, fdata, + COLUMN_ICON, icon, + COLUMN_NAME, utf8_name, + COLUMN_EMBLEM, emblem, + COLUMN_TYPE, _("Folder"), + COLUMN_SIZE, s_size, + COLUMN_TIME, s_time, + COLUMN_PATH, utf8_path, + -1); + g_free (utf8_path); + g_free (s_size); + g_free (s_time); + } + else { + char *utf8_path; + char *s_size; + char *s_time; + const char *desc; + + utf8_path = g_filename_display_name (fdata->path); + + s_size = g_format_size_for_display (fdata->size); + s_time = get_time_string (fdata->modified); + desc = g_content_type_get_description (fdata->content_type); + + gtk_list_store_set (window->priv->list_store, &iter, + COLUMN_FILE_DATA, fdata, + COLUMN_ICON, icon, + COLUMN_NAME, utf8_name, + COLUMN_EMBLEM, emblem, + COLUMN_TYPE, desc, + COLUMN_SIZE, s_size, + COLUMN_TIME, s_time, + COLUMN_PATH, utf8_path, + -1); + g_free (utf8_path); + g_free (s_size); + g_free (s_time); + } + g_free (utf8_name); + if (icon != NULL) + g_object_unref (icon); + if (emblem != NULL) + g_object_unref (emblem); + } + + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), + get_column_from_sort_method (window->priv->sort_method), + window->priv->sort_type); + + fr_window_update_statusbar_list_info (window); + fr_window_stop_activity_mode (window); +} + + +static int +path_compare (gconstpointer a, + gconstpointer b) +{ + char *path_a = *((char**) a); + char *path_b = *((char**) b); + + return strcmp (path_a, path_b); +} + + +static gboolean +get_tree_iter_from_path (FrWindow *window, + const char *path, + GtkTreeIter *parent, + GtkTreeIter *iter) +{ + gboolean result = FALSE; + + if (! gtk_tree_model_iter_children (GTK_TREE_MODEL (window->priv->tree_store), iter, parent)) + return FALSE; + + do { + GtkTreeIter tmp; + char *iter_path; + + if (get_tree_iter_from_path (window, path, iter, &tmp)) { + *iter = tmp; + return TRUE; + } + + gtk_tree_model_get (GTK_TREE_MODEL (window->priv->tree_store), + iter, + TREE_COLUMN_PATH, &iter_path, + -1); + + if ((iter_path != NULL) && (strcmp (path, iter_path) == 0)) { + result = TRUE; + g_free (iter_path); + break; + } + g_free (iter_path); + } while (gtk_tree_model_iter_next (GTK_TREE_MODEL (window->priv->tree_store), iter)); + + return result; +} + + +static void +set_sensitive (FrWindow *window, + const char *action_name, + gboolean sensitive) +{ + GtkAction *action; + + action = gtk_action_group_get_action (window->priv->actions, action_name); + g_object_set (action, "sensitive", sensitive, NULL); +} + + +static void +fr_window_update_current_location (FrWindow *window) +{ + const char *current_dir = fr_window_get_current_location (window); + char *path; + GtkTreeIter iter; + + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) { + gtk_widget_hide (window->priv->location_bar); + return; + } + + gtk_widget_show (window->priv->location_bar); + + gtk_entry_set_text (GTK_ENTRY (window->priv->location_entry), window->priv->archive_present? current_dir: ""); + + set_sensitive (window, "GoBack", window->priv->archive_present && (current_dir != NULL) && (window->priv->history_current != NULL) && (window->priv->history_current->next != NULL)); + set_sensitive (window, "GoForward", window->priv->archive_present && (current_dir != NULL) && (window->priv->history_current != NULL) && (window->priv->history_current->prev != NULL)); + set_sensitive (window, "GoUp", window->priv->archive_present && (current_dir != NULL) && (strcmp (current_dir, "/") != 0)); + set_sensitive (window, "GoHome", window->priv->archive_present); + gtk_widget_set_sensitive (window->priv->location_entry, window->priv->archive_present); + gtk_widget_set_sensitive (window->priv->location_label, window->priv->archive_present); + gtk_widget_set_sensitive (window->priv->filter_entry, window->priv->archive_present); + +#if 0 + fr_window_history_print (window); +#endif + + path = remove_ending_separator (current_dir); + if (get_tree_iter_from_path (window, path, NULL, &iter)) { + GtkTreeSelection *selection; + GtkTreePath *t_path; + + t_path = gtk_tree_model_get_path (GTK_TREE_MODEL (window->priv->tree_store), &iter); + gtk_tree_view_expand_to_path (GTK_TREE_VIEW (window->priv->tree_view), t_path); + gtk_tree_path_free (t_path); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + gtk_tree_selection_select_iter (selection, &iter); + } + g_free (path); +} + + +void +fr_window_update_dir_tree (FrWindow *window) +{ + GPtrArray *dirs; + GHashTable *dir_cache; + int i; + GdkPixbuf *icon; + + gtk_tree_store_clear (window->priv->tree_store); + + if (! window->priv->view_folders + || ! window->priv->archive_present + || (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT)) + { + gtk_widget_set_sensitive (window->priv->tree_view, FALSE); + gtk_widget_hide (window->priv->sidepane); + return; + } + else { + gtk_widget_set_sensitive (window->priv->tree_view, TRUE); + if (! gtk_widget_get_visible (window->priv->sidepane)) + gtk_widget_show_all (window->priv->sidepane); + } + + if (gtk_widget_get_realized (window->priv->tree_view)) + gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (window->priv->tree_view), 0, 0); + + /**/ + + dirs = g_ptr_array_sized_new (128); + + dir_cache = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + char *dir; + + if (gtk_entry_get_text (GTK_ENTRY (window->priv->filter_entry)) != NULL) { + if (! file_data_respects_filter (window, fdata)) + continue; + } + + if (fdata->dir) + dir = remove_ending_separator (fdata->full_path); + else + dir = remove_level_from_path (fdata->full_path); + + while ((dir != NULL) && (strcmp (dir, "/") != 0)) { + char *new_dir; + + if (g_hash_table_lookup (dir_cache, dir) != NULL) + break; + + new_dir = dir; + g_ptr_array_add (dirs, new_dir); + g_hash_table_replace (dir_cache, new_dir, "1"); + + dir = remove_level_from_path (new_dir); + } + + g_free (dir); + } + g_hash_table_destroy (dir_cache); + + g_ptr_array_sort (dirs, path_compare); + dir_cache = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) gtk_tree_path_free); + + /**/ + + icon = get_mime_type_icon (MIME_TYPE_ARCHIVE); + { + GtkTreeIter node; + char *uri; + char *name; + + uri = g_file_get_uri (window->archive->file); + name = g_uri_display_basename (uri); + + gtk_tree_store_append (window->priv->tree_store, &node, NULL); + gtk_tree_store_set (window->priv->tree_store, &node, + TREE_COLUMN_ICON, icon, + TREE_COLUMN_NAME, name, + TREE_COLUMN_PATH, "/", + TREE_COLUMN_WEIGHT, PANGO_WEIGHT_BOLD, + -1); + g_hash_table_replace (dir_cache, "/", gtk_tree_model_get_path (GTK_TREE_MODEL (window->priv->tree_store), &node)); + + g_free (name); + g_free (uri); + } + g_object_unref (icon); + + /**/ + + icon = get_mime_type_icon (MIME_TYPE_DIRECTORY); + for (i = 0; i < dirs->len; i++) { + char *dir = g_ptr_array_index (dirs, i); + char *parent_dir; + GtkTreePath *parent_path; + GtkTreeIter parent; + GtkTreeIter node; + + parent_dir = remove_level_from_path (dir); + if (parent_dir == NULL) + continue; + + parent_path = g_hash_table_lookup (dir_cache, parent_dir); + gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->tree_store), + &parent, + parent_path); + gtk_tree_store_append (window->priv->tree_store, &node, &parent); + gtk_tree_store_set (window->priv->tree_store, &node, + TREE_COLUMN_ICON, icon, + TREE_COLUMN_NAME, file_name_from_path (dir), + TREE_COLUMN_PATH, dir, + TREE_COLUMN_WEIGHT, PANGO_WEIGHT_NORMAL, + -1); + g_hash_table_replace (dir_cache, dir, gtk_tree_model_get_path (GTK_TREE_MODEL (window->priv->tree_store), &node)); + + g_free (parent_dir); + } + g_hash_table_destroy (dir_cache); + if (icon != NULL) + g_object_unref (icon); + + g_ptr_array_free (dirs, TRUE); + + fr_window_update_current_location (window); +} + + +static void +fr_window_update_filter_bar_visibility (FrWindow *window) +{ + const char *filter; + + filter = gtk_entry_get_text (GTK_ENTRY (window->priv->filter_entry)); + if ((filter == NULL) || (*filter == '\0')) + gtk_widget_hide (window->priv->filter_bar); + else + gtk_widget_show (window->priv->filter_bar); +} + + +static void +fr_window_update_file_list (FrWindow *window, + gboolean update_view) +{ + GPtrArray *files; + gboolean free_files = FALSE; + + if (gtk_widget_get_realized (window->priv->list_view)) + gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (window->priv->list_view), 0, 0); + + if (! window->priv->archive_present || window->priv->archive_new) { + if (update_view) + gtk_list_store_clear (window->priv->list_store); + + window->priv->current_view_length = 0; + + if (window->priv->archive_new) { + gtk_widget_set_sensitive (window->priv->list_view, TRUE); + gtk_widget_show_all (gtk_widget_get_parent (window->priv->list_view)); + } + else { + gtk_widget_set_sensitive (window->priv->list_view, FALSE); + gtk_widget_hide_all (gtk_widget_get_parent (window->priv->list_view)); + } + + return; + } + else { + gtk_widget_set_sensitive (window->priv->list_view, TRUE); + if (! gtk_widget_get_visible (window->priv->list_view)) + gtk_widget_show_all (gtk_widget_get_parent (window->priv->list_view)); + } + + if (window->priv->give_focus_to_the_list) { + gtk_widget_grab_focus (window->priv->list_view); + window->priv->give_focus_to_the_list = FALSE; + } + + /**/ + + fr_window_start_activity_mode (window); + + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) { + fr_window_compute_list_names (window, window->archive->command->files); + files = window->archive->command->files; + free_files = FALSE; + } + else { + char *current_dir = g_strdup (fr_window_get_current_location (window)); + + while (! fr_window_dir_exists_in_archive (window, current_dir)) { + char *tmp; + + fr_window_history_pop (window); + + tmp = get_parent_dir (current_dir); + g_free (current_dir); + current_dir = tmp; + + fr_window_history_add (window, current_dir); + } + g_free (current_dir); + + fr_window_compute_list_names (window, window->archive->command->files); + files = fr_window_get_current_dir_list (window); + free_files = TRUE; + } + + if (files != NULL) + window->priv->current_view_length = files->len; + else + window->priv->current_view_length = 0; + + if (update_view) + fr_window_populate_file_list (window, files); + + if (free_files) + g_ptr_array_free (files, TRUE); +} + + +void +fr_window_update_list_order (FrWindow *window) +{ + gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (window->priv->list_store), get_column_from_sort_method (window->priv->sort_method), window->priv->sort_type); +} + + +static void +fr_window_update_title (FrWindow *window) +{ + if (! window->priv->archive_present) + gtk_window_set_title (GTK_WINDOW (window), _("Archive Manager")); + else { + char *title; + char *name; + + name = g_uri_display_basename (fr_window_get_archive_uri (window)); + title = g_strdup_printf ("%s %s", + name, + window->archive->read_only ? _("[read only]") : ""); + + gtk_window_set_title (GTK_WINDOW (window), title); + g_free (title); + g_free (name); + } +} + + +static void +check_whether_has_a_dir (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + gboolean *has_a_dir = data; + FileData *fdata; + + gtk_tree_model_get (model, iter, + COLUMN_FILE_DATA, &fdata, + -1); + if (file_data_is_dir (fdata)) + *has_a_dir = TRUE; +} + + +static gboolean +selection_has_a_dir (FrWindow *window) +{ + GtkTreeSelection *selection; + gboolean has_a_dir = FALSE; + + if (! gtk_widget_get_realized (window->priv->list_view)) + return FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return FALSE; + + gtk_tree_selection_selected_foreach (selection, + check_whether_has_a_dir, + &has_a_dir); + + return has_a_dir; +} + + +static void +set_active (FrWindow *window, + const char *action_name, + gboolean is_active) +{ + GtkAction *action; + + action = gtk_action_group_get_action (window->priv->actions, action_name); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action), is_active); +} + + +static void +fr_window_update_paste_command_sensitivity (FrWindow *window, + GtkClipboard *clipboard) +{ + gboolean running; + gboolean no_archive; + gboolean ro; + gboolean compr_file; + + if (window->priv->closing) + return; + + if (clipboard == NULL) + clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window), FR_CLIPBOARD); + running = window->priv->activity_ref > 0; + no_archive = (window->archive == NULL) || ! window->priv->archive_present; + ro = ! no_archive && window->archive->read_only; + compr_file = ! no_archive && window->archive->is_compressed_file; + + set_sensitive (window, "Paste", ! no_archive && ! ro && ! running && ! compr_file && (window->priv->list_mode != FR_WINDOW_LIST_MODE_FLAT) && gtk_clipboard_wait_is_target_available (clipboard, FR_SPECIAL_URI_LIST)); +} + + +static void +fr_window_update_sensitivity (FrWindow *window) +{ + gboolean no_archive; + gboolean ro; + gboolean file_op; + gboolean running; + gboolean compr_file; + gboolean sel_not_null; + gboolean one_file_selected; + gboolean dir_selected; + int n_selected; + + if (window->priv->batch_mode) + return; + + running = window->priv->activity_ref > 0; + no_archive = (window->archive == NULL) || ! window->priv->archive_present; + ro = ! no_archive && window->archive->read_only; + file_op = ! no_archive && ! window->priv->archive_new && ! running; + compr_file = ! no_archive && window->archive->is_compressed_file; + n_selected = fr_window_get_n_selected_files (window); + sel_not_null = n_selected > 0; + one_file_selected = n_selected == 1; + dir_selected = selection_has_a_dir (window); + + set_sensitive (window, "AddFiles", ! no_archive && ! ro && ! running && ! compr_file); + set_sensitive (window, "AddFiles_Toolbar", ! no_archive && ! ro && ! running && ! compr_file); + set_sensitive (window, "AddFolder", ! no_archive && ! ro && ! running && ! compr_file); + set_sensitive (window, "AddFolder_Toolbar", ! no_archive && ! ro && ! running && ! compr_file); + set_sensitive (window, "Copy", ! no_archive && ! ro && ! running && ! compr_file && sel_not_null && (window->priv->list_mode != FR_WINDOW_LIST_MODE_FLAT)); + set_sensitive (window, "Cut", ! no_archive && ! ro && ! running && ! compr_file && sel_not_null && (window->priv->list_mode != FR_WINDOW_LIST_MODE_FLAT)); + set_sensitive (window, "Delete", ! no_archive && ! ro && ! window->priv->archive_new && ! running && ! compr_file); + set_sensitive (window, "DeselectAll", ! no_archive && sel_not_null); + set_sensitive (window, "Extract", file_op); + set_sensitive (window, "Extract_Toolbar", file_op); + set_sensitive (window, "Find", ! no_archive); + set_sensitive (window, "LastOutput", ((window->archive != NULL) + && (window->archive->process != NULL) + && (window->archive->process->out.raw != NULL))); + set_sensitive (window, "New", ! running); + set_sensitive (window, "Open", ! running); + set_sensitive (window, "Open_Toolbar", ! running); + set_sensitive (window, "OpenSelection", file_op && sel_not_null && ! dir_selected); + set_sensitive (window, "OpenFolder", file_op && one_file_selected && dir_selected); + set_sensitive (window, "Password", ! running && (window->priv->asked_for_password || (! no_archive && window->archive->command->propPassword))); + set_sensitive (window, "Properties", file_op); + set_sensitive (window, "Close", !running || window->priv->stoppable); + set_sensitive (window, "Reload", ! (no_archive || running)); + set_sensitive (window, "Rename", ! no_archive && ! ro && ! running && ! compr_file && one_file_selected); + set_sensitive (window, "SaveAs", ! no_archive && ! compr_file && ! running); + set_sensitive (window, "SelectAll", ! no_archive); + set_sensitive (window, "Stop", running && window->priv->stoppable); + set_sensitive (window, "TestArchive", ! no_archive && ! running && window->archive->command->propTest); + set_sensitive (window, "ViewSelection", file_op && one_file_selected && ! dir_selected); + set_sensitive (window, "ViewSelection_Toolbar", file_op && one_file_selected && ! dir_selected); + + if (window->priv->progress_dialog != NULL) + gtk_dialog_set_response_sensitive (GTK_DIALOG (window->priv->progress_dialog), + GTK_RESPONSE_OK, + running && window->priv->stoppable); + + fr_window_update_paste_command_sensitivity (window, NULL); + + set_sensitive (window, "SelectAll", (window->priv->current_view_length > 0) && (window->priv->current_view_length != n_selected)); + set_sensitive (window, "DeselectAll", n_selected > 0); + set_sensitive (window, "OpenRecentMenu", ! running); + + set_sensitive (window, "ViewFolders", (window->priv->list_mode == FR_WINDOW_LIST_MODE_AS_DIR)); + + set_sensitive (window, "ViewAllFiles", ! window->priv->filter_mode); + set_sensitive (window, "ViewAsFolder", ! window->priv->filter_mode); +} + + +static gboolean +location_entry_key_press_event_cb (GtkWidget *widget, + GdkEventKey *event, + FrWindow *window) +{ + if ((event->keyval == GDK_Return) + || (event->keyval == GDK_KP_Enter) + || (event->keyval == GDK_ISO_Enter)) + { + fr_window_go_to_location (window, gtk_entry_get_text (GTK_ENTRY (window->priv->location_entry)), FALSE); + } + + return FALSE; +} + + +static gboolean +real_close_progress_dialog (gpointer data) +{ + FrWindow *window = data; + + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + + if (window->priv->progress_dialog != NULL) + gtk_widget_hide (window->priv->progress_dialog); + + return FALSE; +} + + +static void +close_progress_dialog (FrWindow *window, + gboolean close_now) +{ + if (window->priv->progress_timeout != 0) { + g_source_remove (window->priv->progress_timeout); + window->priv->progress_timeout = 0; + } + + if (! window->priv->batch_mode && gtk_widget_get_mapped (GTK_WIDGET (window))) + gtk_widget_hide (window->priv->progress_bar); + + if (window->priv->progress_dialog == NULL) + return; + + if (close_now) { + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + real_close_progress_dialog (window); + } + else { + if (window->priv->hide_progress_timeout != 0) + return; + window->priv->hide_progress_timeout = g_timeout_add (HIDE_PROGRESS_TIMEOUT_MSECS, + real_close_progress_dialog, + window); + } +} + + +static gboolean +progress_dialog_delete_event (GtkWidget *caller, + GdkEvent *event, + FrWindow *window) +{ + if (window->priv->stoppable) { + activate_action_stop (NULL, window); + close_progress_dialog (window, TRUE); + } + + return TRUE; +} + + +static void +open_folder (GtkWindow *parent, + const char *folder) +{ + GError *error = NULL; + + if (folder == NULL) + return; + + if (! show_uri (gtk_window_get_screen (parent), folder, GDK_CURRENT_TIME, &error)) { + GtkWidget *d; + char *utf8_name; + char *message; + + utf8_name = g_filename_display_name (folder); + message = g_strdup_printf (_("Could not display the folder \"%s\""), utf8_name); + g_free (utf8_name); + + d = _gtk_error_dialog_new (parent, + GTK_DIALOG_MODAL, + NULL, + message, + "%s", + error->message); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + + g_free (message); + g_clear_error (&error); + } +} + + +static void +fr_window_view_extraction_destination_folder (FrWindow *window) +{ + open_folder (GTK_WINDOW (window), fr_archive_get_last_extraction_destination (window->archive)); +} + + +static void +progress_dialog_response (GtkDialog *dialog, + int response_id, + FrWindow *window) +{ + GtkWidget *new_window; + + switch (response_id) { + case GTK_RESPONSE_CANCEL: + if (window->priv->stoppable) { + activate_action_stop (NULL, window); + close_progress_dialog (window, TRUE); + } + break; + case GTK_RESPONSE_CLOSE: + close_progress_dialog (window, TRUE); + break; + case DIALOG_RESPONSE_OPEN_ARCHIVE: + new_window = fr_window_new (); + gtk_widget_show (new_window); + fr_window_archive_open (FR_WINDOW (new_window), window->priv->convert_data.new_file, GTK_WINDOW (new_window)); + close_progress_dialog (window, TRUE); + break; + case DIALOG_RESPONSE_OPEN_DESTINATION_FOLDER: + fr_window_view_extraction_destination_folder (window); + close_progress_dialog (window, TRUE); + break; + case DIALOG_RESPONSE_QUIT: + fr_window_close (window); + break; + default: + break; + } +} + + +static const char* +get_message_from_action (FrAction action) +{ + char *message = ""; + + switch (action) { + case FR_ACTION_CREATING_NEW_ARCHIVE: + message = _("Creating archive"); + break; + case FR_ACTION_LOADING_ARCHIVE: + message = _("Loading archive"); + break; + case FR_ACTION_LISTING_CONTENT: + message = _("Reading archive"); + break; + case FR_ACTION_DELETING_FILES: + message = _("Deleting files from archive"); + break; + case FR_ACTION_TESTING_ARCHIVE: + message = _("Testing archive"); + break; + case FR_ACTION_GETTING_FILE_LIST: + message = _("Getting the file list"); + break; + case FR_ACTION_COPYING_FILES_FROM_REMOTE: + message = _("Copying the file list"); + break; + case FR_ACTION_ADDING_FILES: + message = _("Adding files to archive"); + break; + case FR_ACTION_EXTRACTING_FILES: + message = _("Extracting files from archive"); + break; + case FR_ACTION_COPYING_FILES_TO_REMOTE: + message = _("Copying the file list"); + break; + case FR_ACTION_CREATING_ARCHIVE: + message = _("Creating archive"); + break; + case FR_ACTION_SAVING_REMOTE_ARCHIVE: + message = _("Saving archive"); + break; + default: + message = ""; + break; + } + + return message; +} + + +static void +progress_dialog__set_last_action (FrWindow *window, + FrAction action) +{ + const char *title; + char *markup; + + window->priv->pd_last_action = action; + title = get_message_from_action (window->priv->pd_last_action); + gtk_window_set_title (GTK_WINDOW (window->priv->progress_dialog), title); + markup = g_markup_printf_escaped ("<span weight=\"bold\" size=\"larger\">%s</span>", title); + gtk_label_set_markup (GTK_LABEL (window->priv->pd_action), markup); + g_free (markup); +} + + +static void +pd_update_archive_name (FrWindow *window) +{ + char *current_archive; + + if (window->priv->convert_data.converting) + current_archive = window->priv->convert_data.new_file; + else if (window->priv->working_archive != NULL) + current_archive = window->priv->working_archive; + else + current_archive = window->priv->archive_uri; + + if (strcmp_null_tolerant (window->priv->pd_last_archive, current_archive) != 0) { + g_free (window->priv->pd_last_archive); + if (current_archive == NULL) { + window->priv->pd_last_archive = NULL; + gtk_label_set_text (GTK_LABEL (window->priv->pd_archive), ""); +#ifdef LOG_PROGRESS + g_print ("archive name > (none)\n"); +#endif + } + else { + char *name; + + window->priv->pd_last_archive = g_strdup (current_archive); + + name = g_uri_display_basename (window->priv->pd_last_archive); + gtk_label_set_text (GTK_LABEL (window->priv->pd_archive), name); +#ifdef LOG_PROGRESS + g_print ("archive name > %s\n", name); +#endif + g_free (name); + } + } +} + + +static gboolean +fr_window_working_archive_cb (FrCommand *command, + const char *archive_filename, + FrWindow *window) +{ + g_free (window->priv->working_archive); + window->priv->working_archive = NULL; + if (archive_filename != NULL) + window->priv->working_archive = g_strdup (archive_filename); + pd_update_archive_name (window); + + return TRUE; +} + + +static gboolean +fr_window_message_cb (FrCommand *command, + const char *msg, + FrWindow *window) +{ + if (window->priv->progress_dialog == NULL) + return TRUE; + + if (msg != NULL) { + while (*msg == ' ') + msg++; + if (*msg == 0) + msg = NULL; + } + + if (msg == NULL) { + gtk_label_set_text (GTK_LABEL (window->priv->pd_message), ""); + } + else { + char *utf8_msg; + + if (! g_utf8_validate (msg, -1, NULL)) + utf8_msg = g_locale_to_utf8 (msg, -1 , 0, 0, 0); + else + utf8_msg = g_strdup (msg); + if (utf8_msg == NULL) + return TRUE; + + if (g_utf8_validate (utf8_msg, -1, NULL)) + gtk_label_set_text (GTK_LABEL (window->priv->pd_message), utf8_msg); +#ifdef LOG_PROGRESS + g_print ("message > %s\n", utf8_msg); +#endif + g_free (utf8_msg); + } + + if (window->priv->convert_data.converting) { + if (window->priv->pd_last_action != FR_ACTION_CREATING_ARCHIVE) + progress_dialog__set_last_action (window, FR_ACTION_CREATING_ARCHIVE); + } + else if (window->priv->pd_last_action != window->priv->action) + progress_dialog__set_last_action (window, window->priv->action); + + pd_update_archive_name (window); + + return TRUE; +} + + +static void +create_the_progress_dialog (FrWindow *window) +{ + GtkWindow *parent; + GtkDialog *d; + GtkWidget *vbox; + GtkWidget *align; + GtkWidget *progress_vbox; + GtkWidget *lbl; + const char *title; + char *markup; + PangoAttrList *attr_list; + + if (window->priv->progress_dialog != NULL) + return; + + if (window->priv->batch_mode) + parent = NULL; + else + parent = GTK_WINDOW (window); + + window->priv->pd_last_action = window->priv->action; + title = get_message_from_action (window->priv->pd_last_action); + window->priv->progress_dialog = gtk_dialog_new_with_buttons (title, + parent, + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL); + + window->priv->pd_quit_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), GTK_STOCK_QUIT, DIALOG_RESPONSE_QUIT); + window->priv->pd_open_archive_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), _("_Open the Archive"), DIALOG_RESPONSE_OPEN_ARCHIVE); + window->priv->pd_open_destination_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), _("_Show the Files"), DIALOG_RESPONSE_OPEN_DESTINATION_FOLDER); + window->priv->pd_close_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE); + window->priv->pd_cancel_button = gtk_dialog_add_button (GTK_DIALOG (window->priv->progress_dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL); + + d = GTK_DIALOG (window->priv->progress_dialog); + gtk_dialog_set_has_separator (d, FALSE); + gtk_window_set_resizable (GTK_WINDOW (d), TRUE); + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_OK); + gtk_window_set_default_size (GTK_WINDOW (d), PROGRESS_DIALOG_DEFAULT_WIDTH, -1); + + /* Main */ + + vbox = gtk_vbox_new (FALSE, 5); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 6); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (d)), vbox, FALSE, FALSE, 10); + + /* action label */ + + lbl = window->priv->pd_action = gtk_label_new (""); + + markup = g_markup_printf_escaped ("<span weight=\"bold\" size=\"larger\">%s</span>", title); + gtk_label_set_markup (GTK_LABEL (lbl), markup); + g_free (markup); + + align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 12, 0, 0); + + gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5); + gtk_misc_set_padding (GTK_MISC (lbl), 0, 0); + gtk_label_set_ellipsize (GTK_LABEL (lbl), PANGO_ELLIPSIZE_END); + + gtk_container_add (GTK_CONTAINER (align), lbl); + gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); + + /* archive name */ + + g_free (window->priv->pd_last_archive); + window->priv->pd_last_archive = NULL; + if (window->priv->archive_uri != NULL) { + GtkWidget *hbox; + char *name; + + hbox = gtk_hbox_new (FALSE, 6); + gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0); + + lbl = gtk_label_new (""); + markup = g_markup_printf_escaped ("<b>%s</b>", _("Archive:")); + gtk_label_set_markup (GTK_LABEL (lbl), markup); + g_free (markup); + gtk_box_pack_start (GTK_BOX (hbox), lbl, FALSE, FALSE, 0); + + window->priv->pd_last_archive = g_strdup (window->priv->archive_uri); + name = g_uri_display_basename (window->priv->pd_last_archive); + lbl = window->priv->pd_archive = gtk_label_new (name); + g_free (name); + + gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5); + gtk_label_set_ellipsize (GTK_LABEL (lbl), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (hbox), lbl, TRUE, TRUE, 0); + } + + /* progress and details */ + + align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0); + gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 6, 0, 0); + + progress_vbox = gtk_vbox_new (FALSE, 6); + gtk_container_add (GTK_CONTAINER (align), progress_vbox); + gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0); + + /* progress bar */ + + window->priv->pd_progress_bar = gtk_progress_bar_new (); + gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (window->priv->pd_progress_bar), ACTIVITY_PULSE_STEP); + gtk_box_pack_start (GTK_BOX (progress_vbox), window->priv->pd_progress_bar, TRUE, TRUE, 0); + + /* details label */ + + lbl = window->priv->pd_message = gtk_label_new (""); + + attr_list = pango_attr_list_new (); + pango_attr_list_insert (attr_list, pango_attr_style_new (PANGO_STYLE_ITALIC)); + gtk_label_set_attributes (GTK_LABEL (lbl), attr_list); + pango_attr_list_unref (attr_list); + + gtk_misc_set_alignment (GTK_MISC (lbl), 0.0, 0.5); + gtk_label_set_ellipsize (GTK_LABEL (lbl), PANGO_ELLIPSIZE_END); + gtk_box_pack_start (GTK_BOX (progress_vbox), lbl, TRUE, TRUE, 0); + + gtk_widget_show_all (vbox); + + /* signals */ + + g_signal_connect (G_OBJECT (window->priv->progress_dialog), + "response", + G_CALLBACK (progress_dialog_response), + window); + g_signal_connect (G_OBJECT (window->priv->progress_dialog), + "delete_event", + G_CALLBACK (progress_dialog_delete_event), + window); +} + + +static gboolean +display_progress_dialog (gpointer data) +{ + FrWindow *window = data; + + if (window->priv->progress_timeout != 0) + g_source_remove (window->priv->progress_timeout); + + if (window->priv->progress_dialog != NULL) { + gtk_dialog_set_response_sensitive (GTK_DIALOG (window->priv->progress_dialog), + GTK_RESPONSE_OK, + window->priv->stoppable); + if (! window->priv->non_interactive) + gtk_widget_show (GTK_WIDGET (window)); + gtk_widget_hide (window->priv->progress_bar); + gtk_widget_show (window->priv->progress_dialog); + fr_window_message_cb (NULL, window->priv->last_status_message, window); + } + + window->priv->progress_timeout = 0; + + return FALSE; +} + + +static void +open_progress_dialog (FrWindow *window, + gboolean open_now) +{ + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + + if (open_now) { + if (window->priv->progress_timeout != 0) + g_source_remove (window->priv->progress_timeout); + window->priv->progress_timeout = 0; + } + + if ((window->priv->progress_timeout != 0) + || ((window->priv->progress_dialog != NULL) && gtk_widget_get_visible (window->priv->progress_dialog))) + return; + + if (! window->priv->batch_mode && ! open_now) + gtk_widget_show (window->priv->progress_bar); + + create_the_progress_dialog (window); + gtk_widget_show (window->priv->pd_cancel_button); + gtk_widget_hide (window->priv->pd_open_archive_button); + gtk_widget_hide (window->priv->pd_open_destination_button); + gtk_widget_hide (window->priv->pd_quit_button); + gtk_widget_hide (window->priv->pd_close_button); + + if (open_now) + display_progress_dialog (window); + else + window->priv->progress_timeout = g_timeout_add (PROGRESS_TIMEOUT_MSECS, + display_progress_dialog, + window); +} + + +static gboolean +fr_window_progress_cb (FrCommand *command, + double fraction, + FrWindow *window) +{ + window->priv->progress_pulse = (fraction < 0.0); + if (! window->priv->progress_pulse) { + fraction = CLAMP (fraction, 0.0, 1.0); + if (window->priv->progress_dialog != NULL) + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->pd_progress_bar), fraction); + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->progress_bar), fraction); +#ifdef LOG_PROGRESS + g_print ("progress > %2.2f\n", fraction); +#endif + } + return TRUE; +} + + +static void +open_progress_dialog_with_open_destination (FrWindow *window) +{ + window->priv->ask_to_open_destination_after_extraction = FALSE; + + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + if (window->priv->progress_timeout != 0) { + g_source_remove (window->priv->progress_timeout); + window->priv->progress_timeout = 0; + } + + create_the_progress_dialog (window); + gtk_widget_hide (window->priv->pd_cancel_button); + gtk_widget_hide (window->priv->pd_open_archive_button); + gtk_widget_show (window->priv->pd_open_destination_button); + gtk_widget_show (window->priv->pd_quit_button); + gtk_widget_show (window->priv->pd_close_button); + display_progress_dialog (window); + fr_window_progress_cb (NULL, 1.0, window); + fr_window_message_cb (NULL, _("Extraction completed successfully"), window); +} + + +static void +open_progress_dialog_with_open_archive (FrWindow *window) +{ + if (window->priv->hide_progress_timeout != 0) { + g_source_remove (window->priv->hide_progress_timeout); + window->priv->hide_progress_timeout = 0; + } + if (window->priv->progress_timeout != 0) { + g_source_remove (window->priv->progress_timeout); + window->priv->progress_timeout = 0; + } + + create_the_progress_dialog (window); + gtk_widget_hide (window->priv->pd_cancel_button); + gtk_widget_hide (window->priv->pd_open_destination_button); + gtk_widget_show (window->priv->pd_open_archive_button); + gtk_widget_show (window->priv->pd_close_button); + display_progress_dialog (window); + fr_window_progress_cb (NULL, 1.0, window); + fr_window_message_cb (NULL, _("Archive created successfully"), window); +} + + +void +fr_window_push_message (FrWindow *window, + const char *msg) +{ + if (! gtk_widget_get_mapped (GTK_WIDGET (window))) + return; + + g_free (window->priv->last_status_message); + window->priv->last_status_message = g_strdup (msg); + + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), window->priv->progress_cid, window->priv->last_status_message); + if (window->priv->progress_dialog != NULL) + gtk_label_set_text (GTK_LABEL (window->priv->pd_message), window->priv->last_status_message); +} + + +void +fr_window_pop_message (FrWindow *window) +{ + if (! gtk_widget_get_mapped (GTK_WIDGET (window))) + return; + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), window->priv->progress_cid); + if (window->priv->progress_dialog != NULL) + gtk_label_set_text (GTK_LABEL (window->priv->pd_message), ""); +} + + +static void +action_started (FrArchive *archive, + FrAction action, + gpointer data) +{ + FrWindow *window = data; + const char *message; + char *full_msg; + + window->priv->action = action; + fr_window_start_activity_mode (window); + +#ifdef DEBUG + debug (DEBUG_INFO, "%s [START] (FR::Window)\n", action_names[action]); +#endif + + message = get_message_from_action (action); + full_msg = g_strdup_printf ("%s, %s", message, _("please wait...")); + fr_window_push_message (window, full_msg); + + switch (action) { + case FR_ACTION_EXTRACTING_FILES: + open_progress_dialog (window, window->priv->ask_to_open_destination_after_extraction || window->priv->convert_data.converting || window->priv->batch_mode); + break; + default: + open_progress_dialog (window, window->priv->batch_mode); + break; + } + + if (archive->command != NULL) { + fr_command_progress (archive->command, -1.0); + fr_command_message (archive->command, message); + } + + g_free (full_msg); +} + + +static void +fr_window_add_to_recent_list (FrWindow *window, + char *uri) +{ + if (window->priv->batch_mode) + return; + + if (is_temp_dir (uri)) + return; + + if (window->archive->content_type != NULL) { + GtkRecentData *recent_data; + + recent_data = g_new0 (GtkRecentData, 1); + recent_data->mime_type = g_content_type_get_mime_type (window->archive->content_type); + recent_data->app_name = "File Roller"; + recent_data->app_exec = "file-roller"; + gtk_recent_manager_add_full (window->priv->recent_manager, uri, recent_data); + + g_free (recent_data); + } + else + gtk_recent_manager_add_item (window->priv->recent_manager, uri); +} + + +static void +fr_window_remove_from_recent_list (FrWindow *window, + char *filename) +{ + if (filename != NULL) + gtk_recent_manager_remove_item (window->priv->recent_manager, filename, NULL); +} + + +static void +error_dialog_response_cb (GtkDialog *dialog, + gint arg1, + gpointer user_data) +{ + FrWindow *window = user_data; + GtkWindow *dialog_parent = window->priv->error_dialog_parent; + + window->priv->showing_error_dialog = FALSE; + window->priv->error_dialog_parent = NULL; + + if ((dialog_parent != NULL) && (gtk_widget_get_toplevel (GTK_WIDGET (dialog_parent)) != (GtkWidget*) dialog_parent)) + gtk_window_set_modal (dialog_parent, TRUE); + gtk_widget_destroy (GTK_WIDGET (dialog)); + if (window->priv->destroy_with_error_dialog) + gtk_widget_destroy (GTK_WIDGET (window)); +} + + +static void +fr_window_show_error_dialog (FrWindow *window, + GtkWidget *dialog, + GtkWindow *dialog_parent) +{ + close_progress_dialog (window, TRUE); + + if (dialog_parent != NULL) + gtk_window_set_modal (dialog_parent, FALSE); + g_signal_connect (dialog, + "response", + (window->priv->batch_mode) ? G_CALLBACK (gtk_main_quit) : G_CALLBACK (error_dialog_response_cb), + window); + gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); + gtk_widget_show (dialog); + + window->priv->showing_error_dialog = TRUE; + window->priv->error_dialog_parent = dialog_parent; +} + + +void +fr_window_destroy_with_error_dialog (FrWindow *window) +{ + window->priv->destroy_with_error_dialog = TRUE; +} + + +static gboolean +handle_errors (FrWindow *window, + FrArchive *archive, + FrAction action, + FrProcError *error) +{ + if (error->type == FR_PROC_ERROR_ASK_PASSWORD) { + close_progress_dialog (window, TRUE); + dlg_ask_password (window); + return FALSE; + } + else if (error->type == FR_PROC_ERROR_UNSUPPORTED_FORMAT) { + close_progress_dialog (window, TRUE); + dlg_package_installer (window, archive, action); + return FALSE; + } +#if 0 + else if (error->type == FR_PROC_ERROR_BAD_CHARSET) { + close_progress_dialog (window, TRUE); + /* dlg_ask_archive_charset (window); FIXME: implement after feature freeze */ + return FALSE; + } +#endif + else if (error->type == FR_PROC_ERROR_STOPPED) { + /* nothing */ + } + else if (error->type != FR_PROC_ERROR_NONE) { + char *msg = NULL; + char *utf8_name; + char *details = NULL; + GtkWindow *dialog_parent; + GtkWidget *dialog; + FrProcess *process = archive->process; + GList *output = NULL; + + if (window->priv->batch_mode) { + dialog_parent = NULL; + window->priv->load_error_parent_window = NULL; + } + else { + dialog_parent = (GtkWindow *) window; + if (window->priv->load_error_parent_window == NULL) + window->priv->load_error_parent_window = (GtkWindow *) window; + } + + if ((action == FR_ACTION_LISTING_CONTENT) || (action == FR_ACTION_LOADING_ARCHIVE)) + fr_window_archive_close (window); + + switch (action) { + case FR_ACTION_CREATING_NEW_ARCHIVE: + dialog_parent = window->priv->load_error_parent_window; + msg = _("Could not create the archive"); + break; + + case FR_ACTION_EXTRACTING_FILES: + case FR_ACTION_COPYING_FILES_TO_REMOTE: + msg = _("An error occurred while extracting files."); + break; + + case FR_ACTION_LOADING_ARCHIVE: + dialog_parent = window->priv->load_error_parent_window; + utf8_name = g_uri_display_basename (window->priv->archive_uri); + msg = g_strdup_printf (_("Could not open \"%s\""), utf8_name); + g_free (utf8_name); + break; + + case FR_ACTION_LISTING_CONTENT: + msg = _("An error occurred while loading the archive."); + break; + + case FR_ACTION_DELETING_FILES: + msg = _("An error occurred while deleting files from the archive."); + break; + + case FR_ACTION_ADDING_FILES: + case FR_ACTION_GETTING_FILE_LIST: + case FR_ACTION_COPYING_FILES_FROM_REMOTE: + msg = _("An error occurred while adding files to the archive."); + break; + + case FR_ACTION_TESTING_ARCHIVE: + msg = _("An error occurred while testing archive."); + break; + + case FR_ACTION_SAVING_REMOTE_ARCHIVE: + msg = _("An error occurred while saving the archive."); + break; + + default: + msg = _("An error occurred."); + break; + } + + switch (error->type) { + case FR_PROC_ERROR_COMMAND_NOT_FOUND: + details = _("Command not found."); + break; + case FR_PROC_ERROR_EXITED_ABNORMALLY: + details = _("Command exited abnormally."); + break; + case FR_PROC_ERROR_SPAWN: + details = error->gerror->message; + break; + default: + if (error->gerror != NULL) + details = error->gerror->message; + else + details = NULL; + break; + } + + if (error->type != FR_PROC_ERROR_GENERIC) + output = (process->err.raw != NULL) ? process->err.raw : process->out.raw; + + dialog = _gtk_error_dialog_new (dialog_parent, + 0, + output, + msg, + ((details != NULL) ? "%s" : NULL), + details); + fr_window_show_error_dialog (window, dialog, dialog_parent); + + return FALSE; + } + + return TRUE; +} + + +static void +convert__action_performed (FrArchive *archive, + FrAction action, + FrProcError *error, + gpointer data) +{ + FrWindow *window = data; + +#ifdef DEBUG + debug (DEBUG_INFO, "%s [CONVERT::DONE] (FR::Window)\n", action_names[action]); +#endif + + if ((action == FR_ACTION_GETTING_FILE_LIST) || (action == FR_ACTION_ADDING_FILES)) { + fr_window_stop_activity_mode (window); + fr_window_pop_message (window); + close_progress_dialog (window, FALSE); + } + + if (action != FR_ACTION_ADDING_FILES) + return; + + handle_errors (window, archive, action, error); + + if (error->type == FR_PROC_ERROR_NONE) + open_progress_dialog_with_open_archive (window); + + remove_local_directory (window->priv->convert_data.temp_dir); + fr_window_convert_data_free (window, FALSE); + + fr_window_update_sensitivity (window); + fr_window_update_statusbar_list_info (window); +} + + +static void fr_window_exec_next_batch_action (FrWindow *window); + + +static void +action_performed (FrArchive *archive, + FrAction action, + FrProcError *error, + gpointer data) +{ + FrWindow *window = data; + gboolean continue_batch = FALSE; + char *archive_dir; + gboolean temp_dir; + +#ifdef DEBUG + debug (DEBUG_INFO, "%s [DONE] (FR::Window)\n", action_names[action]); +#endif + + fr_window_stop_activity_mode (window); + fr_window_pop_message (window); + + continue_batch = handle_errors (window, archive, action, error); + + if ((error->type == FR_PROC_ERROR_ASK_PASSWORD) + || (error->type == FR_PROC_ERROR_UNSUPPORTED_FORMAT) + /*|| (error->type == FR_PROC_ERROR_BAD_CHARSET)*/) + { + return; + } + + switch (action) { + case FR_ACTION_CREATING_NEW_ARCHIVE: + case FR_ACTION_CREATING_ARCHIVE: + close_progress_dialog (window, FALSE); + if (error->type != FR_PROC_ERROR_STOPPED) { + fr_window_history_clear (window); + fr_window_go_to_location (window, "/", TRUE); + fr_window_update_dir_tree (window); + fr_window_update_title (window); + fr_window_update_sensitivity (window); + } + break; + + case FR_ACTION_LOADING_ARCHIVE: + close_progress_dialog (window, FALSE); + if (error->type != FR_PROC_ERROR_NONE) { + fr_window_remove_from_recent_list (window, window->priv->archive_uri); + if (window->priv->non_interactive) { + fr_window_archive_close (window); + fr_window_stop_batch (window); + } + } + else { + fr_window_add_to_recent_list (window, window->priv->archive_uri); + if (! window->priv->non_interactive) + gtk_window_present (GTK_WINDOW (window)); + } + continue_batch = FALSE; + g_signal_emit (window, + fr_window_signals[ARCHIVE_LOADED], + 0, + error->type == FR_PROC_ERROR_NONE); + break; + + case FR_ACTION_LISTING_CONTENT: + /* update the uri because multi-volume archives can have + * a different name after loading. */ + g_free (window->priv->archive_uri); + window->priv->archive_uri = g_file_get_uri (window->archive->file); + + close_progress_dialog (window, FALSE); + if (error->type != FR_PROC_ERROR_NONE) { + fr_window_remove_from_recent_list (window, window->priv->archive_uri); + fr_window_archive_close (window); + fr_window_set_password (window, NULL); + break; + } + + archive_dir = remove_level_from_path (window->priv->archive_uri); + temp_dir = is_temp_dir (archive_dir); + if (! window->priv->archive_present) { + window->priv->archive_present = TRUE; + + fr_window_history_clear (window); + fr_window_history_add (window, "/"); + + if (! temp_dir) { + fr_window_set_open_default_dir (window, archive_dir); + fr_window_set_add_default_dir (window, archive_dir); + if (! window->priv->freeze_default_dir) + fr_window_set_extract_default_dir (window, archive_dir, FALSE); + } + + window->priv->archive_new = FALSE; + } + g_free (archive_dir); + + if (! temp_dir) + fr_window_add_to_recent_list (window, window->priv->archive_uri); + + fr_window_update_title (window); + fr_window_go_to_location (window, fr_window_get_current_location (window), TRUE); + fr_window_update_dir_tree (window); + if (! window->priv->batch_mode && window->priv->non_interactive) + gtk_window_present (GTK_WINDOW (window)); + break; + + case FR_ACTION_DELETING_FILES: + close_progress_dialog (window, FALSE); + fr_window_archive_reload (window); + return; + + case FR_ACTION_ADDING_FILES: + close_progress_dialog (window, FALSE); + + /* update the uri because multi-volume archives can have + * a different name after creation. */ + g_free (window->priv->archive_uri); + window->priv->archive_uri = g_file_get_uri (window->archive->file); + + if (error->type == FR_PROC_ERROR_NONE) { + if (window->priv->archive_new) + window->priv->archive_new = FALSE; + fr_window_add_to_recent_list (window, window->priv->archive_uri); + } + if (! window->priv->batch_mode) { + fr_window_archive_reload (window); + return; + } + break; + + case FR_ACTION_TESTING_ARCHIVE: + close_progress_dialog (window, FALSE); + if (error->type == FR_PROC_ERROR_NONE) + fr_window_view_last_output (window, _("Test Result")); + return; + + case FR_ACTION_EXTRACTING_FILES: + if (error->type != FR_PROC_ERROR_NONE) { + if (window->priv->convert_data.converting) { + remove_local_directory (window->priv->convert_data.temp_dir); + fr_window_convert_data_free (window, TRUE); + } + break; + } + if (window->priv->convert_data.converting) { + char *source_dir; + + source_dir = g_filename_to_uri (window->priv->convert_data.temp_dir, NULL, NULL); + fr_archive_add_with_wildcard ( + window->priv->convert_data.new_archive, + "*", + NULL, + NULL, + source_dir, + NULL, + FALSE, + TRUE, + window->priv->convert_data.password, + window->priv->convert_data.encrypt_header, + window->priv->compression, + window->priv->convert_data.volume_size); + g_free (source_dir); + } + else { + if (window->priv->ask_to_open_destination_after_extraction) + open_progress_dialog_with_open_destination (window); + else + close_progress_dialog (window, FALSE); + } + break; + + default: + close_progress_dialog (window, FALSE); + continue_batch = FALSE; + break; + } + + if (window->priv->batch_action == NULL) { + fr_window_update_sensitivity (window); + fr_window_update_statusbar_list_info (window); + } + + if (continue_batch) { + if (error->type != FR_PROC_ERROR_NONE) + fr_window_stop_batch (window); + else + fr_window_exec_next_batch_action (window); + } +} + + +/* -- selections -- */ + + +static GList * +get_dir_list_from_path (FrWindow *window, + char *path) +{ + char *dirname; + int dirname_l; + GList *list = NULL; + int i; + + if (path[strlen (path) - 1] != '/') + dirname = g_strconcat (path, "/", NULL); + else + dirname = g_strdup (path); + dirname_l = strlen (dirname); + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fd = g_ptr_array_index (window->archive->command->files, i); + + if (strncmp (dirname, fd->full_path, dirname_l) == 0) + list = g_list_prepend (list, g_strdup (fd->original_path)); + } + g_free (dirname); + + return g_list_reverse (list); +} + + +static GList * +get_dir_list_from_file_data (FrWindow *window, + FileData *fdata) +{ + char *dirname; + GList *list; + + dirname = g_strconcat (fr_window_get_current_location (window), + fdata->list_name, + NULL); + list = get_dir_list_from_path (window, dirname); + g_free (dirname); + + return list; +} + + +GList * +fr_window_get_file_list_selection (FrWindow *window, + gboolean recursive, + gboolean *has_dirs) +{ + GtkTreeSelection *selection; + GList *selections = NULL, *list, *scan; + + g_return_val_if_fail (window != NULL, NULL); + + if (has_dirs != NULL) + *has_dirs = FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return NULL; + gtk_tree_selection_selected_foreach (selection, add_selected_from_list_view, &selections); + + list = NULL; + for (scan = selections; scan; scan = scan->next) { + FileData *fd = scan->data; + + if (!fd) + continue; + + if (file_data_is_dir (fd)) { + if (has_dirs != NULL) + *has_dirs = TRUE; + + if (recursive) + list = g_list_concat (list, get_dir_list_from_file_data (window, fd)); + } + else + list = g_list_prepend (list, g_strdup (fd->original_path)); + } + if (selections) + g_list_free (selections); + + return g_list_reverse (list); +} + + +GList * +fr_window_get_folder_tree_selection (FrWindow *window, + gboolean recursive, + gboolean *has_dirs) +{ + GtkTreeSelection *tree_selection; + GList *selections, *list, *scan; + + g_return_val_if_fail (window != NULL, NULL); + + if (has_dirs != NULL) + *has_dirs = FALSE; + + tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + if (tree_selection == NULL) + return NULL; + + selections = NULL; + gtk_tree_selection_selected_foreach (tree_selection, add_selected_from_tree_view, &selections); + if (selections == NULL) + return NULL; + + if (has_dirs != NULL) + *has_dirs = TRUE; + + list = NULL; + for (scan = selections; scan; scan = scan->next) { + char *path = scan->data; + + if (recursive) + list = g_list_concat (list, get_dir_list_from_path (window, path)); + } + path_list_free (selections); + + return g_list_reverse (list); +} + + +GList * +fr_window_get_file_list_from_path_list (FrWindow *window, + GList *path_list, + gboolean *has_dirs) +{ + GtkTreeModel *model; + GList *selections, *list, *scan; + + g_return_val_if_fail (window != NULL, NULL); + + model = GTK_TREE_MODEL (window->priv->list_store); + selections = NULL; + + if (has_dirs != NULL) + *has_dirs = FALSE; + + for (scan = path_list; scan; scan = scan->next) { + GtkTreeRowReference *reference = scan->data; + GtkTreePath *path; + GtkTreeIter iter; + FileData *fdata; + + path = gtk_tree_row_reference_get_path (reference); + if (path == NULL) + continue; + + if (! gtk_tree_model_get_iter (model, &iter, path)) + continue; + + gtk_tree_model_get (model, &iter, + COLUMN_FILE_DATA, &fdata, + -1); + + selections = g_list_prepend (selections, fdata); + } + + list = NULL; + for (scan = selections; scan; scan = scan->next) { + FileData *fd = scan->data; + + if (!fd) + continue; + + if (file_data_is_dir (fd)) { + if (has_dirs != NULL) + *has_dirs = TRUE; + list = g_list_concat (list, get_dir_list_from_file_data (window, fd)); + } + else + list = g_list_prepend (list, g_strdup (fd->original_path)); + } + + if (selections != NULL) + g_list_free (selections); + + return g_list_reverse (list); +} + + +GList * +fr_window_get_file_list_pattern (FrWindow *window, + const char *pattern) +{ + GRegex **regexps; + GList *list; + int i; + + g_return_val_if_fail (window != NULL, NULL); + + regexps = search_util_get_regexps (pattern, G_REGEX_CASELESS); + list = NULL; + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fd = g_ptr_array_index (window->archive->command->files, i); + char *utf8_name; + + /* FIXME: only files in the current location ? */ + + if (fd == NULL) + continue; + + utf8_name = g_filename_to_utf8 (fd->name, -1, NULL, NULL, NULL); + if (match_regexps (regexps, utf8_name, 0)) + list = g_list_prepend (list, g_strdup (fd->original_path)); + g_free (utf8_name); + } + free_regexps (regexps); + + return g_list_reverse (list); +} + + +int +fr_window_get_n_selected_files (FrWindow *window) +{ + return _gtk_count_selected (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view))); +} + + +/**/ + + +static int +dir_tree_button_press_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + FrWindow *window = data; + GtkTreeSelection *selection; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->tree_view))) + return FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + if (selection == NULL) + return FALSE; + + if ((event->type == GDK_BUTTON_PRESS) && (event->button == 3)) { + GtkTreePath *path; + GtkTreeIter iter; + + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->tree_view), + event->x, event->y, + &path, NULL, NULL, NULL)) { + + if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->tree_store), &iter, path)) { + gtk_tree_path_free (path); + return FALSE; + } + gtk_tree_path_free (path); + + if (! gtk_tree_selection_iter_is_selected (selection, &iter)) { + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_iter (selection, &iter); + } + + gtk_menu_popup (GTK_MENU (window->priv->sidebar_folder_popup_menu), + NULL, NULL, NULL, + window, + event->button, + event->time); + } + else + gtk_tree_selection_unselect_all (selection); + + return TRUE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 8)) { + fr_window_go_back (window); + return TRUE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 9)) { + fr_window_go_forward (window); + return TRUE; + } + + return FALSE; +} + + +static FileData * +fr_window_get_selected_item_from_file_list (FrWindow *window) +{ + GtkTreeSelection *tree_selection; + GList *selection; + FileData *fdata = NULL; + + g_return_val_if_fail (window != NULL, NULL); + + tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (tree_selection == NULL) + return NULL; + + selection = NULL; + gtk_tree_selection_selected_foreach (tree_selection, add_selected_from_list_view, &selection); + if ((selection == NULL) || (selection->next != NULL)) { + /* return NULL if the selection contains more than one entry. */ + g_list_free (selection); + return NULL; + } + + fdata = file_data_copy (selection->data); + g_list_free (selection); + + return fdata; +} + + +static char * +fr_window_get_selected_folder_in_tree_view (FrWindow *window) +{ + GtkTreeSelection *tree_selection; + GList *selections; + char *path = NULL; + + g_return_val_if_fail (window != NULL, NULL); + + tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + if (tree_selection == NULL) + return NULL; + + selections = NULL; + gtk_tree_selection_selected_foreach (tree_selection, add_selected_from_tree_view, &selections); + + if (selections != NULL) { + path = selections->data; + g_list_free (selections); + } + + return path; +} + + +void +fr_window_current_folder_activated (FrWindow *window, + gboolean from_sidebar) +{ + char *dir_path; + + if (! from_sidebar) { + FileData *fdata; + char *dir_name; + + fdata = fr_window_get_selected_item_from_file_list (window); + if ((fdata == NULL) || ! file_data_is_dir (fdata)) { + file_data_free (fdata); + return; + } + dir_name = g_strdup (fdata->list_name); + dir_path = g_strconcat (fr_window_get_current_location (window), + dir_name, + "/", + NULL); + g_free (dir_name); + file_data_free (fdata); + } + else + dir_path = fr_window_get_selected_folder_in_tree_view (window); + + fr_window_go_to_location (window, dir_path, FALSE); + + g_free (dir_path); +} + + +static gboolean +row_activated_cb (GtkTreeView *tree_view, + GtkTreePath *path, + GtkTreeViewColumn *column, + gpointer data) +{ + FrWindow *window = data; + FileData *fdata; + GtkTreeIter iter; + + if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), + &iter, + path)) + return FALSE; + + gtk_tree_model_get (GTK_TREE_MODEL (window->priv->list_store), &iter, + COLUMN_FILE_DATA, &fdata, + -1); + + if (! file_data_is_dir (fdata)) { + GList *list = g_list_prepend (NULL, fdata->original_path); + fr_window_open_files (window, list, FALSE); + g_list_free (list); + } + else if (window->priv->list_mode == FR_WINDOW_LIST_MODE_AS_DIR) { + char *new_dir; + new_dir = g_strconcat (fr_window_get_current_location (window), + fdata->list_name, + "/", + NULL); + fr_window_go_to_location (window, new_dir, FALSE); + g_free (new_dir); + } + + return FALSE; +} + + +static int +file_button_press_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + FrWindow *window = data; + GtkTreeSelection *selection; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->list_view))) + return FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return FALSE; + + if (window->priv->path_clicked != NULL) { + gtk_tree_path_free (window->priv->path_clicked); + window->priv->path_clicked = NULL; + } + + if ((event->type == GDK_BUTTON_PRESS) && (event->button == 3)) { + GtkTreePath *path; + GtkTreeIter iter; + int n_selected; + + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view), + event->x, event->y, + &path, NULL, NULL, NULL)) { + + if (! gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), &iter, path)) { + gtk_tree_path_free (path); + return FALSE; + } + gtk_tree_path_free (path); + + if (! gtk_tree_selection_iter_is_selected (selection, &iter)) { + gtk_tree_selection_unselect_all (selection); + gtk_tree_selection_select_iter (selection, &iter); + } + } + else + gtk_tree_selection_unselect_all (selection); + + n_selected = fr_window_get_n_selected_files (window); + if ((n_selected == 1) && selection_has_a_dir (window)) + gtk_menu_popup (GTK_MENU (window->priv->folder_popup_menu), + NULL, NULL, NULL, + window, + event->button, + event->time); + else + gtk_menu_popup (GTK_MENU (window->priv->file_popup_menu), + NULL, NULL, NULL, + window, + event->button, + event->time); + return TRUE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 1)) { + GtkTreePath *path = NULL; + + if (! gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view), + event->x, event->y, + &path, NULL, NULL, NULL)) { + gtk_tree_selection_unselect_all (selection); + } + + if (window->priv->path_clicked != NULL) { + gtk_tree_path_free (window->priv->path_clicked); + window->priv->path_clicked = NULL; + } + + if (path != NULL) { + window->priv->path_clicked = gtk_tree_path_copy (path); + gtk_tree_path_free (path); + } + + return FALSE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 8)) { + // go back + fr_window_go_back (window); + return TRUE; + } + else if ((event->type == GDK_BUTTON_PRESS) && (event->button == 9)) { + // go forward + fr_window_go_forward (window); + return TRUE; + } + + return FALSE; +} + + +static int +file_button_release_cb (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + FrWindow *window = data; + GtkTreeSelection *selection; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->list_view))) + return FALSE; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return FALSE; + + if (window->priv->path_clicked == NULL) + return FALSE; + + if ((event->type == GDK_BUTTON_RELEASE) + && (event->button == 1) + && (window->priv->path_clicked != NULL)) { + GtkTreePath *path = NULL; + + if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (window->priv->list_view), + event->x, event->y, + &path, NULL, NULL, NULL)) { + + if ((gtk_tree_path_compare (window->priv->path_clicked, path) == 0) + && window->priv->single_click + && ! ((event->state & GDK_CONTROL_MASK) || (event->state & GDK_SHIFT_MASK))) { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (widget), + path, + NULL, + FALSE); + gtk_tree_view_row_activated (GTK_TREE_VIEW (widget), + path, + NULL); + } + } + + if (path != NULL) + gtk_tree_path_free (path); + } + + if (window->priv->path_clicked != NULL) { + gtk_tree_path_free (window->priv->path_clicked); + window->priv->path_clicked = NULL; + } + + return FALSE; +} + + +static gboolean +file_motion_notify_callback (GtkWidget *widget, + GdkEventMotion *event, + gpointer user_data) +{ + FrWindow *window = user_data; + GdkCursor *cursor; + GtkTreePath *last_hover_path; + GtkTreeIter iter; + + if (! window->priv->single_click) + return FALSE; + + if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->list_view))) + return FALSE; + + last_hover_path = window->priv->list_hover_path; + + gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), + event->x, event->y, + &window->priv->list_hover_path, + NULL, NULL, NULL); + + if (window->priv->list_hover_path != NULL) + cursor = gdk_cursor_new (GDK_HAND2); + else + cursor = NULL; + + gdk_window_set_cursor (event->window, cursor); + + /* only redraw if the hover row has changed */ + if (!(last_hover_path == NULL && window->priv->list_hover_path == NULL) && + (!(last_hover_path != NULL && window->priv->list_hover_path != NULL) || + gtk_tree_path_compare (last_hover_path, window->priv->list_hover_path))) + { + if (last_hover_path) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), + &iter, last_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (window->priv->list_store), + last_hover_path, &iter); + } + + if (window->priv->list_hover_path) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), + &iter, window->priv->list_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (window->priv->list_store), + window->priv->list_hover_path, &iter); + } + } + + gtk_tree_path_free (last_hover_path); + + return FALSE; +} + + +static gboolean +file_leave_notify_callback (GtkWidget *widget, + GdkEventCrossing *event, + gpointer user_data) +{ + FrWindow *window = user_data; + GtkTreeIter iter; + + if (window->priv->single_click && (window->priv->list_hover_path != NULL)) { + gtk_tree_model_get_iter (GTK_TREE_MODEL (window->priv->list_store), + &iter, + window->priv->list_hover_path); + gtk_tree_model_row_changed (GTK_TREE_MODEL (window->priv->list_store), + window->priv->list_hover_path, + &iter); + + gtk_tree_path_free (window->priv->list_hover_path); + window->priv->list_hover_path = NULL; + } + + return FALSE; +} + + +/* -- drag and drop -- */ + + +static GList * +get_uri_list_from_selection_data (char *uri_list) +{ + GList *list = NULL; + char **uris; + int i; + + if (uri_list == NULL) + return NULL; + + uris = g_uri_list_extract_uris (uri_list); + for (i = 0; uris[i] != NULL; i++) + list = g_list_prepend (list, g_strdup (uris[i])); + g_strfreev (uris); + + return g_list_reverse (list); +} + + +static gboolean +fr_window_drag_motion (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + guint time, + gpointer user_data) +{ + FrWindow *window = user_data; + + if ((gtk_drag_get_source_widget (context) == window->priv->list_view) + || (gtk_drag_get_source_widget (context) == window->priv->tree_view)) + { + gdk_drag_status (context, 0, time); + return FALSE; + } + + return TRUE; +} + + +static void fr_window_paste_from_clipboard_data (FrWindow *window, FrClipboardData *data); + + +static FrClipboardData* +get_clipboard_data_from_selection_data (FrWindow *window, + const char *data) +{ + FrClipboardData *clipboard_data; + char **uris; + int i; + + clipboard_data = fr_clipboard_data_new (); + + uris = g_strsplit (data, "\r\n", -1); + + clipboard_data->archive_filename = g_strdup (uris[0]); + if (window->priv->password_for_paste != NULL) + clipboard_data->archive_password = g_strdup (window->priv->password_for_paste); + else if (strcmp (uris[1], "") != 0) + clipboard_data->archive_password = g_strdup (uris[1]); + clipboard_data->op = (strcmp (uris[2], "copy") == 0) ? FR_CLIPBOARD_OP_COPY : FR_CLIPBOARD_OP_CUT; + clipboard_data->base_dir = g_strdup (uris[3]); + for (i = 4; uris[i] != NULL; i++) + if (uris[i][0] != '\0') + clipboard_data->files = g_list_prepend (clipboard_data->files, g_strdup (uris[i])); + clipboard_data->files = g_list_reverse (clipboard_data->files); + + g_strfreev (uris); + + return clipboard_data; +} + + +static void +fr_window_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer extra_data) +{ + FrWindow *window = extra_data; + GList *list; + gboolean one_file; + gboolean is_an_archive; + + debug (DEBUG_INFO, "::DragDataReceived -->\n"); + + if ((gtk_drag_get_source_widget (context) == window->priv->list_view) + || (gtk_drag_get_source_widget (context) == window->priv->tree_view)) + { + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + + if (! ((gtk_selection_data_get_length (data) >= 0) && (gtk_selection_data_get_format (data) == 8))) { + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + + if (window->priv->activity_ref > 0) { + gtk_drag_finish (context, FALSE, FALSE, time); + return; + } + + gtk_drag_finish (context, TRUE, FALSE, time); + + if (gtk_selection_data_get_target (data) == XFR_ATOM) { + FrClipboardData *dnd_data; + + dnd_data = get_clipboard_data_from_selection_data (window, (char*) gtk_selection_data_get_data (data)); + dnd_data->current_dir = g_strdup (fr_window_get_current_location (window)); + fr_window_paste_from_clipboard_data (window, dnd_data); + + return; + } + + list = get_uri_list_from_selection_data ((char*) gtk_selection_data_get_data (data)); + if (list == NULL) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + NULL, + _("Could not perform the operation"), + NULL); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy(d); + + return; + } + + one_file = (list->next == NULL); + if (one_file) + is_an_archive = uri_is_archive (list->data); + else + is_an_archive = FALSE; + + if (window->priv->archive_present + && (window->archive != NULL) + && ! window->archive->read_only + && ! window->archive->is_compressed_file) + { + if (one_file && is_an_archive) { + GtkWidget *d; + gint r; + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + _("Do you want to add this file to the current archive or open it as a new archive?"), + NULL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_ADD, 0, + GTK_STOCK_OPEN, 1, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), 2); + + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + if (r == 0) /* Add */ + fr_window_archive_add_dropped_items (window, list, FALSE); + else if (r == 1) /* Open */ + fr_window_archive_open (window, list->data, GTK_WINDOW (window)); + } + else + fr_window_archive_add_dropped_items (window, list, FALSE); + } + else { + if (one_file && is_an_archive) + fr_window_archive_open (window, list->data, GTK_WINDOW (window)); + else { + GtkWidget *d; + int r; + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + _("Do you want to create a new archive with these files?"), + NULL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Create _Archive"), GTK_RESPONSE_YES, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES); + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + if (r == GTK_RESPONSE_YES) { + char *first_item; + char *folder; + char *local_path = NULL; + char *utf8_path = NULL; + const char *archive_name; + + fr_window_free_batch_data (window); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_ADD, + path_list_dup (list), + (GFreeFunc) path_list_free); + + first_item = (char*) list->data; + folder = remove_level_from_path (first_item); + if (folder != NULL) + fr_window_set_open_default_dir (window, folder); + + if ((list->next != NULL) && (folder != NULL)) { + archive_name = file_name_from_path (folder); + } + else { + if (uri_is_local (first_item)) { + local_path = g_filename_from_uri (first_item, NULL, NULL); + if (local_path) + utf8_path = g_filename_to_utf8 (local_path, -1, NULL, NULL, NULL); + if (!utf8_path) + utf8_path= g_strdup (first_item); + g_free (local_path); + } + else { + utf8_path = g_strdup (first_item); + } + archive_name = file_name_from_path (utf8_path); + } + + show_new_archive_dialog (window, archive_name); + g_free (utf8_path); + + g_free (folder); + } + } + } + + path_list_free (list); + + debug (DEBUG_INFO, "::DragDataReceived <--\n"); +} + + +static gboolean +file_list_drag_begin (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + FrWindow *window = data; + + debug (DEBUG_INFO, "::DragBegin -->\n"); + + if (window->priv->activity_ref > 0) + return FALSE; + + g_free (window->priv->drag_destination_folder); + window->priv->drag_destination_folder = NULL; + + g_free (window->priv->drag_base_dir); + window->priv->drag_base_dir = NULL; + + gdk_property_change (gdk_drag_context_get_source_window (context), + XDS_ATOM, TEXT_ATOM, + 8, GDK_PROP_MODE_REPLACE, + (guchar *) XDS_FILENAME, + strlen (XDS_FILENAME)); + + return TRUE; +} + + +static void +file_list_drag_end (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + FrWindow *window = data; + + debug (DEBUG_INFO, "::DragEnd -->\n"); + + gdk_property_delete (gdk_drag_context_get_source_window (context), XDS_ATOM); + + if (window->priv->drag_error != NULL) { + _gtk_error_dialog_run (GTK_WINDOW (window), + _("Extraction not performed"), + "%s", + window->priv->drag_error->message); + g_clear_error (&window->priv->drag_error); + } + else if (window->priv->drag_destination_folder != NULL) { + fr_window_archive_extract (window, + window->priv->drag_file_list, + window->priv->drag_destination_folder, + window->priv->drag_base_dir, + FALSE, + TRUE, + FALSE, + FALSE); + path_list_free (window->priv->drag_file_list); + window->priv->drag_file_list = NULL; + } + + debug (DEBUG_INFO, "::DragEnd <--\n"); +} + + +/* The following three functions taken from bugzilla + * (http://bugzilla.mate.org/attachment.cgi?id=49362&action=view) + * Author: Christian Neumair + * Copyright: 2005 Free Software Foundation, Inc + * License: GPL */ +static char * +get_xds_atom_value (GdkDragContext *context) +{ + char *ret; + + g_return_val_if_fail (context != NULL, NULL); + g_return_val_if_fail (gdk_drag_context_get_source_window (context) != NULL, NULL); + + if (gdk_property_get (gdk_drag_context_get_source_window (context), + XDS_ATOM, TEXT_ATOM, + 0, MAX_XDS_ATOM_VAL_LEN, + FALSE, NULL, NULL, NULL, + (unsigned char **) &ret)) + return ret; + + return NULL; +} + + +static gboolean +context_offers_target (GdkDragContext *context, + GdkAtom target) +{ + return (g_list_find (gdk_drag_context_list_targets (context), target) != NULL); +} + + +static gboolean +caja_xds_dnd_is_valid_xds_context (GdkDragContext *context) +{ + char *tmp; + gboolean ret; + + g_return_val_if_fail (context != NULL, FALSE); + + tmp = NULL; + if (context_offers_target (context, XDS_ATOM)) { + tmp = get_xds_atom_value (context); + } + + ret = (tmp != NULL); + g_free (tmp); + + return ret; +} + + +static char * +get_selection_data_from_clipboard_data (FrWindow *window, + FrClipboardData *data) +{ + GString *list; + char *local_filename; + GList *scan; + + list = g_string_new (NULL); + + local_filename = g_file_get_uri (window->archive->local_copy); + g_string_append (list, local_filename); + g_free (local_filename); + + g_string_append (list, "\r\n"); + if (window->priv->password != NULL) + g_string_append (list, window->priv->password); + g_string_append (list, "\r\n"); + g_string_append (list, (data->op == FR_CLIPBOARD_OP_COPY) ? "copy" : "cut"); + g_string_append (list, "\r\n"); + g_string_append (list, data->base_dir); + g_string_append (list, "\r\n"); + for (scan = data->files; scan; scan = scan->next) { + g_string_append (list, scan->data); + g_string_append (list, "\r\n"); + } + + return g_string_free (list, FALSE); +} + + +static gboolean +fr_window_folder_tree_drag_data_get (GtkWidget *widget, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + gpointer user_data) +{ + FrWindow *window = user_data; + GList *file_list; + char *destination; + char *destination_folder; + + debug (DEBUG_INFO, "::DragDataGet -->\n"); + + if (window->priv->activity_ref > 0) + return FALSE; + + file_list = fr_window_get_folder_tree_selection (window, TRUE, NULL); + if (file_list == NULL) + return FALSE; + + if (gtk_selection_data_get_target (selection_data) == XFR_ATOM) { + FrClipboardData *tmp; + char *data; + + tmp = fr_clipboard_data_new (); + tmp->files = file_list; + tmp->op = FR_CLIPBOARD_OP_COPY; + tmp->base_dir = g_strdup (fr_window_get_current_location (window)); + + data = get_selection_data_from_clipboard_data (window, tmp); + gtk_selection_data_set (selection_data, XFR_ATOM, 8, (guchar *) data, strlen (data)); + + fr_clipboard_data_unref (tmp); + g_free (data); + + return TRUE; + } + + if (! caja_xds_dnd_is_valid_xds_context (context)) + return FALSE; + + destination = get_xds_atom_value (context); + g_return_val_if_fail (destination != NULL, FALSE); + + destination_folder = remove_level_from_path (destination); + g_free (destination); + + /* check whether the extraction can be performed in the destination + * folder */ + + g_clear_error (&window->priv->drag_error); + + if (! check_permissions (destination_folder, R_OK | W_OK)) { + char *destination_folder_display_name; + + destination_folder_display_name = g_filename_display_name (destination_folder); + window->priv->drag_error = g_error_new (FR_ERROR, 0, _("You don't have the right permissions to extract archives in the folder \"%s\""), destination_folder_display_name); + g_free (destination_folder_display_name); + } + + if (window->priv->drag_error == NULL) { + g_free (window->priv->drag_destination_folder); + g_free (window->priv->drag_base_dir); + path_list_free (window->priv->drag_file_list); + window->priv->drag_destination_folder = g_strdup (destination_folder); + window->priv->drag_base_dir = fr_window_get_selected_folder_in_tree_view (window); + window->priv->drag_file_list = file_list; + } + + g_free (destination_folder); + + /* sends back the response */ + + gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *) ((window->priv->drag_error == NULL) ? "S" : "E"), 1); + + debug (DEBUG_INFO, "::DragDataGet <--\n"); + + return TRUE; +} + + +gboolean +fr_window_file_list_drag_data_get (FrWindow *window, + GdkDragContext *context, + GtkSelectionData *selection_data, + GList *path_list) +{ + char *destination; + char *destination_folder; + + debug (DEBUG_INFO, "::DragDataGet -->\n"); + + if (window->priv->path_clicked != NULL) { + gtk_tree_path_free (window->priv->path_clicked); + window->priv->path_clicked = NULL; + } + + if (window->priv->activity_ref > 0) + return FALSE; + + if (gtk_selection_data_get_target (selection_data) == XFR_ATOM) { + FrClipboardData *tmp; + char *data; + + tmp = fr_clipboard_data_new (); + tmp->files = fr_window_get_file_list_selection (window, TRUE, NULL); + tmp->op = FR_CLIPBOARD_OP_COPY; + tmp->base_dir = g_strdup (fr_window_get_current_location (window)); + + data = get_selection_data_from_clipboard_data (window, tmp); + gtk_selection_data_set (selection_data, XFR_ATOM, 8, (guchar *) data, strlen (data)); + + fr_clipboard_data_unref (tmp); + g_free (data); + + return TRUE; + } + + if (! caja_xds_dnd_is_valid_xds_context (context)) + return FALSE; + + destination = get_xds_atom_value (context); + g_return_val_if_fail (destination != NULL, FALSE); + + destination_folder = remove_level_from_path (destination); + g_free (destination); + + /* check whether the extraction can be performed in the destination + * folder */ + + g_clear_error (&window->priv->drag_error); + + if (! check_permissions (destination_folder, R_OK | W_OK)) { + char *destination_folder_display_name; + + destination_folder_display_name = g_filename_display_name (destination_folder); + window->priv->drag_error = g_error_new (FR_ERROR, 0, _("You don't have the right permissions to extract archives in the folder \"%s\""), destination_folder_display_name); + g_free (destination_folder_display_name); + } + + if (window->priv->drag_error == NULL) { + g_free (window->priv->drag_destination_folder); + g_free (window->priv->drag_base_dir); + path_list_free (window->priv->drag_file_list); + window->priv->drag_destination_folder = g_strdup (destination_folder); + window->priv->drag_base_dir = g_strdup (fr_window_get_current_location (window)); + window->priv->drag_file_list = fr_window_get_file_list_from_path_list (window, path_list, NULL); + } + + g_free (destination_folder); + + /* sends back the response */ + + gtk_selection_data_set (selection_data, gtk_selection_data_get_target (selection_data), 8, (guchar *) ((window->priv->drag_error == NULL) ? "S" : "E"), 1); + + debug (DEBUG_INFO, "::DragDataGet <--\n"); + + return TRUE; +} + + +/* -- window_new -- */ + + +static void +fr_window_deactivate_filter (FrWindow *window) +{ + window->priv->filter_mode = FALSE; + window->priv->list_mode = window->priv->last_list_mode; + + gtk_entry_set_text (GTK_ENTRY (window->priv->filter_entry), ""); + fr_window_update_filter_bar_visibility (window); + + gtk_list_store_clear (window->priv->list_store); + + fr_window_update_columns_visibility (window); + fr_window_update_file_list (window, TRUE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); +} + + +static gboolean +key_press_cb (GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + FrWindow *window = data; + gboolean retval = FALSE; + gboolean alt; + + if (gtk_widget_has_focus (window->priv->location_entry)) + return FALSE; + + if (gtk_widget_has_focus (window->priv->filter_entry)) { + switch (event->keyval) { + case GDK_Escape: + fr_window_deactivate_filter (window); + retval = TRUE; + break; + default: + break; + } + return retval; + } + + alt = (event->state & GDK_MOD1_MASK) == GDK_MOD1_MASK; + + switch (event->keyval) { + case GDK_Escape: + activate_action_stop (NULL, window); + if (window->priv->filter_mode) + fr_window_deactivate_filter (window); + retval = TRUE; + break; + + case GDK_F10: + if (event->state & GDK_SHIFT_MASK) { + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + if (selection == NULL) + return FALSE; + + gtk_menu_popup (GTK_MENU (window->priv->file_popup_menu), + NULL, NULL, NULL, + window, + 3, + GDK_CURRENT_TIME); + retval = TRUE; + } + break; + + case GDK_Up: + case GDK_KP_Up: + if (alt) { + fr_window_go_up_one_level (window); + retval = TRUE; + } + break; + + case GDK_BackSpace: + fr_window_go_up_one_level (window); + retval = TRUE; + break; + + case GDK_Right: + case GDK_KP_Right: + if (alt) { + fr_window_go_forward (window); + retval = TRUE; + } + break; + + case GDK_Left: + case GDK_KP_Left: + if (alt) { + fr_window_go_back (window); + retval = TRUE; + } + break; + + case GDK_Home: + case GDK_KP_Home: + if (alt) { + fr_window_go_to_location (window, "/", FALSE); + retval = TRUE; + } + break; + + default: + break; + } + + return retval; +} + + +static gboolean +dir_tree_selection_changed_cb (GtkTreeSelection *selection, + gpointer user_data) +{ + FrWindow *window = user_data; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, NULL, &iter)) { + char *path; + + gtk_tree_model_get (GTK_TREE_MODEL (window->priv->tree_store), + &iter, + TREE_COLUMN_PATH, &path, + -1); + fr_window_go_to_location (window, path, FALSE); + g_free (path); + } + + return FALSE; +} + + +static gboolean +selection_changed_cb (GtkTreeSelection *selection, + gpointer user_data) +{ + FrWindow *window = user_data; + + fr_window_update_statusbar_list_info (window); + fr_window_update_sensitivity (window); + + return FALSE; +} + + +static void +fr_window_delete_event_cb (GtkWidget *caller, + GdkEvent *event, + FrWindow *window) +{ + fr_window_close (window); +} + + +static gboolean +is_single_click_policy (void) +{ + char *value; + gboolean result; + + value = eel_mateconf_get_string (PREF_CAJA_CLICK_POLICY, "double"); + result = strncmp (value, "single", 6) == 0; + g_free (value); + + return result; +} + + +static void +filename_cell_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + FrWindow *window) +{ + char *text; + GtkTreePath *path; + PangoUnderline underline; + + gtk_tree_model_get (model, iter, + COLUMN_NAME, &text, + -1); + + if (window->priv->single_click) { + path = gtk_tree_model_get_path (model, iter); + + if ((window->priv->list_hover_path == NULL) + || gtk_tree_path_compare (path, window->priv->list_hover_path)) + underline = PANGO_UNDERLINE_NONE; + else + underline = PANGO_UNDERLINE_SINGLE; + + gtk_tree_path_free (path); + } + else + underline = PANGO_UNDERLINE_NONE; + + g_object_set (G_OBJECT (renderer), + "text", text, + "underline", underline, + NULL); + + g_free (text); +} + + +static void +add_dir_tree_columns (FrWindow *window, + GtkTreeView *treeview) +{ + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GValue value = { 0, }; + + /* First column. */ + + column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("Folders")); + + /* icon */ + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", TREE_COLUMN_ICON, + NULL); + + /* name */ + + renderer = gtk_cell_renderer_text_new (); + + g_value_init (&value, PANGO_TYPE_ELLIPSIZE_MODE); + g_value_set_enum (&value, PANGO_ELLIPSIZE_END); + g_object_set_property (G_OBJECT (renderer), "ellipsize", &value); + g_value_unset (&value); + + gtk_tree_view_column_pack_start (column, + renderer, + TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", TREE_COLUMN_NAME, + "weight", TREE_COLUMN_WEIGHT, + NULL); + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + gtk_tree_view_column_set_sort_column_id (column, TREE_COLUMN_NAME); + + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); +} + + +static void +add_file_list_columns (FrWindow *window, + GtkTreeView *treeview) +{ + static char *titles[] = {NC_("File", "Size"), + NC_("File", "Type"), + NC_("File", "Date Modified"), + NC_("File", "Location")}; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + GValue value = { 0, }; + int i, j, w; + + /* First column. */ + + window->priv->filename_column = column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, C_("File", "Name")); + + /* emblem */ + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_end (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", COLUMN_EMBLEM, + NULL); + + /* icon */ + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, + "pixbuf", COLUMN_ICON, + NULL); + + /* name */ + + window->priv->single_click = is_single_click_policy (); + + renderer = gtk_cell_renderer_text_new (); + + g_value_init (&value, PANGO_TYPE_ELLIPSIZE_MODE); + g_value_set_enum (&value, PANGO_ELLIPSIZE_END); + g_object_set_property (G_OBJECT (renderer), "ellipsize", &value); + g_value_unset (&value); + + gtk_tree_view_column_pack_start (column, + renderer, + TRUE); + gtk_tree_view_column_set_attributes (column, renderer, + "text", COLUMN_NAME, + NULL); + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + w = eel_mateconf_get_integer (PREF_NAME_COLUMN_WIDTH, DEFAULT_NAME_COLUMN_WIDTH); + if (w <= 0) + w = DEFAULT_NAME_COLUMN_WIDTH; + gtk_tree_view_column_set_fixed_width (column, w); + gtk_tree_view_column_set_resizable (column, TRUE); + gtk_tree_view_column_set_sort_column_id (column, COLUMN_NAME); + gtk_tree_view_column_set_cell_data_func (column, renderer, + (GtkTreeCellDataFunc) filename_cell_data_func, + window, NULL); + + gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); + + /* Other columns */ + + for (j = 0, i = COLUMN_SIZE; i < NUMBER_OF_COLUMNS; i++, j++) { + GValue value = { 0, }; + + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_(titles[j]), + renderer, + "text", i, + NULL); + + gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_fixed_width (column, OTHER_COLUMNS_WIDTH); + gtk_tree_view_column_set_resizable (column, TRUE); + + gtk_tree_view_column_set_sort_column_id (column, i); + + g_value_init (&value, PANGO_TYPE_ELLIPSIZE_MODE); + g_value_set_enum (&value, PANGO_ELLIPSIZE_END); + g_object_set_property (G_OBJECT (renderer), "ellipsize", &value); + g_value_unset (&value); + + gtk_tree_view_append_column (treeview, column); + } +} + + +static int +name_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_name (&fdata1, &fdata2); +} + + +static int +size_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_size (&fdata1, &fdata2); +} + + +static int +type_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_type (&fdata1, &fdata2); +} + + +static int +time_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_time (&fdata1, &fdata2); +} + + +static int +path_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + FileData *fdata1, *fdata2; + + gtk_tree_model_get (model, a, COLUMN_FILE_DATA, &fdata1, -1); + gtk_tree_model_get (model, b, COLUMN_FILE_DATA, &fdata2, -1); + + return sort_by_path (&fdata1, &fdata2); +} + + +static int +no_sort_column_sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) +{ + return -1; +} + + +static void +sort_column_changed_cb (GtkTreeSortable *sortable, + gpointer user_data) +{ + FrWindow *window = user_data; + GtkSortType order; + int column_id; + + if (! gtk_tree_sortable_get_sort_column_id (sortable, + &column_id, + &order)) + return; + + window->priv->sort_method = get_sort_method_from_column (column_id); + window->priv->sort_type = order; + + /*set_active (window, get_action_from_sort_method (window->priv->sort_method), TRUE); + set_active (window, "SortReverseOrder", (window->priv->sort_type == GTK_SORT_DESCENDING));*/ +} + + +static gboolean +fr_window_show_cb (GtkWidget *widget, + FrWindow *window) +{ + fr_window_update_current_location (window); + + set_active (window, "ViewToolbar", eel_mateconf_get_boolean (PREF_UI_TOOLBAR, TRUE)); + set_active (window, "ViewStatusbar", eel_mateconf_get_boolean (PREF_UI_STATUSBAR, TRUE)); + + window->priv->view_folders = eel_mateconf_get_boolean (PREF_UI_FOLDERS, FALSE); + set_active (window, "ViewFolders", window->priv->view_folders); + + fr_window_update_filter_bar_visibility (window); + + return TRUE; +} + + +/* preferences changes notification callbacks */ + + +static void +pref_history_len_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (window->priv->recent_chooser_menu), eel_mateconf_get_integer (PREF_UI_HISTORY_LEN, MAX_HISTORY_LEN)); + gtk_recent_chooser_set_limit (GTK_RECENT_CHOOSER (window->priv->recent_chooser_toolbar), eel_mateconf_get_integer (PREF_UI_HISTORY_LEN, MAX_HISTORY_LEN)); +} + + +static void +pref_view_toolbar_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + g_return_if_fail (window != NULL); + + fr_window_set_toolbar_visibility (window, mateconf_value_get_bool (mateconf_entry_get_value (entry))); +} + + +static void +pref_view_statusbar_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + fr_window_set_statusbar_visibility (window, mateconf_value_get_bool (mateconf_entry_get_value (entry))); +} + + +static void +pref_view_folders_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + fr_window_set_folders_visibility (window, mateconf_value_get_bool (mateconf_entry_get_value (entry))); +} + + +static void +pref_show_field_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + fr_window_update_columns_visibility (window); +} + + +static void +pref_click_policy_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + GdkWindow *win = gtk_tree_view_get_bin_window (GTK_TREE_VIEW (window->priv->list_view)); + GdkDisplay *display; + + window->priv->single_click = is_single_click_policy (); + + gdk_window_set_cursor (win, NULL); + display = gtk_widget_get_display (GTK_WIDGET (window->priv->list_view)); + if (display != NULL) + gdk_display_flush (display); +} + + +static void gh_unref_pixbuf (gpointer key, + gpointer value, + gpointer user_data); + + +static void +pref_use_mime_icons_changed (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + gpointer user_data) +{ + FrWindow *window = user_data; + + if (pixbuf_hash != NULL) { + g_hash_table_foreach (pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (pixbuf_hash); + pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + if (tree_pixbuf_hash != NULL) { + g_hash_table_foreach (tree_pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (tree_pixbuf_hash); + tree_pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + + fr_window_update_file_list (window, FALSE); + fr_window_update_dir_tree (window); +} + + +static void +theme_changed_cb (GtkIconTheme *theme, FrWindow *window) +{ + int icon_width, icon_height; + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (window)), + FILE_LIST_ICON_SIZE, + &icon_width, &icon_height); + file_list_icon_size = MAX (icon_width, icon_height); + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (window)), + DIR_TREE_ICON_SIZE, + &icon_width, &icon_height); + dir_tree_icon_size = MAX (icon_width, icon_height); + + if (pixbuf_hash != NULL) { + g_hash_table_foreach (pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (pixbuf_hash); + pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + if (tree_pixbuf_hash != NULL) { + g_hash_table_foreach (tree_pixbuf_hash, + gh_unref_pixbuf, + NULL); + g_hash_table_destroy (tree_pixbuf_hash); + tree_pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + } + + fr_window_update_file_list (window, TRUE); + fr_window_update_dir_tree (window); +} + + +static gboolean +fr_window_stoppable_cb (FrCommand *command, + gboolean stoppable, + FrWindow *window) +{ + window->priv->stoppable = stoppable; + set_sensitive (window, "Stop", stoppable); + if (window->priv->progress_dialog != NULL) + gtk_dialog_set_response_sensitive (GTK_DIALOG (window->priv->progress_dialog), + GTK_RESPONSE_OK, + stoppable); + return TRUE; +} + + +static gboolean +fr_window_fake_load (FrArchive *archive, + gpointer data) +{ + /* fake loads are disabled to allow exact progress dialogs (#153281) */ + + return FALSE; + +#if 0 + FrWindow *window = data; + gboolean add_after_opening = FALSE; + gboolean extract_after_opening = FALSE; + GList *scan; + + /* fake loads are used only in batch mode to avoid unnecessary + * archive loadings. */ + + if (! window->priv->batch_mode) + return FALSE; + + /* Check whether there is an ADD or EXTRACT action in the batch list. */ + + for (scan = window->priv->batch_action; scan; scan = scan->next) { + FRBatchAction *action; + + action = (FRBatchAction *) scan->data; + if (action->type == FR_BATCH_ACTION_ADD) { + add_after_opening = TRUE; + break; + } + if ((action->type == FR_BATCH_ACTION_EXTRACT) + || (action->type == FR_BATCH_ACTION_EXTRACT_HERE) + || (action->type == FR_BATCH_ACTION_EXTRACT_INTERACT)) + { + extract_after_opening = TRUE; + break; + } + } + + /* use fake load when in batch mode and the archive type supports all + * of the required features */ + + return (window->priv->batch_mode + && ! (add_after_opening && window->priv->update_dropped_files && ! archive->command->propAddCanUpdate) + && ! (add_after_opening && ! window->priv->update_dropped_files && ! archive->command->propAddCanReplace) + && ! (extract_after_opening && !archive->command->propCanExtractAll)); +#endif +} + + +static gboolean +fr_window_add_is_stoppable (FrArchive *archive, + gpointer data) +{ + FrWindow *window = data; + return window->priv->archive_new; +} + + +static void +menu_item_select_cb (GtkMenuItem *proxy, + FrWindow *window) +{ + GtkAction *action; + char *message; + + action = gtk_activatable_get_related_action (GTK_ACTIVATABLE (proxy)); + g_return_if_fail (action != NULL); + + g_object_get (G_OBJECT (action), "tooltip", &message, NULL); + if (message) { + gtk_statusbar_push (GTK_STATUSBAR (window->priv->statusbar), + window->priv->help_message_cid, message); + g_free (message); + } +} + + +static void +menu_item_deselect_cb (GtkMenuItem *proxy, + FrWindow *window) +{ + gtk_statusbar_pop (GTK_STATUSBAR (window->priv->statusbar), + window->priv->help_message_cid); +} + + +static void +disconnect_proxy_cb (GtkUIManager *manager, + GtkAction *action, + GtkWidget *proxy, + FrWindow *window) +{ + if (GTK_IS_MENU_ITEM (proxy)) { + g_signal_handlers_disconnect_by_func + (proxy, G_CALLBACK (menu_item_select_cb), window); + g_signal_handlers_disconnect_by_func + (proxy, G_CALLBACK (menu_item_deselect_cb), window); + } +} + + +static void +connect_proxy_cb (GtkUIManager *manager, + GtkAction *action, + GtkWidget *proxy, + FrWindow *window) +{ + if (GTK_IS_MENU_ITEM (proxy)) { + g_signal_connect (proxy, "select", + G_CALLBACK (menu_item_select_cb), window); + g_signal_connect (proxy, "deselect", + G_CALLBACK (menu_item_deselect_cb), window); + } +} + + +static void +view_as_radio_action (GtkAction *action, + GtkRadioAction *current, + gpointer data) +{ + FrWindow *window = data; + fr_window_set_list_mode (window, gtk_radio_action_get_current_value (current)); +} + + +static void +sort_by_radio_action (GtkAction *action, + GtkRadioAction *current, + gpointer data) +{ + FrWindow *window = data; + + window->priv->sort_method = gtk_radio_action_get_current_value (current); + window->priv->sort_type = GTK_SORT_ASCENDING; + fr_window_update_list_order (window); +} + + +static void +recent_chooser_item_activated_cb (GtkRecentChooser *chooser, + FrWindow *window) +{ + char *uri; + + uri = gtk_recent_chooser_get_current_uri (chooser); + if (uri != NULL) { + fr_window_archive_open (window, uri, GTK_WINDOW (window)); + g_free (uri); + } +} + + +static void +fr_window_init_recent_chooser (FrWindow *window, + GtkRecentChooser *chooser) +{ + GtkRecentFilter *filter; + int i; + + g_return_if_fail (chooser != NULL); + + filter = gtk_recent_filter_new (); + gtk_recent_filter_set_name (filter, _("All archives")); + for (i = 0; open_type[i] != -1; i++) + gtk_recent_filter_add_mime_type (filter, mime_type_desc[open_type[i]].mime_type); + gtk_recent_filter_add_application (filter, "File Roller"); + gtk_recent_chooser_add_filter (chooser, filter); + + gtk_recent_chooser_set_local_only (chooser, FALSE); + gtk_recent_chooser_set_limit (chooser, eel_mateconf_get_integer (PREF_UI_HISTORY_LEN, MAX_HISTORY_LEN)); + gtk_recent_chooser_set_show_not_found (chooser, TRUE); + gtk_recent_chooser_set_sort_type (chooser, GTK_RECENT_SORT_MRU); + + g_signal_connect (G_OBJECT (chooser), + "item_activated", + G_CALLBACK (recent_chooser_item_activated_cb), + window); +} + + +static void +close_sidepane_button_clicked_cb (GtkButton *button, + FrWindow *window) +{ + fr_window_set_folders_visibility (window, FALSE); +} + + +static void +fr_window_activate_filter (FrWindow *window) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (window->priv->list_view); + GtkTreeViewColumn *column; + + fr_window_update_filter_bar_visibility (window); + window->priv->list_mode = FR_WINDOW_LIST_MODE_FLAT; + + gtk_list_store_clear (window->priv->list_store); + + column = gtk_tree_view_get_column (tree_view, 4); + gtk_tree_view_column_set_visible (column, TRUE); + + fr_window_update_file_list (window, TRUE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); +} + + +static void +filter_entry_activate_cb (GtkEntry *entry, + FrWindow *window) +{ + fr_window_activate_filter (window); +} + + +static void +filter_entry_icon_release_cb (GtkEntry *entry, + GtkEntryIconPosition icon_pos, + GdkEventButton *event, + gpointer user_data) +{ + FrWindow *window = FR_WINDOW (user_data); + + if ((event->button == 1) && (icon_pos == GTK_ENTRY_ICON_SECONDARY)) + fr_window_deactivate_filter (window); +} + + +static void +fr_window_attach (FrWindow *window, + GtkWidget *child, + FrWindowArea area) +{ + int position; + + g_return_if_fail (window != NULL); + g_return_if_fail (FR_IS_WINDOW (window)); + g_return_if_fail (child != NULL); + g_return_if_fail (GTK_IS_WIDGET (child)); + + switch (area) { + case FR_WINDOW_AREA_MENUBAR: + position = 0; + break; + case FR_WINDOW_AREA_TOOLBAR: + position = 1; + break; + case FR_WINDOW_AREA_LOCATIONBAR: + position = 2; + break; + case FR_WINDOW_AREA_CONTENTS: + position = 3; + if (window->priv->contents != NULL) + gtk_widget_destroy (window->priv->contents); + window->priv->contents = child; + break; + case FR_WINDOW_AREA_FILTERBAR: + position = 4; + break; + case FR_WINDOW_AREA_STATUSBAR: + position = 5; + break; + default: + g_critical ("%s: area not recognized!", G_STRFUNC); + return; + break; + } + + gtk_table_attach (GTK_TABLE (window->priv->layout), + child, + 0, 1, + position, position + 1, + GTK_EXPAND | GTK_FILL, + ((area == FR_WINDOW_AREA_CONTENTS) ? GTK_EXPAND : 0) | GTK_FILL, + 0, 0); +} + + +static void +set_action_important (GtkUIManager *ui, + const char *action_name) +{ + GtkAction *action; + + action = gtk_ui_manager_get_action (ui, action_name); + g_object_set (action, "is_important", TRUE, NULL); + g_object_unref (action); +} + + +static void +fr_window_construct (FrWindow *window) +{ + GtkWidget *menubar; + GtkWidget *toolbar; + GtkWidget *list_scrolled_window; + GtkWidget *location_box; + GtkStatusbar *statusbar; + GtkWidget *statusbar_box; + GtkWidget *filter_box; + GtkWidget *tree_scrolled_window; + GtkWidget *sidepane_title; + GtkWidget *sidepane_title_box; + GtkWidget *sidepane_title_label; + GtkWidget *close_sidepane_button; + GtkTreeSelection *selection; + int i; + int icon_width, icon_height; + GtkActionGroup *actions; + GtkUIManager *ui; + GtkToolItem *open_recent_tool_item; + GtkWidget *menu_item; + GError *error = NULL; + + /* data common to all windows. */ + + if (pixbuf_hash == NULL) + pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + if (tree_pixbuf_hash == NULL) + tree_pixbuf_hash = g_hash_table_new (g_str_hash, g_str_equal); + + if (icon_theme == NULL) + icon_theme = gtk_icon_theme_get_default (); + + /* Create the application. */ + + window->priv->layout = gtk_table_new (4, 1, FALSE); + gtk_container_add (GTK_CONTAINER (window), window->priv->layout); + gtk_widget_show (window->priv->layout); + + gtk_window_set_title (GTK_WINDOW (window), _("Archive Manager")); + + g_signal_connect (G_OBJECT (window), + "delete_event", + G_CALLBACK (fr_window_delete_event_cb), + window); + + g_signal_connect (G_OBJECT (window), + "show", + G_CALLBACK (fr_window_show_cb), + window); + + window->priv->theme_changed_handler_id = + g_signal_connect (icon_theme, + "changed", + G_CALLBACK (theme_changed_cb), + window); + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (window)), + FILE_LIST_ICON_SIZE, + &icon_width, &icon_height); + file_list_icon_size = MAX (icon_width, icon_height); + + gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (GTK_WIDGET (window)), + DIR_TREE_ICON_SIZE, + &icon_width, &icon_height); + dir_tree_icon_size = MAX (icon_width, icon_height); + + gtk_window_set_default_size (GTK_WINDOW (window), + eel_mateconf_get_integer (PREF_UI_WINDOW_WIDTH, DEF_WIN_WIDTH), + eel_mateconf_get_integer (PREF_UI_WINDOW_HEIGHT, DEF_WIN_HEIGHT)); + + gtk_drag_dest_set (GTK_WIDGET (window), + GTK_DEST_DEFAULT_ALL, + target_table, G_N_ELEMENTS (target_table), + GDK_ACTION_COPY); + + g_signal_connect (G_OBJECT (window), + "drag_data_received", + G_CALLBACK (fr_window_drag_data_received), + window); + g_signal_connect (G_OBJECT (window), + "drag_motion", + G_CALLBACK (fr_window_drag_motion), + window); + + g_signal_connect (G_OBJECT (window), + "key_press_event", + G_CALLBACK (key_press_cb), + window); + + /* Initialize Data. */ + + window->archive = fr_archive_new (); + g_signal_connect (G_OBJECT (window->archive), + "start", + G_CALLBACK (action_started), + window); + g_signal_connect (G_OBJECT (window->archive), + "done", + G_CALLBACK (action_performed), + window); + g_signal_connect (G_OBJECT (window->archive), + "progress", + G_CALLBACK (fr_window_progress_cb), + window); + g_signal_connect (G_OBJECT (window->archive), + "message", + G_CALLBACK (fr_window_message_cb), + window); + g_signal_connect (G_OBJECT (window->archive), + "stoppable", + G_CALLBACK (fr_window_stoppable_cb), + window); + g_signal_connect (G_OBJECT (window->archive), + "working_archive", + G_CALLBACK (fr_window_working_archive_cb), + window); + + fr_archive_set_fake_load_func (window->archive, + fr_window_fake_load, + window); + fr_archive_set_add_is_stoppable_func (window->archive, + fr_window_add_is_stoppable, + window); + + window->priv->sort_method = preferences_get_sort_method (); + window->priv->sort_type = preferences_get_sort_type (); + + window->priv->list_mode = window->priv->last_list_mode = preferences_get_list_mode (); + window->priv->history = NULL; + window->priv->history_current = NULL; + + window->priv->action = FR_ACTION_NONE; + + eel_mateconf_set_boolean (PREF_LIST_SHOW_PATH, (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT)); + + window->priv->open_default_dir = g_strdup (get_home_uri ()); + window->priv->add_default_dir = g_strdup (get_home_uri ()); + window->priv->extract_default_dir = g_strdup (get_home_uri ()); + + window->priv->give_focus_to_the_list = FALSE; + + window->priv->activity_ref = 0; + window->priv->activity_timeout_handle = 0; + + window->priv->update_timeout_handle = 0; + + window->priv->archive_present = FALSE; + window->priv->archive_new = FALSE; + window->priv->archive_uri = NULL; + + window->priv->drag_destination_folder = NULL; + window->priv->drag_base_dir = NULL; + window->priv->drag_error = NULL; + window->priv->drag_file_list = NULL; + + window->priv->batch_mode = FALSE; + window->priv->batch_action_list = NULL; + window->priv->batch_action = NULL; + window->priv->extract_interact_use_default_dir = FALSE; + window->priv->non_interactive = FALSE; + + window->priv->password = NULL; + window->priv->compression = preferences_get_compression_level (); + window->priv->encrypt_header = eel_mateconf_get_boolean (PREF_ENCRYPT_HEADER, FALSE); + window->priv->volume_size = 0; + + window->priv->convert_data.converting = FALSE; + window->priv->convert_data.temp_dir = NULL; + window->priv->convert_data.new_archive = NULL; + window->priv->convert_data.password = NULL; + window->priv->convert_data.encrypt_header = FALSE; + window->priv->convert_data.volume_size = 0; + + window->priv->stoppable = TRUE; + + window->priv->batch_adding_one_file = FALSE; + + window->priv->path_clicked = NULL; + + window->priv->current_view_length = 0; + + window->priv->current_batch_action.type = FR_BATCH_ACTION_NONE; + window->priv->current_batch_action.data = NULL; + window->priv->current_batch_action.free_func = NULL; + + window->priv->pd_last_archive = NULL; + + /* Create the widgets. */ + + /* * File list. */ + + window->priv->list_store = fr_list_model_new (NUMBER_OF_COLUMNS, + G_TYPE_POINTER, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING); + g_object_set_data (G_OBJECT (window->priv->list_store), "FrWindow", window); + window->priv->list_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (window->priv->list_store)); + + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (window->priv->list_view), TRUE); + add_file_list_columns (window, GTK_TREE_VIEW (window->priv->list_view)); + gtk_tree_view_set_enable_search (GTK_TREE_VIEW (window->priv->list_view), + FALSE); + gtk_tree_view_set_search_column (GTK_TREE_VIEW (window->priv->list_view), + COLUMN_NAME); + + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_NAME, name_column_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_SIZE, size_column_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_TYPE, type_column_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_TIME, time_column_sort_func, + NULL, NULL); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + COLUMN_PATH, path_column_sort_func, + NULL, NULL); + + gtk_tree_sortable_set_default_sort_func (GTK_TREE_SORTABLE (window->priv->list_store), + no_sort_column_sort_func, + NULL, NULL); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view)); + gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE); + + g_signal_connect (selection, + "changed", + G_CALLBACK (selection_changed_cb), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "row_activated", + G_CALLBACK (row_activated_cb), + window); + + g_signal_connect (G_OBJECT (window->priv->list_view), + "button_press_event", + G_CALLBACK (file_button_press_cb), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "button_release_event", + G_CALLBACK (file_button_release_cb), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "motion_notify_event", + G_CALLBACK (file_motion_notify_callback), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "leave_notify_event", + G_CALLBACK (file_leave_notify_callback), + window); + + g_signal_connect (G_OBJECT (window->priv->list_store), + "sort_column_changed", + G_CALLBACK (sort_column_changed_cb), + window); + + g_signal_connect (G_OBJECT (window->priv->list_view), + "drag_begin", + G_CALLBACK (file_list_drag_begin), + window); + g_signal_connect (G_OBJECT (window->priv->list_view), + "drag_end", + G_CALLBACK (file_list_drag_end), + window); + egg_tree_multi_drag_add_drag_support (GTK_TREE_VIEW (window->priv->list_view)); + + list_scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (list_scrolled_window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (list_scrolled_window), window->priv->list_view); + + /* filter bar */ + + window->priv->filter_bar = filter_box = gtk_hbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (filter_box), 3); + fr_window_attach (FR_WINDOW (window), window->priv->filter_bar, FR_WINDOW_AREA_FILTERBAR); + + gtk_box_pack_start (GTK_BOX (filter_box), + gtk_label_new (_("Find:")), FALSE, FALSE, 0); + + /* * filter entry */ + + window->priv->filter_entry = GTK_WIDGET (gtk_entry_new ()); + gtk_entry_set_icon_from_stock (GTK_ENTRY (window->priv->filter_entry), + GTK_ENTRY_ICON_SECONDARY, + GTK_STOCK_CLEAR); + + gtk_widget_set_size_request (window->priv->filter_entry, 300, -1); + gtk_box_pack_start (GTK_BOX (filter_box), + window->priv->filter_entry, FALSE, FALSE, 6); + + g_signal_connect (G_OBJECT (window->priv->filter_entry), + "activate", + G_CALLBACK (filter_entry_activate_cb), + window); + g_signal_connect (G_OBJECT (window->priv->filter_entry), + "icon-release", + G_CALLBACK (filter_entry_icon_release_cb), + window); + + gtk_widget_show_all (filter_box); + + /* tree view */ + + window->priv->tree_store = gtk_tree_store_new (TREE_NUMBER_OF_COLUMNS, + G_TYPE_STRING, + GDK_TYPE_PIXBUF, + G_TYPE_STRING, + PANGO_TYPE_WEIGHT); + window->priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (window->priv->tree_store)); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (window->priv->tree_view), FALSE); + add_dir_tree_columns (window, GTK_TREE_VIEW (window->priv->tree_view)); + + g_signal_connect (G_OBJECT (window->priv->tree_view), + "button_press_event", + G_CALLBACK (dir_tree_button_press_cb), + window); + + selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->tree_view)); + g_signal_connect (selection, + "changed", + G_CALLBACK (dir_tree_selection_changed_cb), + window); + + g_signal_connect (G_OBJECT (window->priv->tree_view), + "drag_begin", + G_CALLBACK (file_list_drag_begin), + window); + g_signal_connect (G_OBJECT (window->priv->tree_view), + "drag_end", + G_CALLBACK (file_list_drag_end), + window); + g_signal_connect (G_OBJECT (window->priv->tree_view), + "drag_data_get", + G_CALLBACK (fr_window_folder_tree_drag_data_get), + window); + gtk_drag_source_set (window->priv->tree_view, + GDK_BUTTON1_MASK, + folder_tree_targets, G_N_ELEMENTS (folder_tree_targets), + GDK_ACTION_COPY); + + tree_scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (tree_scrolled_window), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_container_add (GTK_CONTAINER (tree_scrolled_window), window->priv->tree_view); + + /* side pane */ + + window->priv->sidepane = gtk_vbox_new (FALSE, 0); + + sidepane_title = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (sidepane_title), GTK_SHADOW_ETCHED_IN); + + sidepane_title_box = gtk_hbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (sidepane_title_box), 2); + gtk_container_add (GTK_CONTAINER (sidepane_title), sidepane_title_box); + sidepane_title_label = gtk_label_new (_("Folders")); + + gtk_misc_set_alignment (GTK_MISC (sidepane_title_label), 0.0, 0.5); + gtk_box_pack_start (GTK_BOX (sidepane_title_box), sidepane_title_label, TRUE, TRUE, 0); + + close_sidepane_button = gtk_button_new (); + gtk_container_add (GTK_CONTAINER (close_sidepane_button), gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU)); + gtk_button_set_relief (GTK_BUTTON (close_sidepane_button), GTK_RELIEF_NONE); + gtk_widget_set_tooltip_text (close_sidepane_button, _("Close the folders pane")); + g_signal_connect (close_sidepane_button, + "clicked", + G_CALLBACK (close_sidepane_button_clicked_cb), + window); + gtk_box_pack_end (GTK_BOX (sidepane_title_box), close_sidepane_button, FALSE, FALSE, 0); + + gtk_box_pack_start (GTK_BOX (window->priv->sidepane), sidepane_title, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (window->priv->sidepane), tree_scrolled_window, TRUE, TRUE, 0); + + /* main content */ + + window->priv->paned = gtk_hpaned_new (); + gtk_paned_pack1 (GTK_PANED (window->priv->paned), window->priv->sidepane, FALSE, TRUE); + gtk_paned_pack2 (GTK_PANED (window->priv->paned), list_scrolled_window, TRUE, TRUE); + gtk_paned_set_position (GTK_PANED (window->priv->paned), eel_mateconf_get_integer (PREF_UI_SIDEBAR_WIDTH, DEF_SIDEBAR_WIDTH)); + + fr_window_attach (FR_WINDOW (window), window->priv->paned, FR_WINDOW_AREA_CONTENTS); + gtk_widget_show_all (window->priv->paned); + + /* Build the menu and the toolbar. */ + + ui = gtk_ui_manager_new (); + + window->priv->actions = actions = gtk_action_group_new ("Actions"); + gtk_action_group_set_translation_domain (actions, NULL); + gtk_action_group_add_actions (actions, + action_entries, + n_action_entries, + window); + gtk_action_group_add_toggle_actions (actions, + action_toggle_entries, + n_action_toggle_entries, + window); + gtk_action_group_add_radio_actions (actions, + view_as_entries, + n_view_as_entries, + window->priv->list_mode, + G_CALLBACK (view_as_radio_action), + window); + gtk_action_group_add_radio_actions (actions, + sort_by_entries, + n_sort_by_entries, + window->priv->sort_type, + G_CALLBACK (sort_by_radio_action), + window); + + g_signal_connect (ui, "connect_proxy", + G_CALLBACK (connect_proxy_cb), window); + g_signal_connect (ui, "disconnect_proxy", + G_CALLBACK (disconnect_proxy_cb), window); + + gtk_ui_manager_insert_action_group (ui, actions, 0); + gtk_window_add_accel_group (GTK_WINDOW (window), + gtk_ui_manager_get_accel_group (ui)); + + /* Add a hidden short cut Ctrl-Q for power users */ + gtk_accel_group_connect (gtk_ui_manager_get_accel_group (ui), + GDK_q, GDK_CONTROL_MASK, 0, + g_cclosure_new_swap (G_CALLBACK (fr_window_close), window, NULL)); + + + if (!gtk_ui_manager_add_ui_from_string (ui, ui_info, -1, &error)) { + g_message ("building menus failed: %s", error->message); + g_error_free (error); + } + + menubar = gtk_ui_manager_get_widget (ui, "/MenuBar"); + fr_window_attach (FR_WINDOW (window), menubar, FR_WINDOW_AREA_MENUBAR); + gtk_widget_show (menubar); + + window->priv->toolbar = toolbar = gtk_ui_manager_get_widget (ui, "/ToolBar"); + gtk_toolbar_set_show_arrow (GTK_TOOLBAR (toolbar), TRUE); + set_action_important (ui, "/ToolBar/Extract_Toolbar"); + + /* location bar */ + + window->priv->location_bar = gtk_ui_manager_get_widget (ui, "/LocationBar"); + gtk_toolbar_set_show_arrow (GTK_TOOLBAR (window->priv->location_bar), FALSE); + gtk_toolbar_set_style (GTK_TOOLBAR (window->priv->location_bar), GTK_TOOLBAR_BOTH_HORIZ); + set_action_important (ui, "/LocationBar/GoBack"); + + /* current location */ + + location_box = gtk_hbox_new (FALSE, 6); + /* Translators: after the colon there is a folder name. */ + window->priv->location_label = gtk_label_new_with_mnemonic (_("_Location:")); + gtk_box_pack_start (GTK_BOX (location_box), + window->priv->location_label, FALSE, FALSE, 5); + + window->priv->location_entry = gtk_entry_new (); + gtk_entry_set_icon_from_stock (GTK_ENTRY (window->priv->location_entry), + GTK_ENTRY_ICON_PRIMARY, + GTK_STOCK_DIRECTORY); + + gtk_box_pack_start (GTK_BOX (location_box), + window->priv->location_entry, TRUE, TRUE, 5); + + g_signal_connect (G_OBJECT (window->priv->location_entry), + "key_press_event", + G_CALLBACK (location_entry_key_press_event_cb), + window); + + { + GtkToolItem *tool_item; + + tool_item = gtk_separator_tool_item_new (); + gtk_widget_show_all (GTK_WIDGET (tool_item)); + gtk_toolbar_insert (GTK_TOOLBAR (window->priv->location_bar), tool_item, -1); + + tool_item = gtk_tool_item_new (); + gtk_tool_item_set_expand (tool_item, TRUE); + gtk_container_add (GTK_CONTAINER (tool_item), location_box); + gtk_widget_show_all (GTK_WIDGET (tool_item)); + gtk_toolbar_insert (GTK_TOOLBAR (window->priv->location_bar), tool_item, -1); + } + + fr_window_attach (FR_WINDOW (window), window->priv->location_bar, FR_WINDOW_AREA_LOCATIONBAR); + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) + gtk_widget_hide (window->priv->location_bar); + else + gtk_widget_show (window->priv->location_bar); + + /* Recent manager */ + + window->priv->recent_manager = gtk_recent_manager_get_default (); + + window->priv->recent_chooser_menu = gtk_recent_chooser_menu_new_for_manager (window->priv->recent_manager); + gtk_recent_chooser_set_sort_type (GTK_RECENT_CHOOSER (window->priv->recent_chooser_menu), GTK_RECENT_SORT_MRU); + fr_window_init_recent_chooser (window, GTK_RECENT_CHOOSER (window->priv->recent_chooser_menu)); + menu_item = gtk_ui_manager_get_widget (ui, "/MenuBar/Archive/OpenRecentMenu"); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (menu_item), window->priv->recent_chooser_menu); + + window->priv->recent_chooser_toolbar = gtk_recent_chooser_menu_new_for_manager (window->priv->recent_manager); + fr_window_init_recent_chooser (window, GTK_RECENT_CHOOSER (window->priv->recent_chooser_toolbar)); + + /* Add the recent menu tool item */ + + open_recent_tool_item = gtk_menu_tool_button_new_from_stock (GTK_STOCK_OPEN); + gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (open_recent_tool_item), window->priv->recent_chooser_toolbar); + gtk_tool_item_set_homogeneous (open_recent_tool_item, FALSE); + gtk_widget_set_tooltip_text (GTK_WIDGET (open_recent_tool_item), _("Open archive")); + gtk_menu_tool_button_set_arrow_tooltip_text (GTK_MENU_TOOL_BUTTON (open_recent_tool_item), _("Open a recently used archive")); + + window->priv->open_action = gtk_action_new ("Toolbar_Open", _("Open"), _("Open archive"), GTK_STOCK_OPEN); + g_object_set (window->priv->open_action, "is_important", TRUE, NULL); + g_signal_connect (window->priv->open_action, + "activate", + G_CALLBACK (activate_action_open), + window); + gtk_activatable_set_related_action (GTK_ACTIVATABLE (open_recent_tool_item), window->priv->open_action); + + gtk_widget_show (GTK_WIDGET (open_recent_tool_item)); + gtk_toolbar_insert (GTK_TOOLBAR (toolbar), open_recent_tool_item, 1); + + /**/ + + fr_window_attach (FR_WINDOW (window), window->priv->toolbar, FR_WINDOW_AREA_TOOLBAR); + if (eel_mateconf_get_boolean (PREF_UI_TOOLBAR, TRUE)) + gtk_widget_show (toolbar); + else + gtk_widget_hide (toolbar); + + window->priv->file_popup_menu = gtk_ui_manager_get_widget (ui, "/FilePopupMenu"); + window->priv->folder_popup_menu = gtk_ui_manager_get_widget (ui, "/FolderPopupMenu"); + window->priv->sidebar_folder_popup_menu = gtk_ui_manager_get_widget (ui, "/SidebarFolderPopupMenu"); + + /* Create the statusbar. */ + + window->priv->statusbar = gtk_statusbar_new (); + window->priv->help_message_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "help_message"); + window->priv->list_info_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "list_info"); + window->priv->progress_cid = gtk_statusbar_get_context_id (GTK_STATUSBAR (window->priv->statusbar), "progress"); + + statusbar = GTK_STATUSBAR (window->priv->statusbar); +#if GTK_CHECK_VERSION (2, 19, 1) + statusbar_box = gtk_statusbar_get_message_area (statusbar); + gtk_box_set_homogeneous (GTK_BOX (statusbar_box), FALSE); + gtk_box_set_spacing (GTK_BOX (statusbar_box), 4); + gtk_box_set_child_packing (GTK_BOX (statusbar_box), gtk_statusbar_get_message_area (statusbar), TRUE, TRUE, 0, GTK_PACK_START ); +#else + statusbar_box = gtk_hbox_new (FALSE, 4); + g_object_ref (statusbar->label); + gtk_container_remove (GTK_CONTAINER (statusbar->frame), statusbar->label); + gtk_box_pack_start (GTK_BOX (statusbar_box), statusbar->label, TRUE, TRUE, 0); + g_object_unref (statusbar->label); + gtk_container_add (GTK_CONTAINER (statusbar->frame), statusbar_box); +#endif + + window->priv->progress_bar = gtk_progress_bar_new (); + gtk_progress_bar_set_pulse_step (GTK_PROGRESS_BAR (window->priv->progress_bar), ACTIVITY_PULSE_STEP); + gtk_widget_set_size_request (window->priv->progress_bar, -1, PROGRESS_BAR_HEIGHT); + { + GtkWidget *vbox; + + vbox = gtk_vbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (statusbar_box), vbox, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), window->priv->progress_bar, TRUE, TRUE, 1); + gtk_widget_show (vbox); + } + gtk_widget_show (statusbar_box); + gtk_statusbar_set_has_resize_grip (GTK_STATUSBAR (window->priv->statusbar), TRUE); + + fr_window_attach (FR_WINDOW (window), window->priv->statusbar, FR_WINDOW_AREA_STATUSBAR); + if (eel_mateconf_get_boolean (PREF_UI_STATUSBAR, TRUE)) + gtk_widget_show (window->priv->statusbar); + else + gtk_widget_hide (window->priv->statusbar); + + /**/ + + fr_window_update_title (window); + fr_window_update_sensitivity (window); + fr_window_update_file_list (window, FALSE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); + fr_window_update_columns_visibility (window); + + /* Add notification callbacks. */ + + i = 0; + + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_UI_HISTORY_LEN, + pref_history_len_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_UI_TOOLBAR, + pref_view_toolbar_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_UI_STATUSBAR, + pref_view_statusbar_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_UI_FOLDERS, + pref_view_folders_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_SHOW_TYPE, + pref_show_field_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_SHOW_SIZE, + pref_show_field_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_SHOW_TIME, + pref_show_field_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_SHOW_PATH, + pref_show_field_changed, + window); + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_LIST_USE_MIME_ICONS, + pref_use_mime_icons_changed, + window); + + window->priv->cnxn_id[i++] = eel_mateconf_notification_add ( + PREF_CAJA_CLICK_POLICY, + pref_click_policy_changed, + window); + + /* Give focus to the list. */ + + gtk_widget_grab_focus (window->priv->list_view); +} + + +GtkWidget * +fr_window_new (void) +{ + GtkWidget *window; + + window = g_object_new (FR_TYPE_WINDOW, NULL); + fr_window_construct ((FrWindow*) window); + + return window; +} + + +static void +fr_window_set_archive_uri (FrWindow *window, + const char *uri) +{ + if (window->priv->archive_uri != NULL) + g_free (window->priv->archive_uri); + window->priv->archive_uri = g_strdup (uri); +} + + +gboolean +fr_window_archive_new (FrWindow *window, + const char *uri) +{ + g_return_val_if_fail (window != NULL, FALSE); + + if (! fr_archive_create (window->archive, uri)) { + GtkWindow *file_sel = g_object_get_data (G_OBJECT (window), "fr_file_sel"); + + window->priv->load_error_parent_window = file_sel; + fr_archive_action_completed (window->archive, + FR_ACTION_CREATING_NEW_ARCHIVE, + FR_PROC_ERROR_GENERIC, + _("Archive type not supported.")); + + return FALSE; + } + + fr_window_set_archive_uri (window, uri); + window->priv->archive_present = TRUE; + window->priv->archive_new = TRUE; + + fr_archive_action_completed (window->archive, + FR_ACTION_CREATING_NEW_ARCHIVE, + FR_PROC_ERROR_NONE, + NULL); + + return TRUE; +} + + +FrWindow * +fr_window_archive_open (FrWindow *current_window, + const char *uri, + GtkWindow *parent) +{ + FrWindow *window = current_window; + + if (current_window->priv->archive_present) + window = (FrWindow *) fr_window_new (); + + g_return_val_if_fail (window != NULL, FALSE); + + fr_window_archive_close (window); + + fr_window_set_archive_uri (window, uri); + window->priv->archive_present = FALSE; + window->priv->give_focus_to_the_list = TRUE; + window->priv->load_error_parent_window = parent; + + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_LOAD, + g_strdup (window->priv->archive_uri), + (GFreeFunc) g_free); + + fr_archive_load (window->archive, window->priv->archive_uri, window->priv->password); + + return window; +} + + +void +fr_window_archive_close (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (! window->priv->archive_new && ! window->priv->archive_present) + return; + + fr_window_free_open_files (window); + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + + fr_window_set_password (window, NULL); + fr_window_set_volume_size(window, 0); + fr_window_history_clear (window); + + window->priv->archive_new = FALSE; + window->priv->archive_present = FALSE; + + fr_window_update_title (window); + fr_window_update_sensitivity (window); + fr_window_update_file_list (window, FALSE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); + fr_window_update_statusbar_list_info (window); +} + + +const char * +fr_window_get_archive_uri (FrWindow *window) +{ + g_return_val_if_fail (window != NULL, NULL); + + return window->priv->archive_uri; +} + + +const char * +fr_window_get_paste_archive_uri (FrWindow *window) +{ + g_return_val_if_fail (window != NULL, NULL); + + if (window->priv->clipboard_data != NULL) + return window->priv->clipboard_data->archive_filename; + else + return NULL; +} + + +gboolean +fr_window_archive_is_present (FrWindow *window) +{ + g_return_val_if_fail (window != NULL, FALSE); + + return window->priv->archive_present; +} + + +typedef struct { + char *uri; + char *password; + gboolean encrypt_header; + guint volume_size; +} SaveAsData; + + +static SaveAsData * +save_as_data_new (const char *uri, + const char *password, + gboolean encrypt_header, + guint volume_size) +{ + SaveAsData *sdata; + + sdata = g_new0 (SaveAsData, 1); + if (uri != NULL) + sdata->uri = g_strdup (uri); + if (password != NULL) + sdata->password = g_strdup (password); + sdata->encrypt_header = encrypt_header; + sdata->volume_size = volume_size; + + return sdata; +} + + +static void +save_as_data_free (SaveAsData *sdata) +{ + if (sdata == NULL) + return; + g_free (sdata->uri); + g_free (sdata->password); + g_free (sdata); +} + + +void +fr_window_archive_save_as (FrWindow *window, + const char *uri, + const char *password, + gboolean encrypt_header, + guint volume_size) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (uri != NULL); + g_return_if_fail (window->archive != NULL); + + fr_window_convert_data_free (window, TRUE); + window->priv->convert_data.new_file = g_strdup (uri); + + /* create the new archive */ + + window->priv->convert_data.new_archive = fr_archive_new (); + if (! fr_archive_create (window->priv->convert_data.new_archive, uri)) { + GtkWidget *d; + char *utf8_name; + char *message; + + utf8_name = g_uri_display_basename (uri); + message = g_strdup_printf (_("Could not save the archive \"%s\""), utf8_name); + g_free (utf8_name); + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + message, + "%s", + _("Archive type not supported.")); + gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (d); + + g_free (message); + + g_object_unref (window->priv->convert_data.new_archive); + window->priv->convert_data.new_archive = NULL; + + return; + } + + g_return_if_fail (window->priv->convert_data.new_archive->command != NULL); + + if (password != NULL) { + window->priv->convert_data.password = g_strdup (password); + window->priv->convert_data.encrypt_header = encrypt_header; + } + else + window->priv->convert_data.encrypt_header = FALSE; + window->priv->convert_data.volume_size = volume_size; + + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_SAVE_AS, + save_as_data_new (uri, password, encrypt_header, volume_size), + (GFreeFunc) save_as_data_free); + + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "start", + G_CALLBACK (action_started), + window); + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "done", + G_CALLBACK (convert__action_performed), + window); + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "progress", + G_CALLBACK (fr_window_progress_cb), + window); + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "message", + G_CALLBACK (fr_window_message_cb), + window); + g_signal_connect (G_OBJECT (window->priv->convert_data.new_archive), + "stoppable", + G_CALLBACK (fr_window_stoppable_cb), + window); + + window->priv->convert_data.converting = TRUE; + window->priv->convert_data.temp_dir = get_temp_work_dir (NULL); + + fr_process_clear (window->archive->process); + fr_archive_extract_to_local (window->archive, + NULL, + window->priv->convert_data.temp_dir, + NULL, + TRUE, + FALSE, + FALSE, + window->priv->password); + fr_process_start (window->archive->process); +} + + +void +fr_window_archive_reload (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->activity_ref > 0) + return; + if (window->priv->archive_new) + return; + + fr_archive_reload (window->archive, window->priv->password); +} + + +void +fr_window_archive_rename (FrWindow *window, + const char *uri) +{ + g_return_if_fail (window != NULL); + + if (window->priv->archive_new) { + fr_window_archive_new (window, uri); + return; + } + + fr_archive_rename (window->archive, uri); + fr_window_set_archive_uri (window, uri); + + fr_window_update_title (window); + fr_window_add_to_recent_list (window, window->priv->archive_uri); +} + + +/**/ + + +void +fr_window_archive_add_files (FrWindow *window, + GList *file_list, /* GFile list */ + gboolean update) +{ + GFile *base; + char *base_dir; + int base_len; + GList *files = NULL; + GList *scan; + char *base_uri; + + base = g_file_get_parent ((GFile *) file_list->data); + base_dir = g_file_get_path (base); + base_len = 0; + if (strcmp (base_dir, "/") != 0) + base_len = strlen (base_dir); + + for (scan = file_list; scan; scan = scan->next) { + GFile *file = scan->data; + char *path; + char *rel_path; + + path = g_file_get_path (file); + rel_path = g_strdup (path + base_len + 1); + files = g_list_prepend (files, rel_path); + + g_free (path); + } + + base_uri = g_file_get_uri (base); + + fr_archive_add_files (window->archive, + files, + base_uri, + fr_window_get_current_location (window), + update, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); + + g_free (base_uri); + path_list_free (files); + g_free (base_dir); + g_object_unref (base); +} + + +void +fr_window_archive_add_with_wildcard (FrWindow *window, + const char *include_files, + const char *exclude_files, + const char *exclude_folders, + const char *base_dir, + const char *dest_dir, + gboolean update, + gboolean follow_links) +{ + fr_archive_add_with_wildcard (window->archive, + include_files, + exclude_files, + exclude_folders, + base_dir, + (dest_dir == NULL)? fr_window_get_current_location (window): dest_dir, + update, + follow_links, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); +} + + +void +fr_window_archive_add_directory (FrWindow *window, + const char *directory, + const char *base_dir, + const char *dest_dir, + gboolean update) +{ + fr_archive_add_directory (window->archive, + directory, + base_dir, + (dest_dir == NULL)? fr_window_get_current_location (window): dest_dir, + update, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); +} + + +void +fr_window_archive_add_items (FrWindow *window, + GList *item_list, + const char *base_dir, + const char *dest_dir, + gboolean update) +{ + fr_archive_add_items (window->archive, + item_list, + base_dir, + (dest_dir == NULL)? fr_window_get_current_location (window): dest_dir, + update, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); +} + + +void +fr_window_archive_add_dropped_items (FrWindow *window, + GList *item_list, + gboolean update) +{ + fr_archive_add_dropped_items (window->archive, + item_list, + fr_window_get_current_location (window), + fr_window_get_current_location (window), + update, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); +} + + +void +fr_window_archive_remove (FrWindow *window, + GList *file_list) +{ + fr_window_clipboard_remove_file_list (window, file_list); + + fr_process_clear (window->archive->process); + fr_archive_remove (window->archive, file_list, window->priv->compression); + fr_process_start (window->archive->process); +} + + +/* -- window_archive_extract -- */ + + +static ExtractData* +extract_data_new (GList *file_list, + const char *extract_to_dir, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths, + gboolean extract_here) +{ + ExtractData *edata; + + edata = g_new0 (ExtractData, 1); + edata->file_list = path_list_dup (file_list); + if (extract_to_dir != NULL) + edata->extract_to_dir = g_strdup (extract_to_dir); + edata->skip_older = skip_older; + edata->overwrite = overwrite; + edata->junk_paths = junk_paths; + if (base_dir != NULL) + edata->base_dir = g_strdup (base_dir); + edata->extract_here = extract_here; + + return edata; +} + + +static ExtractData* +extract_to_data_new (const char *extract_to_dir) +{ + return extract_data_new (NULL, + extract_to_dir, + NULL, + FALSE, + TRUE, + FALSE, + FALSE); +} + + +static void +extract_data_free (ExtractData *edata) +{ + g_return_if_fail (edata != NULL); + + path_list_free (edata->file_list); + g_free (edata->extract_to_dir); + g_free (edata->base_dir); + + g_free (edata); +} + + +static gboolean +archive_is_encrypted (FrWindow *window, + GList *file_list) +{ + gboolean encrypted = FALSE; + + if (file_list == NULL) { + int i; + + for (i = 0; ! encrypted && i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + + if (fdata->encrypted) + encrypted = TRUE; + } + } + else { + + GHashTable *file_hash; + int i; + GList *scan; + + file_hash = g_hash_table_new (g_str_hash, g_str_equal); + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + g_hash_table_insert (file_hash, fdata->original_path, fdata); + } + + for (scan = file_list; ! encrypted && scan; scan = scan->next) { + char *filename = scan->data; + FileData *fdata; + + fdata = g_hash_table_lookup (file_hash, filename); + g_return_val_if_fail (fdata != NULL, FALSE); + + if (fdata->encrypted) + encrypted = TRUE; + } + + g_hash_table_destroy (file_hash); + } + + return encrypted; +} + + +void +fr_window_archive_extract_here (FrWindow *window, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths) +{ + ExtractData *edata; + + edata = extract_data_new (NULL, + NULL, + NULL, + skip_older, + overwrite, + junk_paths, + TRUE); + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_EXTRACT, + edata, + (GFreeFunc) extract_data_free); + + if (archive_is_encrypted (window, NULL) && (window->priv->password == NULL)) { + dlg_ask_password (window); + return; + } + + window->priv->ask_to_open_destination_after_extraction = FALSE; + + fr_process_clear (window->archive->process); + if (fr_archive_extract_here (window->archive, + edata->skip_older, + edata->overwrite, + edata->junk_paths, + window->priv->password)) + { + fr_process_start (window->archive->process); + } +} + + +void +fr_window_archive_extract (FrWindow *window, + GList *file_list, + const char *extract_to_dir, + const char *base_dir, + gboolean skip_older, + gboolean overwrite, + gboolean junk_paths, + gboolean ask_to_open_destination) +{ + ExtractData *edata; + gboolean do_not_extract = FALSE; + GError *error = NULL; + + edata = extract_data_new (file_list, + extract_to_dir, + base_dir, + skip_older, + overwrite, + junk_paths, + FALSE); + + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_EXTRACT, + edata, + (GFreeFunc) extract_data_free); + + if (archive_is_encrypted (window, edata->file_list) && (window->priv->password == NULL)) { + dlg_ask_password (window); + return; + } + + if (! uri_is_dir (edata->extract_to_dir)) { + if (! ForceDirectoryCreation) { + GtkWidget *d; + int r; + char *folder_name; + char *msg; + + folder_name = g_filename_display_name (edata->extract_to_dir); + msg = g_strdup_printf (_("Destination folder \"%s\" does not exist.\n\nDo you want to create it?"), folder_name); + g_free (folder_name); + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + msg, + NULL, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + _("Create _Folder"), GTK_RESPONSE_YES, + NULL); + + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES); + r = gtk_dialog_run (GTK_DIALOG (d)); + gtk_widget_destroy (GTK_WIDGET (d)); + + g_free (msg); + + if (r != GTK_RESPONSE_YES) + do_not_extract = TRUE; + } + + if (! do_not_extract && ! ensure_dir_exists (edata->extract_to_dir, 0755, &error)) { + GtkWidget *d; + + d = _gtk_error_dialog_new (GTK_WINDOW (window), + 0, + NULL, + _("Extraction not performed"), + _("Could not create the destination folder: %s."), + error->message); + g_clear_error (&error); + fr_window_show_error_dialog (window, d, GTK_WINDOW (window)); + fr_window_stop_batch (window); + + return; + } + } + + if (do_not_extract) { + GtkWidget *d; + + d = _gtk_message_dialog_new (GTK_WINDOW (window), + 0, + GTK_STOCK_DIALOG_WARNING, + _("Extraction not performed"), + NULL, + GTK_STOCK_OK, GTK_RESPONSE_OK, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_OK); + fr_window_show_error_dialog (window, d, GTK_WINDOW (window)); + fr_window_stop_batch (window); + + return; + } + + window->priv->ask_to_open_destination_after_extraction = ask_to_open_destination; + + fr_process_clear (window->archive->process); + fr_archive_extract (window->archive, + edata->file_list, + edata->extract_to_dir, + edata->base_dir, + edata->skip_older, + edata->overwrite, + edata->junk_paths, + window->priv->password); + fr_process_start (window->archive->process); +} + + +void +fr_window_archive_test (FrWindow *window) +{ + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_TEST, + NULL, + NULL); + fr_archive_test (window->archive, window->priv->password); +} + + +void +fr_window_set_password (FrWindow *window, + const char *password) +{ + g_return_if_fail (window != NULL); + + if (window->priv->password != NULL) { + g_free (window->priv->password); + window->priv->password = NULL; + } + + if ((password != NULL) && (password[0] != '\0')) + window->priv->password = g_strdup (password); +} + +void +fr_window_set_password_for_paste (FrWindow *window, + const char *password) +{ + g_return_if_fail (window != NULL); + + if (window->priv->password_for_paste != NULL) { + g_free (window->priv->password_for_paste); + window->priv->password_for_paste = NULL; + } + + if ((password != NULL) && (password[0] != '\0')) + window->priv->password_for_paste = g_strdup (password); +} + +const char * +fr_window_get_password (FrWindow *window) +{ + g_return_val_if_fail (window != NULL, NULL); + + return window->priv->password; +} + + +void +fr_window_set_encrypt_header (FrWindow *window, + gboolean encrypt_header) +{ + g_return_if_fail (window != NULL); + + window->priv->encrypt_header = encrypt_header; +} + + +gboolean +fr_window_get_encrypt_header (FrWindow *window) +{ + return window->priv->encrypt_header; +} + + +void +fr_window_set_compression (FrWindow *window, + FrCompression compression) +{ + g_return_if_fail (window != NULL); + + window->priv->compression = compression; +} + + +FrCompression +fr_window_get_compression (FrWindow *window) +{ + return window->priv->compression; +} + + +void +fr_window_set_volume_size (FrWindow *window, + guint volume_size) +{ + g_return_if_fail (window != NULL); + + window->priv->volume_size = volume_size; +} + + +guint +fr_window_get_volume_size (FrWindow *window) +{ + return window->priv->volume_size; +} + + +void +fr_window_go_to_location (FrWindow *window, + const char *path, + gboolean force_update) +{ + char *dir; + + g_return_if_fail (window != NULL); + g_return_if_fail (path != NULL); + + if (force_update) { + g_free (window->priv->last_location); + window->priv->last_location = NULL; + } + + if (path[strlen (path) - 1] != '/') + dir = g_strconcat (path, "/", NULL); + else + dir = g_strdup (path); + + if ((window->priv->last_location == NULL) || (strcmp (window->priv->last_location, dir) != 0)) { + g_free (window->priv->last_location); + window->priv->last_location = dir; + + fr_window_history_add (window, dir); + fr_window_update_file_list (window, TRUE); + fr_window_update_current_location (window); + } + else + g_free (dir); +} + + +const char * +fr_window_get_current_location (FrWindow *window) +{ + if (window->priv->history_current == NULL) { + fr_window_history_add (window, "/"); + return window->priv->history_current->data; + } + else + return (const char*) window->priv->history_current->data; +} + + +void +fr_window_go_up_one_level (FrWindow *window) +{ + char *parent_dir; + + g_return_if_fail (window != NULL); + + parent_dir = get_parent_dir (fr_window_get_current_location (window)); + fr_window_go_to_location (window, parent_dir, FALSE); + g_free (parent_dir); +} + + +void +fr_window_go_back (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->history == NULL) + return; + if (window->priv->history_current == NULL) + return; + if (window->priv->history_current->next == NULL) + return; + window->priv->history_current = window->priv->history_current->next; + + fr_window_go_to_location (window, window->priv->history_current->data, FALSE); +} + + +void +fr_window_go_forward (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->history == NULL) + return; + if (window->priv->history_current == NULL) + return; + if (window->priv->history_current->prev == NULL) + return; + window->priv->history_current = window->priv->history_current->prev; + + fr_window_go_to_location (window, window->priv->history_current->data, FALSE); +} + + +void +fr_window_set_list_mode (FrWindow *window, + FrWindowListMode list_mode) +{ + g_return_if_fail (window != NULL); + + window->priv->list_mode = window->priv->last_list_mode = list_mode; + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) { + fr_window_history_clear (window); + fr_window_history_add (window, "/"); + } + + preferences_set_list_mode (window->priv->last_list_mode); + eel_mateconf_set_boolean (PREF_LIST_SHOW_PATH, (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT)); + + fr_window_update_file_list (window, TRUE); + fr_window_update_dir_tree (window); + fr_window_update_current_location (window); +} + + +GtkTreeModel * +fr_window_get_list_store (FrWindow *window) +{ + return GTK_TREE_MODEL (window->priv->list_store); +} + + +void +fr_window_find (FrWindow *window) +{ + window->priv->filter_mode = TRUE; + gtk_widget_show (window->priv->filter_bar); + gtk_widget_grab_focus (window->priv->filter_entry); +} + + +void +fr_window_select_all (FrWindow *window) +{ + gtk_tree_selection_select_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view))); +} + + +void +fr_window_unselect_all (FrWindow *window) +{ + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (window->priv->list_view))); +} + + +void +fr_window_set_sort_type (FrWindow *window, + GtkSortType sort_type) +{ + window->priv->sort_type = sort_type; + fr_window_update_list_order (window); +} + + +void +fr_window_stop (FrWindow *window) +{ + if (! window->priv->stoppable) + return; + + if (window->priv->activity_ref > 0) + fr_archive_stop (window->archive); + + if (window->priv->convert_data.converting) + fr_window_convert_data_free (window, TRUE); +} + + +/* -- start/stop activity mode -- */ + + +static int +activity_cb (gpointer data) +{ + FrWindow *window = data; + + if ((window->priv->pd_progress_bar != NULL) && window->priv->progress_pulse) + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (window->priv->pd_progress_bar)); + if (window->priv->progress_pulse) + gtk_progress_bar_pulse (GTK_PROGRESS_BAR (window->priv->progress_bar)); + + return TRUE; +} + + +void +fr_window_start_activity_mode (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->activity_ref++ > 0) + return; + + window->priv->activity_timeout_handle = g_timeout_add (ACTIVITY_DELAY, + activity_cb, + window); + fr_window_update_sensitivity (window); +} + + +void +fr_window_stop_activity_mode (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->activity_ref == 0) + return; + + window->priv->activity_ref--; + + if (window->priv->activity_ref > 0) + return; + + if (window->priv->activity_timeout_handle == 0) + return; + + g_source_remove (window->priv->activity_timeout_handle); + window->priv->activity_timeout_handle = 0; + + if (! gtk_widget_get_realized (GTK_WIDGET (window))) + return; + + if (window->priv->progress_dialog != NULL) + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->pd_progress_bar), 0.0); + + if (! window->priv->batch_mode) { + if (window->priv->progress_bar != NULL) + gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (window->priv->progress_bar), 0.0); + fr_window_update_sensitivity (window); + } +} + + +static gboolean +last_output_window__unrealize_cb (GtkWidget *widget, + gpointer data) +{ + pref_util_save_window_geometry (GTK_WINDOW (widget), LAST_OUTPUT_DIALOG_NAME); + return FALSE; +} + + +void +fr_window_view_last_output (FrWindow *window, + const char *title) +{ + GtkWidget *dialog; + GtkWidget *vbox; + GtkWidget *text_view; + GtkWidget *scrolled; + GtkTextBuffer *text_buffer; + GtkTextIter iter; + GList *scan; + + if (title == NULL) + title = _("Last Output"); + + dialog = gtk_dialog_new_with_buttons (title, + GTK_WINDOW (window), + GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, + NULL); + gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CLOSE); + + gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); + gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE); + gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); + gtk_container_set_border_width (GTK_CONTAINER (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 6); + gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), 8); + + gtk_widget_set_size_request (dialog, 500, 300); + + /* Add text */ + + scrolled = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled), + GTK_SHADOW_ETCHED_IN); + + text_buffer = gtk_text_buffer_new (NULL); + gtk_text_buffer_create_tag (text_buffer, "monospace", + "family", "monospace", NULL); + + text_view = gtk_text_view_new_with_buffer (text_buffer); + g_object_unref (text_buffer); + gtk_text_view_set_editable (GTK_TEXT_VIEW (text_view), FALSE); + gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (text_view), FALSE); + + /**/ + + vbox = gtk_vbox_new (FALSE, 6); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 5); + + gtk_container_add (GTK_CONTAINER (scrolled), text_view); + gtk_box_pack_start (GTK_BOX (vbox), scrolled, + TRUE, TRUE, 0); + + gtk_widget_show_all (vbox); + gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), + vbox, + TRUE, TRUE, 0); + + /* signals */ + + g_signal_connect (G_OBJECT (dialog), + "response", + G_CALLBACK (gtk_widget_destroy), + NULL); + + g_signal_connect (G_OBJECT (dialog), + "unrealize", + G_CALLBACK (last_output_window__unrealize_cb), + NULL); + + /**/ + + gtk_text_buffer_get_iter_at_offset (text_buffer, &iter, 0); + scan = window->archive->process->out.raw; + for (; scan; scan = scan->next) { + char *line = scan->data; + char *utf8_line; + gsize bytes_written; + + utf8_line = g_locale_to_utf8 (line, -1, NULL, &bytes_written, NULL); + gtk_text_buffer_insert_with_tags_by_name (text_buffer, + &iter, + utf8_line, + bytes_written, + "monospace", NULL); + g_free (utf8_line); + gtk_text_buffer_insert (text_buffer, &iter, "\n", 1); + } + + /**/ + + pref_util_restore_window_geometry (GTK_WINDOW (dialog), LAST_OUTPUT_DIALOG_NAME); +} + + +/* -- fr_window_rename_selection -- */ + + +typedef struct { + char *path_to_rename; + char *old_name; + char *new_name; + char *current_dir; + gboolean is_dir; + gboolean dir_in_archive; + char *original_path; +} RenameData; + + +static RenameData* +rename_data_new (const char *path_to_rename, + const char *old_name, + const char *new_name, + const char *current_dir, + gboolean is_dir, + gboolean dir_in_archive, + const char *original_path) +{ + RenameData *rdata; + + rdata = g_new0 (RenameData, 1); + rdata->path_to_rename = g_strdup (path_to_rename); + if (old_name != NULL) + rdata->old_name = g_strdup (old_name); + if (new_name != NULL) + rdata->new_name = g_strdup (new_name); + if (current_dir != NULL) + rdata->current_dir = g_strdup (current_dir); + rdata->is_dir = is_dir; + rdata->dir_in_archive = dir_in_archive; + if (original_path != NULL) + rdata->original_path = g_strdup (original_path); + + return rdata; +} + + +static void +rename_data_free (RenameData *rdata) +{ + g_return_if_fail (rdata != NULL); + + g_free (rdata->path_to_rename); + g_free (rdata->old_name); + g_free (rdata->new_name); + g_free (rdata->current_dir); + g_free (rdata->original_path); + g_free (rdata); +} + + +static void +rename_selection (FrWindow *window, + const char *path_to_rename, + const char *old_name, + const char *new_name, + const char *current_dir, + gboolean is_dir, + gboolean dir_in_archive, + const char *original_path) +{ + FrArchive *archive = window->archive; + RenameData *rdata; + char *tmp_dir; + GList *file_list; + gboolean added_dir; + char *new_dirname; + GList *new_file_list; + GList *scan; + + rdata = rename_data_new (path_to_rename, + old_name, + new_name, + current_dir, + is_dir, + dir_in_archive, + original_path); + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_RENAME, + rdata, + (GFreeFunc) rename_data_free); + + fr_process_clear (archive->process); + + tmp_dir = get_temp_work_dir (NULL); + + if (is_dir) + file_list = get_dir_list_from_path (window, rdata->path_to_rename); + else + file_list = g_list_append (NULL, g_strdup (rdata->path_to_rename)); + + fr_archive_extract_to_local (archive, + file_list, + tmp_dir, + NULL, + FALSE, + TRUE, + FALSE, + window->priv->password); + + /* temporarily add the dir to rename to the list if it's stored in the + * archive, this way it will be removed from the archive... */ + added_dir = FALSE; + if (is_dir && dir_in_archive && ! g_list_find_custom (file_list, original_path, (GCompareFunc) strcmp)) { + file_list = g_list_prepend (file_list, g_strdup (original_path)); + added_dir = TRUE; + } + + fr_archive_remove (archive, file_list, window->priv->compression); + fr_window_clipboard_remove_file_list (window, file_list); + + /* ...and remove it from the list again */ + if (added_dir) { + GList *tmp; + + tmp = file_list; + file_list = g_list_remove_link (file_list, tmp); + + g_free (tmp->data); + g_list_free (tmp); + } + + /* rename the files. */ + + new_dirname = g_build_filename (rdata->current_dir + 1, rdata->new_name, "/", NULL); + new_file_list = NULL; + if (rdata->is_dir) { + char *old_path; + char *new_path; + + old_path = g_build_filename (tmp_dir, rdata->current_dir, rdata->old_name, NULL); + new_path = g_build_filename (tmp_dir, rdata->current_dir, rdata->new_name, NULL); + + fr_process_begin_command (archive->process, "mv"); + fr_process_add_arg (archive->process, "-f"); + fr_process_add_arg (archive->process, old_path); + fr_process_add_arg (archive->process, new_path); + fr_process_end_command (archive->process); + + g_free (old_path); + g_free (new_path); + } + + for (scan = file_list; scan; scan = scan->next) { + const char *current_dir_relative = rdata->current_dir + 1; + const char *filename = (char*) scan->data; + char *old_path = NULL, *common = NULL, *new_path = NULL; + char *new_filename; + + old_path = g_build_filename (tmp_dir, filename, NULL); + + if (strlen (filename) > (strlen (rdata->current_dir) + strlen (rdata->old_name))) + common = g_strdup (filename + strlen (rdata->current_dir) + strlen (rdata->old_name)); + new_path = g_build_filename (tmp_dir, rdata->current_dir, rdata->new_name, common, NULL); + + if (! rdata->is_dir) { + fr_process_begin_command (archive->process, "mv"); + fr_process_add_arg (archive->process, "-f"); + fr_process_add_arg (archive->process, old_path); + fr_process_add_arg (archive->process, new_path); + fr_process_end_command (archive->process); + } + + new_filename = g_build_filename (current_dir_relative, rdata->new_name, common, NULL); + new_file_list = g_list_prepend (new_file_list, new_filename); + + g_free (old_path); + g_free (common); + g_free (new_path); + } + new_file_list = g_list_reverse (new_file_list); + + /* FIXME: this is broken for tar archives. + if (is_dir && dir_in_archive && ! g_list_find_custom (new_file_list, new_dirname, (GCompareFunc) strcmp)) + new_file_list = g_list_prepend (new_file_list, g_build_filename (rdata->current_dir + 1, rdata->new_name, NULL)); + */ + + fr_archive_add (archive, + new_file_list, + tmp_dir, + NULL, + FALSE, + FALSE, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); + + g_free (new_dirname); + path_list_free (new_file_list); + path_list_free (file_list); + + /* remove the tmp dir */ + + fr_process_begin_command (archive->process, "rm"); + fr_process_set_working_dir (archive->process, g_get_tmp_dir ()); + fr_process_set_sticky (archive->process, TRUE); + fr_process_add_arg (archive->process, "-rf"); + fr_process_add_arg (archive->process, tmp_dir); + fr_process_end_command (archive->process); + + fr_process_start (archive->process); + + g_free (tmp_dir); +} + + +static gboolean +valid_name (const char *new_name, + const char *old_name, + char **reason) +{ + char *utf8_new_name; + gboolean retval = TRUE; + + new_name = eat_spaces (new_name); + utf8_new_name = g_filename_display_name (new_name); + + if (*new_name == '\0') { + /* Translators: the name references to a filename. This message can appear when renaming a file. */ + *reason = g_strdup_printf ("%s\n\n%s", _("The new name is void."), _("Please use a different name.")); + retval = FALSE; + } + else if (strcmp (new_name, old_name) == 0) { + /* Translators: the name references to a filename. This message can appear when renaming a file. */ + *reason = g_strdup_printf ("%s\n\n%s", _("The new name is equal to the old one."), _("Please use a different name.")); + retval = FALSE; + } + else if (strchrs (new_name, BAD_CHARS)) { + /* Translators: the name references to a filename. This message can appear when renaming a file. */ + *reason = g_strdup_printf (_("The name \"%s\" is not valid because it cannot contain the characters: %s\n\n%s"), utf8_new_name, BAD_CHARS, _("Please use a different name.")); + retval = FALSE; + } + + g_free (utf8_new_name); + + return retval; +} + + +static gboolean +name_is_present (FrWindow *window, + const char *current_dir, + const char *new_name, + char **reason) +{ + gboolean retval = FALSE; + int i; + char *new_filename; + int new_filename_l; + + *reason = NULL; + + new_filename = g_build_filename (current_dir, new_name, NULL); + new_filename_l = strlen (new_filename); + + for (i = 0; i < window->archive->command->files->len; i++) { + FileData *fdata = g_ptr_array_index (window->archive->command->files, i); + const char *filename = fdata->full_path; + + if ((strncmp (filename, new_filename, new_filename_l) == 0) + && ((filename[new_filename_l] == '\0') + || (filename[new_filename_l] == G_DIR_SEPARATOR))) { + char *utf8_name = g_filename_display_name (new_name); + + if (filename[new_filename_l] == G_DIR_SEPARATOR) + *reason = g_strdup_printf (_("A folder named \"%s\" already exists.\n\n%s"), utf8_name, _("Please use a different name.")); + else + *reason = g_strdup_printf (_("A file named \"%s\" already exists.\n\n%s"), utf8_name, _("Please use a different name.")); + + retval = TRUE; + break; + } + } + + g_free (new_filename); + + return retval; +} + + +void +fr_window_rename_selection (FrWindow *window, + gboolean from_sidebar) +{ + char *path_to_rename; + char *parent_dir; + char *old_name; + gboolean renaming_dir = FALSE; + gboolean dir_in_archive = FALSE; + char *original_path = NULL; + char *utf8_old_name; + char *utf8_new_name; + + if (from_sidebar) { + path_to_rename = fr_window_get_selected_folder_in_tree_view (window); + if (path_to_rename == NULL) + return; + parent_dir = remove_level_from_path (path_to_rename); + old_name = g_strdup (file_name_from_path (path_to_rename)); + renaming_dir = TRUE; + } + else { + FileData *selected_item; + + selected_item = fr_window_get_selected_item_from_file_list (window); + if (selected_item == NULL) + return; + + renaming_dir = file_data_is_dir (selected_item); + dir_in_archive = selected_item->dir; + original_path = g_strdup (selected_item->original_path); + + if (renaming_dir && ! dir_in_archive) { + parent_dir = g_strdup (fr_window_get_current_location (window)); + old_name = g_strdup (selected_item->list_name); + path_to_rename = g_build_filename (parent_dir, old_name, NULL); + } + else { + if (renaming_dir) { + path_to_rename = remove_ending_separator (selected_item->full_path); + parent_dir = remove_level_from_path (path_to_rename); + } + else { + path_to_rename = g_strdup (selected_item->original_path); + parent_dir = remove_level_from_path (selected_item->full_path); + } + old_name = g_strdup (selected_item->name); + } + + file_data_free (selected_item); + } + + retry__rename_selection: + utf8_old_name = g_locale_to_utf8 (old_name, -1 ,0 ,0 ,0); + utf8_new_name = _gtk_request_dialog_run (GTK_WINDOW (window), + (GTK_DIALOG_DESTROY_WITH_PARENT + | GTK_DIALOG_MODAL), + _("Rename"), + (renaming_dir ? _("New folder name") : _("New file name")), + utf8_old_name, + 1024, + GTK_STOCK_CANCEL, + _("_Rename")); + g_free (utf8_old_name); + + if (utf8_new_name != NULL) { + char *new_name; + char *reason = NULL; + + new_name = g_filename_from_utf8 (utf8_new_name, -1, 0, 0, 0); + g_free (utf8_new_name); + + if (! valid_name (new_name, old_name, &reason)) { + char *utf8_name = g_filename_display_name (new_name); + GtkWidget *dlg; + + dlg = _gtk_error_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_DESTROY_WITH_PARENT, + NULL, + (renaming_dir ? _("Could not rename the folder") : _("Could not rename the file")), + "%s", + reason); + gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + + g_free (reason); + g_free (utf8_name); + g_free (new_name); + + goto retry__rename_selection; + } + + if (name_is_present (window, parent_dir, new_name, &reason)) { + GtkWidget *dlg; + int r; + + dlg = _gtk_message_dialog_new (GTK_WINDOW (window), + GTK_DIALOG_MODAL, + GTK_STOCK_DIALOG_QUESTION, + (renaming_dir ? _("Could not rename the folder") : _("Could not rename the file")), + reason, + GTK_STOCK_CLOSE, GTK_RESPONSE_OK, + NULL); + r = gtk_dialog_run (GTK_DIALOG (dlg)); + gtk_widget_destroy (dlg); + g_free (reason); + g_free (new_name); + goto retry__rename_selection; + } + + rename_selection (window, + path_to_rename, + old_name, + new_name, + parent_dir, + renaming_dir, + dir_in_archive, + original_path); + + g_free (new_name); + } + + g_free (old_name); + g_free (parent_dir); + g_free (path_to_rename); + g_free (original_path); +} + + +static void +fr_clipboard_get (GtkClipboard *clipboard, + GtkSelectionData *selection_data, + guint info, + gpointer user_data_or_owner) +{ + FrWindow *window = user_data_or_owner; + char *data; + + if (gtk_selection_data_get_target (selection_data) != FR_SPECIAL_URI_LIST) + return; + + data = get_selection_data_from_clipboard_data (window, window->priv->copy_data); + gtk_selection_data_set (selection_data, + gtk_selection_data_get_target (selection_data), + 8, + (guchar *) data, + strlen (data)); + g_free (data); +} + + +static void +fr_clipboard_clear (GtkClipboard *clipboard, + gpointer user_data_or_owner) +{ + FrWindow *window = user_data_or_owner; + + if (window->priv->copy_data != NULL) { + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = NULL; + } +} + + +GList * +fr_window_get_selection (FrWindow *window, + gboolean from_sidebar, + char **return_base_dir) +{ + GList *files; + char *base_dir; + + if (from_sidebar) { + char *selected_folder; + char *parent_folder; + + files = fr_window_get_folder_tree_selection (window, TRUE, NULL); + selected_folder = fr_window_get_selected_folder_in_tree_view (window); + parent_folder = remove_level_from_path (selected_folder); + if (parent_folder == NULL) + base_dir = g_strdup ("/"); + else if (parent_folder[strlen (parent_folder) - 1] == '/') + base_dir = g_strdup (parent_folder); + else + base_dir = g_strconcat (parent_folder, "/", NULL); + g_free (selected_folder); + g_free (parent_folder); + } + else { + files = fr_window_get_file_list_selection (window, TRUE, NULL); + base_dir = g_strdup (fr_window_get_current_location (window)); + } + + if (return_base_dir) + *return_base_dir = base_dir; + else + g_free (base_dir); + + return files; +} + + +static void +fr_window_copy_or_cut_selection (FrWindow *window, + FRClipboardOp op, + gboolean from_sidebar) +{ + GList *files; + char *base_dir; + GtkClipboard *clipboard; + + files = fr_window_get_selection (window, from_sidebar, &base_dir); + + if (window->priv->copy_data != NULL) + fr_clipboard_data_unref (window->priv->copy_data); + window->priv->copy_data = fr_clipboard_data_new (); + window->priv->copy_data->files = files; + window->priv->copy_data->op = op; + window->priv->copy_data->base_dir = base_dir; + + clipboard = gtk_clipboard_get (FR_CLIPBOARD); + gtk_clipboard_set_with_owner (clipboard, + clipboard_targets, + G_N_ELEMENTS (clipboard_targets), + fr_clipboard_get, + fr_clipboard_clear, + G_OBJECT (window)); + + fr_window_update_sensitivity (window); +} + + +void +fr_window_copy_selection (FrWindow *window, + gboolean from_sidebar) +{ + fr_window_copy_or_cut_selection (window, FR_CLIPBOARD_OP_COPY, from_sidebar); +} + + +void +fr_window_cut_selection (FrWindow *window, + gboolean from_sidebar) +{ + fr_window_copy_or_cut_selection (window, FR_CLIPBOARD_OP_CUT, from_sidebar); +} + + +static gboolean +always_fake_load (FrArchive *archive, + gpointer data) +{ + return TRUE; +} + + +static void +add_pasted_files (FrWindow *window, + FrClipboardData *data) +{ + const char *current_dir_relative = data->current_dir + 1; + GList *scan; + GList *new_file_list = NULL; + + if (window->priv->password_for_paste != NULL) { + g_free (window->priv->password_for_paste); + window->priv->password_for_paste = NULL; + } + + fr_process_clear (window->archive->process); + for (scan = data->files; scan; scan = scan->next) { + const char *old_name = (char*) scan->data; + char *new_name = g_build_filename (current_dir_relative, old_name + strlen (data->base_dir) - 1, NULL); + + /* skip folders */ + + if ((strcmp (old_name, new_name) != 0) + && (old_name[strlen (old_name) - 1] != '/')) + { + fr_process_begin_command (window->archive->process, "mv"); + fr_process_set_working_dir (window->archive->process, data->tmp_dir); + fr_process_add_arg (window->archive->process, "-f"); + fr_process_add_arg (window->archive->process, old_name); + fr_process_add_arg (window->archive->process, new_name); + fr_process_end_command (window->archive->process); + } + + new_file_list = g_list_prepend (new_file_list, new_name); + } + + fr_archive_add (window->archive, + new_file_list, + data->tmp_dir, + NULL, + FALSE, + FALSE, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); + + path_list_free (new_file_list); + + /* remove the tmp dir */ + + fr_process_begin_command (window->archive->process, "rm"); + fr_process_set_working_dir (window->archive->process, g_get_tmp_dir ()); + fr_process_set_sticky (window->archive->process, TRUE); + fr_process_add_arg (window->archive->process, "-rf"); + fr_process_add_arg (window->archive->process, data->tmp_dir); + fr_process_end_command (window->archive->process); + + fr_process_start (window->archive->process); +} + + +static void +copy_from_archive_action_performed_cb (FrArchive *archive, + FrAction action, + FrProcError *error, + gpointer data) +{ + FrWindow *window = data; + gboolean continue_batch = FALSE; + +#ifdef DEBUG + debug (DEBUG_INFO, "%s [DONE] (FR::Window)\n", action_names[action]); +#endif + + fr_window_stop_activity_mode (window); + fr_window_pop_message (window); + close_progress_dialog (window, FALSE); + + if (error->type == FR_PROC_ERROR_ASK_PASSWORD) { + dlg_ask_password_for_paste_operation (window); + return; + } + + continue_batch = handle_errors (window, archive, action, error); + + if (error->type != FR_PROC_ERROR_NONE) { + fr_clipboard_data_unref (window->priv->clipboard_data); + window->priv->clipboard_data = NULL; + return; + } + + switch (action) { + case FR_ACTION_LISTING_CONTENT: + fr_process_clear (window->priv->copy_from_archive->process); + fr_archive_extract_to_local (window->priv->copy_from_archive, + window->priv->clipboard_data->files, + window->priv->clipboard_data->tmp_dir, + NULL, + FALSE, + TRUE, + FALSE, + window->priv->clipboard_data->archive_password); + fr_process_start (window->priv->copy_from_archive->process); + break; + + case FR_ACTION_EXTRACTING_FILES: + if (window->priv->clipboard_data->op == FR_CLIPBOARD_OP_CUT) { + fr_process_clear (window->priv->copy_from_archive->process); + fr_archive_remove (window->priv->copy_from_archive, + window->priv->clipboard_data->files, + window->priv->compression); + fr_process_start (window->priv->copy_from_archive->process); + } + else + add_pasted_files (window, window->priv->clipboard_data); + break; + + case FR_ACTION_DELETING_FILES: + add_pasted_files (window, window->priv->clipboard_data); + break; + + default: + break; + } +} + + +static void +fr_window_paste_from_clipboard_data (FrWindow *window, + FrClipboardData *data) +{ + const char *current_dir_relative; + GHashTable *created_dirs; + GList *scan; + + if (window->priv->password_for_paste != NULL) + fr_clipboard_data_set_password (data, window->priv->password_for_paste); + + if (window->priv->clipboard_data != data) { + fr_clipboard_data_unref (window->priv->clipboard_data); + window->priv->clipboard_data = data; + } + + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_PASTE, + fr_clipboard_data_ref (data), + (GFreeFunc) fr_clipboard_data_unref); + + current_dir_relative = data->current_dir + 1; + + data->tmp_dir = get_temp_work_dir (NULL); + created_dirs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + for (scan = data->files; scan; scan = scan->next) { + const char *old_name = (char*) scan->data; + char *new_name = g_build_filename (current_dir_relative, old_name + strlen (data->base_dir) - 1, NULL); + char *dir = remove_level_from_path (new_name); + + if ((dir != NULL) && (g_hash_table_lookup (created_dirs, dir) == NULL)) { + char *dir_path; + + dir_path = g_build_filename (data->tmp_dir, dir, NULL); + debug (DEBUG_INFO, "mktree %s\n", dir_path); + make_directory_tree_from_path (dir_path, 0700, NULL); + + g_free (dir_path); + g_hash_table_replace (created_dirs, g_strdup (dir), "1"); + } + + g_free (dir); + g_free (new_name); + } + g_hash_table_destroy (created_dirs); + + /**/ + + if (window->priv->copy_from_archive == NULL) { + window->priv->copy_from_archive = fr_archive_new (); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "start", + G_CALLBACK (action_started), + window); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "done", + G_CALLBACK (copy_from_archive_action_performed_cb), + window); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "progress", + G_CALLBACK (fr_window_progress_cb), + window); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "message", + G_CALLBACK (fr_window_message_cb), + window); + g_signal_connect (G_OBJECT (window->priv->copy_from_archive), + "stoppable", + G_CALLBACK (fr_window_stoppable_cb), + window); + fr_archive_set_fake_load_func (window->priv->copy_from_archive, always_fake_load, NULL); + } + fr_archive_load_local (window->priv->copy_from_archive, + data->archive_filename, + data->archive_password); +} + + +static void +fr_window_paste_selection_to (FrWindow *window, + const char *current_dir) +{ + GtkClipboard *clipboard; + GtkSelectionData *selection_data; + FrClipboardData *paste_data; + + clipboard = gtk_clipboard_get (FR_CLIPBOARD); + selection_data = gtk_clipboard_wait_for_contents (clipboard, FR_SPECIAL_URI_LIST); + if (selection_data == NULL) + return; + + paste_data = get_clipboard_data_from_selection_data (window, (char*) gtk_selection_data_get_data (selection_data)); + paste_data->current_dir = g_strdup (current_dir); + fr_window_paste_from_clipboard_data (window, paste_data); + + gtk_selection_data_free (selection_data); +} + + +void +fr_window_paste_selection (FrWindow *window, + gboolean from_sidebar) +{ + char *utf8_path, *utf8_old_path, *destination; + char *current_dir; + + if (window->priv->list_mode == FR_WINDOW_LIST_MODE_FLAT) + return; + + /**/ + + utf8_old_path = g_filename_to_utf8 (fr_window_get_current_location (window), -1, NULL, NULL, NULL); + utf8_path = _gtk_request_dialog_run (GTK_WINDOW (window), + (GTK_DIALOG_DESTROY_WITH_PARENT + | GTK_DIALOG_MODAL), + _("Paste Selection"), + _("Destination folder"), + utf8_old_path, + 1024, + GTK_STOCK_CANCEL, + GTK_STOCK_PASTE); + g_free (utf8_old_path); + if (utf8_path == NULL) + return; + + destination = g_filename_from_utf8 (utf8_path, -1, NULL, NULL, NULL); + g_free (utf8_path); + + if (destination[0] != '/') + current_dir = build_uri (fr_window_get_current_location (window), destination, NULL); + else + current_dir = g_strdup (destination); + g_free (destination); + + fr_window_paste_selection_to (window, current_dir); + + g_free (current_dir); +} + + +/* -- fr_window_open_files -- */ + + +void +fr_window_open_files_with_command (FrWindow *window, + GList *file_list, + char *command) +{ + GAppInfo *app; + GError *error = NULL; + + app = g_app_info_create_from_commandline (command, NULL, G_APP_INFO_CREATE_NONE, &error); + if (error != NULL) { + _gtk_error_dialog_run (GTK_WINDOW (window), + _("Could not perform the operation"), + "%s", + error->message); + g_clear_error (&error); + return; + } + + fr_window_open_files_with_application (window, file_list, app); +} + + +void +fr_window_open_files_with_application (FrWindow *window, + GList *file_list, + GAppInfo *app) +{ + GList *uris = NULL, *scan; + GError *error = NULL; + + if (window->priv->activity_ref > 0) + return; + + for (scan = file_list; scan; scan = scan->next) + uris = g_list_prepend (uris, g_filename_to_uri (scan->data, NULL, NULL)); + + if (! g_app_info_launch_uris (app, uris, NULL, &error)) { + _gtk_error_dialog_run (GTK_WINDOW (window), + _("Could not perform the operation"), + "%s", + error->message); + g_clear_error (&error); + } + else { + char *uri; + const char *mime_type; + + uri = g_filename_to_uri (file_list->data, NULL, NULL); + mime_type = get_file_mime_type (uri, FALSE); + if (mime_type != NULL) + g_app_info_set_as_default_for_type (app, mime_type, NULL); + g_free (uri); + } + + path_list_free (uris); +} + + +typedef struct { + FrWindow *window; + GList *file_list; + gboolean ask_application; + CommandData *cdata; +} OpenFilesData; + + +static OpenFilesData* +open_files_data_new (FrWindow *window, + GList *file_list, + gboolean ask_application) + +{ + OpenFilesData *odata; + GList *scan; + + odata = g_new0 (OpenFilesData, 1); + odata->window = window; + odata->file_list = path_list_dup (file_list); + odata->ask_application = ask_application; + odata->cdata = g_new0 (CommandData, 1); + odata->cdata->temp_dir = get_temp_work_dir (NULL); + odata->cdata->file_list = NULL; + for (scan = file_list; scan; scan = scan->next) { + char *file = scan->data; + char *filename; + + filename = g_strconcat (odata->cdata->temp_dir, + "/", + file, + NULL); + odata->cdata->file_list = g_list_prepend (odata->cdata->file_list, filename); + } + + /* Add to CommandList so the cdata is released on exit. */ + CommandList = g_list_prepend (CommandList, odata->cdata); + + return odata; +} + + +static void +open_files_data_free (OpenFilesData *odata) +{ + g_return_if_fail (odata != NULL); + + path_list_free (odata->file_list); + g_free (odata); +} + + +void +fr_window_update_dialog_closed (FrWindow *window) +{ + window->priv->update_dialog = NULL; +} + + +gboolean +fr_window_update_files (FrWindow *window, + GList *file_list) +{ + GList *scan; + + if (window->priv->activity_ref > 0) + return FALSE; + + if (window->archive->read_only) + return FALSE; + + fr_process_clear (window->archive->process); + + for (scan = file_list; scan; scan = scan->next) { + OpenFile *file = scan->data; + GList *local_file_list; + + local_file_list = g_list_append (NULL, file->path); + fr_archive_add (window->archive, + local_file_list, + file->temp_dir, + "/", + FALSE, + FALSE, + window->priv->password, + window->priv->encrypt_header, + window->priv->compression, + window->priv->volume_size); + g_list_free (local_file_list); + } + + fr_process_start (window->archive->process); + + return TRUE; +} + + +static void +open_file_modified_cb (GFileMonitor *monitor, + GFile *monitor_file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FrWindow *window = user_data; + char *monitor_uri; + OpenFile *file; + GList *scan; + + if ((event_type != G_FILE_MONITOR_EVENT_CHANGED) + && (event_type != G_FILE_MONITOR_EVENT_CREATED)) + { + return; + } + + monitor_uri = g_file_get_uri (monitor_file); + file = NULL; + for (scan = window->priv->open_files; scan; scan = scan->next) { + OpenFile *test = scan->data; + if (uricmp (test->extracted_uri, monitor_uri) == 0) { + file = test; + break; + } + } + g_free (monitor_uri); + + g_return_if_fail (file != NULL); + + if (window->priv->update_dialog == NULL) + window->priv->update_dialog = dlg_update (window); + dlg_update_add_file (window->priv->update_dialog, file); +} + + +static void +fr_window_monitor_open_file (FrWindow *window, + OpenFile *file) +{ + GFile *f; + + window->priv->open_files = g_list_prepend (window->priv->open_files, file); + f = g_file_new_for_uri (file->extracted_uri); + file->monitor = g_file_monitor_file (f, 0, NULL, NULL); + g_signal_connect (file->monitor, + "changed", + G_CALLBACK (open_file_modified_cb), + window); + g_object_unref (f); +} + + +static void +monitor_extracted_files (OpenFilesData *odata) +{ + FrWindow *window = odata->window; + GList *scan1, *scan2; + + for (scan1 = odata->file_list, scan2 = odata->cdata->file_list; + scan1 && scan2; + scan1 = scan1->next, scan2 = scan2->next) + { + OpenFile *ofile; + const char *file = scan1->data; + const char *extracted_path = scan2->data; + + ofile = open_file_new (file, extracted_path, odata->cdata->temp_dir); + if (ofile != NULL) + fr_window_monitor_open_file (window, ofile); + } +} + + +static gboolean +fr_window_open_extracted_files (OpenFilesData *odata) +{ + GList *file_list = odata->cdata->file_list; + gboolean result = FALSE; + const char *first_file; + const char *first_mime_type; + GAppInfo *app; + GList *files_to_open = NULL; + GError *error = NULL; + + g_return_val_if_fail (file_list != NULL, FALSE); + + first_file = (char*) file_list->data; + if (first_file == NULL) + return FALSE; + + if (! odata->window->archive->read_only) + monitor_extracted_files (odata); + + if (odata->ask_application) { + dlg_open_with (odata->window, file_list); + return FALSE; + } + + first_mime_type = get_file_mime_type_for_path (first_file, FALSE); + app = g_app_info_get_default_for_type (first_mime_type, FALSE); + + if (app == NULL) { + dlg_open_with (odata->window, file_list); + return FALSE; + } + + files_to_open = g_list_append (files_to_open, g_filename_to_uri (first_file, NULL, NULL)); + + if (g_app_info_supports_files (app)) { + GList *scan; + + for (scan = file_list->next; scan; scan = scan->next) { + const char *path = scan->data; + const char *mime_type; + + mime_type = get_file_mime_type_for_path (path, FALSE); + if (mime_type == NULL) + continue; + + if (strcmp (mime_type, first_mime_type) == 0) { + files_to_open = g_list_append (files_to_open, g_filename_to_uri (path, NULL, NULL)); + } + else { + GAppInfo *app2; + + app2 = g_app_info_get_default_for_type (mime_type, FALSE); + if (g_app_info_equal (app, app2)) + files_to_open = g_list_append (files_to_open, g_filename_to_uri (path, NULL, NULL)); + g_object_unref (app2); + } + } + } + + result = g_app_info_launch_uris (app, files_to_open, NULL, &error); + if (! result) { + _gtk_error_dialog_run (GTK_WINDOW (odata->window), + _("Could not perform the operation"), + "%s", + error->message); + g_clear_error (&error); + } + + g_object_unref (app); + path_list_free (files_to_open); + + return result; +} + + +static void +fr_window_open_files__extract_done_cb (FrArchive *archive, + FrAction action, + FrProcError *error, + gpointer callback_data) +{ + OpenFilesData *odata = callback_data; + + g_signal_handlers_disconnect_matched (G_OBJECT (archive), + G_SIGNAL_MATCH_DATA, + 0, + 0, NULL, + 0, + odata); + + if (error->type == FR_PROC_ERROR_NONE) + fr_window_open_extracted_files (odata); +} + + +void +fr_window_open_files (FrWindow *window, + GList *file_list, + gboolean ask_application) +{ + OpenFilesData *odata; + + if (window->priv->activity_ref > 0) + return; + + odata = open_files_data_new (window, file_list, ask_application); + fr_window_set_current_batch_action (window, + FR_BATCH_ACTION_OPEN_FILES, + odata, + (GFreeFunc) open_files_data_free); + + g_signal_connect (G_OBJECT (window->archive), + "done", + G_CALLBACK (fr_window_open_files__extract_done_cb), + odata); + + fr_process_clear (window->archive->process); + fr_archive_extract_to_local (window->archive, + odata->file_list, + odata->cdata->temp_dir, + NULL, + FALSE, + TRUE, + FALSE, + window->priv->password); + fr_process_start (window->archive->process); +} + + +/**/ + + +static char* +get_default_dir (const char *dir) +{ + if (! is_temp_dir (dir)) + return g_strdup (dir); + else + return NULL; +} + + +void +fr_window_set_open_default_dir (FrWindow *window, + const char *default_dir) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (default_dir != NULL); + + if (window->priv->open_default_dir != NULL) + g_free (window->priv->open_default_dir); + window->priv->open_default_dir = get_default_dir (default_dir); +} + + +const char * +fr_window_get_open_default_dir (FrWindow *window) +{ + if (window->priv->open_default_dir == NULL) + return get_home_uri (); + else + return window->priv->open_default_dir; +} + + +void +fr_window_set_add_default_dir (FrWindow *window, + const char *default_dir) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (default_dir != NULL); + + if (window->priv->add_default_dir != NULL) + g_free (window->priv->add_default_dir); + window->priv->add_default_dir = get_default_dir (default_dir); +} + + +const char * +fr_window_get_add_default_dir (FrWindow *window) +{ + if (window->priv->add_default_dir == NULL) + return get_home_uri (); + else + return window->priv->add_default_dir; +} + + +void +fr_window_set_extract_default_dir (FrWindow *window, + const char *default_dir, + gboolean freeze) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (default_dir != NULL); + + /* do not change this dir while it's used by the non-interactive + * extraction operation. */ + if (window->priv->extract_interact_use_default_dir) + return; + + window->priv->extract_interact_use_default_dir = freeze; + + if (window->priv->extract_default_dir != NULL) + g_free (window->priv->extract_default_dir); + window->priv->extract_default_dir = get_default_dir (default_dir); +} + + +const char * +fr_window_get_extract_default_dir (FrWindow *window) +{ + if (window->priv->extract_default_dir == NULL) + return get_home_uri (); + else + return window->priv->extract_default_dir; +} + + +void +fr_window_set_default_dir (FrWindow *window, + const char *default_dir, + gboolean freeze) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (default_dir != NULL); + + window->priv->freeze_default_dir = freeze; + + fr_window_set_open_default_dir (window, default_dir); + fr_window_set_add_default_dir (window, default_dir); + fr_window_set_extract_default_dir (window, default_dir, FALSE); +} + + +void +fr_window_update_columns_visibility (FrWindow *window) +{ + GtkTreeView *tree_view = GTK_TREE_VIEW (window->priv->list_view); + GtkTreeViewColumn *column; + + column = gtk_tree_view_get_column (tree_view, 1); + gtk_tree_view_column_set_visible (column, eel_mateconf_get_boolean (PREF_LIST_SHOW_SIZE, TRUE)); + + column = gtk_tree_view_get_column (tree_view, 2); + gtk_tree_view_column_set_visible (column, eel_mateconf_get_boolean (PREF_LIST_SHOW_TYPE, TRUE)); + + column = gtk_tree_view_get_column (tree_view, 3); + gtk_tree_view_column_set_visible (column, eel_mateconf_get_boolean (PREF_LIST_SHOW_TIME, TRUE)); + + column = gtk_tree_view_get_column (tree_view, 4); + gtk_tree_view_column_set_visible (column, eel_mateconf_get_boolean (PREF_LIST_SHOW_PATH, TRUE)); +} + + +void +fr_window_set_toolbar_visibility (FrWindow *window, + gboolean visible) +{ + g_return_if_fail (window != NULL); + + if (visible) + gtk_widget_show (window->priv->toolbar); + else + gtk_widget_hide (window->priv->toolbar); + + set_active (window, "ViewToolbar", visible); +} + + +void +fr_window_set_statusbar_visibility (FrWindow *window, + gboolean visible) +{ + g_return_if_fail (window != NULL); + + if (visible) + gtk_widget_show (window->priv->statusbar); + else + gtk_widget_hide (window->priv->statusbar); + + set_active (window, "ViewStatusbar", visible); +} + + +void +fr_window_set_folders_visibility (FrWindow *window, + gboolean value) +{ + g_return_if_fail (window != NULL); + + window->priv->view_folders = value; + fr_window_update_dir_tree (window); + + set_active (window, "ViewFolders", window->priv->view_folders); +} + + +/* -- batch mode procedures -- */ + + +static void fr_window_exec_current_batch_action (FrWindow *window); + + +static void +fr_window_exec_batch_action (FrWindow *window, + FRBatchAction *action) +{ + ExtractData *edata; + RenameData *rdata; + OpenFilesData *odata; + SaveAsData *sdata; + + switch (action->type) { + case FR_BATCH_ACTION_LOAD: + debug (DEBUG_INFO, "[BATCH] LOAD\n"); + + if (! uri_exists ((char*) action->data)) + fr_window_archive_new (window, (char*) action->data); + else + fr_window_archive_open (window, (char*) action->data, GTK_WINDOW (window)); + break; + + case FR_BATCH_ACTION_ADD: + debug (DEBUG_INFO, "[BATCH] ADD\n"); + + fr_window_archive_add_dropped_items (window, (GList*) action->data, FALSE); + break; + + case FR_BATCH_ACTION_OPEN: + debug (DEBUG_INFO, "[BATCH] OPEN\n"); + + fr_window_push_message (window, _("Add files to an archive")); + dlg_batch_add_files (window, (GList*) action->data); + break; + + case FR_BATCH_ACTION_EXTRACT: + debug (DEBUG_INFO, "[BATCH] EXTRACT\n"); + + edata = action->data; + fr_window_archive_extract (window, + edata->file_list, + edata->extract_to_dir, + edata->base_dir, + edata->skip_older, + edata->overwrite, + edata->junk_paths, + TRUE); + break; + + case FR_BATCH_ACTION_EXTRACT_HERE: + debug (DEBUG_INFO, "[BATCH] EXTRACT HERE\n"); + + edata = action->data; + fr_window_archive_extract_here (window, + FALSE, + TRUE, + FALSE); + break; + + case FR_BATCH_ACTION_EXTRACT_INTERACT: + debug (DEBUG_INFO, "[BATCH] EXTRACT_INTERACT\n"); + + if (window->priv->extract_interact_use_default_dir + && (window->priv->extract_default_dir != NULL)) + { + fr_window_archive_extract (window, + NULL, + window->priv->extract_default_dir, + NULL, + FALSE, + TRUE, + FALSE, + TRUE); + } + else { + fr_window_push_message (window, _("Extract archive")); + dlg_extract (NULL, window); + } + break; + + case FR_BATCH_ACTION_RENAME: + debug (DEBUG_INFO, "[BATCH] RENAME\n"); + + rdata = action->data; + rename_selection (window, + rdata->path_to_rename, + rdata->old_name, + rdata->new_name, + rdata->current_dir, + rdata->is_dir, + rdata->dir_in_archive, + rdata->original_path); + break; + + case FR_BATCH_ACTION_PASTE: + debug (DEBUG_INFO, "[BATCH] PASTE\n"); + + fr_window_paste_from_clipboard_data (window, (FrClipboardData*) action->data); + break; + + case FR_BATCH_ACTION_OPEN_FILES: + debug (DEBUG_INFO, "[BATCH] OPEN FILES\n"); + + odata = action->data; + fr_window_open_files (window, odata->file_list, odata->ask_application); + break; + + case FR_BATCH_ACTION_SAVE_AS: + debug (DEBUG_INFO, "[BATCH] SAVE_AS\n"); + + sdata = action->data; + fr_window_archive_save_as (window, + sdata->uri, + sdata->password, + sdata->encrypt_header, + sdata->volume_size); + break; + + case FR_BATCH_ACTION_TEST: + debug (DEBUG_INFO, "[BATCH] TEST\n"); + + fr_window_archive_test (window); + break; + + case FR_BATCH_ACTION_CLOSE: + debug (DEBUG_INFO, "[BATCH] CLOSE\n"); + + fr_window_archive_close (window); + fr_window_exec_next_batch_action (window); + break; + + case FR_BATCH_ACTION_QUIT: + debug (DEBUG_INFO, "[BATCH] QUIT\n"); + + gtk_widget_destroy (GTK_WIDGET (window)); + break; + + default: + break; + } +} + + +void +fr_window_reset_current_batch_action (FrWindow *window) +{ + FRBatchAction *adata = &window->priv->current_batch_action; + + if ((adata->data != NULL) && (adata->free_func != NULL)) + (*adata->free_func) (adata->data); + adata->type = FR_BATCH_ACTION_NONE; + adata->data = NULL; + adata->free_func = NULL; +} + + +void +fr_window_set_current_batch_action (FrWindow *window, + FrBatchActionType action, + void *data, + GFreeFunc free_func) +{ + FRBatchAction *adata = &window->priv->current_batch_action; + + fr_window_reset_current_batch_action (window); + + adata->type = action; + adata->data = data; + adata->free_func = free_func; +} + + +void +fr_window_restart_current_batch_action (FrWindow *window) +{ + fr_window_exec_batch_action (window, &window->priv->current_batch_action); +} + + +void +fr_window_append_batch_action (FrWindow *window, + FrBatchActionType action, + void *data, + GFreeFunc free_func) +{ + FRBatchAction *a_desc; + + g_return_if_fail (window != NULL); + + a_desc = g_new0 (FRBatchAction, 1); + a_desc->type = action; + a_desc->data = data; + a_desc->free_func = free_func; + + window->priv->batch_action_list = g_list_append (window->priv->batch_action_list, a_desc); +} + + +static void +fr_window_exec_current_batch_action (FrWindow *window) +{ + FRBatchAction *action; + + if (window->priv->batch_action == NULL) { + window->priv->batch_mode = FALSE; + return; + } + action = (FRBatchAction *) window->priv->batch_action->data; + fr_window_exec_batch_action (window, action); +} + + +static void +fr_window_exec_next_batch_action (FrWindow *window) +{ + if (window->priv->batch_action != NULL) + window->priv->batch_action = g_list_next (window->priv->batch_action); + else + window->priv->batch_action = window->priv->batch_action_list; + fr_window_exec_current_batch_action (window); +} + + +void +fr_window_start_batch (FrWindow *window) +{ + g_return_if_fail (window != NULL); + + if (window->priv->batch_mode) + return; + + if (window->priv->batch_action_list == NULL) + return; + + window->priv->batch_mode = TRUE; + window->priv->batch_action = window->priv->batch_action_list; + window->archive->can_create_compressed_file = window->priv->batch_adding_one_file; + + fr_window_exec_current_batch_action (window); +} + + +void +fr_window_stop_batch (FrWindow *window) +{ + if (! window->priv->non_interactive) + return; + + window->priv->extract_interact_use_default_dir = FALSE; + window->archive->can_create_compressed_file = FALSE; + + if (window->priv->batch_mode) { + if (! window->priv->showing_error_dialog) { + gtk_widget_destroy (GTK_WIDGET (window)); + return; + } + } + else { + gtk_window_present (GTK_WINDOW (window)); + fr_window_archive_close (window); + } + + window->priv->batch_mode = FALSE; +} + + +void +fr_window_resume_batch (FrWindow *window) +{ + fr_window_exec_current_batch_action (window); +} + + +gboolean +fr_window_is_batch_mode (FrWindow *window) +{ + return window->priv->batch_mode; +} + + +void +fr_window_new_batch (FrWindow *window) +{ + fr_window_free_batch_data (window); + window->priv->non_interactive = TRUE; +} + + +void +fr_window_set_batch__extract_here (FrWindow *window, + const char *filename) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (filename != NULL); + + fr_window_append_batch_action (window, + FR_BATCH_ACTION_LOAD, + g_strdup (filename), + (GFreeFunc) g_free); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_EXTRACT_HERE, + extract_to_data_new (NULL), + (GFreeFunc) extract_data_free); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_CLOSE, + NULL, + NULL); +} + + +void +fr_window_set_batch__extract (FrWindow *window, + const char *filename, + const char *dest_dir) +{ + g_return_if_fail (window != NULL); + g_return_if_fail (filename != NULL); + + fr_window_append_batch_action (window, + FR_BATCH_ACTION_LOAD, + g_strdup (filename), + (GFreeFunc) g_free); + if (dest_dir != NULL) + fr_window_append_batch_action (window, + FR_BATCH_ACTION_EXTRACT, + extract_to_data_new (dest_dir), + (GFreeFunc) extract_data_free); + else + fr_window_append_batch_action (window, + FR_BATCH_ACTION_EXTRACT_INTERACT, + NULL, + NULL); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_CLOSE, + NULL, + NULL); +} + + +void +fr_window_set_batch__add (FrWindow *window, + const char *archive, + GList *file_list) +{ + window->priv->batch_adding_one_file = (file_list->next == NULL) && (uri_is_file (file_list->data)); + + if (archive != NULL) + fr_window_append_batch_action (window, + FR_BATCH_ACTION_LOAD, + g_strdup (archive), + (GFreeFunc) g_free); + else + fr_window_append_batch_action (window, + FR_BATCH_ACTION_OPEN, + file_list, + NULL); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_ADD, + file_list, + NULL); + fr_window_append_batch_action (window, + FR_BATCH_ACTION_CLOSE, + NULL, + NULL); +} |