summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am193
-rw-r--r--src/actions.c873
-rw-r--r--src/actions.h82
-rw-r--r--src/commands/Makefile.am7
-rw-r--r--src/commands/rpm2cpio.c134
-rw-r--r--src/dlg-add-files.c191
-rw-r--r--src/dlg-add-files.h30
-rw-r--r--src/dlg-add-folder.c899
-rw-r--r--src/dlg-add-folder.h30
-rw-r--r--src/dlg-ask-password.c170
-rw-r--r--src/dlg-ask-password.h31
-rw-r--r--src/dlg-batch-add.c611
-rw-r--r--src/dlg-batch-add.h31
-rw-r--r--src/dlg-delete.c189
-rw-r--r--src/dlg-delete.h31
-rw-r--r--src/dlg-extract.c516
-rw-r--r--src/dlg-extract.h32
-rw-r--r--src/dlg-new.c526
-rw-r--r--src/dlg-new.h59
-rw-r--r--src/dlg-open-with.c517
-rw-r--r--src/dlg-open-with.h32
-rw-r--r--src/dlg-package-installer.c294
-rw-r--r--src/dlg-package-installer.h32
-rw-r--r--src/dlg-password.c126
-rw-r--r--src/dlg-password.h31
-rw-r--r--src/dlg-prop.c219
-rw-r--r--src/dlg-prop.h30
-rw-r--r--src/dlg-update.c406
-rw-r--r--src/dlg-update.h34
-rw-r--r--src/egg-macros.h154
-rw-r--r--src/eggfileformatchooser.c1223
-rw-r--r--src/eggfileformatchooser.h85
-rw-r--r--src/eggtreemultidnd.c459
-rw-r--r--src/eggtreemultidnd.h75
-rw-r--r--src/file-data.c152
-rw-r--r--src/file-data.h70
-rw-r--r--src/file-utils.c1395
-rw-r--r--src/file-utils.h130
-rw-r--r--src/fr-archive.c3354
-rw-r--r--src/fr-archive.h219
-rw-r--r--src/fr-command-7z.c686
-rw-r--r--src/fr-command-7z.h55
-rw-r--r--src/fr-command-ace.c342
-rw-r--r--src/fr-command-ace.h62
-rw-r--r--src/fr-command-alz.c406
-rw-r--r--src/fr-command-alz.h57
-rw-r--r--src/fr-command-ar.c392
-rw-r--r--src/fr-command-ar.h52
-rw-r--r--src/fr-command-arj.c438
-rw-r--r--src/fr-command-arj.h57
-rw-r--r--src/fr-command-cfile.c619
-rw-r--r--src/fr-command-cfile.h56
-rw-r--r--src/fr-command-cpio.c329
-rw-r--r--src/fr-command-cpio.h53
-rw-r--r--src/fr-command-dpkg.c314
-rw-r--r--src/fr-command-dpkg.h53
-rw-r--r--src/fr-command-iso.c319
-rw-r--r--src/fr-command-iso.h54
-rw-r--r--src/fr-command-jar.c243
-rw-r--r--src/fr-command-jar.h51
-rw-r--r--src/fr-command-lha.c409
-rw-r--r--src/fr-command-lha.h52
-rw-r--r--src/fr-command-lrzip.c271
-rw-r--r--src/fr-command-lrzip.h37
-rw-r--r--src/fr-command-rar.c813
-rw-r--r--src/fr-command-rar.h57
-rw-r--r--src/fr-command-rpm.c320
-rw-r--r--src/fr-command-rpm.h53
-rw-r--r--src/fr-command-tar.c1233
-rw-r--r--src/fr-command-tar.h61
-rw-r--r--src/fr-command-unstuff.c390
-rw-r--r--src/fr-command-unstuff.h56
-rw-r--r--src/fr-command-zip.c494
-rw-r--r--src/fr-command-zip.h53
-rw-r--r--src/fr-command-zoo.c428
-rw-r--r--src/fr-command-zoo.h53
-rw-r--r--src/fr-command.c826
-rw-r--r--src/fr-command.h228
-rw-r--r--src/fr-error.c36
-rw-r--r--src/fr-error.h32
-rw-r--r--src/fr-list-model.c188
-rw-r--r--src/fr-list-model.h46
-rw-r--r--src/fr-marshal.list8
-rw-r--r--src/fr-process.c1028
-rw-r--r--src/fr-process.h135
-rw-r--r--src/fr-stock.c81
-rw-r--r--src/fr-stock.h33
-rw-r--r--src/fr-window.c8855
-rw-r--r--src/fr-window.h315
-rw-r--r--src/gio-utils.c1497
-rw-r--r--src/gio-utils.h155
-rw-r--r--src/glib-utils.c645
-rw-r--r--src/glib-utils.h86
-rw-r--r--src/gtk-utils.c823
-rw-r--r--src/gtk-utils.h92
-rw-r--r--src/java-utils.c441
-rw-r--r--src/java-utils.h31
-rw-r--r--src/main.c1030
-rw-r--r--src/main.h84
-rw-r--r--src/mateconf-utils.c862
-rw-r--r--src/mateconf-utils.h137
-rw-r--r--src/mkdtemp.c201
-rw-r--r--src/mkdtemp.h40
-rw-r--r--src/open-file.c87
-rw-r--r--src/open-file.h48
-rw-r--r--src/preferences.c268
-rw-r--r--src/preferences.h89
-rw-r--r--src/sh/Makefile.am5
-rw-r--r--src/sh/isoinfo.sh27
-rw-r--r--src/typedefs.h122
-rw-r--r--src/ui.h396
111 files changed, 42067 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..8622fe9
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,193 @@
+SUBDIRS = commands sh
+
+bin_PROGRAMS = file-roller
+
+if MKDTEMP_MISSING
+MKDTEMP_FILES=mkdtemp.c mkdtemp.h
+else
+MKDTEMP_FILES=
+endif
+
+if RUN_IN_PLACE
+privdatadir = $(top_srcdir)/data/
+uidir = $(top_srcdir)/data/ui
+privexecdir = $(abs_top_builddir)/src/commands/
+shdir = $(top_srcdir)/src/sh/
+else
+privdatadir = $(datadir)/file-roller/
+uidir = $(datadir)/file-roller/ui
+privexecdir = $(libexecdir)/file-roller/
+shdir = $(libexecdir)/file-roller/
+endif
+
+INCLUDES = \
+ -I$(top_srcdir)/copy-n-paste/ \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DFR_PREFIX=\"$(prefix)\" \
+ -DFR_SYSCONFDIR=\"$(sysconfdir)\" \
+ -DFR_DATADIR=\"$(datadir)\" \
+ -DPRIVDATADIR=\"$(privdatadir)\" \
+ -DUI_DIR=\"$(uidir)\" \
+ -DFR_LIBDIR=\"$(libdir)\" \
+ -DPKG_DATA_DIR=\"$(pkgdatadir)\" \
+ -DPIXMAPSDIR=\""$(datadir)/pixmaps"\" \
+ -DGLADEDIR=\""$(gladedir)"\" \
+ -DPRIVEXECDIR=\"$(privexecdir)\" \
+ -DSHDIR=\"$(shdir)\" \
+ $(DISABLE_DEPRECATED) \
+ $(FR_CFLAGS)
+
+BUILT_SOURCES = \
+ fr-marshal.c \
+ fr-marshal.h \
+ fr-enum-types.h \
+ fr-enum-types.c
+
+file_roller_SOURCES = \
+ actions.h \
+ actions.c \
+ dlg-add-files.c \
+ dlg-add-files.h \
+ dlg-add-folder.c \
+ dlg-add-folder.h \
+ dlg-ask-password.c \
+ dlg-ask-password.h \
+ dlg-batch-add.c \
+ dlg-batch-add.h \
+ dlg-delete.c \
+ dlg-delete.h \
+ dlg-extract.c \
+ dlg-extract.h \
+ dlg-new.c \
+ dlg-new.h \
+ dlg-open-with.c \
+ dlg-open-with.h \
+ dlg-package-installer.c \
+ dlg-package-installer.h \
+ dlg-password.c \
+ dlg-password.h \
+ dlg-prop.c \
+ dlg-prop.h \
+ dlg-update.c \
+ dlg-update.h \
+ eggfileformatchooser.c \
+ eggfileformatchooser.h \
+ egg-macros.h \
+ eggtreemultidnd.c \
+ eggtreemultidnd.h \
+ file-data.c \
+ file-data.h \
+ file-utils.c \
+ file-utils.h \
+ fr-archive.c \
+ fr-archive.h \
+ fr-command.c \
+ fr-command.h \
+ fr-command-ace.c \
+ fr-command-ace.h \
+ fr-command-alz.c \
+ fr-command-alz.h \
+ fr-command-ar.c \
+ fr-command-ar.h \
+ fr-command-arj.c \
+ fr-command-arj.h \
+ fr-command-cfile.c \
+ fr-command-cfile.h \
+ fr-command-cpio.c \
+ fr-command-cpio.h \
+ fr-command-dpkg.c \
+ fr-command-dpkg.h \
+ fr-command-iso.c \
+ fr-command-iso.h \
+ fr-command-jar.h \
+ fr-command-jar.c \
+ fr-command-lha.c \
+ fr-command-lha.h \
+ fr-command-rar.c \
+ fr-command-rar.h \
+ fr-command-rpm.c \
+ fr-command-rpm.h \
+ fr-command-tar.c \
+ fr-command-tar.h \
+ fr-command-unstuff.c \
+ fr-command-unstuff.h \
+ fr-command-zip.c \
+ fr-command-zip.h \
+ fr-command-lrzip.c \
+ fr-command-lrzip.h \
+ fr-command-zoo.c \
+ fr-command-zoo.h \
+ fr-command-7z.c \
+ fr-command-7z.h \
+ fr-error.c \
+ fr-error.h \
+ fr-list-model.c \
+ fr-list-model.h \
+ fr-stock.c \
+ fr-stock.h \
+ fr-process.c \
+ fr-process.h \
+ fr-window.c \
+ fr-window.h \
+ mateconf-utils.c \
+ mateconf-utils.h \
+ gio-utils.c \
+ gio-utils.h \
+ glib-utils.c \
+ glib-utils.h \
+ gtk-utils.c \
+ gtk-utils.h \
+ java-utils.c \
+ java-utils.h \
+ main.c \
+ main.h \
+ open-file.c \
+ open-file.h \
+ preferences.c \
+ preferences.h \
+ typedefs.h \
+ ui.h \
+ $(MKDTEMP_FILES) \
+ $(BUILT_SOURCES)
+
+fr-marshal.h: fr-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN)( $(GLIB_GENMARSHAL) $< --header --prefix=fr_marshal > $@ )
+
+fr-marshal.c: fr-marshal.list $(GLIB_GENMARSHAL)
+ $(AM_V_GEN)( echo "#include \"fr-marshal.h\"" > $@ && \
+ $(GLIB_GENMARSHAL) $< --body --prefix=fr_marshal >> $@ )
+
+fr-enum-types.h: typedefs.h $(GLIB_MKENUMS)
+ $(AM_V_GEN)( $(GLIB_MKENUMS) \
+ --fhead "#ifndef FR_ENUM__TYPES_H\n#define FR_ENUM_TYPES_H\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+ --fprod "/* enumerations from \"@filename@\" */\n" \
+ --vhead "GType @enum_name@_get_type (void);\n#define FR_TYPE_@ENUMSHORT@ (@enum_name@_get_type())\n" \
+ --ftail "G_END_DECLS\n\n#endif /* FR_ENUM_TYPES_H */" \
+ $^> xgen-$(@F) \
+ && (cmp -s xgen-$(@F) fr-enum-types.h || cp xgen-$(@F) fr-enum-types.h ) \
+ && rm -f xgen-$(@F) )
+
+fr-enum-types.c: typedefs.h fr-enum-types.h
+ $(AM_V_GEN)( $(GLIB_MKENUMS) \
+ --fhead "#include <glib-object.h>\n" \
+ --fprod "\n/* enumerations from \"@filename@\" */\n#include \"@filename@\"" \
+ --vhead "GType\n@enum_name@_get_type (void)\n{\n static GType etype = 0;\n if (etype == 0) {\n static const G@Type@Value values[] = {" \
+ --vprod " { @VALUENAME@, \"@VALUENAME@\", \"@valuenick@\" }," \
+ --vtail " { 0, NULL, NULL }\n };\n etype = g_@type@_register_static (\"@EnumName@\", values);\n }\n return etype;\n}\n" \
+ $^> xgen-$(@F) \
+ && (cmp -s xgen-$(@F) fr-enum-types.c || cp xgen-$(@F) fr-enum-types.c ) \
+ && rm -f xgen-$(@F) )
+
+file_roller_LDADD = \
+ $(top_builddir)/copy-n-paste/libeggsmclient.la \
+ $(FR_LIBS)
+
+EXTRA_DIST = fr-marshal.list
+
+CLEANFILES = $(BUILT_SOURCES)
+
+dist-hook:
+ cd $(distdir); rm -f $(CLEANFILES)
+
+-include $(top_srcdir)/git.mk
diff --git a/src/actions.c b/src/actions.c
new file mode 100644
index 0000000..f44c262
--- /dev/null
+++ b/src/actions.c
@@ -0,0 +1,873 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "actions.h"
+#include "dlg-add-files.h"
+#include "dlg-add-folder.h"
+#include "dlg-extract.h"
+#include "dlg-delete.h"
+#include "dlg-new.h"
+#include "dlg-open-with.h"
+#include "dlg-password.h"
+#include "dlg-prop.h"
+#include "gtk-utils.h"
+#include "fr-window.h"
+#include "file-utils.h"
+#include "fr-process.h"
+#include "mateconf-utils.h"
+#include "glib-utils.h"
+#include "main.h"
+#include "typedefs.h"
+
+
+/* -- new archive -- */
+
+
+static void
+new_archive (DlgNewData *data,
+ char *uri)
+{
+ GtkWidget *archive_window;
+ gboolean new_window;
+ const char *password;
+ gboolean encrypt_header;
+ int volume_size;
+
+ new_window = fr_window_archive_is_present (data->window) && ! fr_window_is_batch_mode (data->window);
+ if (new_window)
+ archive_window = fr_window_new ();
+ else
+ archive_window = (GtkWidget *) data->window;
+
+ password = dlg_new_data_get_password (data);
+ encrypt_header = dlg_new_data_get_encrypt_header (data);
+ volume_size = dlg_new_data_get_volume_size (data);
+
+ fr_window_set_password (FR_WINDOW (archive_window), password);
+ fr_window_set_encrypt_header (FR_WINDOW (archive_window), encrypt_header);
+ fr_window_set_volume_size (FR_WINDOW (archive_window), volume_size);
+
+ if (fr_window_archive_new (FR_WINDOW (archive_window), uri)) {
+ gtk_widget_destroy (data->dialog);
+ if (! fr_window_is_batch_mode (FR_WINDOW (archive_window)))
+ gtk_window_present (GTK_WINDOW (archive_window));
+ }
+ else if (new_window)
+ gtk_widget_destroy (archive_window);
+}
+
+
+/* when on Automatic the user provided extension needs to be supported,
+ otherwise an existing unsupported archive can be deleted (if the user
+ provided name matches with its name) before we find out that the
+ archive is unsupported
+*/
+static gboolean
+is_supported_extension (GtkWidget *file_sel,
+ char *filename,
+ int *file_type)
+{
+ int i;
+ for (i = 0; file_type[i] != -1; i++)
+ if (file_extension_is (filename, mime_type_desc[file_type[i]].default_ext))
+ return TRUE;
+ return FALSE;
+}
+
+
+static char *
+get_full_uri (DlgNewData *data)
+{
+ char *full_uri = NULL;
+ char *uri;
+ const char *filename;
+ int idx;
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog));
+
+ if ((uri == NULL) || (*uri == 0))
+ return NULL;
+
+ filename = file_name_from_path (uri);
+ if ((filename == NULL) || (*filename == 0)) {
+ g_free (uri);
+ return NULL;
+ }
+
+ idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri);
+ if (idx > 0) {
+ const char *uri_ext;
+ char *default_ext;
+
+ uri_ext = get_archive_filename_extension (uri);
+ default_ext = mime_type_desc[data->supported_types[idx-1]].default_ext;
+ if (strcmp_null_tolerant (uri_ext, default_ext) != 0) {
+ full_uri = g_strconcat (uri, default_ext, NULL);
+ g_free (uri);
+ }
+ }
+ if (full_uri == NULL)
+ full_uri = uri;
+
+ return full_uri;
+}
+
+
+static char *
+get_archive_filename_from_selector (DlgNewData *data)
+{
+ char *uri = NULL;
+ GFile *file, *dir;
+ GFileInfo *info;
+ GError *err = NULL;
+
+ uri = get_full_uri (data);
+ if ((uri == NULL) || (*uri == 0)) {
+ GtkWidget *dialog;
+
+ g_free (uri);
+
+ dialog = _gtk_error_dialog_new (GTK_WINDOW (data->dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Could not create the archive"),
+ "%s",
+ _("You have to specify an archive name."));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ return NULL;
+ }
+
+ file = g_file_new_for_uri (uri);
+
+ dir = g_file_get_parent (file);
+ info = g_file_query_info (dir,
+ G_FILE_ATTRIBUTE_ACCESS_CAN_READ ","
+ G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE ","
+ G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
+ 0, NULL, &err);
+ if (err != NULL) {
+ g_warning ("Failed to get permission for extraction dir: %s",
+ err->message);
+ g_clear_error (&err);
+ g_object_unref (info);
+ g_object_unref (dir);
+ g_object_unref (file);
+ g_free (uri);
+ return NULL;
+ }
+
+ if (! g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE)) {
+ GtkWidget *dialog;
+
+ g_object_unref (info);
+ g_object_unref (dir);
+ g_object_unref (file);
+ g_free (uri);
+
+ dialog = _gtk_error_dialog_new (GTK_WINDOW (data->dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Could not create the archive"),
+ "%s",
+ _("You don't have permission to create an archive in this folder"));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ return NULL;
+ }
+ g_object_unref (info);
+ g_object_unref (dir);
+
+ /* if the user did not specify a valid extension use the filetype combobox current type
+ * or tar.gz if automatic is selected. */
+ if (get_archive_filename_extension (uri) == NULL) {
+ int idx;
+ char *new_uri;
+ char *ext = NULL;
+
+ idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri);
+ if (idx > 0)
+ ext = mime_type_desc[data->supported_types[idx-1]].default_ext;
+ else
+ ext = ".tar.gz";
+ new_uri = g_strconcat (uri, ext, NULL);
+ g_free (uri);
+ uri = new_uri;
+ }
+
+ debug (DEBUG_INFO, "create/save %s\n", uri);
+
+ if (uri_exists (uri)) {
+ GtkWidget *dialog;
+
+ if (! is_supported_extension (data->dialog, uri, data->supported_types)) {
+ dialog = _gtk_error_dialog_new (GTK_WINDOW (data->dialog),
+ GTK_DIALOG_MODAL,
+ NULL,
+ _("Could not create the archive"),
+ "%s",
+ _("Archive type not supported."));
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_free (uri);
+
+ return NULL;
+ }
+
+ g_file_delete (file, NULL, &err);
+ if (err != NULL) {
+ GtkWidget *dialog;
+ dialog = _gtk_error_dialog_new (GTK_WINDOW (data->dialog),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Could not delete the old archive."),
+ "%s",
+ err->message);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+ g_error_free (err);
+ g_free (uri);
+ g_object_unref (file);
+ return NULL;
+ }
+ }
+
+ g_object_unref (file);
+
+ return uri;
+}
+
+
+static void
+new_file_response_cb (GtkWidget *w,
+ int response,
+ DlgNewData *data)
+{
+ char *path;
+
+ if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) {
+ fr_archive_action_completed (data->window->archive,
+ FR_ACTION_CREATING_NEW_ARCHIVE,
+ FR_PROC_ERROR_STOPPED,
+ NULL);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+
+ if (response == GTK_RESPONSE_HELP) {
+ show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-create");
+ return;
+ }
+
+ path = get_archive_filename_from_selector (data);
+ if (path != NULL) {
+ new_archive (data, path);
+ g_free (path);
+ }
+}
+
+
+void
+show_new_archive_dialog (FrWindow *window,
+ const char *archive_name)
+{
+ DlgNewData *data;
+
+ if (archive_name != NULL)
+ data = dlg_save_as (window, archive_name);
+ else
+ data = dlg_new (window);
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "response",
+ G_CALLBACK (new_file_response_cb),
+ data);
+ gtk_window_present (GTK_WINDOW (data->dialog));
+}
+
+
+void
+activate_action_new (GtkAction *action,
+ gpointer data)
+{
+ show_new_archive_dialog ((FrWindow*)data, NULL);
+}
+
+
+/* -- open archive -- */
+
+
+static void
+window_archive_loaded_cb (FrWindow *window,
+ gboolean success,
+ GtkWidget *file_sel)
+{
+ if (success) {
+ g_signal_handlers_disconnect_by_data (window, file_sel);
+ gtk_widget_destroy (file_sel);
+ }
+ else {
+ FrWindow *original_window = g_object_get_data (G_OBJECT (file_sel), "fr_window");
+ if (window != original_window)
+ fr_window_destroy_with_error_dialog (window);
+ }
+}
+
+
+static void
+open_file_response_cb (GtkWidget *w,
+ int response,
+ GtkWidget *file_sel)
+{
+ FrWindow *window = NULL;
+ char *uri;
+
+ if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) {
+ gtk_widget_destroy (file_sel);
+ return;
+ }
+
+ window = g_object_get_data (G_OBJECT (file_sel), "fr_window");
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (file_sel));
+
+ if ((window == NULL) || (uri == NULL))
+ return;
+
+ if (fr_window_archive_is_present (window))
+ window = (FrWindow *) fr_window_new ();
+ g_signal_connect (G_OBJECT (window),
+ "archive_loaded",
+ G_CALLBACK (window_archive_loaded_cb),
+ file_sel);
+ fr_window_archive_open (window, uri, GTK_WINDOW (file_sel));
+
+ g_free (uri);
+}
+
+
+void
+activate_action_open (GtkAction *action,
+ gpointer data)
+{
+ GtkWidget *file_sel;
+ FrWindow *window = data;
+ GtkFileFilter *filter;
+ int i;
+
+ file_sel = gtk_file_chooser_dialog_new (_("Open"),
+ GTK_WINDOW (window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE);
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (file_sel), fr_window_get_open_default_dir (window));
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("All archives"));
+ for (i = 0; open_type[i] != -1; i++)
+ gtk_file_filter_add_mime_type (filter, mime_type_desc[open_type[i]].mime_type);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_sel), filter);
+ gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (file_sel), filter);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("All files"));
+ gtk_file_filter_add_pattern (filter, "*");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_sel), filter);
+
+ /**/
+
+ g_object_set_data (G_OBJECT (file_sel), "fr_window", window);
+
+ g_signal_connect (G_OBJECT (file_sel),
+ "response",
+ G_CALLBACK (open_file_response_cb),
+ file_sel);
+
+ gtk_window_set_modal (GTK_WINDOW (file_sel), TRUE);
+ gtk_widget_show (file_sel);
+}
+
+
+/* -- save archive -- */
+
+
+static void
+save_file_response_cb (GtkWidget *w,
+ gint response,
+ DlgNewData *data)
+{
+ char *path;
+ const char *password;
+ gboolean encrypt_header;
+ int volume_size;
+
+ if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) {
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+
+ if (response == GTK_RESPONSE_HELP) {
+ show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-convert-archive");
+ return;
+ }
+
+ path = get_archive_filename_from_selector (data);
+ if (path == NULL)
+ return;
+
+ password = dlg_new_data_get_password (data);
+ encrypt_header = dlg_new_data_get_encrypt_header (data);
+ volume_size = dlg_new_data_get_volume_size (data);
+
+ eel_mateconf_set_integer (PREF_BATCH_VOLUME_SIZE, volume_size);
+
+ fr_window_archive_save_as (data->window, path, password, encrypt_header, volume_size);
+ gtk_widget_destroy (data->dialog);
+
+ g_free (path);
+}
+
+
+void
+activate_action_save_as (GtkAction *action,
+ gpointer callback_data)
+{
+ FrWindow *window = callback_data;
+ DlgNewData *data;
+ char *archive_name = NULL;
+
+ if (fr_window_get_archive_uri (window)) {
+ const char *uri;
+ GFile *file;
+ GFileInfo *info;
+ GError *err = NULL;
+
+ uri = fr_window_get_archive_uri (window);
+ file = g_file_new_for_uri (uri);
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+ 0, NULL, &err);
+
+ if (err != NULL) {
+ g_warning ("Failed to get display name for uri %s: %s", uri, err->message);
+ g_clear_error (&err);
+ }
+ else
+ archive_name = g_strdup (g_file_info_get_display_name (info));
+
+ g_object_unref (info);
+ g_object_unref (file);
+ }
+
+ data = dlg_save_as (window, archive_name);
+ g_signal_connect (G_OBJECT (data->dialog),
+ "response",
+ G_CALLBACK (save_file_response_cb),
+ data);
+ gtk_window_present (GTK_WINDOW (data->dialog));
+
+ g_free (archive_name);
+}
+
+
+void
+activate_action_test_archive (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ fr_window_archive_test (window);
+}
+
+
+void
+activate_action_properties (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ dlg_prop (window);
+}
+
+
+void
+activate_action_close (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ fr_window_close (window);
+}
+
+
+void
+activate_action_add_files (GtkAction *action,
+ gpointer data)
+{
+ add_files_cb (NULL, data);
+}
+
+
+void
+activate_action_add_folder (GtkAction *action,
+ gpointer data)
+{
+ add_folder_cb (NULL, data);
+}
+
+
+void
+activate_action_extract (GtkAction *action,
+ gpointer data)
+{
+ dlg_extract (NULL, data);
+}
+
+
+void
+activate_action_extract_folder_from_sidebar (GtkAction *action,
+ gpointer data)
+{
+ dlg_extract_folder_from_sidebar (NULL, data);
+}
+
+
+void
+activate_action_copy (GtkAction *action,
+ gpointer data)
+{
+ fr_window_copy_selection ((FrWindow*) data, FALSE);
+}
+
+
+void
+activate_action_cut (GtkAction *action,
+ gpointer data)
+{
+ fr_window_cut_selection ((FrWindow*) data, FALSE);
+}
+
+
+void
+activate_action_paste (GtkAction *action,
+ gpointer data)
+{
+ fr_window_paste_selection ((FrWindow*) data, FALSE);
+}
+
+
+void
+activate_action_rename (GtkAction *action,
+ gpointer data)
+{
+ fr_window_rename_selection ((FrWindow*) data, FALSE);
+}
+
+
+void
+activate_action_delete (GtkAction *action,
+ gpointer data)
+{
+ dlg_delete (NULL, data);
+}
+
+
+void
+activate_action_copy_folder_from_sidebar (GtkAction *action,
+ gpointer data)
+{
+ fr_window_copy_selection ((FrWindow*) data, TRUE);
+}
+
+
+void
+activate_action_cut_folder_from_sidebar (GtkAction *action,
+ gpointer data)
+{
+ fr_window_cut_selection ((FrWindow*) data, TRUE);
+}
+
+
+void
+activate_action_paste_folder_from_sidebar (GtkAction *action,
+ gpointer data)
+{
+ fr_window_paste_selection ((FrWindow*) data, TRUE);
+}
+
+
+void
+activate_action_rename_folder_from_sidebar (GtkAction *action,
+ gpointer data)
+{
+ fr_window_rename_selection ((FrWindow*) data, TRUE);
+}
+
+
+void
+activate_action_delete_folder_from_sidebar (GtkAction *action,
+ gpointer data)
+{
+ dlg_delete_from_sidebar (NULL, data);
+}
+
+
+void
+activate_action_find (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ fr_window_find (window);
+}
+
+
+void
+activate_action_select_all (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ fr_window_select_all (window);
+}
+
+
+void
+activate_action_deselect_all (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ fr_window_unselect_all (window);
+}
+
+
+void
+activate_action_open_with (GtkAction *action,
+ gpointer data)
+{
+ open_with_cb (NULL, (FrWindow*) data);
+}
+
+
+void
+activate_action_view_or_open (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ GList *file_list;
+
+ file_list = fr_window_get_file_list_selection (window, FALSE, NULL);
+ if (file_list == NULL)
+ return;
+ fr_window_open_files (window, file_list, FALSE);
+ path_list_free (file_list);
+}
+
+
+void
+activate_action_open_folder (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ fr_window_current_folder_activated (window, FALSE);
+}
+
+
+void
+activate_action_open_folder_from_sidebar (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ fr_window_current_folder_activated (window, TRUE);
+}
+
+
+void
+activate_action_password (GtkAction *action,
+ gpointer data)
+{
+ dlg_password (NULL, (FrWindow*) data);
+}
+
+
+void
+activate_action_view_toolbar (GtkAction *action,
+ gpointer data)
+{
+ eel_mateconf_set_boolean (PREF_UI_TOOLBAR, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+
+void
+activate_action_view_statusbar (GtkAction *action,
+ gpointer data)
+{
+ eel_mateconf_set_boolean (PREF_UI_STATUSBAR, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+
+void
+activate_action_view_folders (GtkAction *action,
+ gpointer data)
+{
+ eel_mateconf_set_boolean (PREF_UI_FOLDERS, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)));
+}
+
+
+void
+activate_action_stop (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ fr_window_stop (window);
+}
+
+
+void
+activate_action_reload (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ fr_window_archive_reload (window);
+}
+
+
+void
+activate_action_sort_reverse_order (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ fr_window_set_sort_type (window, gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)) ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING);
+}
+
+
+void
+activate_action_last_output (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ fr_window_view_last_output (window, _("Last Output"));
+}
+
+
+void
+activate_action_go_back (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ fr_window_go_back (window);
+}
+
+
+void
+activate_action_go_forward (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ fr_window_go_forward (window);
+}
+
+
+void
+activate_action_go_up (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ fr_window_go_up_one_level (window);
+}
+
+
+void
+activate_action_go_home (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ fr_window_go_to_location (window, "/", FALSE);
+}
+
+
+void
+activate_action_manual (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+
+ show_help_dialog (GTK_WINDOW (window) , NULL);
+}
+
+
+void
+activate_action_about (GtkAction *action,
+ gpointer data)
+{
+ FrWindow *window = data;
+ const char *authors[] = {
+ "Paolo Bacchilega <[email protected]>", NULL
+ };
+ const char *documenters [] = {
+ "Alexander Kirillov",
+ "Breda McColgan",
+ NULL
+ };
+ const char *license[] = {
+ N_("File Roller 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."),
+ N_("File Roller 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."),
+ N_("You should have received a copy of the GNU General Public License "
+ "along with File Roller; if not, write to the Free Software Foundation, Inc., "
+ "51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA")
+ };
+ char *license_text;
+
+ license_text = g_strjoin ("\n\n", _(license[0]), _(license[1]), _(license[2]), NULL);
+
+ gtk_show_about_dialog (GTK_WINDOW (window),
+ "version", VERSION,
+ "copyright", _("Copyright \xc2\xa9 2001–2010 Free Software Foundation, Inc."),
+ "comments", _("An archive manager for MATE."),
+ "authors", authors,
+ "documenters", documenters,
+ "translator-credits", _("translator-credits"),
+ "logo-icon-name", "file-roller",
+ "license", license_text,
+ "wrap-license", TRUE,
+ NULL);
+
+ g_free (license_text);
+}
diff --git a/src/actions.h b/src/actions.h
new file mode 100644
index 0000000..b0d1def
--- /dev/null
+++ b/src/actions.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ACTIONS_H
+#define ACTIONS_H
+
+#include <gtk/gtk.h>
+#include "fr-window.h"
+
+void show_new_archive_dialog (FrWindow *window, const char *archive_name);
+
+void activate_action_new (GtkAction *action, gpointer data);
+void activate_action_open (GtkAction *action, gpointer data);
+void activate_action_save_as (GtkAction *action, gpointer data);
+void activate_action_test_archive (GtkAction *action, gpointer data);
+void activate_action_properties (GtkAction *action, gpointer data);
+void activate_action_close (GtkAction *action, gpointer data);
+void activate_action_quit (GtkAction *action, gpointer data);
+
+void activate_action_add_files (GtkAction *action, gpointer data);
+void activate_action_add_folder (GtkAction *action, gpointer data);
+void activate_action_extract (GtkAction *action, gpointer data);
+void activate_action_extract_folder_from_sidebar (GtkAction *action, gpointer data);
+
+void activate_action_copy (GtkAction *action, gpointer data);
+void activate_action_cut (GtkAction *action, gpointer data);
+void activate_action_paste (GtkAction *action, gpointer data);
+void activate_action_rename (GtkAction *action, gpointer data);
+void activate_action_delete (GtkAction *action, gpointer data);
+
+void activate_action_copy_folder_from_sidebar (GtkAction *action, gpointer data);
+void activate_action_cut_folder_from_sidebar (GtkAction *action, gpointer data);
+void activate_action_paste_folder_from_sidebar (GtkAction *action, gpointer data);
+void activate_action_rename_folder_from_sidebar (GtkAction *action, gpointer data);
+void activate_action_delete_folder_from_sidebar (GtkAction *action, gpointer data);
+
+void activate_action_find (GtkAction *action, gpointer data);
+void activate_action_select_all (GtkAction *action, gpointer data);
+void activate_action_deselect_all (GtkAction *action, gpointer data);
+void activate_action_open_with (GtkAction *action, gpointer data);
+void activate_action_view_or_open (GtkAction *action, gpointer data);
+void activate_action_open_folder (GtkAction *action, gpointer data);
+void activate_action_open_folder_from_sidebar (GtkAction *action, gpointer data);
+void activate_action_password (GtkAction *action, gpointer data);
+
+void activate_action_view_toolbar (GtkAction *action, gpointer data);
+void activate_action_view_statusbar (GtkAction *action, gpointer data);
+void activate_action_view_folders (GtkAction *action, gpointer data);
+void activate_action_stop (GtkAction *action, gpointer data);
+void activate_action_reload (GtkAction *action, gpointer data);
+void activate_action_sort_reverse_order (GtkAction *action, gpointer data);
+void activate_action_last_output (GtkAction *action, gpointer data);
+
+void activate_action_go_back (GtkAction *action, gpointer data);
+void activate_action_go_forward (GtkAction *action, gpointer data);
+void activate_action_go_up (GtkAction *action, gpointer data);
+void activate_action_go_home (GtkAction *action, gpointer data);
+
+void activate_action_manual (GtkAction *action, gpointer data);
+void activate_action_about (GtkAction *action, gpointer data);
+
+
+#endif /* ACTIONS_H */
diff --git a/src/commands/Makefile.am b/src/commands/Makefile.am
new file mode 100644
index 0000000..b51c338
--- /dev/null
+++ b/src/commands/Makefile.am
@@ -0,0 +1,7 @@
+privexecdir = $(libexecdir)/$(PACKAGE)
+privexec_PROGRAMS = rpm2cpio
+
+INCLUDES = $(DISABLE_DEPRECATED) $(FR_CFLAGS)
+
+rpm2cpio_SOURCES = rpm2cpio.c
+rpm2cpio_LDADD = $(FR_LIBS)
diff --git a/src/commands/rpm2cpio.c b/src/commands/rpm2cpio.c
new file mode 100644
index 0000000..034cd89
--- /dev/null
+++ b/src/commands/rpm2cpio.c
@@ -0,0 +1,134 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+
+
+static const char *
+get_mime_type_from_magic_numbers (char *buffer)
+{
+ static struct {
+ const char *mime_type;
+ const char *first_bytes;
+ int offset;
+ int len;
+ } sniffer_data [] = {
+ { "application/x-bzip2", "BZh", 0, 3 },
+ { "application/x-gzip", "\037\213", 0, 2 },
+ { "application/x-xz", "\3757zXZ\000", 0, 6 },
+ { NULL, NULL, 0 }
+ };
+ int i;
+
+ for (i = 0; sniffer_data[i].mime_type != NULL; i++)
+ if (memcmp (sniffer_data[i].first_bytes,
+ buffer + sniffer_data[i].offset,
+ sniffer_data[i].len) == 0)
+ {
+ return sniffer_data[i].mime_type;
+ }
+
+ return NULL;
+}
+
+
+int
+main (int argc, char **argv)
+{
+ const char *filename;
+ GString *cpio_args;
+ int i;
+ FILE *stream;
+ guchar bytes[8];
+ int il, dl, sigsize, offset;
+ const char *mime_type;
+ const char *archive_command;
+ char *command;
+
+ if (argc < 3)
+ return 0;
+
+ filename = argv[1];
+ cpio_args = g_string_new (argv[2]);
+ for (i = 3; i < argc; i++) {
+ g_string_append (cpio_args, " ");
+ g_string_append (cpio_args, argv[i]);
+ }
+
+ stream = fopen (filename, "r");
+ if (stream == NULL)
+ return 1;
+
+ if (fseek (stream, 104 , SEEK_CUR) != 0) {
+ fclose (stream);
+ return 1;
+ }
+ if (fread (bytes, 1, 8, stream) == 0) {
+ fclose (stream);
+ return 1;
+ }
+ il = 256 * (256 * (256 * bytes[0] + bytes[1]) + bytes[2]) + bytes[3];
+ dl = 256 * (256 * (256 * bytes[4] + bytes[5]) + bytes[6]) + bytes[7];
+ sigsize = 8 + 16 * il + dl;
+ offset = 104 + sigsize + (8 - (sigsize % 8)) % 8 + 8;
+ if (fseek (stream, offset, SEEK_SET) != 0) {
+ fclose (stream);
+ return 1;
+ }
+ if (fread (bytes, 1, 8, stream) == 0) {
+ fclose (stream);
+ return 1;
+ }
+ il = 256 * (256 * (256 * bytes[0] + bytes[1]) + bytes[2]) + bytes[3];
+ dl = 256 * (256 * (256 * bytes[4] + bytes[5]) + bytes[6]) + bytes[7];
+ sigsize = 8 + 16 * il + dl;
+ offset = offset + sigsize;
+
+ /* get the payload type */
+
+ if (fseek (stream, offset, SEEK_SET) != 0) {
+ fclose (stream);
+ return 1;
+ }
+ if (fread (bytes, 1, 8, stream) == 0) {
+ fclose (stream);
+ return 1;
+ }
+ mime_type = get_mime_type_from_magic_numbers ((char *)bytes);
+ if (mime_type == NULL)
+ archive_command = "lzma -dc";
+ else if (strcmp (mime_type, "application/x-xz") == 0)
+ archive_command = "xz -dc";
+ else if (strcmp (mime_type, "application/x-gzip") == 0)
+ archive_command = "gzip -dc";
+ else
+ archive_command = "bzip2 -dc";
+ fclose (stream);
+
+ command = g_strdup_printf ("sh -c \"dd if=%s ibs=%u skip=1 2>/dev/null | %s | cpio %s\"", g_shell_quote (filename), offset, archive_command, cpio_args->str);
+
+ return system (command);
+}
diff --git a/src/dlg-add-files.c b/src/dlg-add-files.c
new file mode 100644
index 0000000..06c8be9
--- /dev/null
+++ b/src/dlg-add-files.c
@@ -0,0 +1,191 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <unistd.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "file-utils.h"
+#include "fr-stock.h"
+#include "fr-window.h"
+#include "mateconf-utils.h"
+#include "gtk-utils.h"
+#include "preferences.h"
+
+
+typedef struct {
+ FrWindow *window;
+ GtkWidget *dialog;
+ GtkWidget *add_if_newer_checkbutton;
+} DialogData;
+
+
+static void
+open_file_destroy_cb (GtkWidget *file_sel,
+ DialogData *data)
+{
+ g_free (data);
+}
+
+
+static int
+file_sel_response_cb (GtkWidget *widget,
+ int response,
+ DialogData *data)
+{
+ GtkFileChooser *file_sel = GTK_FILE_CHOOSER (widget);
+ FrWindow *window = data->window;
+ char *current_folder;
+ char *uri;
+ gboolean update;
+ GSList *selections, *iter;
+ GList *item_list = NULL;
+
+ current_folder = gtk_file_chooser_get_current_folder_uri (file_sel);
+ uri = gtk_file_chooser_get_uri (file_sel);
+ eel_mateconf_set_string (PREF_ADD_CURRENT_FOLDER, current_folder);
+ eel_mateconf_set_string (PREF_ADD_FILENAME, uri);
+ fr_window_set_add_default_dir (window, current_folder);
+ g_free (uri);
+
+ if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) {
+ gtk_widget_destroy (data->dialog);
+ g_free (current_folder);
+ return TRUE;
+ }
+
+ if (response == GTK_RESPONSE_HELP) {
+ show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-add-options");
+ g_free (current_folder);
+ return TRUE;
+ }
+
+ /* check folder permissions. */
+
+ if (uri_is_dir (current_folder) && ! check_permissions (current_folder, R_OK)) {
+ GtkWidget *d;
+ char *utf8_path;
+
+ utf8_path = g_filename_display_name (current_folder);
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_MODAL,
+ NULL,
+ _("Could not add the files to the archive"),
+ _("You don't have the right permissions to read files from folder \"%s\""),
+ utf8_path);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_free (utf8_path);
+ g_free (current_folder);
+ return FALSE;
+ }
+
+ update = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->add_if_newer_checkbutton));
+
+ /**/
+
+ selections = gtk_file_chooser_get_uris (file_sel);
+ for (iter = selections; iter != NULL; iter = iter->next) {
+ char *uri = iter->data;
+ item_list = g_list_prepend (item_list, g_file_new_for_uri (uri));
+ }
+
+ if (item_list != NULL)
+ fr_window_archive_add_files (window, item_list, update);
+
+ gio_file_list_free (item_list);
+ g_slist_foreach (selections, (GFunc) g_free, NULL);
+ g_slist_free (selections);
+ g_free (current_folder);
+
+ gtk_widget_destroy (data->dialog);
+
+ return TRUE;
+}
+
+
+/* create the "add" dialog. */
+void
+add_files_cb (GtkWidget *widget,
+ void *callback_data)
+{
+ GtkWidget *file_sel;
+ DialogData *data;
+ GtkWidget *main_box;
+ char *folder;
+
+ data = g_new0 (DialogData, 1);
+ data->window = callback_data;
+ data->dialog = file_sel =
+ gtk_file_chooser_dialog_new (_("Add Files"),
+ GTK_WINDOW (data->window),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ FR_STOCK_ADD_FILES, GTK_RESPONSE_OK,
+ GTK_STOCK_HELP, GTK_RESPONSE_HELP,
+ NULL);
+
+ gtk_window_set_default_size (GTK_WINDOW (data->dialog), 530, 450);
+
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_sel), TRUE);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE);
+ gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK);
+
+ /* Translators: add a file to the archive only if the disk version is
+ * newer than the archive version. */
+ data->add_if_newer_checkbutton = gtk_check_button_new_with_mnemonic (_("Add only if _newer"));
+
+ main_box = gtk_hbox_new (FALSE, 20);
+ gtk_container_set_border_width (GTK_CONTAINER (main_box), 0);
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_sel), main_box);
+
+ gtk_box_pack_start (GTK_BOX (main_box), data->add_if_newer_checkbutton,
+ TRUE, TRUE, 0);
+
+ gtk_widget_show_all (main_box);
+
+ /* set data */
+
+ folder = eel_mateconf_get_string (PREF_ADD_CURRENT_FOLDER, "");
+ if ((folder == NULL) || (strcmp (folder, "") == 0))
+ folder = g_strdup (fr_window_get_add_default_dir (data->window));
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (file_sel), folder);
+ g_free (folder);
+
+ /* signals */
+
+ g_signal_connect (G_OBJECT (file_sel),
+ "destroy",
+ G_CALLBACK (open_file_destroy_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (file_sel),
+ "response",
+ G_CALLBACK (file_sel_response_cb),
+ data);
+
+ gtk_window_set_modal (GTK_WINDOW (file_sel), TRUE);
+ gtk_widget_show (file_sel);
+}
diff --git a/src/dlg-add-files.h b/src/dlg-add-files.h
new file mode 100644
index 0000000..dc6c556
--- /dev/null
+++ b/src/dlg-add-files.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_ADD_FILES_H
+#define DLG_ADD_FILES_H
+
+#include <gtk/gtk.h>
+
+void add_files_cb (GtkWidget *widget, void *data);
+
+#endif /* DLG_ADD_FILES_H */
diff --git a/src/dlg-add-folder.c b/src/dlg-add-folder.c
new file mode 100644
index 0000000..cd9f430
--- /dev/null
+++ b/src/dlg-add-folder.c
@@ -0,0 +1,899 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+#include "dlg-add-folder.h"
+#include "file-utils.h"
+#include "fr-stock.h"
+#include "fr-window.h"
+#include "mateconf-utils.h"
+#include "gtk-utils.h"
+#include "preferences.h"
+
+typedef struct {
+ FrWindow *window;
+ GtkWidget *dialog;
+ GtkWidget *include_subfold_checkbutton;
+ GtkWidget *add_if_newer_checkbutton;
+ GtkWidget *exclude_symlinks;
+ GtkWidget *include_files_checkbutton;
+ GtkWidget *include_files_entry;
+ GtkWidget *include_files_label;
+ GtkWidget *exclude_files_entry;
+ GtkWidget *exclude_files_label;
+ GtkWidget *exclude_folders_entry;
+ GtkWidget *exclude_folders_label;
+ GtkWidget *load_button;
+ GtkWidget *save_button;
+ GtkWidget *clear_button;
+ char *last_options;
+} DialogData;
+
+
+static void
+open_file_destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ g_free (data->last_options);
+ g_free (data);
+}
+
+
+static gboolean
+utf8_only_spaces (const char *text)
+{
+ const char *scan;
+
+ if (text == NULL)
+ return TRUE;
+
+ for (scan = text; *scan != 0; scan = g_utf8_next_char (scan)) {
+ gunichar c = g_utf8_get_char (scan);
+ if (! g_unichar_isspace (c))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void dlg_add_folder_save_last_options (DialogData *data);
+
+
+static int
+file_sel_response_cb (GtkWidget *widget,
+ int response,
+ DialogData *data)
+{
+ GtkFileChooser *file_sel = GTK_FILE_CHOOSER (widget);
+ FrWindow *window = data->window;
+ char *selected_folder;
+ gboolean update, recursive, follow_links;
+ const char *include_files;
+ const char *exclude_files;
+ const char *exclude_folders;
+ char *dest_dir;
+ char *local_filename;
+
+
+ dlg_add_folder_save_last_options (data);
+
+ if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) {
+ gtk_widget_destroy (data->dialog);
+ return TRUE;
+ }
+
+ if (response == GTK_RESPONSE_HELP) {
+ show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-add-options");
+ return TRUE;
+ }
+
+ selected_folder = gtk_file_chooser_get_uri (file_sel);
+
+ /* check folder permissions. */
+
+ if (! check_permissions (selected_folder, R_OK)) {
+ GtkWidget *d;
+ char *utf8_path;
+
+ utf8_path = g_filename_display_name (selected_folder);
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_MODAL,
+ NULL,
+ _("Could not add the files to the archive"),
+ _("You don't have the right permissions to read files from folder \"%s\""),
+ utf8_path);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_free (utf8_path);
+ g_free (selected_folder);
+
+ return FALSE;
+ }
+
+ update = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->add_if_newer_checkbutton));
+ recursive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->include_subfold_checkbutton));
+ follow_links = ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->exclude_symlinks));
+
+ include_files = gtk_entry_get_text (GTK_ENTRY (data->include_files_entry));
+ if (utf8_only_spaces (include_files))
+ include_files = "*";
+
+ exclude_files = gtk_entry_get_text (GTK_ENTRY (data->exclude_files_entry));
+ if (utf8_only_spaces (exclude_files))
+ exclude_files = NULL;
+
+ exclude_folders = gtk_entry_get_text (GTK_ENTRY (data->exclude_folders_entry));
+ if (utf8_only_spaces (exclude_folders))
+ exclude_folders = NULL;
+
+ local_filename = g_filename_from_uri (selected_folder, NULL, NULL);
+ dest_dir = build_uri (fr_window_get_current_location (window),
+ file_name_from_path (local_filename),
+ NULL);
+
+ fr_window_archive_add_with_wildcard (window,
+ include_files,
+ exclude_files,
+ exclude_folders,
+ selected_folder,
+ dest_dir,
+ update,
+ follow_links);
+
+ g_free (local_filename);
+ g_free (dest_dir);
+ g_free (selected_folder);
+
+ gtk_widget_destroy (data->dialog);
+
+ return TRUE;
+}
+
+
+static int
+include_subfold_toggled_cb (GtkWidget *widget,
+ gpointer callback_data)
+{
+ DialogData *data = callback_data;
+
+ gtk_widget_set_sensitive (data->exclude_symlinks,
+ gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
+
+ return FALSE;
+}
+
+
+static void load_options_cb (GtkWidget *w, DialogData *data);
+static void save_options_cb (GtkWidget *w, DialogData *data);
+static void clear_options_cb (GtkWidget *w, DialogData *data);
+static void dlg_add_folder_load_last_options (DialogData *data);
+
+
+/* create the "add" dialog. */
+void
+add_folder_cb (GtkWidget *widget,
+ void *callback_data)
+{
+ GtkWidget *file_sel;
+ DialogData *data;
+ GtkWidget *main_box;
+ GtkWidget *vbox;
+ GtkWidget *table;
+ GtkWidget *align;
+
+ data = g_new0 (DialogData, 1);
+
+ data->window = callback_data;
+
+ data->dialog = file_sel =
+ gtk_file_chooser_dialog_new (_("Add a Folder"),
+ GTK_WINDOW (data->window),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ FR_STOCK_ADD_FOLDER, GTK_RESPONSE_OK,
+ GTK_STOCK_HELP, GTK_RESPONSE_HELP,
+ NULL);
+
+ gtk_window_set_default_size (GTK_WINDOW (data->dialog), 530, 510);
+
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_sel), FALSE);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE);
+ gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK);
+
+ data->add_if_newer_checkbutton = gtk_check_button_new_with_mnemonic (_("Add only if _newer"));
+ data->include_subfold_checkbutton = gtk_check_button_new_with_mnemonic (_("_Include subfolders"));
+ data->exclude_symlinks = gtk_check_button_new_with_mnemonic (_("Exclude folders that are symbolic lin_ks"));
+
+ data->include_files_entry = gtk_entry_new ();
+ gtk_widget_set_tooltip_text (data->include_files_entry, _("example: *.o; *.bak"));
+ data->include_files_label = gtk_label_new_with_mnemonic (_("Include _files:"));
+ gtk_misc_set_alignment (GTK_MISC (data->include_files_label), 0.0, 0.5);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (data->include_files_label), data->include_files_entry);
+
+ data->exclude_files_entry = gtk_entry_new ();
+ gtk_widget_set_tooltip_text (data->exclude_files_entry, _("example: *.o; *.bak"));
+ data->exclude_files_label = gtk_label_new_with_mnemonic (_("E_xclude files:"));
+ gtk_misc_set_alignment (GTK_MISC (data->exclude_files_label), 0.0, 0.5);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (data->exclude_files_label), data->exclude_files_entry);
+
+ data->exclude_folders_entry = gtk_entry_new ();
+ gtk_widget_set_tooltip_text (data->exclude_folders_entry, _("example: *.o; *.bak"));
+ data->exclude_folders_label = gtk_label_new_with_mnemonic (_("_Exclude folders:"));
+ gtk_misc_set_alignment (GTK_MISC (data->exclude_folders_label), 0.0, 0.5);
+ gtk_label_set_mnemonic_widget (GTK_LABEL (data->exclude_folders_label), data->exclude_folders_entry);
+
+ data->load_button = gtk_button_new_with_mnemonic (_("_Load Options"));
+ data->save_button = gtk_button_new_with_mnemonic (_("Sa_ve Options"));
+ data->clear_button = gtk_button_new_with_mnemonic (_("_Reset Options"));
+
+ main_box = gtk_hbox_new (FALSE, 20);
+ gtk_container_set_border_width (GTK_CONTAINER (main_box), 0);
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_sel), main_box);
+
+ vbox = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
+ gtk_box_pack_start (GTK_BOX (main_box), vbox, TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), data->include_subfold_checkbutton,
+ TRUE, TRUE, 0);
+
+ align = gtk_alignment_new (0, 0, 0, 0);
+ gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 0, 12, 0);
+ gtk_container_add (GTK_CONTAINER (align), data->exclude_symlinks);
+ gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), data->add_if_newer_checkbutton,
+ TRUE, TRUE, 0);
+
+ table = gtk_table_new (2, 4, FALSE);
+ gtk_table_set_row_spacings (GTK_TABLE (table), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table), 6);
+ gtk_box_pack_start (GTK_BOX (vbox), table,
+ TRUE, TRUE, 0);
+
+ gtk_table_attach (GTK_TABLE (table),
+ data->include_files_label,
+ 0, 1,
+ 0, 1,
+ GTK_FILL, 0,
+ 0, 0);
+ gtk_table_attach (GTK_TABLE (table),
+ data->include_files_entry,
+ 1, 4,
+ 0, 1,
+ GTK_FILL|GTK_EXPAND, 0,
+ 0, 0);
+ gtk_table_attach (GTK_TABLE (table),
+ data->exclude_files_label,
+ 0, 1,
+ 1, 2,
+ GTK_FILL, 0,
+ 0, 0);
+ gtk_table_attach (GTK_TABLE (table),
+ data->exclude_files_entry,
+ 1, 2,
+ 1, 2,
+ GTK_FILL|GTK_EXPAND, 0,
+ 0, 0);
+ gtk_table_attach (GTK_TABLE (table),
+ data->exclude_folders_label,
+ 2, 3,
+ 1, 2,
+ GTK_FILL, 0,
+ 0, 0);
+ gtk_table_attach (GTK_TABLE (table),
+ data->exclude_folders_entry,
+ 3, 4,
+ 1, 2,
+ GTK_FILL|GTK_EXPAND, 0,
+ 0, 0);
+
+ /**/
+
+ vbox = gtk_vbox_new (FALSE, 5);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
+ gtk_box_pack_start (GTK_BOX (main_box), vbox, FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), data->load_button,
+ FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), data->save_button,
+ FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), data->clear_button,
+ FALSE, FALSE, 0);
+
+ gtk_widget_show_all (main_box);
+
+ /* set data */
+
+ dlg_add_folder_load_last_options (data);
+
+ /* signals */
+
+ g_signal_connect (G_OBJECT (file_sel),
+ "destroy",
+ G_CALLBACK (open_file_destroy_cb),
+ data);
+ g_signal_connect (G_OBJECT (file_sel),
+ "response",
+ G_CALLBACK (file_sel_response_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->include_subfold_checkbutton),
+ "toggled",
+ G_CALLBACK (include_subfold_toggled_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->load_button),
+ "clicked",
+ G_CALLBACK (load_options_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->save_button),
+ "clicked",
+ G_CALLBACK (save_options_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->clear_button),
+ "clicked",
+ G_CALLBACK (clear_options_cb),
+ data);
+
+ gtk_window_set_modal (GTK_WINDOW (file_sel),TRUE);
+ gtk_widget_show (file_sel);
+}
+
+
+/* load/save the dialog options */
+
+
+static void
+dlg_add_folder_save_last_used_options (DialogData *data,
+ const char *options_path)
+{
+ g_free (data->last_options);
+ data->last_options = g_strdup (file_name_from_path (options_path));
+}
+
+
+static void
+sync_widgets_with_options (DialogData *data,
+ const char *base_dir,
+ const char *filename,
+ const char *include_files,
+ const char *exclude_files,
+ const char *exclude_folders,
+ gboolean update,
+ gboolean recursive,
+ gboolean no_symlinks)
+{
+ if ((base_dir == NULL) || (strcmp (base_dir, "") == 0))
+ base_dir = fr_window_get_add_default_dir (data->window);
+
+ if ((filename != NULL) && (strcmp (filename, base_dir) != 0))
+ gtk_file_chooser_select_uri (GTK_FILE_CHOOSER (data->dialog), filename);
+ else
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (data->dialog), base_dir);
+
+ if (include_files != NULL)
+ gtk_entry_set_text (GTK_ENTRY (data->include_files_entry), include_files);
+ if (exclude_files != NULL)
+ gtk_entry_set_text (GTK_ENTRY (data->exclude_files_entry), exclude_files);
+ if (exclude_folders != NULL)
+ gtk_entry_set_text (GTK_ENTRY (data->exclude_folders_entry), exclude_folders);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->add_if_newer_checkbutton), update);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->include_subfold_checkbutton), recursive);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->exclude_symlinks), no_symlinks);
+}
+
+
+static void
+clear_options_cb (GtkWidget *w,
+ DialogData *data)
+{
+ sync_widgets_with_options (data,
+ gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (data->dialog)),
+ gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog)),
+ "",
+ "",
+ "",
+ FALSE,
+ TRUE,
+ FALSE);
+}
+
+
+static gboolean
+dlg_add_folder_load_options (DialogData *data,
+ const char *name)
+{
+ GFile *options_dir;
+ GFile *options_file;
+ char *file_path;
+ GKeyFile *key_file;
+ GError *error = NULL;
+ char *base_dir = NULL;
+ char *filename = NULL;
+ char *include_files = NULL;
+ char *exclude_files = NULL;
+ char *exclude_folders = NULL;
+ gboolean update;
+ gboolean recursive;
+ gboolean no_symlinks;
+
+ options_dir = get_home_relative_file (RC_OPTIONS_DIR);
+ options_file = g_file_get_child (options_dir, name);
+ file_path = g_file_get_path (options_file);
+ key_file = g_key_file_new ();
+ if (! g_key_file_load_from_file (key_file, file_path, G_KEY_FILE_KEEP_COMMENTS, &error)) {
+ if (error->code != G_IO_ERROR_NOT_FOUND)
+ g_warning ("Could not load options file: %s\n", error->message);
+ g_clear_error (&error);
+ g_object_unref (options_file);
+ g_object_unref (options_dir);
+ g_key_file_free (key_file);
+ return FALSE;
+ }
+
+ base_dir = g_key_file_get_string (key_file, "Options", "base_dir", NULL);
+ filename = g_key_file_get_string (key_file, "Options", "filename", NULL);
+ include_files = g_key_file_get_string (key_file, "Options", "include_files", NULL);
+ exclude_files = g_key_file_get_string (key_file, "Options", "exclude_files", NULL);
+ exclude_folders = g_key_file_get_string (key_file, "Options", "exclude_folders", NULL);
+ update = g_key_file_get_boolean (key_file, "Options", "update", NULL);
+ recursive = g_key_file_get_boolean (key_file, "Options", "recursive", NULL);
+ no_symlinks = g_key_file_get_boolean (key_file, "Options", "no_symlinks", NULL);
+
+ sync_widgets_with_options (data,
+ base_dir,
+ filename,
+ include_files,
+ exclude_files,
+ exclude_folders,
+ update,
+ recursive,
+ no_symlinks);
+
+ dlg_add_folder_save_last_used_options (data, file_path);
+
+ g_free (base_dir);
+ g_free (filename);
+ g_free (include_files);
+ g_free (exclude_files);
+ g_free (exclude_folders);
+ g_key_file_free (key_file);
+ g_free (file_path);
+ g_object_unref (options_file);
+ g_object_unref (options_dir);
+
+ return TRUE;
+}
+
+
+static void
+dlg_add_folder_load_last_options (DialogData *data)
+{
+ char *base_dir = NULL;
+ char *filename = NULL;
+ char *include_files = NULL;
+ char *exclude_files = NULL;
+ char *exclude_folders = NULL;
+ gboolean update;
+ gboolean recursive;
+ gboolean no_symlinks;
+
+ base_dir = eel_mateconf_get_string (PREF_ADD_CURRENT_FOLDER, "");
+ filename = eel_mateconf_get_string (PREF_ADD_FILENAME, "");
+ include_files = eel_mateconf_get_string (PREF_ADD_INCLUDE_FILES, "");
+ exclude_files = eel_mateconf_get_string (PREF_ADD_EXCLUDE_FILES, "");
+ exclude_folders = eel_mateconf_get_string (PREF_ADD_EXCLUDE_FOLDERS, "");
+ update = eel_mateconf_get_boolean (PREF_ADD_UPDATE, FALSE);
+ recursive = eel_mateconf_get_boolean (PREF_ADD_RECURSIVE, TRUE);
+ no_symlinks = eel_mateconf_get_boolean (PREF_ADD_NO_SYMLINKS, FALSE);
+
+ sync_widgets_with_options (data,
+ base_dir,
+ filename,
+ include_files,
+ exclude_files,
+ exclude_folders,
+ update,
+ recursive,
+ no_symlinks);
+
+ g_free (base_dir);
+ g_free (filename);
+ g_free (include_files);
+ g_free (exclude_files);
+ g_free (exclude_folders);
+}
+
+
+static void
+get_options_from_widgets (DialogData *data,
+ char **base_dir,
+ char **filename,
+ const char **include_files,
+ const char **exclude_files,
+ const char **exclude_folders,
+ gboolean *update,
+ gboolean *recursive,
+ gboolean *no_symlinks)
+{
+ *base_dir = gtk_file_chooser_get_current_folder_uri (GTK_FILE_CHOOSER (data->dialog));
+ *filename = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog));
+ *update = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->add_if_newer_checkbutton));
+ *recursive = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->include_subfold_checkbutton));
+ *no_symlinks = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->exclude_symlinks));
+
+ *include_files = gtk_entry_get_text (GTK_ENTRY (data->include_files_entry));
+ if (utf8_only_spaces (*include_files))
+ *include_files = "";
+
+ *exclude_files = gtk_entry_get_text (GTK_ENTRY (data->exclude_files_entry));
+ if (utf8_only_spaces (*exclude_files))
+ *exclude_files = "";
+
+ *exclude_folders = gtk_entry_get_text (GTK_ENTRY (data->exclude_folders_entry));
+ if (utf8_only_spaces (*exclude_folders))
+ *exclude_folders = "";
+}
+
+
+static void
+dlg_add_folder_save_current_options (DialogData *data,
+ GFile *options_file)
+{
+ char *base_dir;
+ char *filename;
+ const char *include_files;
+ const char *exclude_files;
+ const char *exclude_folders;
+ gboolean update;
+ gboolean recursive;
+ gboolean no_symlinks;
+ GKeyFile *key_file;
+
+ get_options_from_widgets (data,
+ &base_dir,
+ &filename,
+ &include_files,
+ &exclude_files,
+ &exclude_folders,
+ &update,
+ &recursive,
+ &no_symlinks);
+
+ fr_window_set_add_default_dir (data->window, base_dir);
+
+ key_file = g_key_file_new ();
+ g_key_file_set_string (key_file, "Options", "base_dir", base_dir);
+ g_key_file_set_string (key_file, "Options", "filename", filename);
+ g_key_file_set_string (key_file, "Options", "include_files", include_files);
+ g_key_file_set_string (key_file, "Options", "exclude_files", exclude_files);
+ g_key_file_set_string (key_file, "Options", "exclude_folders", exclude_folders);
+ g_key_file_set_boolean (key_file, "Options", "update", update);
+ g_key_file_set_boolean (key_file, "Options", "recursive", recursive);
+ g_key_file_set_boolean (key_file, "Options", "no_symlinks", no_symlinks);
+
+ g_key_file_save (key_file, options_file);
+
+ g_key_file_free (key_file);
+ g_free (base_dir);
+ g_free (filename);
+}
+
+
+static void
+dlg_add_folder_save_last_options (DialogData *data)
+{
+ char *base_dir;
+ char *filename;
+ const char *include_files;
+ const char *exclude_files;
+ const char *exclude_folders;
+ gboolean update;
+ gboolean recursive;
+ gboolean no_symlinks;
+
+ get_options_from_widgets (data,
+ &base_dir,
+ &filename,
+ &include_files,
+ &exclude_files,
+ &exclude_folders,
+ &update,
+ &recursive,
+ &no_symlinks);
+
+ eel_mateconf_set_string (PREF_ADD_CURRENT_FOLDER, base_dir);
+ eel_mateconf_set_string (PREF_ADD_FILENAME, filename);
+ eel_mateconf_set_string (PREF_ADD_INCLUDE_FILES, include_files);
+ eel_mateconf_set_string (PREF_ADD_EXCLUDE_FILES, exclude_files);
+ eel_mateconf_set_string (PREF_ADD_EXCLUDE_FOLDERS, exclude_folders);
+ eel_mateconf_set_boolean (PREF_ADD_UPDATE, update);
+ eel_mateconf_set_boolean (PREF_ADD_RECURSIVE, recursive);
+ eel_mateconf_set_boolean (PREF_ADD_NO_SYMLINKS, no_symlinks);
+
+ g_free (base_dir);
+ g_free (filename);
+}
+
+
+typedef struct {
+ DialogData *data;
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+ GtkWidget *aod_treeview;
+ GtkTreeModel *aod_model;
+} LoadOptionsDialogData;
+
+
+static void
+aod_destroy_cb (GtkWidget *widget,
+ LoadOptionsDialogData *aod_data)
+{
+ g_object_unref (aod_data->builder);
+ g_free (aod_data);
+}
+
+
+static void
+aod_apply_cb (GtkWidget *widget,
+ gpointer callback_data)
+{
+ LoadOptionsDialogData *aod_data = callback_data;
+ DialogData *data = aod_data->data;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ char *options_name;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aod_data->aod_treeview));
+ if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (aod_data->aod_model, &iter, 1, &options_name, -1);
+
+ dlg_add_folder_load_options (data, options_name);
+ g_free (options_name);
+
+ gtk_widget_destroy (aod_data->dialog);
+}
+
+
+static void
+aod_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer callback_data)
+{
+ aod_apply_cb (NULL, callback_data);
+}
+
+
+static void
+aod_update_option_list (LoadOptionsDialogData *aod_data)
+{
+ GtkListStore *list_store = GTK_LIST_STORE (aod_data->aod_model);
+ GFile *options_dir;
+ GFileEnumerator *file_enum;
+ GFileInfo *info;
+ GError *err = NULL;
+
+ gtk_list_store_clear (list_store);
+
+ options_dir = get_home_relative_file (RC_OPTIONS_DIR);
+ make_directory_tree (options_dir, 0700, NULL);
+
+ file_enum = g_file_enumerate_children (options_dir, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &err);
+ if (err != NULL) {
+ g_warning ("Failed to enumerate children: %s", err->message);
+ g_clear_error (&err);
+ g_object_unref (options_dir);
+ return;
+ }
+
+ while ((info = g_file_enumerator_next_file (file_enum, NULL, &err)) != NULL) {
+ const char *name;
+ char *display_name;
+ GtkTreeIter iter;
+
+ if (err != NULL) {
+ g_warning ("Failed to get info while enumerating: %s", err->message);
+ g_clear_error (&err);
+ continue;
+ }
+
+ name = g_file_info_get_name (info);
+ display_name = g_filename_display_name (name);
+
+ gtk_list_store_append (GTK_LIST_STORE (aod_data->aod_model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (aod_data->aod_model), &iter,
+ 0, name,
+ 1, display_name,
+ -1);
+
+ g_free (display_name);
+ g_object_unref (info);
+ }
+
+ if (err != NULL) {
+ g_warning ("Failed to get info after enumeration: %s", err->message);
+ g_clear_error (&err);
+ }
+
+ g_object_unref (options_dir);
+}
+
+
+static void
+aod_remove_cb (GtkWidget *widget,
+ LoadOptionsDialogData *aod_data)
+{
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+ char *filename;
+ GFile *options_dir;
+ GFile *options_file;
+ GError *error = NULL;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (aod_data->aod_treeview));
+ if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (aod_data->aod_model, &iter, 1, &filename, -1);
+ gtk_list_store_remove (GTK_LIST_STORE (aod_data->aod_model), &iter);
+
+ options_dir = get_home_relative_file (RC_OPTIONS_DIR);
+ options_file = g_file_get_child (options_dir, filename);
+ if (! g_file_delete (options_file, NULL, &error)) {
+ g_warning ("could not delete the options: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (options_file);
+ g_object_unref (options_dir);
+ g_free (filename);
+}
+
+
+static void
+load_options_cb (GtkWidget *w,
+ DialogData *data)
+{
+ LoadOptionsDialogData *aod_data;
+ GtkWidget *ok_button;
+ GtkWidget *cancel_button;
+ GtkWidget *remove_button;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ aod_data = g_new0 (LoadOptionsDialogData, 1);
+
+ aod_data->data = data;
+ aod_data->builder = _gtk_builder_new_from_file ("add-options.ui");
+ if (aod_data->builder == NULL) {
+ g_free (aod_data);
+ return;
+ }
+
+ /* Get the widgets. */
+
+ aod_data->dialog = _gtk_builder_get_widget (aod_data->builder, "add_options_dialog");
+ aod_data->aod_treeview = _gtk_builder_get_widget (aod_data->builder, "aod_treeview");
+
+ ok_button = _gtk_builder_get_widget (aod_data->builder, "aod_okbutton");
+ cancel_button = _gtk_builder_get_widget (aod_data->builder, "aod_cancelbutton");
+ remove_button = _gtk_builder_get_widget (aod_data->builder, "aod_remove_button");
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (aod_data->dialog),
+ "destroy",
+ G_CALLBACK (aod_destroy_cb),
+ aod_data);
+ g_signal_connect (G_OBJECT (aod_data->aod_treeview),
+ "row_activated",
+ G_CALLBACK (aod_activated_cb),
+ aod_data);
+ g_signal_connect_swapped (G_OBJECT (cancel_button),
+ "clicked",
+ G_CALLBACK (gtk_widget_destroy),
+ G_OBJECT (aod_data->dialog));
+ g_signal_connect (G_OBJECT (ok_button),
+ "clicked",
+ G_CALLBACK (aod_apply_cb),
+ aod_data);
+ g_signal_connect (G_OBJECT (remove_button),
+ "clicked",
+ G_CALLBACK (aod_remove_cb),
+ aod_data);
+
+ /* Set data. */
+
+ aod_data->aod_model = GTK_TREE_MODEL (gtk_list_store_new (2,
+ G_TYPE_STRING,
+ G_TYPE_STRING));
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (aod_data->aod_model),
+ 0,
+ GTK_SORT_ASCENDING);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (aod_data->aod_treeview),
+ aod_data->aod_model);
+ g_object_unref (aod_data->aod_model);
+
+ /**/
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (NULL,
+ renderer,
+ "text", 0,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, 0);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (aod_data->aod_treeview),
+ column);
+
+ aod_update_option_list (aod_data);
+
+ /* Run */
+
+ gtk_window_set_transient_for (GTK_WINDOW (aod_data->dialog),
+ GTK_WINDOW (data->dialog));
+ gtk_window_set_modal (GTK_WINDOW (aod_data->dialog), TRUE);
+ gtk_widget_show (aod_data->dialog);
+}
+
+
+static void
+save_options_cb (GtkWidget *w,
+ DialogData *data)
+{
+ GFile *options_dir;
+ GFile *options_file;
+ char *opt_filename;
+
+ options_dir = get_home_relative_file (RC_OPTIONS_DIR);
+ make_directory_tree (options_dir, 0700, NULL);
+
+ opt_filename = _gtk_request_dialog_run (
+ GTK_WINDOW (data->dialog),
+ GTK_DIALOG_MODAL,
+ _("Save Options"),
+ _("Options Name:"),
+ (data->last_options != NULL) ? data->last_options : "",
+ 1024,
+ GTK_STOCK_CANCEL,
+ GTK_STOCK_SAVE);
+ if (opt_filename == NULL)
+ return;
+
+ options_file = g_file_get_child_for_display_name (options_dir, opt_filename, NULL);
+ dlg_add_folder_save_current_options (data, options_file);
+ dlg_add_folder_save_last_used_options (data, opt_filename);
+
+ g_free (opt_filename);
+ g_object_unref (options_file);
+ g_object_unref (options_dir);
+}
diff --git a/src/dlg-add-folder.h b/src/dlg-add-folder.h
new file mode 100644
index 0000000..44720a3
--- /dev/null
+++ b/src/dlg-add-folder.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_ADD_FOLDER_H
+#define DLG_ADD_FOLDER_H
+
+#include <gtk/gtk.h>
+
+void add_folder_cb (GtkWidget *widget, void *data);
+
+#endif /* DLG_ADD_FOLDER_H */
diff --git a/src/dlg-ask-password.c b/src/dlg-ask-password.c
new file mode 100644
index 0000000..b5590dc
--- /dev/null
+++ b/src/dlg-ask-password.c
@@ -0,0 +1,170 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "gtk-utils.h"
+#include "fr-window.h"
+
+
+typedef enum {
+ FR_PASSWORD_TYPE_MAIN,
+ FR_PASSWORD_TYPE_PASTE_FROM
+} FrPasswordType;
+
+typedef struct {
+ GtkBuilder *builder;
+ FrWindow *window;
+ FrPasswordType pwd_type;
+ GtkWidget *dialog;
+ GtkWidget *pw_password_entry;
+} DialogData;
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ g_object_unref (data->builder);
+ g_free (data);
+}
+
+
+static void
+ask_password__response_cb (GtkWidget *dialog,
+ int response_id,
+ DialogData *data)
+{
+ char *password;
+
+ switch (response_id) {
+ case GTK_RESPONSE_OK:
+ password = _gtk_entry_get_locale_text (GTK_ENTRY (data->pw_password_entry));
+ if (data->pwd_type == FR_PASSWORD_TYPE_MAIN)
+ fr_window_set_password (data->window, password);
+ else if (data->pwd_type == FR_PASSWORD_TYPE_PASTE_FROM)
+ fr_window_set_password_for_paste (data->window, password);
+ g_free (password);
+ if (fr_window_is_batch_mode (data->window))
+ fr_window_resume_batch (data->window);
+ else
+ fr_window_restart_current_batch_action (data->window);
+ break;
+
+ default:
+ if (fr_window_is_batch_mode (data->window))
+ gtk_widget_destroy (GTK_WIDGET (data->window));
+ else
+ fr_window_reset_current_batch_action (data->window);
+ break;
+ }
+
+ gtk_widget_destroy (data->dialog);
+}
+
+
+static void
+dlg_ask_password__common (FrWindow *window,
+ FrPasswordType pwd_type)
+{
+ DialogData *data;
+ GtkWidget *label;
+ char *text;
+ char *name;
+
+ data = g_new0 (DialogData, 1);
+
+ data->builder = _gtk_builder_new_from_file ("batch-password.ui");
+ if (data->builder == NULL) {
+ g_free (data);
+ return;
+ }
+
+ data->window = window;
+ data->pwd_type = pwd_type;
+
+ /* Get the widgets. */
+
+ data->dialog = _gtk_builder_get_widget (data->builder, "password_dialog");
+ data->pw_password_entry = _gtk_builder_get_widget (data->builder, "pw_password_entry");
+
+ label = _gtk_builder_get_widget (data->builder, "pw_password_label");
+
+ /* Set widgets data. */
+
+ if (data->pwd_type == FR_PASSWORD_TYPE_MAIN)
+ name = g_uri_display_basename (fr_window_get_archive_uri (window));
+ else if (data->pwd_type == FR_PASSWORD_TYPE_PASTE_FROM)
+ name = g_uri_display_basename (fr_window_get_paste_archive_uri (window));
+ text = g_strdup_printf (_("Enter the password for the archive '%s'."), name);
+ gtk_label_set_label (GTK_LABEL (label), text);
+ g_free (text);
+
+ if (fr_window_get_password (window) != NULL)
+ _gtk_entry_set_locale_text (GTK_ENTRY (data->pw_password_entry),
+ fr_window_get_password (window));
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (destroy_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "response",
+ G_CALLBACK (ask_password__response_cb),
+ data);
+
+ /* Run dialog. */
+
+ gtk_widget_grab_focus (data->pw_password_entry);
+ if (gtk_widget_get_realized (GTK_WIDGET (window))) {
+ gtk_window_set_transient_for (GTK_WINDOW (data->dialog),
+ GTK_WINDOW (window));
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
+ }
+ else
+ gtk_window_set_title (GTK_WINDOW (data->dialog), name);
+ g_free (name);
+
+ gtk_widget_show (data->dialog);
+}
+
+
+void
+dlg_ask_password (FrWindow *window)
+{
+ dlg_ask_password__common (window, FR_PASSWORD_TYPE_MAIN);
+}
+
+
+void
+dlg_ask_password_for_paste_operation (FrWindow *window)
+{
+ dlg_ask_password__common (window, FR_PASSWORD_TYPE_PASTE_FROM);
+}
diff --git a/src/dlg-ask-password.h b/src/dlg-ask-password.h
new file mode 100644
index 0000000..83fee75
--- /dev/null
+++ b/src/dlg-ask-password.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_PASSWORD_H
+#define DLG_PASSWORD_H
+
+#include "fr-window.h"
+
+void dlg_ask_password (FrWindow *window);
+void dlg_ask_password_for_paste_operation (FrWindow *window);
+
+#endif /* DLG_PASSWORD_H */
diff --git a/src/dlg-batch-add.c b/src/dlg-batch-add.c
new file mode 100644
index 0000000..47d11e2
--- /dev/null
+++ b/src/dlg-batch-add.c
@@ -0,0 +1,611 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include "file-utils.h"
+#include "fr-stock.h"
+#include "mateconf-utils.h"
+#include "fr-window.h"
+#include "typedefs.h"
+#include "gtk-utils.h"
+#include "glib-utils.h"
+#include "preferences.h"
+#include "main.h"
+
+
+#define ARCHIVE_ICON_SIZE (48)
+#define DEFAULT_EXTENSION ".tar.gz"
+#define BAD_CHARS "/\\*"
+
+
+typedef struct {
+ FrWindow *window;
+ GtkBuilder *builder;
+ int *supported_types;
+
+ GtkWidget *dialog;
+ GtkWidget *a_add_to_entry;
+ GtkWidget *a_location_filechooserbutton;
+ GtkWidget *add_image;
+ GtkWidget *a_archive_type_combo_box;
+ GtkWidget *a_other_options_expander;
+ GtkWidget *a_password_entry;
+ GtkWidget *a_password_label;
+ GtkWidget *a_encrypt_header_checkbutton;
+ GtkWidget *a_volume_checkbutton;
+ GtkWidget *a_volume_spinbutton;
+ GtkWidget *a_volume_box;
+
+ GList *file_list;
+ gboolean add_clicked;
+ const char *last_mime_type;
+ gboolean single_file;
+} DialogData;
+
+
+static const char *
+get_ext (DialogData *data)
+{
+ int idx;
+
+ idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->a_archive_type_combo_box));
+
+ return mime_type_desc[data->supported_types[idx]].default_ext;
+}
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ eel_mateconf_set_string (PREF_BATCH_ADD_DEFAULT_EXTENSION, get_ext (data));
+ /*eel_mateconf_set_boolean (PREF_BATCH_OTHER_OPTIONS, data->add_clicked ? FALSE : gtk_expander_get_expanded (GTK_EXPANDER (data->a_other_options_expander)));*/
+ eel_mateconf_set_boolean (PREF_ENCRYPT_HEADER, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton)));
+
+ if (! data->add_clicked) {
+ fr_window_pop_message (data->window);
+ fr_window_stop_batch (data->window);
+ }
+
+ g_object_unref (data->builder);
+ g_free (data);
+}
+
+
+static void
+set_archive_options (DialogData *data)
+{
+ int idx;
+
+ idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->a_archive_type_combo_box));
+ if (mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_ENCRYPT) {
+ const char *pwd;
+
+ pwd = gtk_entry_get_text (GTK_ENTRY (data->a_password_entry));
+ if (pwd != NULL) {
+ if (strcmp (pwd, "") != 0) {
+ fr_window_set_password (data->window, pwd);
+ if (mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER)
+ fr_window_set_encrypt_header (data->window, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton)));
+ }
+ }
+ }
+
+ if ((mime_type_desc[data->supported_types[idx]].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES)
+ && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->a_volume_checkbutton)))
+ {
+ double value;
+ int size;
+
+ value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (data->a_volume_spinbutton));
+ size = floor (value * MEGABYTE);
+ eel_mateconf_set_integer (PREF_BATCH_VOLUME_SIZE, size);
+ fr_window_set_volume_size (data->window, (guint) size);
+ }
+}
+
+
+static void
+help_clicked_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-fmgr-add");
+}
+
+
+static void
+add_clicked_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ FrWindow *window = data->window;
+ char *archive_name;
+ char *archive_dir;
+ char *archive_file;
+ char *tmp;
+ const char *archive_ext;
+ gboolean do_not_add = FALSE;
+ GError *error = NULL;
+
+ data->add_clicked = TRUE;
+
+ /* Collect data */
+
+ archive_name = g_uri_escape_string (gtk_entry_get_text (GTK_ENTRY (data->a_add_to_entry)), NULL, FALSE);
+
+ /* Check whether the user entered a valid archive name. */
+
+ if ((archive_name == NULL) || (*archive_name == '\0')) {
+ GtkWidget *d;
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Could not create the archive"),
+ "%s",
+ _("You have to specify an archive name."));
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+ g_free (archive_name);
+
+ return;
+ }
+ else if (strchrs (archive_name, BAD_CHARS)) {
+ GtkWidget *d;
+ char *utf8_name = g_filename_display_name (archive_name);
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Could not create the archive"),
+ _("The name \"%s\" is not valid because it cannot contain the characters: %s\n\n%s"),
+ utf8_name,
+ BAD_CHARS,
+ _("Please use a different name."));
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_free (utf8_name);
+ g_free (archive_name);
+
+ return;
+ }
+
+ /* Check directory existence. */
+
+ archive_dir = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->a_location_filechooserbutton));
+ if (archive_dir == NULL) {
+ g_free (archive_dir);
+ g_free (archive_name);
+ return;
+ }
+
+ if (! check_permissions (archive_dir, R_OK|W_OK|X_OK)) {
+ GtkWidget *d;
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Could not create the archive"),
+ "%s",
+ _("You don't have the right permissions to create an archive in the destination folder."));
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_free (archive_dir);
+ g_free (archive_name);
+ return;
+ }
+
+ if (! uri_is_dir (archive_dir)) {
+ GtkWidget *d;
+ int r;
+ char *folder_name;
+ char *msg;
+
+ folder_name = g_filename_display_name (archive_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 (data->dialog),
+ 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);
+
+ do_not_add = (r != GTK_RESPONSE_YES);
+ }
+
+ if (! do_not_add && ! ensure_dir_exists (archive_dir, 0755, &error)) {
+ GtkWidget *d;
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Could not create the archive"),
+ _("Could not create the destination folder: %s."),
+ error->message);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_error_free (error);
+ g_free (archive_dir);
+ g_free (archive_name);
+ return;
+ }
+
+ if (do_not_add) {
+ GtkWidget *d;
+
+ d = _gtk_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_DIALOG_WARNING,
+ _("Archive not created"),
+ NULL,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_OK);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_free (archive_dir);
+ g_free (archive_name);
+
+ return;
+ }
+
+ /**/
+
+ archive_ext = get_ext (data);
+ tmp = archive_name;
+ archive_name = g_strconcat (tmp, archive_ext, NULL);
+ g_free (tmp);
+ archive_file = g_strconcat (archive_dir, "/", archive_name, NULL);
+
+ if (uri_is_dir (archive_file)) {
+ GtkWidget *d;
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Could not create the archive"),
+ "%s",
+ _("You have to specify an archive name."));
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_free (archive_name);
+ g_free (archive_dir);
+ g_free (archive_file);
+
+ return;
+ }
+
+ if (uri_exists (archive_file)) {
+ GtkWidget *d;
+ int r;
+
+ d = _gtk_message_dialog_new (GTK_WINDOW (data->dialog),
+ GTK_DIALOG_MODAL,
+ GTK_STOCK_DIALOG_QUESTION,
+ _("The archive is already present. Do you want to overwrite it?"),
+ NULL,
+ GTK_STOCK_NO, GTK_RESPONSE_NO,
+ _("_Overwrite"), 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) {
+ GFile *file;
+ GError *err = NULL;
+
+ /* FIXME: convert this code in a function in file-utils.c */
+ file = g_file_new_for_uri (archive_file);
+ g_file_delete (file, NULL, &err);
+ if (err != NULL) {
+ g_warning ("Failed to delete file %s: %s",
+ archive_file,
+ err->message);
+ g_clear_error (&err);
+ }
+ g_object_unref (file);
+ }
+ else {
+ g_free (archive_name);
+ g_free (archive_dir);
+ g_free (archive_file);
+ return;
+ }
+ }
+ set_archive_options (data);
+ gtk_widget_destroy (data->dialog);
+
+ fr_window_archive_new (window, archive_file);
+
+ g_free (archive_name);
+ g_free (archive_dir);
+ g_free (archive_file);
+}
+
+
+static void
+update_sensitivity_for_mime_type (DialogData *data,
+ const char *mime_type)
+{
+ int i;
+
+ if (mime_type == NULL) {
+ gtk_widget_set_sensitive (data->a_password_entry, FALSE);
+ gtk_widget_set_sensitive (data->a_password_label, FALSE);
+ gtk_widget_set_sensitive (data->a_encrypt_header_checkbutton, FALSE);
+ gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton), TRUE);
+ gtk_widget_set_sensitive (data->a_volume_box, FALSE);
+ return;
+ }
+
+ for (i = 0; mime_type_desc[i].mime_type != NULL; i++) {
+ if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) {
+ gboolean sensitive;
+
+ sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT;
+ gtk_widget_set_sensitive (data->a_password_entry, sensitive);
+ gtk_widget_set_sensitive (data->a_password_label, sensitive);
+
+ sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER;
+ gtk_widget_set_sensitive (data->a_encrypt_header_checkbutton, sensitive);
+ gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton), ! sensitive);
+
+ sensitive = mime_type_desc[i].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES;
+ gtk_widget_set_sensitive (data->a_volume_box, sensitive);
+
+ break;
+ }
+ }
+}
+
+
+static void
+archive_type_combo_box_changed_cb (GtkComboBox *combo_box,
+ DialogData *data)
+{
+ const char *mime_type;
+ int idx = gtk_combo_box_get_active (combo_box);
+
+ mime_type = mime_type_desc[data->supported_types[idx]].mime_type;
+
+ gtk_image_set_from_pixbuf (GTK_IMAGE (data->add_image), get_mime_type_pixbuf (mime_type, ARCHIVE_ICON_SIZE, NULL));
+ update_sensitivity_for_mime_type (data, mime_type);
+}
+
+
+static void
+update_archive_type_combo_box_from_ext (DialogData *data,
+ const char *ext)
+{
+ int idx = 0;
+ int i;
+
+ if (ext == NULL) {
+ gtk_combo_box_set_active (GTK_COMBO_BOX (data->a_archive_type_combo_box), 0);
+ return;
+ }
+
+ for (i = 0; data->supported_types[i] != -1; i++)
+ if (strcmp (ext, mime_type_desc[data->supported_types[i]].default_ext) == 0) {
+ idx = i;
+ break;
+ }
+
+ gtk_combo_box_set_active (GTK_COMBO_BOX (data->a_archive_type_combo_box), idx);
+}
+
+
+static void
+update_sensitivity (DialogData *data)
+{
+ gtk_widget_set_sensitive (data->a_volume_spinbutton, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->a_volume_checkbutton)));
+}
+
+
+static void
+password_entry_changed_cb (GtkEditable *editable,
+ gpointer user_data)
+{
+ update_sensitivity ((DialogData *) user_data);
+}
+
+
+static void
+volume_toggled_cb (GtkToggleButton *toggle_button,
+ gpointer user_data)
+{
+ update_sensitivity ((DialogData *) user_data);
+}
+
+
+void
+dlg_batch_add_files (FrWindow *window,
+ GList *file_list)
+{
+ DialogData *data;
+ GtkWidget *cancel_button;
+ GtkWidget *help_button;
+ GtkWidget *add_button;
+ GtkWidget *a_archive_type_box;
+ GtkSizeGroup *size_group;
+ char *automatic_name = NULL;
+ char *default_ext;
+ const char *first_filename;
+ char *parent;
+ int i;
+
+ if (file_list == NULL)
+ return;
+
+ data = g_new0 (DialogData, 1);
+
+ data->builder = _gtk_builder_new_from_file ("batch-add-files.ui");
+ if (data->builder == NULL) {
+ g_free (data);
+ return;
+ }
+
+ data->window = window;
+ data->file_list = file_list;
+ data->single_file = ((file_list->next == NULL) && uri_is_file ((char*) file_list->data));
+ data->add_clicked = FALSE;
+
+ /* Get the widgets. */
+
+ data->dialog = _gtk_builder_get_widget (data->builder, "batch_add_files_dialog");
+ data->a_add_to_entry = _gtk_builder_get_widget (data->builder, "a_add_to_entry");
+ data->a_location_filechooserbutton = _gtk_builder_get_widget (data->builder, "a_location_filechooserbutton");
+ data->a_password_entry = _gtk_builder_get_widget (data->builder, "a_password_entry");
+ data->a_password_label = _gtk_builder_get_widget (data->builder, "a_password_label");
+ data->a_other_options_expander = _gtk_builder_get_widget (data->builder, "a_other_options_expander");
+ data->a_encrypt_header_checkbutton = _gtk_builder_get_widget (data->builder, "a_encrypt_header_checkbutton");
+
+ data->a_volume_checkbutton = _gtk_builder_get_widget (data->builder, "a_volume_checkbutton");
+ data->a_volume_spinbutton = _gtk_builder_get_widget (data->builder, "a_volume_spinbutton");
+ data->a_volume_box = _gtk_builder_get_widget (data->builder, "a_volume_box");
+
+ add_button = _gtk_builder_get_widget (data->builder, "a_add_button");
+ cancel_button = _gtk_builder_get_widget (data->builder, "a_cancel_button");
+ help_button = _gtk_builder_get_widget (data->builder, "a_help_button");
+ a_archive_type_box = _gtk_builder_get_widget (data->builder, "a_archive_type_box");
+
+ data->add_image = _gtk_builder_get_widget (data->builder, "a_add_image");
+
+ /* Set widgets data. */
+
+ size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+ gtk_size_group_add_widget (size_group, _gtk_builder_get_widget (data->builder, "a_archive_label"));
+ gtk_size_group_add_widget (size_group, _gtk_builder_get_widget (data->builder, "a_location_label"));
+ gtk_size_group_add_widget (size_group, _gtk_builder_get_widget (data->builder, "a_password_label"));
+
+ gtk_button_set_use_stock (GTK_BUTTON (add_button), TRUE);
+ gtk_button_set_label (GTK_BUTTON (add_button), FR_STOCK_CREATE_ARCHIVE);
+ gtk_expander_set_expanded (GTK_EXPANDER (data->a_other_options_expander), FALSE /*eel_mateconf_get_boolean (PREF_BATCH_OTHER_OPTIONS, FALSE)*/);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->a_encrypt_header_checkbutton), eel_mateconf_get_boolean (PREF_ENCRYPT_HEADER, FALSE));
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->a_volume_spinbutton), (double) eel_mateconf_get_integer (PREF_BATCH_VOLUME_SIZE, 0) / MEGABYTE);
+
+ first_filename = (char*) file_list->data;
+ parent = remove_level_from_path (first_filename);
+
+ if (file_list->next == NULL)
+ automatic_name = g_uri_unescape_string (file_name_from_path ((char*) file_list->data), NULL);
+ else {
+ automatic_name = g_uri_unescape_string (file_name_from_path (parent), NULL);
+ if ((automatic_name == NULL) || (automatic_name[0] == '\0')) {
+ g_free (automatic_name);
+ automatic_name = g_uri_unescape_string (file_name_from_path (first_filename), NULL);
+ }
+ }
+
+ _gtk_entry_set_filename_text (GTK_ENTRY (data->a_add_to_entry), automatic_name);
+ g_free (automatic_name);
+
+ if (check_permissions (parent, R_OK | W_OK))
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (data->a_location_filechooserbutton), parent);
+ else
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (data->a_location_filechooserbutton), get_home_uri ());
+ g_free (parent);
+
+ /* archive type combobox */
+
+ data->a_archive_type_combo_box = gtk_combo_box_new_text ();
+ if (data->single_file)
+ data->supported_types = single_file_save_type;
+ else
+ data->supported_types = save_type;
+ sort_mime_types_by_extension (data->supported_types);
+
+ for (i = 0; data->supported_types[i] != -1; i++)
+ gtk_combo_box_append_text (GTK_COMBO_BOX (data->a_archive_type_combo_box),
+ mime_type_desc[data->supported_types[i]].default_ext);
+
+ gtk_box_pack_start (GTK_BOX (a_archive_type_box), data->a_archive_type_combo_box, TRUE, TRUE, 0);
+ gtk_widget_show_all (a_archive_type_box);
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (destroy_cb),
+ data);
+ g_signal_connect_swapped (G_OBJECT (cancel_button),
+ "clicked",
+ G_CALLBACK (gtk_widget_destroy),
+ G_OBJECT (data->dialog));
+ g_signal_connect (G_OBJECT (add_button),
+ "clicked",
+ G_CALLBACK (add_clicked_cb),
+ data);
+ g_signal_connect (G_OBJECT (help_button),
+ "clicked",
+ G_CALLBACK (help_clicked_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->a_archive_type_combo_box),
+ "changed",
+ G_CALLBACK (archive_type_combo_box_changed_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->a_password_entry),
+ "changed",
+ G_CALLBACK (password_entry_changed_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->a_volume_checkbutton),
+ "toggled",
+ G_CALLBACK (volume_toggled_cb),
+ data);
+
+ /* Run dialog. */
+
+ default_ext = eel_mateconf_get_string (PREF_BATCH_ADD_DEFAULT_EXTENSION, DEFAULT_EXTENSION);
+ update_archive_type_combo_box_from_ext (data, default_ext);
+ g_free (default_ext);
+
+ gtk_widget_grab_focus (data->a_add_to_entry);
+ gtk_editable_select_region (GTK_EDITABLE (data->a_add_to_entry),
+ 0, -1);
+
+ update_sensitivity (data);
+
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), FALSE);
+ gtk_window_present (GTK_WINDOW (data->dialog));
+}
diff --git a/src/dlg-batch-add.h b/src/dlg-batch-add.h
new file mode 100644
index 0000000..d63a545
--- /dev/null
+++ b/src/dlg-batch-add.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_BATCH_ADD_H
+#define DLG_BATCH_ADD_H
+
+#include "fr-window.h"
+
+void dlg_batch_add_files (FrWindow *window,
+ GList *file_list);
+
+#endif /* DLG_BATCH_ADD_H */
diff --git a/src/dlg-delete.c b/src/dlg-delete.c
new file mode 100644
index 0000000..dc4f5d7
--- /dev/null
+++ b/src/dlg-delete.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <gtk/gtk.h>
+#include "fr-window.h"
+#include "gtk-utils.h"
+#include "file-utils.h"
+
+
+typedef struct {
+ FrWindow *window;
+ GList *selected_files;
+ GtkBuilder *builder;
+
+ GtkWidget *dialog;
+ GtkWidget *d_all_files_radio;
+ GtkWidget *d_selected_files_radio;
+ GtkWidget *d_files_radio;
+ GtkWidget *d_files_entry;
+} DialogData;
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ path_list_free (data->selected_files);
+ g_object_unref (G_OBJECT (data->builder));
+ g_free (data);
+}
+
+
+/* called when the "ok" button is pressed. */
+static void
+ok_clicked_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ gboolean selected_files;
+ gboolean pattern_files;
+ FrWindow *window = data->window;
+ GList *file_list = NULL;
+ gboolean do_not_remove_if_null = FALSE;
+
+ selected_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->d_selected_files_radio));
+ pattern_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->d_files_radio));
+
+ /* create the file list. */
+
+ if (selected_files) {
+ file_list = data->selected_files;
+ data->selected_files = NULL; /* do not the list when destroying the dialog. */
+ }
+ else if (pattern_files) {
+ const char *pattern;
+
+ pattern = gtk_entry_get_text (GTK_ENTRY (data->d_files_entry));
+ file_list = fr_window_get_file_list_pattern (window, pattern);
+ if (file_list == NULL)
+ do_not_remove_if_null = TRUE;
+ }
+
+ /* close the dialog. */
+
+ gtk_widget_destroy (data->dialog);
+
+ /* remove ! */
+
+ if (! do_not_remove_if_null || (file_list != NULL))
+ fr_window_archive_remove (window, file_list);
+
+ path_list_free (file_list);
+}
+
+
+static void
+entry_changed_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->d_files_radio)))
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->d_files_radio), TRUE);
+}
+
+
+static void
+dlg_delete__common (FrWindow *window,
+ GList *selected_files)
+{
+ DialogData *data;
+ GtkWidget *cancel_button;
+ GtkWidget *ok_button;
+
+ data = g_new (DialogData, 1);
+ data->window = window;
+ data->selected_files = selected_files;
+
+ data->builder = _gtk_builder_new_from_file ("delete.ui");
+ if (data->builder == NULL) {
+ g_free (data);
+ return;
+ }
+
+ /* Get the widgets. */
+
+ data->dialog = _gtk_builder_get_widget (data->builder, "delete_dialog");
+ data->d_all_files_radio = _gtk_builder_get_widget (data->builder, "d_all_files_radio");
+ data->d_selected_files_radio = _gtk_builder_get_widget (data->builder, "d_selected_files_radio");
+ data->d_files_radio = _gtk_builder_get_widget (data->builder, "d_files_radio");
+ data->d_files_entry = _gtk_builder_get_widget (data->builder, "d_files_entry");
+
+ ok_button = _gtk_builder_get_widget (data->builder, "d_ok_button");
+ cancel_button = _gtk_builder_get_widget (data->builder, "d_cancel_button");
+
+ /* Set widgets data. */
+
+ if (data->selected_files != NULL)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->d_selected_files_radio), TRUE);
+ else {
+ gtk_widget_set_sensitive (data->d_selected_files_radio, FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->d_all_files_radio), TRUE);
+ }
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (destroy_cb),
+ data);
+ g_signal_connect_swapped (G_OBJECT (cancel_button),
+ "clicked",
+ G_CALLBACK (gtk_widget_destroy),
+ G_OBJECT (data->dialog));
+ g_signal_connect (G_OBJECT (ok_button),
+ "clicked",
+ G_CALLBACK (ok_clicked_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->d_files_entry),
+ "changed",
+ G_CALLBACK (entry_changed_cb),
+ data);
+
+ /* Run dialog. */
+
+ gtk_window_set_transient_for (GTK_WINDOW (data->dialog),
+ GTK_WINDOW (window));
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
+
+ gtk_widget_show (data->dialog);
+}
+
+
+void
+dlg_delete (GtkWidget *widget,
+ gpointer callback_data)
+{
+ FrWindow *window = callback_data;
+ dlg_delete__common (window,
+ fr_window_get_file_list_selection (window, TRUE, NULL));
+}
+
+
+void
+dlg_delete_from_sidebar (GtkWidget *widget,
+ gpointer callback_data)
+{
+ FrWindow *window = callback_data;
+ dlg_delete__common (window,
+ fr_window_get_folder_tree_selection (window, TRUE, NULL));
+}
diff --git a/src/dlg-delete.h b/src/dlg-delete.h
new file mode 100644
index 0000000..488fad0
--- /dev/null
+++ b/src/dlg-delete.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_DELETE_H
+#define DLG_DELETE_H
+
+
+void dlg_delete (GtkWidget *widget, gpointer data);
+void dlg_delete_from_sidebar (GtkWidget *widget, gpointer data);
+
+
+#endif /* DLG_DELETE_H */
diff --git a/src/dlg-extract.c b/src/dlg-extract.c
new file mode 100644
index 0000000..f4dd75b
--- /dev/null
+++ b/src/dlg-extract.c
@@ -0,0 +1,516 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2004, 2005 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <unistd.h>
+#include "file-utils.h"
+#include "fr-stock.h"
+#include "main.h"
+#include "gtk-utils.h"
+#include "fr-window.h"
+#include "typedefs.h"
+#include "mateconf-utils.h"
+
+
+typedef struct {
+ FrWindow *window;
+ GList *selected_files;
+ char *base_dir_for_selection;
+
+ GtkWidget *dialog;
+
+ GtkWidget *e_main_vbox;
+ GtkWidget *e_all_radiobutton;
+ GtkWidget *e_selected_radiobutton;
+ GtkWidget *e_files_radiobutton;
+ GtkWidget *e_files_entry;
+ GtkWidget *e_recreate_dir_checkbutton;
+ GtkWidget *e_overwrite_checkbutton;
+ GtkWidget *e_not_newer_checkbutton;
+
+ gboolean extract_clicked;
+} DialogData;
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ if (! data->extract_clicked) {
+ fr_window_pop_message (data->window);
+ fr_window_stop_batch (data->window);
+ }
+ path_list_free (data->selected_files);
+ g_free (data->base_dir_for_selection);
+ g_free (data);
+}
+
+
+static int
+extract_cb (GtkWidget *w,
+ DialogData *data)
+{
+ FrWindow *window = data->window;
+ gboolean do_not_extract = FALSE;
+ char *extract_to_dir;
+ gboolean overwrite;
+ gboolean skip_newer;
+ gboolean selected_files;
+ gboolean pattern_files;
+ gboolean junk_paths;
+ GList *file_list;
+ char *base_dir = NULL;
+ GError *error = NULL;
+
+ data->extract_clicked = TRUE;
+
+ /* collect extraction options. */
+
+ extract_to_dir = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog));
+
+ /* check directory existence. */
+
+ if (! uri_is_dir (extract_to_dir)) {
+ if (! ForceDirectoryCreation) {
+ GtkWidget *d;
+ int r;
+ char *folder_name;
+ char *msg;
+
+ folder_name = g_filename_display_name (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 (data->dialog),
+ 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 (extract_to_dir, 0755, &error)) {
+ GtkWidget *d;
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Extraction not performed"),
+ _("Could not create the destination folder: %s."),
+ error->message);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_error_free (error);
+
+ return FALSE;
+ }
+ }
+
+ if (do_not_extract) {
+ GtkWidget *d;
+
+ d = _gtk_message_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ 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);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ if (fr_window_is_batch_mode (data->window))
+ gtk_widget_destroy (data->dialog);
+
+ return FALSE;
+ }
+
+ /* check extraction directory permissions. */
+
+ if (uri_is_dir (extract_to_dir)
+ && ! check_permissions (extract_to_dir, R_OK | W_OK))
+ {
+ GtkWidget *d;
+ char *utf8_path;
+
+ utf8_path = g_filename_display_name (extract_to_dir);
+
+ d = _gtk_error_dialog_new (GTK_WINDOW (window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ _("Extraction not performed"),
+ _("You don't have the right permissions to extract archives in the folder \"%s\""),
+ utf8_path);
+ gtk_dialog_run (GTK_DIALOG (d));
+ gtk_widget_destroy (GTK_WIDGET (d));
+
+ g_free (utf8_path);
+ g_free (extract_to_dir);
+
+ return FALSE;
+ }
+
+ fr_window_set_extract_default_dir (window, extract_to_dir, TRUE);
+
+ overwrite = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_overwrite_checkbutton));
+ skip_newer = ! gtk_toggle_button_get_inconsistent (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton)) && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton));
+ junk_paths = ! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_recreate_dir_checkbutton));
+
+ eel_mateconf_set_boolean (PREF_EXTRACT_OVERWRITE, overwrite);
+ if (!gtk_toggle_button_get_inconsistent (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton)))
+ eel_mateconf_set_boolean (PREF_EXTRACT_SKIP_NEWER, skip_newer);
+ eel_mateconf_set_boolean (PREF_EXTRACT_RECREATE_FOLDERS, !junk_paths);
+
+ selected_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_selected_radiobutton));
+ pattern_files = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_files_radiobutton));
+
+ /* create the file list. */
+
+ file_list = NULL;
+
+ if (selected_files) {
+ file_list = data->selected_files;
+ data->selected_files = NULL; /* do not the list when destroying the dialog. */
+ }
+ else if (pattern_files) {
+ const char *pattern;
+
+ pattern = gtk_entry_get_text (GTK_ENTRY (data->e_files_entry));
+ file_list = fr_window_get_file_list_pattern (window, pattern);
+ if (file_list == NULL) {
+ gtk_widget_destroy (data->dialog);
+ g_free (extract_to_dir);
+ return FALSE;
+ }
+ }
+
+ if (selected_files) {
+ base_dir = data->base_dir_for_selection;
+ data->base_dir_for_selection = NULL;
+ }
+ else
+ base_dir = NULL;
+
+ /* close the dialog. */
+
+ gtk_widget_destroy (data->dialog);
+
+ /* extract ! */
+
+ fr_window_archive_extract (window,
+ file_list,
+ extract_to_dir,
+ base_dir,
+ skip_newer,
+ overwrite,
+ junk_paths,
+ TRUE);
+
+ path_list_free (file_list);
+ g_free (extract_to_dir);
+ g_free (base_dir);
+
+ return TRUE;
+}
+
+
+static int
+file_sel_response_cb (GtkWidget *widget,
+ int response,
+ DialogData *data)
+{
+ if ((response == GTK_RESPONSE_CANCEL) || (response == GTK_RESPONSE_DELETE_EVENT)) {
+ gtk_widget_destroy (data->dialog);
+ return TRUE;
+ }
+
+ if (response == GTK_RESPONSE_HELP) {
+ show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-extract-options");
+ return TRUE;
+ }
+
+ if (response == GTK_RESPONSE_OK)
+ return extract_cb (widget, data);
+
+ return FALSE;
+}
+
+
+static void
+files_entry_changed_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ if (! gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_files_radiobutton)))
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_files_radiobutton), TRUE);
+}
+
+
+static void
+overwrite_toggled_cb (GtkToggleButton *button,
+ DialogData *data)
+{
+ gboolean active = gtk_toggle_button_get_active (button);
+ gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton), !active);
+ gtk_widget_set_sensitive (data->e_not_newer_checkbutton, active);
+}
+
+
+static void
+set_bold_label (GtkWidget *label,
+ const char *label_txt)
+{
+ char *bold_label;
+
+ bold_label = g_strconcat ("<b>", label_txt, "</b>", NULL);
+ gtk_label_set_markup (GTK_LABEL (label), bold_label);
+ g_free (bold_label);
+}
+
+
+static GtkWidget *
+create_extra_widget (DialogData *data)
+{
+ GtkWidget *vbox1;
+ GtkWidget *hbox28;
+ GtkWidget *vbox19;
+ GtkWidget *e_files_label;
+ GtkWidget *hbox29;
+ GtkWidget *label47;
+ GtkWidget *table1;
+ GSList *e_files_radiobutton_group = NULL;
+ GtkWidget *vbox20;
+ GtkWidget *e_actions_label;
+ GtkWidget *hbox30;
+ GtkWidget *label48;
+ GtkWidget *vbox15;
+
+ vbox1 = gtk_vbox_new (FALSE, 6);
+ gtk_container_set_border_width (GTK_CONTAINER (vbox1), 0);
+
+ hbox28 = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (vbox1), hbox28, TRUE, TRUE, 0);
+
+ vbox19 = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox28), vbox19, TRUE, TRUE, 0);
+
+ e_files_label = gtk_label_new ("");
+ set_bold_label (e_files_label, _("Extract"));
+ gtk_box_pack_start (GTK_BOX (vbox19), e_files_label, FALSE, FALSE, 0);
+ gtk_label_set_justify (GTK_LABEL (e_files_label), GTK_JUSTIFY_LEFT);
+ gtk_misc_set_alignment (GTK_MISC (e_files_label), 0, 0.5);
+
+ hbox29 = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox19), hbox29, TRUE, TRUE, 0);
+
+ label47 = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox29), label47, FALSE, FALSE, 0);
+ gtk_label_set_justify (GTK_LABEL (label47), GTK_JUSTIFY_LEFT);
+
+ table1 = gtk_table_new (3, 2, FALSE);
+ gtk_box_pack_start (GTK_BOX (hbox29), table1, TRUE, TRUE, 0);
+ gtk_table_set_row_spacings (GTK_TABLE (table1), 6);
+ gtk_table_set_col_spacings (GTK_TABLE (table1), 6);
+
+ data->e_files_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, _("_Files:"));
+ gtk_table_attach (GTK_TABLE (table1), data->e_files_radiobutton, 0, 1, 2, 3,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (data->e_files_radiobutton), e_files_radiobutton_group);
+ e_files_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (data->e_files_radiobutton));
+
+ data->e_files_entry = gtk_entry_new ();
+ gtk_table_attach (GTK_TABLE (table1), data->e_files_entry, 1, 2, 2, 3,
+ (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_widget_set_tooltip_text (data->e_files_entry, _("example: *.txt; *.doc"));
+ gtk_entry_set_activates_default (GTK_ENTRY (data->e_files_entry), TRUE);
+
+ data->e_all_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, _("_All files"));
+ gtk_table_attach (GTK_TABLE (table1), data->e_all_radiobutton, 0, 2, 0, 1,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (data->e_all_radiobutton), e_files_radiobutton_group);
+ e_files_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (data->e_all_radiobutton));
+
+ data->e_selected_radiobutton = gtk_radio_button_new_with_mnemonic (NULL, _("_Selected files"));
+ gtk_table_attach (GTK_TABLE (table1), data->e_selected_radiobutton, 0, 2, 1, 2,
+ (GtkAttachOptions) (GTK_FILL),
+ (GtkAttachOptions) (0), 0, 0);
+ gtk_radio_button_set_group (GTK_RADIO_BUTTON (data->e_selected_radiobutton), e_files_radiobutton_group);
+ e_files_radiobutton_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (data->e_selected_radiobutton));
+
+ vbox20 = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox28), vbox20, TRUE, TRUE, 0);
+
+ e_actions_label = gtk_label_new ("");
+ set_bold_label (e_actions_label, _("Actions"));
+ gtk_box_pack_start (GTK_BOX (vbox20), e_actions_label, FALSE, FALSE, 0);
+ gtk_label_set_use_markup (GTK_LABEL (e_actions_label), TRUE);
+ gtk_label_set_justify (GTK_LABEL (e_actions_label), GTK_JUSTIFY_LEFT);
+ gtk_misc_set_alignment (GTK_MISC (e_actions_label), 0, 0.5);
+
+ hbox30 = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox20), hbox30, TRUE, TRUE, 0);
+
+ label48 = gtk_label_new (" ");
+ gtk_box_pack_start (GTK_BOX (hbox30), label48, FALSE, FALSE, 0);
+ gtk_label_set_justify (GTK_LABEL (label48), GTK_JUSTIFY_LEFT);
+
+ vbox15 = gtk_vbox_new (FALSE, 6);
+ gtk_box_pack_start (GTK_BOX (hbox30), vbox15, TRUE, TRUE, 0);
+
+ data->e_recreate_dir_checkbutton = gtk_check_button_new_with_mnemonic (_("Re-crea_te folders"));
+ gtk_box_pack_start (GTK_BOX (vbox15), data->e_recreate_dir_checkbutton, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_recreate_dir_checkbutton), TRUE);
+
+ data->e_overwrite_checkbutton = gtk_check_button_new_with_mnemonic (_("Over_write existing files"));
+ gtk_box_pack_start (GTK_BOX (vbox15), data->e_overwrite_checkbutton, FALSE, FALSE, 0);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_overwrite_checkbutton), TRUE);
+
+ data->e_not_newer_checkbutton = gtk_check_button_new_with_mnemonic (_("Do not e_xtract older files"));
+ gtk_box_pack_start (GTK_BOX (vbox15), data->e_not_newer_checkbutton, FALSE, FALSE, 0);
+
+ gtk_widget_show_all (vbox1);
+
+ return vbox1;
+}
+
+
+static void
+dlg_extract__common (FrWindow *window,
+ GList *selected_files,
+ char *base_dir_for_selection)
+{
+ DialogData *data;
+ GtkWidget *file_sel;
+
+ data = g_new0 (DialogData, 1);
+
+ data->window = window;
+ data->selected_files = selected_files;
+ data->base_dir_for_selection = base_dir_for_selection;
+ data->extract_clicked = FALSE;
+
+ data->dialog = file_sel =
+ gtk_file_chooser_dialog_new (_("Extract"),
+ GTK_WINDOW (data->window),
+ GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ FR_STOCK_EXTRACT, GTK_RESPONSE_OK,
+ GTK_STOCK_HELP, GTK_RESPONSE_HELP,
+ NULL);
+
+ gtk_window_set_default_size (GTK_WINDOW (file_sel), 530, 510);
+
+ gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (file_sel), FALSE);
+ gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (file_sel), FALSE);
+ gtk_dialog_set_default_response (GTK_DIALOG (file_sel), GTK_RESPONSE_OK);
+
+ gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_sel), create_extra_widget (data));
+
+ /* Set widgets data. */
+
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (file_sel), fr_window_get_extract_default_dir (window));
+
+ if (data->selected_files != NULL)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_selected_radiobutton), TRUE);
+ else {
+ gtk_widget_set_sensitive (data->e_selected_radiobutton, FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_all_radiobutton), TRUE);
+ }
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_overwrite_checkbutton), eel_mateconf_get_boolean (PREF_EXTRACT_OVERWRITE, FALSE));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton), eel_mateconf_get_boolean (PREF_EXTRACT_SKIP_NEWER, FALSE));
+ if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->e_overwrite_checkbutton))) {
+ gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->e_not_newer_checkbutton), TRUE);
+ gtk_widget_set_sensitive (data->e_not_newer_checkbutton, FALSE);
+ }
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->e_recreate_dir_checkbutton), eel_mateconf_get_boolean (PREF_EXTRACT_RECREATE_FOLDERS, TRUE));
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (destroy_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (file_sel),
+ "response",
+ G_CALLBACK (file_sel_response_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (data->e_overwrite_checkbutton),
+ "toggled",
+ G_CALLBACK (overwrite_toggled_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->e_files_entry),
+ "changed",
+ G_CALLBACK (files_entry_changed_cb),
+ data);
+
+ /* Run dialog. */
+
+ gtk_window_set_modal (GTK_WINDOW (file_sel),TRUE);
+ gtk_widget_show (file_sel);
+}
+
+
+void
+dlg_extract (GtkWidget *widget,
+ gpointer callback_data)
+{
+ FrWindow *window = callback_data;
+ GList *files;
+ char *base_dir;
+
+ files = fr_window_get_selection (window, FALSE, &base_dir);
+ dlg_extract__common (window, files, base_dir);
+}
+
+
+void
+dlg_extract_folder_from_sidebar (GtkWidget *widget,
+ gpointer callback_data)
+{
+ FrWindow *window = callback_data;
+ GList *files;
+ char *base_dir;
+
+ files = fr_window_get_selection (window, TRUE, &base_dir);
+ dlg_extract__common (window, files, base_dir);
+}
diff --git a/src/dlg-extract.h b/src/dlg-extract.h
new file mode 100644
index 0000000..fcf88e9
--- /dev/null
+++ b/src/dlg-extract.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_EXTRACT_H
+#define DLG_EXTRACT_H
+
+#include "fr-archive.h"
+#include "fr-window.h"
+
+void dlg_extract (GtkWidget *widget, gpointer data);
+void dlg_extract_folder_from_sidebar (GtkWidget *widget, gpointer data);
+
+#endif /* DLG_EXTRACT_H */
diff --git a/src/dlg-new.c b/src/dlg-new.c
new file mode 100644
index 0000000..2c4caf7
--- /dev/null
+++ b/src/dlg-new.c
@@ -0,0 +1,526 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <math.h>
+#include <unistd.h>
+#include <gio/gio.h>
+#include "dlg-new.h"
+#include "file-utils.h"
+#include "fr-stock.h"
+#include "mateconf-utils.h"
+#include "gtk-utils.h"
+#include "main.h"
+#include "preferences.h"
+
+
+#define GET_WIDGET(x) (_gtk_builder_get_widget (data->builder, (x)))
+#define DEFAULT_EXTENSION ".tar.gz"
+#define BAD_CHARS "/\\*"
+#define MEGABYTE (1024 * 1024)
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget *widget,
+ DlgNewData *data)
+{
+ g_object_unref (data->builder);
+ g_free (data);
+}
+
+
+static void
+update_sensitivity (DlgNewData *data)
+{
+ gtk_toggle_button_set_inconsistent (GTK_TOGGLE_BUTTON (data->n_encrypt_header_checkbutton), ! data->can_encrypt_header);
+ gtk_widget_set_sensitive (data->n_encrypt_header_checkbutton, data->can_encrypt_header);
+ gtk_widget_set_sensitive (data->n_volume_spinbutton, ! data->can_create_volumes || gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->n_volume_checkbutton)));
+}
+
+
+static void
+update_sensitivity_for_ext (DlgNewData *data,
+ const char *ext)
+{
+ const char *mime_type;
+ int i;
+
+ data->can_encrypt = FALSE;
+ data->can_encrypt_header = FALSE;
+ data->can_create_volumes = FALSE;
+
+ mime_type = get_mime_type_from_extension (ext);
+
+ if (mime_type == NULL) {
+ gtk_widget_set_sensitive (data->n_password_entry, FALSE);
+ gtk_widget_set_sensitive (data->n_password_label, FALSE);
+ gtk_widget_set_sensitive (data->n_encrypt_header_checkbutton, FALSE);
+ gtk_widget_set_sensitive (data->n_volume_box, FALSE);
+ return;
+ }
+
+ for (i = 0; mime_type_desc[i].mime_type != NULL; i++) {
+ if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) {
+ data->can_encrypt = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT;
+ gtk_widget_set_sensitive (data->n_password_entry, data->can_encrypt);
+ gtk_widget_set_sensitive (data->n_password_label, data->can_encrypt);
+
+ data->can_encrypt_header = mime_type_desc[i].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER;
+ gtk_widget_set_sensitive (data->n_encrypt_header_checkbutton, data->can_encrypt_header);
+
+ data->can_create_volumes = mime_type_desc[i].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES;
+ gtk_widget_set_sensitive (data->n_volume_box, data->can_create_volumes);
+
+ break;
+ }
+ }
+
+ update_sensitivity (data);
+}
+
+
+static int
+get_archive_type (DlgNewData *data)
+{
+ const char *uri;
+ const char *ext;
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog));
+ if (uri == NULL)
+ return -1;
+
+ ext = get_archive_filename_extension (uri);
+ if (ext == NULL) {
+ int idx;
+
+ idx = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri);
+ /*idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->n_archive_type_combo_box)) - 1;*/
+ if (idx >= 0)
+ return data->supported_types[idx];
+
+ ext = DEFAULT_EXTENSION;
+ }
+
+ return get_mime_type_index (get_mime_type_from_extension (ext));
+}
+
+
+/* FIXME
+static void
+archive_type_combo_box_changed_cb (GtkComboBox *combo_box,
+ DlgNewData *data)
+{
+ const char *uri;
+ const char *ext;
+ int idx;
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog));
+
+ ext = get_archive_filename_extension (uri);
+ idx = gtk_combo_box_get_active (GTK_COMBO_BOX (data->n_archive_type_combo_box)) - 1;
+ if ((ext == NULL) && (idx >= 0))
+ ext = mime_type_desc[data->supported_types[idx]].default_ext;
+
+ update_sensitivity_for_ext (data, ext);
+
+ if ((idx >= 0) && (uri != NULL)) {
+ const char *new_ext;
+ const char *basename;
+ char *basename_noext;
+ char *new_basename;
+ char *new_basename_uft8;
+
+ new_ext = mime_type_desc[data->supported_types[idx]].default_ext;
+ basename = file_name_from_path (uri);
+ if (g_str_has_suffix (basename, ext))
+ basename_noext = g_strndup (basename, strlen (basename) - strlen (ext));
+ else
+ basename_noext = g_strdup (basename);
+ new_basename = g_strconcat (basename_noext, new_ext, NULL);
+ new_basename_uft8 = g_uri_unescape_string (new_basename, NULL);
+
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), new_basename_uft8);
+ update_sensitivity_for_ext (data, new_ext);
+
+ g_free (new_basename_uft8);
+ g_free (new_basename);
+ g_free (basename_noext);
+ }
+}
+*/
+
+
+static void
+password_entry_changed_cb (GtkEditable *editable,
+ gpointer user_data)
+{
+ update_sensitivity ((DlgNewData *) user_data);
+}
+
+
+static void
+volume_toggled_cb (GtkToggleButton *toggle_button,
+ gpointer user_data)
+{
+ update_sensitivity ((DlgNewData *) user_data);
+}
+
+
+static void
+format_chooser_selection_changed_cb (EggFileFormatChooser *format_chooser,
+ DlgNewData *data)
+{
+ const char *uri;
+ const char *ext;
+ int n_format;
+
+ uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (data->dialog));
+ if (uri == NULL)
+ return;
+
+ ext = get_archive_filename_extension (uri);
+ n_format = egg_file_format_chooser_get_format (EGG_FILE_FORMAT_CHOOSER (data->format_chooser), uri);
+ if (ext == NULL)
+ ext = mime_type_desc[data->supported_types[n_format - 1]].default_ext;
+
+ update_sensitivity_for_ext (data, ext);
+
+ if (uri != NULL) {
+ const char *new_ext;
+ const char *basename;
+ char *basename_noext;
+ char *new_basename;
+ char *new_basename_uft8;
+
+ new_ext = mime_type_desc[data->supported_types[n_format - 1]].default_ext;
+ basename = file_name_from_path (uri);
+ if (g_str_has_suffix (basename, ext))
+ basename_noext = g_strndup (basename, strlen (basename) - strlen (ext));
+ else
+ basename_noext = g_strdup (basename);
+ new_basename = g_strconcat (basename_noext, new_ext, NULL);
+ new_basename_uft8 = g_uri_unescape_string (new_basename, NULL);
+
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), new_basename_uft8);
+ update_sensitivity_for_ext (data, new_ext);
+
+ g_free (new_basename_uft8);
+ g_free (new_basename);
+ g_free (basename_noext);
+ }
+}
+
+
+static char *
+get_icon_name_for_type (const char *mime_type)
+{
+ char *name = NULL;
+
+ if (mime_type != NULL) {
+ char *s;
+
+ name = g_strconcat ("mate-mime-", mime_type, NULL);
+ for (s = name; *s; ++s)
+ if (! g_ascii_isalpha (*s))
+ *s = '-';
+ }
+
+ if ((name == NULL) || ! gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name)) {
+ g_free (name);
+ name = g_strdup ("package-x-generic");
+ }
+
+ return name;
+}
+
+
+static void
+options_expander_unmap_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ egg_file_format_chooser_emit_size_changed ((EggFileFormatChooser *) user_data);
+}
+
+
+static DlgNewData *
+dlg_new_archive (FrWindow *window,
+ int *supported_types,
+ const char *default_name)
+{
+ DlgNewData *data;
+ GtkWidget *n_new_button;
+ GtkFileFilter *filter;
+ /*char *default_ext;*/
+ int i;
+
+ data = g_new0 (DlgNewData, 1);
+
+ data->builder = _gtk_builder_new_from_file ("new.ui");
+ if (data->builder == NULL) {
+ g_free (data);
+ return NULL;
+ }
+
+ data->window = window;
+ data->supported_types = supported_types;
+ sort_mime_types_by_description (data->supported_types);
+
+ /* Get the widgets. */
+
+ data->dialog = _gtk_builder_get_widget (data->builder, "filechooserdialog");
+
+ data->n_password_entry = _gtk_builder_get_widget (data->builder, "n_password_entry");
+ data->n_password_label = _gtk_builder_get_widget (data->builder, "n_password_label");
+ data->n_other_options_expander = _gtk_builder_get_widget (data->builder, "n_other_options_expander");
+ data->n_encrypt_header_checkbutton = _gtk_builder_get_widget (data->builder, "n_encrypt_header_checkbutton");
+
+ data->n_volume_checkbutton = _gtk_builder_get_widget (data->builder, "n_volume_checkbutton");
+ data->n_volume_spinbutton = _gtk_builder_get_widget (data->builder, "n_volume_spinbutton");
+ data->n_volume_box = _gtk_builder_get_widget (data->builder, "n_volume_box");
+
+ n_new_button = _gtk_builder_get_widget (data->builder, "n_new_button");
+
+ /* Set widgets data. */
+
+ gtk_dialog_set_default_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_OK);
+ gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER (data->dialog), fr_window_get_open_default_dir (window));
+
+ if (default_name != NULL) {
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), default_name);
+ /*char *ext, *name_ext;
+
+ ext = eel_mateconf_get_string (PREF_BATCH_ADD_DEFAULT_EXTENSION, ".tgz");
+ name_ext = g_strconcat (default_name, ext, NULL);
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (data->dialog), name_ext);
+ g_free (name_ext);
+ g_free (ext);*/
+ }
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("All archives"));
+ for (i = 0; data->supported_types[i] != -1; i++)
+ gtk_file_filter_add_mime_type (filter, mime_type_desc[data->supported_types[i]].mime_type);
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (data->dialog), filter);
+ gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (data->dialog), filter);
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, _("All files"));
+ gtk_file_filter_add_pattern (filter, "*");
+ gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (data->dialog), filter);
+
+ /**/
+
+ gtk_button_set_use_stock (GTK_BUTTON (n_new_button), TRUE);
+ gtk_button_set_label (GTK_BUTTON (n_new_button), FR_STOCK_CREATE_ARCHIVE);
+ gtk_expander_set_expanded (GTK_EXPANDER (data->n_other_options_expander), FALSE);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->n_encrypt_header_checkbutton), eel_mateconf_get_boolean (PREF_ENCRYPT_HEADER, FALSE));
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (data->n_volume_spinbutton), (double) eel_mateconf_get_integer (PREF_BATCH_VOLUME_SIZE, 0) / MEGABYTE);
+
+ /* format chooser */
+
+ data->format_chooser = (EggFileFormatChooser *) egg_file_format_chooser_new ();
+ for (i = 0; data->supported_types[i] != -1; i++) {
+ int idx = data->supported_types[i];
+ char *exts[4];
+ int e;
+ int n_exts;
+ char *icon_name;
+
+ n_exts = 0;
+ for (e = 0; (n_exts < 4) && file_ext_type[e].ext != NULL; e++) {
+ if (strcmp (file_ext_type[e].ext, mime_type_desc[idx].default_ext) == 0)
+ continue;
+ if (strcmp (file_ext_type[e].mime_type, mime_type_desc[idx].mime_type) == 0)
+ exts[n_exts++] = file_ext_type[e].ext;
+ }
+ while (n_exts < 4)
+ exts[n_exts++] = NULL;
+
+ /* g_print ("%s => %s, %s, %s, %s\n", mime_type_desc[idx].mime_type, exts[0], exts[1], exts[2], exts[3]); */
+
+ icon_name = get_icon_name_for_type (mime_type_desc[idx].mime_type);
+ egg_file_format_chooser_add_format (data->format_chooser,
+ 0,
+ _(mime_type_desc[idx].name),
+ icon_name,
+ mime_type_desc[idx].default_ext,
+ exts[0],
+ exts[1],
+ exts[2],
+ exts[3],
+ NULL);
+
+ g_free (icon_name);
+ }
+ egg_file_format_chooser_set_format (data->format_chooser, 0);
+ gtk_widget_show (GTK_WIDGET (data->format_chooser));
+ gtk_box_pack_start (GTK_BOX (GET_WIDGET ("format_chooser_box")), GTK_WIDGET (data->format_chooser), TRUE, TRUE, 0);
+
+ /* Set the signals handlers. */
+
+ /*g_signal_connect (G_OBJECT (data->dialog),
+ "response",
+ G_CALLBACK (new_file_response_cb),
+ data);*/
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (destroy_cb),
+ data);
+
+ /*
+ g_signal_connect_swapped (G_OBJECT (cancel_button),
+ "clicked",
+ G_CALLBACK (gtk_widget_destroy),
+ G_OBJECT (data->dialog));
+ g_signal_connect (G_OBJECT (add_button),
+ "clicked",
+ G_CALLBACK (add_clicked_cb),
+ data);*/
+
+ /* FIXME g_signal_connect (G_OBJECT (data->n_archive_type_combo_box),
+ "changed",
+ G_CALLBACK (archive_type_combo_box_changed_cb),
+ data); */
+ g_signal_connect (G_OBJECT (data->n_password_entry),
+ "changed",
+ G_CALLBACK (password_entry_changed_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->n_volume_checkbutton),
+ "toggled",
+ G_CALLBACK (volume_toggled_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->format_chooser),
+ "selection-changed",
+ G_CALLBACK (format_chooser_selection_changed_cb),
+ data);
+ g_signal_connect_after (GET_WIDGET ("other_oprtions_alignment"),
+ "unmap",
+ G_CALLBACK (options_expander_unmap_cb),
+ data->format_chooser);
+
+ /* Run dialog. */
+
+/* default_ext = eel_mateconf_get_string (PREF_BATCH_ADD_DEFAULT_EXTENSION, DEFAULT_EXTENSION);
+ update_archive_type_combo_box_from_ext (data, default_ext);
+ g_free (default_ext);*/
+
+ update_sensitivity (data);
+
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
+ gtk_window_set_transient_for (GTK_WINDOW (data->dialog), GTK_WINDOW (data->window));
+ /*gtk_window_present (GTK_WINDOW (data->dialog));*/
+
+ return data;
+}
+
+
+DlgNewData *
+dlg_new (FrWindow *window)
+{
+ DlgNewData *data;
+
+ data = dlg_new_archive (window, create_type, NULL);
+ gtk_window_set_title (GTK_WINDOW (data->dialog), C_("File", "New"));
+
+ return data;
+}
+
+
+DlgNewData *
+dlg_save_as (FrWindow *window,
+ const char *default_name)
+{
+ DlgNewData *data;
+
+ data = dlg_new_archive (window, save_type, default_name);
+ gtk_window_set_title (GTK_WINDOW (data->dialog), C_("File", "Save"));
+
+ return data;
+}
+
+
+const char *
+dlg_new_data_get_password (DlgNewData *data)
+{
+ const char *password = NULL;
+ int idx;
+
+ idx = get_archive_type (data);
+ if (idx < 0)
+ return NULL;
+
+ if (mime_type_desc[idx].capabilities & FR_COMMAND_CAN_ENCRYPT)
+ password = (char*) gtk_entry_get_text (GTK_ENTRY (data->n_password_entry));
+
+ return password;
+}
+
+
+gboolean
+dlg_new_data_get_encrypt_header (DlgNewData *data)
+{
+ gboolean encrypt_header = FALSE;
+ int idx;
+
+ idx = get_archive_type (data);
+ if (idx < 0)
+ return FALSE;
+
+ if (mime_type_desc[idx].capabilities & FR_COMMAND_CAN_ENCRYPT) {
+ const char *password = gtk_entry_get_text (GTK_ENTRY (data->n_password_entry));
+ if (password != NULL) {
+ if (strcmp (password, "") != 0) {
+ if (mime_type_desc[idx].capabilities & FR_COMMAND_CAN_ENCRYPT_HEADER)
+ encrypt_header = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->n_encrypt_header_checkbutton));
+ }
+ }
+ }
+
+ return encrypt_header;
+}
+
+
+int
+dlg_new_data_get_volume_size (DlgNewData *data)
+{
+ guint volume_size = 0;
+ int idx;
+
+ idx = get_archive_type (data);
+ if (idx < 0)
+ return 0;
+
+ if ((mime_type_desc[idx].capabilities & FR_COMMAND_CAN_CREATE_VOLUMES)
+ && gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->n_volume_checkbutton)))
+ {
+ double value;
+
+ value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (data->n_volume_spinbutton));
+ volume_size = floor (value * MEGABYTE);
+
+ }
+
+ return volume_size;
+}
diff --git a/src/dlg-new.h b/src/dlg-new.h
new file mode 100644
index 0000000..58bc86d
--- /dev/null
+++ b/src/dlg-new.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2008 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_NEW_H
+#define DLG_NEW_H
+
+#include <gtk/gtk.h>
+#include "eggfileformatchooser.h"
+#include "fr-window.h"
+
+
+typedef struct {
+ FrWindow *window;
+ int *supported_types;
+ gboolean can_encrypt;
+ gboolean can_encrypt_header;
+ gboolean can_create_volumes;
+ GtkBuilder *builder;
+
+ GtkWidget *dialog;
+ /*GtkWidget *n_archive_type_combo_box;*/
+ GtkWidget *n_other_options_expander;
+ GtkWidget *n_password_entry;
+ GtkWidget *n_password_label;
+ GtkWidget *n_encrypt_header_checkbutton;
+ GtkWidget *n_volume_checkbutton;
+ GtkWidget *n_volume_spinbutton;
+ GtkWidget *n_volume_box;
+ EggFileFormatChooser *format_chooser;
+} DlgNewData;
+
+
+DlgNewData * dlg_new (FrWindow *window);
+DlgNewData * dlg_save_as (FrWindow *window,
+ const char *default_name);
+const char * dlg_new_data_get_password (DlgNewData *data);
+gboolean dlg_new_data_get_encrypt_header (DlgNewData *data);
+int dlg_new_data_get_volume_size (DlgNewData *data);
+
+#endif /* DLG_NEW_H */
diff --git a/src/dlg-open-with.c b/src/dlg-open-with.c
new file mode 100644
index 0000000..014e59c
--- /dev/null
+++ b/src/dlg-open-with.c
@@ -0,0 +1,517 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2005 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include <gtk/gtk.h>
+#include "file-utils.h"
+#include "mateconf-utils.h"
+#include "glib-utils.h"
+#include "gtk-utils.h"
+#include "main.h"
+#include "fr-window.h"
+
+
+#define TEMP_DOCS "temp_docs"
+
+enum { ICON_COLUMN, TEXT_COLUMN, DATA_COLUMN, N_COLUMNS };
+
+typedef struct {
+ FrWindow *window;
+ GtkBuilder *builder;
+
+ GtkWidget *dialog;
+ GtkWidget *o_app_tree_view;
+ GtkWidget *o_recent_tree_view;
+ GtkWidget *o_app_entry;
+ GtkWidget *o_del_button;
+ GtkWidget *ok_button;
+
+ GList *app_list;
+ GList *file_list;
+
+ GtkTreeModel *app_model;
+ GtkTreeModel *recent_model;
+
+ GtkWidget *last_clicked_list;
+} DialogData;
+
+
+/* called when the main dialog is closed. */
+static void
+open_with__destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ g_object_unref (G_OBJECT (data->builder));
+
+ if (data->app_list != NULL)
+ g_list_free (data->app_list);
+
+ if (data->file_list != NULL)
+ path_list_free (data->file_list);
+
+ g_free (data);
+}
+
+
+static void
+open_cb (GtkWidget *widget,
+ gpointer callback_data)
+{
+ DialogData *data = callback_data;
+ const char *application;
+ gboolean present = FALSE;
+ char *command = NULL;
+ GList *scan;
+ GSList *sscan, *editors;
+
+ application = gtk_entry_get_text (GTK_ENTRY (data->o_app_entry));
+
+ for (scan = data->app_list; scan; scan = scan->next) {
+ GAppInfo *app = scan->data;
+ if (strcmp (g_app_info_get_executable (app), application) == 0) {
+ fr_window_open_files_with_application (data->window, data->file_list, app);
+ gtk_widget_destroy (data->dialog);
+ return;
+ }
+ }
+
+ /* add the command to the editors list if not already present. */
+
+ editors = eel_mateconf_get_string_list (PREF_EDIT_EDITORS);
+ for (sscan = editors; sscan && ! present; sscan = sscan->next) {
+ char *recent_command = sscan->data;
+ if (strcmp (recent_command, application) == 0) {
+ command = g_strdup (recent_command);
+ present = TRUE;
+ }
+ }
+
+ if (! present) {
+ editors = g_slist_prepend (editors, g_strdup (application));
+ command = g_strdup (application);
+ eel_mateconf_set_string_list (PREF_EDIT_EDITORS, editors);
+ }
+
+ g_slist_foreach (editors, (GFunc) g_free, NULL);
+ g_slist_free (editors);
+
+ /* exec the application */
+
+ if (command != NULL) {
+ fr_window_open_files_with_command (data->window, data->file_list, command);
+ g_free (command);
+ }
+
+ gtk_widget_destroy (data->dialog);
+}
+
+
+static void
+app_list_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer p)
+{
+ DialogData *data = p;
+ GtkTreeIter iter;
+ GAppInfo *app;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_app_tree_view));
+ if (selection == NULL)
+ return;
+
+ if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (data->app_model, &iter,
+ DATA_COLUMN, &app,
+ -1);
+ _gtk_entry_set_locale_text (GTK_ENTRY (data->o_app_entry), g_app_info_get_executable (app));
+ data->last_clicked_list = data->o_app_tree_view;
+}
+
+
+static void
+app_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer callback_data)
+{
+ DialogData *data = callback_data;
+ GtkTreeIter iter;
+ GAppInfo *app;
+
+ if (! gtk_tree_model_get_iter (data->app_model, &iter, path))
+ return;
+
+ gtk_tree_model_get (data->app_model, &iter,
+ DATA_COLUMN, &app,
+ -1);
+
+ _gtk_entry_set_locale_text (GTK_ENTRY (data->o_app_entry), g_app_info_get_executable (app));
+
+ open_cb (NULL, data);
+}
+
+
+static void
+recent_list_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer p)
+{
+ DialogData *data = p;
+ GtkTreeIter iter;
+ char *editor;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_recent_tree_view));
+ if (selection == NULL)
+ return;
+
+ if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (data->recent_model, &iter,
+ 0, &editor,
+ -1);
+ _gtk_entry_set_locale_text (GTK_ENTRY (data->o_app_entry), editor);
+ g_free (editor);
+ data->last_clicked_list = data->o_recent_tree_view;
+}
+
+
+static void
+recent_activated_cb (GtkTreeView *tree_view,
+ GtkTreePath *path,
+ GtkTreeViewColumn *column,
+ gpointer callback_data)
+{
+ DialogData *data = callback_data;
+ GtkTreeIter iter;
+ char *editor;
+
+ if (! gtk_tree_model_get_iter (data->recent_model, &iter, path))
+ return;
+
+ gtk_tree_model_get (data->recent_model, &iter,
+ 0, &editor,
+ -1);
+ _gtk_entry_set_locale_text (GTK_ENTRY (data->o_app_entry), editor);
+ g_free (editor);
+
+ open_cb (NULL, data);
+}
+
+
+static void
+app_entry__changed_cb (GtkEditable *editable,
+ gpointer user_data)
+{
+ DialogData *data = user_data;
+ const char *text;
+
+ text = eat_void_chars (gtk_entry_get_text (GTK_ENTRY (data->o_app_entry)));
+ gtk_widget_set_sensitive (data->ok_button, strlen (text) > 0);
+}
+
+
+static void
+delete_recent_cb (GtkWidget *widget,
+ gpointer callback_data)
+{
+ DialogData *data = callback_data;
+ GtkTreeSelection *selection;
+ GtkTreeIter iter;
+
+
+ if (data->last_clicked_list == data->o_recent_tree_view) {
+ char *editor;
+ GSList *editors, *link;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_recent_tree_view));
+ if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (data->recent_model, &iter,
+ 0, &editor,
+ -1);
+ gtk_list_store_remove (GTK_LIST_STORE (data->recent_model), &iter);
+
+ /**/
+
+ editors = eel_mateconf_get_string_list (PREF_EDIT_EDITORS);
+ link = g_slist_find_custom (editors, editor, (GCompareFunc) strcmp);
+ if (link != NULL) {
+ editors = g_slist_remove_link (editors, link);
+ eel_mateconf_set_string_list (PREF_EDIT_EDITORS, editors);
+ g_free (link->data);
+ g_slist_free (link);
+ }
+ g_slist_foreach (editors, (GFunc) g_free, NULL);
+ g_slist_free (editors);
+
+ g_free (editor);
+ }
+ else if (data->last_clicked_list == data->o_app_tree_view) {
+ GAppInfo *app;
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_app_tree_view));
+ if (! gtk_tree_selection_get_selected (selection, NULL, &iter))
+ return;
+
+ gtk_tree_model_get (data->app_model, &iter,
+ DATA_COLUMN, &app,
+ -1);
+ gtk_list_store_remove (GTK_LIST_STORE (data->app_model), &iter);
+
+ if (g_app_info_can_remove_supports_type (app)) {
+ const char *mime_type;
+
+ mime_type = get_file_mime_type_for_path ((char*) data->file_list->data, FALSE);
+ g_app_info_remove_supports_type (app, mime_type, NULL);
+ }
+ }
+}
+
+
+/* create the "open with" dialog. */
+void
+dlg_open_with (FrWindow *window,
+ GList *file_list)
+{
+ DialogData *data;
+ GAppInfo *app;
+ GList *scan, *app_names = NULL;
+ GSList *sscan, *editors;
+ GtkWidget *cancel_button;
+ GtkTreeIter iter;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+ GtkIconTheme *theme;
+ int icon_size;
+
+ if (file_list == NULL)
+ return;
+
+ data = g_new0 (DialogData, 1);
+
+ data->builder = _gtk_builder_new_from_file ("open-with.ui");
+ if (data->builder == NULL) {
+ g_free (data);
+ return;
+ }
+
+ data->file_list = path_list_dup (file_list);
+ data->window = window;
+
+ /* Get the widgets. */
+
+ data->dialog = _gtk_builder_get_widget (data->builder, "open_with_dialog");
+ data->o_app_tree_view = _gtk_builder_get_widget (data->builder, "o_app_list_tree_view");
+ data->o_recent_tree_view = _gtk_builder_get_widget (data->builder, "o_recent_tree_view");
+ data->o_app_entry = _gtk_builder_get_widget (data->builder, "o_app_entry");
+ data->o_del_button = _gtk_builder_get_widget (data->builder, "o_del_button");
+ data->ok_button = _gtk_builder_get_widget (data->builder, "o_ok_button");
+ cancel_button = _gtk_builder_get_widget (data->builder, "o_cancel_button");
+
+ gtk_widget_set_sensitive (data->ok_button, FALSE);
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (open_with__destroy_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (data->o_app_entry),
+ "changed",
+ G_CALLBACK (app_entry__changed_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_app_tree_view))),
+ "changed",
+ G_CALLBACK (app_list_selection_changed_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->o_app_tree_view),
+ "row_activated",
+ G_CALLBACK (app_activated_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (data->o_recent_tree_view))),
+ "changed",
+ G_CALLBACK (recent_list_selection_changed_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->o_recent_tree_view),
+ "row_activated",
+ G_CALLBACK (recent_activated_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (data->ok_button),
+ "clicked",
+ G_CALLBACK (open_cb),
+ data);
+ g_signal_connect_swapped (G_OBJECT (cancel_button),
+ "clicked",
+ G_CALLBACK (gtk_widget_destroy),
+ G_OBJECT (data->dialog));
+ g_signal_connect (G_OBJECT (data->o_del_button),
+ "clicked",
+ G_CALLBACK (delete_recent_cb),
+ data);
+
+ /* Set data. */
+
+ /* * registered applications list. */
+
+ data->app_list = NULL;
+ for (scan = data->file_list; scan; scan = scan->next) {
+ const char *mime_type;
+ const char *name = scan->data;
+
+ mime_type = get_file_mime_type_for_path (name, FALSE);
+ if ((mime_type != NULL) && ! g_content_type_is_unknown (mime_type))
+ data->app_list = g_list_concat (data->app_list, g_app_info_get_all_for_type (mime_type));
+ }
+
+ data->app_model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS,
+ GDK_TYPE_PIXBUF,
+ G_TYPE_STRING,
+ G_TYPE_POINTER));
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->app_model),
+ TEXT_COLUMN,
+ GTK_SORT_ASCENDING);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (data->o_app_tree_view),
+ data->app_model);
+ g_object_unref (G_OBJECT (data->app_model));
+
+ theme = gtk_icon_theme_get_default ();
+ icon_size = get_folder_pixbuf_size_for_list (GTK_WIDGET (data->dialog));
+
+ for (scan = data->app_list; scan; scan = scan->next) {
+ gboolean found;
+ char *utf8_name;
+ GdkPixbuf *icon_image = NULL;
+
+ app = scan->data;
+
+ found = FALSE;
+ if (app_names != NULL) {
+ GList *p;
+ for (p = app_names; p && !found; p = p->next)
+ if (strcmp ((char*)p->data, g_app_info_get_executable (app)) == 0)
+ found = TRUE;
+ }
+
+ if (found)
+ continue;
+
+ app_names = g_list_prepend (app_names, (char*) g_app_info_get_executable (app));
+
+ utf8_name = g_locale_to_utf8 (g_app_info_get_name (app), -1, NULL, NULL, NULL);
+ icon_image = get_icon_pixbuf (g_app_info_get_icon (app), icon_size, theme);
+
+ gtk_list_store_append (GTK_LIST_STORE (data->app_model),
+ &iter);
+ gtk_list_store_set (GTK_LIST_STORE (data->app_model),
+ &iter,
+ ICON_COLUMN, icon_image,
+ TEXT_COLUMN, utf8_name,
+ DATA_COLUMN, app,
+ -1);
+
+ g_free (utf8_name);
+ }
+
+ column = gtk_tree_view_column_new ();
+
+ renderer = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "pixbuf", ICON_COLUMN,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column,
+ renderer,
+ TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "text", TEXT_COLUMN,
+ NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (data->o_app_tree_view),
+ column);
+
+ if (app_names)
+ g_list_free (app_names);
+
+ /* * recent editors list. */
+
+ data->recent_model = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_STRING));
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->recent_model), 0, GTK_SORT_ASCENDING);
+
+ gtk_tree_view_set_model (GTK_TREE_VIEW (data->o_recent_tree_view),
+ data->recent_model);
+ g_object_unref (G_OBJECT (data->recent_model));
+
+ editors = eel_mateconf_get_string_list (PREF_EDIT_EDITORS);
+ for (sscan = editors; sscan; sscan = sscan->next) {
+ char *editor = sscan->data;
+
+ gtk_list_store_append (GTK_LIST_STORE (data->recent_model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (data->recent_model), &iter,
+ 0, editor,
+ -1);
+ }
+ g_slist_foreach (editors, (GFunc) g_free, NULL);
+ g_slist_free (editors);
+
+ renderer = gtk_cell_renderer_text_new ();
+ column = gtk_tree_view_column_new_with_attributes (NULL,
+ renderer,
+ "text", 0,
+ NULL);
+ gtk_tree_view_column_set_sort_column_id (column, 0);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (data->o_recent_tree_view),
+ column);
+
+ /* Run dialog. */
+ gtk_window_set_transient_for (GTK_WINDOW (data->dialog),
+ GTK_WINDOW (window));
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
+ gtk_widget_show_all (data->dialog);
+}
+
+
+void
+open_with_cb (GtkWidget *widget,
+ void *callback_data)
+{
+ FrWindow *window = callback_data;
+ GList *file_list;
+
+ file_list = fr_window_get_file_list_selection (window, FALSE, NULL);
+ if (file_list == NULL)
+ return;
+
+ fr_window_open_files (window, file_list, TRUE);
+ path_list_free (file_list);
+}
diff --git a/src/dlg-open-with.h b/src/dlg-open-with.h
new file mode 100644
index 0000000..9f537d4
--- /dev/null
+++ b/src/dlg-open-with.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_OPEN_WITH_H
+#define DLG_OPEN_WITH_H
+
+#include <gtk/gtk.h>
+#include "fr-window.h"
+
+void open_with_cb (GtkWidget *widget, void *data);
+void dlg_open_with (FrWindow *window, GList *file_list);
+
+#endif /* DLG_OPEN_WITH_H */
diff --git a/src/dlg-package-installer.c b/src/dlg-package-installer.c
new file mode 100644
index 0000000..d1f9e94
--- /dev/null
+++ b/src/dlg-package-installer.c
@@ -0,0 +1,294 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001-2009 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include "dlg-package-installer.h"
+#include "gtk-utils.h"
+#include "main.h"
+
+
+typedef struct {
+ FrWindow *window;
+ FrArchive *archive;
+ FrAction action;
+ const char *packages;
+} InstallerData;
+
+
+static void
+installer_data_free (InstallerData *idata)
+{
+ g_object_unref (idata->archive);
+ g_object_unref (idata->window);
+ g_free (idata);
+}
+
+
+static void
+package_installer_terminated (InstallerData *idata,
+ const char *error)
+{
+ GdkWindow *window;
+
+ window = gtk_widget_get_window (GTK_WIDGET (idata->window));
+ if (window != NULL)
+ gdk_window_set_cursor (window, NULL);
+
+ if (error != NULL) {
+ fr_archive_action_completed (idata->archive,
+ FR_ACTION_CREATING_NEW_ARCHIVE,
+ FR_PROC_ERROR_GENERIC,
+ error);
+ }
+ else {
+ update_registered_commands_capabilities ();
+ if (fr_window_is_batch_mode (idata->window))
+ fr_window_resume_batch (idata->window);
+ else
+ fr_window_restart_current_batch_action (idata->window);
+ }
+
+ installer_data_free (idata);
+}
+
+
+#ifdef ENABLE_PACKAGEKIT
+
+
+static void
+packagekit_install_package_names_ready_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ InstallerData *idata = user_data;
+ GDBusProxy *proxy;
+ GVariant *values;
+ GError *error = NULL;
+ char *message = NULL;
+
+ proxy = G_DBUS_PROXY (source_object);
+ values = g_dbus_proxy_call_finish (proxy, res, &error);
+ if (values == NULL) {
+ message = g_strdup_printf ("%s\n%s",
+ _("There was an internal error trying to search for applications:"),
+ error->message);
+ g_clear_error (&error);
+ }
+
+ package_installer_terminated (idata, message);
+
+ g_free (message);
+ if (values != NULL)
+ g_variant_unref (values);
+ g_object_unref (proxy);
+}
+
+
+static char **
+get_packages_real_names (char **names)
+{
+ char **real_names;
+ GKeyFile *key_file;
+ char *filename;
+ int i;
+
+ real_names = g_new0 (char *, g_strv_length (names));
+ key_file = g_key_file_new ();
+ filename = g_build_filename (PRIVDATADIR, "packages.match", NULL);
+ g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
+
+ for (i = 0; names[i] != NULL; i++) {
+ char *real_name;
+
+ real_name = g_key_file_get_string (key_file, "Package Matches", names[i], NULL);
+ if (real_name != NULL)
+ real_name = g_strstrip (real_name);
+ if ((real_name == NULL) || (strncmp (real_name, "", 1) == 0))
+ real_names[i] = g_strdup (real_name);
+
+ g_free (real_name);
+ }
+
+ g_free (filename);
+ g_key_file_free (key_file);
+
+ return real_names;
+}
+
+
+static void
+install_packages (InstallerData *idata)
+{
+ GDBusConnection *connection;
+ GError *error = NULL;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ if (connection != NULL) {
+ GdkWindow *window;
+ GDBusProxy *proxy;
+
+ window = gtk_widget_get_window (GTK_WIDGET (idata->window));
+ if (window != NULL) {
+ GdkCursor *cursor;
+
+ cursor = gdk_cursor_new (GDK_WATCH);
+ gdk_window_set_cursor (window, cursor);
+ gdk_cursor_unref (cursor);
+ }
+
+ proxy = g_dbus_proxy_new_sync (connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.freedesktop.PackageKit",
+ "/org/freedesktop/PackageKit",
+ "org.freedesktop.PackageKit.Modify",
+ NULL,
+ &error);
+
+ if (proxy != NULL) {
+ guint xid;
+ char **names;
+ char **real_names;
+
+ if (window != NULL)
+ xid = GDK_WINDOW_XID (window);
+ else
+ xid = 0;
+
+ names = g_strsplit (idata->packages, ",", -1);
+ real_names = get_packages_real_names (names);
+
+ g_dbus_proxy_call (proxy,
+ "InstallPackageNames",
+ g_variant_new ("(u^ass)",
+ xid,
+ names,
+ "hide-confirm-search,hide-finished,hide-warning"),
+ G_DBUS_CALL_FLAGS_NONE,
+ G_MAXINT,
+ NULL,
+ packagekit_install_package_names_ready_cb,
+ idata);
+
+ g_strfreev (real_names);
+ g_strfreev (names);
+ }
+ }
+
+ if (error != NULL) {
+ char *message;
+
+ message = g_strdup_printf ("%s\n%s",
+ _("There was an internal error trying to search for applications:"),
+ error->message);
+ package_installer_terminated (idata, message);
+
+ g_clear_error (&error);
+ }
+}
+
+
+static void
+confirm_search_dialog_response_cb (GtkDialog *dialog,
+ int response_id,
+ gpointer user_data)
+{
+ InstallerData *idata = user_data;
+
+ gtk_widget_destroy (GTK_WIDGET (dialog));
+
+ if (response_id == GTK_RESPONSE_YES) {
+ install_packages (idata);
+ }
+ else {
+ fr_window_stop_batch (idata->window);
+ installer_data_free (idata);
+ }
+}
+
+
+#endif /* ENABLE_PACKAGEKIT */
+
+
+void
+dlg_package_installer (FrWindow *window,
+ FrArchive *archive,
+ FrAction action)
+{
+ InstallerData *idata;
+ GType command_type;
+ FrCommand *command;
+
+ idata = g_new0 (InstallerData, 1);
+ idata->window = g_object_ref (window);
+ idata->archive = g_object_ref (archive);
+ idata->action = action;
+
+ command_type = get_preferred_command_for_mime_type (idata->archive->content_type, FR_COMMAND_CAN_READ_WRITE);
+ if (command_type == 0)
+ command_type = get_preferred_command_for_mime_type (idata->archive->content_type, FR_COMMAND_CAN_READ);
+ if (command_type == 0) {
+ package_installer_terminated (idata, _("Archive type not supported."));
+ return;
+ }
+
+ command = g_object_new (command_type, 0);
+ idata->packages = fr_command_get_packages (command, idata->archive->content_type);
+ g_object_unref (command);
+
+ if (idata->packages == NULL) {
+ package_installer_terminated (idata, _("Archive type not supported."));
+ return;
+ }
+
+#ifdef ENABLE_PACKAGEKIT
+
+ {
+ char *secondary_text;
+ GtkWidget *dialog;
+
+ secondary_text = g_strdup_printf (_("There is no command installed for %s files.\nDo you want to search for a command to open this file?"),
+ g_content_type_get_description (idata->archive->content_type));
+ dialog = _gtk_message_dialog_new (GTK_WINDOW (idata->window),
+ GTK_DIALOG_MODAL,
+ GTK_STOCK_DIALOG_ERROR,
+ _("Could not open this file type"),
+ secondary_text,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
+ _("_Search Command"), GTK_RESPONSE_YES,
+ NULL);
+ g_signal_connect (dialog, "response", G_CALLBACK (confirm_search_dialog_response_cb), idata);
+ gtk_widget_show (dialog);
+
+ g_free (secondary_text);
+ }
+
+#else /* ! ENABLE_PACKAGEKIT */
+
+ package_installer_terminated (idata, _("Archive type not supported."));
+
+#endif /* ENABLE_PACKAGEKIT */
+}
diff --git a/src/dlg-package-installer.h b/src/dlg-package-installer.h
new file mode 100644
index 0000000..f7df874
--- /dev/null
+++ b/src/dlg-package-installer.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001-2009 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_PACKAGE_INSTALLER_H
+#define DLG_PACKAGE_INSTALLER_H
+
+#include "fr-window.h"
+
+void dlg_package_installer (FrWindow *window,
+ FrArchive *archive,
+ FrAction action);
+
+#endif /* DLG_PACKAGE_INSTALLER_H */
diff --git a/src/dlg-password.c b/src/dlg-password.c
new file mode 100644
index 0000000..db2c5ac
--- /dev/null
+++ b/src/dlg-password.c
@@ -0,0 +1,126 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "fr-window.h"
+#include "mateconf-utils.h"
+#include "gtk-utils.h"
+#include "preferences.h"
+
+
+typedef struct {
+ GtkBuilder *builder;
+ FrWindow *window;
+ GtkWidget *dialog;
+ GtkWidget *pw_password_entry;
+ GtkWidget *pw_encrypt_header_checkbutton;
+} DialogData;
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ g_object_unref (data->builder);
+ g_free (data);
+}
+
+
+static void
+response_cb (GtkWidget *dialog,
+ int response_id,
+ DialogData *data)
+{
+ char *password;
+ gboolean encrypt_header;
+
+ switch (response_id) {
+ case GTK_RESPONSE_OK:
+ password = _gtk_entry_get_locale_text (GTK_ENTRY (data->pw_password_entry));
+ fr_window_set_password (data->window, password);
+ g_free (password);
+
+ encrypt_header = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (data->pw_encrypt_header_checkbutton));
+ eel_mateconf_set_boolean (PREF_ENCRYPT_HEADER, encrypt_header);
+ fr_window_set_encrypt_header (data->window, encrypt_header);
+ break;
+ default:
+ break;
+ }
+
+ gtk_widget_destroy (data->dialog);
+}
+
+
+void
+dlg_password (GtkWidget *widget,
+ gpointer callback_data)
+{
+ FrWindow *window = callback_data;
+ DialogData *data;
+
+ data = g_new0 (DialogData, 1);
+
+ data->builder = _gtk_builder_new_from_file ("password.ui");
+ if (data->builder == NULL) {
+ g_free (data);
+ return;
+ }
+
+ data->window = window;
+
+ /* Get the widgets. */
+
+ data->dialog = _gtk_builder_get_widget (data->builder, "password_dialog");
+ data->pw_password_entry = _gtk_builder_get_widget (data->builder, "pw_password_entry");
+ data->pw_encrypt_header_checkbutton = _gtk_builder_get_widget (data->builder, "pw_encrypt_header_checkbutton");
+
+ /* Set widgets data. */
+
+ _gtk_entry_set_locale_text (GTK_ENTRY (data->pw_password_entry), fr_window_get_password (window));
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (data->pw_encrypt_header_checkbutton), fr_window_get_encrypt_header (window));
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (destroy_cb),
+ data);
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "response",
+ G_CALLBACK (response_cb),
+ data);
+
+ /* Run dialog. */
+
+ gtk_widget_grab_focus (data->pw_password_entry);
+ if (gtk_widget_get_realized (GTK_WIDGET (window)))
+ gtk_window_set_transient_for (GTK_WINDOW (data->dialog),
+ GTK_WINDOW (window));
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
+
+ gtk_widget_show (data->dialog);
+}
diff --git a/src/dlg-password.h b/src/dlg-password.h
new file mode 100644
index 0000000..b804078
--- /dev/null
+++ b/src/dlg-password.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_PASSWORD_H
+#define DLG_PASSWORD_H
+
+#include "fr-window.h"
+
+void dlg_password (GtkWidget *widget,
+ gpointer callback_data);
+
+#endif /* DLG_PASSWORD_H */
diff --git a/src/dlg-prop.c b/src/dlg-prop.c
new file mode 100644
index 0000000..e359743
--- /dev/null
+++ b/src/dlg-prop.c
@@ -0,0 +1,219 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "glib-utils.h"
+#include "file-utils.h"
+#include "gtk-utils.h"
+#include "fr-window.h"
+
+
+typedef struct {
+ GtkBuilder *builder;
+ GtkWidget *dialog;
+} DialogData;
+
+
+/* called when the main dialog is closed. */
+static void
+destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ g_object_unref (G_OBJECT (data->builder));
+ g_free (data);
+}
+
+
+static void
+set_label_type (GtkWidget *label, const char *text, const char *type)
+{
+ char *t;
+
+ t = g_strdup_printf ("<%s>%s</%s>", type, text, type);
+ gtk_label_set_markup (GTK_LABEL (label), t);
+ g_free (t);
+}
+
+
+static void
+set_label (GtkWidget *label, const char *text)
+{
+ set_label_type (label, text, "b");
+}
+
+
+static int
+help_cb (GtkWidget *w,
+ DialogData *data)
+{
+ show_help_dialog (GTK_WINDOW (data->dialog), "file-roller-view-archive-properties");
+ return TRUE;
+}
+
+
+void
+dlg_prop (FrWindow *window)
+{
+ DialogData *data;
+ GtkWidget *ok_button;
+ GtkWidget *help_button;
+ GtkWidget *label_label;
+ GtkWidget *label;
+ char *s;
+ goffset size, uncompressed_size;
+ char *utf8_name;
+ char *title_txt;
+ double ratio;
+
+ data = g_new (DialogData, 1);
+
+ data->builder = _gtk_builder_new_from_file ("properties.ui");
+ if (data->builder == NULL) {
+ g_free (data);
+ return;
+ }
+
+ /* Get the widgets. */
+
+ data->dialog = _gtk_builder_get_widget (data->builder, "prop_dialog");
+ ok_button = _gtk_builder_get_widget (data->builder, "p_ok_button");
+ help_button = _gtk_builder_get_widget (data->builder, "p_help_button");
+
+ /* Set widgets data. */
+
+ label_label = _gtk_builder_get_widget (data->builder, "p_path_label_label");
+ /* Translators: after the colon there is a folder name. */
+ set_label (label_label, _("Location:"));
+
+ label = _gtk_builder_get_widget (data->builder, "p_path_label");
+ s = remove_level_from_path (fr_window_get_archive_uri (window));
+ utf8_name = g_filename_display_name (s);
+ gtk_label_set_text (GTK_LABEL (label), utf8_name);
+ g_free (utf8_name);
+ g_free (s);
+
+ /**/
+
+ label_label = _gtk_builder_get_widget (data->builder, "p_name_label_label");
+ set_label (label_label, C_("File", "Name:"));
+
+ label = _gtk_builder_get_widget (data->builder, "p_name_label");
+ utf8_name = g_uri_display_basename (fr_window_get_archive_uri (window));
+ gtk_label_set_text (GTK_LABEL (label), utf8_name);
+
+ title_txt = g_strdup_printf (_("%s Properties"), utf8_name);
+ gtk_window_set_title (GTK_WINDOW (data->dialog), title_txt);
+ g_free (title_txt);
+
+ g_free (utf8_name);
+
+ /**/
+
+ label_label = _gtk_builder_get_widget (data->builder, "p_date_label_label");
+ set_label (label_label, _("Modified on:"));
+
+ label = _gtk_builder_get_widget (data->builder, "p_date_label");
+ s = get_time_string (get_file_mtime (fr_window_get_archive_uri (window)));
+ gtk_label_set_text (GTK_LABEL (label), s);
+ g_free (s);
+
+ /**/
+
+ label_label = _gtk_builder_get_widget (data->builder, "p_size_label_label");
+ set_label (label_label, _("Archive size:"));
+
+ label = _gtk_builder_get_widget (data->builder, "p_size_label");
+ size = get_file_size (fr_window_get_archive_uri (window));
+ s = g_format_size_for_display (size);
+ gtk_label_set_text (GTK_LABEL (label), s);
+ g_free (s);
+
+ /**/
+
+ label_label = _gtk_builder_get_widget (data->builder, "p_uncomp_size_label_label");
+ set_label (label_label, _("Content size:"));
+
+ uncompressed_size = 0;
+ if (fr_window_archive_is_present (window)) {
+ int i;
+
+ for (i = 0; i < window->archive->command->files->len; i++) {
+ FileData *fd = g_ptr_array_index (window->archive->command->files, i);
+ uncompressed_size += fd->size;
+ }
+ }
+
+ label = _gtk_builder_get_widget (data->builder, "p_uncomp_size_label");
+ s = g_format_size_for_display (uncompressed_size);
+ gtk_label_set_text (GTK_LABEL (label), s);
+ g_free (s);
+
+ /**/
+
+ label_label = _gtk_builder_get_widget (data->builder, "p_cratio_label_label");
+ set_label (label_label, _("Compression ratio:"));
+
+ label = _gtk_builder_get_widget (data->builder, "p_cratio_label");
+
+ if (uncompressed_size != 0)
+ ratio = (double) uncompressed_size / size;
+ else
+ ratio = 0.0;
+ s = g_strdup_printf ("%0.2f", ratio);
+ gtk_label_set_text (GTK_LABEL (label), s);
+ g_free (s);
+
+ /**/
+
+ label_label = _gtk_builder_get_widget (data->builder, "p_files_label_label");
+ set_label (label_label, _("Number of files:"));
+
+ label = _gtk_builder_get_widget (data->builder, "p_files_label");
+ s = g_strdup_printf ("%d", window->archive->command->n_regular_files);
+ gtk_label_set_text (GTK_LABEL (label), s);
+ g_free (s);
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->dialog),
+ "destroy",
+ G_CALLBACK (destroy_cb),
+ data);
+ g_signal_connect_swapped (G_OBJECT (ok_button),
+ "clicked",
+ G_CALLBACK (gtk_widget_destroy),
+ G_OBJECT (data->dialog));
+ g_signal_connect (G_OBJECT (help_button),
+ "clicked",
+ G_CALLBACK (help_cb),
+ data);
+
+ /* Run dialog. */
+
+ gtk_window_set_transient_for (GTK_WINDOW (data->dialog),
+ GTK_WINDOW (window));
+ gtk_window_set_modal (GTK_WINDOW (data->dialog), TRUE);
+
+ gtk_widget_show (data->dialog);
+}
diff --git a/src/dlg-prop.h b/src/dlg-prop.h
new file mode 100644
index 0000000..50849f5
--- /dev/null
+++ b/src/dlg-prop.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_PROP_H
+#define DLG_PROP_H
+
+#include "fr-window.h"
+
+void dlg_prop (FrWindow *window);
+
+#endif /* DLG_DELETE_H */
diff --git a/src/dlg-update.c b/src/dlg-update.c
new file mode 100644
index 0000000..bd46517
--- /dev/null
+++ b/src/dlg-update.c
@@ -0,0 +1,406 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "dlg-update.h"
+#include "file-utils.h"
+#include "mateconf-utils.h"
+#include "glib-utils.h"
+#include "gtk-utils.h"
+#include "main.h"
+#include "fr-window.h"
+
+
+enum {
+ IS_SELECTED_COLUMN,
+ NAME_COLUMN,
+ DATA_COLUMN,
+ N_COLUMNS
+};
+
+typedef struct {
+ FrWindow *window;
+ GtkBuilder *builder;
+
+ GtkWidget *update_file_dialog;
+ GtkWidget *update_file_primary_text_label;
+ GtkWidget *update_file_secondary_text_label;
+
+ GtkWidget *update_files_dialog;
+ GtkWidget *update_files_primary_text_label;
+ GtkWidget *update_files_secondary_text_label;
+ GtkWidget *update_files_treeview;
+ GtkWidget *update_files_ok_button;
+
+ GList *file_list;
+ GtkTreeModel *list_model;
+} DialogData;
+
+
+/* called when the main dialog is closed. */
+static void
+dlg_update__destroy_cb (GtkWidget *widget,
+ DialogData *data)
+{
+ fr_window_update_dialog_closed (data->window);
+ g_object_unref (G_OBJECT (data->builder));
+ if (data->file_list != NULL)
+ g_list_free (data->file_list);
+ g_free (data);
+}
+
+
+static GList*
+get_selected_files (DialogData *data)
+{
+ GList *selection = NULL;
+ GtkTreeIter iter;
+
+ if (! gtk_tree_model_get_iter_first (data->list_model, &iter))
+ return NULL;
+
+ do {
+ gboolean is_selected;
+ OpenFile *file;
+
+ gtk_tree_model_get (data->list_model, &iter,
+ IS_SELECTED_COLUMN, &is_selected,
+ DATA_COLUMN, &file,
+ -1);
+ if (is_selected)
+ selection = g_list_prepend (selection, file);
+ } while (gtk_tree_model_iter_next (data->list_model, &iter));
+
+ return g_list_reverse (selection);
+}
+
+
+static void
+update_cb (GtkWidget *widget,
+ gpointer callback_data)
+{
+ DialogData *data = callback_data;
+ GList *selection;
+
+ selection = get_selected_files (data);
+ if (fr_window_update_files (data->window, selection)) {
+ int n_files;
+
+ n_files = g_list_length (data->file_list);
+ if (n_files == 1)
+ gtk_widget_destroy (data->update_file_dialog);
+ else
+ gtk_widget_destroy (data->update_files_dialog);
+ }
+ if (selection != NULL)
+ g_list_free (selection);
+}
+
+
+static void
+update_file_list (DialogData *data)
+{
+ gboolean n_files;
+ GList *scan;
+ GtkTreeIter iter;
+
+ n_files = g_list_length (data->file_list);
+
+ /* update the file list */
+
+ gtk_list_store_clear (GTK_LIST_STORE (data->list_model));
+ for (scan = data->file_list; scan; scan = scan->next) {
+ char *utf8_name;
+ OpenFile *file = scan->data;
+
+ gtk_list_store_append (GTK_LIST_STORE (data->list_model),
+ &iter);
+
+ utf8_name = g_filename_display_name (file_name_from_path (file->path));
+ gtk_list_store_set (GTK_LIST_STORE (data->list_model),
+ &iter,
+ IS_SELECTED_COLUMN, TRUE,
+ NAME_COLUMN, utf8_name,
+ DATA_COLUMN, file,
+ -1);
+ g_free (utf8_name);
+ }
+
+ /* update the labels */
+
+ if (n_files == 1) {
+ OpenFile *file = data->file_list->data;
+ char *file_name;
+ char *unescaped;
+ char *archive_name;
+ char *label;
+ char *markup;
+
+ /* primary text */
+
+ file_name = g_filename_display_name (file_name_from_path (file->path));
+ unescaped = g_uri_unescape_string (fr_window_get_archive_uri (data->window), NULL);
+ archive_name = g_path_get_basename (unescaped);
+ label = g_markup_printf_escaped (_("Update the file \"%s\" in the archive \"%s\"?"), file_name, archive_name);
+ markup = g_strdup_printf ("<big><b>%s</b></big>", label);
+ gtk_label_set_markup (GTK_LABEL (data->update_file_primary_text_label), markup);
+
+ g_free (markup);
+ g_free (label);
+ g_free (archive_name);
+ g_free (unescaped);
+ g_free (file_name);
+
+ /* secondary text */
+
+ label = g_strdup_printf (ngettext ("The file has been modified with an external application. If you don't update the file in the archive, all of your changes will be lost.",
+ "%d files have been modified with an external application. If you don't update the files in the archive, all of your changes will be lost.",
+ n_files),
+ n_files);
+ gtk_label_set_text (GTK_LABEL (data->update_file_secondary_text_label), label);
+ g_free (label);
+ }
+ else if (n_files > 1) {
+ char *unescaped;
+ char *archive_name;
+ char *label;
+ char *markup;
+
+ /* primary text */
+
+ unescaped = g_uri_unescape_string (fr_window_get_archive_uri (data->window), NULL);
+ archive_name = g_path_get_basename (unescaped);
+ label = g_markup_printf_escaped (_("Update the files in the archive \"%s\"?"), archive_name);
+ markup = g_strdup_printf ("<big><b>%s</b></big>", label);
+ gtk_label_set_markup (GTK_LABEL (data->update_files_primary_text_label), markup);
+
+ g_free (markup);
+ g_free (label);
+ g_free (archive_name);
+ g_free (unescaped);
+
+ /* secondary text */
+
+ label = g_strdup_printf (ngettext ("The file has been modified with an external application. If you don't update the file in the archive, all of your changes will be lost.",
+ "%d files have been modified with an external application. If you don't update the files in the archive, all of your changes will be lost.",
+ n_files),
+ n_files);
+ gtk_label_set_text (GTK_LABEL (data->update_files_secondary_text_label), label);
+ g_free (label);
+ }
+
+ /* show the appropriate dialog */
+
+ if (n_files == 1) {
+ /*gtk_window_set_modal (GTK_WINDOW (data->update_files_dialog), FALSE);*/
+ gtk_widget_hide (data->update_files_dialog);
+ /*gtk_window_set_modal (GTK_WINDOW (data->update_file_dialog), TRUE);*/
+ gtk_widget_show (data->update_file_dialog);
+ }
+ else if (n_files > 1) {
+ /*gtk_window_set_modal (GTK_WINDOW (data->update_file_dialog), FALSE);*/
+ gtk_widget_hide (data->update_file_dialog);
+ /*gtk_window_set_modal (GTK_WINDOW (data->update_files_dialog), TRUE);*/
+ gtk_widget_show (data->update_files_dialog);
+ }
+ else { /* n_files == 0 */
+ /*gtk_window_set_modal (GTK_WINDOW (data->update_files_dialog), FALSE);*/
+ gtk_widget_hide (data->update_files_dialog);
+ /*gtk_window_set_modal (GTK_WINDOW (data->update_file_dialog), FALSE);*/
+ gtk_widget_hide (data->update_file_dialog);
+ }
+}
+
+
+static int
+n_selected (DialogData *data)
+{
+ int n = 0;
+ GtkTreeIter iter;
+
+ if (! gtk_tree_model_get_iter_first (data->list_model, &iter))
+ return 0;
+
+ do {
+ gboolean is_selected;
+ gtk_tree_model_get (data->list_model, &iter, IS_SELECTED_COLUMN, &is_selected, -1);
+ if (is_selected)
+ n++;
+ } while (gtk_tree_model_iter_next (data->list_model, &iter));
+
+ return n;
+}
+
+
+static void
+is_selected_toggled (GtkCellRendererToggle *cell,
+ char *path_string,
+ gpointer callback_data)
+{
+ DialogData *data = callback_data;
+ GtkTreeModel *model = GTK_TREE_MODEL (data->list_model);
+ GtkTreeIter iter;
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+ guint value;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ value = ! gtk_cell_renderer_toggle_get_active (cell);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter, IS_SELECTED_COLUMN, value, -1);
+
+ gtk_tree_path_free (path);
+
+ gtk_widget_set_sensitive (data->update_files_ok_button, n_selected (data) > 0);
+}
+
+
+gpointer
+dlg_update (FrWindow *window)
+{
+ DialogData *data;
+ GtkWidget *update_file_ok_button;
+ GtkWidget *update_file_cancel_button;
+ GtkWidget *update_files_cancel_button;
+ GtkCellRenderer *renderer;
+ GtkTreeViewColumn *column;
+
+ data = g_new0 (DialogData, 1);
+
+ data->builder = _gtk_builder_new_from_file ("update.ui");
+ if (data->builder == NULL) {
+ g_free (data);
+ return NULL;
+ }
+
+ data->file_list = NULL;
+ data->window = window;
+
+ /* Get the widgets. */
+
+ data->update_file_dialog = _gtk_builder_get_widget (data->builder, "update_file_dialog");
+ data->update_file_primary_text_label = _gtk_builder_get_widget (data->builder, "update_file_primary_text_label");
+ data->update_file_secondary_text_label = _gtk_builder_get_widget (data->builder, "update_file_secondary_text_label");
+
+ update_file_ok_button = _gtk_builder_get_widget (data->builder, "update_file_ok_button");
+ update_file_cancel_button = _gtk_builder_get_widget (data->builder, "update_file_cancel_button");
+
+ data->update_files_dialog = _gtk_builder_get_widget (data->builder, "update_files_dialog");
+ data->update_files_primary_text_label = _gtk_builder_get_widget (data->builder, "update_files_primary_text_label");
+ data->update_files_secondary_text_label = _gtk_builder_get_widget (data->builder, "update_files_secondary_text_label");
+ data->update_files_treeview = _gtk_builder_get_widget (data->builder, "update_files_treeview");
+ data->update_files_ok_button = _gtk_builder_get_widget (data->builder, "update_files_ok_button");
+ update_files_cancel_button = _gtk_builder_get_widget (data->builder, "update_files_cancel_button");
+
+ /* Set the signals handlers. */
+
+ g_signal_connect (G_OBJECT (data->update_file_dialog),
+ "destroy",
+ G_CALLBACK (dlg_update__destroy_cb),
+ data);
+ g_signal_connect (G_OBJECT (update_file_ok_button),
+ "clicked",
+ G_CALLBACK (update_cb),
+ data);
+ g_signal_connect_swapped (G_OBJECT (update_file_cancel_button),
+ "clicked",
+ G_CALLBACK (gtk_widget_destroy),
+ G_OBJECT (data->update_file_dialog));
+ g_signal_connect (G_OBJECT (data->update_files_dialog),
+ "destroy",
+ G_CALLBACK (dlg_update__destroy_cb),
+ data);
+ g_signal_connect (G_OBJECT (data->update_files_ok_button),
+ "clicked",
+ G_CALLBACK (update_cb),
+ data);
+ g_signal_connect_swapped (G_OBJECT (update_files_cancel_button),
+ "clicked",
+ G_CALLBACK (gtk_widget_destroy),
+ G_OBJECT (data->update_files_dialog));
+
+ /* Set dialog data. */
+
+ data->list_model = GTK_TREE_MODEL (gtk_list_store_new (N_COLUMNS,
+ G_TYPE_BOOLEAN,
+ G_TYPE_STRING,
+ G_TYPE_POINTER));
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (data->list_model),
+ NAME_COLUMN,
+ GTK_SORT_ASCENDING);
+ gtk_tree_view_set_model (GTK_TREE_VIEW (data->update_files_treeview),
+ data->list_model);
+ g_object_unref (G_OBJECT (data->list_model));
+
+ column = gtk_tree_view_column_new ();
+
+ renderer = gtk_cell_renderer_toggle_new ();
+ g_signal_connect (G_OBJECT (renderer),
+ "toggled",
+ G_CALLBACK (is_selected_toggled),
+ data);
+ gtk_tree_view_column_pack_start (column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "active", IS_SELECTED_COLUMN,
+ NULL);
+
+ renderer = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes (column, renderer,
+ "text", NAME_COLUMN,
+ NULL);
+
+ gtk_tree_view_append_column (GTK_TREE_VIEW (data->update_files_treeview), column);
+
+ /* Run dialog. */
+
+ gtk_window_set_transient_for (GTK_WINDOW (data->update_file_dialog),
+ GTK_WINDOW (window));
+ gtk_window_set_transient_for (GTK_WINDOW (data->update_files_dialog),
+ GTK_WINDOW (window));
+
+ update_file_list (data);
+
+ return data;
+}
+
+
+void
+dlg_update_add_file (gpointer dialog,
+ OpenFile *file)
+{
+ DialogData *data = dialog;
+ GList *scan;
+
+ /* avoid duplicates */
+
+ for (scan = data->file_list; scan; scan = scan->next) {
+ OpenFile *test = scan->data;
+ if (uricmp (test->extracted_uri, file->extracted_uri) == 0)
+ return;
+ }
+
+ /**/
+
+ data->file_list = g_list_append (data->file_list, file);
+ update_file_list (data);
+}
diff --git a/src/dlg-update.h b/src/dlg-update.h
new file mode 100644
index 0000000..6133887
--- /dev/null
+++ b/src/dlg-update.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2008 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DLG_UPDATE_H
+#define DLG_UPDATE_H
+
+#include <gtk/gtk.h>
+#include "fr-window.h"
+#include "open-file.h"
+
+gpointer dlg_update (FrWindow *window);
+void dlg_update_add_file (gpointer dialog,
+ OpenFile *file);
+
+#endif /* DLG_UPDATE_H */
diff --git a/src/egg-macros.h b/src/egg-macros.h
new file mode 100644
index 0000000..6e56e2e
--- /dev/null
+++ b/src/egg-macros.h
@@ -0,0 +1,154 @@
+/**
+ * Useful macros.
+ *
+ * Author:
+ * Darin Adler <[email protected]>
+ *
+ * Copyright 2001 Ben Tea Spoons, Inc.
+ */
+#ifndef _EGG_MACROS_H_
+#define _EGG_MACROS_H_
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+/* Macros for defining classes. Ideas taken from Caja and GOB. */
+
+/* Define the boilerplate type stuff to reduce typos and code size. Defines
+ * the get_type method and the parent_class static variable. */
+
+#define EGG_BOILERPLATE(type, type_as_function, corba_type, \
+ parent_type, parent_type_macro, \
+ register_type_macro) \
+static void type_as_function ## _class_init (type ## Class *klass); \
+static void type_as_function ## _instance_init (type *object); \
+static parent_type ## Class *parent_class = NULL; \
+static void \
+type_as_function ## _class_init_trampoline (gpointer klass, \
+ gpointer data) \
+{ \
+ parent_class = (parent_type ## Class *)g_type_class_ref ( \
+ parent_type_macro); \
+ type_as_function ## _class_init ((type ## Class *)klass); \
+} \
+GType \
+type_as_function ## _get_type (void) \
+{ \
+ static GType object_type = 0; \
+ if (object_type == 0) { \
+ static const GTypeInfo object_info = { \
+ sizeof (type ## Class), \
+ NULL, /* base_init */ \
+ NULL, /* base_finalize */ \
+ type_as_function ## _class_init_trampoline, \
+ NULL, /* class_finalize */ \
+ NULL, /* class_data */ \
+ sizeof (type), \
+ 0, /* n_preallocs */ \
+ (GInstanceInitFunc) type_as_function ## _instance_init \
+ }; \
+ object_type = register_type_macro \
+ (type, type_as_function, corba_type, \
+ parent_type, parent_type_macro); \
+ } \
+ return object_type; \
+}
+
+/* Just call the parent handler. This assumes that there is a variable
+ * named parent_class that points to the (duh!) parent class. Note that
+ * this macro is not to be used with things that return something, use
+ * the _WITH_DEFAULT version for that */
+#define EGG_CALL_PARENT(parent_class_cast, name, args) \
+ ((parent_class_cast(parent_class)->name != NULL) ? \
+ parent_class_cast(parent_class)->name args : (void)0)
+
+/* Same as above, but in case there is no implementation, it evaluates
+ * to def_return */
+#define EGG_CALL_PARENT_WITH_DEFAULT(parent_class_cast, \
+ name, args, def_return) \
+ ((parent_class_cast(parent_class)->name != NULL) ? \
+ parent_class_cast(parent_class)->name args : def_return)
+
+/* Call a virtual method */
+#define EGG_CALL_VIRTUAL(object, get_class_cast, method, args) \
+ (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : (void)0)
+
+/* Call a virtual method with default */
+#define EGG_CALL_VIRTUAL_WITH_DEFAULT(object, get_class_cast, method, args, default) \
+ (get_class_cast (object)->method ? (* get_class_cast (object)->method) args : default)
+
+#define EGG_CLASS_BOILERPLATE(type, type_as_function, \
+ parent_type, parent_type_macro) \
+ EGG_BOILERPLATE(type, type_as_function, type, \
+ parent_type, parent_type_macro, \
+ EGG_REGISTER_TYPE)
+
+#define EGG_REGISTER_TYPE(type, type_as_function, corba_type, \
+ parent_type, parent_type_macro) \
+ g_type_register_static (parent_type_macro, #type, &object_info, 0)
+
+
+#define EGG_DEFINE_BOXED_TYPE(TN, t_n) \
+EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, {});
+
+#define EGG_DEFINE_BOXED_TYPE_WITH_CODE(TN, t_n, _C_) \
+\
+static gpointer t_n##_copy (gpointer boxed); \
+static void t_n##_free (gpointer boxed); \
+\
+EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, t_n##_copy, t_n##_free, _C_);
+
+#define EGG_DEFINE_BOXED_TYPE_EXTENDED(TN, t_n, b_c, b_f, _C_) \
+\
+_EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TN, t_n, b_c, b_f) {_C_;} \
+_EGG_DEFINE_BOXED_TYPE_EXTENDED_END()
+
+#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_BEGIN(TypeName, type_name, boxed_copy, boxed_free) \
+\
+GType \
+type_name##_get_type (void) \
+{ \
+ static volatile gsize g_define_type_id__volatile = 0; \
+ if (g_once_init_enter (&g_define_type_id__volatile)) \
+ { \
+ GType g_define_type_id = \
+ g_boxed_type_register_static (g_intern_static_string (#TypeName), \
+ boxed_copy, boxed_free); \
+ { /* custom code follows */
+#define _EGG_DEFINE_BOXED_TYPE_EXTENDED_END() \
+ /* following custom code */ \
+ } \
+ g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
+ } \
+ return g_define_type_id__volatile; \
+} /* closes type_name##_get_type() */
+
+#define EGG_DEFINE_QUARK(QN, q_n) \
+\
+GQuark \
+q_n##_quark (void) \
+{ \
+ static volatile gsize g_define_quark__volatile = 0; \
+ if (g_once_init_enter (&g_define_quark__volatile)) \
+ { \
+ GQuark g_define_quark = g_quark_from_string (#QN); \
+ g_once_init_leave (&g_define_quark__volatile, g_define_quark); \
+ } \
+ return g_define_quark__volatile; \
+}
+
+#define EGG_IS_POSITIVE_RESPONSE(response_id) \
+ ((response_id) == GTK_RESPONSE_ACCEPT || \
+ (response_id) == GTK_RESPONSE_OK || \
+ (response_id) == GTK_RESPONSE_YES || \
+ (response_id) == GTK_RESPONSE_APPLY)
+
+#define EGG_IS_NEGATIVE_RESPONSE(response_id) \
+ ((response_id) == GTK_RESPONSE_REJECT || \
+ (response_id) == GTK_RESPONSE_CANCEL || \
+ (response_id) == GTK_RESPONSE_NO)
+
+G_END_DECLS
+
+#endif /* _EGG_MACROS_H_ */
diff --git a/src/eggfileformatchooser.c b/src/eggfileformatchooser.c
new file mode 100644
index 0000000..894da54
--- /dev/null
+++ b/src/eggfileformatchooser.c
@@ -0,0 +1,1223 @@
+/* EggFileFormatChooser
+ * Copyright (C) 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "eggfileformatchooser.h"
+#include "egg-macros.h"
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef struct _EggFileFormatFilterInfo EggFileFormatFilterInfo;
+typedef struct _EggFileFormatSearch EggFileFormatSearch;
+
+enum
+{
+ MODEL_COLUMN_ID,
+ MODEL_COLUMN_NAME,
+ MODEL_COLUMN_ICON,
+ MODEL_COLUMN_EXTENSIONS,
+ MODEL_COLUMN_FILTER,
+ MODEL_COLUMN_DATA,
+ MODEL_COLUMN_DESTROY
+};
+
+enum
+{
+ SIGNAL_SELECTION_CHANGED,
+ SIGNAL_LAST
+};
+
+struct _EggFileFormatChooserPrivate
+{
+ GtkTreeStore *model;
+ GtkTreeSelection *selection;
+ guint idle_hack;
+ guint last_id;
+ gulong size_changed_event;
+
+ GtkFileChooser *chooser;
+ GtkFileFilter *all_files;
+ GtkFileFilter *supported_files;
+};
+
+struct _EggFileFormatFilterInfo
+{
+ GHashTable *extension_set;
+ GSList *extension_list;
+ gboolean show_extensions;
+ gchar *name;
+};
+
+struct _EggFileFormatSearch
+{
+ gboolean success;
+ GtkTreeIter iter;
+
+ guint format;
+ const gchar *extension;
+};
+
+static guint signals[SIGNAL_LAST];
+
+G_DEFINE_TYPE (EggFileFormatChooser,
+ egg_file_format_chooser,
+ GTK_TYPE_EXPANDER);
+static EGG_DEFINE_QUARK (EggFileFormatFilterInfo,
+ egg_file_format_filter_info);
+
+static EggFileFormatFilterInfo*
+egg_file_format_filter_info_new (const gchar *name,
+ gboolean show_extensions)
+{
+ EggFileFormatFilterInfo *self;
+
+ self = g_new0 (EggFileFormatFilterInfo, 1);
+ self->extension_set = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ self->show_extensions = show_extensions;
+ self->name = g_strdup (name);
+
+ return self;
+}
+
+static void
+egg_file_format_filter_info_free (gpointer boxed)
+{
+ EggFileFormatFilterInfo *self;
+
+ if (boxed)
+ {
+ self = boxed;
+
+ g_hash_table_unref (self->extension_set);
+ g_slist_foreach (self->extension_list, (GFunc) g_free, NULL);
+ g_slist_free (self->extension_list);
+ g_free (self->name);
+ g_free (self);
+ }
+}
+
+static gboolean
+egg_file_format_filter_find (gpointer key,
+ gpointer value G_GNUC_UNUSED,
+ gpointer data)
+{
+ const GtkFileFilterInfo *info = data;
+ const gchar *pattern = key;
+
+ return g_str_has_suffix (info->filename, pattern + 1);
+}
+
+static gboolean
+egg_file_format_filter_filter (const GtkFileFilterInfo *info,
+ gpointer data)
+{
+ EggFileFormatFilterInfo *self = data;
+
+ return NULL != g_hash_table_find (self->extension_set,
+ egg_file_format_filter_find,
+ (gpointer) info);
+}
+
+static GtkFileFilter*
+egg_file_format_filter_new (const gchar *name,
+ gboolean show_extensions)
+{
+ GtkFileFilter *filter;
+ EggFileFormatFilterInfo *info;
+
+ filter = gtk_file_filter_new ();
+ gtk_file_filter_set_name (filter, name);
+
+ info = egg_file_format_filter_info_new (name, show_extensions);
+
+ gtk_file_filter_add_custom (filter, GTK_FILE_FILTER_FILENAME,
+ egg_file_format_filter_filter,
+ info, NULL);
+ g_object_set_qdata_full (G_OBJECT (filter),
+ egg_file_format_filter_info_quark (),
+ info, egg_file_format_filter_info_free);
+
+ return filter;
+}
+
+static void
+egg_file_format_filter_add_extensions (GtkFileFilter *filter,
+ const gchar *extensions)
+{
+ EggFileFormatFilterInfo *info;
+ GString *filter_name;
+ const gchar *extptr;
+ gchar *pattern;
+ gsize length;
+
+ g_assert (NULL != extensions);
+
+ info = g_object_get_qdata (G_OBJECT (filter),
+ egg_file_format_filter_info_quark ());
+
+ info->extension_list = g_slist_prepend (info->extension_list,
+ g_strdup (extensions));
+
+ if (info->show_extensions)
+ {
+ filter_name = g_string_new (info->name);
+ g_string_append (filter_name, " (");
+ }
+ else
+ filter_name = NULL;
+
+ extptr = extensions;
+ while (*extptr)
+ {
+ length = strcspn (extptr, ",");
+ pattern = g_new (gchar, length + 3);
+
+ memcpy (pattern, "*.", 2);
+ memcpy (pattern + 2, extptr, length);
+ pattern[length + 2] = '\0';
+
+ if (filter_name)
+ {
+ if (extptr != extensions)
+ g_string_append (filter_name, ", ");
+
+ g_string_append (filter_name, pattern);
+ }
+
+ extptr += length;
+
+ if (*extptr)
+ extptr += 2;
+
+ g_hash_table_replace (info->extension_set, pattern, pattern);
+ }
+
+ if (filter_name)
+ {
+ g_string_append (filter_name, ")");
+ gtk_file_filter_set_name (filter, filter_name->str);
+ g_string_free (filter_name, TRUE);
+ }
+}
+
+static void
+selection_changed_cb (GtkTreeSelection *selection,
+ EggFileFormatChooser *self)
+{
+ gchar *label;
+ gchar *name;
+
+ GtkFileFilter *filter;
+ GtkTreeModel *model;
+ GtkTreeIter parent;
+ GtkTreeIter iter;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_NAME, &name, -1);
+
+ label = g_strdup_printf (_("File _Format: %s"), name);
+ gtk_expander_set_use_underline (GTK_EXPANDER (self), TRUE);
+ gtk_expander_set_label (GTK_EXPANDER (self), label);
+
+ g_free (name);
+ g_free (label);
+
+ if (self->priv->chooser)
+ {
+ while (gtk_tree_model_iter_parent (model, &parent, &iter))
+ iter = parent;
+
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+ gtk_file_chooser_set_filter (self->priv->chooser, filter);
+ g_object_unref (filter);
+ }
+
+ g_signal_emit (self, signals[SIGNAL_SELECTION_CHANGED], 0);
+ }
+}
+
+/* XXX This hack is needed, as gtk_expander_set_label seems
+ * not to work from egg_file_format_chooser_init */
+static gboolean
+select_default_file_format (gpointer data)
+{
+ EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (data);
+ egg_file_format_chooser_set_format (self, 0);
+ self->priv->idle_hack = 0;
+ return FALSE;
+}
+
+static gboolean
+find_by_format (GtkTreeModel *model,
+ GtkTreePath *path G_GNUC_UNUSED,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ EggFileFormatSearch *search = data;
+ guint id;
+
+ gtk_tree_model_get (model, iter, MODEL_COLUMN_ID, &id, -1);
+
+ if (id == search->format)
+ {
+ search->success = TRUE;
+ search->iter = *iter;
+ }
+
+ return search->success;
+}
+
+static gboolean
+find_in_list (gchar *list,
+ const gchar *needle)
+{
+ gchar *saveptr;
+ gchar *token;
+
+ for (token = strtok_r (list, ",", &saveptr); NULL != token;
+ token = strtok_r (NULL, ",", &saveptr))
+ {
+ token = g_strstrip (token);
+
+ if (strcasecmp (needle, token) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+accept_filename (gchar *extensions,
+ const gchar *filename)
+{
+ const gchar *extptr;
+ gchar *saveptr;
+ gchar *token;
+ gsize length;
+
+ length = strlen (filename);
+
+ for (token = strtok_r (extensions, ",", &saveptr); NULL != token;
+ token = strtok_r (NULL, ",", &saveptr))
+ {
+ token = g_strstrip (token);
+ extptr = filename + length - strlen (token) - 1;
+
+ if (extptr > filename && '.' == *extptr &&
+ !strcmp (extptr + 1, token))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+find_by_extension (GtkTreeModel *model,
+ GtkTreePath *path G_GNUC_UNUSED,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ EggFileFormatSearch *search = data;
+
+ gchar *extensions = NULL;
+ guint format = 0;
+
+ gtk_tree_model_get (model, iter,
+ MODEL_COLUMN_EXTENSIONS, &extensions,
+ MODEL_COLUMN_ID, &format,
+ -1);
+
+ if (extensions && find_in_list (extensions, search->extension))
+ {
+ search->format = format;
+ search->success = TRUE;
+ search->iter = *iter;
+ }
+
+ g_free (extensions);
+ return search->success;
+}
+
+static int
+emit_default_size_changed (gpointer user_data)
+{
+ EggFileFormatChooser *self = user_data;
+
+ self->priv->size_changed_event = 0;
+ g_signal_emit_by_name (self->priv->chooser, "default-size-changed");
+ return FALSE;
+}
+
+static void
+expander_unmap_cb (GtkWidget *widget,
+ gpointer user_data)
+{
+ EggFileFormatChooser *self = user_data;
+
+ if (self->priv->size_changed_event == 0)
+ self->priv->size_changed_event = gdk_threads_add_idle (emit_default_size_changed, self);
+}
+
+static void
+egg_file_format_chooser_init (EggFileFormatChooser *self)
+{
+ GtkWidget *scroller;
+ GtkWidget *view;
+
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *cell;
+ GtkTreeIter iter;
+
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, EGG_TYPE_FILE_FORMAT_CHOOSER,
+ EggFileFormatChooserPrivate);
+
+ self->priv->size_changed_event = 0;
+
+/* file filters */
+
+ self->priv->all_files = g_object_ref_sink (gtk_file_filter_new ());
+ gtk_file_filter_set_name (self->priv->all_files, _("All Files"));
+ self->priv->supported_files = egg_file_format_filter_new (_("All Supported Files"), FALSE);
+
+/* tree model */
+
+ self->priv->model = gtk_tree_store_new (7, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ GTK_TYPE_FILE_FILTER, G_TYPE_POINTER, G_TYPE_POINTER);
+
+ gtk_tree_store_append (self->priv->model, &iter, NULL);
+ gtk_tree_store_set (self->priv->model, &iter,
+ MODEL_COLUMN_NAME, _("By Extension"),
+ MODEL_COLUMN_FILTER, self->priv->supported_files,
+ MODEL_COLUMN_ID, 0,
+ -1);
+
+/* tree view */
+
+ view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (self->priv->model));
+ self->priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
+ gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
+
+/* file format column */
+
+ column = gtk_tree_view_column_new ();
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_column_set_title (column, _("File Format"));
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+ cell = gtk_cell_renderer_pixbuf_new ();
+ gtk_tree_view_column_pack_start (column, cell, FALSE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "icon-name", MODEL_COLUMN_ICON,
+ NULL);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_tree_view_column_pack_start (column, cell, TRUE);
+ gtk_tree_view_column_set_attributes (column, cell,
+ "text", MODEL_COLUMN_NAME,
+ NULL);
+
+/* extensions column */
+
+ column = gtk_tree_view_column_new_with_attributes (
+ _("Extension(s)"), gtk_cell_renderer_text_new (),
+ "text", MODEL_COLUMN_EXTENSIONS, NULL);
+ gtk_tree_view_column_set_expand (column, FALSE);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
+
+/* selection */
+
+ gtk_tree_selection_set_mode (self->priv->selection, GTK_SELECTION_BROWSE);
+ g_signal_connect (self->priv->selection, "changed",
+ G_CALLBACK (selection_changed_cb), self);
+ self->priv->idle_hack = g_idle_add (select_default_file_format, self);
+
+/* scroller */
+
+ scroller = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
+ GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
+ GTK_SHADOW_IN);
+ gtk_widget_set_size_request (scroller, -1, 150);
+ gtk_container_add (GTK_CONTAINER (scroller), view);
+ gtk_widget_show_all (scroller);
+
+ gtk_container_add (GTK_CONTAINER (self), scroller);
+
+ g_signal_connect_after (scroller, "unmap", G_CALLBACK (expander_unmap_cb), self);
+}
+
+static void
+reset_model (EggFileFormatChooser *self)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (self->priv->model);
+ GtkTreeIter iter;
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ do
+ {
+ GDestroyNotify destroy = NULL;
+ gpointer data = NULL;
+
+ gtk_tree_model_get (model, &iter,
+ MODEL_COLUMN_DESTROY, &destroy,
+ MODEL_COLUMN_DATA, &data,
+ -1);
+
+ if (destroy)
+ destroy (data);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ gtk_tree_store_clear (self->priv->model);
+}
+
+static void
+egg_file_format_chooser_dispose (GObject *obj)
+{
+ EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (obj);
+
+ if (NULL != self)
+ {
+ if (self->priv->idle_hack)
+ {
+ g_source_remove (self->priv->idle_hack);
+ self->priv->idle_hack = 0;
+ }
+ if (self->priv->size_changed_event != 0)
+ {
+ g_source_remove (self->priv->size_changed_event);
+ self->priv->size_changed_event = 0;
+ }
+ }
+
+ G_OBJECT_CLASS (egg_file_format_chooser_parent_class)->dispose (obj);
+}
+
+static void
+egg_file_format_chooser_finalize (GObject *obj)
+{
+ EggFileFormatChooser *self = EGG_FILE_FORMAT_CHOOSER (obj);
+
+ if (NULL != self)
+ {
+ if (self->priv->model)
+ {
+ reset_model (self);
+
+ g_object_unref (self->priv->model);
+ self->priv->model = NULL;
+
+ g_object_unref (self->priv->all_files);
+ self->priv->all_files = NULL;
+ }
+ }
+
+ G_OBJECT_CLASS (egg_file_format_chooser_parent_class)->finalize (obj);
+}
+
+static void
+filter_changed_cb (GObject *object,
+ GParamSpec *spec,
+ gpointer data)
+{
+ EggFileFormatChooser *self;
+
+ GtkFileFilter *current_filter;
+ GtkFileFilter *format_filter;
+
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeIter parent;
+
+ self = EGG_FILE_FORMAT_CHOOSER (data);
+
+ format_filter = NULL;
+ current_filter = gtk_file_chooser_get_filter (GTK_FILE_CHOOSER (object));
+ model = GTK_TREE_MODEL (self->priv->model);
+
+ if (gtk_tree_selection_get_selected (self->priv->selection, &model, &iter))
+ {
+ while (gtk_tree_model_iter_parent (model, &parent, &iter))
+ iter = parent;
+
+ gtk_tree_model_get (model, &iter,
+ MODEL_COLUMN_FILTER,
+ &format_filter, -1);
+ g_object_unref (format_filter);
+ }
+
+ if (current_filter && current_filter != format_filter &&
+ gtk_tree_model_get_iter_first (model, &iter))
+ {
+ if (current_filter == self->priv->all_files)
+ format_filter = current_filter;
+ else
+ {
+ format_filter = NULL;
+
+ do
+ {
+ gtk_tree_model_get (model, &iter,
+ MODEL_COLUMN_FILTER,
+ &format_filter, -1);
+ g_object_unref (format_filter);
+
+ if (format_filter == current_filter)
+ break;
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ if (format_filter)
+ gtk_tree_selection_select_iter (self->priv->selection, &iter);
+ }
+}
+
+/* Shows an error dialog set as transient for the specified window */
+static void
+error_message_with_parent (GtkWindow *parent,
+ const char *msg,
+ const char *detail)
+{
+ gboolean first_call = TRUE;
+ GtkWidget *dialog;
+
+ if (first_call)
+ {
+ g_warning ("%s: Merge with the code in Gtk{File,Recent}ChooserDefault.", G_STRLOC);
+ first_call = FALSE;
+ }
+
+ dialog = gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s",
+ msg);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", detail);
+
+ if (gtk_window_get_group (parent))
+ gtk_window_group_add_window (gtk_window_get_group (parent), GTK_WINDOW (dialog));
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+/* Returns a toplevel GtkWindow, or NULL if none */
+static GtkWindow *
+get_toplevel (GtkWidget *widget)
+{
+ GtkWidget *toplevel;
+
+ toplevel = gtk_widget_get_toplevel (widget);
+ if (!gtk_widget_is_toplevel (toplevel))
+ return NULL;
+ else
+ return GTK_WINDOW (toplevel);
+}
+
+/* Shows an error dialog for the file chooser */
+static void
+error_message (EggFileFormatChooser *impl,
+ const char *msg,
+ const char *detail)
+{
+ error_message_with_parent (get_toplevel (GTK_WIDGET (impl)), msg, detail);
+}
+
+static void
+chooser_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer data)
+{
+ EggFileFormatChooser *self;
+ gchar *filename, *basename;
+ gchar *message;
+ guint format;
+
+ self = EGG_FILE_FORMAT_CHOOSER (data);
+
+ if (EGG_IS_POSITIVE_RESPONSE (response_id))
+ {
+ filename = gtk_file_chooser_get_filename (self->priv->chooser);
+ basename = g_filename_display_basename (filename);
+ g_free (filename);
+
+ format = egg_file_format_chooser_get_format (self, basename);
+ g_print ("%s: %s - %d\n", G_STRFUNC, basename, format);
+
+ if (0 == format)
+ {
+
+ message = g_strdup_printf (
+ _("The program was not able to find out the file format "
+ "you want to use for `%s'. Please make sure to use a "
+ "known extension for that file or manually choose a "
+ "file format from the list below."),
+ basename);
+
+ error_message (self,
+ _("File format not recognized"),
+ message);
+
+ g_free (message);
+
+ g_signal_stop_emission_by_name (dialog, "response");
+ }
+ else
+ {
+ filename = egg_file_format_chooser_append_extension (self, basename, format);
+
+ if (strcmp (filename, basename))
+ {
+ gtk_file_chooser_set_current_name (self->priv->chooser, filename);
+ g_signal_stop_emission_by_name (dialog, "response");
+ }
+
+ g_free (filename);
+ }
+
+ g_free (basename);
+ }
+
+}
+
+static void
+egg_file_format_chooser_realize (GtkWidget *widget)
+{
+ EggFileFormatChooser *self;
+ GtkWidget *parent;
+
+ GtkFileFilter *filter;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ GTK_WIDGET_CLASS (egg_file_format_chooser_parent_class)->realize (widget);
+
+ self = EGG_FILE_FORMAT_CHOOSER (widget);
+
+ g_return_if_fail (NULL == self->priv->chooser);
+
+ parent = gtk_widget_get_parent (widget);
+ while ((parent != NULL) && !GTK_IS_FILE_CHOOSER (parent))
+ parent = gtk_widget_get_parent (parent);
+
+ self->priv->chooser = GTK_FILE_CHOOSER (parent);
+
+ g_return_if_fail (GTK_IS_FILE_CHOOSER (self->priv->chooser));
+ g_return_if_fail (gtk_file_chooser_get_action (self->priv->chooser) ==
+ GTK_FILE_CHOOSER_ACTION_SAVE);
+
+ g_object_ref (self->priv->chooser);
+
+ g_signal_connect (self->priv->chooser, "notify::filter",
+ G_CALLBACK (filter_changed_cb), self);
+ gtk_file_chooser_add_filter (self->priv->chooser, self->priv->all_files);
+
+ model = GTK_TREE_MODEL (self->priv->model);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+ gtk_file_chooser_add_filter (self->priv->chooser, filter);
+ g_object_unref (filter);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ gtk_file_chooser_set_filter (self->priv->chooser,
+ self->priv->supported_files);
+
+ if (GTK_IS_DIALOG (self->priv->chooser))
+ g_signal_connect (self->priv->chooser, "response",
+ G_CALLBACK (chooser_response_cb), self);
+}
+
+static void
+egg_file_format_chooser_unrealize (GtkWidget *widget)
+{
+ EggFileFormatChooser *self;
+
+ GtkFileFilter *filter;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+
+ GTK_WIDGET_CLASS (egg_file_format_chooser_parent_class)->unrealize (widget);
+
+ self = EGG_FILE_FORMAT_CHOOSER (widget);
+ model = GTK_TREE_MODEL (self->priv->model);
+
+ g_signal_handlers_disconnect_by_func (self->priv->chooser,
+ filter_changed_cb, self);
+ g_signal_handlers_disconnect_by_func (self->priv->chooser,
+ chooser_response_cb, self);
+
+ if (gtk_tree_model_get_iter_first (model, &iter))
+ {
+ do
+ {
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_FILTER, &filter, -1);
+ gtk_file_chooser_remove_filter (self->priv->chooser, filter);
+ g_object_unref (filter);
+ }
+ while (gtk_tree_model_iter_next (model, &iter));
+ }
+
+ gtk_file_chooser_remove_filter (self->priv->chooser, self->priv->all_files);
+ g_object_unref (self->priv->chooser);
+}
+
+static void
+egg_file_format_chooser_class_init (EggFileFormatChooserClass *cls)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (cls);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (cls);
+
+ g_type_class_add_private (cls, sizeof (EggFileFormatChooserPrivate));
+
+ object_class->dispose = egg_file_format_chooser_dispose;
+ object_class->finalize = egg_file_format_chooser_finalize;
+
+ widget_class->realize = egg_file_format_chooser_realize;
+ widget_class->unrealize = egg_file_format_chooser_unrealize;
+
+ signals[SIGNAL_SELECTION_CHANGED] = g_signal_new (
+ "selection-changed", EGG_TYPE_FILE_FORMAT_CHOOSER, G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (EggFileFormatChooserClass, selection_changed),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+}
+
+GtkWidget*
+egg_file_format_chooser_new (void)
+{
+ return g_object_new (EGG_TYPE_FILE_FORMAT_CHOOSER, NULL);
+}
+
+static guint
+egg_file_format_chooser_add_format_impl (EggFileFormatChooser *self,
+ guint parent,
+ const gchar *name,
+ const gchar *icon,
+ const gchar *extensions)
+{
+ EggFileFormatSearch search;
+ GtkFileFilter *filter;
+ GtkTreeIter iter;
+
+ search.success = FALSE;
+ search.format = parent;
+ filter = NULL;
+
+ if (parent > 0)
+ {
+ gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->model),
+ find_by_format, &search);
+ g_return_val_if_fail (search.success, -1);
+ }
+ else
+ filter = egg_file_format_filter_new (name, TRUE);
+
+ gtk_tree_store_append (self->priv->model, &iter,
+ parent > 0 ? &search.iter : NULL);
+
+ gtk_tree_store_set (self->priv->model, &iter,
+ MODEL_COLUMN_ID, ++self->priv->last_id,
+ MODEL_COLUMN_EXTENSIONS, extensions,
+ MODEL_COLUMN_FILTER, filter,
+ MODEL_COLUMN_NAME, name,
+ MODEL_COLUMN_ICON, icon,
+ -1);
+
+ if (extensions)
+ {
+ if (parent > 0)
+ gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model), &search.iter,
+ MODEL_COLUMN_FILTER, &filter, -1);
+
+ egg_file_format_filter_add_extensions (self->priv->supported_files, extensions);
+ egg_file_format_filter_add_extensions (filter, extensions);
+
+ if (parent > 0)
+ g_object_unref (filter);
+ }
+
+ return self->priv->last_id;
+}
+
+guint
+egg_file_format_chooser_add_format (EggFileFormatChooser *self,
+ guint parent,
+ const gchar *name,
+ const gchar *icon,
+ ...)
+{
+ GString *buffer = NULL;
+ const gchar* extptr;
+ va_list extensions;
+ guint id;
+
+ g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), 0);
+ g_return_val_if_fail (NULL != name, 0);
+
+ va_start (extensions, icon);
+
+ while (NULL != (extptr = va_arg (extensions, const gchar*)))
+ {
+ if (NULL == buffer)
+ buffer = g_string_new (NULL);
+ else
+ g_string_append (buffer, ", ");
+
+ g_string_append (buffer, extptr);
+ }
+
+ va_end (extensions);
+
+ id = egg_file_format_chooser_add_format_impl (self, parent, name, icon,
+ buffer ? buffer->str : NULL);
+
+ if (buffer)
+ g_string_free (buffer, TRUE);
+
+ return id;
+}
+
+static gchar*
+get_icon_name (const gchar *mime_type)
+{
+ static gboolean first_call = TRUE;
+ gchar *name = NULL;
+ gchar *s;
+
+ if (first_call)
+ {
+ g_warning ("%s: Replace by g_content_type_get_icon "
+ "when GVFS is merged into GLib.", G_STRLOC);
+ first_call = FALSE;
+ }
+
+ if (mime_type)
+ {
+ name = g_strconcat ("mate-mime-", mime_type, NULL);
+
+ for(s = name; *s; ++s)
+ {
+ if (!isalpha (*s) || !isascii (*s))
+ *s = '-';
+ }
+ }
+
+ if (!name ||
+ !gtk_icon_theme_has_icon (gtk_icon_theme_get_default (), name))
+ {
+ g_free (name);
+ name = g_strdup ("mate-mime-image");
+ }
+
+ return name;
+}
+
+void
+egg_file_format_chooser_add_pixbuf_formats (EggFileFormatChooser *self,
+ guint parent G_GNUC_UNUSED,
+ guint **formats)
+{
+ GSList *pixbuf_formats = NULL;
+ GSList *iter;
+ gint i;
+
+ g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+ pixbuf_formats = gdk_pixbuf_get_formats ();
+
+ if (formats)
+ *formats = g_new0 (guint, g_slist_length (pixbuf_formats) + 1);
+
+ for(iter = pixbuf_formats, i = 0; iter; iter = iter->next, ++i)
+ {
+ GdkPixbufFormat *format = iter->data;
+
+ gchar *description, *name, *extensions, *icon;
+ gchar **mime_types, **extension_list;
+ guint id;
+
+ if (gdk_pixbuf_format_is_disabled (format) ||
+ !gdk_pixbuf_format_is_writable (format))
+ continue;
+
+ mime_types = gdk_pixbuf_format_get_mime_types (format);
+ icon = get_icon_name (mime_types[0]);
+ g_strfreev (mime_types);
+
+ extension_list = gdk_pixbuf_format_get_extensions (format);
+ extensions = g_strjoinv (", ", extension_list);
+ g_strfreev (extension_list);
+
+ description = gdk_pixbuf_format_get_description (format);
+ name = gdk_pixbuf_format_get_name (format);
+
+ id = egg_file_format_chooser_add_format_impl (self, parent, description,
+ icon, extensions);
+
+ g_free (description);
+ g_free (extensions);
+ g_free (icon);
+
+ egg_file_format_chooser_set_format_data (self, id, name, g_free);
+
+ if (formats)
+ *formats[i] = id;
+ }
+
+ g_slist_free (pixbuf_formats);
+}
+
+void
+egg_file_format_chooser_remove_format (EggFileFormatChooser *self,
+ guint format)
+{
+ GDestroyNotify destroy = NULL;
+ gpointer data = NULL;
+
+ EggFileFormatSearch search;
+ GtkFileFilter *filter;
+ GtkTreeModel *model;
+
+ g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+ search.success = FALSE;
+ search.format = format;
+
+ model = GTK_TREE_MODEL (self->priv->model);
+ gtk_tree_model_foreach (model, find_by_format, &search);
+
+ g_return_if_fail (search.success);
+
+ gtk_tree_model_get (model, &search.iter,
+ MODEL_COLUMN_FILTER, &filter,
+ MODEL_COLUMN_DESTROY, &destroy,
+ MODEL_COLUMN_DATA, &data,
+ -1);
+
+ if (destroy)
+ destroy (data);
+
+ if (filter)
+ {
+ if (self->priv->chooser)
+ gtk_file_chooser_remove_filter (self->priv->chooser, filter);
+
+ g_object_unref (filter);
+ }
+ else
+ g_warning ("TODO: Remove extensions from parent filter");
+
+ gtk_tree_store_remove (self->priv->model, &search.iter);
+}
+
+void
+egg_file_format_chooser_set_format (EggFileFormatChooser *self,
+ guint format)
+{
+ EggFileFormatSearch search;
+
+ GtkTreeModel *model;
+ GtkTreePath *path;
+ GtkTreeView *view;
+
+ g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+ search.success = FALSE;
+ search.format = format;
+
+ model = GTK_TREE_MODEL (self->priv->model);
+ gtk_tree_model_foreach (model, find_by_format, &search);
+
+ g_return_if_fail (search.success);
+
+ path = gtk_tree_model_get_path (model, &search.iter);
+ view = gtk_tree_selection_get_tree_view (self->priv->selection);
+
+ gtk_tree_view_expand_to_path (view, path);
+ gtk_tree_selection_unselect_all (self->priv->selection);
+ gtk_tree_selection_select_path (self->priv->selection, path);
+
+ gtk_tree_path_free (path);
+
+ if (self->priv->idle_hack > 0)
+ {
+ g_source_remove (self->priv->idle_hack);
+ self->priv->idle_hack = 0;
+ }
+}
+
+guint
+egg_file_format_chooser_get_format (EggFileFormatChooser *self,
+ const gchar *filename)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ guint format = 0;
+
+ g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), -1);
+
+ if (gtk_tree_selection_get_selected (self->priv->selection, &model, &iter))
+ gtk_tree_model_get (model, &iter, MODEL_COLUMN_ID, &format, -1);
+
+ if (0 == format && NULL != filename)
+ {
+ EggFileFormatSearch search;
+
+ search.extension = strrchr(filename, '.');
+ search.success = FALSE;
+
+ if (search.extension++)
+ gtk_tree_model_foreach (model, find_by_extension, &search);
+ if (search.success)
+ format = search.format;
+ }
+
+ return format;
+}
+
+void
+egg_file_format_chooser_set_format_data (EggFileFormatChooser *self,
+ guint format,
+ gpointer data,
+ GDestroyNotify destroy)
+{
+ EggFileFormatSearch search;
+
+ g_return_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self));
+
+ search.success = FALSE;
+ search.format = format;
+
+ gtk_tree_model_foreach (GTK_TREE_MODEL (self->priv->model),
+ find_by_format, &search);
+
+ g_return_if_fail (search.success);
+
+ gtk_tree_store_set (self->priv->model, &search.iter,
+ MODEL_COLUMN_DESTROY, destroy,
+ MODEL_COLUMN_DATA, data,
+ -1);
+}
+
+gpointer
+egg_file_format_chooser_get_format_data (EggFileFormatChooser *self,
+ guint format)
+{
+ EggFileFormatSearch search;
+ gpointer data = NULL;
+ GtkTreeModel *model;
+
+ g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), NULL);
+
+ search.success = FALSE;
+ search.format = format;
+
+ model = GTK_TREE_MODEL (self->priv->model);
+ gtk_tree_model_foreach (model, find_by_format, &search);
+
+ g_return_val_if_fail (search.success, NULL);
+
+ gtk_tree_model_get (model, &search.iter,
+ MODEL_COLUMN_DATA, &data,
+ -1);
+ return data;
+}
+
+gchar*
+egg_file_format_chooser_append_extension (EggFileFormatChooser *self,
+ const gchar *filename,
+ guint format)
+{
+ EggFileFormatSearch search;
+ GtkTreeModel *model;
+ GtkTreeIter child;
+
+ gchar *extensions;
+ gchar *result;
+
+ g_return_val_if_fail (EGG_IS_FILE_FORMAT_CHOOSER (self), NULL);
+ g_return_val_if_fail (NULL != filename, NULL);
+
+ if (0 == format)
+ format = egg_file_format_chooser_get_format (self, NULL);
+
+ if (0 == format)
+ {
+ g_warning ("%s: No file format selected. Cannot append extension.", G_STRFUNC);
+ return NULL;
+ }
+
+ search.success = FALSE;
+ search.format = format;
+
+ model = GTK_TREE_MODEL (self->priv->model);
+ gtk_tree_model_foreach (model, find_by_format, &search);
+
+ g_return_val_if_fail (search.success, NULL);
+
+ gtk_tree_model_get (model, &search.iter,
+ MODEL_COLUMN_EXTENSIONS, &extensions,
+ -1);
+
+ if (NULL == extensions &&
+ gtk_tree_model_iter_nth_child (model, &child, &search.iter, 0))
+ {
+ gtk_tree_model_get (model, &child,
+ MODEL_COLUMN_EXTENSIONS, &extensions,
+ -1);
+ }
+
+ if (NULL == extensions)
+ {
+ g_warning ("%s: File format %d doesn't provide file extensions. "
+ "Cannot append extension.", G_STRFUNC, format);
+ return NULL;
+ }
+
+ if (accept_filename (extensions, filename))
+ result = g_strdup (filename);
+ else
+ result = g_strconcat (filename, ".", extensions, NULL);
+
+ g_assert (NULL == strchr(extensions, ','));
+ g_free (extensions);
+ return result;
+}
+
+void
+egg_file_format_chooser_emit_size_changed (EggFileFormatChooser *self)
+{
+ if (self->priv->size_changed_event == 0)
+ self->priv->size_changed_event = gdk_threads_add_idle (emit_default_size_changed, self);
+}
+
+/* vim: set sw=2 sta et: */
diff --git a/src/eggfileformatchooser.h b/src/eggfileformatchooser.h
new file mode 100644
index 0000000..6c78891
--- /dev/null
+++ b/src/eggfileformatchooser.h
@@ -0,0 +1,85 @@
+/* EggFileFormatChooser
+ * Copyright (C) 2007 Mathias Hasselmann
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef __EGG_FILE_FORMAT_CHOOSER_H__
+#define __EGG_FILE_FORMAT_CHOOSER_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_FILE_FORMAT_CHOOSER (egg_file_format_chooser_get_type())
+#define EGG_FILE_FORMAT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooser))
+#define EGG_FILE_FORMAT_CHOOSER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST(klass, EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooserClass))
+#define EGG_IS_FILE_FORMAT_CHOOSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE(obj, EGG_TYPE_FILE_FORMAT_CHOOSER))
+#define EGG_IS_FILE_FORMAT_CHOOSER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE(obj, EGG_TYPE_FILE_FORMAT_CHOOSER))
+#define EGG_FILE_FORMAT_CHOOSER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), EGG_TYPE_FILE_FORMAT_CHOOSER, EggFileFormatChooserClass))
+
+typedef struct _EggFileFormatChooser EggFileFormatChooser;
+typedef struct _EggFileFormatChooserClass EggFileFormatChooserClass;
+typedef struct _EggFileFormatChooserPrivate EggFileFormatChooserPrivate;
+
+struct _EggFileFormatChooser
+{
+ GtkExpander parent;
+ EggFileFormatChooserPrivate *priv;
+};
+
+struct _EggFileFormatChooserClass
+{
+ GtkExpanderClass parent;
+
+ void (*selection_changed)(EggFileFormatChooser *self);
+};
+
+GType egg_file_format_chooser_get_type (void) G_GNUC_CONST;
+GtkWidget* egg_file_format_chooser_new (void);
+
+guint egg_file_format_chooser_add_format (EggFileFormatChooser *self,
+ guint parent,
+ const gchar *name,
+ const gchar *icon,
+ ...) G_GNUC_NULL_TERMINATED;
+void egg_file_format_chooser_add_pixbuf_formats (EggFileFormatChooser *self,
+ guint parent,
+ guint **formats);
+void egg_file_format_chooser_remove_format (EggFileFormatChooser *self,
+ guint format);
+
+void egg_file_format_chooser_set_format (EggFileFormatChooser *self,
+ guint format);
+guint egg_file_format_chooser_get_format (EggFileFormatChooser *self,
+ const gchar *filename);
+
+void egg_file_format_chooser_set_format_data (EggFileFormatChooser *self,
+ guint format,
+ gpointer data,
+ GDestroyNotify destroy);
+gpointer egg_file_format_chooser_get_format_data (EggFileFormatChooser *self,
+ guint format);
+
+gchar* egg_file_format_chooser_append_extension (EggFileFormatChooser *self,
+ const gchar *filename,
+ guint format);
+void egg_file_format_chooser_emit_size_changed (EggFileFormatChooser *self);
+
+G_END_DECLS
+
+#endif /* __EGG_FILE_FORMAT_CHOOSER_H__ */
+
+/* vim: set sw=2 sta et: */
diff --git a/src/eggtreemultidnd.c b/src/eggtreemultidnd.c
new file mode 100644
index 0000000..5bf4698
--- /dev/null
+++ b/src/eggtreemultidnd.c
@@ -0,0 +1,459 @@
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ * Author: Paolo Bacchilega
+ */
+
+/* eggtreemultidnd.c
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#include <string.h>
+#include <gtk/gtk.h>
+#include "eggtreemultidnd.h"
+
+#define EGG_TREE_MULTI_DND_STRING "EggTreeMultiDndString"
+
+static GtkTargetEntry target_table[] = {
+ { "XdndDirectSave0", 0, 0 },
+ { "XdndFileRoller0", 0, 1 }
+};
+
+typedef struct
+{
+ guint pressed_button;
+ gint x;
+ gint y;
+ guint motion_notify_handler;
+ guint button_release_handler;
+ guint drag_data_get_handler;
+ GSList *event_list;
+ gboolean pending_event;
+} EggTreeMultiDndData;
+
+
+GType
+egg_tree_multi_drag_source_get_type (void)
+{
+ static GType our_type = 0;
+
+ if (!our_type)
+ {
+ static const GTypeInfo our_info =
+ {
+ sizeof (EggTreeMultiDragSourceIface), /* class_size */
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ NULL,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ 0,
+ 0, /* n_preallocs */
+ NULL
+ };
+
+ our_type = g_type_register_static (G_TYPE_INTERFACE,
+ "EggTreeMultiDragSource",
+ &our_info,
+ 0);
+ }
+
+ return our_type;
+}
+
+
+/**
+ * egg_tree_multi_drag_source_row_draggable:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row on which user is initiating a drag
+ *
+ * Asks the #EggTreeMultiDragSource whether a particular row can be used as
+ * the source of a DND operation. If the source doesn't implement
+ * this interface, the row is assumed draggable.
+ *
+ * Return value: %TRUE if the row can be dragged
+ **/
+gboolean
+egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
+ GList *path_list)
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+ g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+ g_return_val_if_fail (iface->row_draggable != NULL, FALSE);
+ g_return_val_if_fail (path_list != NULL, FALSE);
+
+ if (iface->row_draggable)
+ return (* iface->row_draggable) (drag_source, path_list);
+ else
+ return TRUE;
+}
+
+
+/**
+ * egg_tree_multi_drag_source_drag_data_delete:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was being dragged
+ *
+ * Asks the #EggTreeMultiDragSource to delete the row at @path, because
+ * it was moved somewhere else via drag-and-drop. Returns %FALSE
+ * if the deletion fails because @path no longer exists, or for
+ * some model-specific reason. Should robustly handle a @path no
+ * longer found in the model!
+ *
+ * Return value: %TRUE if the row was successfully deleted
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
+ GList *path_list)
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+ g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+ g_return_val_if_fail (iface->drag_data_delete != NULL, FALSE);
+ g_return_val_if_fail (path_list != NULL, FALSE);
+
+ return (* iface->drag_data_delete) (drag_source, path_list);
+}
+
+
+/**
+ * egg_tree_multi_drag_source_drag_data_get:
+ * @drag_source: a #EggTreeMultiDragSource
+ * @path: row that was dragged
+ * @selection_data: a #EggSelectionData to fill with data from the dragged row
+ *
+ * Asks the #EggTreeMultiDragSource to fill in @selection_data with a
+ * representation of the row at @path. @selection_data->target gives
+ * the required type of the data. Should robustly handle a @path no
+ * longer found in the model!
+ *
+ * Return value: %TRUE if data of the required type was provided
+ **/
+gboolean
+egg_tree_multi_drag_source_drag_data_get (EggTreeMultiDragSource *drag_source,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ GList *path_list)
+{
+ EggTreeMultiDragSourceIface *iface = EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE (drag_source);
+
+ g_return_val_if_fail (EGG_IS_TREE_MULTI_DRAG_SOURCE (drag_source), FALSE);
+ g_return_val_if_fail (iface->drag_data_get != NULL, FALSE);
+ g_return_val_if_fail (path_list != NULL, FALSE);
+ g_return_val_if_fail (selection_data != NULL, FALSE);
+
+ return (* iface->drag_data_get) (drag_source, context, selection_data, path_list);
+}
+
+
+static void
+stop_drag_check (GtkWidget *widget)
+{
+ EggTreeMultiDndData *priv_data;
+ GSList *l;
+
+ priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+ for (l = priv_data->event_list; l != NULL; l = l->next)
+ gdk_event_free (l->data);
+
+ g_slist_free (priv_data->event_list);
+ priv_data->event_list = NULL;
+ priv_data->pending_event = FALSE;
+
+ if (priv_data->motion_notify_handler) {
+ g_signal_handler_disconnect (widget, priv_data->motion_notify_handler);
+ priv_data->motion_notify_handler = 0;
+ }
+ if (priv_data->button_release_handler) {
+ g_signal_handler_disconnect (widget, priv_data->button_release_handler);
+ priv_data->button_release_handler = 0;
+ }
+}
+
+
+static gboolean
+egg_tree_multi_drag_button_release_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ EggTreeMultiDndData *priv_data;
+ GSList *l;
+
+ priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+ for (l = priv_data->event_list; l != NULL; l = l->next)
+ gtk_propagate_event (widget, l->data);
+
+ stop_drag_check (widget);
+
+ return FALSE;
+}
+
+
+static void
+selection_foreach (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ GList **list_ptr;
+
+ list_ptr = (GList **) data;
+
+ *list_ptr = g_list_prepend (*list_ptr, gtk_tree_row_reference_new (model, path));
+}
+
+
+static void
+path_list_free (GList *path_list)
+{
+ g_list_foreach (path_list, (GFunc) gtk_tree_row_reference_free, NULL);
+ g_list_free (path_list);
+}
+
+
+static void
+set_context_data (GdkDragContext *context,
+ GList *path_list)
+{
+ g_object_set_data_full (G_OBJECT (context),
+ "egg-tree-view-multi-source-row",
+ path_list,
+ (GDestroyNotify) path_list_free);
+}
+
+
+static GList *
+get_context_data (GdkDragContext *context)
+{
+ return g_object_get_data (G_OBJECT (context),
+ "egg-tree-view-multi-source-row");
+}
+
+
+static gboolean
+egg_tree_multi_drag_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ GtkTreeView *tree_view;
+ GtkTreeModel *model;
+ GList *path_list;
+
+ tree_view = GTK_TREE_VIEW (widget);
+ model = gtk_tree_view_get_model (tree_view);
+ if (model == NULL)
+ return FALSE;
+
+ path_list = get_context_data (context);
+ if (path_list == NULL)
+ return FALSE;
+
+ /* We can implement the GTK_TREE_MODEL_ROW target generically for
+ * any model; for DragSource models there are some other targets
+ * we also support.
+ */
+
+ if (! EGG_IS_TREE_MULTI_DRAG_SOURCE (model))
+ return FALSE;
+
+ return egg_tree_multi_drag_source_drag_data_get (EGG_TREE_MULTI_DRAG_SOURCE (model),
+ context,
+ selection_data,
+ path_list);
+}
+
+
+static gboolean
+egg_tree_multi_drag_motion_event (GtkWidget *widget,
+ GdkEventMotion *event,
+ gpointer data)
+{
+ EggTreeMultiDndData *priv_data;
+
+ priv_data = g_object_get_data (G_OBJECT (widget), EGG_TREE_MULTI_DND_STRING);
+
+ if (gtk_drag_check_threshold (widget,
+ priv_data->x,
+ priv_data->y,
+ event->x,
+ event->y))
+ {
+ GList *path_list = NULL;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GdkDragContext *context;
+
+ stop_drag_check (widget);
+
+ selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
+ gtk_tree_selection_selected_foreach (selection, selection_foreach, &path_list);
+ if (path_list == NULL)
+ return FALSE;
+
+ path_list = g_list_reverse (path_list);
+
+ model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget));
+ if (egg_tree_multi_drag_source_row_draggable (EGG_TREE_MULTI_DRAG_SOURCE (model), path_list))
+ {
+ GtkTargetList *target_list = gtk_target_list_new (target_table,
+ G_N_ELEMENTS (target_table));
+
+ context = gtk_drag_begin (widget,
+ target_list,
+ GDK_ACTION_COPY,
+ priv_data->pressed_button,
+ (GdkEvent*)event);
+ set_context_data (context, path_list);
+ gtk_drag_set_icon_default (context);
+
+ gtk_target_list_unref (target_list);
+ }
+ else
+ {
+ path_list_free (path_list);
+ }
+ }
+
+ return TRUE;
+}
+
+
+static gboolean
+egg_tree_multi_drag_button_press_event (GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer data)
+{
+ GtkTreeView *tree_view;
+ GtkTreePath *path = NULL;
+ GtkTreeViewColumn *column = NULL;
+ gint cell_x, cell_y;
+ GtkTreeSelection *selection;
+ EggTreeMultiDndData *priv_data;
+
+ if (event->window != gtk_tree_view_get_bin_window (GTK_TREE_VIEW (widget)))
+ return FALSE;
+
+ if (event->button == 3)
+ return FALSE;
+
+ tree_view = GTK_TREE_VIEW (widget);
+ priv_data = g_object_get_data (G_OBJECT (tree_view), EGG_TREE_MULTI_DND_STRING);
+ if (priv_data == NULL)
+ {
+ priv_data = g_new0 (EggTreeMultiDndData, 1);
+ priv_data->pending_event = FALSE;
+ g_object_set_data (G_OBJECT (tree_view),
+ EGG_TREE_MULTI_DND_STRING,
+ priv_data);
+ }
+
+ if (g_slist_find (priv_data->event_list, event))
+ return FALSE;
+
+ if (priv_data->pending_event)
+ {
+ /* save the event to be propagated in order */
+ priv_data->event_list = g_slist_append (priv_data->event_list,
+ gdk_event_copy ((GdkEvent*)event));
+ return TRUE;
+ }
+
+ if (event->type == GDK_2BUTTON_PRESS)
+ return FALSE;
+
+ gtk_tree_view_get_path_at_pos (tree_view,
+ event->x, event->y,
+ &path, &column,
+ &cell_x, &cell_y);
+
+ selection = gtk_tree_view_get_selection (tree_view);
+
+ if (path)
+ {
+ gboolean call_parent = (event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK) ||
+ !gtk_tree_selection_path_is_selected (selection, path) ||
+ event->button != 1);
+
+ if (call_parent)
+ (GTK_WIDGET_GET_CLASS (tree_view))->button_press_event (widget, event);
+
+ if (gtk_tree_selection_path_is_selected (selection, path))
+ {
+ priv_data->pressed_button = event->button;
+ priv_data->x = event->x;
+ priv_data->y = event->y;
+
+ priv_data->pending_event = TRUE;
+ if (!call_parent)
+ priv_data->event_list = g_slist_append (priv_data->event_list,
+ gdk_event_copy ((GdkEvent*)event));
+
+ if (priv_data->motion_notify_handler == 0)
+ {
+ priv_data->motion_notify_handler =
+ g_signal_connect (G_OBJECT (tree_view),
+ "motion_notify_event",
+ G_CALLBACK (egg_tree_multi_drag_motion_event),
+ NULL);
+ }
+
+ if (priv_data->button_release_handler == 0)
+ {
+ priv_data->button_release_handler =
+ g_signal_connect (G_OBJECT (tree_view),
+ "button_release_event",
+ G_CALLBACK (egg_tree_multi_drag_button_release_event),
+ NULL);
+ }
+
+ if (priv_data->drag_data_get_handler == 0)
+ {
+ priv_data->drag_data_get_handler =
+ g_signal_connect (G_OBJECT (tree_view),
+ "drag_data_get",
+ G_CALLBACK (egg_tree_multi_drag_drag_data_get),
+ NULL);
+ }
+ }
+
+ gtk_tree_path_free (path);
+ /* We called the default handler so we don't let the default handler run */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void
+egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view)
+{
+ g_return_if_fail (GTK_IS_TREE_VIEW (tree_view));
+ g_signal_connect (G_OBJECT (tree_view),
+ "button_press_event",
+ G_CALLBACK (egg_tree_multi_drag_button_press_event),
+ NULL);
+}
+
diff --git a/src/eggtreemultidnd.h b/src/eggtreemultidnd.h
new file mode 100644
index 0000000..0e8a790
--- /dev/null
+++ b/src/eggtreemultidnd.h
@@ -0,0 +1,75 @@
+/* eggtreednd.h
+ * Copyright (C) 2001 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_TREE_MULTI_DND_H__
+#define __EGG_TREE_MULTI_DND_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define EGG_TYPE_TREE_MULTI_DRAG_SOURCE (egg_tree_multi_drag_source_get_type ())
+#define EGG_TREE_MULTI_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSource))
+#define EGG_IS_TREE_MULTI_DRAG_SOURCE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE))
+#define EGG_TREE_MULTI_DRAG_SOURCE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), EGG_TYPE_TREE_MULTI_DRAG_SOURCE, EggTreeMultiDragSourceIface))
+
+typedef struct _EggTreeMultiDragSource EggTreeMultiDragSource; /* Dummy typedef */
+typedef struct _EggTreeMultiDragSourceIface EggTreeMultiDragSourceIface;
+
+struct _EggTreeMultiDragSourceIface
+{
+ GTypeInterface g_iface;
+
+ /* VTable - not signals */
+ gboolean (* row_draggable) (EggTreeMultiDragSource *drag_source,
+ GList *path_list);
+
+ gboolean (* drag_data_get) (EggTreeMultiDragSource *drag_source,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ GList *path_list);
+
+ gboolean (* drag_data_delete) (EggTreeMultiDragSource *drag_source,
+ GList *path_list);
+};
+
+GType egg_tree_multi_drag_source_get_type (void) G_GNUC_CONST;
+
+/* Returns whether the given row can be dragged */
+gboolean egg_tree_multi_drag_source_row_draggable (EggTreeMultiDragSource *drag_source,
+ GList *path_list);
+
+/* Deletes the given row, or returns FALSE if it can't */
+gboolean egg_tree_multi_drag_source_drag_data_delete (EggTreeMultiDragSource *drag_source,
+ GList *path_list);
+
+
+/* Fills in selection_data with type selection_data->target based on the row
+ * denoted by path, returns TRUE if it does anything
+ */
+gboolean egg_tree_multi_drag_source_drag_data_get (EggTreeMultiDragSource *drag_source,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ GList *path_list);
+
+void egg_tree_multi_drag_add_drag_support (GtkTreeView *tree_view);
+
+G_END_DECLS
+
+#endif /* __EGG_TREE_MULTI_DND_H__ */
diff --git a/src/file-data.c b/src/file-data.c
new file mode 100644
index 0000000..620accb
--- /dev/null
+++ b/src/file-data.c
@@ -0,0 +1,152 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001-2006 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include "glib-utils.h"
+#include "file-utils.h"
+#include "file-data.h"
+
+
+FileData *
+file_data_new (void)
+{
+ FileData *fdata;
+
+ fdata = g_new0 (FileData, 1);
+ fdata->content_type = NULL;
+ fdata->free_original_path = FALSE;
+ fdata->dir_size = 0;
+
+ return fdata;
+}
+
+
+void
+file_data_free (FileData *fdata)
+{
+ if (fdata == NULL)
+ return;
+ if (fdata->free_original_path)
+ g_free (fdata->original_path);
+ g_free (fdata->full_path);
+ g_free (fdata->name);
+ g_free (fdata->path);
+ g_free (fdata->link);
+ g_free (fdata->list_name);
+ g_free (fdata);
+}
+
+
+FileData *
+file_data_copy (FileData *src)
+{
+ FileData *fdata;
+
+ fdata = g_new0 (FileData, 1);
+
+ fdata->original_path = g_strdup (src->original_path);
+ fdata->free_original_path = TRUE;
+
+ fdata->full_path = g_strdup (src->full_path);
+ fdata->link = g_strdup (src->link);
+ fdata->size = src->size;
+ fdata->modified = src->modified;
+ fdata->name = g_strdup (src->name);
+ fdata->path = g_strdup (src->path);
+ fdata->content_type = src->content_type;
+ fdata->encrypted = src->encrypted;
+ fdata->dir = src->dir;
+ fdata->dir_size = src->dir_size;
+
+ fdata->list_dir = src->list_dir;
+ fdata->list_name = g_strdup (src->list_name);
+
+ return fdata;
+}
+
+
+GType
+file_data_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0)
+ type = g_boxed_type_register_static ("FRFileData", (GBoxedCopyFunc) file_data_copy, (GBoxedFreeFunc) file_data_free);
+
+ return type;
+}
+
+
+void
+file_data_update_content_type (FileData *fdata)
+{
+
+ if (fdata->dir)
+ fdata->content_type = MIME_TYPE_DIRECTORY;
+ else
+ fdata->content_type = get_static_string (g_content_type_guess (fdata->full_path, NULL, 0, NULL));
+}
+
+
+gboolean
+file_data_is_dir (FileData *fdata)
+{
+ return fdata->dir || fdata->list_dir;
+}
+
+
+int
+file_data_compare_by_path (gconstpointer a,
+ gconstpointer b)
+{
+ FileData *data_a = *((FileData **) a);
+ FileData *data_b = *((FileData **) b);
+
+ return strcmp (data_a->full_path, data_b->full_path);
+}
+
+
+int
+find_path_in_file_data_array (GPtrArray *array,
+ const char *path)
+{
+ int l, r, p, cmp = -1;
+ FileData *fd;
+
+ l = 0;
+ r = array->len;
+ while (l < r) {
+ p = l + ((r - l) / 2);
+ fd = (FileData *) g_ptr_array_index (array, p);
+ cmp = strcmp (path, fd->original_path);
+ if (cmp == 0)
+ return p;
+ else if (cmp < 0)
+ r = p;
+ else
+ l = p + 1;
+ }
+
+ return -1;
+}
diff --git a/src/file-data.h b/src/file-data.h
new file mode 100644
index 0000000..cd0147a
--- /dev/null
+++ b/src/file-data.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FILE_DATA_H
+#define FILE_DATA_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <time.h>
+
+typedef struct {
+ char *original_path; /* path read from command line. */
+ char *full_path; /* "/" + original_path. */
+ char *link;
+ goffset size;
+ time_t modified;
+
+ char *name; /* The file name. */
+ char *path; /* The directory. */
+ gboolean encrypted; /* Whether the file is encrypted. */
+ gboolean dir; /* Whether this is a directory listed in the archive */
+ goffset dir_size;
+ const char *content_type;
+
+ /* Additional data. */
+
+ gboolean list_dir; /* Whether this entry is used to show
+ * a directory. */
+ char *list_name; /* The string visualized in the list
+ * view. */
+
+ /* Private data */
+
+ gboolean free_original_path;
+} FileData;
+
+#define FR_TYPE_FILE_DATA (file_data_get_type ())
+
+GType file_data_get_type (void);
+FileData * file_data_new (void);
+FileData * file_data_copy (FileData *src);
+void file_data_free (FileData *fdata);
+void file_data_update_content_type (FileData *fdata);
+gboolean file_data_is_dir (FileData *fdata);
+
+int file_data_compare_by_path (gconstpointer a,
+ gconstpointer b);
+int find_path_in_file_data_array (GPtrArray *array,
+ const char *path);
+
+#endif /* FILE_DATA_H */
diff --git a/src/file-utils.c b/src/file-utils.c
new file mode 100644
index 0000000..9c3bff5
--- /dev/null
+++ b/src/file-utils.c
@@ -0,0 +1,1395 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <mateconf/mateconf-client.h>
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "main.h"
+
+
+#ifndef HAVE_MKDTEMP
+#include "mkdtemp.h"
+#endif
+
+#define BUF_SIZE 4096
+#define FILE_PREFIX "file://"
+#define FILE_PREFIX_L 7
+#define SPECIAL_DIR(x) ((strcmp ((x), "..") == 0) || (strcmp ((x), ".") == 0))
+
+
+gboolean
+uri_exists (const char *uri)
+{
+ GFile *file;
+ gboolean exists;
+
+ if (uri == NULL)
+ return FALSE;
+
+ file = g_file_new_for_uri (uri);
+ exists = g_file_query_exists (file, NULL);
+ g_object_unref (file);
+
+ return exists;
+}
+
+
+static gboolean
+uri_is_filetype (const char *uri,
+ GFileType file_type)
+{
+ gboolean result = FALSE;
+ GFile *file;
+ GFileInfo *info;
+ GError *error = NULL;
+
+ file = g_file_new_for_uri (uri);
+
+ if (! g_file_query_exists (file, NULL)) {
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TYPE, 0, NULL, &error);
+ if (error == NULL) {
+ result = (g_file_info_get_file_type (info) == file_type);
+ }
+ else {
+ g_warning ("Failed to get file type for uri %s: %s", uri, error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (info);
+ g_object_unref (file);
+
+ return result;
+}
+
+
+gboolean
+uri_is_file (const char *uri)
+{
+ return uri_is_filetype (uri, G_FILE_TYPE_REGULAR);
+}
+
+
+gboolean
+uri_is_dir (const char *uri)
+{
+ return uri_is_filetype (uri, G_FILE_TYPE_DIRECTORY);
+}
+
+
+gboolean
+path_is_dir (const char *path)
+{
+ char *uri;
+ gboolean result;
+
+ uri = g_filename_to_uri (path, NULL, NULL);
+ result = uri_is_dir (uri);
+ g_free (uri);
+
+ return result;
+}
+
+gboolean
+uri_is_local (const char *uri)
+{
+ return strncmp (uri, "file://", 7) == 0;
+}
+
+
+gboolean
+dir_is_empty (const char *uri)
+{
+ GFile *file;
+ GFileEnumerator *file_enum;
+ GFileInfo *info;
+ GError *error = NULL;
+ int n = 0;
+
+ file = g_file_new_for_uri (uri);
+
+ if (! g_file_query_exists (file, NULL)) {
+ g_object_unref (file);
+ return TRUE;
+ }
+
+ file_enum = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &error);
+ if (error != NULL) {
+ g_warning ("Failed to enumerate children of %s: %s", uri, error->message);
+ g_error_free (error);
+ g_object_unref (file_enum);
+ g_object_unref (file);
+ return TRUE;
+ }
+
+ while ((n == 0) && ((info = g_file_enumerator_next_file (file_enum, NULL, &error)) != NULL)) {
+ if (error != NULL) {
+ g_warning ("Encountered error while enumerating children of %s (ignoring): %s", uri, error->message);
+ g_error_free (error);
+ }
+ else if (! SPECIAL_DIR (g_file_info_get_name (info)))
+ n++;
+ g_object_unref (info);
+ }
+
+ g_object_unref (file);
+ g_object_unref (file_enum);
+
+ return (n == 0);
+}
+
+
+gboolean
+dir_contains_one_object (const char *uri)
+{
+ GFile *file;
+ GFileEnumerator *file_enum;
+ GFileInfo *info;
+ GError *err = NULL;
+ int n = 0;
+
+ file = g_file_new_for_uri (uri);
+
+ if (! g_file_query_exists (file, NULL)) {
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ file_enum = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &err);
+ if (err != NULL) {
+ g_warning ("Failed to enumerate children of %s: %s", uri, err->message);
+ g_error_free (err);
+ g_object_unref (file_enum);
+ g_object_unref (file);
+ return FALSE;
+ }
+
+ while ((info = g_file_enumerator_next_file (file_enum, NULL, &err)) != NULL) {
+ const char *name;
+
+ if (err != NULL) {
+ g_warning ("Encountered error while enumerating children of %s, ignoring: %s", uri, err->message);
+ g_error_free (err);
+ g_object_unref (info);
+ continue;
+ }
+
+ name = g_file_info_get_name (info);
+ if (strcmp (name, ".") == 0 || strcmp (name, "..") == 0) {
+ g_object_unref (info);
+ continue;
+ }
+
+ g_object_unref (info);
+
+ if (++n > 1)
+ break;
+ }
+
+ g_object_unref (file);
+ g_object_unref (file_enum);
+
+ return (n == 1);
+}
+
+
+char *
+get_dir_content_if_unique (const char *uri)
+{
+ GFile *file;
+ GFileEnumerator *file_enum;
+ GFileInfo *info;
+ GError *err = NULL;
+ char *content_uri = NULL;
+
+ file = g_file_new_for_uri (uri);
+
+ if (! g_file_query_exists (file, NULL)) {
+ g_object_unref (file);
+ return NULL;
+ }
+
+ file_enum = g_file_enumerate_children (file, G_FILE_ATTRIBUTE_STANDARD_NAME, 0, NULL, &err);
+ if (err != NULL) {
+ g_warning ("Failed to enumerate children of %s: %s", uri, err->message);
+ g_error_free (err);
+ return NULL;
+ }
+
+ while ((info = g_file_enumerator_next_file (file_enum, NULL, &err)) != NULL) {
+ const char *name;
+
+ if (err != NULL) {
+ g_warning ("Failed to get info while enumerating children: %s", err->message);
+ g_clear_error (&err);
+ g_object_unref (info);
+ continue;
+ }
+
+ name = g_file_info_get_name (info);
+ if ((strcmp (name, ".") == 0) || (strcmp (name, "..") == 0)) {
+ g_object_unref (info);
+ continue;
+ }
+
+ if (content_uri != NULL) {
+ g_free (content_uri);
+ g_object_unref (info);
+ content_uri = NULL;
+ break;
+ }
+
+ content_uri = build_uri (uri, name, NULL);
+ g_object_unref (info);
+ }
+
+ if (err != NULL) {
+ g_warning ("Failed to get info after enumerating children: %s", err->message);
+ g_clear_error (&err);
+ }
+
+ g_object_unref (file_enum);
+ g_object_unref (file);
+
+ return content_uri;
+}
+
+
+/* Check whether the dirname is contained in filename */
+gboolean
+path_in_path (const char *dirname,
+ const char *filename)
+{
+ int dirname_l, filename_l, separator_position;
+
+ if ((dirname == NULL) || (filename == NULL))
+ return FALSE;
+
+ dirname_l = strlen (dirname);
+ filename_l = strlen (filename);
+
+ if ((dirname_l == filename_l + 1)
+ && (dirname[dirname_l - 1] == '/'))
+ return FALSE;
+
+ if ((filename_l == dirname_l + 1)
+ && (filename[filename_l - 1] == '/'))
+ return FALSE;
+
+ if (dirname[dirname_l - 1] == '/')
+ separator_position = dirname_l - 1;
+ else
+ separator_position = dirname_l;
+
+ return ((filename_l > dirname_l)
+ && (strncmp (dirname, filename, dirname_l) == 0)
+ && (filename[separator_position] == '/'));
+}
+
+
+goffset
+get_file_size (const char *uri)
+{
+ goffset size = 0;
+ GFile *file;
+ GFileInfo *info;
+ GError *err = NULL;
+
+ if ((uri == NULL) || (*uri == '\0'))
+ return 0;
+
+ file = g_file_new_for_uri (uri);
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, 0, NULL, &err);
+ if (err == NULL) {
+ size = g_file_info_get_size (info);
+ }
+ else {
+ g_warning ("Failed to get file size for %s: %s", uri, err->message);
+ g_error_free (err);
+ }
+
+ g_object_unref (info);
+ g_object_unref (file);
+
+ return size;
+}
+
+
+goffset
+get_file_size_for_path (const char *path)
+{
+ char *uri;
+ goffset result;
+
+ uri = g_filename_to_uri (path, NULL, NULL);
+ result = get_file_size (uri);
+ g_free (uri);
+
+ return result;
+}
+
+
+static time_t
+get_file_time_type (const char *uri,
+ const char *type)
+{
+ time_t result = 0;
+ GFile *file;
+ GFileInfo *info;
+ GError *err = NULL;
+
+ if ((uri == NULL) || (*uri == '\0'))
+ return 0;
+
+ file = g_file_new_for_uri (uri);
+ info = g_file_query_info (file, type, 0, NULL, &err);
+ if (err == NULL) {
+ result = (time_t) g_file_info_get_attribute_uint64 (info, type);
+ }
+ else {
+ g_warning ("Failed to get %s: %s", type, err->message);
+ g_error_free (err);
+ result = 0;
+ }
+
+ g_object_unref (info);
+ g_object_unref (file);
+
+ return result;
+}
+
+
+time_t
+get_file_mtime (const char *uri)
+{
+ return get_file_time_type (uri, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+}
+
+
+time_t
+get_file_mtime_for_path (const char *path)
+{
+ char *uri;
+ time_t result;
+
+ uri = g_filename_to_uri (path, NULL, NULL);
+ result = get_file_mtime (uri);
+ g_free (uri);
+
+ return result;
+}
+
+
+time_t
+get_file_ctime (const char *uri)
+{
+ return get_file_time_type (uri, G_FILE_ATTRIBUTE_TIME_CREATED);
+}
+
+
+gboolean
+file_is_hidden (const gchar *name)
+{
+ if (name[0] != '.') return FALSE;
+ if (name[1] == '\0') return FALSE;
+ if ((name[1] == '.') && (name[2] == '\0')) return FALSE;
+
+ return TRUE;
+}
+
+
+/* like g_path_get_basename but does not warn about NULL and does not
+ * alloc a new string. */
+const gchar* file_name_from_path(const gchar *file_name)
+{
+ register char *base;
+ register gssize last_char;
+
+ if (file_name == NULL)
+ return NULL;
+
+ if (file_name[0] == '\0')
+ return "";
+
+ last_char = strlen (file_name) - 1;
+
+ if (file_name [last_char] == G_DIR_SEPARATOR)
+ return "";
+
+ base = g_utf8_strrchr (file_name, -1, G_DIR_SEPARATOR);
+ if (! base)
+ return file_name;
+
+ return base + 1;
+}
+
+
+char *
+dir_name_from_path (const gchar *path)
+{
+ register gssize base;
+ register gssize last_char;
+
+ if (path == NULL)
+ return NULL;
+
+ if (path[0] == '\0')
+ return g_strdup ("");
+
+ last_char = strlen (path) - 1;
+ if (path[last_char] == G_DIR_SEPARATOR)
+ last_char--;
+
+ base = last_char;
+ while ((base >= 0) && (path[base] != G_DIR_SEPARATOR))
+ base--;
+
+ return g_strndup (path + base + 1, last_char - base);
+}
+
+
+gchar *
+remove_level_from_path (const gchar *path)
+{
+ int p;
+ const char *ptr = path;
+ char *new_path;
+
+ if (path == NULL)
+ return NULL;
+
+ p = strlen (path) - 1;
+ if (p < 0)
+ return NULL;
+
+ while ((p > 0) && (ptr[p] != '/'))
+ p--;
+ if ((p == 0) && (ptr[p] == '/'))
+ p++;
+ new_path = g_strndup (path, (guint)p);
+
+ return new_path;
+}
+
+
+char *
+remove_ending_separator (const char *path)
+{
+ gint len, copy_len;
+
+ if (path == NULL)
+ return NULL;
+
+ copy_len = len = strlen (path);
+ if ((len > 1) && (path[len - 1] == '/'))
+ copy_len--;
+
+ return g_strndup (path, copy_len);
+}
+
+
+char *
+build_uri (const char *base, ...)
+{
+ va_list args;
+ const char *child;
+ GString *uri;
+
+ uri = g_string_new (base);
+
+ va_start (args, base);
+ while ((child = va_arg (args, const char *)) != NULL) {
+ if (! g_str_has_suffix (uri->str, "/") && ! g_str_has_prefix (child, "/"))
+ g_string_append (uri, "/");
+ g_string_append (uri, child);
+ }
+ va_end (args);
+
+ return g_string_free (uri, FALSE);
+}
+
+
+gchar *
+remove_extension_from_path (const gchar *path)
+{
+ int len;
+ int p;
+ const char *ptr = path;
+ char *new_path;
+
+ if (! path)
+ return NULL;
+
+ len = strlen (path);
+ if (len == 1)
+ return g_strdup (path);
+
+ p = len - 1;
+ while ((p > 0) && (ptr[p] != '.'))
+ p--;
+ if (p == 0)
+ p = len;
+ new_path = g_strndup (path, (guint) p);
+
+ return new_path;
+}
+
+
+gboolean
+make_directory_tree (GFile *dir,
+ mode_t mode,
+ GError **error)
+{
+ gboolean success = TRUE;
+ GFile *parent;
+
+ if ((dir == NULL) || g_file_query_exists (dir, NULL))
+ return TRUE;
+
+ parent = g_file_get_parent (dir);
+ if (parent != NULL) {
+ success = make_directory_tree (parent, mode, error);
+ g_object_unref (parent);
+ if (! success)
+ return FALSE;
+ }
+
+ success = g_file_make_directory (dir, NULL, error);
+ if ((error != NULL) && (*error != NULL) && g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_EXISTS)) {
+ g_clear_error (error);
+ success = TRUE;
+ }
+
+ if (success)
+ g_file_set_attribute_uint32 (dir,
+ G_FILE_ATTRIBUTE_UNIX_MODE,
+ mode,
+ 0,
+ NULL,
+ NULL);
+
+ return success;
+}
+
+
+gboolean
+ensure_dir_exists (const char *uri,
+ mode_t mode,
+ GError **error)
+{
+ GFile *dir;
+ GError *priv_error = NULL;
+
+ if (uri == NULL)
+ return FALSE;
+
+ if (error == NULL)
+ error = &priv_error;
+
+ dir = g_file_new_for_uri (uri);
+ if (! make_directory_tree (dir, mode, error)) {
+ g_warning ("could create directory %s: %s", uri, (*error)->message);
+ if (priv_error != NULL)
+ g_clear_error (&priv_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+gboolean
+make_directory_tree_from_path (const char *path,
+ mode_t mode,
+ GError **error)
+{
+ char *uri;
+ gboolean result;
+
+ uri = g_filename_to_uri (path, NULL, NULL);
+ result = ensure_dir_exists (uri, mode, error);
+ g_free (uri);
+
+ return result;
+}
+
+
+const char *
+get_file_extension (const char *filename)
+{
+ const char *ptr = filename;
+ int len;
+ int p;
+ const char *ext;
+
+ if (filename == NULL)
+ return NULL;
+
+ len = strlen (filename);
+ if (len <= 1)
+ return NULL;
+
+ p = len - 1;
+ while ((p >= 0) && (ptr[p] != '.'))
+ p--;
+ if (p < 0)
+ return NULL;
+
+ ext = filename + p;
+ if (ext - 4 > filename) {
+ const char *test = ext - 4;
+ if (strncmp (test, ".tar", 4) == 0)
+ ext = ext - 4;
+ }
+ return ext;
+}
+
+
+gboolean
+file_extension_is (const char *filename,
+ const char *ext)
+{
+ int filename_l, ext_l;
+
+ filename_l = strlen (filename);
+ ext_l = strlen (ext);
+
+ if (filename_l < ext_l)
+ return FALSE;
+ return strcasecmp (filename + filename_l - ext_l, ext) == 0;
+}
+
+
+gboolean
+is_mime_type (const char *mime_type,
+ const char *pattern)
+{
+ return (strcasecmp (mime_type, pattern) == 0);
+}
+
+
+const char*
+get_file_mime_type (const char *uri,
+ gboolean fast_file_type)
+{
+ GFile *file;
+ GFileInfo *info;
+ GError *err = NULL;
+ const char *result = NULL;
+
+ file = g_file_new_for_uri (uri);
+ info = g_file_query_info (file,
+ fast_file_type ?
+ G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE :
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, &err);
+ if (info == NULL) {
+ g_warning ("could not get content type for %s: %s", uri, err->message);
+ g_clear_error (&err);
+ }
+ else {
+ result = get_static_string (g_file_info_get_content_type (info));
+ g_object_unref (info);
+ }
+
+ g_object_unref (file);
+
+ return result;
+}
+
+
+const char*
+get_file_mime_type_for_path (const char *filename,
+ gboolean fast_file_type)
+{
+ char *uri;
+ const char *mime_type;
+
+ uri = g_filename_to_uri (filename, NULL, NULL);
+ mime_type = get_file_mime_type (uri, fast_file_type);
+ g_free (uri);
+
+ return mime_type;
+}
+
+
+void
+path_list_free (GList *path_list)
+{
+ if (path_list == NULL)
+ return;
+ g_list_foreach (path_list, (GFunc) g_free, NULL);
+ g_list_free (path_list);
+}
+
+
+GList *
+path_list_dup (GList *path_list)
+{
+ GList *new_list = NULL;
+ GList *scan;
+
+ for (scan = path_list; scan; scan = scan->next)
+ new_list = g_list_prepend (new_list, g_strdup (scan->data));
+
+ return g_list_reverse (new_list);
+}
+
+
+guint64
+get_dest_free_space (const char *path)
+{
+ guint64 freespace = 0;
+ GFile *file;
+ GFileInfo *info;
+ GError *err = NULL;
+
+ file = g_file_new_for_path (path);
+ info = g_file_query_filesystem_info (file, G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, &err);
+ if (info != NULL) {
+ freespace = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
+ g_object_unref (info);
+ }
+ else {
+ g_warning ("Could not get filesystem free space on volume that contains %s: %s", path, err->message);
+ g_error_free (err);
+ }
+ g_object_unref (file);
+
+ return freespace;
+}
+
+
+static gboolean
+delete_directory_recursive (GFile *dir,
+ GError **error)
+{
+ char *uri;
+ GFileEnumerator *file_enum;
+ GFileInfo *info;
+ gboolean error_occurred = FALSE;
+
+ if (error != NULL)
+ *error = NULL;
+
+ file_enum = g_file_enumerate_children (dir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME ","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ 0, NULL, error);
+
+ uri = g_file_get_uri (dir);
+ while (! error_occurred && (info = g_file_enumerator_next_file (file_enum, NULL, error)) != NULL) {
+ char *child_uri;
+ GFile *child;
+
+ child_uri = build_uri (uri, g_file_info_get_name (info), NULL);
+ child = g_file_new_for_uri (child_uri);
+
+ switch (g_file_info_get_file_type (info)) {
+ case G_FILE_TYPE_DIRECTORY:
+ if (! delete_directory_recursive (child, error))
+ error_occurred = TRUE;
+ break;
+ default:
+ if (! g_file_delete (child, NULL, error))
+ error_occurred = TRUE;
+ break;
+ }
+
+ g_object_unref (child);
+ g_free (child_uri);
+ g_object_unref (info);
+ }
+ g_free (uri);
+
+ if (! error_occurred && ! g_file_delete (dir, NULL, error))
+ error_occurred = TRUE;
+
+ g_object_unref (file_enum);
+
+ return ! error_occurred;
+}
+
+
+gboolean
+remove_directory (const char *uri)
+{
+ GFile *dir;
+ gboolean result;
+ GError *error = NULL;
+
+ dir = g_file_new_for_uri (uri);
+ result = delete_directory_recursive (dir, &error);
+ if (! result) {
+ g_warning ("Cannot delete %s: %s", uri, error->message);
+ g_clear_error (&error);
+ }
+ g_object_unref (dir);
+
+ return result;
+}
+
+
+gboolean
+remove_local_directory (const char *path)
+{
+ char *uri;
+ gboolean result;
+
+ if (path == NULL)
+ return TRUE;
+
+ uri = g_filename_to_uri (path, NULL, NULL);
+ result = remove_directory (uri);
+ g_free (uri);
+
+ return result;
+}
+
+
+static const char *try_folder[] = { "cache", "~", "tmp", NULL };
+
+
+static char *
+ith_temp_folder_to_try (int n)
+{
+ const char *folder;
+
+ folder = try_folder[n];
+ if (strcmp (folder, "cache") == 0)
+ folder = g_get_user_cache_dir ();
+ else if (strcmp (folder, "~") == 0)
+ folder = g_get_home_dir ();
+ else if (strcmp (folder, "tmp") == 0)
+ folder = g_get_tmp_dir ();
+
+ return g_strdup (folder);
+}
+
+
+char *
+get_temp_work_dir (const char *parent_folder)
+{
+ guint64 max_size = 0;
+ char *best_folder = NULL;
+ int i;
+ char *template;
+ char *result = NULL;
+
+ if (parent_folder == NULL) {
+ /* find the folder with more free space. */
+
+ for (i = 0; try_folder[i] != NULL; i++) {
+ char *folder;
+ guint64 size;
+
+ folder = ith_temp_folder_to_try (i);
+ size = get_dest_free_space (folder);
+ if (max_size < size) {
+ max_size = size;
+ g_free (best_folder);
+ best_folder = folder;
+ }
+ else
+ g_free (folder);
+ }
+ }
+ else
+ best_folder = g_strdup (parent_folder);
+
+ if (best_folder == NULL)
+ return NULL;
+
+ template = g_strconcat (best_folder, "/.fr-XXXXXX", NULL);
+ result = mkdtemp (template);
+
+ if ((result == NULL) || (*result == '\0')) {
+ g_free (template);
+ result = NULL;
+ }
+
+ return result;
+}
+
+
+gboolean
+is_temp_work_dir (const char *dir)
+{
+ int i;
+ const char *folder;
+
+ if (strncmp (dir, "file://", 7) == 0)
+ dir = dir + 7;
+ else if (dir[0] != '/')
+ return FALSE;
+
+ for (i = 0; try_folder[i] != NULL; i++) {
+
+ folder = ith_temp_folder_to_try (i);
+ if (strncmp (dir, folder, strlen (folder)) == 0)
+ if (strncmp (dir + strlen (folder), "/.fr-", 5) == 0) {
+ g_free (folder);
+ return TRUE;
+ }
+ }
+
+ g_free (folder);
+ return FALSE;
+}
+
+
+gboolean
+is_temp_dir (const char *dir)
+{
+ if (strncmp (dir, "file://", 7) == 0)
+ dir = dir + 7;
+ if (strcmp (g_get_tmp_dir (), dir) == 0)
+ return TRUE;
+ if (path_in_path (g_get_tmp_dir (), dir))
+ return TRUE;
+ else
+ return is_temp_work_dir (dir);
+}
+
+
+/* file list utils */
+
+
+gboolean
+file_list__match_pattern (const char *line,
+ const char *pattern)
+{
+ const char *l = line, *p = pattern;
+
+ for (; (*p != 0) && (*l != 0); p++, l++) {
+ if (*p != '%') {
+ if (*p != *l)
+ return FALSE;
+ }
+ else {
+ p++;
+ switch (*p) {
+ case 'a':
+ break;
+ case 'n':
+ if (!isdigit (*l))
+ return FALSE;
+ break;
+ case 'c':
+ if (!isalpha (*l))
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ }
+
+ return (*p == 0);
+}
+
+
+int
+file_list__get_index_from_pattern (const char *line,
+ const char *pattern)
+{
+ int line_l, pattern_l;
+ const char *l;
+
+ line_l = strlen (line);
+ pattern_l = strlen (pattern);
+
+ if ((pattern_l == 0) || (line_l == 0))
+ return -1;
+
+ for (l = line; *l != 0; l++)
+ if (file_list__match_pattern (l, pattern))
+ return (l - line);
+
+ return -1;
+}
+
+
+char*
+file_list__get_next_field (const char *line,
+ int start_from,
+ int field_n)
+{
+ const char *f_start, *f_end;
+
+ line = line + start_from;
+
+ f_start = line;
+ while ((*f_start == ' ') && (*f_start != *line))
+ f_start++;
+ f_end = f_start;
+
+ while ((field_n > 0) && (*f_end != 0)) {
+ if (*f_end == ' ') {
+ field_n--;
+ if (field_n != 0) {
+ while ((*f_end == ' ') && (*f_end != *line))
+ f_end++;
+ f_start = f_end;
+ }
+ } else
+ f_end++;
+ }
+
+ return g_strndup (f_start, f_end - f_start);
+}
+
+
+char*
+file_list__get_prev_field (const char *line,
+ int start_from,
+ int field_n)
+{
+ const char *f_start, *f_end;
+
+ f_start = line + start_from - 1;
+ while ((*f_start == ' ') && (*f_start != *line))
+ f_start--;
+ f_end = f_start;
+
+ while ((field_n > 0) && (*f_start != *line)) {
+ if (*f_start == ' ') {
+ field_n--;
+ if (field_n != 0) {
+ while ((*f_start == ' ') && (*f_start != *line))
+ f_start--;
+ f_end = f_start;
+ }
+ } else
+ f_start--;
+ }
+
+ return g_strndup (f_start + 1, f_end - f_start);
+}
+
+
+gboolean
+check_permissions (const char *uri,
+ int mode)
+{
+ GFile *file;
+ gboolean result;
+
+ file = g_file_new_for_uri (uri);
+ result = check_file_permissions (file, mode);
+
+ g_object_unref (file);
+
+ return result;
+}
+
+
+gboolean
+check_file_permissions (GFile *file,
+ int mode)
+{
+ gboolean result = TRUE;
+ GFileInfo *info;
+ GError *err = NULL;
+ gboolean default_permission_when_unknown = TRUE;
+
+ info = g_file_query_info (file, "access::*", 0, NULL, &err);
+ if (err != NULL) {
+ g_clear_error (&err);
+ result = FALSE;
+ }
+ else {
+ if ((mode & R_OK) == R_OK) {
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
+ result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ));
+ else
+ result = (result && default_permission_when_unknown);
+ }
+
+ if ((mode & W_OK) == W_OK) {
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
+ result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE));
+ else
+ result = (result && default_permission_when_unknown);
+ }
+
+ if ((mode & X_OK) == X_OK) {
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
+ result = (result && g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE));
+ else
+ result = (result && default_permission_when_unknown);
+ }
+
+ g_object_unref (info);
+ }
+
+ return result;
+}
+
+
+gboolean
+is_program_in_path (const char *filename)
+{
+ char *str;
+ char *value;
+ int result = FALSE;
+
+ value = g_hash_table_lookup (ProgramsCache, filename);
+ if (value != NULL) {
+ result = (strcmp (value, "1") == 0);
+ return result;
+ }
+
+ str = g_find_program_in_path (filename);
+ if (str != NULL) {
+ g_free (str);
+ result = TRUE;
+ }
+
+ g_hash_table_insert (ProgramsCache,
+ g_strdup (filename),
+ result ? "1" : "0");
+
+ return result;
+}
+
+
+gboolean
+is_program_available (const char *filename,
+ gboolean check)
+{
+ return ! check || is_program_in_path (filename);
+}
+
+
+const char *
+get_home_uri (void)
+{
+ static char *home_uri = NULL;
+ if (home_uri == NULL)
+ home_uri = g_filename_to_uri (g_get_home_dir (), NULL, NULL);
+ return home_uri;
+}
+
+
+char *
+get_home_relative_uri (const char *partial_uri)
+{
+ return g_strconcat (get_home_uri (),
+ "/",
+ partial_uri,
+ NULL);
+}
+
+
+GFile *
+get_home_relative_file (const char *partial_uri)
+{
+ GFile *file;
+ char *uri;
+
+ uri = g_strconcat (get_home_uri (), "/", partial_uri, NULL);
+ file = g_file_new_for_uri (uri);
+ g_free (uri);
+
+ return file;
+}
+
+
+const char *
+remove_host_from_uri (const char *uri)
+{
+ const char *idx, *sep;
+
+ if (uri == NULL)
+ return NULL;
+
+ idx = strstr (uri, "://");
+ if (idx == NULL)
+ return uri;
+ idx += 3;
+ if (*idx == '\0')
+ return "/";
+ sep = strstr (idx, "/");
+ if (sep == NULL)
+ return idx;
+ return sep;
+}
+
+
+char *
+get_uri_host (const char *uri)
+{
+ const char *idx;
+
+ idx = strstr (uri, "://");
+ if (idx == NULL)
+ return NULL;
+ idx = strstr (idx + 3, "/");
+ if (idx == NULL)
+ return NULL;
+ return g_strndup (uri, (idx - uri));
+}
+
+
+char *
+get_uri_root (const char *uri)
+{
+ char *host;
+ char *root;
+
+ host = get_uri_host (uri);
+ if (host == NULL)
+ return NULL;
+ root = g_strconcat (host, "/", NULL);
+ g_free (host);
+
+ return root;
+}
+
+
+int
+uricmp (const char *uri1,
+ const char *uri2)
+{
+ return strcmp_null_tolerant (uri1, uri2);
+}
+
+
+char *
+get_alternative_uri (const char *folder,
+ const char *name)
+{
+ char *new_uri = NULL;
+ int n = 1;
+
+ do {
+ g_free (new_uri);
+ if (n == 1)
+ new_uri = g_strconcat (folder, "/", name, NULL);
+ else
+ new_uri = g_strdup_printf ("%s/%s%%20(%d)", folder, name, n);
+ n++;
+ } while (uri_exists (new_uri));
+
+ return new_uri;
+}
+
+
+char *
+get_alternative_uri_for_uri (const char *uri)
+{
+ char *base_uri;
+ char *new_uri;
+
+ base_uri = remove_level_from_path (uri);
+ new_uri = get_alternative_uri (base_uri, file_name_from_path (uri));
+ g_free (base_uri);
+
+ return new_uri;
+}
+
+
+GList *
+gio_file_list_dup (GList *l)
+{
+ GList *r = NULL, *scan;
+ for (scan = l; scan; scan = scan->next)
+ r = g_list_prepend (r, g_file_dup ((GFile*)scan->data));
+ return g_list_reverse (r);
+}
+
+
+void
+gio_file_list_free (GList *l)
+{
+ GList *scan;
+ for (scan = l; scan; scan = scan->next)
+ g_object_unref (scan->data);
+ g_list_free (l);
+}
+
+
+GList *
+gio_file_list_new_from_uri_list (GList *uris)
+{
+ GList *r = NULL, *scan;
+ for (scan = uris; scan; scan = scan->next)
+ r = g_list_prepend (r, g_file_new_for_uri ((char*)scan->data));
+ return g_list_reverse (r);
+}
+
+
+void
+g_key_file_save (GKeyFile *key_file,
+ GFile *file)
+{
+ char *file_data;
+ gsize size;
+ GError *error = NULL;
+
+ file_data = g_key_file_to_data (key_file, &size, &error);
+ if (error != NULL) {
+ g_warning ("Could not save options: %s\n", error->message);
+ g_clear_error (&error);
+ }
+ else {
+ GFileOutputStream *stream;
+
+ stream = g_file_replace (file, NULL, FALSE, 0, NULL, &error);
+ if (stream == NULL) {
+ g_warning ("Could not save options: %s\n", error->message);
+ g_clear_error (&error);
+ }
+ else if (! g_output_stream_write_all (G_OUTPUT_STREAM (stream), file_data, size, NULL, NULL, &error)) {
+ g_warning ("Could not save options: %s\n", error->message);
+ g_clear_error (&error);
+ }
+ else if (! g_output_stream_close (G_OUTPUT_STREAM (stream), NULL, &error)) {
+ g_warning ("Could not save options: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ g_object_unref (stream);
+ }
+
+ g_free (file_data);
+}
diff --git a/src/file-utils.h b/src/file-utils.h
new file mode 100644
index 0000000..d89d5bf
--- /dev/null
+++ b/src/file-utils.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FILE_UTILS_H
+#define FILE_UTILS_H
+
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <gio/gio.h>
+
+#define MIME_TYPE_DIRECTORY "folder"
+#define MIME_TYPE_ARCHIVE "application/x-archive"
+
+#define get_home_relative_path(x) \
+ g_strconcat (g_get_home_dir (), \
+ "/", \
+ (x), \
+ NULL)
+
+gboolean uri_exists (const char *uri);
+gboolean uri_is_file (const char *uri);
+gboolean uri_is_dir (const char *uri);
+gboolean path_is_dir (const char *path);
+gboolean uri_is_local (const char *uri);
+gboolean dir_is_empty (const char *uri);
+gboolean dir_contains_one_object (const char *uri);
+char * get_dir_content_if_unique (const char *uri);
+gboolean path_in_path (const char *path_src,
+ const char *path_dest);
+goffset get_file_size (const char *uri);
+goffset get_file_size_for_path (const char *path);
+time_t get_file_mtime (const char *uri);
+time_t get_file_mtime_for_path (const char *path);
+time_t get_file_ctime (const char *uri);
+gboolean make_directory_tree (GFile *dir,
+ mode_t mode,
+ GError **error);
+gboolean ensure_dir_exists (const char *uri,
+ mode_t mode,
+ GError **error);
+gboolean make_directory_tree_from_path (const char *path,
+ mode_t mode,
+ GError **error);
+gboolean file_is_hidden (const char *name);
+const char* file_name_from_path(const char* path);
+char * dir_name_from_path (const char *path);
+char * remove_level_from_path (const char *path);
+char * remove_ending_separator (const char *path);
+char * build_uri (const char *base, ...);
+char * remove_extension_from_path (const char *path);
+const char * get_file_extension (const char *filename);
+gboolean file_extension_is (const char *filename,
+ const char *ext);
+gboolean is_mime_type (const char *type,
+ const char *pattern);
+const char* get_file_mime_type (const char *uri,
+ gboolean fast_file_type);
+const char* get_file_mime_type_for_path (const char *filename,
+ gboolean fast_file_type);
+guint64 get_dest_free_space (const char *path);
+gboolean remove_directory (const char *uri);
+gboolean remove_local_directory (const char *directory);
+char * get_temp_work_dir (const char *parent_folder);
+gboolean is_temp_work_dir (const char *dir);
+gboolean is_temp_dir (const char *dir);
+
+/* misc functions used to parse a command output lines. */
+
+gboolean file_list__match_pattern (const char *line,
+ const char *pattern);
+int file_list__get_index_from_pattern (const char *line,
+ const char *pattern);
+char* file_list__get_next_field (const char *line,
+ int start_from,
+ int field_n);
+char* file_list__get_prev_field (const char *line,
+ int start_from,
+ int field_n);
+gboolean check_permissions (const char *path,
+ int mode);
+gboolean check_file_permissions (GFile *file,
+ int mode);
+gboolean is_program_in_path (const char *filename);
+gboolean is_program_available (const char *filename,
+ gboolean check);
+
+/* URI utils */
+
+const char * get_home_uri (void);
+char * get_home_relative_uri (const char *partial_uri);
+GFile * get_home_relative_file (const char *partial_uri);
+const char * remove_host_from_uri (const char *uri);
+char * get_uri_host (const char *uri);
+char * get_uri_root (const char *uri);
+int uricmp (const char *uri1,
+ const char *uri2);
+char * get_alternative_uri (const char *folder,
+ const char *name);
+char * get_alternative_uri_for_uri (const char *uri);
+
+void path_list_free (GList *path_list);
+GList * path_list_dup (GList *path_list);
+
+GList * gio_file_list_dup (GList *l);
+void gio_file_list_free (GList *l);
+GList * gio_file_list_new_from_uri_list (GList *uris);
+void g_key_file_save (GKeyFile *key_file,
+ GFile *file);
+
+#endif /* FILE_UTILS_H */
diff --git a/src/fr-archive.c b/src/fr-archive.c
new file mode 100644
index 0000000..1e79812
--- /dev/null
+++ b/src/fr-archive.c
@@ -0,0 +1,3354 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2007, 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/param.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include "glib-utils.h"
+#include "file-utils.h"
+#include "gio-utils.h"
+#include "file-data.h"
+#include "fr-archive.h"
+#include "fr-command.h"
+#include "fr-error.h"
+#include "fr-marshal.h"
+#include "fr-process.h"
+#include "main.h"
+
+#ifndef NCARGS
+#define NCARGS _POSIX_ARG_MAX
+#endif
+
+
+/* -- DroppedItemsData -- */
+
+
+typedef struct {
+ FrArchive *archive;
+ GList *item_list;
+ char *base_dir;
+ char *dest_dir;
+ gboolean update;
+ char *password;
+ gboolean encrypt_header;
+ FrCompression compression;
+ guint volume_size;
+} DroppedItemsData;
+
+
+static DroppedItemsData *
+dropped_items_data_new (FrArchive *archive,
+ GList *item_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size)
+{
+ DroppedItemsData *data;
+
+ data = g_new0 (DroppedItemsData, 1);
+ data->archive = archive;
+ data->item_list = path_list_dup (item_list);
+ if (base_dir != NULL)
+ data->base_dir = g_strdup (base_dir);
+ if (dest_dir != NULL)
+ data->dest_dir = g_strdup (dest_dir);
+ data->update = update;
+ if (password != NULL)
+ data->password = g_strdup (password);
+ data->encrypt_header = encrypt_header;
+ data->compression = compression;
+ data->volume_size = volume_size;
+
+ return data;
+}
+
+
+static void
+dropped_items_data_free (DroppedItemsData *data)
+{
+ if (data == NULL)
+ return;
+ path_list_free (data->item_list);
+ g_free (data->base_dir);
+ g_free (data->dest_dir);
+ g_free (data->password);
+ g_free (data);
+}
+
+
+struct _FrArchivePrivData {
+ FakeLoadFunc fake_load_func; /* If returns TRUE, archives are not read when
+ * fr_archive_load is invoked, used
+ * in batch mode. */
+ gpointer fake_load_data;
+ FakeLoadFunc add_is_stoppable_func; /* Returns whether the add operation is
+ * stoppable. */
+ gpointer add_is_stoppable_data;
+ GCancellable *cancellable;
+ char *temp_dir;
+ gboolean continue_adding_dropped_items;
+ DroppedItemsData *dropped_items_data;
+
+ char *temp_extraction_dir;
+ char *extraction_destination;
+ gboolean remote_extraction;
+ gboolean extract_here;
+};
+
+
+typedef struct {
+ FrArchive *archive;
+ char *uri;
+ FrAction action;
+ GList *file_list;
+ char *base_uri;
+ char *dest_dir;
+ gboolean update;
+ char *tmp_dir;
+ guint source_id;
+ char *password;
+ gboolean encrypt_header;
+ FrCompression compression;
+ guint volume_size;
+} XferData;
+
+
+static void
+xfer_data_free (XferData *data)
+{
+ if (data == NULL)
+ return;
+
+ g_free (data->uri);
+ g_free (data->password);
+ path_list_free (data->file_list);
+ g_free (data->base_uri);
+ g_free (data->dest_dir);
+ g_free (data->tmp_dir);
+ g_free (data);
+}
+
+
+#define MAX_CHUNK_LEN (NCARGS * 2 / 3) /* Max command line length */
+#define UNKNOWN_TYPE "application/octet-stream"
+#define SAME_FS (FALSE)
+#define NO_BACKUP_FILES (TRUE)
+#define NO_DOT_FILES (FALSE)
+#define IGNORE_CASE (FALSE)
+#define LIST_LENGTH_TO_USE_FILE 10 /* FIXME: find a good value */
+
+
+enum {
+ START,
+ DONE,
+ PROGRESS,
+ MESSAGE,
+ STOPPABLE,
+ WORKING_ARCHIVE,
+ LAST_SIGNAL
+};
+
+static GObjectClass *parent_class;
+static guint fr_archive_signals[LAST_SIGNAL] = { 0 };
+
+static void fr_archive_class_init (FrArchiveClass *class);
+static void fr_archive_init (FrArchive *archive);
+static void fr_archive_finalize (GObject *object);
+
+
+GType
+fr_archive_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ static const GTypeInfo type_info = {
+ sizeof (FrArchiveClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_archive_class_init,
+ NULL,
+ NULL,
+ sizeof (FrArchive),
+ 0,
+ (GInstanceInitFunc) fr_archive_init
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT,
+ "FrArchive",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+static void
+fr_archive_class_init (FrArchiveClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->finalize = fr_archive_finalize;
+
+ class->start = NULL;
+ class->done = NULL;
+ class->progress = NULL;
+ class->message = NULL;
+ class->working_archive = NULL;
+
+ /* signals */
+
+ fr_archive_signals[START] =
+ g_signal_new ("start",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrArchiveClass, start),
+ NULL, NULL,
+ fr_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1, G_TYPE_INT);
+ fr_archive_signals[DONE] =
+ g_signal_new ("done",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrArchiveClass, done),
+ NULL, NULL,
+ fr_marshal_VOID__INT_POINTER,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT,
+ G_TYPE_POINTER);
+ fr_archive_signals[PROGRESS] =
+ g_signal_new ("progress",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrArchiveClass, progress),
+ NULL, NULL,
+ fr_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1,
+ G_TYPE_DOUBLE);
+ fr_archive_signals[MESSAGE] =
+ g_signal_new ("message",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrArchiveClass, message),
+ NULL, NULL,
+ fr_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+ fr_archive_signals[STOPPABLE] =
+ g_signal_new ("stoppable",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrArchiveClass, stoppable),
+ NULL, NULL,
+ fr_marshal_VOID__BOOL,
+ G_TYPE_NONE,
+ 1, G_TYPE_BOOLEAN);
+ fr_archive_signals[WORKING_ARCHIVE] =
+ g_signal_new ("working_archive",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrArchiveClass, working_archive),
+ NULL, NULL,
+ fr_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+}
+
+
+void
+fr_archive_stoppable (FrArchive *archive,
+ gboolean stoppable)
+{
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[STOPPABLE],
+ 0,
+ stoppable);
+}
+
+
+void
+fr_archive_stop (FrArchive *archive)
+{
+ if (archive->process != NULL) {
+ fr_process_stop (archive->process);
+ return;
+ }
+
+ if (! g_cancellable_is_cancelled (archive->priv->cancellable))
+ g_cancellable_cancel (archive->priv->cancellable);
+}
+
+
+void
+fr_archive_action_completed (FrArchive *archive,
+ FrAction action,
+ FrProcErrorType error_type,
+ const char *error_details)
+{
+ archive->error.type = error_type;
+ archive->error.status = 0;
+ g_clear_error (&archive->error.gerror);
+ if (error_details != NULL)
+ archive->error.gerror = g_error_new_literal (fr_error_quark (),
+ 0,
+ error_details);
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[DONE],
+ 0,
+ action,
+ &archive->error);
+}
+
+
+static gboolean
+fr_archive_add_is_stoppable (FrArchive *archive)
+{
+ if (archive->priv->add_is_stoppable_func != NULL)
+ return (*archive->priv->add_is_stoppable_func) (archive, archive->priv->add_is_stoppable_data);
+ else
+ return FALSE;
+}
+
+
+static gboolean
+archive_sticky_only_cb (FrProcess *process,
+ FrArchive *archive)
+{
+ fr_archive_stoppable (archive, fr_archive_add_is_stoppable (archive));
+ return TRUE;
+}
+
+
+static void
+fr_archive_init (FrArchive *archive)
+{
+ archive->file = NULL;
+ archive->local_copy = NULL;
+ archive->is_remote = FALSE;
+ archive->command = NULL;
+ archive->is_compressed_file = FALSE;
+ archive->can_create_compressed_file = FALSE;
+
+ archive->priv = g_new0 (FrArchivePrivData, 1);
+ archive->priv->fake_load_func = NULL;
+ archive->priv->fake_load_data = NULL;
+ archive->priv->add_is_stoppable_func = NULL;
+ archive->priv->add_is_stoppable_data = NULL;
+
+ archive->priv->extraction_destination = NULL;
+ archive->priv->temp_extraction_dir = NULL;
+ archive->priv->cancellable = g_cancellable_new ();
+
+ archive->process = fr_process_new ();
+ g_signal_connect (G_OBJECT (archive->process),
+ "sticky_only",
+ G_CALLBACK (archive_sticky_only_cb),
+ archive);
+}
+
+
+FrArchive *
+fr_archive_new (void)
+{
+ return FR_ARCHIVE (g_object_new (FR_TYPE_ARCHIVE, NULL));
+}
+
+
+static GFile *
+get_local_copy_for_file (GFile *remote_file)
+{
+ char *temp_dir;
+ GFile *local_copy = NULL;
+
+ temp_dir = get_temp_work_dir (NULL);
+ if (temp_dir != NULL) {
+ char *archive_name;
+ char *local_path;
+
+ archive_name = g_file_get_basename (remote_file);
+ local_path = g_build_filename (temp_dir, archive_name, NULL);
+ local_copy = g_file_new_for_path (local_path);
+
+ g_free (local_path);
+ g_free (archive_name);
+ }
+ g_free (temp_dir);
+
+ return local_copy;
+}
+
+
+static void
+fr_archive_set_uri (FrArchive *archive,
+ const char *uri)
+{
+ if ((archive->local_copy != NULL) && archive->is_remote) {
+ GFile *temp_folder;
+ GError *err = NULL;
+
+ g_file_delete (archive->local_copy, NULL, &err);
+ if (err != NULL) {
+ g_warning ("Failed to delete the local copy: %s", err->message);
+ g_clear_error (&err);
+ }
+
+ temp_folder = g_file_get_parent (archive->local_copy);
+ g_file_delete (temp_folder, NULL, &err);
+ if (err != NULL) {
+ g_warning ("Failed to delete temp folder: %s", err->message);
+ g_clear_error (&err);
+ }
+
+ g_object_unref (temp_folder);
+ }
+
+ if (archive->file != NULL) {
+ g_object_unref (archive->file);
+ archive->file = NULL;
+ }
+ if (archive->local_copy != NULL) {
+ g_object_unref (archive->local_copy);
+ archive->local_copy = NULL;
+ }
+ archive->content_type = NULL;
+
+ if (uri == NULL)
+ return;
+
+ archive->file = g_file_new_for_uri (uri);
+ archive->is_remote = ! g_file_has_uri_scheme (archive->file, "file");
+ if (archive->is_remote)
+ archive->local_copy = get_local_copy_for_file (archive->file);
+ else
+ archive->local_copy = g_file_dup (archive->file);
+}
+
+
+static void
+fr_archive_remove_temp_work_dir (FrArchive *archive)
+{
+ if (archive->priv->temp_dir == NULL)
+ return;
+ remove_local_directory (archive->priv->temp_dir);
+ g_free (archive->priv->temp_dir);
+ archive->priv->temp_dir = NULL;
+}
+
+
+static void
+fr_archive_finalize (GObject *object)
+{
+ FrArchive *archive;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_ARCHIVE (object));
+
+ archive = FR_ARCHIVE (object);
+
+ fr_archive_set_uri (archive, NULL);
+ fr_archive_remove_temp_work_dir (archive);
+ if (archive->command != NULL)
+ g_object_unref (archive->command);
+ g_object_unref (archive->process);
+ if (archive->priv->dropped_items_data != NULL) {
+ dropped_items_data_free (archive->priv->dropped_items_data);
+ archive->priv->dropped_items_data = NULL;
+ }
+ g_free (archive->priv->temp_extraction_dir);
+ g_free (archive->priv->extraction_destination);
+ g_free (archive->priv);
+
+ /* Chain up */
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static const char *
+get_mime_type_from_content (GFile *file)
+{
+ GFileInfo *info;
+ GError *err = NULL;
+ const char *content_type = NULL;
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
+ 0, NULL, &err);
+ if (info == NULL) {
+ g_warning ("could not get content type: %s", err->message);
+ g_clear_error (&err);
+ }
+ else {
+ content_type = get_static_string (g_file_info_get_content_type (info));
+ g_object_unref (info);
+ }
+
+ return content_type;
+}
+
+
+static const char *
+get_mime_type_from_magic_numbers (GFile *file)
+{
+ static struct {
+ const char *mime_type;
+ const char *first_bytes;
+ int offset;
+ int len;
+ } sniffer_data [] = {
+ /* Magic numbers taken from magic/Magdir/archive from the
+ * file-4.21 tarball. */
+ { "application/x-7z-compressed", "7z\274\257\047\034", 0, 5 },
+ { "application/x-ace", "**ACE**", 7, 7 },
+ { "application/x-arj", "\x60\xea", 0, 2 },
+ { "application/x-bzip2", "BZh", 0, 3 },
+ { "application/x-gzip", "\037\213", 0, 2 },
+ { "application/x-lzip", "LZIP", 0, 4 },
+ { "application/x-lzop", "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a", 0, 9 },
+ { "application/x-rar", "Rar!", 0, 4 },
+ { "application/x-rzip", "RZIP", 0, 4 },
+ { "application/x-xz", "\3757zXZ\000", 0, 6 },
+ { "application/x-zoo", "\xdc\xa7\xc4\xfd", 20, 4 },
+ { "application/zip", "PK\003\004", 0, 4 },
+ { "application/zip", "PK00PK\003\004", 0, 8 },
+ { "application/x-lrzip", "LRZI", 0, 4 },
+ { NULL, NULL, 0 }
+ };
+ char buffer[32];
+ int i;
+
+ if (! g_load_file_in_buffer (file, buffer, 32, NULL))
+ return NULL;
+
+ for (i = 0; sniffer_data[i].mime_type != NULL; i++)
+ if (memcmp (sniffer_data[i].first_bytes,
+ buffer + sniffer_data[i].offset,
+ sniffer_data[i].len) == 0)
+ {
+ return sniffer_data[i].mime_type;
+ }
+
+ return NULL;
+}
+
+
+static const char *
+get_mime_type_from_filename (GFile *file)
+{
+ const char *mime_type = NULL;
+ char *filename;
+
+ if (file == NULL)
+ return NULL;
+
+ filename = g_file_get_path (file);
+ mime_type = get_mime_type_from_extension (get_file_extension (filename));
+ g_free (filename);
+
+ return mime_type;
+}
+
+
+static gboolean
+create_command_from_type (FrArchive *archive,
+ const char *mime_type,
+ GType command_type,
+ FrCommandCaps requested_capabilities)
+{
+ char *filename;
+
+ if (command_type == 0)
+ return FALSE;
+
+ filename = g_file_get_path (archive->local_copy);
+ archive->command = FR_COMMAND (g_object_new (command_type,
+ "process", archive->process,
+ "filename", filename,
+ "mime-type", mime_type,
+ NULL));
+ g_free (filename);
+
+ if (! fr_command_is_capable_of (archive->command, requested_capabilities)) {
+ g_object_unref (archive->command);
+ archive->command = NULL;
+ archive->is_compressed_file = FALSE;
+ }
+ else
+ archive->is_compressed_file = ! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_ARCHIVE_MANY_FILES);
+
+ return (archive->command != NULL);
+}
+
+
+static gboolean
+create_command_to_load_archive (FrArchive *archive,
+ const char *mime_type)
+{
+ FrCommandCaps requested_capabilities = FR_COMMAND_CAN_DO_NOTHING;
+ GType command_type;
+
+ if (mime_type == NULL)
+ return FALSE;
+
+ /* try with the WRITE capability even when loading, this way we give
+ * priority to the commands that can read and write over commands
+ * that can only read a specific file format. */
+
+ requested_capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ command_type = get_command_type_from_mime_type (mime_type, requested_capabilities);
+
+ /* if no command was found, remove the write capability and try again */
+
+ if (command_type == 0) {
+ requested_capabilities ^= FR_COMMAND_CAN_WRITE;
+ command_type = get_command_type_from_mime_type (mime_type, requested_capabilities);
+ }
+
+ return create_command_from_type (archive,
+ mime_type,
+ command_type,
+ requested_capabilities);
+}
+
+
+static gboolean
+create_command_to_create_archive (FrArchive *archive,
+ const char *mime_type)
+{
+ FrCommandCaps requested_capabilities = FR_COMMAND_CAN_DO_NOTHING;
+ GType command_type;
+
+ if (mime_type == NULL)
+ return FALSE;
+
+ requested_capabilities |= FR_COMMAND_CAN_WRITE;
+ command_type = get_command_type_from_mime_type (mime_type, requested_capabilities);
+
+ return create_command_from_type (archive,
+ mime_type,
+ command_type,
+ requested_capabilities);
+}
+
+
+static void
+action_started (FrCommand *command,
+ FrAction action,
+ FrArchive *archive)
+{
+#ifdef DEBUG
+ debug (DEBUG_INFO, "%s [START] (FR::Archive)\n", action_names[action]);
+#endif
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[START],
+ 0,
+ action);
+}
+
+
+/* -- copy_to_remote_location -- */
+
+
+static void
+fr_archive_copy_done (FrArchive *archive,
+ FrAction action,
+ GError *error)
+{
+ FrProcErrorType error_type = FR_PROC_ERROR_NONE;
+ const char *error_details = NULL;
+
+ if (error != NULL) {
+ error_type = (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC);
+ error_details = error->message;
+ }
+ fr_archive_action_completed (archive, action, error_type, error_details);
+}
+
+
+static void
+copy_to_remote_location_done (GError *error,
+ gpointer user_data)
+{
+ XferData *xfer_data = user_data;
+
+ fr_archive_copy_done (xfer_data->archive, xfer_data->action, error);
+ xfer_data_free (xfer_data);
+}
+
+
+static void
+copy_to_remote_location_progress (goffset current_file,
+ goffset total_files,
+ GFile *source,
+ GFile *destination,
+ goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ XferData *xfer_data = user_data;
+
+ g_signal_emit (G_OBJECT (xfer_data->archive),
+ fr_archive_signals[PROGRESS],
+ 0,
+ (double) current_num_bytes / total_num_bytes);
+}
+
+
+static void
+copy_to_remote_location (FrArchive *archive,
+ FrAction action)
+{
+ XferData *xfer_data;
+
+ xfer_data = g_new0 (XferData, 1);
+ xfer_data->archive = archive;
+ xfer_data->action = action;
+
+ g_copy_file_async (archive->local_copy,
+ archive->file,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ archive->priv->cancellable,
+ copy_to_remote_location_progress,
+ xfer_data,
+ copy_to_remote_location_done,
+ xfer_data);
+}
+
+
+/* -- copy_extracted_files_to_destination -- */
+
+
+static void
+move_here (FrArchive *archive)
+{
+ char *content_uri;
+ char *parent;
+ char *parent_parent;
+ char *new_content_uri;
+ GFile *source, *destination, *parent_file;
+ GError *error = NULL;
+
+ content_uri = get_dir_content_if_unique (archive->priv->extraction_destination);
+ if (content_uri == NULL)
+ return;
+
+ parent = remove_level_from_path (content_uri);
+
+ if (uricmp (parent, archive->priv->extraction_destination) == 0) {
+ char *new_uri;
+
+ new_uri = get_alternative_uri_for_uri (archive->priv->extraction_destination);
+
+ source = g_file_new_for_uri (archive->priv->extraction_destination);
+ destination = g_file_new_for_uri (new_uri);
+ if (! g_file_move (source, destination, 0, NULL, NULL, NULL, &error)) {
+ g_warning ("could not rename %s to %s: %s", archive->priv->extraction_destination, new_uri, error->message);
+ g_clear_error (&error);
+ }
+ g_object_unref (source);
+ g_object_unref (destination);
+
+ g_free (archive->priv->extraction_destination);
+ archive->priv->extraction_destination = new_uri;
+
+ g_free (parent);
+
+ content_uri = get_dir_content_if_unique (archive->priv->extraction_destination);
+ if (content_uri == NULL)
+ return;
+
+ parent = remove_level_from_path (content_uri);
+ }
+
+ parent_parent = remove_level_from_path (parent);
+ new_content_uri = get_alternative_uri (parent_parent, file_name_from_path (content_uri));
+
+ source = g_file_new_for_uri (content_uri);
+ destination = g_file_new_for_uri (new_content_uri);
+ if (! g_file_move (source, destination, 0, NULL, NULL, NULL, &error)) {
+ g_warning ("could not rename %s to %s: %s", content_uri, new_content_uri, error->message);
+ g_clear_error (&error);
+ }
+
+ parent_file = g_file_new_for_uri (parent);
+ if (! g_file_delete (parent_file, NULL, &error)) {
+ g_warning ("could not remove directory %s: %s", parent, error->message);
+ g_clear_error (&error);
+ }
+ g_object_unref (parent_file);
+
+ g_free (archive->priv->extraction_destination);
+ archive->priv->extraction_destination = new_content_uri;
+
+ g_free (parent_parent);
+ g_free (parent);
+ g_free (content_uri);
+}
+
+
+static void
+copy_extracted_files_done (GError *error,
+ gpointer user_data)
+{
+ FrArchive *archive = user_data;
+
+ remove_local_directory (archive->priv->temp_extraction_dir);
+ g_free (archive->priv->temp_extraction_dir);
+ archive->priv->temp_extraction_dir = NULL;
+
+ fr_archive_action_completed (archive,
+ FR_ACTION_COPYING_FILES_TO_REMOTE,
+ FR_PROC_ERROR_NONE,
+ NULL);
+
+ if ((error == NULL) && (archive->priv->extract_here))
+ move_here (archive);
+
+ fr_archive_copy_done (archive, FR_ACTION_EXTRACTING_FILES, error);
+}
+
+
+static void
+copy_extracted_files_progress (goffset current_file,
+ goffset total_files,
+ GFile *source,
+ GFile *destination,
+ goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ FrArchive *archive = user_data;
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[PROGRESS],
+ 0,
+ (double) current_file / (total_files + 1));
+}
+
+
+static void
+copy_extracted_files_to_destination (FrArchive *archive)
+{
+ g_directory_copy_async (archive->priv->temp_extraction_dir,
+ archive->priv->extraction_destination,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ archive->priv->cancellable,
+ copy_extracted_files_progress,
+ archive,
+ copy_extracted_files_done,
+ archive);
+}
+
+
+static void add_dropped_items (DroppedItemsData *data);
+
+
+static void
+fr_archive_change_name (FrArchive *archive,
+ const char *filename)
+{
+ const char *name;
+ GFile *parent;
+
+ name = file_name_from_path (filename);
+
+ parent = g_file_get_parent (archive->file);
+ g_object_unref (archive->file);
+ archive->file = g_file_get_child (parent, name);
+ g_object_unref (parent);
+
+ parent = g_file_get_parent (archive->local_copy);
+ g_object_unref (archive->local_copy);
+ archive->local_copy = g_file_get_child (parent, name);
+ g_object_unref (parent);
+}
+
+
+static void
+action_performed (FrCommand *command,
+ FrAction action,
+ FrProcError *error,
+ FrArchive *archive)
+{
+#ifdef DEBUG
+ debug (DEBUG_INFO, "%s [DONE] (FR::Archive)\n", action_names[action]);
+#endif
+
+ switch (action) {
+ case FR_ACTION_DELETING_FILES:
+ if (error->type == FR_PROC_ERROR_NONE) {
+ if (! g_file_has_uri_scheme (archive->file, "file")) {
+ copy_to_remote_location (archive, action);
+ return;
+ }
+ }
+ break;
+
+ case FR_ACTION_ADDING_FILES:
+ if (error->type == FR_PROC_ERROR_NONE) {
+ fr_archive_remove_temp_work_dir (archive);
+ if (archive->priv->continue_adding_dropped_items) {
+ add_dropped_items (archive->priv->dropped_items_data);
+ return;
+ }
+ if (archive->priv->dropped_items_data != NULL) {
+ dropped_items_data_free (archive->priv->dropped_items_data);
+ archive->priv->dropped_items_data = NULL;
+ }
+ /* the name of the volumes are different from the
+ * original name */
+ if (archive->command->multi_volume)
+ fr_archive_change_name (archive, archive->command->filename);
+ if (! g_file_has_uri_scheme (archive->file, "file")) {
+ copy_to_remote_location (archive, action);
+ return;
+ }
+ }
+ break;
+
+ case FR_ACTION_EXTRACTING_FILES:
+ if (error->type == FR_PROC_ERROR_NONE) {
+ if (archive->priv->remote_extraction) {
+ copy_extracted_files_to_destination (archive);
+ return;
+ }
+ else if (archive->priv->extract_here)
+ move_here (archive);
+ }
+ else {
+ /* if an error occurred during extraction remove the
+ * temp extraction dir, if used. */
+ g_print ("action_performed: ERROR!\n");
+
+ if ((archive->priv->remote_extraction) && (archive->priv->temp_extraction_dir != NULL)) {
+ remove_local_directory (archive->priv->temp_extraction_dir);
+ g_free (archive->priv->temp_extraction_dir);
+ archive->priv->temp_extraction_dir = NULL;
+ }
+
+ if (archive->priv->extract_here)
+ remove_directory (archive->priv->extraction_destination);
+ }
+ break;
+
+ case FR_ACTION_LISTING_CONTENT:
+ /* the name of the volumes are different from the
+ * original name */
+ if (archive->command->multi_volume)
+ fr_archive_change_name (archive, archive->command->filename);
+ fr_command_update_capabilities (archive->command);
+ if (! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_WRITE))
+ archive->read_only = TRUE;
+ break;
+
+ default:
+ /* nothing */
+ break;
+ }
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[DONE],
+ 0,
+ action,
+ error);
+}
+
+
+static gboolean
+archive_progress_cb (FrCommand *command,
+ double fraction,
+ FrArchive *archive)
+{
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[PROGRESS],
+ 0,
+ fraction);
+ return TRUE;
+}
+
+
+static gboolean
+archive_message_cb (FrCommand *command,
+ const char *msg,
+ FrArchive *archive)
+{
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[MESSAGE],
+ 0,
+ msg);
+ return TRUE;
+}
+
+
+static gboolean
+archive_working_archive_cb (FrCommand *command,
+ const char *archive_filename,
+ FrArchive *archive)
+{
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[WORKING_ARCHIVE],
+ 0,
+ archive_filename);
+ return TRUE;
+}
+
+
+static void
+fr_archive_connect_to_command (FrArchive *archive)
+{
+ g_signal_connect (G_OBJECT (archive->command),
+ "start",
+ G_CALLBACK (action_started),
+ archive);
+ g_signal_connect (G_OBJECT (archive->command),
+ "done",
+ G_CALLBACK (action_performed),
+ archive);
+ g_signal_connect (G_OBJECT (archive->command),
+ "progress",
+ G_CALLBACK (archive_progress_cb),
+ archive);
+ g_signal_connect (G_OBJECT (archive->command),
+ "message",
+ G_CALLBACK (archive_message_cb),
+ archive);
+ g_signal_connect (G_OBJECT (archive->command),
+ "working_archive",
+ G_CALLBACK (archive_working_archive_cb),
+ archive);
+}
+
+
+gboolean
+fr_archive_create (FrArchive *archive,
+ const char *uri)
+{
+ FrCommand *tmp_command;
+ const char *mime_type;
+
+ if (uri == NULL)
+ return FALSE;
+
+ fr_archive_set_uri (archive, uri);
+
+ tmp_command = archive->command;
+
+ mime_type = get_mime_type_from_filename (archive->local_copy);
+ if (! create_command_to_create_archive (archive, mime_type)) {
+ archive->command = tmp_command;
+ return FALSE;
+ }
+
+ if (tmp_command != NULL) {
+ g_signal_handlers_disconnect_by_data (tmp_command, archive);
+ g_object_unref (G_OBJECT (tmp_command));
+ }
+
+ fr_archive_connect_to_command (archive);
+ archive->read_only = FALSE;
+
+ return TRUE;
+}
+
+
+void
+fr_archive_set_fake_load_func (FrArchive *archive,
+ FakeLoadFunc func,
+ gpointer data)
+{
+ archive->priv->fake_load_func = func;
+ archive->priv->fake_load_data = data;
+}
+
+
+gboolean
+fr_archive_fake_load (FrArchive *archive)
+{
+ if (archive->priv->fake_load_func != NULL)
+ return (*archive->priv->fake_load_func) (archive, archive->priv->fake_load_data);
+ else
+ return FALSE;
+}
+
+
+/* -- fr_archive_load -- */
+
+
+static void
+load_local_archive (FrArchive *archive,
+ const char *password)
+{
+ FrCommand *tmp_command;
+ const char *mime_type;
+
+ if (! g_file_query_exists (archive->file, NULL)) {
+ fr_archive_action_completed (archive,
+ FR_ACTION_LOADING_ARCHIVE,
+ FR_PROC_ERROR_GENERIC,
+ _("File not found."));
+ return;
+ }
+
+ archive->have_permissions = check_file_permissions (archive->file, W_OK);
+ archive->read_only = ! archive->have_permissions;
+
+ tmp_command = archive->command;
+
+ mime_type = get_mime_type_from_filename (archive->local_copy);
+ if (! create_command_to_load_archive (archive, mime_type)) {
+ mime_type = get_mime_type_from_content (archive->local_copy);
+ if (! create_command_to_load_archive (archive, mime_type)) {
+ mime_type = get_mime_type_from_magic_numbers (archive->local_copy);
+ if (! create_command_to_load_archive (archive, mime_type)) {
+ archive->command = tmp_command;
+ archive->content_type = mime_type;
+ fr_archive_action_completed (archive,
+ FR_ACTION_LOADING_ARCHIVE,
+ FR_PROC_ERROR_UNSUPPORTED_FORMAT,
+ _("Archive type not supported."));
+ return;
+ }
+ }
+ }
+
+ if (tmp_command != NULL) {
+ g_signal_handlers_disconnect_by_data (tmp_command, archive);
+ g_object_unref (tmp_command);
+ }
+
+ fr_archive_connect_to_command (archive);
+ archive->content_type = mime_type;
+ if (! fr_command_is_capable_of (archive->command, FR_COMMAND_CAN_WRITE))
+ archive->read_only = TRUE;
+ fr_archive_stoppable (archive, TRUE);
+ archive->command->fake_load = fr_archive_fake_load (archive);
+
+ fr_archive_action_completed (archive,
+ FR_ACTION_LOADING_ARCHIVE,
+ FR_PROC_ERROR_NONE,
+ NULL);
+
+ /**/
+
+ fr_process_clear (archive->process);
+ g_object_set (archive->command, "password", password, NULL);
+ fr_command_list (archive->command);
+ fr_process_start (archive->process);
+}
+
+
+static void
+copy_remote_file_done (GError *error,
+ gpointer user_data)
+{
+ XferData *xfer_data = user_data;
+
+ if (error != NULL)
+ fr_archive_copy_done (xfer_data->archive, FR_ACTION_LOADING_ARCHIVE, error);
+ else
+ load_local_archive (xfer_data->archive, xfer_data->password);
+ xfer_data_free (xfer_data);
+}
+
+
+static void
+copy_remote_file_progress (goffset current_file,
+ goffset total_files,
+ GFile *source,
+ GFile *destination,
+ goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ XferData *xfer_data = user_data;
+
+ g_signal_emit (G_OBJECT (xfer_data->archive),
+ fr_archive_signals[PROGRESS],
+ 0,
+ (double) current_num_bytes / total_num_bytes);
+}
+
+
+static gboolean
+copy_remote_file_done_cb (gpointer user_data)
+{
+ XferData *xfer_data = user_data;
+
+ g_source_remove (xfer_data->source_id);
+ copy_remote_file_done (NULL, xfer_data);
+ return FALSE;
+}
+
+
+static void
+copy_remote_file (FrArchive *archive,
+ const char *password)
+{
+ XferData *xfer_data;
+
+ if (! g_file_query_exists (archive->file, NULL)) {
+ GError *error;
+ error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("The file doesn't exist"));
+ fr_archive_copy_done (archive, FR_ACTION_LOADING_ARCHIVE, error);
+ g_error_free (error);
+ return;
+ }
+
+ xfer_data = g_new0 (XferData, 1);
+ xfer_data->archive = archive;
+ xfer_data->uri = g_file_get_uri (archive->file);
+ if (password != NULL)
+ xfer_data->password = g_strdup (password);
+
+ if (! archive->is_remote) {
+ xfer_data->source_id = g_idle_add (copy_remote_file_done_cb, xfer_data);
+ return;
+ }
+
+ g_copy_file_async (archive->file,
+ archive->local_copy,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ archive->priv->cancellable,
+ copy_remote_file_progress,
+ xfer_data,
+ copy_remote_file_done,
+ xfer_data);
+}
+
+
+gboolean
+fr_archive_load (FrArchive *archive,
+ const char *uri,
+ const char *password)
+{
+ g_return_val_if_fail (archive != NULL, FALSE);
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[START],
+ 0,
+ FR_ACTION_LOADING_ARCHIVE);
+
+ fr_archive_set_uri (archive, uri);
+ copy_remote_file (archive, password);
+
+ return TRUE;
+}
+
+
+gboolean
+fr_archive_load_local (FrArchive *archive,
+ const char *uri,
+ const char *password)
+{
+ g_return_val_if_fail (archive != NULL, FALSE);
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[START],
+ 0,
+ FR_ACTION_LOADING_ARCHIVE);
+
+ fr_archive_set_uri (archive, uri);
+ copy_remote_file (archive, password);
+
+ return TRUE;
+}
+
+
+void
+fr_archive_reload (FrArchive *archive,
+ const char *password)
+{
+ char *uri;
+
+ g_return_if_fail (archive != NULL);
+ g_return_if_fail (archive->file != NULL);
+
+ fr_archive_stoppable (archive, TRUE);
+ archive->command->fake_load = fr_archive_fake_load (archive);
+
+ uri = g_file_get_uri (archive->file);
+ fr_archive_load (archive, uri, password);
+ g_free (uri);
+}
+
+
+void
+fr_archive_rename (FrArchive *archive,
+ const char *filename)
+{
+ g_return_if_fail (archive != NULL);
+
+ if (archive->is_compressed_file) {
+ /* If the archive is a compressed file we have to reload it,
+ * because in this case the 'content' of the archive changes
+ * too. */
+ fr_archive_load (archive, filename, NULL);
+ }
+ else {
+ if (archive->file != NULL)
+ g_object_unref (archive->file);
+ archive->file = g_file_new_for_path (filename);
+ fr_command_set_filename (archive->command, filename);
+ }
+}
+
+
+/* -- add -- */
+
+
+static char *
+create_tmp_base_dir (const char *base_dir,
+ const char *dest_path)
+{
+ char *dest_dir;
+ char *temp_dir;
+ char *tmp;
+ char *parent_dir;
+ char *dir;
+
+ if ((dest_path == NULL)
+ || (*dest_path == '\0')
+ || (strcmp (dest_path, "/") == 0))
+ {
+ return g_strdup (base_dir);
+ }
+
+ dest_dir = g_strdup (dest_path);
+ if (dest_dir[strlen (dest_dir) - 1] == G_DIR_SEPARATOR)
+ dest_dir[strlen (dest_dir) - 1] = 0;
+
+ debug (DEBUG_INFO, "base_dir: %s\n", base_dir);
+ debug (DEBUG_INFO, "dest_dir: %s\n", dest_dir);
+
+ temp_dir = get_temp_work_dir (NULL);
+ tmp = remove_level_from_path (dest_dir);
+ parent_dir = g_build_filename (temp_dir, tmp, NULL);
+ g_free (tmp);
+
+ debug (DEBUG_INFO, "mkdir %s\n", parent_dir);
+ make_directory_tree_from_path (parent_dir, 0700, NULL);
+ g_free (parent_dir);
+
+ dir = g_build_filename (temp_dir, "/", dest_dir, NULL);
+ debug (DEBUG_INFO, "symlink %s --> %s\n", dir, base_dir);
+ symlink (base_dir, dir);
+
+ g_free (dir);
+ g_free (dest_dir);
+
+ return temp_dir;
+}
+
+
+static FileData *
+find_file_in_archive (FrArchive *archive,
+ char *path)
+{
+ int i;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ i = find_path_in_file_data_array (archive->command->files, path);
+ if (i >= 0)
+ return (FileData *) g_ptr_array_index (archive->command->files, i);
+ else
+ return NULL;
+}
+
+
+static void delete_from_archive (FrArchive *archive, GList *file_list);
+
+
+static GList *
+newer_files_only (FrArchive *archive,
+ GList *file_list,
+ const char *base_dir)
+{
+ GList *newer_files = NULL;
+ GList *scan;
+
+ for (scan = file_list; scan; scan = scan->next) {
+ char *filename = scan->data;
+ char *fullpath;
+ char *uri;
+ FileData *fdata;
+
+ fdata = find_file_in_archive (archive, filename);
+
+ if (fdata == NULL) {
+ newer_files = g_list_prepend (newer_files, g_strdup (scan->data));
+ continue;
+ }
+
+ fullpath = g_strconcat (base_dir, "/", filename, NULL);
+ uri = g_filename_to_uri (fullpath, NULL, NULL);
+
+ if (fdata->modified >= get_file_mtime (uri)) {
+ g_free (fullpath);
+ g_free (uri);
+ continue;
+ }
+ g_free (fullpath);
+ g_free (uri);
+
+ newer_files = g_list_prepend (newer_files, g_strdup (scan->data));
+ }
+
+ return newer_files;
+}
+
+
+void
+fr_archive_set_add_is_stoppable_func (FrArchive *archive,
+ FakeLoadFunc func,
+ gpointer data)
+{
+ archive->priv->add_is_stoppable_func = func;
+ archive->priv->add_is_stoppable_data = data;
+}
+
+
+static GList *
+convert_to_local_file_list (GList *file_list)
+{
+ GList *local_file_list = NULL;
+ GList *scan;
+
+ for (scan = file_list; scan; scan = scan->next) {
+ char *uri = scan->data;
+ char *local_filename;
+
+ local_filename = g_uri_unescape_string (uri, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH);
+ if (local_filename != NULL)
+ local_file_list = g_list_prepend (local_file_list, local_filename);
+ }
+
+ return local_file_list;
+}
+
+
+static gboolean
+save_list_to_temp_file (GList *file_list,
+ char **list_dir,
+ char **list_filename,
+ GError **error)
+{
+ gboolean error_occurred = FALSE;
+ GFile *list_file;
+ GFileOutputStream *ostream;
+
+ if (error != NULL)
+ *error = NULL;
+ *list_dir = get_temp_work_dir (NULL);
+ *list_filename = g_build_filename (*list_dir, "file-list", NULL);
+ list_file = g_file_new_for_path (*list_filename);
+ ostream = g_file_create (list_file, G_FILE_CREATE_PRIVATE, NULL, error);
+
+ if (ostream != NULL) {
+ GList *scan;
+
+ for (scan = file_list; scan != NULL; scan = scan->next) {
+ char *filename = scan->data;
+
+ filename = str_substitute (filename, "\n", "\\n");
+ if ((g_output_stream_write (G_OUTPUT_STREAM (ostream), filename, strlen (filename), NULL, error) < 0)
+ || (g_output_stream_write (G_OUTPUT_STREAM (ostream), "\n", 1, NULL, error) < 0))
+ {
+ error_occurred = TRUE;
+ }
+
+ g_free (filename);
+
+ if (error_occurred)
+ break;
+ }
+ if (! error_occurred && ! g_output_stream_close (G_OUTPUT_STREAM (ostream), NULL, error))
+ error_occurred = TRUE;
+ g_object_unref (ostream);
+ }
+ else
+ error_occurred = TRUE;
+
+ if (error_occurred) {
+ remove_local_directory (*list_dir);
+ g_free (*list_dir);
+ g_free (*list_filename);
+ *list_dir = NULL;
+ *list_filename = NULL;
+ }
+
+ g_object_unref (list_file);
+
+ return ! error_occurred;
+}
+
+
+static GList *
+split_in_chunks (GList *file_list)
+{
+ GList *chunks = NULL;
+ GList *new_file_list;
+ GList *scan;
+
+ new_file_list = g_list_copy (file_list);
+ for (scan = new_file_list; scan != NULL; /* void */) {
+ GList *prev = scan->prev;
+ GList *chunk;
+ int l;
+
+ chunk = scan;
+ l = 0;
+ while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
+ if (l == 0)
+ l = strlen (scan->data);
+ prev = scan;
+ scan = scan->next;
+ if (scan != NULL)
+ l += strlen (scan->data);
+ }
+ if (prev != NULL) {
+ if (prev->next != NULL)
+ prev->next->prev = NULL;
+ prev->next = NULL;
+ }
+ chunks = g_list_append (chunks, chunk);
+ }
+
+ return chunks;
+}
+
+
+void
+fr_archive_add (FrArchive *archive,
+ GList *file_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ gboolean recursive,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size)
+{
+ GList *new_file_list = NULL;
+ gboolean base_dir_created = FALSE;
+ GList *scan;
+ char *tmp_base_dir = NULL;
+ gboolean error_occurred = FALSE;
+
+ if (file_list == NULL)
+ return;
+
+ if (archive->read_only)
+ return;
+
+ g_object_set (archive->command,
+ "password", password,
+ "encrypt_header", encrypt_header,
+ "compression", compression,
+ "volume_size", volume_size,
+ NULL);
+
+ fr_archive_stoppable (archive, fr_archive_add_is_stoppable (archive));
+
+ file_list = convert_to_local_file_list (file_list);
+ tmp_base_dir = g_strdup (base_dir);
+
+ if ((dest_dir != NULL) && (*dest_dir != '\0') && (strcmp (dest_dir, "/") != 0)) {
+ const char *rel_dest_dir = dest_dir;
+
+ tmp_base_dir = create_tmp_base_dir (base_dir, dest_dir);
+ base_dir_created = TRUE;
+
+ if (dest_dir[0] == G_DIR_SEPARATOR)
+ rel_dest_dir = dest_dir + 1;
+
+ new_file_list = NULL;
+ for (scan = file_list; scan != NULL; scan = scan->next) {
+ char *filename = scan->data;
+ new_file_list = g_list_prepend (new_file_list, g_build_filename (rel_dest_dir, filename, NULL));
+ }
+ path_list_free (file_list);
+ }
+ else
+ new_file_list = file_list;
+
+ /* if the command cannot update, get the list of files that are
+ * newer than the ones in the archive. */
+
+ if (update && ! archive->command->propAddCanUpdate) {
+ GList *tmp_file_list;
+
+ tmp_file_list = new_file_list;
+ new_file_list = newer_files_only (archive, tmp_file_list, tmp_base_dir);
+ path_list_free (tmp_file_list);
+ }
+
+ if (new_file_list == NULL) {
+ debug (DEBUG_INFO, "nothing to update.\n");
+
+ if (base_dir_created)
+ remove_local_directory (tmp_base_dir);
+ g_free (tmp_base_dir);
+
+ archive->process->error.type = FR_PROC_ERROR_NONE;
+ g_signal_emit_by_name (G_OBJECT (archive->process),
+ "done",
+ FR_ACTION_ADDING_FILES);
+ return;
+ }
+
+ archive->command->creating_archive = ! g_file_test (archive->command->filename, G_FILE_TEST_EXISTS);
+
+ fr_command_uncompress (archive->command);
+
+ /* when files are already present in a tar archive and are added
+ * again, they are not replaced, so we have to delete them first. */
+
+ /* if we are adding (== ! update) and 'add' cannot replace or
+ * if we are updating and 'add' cannot update,
+ * delete the files first. */
+
+ if ((! update && ! archive->command->propAddCanReplace)
+ || (update && ! archive->command->propAddCanUpdate))
+ {
+ GList *del_list = NULL;
+
+ for (scan = new_file_list; scan != NULL; scan = scan->next) {
+ char *filename = scan->data;
+ if (find_file_in_archive (archive, filename))
+ del_list = g_list_prepend (del_list, filename);
+ }
+
+ /* delete */
+
+ if (del_list != NULL) {
+ delete_from_archive (archive, del_list);
+ fr_process_set_ignore_error (archive->process, TRUE);
+ g_list_free (del_list);
+ }
+ }
+
+ /* add now. */
+
+ fr_command_set_n_files (archive->command, g_list_length (new_file_list));
+
+ if (archive->command->propListFromFile
+ && (archive->command->n_files > LIST_LENGTH_TO_USE_FILE))
+ {
+ char *list_dir;
+ char *list_filename;
+ GError *error = NULL;
+
+ if (! save_list_to_temp_file (new_file_list, &list_dir, &list_filename, &error)) {
+ archive->process->error.type = FR_PROC_ERROR_GENERIC;
+ archive->process->error.status = 0;
+ archive->process->error.gerror = g_error_copy (error);
+ g_signal_emit_by_name (G_OBJECT (archive->process),
+ "done",
+ FR_ACTION_ADDING_FILES);
+ g_clear_error (&error);
+ error_occurred = TRUE;
+ }
+ else {
+ fr_command_add (archive->command,
+ list_filename,
+ new_file_list,
+ tmp_base_dir,
+ update,
+ recursive);
+
+ /* remove the temp 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, list_dir);
+ fr_process_end_command (archive->process);
+ }
+
+ g_free (list_filename);
+ g_free (list_dir);
+ }
+ else {
+ GList *chunks = NULL;
+
+ /* specify the file list on the command line, splitting
+ * in more commands to avoid to overflow the command line
+ * length limit. */
+
+ chunks = split_in_chunks (new_file_list);
+ for (scan = chunks; scan != NULL; scan = scan->next) {
+ GList *chunk = scan->data;
+
+ fr_command_add (archive->command,
+ NULL,
+ chunk,
+ tmp_base_dir,
+ update,
+ recursive);
+ g_list_free (chunk);
+ }
+
+ g_list_free (chunks);
+ }
+
+ path_list_free (new_file_list);
+
+ if (! error_occurred) {
+ fr_command_recompress (archive->command);
+
+ if (base_dir_created) { /* remove the temp 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_base_dir);
+ fr_process_end_command (archive->process);
+ }
+ }
+
+ g_free (tmp_base_dir);
+}
+
+
+static void
+fr_archive_add_local_files (FrArchive *archive,
+ GList *file_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size)
+{
+ fr_archive_stoppable (archive, TRUE);
+ fr_process_clear (archive->process);
+ fr_archive_add (archive,
+ file_list,
+ base_dir,
+ dest_dir,
+ update,
+ FALSE,
+ password,
+ encrypt_header,
+ compression,
+ volume_size);
+ fr_process_start (archive->process);
+}
+
+
+static void
+copy_remote_files_done (GError *error,
+ gpointer user_data)
+{
+ XferData *xfer_data = user_data;
+
+ fr_archive_copy_done (xfer_data->archive, FR_ACTION_COPYING_FILES_FROM_REMOTE, error);
+
+ if (error == NULL)
+ fr_archive_add_local_files (xfer_data->archive,
+ xfer_data->file_list,
+ xfer_data->tmp_dir,
+ xfer_data->dest_dir,
+ FALSE,
+ xfer_data->password,
+ xfer_data->encrypt_header,
+ xfer_data->compression,
+ xfer_data->volume_size);
+ xfer_data_free (xfer_data);
+}
+
+
+static void
+copy_remote_files_progress (goffset current_file,
+ goffset total_files,
+ GFile *source,
+ GFile *destination,
+ goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ XferData *xfer_data = user_data;
+
+ g_signal_emit (G_OBJECT (xfer_data->archive),
+ fr_archive_signals[PROGRESS],
+ 0,
+ (double) current_file / (total_files + 1));
+}
+
+
+static void
+copy_remote_files (FrArchive *archive,
+ GList *file_list,
+ const char *base_uri,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size,
+ const char *tmp_dir)
+{
+ GList *sources = NULL, *destinations = NULL;
+ GHashTable *created_folders;
+ GList *scan;
+ XferData *xfer_data;
+
+ created_folders = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
+ for (scan = file_list; scan; scan = scan->next) {
+ char *partial_filename = scan->data;
+ char *local_uri;
+ char *local_folder_uri;
+ char *remote_uri;
+
+ local_uri = g_strconcat ("file://", tmp_dir, "/", partial_filename, NULL);
+ local_folder_uri = remove_level_from_path (local_uri);
+ if (g_hash_table_lookup (created_folders, local_folder_uri) == NULL) {
+ GError *error = NULL;
+ if (! ensure_dir_exists (local_folder_uri, 0755, &error)) {
+ g_free (local_folder_uri);
+ g_free (local_uri);
+ gio_file_list_free (sources);
+ gio_file_list_free (destinations);
+ g_hash_table_destroy (created_folders);
+
+ fr_archive_action_completed (archive,
+ FR_ACTION_COPYING_FILES_FROM_REMOTE,
+ FR_PROC_ERROR_GENERIC,
+ error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ g_hash_table_insert (created_folders, local_folder_uri, GINT_TO_POINTER (1));
+ }
+ else
+ g_free (local_folder_uri);
+
+ remote_uri = g_strconcat (base_uri, "/", partial_filename, NULL);
+ sources = g_list_append (sources, g_file_new_for_uri (remote_uri));
+ g_free (remote_uri);
+
+ destinations = g_list_append (destinations, g_file_new_for_uri (local_uri));
+ g_free (local_uri);
+ }
+ g_hash_table_destroy (created_folders);
+
+ xfer_data = g_new0 (XferData, 1);
+ xfer_data->archive = archive;
+ xfer_data->file_list = path_list_dup (file_list);
+ xfer_data->base_uri = g_strdup (base_uri);
+ xfer_data->dest_dir = g_strdup (dest_dir);
+ xfer_data->update = update;
+ xfer_data->dest_dir = g_strdup (dest_dir);
+ xfer_data->password = g_strdup (password);
+ xfer_data->encrypt_header = encrypt_header;
+ xfer_data->compression = compression;
+ xfer_data->volume_size = volume_size;
+ xfer_data->tmp_dir = g_strdup (tmp_dir);
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[START],
+ 0,
+ FR_ACTION_COPYING_FILES_FROM_REMOTE);
+
+ g_copy_files_async (sources,
+ destinations,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ archive->priv->cancellable,
+ copy_remote_files_progress,
+ xfer_data,
+ copy_remote_files_done,
+ xfer_data);
+
+ gio_file_list_free (sources);
+ gio_file_list_free (destinations);
+}
+
+
+static char *
+fr_archive_get_temp_work_dir (FrArchive *archive)
+{
+ fr_archive_remove_temp_work_dir (archive);
+ archive->priv->temp_dir = get_temp_work_dir (NULL);
+ return archive->priv->temp_dir;
+}
+
+
+void
+fr_archive_add_files (FrArchive *archive,
+ GList *file_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size)
+{
+ if (uri_is_local (base_dir)) {
+ char *local_dir = g_filename_from_uri (base_dir, NULL, NULL);
+ fr_archive_add_local_files (archive,
+ file_list,
+ local_dir,
+ dest_dir,
+ update,
+ password,
+ encrypt_header,
+ compression,
+ volume_size);
+ g_free (local_dir);
+ }
+ else
+ copy_remote_files (archive,
+ file_list,
+ base_dir,
+ dest_dir,
+ update,
+ password,
+ encrypt_header,
+ compression,
+ volume_size,
+ fr_archive_get_temp_work_dir (archive));
+}
+
+
+/* -- add with wildcard -- */
+
+
+typedef struct {
+ FrArchive *archive;
+ char *source_dir;
+ char *dest_dir;
+ gboolean update;
+ char *password;
+ gboolean encrypt_header;
+ FrCompression compression;
+ guint volume_size;
+} AddWithWildcardData;
+
+
+static void
+add_with_wildcard_data_free (AddWithWildcardData *aww_data)
+{
+ g_free (aww_data->source_dir);
+ g_free (aww_data->dest_dir);
+ g_free (aww_data->password);
+ g_free (aww_data);
+}
+
+
+static void
+add_with_wildcard__step2 (GList *file_list,
+ GList *dirs_list,
+ GError *error,
+ gpointer data)
+{
+ AddWithWildcardData *aww_data = data;
+ FrArchive *archive = aww_data->archive;
+
+ if (error != NULL) {
+ fr_archive_action_completed (archive,
+ FR_ACTION_GETTING_FILE_LIST,
+ (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC),
+ error->message);
+ return;
+ }
+
+ fr_archive_action_completed (archive,
+ FR_ACTION_GETTING_FILE_LIST,
+ FR_PROC_ERROR_NONE,
+ NULL);
+
+ if (file_list != NULL)
+ fr_archive_add_files (aww_data->archive,
+ file_list,
+ aww_data->source_dir,
+ aww_data->dest_dir,
+ aww_data->update,
+ aww_data->password,
+ aww_data->encrypt_header,
+ aww_data->compression,
+ aww_data->volume_size);
+
+ path_list_free (file_list);
+ path_list_free (dirs_list);
+ add_with_wildcard_data_free (aww_data);
+}
+
+
+void
+fr_archive_add_with_wildcard (FrArchive *archive,
+ const char *include_files,
+ const char *exclude_files,
+ const char *exclude_folders,
+ const char *source_dir,
+ const char *dest_dir,
+ gboolean update,
+ gboolean follow_links,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size)
+{
+ AddWithWildcardData *aww_data;
+
+ g_return_if_fail (! archive->read_only);
+
+ aww_data = g_new0 (AddWithWildcardData, 1);
+ aww_data->archive = archive;
+ aww_data->source_dir = g_strdup (source_dir);
+ aww_data->dest_dir = g_strdup (dest_dir);
+ aww_data->update = update;
+ aww_data->password = g_strdup (password);
+ aww_data->encrypt_header = encrypt_header;
+ aww_data->compression = compression;
+ aww_data->volume_size = volume_size;
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[START],
+ 0,
+ FR_ACTION_GETTING_FILE_LIST);
+
+ g_directory_list_async (source_dir,
+ source_dir,
+ TRUE,
+ follow_links,
+ NO_BACKUP_FILES,
+ NO_DOT_FILES,
+ include_files,
+ exclude_files,
+ exclude_folders,
+ IGNORE_CASE,
+ archive->priv->cancellable,
+ add_with_wildcard__step2,
+ aww_data);
+}
+
+
+/* -- fr_archive_add_directory -- */
+
+
+typedef struct {
+ FrArchive *archive;
+ char *base_dir;
+ char *dest_dir;
+ gboolean update;
+ char *password;
+ gboolean encrypt_header;
+ FrCompression compression;
+ guint volume_size;
+} AddDirectoryData;
+
+
+static void
+add_directory_data_free (AddDirectoryData *ad_data)
+{
+ g_free (ad_data->base_dir);
+ g_free (ad_data->dest_dir);
+ g_free (ad_data->password);
+ g_free (ad_data);
+}
+
+
+static void
+add_directory__step2 (GList *file_list,
+ GList *dir_list,
+ GError *error,
+ gpointer data)
+{
+ AddDirectoryData *ad_data = data;
+ FrArchive *archive = ad_data->archive;
+
+ if (error != NULL) {
+ fr_archive_action_completed (archive,
+ FR_ACTION_GETTING_FILE_LIST,
+ (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ? FR_PROC_ERROR_STOPPED : FR_PROC_ERROR_GENERIC),
+ error->message);
+ return;
+ }
+
+ fr_archive_action_completed (archive,
+ FR_ACTION_GETTING_FILE_LIST,
+ FR_PROC_ERROR_NONE,
+ NULL);
+
+ if (archive->command->propAddCanStoreFolders)
+ file_list = g_list_concat (file_list, dir_list);
+ else
+ path_list_free (dir_list);
+
+ if (file_list != NULL) {
+ fr_archive_add_files (ad_data->archive,
+ file_list,
+ ad_data->base_dir,
+ ad_data->dest_dir,
+ ad_data->update,
+ ad_data->password,
+ ad_data->encrypt_header,
+ ad_data->compression,
+ ad_data->volume_size);
+ path_list_free (file_list);
+ }
+
+ add_directory_data_free (ad_data);
+}
+
+
+void
+fr_archive_add_directory (FrArchive *archive,
+ const char *directory,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size)
+
+{
+ AddDirectoryData *ad_data;
+
+ g_return_if_fail (! archive->read_only);
+
+ ad_data = g_new0 (AddDirectoryData, 1);
+ ad_data->archive = archive;
+ ad_data->base_dir = g_strdup (base_dir);
+ ad_data->dest_dir = g_strdup (dest_dir);
+ ad_data->update = update;
+ ad_data->password = g_strdup (password);
+ ad_data->encrypt_header = encrypt_header;
+ ad_data->compression = compression;
+ ad_data->volume_size = volume_size;
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[START],
+ 0,
+ FR_ACTION_GETTING_FILE_LIST);
+
+ g_directory_list_all_async (directory,
+ base_dir,
+ TRUE,
+ archive->priv->cancellable,
+ add_directory__step2,
+ ad_data);
+}
+
+
+void
+fr_archive_add_items (FrArchive *archive,
+ GList *item_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size)
+
+{
+ AddDirectoryData *ad_data;
+
+ g_return_if_fail (! archive->read_only);
+
+ ad_data = g_new0 (AddDirectoryData, 1);
+ ad_data->archive = archive;
+ ad_data->base_dir = g_strdup (base_dir);
+ ad_data->dest_dir = g_strdup (dest_dir);
+ ad_data->update = update;
+ ad_data->password = g_strdup (password);
+ ad_data->encrypt_header = encrypt_header;
+ ad_data->compression = compression;
+ ad_data->volume_size = volume_size;
+
+ g_signal_emit (G_OBJECT (archive),
+ fr_archive_signals[START],
+ 0,
+ FR_ACTION_GETTING_FILE_LIST);
+
+ g_list_items_async (item_list,
+ base_dir,
+ archive->priv->cancellable,
+ add_directory__step2,
+ ad_data);
+}
+
+
+/* -- fr_archive_add_dropped_items -- */
+
+
+static gboolean
+all_files_in_same_dir (GList *list)
+{
+ gboolean same_dir = TRUE;
+ char *first_basedir;
+ GList *scan;
+
+ if (list == NULL)
+ return FALSE;
+
+ first_basedir = remove_level_from_path (list->data);
+ if (first_basedir == NULL)
+ return TRUE;
+
+ for (scan = list->next; scan; scan = scan->next) {
+ char *path = scan->data;
+ char *basedir;
+
+ basedir = remove_level_from_path (path);
+ if (basedir == NULL) {
+ same_dir = FALSE;
+ break;
+ }
+
+ if (strcmp (first_basedir, basedir) != 0) {
+ same_dir = FALSE;
+ g_free (basedir);
+ break;
+ }
+ g_free (basedir);
+ }
+ g_free (first_basedir);
+
+ return same_dir;
+}
+
+
+static void
+add_dropped_items (DroppedItemsData *data)
+{
+ FrArchive *archive = data->archive;
+ GList *list = data->item_list;
+ GList *scan;
+
+ if (list == NULL) {
+ dropped_items_data_free (archive->priv->dropped_items_data);
+ archive->priv->dropped_items_data = NULL;
+ fr_archive_action_completed (archive,
+ FR_ACTION_ADDING_FILES,
+ FR_PROC_ERROR_NONE,
+ NULL);
+ return;
+ }
+
+ /* if all files/dirs are in the same directory call fr_archive_add_items... */
+
+ if (all_files_in_same_dir (list)) {
+ char *first_base_dir;
+
+ first_base_dir = remove_level_from_path (list->data);
+ fr_archive_add_items (data->archive,
+ list,
+ first_base_dir,
+ data->dest_dir,
+ data->update,
+ data->password,
+ data->encrypt_header,
+ data->compression,
+ data->volume_size);
+ g_free (first_base_dir);
+
+ dropped_items_data_free (archive->priv->dropped_items_data);
+ archive->priv->dropped_items_data = NULL;
+
+ return;
+ }
+
+ /* ...else add a directory at a time. */
+
+ for (scan = list; scan; scan = scan->next) {
+ char *path = scan->data;
+ char *base_dir;
+
+ if (! uri_is_dir (path))
+ continue;
+
+ data->item_list = g_list_remove_link (list, scan);
+ if (data->item_list != NULL)
+ archive->priv->continue_adding_dropped_items = TRUE;
+ base_dir = remove_level_from_path (path);
+
+ fr_archive_add_directory (archive,
+ file_name_from_path (path),
+ base_dir,
+ data->dest_dir,
+ data->update,
+ data->password,
+ data->encrypt_header,
+ data->compression,
+ data->volume_size);
+
+ g_free (base_dir);
+ g_free (path);
+
+ return;
+ }
+
+ /* if all files are in the same directory call fr_archive_add_files. */
+
+ if (all_files_in_same_dir (list)) {
+ char *first_basedir;
+ GList *only_names_list = NULL;
+
+ first_basedir = remove_level_from_path (list->data);
+
+ for (scan = list; scan; scan = scan->next) {
+ char *name;
+
+ name = g_uri_unescape_string (file_name_from_path (scan->data), NULL);
+ only_names_list = g_list_prepend (only_names_list, name);
+ }
+
+ fr_archive_add_files (archive,
+ only_names_list,
+ first_basedir,
+ data->dest_dir,
+ data->update,
+ data->password,
+ data->encrypt_header,
+ data->compression,
+ data->volume_size);
+
+ path_list_free (only_names_list);
+ g_free (first_basedir);
+
+ return;
+ }
+
+ /* ...else call fr_command_add for each file. This is needed to add
+ * files without path info. FIXME: doesn't work with remote files. */
+
+ fr_archive_stoppable (archive, FALSE);
+ g_object_set (archive->command,
+ "password", data->password,
+ "encrypt_header", data->encrypt_header,
+ "compression", data->compression,
+ "volume_size", data->volume_size,
+ NULL);
+ fr_process_clear (archive->process);
+ fr_command_uncompress (archive->command);
+ for (scan = list; scan; scan = scan->next) {
+ char *fullpath = scan->data;
+ char *basedir;
+ GList *singleton;
+
+ basedir = remove_level_from_path (fullpath);
+ singleton = g_list_prepend (NULL, (char*)file_name_from_path (fullpath));
+ fr_command_add (archive->command,
+ NULL,
+ singleton,
+ basedir,
+ data->update,
+ FALSE);
+ g_list_free (singleton);
+ g_free (basedir);
+ }
+ fr_command_recompress (archive->command);
+ fr_process_start (archive->process);
+
+ path_list_free (data->item_list);
+ data->item_list = NULL;
+}
+
+
+void
+fr_archive_add_dropped_items (FrArchive *archive,
+ GList *item_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size)
+{
+ GList *scan;
+ char *archive_uri;
+
+ if (archive->read_only) {
+ fr_archive_action_completed (archive,
+ FR_ACTION_ADDING_FILES,
+ FR_PROC_ERROR_GENERIC,
+ ! archive->have_permissions ? _("You don't have the right permissions.") : _("This archive type cannot be modified"));
+ return;
+ }
+
+ /* FIXME: make this check for all the add actions */
+ archive_uri = g_file_get_uri (archive->file);
+ for (scan = item_list; scan; scan = scan->next) {
+ if (uricmp (scan->data, archive_uri) == 0) {
+ g_free (archive_uri);
+ fr_archive_action_completed (archive,
+ FR_ACTION_ADDING_FILES,
+ FR_PROC_ERROR_GENERIC,
+ _("You can't add an archive to itself."));
+ return;
+ }
+ }
+ g_free (archive_uri);
+
+ if (archive->priv->dropped_items_data != NULL)
+ dropped_items_data_free (archive->priv->dropped_items_data);
+ archive->priv->dropped_items_data = dropped_items_data_new (
+ archive,
+ item_list,
+ base_dir,
+ dest_dir,
+ update,
+ password,
+ encrypt_header,
+ compression,
+ volume_size);
+ add_dropped_items (archive->priv->dropped_items_data);
+}
+
+
+/* -- remove -- */
+
+
+static gboolean
+file_is_in_subfolder_of (const char *filename,
+ GList *folder_list)
+{
+ GList *scan;
+
+ if (filename == NULL)
+ return FALSE;
+
+ for (scan = folder_list; scan; scan = scan->next) {
+ char *folder_in_list = (char*) scan->data;
+
+ if (path_in_path (folder_in_list, filename))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+archive_type_has_issues_deleting_non_empty_folders (FrArchive *archive)
+{
+ return ! archive->command->propCanDeleteNonEmptyFolders;
+}
+
+
+static void
+delete_from_archive (FrArchive *archive,
+ GList *file_list)
+{
+ gboolean file_list_created = FALSE;
+ GList *tmp_file_list = NULL;
+ gboolean tmp_file_list_created = FALSE;
+ GList *scan;
+
+ /* file_list == NULL means delete all the files in the archive. */
+
+ if (file_list == NULL) {
+ int i;
+
+ for (i = 0; i < archive->command->files->len; i++) {
+ FileData *fdata = g_ptr_array_index (archive->command->files, i);
+ file_list = g_list_prepend (file_list, fdata->original_path);
+ }
+
+ file_list_created = TRUE;
+ }
+
+ if (archive_type_has_issues_deleting_non_empty_folders (archive)) {
+ GList *folders_to_remove;
+
+ /* remove from the list the files contained in folders to be
+ * removed. */
+
+ folders_to_remove = NULL;
+ for (scan = file_list; scan != NULL; scan = scan->next) {
+ char *path = scan->data;
+
+ if (path[strlen (path) - 1] == '/')
+ folders_to_remove = g_list_prepend (folders_to_remove, path);
+ }
+
+ if (folders_to_remove != NULL) {
+ tmp_file_list = NULL;
+ for (scan = file_list; scan != NULL; scan = scan->next) {
+ char *path = scan->data;
+
+ if (! file_is_in_subfolder_of (path, folders_to_remove))
+ tmp_file_list = g_list_prepend (tmp_file_list, path);
+ }
+ tmp_file_list_created = TRUE;
+ g_list_free (folders_to_remove);
+ }
+ }
+
+ if (! tmp_file_list_created)
+ tmp_file_list = g_list_copy (file_list);
+
+ if (file_list_created)
+ g_list_free (file_list);
+
+ fr_command_set_n_files (archive->command, g_list_length (tmp_file_list));
+
+ if (archive->command->propListFromFile
+ && (archive->command->n_files > LIST_LENGTH_TO_USE_FILE))
+ {
+ char *list_dir;
+ char *list_filename;
+
+ if (save_list_to_temp_file (tmp_file_list, &list_dir, &list_filename, NULL)) {
+ fr_command_delete (archive->command,
+ list_filename,
+ tmp_file_list);
+
+ /* remove the temp 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, list_dir);
+ fr_process_end_command (archive->process);
+ }
+
+ g_free (list_filename);
+ g_free (list_dir);
+ }
+ else {
+ for (scan = tmp_file_list; scan != NULL; ) {
+ GList *prev = scan->prev;
+ GList *chunk_list;
+ int l;
+
+ chunk_list = scan;
+ l = 0;
+ while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
+ if (l == 0)
+ l = strlen (scan->data);
+ prev = scan;
+ scan = scan->next;
+ if (scan != NULL)
+ l += strlen (scan->data);
+ }
+
+ prev->next = NULL;
+ fr_command_delete (archive->command, NULL, chunk_list);
+ prev->next = scan;
+ }
+ }
+
+ g_list_free (tmp_file_list);
+}
+
+
+void
+fr_archive_remove (FrArchive *archive,
+ GList *file_list,
+ FrCompression compression)
+{
+ g_return_if_fail (archive != NULL);
+
+ if (archive->read_only)
+ return;
+
+ fr_archive_stoppable (archive, FALSE);
+ g_object_set (archive->command, "compression", compression, NULL);
+ fr_command_uncompress (archive->command);
+ delete_from_archive (archive, file_list);
+ fr_command_recompress (archive->command);
+}
+
+
+/* -- extract -- */
+
+
+static void
+move_files_to_dir (FrArchive *archive,
+ GList *file_list,
+ const char *source_dir,
+ const char *dest_dir,
+ gboolean overwrite)
+{
+ GList *list;
+ GList *scan;
+
+ /* we prefer mv instead of cp for performance reasons,
+ * but if the destination folder already exists mv
+ * doesn't work correctly. (bug #590027) */
+
+ list = g_list_copy (file_list);
+ for (scan = list; scan; /* void */) {
+ GList *next = scan->next;
+ char *filename = scan->data;
+ char *basename;
+ char *destname;
+
+ basename = g_path_get_basename (filename);
+ destname = g_build_filename (dest_dir, basename, NULL);
+ if (g_file_test (destname, G_FILE_TEST_IS_DIR)) {
+ fr_process_begin_command (archive->process, "cp");
+ fr_process_add_arg (archive->process, "-R");
+ if (overwrite)
+ fr_process_add_arg (archive->process, "-f");
+ else
+ fr_process_add_arg (archive->process, "-n");
+ if (filename[0] == '/')
+ fr_process_add_arg_concat (archive->process, source_dir, filename, NULL);
+ else
+ fr_process_add_arg_concat (archive->process, source_dir, "/", filename, NULL);
+ fr_process_add_arg (archive->process, dest_dir);
+ fr_process_end_command (archive->process);
+
+ list = g_list_remove_link (list, scan);
+ g_list_free (scan);
+ }
+
+ g_free (destname);
+ g_free (basename);
+
+ scan = next;
+ }
+
+ if (list == NULL)
+ return;
+
+ /* 'list' now contains the files that can be moved without problems */
+
+ fr_process_begin_command (archive->process, "mv");
+ if (overwrite)
+ fr_process_add_arg (archive->process, "-f");
+ else
+ fr_process_add_arg (archive->process, "-n");
+ for (scan = list; scan; scan = scan->next) {
+ char *filename = scan->data;
+
+ if (filename[0] == '/')
+ fr_process_add_arg_concat (archive->process, source_dir, filename, NULL);
+ else
+ fr_process_add_arg_concat (archive->process, source_dir, "/", filename, NULL);
+ }
+ fr_process_add_arg (archive->process, dest_dir);
+ fr_process_end_command (archive->process);
+
+ g_list_free (list);
+}
+
+
+static void
+move_files_in_chunks (FrArchive *archive,
+ GList *file_list,
+ const char *temp_dir,
+ const char *dest_dir,
+ gboolean overwrite)
+{
+ GList *scan;
+ int temp_dir_l;
+
+ temp_dir_l = strlen (temp_dir);
+
+ for (scan = file_list; scan != NULL; ) {
+ GList *prev = scan->prev;
+ GList *chunk_list;
+ int l;
+
+ chunk_list = scan;
+ l = 0;
+ while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
+ if (l == 0)
+ l = temp_dir_l + 1 + strlen (scan->data);
+ prev = scan;
+ scan = scan->next;
+ if (scan != NULL)
+ l += temp_dir_l + 1 + strlen (scan->data);
+ }
+
+ prev->next = NULL;
+ move_files_to_dir (archive, chunk_list, temp_dir, dest_dir, overwrite);
+ prev->next = scan;
+ }
+}
+
+
+static void
+extract_from_archive (FrArchive *archive,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths,
+ const char *password)
+{
+ FrCommand *command = archive->command;
+ GList *scan;
+
+ g_object_set (command, "password", password, NULL);
+
+ if (file_list == NULL) {
+ fr_command_extract (command,
+ NULL,
+ file_list,
+ dest_dir,
+ overwrite,
+ skip_older,
+ junk_paths);
+ return;
+ }
+
+ if (command->propListFromFile
+ && (g_list_length (file_list) > LIST_LENGTH_TO_USE_FILE))
+ {
+ char *list_dir;
+ char *list_filename;
+
+ if (save_list_to_temp_file (file_list, &list_dir, &list_filename, NULL)) {
+ fr_command_extract (command,
+ list_filename,
+ file_list,
+ dest_dir,
+ overwrite,
+ skip_older,
+ junk_paths);
+
+ /* remove the temp 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, list_dir);
+ fr_process_end_command (archive->process);
+ }
+
+ g_free (list_filename);
+ g_free (list_dir);
+ }
+ else {
+ for (scan = file_list; scan != NULL; ) {
+ GList *prev = scan->prev;
+ GList *chunk_list;
+ int l;
+
+ chunk_list = scan;
+ l = 0;
+ while ((scan != NULL) && (l < MAX_CHUNK_LEN)) {
+ if (l == 0)
+ l = strlen (scan->data);
+ prev = scan;
+ scan = scan->next;
+ if (scan != NULL)
+ l += strlen (scan->data);
+ }
+
+ prev->next = NULL;
+ fr_command_extract (command,
+ NULL,
+ chunk_list,
+ dest_dir,
+ overwrite,
+ skip_older,
+ junk_paths);
+ prev->next = scan;
+ }
+ }
+}
+
+
+static char*
+compute_base_path (const char *base_dir,
+ const char *path,
+ gboolean junk_paths,
+ gboolean can_junk_paths)
+{
+ int base_dir_len = strlen (base_dir);
+ int path_len = strlen (path);
+ const char *base_path;
+ char *name_end;
+ char *new_path;
+
+ if (junk_paths) {
+ if (can_junk_paths)
+ new_path = g_strdup (file_name_from_path (path));
+ else
+ new_path = g_strdup (path);
+
+ /*debug (DEBUG_INFO, "%s, %s --> %s\n", base_dir, path, new_path);*/
+
+ return new_path;
+ }
+
+ if (path_len <= base_dir_len)
+ return NULL;
+
+ base_path = path + base_dir_len;
+ if (path[0] != '/')
+ base_path -= 1;
+ name_end = strchr (base_path, '/');
+
+ if (name_end == NULL)
+ new_path = g_strdup (path);
+ else {
+ int name_len = name_end - path;
+ new_path = g_strndup (path, name_len);
+ }
+
+ /*debug (DEBUG_INFO, "%s, %s --> %s\n", base_dir, path, new_path);*/
+
+ return new_path;
+}
+
+
+static GList*
+compute_list_base_path (const char *base_dir,
+ GList *filtered,
+ gboolean junk_paths,
+ gboolean can_junk_paths)
+{
+ GList *scan;
+ GList *list = NULL, *list_unique = NULL;
+ GList *last_inserted;
+
+ if (filtered == NULL)
+ return NULL;
+
+ for (scan = filtered; scan; scan = scan->next) {
+ const char *path = scan->data;
+ char *new_path;
+
+ new_path = compute_base_path (base_dir, path, junk_paths, can_junk_paths);
+ if (new_path != NULL)
+ list = g_list_prepend (list, new_path);
+ }
+
+ /* The above operation can create duplicates, we remove them here. */
+ list = g_list_sort (list, (GCompareFunc)strcmp);
+
+ last_inserted = NULL;
+ for (scan = list; scan; scan = scan->next) {
+ const char *path = scan->data;
+
+ if (last_inserted != NULL) {
+ const char *last_path = (const char*)last_inserted->data;
+ if (strcmp (last_path, path) == 0) {
+ g_free (scan->data);
+ continue;
+ }
+ }
+
+ last_inserted = scan;
+ list_unique = g_list_prepend (list_unique, scan->data);
+ }
+
+ g_list_free (list);
+
+ return list_unique;
+}
+
+
+static gboolean
+archive_type_has_issues_extracting_non_empty_folders (FrArchive *archive)
+{
+ /*if ((archive->command->files == NULL) || (archive->command->files->len == 0))
+ return FALSE; FIXME: test with extract_here */
+ return ! archive->command->propCanExtractNonEmptyFolders;
+}
+
+
+static gboolean
+file_list_contains_files_in_this_dir (GList *file_list,
+ const char *dirname)
+{
+ GList *scan;
+
+ for (scan = file_list; scan; scan = scan->next) {
+ char *filename = scan->data;
+
+ if (path_in_path (dirname, filename))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static GList*
+remove_files_contained_in_this_dir (GList *file_list,
+ GList *dir_pointer)
+{
+ char *dirname = dir_pointer->data;
+ int dirname_l = strlen (dirname);
+ GList *scan;
+
+ for (scan = dir_pointer->next; scan; /* empty */) {
+ char *filename = scan->data;
+
+ if (strncmp (dirname, filename, dirname_l) != 0)
+ break;
+
+ if (path_in_path (dirname, filename)) {
+ GList *next = scan->next;
+
+ file_list = g_list_remove_link (file_list, scan);
+ g_list_free (scan);
+
+ scan = next;
+ }
+ else
+ scan = scan->next;
+ }
+
+ return file_list;
+}
+
+
+void
+fr_archive_extract_to_local (FrArchive *archive,
+ GList *file_list,
+ const char *destination,
+ const char *base_dir,
+ gboolean skip_older,
+ gboolean overwrite,
+ gboolean junk_paths,
+ const char *password)
+{
+ GList *filtered;
+ GList *scan;
+ gboolean extract_all;
+ gboolean use_base_dir;
+ gboolean all_options_supported;
+ gboolean move_to_dest_dir;
+ gboolean file_list_created = FALSE;
+
+ g_return_if_fail (archive != NULL);
+
+ fr_archive_stoppable (archive, TRUE);
+
+ /* if a command supports all the requested options use
+ * fr_command_extract directly. */
+
+ use_base_dir = ! ((base_dir == NULL)
+ || (strcmp (base_dir, "") == 0)
+ || (strcmp (base_dir, "/") == 0));
+
+ all_options_supported = (! use_base_dir
+ && ! (! overwrite && ! archive->command->propExtractCanAvoidOverwrite)
+ && ! (skip_older && ! archive->command->propExtractCanSkipOlder)
+ && ! (junk_paths && ! archive->command->propExtractCanJunkPaths));
+
+ extract_all = (file_list == NULL);
+ if (extract_all && (! all_options_supported || ! archive->command->propCanExtractAll)) {
+ int i;
+
+ file_list = NULL;
+ for (i = 0; i < archive->command->files->len; i++) {
+ FileData *fdata = g_ptr_array_index (archive->command->files, i);
+ file_list = g_list_prepend (file_list, g_strdup (fdata->original_path));
+ }
+ file_list_created = TRUE;
+ }
+
+ if (extract_all && (file_list == NULL))
+ fr_command_set_n_files (archive->command, archive->command->n_regular_files);
+ else
+ fr_command_set_n_files (archive->command, g_list_length (file_list));
+
+ if (all_options_supported) {
+ gboolean created_filtered_list = FALSE;
+
+ if (! extract_all && archive_type_has_issues_extracting_non_empty_folders (archive)) {
+ created_filtered_list = TRUE;
+ filtered = g_list_copy (file_list);
+ filtered = g_list_sort (filtered, (GCompareFunc) strcmp);
+ for (scan = filtered; scan; scan = scan->next)
+ filtered = remove_files_contained_in_this_dir (filtered, scan);
+ }
+ else
+ filtered = file_list;
+
+ if (! (created_filtered_list && (filtered == NULL)))
+ extract_from_archive (archive,
+ filtered,
+ destination,
+ overwrite,
+ skip_older,
+ junk_paths,
+ password);
+
+ if (created_filtered_list && (filtered != NULL))
+ g_list_free (filtered);
+
+ if (file_list_created)
+ path_list_free (file_list);
+
+ return;
+ }
+
+ /* .. else we have to implement the unsupported options. */
+
+ move_to_dest_dir = (use_base_dir
+ || ((junk_paths
+ && ! archive->command->propExtractCanJunkPaths)));
+
+ if (extract_all && ! file_list_created) {
+ int i;
+
+ file_list = NULL;
+ for (i = 0; i < archive->command->files->len; i++) {
+ FileData *fdata = g_ptr_array_index (archive->command->files, i);
+ file_list = g_list_prepend (file_list, g_strdup (fdata->original_path));
+ }
+
+ file_list_created = TRUE;
+ }
+
+ filtered = NULL;
+ for (scan = file_list; scan; scan = scan->next) {
+ FileData *fdata;
+ char *archive_list_filename = scan->data;
+ char dest_filename[4096];
+ const char *filename;
+
+ fdata = find_file_in_archive (archive, archive_list_filename);
+
+ if (fdata == NULL)
+ continue;
+
+ if (archive_type_has_issues_extracting_non_empty_folders (archive)
+ && fdata->dir
+ && file_list_contains_files_in_this_dir (file_list, archive_list_filename))
+ continue;
+
+ /* get the destination file path. */
+
+ if (! junk_paths)
+ filename = archive_list_filename;
+ else
+ filename = file_name_from_path (archive_list_filename);
+
+ if ((destination[strlen (destination) - 1] == '/')
+ || (filename[0] == '/'))
+ sprintf (dest_filename, "%s%s", destination, filename);
+ else
+ sprintf (dest_filename, "%s/%s", destination, filename);
+
+ /*debug (DEBUG_INFO, "-> %s\n", dest_filename);*/
+
+ /**/
+
+ if (! archive->command->propExtractCanSkipOlder
+ && skip_older
+ && g_file_test (dest_filename, G_FILE_TEST_EXISTS)
+ && (fdata->modified < get_file_mtime_for_path (dest_filename)))
+ continue;
+
+ if (! archive->command->propExtractCanAvoidOverwrite
+ && ! overwrite
+ && g_file_test (dest_filename, G_FILE_TEST_EXISTS))
+ continue;
+
+ filtered = g_list_prepend (filtered, fdata->original_path);
+ }
+
+ if (filtered == NULL) {
+ /* all files got filtered, do nothing. */
+ debug (DEBUG_INFO, "All files got filtered, nothing to do.\n");
+
+ if (extract_all)
+ path_list_free (file_list);
+ return;
+ }
+
+ if (move_to_dest_dir) {
+ char *temp_dir;
+
+ temp_dir = get_temp_work_dir (destination);
+ extract_from_archive (archive,
+ filtered,
+ temp_dir,
+ overwrite,
+ skip_older,
+ junk_paths,
+ password);
+
+ if (use_base_dir) {
+ GList *tmp_list = compute_list_base_path (base_dir, filtered, junk_paths, archive->command->propExtractCanJunkPaths);
+ g_list_free (filtered);
+ filtered = tmp_list;
+ }
+
+ move_files_in_chunks (archive,
+ filtered,
+ temp_dir,
+ destination,
+ overwrite);
+
+ /* remove the temp dir. */
+
+ fr_process_begin_command (archive->process, "rm");
+ fr_process_add_arg (archive->process, "-rf");
+ fr_process_add_arg (archive->process, temp_dir);
+ fr_process_end_command (archive->process);
+
+ g_free (temp_dir);
+ }
+ else
+ extract_from_archive (archive,
+ filtered,
+ destination,
+ overwrite,
+ skip_older,
+ junk_paths,
+ password);
+
+ if (filtered != NULL)
+ g_list_free (filtered);
+ if (file_list_created)
+ path_list_free (file_list);
+}
+
+
+void
+fr_archive_extract (FrArchive *archive,
+ GList *file_list,
+ const char *destination,
+ const char *base_dir,
+ gboolean skip_older,
+ gboolean overwrite,
+ gboolean junk_paths,
+ const char *password)
+{
+ g_free (archive->priv->extraction_destination);
+ archive->priv->extraction_destination = g_strdup (destination);
+
+ g_free (archive->priv->temp_extraction_dir);
+ archive->priv->temp_extraction_dir = NULL;
+
+ archive->priv->remote_extraction = ! uri_is_local (destination);
+ if (archive->priv->remote_extraction) {
+ archive->priv->temp_extraction_dir = get_temp_work_dir (NULL);
+ fr_archive_extract_to_local (archive,
+ file_list,
+ archive->priv->temp_extraction_dir,
+ base_dir,
+ skip_older,
+ overwrite,
+ junk_paths,
+ password);
+ }
+ else {
+ char *local_destination;
+
+ local_destination = g_filename_from_uri (destination, NULL, NULL);
+ fr_archive_extract_to_local (archive,
+ file_list,
+ local_destination,
+ base_dir,
+ skip_older,
+ overwrite,
+ junk_paths,
+ password);
+ g_free (local_destination);
+ }
+}
+
+
+static char *
+get_desired_destination_for_archive (GFile *file)
+{
+ GFile *directory;
+ char *directory_uri;
+ char *name;
+ const char *ext;
+ char *new_name;
+ char *new_name_escaped;
+ char *desired_destination = NULL;
+
+ directory = g_file_get_parent (file);
+ directory_uri = g_file_get_uri (directory);
+
+ name = g_file_get_basename (file);
+ ext = get_archive_filename_extension (name);
+ if (ext == NULL)
+ /* if no extension is present add a suffix to the name... */
+ new_name = g_strconcat (name, "_FILES", NULL);
+ else
+ /* ...else use the name without the extension */
+ new_name = g_strndup (name, strlen (name) - strlen (ext));
+ new_name_escaped = g_uri_escape_string (new_name, "", FALSE);
+
+ desired_destination = g_strconcat (directory_uri, "/", new_name_escaped, NULL);
+
+ g_free (new_name_escaped);
+ g_free (new_name);
+ g_free (name);
+ g_free (directory_uri);
+ g_object_unref (directory);
+
+ return desired_destination;
+}
+
+
+static char *
+get_extract_here_destination (GFile *file,
+ GError **error)
+{
+ char *desired_destination;
+ char *destination = NULL;
+ int n = 1;
+ GFile *directory;
+
+ desired_destination = get_desired_destination_for_archive (file);
+ do {
+ *error = NULL;
+
+ g_free (destination);
+ if (n == 1)
+ destination = g_strdup (desired_destination);
+ else
+ destination = g_strdup_printf ("%s%%20(%d)", desired_destination, n);
+
+ directory = g_file_new_for_uri (destination);
+ g_file_make_directory (directory, NULL, error);
+ g_object_unref (directory);
+
+ n++;
+ } while (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_EXISTS));
+
+ g_free (desired_destination);
+
+ if (*error != NULL) {
+ g_warning ("could not create destination folder: %s\n", (*error)->message);
+ g_free (destination);
+ destination = NULL;
+ }
+
+ return destination;
+}
+
+
+gboolean
+fr_archive_extract_here (FrArchive *archive,
+ gboolean skip_older,
+ gboolean overwrite,
+ gboolean junk_path,
+ const char *password)
+{
+ char *destination;
+ GError *error = NULL;
+
+ destination = get_extract_here_destination (archive->file, &error);
+ if (error != NULL) {
+ fr_archive_action_completed (archive,
+ FR_ACTION_EXTRACTING_FILES,
+ FR_PROC_ERROR_GENERIC,
+ error->message);
+ g_clear_error (&error);
+ return FALSE;
+ }
+
+ archive->priv->extract_here = TRUE;
+ fr_archive_extract (archive,
+ NULL,
+ destination,
+ NULL,
+ skip_older,
+ overwrite,
+ junk_path,
+ password);
+
+ g_free (destination);
+
+ return TRUE;
+}
+
+
+const char *
+fr_archive_get_last_extraction_destination (FrArchive *archive)
+{
+ return archive->priv->extraction_destination;
+}
+
+
+void
+fr_archive_test (FrArchive *archive,
+ const char *password)
+{
+ fr_archive_stoppable (archive, TRUE);
+
+ g_object_set (archive->command, "password", password, NULL);
+ fr_process_clear (archive->process);
+ fr_command_set_n_files (archive->command, 0);
+ fr_command_test (archive->command);
+ fr_process_start (archive->process);
+}
+
+
+gboolean
+uri_is_archive (const char *uri)
+{
+ GFile *file;
+ const char *mime_type;
+ gboolean is_archive = FALSE;
+
+ file = g_file_new_for_uri (uri);
+ mime_type = get_mime_type_from_magic_numbers (file);
+ if (mime_type == NULL)
+ mime_type = get_mime_type_from_content (file);
+ if (mime_type == NULL)
+ mime_type = get_mime_type_from_filename (file);
+
+ if (mime_type != NULL) {
+ int i;
+
+ for (i = 0; mime_type_desc[i].mime_type != NULL; i++) {
+ if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0) {
+ is_archive = TRUE;
+ break;
+ }
+ }
+ }
+ g_object_unref (file);
+
+ return is_archive;
+}
diff --git a/src/fr-archive.h b/src/fr-archive.h
new file mode 100644
index 0000000..530e49e
--- /dev/null
+++ b/src/fr-archive.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ARCHIVE_H
+#define ARCHIVE_H
+
+#include <glib.h>
+#include "fr-process.h"
+#include "fr-command.h"
+
+#define FR_TYPE_ARCHIVE (fr_archive_get_type ())
+#define FR_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_ARCHIVE, FrArchive))
+#define FR_ARCHIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_ARCHIVE, FrArchiveClass))
+#define FR_IS_ARCHIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_ARCHIVE))
+#define FR_IS_ARCHIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_ARCHIVE))
+#define FR_ARCHIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_ARCHIVE, FrArchiveClass))
+
+typedef struct _FrArchive FrArchive;
+typedef struct _FrArchiveClass FrArchiveClass;
+typedef struct _FrArchivePrivData FrArchivePrivData;
+
+typedef gboolean (*FakeLoadFunc) (FrArchive *archive, gpointer data);
+
+struct _FrArchive {
+ GObject __parent;
+
+ GFile *file;
+ GFile *local_copy;
+ gboolean is_remote;
+ const char *content_type;
+ FrCommand *command;
+ FrProcess *process;
+ FrProcError error;
+ gboolean can_create_compressed_file;
+ gboolean is_compressed_file; /* Whether the file is not an
+ * archive that can contain
+ * many files but simply a
+ * compressed file, for
+ * example foo.txt.gz is a
+ * compressed file, foo.zip
+ * is not. */
+ gboolean read_only; /* Whether archive is
+ * read-only for whatever
+ * reason. */
+ gboolean have_permissions; /* true if we have the
+ * permissions to write the
+ * file. */
+
+ FrArchivePrivData *priv;
+};
+
+struct _FrArchiveClass {
+ GObjectClass __parent_class;
+
+ /* -- Signals -- */
+
+ void (*start) (FrArchive *archive,
+ FrAction action);
+ void (*done) (FrArchive *archive,
+ FrAction action,
+ FrProcError *error);
+ void (*progress) (FrArchive *archive,
+ double fraction);
+ void (*message) (FrArchive *archive,
+ const char *msg);
+ void (*stoppable) (FrArchive *archive,
+ gboolean value);
+ void (*working_archive) (FrCommand *comm,
+ const char *filename);
+};
+
+GType fr_archive_get_type (void);
+FrArchive * fr_archive_new (void);
+void fr_archive_set_fake_load_func (FrArchive *archive,
+ FakeLoadFunc func,
+ gpointer data);
+gboolean fr_archive_fake_load (FrArchive *archive);
+void fr_archive_set_add_is_stoppable_func (FrArchive *archive,
+ FakeLoadFunc func,
+ gpointer data);
+void fr_archive_stoppable (FrArchive *archive,
+ gboolean stoppable);
+void fr_archive_stop (FrArchive *archive);
+void fr_archive_action_completed (FrArchive *archive,
+ FrAction action,
+ FrProcErrorType error_type,
+ const char *error_details);
+
+/**/
+
+gboolean fr_archive_create (FrArchive *archive,
+ const char *uri);
+gboolean fr_archive_load (FrArchive *archive,
+ const char *uri,
+ const char *password);
+gboolean fr_archive_load_local (FrArchive *archive,
+ const char *uri,
+ const char *password);
+void fr_archive_reload (FrArchive *archive,
+ const char *password);
+void fr_archive_rename (FrArchive *archive,
+ const char *new_uri);
+
+/**/
+
+void fr_archive_add (FrArchive *archive,
+ GList *file_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ gboolean recursive,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size);
+void fr_archive_remove (FrArchive *archive,
+ GList *file_list,
+ FrCompression compression);
+void fr_archive_extract (FrArchive *archive,
+ GList *file_list,
+ const char *dest_uri,
+ const char *base_dir,
+ gboolean skip_older,
+ gboolean overwrite,
+ gboolean junk_path,
+ const char *password);
+void fr_archive_extract_to_local (FrArchive *archive,
+ GList *file_list,
+ const char *dest_path,
+ const char *base_dir,
+ gboolean skip_older,
+ gboolean overwrite,
+ gboolean junk_path,
+ const char *password);
+gboolean fr_archive_extract_here (FrArchive *archive,
+ gboolean skip_older,
+ gboolean overwrite,
+ gboolean junk_path,
+ const char *password);
+const char *fr_archive_get_last_extraction_destination
+ (FrArchive *archive);
+
+/**/
+
+void fr_archive_add_files (FrArchive *archive,
+ GList *file_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size);
+void fr_archive_add_with_wildcard (FrArchive *archive,
+ 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,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size);
+void fr_archive_add_directory (FrArchive *archive,
+ const char *directory,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size);
+void fr_archive_add_items (FrArchive *archive,
+ GList *item_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size);
+void fr_archive_add_dropped_items (FrArchive *archive,
+ GList *item_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update,
+ const char *password,
+ gboolean encrypt_header,
+ FrCompression compression,
+ guint volume_size);
+void fr_archive_test (FrArchive *archive,
+ const char *password);
+
+/* utilities */
+
+gboolean uri_is_archive (const char *uri);
+
+#endif /* ARCHIVE_H */
diff --git a/src/fr-command-7z.c b/src/fr-command-7z.c
new file mode 100644
index 0000000..76fe48d
--- /dev/null
+++ b/src/fr-command-7z.c
@@ -0,0 +1,686 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2004, 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-7z.h"
+
+static void fr_command_7z_class_init (FrCommand7zClass *class);
+static void fr_command_7z_init (FrCommand *afile);
+static void fr_command_7z_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+
+static time_t
+mktime_from_string (char *date_s,
+ char *time_s)
+{
+ struct tm tm = {0, };
+ char **fields;
+
+ tm.tm_isdst = -1;
+
+ /* date */
+
+ fields = g_strsplit (date_s, "-", 3);
+ if (fields[0] != NULL) {
+ tm.tm_year = atoi (fields[0]) - 1900;
+ tm.tm_mon = atoi (fields[1]) - 1;
+ tm.tm_mday = atoi (fields[2]);
+ }
+ g_strfreev (fields);
+
+ /* time */
+
+ fields = g_strsplit (time_s, ":", 3);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL) {
+ tm.tm_min = atoi (fields[1]);
+ if (fields[2] != NULL)
+ tm.tm_sec = atoi (fields[2]);
+ }
+ }
+ g_strfreev (fields);
+
+ return mktime (&tm);
+}
+
+
+static void
+list__process_line (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+ FrCommand7z *p7z_comm = FR_COMMAND_7Z (comm);
+ char **fields;
+ FileData *fdata;
+
+ g_return_if_fail (line != NULL);
+
+ if (! p7z_comm->list_started) {
+ if (strncmp (line, "p7zip Version ", 14) == 0) {
+ const char *ver_start;
+ int ver_len;
+ char version[256];
+
+ ver_start = eat_spaces (line + 14);
+ ver_len = strchr (ver_start, ' ') - ver_start;
+ strncpy (version, ver_start, ver_len);
+ version[ver_len] = 0;
+
+ if (strcmp (version, "4.55") < 0)
+ p7z_comm->old_style = TRUE;
+ else
+ p7z_comm->old_style = FALSE;
+ }
+ else if (p7z_comm->old_style && (strncmp (line, "Listing archive: ", 17) == 0))
+ p7z_comm->list_started = TRUE;
+ else if (! p7z_comm->old_style && (strcmp (line, "----------") == 0))
+ p7z_comm->list_started = TRUE;
+ else if (strncmp (line, "Multivolume = ", 14) == 0) {
+ fields = g_strsplit (line, " = ", 2);
+ comm->multi_volume = (strcmp (fields[1], "+") == 0);
+ g_strfreev (fields);
+ }
+ return;
+ }
+
+ if (strcmp (line, "") == 0) {
+ if (p7z_comm->fdata != NULL) {
+ if (p7z_comm->fdata->original_path == NULL) {
+ file_data_free (p7z_comm->fdata);
+ p7z_comm->fdata = NULL;
+ }
+ else {
+ fdata = p7z_comm->fdata;
+ if (fdata->dir)
+ fdata->name = dir_name_from_path (fdata->full_path);
+ else
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+ fr_command_add_file (comm, fdata);
+ p7z_comm->fdata = NULL;
+ }
+ }
+ return;
+ }
+
+ if (p7z_comm->fdata == NULL)
+ p7z_comm->fdata = file_data_new ();
+
+ fields = g_strsplit (line, " = ", 2);
+
+ if (g_strv_length (fields) < 2) {
+ g_strfreev (fields);
+ return;
+ }
+
+ fdata = p7z_comm->fdata;
+
+ if (strcmp (fields[0], "Path") == 0) {
+ fdata->free_original_path = TRUE;
+ fdata->original_path = g_strdup (fields[1]);
+ fdata->full_path = g_strconcat ((fdata->original_path[0] != '/') ? "/" : "",
+ fdata->original_path,
+ (fdata->dir && (fdata->original_path[strlen (fdata->original_path - 1)] != '/')) ? "/" : "",
+ NULL);
+ }
+ else if (strcmp (fields[0], "Folder") == 0) {
+ fdata->dir = (strcmp (fields[1], "+") == 0);
+ }
+ else if (strcmp (fields[0], "Size") == 0) {
+ fdata->size = g_ascii_strtoull (fields[1], NULL, 10);
+ }
+ else if (strcmp (fields[0], "Modified") == 0) {
+ char **modified_fields;
+
+ modified_fields = g_strsplit (fields[1], " ", 2);
+ if (modified_fields[0] != NULL)
+ fdata->modified = mktime_from_string (modified_fields[0], modified_fields[1]);
+ g_strfreev (modified_fields);
+ }
+ else if (strcmp (fields[0], "Encrypted") == 0) {
+ if (strcmp (fields[1], "+") == 0)
+ fdata->encrypted = TRUE;
+ }
+ else if (strcmp (fields[0], "Method") == 0) {
+ if (strstr (fields[1], "AES") != NULL)
+ fdata->encrypted = TRUE;
+ }
+ else if (strcmp (fields[0], "Attributes") == 0) {
+ if (fields[1][0] == 'D')
+ fdata->dir = TRUE;
+ }
+ g_strfreev (fields);
+}
+
+
+static void
+fr_command_7z_begin_command (FrCommand *comm)
+{
+ if (is_program_in_path ("7z"))
+ fr_process_begin_command (comm->process, "7z");
+ else if (is_program_in_path ("7za"))
+ fr_process_begin_command (comm->process, "7za");
+ else if (is_program_in_path ("7zr"))
+ fr_process_begin_command (comm->process, "7zr");
+}
+
+
+static void
+add_password_arg (FrCommand *comm,
+ const char *password,
+ gboolean always_specify)
+{
+ if (always_specify || ((password != NULL) && (*password != 0))) {
+ char *arg;
+
+ arg = g_strconcat ("-p", password, NULL);
+ fr_process_add_arg (comm->process, arg);
+ g_free (arg);
+ }
+}
+
+
+static void
+list__begin (gpointer data)
+{
+ FrCommand7z *p7z_comm = data;
+
+ if (p7z_comm->fdata != NULL) {
+ file_data_free (p7z_comm->fdata);
+ p7z_comm->fdata = NULL;
+ }
+ p7z_comm->list_started = FALSE;
+}
+
+
+static void
+fr_command_7z_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, list__process_line, comm);
+
+ fr_command_7z_begin_command (comm);
+ fr_process_set_begin_func (comm->process, list__begin, comm);
+ fr_process_add_arg (comm->process, "l");
+ fr_process_add_arg (comm->process, "-slt");
+ fr_process_add_arg (comm->process, "-bd");
+ fr_process_add_arg (comm->process, "-y");
+ add_password_arg (comm, comm->password, FALSE);
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+
+ fr_process_start (comm->process);
+}
+
+
+static char Progress_Message[4196];
+static char Progress_Filename[4096];
+
+
+static void
+parse_progress_line (FrCommand *comm,
+ const char *prefix,
+ const char *message_prefix,
+ const char *line)
+{
+ int prefix_len;
+
+ prefix_len = strlen (prefix);
+ if (strncmp (line, prefix, prefix_len) == 0) {
+ double fraction;
+
+ strcpy (Progress_Filename, line + prefix_len);
+ sprintf (Progress_Message, "%s%s", message_prefix, file_name_from_path (Progress_Filename));
+ fr_command_message (comm, Progress_Message);
+
+ fraction = (double) ++comm->n_file / (comm->n_files + 1);
+ fr_command_progress (comm, fraction);
+ }
+}
+
+
+static void
+process_line__add (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+
+ if ((comm->volume_size > 0) && (strncmp (line, "Creating archive ", 17) == 0)) {
+ char *volume_filename;
+
+ volume_filename = g_strconcat (comm->filename, ".001", NULL);
+ fr_command_set_multi_volume (comm, volume_filename);
+ g_free (volume_filename);
+ }
+
+ if (comm->n_files != 0)
+ parse_progress_line (comm, "Compressing ", _("Adding file: "), line);
+}
+
+
+static void
+fr_command_7z_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ GList *scan;
+
+ fr_process_use_standard_locale (comm->process, TRUE);
+ fr_process_set_out_line_func (comm->process,
+ process_line__add,
+ comm);
+
+ fr_command_7z_begin_command (comm);
+
+ if (update)
+ fr_process_add_arg (comm->process, "u");
+ else
+ fr_process_add_arg (comm->process, "a");
+
+ if (base_dir != NULL) {
+ fr_process_set_working_dir (comm->process, base_dir);
+ fr_process_add_arg_concat (comm->process, "-w", base_dir, NULL);
+ }
+
+ if (is_mime_type (comm->mime_type, "application/zip")
+ || is_mime_type (comm->mime_type, "application/x-cbz"))
+ {
+ fr_process_add_arg (comm->process, "-tzip");
+ fr_process_add_arg (comm->process, "-mem=AES128");
+ }
+
+ fr_process_add_arg (comm->process, "-bd");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, "-l");
+ add_password_arg (comm, comm->password, FALSE);
+ if ((comm->password != NULL)
+ && (*comm->password != 0)
+ && comm->encrypt_header
+ && fr_command_is_capable_of (comm, FR_COMMAND_CAN_ENCRYPT_HEADER))
+ {
+ fr_process_add_arg (comm->process, "-mhe=on");
+ }
+
+ /* fr_process_add_arg (comm->process, "-ms=off"); FIXME: solid mode off? */
+
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-mx=1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-mx=5"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-mx=5"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-mx=7"); break;
+ }
+
+ if (is_mime_type (comm->mime_type, "application/x-ms-dos-executable"))
+ fr_process_add_arg (comm->process, "-sfx");
+
+ if (comm->volume_size > 0)
+ fr_process_add_arg_printf (comm->process, "-v%ub", comm->volume_size);
+
+ if (from_file != NULL)
+ fr_process_add_arg_concat (comm->process, "-i@", from_file, NULL);
+
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, comm->filename);
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_7z_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ GList *scan;
+
+ fr_command_7z_begin_command (comm);
+ fr_process_add_arg (comm->process, "d");
+ fr_process_add_arg (comm->process, "-bd");
+ fr_process_add_arg (comm->process, "-y");
+ if (is_mime_type (comm->mime_type, "application/x-ms-dos-executable"))
+ fr_process_add_arg (comm->process, "-sfx");
+
+ if (from_file != NULL)
+ fr_process_add_arg_concat (comm->process, "-i@", from_file, NULL);
+
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, comm->filename);
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+process_line__extract (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+
+ if (comm->n_files != 0)
+ parse_progress_line (comm, "Extracting ", _("Extracting file: "), line);
+}
+
+
+static void
+fr_command_7z_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ fr_process_use_standard_locale (comm->process, TRUE);
+ fr_process_set_out_line_func (comm->process,
+ process_line__extract,
+ comm);
+ fr_command_7z_begin_command (comm);
+
+ if (junk_paths)
+ fr_process_add_arg (comm->process, "e");
+ else
+ fr_process_add_arg (comm->process, "x");
+
+ fr_process_add_arg (comm->process, "-bd");
+ fr_process_add_arg (comm->process, "-y");
+ add_password_arg (comm, comm->password, FALSE);
+
+ if (dest_dir != NULL)
+ fr_process_add_arg_concat (comm->process, "-o", dest_dir, NULL);
+
+ if (from_file != NULL)
+ fr_process_add_arg_concat (comm->process, "-i@", from_file, NULL);
+
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, comm->filename);
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_7z_test (FrCommand *comm)
+{
+ fr_command_7z_begin_command (comm);
+ fr_process_add_arg (comm->process, "t");
+ fr_process_add_arg (comm->process, "-bd");
+ fr_process_add_arg (comm->process, "-y");
+ add_password_arg (comm, comm->password, FALSE);
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_7z_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ if (error->type == FR_PROC_ERROR_NONE) {
+ FileData *first;
+ char *basename;
+ char *testname;
+
+ /* This is a way to fix bug #582712. */
+
+ if (comm->files->len != 1)
+ return;
+
+ if (! g_str_has_suffix (comm->filename, ".001"))
+ return;
+
+ first = g_ptr_array_index (comm->files, 0);
+ basename = g_path_get_basename (comm->filename);
+ testname = g_strconcat (first->original_path, ".001", NULL);
+
+ if (strcmp (basename, testname) == 0)
+ error->type = FR_PROC_ERROR_ASK_PASSWORD;
+
+ g_free (testname);
+ g_free (basename);
+
+ return;
+ }
+
+ if (error->status <= 1) {
+ error->type = FR_PROC_ERROR_NONE;
+ }
+ else {
+ GList *scan;
+
+ for (scan = g_list_last (comm->process->out.raw); scan; scan = scan->prev) {
+ char *line = scan->data;
+
+ if ((strstr (line, "Wrong password?") != NULL)
+ || (strstr (line, "Enter password") != NULL))
+ {
+ error->type = FR_PROC_ERROR_ASK_PASSWORD;
+ break;
+ }
+ }
+ }
+}
+
+
+const char *sevenz_mime_types[] = { "application/x-7z-compressed",
+ "application/x-arj",
+ "application/vnd.ms-cab-compressed",
+ "application/x-cd-image",
+ /*"application/x-cbr",*/
+ "application/x-cbz",
+ "application/x-ms-dos-executable",
+ "application/x-rar",
+ "application/zip",
+ NULL };
+
+
+const char **
+fr_command_7z_get_mime_types (FrCommand *comm)
+{
+ return sevenz_mime_types;
+}
+
+
+FrCommandCap
+fr_command_7z_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (! is_program_available ("7za", check_command) && ! is_program_available ("7zr", check_command) && ! is_program_available ("7z", check_command))
+ return capabilities;
+
+ if (is_mime_type (mime_type, "application/x-7z-compressed")) {
+ capabilities |= FR_COMMAND_CAN_READ_WRITE | FR_COMMAND_CAN_CREATE_VOLUMES;
+ if (is_program_available ("7z", check_command))
+ capabilities |= FR_COMMAND_CAN_ENCRYPT | FR_COMMAND_CAN_ENCRYPT_HEADER;
+ }
+ else if (is_mime_type (mime_type, "application/x-7z-compressed-tar")) {
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ if (is_program_available ("7z", check_command))
+ capabilities |= FR_COMMAND_CAN_ENCRYPT | FR_COMMAND_CAN_ENCRYPT_HEADER;
+ }
+ else if (is_program_available ("7z", check_command)) {
+ if (is_mime_type (mime_type, "application/x-rar")
+ || is_mime_type (mime_type, "application/x-cbr"))
+ {
+ if (! check_command || g_file_test ("/usr/lib/p7zip/Codecs/Rar29.so", G_FILE_TEST_EXISTS))
+ capabilities |= FR_COMMAND_CAN_READ;
+ }
+ else
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ if (is_mime_type (mime_type, "application/x-cbz")
+ || is_mime_type (mime_type, "application/x-ms-dos-executable")
+ || is_mime_type (mime_type, "application/zip"))
+ {
+ capabilities |= FR_COMMAND_CAN_WRITE | FR_COMMAND_CAN_ENCRYPT;
+ }
+ }
+ else if (is_program_available ("7za", check_command)) {
+ if (is_mime_type (mime_type, "application/vnd.ms-cab-compressed")
+ || is_mime_type (mime_type, "application/zip"))
+ {
+ capabilities |= FR_COMMAND_CAN_READ;
+ }
+
+ if (is_mime_type (mime_type, "application/zip"))
+ capabilities |= FR_COMMAND_CAN_WRITE;
+ }
+
+ /* multi-volumes are read-only */
+ if ((comm->files->len > 0) && comm->multi_volume && (capabilities & FR_COMMAND_CAN_WRITE))
+ capabilities ^= FR_COMMAND_CAN_WRITE;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_7z_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ if (is_mime_type (mime_type, "application/x-rar"))
+ return PACKAGES ("p7zip,p7zip-rar");
+ else if (is_mime_type (mime_type, "application/zip") || is_mime_type (mime_type, "application/vnd.ms-cab-compressed"))
+ return PACKAGES ("p7zip,p7zip-full");
+ else
+ return PACKAGES ("p7zip");
+}
+
+
+static void
+fr_command_7z_class_init (FrCommand7zClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_7z_finalize;
+
+ afc->list = fr_command_7z_list;
+ afc->add = fr_command_7z_add;
+ afc->delete = fr_command_7z_delete;
+ afc->extract = fr_command_7z_extract;
+ afc->test = fr_command_7z_test;
+ afc->handle_error = fr_command_7z_handle_error;
+ afc->get_mime_types = fr_command_7z_get_mime_types;
+ afc->get_capabilities = fr_command_7z_get_capabilities;
+ afc->get_packages = fr_command_7z_get_packages;
+}
+
+
+static void
+fr_command_7z_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propAddCanStoreFolders = TRUE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = TRUE;
+ comm->propPassword = TRUE;
+ comm->propTest = TRUE;
+ comm->propListFromFile = TRUE;
+}
+
+
+static void
+fr_command_7z_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_7Z (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_7z_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommand7zClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_7z_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommand7z),
+ 0,
+ (GInstanceInitFunc) fr_command_7z_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommand7z",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-7z.h b/src/fr-command-7z.h
new file mode 100644
index 0000000..fc7aa32
--- /dev/null
+++ b/src/fr-command-7z.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_7Z_H
+#define FR_COMMAND_7Z_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_7Z (fr_command_7z_get_type ())
+#define FR_COMMAND_7Z(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_7Z, FrCommand7z))
+#define FR_COMMAND_7Z_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_7Z, FrCommand7zClass))
+#define FR_IS_COMMAND_7Z(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_7Z))
+#define FR_IS_COMMAND_7Z_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_7Z))
+#define FR_COMMAND_7Z_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_7Z, FrCommand7zClass))
+
+typedef struct _FrCommand7z FrCommand7z;
+typedef struct _FrCommand7zClass FrCommand7zClass;
+
+struct _FrCommand7z
+{
+ FrCommand __parent;
+ gboolean list_started;
+ gboolean old_style;
+ FileData *fdata;
+};
+
+struct _FrCommand7zClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_7z_get_type (void);
+
+#endif /* FR_COMMAND_7Z_H */
diff --git a/src/fr-command-ace.c b/src/fr-command-ace.c
new file mode 100644
index 0000000..c6e889e
--- /dev/null
+++ b/src/fr-command-ace.c
@@ -0,0 +1,342 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-ace.h"
+
+static void fr_command_ace_class_init (FrCommandAceClass *class);
+static void fr_command_ace_init (FrCommand *afile);
+static void fr_command_ace_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+
+static time_t
+mktime_from_string (char *date,
+ char *time)
+{
+ struct tm tm = {0, };
+ char **fields;
+
+ tm.tm_isdst = -1;
+
+ /* date */
+
+ fields = g_strsplit (date, ".", 3);
+ if (fields[0] != NULL) {
+ tm.tm_mday = atoi (fields[0]);
+ if (fields[1] != NULL) {
+ tm.tm_mon = atoi (fields[1]) - 1;
+ if (fields[2] != NULL) {
+ int y = atoi (fields[2]);
+ if (y > 75)
+ tm.tm_year = y;
+ else
+ tm.tm_year = 100 + y;
+ }
+ }
+ }
+ g_strfreev (fields);
+
+ /* time */
+
+ fields = g_strsplit (time, ":", 2);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL)
+ tm.tm_min = atoi (fields[1]);
+ }
+ tm.tm_sec = 0;
+ g_strfreev (fields);
+
+ return mktime (&tm);
+}
+
+
+static void
+process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommandAce *ace_comm = FR_COMMAND_ACE (data);
+ FrCommand *comm = FR_COMMAND (data);
+ char **fields;
+ const char *field_name;
+
+ g_return_if_fail (line != NULL);
+
+ if (ace_comm->command_type == FR_ACE_COMMAND_UNKNOWN) {
+ if (g_str_has_prefix (line, "UNACE")) {
+ if (strstr (line, "public version") != NULL)
+ ace_comm->command_type = FR_ACE_COMMAND_PUBLIC;
+ else
+ ace_comm->command_type = FR_ACE_COMMAND_NONFREE;
+ }
+ return;
+ }
+
+ if (! ace_comm->list_started) {
+ if (ace_comm->command_type == FR_ACE_COMMAND_PUBLIC) {
+ if (g_str_has_prefix (line, "Date"))
+ ace_comm->list_started = TRUE;
+ }
+ else if (ace_comm->command_type == FR_ACE_COMMAND_NONFREE) {
+ if (g_str_has_prefix (line, " Date"))
+ ace_comm->list_started = TRUE;
+ }
+ return;
+ }
+
+ fdata = file_data_new ();
+
+ if (ace_comm->command_type == FR_ACE_COMMAND_PUBLIC)
+ fields = g_strsplit (line, "|", 6);
+ else if (ace_comm->command_type == FR_ACE_COMMAND_NONFREE)
+ fields = split_line (line, 5);
+
+ if ((fields == NULL) || (fields[0] == NULL) || (n_fields (fields) < 5))
+ return;
+
+ fdata->size = g_ascii_strtoull (fields[3], NULL, 10);
+ fdata->modified = mktime_from_string (fields[0], fields[1]);
+
+ if (ace_comm->command_type == FR_ACE_COMMAND_PUBLIC) {
+ field_name = fields[5];
+ field_name = field_name + 1;
+ }
+ else if (ace_comm->command_type == FR_ACE_COMMAND_NONFREE)
+ field_name = get_last_field (line, 6);
+
+ if (field_name[0] != '/') {
+ fdata->full_path = g_strconcat ("/", field_name, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+ else {
+ fdata->full_path = g_strdup (field_name);
+ fdata->original_path = fdata->full_path;
+ }
+
+ g_strfreev (fields);
+
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+list__begin (gpointer data)
+{
+ FrCommandAce *comm = data;
+
+ comm->list_started = FALSE;
+ comm->command_type = FR_ACE_COMMAND_UNKNOWN;
+}
+
+
+static void
+fr_command_ace_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, process_line, comm);
+
+ fr_process_begin_command (comm->process, "unace");
+ fr_process_set_begin_func (comm->process, list__begin, comm);
+ fr_process_add_arg (comm->process, "v");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_ace_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "unace");
+
+ if (dest_dir != NULL)
+ fr_process_set_working_dir (comm->process, dest_dir);
+
+ if (junk_paths)
+ fr_process_add_arg (comm->process, "e");
+ else
+ fr_process_add_arg (comm->process, "x");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, comm->filename);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_ace_test (FrCommand *comm)
+{
+ fr_process_begin_command (comm->process, "unace");
+ fr_process_add_arg (comm->process, "t");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_ace_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ /* FIXME */
+}
+
+
+const char *ace_mime_type[] = { "application/x-ace", NULL };
+
+
+const char **
+fr_command_ace_get_mime_types (FrCommand *comm)
+{
+ return ace_mime_type;
+}
+
+
+FrCommandCap
+fr_command_ace_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("unace", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_ace_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("unace");
+}
+
+
+static void
+fr_command_ace_class_init (FrCommandAceClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_ace_finalize;
+
+ afc->list = fr_command_ace_list;
+ afc->extract = fr_command_ace_extract;
+ afc->test = fr_command_ace_test;
+ afc->handle_error = fr_command_ace_handle_error;
+ afc->get_mime_types = fr_command_ace_get_mime_types;
+ afc->get_capabilities = fr_command_ace_get_capabilities;
+ afc->get_packages = fr_command_ace_get_packages;
+}
+
+
+static void
+fr_command_ace_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = TRUE;
+ comm->propPassword = FALSE;
+ comm->propTest = TRUE;
+}
+
+
+static void
+fr_command_ace_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_ACE (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_ace_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandAceClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_ace_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandAce),
+ 0,
+ (GInstanceInitFunc) fr_command_ace_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandAce",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-ace.h b/src/fr-command-ace.h
new file mode 100644
index 0000000..e88586e
--- /dev/null
+++ b/src/fr-command-ace.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2006 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_ACE_H
+#define FR_COMMAND_ACE_H
+
+#include <glib.h>
+#include "file-data.h"
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_ACE (fr_command_ace_get_type ())
+#define FR_COMMAND_ACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ACE, FrCommandAce))
+#define FR_COMMAND_ACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ACE, FrCommandAceClass))
+#define FR_IS_COMMAND_ACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ACE))
+#define FR_IS_COMMAND_ACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ACE))
+#define FR_COMMAND_ACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ACE, FrCommandAceClass))
+
+typedef enum {
+ FR_ACE_COMMAND_UNKNOWN = 0,
+ FR_ACE_COMMAND_PUBLIC,
+ FR_ACE_COMMAND_NONFREE
+} FrAceCommand;
+
+typedef struct _FrCommandAce FrCommandAce;
+typedef struct _FrCommandAceClass FrCommandAceClass;
+
+struct _FrCommandAce
+{
+ FrCommand __parent;
+
+ gboolean list_started;
+ FrAceCommand command_type;
+};
+
+struct _FrCommandAceClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_ace_get_type (void);
+
+#endif /* FR_COMMAND_ACE_H */
diff --git a/src/fr-command-alz.c b/src/fr-command-alz.c
new file mode 100644
index 0000000..d800e6c
--- /dev/null
+++ b/src/fr-command-alz.c
@@ -0,0 +1,406 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "fr-command.h"
+#include "fr-command-alz.h"
+#include "glib-utils.h"
+
+static void fr_command_alz_class_init (FrCommandAlzClass *class);
+static void fr_command_alz_init (FrCommand *afile);
+static void fr_command_alz_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+
+static time_t
+mktime_from_string (char *date_s,
+ char *time_s)
+{
+ struct tm tm = {0, };
+ char **fields;
+
+ /* date */
+
+ fields = g_strsplit (date_s, "/", 3);
+ if (fields[0] != NULL) {
+ tm.tm_mon = atoi (fields[0]) - 1;
+ if (fields[1] != NULL) {
+ tm.tm_mday = atoi (fields[1]);
+ if (fields[2] != NULL)
+ tm.tm_year = 100 + atoi (fields[2]);
+ }
+ }
+ g_strfreev (fields);
+
+ /* time */
+
+ fields = g_strsplit (time_s, ":", 3);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL)
+ tm.tm_min = atoi (fields[1]);
+ }
+ g_strfreev (fields);
+
+ return mktime (&tm);
+}
+
+
+static void
+process_line (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+ FrCommandAlz *alz_comm = FR_COMMAND_ALZ (comm);
+ FileData *fdata;
+ char **fields;
+ char *name_field;
+ char name_last;
+ gsize name_len;
+
+ g_return_if_fail (line != NULL);
+
+
+ if (! alz_comm->list_started) {
+ if (strncmp (line, "-----", 5 ) == 0 )
+ alz_comm->list_started = TRUE;
+ return;
+ }
+
+ if (strncmp (line, "-----", 5 ) == 0) {
+ alz_comm->list_started = FALSE;
+ return;
+
+ }
+
+ if (! alz_comm->list_started)
+ return;
+
+ fdata = file_data_new ();
+ fields = split_line (line, 5);
+ fdata->modified = mktime_from_string (fields[0], fields[1]);
+ fdata->size = g_ascii_strtoull (fields[3], NULL, 10);
+
+ name_field = g_strdup (get_last_field (line, 6));
+ name_len = strlen (name_field);
+
+ name_last = name_field[name_len - 1];
+ fdata->dir = name_last == '\\';
+ fdata->encrypted = name_last == '*';
+
+ if (fdata->dir || fdata->encrypted)
+ name_field[--name_len] = '\0';
+
+ if (*name_field == '/') {
+ fdata->full_path = g_strdup (name_field);
+ fdata->original_path = fdata->full_path;
+ }
+ else {
+ fdata->full_path = g_strconcat ("/", name_field, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+
+ if (fdata->dir) {
+ char *s;
+ for (s = fdata->full_path; *s != '\0'; ++s)
+ if (*s == '\\') *s = '/';
+ for (s = fdata->original_path; *s != '\0'; ++s)
+ if (*s == '\\') *s = '/';
+ fdata->name = dir_name_from_path (fdata->full_path);
+ }
+ else {
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ }
+
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+
+ g_free (name_field);
+ g_strfreev (fields);
+}
+
+
+static void
+add_codepage_arg (FrCommand *comm)
+{
+ const char *env_list[] = { "LC_CTYPE", "LC_ALL", "LANG", NULL };
+ const char **scan;
+ const char *arg = "-cp949";
+
+ for (scan = env_list; *scan != NULL; ++scan) {
+ char *env = getenv (*scan);
+
+ if (! env)
+ continue;
+
+ if (strstr (env, "UTF-8") || strstr (env, "utf-8"))
+ arg = "-utf8";
+ else if (strstr (env, "euc") || strstr (env, "EUC"))
+ arg = "-euc-kr";
+ else
+ continue;
+ break;
+ }
+
+ fr_process_add_arg (comm->process, arg);
+}
+
+
+static void
+add_password_arg (FrCommand *comm,
+ const char *password,
+ gboolean disable_query)
+{
+ if (password != NULL) {
+ fr_process_add_arg (comm->process, "-pwd");
+ fr_process_add_arg (comm->process, password);
+ }
+ else if (disable_query) {
+ fr_process_add_arg (comm->process, "-pwd");
+ fr_process_add_arg (comm->process, "");
+ }
+}
+
+
+static void
+list__begin (gpointer data)
+{
+ FrCommandAlz *comm = data;
+
+ comm->list_started = FALSE;
+ comm->invalid_password = FALSE;
+}
+
+
+static void
+fr_command_alz_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process, process_line, comm);
+
+ fr_process_begin_command (comm->process, "unalz");
+ fr_process_set_begin_func (comm->process, list__begin, comm);
+ fr_process_add_arg (comm->process, "-l");
+ add_codepage_arg(comm);
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_use_standard_locale (comm->process, TRUE);
+ fr_process_start (comm->process);
+}
+
+
+/* -- extract -- */
+
+static void
+process_extract_line (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+ FrCommandAlz *alz_comm = FR_COMMAND_ALZ (comm);
+
+ g_return_if_fail (line != NULL);
+
+ /* - error check - */
+
+ if (strncmp (line, "err code(28) (invalid password)", 31) == 0) {
+ alz_comm->invalid_password = TRUE;
+ fr_process_stop (comm->process);
+ return;
+ }
+
+ if (alz_comm->extract_none && (strncmp (line, "unalziiiing :", 13) == 0)) {
+ alz_comm->extract_none = FALSE;
+ }
+ else if ((strncmp (line, "done..", 6) == 0) && alz_comm->extract_none) {
+ fr_process_stop (comm->process);
+ return;
+ }
+}
+
+
+static void
+fr_command_alz_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ FR_COMMAND_ALZ (comm)->extract_none = TRUE;
+
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process,
+ process_extract_line,
+ comm);
+
+ fr_process_begin_command (comm->process, "unalz");
+ if (dest_dir != NULL) {
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, dest_dir);
+ }
+ add_codepage_arg (comm);
+ add_password_arg (comm, comm->password, TRUE);
+ fr_process_add_arg (comm->process, comm->filename);
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_alz_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ if ((error->type == FR_PROC_ERROR_STOPPED)) {
+ if (FR_COMMAND_ALZ (comm)->extract_none ||
+ FR_COMMAND_ALZ (comm)->invalid_password ) {
+ error->type = FR_PROC_ERROR_ASK_PASSWORD;
+ }
+ }
+}
+
+
+const char *alz_mime_type[] = { "application/x-alz", NULL };
+
+
+const char **
+fr_command_alz_get_mime_types (FrCommand *comm)
+{
+ return alz_mime_type;
+}
+
+
+FrCommandCap
+fr_command_alz_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("unalz", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_alz_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("unalz");
+}
+
+
+static void
+fr_command_alz_class_init (FrCommandAlzClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_alz_finalize;
+
+ afc->list = fr_command_alz_list;
+ afc->add = NULL;
+ afc->delete = NULL;
+ afc->extract = fr_command_alz_extract;
+ afc->handle_error = fr_command_alz_handle_error;
+ afc->get_mime_types = fr_command_alz_get_mime_types;
+ afc->get_capabilities = fr_command_alz_get_capabilities;
+ afc->get_packages = fr_command_alz_get_packages;
+}
+
+
+static void
+fr_command_alz_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = TRUE;
+ comm->propTest = FALSE;
+}
+
+
+static void
+fr_command_alz_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_ALZ (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_alz_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandAlzClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_alz_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandAlz),
+ 0,
+ (GInstanceInitFunc) fr_command_alz_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FrCommandAlz",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-alz.h b/src/fr-command-alz.h
new file mode 100644
index 0000000..40add85
--- /dev/null
+++ b/src/fr-command-alz.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_ALZ_H
+#define FR_COMMAND_ALZ_H
+
+#include <glib.h>
+#include "file-data.h"
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_ALZ (fr_command_alz_get_type ())
+#define FR_COMMAND_ALZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ALZ, FrCommandAlz))
+#define FR_COMMAND_ALZ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ALZ, FrCommandAlzClass))
+#define FR_IS_COMMAND_ALZ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ALZ))
+#define FR_IS_COMMAND_ALZ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ALZ))
+#define FR_COMMAND_ALZ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ALZ, FrCommandAlzClass))
+
+typedef struct _FrCommandAlz FrCommandAlz;
+typedef struct _FrCommandAlzClass FrCommandAlzClass;
+
+struct _FrCommandAlz
+{
+ FrCommand __parent;
+
+ gboolean extract_none;
+ gboolean invalid_password;
+ gboolean list_started;
+};
+
+struct _FrCommandAlzClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_alz_get_type (void);
+
+#endif /* FR_COMMAND_ALZ_H */
diff --git a/src/fr-command-ar.c b/src/fr-command-ar.c
new file mode 100644
index 0000000..143687e
--- /dev/null
+++ b/src/fr-command-ar.c
@@ -0,0 +1,392 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "fr-command.h"
+#include "fr-command-ar.h"
+
+static void fr_command_ar_class_init (FrCommandArClass *class);
+static void fr_command_ar_init (FrCommand *afile);
+static void fr_command_ar_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+static time_t
+mktime_from_string (char *time_s,
+ char *day_s,
+ char *month_s,
+ char *year_s)
+{
+ static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ struct tm tm = {0, };
+ char **fields;
+
+ tm.tm_isdst = -1;
+
+ /* date */
+
+ if (month_s != NULL) {
+ int i;
+ for (i = 0; i < 12; i++)
+ if (strcmp (months[i], month_s) == 0) {
+ tm.tm_mon = i;
+ break;
+ }
+ }
+ tm.tm_mday = atoi (day_s);
+ tm.tm_year = atoi (year_s) - 1900;
+
+ /* time */
+
+ fields = g_strsplit (time_s, ":", 3);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL) {
+ tm.tm_min = atoi (fields[1]);
+ if (fields[2] != NULL)
+ tm.tm_sec = atoi (fields[2]);
+ }
+ }
+ g_strfreev (fields);
+
+ return mktime (&tm);
+}
+
+
+static char*
+ar_get_last_field (const char *line,
+ int start_from,
+ int field_n)
+{
+ const char *f_start, *f_end;
+
+ line = line + start_from;
+
+ f_start = line;
+ while ((*f_start == ' ') && (*f_start != *line))
+ f_start++;
+ f_end = f_start;
+
+ while ((field_n > 0) && (*f_end != 0)) {
+ if (*f_end == ' ') {
+ field_n--;
+ if (field_n != 0) {
+ while ((*f_end == ' ') && (*f_end != *line))
+ f_end++;
+ f_start = f_end;
+ }
+ } else
+ f_end++;
+ }
+
+ return g_strdup (f_start);
+}
+
+
+static void
+process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+ char **fields;
+ int date_idx;
+ char *field_month, *field_day, *field_time, *field_year;
+ char *field_size, *field_name;
+
+ g_return_if_fail (line != NULL);
+
+ fdata = file_data_new ();
+
+ date_idx = file_list__get_index_from_pattern (line, "%c%c%c %a%n %n%n:%n%n %n%n%n%n");
+
+ field_size = file_list__get_prev_field (line, date_idx, 1);
+ fdata->size = g_ascii_strtoull (field_size, NULL, 10);
+ g_free (field_size);
+
+ field_month = file_list__get_next_field (line, date_idx, 1);
+ field_day = file_list__get_next_field (line, date_idx, 2);
+ field_time = file_list__get_next_field (line, date_idx, 3);
+ field_year = file_list__get_next_field (line, date_idx, 4);
+ fdata->modified = mktime_from_string (field_time, field_day, field_month, field_year);
+ g_free (field_day);
+ g_free (field_month);
+ g_free (field_year);
+ g_free (field_time);
+
+ /* Full path */
+
+ field_name = ar_get_last_field (line, date_idx, 5);
+
+ fields = g_strsplit (field_name, " -> ", 2);
+
+ if (fields[0] == NULL) {
+ g_strfreev (fields);
+ g_free (field_name);
+ file_data_free (fdata);
+ return;
+ }
+
+ if (fields[1] == NULL) {
+ g_strfreev (fields);
+ fields = g_strsplit (field_name, " link to ", 2);
+ }
+
+ if (*(fields[0]) == '/') {
+ fdata->full_path = g_strdup (fields[0]);
+ fdata->original_path = fdata->full_path;
+ } else {
+ fdata->full_path = g_strconcat ("/", fields[0], NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+
+ if (fields[1] != NULL)
+ fdata->link = g_strdup (fields[1]);
+ g_strfreev (fields);
+ g_free (field_name);
+
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+fr_command_ar_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, process_line, comm);
+
+ fr_process_begin_command (comm->process, "ar");
+ fr_process_add_arg (comm->process, "tv");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_ar_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "ar");
+
+ if (update)
+ fr_process_add_arg (comm->process, "ru");
+ else
+ fr_process_add_arg (comm->process, "r");
+
+ if (base_dir != NULL)
+ fr_process_set_working_dir (comm->process, base_dir);
+
+ fr_process_add_arg (comm->process, comm->filename);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_ar_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "ar");
+ fr_process_add_arg (comm->process, "d");
+ fr_process_add_arg (comm->process, comm->filename);
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_ar_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "ar");
+
+ if (dest_dir != NULL)
+ fr_process_set_working_dir (comm->process, dest_dir);
+
+ fr_process_add_arg (comm->process, "x");
+ fr_process_add_arg (comm->process, comm->filename);
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_ar_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ /* FIXME */
+}
+
+
+const char *ar_mime_type[] = { "application/x-ar",
+ "application/x-deb",
+ NULL };
+
+
+const char **
+fr_command_ar_get_mime_types (FrCommand *comm)
+{
+ return ar_mime_type;
+}
+
+
+FrCommandCap
+fr_command_ar_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("ar", check_command)) {
+ if (is_mime_type (mime_type, "application/x-deb"))
+ capabilities |= FR_COMMAND_CAN_READ;
+ else if (is_mime_type (mime_type, "application/x-ar"))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_ar_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("binutils");
+}
+
+
+static void
+fr_command_ar_class_init (FrCommandArClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_ar_finalize;
+
+ afc->list = fr_command_ar_list;
+ afc->add = fr_command_ar_add;
+ afc->delete = fr_command_ar_delete;
+ afc->extract = fr_command_ar_extract;
+ afc->handle_error = fr_command_ar_handle_error;
+ afc->get_mime_types = fr_command_ar_get_mime_types;
+ afc->get_capabilities = fr_command_ar_get_capabilities;
+ afc->get_packages = fr_command_ar_get_packages;
+}
+
+
+static void
+fr_command_ar_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propAddCanStoreFolders = FALSE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+}
+
+
+static void
+fr_command_ar_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_AR (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_ar_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandArClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_ar_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandAr),
+ 0,
+ (GInstanceInitFunc) fr_command_ar_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandAr",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-ar.h b/src/fr-command-ar.h
new file mode 100644
index 0000000..44b724b
--- /dev/null
+++ b/src/fr-command-ar.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_AR_H
+#define FR_COMMAND_AR_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_AR (fr_command_ar_get_type ())
+#define FR_COMMAND_AR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_AR, FrCommandAr))
+#define FR_COMMAND_AR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_AR, FrCommandArClass))
+#define FR_IS_COMMAND_AR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_AR))
+#define FR_IS_COMMAND_AR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_AR))
+#define FR_COMMAND_AR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_AR, FrCommandArClass))
+
+typedef struct _FrCommandAr FrCommandAr;
+typedef struct _FrCommandArClass FrCommandArClass;
+
+struct _FrCommandAr
+{
+ FrCommand __parent;
+};
+
+struct _FrCommandArClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_ar_get_type (void);
+
+#endif /* FR_COMMAND_AR_H */
diff --git a/src/fr-command-arj.c b/src/fr-command-arj.c
new file mode 100644
index 0000000..ee214c0
--- /dev/null
+++ b/src/fr-command-arj.c
@@ -0,0 +1,438 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-arj.h"
+
+static void fr_command_arj_class_init (FrCommandArjClass *class);
+static void fr_command_arj_init (FrCommand *afile);
+static void fr_command_arj_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+static time_t
+mktime_from_string (char *date_s,
+ char *time_s)
+{
+ struct tm tm = {0, };
+ char **fields;
+
+ tm.tm_isdst = -1;
+
+ /* date */
+
+ fields = g_strsplit (date_s, "-", 3);
+ if (fields[0] != NULL) {
+ /* warning : this will work until 2075 ;) */
+ int y = atoi (fields[0]);
+ if (y >= 75)
+ tm.tm_year = y;
+ else
+ tm.tm_year = 100 + y;
+
+ tm.tm_mon = atoi (fields[1]) - 1;
+ tm.tm_mday = atoi (fields[2]);
+ }
+ g_strfreev (fields);
+
+ /* time */
+
+ fields = g_strsplit (time_s, ":", 3);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL) {
+ tm.tm_min = atoi (fields[1]);
+ if (fields[2] != NULL)
+ tm.tm_sec = atoi (fields[2]);
+ }
+ }
+ g_strfreev (fields);
+
+ return mktime (&tm);
+}
+
+
+static void
+list__process_line (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+ FrCommandArj *arj_comm = FR_COMMAND_ARJ (comm);
+
+ g_return_if_fail (line != NULL);
+
+ if (! arj_comm->list_started) {
+ if (strncmp (line, "--------", 8) == 0) {
+ arj_comm->list_started = TRUE;
+ arj_comm->line_no = 1;
+ }
+ return;
+ }
+
+ if (strncmp (line, "--------", 8) == 0) {
+ arj_comm->list_started = FALSE;
+ return;
+ }
+
+ if (g_regex_match (arj_comm->filename_line_regex, line, 0, NULL)) { /* Read the filename. */
+ FileData *fdata;
+ const char *name_field;
+
+ arj_comm->line_no = 1;
+
+ arj_comm->fdata = fdata = file_data_new ();
+
+ name_field = get_last_field (line, 2);
+
+ if (*name_field == '/') {
+ fdata->full_path = g_strdup (name_field);
+ fdata->original_path = fdata->full_path;
+ }
+ else {
+ fdata->full_path = g_strconcat ("/", name_field, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+
+ fdata->link = NULL;
+
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+ }
+ else if (arj_comm->line_no == 2) { /* Read file size and date. */
+ FileData *fdata;
+ char **fields;
+
+ fdata = arj_comm->fdata;
+
+ /* read file info. */
+
+ fields = split_line (line, 10);
+ fdata->size = g_ascii_strtoull (fields[2], NULL, 10);
+ fdata->modified = mktime_from_string (fields[5], fields[6]);
+ if ((strcmp (fields[1], "MS-DOS") == 0) || (strcmp (fields[1], "WIN32") == 0))
+ fdata->encrypted = (g_ascii_strcasecmp (fields[7], "11") == 0);
+ else
+ fdata->encrypted = (g_ascii_strcasecmp (fields[9], "11") == 0);
+ g_strfreev (fields);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+ arj_comm->fdata = NULL;
+ }
+
+ arj_comm->line_no++;
+}
+
+
+static void
+fr_command_arj_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, list__process_line, comm);
+
+ fr_process_begin_command (comm->process, "arj");
+ fr_process_add_arg (comm->process, "v");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, "-");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_arj_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "arj");
+
+ fr_process_add_arg (comm->process, "a");
+
+ if (base_dir != NULL)
+ fr_process_set_working_dir (comm->process, base_dir);
+
+ if (update)
+ fr_process_add_arg (comm->process, "-u");
+
+ if (comm->password != NULL)
+ fr_process_add_arg_concat (comm->process, "-g/", comm->password, NULL);
+
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-m3"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-m2"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-m1"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-m1"); break;
+ }
+
+ fr_process_add_arg (comm->process, "-i");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, "-");
+
+ fr_process_add_arg (comm->process, comm->filename);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, (gchar*) scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_arj_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "arj");
+ fr_process_add_arg (comm->process, "d");
+
+ fr_process_add_arg (comm->process, "-i");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, "-");
+
+ fr_process_add_arg (comm->process, comm->filename);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_arj_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "arj");
+
+ if (junk_paths)
+ fr_process_add_arg (comm->process, "e");
+ else
+ fr_process_add_arg (comm->process, "x");
+
+ if (dest_dir != NULL)
+ fr_process_add_arg_concat (comm->process, "-ht/", dest_dir, NULL);
+
+ if (! overwrite)
+ fr_process_add_arg (comm->process, "-n");
+
+ if (skip_older)
+ fr_process_add_arg (comm->process, "-u");
+
+ if (comm->password != NULL)
+ fr_process_add_arg_concat (comm->process, "-g/", comm->password, NULL);
+ else
+ fr_process_add_arg (comm->process, "-g/");
+
+ fr_process_add_arg (comm->process, "-i");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, "-");
+
+ fr_process_add_arg (comm->process, comm->filename);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_arj_test (FrCommand *comm)
+{
+ fr_process_begin_command (comm->process, "arj");
+ fr_process_add_arg (comm->process, "t");
+ if (comm->password != NULL)
+ fr_process_add_arg_concat (comm->process, "-g/", comm->password, NULL);
+ fr_process_add_arg (comm->process, "-i");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, "-");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_arj_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ if (error->type != FR_PROC_ERROR_NONE) {
+ if (error->status <= 1)
+ error->type = FR_PROC_ERROR_NONE;
+ else if (error->status == 3)
+ error->type = FR_PROC_ERROR_ASK_PASSWORD;
+ }
+}
+
+
+const char *arj_mime_type[] = { "application/x-arj", NULL };
+
+
+const char **
+fr_command_arj_get_mime_types (FrCommand *comm)
+{
+ return arj_mime_type;
+}
+
+
+FrCommandCap
+fr_command_arj_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES | FR_COMMAND_CAN_ENCRYPT;
+ if (is_program_available ("arj", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_arj_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("arj");
+}
+
+
+static void
+fr_command_arj_class_init (FrCommandArjClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_arj_finalize;
+
+ afc->list = fr_command_arj_list;
+ afc->add = fr_command_arj_add;
+ afc->delete = fr_command_arj_delete;
+ afc->extract = fr_command_arj_extract;
+ afc->test = fr_command_arj_test;
+ afc->handle_error = fr_command_arj_handle_error;
+ afc->get_mime_types = fr_command_arj_get_mime_types;
+ afc->get_capabilities = fr_command_arj_get_capabilities;
+ afc->get_packages = fr_command_arj_get_packages;
+}
+
+
+static void
+fr_command_arj_init (FrCommand *comm)
+{
+ FrCommandArj *arj_comm;
+
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propAddCanStoreFolders = FALSE;
+ comm->propExtractCanAvoidOverwrite = TRUE;
+ comm->propExtractCanSkipOlder = TRUE;
+ comm->propExtractCanJunkPaths = TRUE;
+ comm->propPassword = TRUE;
+ comm->propTest = TRUE;
+
+ arj_comm = FR_COMMAND_ARJ (comm);
+ arj_comm->list_started = FALSE;
+ arj_comm->fdata = FALSE;
+ arj_comm->filename_line_regex = g_regex_new ("[0-9]+\\) ", G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+
+static void
+fr_command_arj_finalize (GObject *object)
+{
+ FrCommandArj *arj_comm;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_ARJ (object));
+
+ arj_comm = FR_COMMAND_ARJ (object);
+ g_regex_unref (arj_comm->filename_line_regex);
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_arj_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandArjClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_arj_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandArj),
+ 0,
+ (GInstanceInitFunc) fr_command_arj_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandArj",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-arj.h b/src/fr-command-arj.h
new file mode 100644
index 0000000..b1b860c
--- /dev/null
+++ b/src/fr-command-arj.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_ARJ_H
+#define FR_COMMAND_ARJ_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_ARJ (fr_command_arj_get_type ())
+#define FR_COMMAND_ARJ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ARJ, FrCommandArj))
+#define FR_COMMAND_ARJ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ARJ, FrCommandArjClass))
+#define FR_IS_COMMAND_ARJ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ARJ))
+#define FR_IS_COMMAND_ARJ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ARJ))
+#define FR_COMMAND_ARJ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ARJ, FrCommandArjClass))
+
+typedef struct _FrCommandArj FrCommandArj;
+typedef struct _FrCommandArjClass FrCommandArjClass;
+
+struct _FrCommandArj
+{
+ FrCommand __parent;
+
+ gboolean list_started;
+ int line_no;
+ FileData *fdata;
+ GRegex *filename_line_regex;
+};
+
+struct _FrCommandArjClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_arj_get_type (void);
+
+#endif /* FR_COMMAND_ARJ_H */
diff --git a/src/fr-command-cfile.c b/src/fr-command-cfile.c
new file mode 100644
index 0000000..d986575
--- /dev/null
+++ b/src/fr-command-cfile.c
@@ -0,0 +1,619 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-cfile.h"
+
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+static char *
+get_uncompressed_name_from_archive (FrCommand *comm,
+ const char *archive)
+{
+ GFile *file;
+ GInputStream *stream;
+ char *filename = NULL;
+
+ if (! is_mime_type (comm->mime_type, "application/x-gzip"))
+ return NULL;
+
+ file = g_file_new_for_path (archive);
+
+ stream = (GInputStream *) g_file_read (file, NULL, NULL);
+ if (stream != NULL) {
+ gboolean filename_present = TRUE;
+ char buffer[10];
+
+ if (g_input_stream_read (stream, buffer, 10, NULL, NULL) >= 0) {
+ /* Check whether the FLG.FNAME is set */
+ if (((unsigned char)(buffer[3]) & 0x08) != 0x08)
+ filename_present = FALSE;
+
+ /* Check whether the FLG.FEXTRA is set */
+ if (((unsigned char)(buffer[3]) & 0x04) == 0x04)
+ filename_present = FALSE;
+ }
+
+ if (filename_present) {
+ GString *str = NULL;
+
+ str = g_string_new ("");
+ while (g_input_stream_read (stream, buffer, 1, NULL, NULL) > 0) {
+ if (buffer[0] == '\0') {
+ filename = g_strdup (file_name_from_path (str->str));
+#ifdef DEBUG
+ g_message ("filename is: %s", filename);
+#endif
+ break;
+ }
+ g_string_append_c (str, buffer[0]);
+ }
+ g_string_free (str, TRUE);
+ }
+ g_object_unref (stream);
+ }
+ g_object_unref (file);
+
+ return filename;
+}
+
+
+static void
+list__process_line (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+ FileData *fdata;
+ char **fields;
+ char *filename;
+
+ fdata = file_data_new ();
+
+ fields = split_line (line, 2);
+ if (strcmp (fields[1], "-1") != 0)
+ fdata->size = g_ascii_strtoull (fields[1], NULL, 10);
+ g_strfreev (fields);
+
+ if (fdata->size == 0)
+ fdata->size = get_file_size (comm->filename);
+
+ filename = get_uncompressed_name_from_archive (comm, comm->filename);
+ if (filename == NULL)
+ filename = remove_extension_from_path (comm->filename);
+
+ fdata->full_path = g_strconcat ("/",
+ file_name_from_path (filename),
+ NULL);
+ g_free (filename);
+
+ fdata->original_path = fdata->full_path + 1;
+ fdata->link = NULL;
+ fdata->modified = get_file_mtime_for_path (comm->filename);
+
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+fr_command_cfile_list (FrCommand *comm)
+{
+ FrCommandCFile *comm_cfile = FR_COMMAND_CFILE (comm);
+
+ if (is_mime_type (comm->mime_type, "application/x-gzip")) {
+ /* gzip let us known the uncompressed size */
+
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process,
+ list__process_line,
+ comm);
+
+ fr_process_begin_command (comm->process, "gzip");
+ fr_process_add_arg (comm->process, "-l");
+ fr_process_add_arg (comm->process, "-q");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+ }
+ else {
+ /* ... other compressors do not support this feature so
+ * simply use the archive size, suboptimal but there is no
+ * alternative. */
+
+ FileData *fdata;
+ char *filename;
+
+ fdata = file_data_new ();
+
+ filename = remove_extension_from_path (comm->filename);
+ fdata->full_path = g_strconcat ("/",
+ file_name_from_path (filename),
+ NULL);
+ g_free (filename);
+
+ fdata->original_path = fdata->full_path + 1;
+ fdata->link = NULL;
+ fdata->size = get_file_size_for_path (comm->filename);
+ fdata->modified = get_file_mtime_for_path (comm->filename);
+
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+
+ comm_cfile->error.type = FR_PROC_ERROR_NONE;
+ comm_cfile->error.status = 0;
+ g_signal_emit_by_name (G_OBJECT (comm),
+ "done",
+ comm->action,
+ &comm_cfile->error);
+ }
+}
+
+
+static void
+fr_command_cfile_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ const char *filename;
+ char *temp_dir;
+ char *temp_file;
+ char *compressed_filename;
+
+ if ((file_list == NULL) || (file_list->data == NULL))
+ return;
+
+ /* copy file to the temp dir */
+
+ temp_dir = get_temp_work_dir (NULL);
+ filename = file_list->data;
+ temp_file = g_strconcat (temp_dir, "/", filename, NULL);
+
+ fr_process_begin_command (comm->process, "cp");
+ fr_process_set_working_dir (comm->process, base_dir);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, filename);
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+
+ /**/
+
+ if (is_mime_type (comm->mime_type, "application/x-gzip")) {
+ fr_process_begin_command (comm->process, "gzip");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+ compressed_filename = g_strconcat (filename, ".gz", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-bzip")) {
+ fr_process_begin_command (comm->process, "bzip2");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+ compressed_filename = g_strconcat (filename, ".bz2", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-compress")) {
+ fr_process_begin_command (comm->process, "compress");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+ compressed_filename = g_strconcat (filename, ".Z", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzip")) {
+ fr_process_begin_command (comm->process, "lzip");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+ compressed_filename = g_strconcat (filename, ".lz", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzma")) {
+ fr_process_begin_command (comm->process, "lzma");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+ compressed_filename = g_strconcat (filename, ".lzma", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-xz")) {
+ fr_process_begin_command (comm->process, "xz");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+ compressed_filename = g_strconcat (filename, ".xz", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzop")) {
+ fr_process_begin_command (comm->process, "lzop");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "-fU");
+ fr_process_add_arg (comm->process, "--no-stdin");
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+ compressed_filename = g_strconcat (filename, ".lzo", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-rzip")) {
+ fr_process_begin_command (comm->process, "rzip");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+ compressed_filename = g_strconcat (filename, ".rz", NULL);
+ }
+
+ /* copy compressed file to the dest dir */
+
+ fr_process_begin_command (comm->process, "cp");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, compressed_filename);
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+
+ /* remove the temp dir */
+
+ fr_process_begin_command (comm->process, "rm");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_add_arg (comm->process, "-rf");
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, temp_dir);
+ fr_process_end_command (comm->process);
+
+ g_free (compressed_filename);
+ g_free (temp_file);
+ g_free (temp_dir);
+}
+
+
+static void
+fr_command_cfile_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ /* never called */
+}
+
+
+static void
+fr_command_cfile_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ char *temp_dir;
+ char *dest_file;
+ char *temp_file;
+ char *uncompr_file;
+ char *compr_file;
+
+ /* copy file to the temp dir, remove the already existing file first */
+
+ temp_dir = get_temp_work_dir (NULL);
+ temp_file = g_strconcat (temp_dir,
+ "/",
+ file_name_from_path (comm->filename),
+ NULL);
+
+ fr_process_begin_command (comm->process, "cp");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+
+ /* uncompress the file */
+
+ if (is_mime_type (comm->mime_type, "application/x-gzip")) {
+ fr_process_begin_command (comm->process, "gzip");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, "-n");
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-bzip")) {
+ fr_process_begin_command (comm->process, "bzip2");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-compress")) {
+ if (is_program_in_path ("gzip")) {
+ fr_process_begin_command (comm->process, "gzip");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, "-n");
+ }
+ else
+ fr_process_begin_command (comm->process, "uncompress");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzip")) {
+ fr_process_begin_command (comm->process, "lzip");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzma")) {
+ fr_process_begin_command (comm->process, "lzma");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-xz")) {
+ fr_process_begin_command (comm->process, "xz");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzop")) {
+ fr_process_begin_command (comm->process, "lzop");
+ fr_process_set_working_dir (comm->process, temp_dir);
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, "-fU");
+ fr_process_add_arg (comm->process, "--no-stdin");
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-rzip")) {
+ fr_process_begin_command (comm->process, "rzip");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, temp_file);
+ fr_process_end_command (comm->process);
+ }
+
+ /* copy uncompress file to the dest dir */
+
+ uncompr_file = remove_extension_from_path (temp_file);
+
+ compr_file = get_uncompressed_name_from_archive (comm, comm->filename);
+ if (compr_file == NULL)
+ compr_file = remove_extension_from_path (file_name_from_path (comm->filename));
+ dest_file = g_strconcat (dest_dir,
+ "/",
+ compr_file,
+ NULL);
+
+ fr_process_begin_command (comm->process, "cp");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, uncompr_file);
+ fr_process_add_arg (comm->process, dest_file);
+ fr_process_end_command (comm->process);
+
+ /* remove the temp dir */
+
+ fr_process_begin_command (comm->process, "rm");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_add_arg (comm->process, "-rf");
+ fr_process_add_arg (comm->process, temp_dir);
+ fr_process_end_command (comm->process);
+
+ g_free (dest_file);
+ g_free (compr_file);
+ g_free (uncompr_file);
+ g_free (temp_file);
+ g_free (temp_dir);
+}
+
+
+const char *cfile_mime_type[] = { "application/x-gzip",
+ "application/x-bzip",
+ "application/x-compress",
+ "application/x-lzip",
+ "application/x-lzma",
+ "application/x-lzop",
+ "application/x-rzip",
+ "application/x-xz",
+ NULL };
+
+
+const char **
+fr_command_cfile_get_mime_types (FrCommand *comm)
+{
+ return cfile_mime_type;
+}
+
+
+FrCommandCap
+fr_command_cfile_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_DO_NOTHING;
+ if (is_mime_type (mime_type, "application/x-gzip")) {
+ if (is_program_available ("gzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-bzip")) {
+ if (is_program_available ("bzip2", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-compress")) {
+ if (is_program_available ("compress", check_command))
+ capabilities |= FR_COMMAND_CAN_WRITE;
+ if (is_program_available ("uncompress", check_command) || is_program_available ("gzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+ }
+ else if (is_mime_type (mime_type, "application/x-lzip")) {
+ if (is_program_available ("lzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-lzma")) {
+ if (is_program_available ("lzma", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-xz")) {
+ if (is_program_available ("xz", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-lzop")) {
+ if (is_program_available ("lzop", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-rzip")) {
+ if (is_program_available ("rzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+
+ return capabilities;
+}
+
+
+static void
+fr_command_cfile_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_CFILE (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static const char *
+fr_command_cfile_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ if (is_mime_type (mime_type, "application/x-gzip"))
+ return PACKAGES ("gzip");
+ else if (is_mime_type (mime_type, "application/x-bzip"))
+ return PACKAGES ("bzip2");
+ else if (is_mime_type (mime_type, "application/x-compress"))
+ return PACKAGES ("ncompress");
+ else if (is_mime_type (mime_type, "application/x-lzip"))
+ return PACKAGES ("lzip");
+ else if (is_mime_type (mime_type, "application/x-lzma"))
+ return PACKAGES ("lzma");
+ else if (is_mime_type (mime_type, "application/x-xz"))
+ return PACKAGES ("xz");
+ else if (is_mime_type (mime_type, "application/x-lzop"))
+ return PACKAGES ("lzop");
+ else if (is_mime_type (mime_type, "application/x-rzip"))
+ return PACKAGES ("rzip");
+
+ return NULL;
+}
+
+
+static void
+fr_command_cfile_class_init (FrCommandCFileClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_cfile_finalize;
+
+ afc->list = fr_command_cfile_list;
+ afc->add = fr_command_cfile_add;
+ afc->delete = fr_command_cfile_delete;
+ afc->extract = fr_command_cfile_extract;
+ afc->get_mime_types = fr_command_cfile_get_mime_types;
+ afc->get_capabilities = fr_command_cfile_get_capabilities;
+ afc->get_packages = fr_command_cfile_get_packages;
+}
+
+
+static void
+fr_command_cfile_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+}
+
+
+GType
+fr_command_cfile_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandCFileClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_cfile_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandCFile),
+ 0,
+ (GInstanceInitFunc) fr_command_cfile_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandCFile",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-cfile.h b/src/fr-command-cfile.h
new file mode 100644
index 0000000..d4e251c
--- /dev/null
+++ b/src/fr-command-cfile.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_CFILE_H
+#define FR_COMMAND_CFILE_H
+
+#include <gtk/gtk.h>
+#include "fr-command.h"
+#include "typedefs.h"
+
+#define FR_TYPE_COMMAND_CFILE (fr_command_cfile_get_type ())
+#define FR_COMMAND_CFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_CFILE, FrCommandCFile))
+#define FR_COMMAND_CFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_CFILE, FrCommandCFileClass))
+#define FR_IS_COMMAND_CFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_CFILE))
+#define FR_IS_COMMAND_CFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_CFILE))
+#define FR_COMMAND_CFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_CFILE, FrCommandCFileClass))
+
+typedef struct _FrCommandCFile FrCommandCFile;
+typedef struct _FrCommandCFileClass FrCommandCFileClass;
+
+struct _FrCommandCFile
+{
+ FrCommand __parent;
+
+ /*<private>*/
+
+ FrProcError error;
+};
+
+struct _FrCommandCFileClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_cfile_get_type (void);
+
+#endif /* FR_COMMAND_CFILE_H */
diff --git a/src/fr-command-cpio.c b/src/fr-command-cpio.c
new file mode 100644
index 0000000..feeaad7
--- /dev/null
+++ b/src/fr-command-cpio.c
@@ -0,0 +1,329 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2006 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-cpio.h"
+
+static void fr_command_cpio_class_init (FrCommandCpioClass *class);
+static void fr_command_cpio_init (FrCommand *afile);
+static void fr_command_cpio_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+static time_t
+mktime_from_string (char *month,
+ char *mday,
+ char *year)
+{
+ static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ struct tm tm = {0, };
+
+ tm.tm_isdst = -1;
+
+ if (month != NULL) {
+ int i;
+ for (i = 0; i < 12; i++)
+ if (strcmp (months[i], month) == 0) {
+ tm.tm_mon = i;
+ break;
+ }
+ }
+ tm.tm_mday = atoi (mday);
+ if (strchr (year, ':') != NULL) {
+ char **fields = g_strsplit (year, ":", 2);
+ if (n_fields (fields) == 2) {
+ time_t now;
+ struct tm *now_tm;
+
+ tm.tm_hour = atoi (fields[0]);
+ tm.tm_min = atoi (fields[1]);
+
+ now = time(NULL);
+ now_tm = localtime (&now);
+ tm.tm_year = now_tm->tm_year;
+ }
+ } else
+ tm.tm_year = atoi (year) - 1900;
+
+ return mktime (&tm);
+}
+
+
+static void
+list__process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+ char **fields;
+ const char *name_field;
+ char *name;
+ int ofs = 0;
+
+ g_return_if_fail (line != NULL);
+
+ fdata = file_data_new ();
+
+#ifdef __sun
+ fields = split_line (line, 9);
+ fdata->size = g_ascii_strtoull (fields[4], NULL, 10);
+ fdata->modified = mktime_from_string (fields[5], fields[6], fields[8]);
+ g_strfreev (fields);
+
+ name_field = get_last_field (line, 10);
+#else /* !__sun */
+ /* Handle char and block device files */
+ if ((line[0] == 'c') || (line[0] == 'b')) {
+ fields = split_line (line, 9);
+ ofs = 1;
+ fdata->size = 0;
+ /* FIXME: We should also specify the content type */
+ }
+ else {
+ fields = split_line (line, 8);
+ fdata->size = g_ascii_strtoull (fields[4], NULL, 10);
+ }
+ fdata->modified = mktime_from_string (fields[5+ofs], fields[6+ofs], fields[7+ofs]);
+ g_strfreev (fields);
+
+ name_field = get_last_field (line, 9+ofs);
+#endif /* !__sun */
+
+ fields = g_strsplit (name_field, " -> ", 2);
+
+ if (fields[1] == NULL) {
+ g_strfreev (fields);
+ fields = g_strsplit (name_field, " link to ", 2);
+ }
+
+ fdata->dir = line[0] == 'd';
+
+ name = g_strcompress (fields[0]);
+ if (*(fields[0]) == '/') {
+ fdata->full_path = g_strdup (name);
+ fdata->original_path = fdata->full_path;
+ }
+ else {
+ fdata->full_path = g_strconcat ("/", name, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+
+ if (fdata->dir && (name[strlen (name) - 1] != '/')) {
+ char *old_full_path = fdata->full_path;
+ fdata->full_path = g_strconcat (old_full_path, "/", NULL);
+ g_free (old_full_path);
+ fdata->original_path = g_strdup (name);
+ fdata->free_original_path = TRUE;
+ }
+ g_free (name);
+
+ if (fields[1] != NULL)
+ fdata->link = g_strcompress (fields[1]);
+ g_strfreev (fields);
+
+ if (fdata->dir)
+ fdata->name = dir_name_from_path (fdata->full_path);
+ else
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+fr_command_cpio_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, list__process_line, comm);
+
+ fr_process_begin_command (comm->process, "sh");
+ fr_process_add_arg (comm->process, "-c");
+ fr_process_add_arg_concat (comm->process, "cpio -itv < ", comm->e_filename, NULL);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_cpio_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+ GString *cmd;
+
+ fr_process_begin_command (comm->process, "sh");
+ if (dest_dir != NULL)
+ fr_process_set_working_dir (comm->process, dest_dir);
+ fr_process_add_arg (comm->process, "-c");
+
+ cmd = g_string_new ("cpio -idu --no-absolute-filenames ");
+ for (scan = file_list; scan; scan = scan->next) {
+ char *filepath = scan->data;
+ char *filename;
+
+ if (filepath[0] == '/')
+ filename = g_shell_quote (filepath + 1);
+ else
+ filename = g_shell_quote (filepath);
+ g_string_append (cmd, filename);
+ g_string_append (cmd, " ");
+
+ g_free (filename);
+ }
+ g_string_append (cmd, " < ");
+ g_string_append (cmd, comm->e_filename);
+ fr_process_add_arg (comm->process, cmd->str);
+ g_string_free (cmd, TRUE);
+
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+const char *cpio_mime_type[] = { "application/x-cpio", NULL };
+
+
+const char **
+fr_command_cpio_get_mime_types (FrCommand *comm)
+{
+ return cpio_mime_type;
+}
+
+
+FrCommandCap
+fr_command_cpio_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("cpio", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_cpio_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("cpio");
+}
+
+
+static void
+fr_command_cpio_class_init (FrCommandCpioClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_cpio_finalize;
+
+ afc->list = fr_command_cpio_list;
+ afc->extract = fr_command_cpio_extract;
+ afc->get_mime_types = fr_command_cpio_get_mime_types;
+ afc->get_capabilities = fr_command_cpio_get_capabilities;
+ afc->get_packages = fr_command_cpio_get_packages;
+}
+
+
+static void
+fr_command_cpio_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = FALSE;
+ comm->propAddCanReplace = FALSE;
+ comm->propAddCanStoreFolders = FALSE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+}
+
+
+static void
+fr_command_cpio_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_CPIO (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_cpio_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandCpioClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_cpio_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandCpio),
+ 0,
+ (GInstanceInitFunc) fr_command_cpio_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandCpio",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-cpio.h b/src/fr-command-cpio.h
new file mode 100644
index 0000000..f94159d
--- /dev/null
+++ b/src/fr-command-cpio.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2006 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_CPIO_H
+#define FR_COMMAND_CPIO_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_CPIO (fr_command_cpio_get_type ())
+#define FR_COMMAND_CPIO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_CPIO, FrCommandCpio))
+#define FR_COMMAND_CPIO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_CPIO, FrCommandCpioClass))
+#define FR_IS_COMMAND_CPIO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_CPIO))
+#define FR_IS_COMMAND_CPIO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_CPIO))
+#define FR_COMMAND_CPIO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_CPIO, FrCommandCpioClass))
+
+typedef struct _FrCommandCpio FrCommandCpio;
+typedef struct _FrCommandCpioClass FrCommandCpioClass;
+
+struct _FrCommandCpio
+{
+ FrCommand __parent;
+ gboolean is_empty;
+};
+
+struct _FrCommandCpioClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_cpio_get_type (void);
+
+#endif /* FR_COMMAND_CPIO_H */
diff --git a/src/fr-command-dpkg.c b/src/fr-command-dpkg.c
new file mode 100644
index 0000000..8c2c012
--- /dev/null
+++ b/src/fr-command-dpkg.c
@@ -0,0 +1,314 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-dpkg.h"
+
+static void fr_command_dpkg_class_init (FrCommandDpkgClass *class);
+static void fr_command_dpkg_init (FrCommand *afile);
+static void fr_command_dpkg_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+static void
+process_metadata_line (char *line,
+ FrCommand *comm)
+{
+ FileData *fdata;
+ char **fields;
+ char *name;
+
+ g_return_if_fail (line != NULL);
+
+ fields = split_line (line, 6);
+ if (!fields[1] || !g_str_equal (fields[1], "bytes,")) {
+ g_strfreev (fields);
+ return;
+ }
+
+ fdata = file_data_new ();
+ fdata->size = g_ascii_strtoull (fields[0], NULL, 10);
+
+ if (fields[5] && g_str_equal (fields[4],"*")) {
+ name = g_strdup (fields[5]);
+ } else {
+ name = g_strdup (get_last_field (line, 5));
+ }
+ g_strstrip (name);
+
+ fdata->full_path = g_strconcat ("/DEBIAN/", name, NULL);
+ fdata->original_path = fdata->full_path + 1;
+
+ g_strfreev (fields);
+ g_free (name);
+
+ fdata->name = g_strdup (name);
+ fdata->path = g_strdup ("DEBIAN");
+ fr_command_add_file (comm, fdata);
+}
+
+static void
+process_data_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+ char **fields;
+ char **tmfields;
+ struct tm tm = {0, };
+ const char *name;
+
+ g_return_if_fail (line != NULL);
+
+ if (line[0] == ' ') {
+ /* This is the output of dpkg-deb -I */
+ process_metadata_line (line, comm);
+ return;
+ }
+
+ fdata = file_data_new ();
+
+ fields = split_line (line, 5);
+ fdata->size = g_ascii_strtoull (fields[2], NULL, 10);
+ tmfields = g_strsplit(fields[3], "-", 3);
+ if (tmfields[2]) {
+ tm.tm_year = atoi (tmfields[0]) - 1900;
+ tm.tm_mon = atoi (tmfields[1]);
+ tm.tm_mday = atoi (tmfields[2]);
+ }
+ g_strfreev (tmfields);
+ tmfields = g_strsplit (fields[4], ":", 2);
+ if (tmfields[1]) {
+ tm.tm_hour = atoi (tmfields[0]);
+ tm.tm_min = atoi (tmfields[1]);
+ }
+ g_strfreev (tmfields);
+ fdata->modified = mktime (&tm);
+ g_strfreev (fields);
+
+ name = get_last_field (line, 6);
+ fields = g_strsplit (name, " -> ", 2);
+
+ fdata->dir = line[0] == 'd';
+ name = fields[0];
+ if (g_str_has_prefix (name, "./")) { /* Should generally be the case */
+ fdata->full_path = g_strdup (name + 1);
+ fdata->original_path = fdata->full_path + 1;
+ } else if (name[0] == '/') {
+ fdata->full_path = g_strdup (name);
+ fdata->original_path = fdata->full_path;
+ } else {
+ fdata->full_path = g_strconcat ("/", name, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+ if (fdata->dir && (name[strlen (name) - 1] != '/')) {
+ char *old_full_path = fdata->full_path;
+ fdata->full_path = g_strconcat (old_full_path, "/", NULL);
+ g_free (old_full_path);
+ fdata->original_path = g_strdup (name);
+ fdata->free_original_path = TRUE;
+ }
+
+ if (fields[1] != NULL)
+ fdata->link = g_strdup (fields[1]);
+ g_strfreev (fields);
+
+ if (fdata->dir)
+ fdata->name = dir_name_from_path (fdata->full_path);
+ else
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+fr_command_dpkg_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, process_data_line, comm);
+
+ fr_process_begin_command (comm->process, "dpkg-deb");
+ fr_process_add_arg (comm->process, "-I");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+
+ fr_process_begin_command (comm->process, "dpkg-deb");
+ fr_process_add_arg (comm->process, "-c");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_dpkg_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ fr_process_begin_command (comm->process, "dpkg-deb");
+ fr_process_add_arg (comm->process, "-x");
+ fr_process_add_arg (comm->process, comm->filename);
+ if (dest_dir != NULL) {
+ fr_process_add_arg (comm->process, dest_dir);
+ } else {
+ fr_process_add_arg (comm->process, ".");
+ }
+ /* FIXME it is not possible to unpack only some files */
+ fr_process_end_command (comm->process);
+
+ /* Also extract metadata in DEBIAN/ */
+ fr_process_begin_command (comm->process, "dpkg-deb");
+ if (dest_dir != NULL) {
+ fr_process_set_working_dir (comm->process, dest_dir);
+ }
+ fr_process_add_arg (comm->process, "-e");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+
+ fr_process_start (comm->process);
+}
+
+
+const char *dpkg_mime_type[] = { "application/x-deb", NULL };
+
+
+const char **
+fr_command_dpkg_get_mime_types (FrCommand *comm)
+{
+ return dpkg_mime_type;
+}
+
+
+FrCommandCap
+fr_command_dpkg_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("dpkg-deb", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_dpkg_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("dpkg");
+}
+
+
+static void
+fr_command_dpkg_class_init (FrCommandDpkgClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_dpkg_finalize;
+
+ afc->list = fr_command_dpkg_list;
+ afc->extract = fr_command_dpkg_extract;
+ afc->get_mime_types = fr_command_dpkg_get_mime_types;
+ afc->get_capabilities = fr_command_dpkg_get_capabilities;
+ afc->get_packages = fr_command_dpkg_get_packages;
+}
+
+
+static void
+fr_command_dpkg_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = FALSE;
+ comm->propAddCanReplace = FALSE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+}
+
+
+static void
+fr_command_dpkg_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_DPKG (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_dpkg_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandDpkgClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_dpkg_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandDpkg),
+ 0,
+ (GInstanceInitFunc) fr_command_dpkg_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandDpkg",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-dpkg.h b/src/fr-command-dpkg.h
new file mode 100644
index 0000000..802eb54
--- /dev/null
+++ b/src/fr-command-dpkg.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_DPKG_H
+#define FR_COMMAND_DPKG_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_DPKG (fr_command_dpkg_get_type ())
+#define FR_COMMAND_DPKG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_DPKG, FrCommandDpkg))
+#define FR_COMMAND_DPKG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_DPKG, FrCommandDpkgClass))
+#define FR_IS_COMMAND_DPKG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_DPKG))
+#define FR_IS_COMMAND_DPKG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_DPKG))
+#define FR_COMMAND_DPKG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_DPKG, FrCommandDpkgClass))
+
+typedef struct _FrCommandDpkg FrCommandDpkg;
+typedef struct _FrCommandDpkgClass FrCommandDpkgClass;
+
+struct _FrCommandDpkg
+{
+ FrCommand __parent;
+ gboolean is_empty;
+};
+
+struct _FrCommandDpkgClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_dpkg_get_type (void);
+
+#endif /* FR_COMMAND_DPKG_H */
diff --git a/src/fr-command-iso.c b/src/fr-command-iso.c
new file mode 100644
index 0000000..011c734
--- /dev/null
+++ b/src/fr-command-iso.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2004 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-iso.h"
+
+static void fr_command_iso_class_init (FrCommandIsoClass *class);
+static void fr_command_iso_init (FrCommand *afile);
+static void fr_command_iso_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+static time_t
+mktime_from_string (char *month,
+ char *mday,
+ char *year)
+{
+ static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ struct tm tm = {0, };
+
+ tm.tm_isdst = -1;
+
+ if (month != NULL) {
+ int i;
+ for (i = 0; i < 12; i++)
+ if (strcmp (months[i], month) == 0) {
+ tm.tm_mon = i;
+ break;
+ }
+ }
+ tm.tm_mday = atoi (mday);
+ tm.tm_year = atoi (year) - 1900;
+
+ return mktime (&tm);
+}
+
+
+static void
+list__process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+ FrCommandIso *comm_iso = FR_COMMAND_ISO (comm);
+ char **fields;
+ const char *name_field;
+
+ g_return_if_fail (line != NULL);
+
+ if (line[0] == 'd') /* Ignore directories. */
+ return;
+
+ if (line[0] == 'D') {
+ g_free (comm_iso->cur_path);
+ comm_iso->cur_path = g_strdup (get_last_field (line, 4));
+
+ } else if (line[0] == '-') { /* Is file */
+ const char *last_field, *first_bracket;
+
+ fdata = file_data_new ();
+
+ fields = split_line (line, 8);
+ fdata->size = g_ascii_strtoull (fields[4], NULL, 10);
+ fdata->modified = mktime_from_string (fields[5], fields[6], fields[7]);
+ g_strfreev (fields);
+
+ /* Full path */
+
+ last_field = get_last_field (line, 9);
+ first_bracket = strchr (last_field, ']');
+ if (first_bracket == NULL) {
+ file_data_free (fdata);
+ return;
+ }
+
+ name_field = eat_spaces (first_bracket + 1);
+ if ((name_field == NULL)
+ || (strcmp (name_field, ".") == 0)
+ || (strcmp (name_field, "..") == 0)) {
+ file_data_free (fdata);
+ return;
+ }
+
+ if (comm_iso->cur_path[0] != '/')
+ fdata->full_path = g_strstrip (g_strconcat ("/", comm_iso->cur_path, name_field, NULL));
+ else
+ fdata->full_path = g_strstrip (g_strconcat (comm_iso->cur_path, name_field, NULL));
+ fdata->original_path = fdata->full_path;
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ fr_command_add_file (comm, fdata);
+ }
+}
+
+
+static void
+list__begin (gpointer data)
+{
+ FrCommandIso *comm = data;
+
+ g_free (comm->cur_path);
+ comm->cur_path = NULL;
+}
+
+
+static void
+fr_command_iso_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, list__process_line, comm);
+
+ fr_process_begin_command (comm->process, "sh");
+ fr_process_set_begin_func (comm->process, list__begin, comm);
+ fr_process_add_arg (comm->process, SHDIR "isoinfo.sh");
+ fr_process_add_arg (comm->process, "-i");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_add_arg (comm->process, "-l");
+ fr_process_end_command (comm->process);
+
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_iso_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ for (scan = file_list; scan; scan = scan->next) {
+ char *path = scan->data;
+ const char *filename;
+ char *file_dir;
+ char *temp_dest_dir = NULL;
+
+ filename = file_name_from_path (path);
+ file_dir = remove_level_from_path (path);
+ if ((file_dir != NULL) && (strcmp (file_dir, "/") != 0))
+ temp_dest_dir = g_build_filename (dest_dir, file_dir, NULL);
+ else
+ temp_dest_dir = g_strdup (dest_dir);
+ g_free (file_dir);
+
+ if (temp_dest_dir == NULL)
+ continue;
+
+ make_directory_tree_from_path (temp_dest_dir, 0700, NULL);
+
+ fr_process_begin_command (comm->process, "sh");
+ fr_process_set_working_dir (comm->process, temp_dest_dir);
+ fr_process_add_arg (comm->process, SHDIR "isoinfo.sh");
+ fr_process_add_arg (comm->process, "-i");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_add_arg (comm->process, "-x");
+ fr_process_add_arg (comm->process, path);
+ fr_process_add_arg (comm->process, filename);
+ fr_process_end_command (comm->process);
+
+ g_free (temp_dest_dir);
+ }
+
+ fr_process_start (comm->process);
+}
+
+
+const char *iso_mime_type[] = { "application/x-cd-image", NULL };
+
+
+const char **
+fr_command_iso_get_mime_types (FrCommand *comm)
+{
+ return iso_mime_type;
+}
+
+
+FrCommandCap
+fr_command_iso_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("isoinfo", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_iso_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("genisoimage");
+}
+
+
+static void
+fr_command_iso_class_init (FrCommandIsoClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_iso_finalize;
+
+ afc->list = fr_command_iso_list;
+ afc->extract = fr_command_iso_extract;
+ afc->get_mime_types = fr_command_iso_get_mime_types;
+ afc->get_capabilities = fr_command_iso_get_capabilities;
+ afc->get_packages = fr_command_iso_get_packages;
+}
+
+
+static void
+fr_command_iso_init (FrCommand *comm)
+{
+ FrCommandIso *comm_iso = FR_COMMAND_ISO (comm);
+
+ comm_iso->cur_path = NULL;
+ comm_iso->joliet = TRUE;
+
+ comm->propAddCanUpdate = FALSE;
+ comm->propAddCanReplace = FALSE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+ comm->propCanExtractAll = FALSE;
+}
+
+
+static void
+fr_command_iso_finalize (GObject *object)
+{
+ FrCommandIso *comm_iso;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_ISO (object));
+
+ comm_iso = FR_COMMAND_ISO (object);
+
+ g_free (comm_iso->cur_path);
+ comm_iso->cur_path = NULL;
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_iso_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandIsoClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_iso_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandIso),
+ 0,
+ (GInstanceInitFunc) fr_command_iso_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandIso",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-iso.h b/src/fr-command-iso.h
new file mode 100644
index 0000000..aba1814
--- /dev/null
+++ b/src/fr-command-iso.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_ISO_H
+#define FR_COMMAND_ISO_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_ISO (fr_command_iso_get_type ())
+#define FR_COMMAND_ISO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ISO, FrCommandIso))
+#define FR_COMMAND_ISO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ISO, FrCommandIsoClass))
+#define FR_IS_COMMAND_ISO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ISO))
+#define FR_IS_COMMAND_ISO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ISO))
+#define FR_COMMAND_ISO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ISO, FrCommandIsoClass))
+
+typedef struct _FrCommandIso FrCommandIso;
+typedef struct _FrCommandIsoClass FrCommandIsoClass;
+
+struct _FrCommandIso
+{
+ FrCommand __parent;
+ char *cur_path;
+ gboolean joliet;
+};
+
+struct _FrCommandIsoClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_iso_get_type (void);
+
+#endif /* FR_COMMAND_ISO_H */
diff --git a/src/fr-command-jar.c b/src/fr-command-jar.c
new file mode 100644
index 0000000..3edb2e9
--- /dev/null
+++ b/src/fr-command-jar.c
@@ -0,0 +1,243 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2006 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <glib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "file-utils.h"
+#include "fr-command.h"
+#include "fr-command-zip.h"
+#include "fr-command-jar.h"
+#include "java-utils.h"
+
+
+typedef struct {
+ char *filename;
+ char *rel_path;
+ char *package_minus_one_level;
+ char *link_name; /* package dir = package_minus_one_level + '/' + link_name */
+} JarData;
+
+
+static void fr_command_jar_class_init (FrCommandJarClass *class);
+static void fr_command_jar_init (FrCommand *afile);
+static void fr_command_jar_finalize (GObject *object);
+
+
+static FrCommandClass *parent_class = NULL;
+
+
+static void
+fr_command_jar_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ FrProcess *proc = comm->process;
+ GList *zip_list = NULL, *jardata_list = NULL, *jar_list = NULL;
+ GList *scan;
+ char *tmp_dir;
+
+ for (scan = file_list; scan; scan = scan->next) {
+ char *filename = scan->data;
+ char *path = build_uri (base_dir, filename, NULL);
+ char *package = NULL;
+
+ if (file_extension_is (filename, ".java"))
+ package = get_package_name_from_java_file (path);
+ else if (file_extension_is (filename, ".class"))
+ package = get_package_name_from_class_file (path);
+
+ if ((package == NULL) || (strlen (package) == 0))
+ zip_list = g_list_append (zip_list, g_strdup (filename));
+ else {
+ JarData *newdata = g_new0 (JarData, 1);
+
+ newdata->package_minus_one_level = remove_level_from_path (package);
+ newdata->link_name = g_strdup (file_name_from_path (package));
+ newdata->rel_path = remove_level_from_path (filename);
+ newdata->filename = g_strdup (file_name_from_path (filename));
+ jardata_list = g_list_append (jardata_list, newdata);
+ }
+
+ g_free (package);
+ g_free (path);
+ }
+
+ tmp_dir = get_temp_work_dir (NULL);
+ for (scan = jardata_list; scan ; scan = scan->next) {
+ JarData *jdata = scan->data;
+ char *pack_path;
+ char *old_link;
+ char *link_name;
+ int retval;
+
+ pack_path = build_uri (tmp_dir, jdata->package_minus_one_level, NULL);
+ if (! make_directory_tree_from_path (pack_path, 0755, NULL)) {
+ g_free (pack_path);
+ continue;
+ }
+
+ old_link = build_uri (base_dir, jdata->rel_path, NULL);
+ link_name = g_build_filename (pack_path, jdata->link_name, NULL);
+
+ retval = symlink (old_link, link_name);
+ if ((retval != -1) || (errno == EEXIST))
+ jar_list = g_list_append (jar_list,
+ g_build_filename (jdata->package_minus_one_level,
+ jdata->link_name,
+ jdata->filename,
+ NULL));
+
+ g_free (link_name);
+ g_free (old_link);
+ g_free (pack_path);
+ }
+
+ if (zip_list != NULL)
+ parent_class->add (comm, NULL, zip_list, base_dir, update, FALSE);
+
+ if (jar_list != NULL)
+ parent_class->add (comm, NULL, jar_list, tmp_dir, update, FALSE);
+
+ fr_process_begin_command (proc, "rm");
+ fr_process_set_working_dir (proc, "/");
+ fr_process_add_arg (proc, "-r");
+ fr_process_add_arg (proc, "-f");
+ fr_process_add_arg (proc, tmp_dir);
+ fr_process_end_command (proc);
+ fr_process_set_sticky (proc, TRUE);
+
+ for (scan = jardata_list; scan ; scan = scan->next) {
+ JarData *jdata = scan->data;
+ g_free (jdata->filename);
+ g_free (jdata->package_minus_one_level);
+ g_free (jdata->link_name);
+ g_free (jdata->rel_path);
+ }
+
+ path_list_free (jardata_list);
+ path_list_free (jar_list);
+ path_list_free (zip_list);
+ g_free (tmp_dir);
+}
+
+
+const char *jar_mime_type[] = { "application/x-java-archive",
+ NULL };
+
+
+const char **
+fr_command_jar_get_mime_types (FrCommand *comm)
+{
+ return jar_mime_type;
+}
+
+
+FrCommandCap
+fr_command_jar_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("zip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_jar_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("zip,unzip");
+}
+
+
+static void
+fr_command_jar_class_init (FrCommandJarClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS(class);
+ FrCommandClass *afc = FR_COMMAND_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class->finalize = fr_command_jar_finalize;
+
+ afc->add = fr_command_jar_add;
+ afc->get_mime_types = fr_command_jar_get_mime_types;
+ afc->get_capabilities = fr_command_jar_get_capabilities;
+ afc->get_packages = fr_command_jar_get_packages;
+}
+
+
+static void
+fr_command_jar_init (FrCommand *comm)
+{
+}
+
+
+static void
+fr_command_jar_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_JAR (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_jar_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandJarClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_jar_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandJar),
+ 0,
+ (GInstanceInitFunc) fr_command_jar_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND_ZIP,
+ "FRCommandJar",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-jar.h b/src/fr-command-jar.h
new file mode 100644
index 0000000..cf108b1
--- /dev/null
+++ b/src/fr-command-jar.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2006 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_JAR_H
+#define FR_COMMAND_JAR_H
+
+#include <glib.h>
+#include "fr-command-zip.h"
+
+#define FR_TYPE_COMMAND_JAR (fr_command_jar_get_type ())
+#define FR_COMMAND_JAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_JAR, FrCommandJar))
+#define FR_COMMAND_JAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_JAR, FrCommandJarClass))
+#define FR_IS_COMMAND_JAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_JAR))
+#define FR_IS_COMMAND_JAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_JAR))
+#define FR_COMMAND_JAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_JAR, FrCommandJarClass))
+
+typedef struct _FrCommandJar FrCommandJar;
+typedef struct _FrCommandJarClass FrCommandJarClass;
+
+struct _FrCommandJar
+{
+ FrCommandZip __parent;
+};
+
+struct _FrCommandJarClass
+{
+ FrCommandZipClass __parent_class;
+};
+
+GType fr_command_jar_get_type (void);
+
+#endif /* FR_COMMAND_JAR_H */
diff --git a/src/fr-command-lha.c b/src/fr-command-lha.c
new file mode 100644
index 0000000..8fce58c
--- /dev/null
+++ b/src/fr-command-lha.c
@@ -0,0 +1,409 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-lha.h"
+
+static void fr_command_lha_class_init (FrCommandLhaClass *class);
+static void fr_command_lha_init (FrCommand *afile);
+static void fr_command_lha_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+static time_t
+mktime_from_string (char *month,
+ char *mday,
+ char *time_or_year)
+{
+ static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ struct tm tm = {0, };
+ char **fields;
+
+ tm.tm_isdst = -1;
+
+ /* date */
+
+ if (month != NULL) {
+ int i;
+ for (i = 0; i < 12; i++)
+ if (strcmp (months[i], month) == 0) {
+ tm.tm_mon = i;
+ break;
+ }
+ }
+ tm.tm_mday = atoi (mday);
+ if (strchr (time_or_year, ':') == NULL)
+ tm.tm_year = atoi (time_or_year) - 1900;
+ else {
+ time_t now;
+ struct tm *tm_now;
+
+ now = time (NULL);
+ tm_now = localtime (&now);
+ if (tm_now != NULL)
+ tm.tm_year = tm_now->tm_year;
+
+ /* time */
+
+ fields = g_strsplit (time_or_year, ":", 2);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL)
+ tm.tm_min = atoi (fields[1]);
+ }
+ g_strfreev (fields);
+ }
+
+ return mktime (&tm);
+}
+
+
+static char **
+split_line_lha (char *line)
+{
+ char **fields;
+ int n_fields = 7;
+ const char *scan, *field_end;
+ int i;
+
+ fields = g_new0 (char *, n_fields + 1);
+ fields[n_fields] = NULL;
+
+ i = 0;
+
+ if (strncmp (line, "[MS-DOS]", 8) == 0) {
+ fields[i++] = g_strdup ("");
+ fields[i++] = g_strdup ("");
+ line += strlen ("[MS-DOS]");
+ }
+ else if (strncmp (line, "[generic]", 9) == 0) {
+ fields[i++] = g_strdup ("");
+ fields[i++] = g_strdup ("");
+ line += strlen ("[generic]");
+ }
+ else if (strncmp (line, "[unknown]", 9) == 0) {
+ fields[i++] = g_strdup ("");
+ fields[i++] = g_strdup ("");
+ line += strlen ("[unknown]");
+ }
+
+ scan = eat_spaces (line);
+ for (; i < n_fields; i++) {
+ field_end = strchr (scan, ' ');
+ if (field_end != NULL) {
+ fields[i] = g_strndup (scan, field_end - scan);
+ scan = eat_spaces (field_end);
+ }
+ }
+
+ return fields;
+}
+
+
+static const char *
+get_last_field_lha (char *line)
+{
+ int i;
+ const char *field;
+ int n = 7;
+
+ if (strncmp (line, "[MS-DOS]", 8) == 0)
+ n--;
+
+ if (strncmp (line, "[generic]", 9) == 0)
+ n--;
+
+ if (strncmp (line, "[unknown]", 9) == 0)
+ n--;
+
+ field = eat_spaces (line);
+ for (i = 0; i < n; i++) {
+ field = strchr (field, ' ');
+ field = eat_spaces (field);
+ }
+
+ return field;
+}
+
+
+static void
+process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+ char **fields;
+ const char *name_field;
+
+ g_return_if_fail (line != NULL);
+
+ fdata = file_data_new ();
+
+ fields = split_line_lha (line);
+ fdata->size = g_ascii_strtoull (fields[2], NULL, 10);
+ fdata->modified = mktime_from_string (fields[4],
+ fields[5],
+ fields[6]);
+ g_strfreev (fields);
+
+ /* Full path */
+
+ name_field = get_last_field_lha (line);
+
+ if (name_field && *name_field == '/') {
+ fdata->full_path = g_strdup (name_field);
+ fdata->original_path = fdata->full_path;
+ } else {
+ fdata->full_path = g_strconcat ("/", name_field, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+
+ fdata->link = NULL;
+
+ fdata->dir = line[0] == 'd';
+ if (fdata->dir)
+ fdata->name = dir_name_from_path (fdata->full_path);
+ else
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+fr_command_lha_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, process_line, comm);
+
+ fr_process_begin_command (comm->process, "lha");
+ fr_process_add_arg (comm->process, "lq");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_lha_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "lha");
+ if (base_dir != NULL)
+ fr_process_set_working_dir (comm->process, base_dir);
+ if (update)
+ fr_process_add_arg (comm->process, "u");
+ else
+ fr_process_add_arg (comm->process, "a");
+ fr_process_add_arg (comm->process, comm->filename);
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_lha_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "lha");
+ fr_process_add_arg (comm->process, "d");
+ fr_process_add_arg (comm->process, comm->filename);
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_lha_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+ char options[5];
+ int i = 0;
+
+ fr_process_begin_command (comm->process, "lha");
+
+ if (dest_dir != NULL)
+ fr_process_set_working_dir (comm->process, dest_dir);
+
+ options[i++] = 'x';
+ options[i++] = 'f'; /* Always overwrite.
+ * The overwrite option is handled in
+ * fr_archive_extract,
+ * this is because lha asks the user whether he
+ * wants to overwrite a file. */
+
+ if (junk_paths)
+ options[i++] = 'i';
+
+ options[i++] = 0;
+ fr_process_add_arg (comm->process, options);
+ fr_process_add_arg (comm->process, comm->filename);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+const char *lha_mime_type[] = { "application/x-lha", NULL };
+
+
+const char **
+fr_command_lha_get_mime_types (FrCommand *comm)
+{
+ return lha_mime_type;
+}
+
+
+FrCommandCap
+fr_command_lha_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("lha", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_lha_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("lha");
+}
+
+
+static void
+fr_command_lha_class_init (FrCommandLhaClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_lha_finalize;
+
+ afc->list = fr_command_lha_list;
+ afc->add = fr_command_lha_add;
+ afc->delete = fr_command_lha_delete;
+ afc->extract = fr_command_lha_extract;
+ afc->get_mime_types = fr_command_lha_get_mime_types;
+ afc->get_capabilities = fr_command_lha_get_capabilities;
+ afc->get_packages = fr_command_lha_get_packages;
+}
+
+
+static void
+fr_command_lha_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propAddCanStoreFolders = TRUE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = TRUE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+}
+
+
+static void
+fr_command_lha_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_LHA (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_lha_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandLhaClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_lha_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandLha),
+ 0,
+ (GInstanceInitFunc) fr_command_lha_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandLha",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-lha.h b/src/fr-command-lha.h
new file mode 100644
index 0000000..507f189
--- /dev/null
+++ b/src/fr-command-lha.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_LHA_H
+#define FR_COMMAND_LHA_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_LHA (fr_command_lha_get_type ())
+#define FR_COMMAND_LHA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_LHA, FrCommandLha))
+#define FR_COMMAND_LHA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_LHA, FrCommandLhaClass))
+#define FR_IS_COMMAND_LHA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_LHA))
+#define FR_IS_COMMAND_LHA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_LHA))
+#define FR_COMMAND_LHA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_LHA, FrCommandLhaClass))
+
+typedef struct _FrCommandLha FrCommandLha;
+typedef struct _FrCommandLhaClass FrCommandLhaClass;
+
+struct _FrCommandLha
+{
+ FrCommand __parent;
+};
+
+struct _FrCommandLhaClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_lha_get_type (void);
+
+#endif /* FR_COMMAND_LHA_H */
diff --git a/src/fr-command-lrzip.c b/src/fr-command-lrzip.c
new file mode 100644
index 0000000..6a2833c
--- /dev/null
+++ b/src/fr-command-lrzip.c
@@ -0,0 +1,271 @@
+/*
+ * fr-command-lrzip.c
+ *
+ * Created on: 10.04.2010
+ * Author: Alexander Saprykin
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-lrzip.h"
+
+static void fr_command_lrzip_class_init (FrCommandLrzipClass *class);
+static void fr_command_lrzip_init (FrCommand *afile);
+static void fr_command_lrzip_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+
+static void
+list__process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+
+ g_return_if_fail (line != NULL);
+
+ if (strlen (line) == 0)
+ return;
+
+ if (! g_str_has_prefix (line, "Decompressed file size:"))
+ return;
+
+ fdata = file_data_new ();
+ fdata->size = g_ascii_strtoull (get_last_field (line, 4), NULL, 10);
+
+ struct stat st;
+ time_t tt;
+ if (stat (comm->filename, &st) == 0)
+ fdata->modified = st.st_mtim.tv_sec;
+ else
+ time(&(fdata->modified));
+ fdata->modified;
+ fdata->encrypted = FALSE;
+
+ char *new_fname = g_strdup (file_name_from_path (comm->filename));
+ if (g_str_has_suffix (new_fname, ".lrz"))
+ new_fname[strlen (new_fname) - 4] = '\0';
+
+ if (*new_fname == '/') {
+ fdata->full_path = g_strdup (new_fname);
+ fdata->original_path = fdata->full_path;
+ }
+ else {
+ fdata->full_path = g_strconcat ("/", new_fname, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+ fdata->path = remove_level_from_path (fdata->full_path);
+ fdata->name = new_fname;
+ fdata->dir = FALSE;
+ fdata->link = NULL;
+
+ if (fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+fr_command_lrzip_list (FrCommand *comm)
+{
+ fr_process_set_err_line_func (comm->process, list__process_line, comm);
+
+ fr_process_begin_command (comm->process, "lrzip");
+ fr_process_add_arg (comm->process, "-i");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_lrzip_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ fr_process_begin_command (comm->process, "lrzip");
+
+ if (base_dir != NULL)
+ fr_process_set_working_dir (comm->process, base_dir);
+
+ /* preserve links. */
+
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-l"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-g"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-b"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-z"); break;
+ }
+
+ fr_process_add_arg (comm->process, "-o");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_add_arg (comm->process, (char *) file_list->data);
+
+ fr_process_end_command (comm->process);
+}
+
+static void
+fr_command_lrzip_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ fr_process_begin_command (comm->process, "lrzip");
+ fr_process_add_arg (comm->process, "-d");
+
+ if (dest_dir != NULL) {
+ fr_process_add_arg (comm->process, "-O");
+ fr_process_add_arg (comm->process, dest_dir);
+ }
+ if (overwrite)
+ fr_process_add_arg (comm->process, "-f");
+
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+}
+
+
+/*
+static void
+fr_command_lrzip_test (FrCommand *comm)
+{
+ fr_process_begin_command (comm->process, "lrzip");
+ fr_process_add_arg (comm->process, "-t");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+}
+*/
+
+
+const char *lrzip_mime_type[] = { "application/x-lrzip", NULL };
+
+
+const char **
+fr_command_lrzip_get_mime_types (FrCommand *comm)
+{
+ return lrzip_mime_type;
+}
+
+
+FrCommandCap
+fr_command_lrzip_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities = FR_COMMAND_CAN_DO_NOTHING;
+
+ if (is_program_available ("lrzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_lrzip_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("lrzip");
+}
+
+
+static void
+fr_command_lrzip_class_init (FrCommandLrzipClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_lrzip_finalize;
+
+ afc->list = fr_command_lrzip_list;
+ afc->add = fr_command_lrzip_add;
+ afc->extract = fr_command_lrzip_extract;
+ afc->get_mime_types = fr_command_lrzip_get_mime_types;
+ afc->get_capabilities = fr_command_lrzip_get_capabilities;
+ afc->get_packages = fr_command_lrzip_get_packages;
+}
+
+
+static void
+fr_command_lrzip_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = FALSE;
+ comm->propAddCanReplace = FALSE;
+ comm->propAddCanStoreFolders = FALSE;
+ comm->propExtractCanAvoidOverwrite = TRUE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+}
+
+
+static void
+fr_command_lrzip_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_LRZIP (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_lrzip_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandLrzipClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_lrzip_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandLrzip),
+ 0,
+ (GInstanceInitFunc) fr_command_lrzip_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandLrzip",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-lrzip.h b/src/fr-command-lrzip.h
new file mode 100644
index 0000000..910a696
--- /dev/null
+++ b/src/fr-command-lrzip.h
@@ -0,0 +1,37 @@
+/*
+ * fr-command-lrzip.h
+ *
+ * Created on: 10.04.2010
+ * Author: Alexander Saprykin
+ */
+
+#ifndef FRCOMMANDLRZIP_H_
+#define FRCOMMANDLRZIP_H_
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_LRZIP (fr_command_lrzip_get_type ())
+#define FR_COMMAND_LRZIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_LRZIP, FrCommandLrzip))
+#define FR_COMMAND_LRZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_LRZIP, FrCommandLrzipClass))
+#define FR_IS_COMMAND_LRZIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_LRZIP))
+#define FR_IS_COMMAND_LRZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_LRZIP))
+#define FR_COMMAND_LRZIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_LRZIP, FrCommandLrzipClass))
+
+typedef struct _FrCommandLrzip FrCommandLrzip;
+typedef struct _FrCommandLrzipClass FrCommandLrzipClass;
+
+struct _FrCommandLrzip
+{
+ FrCommand __parent;
+};
+
+struct _FrCommandLrzipClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_lrzip_get_type (void);
+
+#endif /* FRCOMMANDLRZIP_H_ */
diff --git a/src/fr-command-rar.c b/src/fr-command-rar.c
new file mode 100644
index 0000000..2c42ba4
--- /dev/null
+++ b/src/fr-command-rar.c
@@ -0,0 +1,813 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "gio-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-rar.h"
+#include "fr-error.h"
+
+static void fr_command_rar_class_init (FrCommandRarClass *class);
+static void fr_command_rar_init (FrCommand *afile);
+static void fr_command_rar_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+static gboolean
+have_rar (void)
+{
+ return is_program_in_path ("rar");
+}
+
+
+/* -- list -- */
+
+
+static time_t
+mktime_from_string (char *date_s,
+ char *time_s)
+{
+ struct tm tm = {0, };
+ char **fields;
+
+ tm.tm_isdst = -1;
+
+ /* date */
+
+ fields = g_strsplit (date_s, "-", 3);
+ if (fields[0] != NULL) {
+ tm.tm_mday = atoi (fields[0]);
+ if (fields[1] != NULL) {
+ tm.tm_mon = atoi (fields[1]) - 1;
+ if (fields[2] != NULL)
+ tm.tm_year = 100 + atoi (fields[2]);
+ }
+ }
+ g_strfreev (fields);
+
+ /* time */
+
+ fields = g_strsplit (time_s, ":", 2);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL)
+ tm.tm_min = atoi (fields[1]);
+ }
+ g_strfreev (fields);
+
+ return mktime (&tm);
+}
+
+
+static void
+process_line (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+ FrCommandRar *rar_comm = FR_COMMAND_RAR (comm);
+ char **fields;
+ const char *name_field;
+
+ g_return_if_fail (line != NULL);
+
+ if (! rar_comm->list_started) {
+ if (strncmp (line, "--------", 8) == 0) {
+ rar_comm->list_started = TRUE;
+ rar_comm->odd_line = TRUE;
+ }
+ else if (strncmp (line, "Volume ", 7) == 0)
+ comm->multi_volume = TRUE;
+ return;
+ }
+
+ if (strncmp (line, "--------", 8) == 0) {
+ rar_comm->list_started = FALSE;
+ return;
+ }
+
+ if (! rar_comm->odd_line) {
+ FileData *fdata;
+
+ fdata = rar_comm->fdata;
+
+ /* read file info. */
+
+ fields = split_line (line, 6);
+ if (g_strv_length (fields) < 6) {
+ /* wrong line format, treat this line as a filename line */
+ g_strfreev (fields);
+ file_data_free (rar_comm->fdata);
+ rar_comm->fdata = NULL;
+ rar_comm->odd_line = TRUE;
+ }
+ else {
+ if ((strcmp (fields[2], "<->") == 0)
+ || (strcmp (fields[2], "<--") == 0))
+ {
+ /* ignore files that span more volumes */
+
+ file_data_free (rar_comm->fdata);
+ rar_comm->fdata = NULL;
+ }
+ else {
+ fdata->size = g_ascii_strtoull (fields[0], NULL, 10);
+ fdata->modified = mktime_from_string (fields[3], fields[4]);
+
+ if ((fields[5][1] == 'D') || (fields[5][0] == 'd')) {
+ char *tmp;
+
+ tmp = fdata->full_path;
+ fdata->full_path = g_strconcat (fdata->full_path, "/", NULL);
+
+ fdata->original_path = g_strdup (fdata->original_path);
+ fdata->free_original_path = TRUE;
+
+ g_free (tmp);
+
+ fdata->name = dir_name_from_path (fdata->full_path);
+ fdata->dir = TRUE;
+ }
+ else
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+
+ fr_command_add_file (comm, fdata);
+ rar_comm->fdata = NULL;
+ }
+
+ g_strfreev (fields);
+ }
+ }
+
+ if (rar_comm->odd_line) {
+ FileData *fdata;
+
+ rar_comm->fdata = fdata = file_data_new ();
+
+ /* read file name. */
+
+ fdata->encrypted = (line[0] == '*') ? TRUE : FALSE;
+
+ name_field = line + 1;
+
+ if (*name_field == '/') {
+ fdata->full_path = g_strdup (name_field);
+ fdata->original_path = fdata->full_path;
+ }
+ else {
+ fdata->full_path = g_strconcat ("/", name_field, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+
+ fdata->link = NULL;
+ fdata->path = remove_level_from_path (fdata->full_path);
+ }
+ else {
+
+ }
+
+ rar_comm->odd_line = ! rar_comm->odd_line;
+}
+
+
+static void
+add_password_arg (FrCommand *comm,
+ const char *password,
+ gboolean disable_query)
+{
+ if ((password != NULL) && (password[0] != '\0')) {
+ if (comm->encrypt_header)
+ fr_process_add_arg_concat (comm->process, "-hp", password, NULL);
+ else
+ fr_process_add_arg_concat (comm->process, "-p", password, NULL);
+ }
+ else if (disable_query)
+ fr_process_add_arg (comm->process, "-p-");
+}
+
+
+typedef enum {
+ FIRST_VOLUME_IS_000,
+ FIRST_VOLUME_IS_001,
+ FIRST_VOLUME_IS_RAR
+} FirstVolumeExtension;
+
+
+static char *
+get_first_volume_name (const char *name,
+ const char *pattern,
+ FirstVolumeExtension extension_type)
+{
+ char *volume_name = NULL;
+ GRegex *re;
+
+ re = g_regex_new (pattern, G_REGEX_CASELESS, 0, NULL);
+ if (g_regex_match (re, name, 0, NULL)) {
+ char **parts;
+ int l, i;
+
+ parts = g_regex_split (re, name, 0);
+ l = strlen (parts[2]);
+ switch (extension_type) {
+ case FIRST_VOLUME_IS_000:
+ for (i = 0; i < l; i++)
+ parts[2][i] = '0';
+ break;
+
+ case FIRST_VOLUME_IS_001:
+ for (i = 0; i < l; i++)
+ parts[2][i] = (i < l - 1) ? '0' : '1';
+ break;
+
+ case FIRST_VOLUME_IS_RAR:
+ if (g_str_has_suffix (parts[1], "r")) {
+ parts[2][0] = 'a';
+ parts[2][1] = 'r';
+ }
+ else {
+ parts[2][0] = 'A';
+ parts[2][1] = 'R';
+ }
+ break;
+ }
+
+ volume_name = g_strjoinv ("", parts);
+
+ g_strfreev (parts);
+ }
+ g_regex_unref (re);
+
+ if (volume_name != NULL) {
+ char *tmp;
+
+ tmp = volume_name;
+ volume_name = g_filename_from_utf8 (tmp, -1, NULL, NULL, NULL);
+ g_free (tmp);
+ }
+
+ return volume_name;
+}
+
+
+static void
+check_multi_vomule (FrCommand *comm)
+{
+ GFile *file;
+ char buffer[11];
+
+ file = g_file_new_for_path (comm->filename);
+ if (! g_load_file_in_buffer (file, buffer, 11, NULL)) {
+ g_object_unref (file);
+ return;
+ }
+
+ if ((buffer[10] & 0x01) == 0x01) {
+ char *volume_name = NULL;
+ char *name;
+
+ name = g_filename_to_utf8 (file_name_from_path (comm->filename), -1, NULL, NULL, NULL);
+
+ volume_name = get_first_volume_name (name, "^(.*\\.part)([0-9]+)(\\.rar)$", FIRST_VOLUME_IS_001);
+ if (volume_name == NULL)
+ volume_name = get_first_volume_name (name, "^(.*\\.r)([0-9]+)$", FIRST_VOLUME_IS_RAR);
+ if (volume_name == NULL)
+ volume_name = get_first_volume_name (name, "^(.*\\.)([0-9]+)$", FIRST_VOLUME_IS_001);
+
+ if (volume_name != NULL) {
+ GFile *parent;
+ GFile *child;
+ char *volume_filename;
+
+ parent = g_file_get_parent (file);
+ child = g_file_get_child (parent, volume_name);
+ volume_filename = g_file_get_path (child);
+ fr_command_set_multi_volume (comm, volume_filename);
+
+ g_free (volume_filename);
+ g_object_unref (child);
+ g_object_unref (parent);
+ }
+
+ g_free (name);
+ }
+
+ g_object_unref (file);
+}
+
+
+static void
+list__begin (gpointer data)
+{
+ FrCommandRar *comm = data;
+
+ comm->list_started = FALSE;
+}
+
+
+static void
+fr_command_rar_list (FrCommand *comm)
+{
+ check_multi_vomule (comm);
+
+ fr_process_set_out_line_func (comm->process, process_line, comm);
+
+ if (have_rar ())
+ fr_process_begin_command (comm->process, "rar");
+ else
+ fr_process_begin_command (comm->process, "unrar");
+ fr_process_set_begin_func (comm->process, list__begin, comm);
+ fr_process_add_arg (comm->process, "v");
+ fr_process_add_arg (comm->process, "-c-");
+ fr_process_add_arg (comm->process, "-v");
+
+ add_password_arg (comm, comm->password, TRUE);
+
+ /* stop switches scanning */
+ fr_process_add_arg (comm->process, "--");
+
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+
+ fr_process_start (comm->process);
+}
+
+
+static char Progress_Message[4196];
+static char Progress_Filename[4096];
+
+
+static void
+parse_progress_line (FrCommand *comm,
+ const char *prefix,
+ const char *message_prefix,
+ const char *line)
+{
+ int prefix_len;
+
+ prefix_len = strlen (prefix);
+ if (strncmp (line, prefix, prefix_len) == 0) {
+ double fraction;
+ int len;
+ char *b_idx;
+
+ strcpy (Progress_Filename, line + prefix_len);
+
+ /* when a new volume is created a sequence of backspaces is
+ * issued, remove the backspaces from the filename */
+ b_idx = strchr (Progress_Filename, '\x08');
+ if (b_idx != NULL)
+ *b_idx = 0;
+
+ /* remove the OK at the end of the filename */
+ len = strlen (Progress_Filename);
+ if ((len > 5) && (strncmp (Progress_Filename + len - 5, " OK ", 5) == 0))
+ Progress_Filename[len - 5] = 0;
+
+ sprintf (Progress_Message, "%s%s", message_prefix, file_name_from_path (Progress_Filename));
+ fr_command_message (comm, Progress_Message);
+
+ fraction = (double) ++comm->n_file / (comm->n_files + 1);
+ fr_command_progress (comm, fraction);
+ }
+}
+
+
+static void
+process_line__add (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+
+ if (strncmp (line, "Creating archive ", 17) == 0) {
+ const char *archive_filename = line + 17;
+ char *uri;
+
+ uri = g_filename_to_uri (archive_filename, NULL, NULL);
+ if ((comm->volume_size > 0)
+ && g_regex_match_simple ("^.*\\.part(0)*2\\.rar$", uri, G_REGEX_CASELESS, 0))
+ {
+ char *volume_filename;
+
+ volume_filename = g_strdup (archive_filename);
+ volume_filename[strlen (volume_filename) - 5] = '1';
+ fr_command_set_multi_volume (comm, volume_filename);
+ g_free (volume_filename);
+ }
+ fr_command_working_archive (comm, uri);
+
+ g_free (uri);
+ return;
+ }
+
+ if (comm->n_files != 0)
+ parse_progress_line (comm, "Adding ", _("Adding file: "), line);
+}
+
+
+static void
+fr_command_rar_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ GList *scan;
+
+ fr_process_use_standard_locale (comm->process, TRUE);
+ fr_process_set_out_line_func (comm->process,
+ process_line__add,
+ comm);
+
+ fr_process_begin_command (comm->process, "rar");
+
+ if (base_dir != NULL)
+ fr_process_set_working_dir (comm->process, base_dir);
+
+ if (update)
+ fr_process_add_arg (comm->process, "u");
+ else
+ fr_process_add_arg (comm->process, "a");
+
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-m1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-m2"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-m3"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-m5"); break;
+ }
+
+ add_password_arg (comm, comm->password, FALSE);
+
+ if (comm->volume_size > 0)
+ fr_process_add_arg_printf (comm->process, "-v%ub", comm->volume_size);
+
+ /* disable percentage indicator */
+ fr_process_add_arg (comm->process, "-Idp");
+
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, comm->filename);
+
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ else
+ fr_process_add_arg_concat (comm->process, "@", from_file, NULL);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+process_line__delete (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+
+ if (strncmp (line, "Deleting from ", 14) == 0) {
+ char *uri;
+
+ uri = g_filename_to_uri (line + 14, NULL, NULL);
+ fr_command_working_archive (comm, uri);
+ g_free (uri);
+
+ return;
+ }
+
+ if (comm->n_files != 0)
+ parse_progress_line (comm, "Deleting ", _("Removing file: "), line);
+}
+
+
+static void
+fr_command_rar_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ GList *scan;
+
+ fr_process_use_standard_locale (comm->process, TRUE);
+ fr_process_set_out_line_func (comm->process,
+ process_line__delete,
+ comm);
+
+ fr_process_begin_command (comm->process, "rar");
+ fr_process_add_arg (comm->process, "d");
+
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, comm->filename);
+
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ else
+ fr_process_add_arg_concat (comm->process, "@", from_file, NULL);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+process_line__extract (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+
+ if (strncmp (line, "Extracting from ", 16) == 0) {
+ char *uri;
+
+ uri = g_filename_to_uri (line + 16, NULL, NULL);
+ fr_command_working_archive (comm, uri);
+ g_free (uri);
+
+ return;
+ }
+
+ if (comm->n_files != 0)
+ parse_progress_line (comm, "Extracting ", _("Extracting file: "), line);
+}
+
+
+static void
+fr_command_rar_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ fr_process_use_standard_locale (comm->process, TRUE);
+ fr_process_set_out_line_func (comm->process,
+ process_line__extract,
+ comm);
+
+ if (have_rar ())
+ fr_process_begin_command (comm->process, "rar");
+ else
+ fr_process_begin_command (comm->process, "unrar");
+
+ fr_process_add_arg (comm->process, "x");
+
+ /* keep broken extracted files */
+ fr_process_add_arg (comm->process, "-kb");
+
+ if (overwrite)
+ fr_process_add_arg (comm->process, "-o+");
+ else
+ fr_process_add_arg (comm->process, "-o-");
+
+ if (skip_older)
+ fr_process_add_arg (comm->process, "-u");
+
+ if (junk_paths)
+ fr_process_add_arg (comm->process, "-ep");
+
+ add_password_arg (comm, comm->password, TRUE);
+
+ /* disable percentage indicator */
+ fr_process_add_arg (comm->process, "-Idp");
+
+ fr_process_add_arg (comm->process, "--");
+ fr_process_add_arg (comm->process, comm->filename);
+
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ else
+ fr_process_add_arg_concat (comm->process, "@", from_file, NULL);
+
+ if (dest_dir != NULL)
+ fr_process_add_arg (comm->process, dest_dir);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_rar_test (FrCommand *comm)
+{
+ if (have_rar ())
+ fr_process_begin_command (comm->process, "rar");
+ else
+ fr_process_begin_command (comm->process, "unrar");
+
+ fr_process_add_arg (comm->process, "t");
+
+ add_password_arg (comm, comm->password, TRUE);
+
+ /* disable percentage indicator */
+ fr_process_add_arg (comm->process, "-Idp");
+
+ /* stop switches scanning */
+ fr_process_add_arg (comm->process, "--");
+
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_rar_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ GList *scan;
+
+#if 0
+ {
+ GList *scan;
+
+ for (scan = g_list_last (comm->process->err.raw); scan; scan = scan->prev)
+ g_print ("%s\n", (char*)scan->data);
+ }
+#endif
+
+ if (error->type == FR_PROC_ERROR_NONE)
+ return;
+
+ /*if (error->status == 3)
+ error->type = FR_PROC_ERROR_ASK_PASSWORD;
+ else */
+ if (error->status <= 1)
+ error->type = FR_PROC_ERROR_NONE;
+
+ for (scan = g_list_last (comm->process->err.raw); scan; scan = scan->prev) {
+ char *line = scan->data;
+
+ if (strstr (line, "password incorrect") != NULL) {
+ error->type = FR_PROC_ERROR_ASK_PASSWORD;
+ break;
+ }
+
+ if (strncmp (line, "Unexpected end of archive", 25) == 0) {
+ /* FIXME: handle this type of errors at a higher level when the freeze is over. */
+ }
+
+ if (strncmp (line, "Cannot find volume", 18) == 0) {
+ char *volume_filename;
+
+ g_clear_error (&error->gerror);
+
+ error->type = FR_PROC_ERROR_MISSING_VOLUME;
+ volume_filename = g_path_get_basename (line + strlen ("Cannot find volume "));
+ error->gerror = g_error_new (FR_ERROR, error->status, _("Could not find the volume: %s"), volume_filename);
+ g_free (volume_filename);
+ break;
+ }
+ }
+}
+
+
+const char *rar_mime_type[] = { "application/x-cbr",
+ "application/x-rar",
+ NULL };
+
+
+const char **
+fr_command_rar_get_mime_types (FrCommand *comm)
+{
+ return rar_mime_type;
+}
+
+
+FrCommandCap
+fr_command_rar_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES | FR_COMMAND_CAN_ENCRYPT | FR_COMMAND_CAN_ENCRYPT_HEADER;
+ if (is_program_available ("rar", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE | FR_COMMAND_CAN_CREATE_VOLUMES;
+ else if (is_program_available ("unrar", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ /* multi-volumes are read-only */
+ if ((comm->files->len > 0) && comm->multi_volume && (capabilities & FR_COMMAND_CAN_WRITE))
+ capabilities ^= FR_COMMAND_CAN_WRITE;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_rar_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("rar,unrar");
+}
+
+
+static void
+fr_command_rar_class_init (FrCommandRarClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_rar_finalize;
+
+ afc->list = fr_command_rar_list;
+ afc->add = fr_command_rar_add;
+ afc->delete = fr_command_rar_delete;
+ afc->extract = fr_command_rar_extract;
+ afc->test = fr_command_rar_test;
+ afc->handle_error = fr_command_rar_handle_error;
+ afc->get_mime_types = fr_command_rar_get_mime_types;
+ afc->get_capabilities = fr_command_rar_get_capabilities;
+ afc->get_packages = fr_command_rar_get_packages;
+}
+
+
+static void
+fr_command_rar_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propAddCanStoreFolders = TRUE;
+ comm->propExtractCanAvoidOverwrite = TRUE;
+ comm->propExtractCanSkipOlder = TRUE;
+ comm->propExtractCanJunkPaths = TRUE;
+ comm->propPassword = TRUE;
+ comm->propTest = TRUE;
+ comm->propListFromFile = TRUE;
+}
+
+
+static void
+fr_command_rar_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_RAR (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_rar_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandRarClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_rar_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandRar),
+ 0,
+ (GInstanceInitFunc) fr_command_rar_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandRar",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-rar.h b/src/fr-command-rar.h
new file mode 100644
index 0000000..b9fffdc
--- /dev/null
+++ b/src/fr-command-rar.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_RAR_H
+#define FR_COMMAND_RAR_H
+
+#include <glib.h>
+#include "file-data.h"
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_RAR (fr_command_rar_get_type ())
+#define FR_COMMAND_RAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_RAR, FrCommandRar))
+#define FR_COMMAND_RAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_RAR, FrCommandRarClass))
+#define FR_IS_COMMAND_RAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_RAR))
+#define FR_IS_COMMAND_RAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_RAR))
+#define FR_COMMAND_RAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_RAR, FrCommandRarClass))
+
+typedef struct _FrCommandRar FrCommandRar;
+typedef struct _FrCommandRarClass FrCommandRarClass;
+
+struct _FrCommandRar
+{
+ FrCommand __parent;
+
+ gboolean list_started;
+ gboolean odd_line;
+ FileData *fdata;
+};
+
+struct _FrCommandRarClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_rar_get_type (void);
+
+#endif /* FR_COMMAND_RAR_H */
diff --git a/src/fr-command-rpm.c b/src/fr-command-rpm.c
new file mode 100644
index 0000000..73f5e39
--- /dev/null
+++ b/src/fr-command-rpm.c
@@ -0,0 +1,320 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-rpm.h"
+
+static void fr_command_rpm_class_init (FrCommandRpmClass *class);
+static void fr_command_rpm_init (FrCommand *afile);
+static void fr_command_rpm_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+static time_t
+mktime_from_string (char *month,
+ char *mday,
+ char *year)
+{
+ static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ struct tm tm = {0, };
+
+ tm.tm_isdst = -1;
+
+ if (month != NULL) {
+ int i;
+ for (i = 0; i < 12; i++)
+ if (strcmp (months[i], month) == 0) {
+ tm.tm_mon = i;
+ break;
+ }
+ }
+ tm.tm_mday = atoi (mday);
+ if (strchr (year, ':') != NULL) {
+ char **fields = g_strsplit (year, ":", 2);
+ if (n_fields (fields) == 2) {
+ time_t now;
+ struct tm *now_tm;
+
+ tm.tm_hour = atoi (fields[0]);
+ tm.tm_min = atoi (fields[1]);
+
+ now = time(NULL);
+ now_tm = localtime (&now);
+ tm.tm_year = now_tm->tm_year;
+ }
+ } else
+ tm.tm_year = atoi (year) - 1900;
+
+ return mktime (&tm);
+}
+
+
+static void
+list__process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+ char **fields;
+ const char *name_field;
+ char *name;
+ int ofs = 0;
+
+ g_return_if_fail (line != NULL);
+
+ fdata = file_data_new ();
+
+#ifdef __sun
+ fields = split_line (line, 9);
+ fdata->size = g_ascii_strtoull (fields[4], NULL, 10);
+ fdata->modified = mktime_from_string (fields[5], fields[6], fields[8]);
+ g_strfreev (fields);
+
+ name_field = get_last_field (line, 10);
+#else /* !__sun */
+ /* Handle char and block device files */
+ if ((line[0] == 'c') || (line[0] == 'b')) {
+ fields = split_line (line, 9);
+ ofs = 1;
+ fdata->size = 0;
+ /* TODO We should also specify the content type */
+ }
+ else {
+ fields = split_line (line, 8);
+ fdata->size = g_ascii_strtoull (fields[4], NULL, 10);
+ }
+ fdata->modified = mktime_from_string (fields[5+ofs], fields[6+ofs], fields[7+ofs]);
+ g_strfreev (fields);
+
+ name_field = get_last_field (line, 9+ofs);
+#endif /* !__sun */
+
+ fields = g_strsplit (name_field, " -> ", 2);
+
+ if (fields[1] == NULL) {
+ g_strfreev (fields);
+ fields = g_strsplit (name_field, " link to ", 2);
+ }
+
+ fdata->dir = line[0] == 'd';
+
+ name = g_strcompress (fields[0]);
+ if (*(fields[0]) == '/') {
+ fdata->full_path = g_strdup (name);
+ fdata->original_path = fdata->full_path;
+ }
+ else {
+ fdata->full_path = g_strconcat ("/", name, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+ if (fdata->dir && (name[strlen (name) - 1] != '/')) {
+ char *old_full_path = fdata->full_path;
+ fdata->full_path = g_strconcat (old_full_path, "/", NULL);
+ g_free (old_full_path);
+ fdata->original_path = g_strdup (name);
+ fdata->free_original_path = TRUE;
+ }
+ g_free (name);
+
+ if (fields[1] != NULL)
+ fdata->link = g_strcompress (fields[1]);
+ g_strfreev (fields);
+
+ if (fdata->dir)
+ fdata->name = dir_name_from_path (fdata->full_path);
+ else
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+fr_command_rpm_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, list__process_line, comm);
+
+ fr_process_begin_command (comm->process, "sh");
+ fr_process_add_arg (comm->process, "-c");
+ fr_process_add_arg_concat (comm->process, PRIVEXECDIR "rpm2cpio ", comm->e_filename, " -itv", NULL);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_rpm_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+ GString *cmd;
+
+ fr_process_begin_command (comm->process, "sh");
+ if (dest_dir != NULL)
+ fr_process_set_working_dir (comm->process, dest_dir);
+ fr_process_add_arg (comm->process, "-c");
+
+ cmd = g_string_new (PRIVEXECDIR "rpm2cpio ");
+ g_string_append (cmd, comm->e_filename);
+ g_string_append (cmd, " -idu ");
+ for (scan = file_list; scan; scan = scan->next) {
+ char *filename = g_shell_quote (scan->data);
+ g_string_append (cmd, filename);
+ g_free (filename);
+ g_string_append (cmd, " ");
+ }
+ fr_process_add_arg (comm->process, cmd->str);
+ g_string_free (cmd, TRUE);
+
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+const char *rpm_mime_type[] = { "application/x-rpm", NULL };
+
+
+const char **
+fr_command_rpm_get_mime_types (FrCommand *comm)
+{
+ return rpm_mime_type;
+}
+
+
+FrCommandCap
+fr_command_rpm_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("cpio", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_rpm_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("cpio,rpm");
+}
+
+
+static void
+fr_command_rpm_class_init (FrCommandRpmClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_rpm_finalize;
+
+ afc->list = fr_command_rpm_list;
+ afc->extract = fr_command_rpm_extract;
+ afc->get_mime_types = fr_command_rpm_get_mime_types;
+ afc->get_capabilities = fr_command_rpm_get_capabilities;
+ afc->get_packages = fr_command_rpm_get_packages;
+}
+
+
+static void
+fr_command_rpm_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = FALSE;
+ comm->propAddCanReplace = FALSE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+}
+
+
+static void
+fr_command_rpm_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_RPM (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_rpm_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandRpmClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_rpm_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandRpm),
+ 0,
+ (GInstanceInitFunc) fr_command_rpm_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandRpm",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-rpm.h b/src/fr-command-rpm.h
new file mode 100644
index 0000000..9b30ed3
--- /dev/null
+++ b/src/fr-command-rpm.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_RPM_H
+#define FR_COMMAND_RPM_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_RPM (fr_command_rpm_get_type ())
+#define FR_COMMAND_RPM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_RPM, FrCommandRpm))
+#define FR_COMMAND_RPM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_RPM, FrCommandRpmClass))
+#define FR_IS_COMMAND_RPM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_RPM))
+#define FR_IS_COMMAND_RPM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_RPM))
+#define FR_COMMAND_RPM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_RPM, FrCommandRpmClass))
+
+typedef struct _FrCommandRpm FrCommandRpm;
+typedef struct _FrCommandRpmClass FrCommandRpmClass;
+
+struct _FrCommandRpm
+{
+ FrCommand __parent;
+ gboolean is_empty;
+};
+
+struct _FrCommandRpmClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_rpm_get_type (void);
+
+#endif /* FR_COMMAND_RPM_H */
diff --git a/src/fr-command-tar.c b/src/fr-command-tar.c
new file mode 100644
index 0000000..4c214f4
--- /dev/null
+++ b/src/fr-command-tar.c
@@ -0,0 +1,1233 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-tar.h"
+
+#define ACTIVITY_DELAY 20
+
+static void fr_command_tar_class_init (FrCommandTarClass *class);
+static void fr_command_tar_init (FrCommand *afile);
+static void fr_command_tar_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+static time_t
+mktime_from_string (char *date_s,
+ char *time_s)
+{
+ struct tm tm = {0, };
+ char **fields;
+
+ tm.tm_isdst = -1;
+
+ /* date */
+
+ fields = g_strsplit (date_s, "-", 3);
+ if (fields[0] != NULL) {
+ tm.tm_year = atoi (fields[0]) - 1900;
+ if (fields[1] != NULL) {
+ tm.tm_mon = atoi (fields[1]) - 1;
+ if (fields[2] != NULL)
+ tm.tm_mday = atoi (fields[2]);
+ }
+ }
+ g_strfreev (fields);
+
+ /* time */
+
+ fields = g_strsplit (time_s, ":", 3);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL) {
+ tm.tm_min = atoi (fields[1]);
+ if (fields[2] != NULL)
+ tm.tm_sec = atoi (fields[2]);
+ }
+ }
+ g_strfreev (fields);
+
+ return mktime (&tm);
+}
+
+
+static char*
+tar_get_last_field (const char *line,
+ int start_from,
+ int field_n)
+{
+ const char *f_start, *f_end;
+
+ line = line + start_from;
+
+ f_start = line;
+ while ((*f_start == ' ') && (*f_start != *line))
+ f_start++;
+ f_end = f_start;
+
+ while ((field_n > 0) && (*f_end != 0)) {
+ if (*f_end == ' ') {
+ field_n--;
+ if (field_n != 0) {
+ while ((*f_end == ' ') && (*f_end != *line))
+ f_end++;
+ f_start = f_end;
+ }
+ } else
+ f_end++;
+ }
+
+ return g_strdup (f_start);
+}
+
+
+static void
+process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+ char **fields;
+ int date_idx;
+ char *field_date, *field_time, *field_size, *field_name;
+ char *name;
+
+ g_return_if_fail (line != NULL);
+
+ date_idx = file_list__get_index_from_pattern (line, "%n%n%n%n-%n%n-%n%n %n%n:%n%n");
+ if (date_idx < 0)
+ return;
+
+ fdata = file_data_new ();
+
+ field_size = file_list__get_prev_field (line, date_idx, 1);
+ fdata->size = g_ascii_strtoull (field_size, NULL, 10);
+ g_free (field_size);
+
+ field_date = file_list__get_next_field (line, date_idx, 1);
+ field_time = file_list__get_next_field (line, date_idx, 2);
+ fdata->modified = mktime_from_string (field_date, field_time);
+ g_free (field_date);
+ g_free (field_time);
+
+ /* Full path */
+
+ field_name = tar_get_last_field (line, date_idx, 3);
+ fields = g_strsplit (field_name, " -> ", 2);
+
+ if (fields[1] == NULL) {
+ g_strfreev (fields);
+ fields = g_strsplit (field_name, " link to ", 2);
+ }
+
+ name = g_strcompress (fields[0]);
+ if (*name == '/') {
+ fdata->full_path = g_strdup (name);
+ fdata->original_path = fdata->full_path;
+ } else {
+ fdata->full_path = g_strconcat ("/", name, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+ g_free (name);
+ name = g_filename_from_utf8 (fdata->original_path, -1, NULL, NULL, NULL);
+ if (name)
+ fdata->original_path = name;
+
+ if (fields[1] != NULL)
+ fdata->link = g_strdup (fields[1]);
+ g_strfreev (fields);
+ g_free (field_name);
+
+ fdata->dir = line[0] == 'd';
+ if (fdata->dir)
+ fdata->name = dir_name_from_path (fdata->full_path);
+ else
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+add_compress_arg (FrCommand *comm)
+{
+ if (is_mime_type (comm->mime_type, "application/x-compressed-tar"))
+ fr_process_add_arg (comm->process, "-z");
+
+ else if (is_mime_type (comm->mime_type, "application/x-bzip-compressed-tar"))
+ fr_process_add_arg (comm->process, "--use-compress-program=bzip2");
+
+ else if (is_mime_type (comm->mime_type, "application/x-tarz")) {
+ if (is_program_in_path ("gzip"))
+ fr_process_add_arg (comm->process, "-z");
+ else
+ fr_process_add_arg (comm->process, "-Z");
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lrzip-compressed-tar"))
+ fr_process_add_arg (comm->process, "--use-compress-program=lrzip");
+
+ else if (is_mime_type (comm->mime_type, "application/x-lzip-compressed-tar"))
+ fr_process_add_arg (comm->process, "--use-compress-program=lzip");
+
+ else if (is_mime_type (comm->mime_type, "application/x-lzma-compressed-tar"))
+ fr_process_add_arg (comm->process, "--use-compress-program=lzma");
+
+ else if (is_mime_type (comm->mime_type, "application/x-xz-compressed-tar"))
+ fr_process_add_arg (comm->process, "--use-compress-program=xz");
+
+ else if (is_mime_type (comm->mime_type, "application/x-lzop-compressed-tar"))
+ fr_process_add_arg (comm->process, "--use-compress-program=lzop");
+
+ else if (is_mime_type (comm->mime_type, "application/x-7z-compressed-tar")) {
+ FrCommandTar *comm_tar = (FrCommandTar*) comm;
+ char *option;
+
+ option = g_strdup_printf ("--use-compress-program=%s", comm_tar->compress_command);
+ fr_process_add_arg (comm->process, option);
+ g_free (option);
+ }
+}
+
+
+static void
+begin_tar_command (FrCommand *comm)
+{
+ char *command = NULL;
+
+ /* In solaris gtar is present under /usr/sfw/bin */
+
+ command = g_find_program_in_path ("gtar");
+#if defined (__SVR4) && defined (__sun)
+ if (g_file_test ("/usr/sfw/bin/gtar", G_FILE_TEST_IS_EXECUTABLE))
+ command = g_strdup ("/usr/sfw/bin/gtar");
+#endif
+ if (command != NULL)
+ fr_process_begin_command (comm->process, command);
+ else
+ fr_process_begin_command (comm->process, "tar");
+ g_free (command);
+}
+
+
+static void
+fr_command_tar_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, process_line, comm);
+
+ begin_tar_command (comm);
+ fr_process_add_arg (comm->process, "--force-local");
+ fr_process_add_arg (comm->process, "--no-wildcards");
+ fr_process_add_arg (comm->process, "-tvf");
+ fr_process_add_arg (comm->process, comm->filename);
+ add_compress_arg (comm);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static gboolean
+can_create_a_compressed_archive (FrCommand *comm)
+{
+ return comm->creating_archive && ! is_mime_type (comm->mime_type, "application/x-7z-compressed-tar");
+}
+
+
+static void
+process_line__generic (char *line,
+ gpointer data,
+ char *action_msg)
+{
+ FrCommand *comm = FR_COMMAND (data);
+ char *msg;
+
+ if (line == NULL)
+ return;
+
+ if (line[strlen (line) - 1] == '/') /* ignore directories */
+ return;
+
+ msg = g_strconcat (action_msg, file_name_from_path (line), NULL);
+ fr_command_message (comm, msg);
+ g_free (msg);
+
+ if (comm->n_files != 0) {
+ double fraction = (double) ++comm->n_file / (comm->n_files + 1);
+ fr_command_progress (comm, fraction);
+ }
+}
+
+
+static void
+process_line__add (char *line,
+ gpointer data)
+{
+ /* Translators: after the colon there is a filename. */
+ process_line__generic (line, data, _("Adding file: "));
+}
+
+
+static void
+fr_command_tar_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ FrCommandTar *c_tar = FR_COMMAND_TAR (comm);
+ GList *scan;
+
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process,
+ process_line__add,
+ comm);
+
+ begin_tar_command (comm);
+ fr_process_add_arg (comm->process, "--force-local");
+ if (! recursive)
+ fr_process_add_arg (comm->process, "--no-recursion");
+ fr_process_add_arg (comm->process, "--no-wildcards");
+ fr_process_add_arg (comm->process, "-v");
+ fr_process_add_arg (comm->process, "-p");
+
+ if (base_dir != NULL) {
+ fr_process_add_arg (comm->process, "-C");
+ fr_process_add_arg (comm->process, base_dir);
+ }
+
+ if (can_create_a_compressed_archive (comm)) {
+ fr_process_add_arg (comm->process, "-cf");
+ fr_process_add_arg (comm->process, comm->filename);
+ add_compress_arg (comm);
+ }
+ else {
+ if (comm->creating_archive)
+ fr_process_add_arg (comm->process, "-cf");
+ else
+ fr_process_add_arg (comm->process, "-rf");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ }
+
+ if (from_file != NULL) {
+ fr_process_add_arg (comm->process, "-T");
+ fr_process_add_arg (comm->process, from_file);
+ }
+
+ fr_process_add_arg (comm->process, "--");
+
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+process_line__delete (char *line,
+ gpointer data)
+{
+ /* Translators: after the colon there is a filename. */
+ process_line__generic (line, data, _("Removing file: "));
+}
+
+
+static void
+begin_func__delete (gpointer data)
+{
+ FrCommand *comm = data;
+ fr_command_progress (comm, -1.0);
+ fr_command_message (comm, _("Deleting files from archive"));
+}
+
+
+static void
+fr_command_tar_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ FrCommandTar *c_tar = FR_COMMAND_TAR (comm);
+ GList *scan;
+
+ fr_process_set_out_line_func (comm->process,
+ process_line__delete,
+ comm);
+
+ begin_tar_command (comm);
+ fr_process_set_begin_func (comm->process, begin_func__delete, comm);
+ fr_process_add_arg (comm->process, "--force-local");
+ fr_process_add_arg (comm->process, "--no-wildcards");
+ fr_process_add_arg (comm->process, "-v");
+ fr_process_add_arg (comm->process, "--delete");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+
+ if (from_file != NULL) {
+ fr_process_add_arg (comm->process, "-T");
+ fr_process_add_arg (comm->process, from_file);
+ }
+
+ fr_process_add_arg (comm->process, "--");
+
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+process_line__extract (char *line,
+ gpointer data)
+{
+ /* Translators: after the colon there is a filename. */
+ process_line__generic (line, data, _("Extracting file: "));
+}
+
+
+static void
+fr_command_tar_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ fr_process_set_out_line_func (comm->process,
+ process_line__extract,
+ comm);
+
+ begin_tar_command (comm);
+ fr_process_add_arg (comm->process, "--force-local");
+ fr_process_add_arg (comm->process, "--no-wildcards");
+ fr_process_add_arg (comm->process, "-v");
+ fr_process_add_arg (comm->process, "-p");
+
+ if (! overwrite)
+ fr_process_add_arg (comm->process, "-k");
+ if (skip_older)
+ fr_process_add_arg (comm->process, "--keep-newer-files");
+
+ fr_process_add_arg (comm->process, "-xf");
+ fr_process_add_arg (comm->process, comm->filename);
+ add_compress_arg (comm);
+
+ if (dest_dir != NULL) {
+ fr_process_add_arg (comm->process, "-C");
+ fr_process_add_arg (comm->process, dest_dir);
+ }
+
+ if (from_file != NULL) {
+ fr_process_add_arg (comm->process, "-T");
+ fr_process_add_arg (comm->process, from_file);
+ }
+
+ fr_process_add_arg (comm->process, "--");
+
+ if (from_file == NULL)
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+begin_func__recompress (gpointer data)
+{
+ FrCommand *comm = data;
+ fr_command_progress (comm, -1.0);
+ fr_command_message (comm, _("Recompressing archive"));
+}
+
+
+static gboolean
+gzip_continue_func (gpointer user_data)
+{
+ FrCommand *comm = user_data;
+
+ /* ignore gzip warnings */
+
+ if (comm->process->error.status == 2) {
+ comm->process->error.type = FR_PROC_ERROR_NONE;
+ comm->process->error.status = 0;
+ g_clear_error (&comm->process->error.gerror);
+ }
+
+ return comm->process->error.status == 0;
+}
+
+
+static void
+fr_command_tar_recompress (FrCommand *comm)
+{
+ FrCommandTar *c_tar = FR_COMMAND_TAR (comm);
+ char *new_name = NULL;
+
+ if (can_create_a_compressed_archive (comm))
+ return;
+
+ if (is_mime_type (comm->mime_type, "application/x-compressed-tar")) {
+ fr_process_begin_command (comm->process, "gzip");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ fr_process_set_continue_func (comm->process, gzip_continue_func, comm);
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-3"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-6"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-9"); break;
+ }
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".gz", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-bzip-compressed-tar")) {
+ fr_process_begin_command (comm->process, "bzip2");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-3"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-6"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-9"); break;
+ }
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".bz2", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-tarz")) {
+ fr_process_begin_command (comm->process, "compress");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".Z", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lrzip-compressed-tar")) {
+ fr_process_begin_command (comm->process, "lrzip");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-l"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-g"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-b"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-z"); break;
+ }
+ fr_process_add_arg (comm->process, "-o");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".lrz", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzip-compressed-tar")) {
+ fr_process_begin_command (comm->process, "lzip");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-3"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-6"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-9"); break;
+ }
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".lz", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzma-compressed-tar")) {
+ fr_process_begin_command (comm->process, "lzma");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-3"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-6"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-9"); break;
+ }
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".lzma", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-xz-compressed-tar")) {
+ fr_process_begin_command (comm->process, "xz");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-3"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-6"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-9"); break;
+ }
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".xz", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzop-compressed-tar")) {
+ fr_process_begin_command (comm->process, "lzop");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-3"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-6"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-9"); break;
+ }
+ fr_process_add_arg (comm->process, "-fU");
+ fr_process_add_arg (comm->process, "--no-stdin");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".lzo", NULL);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-7z-compressed-tar")) {
+ FrCommandTar *comm_tar = (FrCommandTar*) comm;
+
+ fr_process_begin_command (comm->process, comm_tar->compress_command);
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_set_begin_func (comm->process, begin_func__recompress, comm);
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-mx=1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-mx=5"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-mx=5"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-mx=7"); break;
+ }
+ fr_process_add_arg (comm->process, "a");
+ fr_process_add_arg (comm->process, "-bd");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, "-l");
+
+ new_name = g_strconcat (c_tar->uncomp_filename, ".7z", NULL);
+ fr_process_add_arg_concat (comm->process, new_name, NULL);
+
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+
+ /* remove the uncompressed tar */
+
+ fr_process_begin_command (comm->process, "rm");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, c_tar->uncomp_filename);
+ fr_process_end_command (comm->process);
+ }
+
+ if (c_tar->name_modified) {
+ char *tmp_dir;
+
+ /* Restore original name. */
+
+ fr_process_begin_command (comm->process, "mv");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, new_name);
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+
+ tmp_dir = remove_level_from_path (new_name);
+
+ fr_process_begin_command (comm->process, "rm");
+ fr_process_set_sticky (comm->process, TRUE);
+ fr_process_add_arg (comm->process, "-fr");
+ fr_process_add_arg (comm->process, tmp_dir);
+ fr_process_end_command (comm->process);
+
+ g_free (tmp_dir);
+ }
+
+ g_free (new_name);
+ g_free (c_tar->uncomp_filename);
+ c_tar->uncomp_filename = NULL;
+}
+
+
+static void
+begin_func__uncompress (gpointer data)
+{
+ FrCommand *comm = data;
+ fr_command_progress (comm, -1.0);
+ fr_command_message (comm, _("Decompressing archive"));
+}
+
+
+static char *
+get_uncompressed_name (FrCommandTar *c_tar,
+ const char *e_filename)
+{
+ FrCommand *comm = FR_COMMAND (c_tar);
+ char *new_name = g_strdup (e_filename);
+ int l = strlen (new_name);
+
+ if (is_mime_type (comm->mime_type, "application/x-compressed-tar")) {
+ /* X.tgz --> X.tar
+ * X.tar.gz --> X.tar */
+ if (file_extension_is (e_filename, ".tgz")) {
+ new_name[l - 2] = 'a';
+ new_name[l - 1] = 'r';
+ }
+ else if (file_extension_is (e_filename, ".tar.gz"))
+ new_name[l - 3] = 0;
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-bzip-compressed-tar")) {
+ /* X.tbz2 --> X.tar
+ * X.tar.bz2 --> X.tar */
+ if (file_extension_is (e_filename, ".tbz2")) {
+ new_name[l - 3] = 'a';
+ new_name[l - 2] = 'r';
+ new_name[l - 1] = 0;
+ }
+ else if (file_extension_is (e_filename, ".tar.bz2"))
+ new_name[l - 4] = 0;
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-tarz")) {
+ /* X.taz --> X.tar
+ * X.tar.Z --> X.tar */
+ if (file_extension_is (e_filename, ".taz"))
+ new_name[l - 1] = 'r';
+ else if (file_extension_is (e_filename, ".tar.Z"))
+ new_name[l - 2] = 0;
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lrzip-compressed-tar")) {
+ /* X.tlrz --> X.tar
+ * X.tar.lrz --> X.tar */
+ if (file_extension_is (e_filename, ".tlrz")) {
+ new_name[l - 3] = 'a';
+ new_name[l - 2] = 'r';
+ new_name[l - 1] = 0;
+ }
+ else if (file_extension_is (e_filename, ".tar.lrz"))
+ new_name[l - 4] = 0;
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzip-compressed-tar")) {
+ /* X.tlz --> X.tar
+ * X.tar.lz --> X.tar */
+ if (file_extension_is (e_filename, ".tlz")) {
+ new_name[l - 2] = 'a';
+ new_name[l - 1] = 'r';
+ }
+ else if (file_extension_is (e_filename, ".tar.lz"))
+ new_name[l - 3] = 0;
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzma-compressed-tar")) {
+ /* X.tar.lzma --> X.tar
+ * (There doesn't seem to be a shorthand suffix) */
+ if (file_extension_is (e_filename, ".tar.lzma"))
+ new_name[l - 5] = 0;
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-xz-compressed-tar")) {
+ /* X.tar.xz --> X.tar
+ * (There doesn't seem to be a shorthand suffix) */
+ if (file_extension_is (e_filename, ".tar.xz"))
+ new_name[l - 3] = 0;
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzop-compressed-tar")) {
+ /* X.tzo --> X.tar
+ * X.tar.lzo --> X.tar */
+ if (file_extension_is (e_filename, ".tzo")) {
+ new_name[l - 2] = 'a';
+ new_name[l - 1] = 'r';
+ }
+ else if (file_extension_is (e_filename, ".tar.lzo"))
+ new_name[l - 4] = 0;
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-7z-compressed-tar")) {
+ /* X.tar.7z --> X.tar */
+ if (file_extension_is (e_filename, ".tar.7z"))
+ new_name[l - 3] = 0;
+ }
+
+ return new_name;
+}
+
+
+#define MAX_TRIES 50
+
+
+static char *
+get_temp_name (FrCommandTar *c_tar,
+ const char *filepath)
+{
+ char *dirname = remove_level_from_path (filepath);
+ char *template;
+ char *result = NULL;
+ char *temp_name = NULL;
+
+ template = g_strconcat (dirname, "/.fr-XXXXXX", NULL);
+ result = mkdtemp (template);
+ temp_name = g_build_filename (result, file_name_from_path (filepath), NULL);
+ g_free (template);
+
+ return temp_name;
+}
+
+
+static void
+fr_command_tar_uncompress (FrCommand *comm)
+{
+ FrCommandTar *c_tar = FR_COMMAND_TAR (comm);
+ char *tmp_name;
+ gboolean archive_exists;
+
+ if (can_create_a_compressed_archive (comm))
+ return;
+
+ if (c_tar->uncomp_filename != NULL) {
+ g_free (c_tar->uncomp_filename);
+ c_tar->uncomp_filename = NULL;
+ }
+
+ {
+ char *uri = g_filename_to_uri (comm->filename, NULL, NULL);
+ archive_exists = uri_exists (uri);
+ g_free (uri);
+ }
+
+ c_tar->name_modified = ! is_mime_type (comm->mime_type, "application/x-tar");
+ if (c_tar->name_modified) {
+ tmp_name = get_temp_name (c_tar, comm->filename);
+ if (archive_exists) {
+ fr_process_begin_command (comm->process, "mv");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ }
+ else
+ tmp_name = g_strdup (comm->filename);
+
+ if (archive_exists) {
+ if (is_mime_type (comm->mime_type, "application/x-compressed-tar")) {
+ fr_process_begin_command (comm->process, "gzip");
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_set_continue_func (comm->process, gzip_continue_func, comm);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-bzip-compressed-tar")) {
+ fr_process_begin_command (comm->process, "bzip2");
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-tarz")) {
+ if (is_program_in_path ("gzip")) {
+ fr_process_begin_command (comm->process, "gzip");
+ fr_process_set_continue_func (comm->process, gzip_continue_func, comm);
+ }
+ else
+ fr_process_begin_command (comm->process, "uncompress");
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lrzip-compressed-tar")) {
+ fr_process_begin_command (comm->process, "lrzip");
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzip-compressed-tar")) {
+ fr_process_begin_command (comm->process, "lzip");
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzma-compressed-tar")) {
+ fr_process_begin_command (comm->process, "lzma");
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-xz-compressed-tar")) {
+ fr_process_begin_command (comm->process, "xz");
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-lzop-compressed-tar")) {
+ fr_process_begin_command (comm->process, "lzop");
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_add_arg (comm->process, "-dfU");
+ fr_process_add_arg (comm->process, "--no-stdin");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ else if (is_mime_type (comm->mime_type, "application/x-7z-compressed-tar")) {
+ FrCommandTar *comm_tar = (FrCommandTar*) comm;
+
+ fr_process_begin_command (comm->process, comm_tar->compress_command);
+ fr_process_set_begin_func (comm->process, begin_func__uncompress, comm);
+ fr_process_add_arg (comm->process, "e");
+ fr_process_add_arg (comm->process, "-bd");
+ fr_process_add_arg (comm->process, "-y");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+
+ /* remove the compressed tar */
+
+ fr_process_begin_command (comm->process, "rm");
+ fr_process_add_arg (comm->process, "-f");
+ fr_process_add_arg (comm->process, tmp_name);
+ fr_process_end_command (comm->process);
+ }
+ }
+
+ c_tar->uncomp_filename = get_uncompressed_name (c_tar, tmp_name);
+ g_free (tmp_name);
+}
+
+
+static void
+fr_command_tar_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ if (error->type != FR_PROC_ERROR_NONE) {
+ if (error->status <= 1)
+ error->type = FR_PROC_ERROR_NONE;
+ }
+}
+
+
+const char *tar_mime_types[] = { "application/x-compressed-tar",
+ "application/x-bzip-compressed-tar",
+ "application/x-tar",
+ "application/x-7z-compressed-tar",
+ "application/x-lrzip-compressed-tar",
+ "application/x-lzip-compressed-tar",
+ "application/x-lzma-compressed-tar",
+ "application/x-lzop-compressed-tar",
+ "application/x-tarz",
+ "application/x-xz-compressed-tar",
+ NULL };
+
+
+const char **
+fr_command_tar_get_mime_types (FrCommand *comm)
+{
+ return tar_mime_types;
+}
+
+
+FrCommandCap
+fr_command_tar_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+
+ /* In solaris gtar is present under /usr/sfw/bin */
+ if (! is_program_available ("tar", check_command) && ! is_program_available ("/usr/sfw/bin/gtar", check_command))
+ return capabilities;
+
+ if (is_mime_type (mime_type, "application/x-tar")) {
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-compressed-tar")) {
+ if (is_program_available ("gzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-bzip-compressed-tar")) {
+ if (is_program_available ("bzip2", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-tarz")) {
+ if (is_program_available ("compress", check_command) && is_program_available ("uncompress", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ else if (is_program_available ("gzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+ }
+ else if (is_mime_type (mime_type, "application/x-lrzip-compressed-tar")) {
+ if (is_program_available ("lrzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-lzip-compressed-tar")) {
+ if (is_program_available ("lzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-lzma-compressed-tar")) {
+ if (is_program_available ("lzma", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-xz-compressed-tar")) {
+ if (is_program_available ("xz", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-lzop-compressed-tar")) {
+ if (is_program_available ("lzop", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_mime_type (mime_type, "application/x-7z-compressed-tar")) {
+ char *try_command[3] = { "7za", "7zr", "7z" };
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (try_command); i++) {
+ if (is_program_available (try_command[i], check_command)) {
+ capabilities |= FR_COMMAND_CAN_WRITE;
+ break;
+ }
+ }
+ }
+
+ return capabilities;
+}
+
+
+static void
+fr_command_tar_set_mime_type (FrCommand *comm,
+ const char *mime_type)
+{
+ FrCommandTar *comm_tar = FR_COMMAND_TAR (comm);
+
+ FR_COMMAND_CLASS (parent_class)->set_mime_type (comm, mime_type);
+
+ if (is_mime_type (mime_type, "application/x-7z-compressed-tar")) {
+ char *try_command[3] = { "7za", "7zr", "7z" };
+ int i;
+
+ for (i = 0; i < G_N_ELEMENTS (try_command); i++) {
+ if (is_program_in_path (try_command[i])) {
+ comm_tar->compress_command = g_strdup (try_command[i]);
+ break;
+ }
+ }
+ }
+}
+
+
+static const char *
+fr_command_tar_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ if (is_mime_type (mime_type, "application/x-tar"))
+ return PACKAGES ("tar");
+ else if (is_mime_type (mime_type, "application/x-compressed-tar"))
+ return PACKAGES ("tar,gzip");
+ else if (is_mime_type (mime_type, "application/x-bzip-compressed-tar"))
+ return PACKAGES ("tar,bzip2");
+ else if (is_mime_type (mime_type, "application/x-tarz"))
+ return PACKAGES ("tar,gzip,ncompress");
+ else if (is_mime_type (mime_type, "application/x-lrzip-compressed-tar"))
+ return PACKAGES ("tar,lrzip");
+ else if (is_mime_type (mime_type, "application/x-lzip-compressed-tar"))
+ return PACKAGES ("tar,lzip");
+ else if (is_mime_type (mime_type, "application/x-lzma-compressed-tar"))
+ return PACKAGES ("tar,lzma");
+ else if (is_mime_type (mime_type, "application/x-xz-compressed-tar"))
+ return PACKAGES ("tar,xz");
+ else if (is_mime_type (mime_type, "application/x-lzop-compressed-tar"))
+ return PACKAGES ("tar,lzop");
+ else if (is_mime_type (mime_type, "application/x-7z-compressed-tar"))
+ return PACKAGES ("tar,p7zip");
+
+ return NULL;
+}
+
+
+static void
+fr_command_tar_class_init (FrCommandTarClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_tar_finalize;
+
+ afc->list = fr_command_tar_list;
+ afc->add = fr_command_tar_add;
+ afc->delete = fr_command_tar_delete;
+ afc->extract = fr_command_tar_extract;
+ afc->handle_error = fr_command_tar_handle_error;
+ afc->get_mime_types = fr_command_tar_get_mime_types;
+ afc->get_capabilities = fr_command_tar_get_capabilities;
+ afc->set_mime_type = fr_command_tar_set_mime_type;
+ afc->recompress = fr_command_tar_recompress;
+ afc->uncompress = fr_command_tar_uncompress;
+ afc->get_packages = fr_command_tar_get_packages;
+}
+
+
+static void
+fr_command_tar_init (FrCommand *comm)
+{
+ FrCommandTar *comm_tar = (FrCommandTar*) comm;
+
+ comm->propAddCanUpdate = FALSE;
+ comm->propAddCanReplace = FALSE;
+ comm->propAddCanStoreFolders = TRUE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = TRUE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+ comm->propCanDeleteNonEmptyFolders = FALSE;
+ comm->propCanExtractNonEmptyFolders = FALSE;
+ comm->propListFromFile = TRUE;
+
+ comm_tar->msg = NULL;
+ comm_tar->uncomp_filename = NULL;
+}
+
+
+static void
+fr_command_tar_finalize (GObject *object)
+{
+ FrCommandTar *comm_tar;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_TAR (object));
+
+ comm_tar = FR_COMMAND_TAR (object);
+
+ if (comm_tar->uncomp_filename != NULL) {
+ g_free (comm_tar->uncomp_filename);
+ comm_tar->uncomp_filename = NULL;
+ }
+
+ if (comm_tar->msg != NULL) {
+ g_free (comm_tar->msg);
+ comm_tar->msg = NULL;
+ }
+
+ if (comm_tar->compress_command != NULL) {
+ g_free (comm_tar->compress_command);
+ comm_tar->compress_command = NULL;
+ }
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_tar_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandTarClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_tar_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandTar),
+ 0,
+ (GInstanceInitFunc) fr_command_tar_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandTar",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-tar.h b/src/fr-command-tar.h
new file mode 100644
index 0000000..a214353
--- /dev/null
+++ b/src/fr-command-tar.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_TAR_H
+#define FR_COMMAND_TAR_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+#include "typedefs.h"
+
+#define FR_TYPE_COMMAND_TAR (fr_command_tar_get_type ())
+#define FR_COMMAND_TAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_TAR, FrCommandTar))
+#define FR_COMMAND_TAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_TAR, FrCommandTarClass))
+#define FR_IS_COMMAND_TAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_TAR))
+#define FR_IS_COMMAND_TAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_TAR))
+#define FR_COMMAND_TAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_TAR, FrCommandTarClass))
+
+typedef struct _FrCommandTar FrCommandTar;
+typedef struct _FrCommandTarClass FrCommandTarClass;
+
+struct _FrCommandTar
+{
+ FrCommand __parent;
+
+ /*<private>*/
+
+ char *uncomp_filename;
+ gboolean name_modified;
+ char *compress_command;
+
+ char *msg;
+};
+
+struct _FrCommandTarClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_tar_get_type (void);
+
+#endif /* FR_COMMAND_TAR_H */
diff --git a/src/fr-command-unstuff.c b/src/fr-command-unstuff.c
new file mode 100644
index 0000000..5bd5e36
--- /dev/null
+++ b/src/fr-command-unstuff.c
@@ -0,0 +1,390 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "fr-command.h"
+#include "fr-command-unstuff.h"
+
+static void fr_command_unstuff_class_init (FrCommandUnstuffClass *class);
+static void fr_command_unstuff_init (FrCommand *afile);
+static void fr_command_unstuff_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+/* recursive rmdir to remove the left-overs from unstuff */
+static void
+recursive_rmdir (const char *path)
+{
+ GDir *dir;
+ const char *dirname;
+
+ dir = g_dir_open (path, 0, NULL);
+ if (dir == NULL)
+ return;
+
+ dirname = g_dir_read_name (dir);
+ while (dirname != NULL)
+ {
+ char *full_path;
+
+ if (strcmp (dirname, ".") == 0 || strcmp (dirname, "..") == 0)
+ continue;
+
+ full_path = g_build_filename (path, dirname, NULL);
+ recursive_rmdir (full_path);
+ g_free (full_path);
+
+ dirname = g_dir_read_name (dir);
+ }
+
+ rmdir (path);
+
+ g_dir_close (dir);
+}
+
+
+/* unstuff doesn't like file paths starting with /, that's so shite */
+static char *
+unstuff_is_shit_with_filenames (const char *orig)
+{
+ int i, num_slashes;
+ char *current_dir, *filename;
+
+ g_return_val_if_fail (orig != NULL, NULL);
+
+ current_dir = g_get_current_dir ();
+ i = num_slashes = 0;
+ while (current_dir[i] != '\0') {
+ if (current_dir[i] == '/')
+ num_slashes++;
+ i++;
+ }
+ g_free (current_dir);
+
+ /* 3 characters for each ../ plus filename length plus \0 */
+ filename = g_malloc (3 * i + strlen (orig) + 1);
+ i = 0;
+ for ( ; num_slashes > 0 ; num_slashes--) {
+ memcpy (filename + i, "../", 3);
+ i+=3;
+ }
+ memcpy (filename + i, orig, strlen (orig) + 1);
+
+ return filename;
+}
+
+
+static void
+process_line (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+ FrCommandUnstuff *unstuff_comm = FR_COMMAND_UNSTUFF (comm);
+ const char *str_start;
+ char *filename, *real_filename;
+ int i;
+ FileData *fdata;
+
+ g_return_if_fail (line != NULL);
+
+ if (strstr (line, "progressEvent - ")) {
+ const char *ssize;
+ guint size;
+
+ ssize = strstr (line, "progressEvent - ")
+ + strlen ("progressEvent - ");
+ if (ssize[0] == '\0')
+ size = 0;
+ else
+ size = g_ascii_strtoull (ssize, NULL, 10);
+
+ if (unstuff_comm->fdata != NULL)
+ unstuff_comm->fdata->size = size;
+
+ return;
+ }
+
+ if (strstr (line, "fileEvent") == NULL)
+ return;
+ if (strstr (line, unstuff_comm->target_dir + 1) == NULL)
+ return;
+
+ /* Look for the filename, ends with a comma */
+ str_start = strstr (line, unstuff_comm->target_dir + 1);
+ str_start = str_start + strlen (unstuff_comm->target_dir) - 1;
+ if (str_start[0] != '/')
+ str_start--;
+ if (str_start[0] == '.')
+ str_start--;
+ i = 0;
+ while (str_start[i] != '\0' && str_start[i] != ',') {
+ i++;
+ }
+ /* This is not supposed to happen */
+ g_return_if_fail (str_start[i] != '\0');
+ filename = g_strndup (str_start, i);
+
+ /* Same thing with the real filename */
+ str_start = strstr (line, unstuff_comm->target_dir);
+ i = 0;
+ while (str_start[i] != '\0' && str_start[i] != ',') {
+ i++;
+ }
+ real_filename = g_strndup (str_start, i);
+
+ fdata = file_data_new ();
+ fdata->full_path = filename;
+ fdata->original_path = filename;
+ fdata->link = NULL;
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ fdata->size = 0;
+ fdata->modified = time (NULL);
+
+ unstuff_comm->fdata = fdata;
+ fr_command_add_file (comm, fdata);
+
+ unlink (real_filename);
+ g_free (real_filename);
+}
+
+
+static void
+list__begin (gpointer data)
+{
+ FrCommandUnstuff *comm = data;
+
+ comm->fdata = NULL;
+}
+
+
+static void
+fr_command_unstuff_list (FrCommand *comm)
+{
+ char *arg, *path;
+ char *filename;
+ char *path_dots;
+
+ fr_process_set_out_line_func (comm->process, process_line, comm);
+
+ fr_process_begin_command (comm->process, "unstuff");
+ fr_process_set_begin_func (comm->process, list__begin, comm);
+ fr_process_add_arg (comm->process, "--trace");
+
+ /* Actually unpack everything in a temporary directory */
+ path = get_temp_work_dir (NULL);
+ path_dots = unstuff_is_shit_with_filenames (path);
+ g_free (path);
+
+ arg = g_strdup_printf ("-d=%s", path_dots);
+ FR_COMMAND_UNSTUFF (comm)->target_dir = path_dots;
+ fr_process_add_arg (comm->process, arg);
+ g_free (arg);
+
+ filename = unstuff_is_shit_with_filenames (comm->filename);
+ fr_process_add_arg (comm->process, filename);
+ g_free (filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+fr_command_unstuff_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+#if 0
+ GList *scan;
+#endif
+ char *filename;
+
+ fr_process_begin_command (comm->process, "unstuff");
+
+ if (dest_dir != NULL) {
+ char *dest_dir_dots;
+ char *arg;
+
+ dest_dir_dots = unstuff_is_shit_with_filenames (dest_dir);
+ arg = g_strdup_printf ("-d=%s", dest_dir_dots);
+ fr_process_add_arg (comm->process, arg);
+ FR_COMMAND_UNSTUFF (comm)->target_dir = NULL;
+ g_free (arg);
+ g_free (dest_dir_dots);
+ }
+
+ fr_process_add_arg (comm->process, "--trace");
+
+ /* unstuff doesn't like file paths starting with /, that's so shite */
+ filename = unstuff_is_shit_with_filenames (comm->filename);
+ fr_process_add_arg (comm->process, filename);
+ g_free (filename);
+
+ /* FIXME it is not possible to unpack only some files */
+#if 0
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+#endif
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_unstuff_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ if ((error->type != FR_PROC_ERROR_NONE)
+ && (error->status <= 1))
+ {
+ error->type = FR_PROC_ERROR_NONE;
+ }
+}
+
+
+const char *unstuff_mime_type[] = { "application/x-stuffit", NULL };
+
+
+const char **
+fr_command_unstuff_get_mime_types (FrCommand *comm)
+{
+ return unstuff_mime_type;
+}
+
+
+FrCommandCap
+fr_command_unstuff_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("unstuff", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_unstaff_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("unstaff");
+}
+
+
+static void
+fr_command_unstuff_class_init (FrCommandUnstuffClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_unstuff_finalize;
+
+ afc->list = fr_command_unstuff_list;
+ afc->add = NULL;
+ afc->delete = NULL;
+ afc->extract = fr_command_unstuff_extract;
+ afc->handle_error = fr_command_unstuff_handle_error;
+ afc->get_mime_types = fr_command_unstuff_get_mime_types;
+ afc->get_capabilities = fr_command_unstuff_get_capabilities;
+ afc->get_packages = fr_command_unstaff_get_packages;
+}
+
+
+static void
+fr_command_unstuff_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = FALSE;
+ comm->propAddCanReplace = FALSE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = TRUE;
+ comm->propTest = FALSE;
+}
+
+
+static void
+fr_command_unstuff_finalize (GObject *object)
+{
+ FrCommandUnstuff *unstuff_comm = FR_COMMAND_UNSTUFF (object);
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_UNSTUFF (object));
+
+ if (unstuff_comm->target_dir != NULL) {
+ recursive_rmdir (unstuff_comm->target_dir);
+ g_free (unstuff_comm->target_dir);
+ }
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_unstuff_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandUnstuffClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_unstuff_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandUnstuff),
+ 0,
+ (GInstanceInitFunc) fr_command_unstuff_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandUnstuff",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-unstuff.h b/src/fr-command-unstuff.h
new file mode 100644
index 0000000..e0fb7fb
--- /dev/null
+++ b/src/fr-command-unstuff.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_UNSTUFF_H
+#define FR_COMMAND_UNSTUFF_H
+
+#include <glib.h>
+#include "file-data.h"
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_UNSTUFF (fr_command_unstuff_get_type ())
+#define FR_COMMAND_UNSTUFF(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_UNSTUFF, FrCommandUnstuff))
+#define FR_COMMAND_UNSTUFF_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_UNSTUFF, FrCommandUnstuffClass))
+#define FR_IS_COMMAND_UNSTUFF(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_UNSTUFF))
+#define FR_IS_COMMAND_UNSTUFF_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_UNSTUFF))
+#define FR_COMMAND_UNSTUFF_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_UNSTUFF, FrCommandUnstuffClass))
+
+typedef struct _FrCommandUnstuff FrCommandUnstuff;
+typedef struct _FrCommandUnstuffClass FrCommandUnstuffClass;
+
+struct _FrCommandUnstuff
+{
+ FrCommand __parent;
+
+ char *target_dir;
+ FileData *fdata;
+};
+
+struct _FrCommandUnstuffClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_unstuff_get_type (void);
+
+#endif /* FR_COMMAND_UNSTUFF_H */
diff --git a/src/fr-command-zip.c b/src/fr-command-zip.c
new file mode 100644
index 0000000..3e651f7
--- /dev/null
+++ b/src/fr-command-zip.c
@@ -0,0 +1,494 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-zip.h"
+
+#define EMPTY_ARCHIVE_WARNING "Empty zipfile."
+#define ZIP_SPECIAL_CHARACTERS "[]*?!^-\\"
+
+static void fr_command_zip_class_init (FrCommandZipClass *class);
+static void fr_command_zip_init (FrCommand *afile);
+static void fr_command_zip_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+static time_t
+mktime_from_string (char *datetime_s)
+{
+ struct tm tm = {0, };
+ char *date;
+ char *time;
+ char *year;
+ char *month;
+ char *day;
+ char *hour;
+ char *min;
+ char *sec;
+
+ tm.tm_isdst = -1;
+
+ /* date */
+
+ date = datetime_s;
+ year = g_strndup (date, 4);
+ month = g_strndup (date + 4, 2);
+ day = g_strndup (date + 6, 2);
+ tm.tm_year = atoi (year) - 1900;
+ tm.tm_mon = atoi (month) - 1;
+ tm.tm_mday = atoi (day);
+ g_free (year);
+ g_free (month);
+ g_free (day);
+
+ /* time */
+
+ time = datetime_s + 9;
+ hour = g_strndup (time, 2);
+ min = g_strndup (time + 2, 2);
+ sec = g_strndup (time + 4, 2);
+ tm.tm_hour = atoi (hour);
+ tm.tm_min = atoi (min);
+ tm.tm_sec = atoi (sec);
+ g_free(hour);
+ g_free(min);
+ g_free(sec);
+
+ return mktime (&tm);
+}
+
+
+static void
+list__process_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *comm = FR_COMMAND (data);
+ char **fields;
+ const char *name_field;
+ gint line_l;
+
+ g_return_if_fail (line != NULL);
+
+ /* check whether unzip gave the empty archive warning. */
+
+ if (FR_COMMAND_ZIP (comm)->is_empty)
+ return;
+
+ line_l = strlen (line);
+
+ if (line_l == 0)
+ return;
+
+ if (strcmp (line, EMPTY_ARCHIVE_WARNING) == 0) {
+ FR_COMMAND_ZIP (comm)->is_empty = TRUE;
+ return;
+ }
+
+ /* ignore lines that do not describe a file or a
+ * directory. */
+ if ((line[0] != '?') && (line[0] != 'd') && (line[0] != '-'))
+ return;
+
+ /**/
+
+ fdata = file_data_new ();
+
+ fields = split_line (line, 7);
+ fdata->size = g_ascii_strtoull (fields[3], NULL, 10);
+ fdata->modified = mktime_from_string (fields[6]);
+ fdata->encrypted = (*fields[4] == 'B') || (*fields[4] == 'T');
+ g_strfreev (fields);
+
+ /* Full path */
+
+ name_field = get_last_field (line, 8);
+
+ if (*name_field == '/') {
+ fdata->full_path = g_strdup (name_field);
+ fdata->original_path = fdata->full_path;
+ } else {
+ fdata->full_path = g_strconcat ("/", name_field, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+
+ fdata->link = NULL;
+
+ fdata->dir = line[0] == 'd';
+ if (fdata->dir)
+ fdata->name = dir_name_from_path (fdata->full_path);
+ else
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (comm, fdata);
+}
+
+
+static void
+add_password_arg (FrCommand *comm,
+ const char *password)
+{
+ if ((password != NULL) && (password[0] != '\0')) {
+ fr_process_add_arg (comm->process, "-P");
+ fr_process_add_arg (comm->process, password);
+ }
+}
+
+
+static void
+list__begin (gpointer data)
+{
+ FrCommandZip *comm = data;
+
+ comm->is_empty = FALSE;
+}
+
+
+static void
+fr_command_zip_list (FrCommand *comm)
+{
+ fr_process_set_out_line_func (comm->process, list__process_line, comm);
+
+ fr_process_begin_command (comm->process, "unzip");
+ fr_process_set_begin_func (comm->process, list__begin, comm);
+ fr_process_add_arg (comm->process, "-ZTs");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+ fr_process_start (comm->process);
+}
+
+
+static void
+process_line__common (char *line,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+
+ if (line == NULL)
+ return;
+
+ fr_command_message (comm, line);
+
+ if (comm->n_files != 0) {
+ double fraction = (double) ++comm->n_file / (comm->n_files + 1);
+ fr_command_progress (comm, fraction);
+ }
+}
+
+
+static void
+fr_command_zip_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ GList *scan;
+
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process,
+ process_line__common,
+ comm);
+
+ fr_process_begin_command (comm->process, "zip");
+
+ if (base_dir != NULL)
+ fr_process_set_working_dir (comm->process, base_dir);
+
+ /* preserve links. */
+ fr_process_add_arg (comm->process, "-y");
+
+ if (update)
+ fr_process_add_arg (comm->process, "-u");
+
+ add_password_arg (comm, comm->password);
+
+ switch (comm->compression) {
+ case FR_COMPRESSION_VERY_FAST:
+ fr_process_add_arg (comm->process, "-1"); break;
+ case FR_COMPRESSION_FAST:
+ fr_process_add_arg (comm->process, "-3"); break;
+ case FR_COMPRESSION_NORMAL:
+ fr_process_add_arg (comm->process, "-6"); break;
+ case FR_COMPRESSION_MAXIMUM:
+ fr_process_add_arg (comm->process, "-9"); break;
+ }
+
+ fr_process_add_arg (comm->process, comm->filename);
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_zip_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ GList *scan;
+
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process,
+ process_line__common,
+ comm);
+
+ fr_process_begin_command (comm->process, "zip");
+ fr_process_add_arg (comm->process, "-d");
+
+ fr_process_add_arg (comm->process, comm->filename);
+ for (scan = file_list; scan; scan = scan->next) {
+ char *escaped;
+
+ escaped = escape_str (scan->data, ZIP_SPECIAL_CHARACTERS);
+ fr_process_add_arg (comm->process, escaped);
+ g_free (escaped);
+ }
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_zip_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process,
+ process_line__common,
+ comm);
+
+ fr_process_begin_command (comm->process, "unzip");
+
+ if (dest_dir != NULL) {
+ fr_process_add_arg (comm->process, "-d");
+ fr_process_add_arg (comm->process, dest_dir);
+ }
+ if (overwrite)
+ fr_process_add_arg (comm->process, "-o");
+ else
+ fr_process_add_arg (comm->process, "-n");
+ if (skip_older)
+ fr_process_add_arg (comm->process, "-u");
+ if (junk_paths)
+ fr_process_add_arg (comm->process, "-j");
+ add_password_arg (comm, comm->password);
+
+ fr_process_add_arg (comm->process, comm->filename);
+ for (scan = file_list; scan; scan = scan->next) {
+ char *escaped;
+
+ escaped = escape_str (scan->data, ZIP_SPECIAL_CHARACTERS);
+ fr_process_add_arg (comm->process, escaped);
+ g_free (escaped);
+ }
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_zip_test (FrCommand *comm)
+{
+ fr_process_begin_command (comm->process, "unzip");
+ fr_process_add_arg (comm->process, "-t");
+ add_password_arg (comm, comm->password);
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_zip_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ if (error->type != FR_PROC_ERROR_NONE) {
+ if (error->status <= 1)
+ error->type = FR_PROC_ERROR_NONE;
+ else if ((error->status == 82) || (error->status == 5))
+ error->type = FR_PROC_ERROR_ASK_PASSWORD;
+ else {
+ GList *output;
+ GList *scan;
+
+ if (comm->action == FR_ACTION_TESTING_ARCHIVE)
+ output = comm->process->out.raw;
+ else
+ output = comm->process->err.raw;
+
+ for (scan = g_list_last (output); scan; scan = scan->prev) {
+ char *line = scan->data;
+
+ if (strstr (line, "incorrect password") != NULL) {
+ error->type = FR_PROC_ERROR_ASK_PASSWORD;
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+const char *zip_mime_type[] = { "application/x-cbz",
+ "application/x-ms-dos-executable",
+ "application/zip",
+ NULL };
+
+
+const char **
+fr_command_zip_get_mime_types (FrCommand *comm)
+{
+ return zip_mime_type;
+}
+
+
+FrCommandCap
+fr_command_zip_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES | FR_COMMAND_CAN_ENCRYPT;
+ if (is_program_available ("zip", check_command)) {
+ if (strcmp (mime_type, "application/x-ms-dos-executable") == 0)
+ capabilities |= FR_COMMAND_CAN_READ;
+ else
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+ }
+ else if (is_program_available ("unzip", check_command))
+ capabilities |= FR_COMMAND_CAN_READ;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_zip_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("zip,unzip");
+}
+
+
+static void
+fr_command_zip_class_init (FrCommandZipClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_zip_finalize;
+
+ afc->list = fr_command_zip_list;
+ afc->add = fr_command_zip_add;
+ afc->delete = fr_command_zip_delete;
+ afc->extract = fr_command_zip_extract;
+ afc->test = fr_command_zip_test;
+ afc->handle_error = fr_command_zip_handle_error;
+ afc->get_mime_types = fr_command_zip_get_mime_types;
+ afc->get_capabilities = fr_command_zip_get_capabilities;
+ afc->get_packages = fr_command_zip_get_packages;
+}
+
+
+static void
+fr_command_zip_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = TRUE;
+ comm->propAddCanStoreFolders = TRUE;
+ comm->propExtractCanAvoidOverwrite = TRUE;
+ comm->propExtractCanSkipOlder = TRUE;
+ comm->propExtractCanJunkPaths = TRUE;
+ comm->propPassword = TRUE;
+ comm->propTest = TRUE;
+
+ FR_COMMAND_ZIP (comm)->is_empty = FALSE;
+}
+
+
+static void
+fr_command_zip_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_ZIP (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_zip_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandZipClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_zip_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandZip),
+ 0,
+ (GInstanceInitFunc) fr_command_zip_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandZip",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-zip.h b/src/fr-command-zip.h
new file mode 100644
index 0000000..969985d
--- /dev/null
+++ b/src/fr-command-zip.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_ZIP_H
+#define FR_COMMAND_ZIP_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+
+#define FR_TYPE_COMMAND_ZIP (fr_command_zip_get_type ())
+#define FR_COMMAND_ZIP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ZIP, FrCommandZip))
+#define FR_COMMAND_ZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ZIP, FrCommandZipClass))
+#define FR_IS_COMMAND_ZIP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ZIP))
+#define FR_IS_COMMAND_ZIP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ZIP))
+#define FR_COMMAND_ZIP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ZIP, FrCommandZipClass))
+
+typedef struct _FrCommandZip FrCommandZip;
+typedef struct _FrCommandZipClass FrCommandZipClass;
+
+struct _FrCommandZip
+{
+ FrCommand __parent;
+ gboolean is_empty;
+};
+
+struct _FrCommandZipClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_zip_get_type (void);
+
+#endif /* FR_COMMAND_ZIP_H */
diff --git a/src/fr-command-zoo.c b/src/fr-command-zoo.c
new file mode 100644
index 0000000..00b79de
--- /dev/null
+++ b/src/fr-command-zoo.c
@@ -0,0 +1,428 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <glib.h>
+
+#include "file-data.h"
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-zoo.h"
+
+static void fr_command_zoo_class_init (FrCommandZooClass *class);
+static void fr_command_zoo_init (FrCommand *afile);
+static void fr_command_zoo_finalize (GObject *object);
+
+/* Parent Class */
+
+static FrCommandClass *parent_class = NULL;
+
+
+/* -- list -- */
+
+static time_t
+mktime_from_string_zoo (char *mday_s,
+ char *month_s,
+ char *year_s,
+ char *time_s)
+{
+ struct tm tm = {0, };
+ char **fields;
+ int year;
+
+ tm.tm_isdst = -1;
+
+ /* This will break in 2075 */
+ year = atoi (year_s);
+ if (year >= 75) {
+ tm.tm_year = year;
+ } else {
+ tm.tm_year = 100 + year;
+ }
+
+ if (g_ascii_strncasecmp(month_s, "Jan", 3) == 0) {
+ tm.tm_mon = 0;
+ } else if (g_ascii_strncasecmp(month_s, "Feb", 3) == 0) {
+ tm.tm_mon = 1;
+ } else if (g_ascii_strncasecmp(month_s, "Mar", 3) == 0) {
+ tm.tm_mon = 2;
+ } else if (g_ascii_strncasecmp(month_s, "Apr", 3) == 0) {
+ tm.tm_mon = 3;
+ } else if (g_ascii_strncasecmp(month_s, "May", 3) == 0) {
+ tm.tm_mon = 4;
+ } else if (g_ascii_strncasecmp(month_s, "Jun", 3) == 0) {
+ tm.tm_mon = 5;
+ } else if (g_ascii_strncasecmp(month_s, "Jul", 3) == 0) {
+ tm.tm_mon = 6;
+ } else if (g_ascii_strncasecmp(month_s, "Aug", 3) == 0) {
+ tm.tm_mon = 7;
+ } else if (g_ascii_strncasecmp(month_s, "Sep", 3) == 0) {
+ tm.tm_mon = 8;
+ } else if (g_ascii_strncasecmp(month_s, "Oct", 3) == 0) {
+ tm.tm_mon = 9;
+ } else if (g_ascii_strncasecmp(month_s, "Nov", 3) == 0) {
+ tm.tm_mon = 10;
+ } else if (g_ascii_strncasecmp(month_s, "Dec", 3) == 0) {
+ tm.tm_mon = 11;
+ }
+
+ tm.tm_mday = atoi (mday_s);
+
+ fields = g_strsplit (time_s, ":", 3);
+ if (fields[0] != NULL) {
+ tm.tm_hour = atoi (fields[0]);
+ if (fields[1] != NULL) {
+ tm.tm_min = atoi (fields[1]);
+ if (fields[2] != NULL)
+ tm.tm_sec = atoi (fields[2]);
+ }
+ }
+
+ g_strfreev (fields);
+
+ return mktime (&tm);
+}
+
+
+static char **
+split_line_zoo (char *line)
+{
+ char **fields;
+ const char *scan, *field_end;
+ int i;
+
+ if (line[0] == '-') {
+ return NULL;
+ }
+
+ fields = g_new0 (char *, 6);
+ fields[5] = NULL;
+
+ /* Get Length */
+ scan = eat_spaces (line);
+ field_end = strchr (scan, ' ');
+ fields[0] = g_strndup (scan, field_end - scan);
+ scan = eat_spaces (field_end);
+
+ /* Toss CF, Size Now */
+ for (i = 0; i < 2; i++) {
+ field_end = strchr (scan, ' ');
+ scan = eat_spaces (field_end);
+ }
+
+ /* Get Day, Month, Year, Time */
+ for (i = 1; i < 5; i++) {
+ if (i == 2 && g_ascii_strncasecmp (scan, "file", 4) == 0) {
+ g_strfreev(fields);
+ return NULL;
+ }
+ field_end = strchr (scan, ' ');
+ fields[i] = g_strndup (scan, field_end - scan);
+ scan = eat_spaces (field_end);
+ }
+
+ return fields;
+}
+
+
+static const char *
+get_last_field_zoo (char *line)
+{
+ const char *field;
+ int i;
+ int n = 6;
+
+ field = eat_spaces (line);
+ for (i = 0; i < n; i++) {
+ field = strchr (field, ' ');
+ field = eat_spaces (field);
+ }
+ field = strchr (field, ' ');
+ if (g_ascii_strncasecmp (field, " C ", 3) == 0) {
+ field = eat_spaces (field);
+ field = strchr (field, ' ');
+ field = eat_spaces (field);
+ } else
+ field = eat_spaces (field);
+
+ return field;
+}
+
+
+static void
+process_zoo_line (char *line,
+ gpointer data)
+{
+ FileData *fdata;
+ FrCommand *zoo_comm = FR_COMMAND (data);
+ char **fields;
+ const char *name_field;
+
+ g_return_if_fail (line != NULL);
+ if (line[0] == '-')
+ return;
+
+ fields = split_line_zoo (line);
+ if (fields == NULL)
+ return;
+
+ fdata = file_data_new ();
+
+ fdata->size = g_ascii_strtoull (fields[0], NULL, 10);
+ fdata->modified = mktime_from_string_zoo (fields[1],
+ fields[2],
+ fields[3],
+ fields[4]);
+ g_strfreev (fields);
+
+ /* Full path */
+
+ name_field = get_last_field_zoo (line);
+ if (*(name_field) == '/') {
+ fdata->full_path = g_strdup (name_field);
+ fdata->original_path = fdata->full_path;
+ } else {
+ fdata->full_path = g_strconcat ("/", name_field, NULL);
+ fdata->original_path = fdata->full_path + 1;
+ }
+
+ fdata->name = g_strdup (file_name_from_path (fdata->full_path));
+ fdata->path = remove_level_from_path (fdata->full_path);
+
+ if (*fdata->name == 0)
+ file_data_free (fdata);
+ else
+ fr_command_add_file (zoo_comm, fdata);
+}
+
+
+static void
+fr_command_zoo_list (FrCommand *zoo_comm)
+{
+ fr_process_set_out_line_func (zoo_comm->process, process_zoo_line, zoo_comm);
+
+ fr_process_begin_command (zoo_comm->process, "zoo");
+ fr_process_add_arg (zoo_comm->process, "lq");
+ fr_process_add_arg (zoo_comm->process, zoo_comm->filename);
+ fr_process_end_command (zoo_comm->process);
+ fr_process_start (zoo_comm->process);
+}
+
+
+static void
+fr_command_zoo_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ GList *scan;
+
+ /* Add files. */
+
+ fr_process_begin_command (comm->process, "zoo");
+
+ fr_process_set_working_dir (comm->process, base_dir);
+
+ if (update)
+ fr_process_add_arg (comm->process, "auP");
+ else
+ fr_process_add_arg (comm->process, "aP");
+
+ fr_process_add_arg (comm->process, comm->filename);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_zoo_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ GList *scan;
+
+ /* Delete files. */
+
+ fr_process_begin_command (comm->process, "zoo");
+ fr_process_add_arg (comm->process, "DP");
+ fr_process_add_arg (comm->process, comm->filename);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_zoo_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ GList *scan;
+
+ fr_process_begin_command (comm->process, "zoo");
+
+ if (overwrite)
+ fr_process_add_arg (comm->process, "xO");
+ else
+ fr_process_add_arg (comm->process, "x");
+
+ fr_process_add_arg (comm->process, comm->filename);
+
+ if (dest_dir != NULL)
+ fr_process_set_working_dir (comm->process, dest_dir);
+
+ for (scan = file_list; scan; scan = scan->next)
+ fr_process_add_arg (comm->process, scan->data);
+
+ fr_process_end_command (comm->process);
+}
+
+
+static void
+fr_command_zoo_test (FrCommand *comm)
+{
+ fr_process_begin_command (comm->process, "zoo");
+ fr_process_add_arg (comm->process, "-test");
+ fr_process_add_arg (comm->process, comm->filename);
+ fr_process_end_command (comm->process);
+}
+
+
+const char *zoo_mime_type[] = { "application/x-zoo", NULL };
+
+
+const char **
+fr_command_zoo_get_mime_types (FrCommand *comm)
+{
+ return zoo_mime_type;
+}
+
+
+FrCommandCap
+fr_command_zoo_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ FrCommandCap capabilities;
+
+ capabilities = FR_COMMAND_CAN_ARCHIVE_MANY_FILES;
+ if (is_program_available ("zoo", check_command))
+ capabilities |= FR_COMMAND_CAN_READ_WRITE;
+
+ return capabilities;
+}
+
+
+static const char *
+fr_command_zoo_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return PACKAGES ("zoo");
+}
+
+
+static void
+fr_command_zoo_class_init (FrCommandZooClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ FrCommandClass *afc;
+
+ parent_class = g_type_class_peek_parent (class);
+ afc = (FrCommandClass*) class;
+
+ gobject_class->finalize = fr_command_zoo_finalize;
+
+ afc->list = fr_command_zoo_list;
+ afc->add = fr_command_zoo_add;
+ afc->delete = fr_command_zoo_delete;
+ afc->extract = fr_command_zoo_extract;
+ afc->test = fr_command_zoo_test;
+ afc->get_mime_types = fr_command_zoo_get_mime_types;
+ afc->get_capabilities = fr_command_zoo_get_capabilities;
+ afc->get_packages = fr_command_zoo_get_packages;
+}
+
+
+static void
+fr_command_zoo_init (FrCommand *comm)
+{
+ comm->propAddCanUpdate = TRUE;
+ comm->propAddCanReplace = FALSE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = TRUE;
+}
+
+
+static void
+fr_command_zoo_finalize (GObject *object)
+{
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND_ZOO (object));
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+GType
+fr_command_zoo_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandZooClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_zoo_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommandZoo),
+ 0,
+ (GInstanceInitFunc) fr_command_zoo_init
+ };
+
+ type = g_type_register_static (FR_TYPE_COMMAND,
+ "FRCommandZoo",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
diff --git a/src/fr-command-zoo.h b/src/fr-command-zoo.h
new file mode 100644
index 0000000..4b889c3
--- /dev/null
+++ b/src/fr-command-zoo.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2003 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_ZOO_H
+#define FR_COMMAND_ZOO_H
+
+#include <glib.h>
+#include "fr-command.h"
+#include "fr-process.h"
+#include "typedefs.h"
+
+#define FR_TYPE_COMMAND_ZOO (fr_command_zoo_get_type ())
+#define FR_COMMAND_ZOO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND_ZOO, FrCommandZoo))
+#define FR_COMMAND_ZOO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND_ZOO, FrCommandZooClass))
+#define FR_IS_COMMAND_ZOO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND_ZOO))
+#define FR_IS_COMMAND_ZOO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND_ZOO))
+#define FR_COMMAND_ZOO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND_ZOO, FrCommandZooClass))
+
+typedef struct _FrCommandZoo FrCommandZoo;
+typedef struct _FrCommandZooClass FrCommandZooClass;
+
+struct _FrCommandZoo
+{
+ FrCommand __parent;
+};
+
+struct _FrCommandZooClass
+{
+ FrCommandClass __parent_class;
+};
+
+GType fr_command_zoo_get_type (void);
+
+#endif /* FR_COMMAND_ZOO_H */
diff --git a/src/fr-command.c b/src/fr-command.c
new file mode 100644
index 0000000..873c02d
--- /dev/null
+++ b/src/fr-command.c
@@ -0,0 +1,826 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <glib.h>
+#include "file-data.h"
+#include "file-utils.h"
+#include "fr-command.h"
+#include "fr-enum-types.h"
+#include "fr-marshal.h"
+#include "fr-process.h"
+#include "glib-utils.h"
+
+#define INITIAL_SIZE 256
+
+
+/* Signals */
+enum {
+ START,
+ DONE,
+ PROGRESS,
+ MESSAGE,
+ WORKING_ARCHIVE,
+ LAST_SIGNAL
+};
+
+/* Properties */
+enum {
+ PROP_0,
+ PROP_FILENAME,
+ PROP_MIME_TYPE,
+ PROP_PROCESS,
+ PROP_PASSWORD,
+ PROP_ENCRYPT_HEADER,
+ PROP_COMPRESSION,
+ PROP_VOLUME_SIZE
+};
+
+static GObjectClass *parent_class = NULL;
+static guint fr_command_signals[LAST_SIGNAL] = { 0 };
+
+static void fr_command_class_init (FrCommandClass *class);
+static void fr_command_init (FrCommand *afile);
+static void fr_command_finalize (GObject *object);
+
+char *action_names[] = { "NONE",
+ "CREATING_NEW_ARCHIVE",
+ "LOADING_ARCHIVE",
+ "LISTING_CONTENT",
+ "DELETING_FILES",
+ "TESTING_ARCHIVE",
+ "GETTING_FILE_LIST",
+ "COPYING_FILES_FROM_REMOTE",
+ "ADDING_FILES",
+ "EXTRACTING_FILES",
+ "COPYING_FILES_TO_REMOTE",
+ "CREATING_ARCHIVE",
+ "SAVING_REMOTE_ARCHIVE" };
+
+GType
+fr_command_get_type ()
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrCommandClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_command_class_init,
+ NULL,
+ NULL,
+ sizeof (FrCommand),
+ 0,
+ (GInstanceInitFunc) fr_command_init
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT,
+ "FRCommand",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+static void
+base_fr_command_list (FrCommand *comm)
+{
+}
+
+
+static void
+base_fr_command_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+}
+
+
+static void
+base_fr_command_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+}
+
+
+static void
+base_fr_command_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+}
+
+
+static void
+base_fr_command_test (FrCommand *comm)
+{
+}
+
+
+static void
+base_fr_command_uncompress (FrCommand *comm)
+{
+}
+
+
+static void
+base_fr_command_recompress (FrCommand *comm)
+{
+}
+
+
+static void
+base_fr_command_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+}
+
+
+const char **void_mime_types = { NULL };
+
+
+const char **
+base_fr_command_get_mime_types (FrCommand *comm)
+{
+ return void_mime_types;
+}
+
+
+FrCommandCap
+base_fr_command_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ return FR_COMMAND_CAN_DO_NOTHING;
+}
+
+
+static void
+base_fr_command_set_mime_type (FrCommand *comm,
+ const char *mime_type)
+{
+ comm->mime_type = get_static_string (mime_type);
+ fr_command_update_capabilities (comm);
+}
+
+
+static const char *
+base_fr_command_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return NULL;
+}
+
+
+static void
+fr_command_start (FrProcess *process,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+
+ g_signal_emit (G_OBJECT (comm),
+ fr_command_signals[START],
+ 0,
+ comm->action);
+}
+
+
+static void
+fr_command_done (FrProcess *process,
+ gpointer data)
+{
+ FrCommand *comm = FR_COMMAND (data);
+
+ comm->process->restart = FALSE;
+ fr_command_handle_error (comm, &process->error);
+
+ if (comm->process->restart) {
+ fr_process_start (comm->process);
+ return;
+ }
+
+ if (comm->action == FR_ACTION_LISTING_CONTENT) {
+ /* order the list by name to speed up search */
+ g_ptr_array_sort (comm->files, file_data_compare_by_path);
+ }
+
+ g_signal_emit (G_OBJECT (comm),
+ fr_command_signals[DONE],
+ 0,
+ comm->action,
+ &process->error);
+}
+
+
+static void
+fr_command_set_process (FrCommand *comm,
+ FrProcess *process)
+{
+ if (comm->process != NULL) {
+ g_signal_handlers_disconnect_matched (G_OBJECT (comm->process),
+ G_SIGNAL_MATCH_DATA,
+ 0,
+ 0, NULL,
+ 0,
+ comm);
+ g_object_unref (G_OBJECT (comm->process));
+ comm->process = NULL;
+ }
+
+ if (process == NULL)
+ return;
+
+ g_object_ref (G_OBJECT (process));
+ comm->process = process;
+ g_signal_connect (G_OBJECT (comm->process),
+ "start",
+ G_CALLBACK (fr_command_start),
+ comm);
+ g_signal_connect (G_OBJECT (comm->process),
+ "done",
+ G_CALLBACK (fr_command_done),
+ comm);
+}
+
+
+static void
+fr_command_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ FrCommand *comm;
+
+ comm = FR_COMMAND (object);
+
+ switch (prop_id) {
+ case PROP_PROCESS:
+ fr_command_set_process (comm, g_value_get_object (value));
+ break;
+ case PROP_FILENAME:
+ fr_command_set_filename (comm, g_value_get_string (value));
+ break;
+ case PROP_MIME_TYPE:
+ fr_command_set_mime_type (comm, g_value_get_string (value));
+ break;
+ case PROP_PASSWORD:
+ g_free (comm->password);
+ comm->password = g_strdup (g_value_get_string (value));
+ break;
+ case PROP_ENCRYPT_HEADER:
+ comm->encrypt_header = g_value_get_boolean (value);
+ break;
+ case PROP_COMPRESSION:
+ comm->compression = g_value_get_enum (value);
+ break;
+ case PROP_VOLUME_SIZE:
+ comm->volume_size = g_value_get_uint (value);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+fr_command_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ FrCommand *comm;
+
+ comm = FR_COMMAND (object);
+
+ switch (prop_id) {
+ case PROP_PROCESS:
+ g_value_set_object (value, comm->process);
+ break;
+ case PROP_FILENAME:
+ g_value_set_string (value, comm->filename);
+ break;
+ case PROP_MIME_TYPE:
+ g_value_set_static_string (value, comm->mime_type);
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, comm->password);
+ break;
+ case PROP_ENCRYPT_HEADER:
+ g_value_set_boolean (value, comm->encrypt_header);
+ break;
+ case PROP_COMPRESSION:
+ g_value_set_enum (value, comm->compression);
+ break;
+ case PROP_VOLUME_SIZE:
+ g_value_set_uint (value, comm->volume_size);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+fr_command_class_init (FrCommandClass *class)
+{
+ GObjectClass *gobject_class;
+
+ parent_class = g_type_class_peek_parent (class);
+
+ gobject_class = G_OBJECT_CLASS (class);
+
+ /* virtual functions */
+
+ gobject_class->finalize = fr_command_finalize;
+ gobject_class->set_property = fr_command_set_property;
+ gobject_class->get_property = fr_command_get_property;
+
+ class->list = base_fr_command_list;
+ class->add = base_fr_command_add;
+ class->delete = base_fr_command_delete;
+ class->extract = base_fr_command_extract;
+ class->test = base_fr_command_test;
+ class->uncompress = base_fr_command_uncompress;
+ class->recompress = base_fr_command_recompress;
+ class->handle_error = base_fr_command_handle_error;
+ class->get_mime_types = base_fr_command_get_mime_types;
+ class->get_capabilities = base_fr_command_get_capabilities;
+ class->set_mime_type = base_fr_command_set_mime_type;
+ class->get_packages = base_fr_command_get_packages;
+ class->start = NULL;
+ class->done = NULL;
+ class->progress = NULL;
+ class->message = NULL;
+
+ /* signals */
+
+ fr_command_signals[START] =
+ g_signal_new ("start",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrCommandClass, start),
+ NULL, NULL,
+ fr_marshal_VOID__INT,
+ G_TYPE_NONE,
+ 1, G_TYPE_INT);
+ fr_command_signals[DONE] =
+ g_signal_new ("done",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrCommandClass, done),
+ NULL, NULL,
+ fr_marshal_VOID__INT_POINTER,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT,
+ G_TYPE_POINTER);
+ fr_command_signals[PROGRESS] =
+ g_signal_new ("progress",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrCommandClass, progress),
+ NULL, NULL,
+ fr_marshal_VOID__DOUBLE,
+ G_TYPE_NONE, 1,
+ G_TYPE_DOUBLE);
+ fr_command_signals[MESSAGE] =
+ g_signal_new ("message",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrCommandClass, message),
+ NULL, NULL,
+ fr_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+ fr_command_signals[WORKING_ARCHIVE] =
+ g_signal_new ("working_archive",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrCommandClass, working_archive),
+ NULL, NULL,
+ fr_marshal_VOID__STRING,
+ G_TYPE_NONE, 1,
+ G_TYPE_STRING);
+
+ /* properties */
+
+ g_object_class_install_property (gobject_class,
+ PROP_PROCESS,
+ g_param_spec_object ("process",
+ "Process",
+ "The process object used by the command",
+ FR_TYPE_PROCESS,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_FILENAME,
+ g_param_spec_string ("filename",
+ "Filename",
+ "The archive filename",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_MIME_TYPE,
+ g_param_spec_string ("mime-type",
+ "Mime type",
+ "The file mime-type",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_PASSWORD,
+ g_param_spec_string ("password",
+ "Password",
+ "The archive password",
+ NULL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_ENCRYPT_HEADER,
+ g_param_spec_boolean ("encrypt-header",
+ "Encrypt header",
+ "Whether to encrypt the archive header when creating the archive",
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_COMPRESSION,
+ g_param_spec_enum ("compression",
+ "Compression type",
+ "The compression type to use when creating the archive",
+ FR_TYPE_COMPRESSION,
+ FR_COMPRESSION_NORMAL,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (gobject_class,
+ PROP_VOLUME_SIZE,
+ g_param_spec_uint ("volume-size",
+ "Volume size",
+ "The size of each volume or 0 to not use volumes",
+ 0L,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE));
+}
+
+
+static void
+fr_command_init (FrCommand *comm)
+{
+ comm->files = g_ptr_array_sized_new (INITIAL_SIZE);
+
+ comm->password = NULL;
+ comm->encrypt_header = FALSE;
+ comm->compression = FR_COMPRESSION_NORMAL;
+ comm->volume_size = 0;
+ comm->filename = NULL;
+ comm->e_filename = NULL;
+ comm->fake_load = FALSE;
+
+ comm->propAddCanUpdate = FALSE;
+ comm->propAddCanReplace = FALSE;
+ comm->propAddCanStoreFolders = FALSE;
+ comm->propExtractCanAvoidOverwrite = FALSE;
+ comm->propExtractCanSkipOlder = FALSE;
+ comm->propExtractCanJunkPaths = FALSE;
+ comm->propPassword = FALSE;
+ comm->propTest = FALSE;
+ comm->propCanExtractAll = TRUE;
+ comm->propCanDeleteNonEmptyFolders = TRUE;
+ comm->propCanExtractNonEmptyFolders = TRUE;
+ comm->propListFromFile = FALSE;
+}
+
+
+static void
+fr_command_finalize (GObject *object)
+{
+ FrCommand* comm;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_COMMAND (object));
+
+ comm = FR_COMMAND (object);
+
+ g_free (comm->filename);
+ g_free (comm->e_filename);
+ g_free (comm->password);
+ if (comm->files != NULL)
+ g_ptr_array_free_full (comm->files, (GFunc) file_data_free, NULL);
+ fr_command_set_process (comm, NULL);
+
+ /* Chain up */
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+void
+fr_command_set_filename (FrCommand *comm,
+ const char *filename)
+{
+ g_return_if_fail (FR_IS_COMMAND (comm));
+
+ if (comm->filename != NULL) {
+ g_free (comm->filename);
+ comm->filename = NULL;
+ }
+
+ if (comm->e_filename != NULL) {
+ g_free (comm->e_filename);
+ comm->e_filename = NULL;
+ }
+
+ if (filename != NULL) {
+ if (! g_path_is_absolute (filename)) {
+ char *current_dir;
+
+ current_dir = g_get_current_dir ();
+ comm->filename = g_strconcat (current_dir,
+ "/",
+ filename,
+ NULL);
+ g_free (current_dir);
+ }
+ else
+ comm->filename = g_strdup (filename);
+
+ comm->e_filename = g_shell_quote (comm->filename);
+
+ debug (DEBUG_INFO, "filename : %s\n", comm->filename);
+ debug (DEBUG_INFO, "e_filename : %s\n", comm->e_filename);
+ }
+
+ fr_command_working_archive (comm, comm->filename);
+}
+
+
+void
+fr_command_set_multi_volume (FrCommand *comm,
+ const char *filename)
+{
+ comm->multi_volume = TRUE;
+ fr_command_set_filename (comm, filename);
+}
+
+
+void
+fr_command_list (FrCommand *comm)
+{
+ g_return_if_fail (FR_IS_COMMAND (comm));
+
+ fr_command_progress (comm, -1.0);
+
+ if (comm->files != NULL) {
+ g_ptr_array_free_full (comm->files, (GFunc) file_data_free, NULL);
+ comm->files = g_ptr_array_sized_new (INITIAL_SIZE);
+ }
+
+ comm->action = FR_ACTION_LISTING_CONTENT;
+ fr_process_set_out_line_func (comm->process, NULL, NULL);
+ fr_process_set_err_line_func (comm->process, NULL, NULL);
+ fr_process_use_standard_locale (comm->process, TRUE);
+ comm->multi_volume = FALSE;
+
+ if (! comm->fake_load)
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->list (comm);
+ else
+ g_signal_emit (G_OBJECT (comm),
+ fr_command_signals[DONE],
+ 0,
+ comm->action,
+ &comm->process->error);
+}
+
+
+void
+fr_command_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive)
+{
+ fr_command_progress (comm, -1.0);
+
+ comm->action = FR_ACTION_ADDING_FILES;
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process, NULL, NULL);
+ fr_process_set_err_line_func (FR_COMMAND (comm)->process, NULL, NULL);
+
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->add (comm,
+ from_file,
+ file_list,
+ base_dir,
+ update,
+ recursive);
+}
+
+
+void
+fr_command_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list)
+{
+ fr_command_progress (comm, -1.0);
+
+ comm->action = FR_ACTION_DELETING_FILES;
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process, NULL, NULL);
+ fr_process_set_err_line_func (FR_COMMAND (comm)->process, NULL, NULL);
+
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->delete (comm, from_file, file_list);
+}
+
+
+void
+fr_command_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths)
+{
+ fr_command_progress (comm, -1.0);
+
+ comm->action = FR_ACTION_EXTRACTING_FILES;
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process, NULL, NULL);
+ fr_process_set_err_line_func (FR_COMMAND (comm)->process, NULL, NULL);
+
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->extract (comm,
+ from_file,
+ file_list,
+ dest_dir,
+ overwrite,
+ skip_older,
+ junk_paths);
+}
+
+
+void
+fr_command_test (FrCommand *comm)
+{
+ fr_command_progress (comm, -1.0);
+
+ comm->action = FR_ACTION_TESTING_ARCHIVE;
+ fr_process_set_out_line_func (FR_COMMAND (comm)->process, NULL, NULL);
+ fr_process_set_err_line_func (FR_COMMAND (comm)->process, NULL, NULL);
+
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->test (comm);
+}
+
+
+void
+fr_command_uncompress (FrCommand *comm)
+{
+ fr_command_progress (comm, -1.0);
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->uncompress (comm);
+}
+
+
+void
+fr_command_recompress (FrCommand *comm)
+{
+ fr_command_progress (comm, -1.0);
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->recompress (comm);
+}
+
+
+const char **
+fr_command_get_mime_types (FrCommand *comm)
+{
+ return FR_COMMAND_GET_CLASS (G_OBJECT (comm))->get_mime_types (comm);
+}
+
+
+void
+fr_command_update_capabilities (FrCommand *comm)
+{
+ comm->capabilities = fr_command_get_capabilities (comm, comm->mime_type, TRUE);
+}
+
+
+FrCommandCap
+fr_command_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command)
+{
+ return FR_COMMAND_GET_CLASS (G_OBJECT (comm))->get_capabilities (comm, mime_type, check_command);
+}
+
+
+gboolean
+fr_command_is_capable_of (FrCommand *comm,
+ FrCommandCaps requested_capabilities)
+{
+ return (((comm->capabilities ^ requested_capabilities) & requested_capabilities) == 0);
+}
+
+
+const char *
+fr_command_get_packages (FrCommand *comm,
+ const char *mime_type)
+{
+ return FR_COMMAND_GET_CLASS (G_OBJECT (comm))->get_packages (comm, mime_type);
+}
+
+
+/* fraction == -1 means : I don't known how much time the current operation
+ * will take, the dialog will display this info pulsing
+ * the progress bar.
+ * fraction in [0.0, 1.0] means the amount of work, in percentage,
+ * accomplished.
+ */
+void
+fr_command_progress (FrCommand *comm,
+ double fraction)
+{
+ g_signal_emit (G_OBJECT (comm),
+ fr_command_signals[PROGRESS],
+ 0,
+ fraction);
+}
+
+
+void
+fr_command_message (FrCommand *comm,
+ const char *msg)
+{
+ g_signal_emit (G_OBJECT (comm),
+ fr_command_signals[MESSAGE],
+ 0,
+ msg);
+}
+
+
+void
+fr_command_working_archive (FrCommand *comm,
+ const char *archive_name)
+{
+ g_signal_emit (G_OBJECT (comm),
+ fr_command_signals[WORKING_ARCHIVE],
+ 0,
+ archive_name);
+}
+
+
+void
+fr_command_set_n_files (FrCommand *comm,
+ int n_files)
+{
+ comm->n_files = n_files;
+ comm->n_file = 0;
+}
+
+
+void
+fr_command_add_file (FrCommand *comm,
+ FileData *fdata)
+{
+ file_data_update_content_type (fdata);
+ g_ptr_array_add (comm->files, fdata);
+ if (! fdata->dir)
+ comm->n_regular_files++;
+}
+
+
+void
+fr_command_set_mime_type (FrCommand *comm,
+ const char *mime_type)
+{
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->set_mime_type (comm, mime_type);
+}
+
+
+void
+fr_command_handle_error (FrCommand *comm,
+ FrProcError *error)
+{
+ FR_COMMAND_GET_CLASS (G_OBJECT (comm))->handle_error (comm, error);
+}
diff --git a/src/fr-command.h b/src/fr-command.h
new file mode 100644
index 0000000..39b18de
--- /dev/null
+++ b/src/fr-command.h
@@ -0,0 +1,228 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_COMMAND_H
+#define FR_COMMAND_H
+
+#include <glib.h>
+#include "file-data.h"
+#include "fr-process.h"
+
+#define PACKAGES(x) (x)
+
+#define FR_TYPE_COMMAND (fr_command_get_type ())
+#define FR_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_COMMAND, FrCommand))
+#define FR_COMMAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_COMMAND, FrCommandClass))
+#define FR_IS_COMMAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_COMMAND))
+#define FR_IS_COMMAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_COMMAND))
+#define FR_COMMAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_COMMAND, FrCommandClass))
+
+typedef struct _FrCommand FrCommand;
+typedef struct _FrCommandClass FrCommandClass;
+
+typedef enum {
+ FR_ACTION_NONE,
+ FR_ACTION_CREATING_NEW_ARCHIVE,
+ FR_ACTION_LOADING_ARCHIVE, /* loading the archive from a remote location */
+ FR_ACTION_LISTING_CONTENT, /* listing the content of the archive */
+ FR_ACTION_DELETING_FILES, /* deleting files from the archive */
+ FR_ACTION_TESTING_ARCHIVE, /* testing the archive integrity */
+ FR_ACTION_GETTING_FILE_LIST, /* getting the file list (when fr_archive_add_with_wildcard or
+ fr_archive_add_directory are used, we need to scan a directory
+ and collect the files to add to the archive, this
+ may require some time to complete, so the operation
+ is asynchronous) */
+ FR_ACTION_COPYING_FILES_FROM_REMOTE, /* copying files to be added to the archive from a remote location */
+ FR_ACTION_ADDING_FILES, /* adding files to an archive */
+ FR_ACTION_EXTRACTING_FILES, /* extracting files */
+ FR_ACTION_COPYING_FILES_TO_REMOTE, /* copying extracted files to a remote location */
+ FR_ACTION_CREATING_ARCHIVE, /* creating a local archive */
+ FR_ACTION_SAVING_REMOTE_ARCHIVE /* copying the archive to a remote location */
+} FrAction;
+
+#ifdef DEBUG
+extern char *action_names[];
+#endif
+
+struct _FrCommand
+{
+ GObject __parent;
+
+ /*<public, read only>*/
+
+ GPtrArray *files; /* Array of FileData* */
+ int n_regular_files;
+ FrProcess *process; /* the process object used to execute
+ * commands. */
+ char *filename; /* archive file path. */
+ char *e_filename; /* escaped archive filename. */
+ const char *mime_type;
+ gboolean multi_volume;
+
+ /*<protected>*/
+
+ /* options */
+
+ char *password;
+ gboolean encrypt_header : 1;
+ FrCompression compression;
+ guint volume_size;
+ gboolean creating_archive;
+
+ /* features. */
+
+ guint propAddCanUpdate : 1;
+ guint propAddCanReplace : 1;
+ guint propAddCanStoreFolders : 1;
+ guint propExtractCanAvoidOverwrite : 1;
+ guint propExtractCanSkipOlder : 1;
+ guint propExtractCanJunkPaths : 1;
+ guint propPassword : 1;
+ guint propTest : 1;
+ guint propCanExtractAll : 1;
+ guint propCanDeleteNonEmptyFolders : 1;
+ guint propCanExtractNonEmptyFolders : 1;
+ guint propListFromFile : 1;
+
+ /*<private>*/
+
+ FrCommandCaps capabilities;
+ FrAction action; /* current action. */
+ gboolean fake_load; /* if TRUE does nothing when the list
+ * operation is invoked. */
+
+ /* progress data */
+
+ int n_file;
+ int n_files;
+};
+
+struct _FrCommandClass
+{
+ GObjectClass __parent_class;
+
+ /*<virtual functions>*/
+
+ void (*list) (FrCommand *comm);
+ void (*add) (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive);
+ void (*delete) (FrCommand *comm,
+ const char *from_file,
+ GList *file_list);
+ void (*extract) (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths);
+ void (*test) (FrCommand *comm);
+ void (*uncompress) (FrCommand *comm);
+ void (*recompress) (FrCommand *comm);
+ void (*handle_error) (FrCommand *comm,
+ FrProcError *error);
+ const char ** (*get_mime_types) (FrCommand *comm);
+ FrCommandCap (*get_capabilities) (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command);
+ void (*set_mime_type) (FrCommand *comm,
+ const char *mime_type);
+ const char * (*get_packages) (FrCommand *comm,
+ const char *mime_type);
+
+ /*<signals>*/
+
+ void (*start) (FrCommand *comm,
+ FrAction action);
+ void (*done) (FrCommand *comm,
+ FrAction action,
+ FrProcError *error);
+ void (*progress) (FrCommand *comm,
+ double fraction);
+ void (*message) (FrCommand *comm,
+ const char *msg);
+ void (*working_archive) (FrCommand *comm,
+ const char *filename);
+};
+
+GType fr_command_get_type (void);
+void fr_command_set_filename (FrCommand *comm,
+ const char *filename);
+void fr_command_set_multi_volume (FrCommand *comm,
+ const char *filename);
+void fr_command_list (FrCommand *comm);
+void fr_command_add (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *base_dir,
+ gboolean update,
+ gboolean recursive);
+void fr_command_delete (FrCommand *comm,
+ const char *from_file,
+ GList *file_list);
+void fr_command_extract (FrCommand *comm,
+ const char *from_file,
+ GList *file_list,
+ const char *dest_dir,
+ gboolean overwrite,
+ gboolean skip_older,
+ gboolean junk_paths);
+void fr_command_test (FrCommand *comm);
+void fr_command_uncompress (FrCommand *comm);
+void fr_command_recompress (FrCommand *comm);
+gboolean fr_command_is_capable_of (FrCommand *comm,
+ FrCommandCaps capabilities);
+const char ** fr_command_get_mime_types (FrCommand *comm);
+void fr_command_update_capabilities (FrCommand *comm);
+FrCommandCap fr_command_get_capabilities (FrCommand *comm,
+ const char *mime_type,
+ gboolean check_command);
+void fr_command_set_mime_type (FrCommand *comm,
+ const char *mime_type);
+gboolean fr_command_is_capable_of (FrCommand *comm,
+ FrCommandCaps capabilities);
+const char * fr_command_get_packages (FrCommand *comm,
+ const char *mime_type);
+
+/* protected functions */
+
+void fr_command_progress (FrCommand *comm,
+ double fraction);
+void fr_command_message (FrCommand *comm,
+ const char *msg);
+void fr_command_working_archive (FrCommand *comm,
+ const char *archive_name);
+void fr_command_set_n_files (FrCommand *comm,
+ int n_files);
+void fr_command_add_file (FrCommand *comm,
+ FileData *fdata);
+
+/* private functions */
+
+void fr_command_handle_error (FrCommand *comm,
+ FrProcError *error);
+
+#endif /* FR_COMMAND_H */
diff --git a/src/fr-error.c b/src/fr-error.c
new file mode 100644
index 0000000..ab48661
--- /dev/null
+++ b/src/fr-error.c
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+
+#include "fr-error.h"
+
+
+GQuark
+fr_error_quark (void)
+{
+ static GQuark quark;
+
+ if (!quark)
+ quark = g_quark_from_static_string ("file_roller_error");
+
+ return quark;
+}
diff --git a/src/fr-error.h b/src/fr-error.h
new file mode 100644
index 0000000..59399e8
--- /dev/null
+++ b/src/fr-error.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __FR_ERROR_H__
+#define __FR_ERROR_H__
+
+#include <glib.h>
+
+#define FR_ERROR fr_error_quark ()
+GQuark fr_error_quark (void);
+
+
+#endif /* __FR_ERROR_H__ */
diff --git a/src/fr-list-model.c b/src/fr-list-model.c
new file mode 100644
index 0000000..61b71d1
--- /dev/null
+++ b/src/fr-list-model.c
@@ -0,0 +1,188 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2005 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "eggtreemultidnd.h"
+#include "fr-list-model.h"
+#include "fr-window.h"
+
+
+static GtkListStoreClass *parent_class;
+
+
+static gboolean
+fr_list_model_multi_row_draggable (EggTreeMultiDragSource *drag_source,
+ GList *path_list)
+{
+ FrWindow *window;
+ GtkTreeModel *model;
+ GList *scan;
+
+ window = g_object_get_data (G_OBJECT (drag_source), "FrWindow");
+ g_return_val_if_fail (window != NULL, FALSE);
+
+ model = fr_window_get_list_store (window);
+
+ 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);
+
+ if (fdata != NULL)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+fr_list_model_multi_drag_data_get (EggTreeMultiDragSource *drag_source,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ GList *path_list)
+{
+ FrWindow *window;
+
+ window = g_object_get_data (G_OBJECT (drag_source), "FrWindow");
+ g_return_val_if_fail (window != NULL, FALSE);
+
+ return fr_window_file_list_drag_data_get (window,
+ context,
+ selection_data,
+ path_list);
+}
+
+
+static gboolean
+fr_list_model_multi_drag_data_delete (EggTreeMultiDragSource *drag_source,
+ GList *path_list)
+{
+ return TRUE;
+}
+
+
+static void
+fr_list_model_finalize (GObject *object)
+{
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+static void
+fr_list_model_init (FRListModel *model)
+{
+}
+
+
+static void
+fr_list_model_class_init (FRListModelClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = (GObjectClass *)klass;
+ parent_class = g_type_class_peek_parent (klass);
+
+ object_class->finalize = fr_list_model_finalize;
+}
+
+
+
+static void
+fr_list_model_multi_drag_source_init (EggTreeMultiDragSourceIface *iface)
+{
+ iface->row_draggable = fr_list_model_multi_row_draggable;
+ iface->drag_data_get = fr_list_model_multi_drag_data_get;
+ iface->drag_data_delete = fr_list_model_multi_drag_data_delete;
+}
+
+
+GType
+fr_list_model_get_type (void)
+{
+ static GType object_type = 0;
+
+ if (object_type == 0) {
+ static const GTypeInfo object_info = {
+ sizeof (FRListModelClass),
+ NULL, /* base_init */
+ NULL, /* base_finalize */
+ (GClassInitFunc) fr_list_model_class_init,
+ NULL, /* class_finalize */
+ NULL, /* class_data */
+ sizeof (FRListModel),
+ 0,
+ (GInstanceInitFunc) fr_list_model_init,
+ };
+
+ static const GInterfaceInfo multi_drag_source_info = {
+ (GInterfaceInitFunc) fr_list_model_multi_drag_source_init,
+ NULL,
+ NULL
+ };
+
+ object_type = g_type_register_static (GTK_TYPE_LIST_STORE, "FRListModel", &object_info, 0);
+ g_type_add_interface_static (object_type,
+ EGG_TYPE_TREE_MULTI_DRAG_SOURCE,
+ &multi_drag_source_info);
+ }
+
+ return object_type;
+}
+
+
+GtkListStore *
+fr_list_model_new (int n_columns, ...)
+{
+ GtkListStore *retval;
+ GType *types;
+ va_list args;
+ int i;
+
+ g_return_val_if_fail (n_columns > 0, NULL);
+
+ retval = g_object_new (FR_TYPE_LIST_MODEL, NULL);
+
+ va_start (args, n_columns);
+ types = g_new0 (GType, n_columns);
+ for (i = 0; i < n_columns; i++)
+ types[i] = va_arg (args, GType);
+ va_end (args);
+
+ gtk_list_store_set_column_types (retval, n_columns, types);
+ g_free (types);
+
+ return retval;
+}
diff --git a/src/fr-list-model.h b/src/fr-list-model.h
new file mode 100644
index 0000000..c59f9dd
--- /dev/null
+++ b/src/fr-list-model.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2005 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_LIST_MODEL_H
+#define FR_LIST_MODEL_H
+
+#include <gtk/gtk.h>
+
+#define FR_TYPE_LIST_MODEL (fr_list_model_get_type ())
+#define FR_LIST_MODEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_LIST_MODEL, FRListModel))
+#define FR_LIST_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_LIST_MODEL, FRListModelClass))
+#define FR_IS_LIST_MODEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_LIST_MODEL))
+#define FR_IS_LIST_MODEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_LIST_MODEL))
+#define FR_LIST_MODEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_LIST_MODEL, FRListModelClass))
+
+typedef struct FRListModel {
+ GtkListStore __parent;
+} FRListModel;
+
+typedef struct FRListModelClass {
+ GtkListStoreClass __parent_class;
+} FRListModelClass;
+
+GType fr_list_model_get_type (void);
+GtkListStore *fr_list_model_new (int n_columns, ...);
+
+#endif /* FR_LIST_MODEL_H */
diff --git a/src/fr-marshal.list b/src/fr-marshal.list
new file mode 100644
index 0000000..d3fcc1a
--- /dev/null
+++ b/src/fr-marshal.list
@@ -0,0 +1,8 @@
+VOID:INT
+VOID:INT,INT
+VOID:INT,POINTER
+VOID:POINTER
+VOID:VOID
+VOID:DOUBLE
+VOID:STRING
+VOID:BOOL
diff --git a/src/fr-process.c b/src/fr-process.c
new file mode 100644
index 0000000..4750f59
--- /dev/null
+++ b/src/fr-process.c
@@ -0,0 +1,1028 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <glib.h>
+#include "fr-process.h"
+#include "fr-marshal.h"
+#include "glib-utils.h"
+
+#define REFRESH_RATE 20
+#define BUFFER_SIZE 16384
+
+enum {
+ START,
+ DONE,
+ STICKY_ONLY,
+ LAST_SIGNAL
+};
+
+static GObjectClass *parent_class;
+static guint fr_process_signals[LAST_SIGNAL] = { 0 };
+
+static void fr_process_class_init (FrProcessClass *class);
+static void fr_process_init (FrProcess *process);
+static void fr_process_finalize (GObject *object);
+
+
+typedef struct {
+ GList *args; /* command to execute */
+ char *dir; /* working directory */
+ guint sticky : 1; /* whether the command must be
+ * executed even if a previous
+ * command has failed. */
+ guint ignore_error : 1; /* whether to continue to execute
+ * other commands if this command
+ * fails. */
+ ContinueFunc continue_func;
+ gpointer continue_data;
+ ProcFunc begin_func;
+ gpointer begin_data;
+ ProcFunc end_func;
+ gpointer end_data;
+} FrCommandInfo;
+
+
+static FrCommandInfo *
+fr_command_info_new (void)
+{
+ FrCommandInfo *info;
+
+ info = g_new0 (FrCommandInfo, 1);
+ info->args = NULL;
+ info->dir = NULL;
+ info->sticky = FALSE;
+ info->ignore_error = FALSE;
+
+ return info;
+}
+
+
+static void
+fr_command_info_free (FrCommandInfo *info)
+{
+ if (info == NULL)
+ return;
+
+ if (info->args != NULL) {
+ g_list_foreach (info->args, (GFunc) g_free, NULL);
+ g_list_free (info->args);
+ info->args = NULL;
+ }
+
+ if (info->dir != NULL) {
+ g_free (info->dir);
+ info->dir = NULL;
+ }
+
+ g_free (info);
+}
+
+
+static void
+fr_channel_data_init (FrChannelData *channel)
+{
+ channel->source = NULL;
+ channel->raw = NULL;
+ channel->status = G_IO_STATUS_NORMAL;
+ channel->error = NULL;
+}
+
+
+static void
+fr_channel_data_close_source (FrChannelData *channel)
+{
+ if (channel->source != NULL) {
+ g_io_channel_shutdown (channel->source, FALSE, NULL);
+ g_io_channel_unref (channel->source);
+ channel->source = NULL;
+ }
+}
+
+
+static GIOStatus
+fr_channel_data_read (FrChannelData *channel)
+{
+ char *line;
+ gsize length;
+ gsize terminator_pos;
+
+ channel->status = G_IO_STATUS_NORMAL;
+ g_clear_error (&channel->error);
+
+ while ((channel->status = g_io_channel_read_line (channel->source,
+ &line,
+ &length,
+ &terminator_pos,
+ &channel->error)) == G_IO_STATUS_NORMAL)
+ {
+ line[terminator_pos] = 0;
+ channel->raw = g_list_prepend (channel->raw, line);
+ if (channel->line_func != NULL)
+ (*channel->line_func) (line, channel->line_data);
+ }
+
+ return channel->status;
+}
+
+
+static GIOStatus
+fr_channel_data_flush (FrChannelData *channel)
+{
+ GIOStatus status;
+
+ while (((status = fr_channel_data_read (channel)) != G_IO_STATUS_ERROR) && (status != G_IO_STATUS_EOF))
+ /* void */;
+ fr_channel_data_close_source (channel);
+
+ return status;
+}
+
+
+static void
+fr_channel_data_reset (FrChannelData *channel)
+{
+ fr_channel_data_close_source (channel);
+
+ if (channel->raw != NULL) {
+ g_list_foreach (channel->raw, (GFunc) g_free, NULL);
+ g_list_free (channel->raw);
+ channel->raw = NULL;
+ }
+}
+
+
+static void
+fr_channel_data_free (FrChannelData *channel)
+{
+ fr_channel_data_reset (channel);
+}
+
+
+static void
+fr_channel_data_set_fd (FrChannelData *channel,
+ int fd,
+ const char *charset)
+{
+ fr_channel_data_reset (channel);
+
+ channel->source = g_io_channel_unix_new (fd);
+ g_io_channel_set_flags (channel->source, G_IO_FLAG_NONBLOCK, NULL);
+ g_io_channel_set_buffer_size (channel->source, BUFFER_SIZE);
+ if (charset != NULL)
+ g_io_channel_set_encoding (channel->source, charset, NULL);
+}
+
+
+const char *try_charsets[] = { "UTF-8", "ISO-8859-1", "WINDOW-1252" };
+int n_charsets = G_N_ELEMENTS (try_charsets);
+
+
+struct _FrProcessPrivate {
+ GPtrArray *comm; /* FrCommandInfo elements. */
+ gint n_comm; /* total number of commands */
+ gint current_comm; /* currenlty editing command. */
+
+ GPid command_pid;
+ guint check_timeout;
+
+ FrProcError first_error;
+
+ gboolean running;
+ gboolean stopping;
+ gint current_command;
+ gint error_command; /* command that coused an error. */
+
+ gboolean use_standard_locale;
+ gboolean sticky_only; /* whether to execute only sticky
+ * commands. */
+ int current_charset;
+};
+
+
+GType
+fr_process_get_type (void)
+{
+ static GType type = 0;
+
+ if (! type) {
+ GTypeInfo type_info = {
+ sizeof (FrProcessClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) fr_process_class_init,
+ NULL,
+ NULL,
+ sizeof (FrProcess),
+ 0,
+ (GInstanceInitFunc) fr_process_init
+ };
+
+ type = g_type_register_static (G_TYPE_OBJECT,
+ "FRProcess",
+ &type_info,
+ 0);
+ }
+
+ return type;
+}
+
+
+static void
+fr_process_class_init (FrProcessClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ fr_process_signals[START] =
+ g_signal_new ("start",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrProcessClass, start),
+ NULL, NULL,
+ fr_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ fr_process_signals[DONE] =
+ g_signal_new ("done",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrProcessClass, done),
+ NULL, NULL,
+ fr_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ fr_process_signals[STICKY_ONLY] =
+ g_signal_new ("sticky_only",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (FrProcessClass, sticky_only),
+ NULL, NULL,
+ fr_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ gobject_class->finalize = fr_process_finalize;
+
+ class->start = NULL;
+ class->done = NULL;
+}
+
+
+static void
+fr_process_init (FrProcess *process)
+{
+ process->priv = g_new0 (FrProcessPrivate, 1);
+
+ process->term_on_stop = TRUE;
+
+ process->priv->comm = g_ptr_array_new ();
+ process->priv->n_comm = -1;
+ process->priv->current_comm = -1;
+
+ process->priv->command_pid = 0;
+ fr_channel_data_init (&process->out);
+ fr_channel_data_init (&process->err);
+
+ process->error.gerror = NULL;
+ process->priv->first_error.gerror = NULL;
+
+ process->priv->check_timeout = 0;
+ process->priv->running = FALSE;
+ process->priv->stopping = FALSE;
+ process->restart = FALSE;
+
+ process->priv->current_charset = -1;
+
+ process->priv->use_standard_locale = FALSE;
+}
+
+
+FrProcess *
+fr_process_new (void)
+{
+ return FR_PROCESS (g_object_new (FR_TYPE_PROCESS, NULL));
+}
+
+
+static void fr_process_stop_priv (FrProcess *process, gboolean emit_signal);
+
+
+static void
+fr_process_finalize (GObject *object)
+{
+ FrProcess *process;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (FR_IS_PROCESS (object));
+
+ process = FR_PROCESS (object);
+
+ fr_process_stop_priv (process, FALSE);
+ fr_process_clear (process);
+
+ g_ptr_array_free (process->priv->comm, FALSE);
+
+ fr_channel_data_free (&process->out);
+ fr_channel_data_free (&process->err);
+
+ g_clear_error (&process->error.gerror);
+ g_clear_error (&process->priv->first_error.gerror);
+
+ g_free (process->priv);
+
+ /* Chain up */
+
+ if (G_OBJECT_CLASS (parent_class)->finalize)
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+
+void
+fr_process_begin_command (FrProcess *process,
+ const char *arg)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ info = fr_command_info_new ();
+ info->args = g_list_prepend (NULL, g_strdup (arg));
+
+ g_ptr_array_add (process->priv->comm, info);
+
+ process->priv->n_comm++;
+ process->priv->current_comm = process->priv->n_comm;
+}
+
+
+void
+fr_process_begin_command_at (FrProcess *process,
+ const char *arg,
+ int index)
+{
+ FrCommandInfo *info, *old_c_info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (index >= 0 && index <= process->priv->n_comm);
+
+ process->priv->current_comm = index;
+
+ old_c_info = g_ptr_array_index (process->priv->comm, index);
+
+ if (old_c_info != NULL)
+ fr_command_info_free (old_c_info);
+
+ info = fr_command_info_new ();
+ info->args = g_list_prepend (NULL, g_strdup (arg));
+
+ g_ptr_array_index (process->priv->comm, index) = info;
+}
+
+
+void
+fr_process_set_working_dir (FrProcess *process,
+ const char *dir)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (process->priv->current_comm >= 0);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ if (info->dir != NULL)
+ g_free (info->dir);
+ info->dir = g_strdup (dir);
+}
+
+
+void
+fr_process_set_sticky (FrProcess *process,
+ gboolean sticky)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (process->priv->current_comm >= 0);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->sticky = sticky;
+}
+
+
+void
+fr_process_set_ignore_error (FrProcess *process,
+ gboolean ignore_error)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (process->priv->current_comm >= 0);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->ignore_error = ignore_error;
+}
+
+
+void
+fr_process_add_arg (FrProcess *process,
+ const char *arg)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+ g_return_if_fail (process->priv->current_comm >= 0);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->args = g_list_prepend (info->args, g_strdup (arg));
+}
+
+
+void
+fr_process_add_arg_concat (FrProcess *process,
+ const char *arg1,
+ ...)
+{
+ GString *arg;
+ va_list args;
+ char *s;
+
+ arg = g_string_new (arg1);
+
+ va_start (args, arg1);
+ while ((s = va_arg (args, char*)) != NULL)
+ g_string_append (arg, s);
+ va_end (args);
+
+ fr_process_add_arg (process, arg->str);
+ g_string_free (arg, TRUE);
+}
+
+
+void
+fr_process_add_arg_printf (FrProcess *fr_proc,
+ const char *format,
+ ...)
+{
+ va_list args;
+ char *arg;
+
+ va_start (args, format);
+ arg = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ fr_process_add_arg (fr_proc, arg);
+
+ g_free (arg);
+}
+
+
+void
+fr_process_set_arg_at (FrProcess *process,
+ int n_comm,
+ int n_arg,
+ const char *arg_value)
+{
+ FrCommandInfo *info;
+ GList *arg;
+
+ g_return_if_fail (process != NULL);
+
+ info = g_ptr_array_index (process->priv->comm, n_comm);
+ arg = g_list_nth (info->args, n_arg);
+ g_return_if_fail (arg != NULL);
+
+ g_free (arg->data);
+ arg->data = g_strdup (arg_value);
+}
+
+
+void
+fr_process_set_begin_func (FrProcess *process,
+ ProcFunc func,
+ gpointer func_data)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->begin_func = func;
+ info->begin_data = func_data;
+}
+
+
+void
+fr_process_set_end_func (FrProcess *process,
+ ProcFunc func,
+ gpointer func_data)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->end_func = func;
+ info->end_data = func_data;
+}
+
+
+void
+fr_process_set_continue_func (FrProcess *process,
+ ContinueFunc func,
+ gpointer func_data)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ if (process->priv->current_comm < 0)
+ return;
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->continue_func = func;
+ info->continue_data = func_data;
+}
+
+
+void
+fr_process_end_command (FrProcess *process)
+{
+ FrCommandInfo *info;
+
+ g_return_if_fail (process != NULL);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_comm);
+ info->args = g_list_reverse (info->args);
+}
+
+
+void
+fr_process_clear (FrProcess *process)
+{
+ gint i;
+
+ g_return_if_fail (process != NULL);
+
+ for (i = 0; i <= process->priv->n_comm; i++) {
+ FrCommandInfo *info;
+
+ info = g_ptr_array_index (process->priv->comm, i);
+ fr_command_info_free (info);
+ g_ptr_array_index (process->priv->comm, i) = NULL;
+ }
+
+ for (i = 0; i <= process->priv->n_comm; i++)
+ g_ptr_array_remove_index_fast (process->priv->comm, 0);
+
+ process->priv->n_comm = -1;
+ process->priv->current_comm = -1;
+}
+
+
+void
+fr_process_set_out_line_func (FrProcess *process,
+ LineFunc func,
+ gpointer data)
+{
+ g_return_if_fail (process != NULL);
+
+ process->out.line_func = func;
+ process->out.line_data = data;
+}
+
+
+void
+fr_process_set_err_line_func (FrProcess *process,
+ LineFunc func,
+ gpointer data)
+{
+ g_return_if_fail (process != NULL);
+
+ process->err.line_func = func;
+ process->err.line_data = data;
+}
+
+
+static gboolean check_child (gpointer data);
+
+
+static void
+child_setup (gpointer user_data)
+{
+ FrProcess *process = user_data;
+
+ if (process->priv->use_standard_locale)
+ putenv ("LC_MESSAGES=C");
+
+ /* detach from the tty */
+
+ setsid ();
+}
+
+
+static const char *
+fr_process_get_charset (FrProcess *process)
+{
+ const char *charset = NULL;
+
+ if (process->priv->current_charset >= 0)
+ charset = try_charsets[process->priv->current_charset];
+ else if (g_get_charset (&charset))
+ charset = NULL;
+
+ return charset;
+}
+
+
+static void
+start_current_command (FrProcess *process)
+{
+ FrCommandInfo *info;
+ GList *scan;
+ char **argv;
+ int out_fd, err_fd;
+ int i = 0;
+
+ debug (DEBUG_INFO, "%d/%d) ", process->priv->current_command, process->priv->n_comm);
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_command);
+
+ argv = g_new (char *, g_list_length (info->args) + 1);
+ for (scan = info->args; scan; scan = scan->next)
+ argv[i++] = scan->data;
+ argv[i] = NULL;
+
+#ifdef DEBUG
+ {
+ int j;
+
+ if (process->priv->use_standard_locale)
+ g_print ("\tLC_MESSAGES=C\n");
+
+ if (info->dir != NULL)
+ g_print ("\tcd %s\n", info->dir);
+
+ g_print ("\t");
+ for (j = 0; j < i; j++)
+ g_print ("%s ", argv[j]);
+ g_print ("\n");
+ }
+#endif
+
+ if (info->begin_func != NULL)
+ (*info->begin_func) (info->begin_data);
+
+ if (! g_spawn_async_with_pipes (info->dir,
+ argv,
+ NULL,
+ (G_SPAWN_LEAVE_DESCRIPTORS_OPEN
+ | G_SPAWN_SEARCH_PATH
+ | G_SPAWN_DO_NOT_REAP_CHILD),
+ child_setup,
+ process,
+ &process->priv->command_pid,
+ NULL,
+ &out_fd,
+ &err_fd,
+ &process->error.gerror))
+ {
+ process->error.type = FR_PROC_ERROR_SPAWN;
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[DONE],
+ 0);
+ g_free (argv);
+ return;
+ }
+
+ g_free (argv);
+
+ fr_channel_data_set_fd (&process->out, out_fd, fr_process_get_charset (process));
+ fr_channel_data_set_fd (&process->err, err_fd, fr_process_get_charset (process));
+
+ process->priv->check_timeout = g_timeout_add (REFRESH_RATE,
+ check_child,
+ process);
+}
+
+
+static gboolean
+command_is_sticky (FrProcess *process,
+ int i)
+{
+ FrCommandInfo *info;
+
+ info = g_ptr_array_index (process->priv->comm, i);
+ return info->sticky;
+}
+
+
+static void
+allow_sticky_processes_only (FrProcess *process,
+ gboolean emit_signal)
+{
+ if (! process->priv->sticky_only) {
+ /* Remember the first error. */
+ process->priv->error_command = process->priv->current_command;
+ process->priv->first_error.type = process->error.type;
+ process->priv->first_error.status = process->error.status;
+ g_clear_error (&process->priv->first_error.gerror);
+ if (process->error.gerror != NULL)
+ process->priv->first_error.gerror = g_error_copy (process->error.gerror);
+ }
+
+ process->priv->sticky_only = TRUE;
+ if (emit_signal)
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[STICKY_ONLY],
+ 0);
+}
+
+
+static void
+fr_process_set_error (FrProcess *process,
+ FrProcErrorType type,
+ int status,
+ GError *gerror)
+{
+ process->error.type = type;
+ process->error.status = status;
+ if (gerror != process->error.gerror) {
+ g_clear_error (&process->error.gerror);
+ if (gerror != NULL)
+ process->error.gerror = g_error_copy (gerror);
+ }
+}
+
+
+static gint
+check_child (gpointer data)
+{
+ FrProcess *process = data;
+ FrCommandInfo *info;
+ pid_t pid;
+ int status;
+ gboolean continue_process;
+ gboolean channel_error = FALSE;
+
+ info = g_ptr_array_index (process->priv->comm, process->priv->current_command);
+
+ /* Remove check. */
+
+ g_source_remove (process->priv->check_timeout);
+ process->priv->check_timeout = 0;
+
+ if (fr_channel_data_read (&process->out) == G_IO_STATUS_ERROR) {
+ fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->out.error);
+ channel_error = TRUE;
+ }
+ else if (fr_channel_data_read (&process->err) == G_IO_STATUS_ERROR) {
+ fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->err.error);
+ channel_error = TRUE;
+ }
+ else {
+ pid = waitpid (process->priv->command_pid, &status, WNOHANG);
+ if (pid != process->priv->command_pid) {
+ /* Add check again. */
+ process->priv->check_timeout = g_timeout_add (REFRESH_RATE,
+ check_child,
+ process);
+ return FALSE;
+ }
+ }
+
+ if (info->ignore_error) {
+ process->error.type = FR_PROC_ERROR_NONE;
+ debug (DEBUG_INFO, "[ignore error]\n");
+ }
+ else if (! channel_error && (process->error.type != FR_PROC_ERROR_STOPPED)) {
+ if (WIFEXITED (status)) {
+ if (WEXITSTATUS (status) == 0)
+ process->error.type = FR_PROC_ERROR_NONE;
+ else if (WEXITSTATUS (status) == 255)
+ process->error.type = FR_PROC_ERROR_COMMAND_NOT_FOUND;
+ else {
+ process->error.type = FR_PROC_ERROR_COMMAND_ERROR;
+ process->error.status = WEXITSTATUS (status);
+ }
+ }
+ else {
+ process->error.type = FR_PROC_ERROR_EXITED_ABNORMALLY;
+ process->error.status = 255;
+ }
+ }
+
+ process->priv->command_pid = 0;
+
+ if (fr_channel_data_flush (&process->out) == G_IO_STATUS_ERROR) {
+ fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->out.error);
+ channel_error = TRUE;
+ }
+ else if (fr_channel_data_flush (&process->err) == G_IO_STATUS_ERROR) {
+ fr_process_set_error (process, FR_PROC_ERROR_IO_CHANNEL, 0, process->err.error);
+ channel_error = TRUE;
+ }
+
+ if (info->end_func != NULL)
+ (*info->end_func) (info->end_data);
+
+ /**/
+
+ if (channel_error
+ && (process->error.type == FR_PROC_ERROR_IO_CHANNEL)
+ && g_error_matches (process->error.gerror, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
+ {
+ if (process->priv->current_charset < n_charsets - 1) {
+ /* try with another charset */
+ process->priv->current_charset++;
+ process->priv->running = FALSE;
+ process->restart = TRUE;
+ fr_process_start (process);
+ return FALSE;
+ }
+ /*fr_process_set_error (process, FR_PROC_ERROR_NONE, 0, NULL);*/
+ fr_process_set_error (process, FR_PROC_ERROR_BAD_CHARSET, 0, process->error.gerror);
+ }
+
+ /* Check whether to continue or stop the process */
+
+ continue_process = TRUE;
+ if (info->continue_func != NULL)
+ continue_process = (*info->continue_func) (info->continue_data);
+
+ /* Execute next command. */
+ if (continue_process) {
+ if (process->error.type != FR_PROC_ERROR_NONE) {
+ allow_sticky_processes_only (process, TRUE);
+#ifdef DEBUG
+ {
+ GList *scan;
+
+ g_print ("** ERROR **\n");
+ for (scan = process->err.raw; scan; scan = scan->next)
+ g_print ("%s\n", (char *)scan->data);
+ }
+#endif
+ }
+
+ if (process->priv->sticky_only) {
+ do {
+ process->priv->current_command++;
+ } while ((process->priv->current_command <= process->priv->n_comm)
+ && ! command_is_sticky (process, process->priv->current_command));
+ }
+ else
+ process->priv->current_command++;
+
+ if (process->priv->current_command <= process->priv->n_comm) {
+ start_current_command (process);
+ return FALSE;
+ }
+ }
+
+ /* Done */
+
+ process->priv->current_command = -1;
+ process->priv->use_standard_locale = FALSE;
+
+ if (process->out.raw != NULL)
+ process->out.raw = g_list_reverse (process->out.raw);
+ if (process->err.raw != NULL)
+ process->err.raw = g_list_reverse (process->err.raw);
+
+ process->priv->running = FALSE;
+ process->priv->stopping = FALSE;
+
+ if (process->priv->sticky_only) {
+ /* Restore the first error. */
+ fr_process_set_error (process,
+ process->priv->first_error.type,
+ process->priv->first_error.status,
+ process->priv->first_error.gerror);
+ }
+
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[DONE],
+ 0);
+
+ return FALSE;
+}
+
+
+void
+fr_process_use_standard_locale (FrProcess *process,
+ gboolean use_stand_locale)
+{
+ g_return_if_fail (process != NULL);
+ process->priv->use_standard_locale = use_stand_locale;
+}
+
+
+void
+fr_process_start (FrProcess *process)
+{
+ g_return_if_fail (process != NULL);
+
+ if (process->priv->running)
+ return;
+
+ fr_channel_data_reset (&process->out);
+ fr_channel_data_reset (&process->err);
+
+ process->priv->sticky_only = FALSE;
+ process->priv->current_command = 0;
+ fr_process_set_error (process, FR_PROC_ERROR_NONE, 0, NULL);
+
+ if (! process->restart) {
+ process->priv->current_charset = -1;
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[START],
+ 0);
+ }
+
+ process->priv->stopping = FALSE;
+
+ if (process->priv->n_comm == -1) {
+ process->priv->running = FALSE;
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[DONE],
+ 0);
+ }
+ else {
+ process->priv->running = TRUE;
+ start_current_command (process);
+ }
+}
+
+
+static void
+fr_process_stop_priv (FrProcess *process,
+ gboolean emit_signal)
+{
+ g_return_if_fail (process != NULL);
+
+ if (! process->priv->running)
+ return;
+
+ if (process->priv->stopping)
+ return;
+
+ process->priv->stopping = TRUE;
+ process->error.type = FR_PROC_ERROR_STOPPED;
+
+ if (command_is_sticky (process, process->priv->current_command))
+ allow_sticky_processes_only (process, emit_signal);
+
+ else if (process->term_on_stop && (process->priv->command_pid > 0))
+ kill (process->priv->command_pid, SIGTERM);
+
+ else {
+ if (process->priv->check_timeout != 0) {
+ g_source_remove (process->priv->check_timeout);
+ process->priv->check_timeout = 0;
+ }
+
+ process->priv->command_pid = 0;
+ fr_channel_data_close_source (&process->out);
+ fr_channel_data_close_source (&process->err);
+
+ process->priv->running = FALSE;
+
+ if (emit_signal)
+ g_signal_emit (G_OBJECT (process),
+ fr_process_signals[DONE],
+ 0);
+ }
+}
+
+
+void
+fr_process_stop (FrProcess *process)
+{
+ fr_process_stop_priv (process, TRUE);
+}
diff --git a/src/fr-process.h b/src/fr-process.h
new file mode 100644
index 0000000..d519b03
--- /dev/null
+++ b/src/fr-process.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003, 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_PROCESS_H
+#define FR_PROCESS_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <sys/types.h>
+#include "typedefs.h"
+
+#define FR_TYPE_PROCESS (fr_process_get_type ())
+#define FR_PROCESS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_PROCESS, FrProcess))
+#define FR_PROCESS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_TYPE_PROCESS, FrProcessClass))
+#define FR_IS_PROCESS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_PROCESS))
+#define FR_IS_PROCESS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_PROCESS))
+#define FR_PROCESS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_PROCESS, FrProcessClass))
+
+typedef struct _FrProcess FrProcess;
+typedef struct _FrProcessClass FrProcessClass;
+typedef struct _FrProcessPrivate FrProcessPrivate;
+
+typedef void (*ProcFunc) (gpointer data);
+typedef gboolean (*ContinueFunc) (gpointer data);
+typedef void (*LineFunc) (char *line, gpointer data);
+
+typedef struct {
+ GIOChannel *source;
+ GList *raw;
+ LineFunc line_func;
+ gpointer line_data;
+ GIOStatus status;
+ GError *error;
+} FrChannelData;
+
+struct _FrProcess {
+ GObject __parent;
+
+ /*< public >*/
+
+ gboolean term_on_stop; /* whether we must terminate the
+ * command when calling
+ * fr_process_stop. */
+
+ /*< public read-only >*/
+
+ FrChannelData out;
+ FrChannelData err;
+ FrProcError error;
+
+ /*< protected >*/
+
+ gboolean restart; /* whether to restart the process
+ * after an error. */
+
+ FrProcessPrivate *priv;
+};
+
+struct _FrProcessClass {
+ GObjectClass __parent_class;
+
+ /* -- Signals -- */
+
+ void (* start) (FrProcess *fr_proc);
+ void (* done) (FrProcess *fr_proc);
+ void (* sticky_only) (FrProcess *fr_proc);
+};
+
+GType fr_process_get_type (void);
+FrProcess * fr_process_new (void);
+void fr_process_clear (FrProcess *fr_proc);
+void fr_process_begin_command (FrProcess *fr_proc,
+ const char *arg);
+void fr_process_begin_command_at (FrProcess *fr_proc,
+ const char *arg,
+ int index);
+void fr_process_add_arg (FrProcess *fr_proc,
+ const char *arg);
+void fr_process_add_arg_concat (FrProcess *fr_proc,
+ const char *arg,
+ ...) G_GNUC_NULL_TERMINATED;
+void fr_process_add_arg_printf (FrProcess *fr_proc,
+ const char *format,
+ ...) G_GNUC_PRINTF (2, 3);
+void fr_process_set_arg_at (FrProcess *fr_proc,
+ int n_comm,
+ int n_arg,
+ const char *arg);
+void fr_process_set_begin_func (FrProcess *fr_proc,
+ ProcFunc func,
+ gpointer func_data);
+void fr_process_set_end_func (FrProcess *fr_proc,
+ ProcFunc func,
+ gpointer func_data);
+void fr_process_set_continue_func (FrProcess *fr_proc,
+ ContinueFunc func,
+ gpointer func_data);
+void fr_process_end_command (FrProcess *fr_proc);
+void fr_process_set_working_dir (FrProcess *fr_proc,
+ const char *arg);
+void fr_process_set_sticky (FrProcess *fr_proc,
+ gboolean sticky);
+void fr_process_set_ignore_error (FrProcess *fr_proc,
+ gboolean ignore_error);
+void fr_process_use_standard_locale (FrProcess *fr_proc,
+ gboolean use_stand_locale);
+void fr_process_set_out_line_func (FrProcess *fr_proc,
+ LineFunc func,
+ gpointer func_data);
+void fr_process_set_err_line_func (FrProcess *fr_proc,
+ LineFunc func,
+ gpointer func_data);
+void fr_process_start (FrProcess *fr_proc);
+void fr_process_stop (FrProcess *fr_proc);
+
+#endif /* FR_PROCESS_H */
diff --git a/src/fr-stock.c b/src/fr-stock.c
new file mode 100644
index 0000000..eecd5d7
--- /dev/null
+++ b/src/fr-stock.c
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File Roller
+ *
+ * Copyright (C) 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include "fr-stock.h"
+
+
+static gboolean stock_initialized = FALSE;
+
+static const struct {
+ char *stock_id;
+ char *icon;
+} stock_icons [] = {
+ { FR_STOCK_CREATE_ARCHIVE, "add-files-to-archive" },
+ { FR_STOCK_ADD_FILES, "add-files-to-archive" },
+ { FR_STOCK_ADD_FOLDER, "add-folder-to-archive" },
+ { FR_STOCK_EXTRACT, "extract-archive" }
+};
+
+static const GtkStockItem stock_items [] = {
+ { FR_STOCK_CREATE_ARCHIVE, N_("C_reate"), 0, 0, GETTEXT_PACKAGE },
+ { FR_STOCK_ADD_FILES, N_("_Add"), 0, 0, GETTEXT_PACKAGE },
+ { FR_STOCK_ADD_FOLDER, N_("_Add"), 0, 0, GETTEXT_PACKAGE },
+ { FR_STOCK_EXTRACT, N_("_Extract"), 0, 0, GETTEXT_PACKAGE }
+};
+
+void
+fr_stock_init (void)
+{
+ GtkIconFactory *factory;
+ GtkIconSource *source;
+ int i;
+
+ if (stock_initialized)
+ return;
+ stock_initialized = TRUE;
+
+ gtk_stock_add_static (stock_items, G_N_ELEMENTS (stock_items));
+
+ factory = gtk_icon_factory_new ();
+ gtk_icon_factory_add_default (factory);
+
+ source = gtk_icon_source_new ();
+
+ for (i = 0; i < G_N_ELEMENTS (stock_icons); i++) {
+ GtkIconSet *set;
+
+ gtk_icon_source_set_icon_name (source, stock_icons [i].icon);
+
+ set = gtk_icon_set_new ();
+ gtk_icon_set_add_source (set, source);
+
+ gtk_icon_factory_add (factory, stock_icons [i].stock_id, set);
+ gtk_icon_set_unref (set);
+ }
+
+ gtk_icon_source_free (source);
+
+ g_object_unref (factory);
+}
diff --git a/src/fr-stock.h b/src/fr-stock.h
new file mode 100644
index 0000000..8d43bcd
--- /dev/null
+++ b/src/fr-stock.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File Roller
+ *
+ * Copyright (C) 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_STOCK_H
+#define FR_STOCK_H
+
+#define FR_STOCK_CREATE_ARCHIVE "create-archive"
+#define FR_STOCK_ADD_FILES "add-files-to-archive"
+#define FR_STOCK_ADD_FOLDER "add-folder-to-archive"
+#define FR_STOCK_EXTRACT "extract-archive"
+
+void fr_stock_init (void);
+
+#endif /* FR_STOCK_H */
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);
+}
diff --git a/src/fr-window.h b/src/fr-window.h
new file mode 100644
index 0000000..adde1bf
--- /dev/null
+++ b/src/fr-window.h
@@ -0,0 +1,315 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef FR_WINDOW_H
+#define FR_WINDOW_H
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include "typedefs.h"
+#include "fr-archive.h"
+
+#define MATECONF_NOTIFICATIONS 10
+
+enum {
+ COLUMN_FILE_DATA,
+ COLUMN_ICON,
+ COLUMN_NAME,
+ COLUMN_EMBLEM,
+ COLUMN_SIZE,
+ COLUMN_TYPE,
+ COLUMN_TIME,
+ COLUMN_PATH,
+ NUMBER_OF_COLUMNS
+};
+
+enum {
+ TREE_COLUMN_PATH,
+ TREE_COLUMN_ICON,
+ TREE_COLUMN_NAME,
+ TREE_COLUMN_WEIGHT,
+ TREE_NUMBER_OF_COLUMNS
+};
+
+typedef enum {
+ FR_BATCH_ACTION_NONE,
+ FR_BATCH_ACTION_LOAD,
+ FR_BATCH_ACTION_OPEN,
+ FR_BATCH_ACTION_ADD,
+ FR_BATCH_ACTION_EXTRACT,
+ FR_BATCH_ACTION_EXTRACT_HERE,
+ FR_BATCH_ACTION_EXTRACT_INTERACT,
+ FR_BATCH_ACTION_RENAME,
+ FR_BATCH_ACTION_PASTE,
+ FR_BATCH_ACTION_OPEN_FILES,
+ FR_BATCH_ACTION_SAVE_AS,
+ FR_BATCH_ACTION_TEST,
+ FR_BATCH_ACTION_CLOSE,
+ FR_BATCH_ACTION_QUIT,
+ FR_BATCH_ACTIONS
+} FrBatchActionType;
+
+/* -- FrWindow -- */
+
+#define FR_TYPE_WINDOW (fr_window_get_type ())
+#define FR_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FR_TYPE_WINDOW, FrWindow))
+#define FR_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FR_WINDOW_TYPE, FrWindowClass))
+#define FR_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FR_TYPE_WINDOW))
+#define FR_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FR_TYPE_WINDOW))
+#define FR_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), FR_TYPE_WINDOW, FrWindowClass))
+
+typedef struct _FrWindow FrWindow;
+typedef struct _FrWindowClass FrWindowClass;
+typedef struct _FrWindowPrivateData FrWindowPrivateData;
+
+struct _FrWindow
+{
+ GtkWindow __parent;
+ FrArchive *archive;
+ FrWindowPrivateData *priv;
+};
+
+struct _FrWindowClass
+{
+ GtkWindowClass __parent_class;
+
+ /*<signals>*/
+
+ void (*archive_loaded) (FrWindow *window,
+ gboolean success);
+};
+
+GType fr_window_get_type (void);
+GtkWidget * fr_window_new (void);
+void fr_window_close (FrWindow *window);
+
+/* archive operations */
+
+gboolean fr_window_archive_new (FrWindow *window,
+ const char *uri);
+FrWindow * fr_window_archive_open (FrWindow *window,
+ const char *uri,
+ GtkWindow *parent);
+void fr_window_archive_close (FrWindow *window);
+const char *fr_window_get_archive_uri (FrWindow *window);
+const char *fr_window_get_paste_archive_uri (FrWindow *window);
+gboolean fr_window_archive_is_present (FrWindow *window);
+void fr_window_archive_save_as (FrWindow *window,
+ const char *filename,
+ const char *password,
+ gboolean encrypt_header,
+ guint volume_size);
+void fr_window_archive_reload (FrWindow *window);
+void fr_window_archive_rename (FrWindow *window,
+ const char *filename);
+void fr_window_archive_add_files (FrWindow *window,
+ GList *file_list, /* GFile list */
+ gboolean update);
+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);
+void fr_window_archive_add_directory (FrWindow *window,
+ const char *directory,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update);
+void fr_window_archive_add_items (FrWindow *window,
+ GList *dir_list,
+ const char *base_dir,
+ const char *dest_dir,
+ gboolean update);
+void fr_window_archive_add_dropped_items (FrWindow *window,
+ GList *item_list,
+ gboolean update);
+void fr_window_archive_remove (FrWindow *window,
+ GList *file_list);
+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);
+void fr_window_archive_extract_here (FrWindow *window,
+ gboolean skip_older,
+ gboolean overwrite,
+ gboolean junk_paths);
+void fr_window_archive_test (FrWindow *window);
+
+/**/
+
+void fr_window_set_password (FrWindow *window,
+ const char *password);
+void fr_window_set_password_for_paste (FrWindow *window,
+ const char *password);
+const char *fr_window_get_password (FrWindow *window);
+void fr_window_set_encrypt_header (FrWindow *window,
+ gboolean encrypt_header);
+gboolean fr_window_get_encrypt_header (FrWindow *window);
+void fr_window_set_compression (FrWindow *window,
+ FrCompression compression);
+FrCompression fr_window_get_compression (FrWindow *window);
+void fr_window_set_volume_size (FrWindow *window,
+ guint volume_size);
+guint fr_window_get_volume_size (FrWindow *window);
+
+/**/
+
+void fr_window_go_to_location (FrWindow *window,
+ const char *path,
+ gboolean force_update);
+const char*fr_window_get_current_location (FrWindow *window);
+void fr_window_go_up_one_level (FrWindow *window);
+void fr_window_go_back (FrWindow *window);
+void fr_window_go_forward (FrWindow *window);
+void fr_window_current_folder_activated (FrWindow *window,
+ gboolean from_sidebar);
+void fr_window_set_list_mode (FrWindow *window,
+ FrWindowListMode list_mode);
+
+/**/
+
+void fr_window_update_list_order (FrWindow *window);
+GList * fr_window_get_file_list_selection (FrWindow *window,
+ gboolean recursive,
+ gboolean *has_dirs);
+GList * fr_window_get_file_list_from_path_list (FrWindow *window,
+ GList *path_list,
+ gboolean *has_dirs);
+GList * fr_window_get_file_list_pattern (FrWindow *window,
+ const char *pattern);
+int fr_window_get_n_selected_files (FrWindow *window);
+GList * fr_window_get_folder_tree_selection (FrWindow *window,
+ gboolean recursive,
+ gboolean *has_dirs);
+GList * fr_window_get_selection (FrWindow *window,
+ gboolean from_sidebar,
+ char **return_base_dir);
+GtkTreeModel *
+ fr_window_get_list_store (FrWindow *window);
+void fr_window_find (FrWindow *window);
+void fr_window_select_all (FrWindow *window);
+void fr_window_unselect_all (FrWindow *window);
+void fr_window_set_sort_type (FrWindow *window,
+ GtkSortType sort_type);
+
+/**/
+
+void fr_window_rename_selection (FrWindow *window,
+ gboolean from_sidebar);
+void fr_window_cut_selection (FrWindow *window,
+ gboolean from_sidebar);
+void fr_window_copy_selection (FrWindow *window,
+ gboolean from_sidebar);
+void fr_window_paste_selection (FrWindow *window,
+ gboolean from_sidebar);
+
+/**/
+
+void fr_window_stop (FrWindow *window);
+void fr_window_start_activity_mode (FrWindow *window);
+void fr_window_stop_activity_mode (FrWindow *window);
+
+/**/
+
+void fr_window_view_last_output (FrWindow *window,
+ const char *title);
+
+void fr_window_open_files (FrWindow *window,
+ GList *file_list,
+ gboolean ask_application);
+void fr_window_open_files_with_command (FrWindow *window,
+ GList *file_list,
+ char *command);
+void fr_window_open_files_with_application (FrWindow *window,
+ GList *file_list,
+ GAppInfo *app);
+gboolean fr_window_update_files (FrWindow *window,
+ GList *file_list);
+void fr_window_update_columns_visibility (FrWindow *window);
+void fr_window_update_history_list (FrWindow *window);
+void fr_window_set_default_dir (FrWindow *window,
+ const char *default_dir,
+ gboolean freeze);
+void fr_window_set_open_default_dir (FrWindow *window,
+ const char *default_dir);
+const char *fr_window_get_open_default_dir (FrWindow *window);
+void fr_window_set_add_default_dir (FrWindow *window,
+ const char *default_dir);
+const char *fr_window_get_add_default_dir (FrWindow *window);
+void fr_window_set_extract_default_dir (FrWindow *window,
+ const char *default_dir,
+ gboolean freeze);
+const char *fr_window_get_extract_default_dir (FrWindow *window);
+void fr_window_push_message (FrWindow *window,
+ const char *msg);
+void fr_window_pop_message (FrWindow *window);
+void fr_window_set_toolbar_visibility (FrWindow *window,
+ gboolean value);
+void fr_window_set_statusbar_visibility (FrWindow *window,
+ gboolean value);
+void fr_window_set_folders_visibility (FrWindow *window,
+ gboolean value);
+
+/* batch mode procedures. */
+
+void fr_window_new_batch (FrWindow *window);
+void fr_window_set_current_batch_action (FrWindow *window,
+ FrBatchActionType action,
+ void *data,
+ GFreeFunc free_func);
+void fr_window_reset_current_batch_action (FrWindow *window);
+void fr_window_restart_current_batch_action (FrWindow *window);
+void fr_window_append_batch_action (FrWindow *window,
+ FrBatchActionType action,
+ void *data,
+ GFreeFunc free_func);
+void fr_window_start_batch (FrWindow *window);
+void fr_window_stop_batch (FrWindow *window);
+void fr_window_resume_batch (FrWindow *window);
+gboolean fr_window_is_batch_mode (FrWindow *window);
+void fr_window_set_batch__extract (FrWindow *window,
+ const char *filename,
+ const char *dest_dir);
+void fr_window_set_batch__extract_here (FrWindow *window,
+ const char *filename);
+void fr_window_set_batch__add (FrWindow *window,
+ const char *archive,
+ GList *file_list);
+void fr_window_destroy_with_error_dialog (FrWindow *window);
+
+/**/
+
+gboolean fr_window_file_list_drag_data_get (FrWindow *window,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ GList *path_list);
+
+void fr_window_update_dialog_closed (FrWindow *window);
+
+#endif /* FR_WINDOW_H */
diff --git a/src/gio-utils.c b/src/gio-utils.c
new file mode 100644
index 0000000..1123865
--- /dev/null
+++ b/src/gio-utils.c
@@ -0,0 +1,1497 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include "glib-utils.h"
+#include "file-utils.h"
+#include "gio-utils.h"
+
+
+#define N_FILES_PER_REQUEST 128
+
+
+/* -- filter -- */
+
+
+typedef enum {
+ FILTER_DEFAULT = 0,
+ FILTER_NODOTFILES = 1 << 1,
+ FILTER_IGNORECASE = 1 << 2,
+ FILTER_NOBACKUPFILES = 1 << 3
+} FilterOptions;
+
+
+typedef struct {
+ char *pattern;
+ FilterOptions options;
+ GRegex **regexps;
+} Filter;
+
+
+static Filter *
+filter_new (const char *pattern,
+ FilterOptions options)
+{
+ Filter *filter;
+ GRegexCompileFlags flags;
+
+ filter = g_new0 (Filter, 1);
+
+ if ((pattern != NULL) && (strcmp (pattern, "*") != 0))
+ filter->pattern = g_strdup (pattern);
+
+ filter->options = options;
+ if (filter->options & FILTER_IGNORECASE)
+ flags = G_REGEX_CASELESS;
+ else
+ flags = 0;
+ filter->regexps = search_util_get_regexps (pattern, flags);
+
+ return filter;
+}
+
+
+static void
+filter_destroy (Filter *filter)
+{
+ if (filter == NULL)
+ return;
+
+ g_free (filter->pattern);
+ if (filter->regexps != NULL)
+ free_regexps (filter->regexps);
+ g_free (filter);
+}
+
+
+static gboolean
+filter_matches (Filter *filter,
+ const char *name)
+{
+ const char *file_name;
+ char *utf8_name;
+ gboolean matched;
+
+ g_return_val_if_fail (name != NULL, FALSE);
+
+ file_name = file_name_from_path (name);
+
+ if ((filter->options & FILTER_NODOTFILES)
+ && ((file_name[0] == '.') || (strstr (file_name, "/.") != NULL)))
+ return FALSE;
+
+ if ((filter->options & FILTER_NOBACKUPFILES)
+ && (file_name[strlen (file_name) - 1] == '~'))
+ return FALSE;
+
+ if (filter->pattern == NULL)
+ return TRUE;
+
+ utf8_name = g_filename_to_utf8 (file_name, -1, NULL, NULL, NULL);
+ matched = match_regexps (filter->regexps, utf8_name, 0);
+ g_free (utf8_name);
+
+ return matched;
+}
+
+
+static gboolean
+filter_empty (Filter *filter)
+{
+ return ((filter->pattern == NULL) || (strcmp (filter->pattern, "*") == 0));
+}
+
+
+/* -- g_directory_foreach_child -- */
+
+
+typedef struct {
+ GFile *base_directory;
+ gboolean recursive;
+ gboolean follow_links;
+ StartDirCallback start_dir_func;
+ ForEachChildCallback for_each_file_func;
+ ForEachDoneCallback done_func;
+ gpointer user_data;
+
+ /* private */
+
+ GFile *current;
+ GHashTable *already_visited;
+ GList *to_visit;
+ GCancellable *cancellable;
+ GFileEnumerator *enumerator;
+ GError *error;
+ guint source_id;
+} ForEachChildData;
+
+
+static void
+for_each_child_data_free (ForEachChildData *fec)
+{
+ if (fec == NULL)
+ return;
+
+ if (fec->base_directory != NULL)
+ g_object_unref (fec->base_directory);
+ if (fec->current != NULL)
+ g_object_unref (fec->current);
+ if (fec->already_visited)
+ g_hash_table_destroy (fec->already_visited);
+ if (fec->to_visit != NULL)
+ g_list_free (fec->to_visit);
+ g_free (fec);
+}
+
+
+static gboolean
+for_each_child_done_cb (gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+
+ g_source_remove (fec->source_id);
+ if (fec->current != NULL) {
+ g_object_unref (fec->current);
+ fec->current = NULL;
+ }
+ if (fec->done_func)
+ fec->done_func (fec->error, fec->user_data);
+ for_each_child_data_free (fec);
+
+ return FALSE;
+}
+
+
+static void
+for_each_child_done (ForEachChildData *fec)
+{
+ fec->source_id = g_idle_add (for_each_child_done_cb, fec);
+}
+
+
+static void for_each_child_start_current (ForEachChildData *fec);
+
+
+static gboolean
+for_each_child_start_cb (gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+
+ g_source_remove (fec->source_id);
+ for_each_child_start_current (fec);
+
+ return FALSE;
+}
+
+
+static void
+for_each_child_start (ForEachChildData *fec)
+{
+ fec->source_id = g_idle_add (for_each_child_start_cb, fec);
+}
+
+
+static void
+for_each_child_set_current_uri (ForEachChildData *fec,
+ const char *directory)
+{
+ if (fec->current != NULL)
+ g_object_unref (fec->current);
+ fec->current = g_file_new_for_uri (directory);
+}
+
+
+static void
+for_each_child_set_current (ForEachChildData *fec,
+ GFile *directory)
+{
+ if (fec->current != NULL)
+ g_object_unref (fec->current);
+ fec->current = g_file_dup (directory);
+}
+
+static void
+for_each_child_start_next_sub_directory (ForEachChildData *fec)
+{
+ char *sub_directory = NULL;
+
+ if (fec->to_visit != NULL) {
+ GList *tmp;
+
+ sub_directory = (char*) fec->to_visit->data;
+ tmp = fec->to_visit;
+ fec->to_visit = g_list_remove_link (fec->to_visit, tmp);
+ g_list_free (tmp);
+ }
+
+ if (sub_directory != NULL) {
+ for_each_child_set_current_uri (fec, sub_directory);
+ for_each_child_start (fec);
+ }
+ else
+ for_each_child_done (fec);
+}
+
+
+static void
+for_each_child_close_enumerator (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+ GError *error = NULL;
+
+ if (! g_file_enumerator_close_finish (fec->enumerator,
+ result,
+ &error))
+ {
+ if (fec->error == NULL)
+ fec->error = g_error_copy (error);
+ else
+ g_clear_error (&error);
+ }
+
+ if ((fec->error == NULL) && fec->recursive)
+ for_each_child_start_next_sub_directory (fec);
+ else
+ for_each_child_done (fec);
+}
+
+
+static void
+for_each_child_next_files_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+ GList *children, *scan;
+
+ children = g_file_enumerator_next_files_finish (fec->enumerator,
+ result,
+ &(fec->error));
+
+ if (children == NULL) {
+ g_file_enumerator_close_async (fec->enumerator,
+ G_PRIORITY_DEFAULT,
+ fec->cancellable,
+ for_each_child_close_enumerator,
+ fec);
+ return;
+ }
+
+ for (scan = children; scan; scan = scan->next) {
+ GFileInfo *child_info = scan->data;
+ GFile *f;
+ char *uri;
+
+ f = g_file_get_child (fec->current, g_file_info_get_name (child_info));
+ uri = g_file_get_uri (f);
+
+ if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY) {
+ /* avoid to visit a directory more than once */
+
+ if (g_hash_table_lookup (fec->already_visited, uri) == NULL) {
+ char *sub_directory;
+
+ sub_directory = g_strdup (uri);
+ g_hash_table_insert (fec->already_visited, sub_directory, GINT_TO_POINTER (1));
+ fec->to_visit = g_list_append (fec->to_visit, sub_directory);
+ }
+ }
+
+ fec->for_each_file_func (uri, child_info, fec->user_data);
+
+ g_free (uri);
+ g_object_unref (f);
+ }
+
+ g_file_enumerator_next_files_async (fec->enumerator,
+ N_FILES_PER_REQUEST,
+ G_PRIORITY_DEFAULT,
+ fec->cancellable,
+ for_each_child_next_files_ready,
+ fec);
+}
+
+static void
+for_each_child_ready (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ForEachChildData *fec = user_data;
+
+ fec->enumerator = g_file_enumerate_children_finish (fec->current, result, &(fec->error));
+ if (fec->enumerator == NULL) {
+ for_each_child_done (fec);
+ return;
+ }
+
+ g_file_enumerator_next_files_async (fec->enumerator,
+ N_FILES_PER_REQUEST,
+ G_PRIORITY_DEFAULT,
+ fec->cancellable,
+ for_each_child_next_files_ready,
+ fec);
+}
+
+
+static void
+for_each_child_start_current (ForEachChildData *fec)
+{
+ if (fec->start_dir_func != NULL) {
+ char *directory;
+ DirOp op;
+
+ directory = g_file_get_uri (fec->current);
+ op = fec->start_dir_func (directory, &(fec->error), fec->user_data);
+ g_free (directory);
+
+ switch (op) {
+ case DIR_OP_SKIP:
+ for_each_child_start_next_sub_directory (fec);
+ return;
+ case DIR_OP_STOP:
+ for_each_child_done (fec);
+ return;
+ case DIR_OP_CONTINUE:
+ break;
+ }
+ }
+
+ g_file_enumerate_children_async (fec->current,
+ "standard::name,standard::type",
+ fec->follow_links ? G_FILE_QUERY_INFO_NONE : G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+ G_PRIORITY_DEFAULT,
+ fec->cancellable,
+ for_each_child_ready,
+ fec);
+}
+
+/**
+ * g_directory_foreach_child:
+ * @directory: The directory to visit.
+ * @recursive: Whether to traverse the @directory recursively.
+ * @follow_links: Whether to dereference the symbolic links.
+ * @cancellable: An optional @GCancellable object, used to cancel the process.
+ * @start_dir_func: the function called for each sub-directory, or %NULL if
+ * not needed.
+ * @for_each_file_func: the function called for each file. Can't be %NULL.
+ * @done_func: the function called at the end of the traversing process.
+ * Can't be %NULL.
+ * @user_data: data to pass to @done_func
+ *
+ * Traverse the @directory's filesystem structure calling the
+ * @for_each_file_func function for each file in the directory; the
+ * @start_dir_func function on each directory before it's going to be
+ * traversed, this includes @directory too; the @done_func function is
+ * called at the end of the process.
+ * Some traversing options are available: if @recursive is TRUE the
+ * directory is traversed recursively; if @follow_links is TRUE, symbolic
+ * links are dereferenced, otherwise they are returned as links.
+ * Each callback uses the same @user_data additional parameter.
+ */
+void
+g_directory_foreach_child (GFile *directory,
+ gboolean recursive,
+ gboolean follow_links,
+ GCancellable *cancellable,
+ StartDirCallback start_dir_func,
+ ForEachChildCallback for_each_file_func,
+ ForEachDoneCallback done_func,
+ gpointer user_data)
+{
+ ForEachChildData *fec;
+
+ g_return_if_fail (for_each_file_func != NULL);
+
+ fec = g_new0 (ForEachChildData, 1);
+
+ fec->base_directory = g_object_ref (directory);
+ fec->recursive = recursive;
+ fec->follow_links = follow_links;
+ fec->cancellable = cancellable;
+ fec->start_dir_func = start_dir_func;
+ fec->for_each_file_func = for_each_file_func;
+ fec->done_func = done_func;
+ fec->user_data = user_data;
+ fec->already_visited = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ for_each_child_set_current (fec, fec->base_directory);
+ for_each_child_start_current (fec);
+}
+
+
+/* -- get_file_list_data -- */
+
+
+typedef struct {
+ GList *files;
+ GList *dirs;
+ GFile *directory;
+ GFile *base_dir;
+ GCancellable *cancellable;
+ ListReadyCallback done_func;
+ gpointer done_data;
+ GList *to_visit;
+ GList *current_dir;
+ Filter *include_filter;
+ Filter *exclude_filter;
+ Filter *exclude_folders_filter;
+ guint visit_timeout;
+} GetFileListData;
+
+
+static void
+get_file_list_data_free (GetFileListData *gfl)
+{
+ if (gfl == NULL)
+ return;
+
+ filter_destroy (gfl->include_filter);
+ filter_destroy (gfl->exclude_filter);
+ filter_destroy (gfl->exclude_folders_filter);
+ path_list_free (gfl->files);
+ path_list_free (gfl->dirs);
+ path_list_free (gfl->to_visit);
+ if (gfl->directory != NULL)
+ g_object_unref (gfl->directory);
+ if (gfl->base_dir != NULL)
+ g_object_unref (gfl->base_dir);
+ g_free (gfl);
+}
+
+
+/* -- g_directory_list_async -- */
+
+
+static GList*
+get_relative_file_list (GList *rel_list,
+ GList *file_list,
+ GFile *base_dir)
+{
+ GList *scan;
+
+ if (base_dir == NULL)
+ return NULL;
+
+ for (scan = file_list; scan; scan = scan->next) {
+ char *full_path = scan->data;
+ GFile *f;
+ char *relative_path;
+
+ f = g_file_new_for_commandline_arg (full_path);
+ relative_path = g_file_get_relative_path (base_dir, f);
+ if (relative_path != NULL)
+ rel_list = g_list_prepend (rel_list, relative_path);
+ g_object_unref (f);
+ }
+
+ return rel_list;
+}
+
+
+static GList*
+get_dir_list_from_file_list (GHashTable *h_dirs,
+ const char *base_dir,
+ GList *files,
+ gboolean is_dir_list)
+{
+ GList *scan;
+ GList *dir_list = NULL;
+ int base_dir_len;
+
+ if (base_dir == NULL)
+ base_dir = "";
+ base_dir_len = strlen (base_dir);
+
+ for (scan = files; scan; scan = scan->next) {
+ char *filename = scan->data;
+ char *dir_name;
+
+ if (strlen (filename) <= base_dir_len)
+ continue;
+
+ if (is_dir_list)
+ dir_name = g_strdup (filename + base_dir_len + 1);
+ else
+ dir_name = remove_level_from_path (filename + base_dir_len + 1);
+
+ while ((dir_name != NULL) && (dir_name[0] != '\0') && (strcmp (dir_name, "/") != 0)) {
+ char *tmp;
+ char *dir;
+
+ /* avoid to insert duplicated folders */
+
+ dir = g_strconcat (base_dir, "/", dir_name, NULL);
+ if (g_hash_table_lookup (h_dirs, dir) == NULL) {
+ g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1));
+ dir_list = g_list_prepend (dir_list, dir);
+ }
+ else
+ g_free (dir);
+
+ tmp = dir_name;
+ dir_name = remove_level_from_path (tmp);
+ g_free (tmp);
+ }
+
+ g_free (dir_name);
+ }
+
+ return dir_list;
+}
+
+
+static void
+get_file_list_done (GError *error,
+ gpointer user_data)
+{
+ GetFileListData *gfl = user_data;
+ GHashTable *h_dirs;
+ GList *scan;
+ char *uri;
+
+ gfl->files = g_list_reverse (gfl->files);
+ gfl->dirs = g_list_reverse (gfl->dirs);
+
+ if (! filter_empty (gfl->include_filter) || (gfl->exclude_filter->pattern != NULL)) {
+ path_list_free (gfl->dirs);
+ gfl->dirs = NULL;
+ }
+
+ h_dirs = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /* Always include the base directory, this way empty base
+ * directories are added to the archive as well. */
+
+ if (gfl->base_dir != NULL) {
+ char *dir;
+
+ dir = g_file_get_uri (gfl->base_dir);
+ gfl->dirs = g_list_prepend (gfl->dirs, dir);
+ g_hash_table_insert (h_dirs, dir, GINT_TO_POINTER (1));
+ }
+
+ /* Add all the parent directories in gfl->files/gfl->dirs to the
+ * gfl->dirs list, the hash table is used to avoid duplicated
+ * entries. */
+
+ for (scan = gfl->dirs; scan; scan = scan->next)
+ g_hash_table_insert (h_dirs, (char*)scan->data, GINT_TO_POINTER (1));
+
+ uri = g_file_get_uri (gfl->base_dir);
+ gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->files, FALSE));
+
+ if (filter_empty (gfl->include_filter))
+ gfl->dirs = g_list_concat (gfl->dirs, get_dir_list_from_file_list (h_dirs, uri, gfl->dirs, TRUE));
+
+ g_free (uri);
+ /**/
+
+ if (error == NULL) {
+ GList *rel_files, *rel_dirs;
+
+ if (gfl->base_dir != NULL) {
+ rel_files = get_relative_file_list (NULL, gfl->files, gfl->base_dir);
+ rel_dirs = get_relative_file_list (NULL, gfl->dirs, gfl->base_dir);
+ }
+ else {
+ rel_files = gfl->files;
+ rel_dirs = gfl->dirs;
+ gfl->files = NULL;
+ gfl->dirs = NULL;
+ }
+
+ /* rel_files/rel_dirs must be deallocated in done_func */
+ gfl->done_func (rel_files, rel_dirs, NULL, gfl->done_data);
+ }
+ else
+ gfl->done_func (NULL, NULL, error, gfl->done_data);
+
+ g_hash_table_destroy (h_dirs);
+ get_file_list_data_free (gfl);
+}
+
+
+static void
+get_file_list_for_each_file (const char *uri,
+ GFileInfo *info,
+ gpointer user_data)
+{
+ GetFileListData *gfl = user_data;
+
+ switch (g_file_info_get_file_type (info)) {
+ case G_FILE_TYPE_REGULAR:
+ if (filter_matches (gfl->include_filter, uri))
+ if ((gfl->exclude_filter->pattern == NULL) || ! filter_matches (gfl->exclude_filter, uri))
+ gfl->files = g_list_prepend (gfl->files, g_strdup (uri));
+ break;
+ default:
+ break;
+ }
+}
+
+
+static DirOp
+get_file_list_start_dir (const char *uri,
+ GError **error,
+ gpointer user_data)
+{
+ GetFileListData *gfl = user_data;
+
+ if ((gfl->exclude_folders_filter->pattern == NULL) || ! filter_matches (gfl->exclude_folders_filter, uri)) {
+ gfl->dirs = g_list_prepend (gfl->dirs, g_strdup (uri));
+ return DIR_OP_CONTINUE;
+ }
+ else
+ return DIR_OP_SKIP;
+}
+
+
+void
+g_directory_list_async (const char *directory,
+ const char *base_dir,
+ gboolean recursive,
+ gboolean follow_links,
+ gboolean no_backup_files,
+ gboolean no_dot_files,
+ const char *include_files,
+ const char *exclude_files,
+ const char *exclude_folders,
+ gboolean ignorecase,
+ GCancellable *cancellable,
+ ListReadyCallback done_func,
+ gpointer done_data)
+{
+ GetFileListData *gfl;
+ FilterOptions filter_options;
+
+ gfl = g_new0 (GetFileListData, 1);
+ gfl->directory = g_file_new_for_commandline_arg (directory);
+ gfl->base_dir = g_file_new_for_commandline_arg (base_dir);
+ gfl->done_func = done_func;
+ gfl->done_data = done_data;
+
+ filter_options = FILTER_DEFAULT;
+ if (no_backup_files)
+ filter_options |= FILTER_NOBACKUPFILES;
+ if (no_dot_files)
+ filter_options |= FILTER_NODOTFILES;
+ if (ignorecase)
+ filter_options |= FILTER_IGNORECASE;
+ gfl->include_filter = filter_new (include_files, filter_options);
+ gfl->exclude_filter = filter_new (exclude_files, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT);
+ gfl->exclude_folders_filter = filter_new (exclude_folders, ignorecase ? FILTER_IGNORECASE : FILTER_DEFAULT);
+
+ g_directory_foreach_child (gfl->directory,
+ recursive,
+ follow_links,
+ cancellable,
+ get_file_list_start_dir,
+ get_file_list_for_each_file,
+ get_file_list_done,
+ gfl);
+}
+
+
+/* -- g_list_items_async -- */
+
+
+static void get_items_for_current_dir (GetFileListData *gfl);
+
+
+static gboolean
+get_items_for_next_dir_idle_cb (gpointer data)
+{
+ GetFileListData *gfl = data;
+
+ g_source_remove (gfl->visit_timeout);
+ gfl->visit_timeout = 0;
+
+ gfl->current_dir = g_list_next (gfl->current_dir);
+ get_items_for_current_dir (gfl);
+
+ return FALSE;
+}
+
+
+static void
+get_items_for_current_dir_done (GList *files,
+ GList *dirs,
+ GError *error,
+ gpointer data)
+{
+ GetFileListData *gfl = data;
+
+ if (error != NULL) {
+ if (gfl->done_func)
+ gfl->done_func (NULL, NULL, error, gfl->done_data);
+ path_list_free (files);
+ path_list_free (dirs);
+ get_file_list_data_free (gfl);
+ return;
+ }
+
+ gfl->files = g_list_concat (gfl->files, files);
+ gfl->dirs = g_list_concat (gfl->dirs, dirs);
+
+ gfl->visit_timeout = g_idle_add (get_items_for_next_dir_idle_cb, gfl);
+}
+
+
+static void
+get_items_for_current_dir (GetFileListData *gfl)
+{
+ GFile *current_dir;
+ char *directory_name;
+ GFile *directory_file;
+ char *directory_uri;
+ char *base_dir_uri;
+
+ if (gfl->current_dir == NULL) {
+ if (gfl->done_func) {
+ /* gfl->files/gfl->dirs must be deallocated in gfl->done_func */
+ gfl->done_func (gfl->files, gfl->dirs, NULL, gfl->done_data);
+ gfl->files = NULL;
+ gfl->dirs = NULL;
+ }
+ get_file_list_data_free (gfl);
+ return;
+ }
+
+ current_dir = g_file_new_for_uri ((char*) gfl->current_dir->data);
+ directory_name = g_file_get_basename (current_dir);
+ directory_file = g_file_get_child (gfl->base_dir, directory_name);
+ directory_uri = g_file_get_uri (directory_file);
+ base_dir_uri = g_file_get_uri (gfl->base_dir);
+
+ g_directory_list_all_async (directory_uri,
+ base_dir_uri,
+ TRUE,
+ gfl->cancellable,
+ get_items_for_current_dir_done,
+ gfl);
+
+ g_free (base_dir_uri);
+ g_free (directory_uri);
+ g_object_unref (directory_file);
+ g_free (directory_name);
+ g_object_unref (current_dir);
+}
+
+
+void
+g_list_items_async (GList *items,
+ const char *base_dir,
+ GCancellable *cancellable,
+ ListReadyCallback done_func,
+ gpointer done_data)
+{
+ GetFileListData *gfl;
+ int base_len;
+ GList *scan;
+
+ g_return_if_fail (base_dir != NULL);
+
+ gfl = g_new0 (GetFileListData, 1);
+ gfl->base_dir = g_file_new_for_commandline_arg (base_dir);
+ gfl->cancellable = cancellable;
+ gfl->done_func = done_func;
+ gfl->done_data = done_data;
+
+ base_len = 0;
+ if (strcmp (base_dir, "/") != 0)
+ base_len = strlen (base_dir);
+
+ for (scan = items; scan; scan = scan->next) {
+ char *uri = scan->data;
+
+ /* FIXME: this is not async */
+ if (uri_is_dir (uri)) {
+ gfl->to_visit = g_list_prepend (gfl->to_visit, g_strdup (uri));
+ }
+ else {
+ char *rel_path = g_uri_unescape_string (uri + base_len + 1, NULL);
+ gfl->files = g_list_prepend (gfl->files, rel_path);
+ }
+ }
+
+ gfl->current_dir = gfl->to_visit;
+ get_items_for_current_dir (gfl);
+}
+
+
+/* -- g_copy_files_async -- */
+
+
+typedef struct {
+ GList *sources;
+ GList *destinations;
+ GFileCopyFlags flags;
+ int io_priority;
+ GCancellable *cancellable;
+ CopyProgressCallback progress_callback;
+ gpointer progress_callback_data;
+ CopyDoneCallback callback;
+ gpointer user_data;
+
+ GList *source;
+ GList *destination;
+ int n_file;
+ int tot_files;
+} CopyFilesData;
+
+
+static CopyFilesData*
+copy_files_data_new (GList *sources,
+ GList *destinations,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ CopyFilesData *cfd;
+
+ cfd = g_new0 (CopyFilesData, 1);
+ cfd->sources = gio_file_list_dup (sources);
+ cfd->destinations = gio_file_list_dup (destinations);
+ cfd->flags = flags;
+ cfd->io_priority = io_priority;
+ cfd->cancellable = cancellable;
+ cfd->progress_callback = progress_callback;
+ cfd->progress_callback_data = progress_callback_data;
+ cfd->callback = callback;
+ cfd->user_data = user_data;
+
+ cfd->source = cfd->sources;
+ cfd->destination = cfd->destinations;
+ cfd->n_file = 1;
+ cfd->tot_files = g_list_length (cfd->sources);
+
+ return cfd;
+}
+
+
+static void
+copy_files_data_free (CopyFilesData *cfd)
+{
+ if (cfd == NULL)
+ return;
+ gio_file_list_free (cfd->sources);
+ gio_file_list_free (cfd->destinations);
+ g_free (cfd);
+}
+
+
+static void g_copy_current_file (CopyFilesData *cfd);
+
+
+static void
+g_copy_next_file (CopyFilesData *cfd)
+{
+ cfd->source = g_list_next (cfd->source);
+ cfd->destination = g_list_next (cfd->destination);
+ cfd->n_file++;
+
+ g_copy_current_file (cfd);
+}
+
+
+static void
+g_copy_files_ready_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CopyFilesData *cfd = user_data;
+ GFile *source = cfd->source->data;
+ GError *error = NULL;
+
+ if (! g_file_copy_finish (source, result, &error)) {
+ /* source and target are directories, ignore the error */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_MERGE))
+ g_clear_error (&error);
+ /* source is directory, create target directory */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE)) {
+ g_clear_error (&error);
+ g_file_make_directory ((GFile*) cfd->destination->data,
+ cfd->cancellable,
+ &error);
+ }
+ }
+
+ if (error) {
+ if (cfd->callback)
+ cfd->callback (error, cfd->user_data);
+ g_clear_error (&error);
+ copy_files_data_free (cfd);
+ return;
+ }
+
+ g_copy_next_file (cfd);
+}
+
+
+static void
+g_copy_files_progess_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ CopyFilesData *cfd = user_data;
+
+ if (cfd->progress_callback)
+ cfd->progress_callback (cfd->n_file,
+ cfd->tot_files,
+ (GFile*) cfd->source->data,
+ (GFile*) cfd->destination->data,
+ current_num_bytes,
+ total_num_bytes,
+ cfd->progress_callback_data);
+}
+
+
+static void
+g_copy_current_file (CopyFilesData *cfd)
+{
+ if ((cfd->source == NULL) || (cfd->destination == NULL)) {
+ if (cfd->callback)
+ cfd->callback (NULL, cfd->user_data);
+ copy_files_data_free (cfd);
+ return;
+ }
+
+ g_file_copy_async ((GFile*) cfd->source->data,
+ (GFile*) cfd->destination->data,
+ cfd->flags,
+ cfd->io_priority,
+ cfd->cancellable,
+ g_copy_files_progess_cb,
+ cfd,
+ g_copy_files_ready_cb,
+ cfd);
+}
+
+
+void
+g_copy_files_async (GList *sources,
+ GList *destinations,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ CopyFilesData *cfd;
+
+ cfd = copy_files_data_new (sources,
+ destinations,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+ g_copy_current_file (cfd);
+}
+
+
+void
+g_copy_file_async (GFile *source,
+ GFile *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ GList *source_files;
+ GList *destination_files;
+
+ source_files = g_list_append (NULL, (gpointer) source);
+ destination_files = g_list_append (NULL, (gpointer) destination);
+
+ g_copy_files_async (source_files,
+ destination_files,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+
+ g_list_free (source_files);
+ g_list_free (destination_files);
+}
+
+
+void
+g_copy_uris_async (GList *sources,
+ GList *destinations,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ GList *source_files, *destination_files;
+
+ source_files = gio_file_list_new_from_uri_list (sources);
+ destination_files = gio_file_list_new_from_uri_list (destinations);
+
+ g_copy_files_async (source_files,
+ destination_files,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+
+ gio_file_list_free (source_files);
+ gio_file_list_free (destination_files);
+}
+
+
+void
+g_copy_uri_async (const char *source,
+ const char *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ GList *source_list;
+ GList *destination_list;
+
+ source_list = g_list_append (NULL, (gpointer)source);
+ destination_list = g_list_append (NULL, (gpointer)destination);
+
+ g_copy_uris_async (source_list,
+ destination_list,
+ flags,
+ io_priority,
+ cancellable,
+ progress_callback,
+ progress_callback_data,
+ callback,
+ user_data);
+
+ g_list_free (source_list);
+ g_list_free (destination_list);
+}
+
+
+/* -- g_directory_copy_async -- */
+
+
+typedef struct {
+ char *uri;
+ GFileInfo *info;
+} ChildData;
+
+
+static ChildData*
+child_data_new (const char *uri,
+ GFileInfo *info)
+{
+ ChildData *data;
+
+ data = g_new0 (ChildData, 1);
+ data->uri = g_strdup (uri);
+ data->info = g_file_info_dup (info);
+
+ return data;
+}
+
+
+static void
+child_data_free (ChildData *child)
+{
+ if (child == NULL)
+ return;
+ g_free (child->uri);
+ g_object_unref (child->info);
+ g_free (child);
+}
+
+
+typedef struct {
+ GFile *source;
+ GFile *destination;
+ GFileCopyFlags flags;
+ int io_priority;
+ GCancellable *cancellable;
+ CopyProgressCallback progress_callback;
+ gpointer progress_callback_data;
+ CopyDoneCallback callback;
+ gpointer user_data;
+ GError *error;
+
+ GList *to_copy;
+ GList *current;
+ GFile *current_source;
+ GFile *current_destination;
+ int n_file, tot_files;
+ guint source_id;
+} DirectoryCopyData;
+
+
+static void
+directory_copy_data_free (DirectoryCopyData *dcd)
+{
+ if (dcd == NULL)
+ return;
+
+ if (dcd->source != NULL)
+ g_object_unref (dcd->source);
+ if (dcd->destination != NULL)
+ g_object_unref (dcd->destination);
+ if (dcd->current_source != NULL) {
+ g_object_unref (dcd->current_source);
+ dcd->current_source = NULL;
+ }
+ if (dcd->current_destination != NULL) {
+ g_object_unref (dcd->current_destination);
+ dcd->current_destination = NULL;
+ }
+ g_list_foreach (dcd->to_copy, (GFunc) child_data_free, NULL);
+ g_list_free (dcd->to_copy);
+ g_free (dcd);
+}
+
+
+static gboolean
+g_directory_copy_done (gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ g_source_remove (dcd->source_id);
+
+ if (dcd->callback)
+ dcd->callback (dcd->error, dcd->user_data);
+ if (dcd->error != NULL)
+ g_clear_error (&(dcd->error));
+ directory_copy_data_free (dcd);
+
+ return FALSE;
+}
+
+
+static GFile *
+get_destination_for_uri (DirectoryCopyData *dcd,
+ const char *uri)
+{
+ GFile *f_uri;
+ GFile *destination_file;
+ char *relative_path;
+
+ f_uri = g_file_new_for_uri (uri);
+ relative_path = g_file_get_relative_path (dcd->source, f_uri);
+ if (relative_path != NULL)
+ destination_file = g_file_resolve_relative_path (dcd->destination, relative_path);
+ else
+ destination_file = g_file_dup (dcd->destination);
+
+ g_free (relative_path);
+ g_object_unref (f_uri);
+
+ return destination_file;
+}
+
+
+static void g_directory_copy_current_child (DirectoryCopyData *dcd);
+
+
+static gboolean
+g_directory_copy_next_child (gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ g_source_remove (dcd->source_id);
+
+ dcd->current = g_list_next (dcd->current);
+ dcd->n_file++;
+ g_directory_copy_current_child (dcd);
+
+ return FALSE;
+}
+
+
+static void
+g_directory_copy_child_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ if (! g_file_copy_finish ((GFile*)source_object, result, &(dcd->error))) {
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }
+
+ dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
+}
+
+
+static void
+g_directory_copy_child_progess_cb (goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ if (dcd->progress_callback)
+ dcd->progress_callback (dcd->n_file,
+ dcd->tot_files,
+ dcd->current_source,
+ dcd->current_destination,
+ current_num_bytes,
+ total_num_bytes,
+ dcd->progress_callback_data);
+}
+
+
+static void
+g_directory_copy_current_child (DirectoryCopyData *dcd)
+{
+ ChildData *child;
+ gboolean async_op = FALSE;
+
+ if (dcd->current == NULL) {
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }
+
+ if (dcd->current_source != NULL) {
+ g_object_unref (dcd->current_source);
+ dcd->current_source = NULL;
+ }
+ if (dcd->current_destination != NULL) {
+ g_object_unref (dcd->current_destination);
+ dcd->current_destination = NULL;
+ }
+
+ child = dcd->current->data;
+ dcd->current_source = g_file_new_for_uri (child->uri);
+ dcd->current_destination = get_destination_for_uri (dcd, child->uri);
+ if (dcd->current_destination == NULL) {
+ dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
+ return;
+ }
+
+ switch (g_file_info_get_file_type (child->info)) {
+ case G_FILE_TYPE_DIRECTORY:
+ /* FIXME: how to make a directory asynchronously ? */
+
+ /* doesn't check the returned error for now, because when an
+ * error occurs the code is not returned (for example when
+ * a directory already exists the G_IO_ERROR_EXISTS code is
+ * *not* returned), so we cannot discriminate between warnings
+ * and fatal errors. (see bug #525155) */
+
+ g_file_make_directory (dcd->current_destination,
+ NULL,
+ NULL);
+
+ /*if (! g_file_make_directory (dcd->current_destination,
+ dcd->cancellable,
+ &(dcd->error)))
+ {
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }*/
+ break;
+ case G_FILE_TYPE_SYMBOLIC_LINK:
+ /* FIXME: how to make a link asynchronously ? */
+
+ g_file_make_symbolic_link (dcd->current_destination,
+ g_file_info_get_symlink_target (child->info),
+ NULL,
+ NULL);
+
+ /*if (! g_file_make_symbolic_link (dcd->current_destination,
+ g_file_info_get_symlink_target (child->info),
+ dcd->cancellable,
+ &(dcd->error)))
+ {
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }*/
+ break;
+ case G_FILE_TYPE_REGULAR:
+ g_file_copy_async (dcd->current_source,
+ dcd->current_destination,
+ dcd->flags,
+ dcd->io_priority,
+ dcd->cancellable,
+ g_directory_copy_child_progess_cb,
+ dcd,
+ g_directory_copy_child_done_cb,
+ dcd);
+ async_op = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ if (! async_op)
+ dcd->source_id = g_idle_add (g_directory_copy_next_child, dcd);
+}
+
+
+static gboolean
+g_directory_copy_start_copying (gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ g_source_remove (dcd->source_id);
+
+ dcd->to_copy = g_list_reverse (dcd->to_copy);
+ dcd->current = dcd->to_copy;
+ dcd->n_file = 1;
+ g_directory_copy_current_child (dcd);
+
+ return FALSE;
+}
+
+
+static void
+g_directory_copy_list_ready (GError *error,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ if (error != NULL) {
+ dcd->error = g_error_copy (error);
+ dcd->source_id = g_idle_add (g_directory_copy_done, dcd);
+ return;
+ }
+
+ dcd->source_id = g_idle_add (g_directory_copy_start_copying, dcd);
+}
+
+
+static void
+g_directory_copy_for_each_file (const char *uri,
+ GFileInfo *info,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+
+ dcd->to_copy = g_list_prepend (dcd->to_copy, child_data_new (uri, info));
+ dcd->tot_files++;
+}
+
+
+static DirOp
+g_directory_copy_start_dir (const char *uri,
+ GError **error,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd = user_data;
+ GFileInfo *info;
+
+ info = g_file_info_new ();
+ g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+ dcd->to_copy = g_list_prepend (dcd->to_copy, child_data_new (uri, info));
+ g_object_unref (info);
+
+ dcd->tot_files++;
+
+ return DIR_OP_CONTINUE;
+}
+
+
+void
+g_directory_copy_async (const char *source,
+ const char *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data)
+{
+ DirectoryCopyData *dcd;
+
+ /* Creating GFile objects here will save us lot of effort in path construction */
+ dcd = g_new0 (DirectoryCopyData, 1);
+ dcd->source = g_file_new_for_commandline_arg (source);
+ dcd->destination = g_file_new_for_commandline_arg (destination);
+ dcd->flags = flags;
+ dcd->io_priority = io_priority;
+ dcd->cancellable = cancellable;
+ dcd->progress_callback = progress_callback;
+ dcd->progress_callback_data = progress_callback_data;
+ dcd->callback = callback;
+ dcd->user_data = user_data;
+
+ g_directory_foreach_child (dcd->source,
+ TRUE,
+ TRUE,
+ dcd->cancellable,
+ g_directory_copy_start_dir,
+ g_directory_copy_for_each_file,
+ g_directory_copy_list_ready,
+ dcd);
+}
+
+
+gboolean
+g_load_file_in_buffer (GFile *file,
+ void *buffer,
+ gsize size,
+ GError **error)
+{
+ GFileInputStream *istream;
+ int n;
+
+ istream = g_file_read (file, NULL, error);
+ if (istream == NULL)
+ return FALSE;
+
+ n = g_input_stream_read (G_INPUT_STREAM (istream), buffer, size, NULL, error);
+ g_object_unref (istream);
+
+ return (n >= 0);
+}
+
diff --git a/src/gio-utils.h b/src/gio-utils.h
new file mode 100644
index 0000000..7dfe306
--- /dev/null
+++ b/src/gio-utils.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2008 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GIO_UTILS_H
+#define _GIO_UTILS_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+/* callback types */
+
+typedef enum {
+ DIR_OP_CONTINUE,
+ DIR_OP_SKIP,
+ DIR_OP_STOP
+} DirOp;
+
+typedef DirOp (*StartDirCallback) (const char *uri,
+ GError **error,
+ gpointer user_data);
+typedef void (*ForEachChildCallback) (const char *uri,
+ GFileInfo *info,
+ gpointer user_data);
+typedef void (*ForEachDoneCallback) (GError *error,
+ gpointer data);
+typedef void (*ListReadyCallback) (GList *files,
+ GList *dirs,
+ GError *error,
+ gpointer user_data);
+typedef void (*CopyProgressCallback) (goffset current_file,
+ goffset total_files,
+ GFile *source,
+ GFile *destination,
+ goffset current_num_bytes,
+ goffset total_num_bytes,
+ gpointer user_data);
+typedef void (*CopyDoneCallback) (GError *error,
+ gpointer user_data);
+
+/* asynchronous recursive list functions */
+
+void g_directory_foreach_child (GFile *directory,
+ gboolean recursive,
+ gboolean follow_links,
+ GCancellable *cancellable,
+ StartDirCallback start_dir_func,
+ ForEachChildCallback for_each_file_func,
+ ForEachDoneCallback done_func,
+ gpointer user_data);
+void g_directory_list_async (const char *directory,
+ const char *base_dir,
+ gboolean recursive,
+ gboolean follow_links,
+ gboolean no_backup_files,
+ gboolean no_dot_files,
+ const char *include_files,
+ const char *exclude_files,
+ const char *exclude_folders,
+ gboolean ignorecase,
+ GCancellable *cancellable,
+ ListReadyCallback done_func,
+ gpointer done_data);
+void g_list_items_async (GList *items,
+ const char *base_dir,
+ GCancellable *cancellable,
+ ListReadyCallback done_func,
+ gpointer done_data);
+
+/* asynchronous copy functions */
+
+void g_copy_files_async (GList *sources,
+ GList *destinations,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data);
+void g_copy_uris_async (GList *sources,
+ GList *destinations,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data);
+void g_copy_file_async (GFile *source,
+ GFile *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data);
+void g_copy_uri_async (const char *source,
+ const char *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data);
+void g_directory_copy_async (const char *source,
+ const char *destination,
+ GFileCopyFlags flags,
+ int io_priority,
+ GCancellable *cancellable,
+ CopyProgressCallback progress_callback,
+ gpointer progress_callback_data,
+ CopyDoneCallback callback,
+ gpointer user_data);
+gboolean g_load_file_in_buffer (GFile *file,
+ void *buffer,
+ gsize size,
+ GError **error);
+
+/* convenience macros */
+
+/**
+ * g_directory_list_all_async:
+ * @directory:
+ * @base_dir:
+ * @recursive:
+ * @cancellable:
+ * @done_func:
+ * @done_data:
+ *
+ */
+#define g_directory_list_all_async(directory, base_dir, recursive, cancellable, done_func, done_data) \
+ g_directory_list_async ((directory), (base_dir), (recursive), TRUE, FALSE, FALSE, NULL, NULL, NULL, FALSE, (cancellable), (done_func), (done_data))
+
+#endif /* _GIO_UTILS_H */
diff --git a/src/glib-utils.c b/src/glib-utils.c
new file mode 100644
index 0000000..7935f13
--- /dev/null
+++ b/src/glib-utils.c
@@ -0,0 +1,645 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2005 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include "glib-utils.h"
+
+
+#define MAX_PATTERNS 128
+
+
+gboolean
+strchrs (const char *str,
+ const char *chars)
+{
+ const char *c;
+ for (c = chars; *c != '\0'; c++)
+ if (strchr (str, *c) != NULL)
+ return TRUE;
+ return FALSE;
+}
+
+
+char *
+str_substitute (const char *str,
+ const char *from_str,
+ const char *to_str)
+{
+ char **tokens;
+ int i;
+ GString *gstr;
+
+ if (str == NULL)
+ return NULL;
+
+ if (from_str == NULL)
+ return g_strdup (str);
+
+ if (strcmp (str, from_str) == 0)
+ return g_strdup (to_str);
+
+ tokens = g_strsplit (str, from_str, -1);
+
+ gstr = g_string_new (NULL);
+ for (i = 0; tokens[i] != NULL; i++) {
+ gstr = g_string_append (gstr, tokens[i]);
+ if ((to_str != NULL) && (tokens[i+1] != NULL))
+ gstr = g_string_append (gstr, to_str);
+ }
+
+ return g_string_free (gstr, FALSE);
+}
+
+
+int
+strcmp_null_tolerant (const char *s1, const char *s2)
+{
+ if ((s1 == NULL) && (s2 == NULL))
+ return 0;
+ else if ((s1 != NULL) && (s2 == NULL))
+ return 1;
+ else if ((s1 == NULL) && (s2 != NULL))
+ return -1;
+ else
+ return strcmp (s1, s2);
+}
+
+
+/* counts how many characters to escape in @str. */
+static int
+count_chars_to_escape (const char *str,
+ const char *meta_chars)
+{
+ int meta_chars_n = strlen (meta_chars);
+ const char *s;
+ int n = 0;
+
+ for (s = str; *s != 0; s++) {
+ int i;
+ for (i = 0; i < meta_chars_n; i++)
+ if (*s == meta_chars[i]) {
+ n++;
+ break;
+ }
+ }
+ return n;
+}
+
+
+char*
+escape_str_common (const char *str,
+ const char *meta_chars,
+ const char prefix,
+ const char postfix)
+{
+ int meta_chars_n = strlen (meta_chars);
+ char *escaped;
+ int i, new_l, extra_chars = 0;
+ const char *s;
+ char *t;
+
+ if (str == NULL)
+ return NULL;
+
+ if (prefix)
+ extra_chars++;
+ if (postfix)
+ extra_chars++;
+
+ new_l = strlen (str) + (count_chars_to_escape (str, meta_chars) * extra_chars);
+ escaped = g_malloc (new_l + 1);
+
+ s = str;
+ t = escaped;
+ while (*s) {
+ gboolean is_bad = FALSE;
+ for (i = 0; (i < meta_chars_n) && !is_bad; i++)
+ is_bad = (*s == meta_chars[i]);
+ if (is_bad && prefix)
+ *t++ = prefix;
+ *t++ = *s++;
+ if (is_bad && postfix)
+ *t++ = postfix;
+ }
+ *t = 0;
+
+ return escaped;
+}
+
+
+/* escape with backslash the string @str. */
+char*
+escape_str (const char *str,
+ const char *meta_chars)
+{
+ return escape_str_common (str, meta_chars, '\\', 0);
+}
+
+
+/* escape with backslash the file name. */
+char*
+shell_escape (const char *filename)
+{
+ return escape_str (filename, "$'`\"\\!?* ()[]&|:;<>#");
+}
+
+
+static const char *
+g_utf8_strstr (const char *haystack, const char *needle)
+{
+ const char *s;
+ gsize i;
+ gsize haystack_len = g_utf8_strlen (haystack, -1);
+ gsize needle_len = g_utf8_strlen (needle, -1);
+ int needle_size = strlen (needle);
+
+ s = haystack;
+ for (i = 0; i <= haystack_len - needle_len; i++) {
+ if (strncmp (s, needle, needle_size) == 0)
+ return s;
+ s = g_utf8_next_char(s);
+ }
+
+ return NULL;
+}
+
+
+static char**
+g_utf8_strsplit (const char *string,
+ const char *delimiter,
+ int max_tokens)
+{
+ GSList *string_list = NULL, *slist;
+ char **str_array;
+ const char *s;
+ guint n = 0;
+ const char *remainder;
+
+ g_return_val_if_fail (string != NULL, NULL);
+ g_return_val_if_fail (delimiter != NULL, NULL);
+ g_return_val_if_fail (delimiter[0] != '\0', NULL);
+
+ if (max_tokens < 1)
+ max_tokens = G_MAXINT;
+
+ remainder = string;
+ s = g_utf8_strstr (remainder, delimiter);
+ if (s != NULL) {
+ gsize delimiter_size = strlen (delimiter);
+
+ while (--max_tokens && (s != NULL)) {
+ gsize size = s - remainder;
+ char *new_string;
+
+ new_string = g_new (char, size + 1);
+ strncpy (new_string, remainder, size);
+ new_string[size] = 0;
+
+ string_list = g_slist_prepend (string_list, new_string);
+ n++;
+ remainder = s + delimiter_size;
+ s = g_utf8_strstr (remainder, delimiter);
+ }
+ }
+ if (*string) {
+ n++;
+ string_list = g_slist_prepend (string_list, g_strdup (remainder));
+ }
+
+ str_array = g_new (char*, n + 1);
+
+ str_array[n--] = NULL;
+ for (slist = string_list; slist; slist = slist->next)
+ str_array[n--] = slist->data;
+
+ g_slist_free (string_list);
+
+ return str_array;
+}
+
+
+static char*
+g_utf8_strchug (char *string)
+{
+ char *scan;
+ gunichar c;
+
+ g_return_val_if_fail (string != NULL, NULL);
+
+ scan = string;
+ c = g_utf8_get_char (scan);
+ while (g_unichar_isspace (c)) {
+ scan = g_utf8_next_char (scan);
+ c = g_utf8_get_char (scan);
+ }
+
+ g_memmove (string, scan, strlen (scan) + 1);
+
+ return string;
+}
+
+
+static char*
+g_utf8_strchomp (char *string)
+{
+ char *scan;
+ gsize len;
+
+ g_return_val_if_fail (string != NULL, NULL);
+
+ len = g_utf8_strlen (string, -1);
+
+ if (len == 0)
+ return string;
+
+ scan = g_utf8_offset_to_pointer (string, len - 1);
+
+ while (len--) {
+ gunichar c = g_utf8_get_char (scan);
+ if (g_unichar_isspace (c))
+ *scan = '\0';
+ else
+ break;
+ scan = g_utf8_find_prev_char (string, scan);
+ }
+
+ return string;
+}
+
+
+#define g_utf8_strstrip(string) g_utf8_strchomp (g_utf8_strchug (string))
+
+
+gboolean
+match_regexps (GRegex **regexps,
+ const char *string,
+ GRegexMatchFlags match_options)
+{
+ gboolean matched;
+ int i;
+
+ if ((regexps == NULL) || (regexps[0] == NULL))
+ return TRUE;
+
+ if (string == NULL)
+ return FALSE;
+
+ matched = FALSE;
+ for (i = 0; regexps[i] != NULL; i++)
+ if (g_regex_match (regexps[i], string, match_options, NULL)) {
+ matched = TRUE;
+ break;
+ }
+
+ return matched;
+}
+
+
+void
+free_regexps (GRegex **regexps)
+{
+ int i;
+
+ if (regexps == NULL)
+ return;
+
+ for (i = 0; regexps[i] != NULL; i++)
+ g_regex_unref (regexps[i]);
+ g_free (regexps);
+}
+
+
+char **
+search_util_get_patterns (const char *pattern_string)
+{
+ char **patterns;
+ int i;
+
+ if (pattern_string == NULL)
+ return NULL;
+
+ patterns = g_utf8_strsplit (pattern_string, ";", MAX_PATTERNS);
+ for (i = 0; patterns[i] != NULL; i++) {
+ char *p1, *p2;
+
+ p1 = g_utf8_strstrip (patterns[i]);
+ p2 = str_substitute (p1, ".", "\\.");
+ patterns[i] = str_substitute (p2, "*", ".*");
+
+ g_free (p2);
+ g_free (p1);
+ }
+
+ return patterns;
+}
+
+
+GRegex **
+search_util_get_regexps (const char *pattern_string,
+ GRegexCompileFlags compile_options)
+{
+ char **patterns;
+ GRegex **regexps;
+ int i;
+
+ patterns = search_util_get_patterns (pattern_string);
+ if (patterns == NULL)
+ return NULL;
+
+ regexps = g_new0 (GRegex*, n_fields (patterns) + 1);
+ for (i = 0; patterns[i] != NULL; i++)
+ regexps[i] = g_regex_new (patterns[i],
+ G_REGEX_OPTIMIZE | compile_options,
+ G_REGEX_MATCH_NOTEMPTY,
+ NULL);
+ g_strfreev (patterns);
+
+ return regexps;
+}
+
+
+char *
+_g_strdup_with_max_size (const char *s,
+ int max_size)
+{
+ char *result;
+ int l = strlen (s);
+
+ if (l > max_size) {
+ char *first_half;
+ char *second_half;
+ int offset;
+ int half_max_size = max_size / 2 + 1;
+
+ first_half = g_strndup (s, half_max_size);
+ offset = half_max_size + l - max_size;
+ second_half = g_strndup (s + offset, half_max_size);
+
+ result = g_strconcat (first_half, "...", second_half, NULL);
+
+ g_free (first_half);
+ g_free (second_half);
+ } else
+ result = g_strdup (s);
+
+ return result;
+}
+
+
+const char *
+eat_spaces (const char *line)
+{
+ if (line == NULL)
+ return NULL;
+ while ((*line == ' ') && (*line != 0))
+ line++;
+ return line;
+}
+
+
+const char *
+eat_void_chars (const char *line)
+{
+ if (line == NULL)
+ return NULL;
+ while (((*line == ' ') || (*line == '\t')) && (*line != 0))
+ line++;
+ return line;
+}
+
+
+char **
+split_line (const char *line,
+ int n_fields)
+{
+ char **fields;
+ const char *scan, *field_end;
+ int i;
+
+ fields = g_new0 (char *, n_fields + 1);
+ fields[n_fields] = NULL;
+
+ scan = eat_spaces (line);
+ for (i = 0; i < n_fields; i++) {
+ if (scan == NULL) {
+ fields[i] = NULL;
+ continue;
+ }
+ field_end = strchr (scan, ' ');
+ if (field_end != NULL) {
+ fields[i] = g_strndup (scan, field_end - scan);
+ scan = eat_spaces (field_end);
+ }
+ }
+
+ return fields;
+}
+
+
+const char *
+get_last_field (const char *line,
+ int last_field)
+{
+ const char *field;
+ int i;
+
+ if (line == NULL)
+ return NULL;
+
+ last_field--;
+ field = eat_spaces (line);
+ for (i = 0; i < last_field; i++) {
+ if (field == NULL)
+ return NULL;
+ field = strchr (field, ' ');
+ field = eat_spaces (field);
+ }
+
+ return field;
+}
+
+
+int
+n_fields (char **str_array)
+{
+ int i;
+
+ if (str_array == NULL)
+ return 0;
+
+ i = 0;
+ while (str_array[i] != NULL)
+ i++;
+ return i;
+}
+
+
+void
+debug (const char *file,
+ int line,
+ const char *function,
+ const char *format, ...)
+{
+#ifdef DEBUG
+ va_list args;
+ char *str;
+
+ g_return_if_fail (format != NULL);
+
+ va_start (args, format);
+ str = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ g_fprintf (stderr, "[FR] %s:%d (%s):\n\t%s\n", file, line, function, str);
+
+ g_free (str);
+#else /* ! DEBUG */
+#endif
+}
+
+
+char *
+get_time_string (time_t time)
+{
+ struct tm *tm;
+ char s_time[256];
+ char *locale_format = NULL;
+ char *time_utf8;
+
+ tm = localtime (&time);
+ /* This is the time format used in the "Date Modified" column and
+ * in the Properties dialog. See the man page of strftime for an
+ * explanation of the values. */
+ locale_format = g_locale_from_utf8 (_("%d %B %Y, %H:%M"), -1, NULL, NULL, NULL);
+ strftime (s_time, sizeof (s_time) - 1, locale_format, tm);
+ g_free (locale_format);
+ time_utf8 = g_locale_to_utf8 (s_time, -1, NULL, NULL, NULL);
+
+ return time_utf8;
+}
+
+
+GPtrArray *
+g_ptr_array_copy (GPtrArray *array)
+{
+ GPtrArray *new_array;
+
+ if (array == NULL)
+ return NULL;
+
+ new_array = g_ptr_array_sized_new (array->len);
+ memcpy (new_array->pdata, array->pdata, array->len * sizeof (gpointer));
+ new_array->len = array->len;
+
+ return new_array;
+}
+
+
+void
+g_ptr_array_free_full (GPtrArray *array,
+ GFunc free_func,
+ gpointer user_data)
+{
+ g_ptr_array_foreach (array, free_func, user_data);
+ g_ptr_array_free (array, TRUE);
+}
+
+
+void
+g_ptr_array_reverse (GPtrArray *array)
+{
+ int i, j;
+ gpointer tmp;
+
+ for (i = 0; i < array->len / 2; i++) {
+ j = array->len - i - 1;
+ tmp = g_ptr_array_index (array, i);
+ g_ptr_array_index (array, i) = g_ptr_array_index (array, j);
+ g_ptr_array_index (array, j) = tmp;
+ }
+}
+
+
+int
+g_ptr_array_binary_search (GPtrArray *array,
+ gpointer value,
+ GCompareFunc func)
+{
+ int l, r, p, cmp = -1;
+
+ l = 0;
+ r = array->len;
+ while (l < r) {
+ p = l + ((r - l) / 2);
+ cmp = func(value, &g_ptr_array_index (array, p));
+ if (cmp == 0)
+ return p;
+ else if (cmp < 0)
+ r = p;
+ else
+ l = p + 1;
+ }
+
+ return -1;
+}
+
+
+GHashTable *static_strings = NULL;
+
+
+const char *
+get_static_string (const char *s)
+{
+ const char *result;
+
+ if (s == NULL)
+ return NULL;
+
+ if (static_strings == NULL)
+ static_strings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ if (! g_hash_table_lookup_extended (static_strings, s, (gpointer*) &result, NULL)) {
+ result = g_strdup (s);
+ g_hash_table_insert (static_strings,
+ (gpointer) result,
+ GINT_TO_POINTER (1));
+ }
+
+ return result;
+}
+
+
+char*
+g_uri_display_basename (const char *uri)
+{
+ char *e_name, *name;
+
+ e_name = g_filename_display_basename (uri);
+ name = g_uri_unescape_string (e_name, "");
+ g_free (e_name);
+
+ return name;
+}
diff --git a/src/glib-utils.h b/src/glib-utils.h
new file mode 100644
index 0000000..3ef92d2
--- /dev/null
+++ b/src/glib-utils.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2005 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _GLIB_UTILS_H
+#define _GLIB_UTILS_H
+
+#include <time.h>
+
+#define g_signal_handlers_disconnect_by_data(instance, data) \
+ g_signal_handlers_disconnect_matched ((instance), G_SIGNAL_MATCH_DATA, \
+ 0, 0, NULL, NULL, (data))
+
+gboolean strchrs (const char *str,
+ const char *chars);
+char * str_substitute (const char *str,
+ const char *from_str,
+ const char *to_str);
+int strcmp_null_tolerant (const char *s1, const char *s2);
+char* escape_str_common (const char *str,
+ const char *meta_chars,
+ const char prefix,
+ const char postfix);
+char* escape_str (const char *str,
+ const char *meta_chars);
+gchar * shell_escape (const gchar *filename);
+gboolean match_regexps (GRegex **regexps,
+ const char *string,
+ GRegexMatchFlags match_options);
+char ** search_util_get_patterns (const char *pattern_string);
+GRegex ** search_util_get_regexps (const char *pattern_string,
+ GRegexCompileFlags compile_options);
+void free_regexps (GRegex **regexps);
+char * _g_strdup_with_max_size (const char *s,
+ int max_size);
+const char * eat_spaces (const char *line);
+const char * eat_void_chars (const char *line);
+char ** split_line (const char *line,
+ int n_fields);
+const char * get_last_field (const char *line,
+ int last_field);
+int n_fields (char **str_array);
+char * get_time_string (time_t time);
+GPtrArray * g_ptr_array_copy (GPtrArray *array);
+void g_ptr_array_free_full (GPtrArray *array,
+ GFunc func,
+ gpointer user_data);
+void g_ptr_array_reverse (GPtrArray *array);
+int g_ptr_array_binary_search (GPtrArray *array,
+ gpointer value,
+ GCompareFunc func);
+const char * get_static_string (const char *s);
+char* g_uri_display_basename (const char *uri);
+
+/**/
+
+#ifndef __GNUC__
+#define __FUNCTION__ ""
+#endif
+
+#define DEBUG_INFO __FILE__, __LINE__, __FUNCTION__
+
+void debug (const char *file,
+ int line,
+ const char *function,
+ const char *format, ...);
+
+#endif /* _GLIB_UTILS_H */
diff --git a/src/gtk-utils.c b/src/gtk-utils.c
new file mode 100644
index 0000000..c18ec62
--- /dev/null
+++ b/src/gtk-utils.c
@@ -0,0 +1,823 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include "gtk-utils.h"
+
+#define LOAD_BUFFER_SIZE 65536
+
+
+static void
+count_selected (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ int *n = data;
+ *n = *n + 1;
+}
+
+
+int
+_gtk_count_selected (GtkTreeSelection *selection)
+{
+ int n = 0;
+
+ if (selection == NULL)
+ return 0;
+ gtk_tree_selection_selected_foreach (selection, count_selected, &n);
+ return n;
+}
+
+
+GtkWidget*
+_gtk_message_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *stock_id,
+ const char *message,
+ const char *secondary_message,
+ const gchar *first_button_text,
+ ...)
+{
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *image;
+ GtkWidget *hbox;
+ GtkWidget *content_area;
+ va_list args;
+ const gchar *text;
+ int response_id;
+ char *markup_text;
+
+ g_return_val_if_fail ((message != NULL) || (secondary_message != NULL), NULL);
+
+ if (stock_id == NULL)
+ stock_id = GTK_STOCK_DIALOG_INFO;
+
+ dialog = gtk_dialog_new_with_buttons ("", parent, flags, NULL);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ 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 (content_area), 6);
+ gtk_box_set_spacing (GTK_BOX (content_area), 8);
+
+ /* Add label and image */
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+
+ label = gtk_label_new ("");
+
+ if (message != NULL) {
+ char *escaped_message;
+
+ escaped_message = g_markup_escape_text (message, -1);
+ if (secondary_message != NULL) {
+ char *escaped_secondary_message = g_markup_escape_text (secondary_message, -1);
+ markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s",
+ escaped_message,
+ escaped_secondary_message);
+ g_free (escaped_secondary_message);
+ }
+ else
+ markup_text = g_strdup (escaped_message);
+ g_free (escaped_message);
+ }
+ else
+ markup_text = g_markup_escape_text (secondary_message, -1);
+
+ gtk_label_set_markup (GTK_LABEL (label), markup_text);
+ g_free (markup_text);
+
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+ hbox = gtk_hbox_new (FALSE, 24);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+
+ gtk_box_pack_start (GTK_BOX (hbox), image,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), label,
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (content_area),
+ hbox,
+ FALSE, FALSE, 0);
+
+ gtk_widget_show_all (hbox);
+
+ /* Add buttons */
+
+ if (first_button_text == NULL)
+ return dialog;
+
+ va_start (args, first_button_text);
+
+ text = first_button_text;
+ response_id = va_arg (args, gint);
+
+ while (text != NULL) {
+ gtk_dialog_add_button (GTK_DIALOG (dialog), text, response_id);
+
+ text = va_arg (args, char*);
+ if (text == NULL)
+ break;
+ response_id = va_arg (args, int);
+ }
+
+ va_end (args);
+
+ return dialog;
+}
+
+
+static GtkWidget *
+create_button (const char *stock_id,
+ const char *text)
+{
+ GtkWidget *button;
+ GtkWidget *hbox;
+ GtkWidget *image;
+ GtkWidget *label;
+ GtkWidget *align;
+ const char *label_text;
+ gboolean text_is_stock;
+ GtkStockItem stock_item;
+
+ if (gtk_stock_lookup (text, &stock_item)) {
+ label_text = stock_item.label;
+ text_is_stock = TRUE;
+ } else {
+ label_text = text;
+ text_is_stock = FALSE;
+ }
+
+ if (text_is_stock)
+ image = gtk_image_new_from_stock (text, GTK_ICON_SIZE_BUTTON);
+ else
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_BUTTON);
+ button = gtk_button_new_with_mnemonic (label_text);
+ gtk_button_set_image (GTK_BUTTON (button), image);
+
+ gtk_widget_set_can_default (button, TRUE);
+
+ gtk_widget_show (button);
+
+ return button;
+}
+
+
+char *
+_gtk_request_dialog_run (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *title,
+ const char *message,
+ const char *default_value,
+ int max_length,
+ const gchar *no_button_text,
+ const gchar *yes_button_text)
+{
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *image;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *entry;
+ GtkWidget *button;
+ GtkWidget *content_area;
+ char *stock_id;
+ char *result;
+
+ stock_id = GTK_STOCK_DIALOG_QUESTION;
+
+ dialog = gtk_dialog_new_with_buttons (title, parent, flags, NULL);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ 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 (content_area), 6);
+ gtk_box_set_spacing (GTK_BOX (content_area), 8);
+
+ /* Add label and image */
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+
+ label = gtk_label_new (message);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (label), FALSE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
+
+ entry = gtk_entry_new ();
+ gtk_widget_set_size_request (entry, 250, -1);
+ gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
+ gtk_entry_set_max_length (GTK_ENTRY (entry), max_length);
+ gtk_entry_set_text (GTK_ENTRY (entry), default_value);
+
+ hbox = gtk_hbox_new (FALSE, 24);
+ vbox = gtk_vbox_new (FALSE, 6);
+
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
+ gtk_box_pack_start (GTK_BOX (hbox), image,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), vbox,
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), label,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (vbox), entry,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (content_area),
+ hbox,
+ FALSE, FALSE, 0);
+
+ gtk_widget_show_all (hbox);
+
+ /* Add buttons */
+
+ button = create_button (GTK_STOCK_CANCEL, no_button_text);
+ gtk_dialog_add_action_widget (GTK_DIALOG (dialog),
+ button,
+ GTK_RESPONSE_CANCEL);
+
+ button = create_button (GTK_STOCK_OK, yes_button_text);
+ gtk_dialog_add_action_widget (GTK_DIALOG (dialog),
+ button,
+ GTK_RESPONSE_YES);
+
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
+
+ /* Run dialog */
+
+ gtk_widget_grab_focus (entry);
+
+ if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES)
+ result = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
+ else
+ result = NULL;
+
+ gtk_widget_destroy (dialog);
+
+ return result;
+}
+
+
+GtkWidget*
+_gtk_yesno_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *message,
+ const char *no_button_text,
+ const char *yes_button_text)
+{
+ GtkWidget *d;
+ GtkWidget *label;
+ GtkWidget *image;
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *content_area;
+ char *stock_id = GTK_STOCK_DIALOG_WARNING;
+
+ d = gtk_dialog_new_with_buttons ("", parent, flags, NULL);
+ gtk_window_set_resizable (GTK_WINDOW (d), FALSE);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (d));
+
+ gtk_dialog_set_has_separator (GTK_DIALOG (d), FALSE);
+ gtk_container_set_border_width (GTK_CONTAINER (d), 6);
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 6);
+ gtk_box_set_spacing (GTK_BOX (content_area), 8);
+
+ /* Add label and image */
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+
+ label = gtk_label_new (message);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+ hbox = gtk_hbox_new (FALSE, 24);
+ gtk_container_set_border_width (GTK_CONTAINER (hbox), 6);
+
+ gtk_box_pack_start (GTK_BOX (hbox), image,
+ FALSE, FALSE, 0);
+
+ gtk_box_pack_start (GTK_BOX (hbox), label,
+ TRUE, TRUE, 0);
+
+ gtk_box_pack_start (GTK_BOX (content_area),
+ hbox,
+ FALSE, FALSE, 0);
+
+ gtk_widget_show_all (hbox);
+
+ /* Add buttons */
+
+ button = create_button (GTK_STOCK_CANCEL, no_button_text);
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ button,
+ GTK_RESPONSE_CANCEL);
+
+ /**/
+
+ button = create_button (GTK_STOCK_OK, yes_button_text);
+ gtk_dialog_add_action_widget (GTK_DIALOG (d),
+ button,
+ GTK_RESPONSE_YES);
+
+ /**/
+
+ gtk_dialog_set_default_response (GTK_DIALOG (d), GTK_RESPONSE_YES);
+
+ return d;
+}
+
+
+GtkWidget*
+_gtk_error_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ GList *row_output,
+ const char *primary_text,
+ const char *secondary_text,
+ ...)
+{
+ GtkWidget *dialog;
+ GtkWidget *label;
+ GtkWidget *image;
+ GtkWidget *hbox;
+ GtkWidget *vbox;
+ GtkWidget *text_view;
+ GtkWidget *scrolled = NULL;
+ GtkWidget *expander;
+ GtkWidget *content_area;
+ GtkTextBuffer *text_buf;
+ GtkTextIter iter;
+ char *stock_id;
+ GList *scan;
+ char *escaped_message, *markup_text;
+ va_list args;
+ gboolean view_output = (row_output != NULL);
+
+ stock_id = GTK_STOCK_DIALOG_ERROR;
+
+ dialog = gtk_dialog_new_with_buttons ("",
+ parent,
+ flags,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ 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 (content_area), 6);
+ gtk_box_set_spacing (GTK_BOX (content_area), 8);
+
+ gtk_widget_set_size_request (dialog, 500, -1);
+
+ /* Add label and image */
+
+ image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_DIALOG);
+ gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
+
+ label = gtk_label_new ("");
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+
+ escaped_message = g_markup_escape_text (primary_text, -1);
+ if (secondary_text != NULL) {
+ char *secondary_message;
+ char *escaped_secondary_message;
+
+ va_start (args, secondary_text);
+ secondary_message = g_strdup_vprintf (secondary_text, args);
+ va_end (args);
+ escaped_secondary_message = g_markup_escape_text (secondary_message, -1);
+
+ markup_text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n",
+ escaped_message,
+ escaped_secondary_message);
+
+ g_free (escaped_secondary_message);
+ g_free (secondary_message);
+ }
+ else
+ markup_text = g_strdup (escaped_message);
+ gtk_label_set_markup (GTK_LABEL (label), markup_text);
+ g_free (markup_text);
+ g_free (escaped_message);
+
+ if (view_output) {
+ /* Expander */
+
+ expander = gtk_expander_new_with_mnemonic (_("Command _Line Output"));
+ gtk_expander_set_expanded (GTK_EXPANDER (expander), secondary_text == NULL);
+
+ /* 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);
+ gtk_widget_set_size_request (scrolled, -1, 200);
+
+ text_buf = gtk_text_buffer_new (NULL);
+ gtk_text_buffer_create_tag (text_buf, "monospace",
+ "family", "monospace", NULL);
+ gtk_text_buffer_get_iter_at_offset (text_buf, &iter, 0);
+ for (scan = row_output; 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_buf,
+ &iter,
+ utf8_line,
+ bytes_written,
+ "monospace", NULL);
+ g_free (utf8_line);
+
+ gtk_text_buffer_insert (text_buf, &iter, "\n", 1);
+ }
+ text_view = gtk_text_view_new_with_buffer (text_buf);
+ g_object_unref (text_buf);
+ 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);
+
+ hbox = gtk_hbox_new (FALSE, 12);
+ gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
+
+ if (view_output) {
+ gtk_container_add (GTK_CONTAINER (scrolled), text_view);
+ gtk_container_add (GTK_CONTAINER (expander), scrolled);
+ gtk_box_pack_start (GTK_BOX (vbox), expander, TRUE, TRUE, 0);
+ }
+
+ gtk_box_pack_start (GTK_BOX (content_area),
+ vbox,
+ FALSE, FALSE, 0);
+ gtk_widget_show_all (vbox);
+
+ return dialog;
+}
+
+
+void
+_gtk_error_dialog_run (GtkWindow *parent,
+ const gchar *main_message,
+ const gchar *format,
+ ...)
+{
+ GtkWidget *d;
+ char *message;
+ va_list args;
+
+ va_start (args, format);
+ message = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ d = _gtk_message_dialog_new (parent,
+ GTK_DIALOG_MODAL,
+ GTK_STOCK_DIALOG_ERROR,
+ main_message,
+ message,
+ GTK_STOCK_CLOSE, GTK_RESPONSE_CANCEL,
+ NULL);
+ g_free (message);
+
+ g_signal_connect (G_OBJECT (d), "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+
+ gtk_widget_show (d);
+}
+
+
+void
+_gtk_entry_set_locale_text (GtkEntry *entry,
+ const char *text)
+{
+ char *utf8_text;
+
+ if (text == NULL)
+ return;
+
+ utf8_text = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
+ if (utf8_text != NULL)
+ gtk_entry_set_text (entry, utf8_text);
+ else
+ gtk_entry_set_text (entry, "");
+ g_free (utf8_text);
+}
+
+
+char *
+_gtk_entry_get_locale_text (GtkEntry *entry)
+{
+ const char *utf8_text;
+ char *text;
+
+ utf8_text = gtk_entry_get_text (entry);
+ if (utf8_text == NULL)
+ return NULL;
+
+ text = g_locale_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+
+ return text;
+}
+
+
+void
+_gtk_label_set_locale_text (GtkLabel *label,
+ const char *text)
+{
+ char *utf8_text;
+
+ utf8_text = g_locale_to_utf8 (text, -1, NULL, NULL, NULL);
+ if (utf8_text != NULL) {
+ gtk_label_set_text (label, utf8_text);
+ g_free (utf8_text);
+ } else
+ gtk_label_set_text (label, "");
+}
+
+
+char *
+_gtk_label_get_locale_text (GtkLabel *label)
+{
+ const char *utf8_text;
+ char *text;
+
+ utf8_text = gtk_label_get_text (label);
+ if (utf8_text == NULL)
+ return NULL;
+
+ text = g_locale_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+
+ return text;
+}
+
+
+void
+_gtk_entry_set_filename_text (GtkEntry *entry,
+ const char *text)
+{
+ char *utf8_text;
+
+ utf8_text = g_filename_to_utf8 (text, -1, NULL, NULL, NULL);
+ if (utf8_text != NULL) {
+ gtk_entry_set_text (entry, utf8_text);
+ g_free (utf8_text);
+ } else
+ gtk_entry_set_text (entry, "");
+}
+
+
+char *
+_gtk_entry_get_filename_text (GtkEntry *entry)
+{
+ const char *utf8_text;
+ char *text;
+
+ utf8_text = gtk_entry_get_text (entry);
+ if (utf8_text == NULL)
+ return NULL;
+
+ text = g_filename_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+
+ return text;
+}
+
+
+void
+_gtk_label_set_filename_text (GtkLabel *label,
+ const char *text)
+{
+ char *utf8_text;
+
+ utf8_text = g_filename_display_name (text);
+ gtk_label_set_text (label, utf8_text);
+ g_free (utf8_text);
+}
+
+
+char *
+_gtk_label_get_filename_text (GtkLabel *label)
+{
+ const char *utf8_text;
+ char *text;
+
+ utf8_text = gtk_label_get_text (label);
+ if (utf8_text == NULL)
+ return NULL;
+
+ text = g_filename_from_utf8 (utf8_text, -1, NULL, NULL, NULL);
+
+ return text;
+}
+
+
+static GdkPixbuf *
+get_themed_icon_pixbuf (GThemedIcon *icon,
+ int size,
+ GtkIconTheme *icon_theme)
+{
+ char **icon_names;
+ GtkIconInfo *icon_info;
+ GdkPixbuf *pixbuf;
+ GError *error = NULL;
+
+ g_object_get (icon, "names", &icon_names, NULL);
+
+ icon_info = gtk_icon_theme_choose_icon (icon_theme, (const char **)icon_names, size, 0);
+ if (icon_info == NULL)
+ icon_info = gtk_icon_theme_lookup_icon (icon_theme, "text-x-generic", size, GTK_ICON_LOOKUP_USE_BUILTIN);
+
+ pixbuf = gtk_icon_info_load_icon (icon_info, &error);
+ if (pixbuf == NULL) {
+ g_warning ("could not load icon pixbuf: %s\n", error->message);
+ g_clear_error (&error);
+ }
+
+ gtk_icon_info_free (icon_info);
+ g_strfreev (icon_names);
+
+ return pixbuf;
+}
+
+
+static GdkPixbuf *
+get_file_icon_pixbuf (GFileIcon *icon,
+ int size)
+{
+ GFile *file;
+ char *filename;
+ GdkPixbuf *pixbuf;
+
+ file = g_file_icon_get_file (icon);
+ filename = g_file_get_path (file);
+ pixbuf = gdk_pixbuf_new_from_file_at_size (filename, size, -1, NULL);
+ g_free (filename);
+ g_object_unref (file);
+
+ return pixbuf;
+}
+
+
+GdkPixbuf *
+get_icon_pixbuf (GIcon *icon,
+ int size,
+ GtkIconTheme *theme)
+{
+ if (icon == NULL)
+ return NULL;
+ if (G_IS_THEMED_ICON (icon))
+ return get_themed_icon_pixbuf (G_THEMED_ICON (icon), size, theme);
+ if (G_IS_FILE_ICON (icon))
+ return get_file_icon_pixbuf (G_FILE_ICON (icon), size);
+ return NULL;
+}
+
+
+GdkPixbuf *
+get_mime_type_pixbuf (const char *mime_type,
+ int icon_size,
+ GtkIconTheme *icon_theme)
+{
+ GdkPixbuf *pixbuf = NULL;
+ GIcon *icon;
+
+ if (icon_theme == NULL)
+ icon_theme = gtk_icon_theme_get_default ();
+
+ icon = g_content_type_get_icon (mime_type);
+ pixbuf = get_icon_pixbuf (icon, icon_size, icon_theme);
+ g_object_unref (icon);
+
+ return pixbuf;
+}
+
+
+int
+get_folder_pixbuf_size_for_list (GtkWidget *widget)
+{
+ int icon_width, icon_height;
+
+ gtk_icon_size_lookup_for_settings (gtk_widget_get_settings (widget),
+ GTK_ICON_SIZE_SMALL_TOOLBAR,
+ &icon_width, &icon_height);
+ return MAX (icon_width, icon_height);
+}
+
+
+gboolean
+show_uri (GdkScreen *screen,
+ const char *uri,
+ guint32 timestamp,
+ GError **error)
+{
+ gboolean result;
+
+ result = gtk_show_uri (screen, uri, timestamp, error);
+
+ return result;
+}
+
+
+void
+show_help_dialog (GtkWindow *parent,
+ const char *section)
+{
+ char *uri;
+ GError *error = NULL;
+
+ uri = g_strconcat ("ghelp:file-roller", section ? "?" : NULL, section, NULL);
+ if (! show_uri (gtk_window_get_screen (parent), uri, GDK_CURRENT_TIME, &error)) {
+ GtkWidget *dialog;
+
+ dialog = _gtk_message_dialog_new (parent,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_DIALOG_ERROR,
+ _("Could not display help"),
+ error->message,
+ GTK_STOCK_OK, GTK_RESPONSE_OK,
+ NULL);
+ gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
+
+ g_signal_connect (G_OBJECT (dialog), "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+
+ gtk_widget_show (dialog);
+
+ g_clear_error (&error);
+ }
+ g_free (uri);
+}
+
+
+GtkBuilder *
+_gtk_builder_new_from_file (const char *ui_file)
+{
+ char *filename;
+ GtkBuilder *builder;
+ GError *error = NULL;
+
+ filename = g_build_filename (UI_DIR, ui_file, NULL);
+ builder = gtk_builder_new ();
+ if (! gtk_builder_add_from_file (builder, filename, &error)) {
+ g_warning ("%s\n", error->message);
+ g_clear_error (&error);
+ }
+ g_free (filename);
+
+ return builder;
+}
+
+
+GtkWidget *
+_gtk_builder_get_widget (GtkBuilder *builder,
+ const char *name)
+{
+ return (GtkWidget *) gtk_builder_get_object (builder, name);
+}
diff --git a/src/gtk-utils.h b/src/gtk-utils.h
new file mode 100644
index 0000000..c2c5837
--- /dev/null
+++ b/src/gtk-utils.h
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef GTK_UTILS_H
+#define GTK_UTILS_H
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+int _gtk_count_selected (GtkTreeSelection *selection);
+GtkWidget* _gtk_message_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *stock_id,
+ const char *message,
+ const char *secondary_message,
+ const char *first_button_text,
+ ...);
+gchar* _gtk_request_dialog_run (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *title,
+ const char *message,
+ const char *default_value,
+ int max_length,
+ const char *no_button_text,
+ const char *yes_button_text);
+GtkWidget* _gtk_yesno_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ const char *message,
+ const char *no_button_text,
+ const char *yes_button_text);
+GtkWidget* _gtk_error_dialog_new (GtkWindow *parent,
+ GtkDialogFlags flags,
+ GList *row_output,
+ const char *primary_text,
+ const char *secondary_text,
+ ...) G_GNUC_PRINTF (5, 6);
+void _gtk_error_dialog_run (GtkWindow *parent,
+ const gchar *main_message,
+ const gchar *format,
+ ...);
+void _gtk_entry_set_locale_text (GtkEntry *entry,
+ const char *text);
+char * _gtk_entry_get_locale_text (GtkEntry *entry);
+void _gtk_label_set_locale_text (GtkLabel *label,
+ const char *text);
+char * _gtk_label_get_locale_text (GtkLabel *label);
+void _gtk_entry_set_filename_text (GtkEntry *entry,
+ const char *text);
+char * _gtk_entry_get_filename_text (GtkEntry *entry);
+void _gtk_label_set_filename_text (GtkLabel *label,
+ const char *text);
+char * _gtk_label_get_filename_text (GtkLabel *label);
+GdkPixbuf * get_icon_pixbuf (GIcon *icon,
+ int size,
+ GtkIconTheme *icon_theme);
+GdkPixbuf * get_mime_type_pixbuf (const char *mime_type,
+ int icon_size,
+ GtkIconTheme *icon_theme);
+int get_folder_pixbuf_size_for_list (GtkWidget *widget);
+gboolean show_uri (GdkScreen *screen,
+ const char *uri,
+ guint32 timestamp,
+ GError **error);
+void show_help_dialog (GtkWindow *parent,
+ const char *section);
+GtkBuilder *
+ _gtk_builder_new_from_file (const char *filename);
+GtkWidget *
+ _gtk_builder_get_widget (GtkBuilder *builder,
+ const char *name);
+
+#endif
diff --git a/src/java-utils.c b/src/java-utils.c
new file mode 100644
index 0000000..b48bdd0
--- /dev/null
+++ b/src/java-utils.c
@@ -0,0 +1,441 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2006 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+#include "java-utils.h"
+
+
+/*
+ * The following code conforms to the JVM specification.(Java 2 Platform)
+ * For further changes to the classfile structure, please update the
+ * following macros.
+ */
+
+
+/* Tags that identify structures */
+
+#define CONST_CLASS 7
+#define CONST_FIELDREF 9
+#define CONST_METHODREF 10
+#define CONST_INTERFACEMETHODREF 11
+#define CONST_STRING 8
+#define CONST_INTEGER 3
+#define CONST_FLOAT 4
+#define CONST_LONG 5
+#define CONST_DOUBLE 6
+#define CONST_NAMEANDTYPE 12
+#define CONST_UTF8 1
+
+/* Sizes of structures */
+
+#define CONST_CLASS_INFO 2
+#define CONST_FIELDREF_INFO 4
+#define CONST_METHODREF_INFO 4
+#define CONST_INTERFACEMETHODREF_INFO 4
+#define CONST_STRING_INFO 2
+#define CONST_INTEGER_INFO 4
+#define CONST_FLOAT_INFO 4
+#define CONST_LONG_INFO 8
+#define CONST_DOUBLE_INFO 8
+#define CONST_NAMEANDTYPE_INFO 4
+
+
+/* represents the utf8 strings in class file */
+struct utf_string
+{
+ guint16 index;
+ guint16 length;
+ char *str;
+};
+
+/* structure that holds class information in a class file */
+struct class_info
+{
+ guint16 index;
+ guint16 name_index; /* index into the utf_strings */
+};
+
+typedef struct {
+ int fd;
+
+ guint32 magic_no; /* 0xCAFEBABE (JVM Specification) :) */
+
+ guint16 major; /* versions */
+ guint16 minor;
+
+ guint16 const_pool_count;
+ GSList *const_pool_class; /* (const_pool_count - 1) elements of tye 'CONST_class_info' */
+ GSList *const_pool_utf; /* (const_pool_count - 1) elements of type 'utf_strings' */
+
+ guint16 access_flags;
+ guint16 this_class; /* the index of the class the file is named after. */
+
+#if 0 /* not needed */
+ guint16 super_class;
+ guint16 interfaces_count;
+ guint16 *interfaces;
+ guint16 fields_count;
+ field_info *fields;
+ guint16 methods_count;
+ method_info *methods;
+ guint16 attributes_count;
+ attribute_info *attributes;
+#endif
+} JavaClassFile;
+
+
+static JavaClassFile*
+java_class_file_new (void)
+{
+ JavaClassFile *cfile;
+
+ cfile = g_new0 (JavaClassFile, 1);
+ cfile->fd = -1;
+
+ return cfile;
+}
+
+
+static void
+java_class_file_free (JavaClassFile *cfile)
+{
+ GSList *scan;
+
+ if (cfile->const_pool_class != NULL) {
+ g_slist_foreach (cfile->const_pool_class, (GFunc)g_free, NULL);
+ g_slist_free (cfile->const_pool_class);
+ }
+
+ for (scan = cfile->const_pool_utf; scan ; scan = scan->next) {
+ struct utf_string *string = scan->data;
+ g_free (string->str);
+ }
+
+ if (cfile->const_pool_utf != NULL) {
+ g_slist_foreach (cfile->const_pool_utf, (GFunc)g_free, NULL);
+ g_slist_free (cfile->const_pool_utf);
+ }
+
+ if (cfile->fd != -1)
+ close (cfile->fd);
+
+ g_free (cfile);
+}
+
+
+/* The following function loads the utf8 strings and class structures from the
+ * class file. */
+static void
+load_constant_pool_utfs (JavaClassFile *cfile)
+{
+ guint8 tag;
+ guint16 i = 0; /* should be comparable with const_pool_count */
+
+ while ((i < cfile->const_pool_count - 1) && (read (cfile->fd, &tag, 1) != -1)) {
+ struct utf_string *txt = NULL;
+ struct class_info *class = NULL;
+
+ switch (tag) {
+ case CONST_CLASS:
+ class = g_new0 (struct class_info, 1);
+ class->index = i + 1;
+ if (read (cfile->fd, &class->name_index, 2) != 2) {
+ g_free (class);
+ return; /* error reading */
+ }
+ class->name_index = GUINT16_FROM_BE (class->name_index);
+ cfile->const_pool_class = g_slist_append (cfile->const_pool_class, class);
+ break;
+
+ case CONST_FIELDREF:
+ lseek (cfile->fd, CONST_FIELDREF_INFO, SEEK_CUR);
+ break;
+
+ case CONST_METHODREF:
+ lseek (cfile->fd, CONST_METHODREF_INFO, SEEK_CUR);
+ break;
+
+ case CONST_INTERFACEMETHODREF:
+ lseek (cfile->fd, CONST_INTERFACEMETHODREF_INFO, SEEK_CUR);
+ break;
+
+ case CONST_STRING:
+ lseek (cfile->fd, CONST_STRING_INFO, SEEK_CUR);
+ break;
+
+ case CONST_INTEGER:
+ lseek (cfile->fd, CONST_INTEGER_INFO, SEEK_CUR);
+ break;
+
+ case CONST_FLOAT:
+ lseek (cfile->fd, CONST_FLOAT_INFO, SEEK_CUR);
+ break;
+
+ case CONST_LONG:
+ lseek (cfile->fd, CONST_LONG_INFO, SEEK_CUR);
+ break;
+
+ case CONST_DOUBLE:
+ lseek (cfile->fd, CONST_DOUBLE_INFO, SEEK_CUR);
+ break;
+
+ case CONST_NAMEANDTYPE:
+ lseek (cfile->fd, CONST_NAMEANDTYPE_INFO, SEEK_CUR);
+ break;
+
+ case CONST_UTF8:
+ txt = g_new0 (struct utf_string, 1);
+ txt->index = i + 1;
+ if (read (cfile->fd, &(txt->length), 2) == -1) {
+ g_free (txt);
+ return; /* error while reading */
+ }
+ txt->length = GUINT16_FROM_BE (txt->length);
+ txt->str = g_new0 (char, txt->length);
+ if (read (cfile->fd, txt->str, txt->length) == -1) {
+ g_free (txt);
+ return; /* error while reading */
+ }
+ cfile->const_pool_utf = g_slist_append (cfile->const_pool_utf, txt);
+ break;
+
+ default:
+ return; /* error - unknown tag in class file */
+ break;
+ }
+ i++;
+ }
+
+#ifdef DEBUG
+ g_print( "Number of Entries: %d\n", i );
+#endif
+}
+
+
+static char*
+close_and_exit (JavaClassFile *cfile)
+{
+ java_class_file_free (cfile);
+ return NULL;
+}
+
+
+/* This function extracts the package name from a class file */
+char*
+get_package_name_from_class_file (char *fname)
+{
+ char *package = NULL;
+ JavaClassFile *cfile;
+ guint16 length = 0, end = 0, utf_index = 0;
+ guint32 magic;
+ guint16 major, minor, count;
+ int i = 0;
+
+ if (! g_file_test (fname, G_FILE_TEST_EXISTS))
+ return NULL;
+
+ cfile = java_class_file_new ();
+ cfile->fd = open (fname, O_RDONLY);
+ if (cfile->fd == -1)
+ return close_and_exit (cfile);
+
+ if ((i = read (cfile->fd, &magic, 4)) != 4)
+ return close_and_exit (cfile);
+ cfile->magic_no = GUINT32_FROM_BE (magic);
+
+ if (read (cfile->fd, &major, 2 ) != 2)
+ return close_and_exit (cfile);
+ cfile->major = GUINT16_FROM_BE (major);
+
+ if (read (cfile->fd, &minor, 2) != 2)
+ return close_and_exit (cfile);
+ cfile->minor = GUINT16_FROM_BE (minor);
+
+ if (read (cfile->fd, &count, 2) != 2)
+ return close_and_exit (cfile);
+ cfile->const_pool_count = GUINT16_FROM_BE(count);
+ load_constant_pool_utfs (cfile);
+
+ if (read (cfile->fd, &cfile->access_flags, 2) != 2)
+ return close_and_exit (cfile);
+ cfile->access_flags = GUINT16_FROM_BE (cfile->access_flags);
+
+ if (read (cfile->fd, &cfile->this_class, 2) != 2)
+ return close_and_exit (cfile);
+ cfile->this_class = GUINT16_FROM_BE(cfile->this_class);
+
+ /* now search for the class structure with index = cfile->this_class */
+
+ for (i = 0; (i < g_slist_length (cfile->const_pool_class)) && (utf_index == 0); i++ ) {
+ struct class_info *class = g_slist_nth_data (cfile->const_pool_class, i);
+ if (class->index == cfile->this_class)
+ utf_index = class->name_index; /* terminates loop */
+ }
+
+ /* now search for the utf8 string with index = utf_index */
+
+ for (i = 0; i < g_slist_length (cfile->const_pool_utf); i++) {
+ struct utf_string *data = g_slist_nth_data (cfile->const_pool_utf, i);
+ if (data->index == utf_index) {
+ package = g_strndup (data->str, data->length);
+ length = data->length;
+ break;
+ }
+ }
+
+ if (package != NULL) {
+ for (i = length; (i >= 0) && (end == 0); i-- )
+ if (package[i] == '/')
+ end = i;
+ package = g_strndup (package, end);
+ }
+
+ java_class_file_free (cfile);
+
+ return package;
+}
+
+
+/* This function consumes a comment from the java file
+ * multiline = TRUE implies that comment is multiline */
+static void
+consume_comment (int fdesc,
+ gboolean multiline)
+{
+ gboolean escaped = FALSE;
+ gboolean star = FALSE;
+ char ch;
+
+ while (read (fdesc, &ch, 1) == 1) {
+ switch (ch) {
+ case '/':
+ if (escaped)
+ break;
+ else if (star)
+ return;
+ break;
+
+ case '\n':
+ if (! multiline)
+ return;
+ break;
+
+ case '*':
+ escaped = FALSE;
+ star = TRUE;
+ break;
+
+ case '\\':
+ escaped = ! escaped;
+ break;
+
+ default:
+ escaped = FALSE;
+ star = FALSE;
+ break;
+ }
+ }
+}
+
+
+/* This function extracts package name from a java file */
+char*
+get_package_name_from_java_file (char *fname)
+{
+ char *package = NULL;
+ JavaClassFile *cfile;
+ gboolean prev_char_is_bslash = FALSE;
+ gboolean valid_char_found = FALSE;
+ char ch;
+
+ if (! g_file_test (fname, G_FILE_TEST_EXISTS))
+ return NULL;
+
+ cfile = java_class_file_new ();
+ cfile->fd = open (fname, O_RDONLY);
+ if (cfile->fd == -1)
+ return close_and_exit (cfile);
+
+ while (! valid_char_found && (read (cfile->fd, &ch, 1) == 1)) {
+ switch (ch) {
+ case '/':
+ if (prev_char_is_bslash == TRUE) {
+ consume_comment (cfile->fd, FALSE);
+ prev_char_is_bslash = FALSE;
+ }
+ else
+ prev_char_is_bslash = TRUE;
+ break;
+
+ case '*':
+ if (prev_char_is_bslash == TRUE)
+ consume_comment (cfile->fd, TRUE);
+ prev_char_is_bslash = FALSE;
+ break;
+
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ prev_char_is_bslash = FALSE;
+ break;
+
+ default:
+ prev_char_is_bslash = FALSE;
+ valid_char_found = TRUE;
+ break;
+ }
+ }
+
+ if (ch == 'p') {
+ char first_valid_word[8] = "";
+
+ first_valid_word[0] = 'p';
+ if (read (cfile->fd, &first_valid_word[1], 6) != 6)
+ return close_and_exit (cfile);
+
+ first_valid_word[7] = 0;
+ if (g_ascii_strcasecmp (first_valid_word, "package") == 0) {
+ char buffer[500];
+ int index = 0;
+
+ while (read (cfile->fd, &ch, 1) == 1) {
+ if (ch == ';')
+ break;
+ if (ch == '.')
+ buffer[index++] = '/';
+ else
+ buffer[index++] = ch;
+ }
+ buffer[index] = 0;
+ package = g_strdup (buffer);
+ }
+ }
+
+ java_class_file_free (cfile);
+
+ return package;
+}
diff --git a/src/java-utils.h b/src/java-utils.h
new file mode 100644
index 0000000..3cea4cb
--- /dev/null
+++ b/src/java-utils.h
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2006 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef JAVA_UTILS_H
+#define JAVA_UTILS_H
+
+
+char* get_package_name_from_class_file (char *fname);
+char* get_package_name_from_java_file (char *fname);
+
+
+#endif /* JAVA_UTILS_H */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..f94956a
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,1030 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <gio/gio.h>
+#include "file-utils.h"
+#include "glib-utils.h"
+#include "fr-command.h"
+#include "fr-command-ace.h"
+#include "fr-command-alz.h"
+#include "fr-command-ar.h"
+#include "fr-command-arj.h"
+#include "fr-command-cfile.h"
+#include "fr-command-cpio.h"
+#include "fr-command-dpkg.h"
+#include "fr-command-iso.h"
+#include "fr-command-jar.h"
+#include "fr-command-lha.h"
+#include "fr-command-rar.h"
+#include "fr-command-rpm.h"
+#include "fr-command-tar.h"
+#include "fr-command-unstuff.h"
+#include "fr-command-zip.h"
+#include "fr-command-zoo.h"
+#include "fr-command-7z.h"
+#include "fr-command-lrzip.h"
+#include "fr-process.h"
+#include "fr-stock.h"
+#include "mateconf-utils.h"
+#include "fr-window.h"
+#include "typedefs.h"
+#include "preferences.h"
+#include "file-data.h"
+#include "main.h"
+
+#include "eggsmclient.h"
+
+static void prepare_app (void);
+static void initialize_data (void);
+static void release_data (void);
+
+GList *WindowList = NULL;
+GList *CommandList = NULL;
+gint ForceDirectoryCreation;
+GHashTable *ProgramsCache = NULL;
+GPtrArray *Registered_Commands = NULL;
+
+static char **remaining_args;
+
+static char *add_to = NULL;
+static int add;
+static char *extract_to = NULL;
+static int extract;
+static int extract_here;
+static char *default_url = NULL;
+
+/* The capabilities are computed automatically in
+ * compute_supported_archive_types() so it's correct to initialize to 0 here. */
+FrMimeTypeDescription mime_type_desc[] = {
+ { "application/x-7z-compressed", ".7z", N_("7-Zip (.7z)"), 0 },
+ { "application/x-7z-compressed-tar", ".tar.7z", N_("Tar compressed with 7z (.tar.7z)"), 0 },
+ { "application/x-ace", ".ace", N_("Ace (.ace)"), 0 },
+ { "application/x-alz", ".alz", NULL, 0 },
+ { "application/x-ar", ".ar", N_("Ar (.ar)"), 0 },
+ { "application/x-arj", ".arj", N_("Arj (.arj)"), 0 },
+ { "application/x-bzip", ".bz2", NULL, 0 },
+ { "application/x-bzip-compressed-tar", ".tar.bz2", N_("Tar compressed with bzip2 (.tar.bz2)"), 0 },
+ { "application/x-bzip1", ".bz", NULL, 0 },
+ { "application/x-bzip1-compressed-tar", ".tar.bz", N_("Tar compressed with bzip (.tar.bz)"), 0 },
+ { "application/vnd.ms-cab-compressed", ".cab", N_("Cabinet (.cab)"), 0 },
+ { "application/x-cbr", ".cbr", N_("Rar Archived Comic Book (.cbr)"), 0 },
+ { "application/x-cbz", ".cbz", N_("Zip Archived Comic Book (.cbz)"), 0 },
+ { "application/x-cd-image", ".iso", NULL, 0 },
+ { "application/x-compress", ".Z", NULL, 0 },
+ { "application/x-compressed-tar", ".tar.gz", N_("Tar compressed with gzip (.tar.gz)"), 0 },
+ { "application/x-cpio", ".cpio", NULL, 0 },
+ { "application/x-deb", ".deb", NULL, 0 },
+ { "application/x-ear", ".ear", N_("Ear (.ear)"), 0 },
+ { "application/x-ms-dos-executable", ".exe", N_("Self-extracting zip (.exe)"), 0 },
+ { "application/x-gzip", ".gz", NULL, 0 },
+ { "application/x-java-archive", ".jar", N_("Jar (.jar)"), 0 },
+ { "application/x-lha", ".lzh", N_("Lha (.lzh)"), 0 },
+ { "application/x-lrzip", ".lrz", N_("Lrzip (.lrz)"), 0},
+ { "application/x-lrzip-compressed-tar", ".tar.lrz", N_("Tar compressed with lrzip (.tar.lrz)"), 0 },
+ { "application/x-lzip", ".lz", NULL, 0 },
+ { "application/x-lzip-compressed-tar", ".tar.lz", N_("Tar compressed with lzip (.tar.lz)"), 0 },
+ { "application/x-lzma", ".lzma", NULL, 0 },
+ { "application/x-lzma-compressed-tar", ".tar.lzma", N_("Tar compressed with lzma (.tar.lzma)"), 0 },
+ { "application/x-lzop", ".lzo", NULL, 0 },
+ { "application/x-lzop-compressed-tar", ".tar.lzo", N_("Tar compressed with lzop (.tar.lzo)"), 0 },
+ { "application/x-rar", ".rar", N_("Rar (.rar)"), 0 },
+ { "application/x-rpm", ".rpm", NULL, 0 },
+ { "application/x-rzip", ".rz", NULL, 0 },
+ { "application/x-tar", ".tar", N_("Tar uncompressed (.tar)"), 0 },
+ { "application/x-tarz", ".tar.Z", N_("Tar compressed with compress (.tar.Z)"), 0 },
+ { "application/x-stuffit", ".sit", NULL, 0 },
+ { "application/x-war", ".war", N_("War (.war)"), 0 },
+ { "application/x-xz", ".xz", N_("Xz (.xz)"), 0 },
+ { "application/x-xz-compressed-tar", ".tar.xz", N_("Tar compressed with xz (.tar.xz)"), 0 },
+ { "application/x-zoo", ".zoo", N_("Zoo (.zoo)"), 0 },
+ { "application/zip", ".zip", N_("Zip (.zip)"), 0 },
+ { NULL, NULL, NULL, 0 }
+};
+
+FrExtensionType file_ext_type[] = {
+ { ".7z", "application/x-7z-compressed" },
+ { ".ace", "application/x-ace" },
+ { ".alz", "application/x-alz" },
+ { ".ar", "application/x-ar" },
+ { ".arj", "application/x-arj" },
+ { ".bin", "application/x-stuffit" },
+ { ".bz", "application/x-bzip" },
+ { ".bz2", "application/x-bzip" },
+ { ".cab", "application/vnd.ms-cab-compressed" },
+ { ".cbr", "application/x-cbr" },
+ { ".cbz", "application/x-cbz" },
+ { ".cpio", "application/x-cpio" },
+ { ".deb", "application/x-deb" },
+ { ".ear", "application/x-ear" },
+ { ".exe", "application/x-ms-dos-executable" },
+ { ".gz", "application/x-gzip" },
+ { ".iso", "application/x-cd-image" },
+ { ".jar", "application/x-java-archive" },
+ { ".lha", "application/x-lha" },
+ { ".lrz", "application/x-lrzip" },
+ { ".lzh", "application/x-lha" },
+ { ".lz", "application/x-lzip" },
+ { ".lzma", "application/x-lzma" },
+ { ".lzo", "application/x-lzop" },
+ { ".rar", "application/x-rar" },
+ { ".rpm", "application/x-rpm" },
+ { ".rz", "application/x-rzip" },
+ { ".sit", "application/x-stuffit" },
+ { ".tar", "application/x-tar" },
+ { ".tar.bz", "application/x-bzip-compressed-tar" },
+ { ".tar.bz2", "application/x-bzip-compressed-tar" },
+ { ".tar.gz", "application/x-compressed-tar" },
+ { ".tar.lrz", "application/x-lrzip-compressed-tar" },
+ { ".tar.lz", "application/x-lzip-compressed-tar" },
+ { ".tar.lzma", "application/x-lzma-compressed-tar" },
+ { ".tar.lzo", "application/x-lzop-compressed-tar" },
+ { ".tar.7z", "application/x-7z-compressed-tar" },
+ { ".tar.xz", "application/x-xz-compressed-tar" },
+ { ".tar.Z", "application/x-tarz" },
+ { ".taz", "application/x-tarz" },
+ { ".tbz", "application/x-bzip-compressed-tar" },
+ { ".tbz2", "application/x-bzip-compressed-tar" },
+ { ".tgz", "application/x-compressed-tar" },
+ { ".txz", "application/x-xz-compressed-tar" },
+ { ".tlz", "application/x-lzip-compressed-tar" },
+ { ".tzma", "application/x-lzma-compressed-tar" },
+ { ".tzo", "application/x-lzop-compressed-tar" },
+ { ".war", "application/x-war" },
+ { ".xz", "application/x-xz" },
+ { ".z", "application/x-gzip" },
+ { ".Z", "application/x-compress" },
+ { ".zip", "application/zip" },
+ { ".zoo", "application/x-zoo" },
+ { NULL, NULL }
+};
+
+int single_file_save_type[64];
+int save_type[64];
+int open_type[64];
+int create_type[64];
+
+static const GOptionEntry options[] = {
+ { "add-to", 'a', 0, G_OPTION_ARG_STRING, &add_to,
+ N_("Add files to the specified archive and quit the program"),
+ N_("ARCHIVE") },
+
+ { "add", 'd', 0, G_OPTION_ARG_NONE, &add,
+ N_("Add files asking the name of the archive and quit the program"),
+ NULL },
+
+ { "extract-to", 'e', 0, G_OPTION_ARG_STRING, &extract_to,
+ N_("Extract archives to the specified folder and quit the program"),
+ N_("FOLDER") },
+
+ { "extract", 'f', 0, G_OPTION_ARG_NONE, &extract,
+ N_("Extract archives asking the destination folder and quit the program"),
+ NULL },
+
+ { "extract-here", 'h', 0, G_OPTION_ARG_NONE, &extract_here,
+ N_("Extract the contents of the archives in the archive folder and quit the program"),
+ NULL },
+
+ { "default-dir", '\0', 0, G_OPTION_ARG_STRING, &default_url,
+ N_("Default folder to use for the '--add' and '--extract' commands"),
+ N_("FOLDER") },
+
+ { "force", '\0', 0, G_OPTION_ARG_NONE, &ForceDirectoryCreation,
+ N_("Create destination folder without asking confirmation"),
+ NULL },
+
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining_args,
+ NULL,
+ NULL },
+
+ { NULL }
+};
+
+
+/* -- Main -- */
+
+
+static guint startup_id = 0;
+
+/* argv[0] from main(); used as the command to restart the program */
+static const char *program_argv0 = NULL;
+
+
+static gboolean
+startup_cb (gpointer data)
+{
+ g_source_remove (startup_id);
+ startup_id = 0;
+
+ initialize_data ();
+ prepare_app ();
+
+ return FALSE;
+}
+
+
+static void
+fr_save_state (EggSMClient *client, GKeyFile *state, gpointer user_data)
+{
+ /* discard command is automatically set by EggSMClient */
+
+ GList *window;
+ const char *argv[2] = { NULL };
+ guint i;
+
+ /* restart command */
+ argv[0] = program_argv0;
+ argv[1] = NULL;
+
+ egg_sm_client_set_restart_command (client, 1, argv);
+
+ /* state */
+ for (window = WindowList, i = 0; window; window = window->next, i++) {
+ FrWindow *session = window->data;
+ gchar *key;
+
+ key = g_strdup_printf ("archive%d", i);
+ if ((session->archive == NULL) || (session->archive->file == NULL)) {
+ g_key_file_set_string (state, "Session", key, "");
+ } else {
+ gchar *uri;
+
+ uri = g_file_get_uri (session->archive->file);
+ g_key_file_set_string (state, "Session", key, uri);
+ g_free (uri);
+ }
+ g_free (key);
+ }
+
+ g_key_file_set_integer (state, "Session", "archives", i);
+}
+
+int
+main (int argc, char **argv)
+{
+ GError *error = NULL;
+ EggSMClient *client = NULL;
+ GOptionContext *context = NULL;
+
+ program_argv0 = argv[0];
+
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ context = g_option_context_new (N_("- Create and modify an archive"));
+ g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+ g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
+
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ g_option_context_add_group (context, egg_sm_client_get_option_group ());
+
+ if (! g_option_context_parse (context, &argc, &argv, &error)) {
+ g_critical ("Failed to parse arguments: %s", error->message);
+ g_error_free (error);
+ g_option_context_free (context);
+ return EXIT_FAILURE;
+ }
+
+ g_option_context_free (context);
+
+ g_set_application_name (_("File Roller"));
+ gtk_window_set_default_icon_name ("file-roller");
+
+ client = egg_sm_client_get ();
+ g_signal_connect (client, "save-state", G_CALLBACK (fr_save_state), NULL);
+
+ gtk_icon_theme_append_search_path (gtk_icon_theme_get_default (),
+ PKG_DATA_DIR G_DIR_SEPARATOR_S "icons");
+
+ fr_stock_init ();
+ /* init_session (argv); */
+ startup_id = g_idle_add (startup_cb, NULL);
+ gtk_main ();
+ release_data ();
+
+ return 0;
+}
+
+/* Initialize application data. */
+
+
+static void
+initialize_data (void)
+{
+ eel_mateconf_monitor_add ("/apps/file-roller");
+ eel_mateconf_monitor_add (PREF_CAJA_CLICK_POLICY);
+
+ ProgramsCache = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+}
+
+/* Free application data. */
+
+
+void
+command_done (CommandData *cdata)
+{
+ if (cdata == NULL)
+ return;
+
+ if ((cdata->temp_dir != NULL) && path_is_dir (cdata->temp_dir)) {
+ char *argv[4];
+
+ argv[0] = "rm";
+ argv[1] = "-rf";
+ argv[2] = cdata->temp_dir;
+ argv[3] = NULL;
+ g_spawn_sync (g_get_tmp_dir (), argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL,
+ NULL, NULL, NULL,
+ NULL);
+ }
+
+ g_free (cdata->command);
+ if (cdata->app != NULL)
+ g_object_unref (cdata->app);
+ path_list_free (cdata->file_list);
+ g_free (cdata->temp_dir);
+ if (cdata->process != NULL)
+ g_object_unref (cdata->process);
+
+ CommandList = g_list_remove (CommandList, cdata);
+ g_free (cdata);
+}
+
+
+static void
+release_data ()
+{
+ g_hash_table_destroy (ProgramsCache);
+
+ eel_global_client_free ();
+
+ while (CommandList != NULL) {
+ CommandData *cdata = CommandList->data;
+ command_done (cdata);
+ }
+}
+
+/* Create the windows. */
+
+
+static void
+migrate_dir_from_to (const char *from_dir,
+ const char *to_dir)
+{
+ char *from_path;
+ char *to_path;
+
+ from_path = get_home_relative_path (from_dir);
+ to_path = get_home_relative_path (to_dir);
+
+ if (uri_is_dir (from_path) && ! uri_exists (to_path)) {
+ char *line;
+ char *e1;
+ char *e2;
+
+ e1 = g_shell_quote (from_path);
+ e2 = g_shell_quote (to_path);
+ line = g_strdup_printf ("mv -f %s %s", e1, e2);
+ g_free (e1);
+ g_free (e2);
+
+ g_spawn_command_line_sync (line, NULL, NULL, NULL, NULL);
+ g_free (line);
+ }
+
+ g_free (from_path);
+ g_free (to_path);
+}
+
+
+static void
+migrate_file_from_to (const char *from_file,
+ const char *to_file)
+{
+ char *from_path;
+ char *to_path;
+
+ from_path = get_home_relative_path (from_file);
+ to_path = get_home_relative_path (to_file);
+
+ if (uri_is_file (from_path) && ! uri_exists (to_path)) {
+ char *line;
+ char *e1;
+ char *e2;
+
+ e1 = g_shell_quote (from_path);
+ e2 = g_shell_quote (to_path);
+ line = g_strdup_printf ("mv -f %s %s", e1, e2);
+ g_free (e1);
+ g_free (e2);
+
+ g_spawn_command_line_sync (line, NULL, NULL, NULL, NULL);
+ g_free (line);
+ }
+
+ g_free (from_path);
+ g_free (to_path);
+}
+
+
+static void
+migrate_to_new_directories (void)
+{
+ migrate_dir_from_to (OLD_RC_OPTIONS_DIR, RC_OPTIONS_DIR);
+ migrate_file_from_to (OLD_RC_BOOKMARKS_FILE, RC_BOOKMARKS_FILE);
+ migrate_file_from_to (OLD_RC_RECENT_FILE, RC_RECENT_FILE);
+
+ eel_mateconf_set_boolean (PREF_MIGRATE_DIRECTORIES, FALSE);
+}
+
+
+/* -- FrRegisteredCommand -- */
+
+
+FrRegisteredCommand *
+fr_registered_command_new (GType command_type)
+{
+ FrRegisteredCommand *reg_com;
+ FrCommand *command;
+ const char **mime_types;
+ int i;
+
+ reg_com = g_new0 (FrRegisteredCommand, 1);
+ reg_com->ref = 1;
+ reg_com->type = command_type;
+ reg_com->caps = g_ptr_array_new ();
+ reg_com->packages = g_ptr_array_new ();
+
+ command = (FrCommand*) g_object_new (reg_com->type, NULL);
+ mime_types = fr_command_get_mime_types (command);
+ for (i = 0; mime_types[i] != NULL; i++) {
+ const char *mime_type;
+ FrMimeTypeCap *cap;
+ FrMimeTypePackages *packages;
+
+ mime_type = get_static_string (mime_types[i]);
+
+ cap = g_new0 (FrMimeTypeCap, 1);
+ cap->mime_type = mime_type;
+ cap->current_capabilities = fr_command_get_capabilities (command, mime_type, TRUE);
+ cap->potential_capabilities = fr_command_get_capabilities (command, mime_type, FALSE);
+ g_ptr_array_add (reg_com->caps, cap);
+
+ packages = g_new0 (FrMimeTypePackages, 1);
+ packages->mime_type = mime_type;
+ packages->packages = fr_command_get_packages (command, mime_type);
+ g_ptr_array_add (reg_com->packages, packages);
+ }
+
+ g_object_unref (command);
+
+ return reg_com;
+}
+
+
+void
+fr_registered_command_ref (FrRegisteredCommand *reg_com)
+{
+ reg_com->ref++;
+}
+
+
+void
+fr_registered_command_unref (FrRegisteredCommand *reg_com)
+{
+ if (--(reg_com->ref) != 0)
+ return;
+
+ g_ptr_array_foreach (reg_com->caps, (GFunc) g_free, NULL);
+ g_ptr_array_free (reg_com->caps, TRUE);
+ g_free (reg_com);
+}
+
+
+FrCommandCaps
+fr_registered_command_get_capabilities (FrRegisteredCommand *reg_com,
+ const char *mime_type)
+{
+ int i;
+
+ for (i = 0; i < reg_com->caps->len; i++) {
+ FrMimeTypeCap *cap;
+
+ cap = g_ptr_array_index (reg_com->caps, i);
+ if (strcmp (mime_type, cap->mime_type) == 0)
+ return cap->current_capabilities;
+ }
+
+ return FR_COMMAND_CAN_DO_NOTHING;
+}
+
+
+FrCommandCaps
+fr_registered_command_get_potential_capabilities (FrRegisteredCommand *reg_com,
+ const char *mime_type)
+{
+ int i;
+
+ if (mime_type == NULL)
+ return FR_COMMAND_CAN_DO_NOTHING;
+
+ for (i = 0; i < reg_com->caps->len; i++) {
+ FrMimeTypeCap *cap;
+
+ cap = g_ptr_array_index (reg_com->caps, i);
+ if ((cap->mime_type != NULL) && (strcmp (mime_type, cap->mime_type) == 0))
+ return cap->potential_capabilities;
+ }
+
+ return FR_COMMAND_CAN_DO_NOTHING;
+}
+
+
+void
+register_command (GType command_type)
+{
+ if (Registered_Commands == NULL)
+ Registered_Commands = g_ptr_array_sized_new (5);
+ g_ptr_array_add (Registered_Commands, fr_registered_command_new (command_type));
+}
+
+
+gboolean
+unregister_command (GType command_type)
+{
+ int i;
+
+ for (i = 0; i < Registered_Commands->len; i++) {
+ FrRegisteredCommand *command;
+
+ command = g_ptr_array_index (Registered_Commands, i);
+ if (command->type == command_type) {
+ g_ptr_array_remove_index (Registered_Commands, i);
+ fr_registered_command_unref (command);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void
+register_commands (void)
+{
+ /* The order here is important. Commands registered earlier have higher
+ * priority. However commands that can read and write a file format
+ * have higher priority over commands that can only read the same
+ * format, regardless of the registration order. */
+
+ register_command (FR_TYPE_COMMAND_TAR);
+ register_command (FR_TYPE_COMMAND_CFILE);
+ register_command (FR_TYPE_COMMAND_7Z);
+ register_command (FR_TYPE_COMMAND_DPKG);
+
+ register_command (FR_TYPE_COMMAND_ACE);
+ register_command (FR_TYPE_COMMAND_ALZ);
+ register_command (FR_TYPE_COMMAND_AR);
+ register_command (FR_TYPE_COMMAND_ARJ);
+ register_command (FR_TYPE_COMMAND_CPIO);
+ register_command (FR_TYPE_COMMAND_ISO);
+ register_command (FR_TYPE_COMMAND_JAR);
+ register_command (FR_TYPE_COMMAND_LHA);
+ register_command (FR_TYPE_COMMAND_RAR);
+ register_command (FR_TYPE_COMMAND_RPM);
+ register_command (FR_TYPE_COMMAND_UNSTUFF);
+ register_command (FR_TYPE_COMMAND_ZIP);
+ register_command (FR_TYPE_COMMAND_LRZIP);
+ register_command (FR_TYPE_COMMAND_ZOO);
+}
+
+
+GType
+get_command_type_from_mime_type (const char *mime_type,
+ FrCommandCaps requested_capabilities)
+{
+ int i;
+
+ if (mime_type == NULL)
+ return 0;
+
+ for (i = 0; i < Registered_Commands->len; i++) {
+ FrRegisteredCommand *command;
+ FrCommandCaps capabilities;
+
+ command = g_ptr_array_index (Registered_Commands, i);
+ capabilities = fr_registered_command_get_capabilities (command, mime_type);
+
+ /* the command must support all the requested capabilities */
+ if (((capabilities ^ requested_capabilities) & requested_capabilities) == 0)
+ return command->type;
+ }
+
+ return 0;
+}
+
+
+GType
+get_preferred_command_for_mime_type (const char *mime_type,
+ FrCommandCaps requested_capabilities)
+{
+ int i;
+
+ for (i = 0; i < Registered_Commands->len; i++) {
+ FrRegisteredCommand *command;
+ FrCommandCaps capabilities;
+
+ command = g_ptr_array_index (Registered_Commands, i);
+ capabilities = fr_registered_command_get_potential_capabilities (command, mime_type);
+
+ /* the command must support all the requested capabilities */
+ if (((capabilities ^ requested_capabilities) & requested_capabilities) == 0)
+ return command->type;
+ }
+
+ return 0;
+}
+
+
+void
+update_registered_commands_capabilities (void)
+{
+ int i;
+
+ g_hash_table_remove_all (ProgramsCache);
+
+ for (i = 0; i < Registered_Commands->len; i++) {
+ FrRegisteredCommand *reg_com;
+ FrCommand *command;
+ int j;
+
+ reg_com = g_ptr_array_index (Registered_Commands, i);
+ command = (FrCommand*) g_object_new (reg_com->type, NULL);
+ for (j = 0; j < reg_com->caps->len; j++) {
+ FrMimeTypeCap *cap = g_ptr_array_index (reg_com->caps, j);
+
+ cap->current_capabilities = fr_command_get_capabilities (command, cap->mime_type, TRUE);
+ cap->potential_capabilities = fr_command_get_capabilities (command, cap->mime_type, FALSE);
+ }
+
+ g_object_unref (command);
+ }
+}
+
+
+const char *
+get_mime_type_from_extension (const char *ext)
+{
+ int i;
+
+ if (ext == NULL)
+ return NULL;
+
+ for (i = G_N_ELEMENTS (file_ext_type) - 1; i >= 0; i--) {
+ if (file_ext_type[i].ext == NULL)
+ continue;
+ if (strcasecmp (ext, file_ext_type[i].ext) == 0)
+ return get_static_string (file_ext_type[i].mime_type);
+ }
+
+ return NULL;
+}
+
+
+const char *
+get_archive_filename_extension (const char *filename)
+{
+ const char *ext;
+ int i;
+
+ if (filename == NULL)
+ return NULL;
+
+ ext = get_file_extension (filename);
+ if (ext == NULL)
+ return NULL;
+
+ for (i = G_N_ELEMENTS (file_ext_type) - 1; i >= 0; i--) {
+ if (file_ext_type[i].ext == NULL)
+ continue;
+ if (strcasecmp (ext, file_ext_type[i].ext) == 0)
+ return ext;
+ }
+
+ return NULL;
+}
+
+
+int
+get_mime_type_index (const char *mime_type)
+{
+ int i;
+
+ for (i = 0; mime_type_desc[i].mime_type != NULL; i++)
+ if (strcmp (mime_type_desc[i].mime_type, mime_type) == 0)
+ return i;
+ return -1;
+}
+
+
+static void
+add_if_non_present (int *a,
+ int *n,
+ int o)
+{
+ int i;
+
+ for (i = 0; i < *n; i++) {
+ if (a[i] == o)
+ return;
+ }
+ a[*n] = o;
+ *n = *n + 1;
+}
+
+
+static int
+cmp_mime_type_by_extension (const void *p1,
+ const void *p2)
+{
+ int i1 = * (int*) p1;
+ int i2 = * (int*) p2;
+
+ return strcmp (mime_type_desc[i1].default_ext, mime_type_desc[i2].default_ext);
+}
+
+
+static int
+cmp_mime_type_by_description (const void *p1,
+ const void *p2)
+{
+ int i1 = * (int*) p1;
+ int i2 = * (int*) p2;
+
+ return g_utf8_collate (_(mime_type_desc[i1].name), _(mime_type_desc[i2].name));
+}
+
+
+static void
+sort_mime_types (int *a,
+ int(*compar)(const void *, const void *))
+{
+ int n = 0;
+
+ while (a[n] != -1)
+ n++;
+ qsort (a, n, sizeof (int), compar);
+}
+
+
+void
+sort_mime_types_by_extension (int *a)
+{
+ sort_mime_types (a, cmp_mime_type_by_extension);
+}
+
+
+void
+sort_mime_types_by_description (int *a)
+{
+ sort_mime_types (a, cmp_mime_type_by_description);
+}
+
+
+static void
+compute_supported_archive_types (void)
+{
+ int sf_i = 0, s_i = 0, o_i = 0, c_i = 0;
+ int i;
+
+ for (i = 0; i < Registered_Commands->len; i++) {
+ FrRegisteredCommand *reg_com;
+ int j;
+
+ reg_com = g_ptr_array_index (Registered_Commands, i);
+ for (j = 0; j < reg_com->caps->len; j++) {
+ FrMimeTypeCap *cap;
+ int idx;
+
+ cap = g_ptr_array_index (reg_com->caps, j);
+ idx = get_mime_type_index (cap->mime_type);
+ if (idx < 0) {
+ g_warning ("mime type not recognized: %s", cap->mime_type);
+ continue;
+ }
+ mime_type_desc[idx].capabilities |= cap->current_capabilities;
+ if (cap->current_capabilities & FR_COMMAND_CAN_READ)
+ add_if_non_present (open_type, &o_i, idx);
+ if (cap->current_capabilities & FR_COMMAND_CAN_WRITE) {
+ if (cap->current_capabilities & FR_COMMAND_CAN_ARCHIVE_MANY_FILES) {
+ add_if_non_present (save_type, &s_i, idx);
+ if (cap->current_capabilities & FR_COMMAND_CAN_WRITE)
+ add_if_non_present (create_type, &c_i, idx);
+ }
+ add_if_non_present (single_file_save_type, &sf_i, idx);
+ }
+ }
+ }
+
+ open_type[o_i] = -1;
+ save_type[s_i] = -1;
+ single_file_save_type[sf_i] = -1;
+ create_type[c_i] = -1;
+}
+
+
+static char *
+get_uri_from_command_line (const char *path)
+{
+ GFile *file;
+ char *uri;
+
+ file = g_file_new_for_commandline_arg (path);
+ uri = g_file_get_uri (file);
+ g_object_unref (file);
+
+ return uri;
+}
+
+
+static void
+fr_restore_session (EggSMClient *client)
+{
+ GKeyFile *state = NULL;
+ guint i;
+
+ state = egg_sm_client_get_state_file (client);
+
+ i = g_key_file_get_integer (state, "Session", "archives", NULL);
+
+ for (; i > 0; i--) {
+ GtkWidget *window;
+ gchar *key, *archive;
+
+ key = g_strdup_printf ("archive%d", i);
+ archive = g_key_file_get_string (state, "Session", key, NULL);
+ g_free (key);
+
+ window = fr_window_new ();
+ gtk_widget_show (window);
+ if (strlen (archive))
+ fr_window_archive_open (FR_WINDOW (window), archive, GTK_WINDOW (window));
+
+ g_free (archive);
+ }
+}
+
+static void
+prepare_app (void)
+{
+ char *uri;
+ char *extract_to_path = NULL;
+ char *add_to_path = NULL;
+ EggSMClient *client = NULL;
+
+ /* create the config dir if necessary. */
+
+ uri = get_home_relative_uri (RC_DIR);
+
+ if (uri_is_file (uri)) { /* before the mateconf port this was a file, now it's folder. */
+ GFile *file;
+
+ file = g_file_new_for_uri (uri);
+ g_file_delete (file, NULL, NULL);
+ g_object_unref (file);
+ }
+
+ ensure_dir_exists (uri, 0700, NULL);
+ g_free (uri);
+
+ if (eel_mateconf_get_boolean (PREF_MIGRATE_DIRECTORIES, TRUE))
+ migrate_to_new_directories ();
+
+ register_commands ();
+ compute_supported_archive_types ();
+
+ client = egg_sm_client_get ();
+ if (egg_sm_client_is_resumed (client)) {
+ fr_restore_session (client);
+ return;
+ }
+
+ /**/
+
+ if (remaining_args == NULL) { /* No archive specified. */
+ gtk_widget_show (fr_window_new ());
+ return;
+ }
+
+ if (extract_to != NULL)
+ extract_to_path = get_uri_from_command_line (extract_to);
+
+ if (add_to != NULL)
+ add_to_path = get_uri_from_command_line (add_to);
+
+ if ((add_to != NULL) || (add == 1)) { /* Add files to an archive */
+ GtkWidget *window;
+ GList *file_list = NULL;
+ const char *filename;
+ int i = 0;
+
+ window = fr_window_new ();
+ if (default_url != NULL)
+ fr_window_set_default_dir (FR_WINDOW (window), default_url, TRUE);
+
+ while ((filename = remaining_args[i++]) != NULL)
+ file_list = g_list_prepend (file_list, get_uri_from_command_line (filename));
+ file_list = g_list_reverse (file_list);
+
+ fr_window_new_batch (FR_WINDOW (window));
+ fr_window_set_batch__add (FR_WINDOW (window), add_to_path, file_list);
+ fr_window_append_batch_action (FR_WINDOW (window),
+ FR_BATCH_ACTION_QUIT,
+ NULL,
+ NULL);
+ fr_window_start_batch (FR_WINDOW (window));
+ }
+ else if ((extract_to != NULL)
+ || (extract == 1)
+ || (extract_here == 1)) { /* Extract all archives. */
+ GtkWidget *window;
+ const char *archive;
+ int i = 0;
+
+ window = fr_window_new ();
+ if (default_url != NULL)
+ fr_window_set_default_dir (FR_WINDOW (window), default_url, TRUE);
+
+ fr_window_new_batch (FR_WINDOW (window));
+ while ((archive = remaining_args[i++]) != NULL) {
+ char *archive_uri;
+
+ archive_uri = get_uri_from_command_line (archive);
+ if (extract_here == 1)
+ fr_window_set_batch__extract_here (FR_WINDOW (window),
+ archive_uri);
+ else
+ fr_window_set_batch__extract (FR_WINDOW (window),
+ archive_uri,
+ extract_to_path);
+ g_free (archive_uri);
+ }
+ fr_window_append_batch_action (FR_WINDOW (window),
+ FR_BATCH_ACTION_QUIT,
+ NULL,
+ NULL);
+
+ fr_window_start_batch (FR_WINDOW (window));
+ }
+ else { /* Open each archive in a window */
+ const char *filename = NULL;
+
+ int i = 0;
+ while ((filename = remaining_args[i++]) != NULL) {
+ GtkWidget *window;
+ GFile *file;
+ char *uri;
+
+ window = fr_window_new ();
+ gtk_widget_show (window);
+
+ file = g_file_new_for_commandline_arg (filename);
+ uri = g_file_get_uri (file);
+ fr_window_archive_open (FR_WINDOW (window), uri, GTK_WINDOW (window));
+ g_free (uri);
+ g_object_unref (file);
+ }
+ }
+
+ g_free (add_to_path);
+ g_free (extract_to_path);
+}
diff --git a/src/main.h b/src/main.h
new file mode 100644
index 0000000..fa88514
--- /dev/null
+++ b/src/main.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2008 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef MAIN_H
+#define MAIN_H
+
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include "preferences.h"
+#include "fr-process.h"
+#include "fr-window.h"
+
+
+typedef struct {
+ FrWindow *window;
+ FrProcess *process;
+ char *filename;
+ char *e_filename;
+ char *temp_dir;
+} ViewerData;
+
+typedef struct {
+ FrWindow *window;
+ FrProcess *process;
+ char *command;
+ GAppInfo *app;
+ GList *file_list;
+ char *temp_dir;
+} CommandData;
+
+
+void viewer_done (ViewerData *vdata);
+void command_done (CommandData *cdata);
+
+
+extern GList *WindowList;
+extern GList *CommandList;
+extern gint ForceDirectoryCreation;
+extern GHashTable *ProgramsCache;
+extern GPtrArray *Registered_Commands;
+
+extern FrMimeTypeDescription mime_type_desc[];
+extern FrExtensionType file_ext_type[];
+extern int single_file_save_type[]; /* File types that can be saved when
+ * a single file is selected.
+ * Includes single file compressors
+ * such as gzip, compress, etc. */
+extern int save_type[]; /* File types that can be saved. */
+extern int open_type[]; /* File types that can be opened. */
+extern int create_type[]; /* File types that can be created. */
+
+GType get_command_type_from_mime_type (const char *mime_type,
+ FrCommandCaps requested_capabilities);
+GType get_preferred_command_for_mime_type (const char *mime_type,
+ FrCommandCaps requested_capabilities);
+void update_registered_commands_capabilities (void);
+const char * get_mime_type_from_extension (const char *ext);
+const char * get_archive_filename_extension (const char *uri);
+int get_mime_type_index (const char *mime_type);
+void sort_mime_types_by_extension (int *a);
+void sort_mime_types_by_description (int *a);
+
+#endif /* MAIN_H */
diff --git a/src/mateconf-utils.c b/src/mateconf-utils.c
new file mode 100644
index 0000000..f851200
--- /dev/null
+++ b/src/mateconf-utils.c
@@ -0,0 +1,862 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2002, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+/* eel-mateconf-extensions.c - Stuff to make MateConf easier to use.
+
+ Copyright (C) 2000, 2001 Eazel, Inc.
+
+ The Mate Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Mate Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Mate Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: Ramiro Estrugo <[email protected]>
+*/
+
+/* Modified by Paolo Bacchilega <[email protected]> for File Roller. */
+
+#include <config.h>
+#include <string.h>
+#include <errno.h>
+
+#include <mateconf/mateconf-client.h>
+#include <mateconf/mateconf.h>
+#include "mateconf-utils.h"
+#include "gtk-utils.h"
+#include "fr-error.h"
+
+static MateConfClient *global_mateconf_client = NULL;
+
+
+void
+eel_global_client_free (void)
+{
+ if (global_mateconf_client == NULL) {
+ return;
+ }
+
+ g_object_unref (global_mateconf_client);
+ global_mateconf_client = NULL;
+}
+
+
+MateConfClient *
+eel_mateconf_client_get_global (void)
+{
+ /* Initialize mateconf if needed */
+ if (!mateconf_is_initialized ()) {
+ char *argv[] = { "eel-preferences", NULL };
+ GError *error = NULL;
+
+ if (!mateconf_init (1, argv, &error)) {
+ if (eel_mateconf_handle_error (&error)) {
+ return NULL;
+ }
+ }
+ }
+
+ if (global_mateconf_client == NULL)
+ global_mateconf_client = mateconf_client_get_default ();
+
+ return global_mateconf_client;
+}
+
+
+gboolean
+eel_mateconf_handle_error (GError **error)
+{
+ static gboolean shown_dialog = FALSE;
+
+ g_return_val_if_fail (error != NULL, FALSE);
+
+ if (*error != NULL) {
+ g_warning ("MateConf error:\n %s", (*error)->message);
+ if (! shown_dialog) {
+ GtkWidget *d;
+ shown_dialog = TRUE;
+ d = _gtk_error_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ NULL,
+ "MateConf error",
+ "MateConf error: %s\n"
+ "All further errors "
+ "shown only on terminal",
+ (*error)->message);
+ g_signal_connect (d, "response",
+ G_CALLBACK (gtk_widget_destroy),
+ NULL);
+
+ gtk_widget_show (d);
+ }
+
+ g_error_free (*error);
+ *error = NULL;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static gboolean
+check_type (const char *key,
+ MateConfValue *val,
+ MateConfValueType t,
+ GError **err)
+{
+ if (val->type != t) {
+ g_set_error (err,
+ FR_ERROR,
+ errno,
+ "Type mismatch for key %s",
+ key);
+ return FALSE;
+ } else
+ return TRUE;
+}
+
+
+void
+eel_mateconf_set_boolean (const char *key,
+ gboolean boolean_value)
+{
+ MateConfClient *client;
+ GError *error = NULL;
+
+ g_return_if_fail (key != NULL);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_if_fail (client != NULL);
+
+ mateconf_client_set_bool (client, key, boolean_value, &error);
+ eel_mateconf_handle_error (&error);
+}
+
+
+gboolean
+eel_mateconf_get_boolean (const char *key,
+ gboolean def)
+{
+ GError *error = NULL;
+ gboolean result = def;
+ MateConfClient *client;
+ MateConfValue *val;
+
+ g_return_val_if_fail (key != NULL, def);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_val_if_fail (client != NULL, def);
+
+ val = mateconf_client_get (client, key, &error);
+
+ if (val != NULL) {
+ if (check_type (key, val, MATECONF_VALUE_BOOL, &error))
+ result = mateconf_value_get_bool (val);
+ else
+ eel_mateconf_handle_error (&error);
+ mateconf_value_free (val);
+
+ } else if (error != NULL)
+ eel_mateconf_handle_error (&error);
+
+ return result;
+}
+
+
+void
+eel_mateconf_set_integer (const char *key,
+ int int_value)
+{
+ MateConfClient *client;
+ GError *error = NULL;
+
+ g_return_if_fail (key != NULL);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_if_fail (client != NULL);
+
+ mateconf_client_set_int (client, key, int_value, &error);
+ eel_mateconf_handle_error (&error);
+}
+
+
+int
+eel_mateconf_get_integer (const char *key,
+ int def)
+{
+ GError *error = NULL;
+ int result = def;
+ MateConfClient *client;
+ MateConfValue *val;
+
+ g_return_val_if_fail (key != NULL, def);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_val_if_fail (client != NULL, def);
+
+ val = mateconf_client_get (client, key, &error);
+
+ if (val != NULL) {
+ if (check_type (key, val, MATECONF_VALUE_INT, &error))
+ result = mateconf_value_get_int (val);
+ else
+ eel_mateconf_handle_error (&error);
+ mateconf_value_free (val);
+
+ } else if (error != NULL)
+ eel_mateconf_handle_error (&error);
+
+ return result;
+}
+
+
+void
+eel_mateconf_set_float (const char *key,
+ float float_value)
+{
+ MateConfClient *client;
+ GError *error = NULL;
+
+ g_return_if_fail (key != NULL);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_if_fail (client != NULL);
+
+ mateconf_client_set_float (client, key, float_value, &error);
+ eel_mateconf_handle_error (&error);
+}
+
+
+float
+eel_mateconf_get_float (const char *key,
+ float def)
+{
+ GError *error = NULL;
+ float result = def;
+ MateConfClient *client;
+ MateConfValue *val;
+
+ g_return_val_if_fail (key != NULL, def);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_val_if_fail (client != NULL, def);
+
+ val = mateconf_client_get (client, key, &error);
+
+ if (val != NULL) {
+ if (check_type (key, val, MATECONF_VALUE_FLOAT, &error))
+ result = mateconf_value_get_float (val);
+ else
+ eel_mateconf_handle_error (&error);
+ mateconf_value_free (val);
+
+ } else if (error != NULL)
+ eel_mateconf_handle_error (&error);
+
+ return result;
+}
+
+
+void
+eel_mateconf_set_string (const char *key,
+ const char *string_value)
+{
+ MateConfClient *client;
+ GError *error = NULL;
+
+ g_return_if_fail (key != NULL);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_if_fail (client != NULL);
+
+ mateconf_client_set_string (client, key, string_value, &error);
+ eel_mateconf_handle_error (&error);
+}
+
+
+char *
+eel_mateconf_get_string (const char *key,
+ const char *def)
+{
+ GError *error = NULL;
+ char *result;
+ MateConfClient *client;
+ char *val;
+
+ if (def != NULL)
+ result = g_strdup (def);
+ else
+ result = NULL;
+
+ g_return_val_if_fail (key != NULL, result);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_val_if_fail (client != NULL, result);
+
+ val = mateconf_client_get_string (client, key, &error);
+
+ if (val != NULL) {
+ g_return_val_if_fail (error == NULL, result);
+ g_free (result);
+ result = g_strdup (val);
+ g_free (val);
+ } else if (error != NULL)
+ eel_mateconf_handle_error (&error);
+
+ return result;
+}
+
+
+void
+eel_mateconf_set_locale_string (const char *key,
+ const char *string_value)
+{
+ char *utf8;
+
+ utf8 = g_locale_to_utf8 (string_value, -1, 0, 0, 0);
+
+ if (utf8 != NULL) {
+ eel_mateconf_set_string (key, utf8);
+ g_free (utf8);
+ }
+}
+
+
+char *
+eel_mateconf_get_locale_string (const char *key,
+ const char *def)
+{
+ char *utf8;
+ char *result;
+
+ utf8 = eel_mateconf_get_string (key, def);
+
+ if (utf8 == NULL)
+ return NULL;
+
+ result = g_locale_from_utf8 (utf8, -1, 0, 0, 0);
+ g_free (utf8);
+
+ return result;
+}
+
+
+void
+eel_mateconf_set_string_list (const char *key,
+ const GSList *slist)
+{
+ MateConfClient *client;
+ GError *error;
+
+ g_return_if_fail (key != NULL);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_if_fail (client != NULL);
+
+ error = NULL;
+ mateconf_client_set_list (client, key, MATECONF_VALUE_STRING,
+ /* Need cast cause of MateConf api bug */
+ (GSList *) slist,
+ &error);
+ eel_mateconf_handle_error (&error);
+}
+
+
+GSList *
+eel_mateconf_get_string_list (const char *key)
+{
+ GSList *slist;
+ MateConfClient *client;
+ GError *error;
+
+ g_return_val_if_fail (key != NULL, NULL);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_val_if_fail (client != NULL, NULL);
+
+ error = NULL;
+ slist = mateconf_client_get_list (client, key, MATECONF_VALUE_STRING, &error);
+ if (eel_mateconf_handle_error (&error)) {
+ slist = NULL;
+ }
+
+ return slist;
+}
+
+
+GSList *
+eel_mateconf_get_locale_string_list (const char *key)
+{
+ GSList *utf8_slist, *slist, *scan;
+
+ utf8_slist = eel_mateconf_get_string_list (key);
+
+ slist = NULL;
+ for (scan = utf8_slist; scan; scan = scan->next) {
+ char *utf8 = scan->data;
+ char *locale = g_locale_from_utf8 (utf8, -1, 0, 0, 0);
+ slist = g_slist_prepend (slist, locale);
+ }
+
+ g_slist_foreach (utf8_slist, (GFunc) g_free, NULL);
+ g_slist_free (utf8_slist);
+
+ return g_slist_reverse (slist);
+}
+
+
+void
+eel_mateconf_set_locale_string_list (const char *key,
+ const GSList *string_list_value)
+{
+ GSList *utf8_slist;
+ const GSList *scan;
+
+ utf8_slist = NULL;
+ for (scan = string_list_value; scan; scan = scan->next) {
+ char *locale = scan->data;
+ char *utf8 = g_locale_to_utf8 (locale, -1, 0, 0, 0);
+ utf8_slist = g_slist_prepend (utf8_slist, utf8);
+ }
+
+ utf8_slist = g_slist_reverse (utf8_slist);
+
+ eel_mateconf_set_string_list (key, utf8_slist);
+
+ g_slist_foreach (utf8_slist, (GFunc) g_free, NULL);
+ g_slist_free (utf8_slist);
+}
+
+
+gboolean
+eel_mateconf_is_default (const char *key)
+{
+ gboolean result;
+ MateConfValue *value;
+ GError *error = NULL;
+
+ g_return_val_if_fail (key != NULL, FALSE);
+
+ value = mateconf_client_get_without_default (eel_mateconf_client_get_global (), key, &error);
+
+ if (eel_mateconf_handle_error (&error)) {
+ if (value != NULL) {
+ mateconf_value_free (value);
+ }
+ return FALSE;
+ }
+
+ result = (value == NULL);
+ eel_mateconf_value_free (value);
+ return result;
+}
+
+
+gboolean
+eel_mateconf_monitor_add (const char *directory)
+{
+ GError *error = NULL;
+ MateConfClient *client;
+
+ g_return_val_if_fail (directory != NULL, FALSE);
+
+ client = mateconf_client_get_default ();
+ g_return_val_if_fail (client != NULL, FALSE);
+
+ mateconf_client_add_dir (client,
+ directory,
+ MATECONF_CLIENT_PRELOAD_NONE,
+ &error);
+
+ if (eel_mateconf_handle_error (&error)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+gboolean
+eel_mateconf_monitor_remove (const char *directory)
+{
+ GError *error = NULL;
+ MateConfClient *client;
+
+ if (directory == NULL) {
+ return FALSE;
+ }
+
+ client = mateconf_client_get_default ();
+ g_return_val_if_fail (client != NULL, FALSE);
+
+ mateconf_client_remove_dir (client,
+ directory,
+ &error);
+
+ if (eel_mateconf_handle_error (&error)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void
+eel_mateconf_preload_cache (const char *directory,
+ MateConfClientPreloadType preload_type)
+{
+ GError *error = NULL;
+ MateConfClient *client;
+
+ if (directory == NULL) {
+ return;
+ }
+
+ client = mateconf_client_get_default ();
+ g_return_if_fail (client != NULL);
+
+ mateconf_client_preload (client,
+ directory,
+ preload_type,
+ &error);
+
+ eel_mateconf_handle_error (&error);
+}
+
+
+void
+eel_mateconf_suggest_sync (void)
+{
+ MateConfClient *client;
+ GError *error = NULL;
+
+ client = eel_mateconf_client_get_global ();
+ g_return_if_fail (client != NULL);
+
+ mateconf_client_suggest_sync (client, &error);
+ eel_mateconf_handle_error (&error);
+}
+
+
+MateConfValue*
+eel_mateconf_get_value (const char *key)
+{
+ MateConfValue *value = NULL;
+ MateConfClient *client;
+ GError *error = NULL;
+
+ g_return_val_if_fail (key != NULL, NULL);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_val_if_fail (client != NULL, NULL);
+
+ value = mateconf_client_get (client, key, &error);
+
+ if (eel_mateconf_handle_error (&error)) {
+ if (value != NULL) {
+ mateconf_value_free (value);
+ value = NULL;
+ }
+ }
+
+ return value;
+}
+
+
+MateConfValue*
+eel_mateconf_get_default_value (const char *key)
+{
+ MateConfValue *value = NULL;
+ MateConfClient *client;
+ GError *error = NULL;
+
+ g_return_val_if_fail (key != NULL, NULL);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_val_if_fail (client != NULL, NULL);
+
+ value = mateconf_client_get_default_from_schema (client, key, &error);
+
+ if (eel_mateconf_handle_error (&error)) {
+ if (value != NULL) {
+ mateconf_value_free (value);
+ value = NULL;
+ }
+ }
+
+ return value;
+}
+
+
+static int
+eel_strcmp (const char *string_a, const char *string_b)
+{
+ /* FIXME bugzilla.eazel.com 5450: Maybe we need to make this
+ * treat 'NULL < ""', or have a flavor that does that. If we
+ * didn't have code that already relies on 'NULL == ""', I
+ * would change it right now.
+ */
+ return strcmp (string_a == NULL ? "" : string_a,
+ string_b == NULL ? "" : string_b);
+}
+
+
+static gboolean
+eel_str_is_equal (const char *string_a, const char *string_b)
+{
+ /* FIXME bugzilla.eazel.com 5450: Maybe we need to make this
+ * treat 'NULL != ""', or have a flavor that does that. If we
+ * didn't have code that already relies on 'NULL == ""', I
+ * would change it right now.
+ */
+ return eel_strcmp (string_a, string_b) == 0;
+}
+
+
+static gboolean
+simple_value_is_equal (const MateConfValue *a,
+ const MateConfValue *b)
+{
+ g_return_val_if_fail (a != NULL, FALSE);
+ g_return_val_if_fail (b != NULL, FALSE);
+
+ switch (a->type) {
+ case MATECONF_VALUE_STRING:
+ return eel_str_is_equal (mateconf_value_get_string (a),
+ mateconf_value_get_string (b));
+ break;
+
+ case MATECONF_VALUE_INT:
+ return mateconf_value_get_int (a) ==
+ mateconf_value_get_int (b);
+ break;
+
+ case MATECONF_VALUE_FLOAT:
+ return mateconf_value_get_float (a) ==
+ mateconf_value_get_float (b);
+ break;
+
+ case MATECONF_VALUE_BOOL:
+ return mateconf_value_get_bool (a) ==
+ mateconf_value_get_bool (b);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ return FALSE;
+}
+
+
+gboolean
+eel_mateconf_value_is_equal (const MateConfValue *a,
+ const MateConfValue *b)
+{
+ GSList *node_a;
+ GSList *node_b;
+
+ if (a == NULL && b == NULL) {
+ return TRUE;
+ }
+
+ if (a == NULL || b == NULL) {
+ return FALSE;
+ }
+
+ if (a->type != b->type) {
+ return FALSE;
+ }
+
+ switch (a->type) {
+ case MATECONF_VALUE_STRING:
+ case MATECONF_VALUE_INT:
+ case MATECONF_VALUE_FLOAT:
+ case MATECONF_VALUE_BOOL:
+ return simple_value_is_equal (a, b);
+ break;
+
+ case MATECONF_VALUE_LIST:
+ if (mateconf_value_get_list_type (a) !=
+ mateconf_value_get_list_type (b)) {
+ return FALSE;
+ }
+
+ node_a = mateconf_value_get_list (a);
+ node_b = mateconf_value_get_list (b);
+
+ if (node_a == NULL && node_b == NULL) {
+ return TRUE;
+ }
+
+ if (g_slist_length (node_a) !=
+ g_slist_length (node_b)) {
+ return FALSE;
+ }
+
+ for (;
+ node_a != NULL && node_b != NULL;
+ node_a = node_a->next, node_b = node_b->next) {
+ g_assert (node_a->data != NULL);
+ g_assert (node_b->data != NULL);
+ if (!simple_value_is_equal (node_a->data, node_b->data)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ default:
+ /* FIXME: pair ? */
+ g_assert (0);
+ break;
+ }
+
+ g_assert_not_reached ();
+ return FALSE;
+}
+
+
+void
+eel_mateconf_value_free (MateConfValue *value)
+{
+ if (value == NULL) {
+ return;
+ }
+
+ mateconf_value_free (value);
+}
+
+
+guint
+eel_mateconf_notification_add (const char *key,
+ MateConfClientNotifyFunc notification_callback,
+ gpointer callback_data)
+{
+ guint notification_id;
+ MateConfClient *client;
+ GError *error = NULL;
+
+ g_return_val_if_fail (key != NULL, EEL_MATECONF_UNDEFINED_CONNECTION);
+ g_return_val_if_fail (notification_callback != NULL, EEL_MATECONF_UNDEFINED_CONNECTION);
+
+ client = eel_mateconf_client_get_global ();
+ g_return_val_if_fail (client != NULL, EEL_MATECONF_UNDEFINED_CONNECTION);
+
+ notification_id = mateconf_client_notify_add (client,
+ key,
+ notification_callback,
+ callback_data,
+ NULL,
+ &error);
+
+ if (eel_mateconf_handle_error (&error)) {
+ if (notification_id != EEL_MATECONF_UNDEFINED_CONNECTION) {
+ mateconf_client_notify_remove (client, notification_id);
+ notification_id = EEL_MATECONF_UNDEFINED_CONNECTION;
+ }
+ }
+
+ return notification_id;
+}
+
+
+void
+eel_mateconf_notification_remove (guint notification_id)
+{
+ MateConfClient *client;
+
+ if (notification_id == EEL_MATECONF_UNDEFINED_CONNECTION) {
+ return;
+ }
+
+ client = eel_mateconf_client_get_global ();
+ g_return_if_fail (client != NULL);
+
+ mateconf_client_notify_remove (client, notification_id);
+}
+
+
+GSList *
+eel_mateconf_value_get_string_list (const MateConfValue *value)
+{
+ GSList *result;
+ const GSList *slist;
+ const GSList *node;
+ const char *string;
+ const MateConfValue *next_value;
+
+ if (value == NULL) {
+ return NULL;
+ }
+
+ g_return_val_if_fail (value->type == MATECONF_VALUE_LIST, NULL);
+ g_return_val_if_fail (mateconf_value_get_list_type (value) == MATECONF_VALUE_STRING, NULL);
+
+ slist = mateconf_value_get_list (value);
+ result = NULL;
+ for (node = slist; node != NULL; node = node->next) {
+ next_value = node->data;
+ g_return_val_if_fail (next_value != NULL, NULL);
+ g_return_val_if_fail (next_value->type == MATECONF_VALUE_STRING, NULL);
+ string = mateconf_value_get_string (next_value);
+ result = g_slist_append (result, g_strdup (string));
+ }
+ return result;
+}
+
+
+void
+eel_mateconf_value_set_string_list (MateConfValue *value,
+ const GSList *string_list)
+{
+ const GSList *node;
+ MateConfValue *next_value;
+ GSList *value_list;
+
+ g_return_if_fail (value->type == MATECONF_VALUE_LIST);
+ g_return_if_fail (mateconf_value_get_list_type (value) == MATECONF_VALUE_STRING);
+
+ value_list = NULL;
+ for (node = string_list; node != NULL; node = node->next) {
+ next_value = mateconf_value_new (MATECONF_VALUE_STRING);
+ mateconf_value_set_string (next_value, node->data);
+ value_list = g_slist_append (value_list, next_value);
+ }
+
+ mateconf_value_set_list (value, value_list);
+
+ for (node = value_list; node != NULL; node = node->next) {
+ mateconf_value_free (node->data);
+ }
+ g_slist_free (value_list);
+}
diff --git a/src/mateconf-utils.h b/src/mateconf-utils.h
new file mode 100644
index 0000000..1f24b68
--- /dev/null
+++ b/src/mateconf-utils.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2002, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+/* eel-mateconf-extensions.h - Stuff to make MateConf easier to use.
+
+ Copyright (C) 2000, 2001 Eazel, Inc.
+
+ The Mate Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The Mate Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the Mate Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Authors: Ramiro Estrugo <[email protected]>
+*/
+
+/* Modified by Paolo Bacchilega <[email protected]> for File Roller. */
+
+#ifndef MATECONF_UTILS_H
+#define MATECONF_UTILS_H
+
+#include <glib.h>
+#include <mateconf/mateconf.h>
+#include <mateconf/mateconf-client.h>
+
+G_BEGIN_DECLS
+
+#define EEL_MATECONF_UNDEFINED_CONNECTION 0
+
+MateConfClient *eel_mateconf_client_get_global (void);
+
+void eel_global_client_free (void);
+
+gboolean eel_mateconf_handle_error (GError **error);
+
+gboolean eel_mateconf_get_boolean (const char *key,
+ gboolean def_val);
+
+void eel_mateconf_set_boolean (const char *key,
+ gboolean value);
+
+int eel_mateconf_get_integer (const char *key,
+ int def_val);
+
+void eel_mateconf_set_integer (const char *key,
+ int value);
+
+float eel_mateconf_get_float (const char *key,
+ float def_val);
+
+void eel_mateconf_set_float (const char *key,
+ float value);
+
+char * eel_mateconf_get_string (const char *key,
+ const char *def_val);
+
+void eel_mateconf_set_string (const char *key,
+ const char *value);
+
+char * eel_mateconf_get_locale_string (const char *key,
+ const char *def_val);
+
+void eel_mateconf_set_locale_string (const char *key,
+ const char *value);
+
+GSList * eel_mateconf_get_string_list (const char *key);
+
+void eel_mateconf_set_string_list (const char *key,
+ const GSList *string_list_value);
+
+GSList * eel_mateconf_get_locale_string_list(const char *key);
+
+void eel_mateconf_set_locale_string_list(const char *key,
+ const GSList *string_list_value);
+
+gboolean eel_mateconf_is_default (const char *key);
+
+gboolean eel_mateconf_monitor_add (const char *directory);
+
+gboolean eel_mateconf_monitor_remove (const char *directory);
+
+void eel_mateconf_preload_cache (const char *directory,
+ MateConfClientPreloadType preload_type);
+
+void eel_mateconf_suggest_sync (void);
+
+MateConfValue* eel_mateconf_get_value (const char *key);
+
+MateConfValue* eel_mateconf_get_default_value (const char *key);
+
+gboolean eel_mateconf_value_is_equal (const MateConfValue *a,
+ const MateConfValue *b);
+
+void eel_mateconf_value_free (MateConfValue *value);
+
+guint eel_mateconf_notification_add (const char *key,
+ MateConfClientNotifyFunc notification_callback,
+ gpointer callback_data);
+
+void eel_mateconf_notification_remove (guint notification_id);
+
+GSList * eel_mateconf_value_get_string_list (const MateConfValue *value);
+
+void eel_mateconf_value_set_string_list (MateConfValue *value,
+ const GSList *string_list);
+
+G_END_DECLS
+
+#endif /* MATECONF_UTILS_H */
diff --git a/src/mkdtemp.c b/src/mkdtemp.c
new file mode 100644
index 0000000..1508102
--- /dev/null
+++ b/src/mkdtemp.c
@@ -0,0 +1,201 @@
+/* Copyright (C) 1999, 2001-2002 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* Extracted from misc/mkdtemp.c and sysdeps/posix/tempname.c. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+/* Specification. */
+#include "mkdtemp.h"
+
+#include <errno.h>
+#ifndef __set_errno
+# define __set_errno(Val) errno = (Val)
+#endif
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <stdio.h>
+#ifndef TMP_MAX
+# define TMP_MAX 238328
+#endif
+
+#if HAVE_STDINT_H_WITH_UINTMAX || _LIBC
+# include <stdint.h>
+#endif
+
+#if HAVE_INTTYPES_H_WITH_UINTMAX || _LIBC
+# include <inttypes.h>
+#endif
+
+#if HAVE_UNISTD_H || _LIBC
+# include <unistd.h>
+#endif
+
+#if HAVE_GETTIMEOFDAY || _LIBC
+# if HAVE_SYS_TIME_H || _LIBC
+# include <sys/time.h>
+# endif
+#else
+# if HAVE_TIME_H || _LIBC
+# include <time.h>
+# endif
+#endif
+
+#include <sys/stat.h>
+#if STAT_MACROS_BROKEN
+# undef S_ISDIR
+#endif
+#if !defined S_ISDIR && defined S_IFDIR
+# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
+#endif
+#if !S_IRUSR && S_IREAD
+# define S_IRUSR S_IREAD
+#endif
+#if !S_IRUSR
+# define S_IRUSR 00400
+#endif
+#if !S_IWUSR && S_IWRITE
+# define S_IWUSR S_IWRITE
+#endif
+#if !S_IWUSR
+# define S_IWUSR 00200
+#endif
+#if !S_IXUSR && S_IEXEC
+# define S_IXUSR S_IEXEC
+#endif
+#if !S_IXUSR
+# define S_IXUSR 00100
+#endif
+
+#if !_LIBC
+# define __getpid getpid
+# define __gettimeofday gettimeofday
+# define __mkdir mkdir
+#endif
+
+/* Use the widest available unsigned type if uint64_t is not
+ available. The algorithm below extracts a number less than 62**6
+ (approximately 2**35.725) from uint64_t, so ancient hosts where
+ uintmax_t is only 32 bits lose about 3.725 bits of randomness,
+ which is better than not having mkstemp at all. */
+#if !defined UINT64_MAX && !defined uint64_t
+# define uint64_t uintmax_t
+#endif
+
+/* These are the characters used in temporary filenames. */
+static const char letters[] =
+"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+/* Generate a temporary file name based on TMPL. TMPL must match the
+ rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed
+ does not exist at the time of the call to __gen_tempname. TMPL is
+ overwritten with the result.
+
+ KIND is:
+ __GT_DIR: create a directory, which will be mode 0700.
+
+ We use a clever algorithm to get hard-to-predict names. */
+static int
+gen_tempname (tmpl)
+ char *tmpl;
+{
+ int len;
+ char *XXXXXX;
+ static uint64_t value;
+ uint64_t random_time_bits;
+ int count, fd = -1;
+ int save_errno = errno;
+
+ len = strlen (tmpl);
+ if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
+
+ /* This is where the Xs start. */
+ XXXXXX = &tmpl[len - 6];
+
+ /* Get some more or less random data. */
+#ifdef RANDOM_BITS
+ RANDOM_BITS (random_time_bits);
+#else
+# if HAVE_GETTIMEOFDAY || _LIBC
+ {
+ struct timeval tv;
+ __gettimeofday (&tv, NULL);
+ random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;
+ }
+# else
+ random_time_bits = time (NULL);
+# endif
+#endif
+ value += random_time_bits ^ __getpid ();
+
+ for (count = 0; count < TMP_MAX; value += 7777, ++count)
+ {
+ uint64_t v = value;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % 62];
+ v /= 62;
+ XXXXXX[1] = letters[v % 62];
+ v /= 62;
+ XXXXXX[2] = letters[v % 62];
+ v /= 62;
+ XXXXXX[3] = letters[v % 62];
+ v /= 62;
+ XXXXXX[4] = letters[v % 62];
+ v /= 62;
+ XXXXXX[5] = letters[v % 62];
+
+ fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
+
+ if (fd >= 0)
+ {
+ __set_errno (save_errno);
+ return fd;
+ }
+ else if (errno != EEXIST)
+ return -1;
+ }
+
+ /* We got out of the loop because we ran out of combinations to try. */
+ __set_errno (EEXIST);
+ return -1;
+}
+
+/* Generate a unique temporary directory from TEMPLATE.
+ The last six characters of TEMPLATE must be "XXXXXX";
+ they are replaced with a string that makes the filename unique.
+ The directory is created, mode 700, and its name is returned.
+ (This function comes from OpenBSD.) */
+char *
+mkdtemp (template)
+ char *template;
+{
+ if (gen_tempname (template))
+ return NULL;
+ else
+ return template;
+}
diff --git a/src/mkdtemp.h b/src/mkdtemp.h
new file mode 100644
index 0000000..dbc47ac
--- /dev/null
+++ b/src/mkdtemp.h
@@ -0,0 +1,40 @@
+/* Creating a private temporary directory.
+ Copyright (C) 2001 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, 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef PARAMS
+# if __STDC__ || defined __GNUC__ || defined __SUNPRO_C || defined __cplusplus || __PROTOTYPES
+# define PARAMS(Args) Args
+# else
+# define PARAMS(Args) ()
+# endif
+#endif
+
+#if HAVE_MKDTEMP
+
+/* Get mkdtemp() declaration. */
+#include <stdlib.h>
+
+#else
+
+/* Create a unique temporary directory from TEMPLATE.
+ The last six characters of TEMPLATE must be "XXXXXX";
+ they are replaced with a string that makes the directory name unique.
+ Returns TEMPLATE, or a null pointer if it cannot get a unique name.
+ The directory is created mode 700. */
+extern char * mkdtemp PARAMS ((char *template));
+
+#endif
diff --git a/src/open-file.c b/src/open-file.c
new file mode 100644
index 0000000..d182d07
--- /dev/null
+++ b/src/open-file.c
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001-2008 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <config.h>
+#include "open-file.h"
+#include "file-utils.h"
+
+
+OpenFile*
+open_file_new (const char *path,
+ const char *extracted_path,
+ const char *temp_dir)
+{
+ OpenFile *ofile;
+
+ ofile = g_new0 (OpenFile, 1);
+ ofile->path = g_strdup (path);
+ ofile->extracted_uri = g_filename_to_uri (extracted_path, NULL, NULL);
+ if (! uri_exists (ofile->extracted_uri)) {
+ open_file_free (ofile);
+ return NULL;
+ }
+ ofile->temp_dir = g_strdup (temp_dir);
+ ofile->last_modified = get_file_mtime (ofile->extracted_uri);
+
+ return ofile;
+}
+
+
+void
+open_file_free (OpenFile *ofile)
+{
+ if (ofile == NULL)
+ return;
+ if (ofile->monitor != NULL)
+ g_object_unref (ofile->monitor);
+ g_free (ofile->path);
+ g_free (ofile->extracted_uri);
+ g_free (ofile->temp_dir);
+ g_free (ofile);
+}
+
+
+OpenFile *
+open_file_copy (OpenFile *src)
+{
+ OpenFile *ofile;
+
+ ofile = g_new0 (OpenFile, 1);
+ ofile->path = g_strdup (src->path);
+ ofile->extracted_uri = g_strdup (src->extracted_uri);
+ ofile->temp_dir = g_strdup (src->temp_dir);
+ ofile->last_modified = src->last_modified;
+
+ return ofile;
+}
+
+
+GType
+open_file_get_type (void)
+{
+ static GType type = 0;
+
+ if (type == 0)
+ type = g_boxed_type_register_static ("FROpenFile", (GBoxedCopyFunc) open_file_copy, (GBoxedFreeFunc) open_file_free);
+
+ return type;
+}
diff --git a/src/open-file.h b/src/open-file.h
new file mode 100644
index 0000000..605f25e
--- /dev/null
+++ b/src/open-file.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001-2008 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef OPEN_FILE_H
+#define OPEN_FILE_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <time.h>
+
+typedef struct {
+ char *path;
+ char *extracted_uri;
+ char *temp_dir;
+ time_t last_modified;
+ GFileMonitor *monitor;
+} OpenFile;
+
+#define FR_TYPE_OPEN_FILE (open_file_get_type ())
+
+GType open_file_get_type (void);
+OpenFile * open_file_new (const char *path,
+ const char *extracted_path,
+ const char *temp_dir);
+OpenFile * open_file_copy (OpenFile *src);
+void open_file_free (OpenFile *ofile);
+
+#endif /* OPEN_FILE_H */
diff --git a/src/preferences.c b/src/preferences.c
new file mode 100644
index 0000000..6ca6d70
--- /dev/null
+++ b/src/preferences.c
@@ -0,0 +1,268 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <mateconf/mateconf-client.h>
+#include "typedefs.h"
+#include "preferences.h"
+#include "main.h"
+#include "file-utils.h"
+#include "mateconf-utils.h"
+#include "fr-window.h"
+
+
+#define DIALOG_PREFIX "/apps/file-roller/dialogs/"
+
+
+typedef struct {
+ int i_value;
+ char *s_value;
+} EnumStringTable;
+
+
+static int
+get_enum_from_string (EnumStringTable *table,
+ const char *s_value)
+{
+ int i;
+
+ /* return the first value if s_value is invalid */
+
+ if (s_value == NULL)
+ return table[0].i_value;
+
+ for (i = 0; table[i].s_value != NULL; i++)
+ if (strcmp (s_value, table[i].s_value) == 0)
+ return table[i].i_value;
+
+ return table[0].i_value;
+}
+
+
+static char *
+get_string_from_enum (EnumStringTable *table,
+ int i_value)
+{
+ int i;
+
+ for (i = 0; table[i].s_value != NULL; i++)
+ if (i_value == table[i].i_value)
+ return table[i].s_value;
+
+ /* return the first value if i_value is invalid */
+
+ return table[0].s_value;
+}
+
+
+/* --------------- */
+
+
+static EnumStringTable sort_method_table [] = {
+ { FR_WINDOW_SORT_BY_NAME, "name" },
+ { FR_WINDOW_SORT_BY_SIZE, "size" },
+ { FR_WINDOW_SORT_BY_TYPE, "type" },
+ { FR_WINDOW_SORT_BY_TIME, "time" },
+ { FR_WINDOW_SORT_BY_PATH, "path" },
+ { 0, NULL }
+};
+
+static EnumStringTable sort_type_table [] = {
+ { GTK_SORT_ASCENDING, "ascending" },
+ { GTK_SORT_DESCENDING, "descending" },
+ { 0, NULL }
+};
+
+static EnumStringTable list_mode_table [] = {
+ { FR_WINDOW_LIST_MODE_FLAT, "all_files" },
+ { FR_WINDOW_LIST_MODE_AS_DIR, "as_folder" },
+ { 0, NULL }
+};
+
+static EnumStringTable compression_level_table [] = {
+ { FR_COMPRESSION_VERY_FAST, "very_fast" },
+ { FR_COMPRESSION_FAST, "fast" },
+ { FR_COMPRESSION_NORMAL, "normal" },
+ { FR_COMPRESSION_MAXIMUM, "maximum" },
+ { 0, NULL }
+};
+
+
+/* --------------- */
+
+
+FrWindowSortMethod
+preferences_get_sort_method (void)
+{
+ char *s_value;
+ int i_value;
+
+ s_value = eel_mateconf_get_string (PREF_LIST_SORT_METHOD, "name");
+ i_value = get_enum_from_string (sort_method_table, s_value);
+ g_free (s_value);
+
+ return (FrWindowSortMethod) i_value;
+}
+
+
+void
+preferences_set_sort_method (FrWindowSortMethod i_value)
+{
+ char *s_value;
+
+ s_value = get_string_from_enum (sort_method_table, i_value);
+ eel_mateconf_set_string (PREF_LIST_SORT_METHOD, s_value);
+}
+
+
+GtkSortType
+preferences_get_sort_type (void)
+{
+ char *s_value;
+ int i_value;
+
+ s_value = eel_mateconf_get_string (PREF_LIST_SORT_TYPE, "ascending");
+ i_value = get_enum_from_string (sort_type_table, s_value);
+ g_free (s_value);
+
+ return (GtkSortType) i_value;
+}
+
+
+void
+preferences_set_sort_type (GtkSortType i_value)
+{
+ char *s_value;
+
+ s_value = get_string_from_enum (sort_type_table, i_value);
+ eel_mateconf_set_string (PREF_LIST_SORT_TYPE, s_value);
+}
+
+
+FrWindowListMode
+preferences_get_list_mode (void)
+{
+ char *s_value;
+ int i_value;
+
+ s_value = eel_mateconf_get_string (PREF_LIST_MODE, "as_folder");
+ i_value = get_enum_from_string (list_mode_table, s_value);
+ g_free (s_value);
+
+ return (FrWindowListMode) i_value;
+}
+
+
+void
+preferences_set_list_mode (FrWindowListMode i_value)
+{
+ char *s_value;
+
+ s_value = get_string_from_enum (list_mode_table, i_value);
+ eel_mateconf_set_string (PREF_LIST_MODE, s_value);
+}
+
+
+FrCompression
+preferences_get_compression_level (void)
+{
+ char *s_value;
+ int i_value;
+
+ s_value = eel_mateconf_get_string (PREF_ADD_COMPRESSION_LEVEL, "normal");
+ i_value = get_enum_from_string (compression_level_table, s_value);
+ g_free (s_value);
+
+ return (FrCompression) i_value;
+}
+
+
+void
+preferences_set_compression_level (FrCompression i_value)
+{
+ char *s_value;
+
+ s_value = get_string_from_enum (compression_level_table, i_value);
+ eel_mateconf_set_string (PREF_ADD_COMPRESSION_LEVEL, s_value);
+}
+
+
+static void
+set_dialog_property_int (const char *dialog,
+ const char *property,
+ int value)
+{
+ char *key;
+
+ key = g_strconcat (DIALOG_PREFIX, dialog, "/", property, NULL);
+ eel_mateconf_set_integer (key, value);
+ g_free (key);
+}
+
+
+void
+pref_util_save_window_geometry (GtkWindow *window,
+ const char *dialog)
+{
+ int x, y, width, height;
+
+ gtk_window_get_position (window, &x, &y);
+ set_dialog_property_int (dialog, "x", x);
+ set_dialog_property_int (dialog, "y", y);
+
+ gtk_window_get_size (window, &width, &height);
+ set_dialog_property_int (dialog, "width", width);
+ set_dialog_property_int (dialog, "height", height);
+}
+
+
+static int
+get_dialog_property_int (const char *dialog,
+ const char *property)
+{
+ char *key;
+ int value;
+
+ key = g_strconcat (DIALOG_PREFIX, dialog, "/", property, NULL);
+ value = eel_mateconf_get_integer (key, -1);
+ g_free (key);
+
+ return value;
+}
+
+
+void
+pref_util_restore_window_geometry (GtkWindow *window,
+ const char *dialog)
+{
+ int x, y, width, height;
+
+ x = get_dialog_property_int (dialog, "x");
+ y = get_dialog_property_int (dialog, "y");
+ width = get_dialog_property_int (dialog, "width");
+ height = get_dialog_property_int (dialog, "height");
+
+ if (width != -1 && height != 1)
+ gtk_window_set_default_size (window, width, height);
+
+ gtk_window_present (window);
+}
diff --git a/src/preferences.h b/src/preferences.h
new file mode 100644
index 0000000..6a9224f
--- /dev/null
+++ b/src/preferences.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001, 2003 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PREFERENCES_H
+#define PREFERENCES_H
+
+#include <gtk/gtk.h>
+#include "typedefs.h"
+#include "fr-window.h"
+
+#define PREF_LIST_SORT_METHOD "/apps/file-roller/listing/sort_method"
+#define PREF_LIST_SORT_TYPE "/apps/file-roller/listing/sort_type"
+#define PREF_LIST_MODE "/apps/file-roller/listing/list_mode"
+#define PREF_LIST_SHOW_TYPE "/apps/file-roller/listing/show_type"
+#define PREF_LIST_SHOW_SIZE "/apps/file-roller/listing/show_size"
+#define PREF_LIST_SHOW_TIME "/apps/file-roller/listing/show_time"
+#define PREF_LIST_SHOW_PATH "/apps/file-roller/listing/show_path"
+#define PREF_LIST_USE_MIME_ICONS "/apps/file-roller/listing/use_mime_icons"
+#define PREF_NAME_COLUMN_WIDTH "/apps/file-roller/listing/name_column_width"
+
+#define PREF_UI_WINDOW_WIDTH "/apps/file-roller/ui/window_width"
+#define PREF_UI_WINDOW_HEIGHT "/apps/file-roller/ui/window_height"
+#define PREF_UI_SIDEBAR_WIDTH "/apps/file-roller/ui/sidebar_width"
+#define PREF_UI_HISTORY_LEN "/apps/file-roller/ui/history_len"
+#define PREF_UI_TOOLBAR "/apps/file-roller/ui/view_toolbar"
+#define PREF_UI_STATUSBAR "/apps/file-roller/ui/view_statusbar"
+#define PREF_UI_FOLDERS "/apps/file-roller/ui/view_folders"
+
+#define PREF_EDIT_EDITORS "/apps/file-roller/general/editors"
+#define PREF_ADD_COMPRESSION_LEVEL "/apps/file-roller/general/compression_level"
+#define PREF_ENCRYPT_HEADER "/apps/file-roller/general/encrypt_header"
+#define PREF_MIGRATE_DIRECTORIES "/apps/file-roller/general/migrate_directories"
+
+#define PREF_EXTRACT_OVERWRITE "/apps/file-roller/dialogs/extract/overwrite"
+#define PREF_EXTRACT_SKIP_NEWER "/apps/file-roller/dialogs/extract/skip_newer"
+#define PREF_EXTRACT_RECREATE_FOLDERS "/apps/file-roller/dialogs/extract/recreate_folders"
+
+#define PREF_ADD_CURRENT_FOLDER "/apps/file-roller/dialogs/add/current_folder"
+#define PREF_ADD_FILENAME "/apps/file-roller/dialogs/add/filename"
+#define PREF_ADD_INCLUDE_FILES "/apps/file-roller/dialogs/add/include_files"
+#define PREF_ADD_EXCLUDE_FILES "/apps/file-roller/dialogs/add/exclude_files"
+#define PREF_ADD_EXCLUDE_FOLDERS "/apps/file-roller/dialogs/add/exclude_folders"
+#define PREF_ADD_UPDATE "/apps/file-roller/dialogs/add/update"
+#define PREF_ADD_RECURSIVE "/apps/file-roller/dialogs/add/recursive"
+#define PREF_ADD_NO_SYMLINKS "/apps/file-roller/dialogs/add/no_symlinks"
+
+#define PREF_BATCH_ADD_DEFAULT_EXTENSION "/apps/file-roller/dialogs/batch-add/default_extension"
+#define PREF_BATCH_OTHER_OPTIONS "/apps/file-roller/dialogs/batch-add/other_options"
+#define PREF_BATCH_VOLUME_SIZE "/apps/file-roller/dialogs/batch-add/volume_size"
+
+#define PREF_DESKTOP_ICON_THEME "/desktop/mate/file_views/icon_theme"
+#define PREF_DESKTOP_MENUS_HAVE_TEAROFF "/desktop/mate/interface/menus_have_tearoff"
+#define PREF_DESKTOP_MENUBAR_DETACHABLE "/desktop/mate/interface/menubar_detachable"
+#define PREF_DESKTOP_TOOLBAR_DETACHABLE "/desktop/mate/interface/toolbar_detachable"
+#define PREF_CAJA_CLICK_POLICY "/apps/caja/preferences/click_policy"
+
+FrWindowSortMethod preferences_get_sort_method (void);
+void preferences_set_sort_method (FrWindowSortMethod i_value);
+GtkSortType preferences_get_sort_type (void);
+void preferences_set_sort_type (GtkSortType i_value);
+FrWindowListMode preferences_get_list_mode (void);
+void preferences_set_list_mode (FrWindowListMode i_value);
+FrCompression preferences_get_compression_level (void);
+void preferences_set_compression_level (FrCompression i_value);
+void pref_util_save_window_geometry (GtkWindow *window,
+ const char *dialog);
+void pref_util_restore_window_geometry (GtkWindow *window,
+ const char *dialog);
+
+#endif /* PREFERENCES_H */
diff --git a/src/sh/Makefile.am b/src/sh/Makefile.am
new file mode 100644
index 0000000..25f0f89
--- /dev/null
+++ b/src/sh/Makefile.am
@@ -0,0 +1,5 @@
+shdir = $(libexecdir)/$(PACKAGE)
+sh_DATA = isoinfo.sh
+
+EXTRA_DIST = $(sh_DATA)
+-include $(top_srcdir)/git.mk
diff --git a/src/sh/isoinfo.sh b/src/sh/isoinfo.sh
new file mode 100644
index 0000000..8bcdf35
--- /dev/null
+++ b/src/sh/isoinfo.sh
@@ -0,0 +1,27 @@
+filename=$2
+
+JOLIET=true
+ROCK_RIDGE=true
+
+ISOINFO=`isoinfo -d -i "$filename"`
+if echo $ISOINFO | grep "NO Joliet present" >/dev/null 2>&1; then
+ JOLIET=false
+fi
+if echo $ISOINFO | grep "NO Rock Ridge present" >/dev/null 2>&1; then
+ ROCK_RIDGE=false
+fi
+
+iso_extensions=""
+if test $ROCK_RIDGE = true; then
+ iso_extensions="-R"
+elif test $JOLIET = true; then
+ iso_extensions="-J"
+fi
+
+if test "x$3" = x-x; then
+ file_to_extract=$4
+ outfile=$5
+ isoinfo $iso_extensions -i "$filename" -x "$file_to_extract" > "$outfile"
+else
+ isoinfo $iso_extensions -i "$filename" -l
+fi
diff --git a/src/typedefs.h b/src/typedefs.h
new file mode 100644
index 0000000..36f2c90
--- /dev/null
+++ b/src/typedefs.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2001 The 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TYPEDEFS_H
+#define TYPEDEFS_H
+
+#define MEGABYTE (1024 * 1024)
+
+#define RC_DIR ".mate2/file-roller"
+#define RC_BOOKMARKS_FILE ".mate2/file-roller/bookmarks"
+#define RC_RECENT_FILE ".mate2/file-roller/recents"
+#define RC_OPTIONS_DIR ".mate2/file-roller/options"
+
+#define OLD_RC_BOOKMARKS_FILE ".file-roller/bookmarks"
+#define OLD_RC_RECENT_FILE ".file-roller/recents"
+#define OLD_RC_OPTIONS_DIR ".file-roller/options"
+
+typedef enum { /*< skip >*/
+ FR_WINDOW_SORT_BY_NAME = 0,
+ FR_WINDOW_SORT_BY_SIZE = 1,
+ FR_WINDOW_SORT_BY_TYPE = 2,
+ FR_WINDOW_SORT_BY_TIME = 3,
+ FR_WINDOW_SORT_BY_PATH = 4
+} FrWindowSortMethod;
+
+typedef enum { /*< skip >*/
+ FR_WINDOW_LIST_MODE_FLAT,
+ FR_WINDOW_LIST_MODE_AS_DIR
+} FrWindowListMode;
+
+typedef enum {
+ FR_COMPRESSION_VERY_FAST,
+ FR_COMPRESSION_FAST,
+ FR_COMPRESSION_NORMAL,
+ FR_COMPRESSION_MAXIMUM
+} FrCompression;
+
+typedef enum { /*< skip >*/
+ FR_PROC_ERROR_NONE,
+ FR_PROC_ERROR_GENERIC,
+ FR_PROC_ERROR_COMMAND_ERROR,
+ FR_PROC_ERROR_COMMAND_NOT_FOUND,
+ FR_PROC_ERROR_EXITED_ABNORMALLY,
+ FR_PROC_ERROR_SPAWN,
+ FR_PROC_ERROR_STOPPED,
+ FR_PROC_ERROR_ASK_PASSWORD,
+ FR_PROC_ERROR_MISSING_VOLUME,
+ FR_PROC_ERROR_IO_CHANNEL,
+ FR_PROC_ERROR_BAD_CHARSET,
+ FR_PROC_ERROR_UNSUPPORTED_FORMAT
+} FrProcErrorType;
+
+typedef struct {
+ FrProcErrorType type;
+ int status;
+ GError *gerror;
+} FrProcError;
+
+typedef enum { /*< skip >*/
+ FR_COMMAND_CAN_DO_NOTHING = 0,
+ FR_COMMAND_CAN_READ = 1 << 0,
+ FR_COMMAND_CAN_WRITE = 1 << 1,
+ FR_COMMAND_CAN_ARCHIVE_MANY_FILES = 1 << 2,
+ FR_COMMAND_CAN_ENCRYPT = 1 << 3,
+ FR_COMMAND_CAN_ENCRYPT_HEADER = 1 << 4,
+ FR_COMMAND_CAN_CREATE_VOLUMES = 1 << 5
+} FrCommandCap;
+
+#define FR_COMMAND_CAN_READ_WRITE (FR_COMMAND_CAN_READ | FR_COMMAND_CAN_WRITE)
+
+typedef guint8 FrCommandCaps;
+
+typedef struct {
+ const char *mime_type;
+ FrCommandCaps current_capabilities;
+ FrCommandCaps potential_capabilities;
+} FrMimeTypeCap;
+
+typedef struct {
+ const char *mime_type;
+ const char *packages;
+} FrMimeTypePackages;
+
+typedef struct {
+ int ref;
+ GType type;
+ GPtrArray *caps; /* array of FrMimeTypeCap */
+ GPtrArray *packages; /* array of FrMimeTypePackages */
+} FrRegisteredCommand;
+
+typedef struct {
+ const char *mime_type;
+ char *default_ext;
+ char *name;
+ FrCommandCaps capabilities;
+} FrMimeTypeDescription;
+
+typedef struct {
+ char *ext;
+ const char *mime_type;
+} FrExtensionType;
+
+#endif /* TYPEDEFS_H */
diff --git a/src/ui.h b/src/ui.h
new file mode 100644
index 0000000..3287196
--- /dev/null
+++ b/src/ui.h
@@ -0,0 +1,396 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * File-Roller
+ *
+ * Copyright (C) 2004 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef UI_H
+#define UI_H
+
+
+#include "actions.h"
+#include "fr-stock.h"
+
+
+static GtkActionEntry action_entries[] = {
+ { "FileMenu", NULL, N_("_Archive") },
+ { "EditMenu", NULL, N_("_Edit") },
+ { "ViewMenu", NULL, N_("_View") },
+ { "HelpMenu", NULL, N_("_Help") },
+ { "ArrangeFilesMenu", NULL, N_("_Arrange Files") },
+ /* Translators: this is the label for the "open recent file" sub-menu. */
+ { "OpenRecentMenu", NULL, N_("Open _Recent") },
+
+ { "About", GTK_STOCK_ABOUT,
+ NULL, NULL,
+ N_("Information about the program"),
+ G_CALLBACK (activate_action_about) },
+ { "AddFiles", FR_STOCK_ADD_FILES,
+ N_("_Add Files..."), NULL,
+ N_("Add files to the archive"),
+ G_CALLBACK (activate_action_add_files) },
+ { "AddFiles_Toolbar", FR_STOCK_ADD_FILES,
+ N_("Add Files"), NULL,
+ N_("Add files to the archive"),
+ G_CALLBACK (activate_action_add_files) },
+ { "AddFolder", FR_STOCK_ADD_FOLDER,
+ N_("Add a _Folder..."), NULL,
+ N_("Add a folder to the archive"),
+ G_CALLBACK (activate_action_add_folder) },
+ { "AddFolder_Toolbar", FR_STOCK_ADD_FOLDER,
+ N_("Add Folder"), NULL,
+ N_("Add a folder to the archive"),
+ G_CALLBACK (activate_action_add_folder) },
+ { "Close", GTK_STOCK_CLOSE,
+ NULL, NULL,
+ N_("Close the current archive"),
+ G_CALLBACK (activate_action_close) },
+ { "Contents", GTK_STOCK_HELP,
+ N_("Contents"), "F1",
+ N_("Display the File Roller Manual"),
+ G_CALLBACK (activate_action_manual) },
+
+ { "Copy", GTK_STOCK_COPY,
+ NULL, NULL,
+ N_("Copy the selection"),
+ G_CALLBACK (activate_action_copy) },
+ { "Cut", GTK_STOCK_CUT,
+ NULL, NULL,
+ N_("Cut the selection"),
+ G_CALLBACK (activate_action_cut) },
+ { "Paste", GTK_STOCK_PASTE,
+ NULL, NULL,
+ N_("Paste the clipboard"),
+ G_CALLBACK (activate_action_paste) },
+ { "Rename", NULL,
+ N_("_Rename..."), "F2",
+ N_("Rename the selection"),
+ G_CALLBACK (activate_action_rename) },
+ { "Delete", GTK_STOCK_DELETE,
+ NULL, "Delete",
+ N_("Delete the selection from the archive"),
+ G_CALLBACK (activate_action_delete) },
+
+ { "CopyFolderFromSidebar", GTK_STOCK_COPY,
+ NULL, NULL,
+ N_("Copy the selection"),
+ G_CALLBACK (activate_action_copy_folder_from_sidebar) },
+ { "CutFolderFromSidebar", GTK_STOCK_CUT,
+ NULL, NULL,
+ N_("Cut the selection"),
+ G_CALLBACK (activate_action_cut_folder_from_sidebar) },
+ { "PasteFolderFromSidebar", GTK_STOCK_PASTE,
+ NULL, NULL,
+ N_("Paste the clipboard"),
+ G_CALLBACK (activate_action_paste_folder_from_sidebar) },
+ { "RenameFolderFromSidebar", NULL,
+ N_("_Rename..."), "F2",
+ N_("Rename the selection"),
+ G_CALLBACK (activate_action_rename_folder_from_sidebar) },
+ { "DeleteFolderFromSidebar", GTK_STOCK_DELETE,
+ NULL, NULL,
+ N_("Delete the selection from the archive"),
+ G_CALLBACK (activate_action_delete_folder_from_sidebar) },
+
+ { "DeselectAll", NULL,
+ N_("Dese_lect All"), "<shift><control>A",
+ N_("Deselect all files"),
+ G_CALLBACK (activate_action_deselect_all) },
+ { "Extract", FR_STOCK_EXTRACT,
+ N_("_Extract..."), "<control>E",
+ N_("Extract files from the archive"),
+ G_CALLBACK (activate_action_extract) },
+ { "ExtractFolderFromSidebar", FR_STOCK_EXTRACT,
+ N_("_Extract..."), NULL,
+ N_("Extract files from the archive"),
+ G_CALLBACK (activate_action_extract_folder_from_sidebar) },
+ { "Extract_Toolbar", FR_STOCK_EXTRACT,
+ N_("Extract"), NULL,
+ N_("Extract files from the archive"),
+ G_CALLBACK (activate_action_extract) },
+ { "Find", GTK_STOCK_FIND,
+ N_("Find..."), NULL,
+ NULL,
+ G_CALLBACK (activate_action_find) },
+
+ { "LastOutput", NULL,
+ N_("_Last Output"), NULL,
+ N_("View the output produced by the last executed command"),
+ G_CALLBACK (activate_action_last_output) },
+ { "New", GTK_STOCK_NEW,
+ NC_("File", "New..."), NULL,
+ N_("Create a new archive"),
+ G_CALLBACK (activate_action_new) },
+ { "Open", GTK_STOCK_OPEN,
+ NC_("File", "Open..."), NULL,
+ N_("Open archive"),
+ G_CALLBACK (activate_action_open) },
+ { "Open_Toolbar", GTK_STOCK_OPEN,
+ NULL, NULL,
+ N_("Open archive"),
+ G_CALLBACK (activate_action_open) },
+ { "OpenSelection", NULL,
+ N_("_Open With..."), NULL,
+ N_("Open selected files with an application"),
+ G_CALLBACK (activate_action_open_with) },
+ { "Password", NULL,
+ N_("Pass_word..."), NULL,
+ N_("Specify a password for this archive"),
+ G_CALLBACK (activate_action_password) },
+ { "Properties", GTK_STOCK_PROPERTIES,
+ NULL, "<alt>Return",
+ N_("Show archive properties"),
+ G_CALLBACK (activate_action_properties) },
+ { "Reload", GTK_STOCK_REFRESH,
+ NULL, "<control>R",
+ N_("Reload current archive"),
+ G_CALLBACK (activate_action_reload) },
+ { "SaveAs", GTK_STOCK_SAVE_AS,
+ NC_("File", "Save As..."), NULL,
+ N_("Save the current archive with a different name"),
+ G_CALLBACK (activate_action_save_as) },
+ { "SelectAll", GTK_STOCK_SELECT_ALL,
+ NULL, "<control>A",
+ N_("Select all files"),
+ G_CALLBACK (activate_action_select_all) },
+ { "Stop", GTK_STOCK_STOP,
+ NULL, "Escape",
+ N_("Stop current operation"),
+ G_CALLBACK (activate_action_stop) },
+ { "TestArchive", NULL,
+ N_("_Test Integrity"), NULL,
+ N_("Test whether the archive contains errors"),
+ G_CALLBACK (activate_action_test_archive) },
+ { "ViewSelection", GTK_STOCK_OPEN,
+ NULL, NULL,
+ N_("Open the selected file"),
+ G_CALLBACK (activate_action_view_or_open) },
+ { "ViewSelection_Toolbar", GTK_STOCK_OPEN,
+ NULL, NULL,
+ N_("Open the selected file"),
+ G_CALLBACK (activate_action_view_or_open) },
+ { "OpenFolder", GTK_STOCK_OPEN,
+ NULL, NULL,
+ N_("Open the selected folder"),
+ G_CALLBACK (activate_action_open_folder) },
+ { "OpenFolderFromSidebar", GTK_STOCK_OPEN,
+ NULL, NULL,
+ N_("Open the selected folder"),
+ G_CALLBACK (activate_action_open_folder_from_sidebar) },
+
+ { "GoBack", GTK_STOCK_GO_BACK,
+ NULL, NULL,
+ N_("Go to the previous visited location"),
+ G_CALLBACK (activate_action_go_back) },
+ { "GoForward", GTK_STOCK_GO_FORWARD,
+ NULL, NULL,
+ N_("Go to the next visited location"),
+ G_CALLBACK (activate_action_go_forward) },
+ { "GoUp", GTK_STOCK_GO_UP,
+ NULL, NULL,
+ N_("Go up one level"),
+ G_CALLBACK (activate_action_go_up) },
+ { "GoHome", GTK_STOCK_HOME,
+ NULL, NULL,
+ /* Translators: the home location is the home folder. */
+ N_("Go to the home location"),
+ G_CALLBACK (activate_action_go_home) },
+};
+static guint n_action_entries = G_N_ELEMENTS (action_entries);
+
+
+static GtkToggleActionEntry action_toggle_entries[] = {
+ { "ViewToolbar", NULL,
+ N_("_Toolbar"), NULL,
+ N_("View the main toolbar"),
+ G_CALLBACK (activate_action_view_toolbar),
+ TRUE },
+ { "ViewStatusbar", NULL,
+ N_("Stat_usbar"), NULL,
+ N_("View the statusbar"),
+ G_CALLBACK (activate_action_view_statusbar),
+ TRUE },
+ { "SortReverseOrder", NULL,
+ N_("_Reversed Order"), NULL,
+ N_("Reverse the list order"),
+ G_CALLBACK (activate_action_sort_reverse_order),
+ FALSE },
+ { "ViewFolders", NULL,
+ N_("_Folders"), "F9",
+ N_("View the folders pane"),
+ G_CALLBACK (activate_action_view_folders),
+ FALSE },
+};
+static guint n_action_toggle_entries = G_N_ELEMENTS (action_toggle_entries);
+
+
+static GtkRadioActionEntry view_as_entries[] = {
+ { "ViewAllFiles", NULL,
+ N_("View All _Files"), "<control>1",
+ " ", FR_WINDOW_LIST_MODE_FLAT },
+ { "ViewAsFolder", NULL,
+ N_("View as a F_older"), "<control>2",
+ " ", FR_WINDOW_LIST_MODE_AS_DIR },
+};
+static guint n_view_as_entries = G_N_ELEMENTS (view_as_entries);
+
+
+static GtkRadioActionEntry sort_by_entries[] = {
+ { "SortByName", NULL,
+ N_("by _Name"), NULL,
+ N_("Sort file list by name"), FR_WINDOW_SORT_BY_NAME },
+ { "SortBySize", NULL,
+ N_("by _Size"), NULL,
+ N_("Sort file list by file size"), FR_WINDOW_SORT_BY_SIZE },
+ { "SortByType", NULL,
+ N_("by T_ype"), NULL,
+ N_("Sort file list by type"), FR_WINDOW_SORT_BY_TYPE },
+ { "SortByDate", NULL,
+ N_("by _Date Modified"), NULL,
+ N_("Sort file list by modification time"), FR_WINDOW_SORT_BY_TIME },
+ { "SortByLocation", NULL,
+ /* Translators: this is the "sort by file location" menu item */
+ N_("by _Location"), NULL,
+ /* Translators: location is the file location */
+ N_("Sort file list by location"), FR_WINDOW_SORT_BY_PATH },
+};
+static guint n_sort_by_entries = G_N_ELEMENTS (sort_by_entries);
+
+
+static const gchar *ui_info =
+"<ui>"
+" <menubar name='MenuBar'>"
+" <menu name='Archive' action='FileMenu'>"
+" <menuitem action='New'/>"
+" <menuitem action='Open'/>"
+" <menu name='OpenRecentMenu' action='OpenRecentMenu'>"
+" <menuitem action='Open'/>"
+" </menu>"
+" <menuitem action='SaveAs'/>"
+" <separator/>"
+" <menuitem action='Extract'/>"
+" <menuitem action='TestArchive'/>"
+" <separator/>"
+" <menuitem action='Properties'/>"
+" <separator/>"
+" <menuitem action='Close'/>"
+" </menu>"
+" <menu action='EditMenu'>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Copy'/>"
+" <menuitem action='Paste'/>"
+" <menuitem action='Rename'/>"
+" <menuitem action='Delete'/>"
+" <separator/>"
+" <menuitem action='SelectAll'/>"
+" <menuitem action='DeselectAll'/>"
+" <separator/>"
+" <menuitem action='Find'/>"
+" <separator/>"
+" <menuitem action='AddFiles'/>"
+" <menuitem action='AddFolder'/>"
+" <separator/>"
+" <menuitem action='Password'/>"
+" </menu>"
+" <menu action='ViewMenu'>"
+" <menuitem action='ViewToolbar'/>"
+" <menuitem action='ViewStatusbar'/>"
+" <menuitem action='ViewFolders'/>"
+" <separator/>"
+" <menuitem action='ViewAllFiles'/>"
+" <menuitem action='ViewAsFolder'/>"
+/*" <separator/>"
+" <menu action='ArrangeFilesMenu'>"
+" <menuitem action='SortByName'/>"
+" <menuitem action='SortBySize'/>"
+" <menuitem action='SortByType'/>"
+" <menuitem action='SortByDate'/>"
+" <menuitem action='SortByLocation'/>"
+" <separator/>"
+" <menuitem action='SortReverseOrder'/>"
+" </menu>"*/
+" <separator/>"
+" <menuitem action='LastOutput'/>"
+" <separator/>"
+" <menuitem action='Stop'/>"
+" <menuitem action='Reload'/>"
+" </menu>"
+" <menu action='HelpMenu'>"
+" <menuitem action='Contents'/>"
+" <menuitem action='About'/>"
+" </menu>"
+" </menubar>"
+" <toolbar name='ToolBar'>"
+" <toolitem action='New'/>"
+" <separator/>"
+" <toolitem action='Extract_Toolbar'/>"
+" <separator/>"
+" <toolitem action='AddFiles_Toolbar'/>"
+" <toolitem action='AddFolder_Toolbar'/>"
+" <separator/>"
+" <toolitem action='Stop'/>"
+" </toolbar>"
+" <toolbar name='LocationBar'>"
+" <toolitem action='GoBack'/>"
+" <toolitem action='GoForward'/>"
+" <toolitem action='GoUp'/>"
+" <toolitem action='GoHome'/>"
+" </toolbar>"
+" <popup name='FilePopupMenu'>"
+" <menuitem action='ViewSelection'/>"
+" <menuitem action='OpenSelection'/>"
+" <separator/>"
+" <menuitem action='Extract'/>"
+" <separator/>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Copy'/>"
+" <menuitem action='Paste'/>"
+" <menuitem action='Rename'/>"
+" <menuitem action='Delete'/>"
+" </popup>"
+" <popup name='FolderPopupMenu'>"
+" <menuitem action='OpenFolder'/>"
+" <separator/>"
+" <menuitem action='Extract'/>"
+" <separator/>"
+" <menuitem action='Cut'/>"
+" <menuitem action='Copy'/>"
+" <menuitem action='Paste'/>"
+" <menuitem action='Rename'/>"
+" <menuitem action='Delete'/>"
+" </popup>"
+" <popup name='AddMenu'>"
+" <menuitem action='AddFiles'/>"
+" <menuitem action='AddFolder'/>"
+" </popup>"
+" <popup name='SidebarFolderPopupMenu'>"
+" <menuitem action='OpenFolderFromSidebar'/>"
+" <separator/>"
+" <menuitem action='ExtractFolderFromSidebar'/>"
+" <separator/>"
+" <menuitem action='CutFolderFromSidebar'/>"
+" <menuitem action='CopyFolderFromSidebar'/>"
+" <menuitem action='PasteFolderFromSidebar'/>"
+" <menuitem action='RenameFolderFromSidebar'/>"
+" <menuitem action='DeleteFolderFromSidebar'/>"
+" </popup>"
+"</ui>";
+
+
+#endif /* UI_H */