From: Colin Clark Date: Fri, 17 Sep 2021 15:05:33 +0000 (+0100) Subject: Addl fix #299: File Compression and Archiving X-Git-Tag: v1.7~56 X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?p=geeqie.git;a=commitdiff_plain;h=5cf8ef1414b3fc5a64bf6d40be6528d4221f8a76 Addl fix #299: File Compression and Archiving https://github.com/BestImageViewer/geeqie/issues/299 Use libarchive to extract the files, instead of a plugin. --- diff --git a/README.md b/README.md index 779893f0..b5b23f08 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,8 @@ Geeqie is a graphics file viewer. Basic features: * Viewing raster and vector images, in the following formats: 3FR, ANI, APM, ARW, AVIF, BMP, CR2, CR3, CRW, CUR, DDS, DjVu, DNG, ERF, GIF, HEIC, HEIF, ICNS, ICO, JP2. JPE/JPEG/JPG, JPEG XL, JPS, KDC, MEF, MOS, MPO, MRW, NEF, ORF, PBM/PGM/PNM/PPM, PEF, PNG, PSD, PTX, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WEBP, WMF, XBM, XPM. -Animated GIFs are supported. + * Display images in archive files (.ZIP, .RAR etc.). + * Animated GIFs are supported. * Preview and thumbnails of video clips can be displayed. Clips can be run via a defined external program. @@ -279,6 +280,9 @@ And either the ChangeLog file or [Geeqie ChangeLog](http://geeqie.org/cgi-bin/gi libraw 0.20 For displaying CR3 images + libarchive 3.4.3 + For opening archive files (.ZIP, .RAR etc.) + yelp-tools For creating the Help files diff --git a/configure.ac b/configure.ac index ff6d44d7..c4c1bcd6 100644 --- a/configure.ac +++ b/configure.ac @@ -660,6 +660,31 @@ AM_CONDITIONAL(HAVE_WEBP, [test "x$HAVE_WEBP" = xyes]) AC_SUBST(WEBP_CFLAGS) AC_SUBST(WEBP_LIBS) +# Libarchive support +# ---------------------------------------------------------------------- + +AC_ARG_ENABLE([archive], + AC_HELP_STRING([--disable-archive], [disable archive support]), + [libarchive=$enableval], [libarchive=auto]) + +if test "x${libarchive}" != "xno"; then + PKG_CHECK_MODULES(ARCHIVE, libarchive >= 3.4.3, + [ + HAVE_ARCHIVE=yes + AC_DEFINE(HAVE_ARCHIVE, 1, [define to enable archive support]) + ], + [ + HAVE_ARCHIVE=no + AC_MSG_WARN([$ARCHIVE_PKG_ERRORS]) + ]) +else + HAVE_ARCHIVE=disabled +fi + +AM_CONDITIONAL(HAVE_ARCHIVE, [test "x$HAVE_ARCHIVE" = xyes]) +AC_SUBST(ARCHIVE_CFLAGS) +AC_SUBST(ARCHIVE_LIBS) + # J2K support # ---------------------------------------------------------------------- @@ -805,7 +830,6 @@ AC_CONFIG_FILES([ plugins/image-crop/Makefile plugins/random-image/Makefile plugins/lens/Makefile - plugins/open-archive/Makefile geeqie.spec ]) @@ -863,6 +887,7 @@ Support: J2K: $HAVE_J2K LibRaw: $HAVE_RAW Libjxl: $HAVE_JPEGXL + Libarchive: $HAVE_ARCHIVE Documentation: Doxygen: $DX_DOXYGEN diff --git a/doc/docbook/GuideOptionsBehavior.xml b/doc/docbook/GuideOptionsBehavior.xml index 1d3ae3b3..3459f4da 100644 --- a/doc/docbook/GuideOptionsBehavior.xml +++ b/doc/docbook/GuideOptionsBehavior.xml @@ -279,6 +279,14 @@ If selected, mouse left-click will select the next image; mouse middle-click will select the previous image. + + + Open archive by left click on image + + + If selected, mouse left-click on an image belonging to the archive class will decompress the file contents and display them in a new Geeqie window. + + Play video by left click on image diff --git a/doc/docbook/GuideOptionsFiltering.xml b/doc/docbook/GuideOptionsFiltering.xml index 64e977c0..da7861c5 100644 --- a/doc/docbook/GuideOptionsFiltering.xml +++ b/doc/docbook/GuideOptionsFiltering.xml @@ -161,6 +161,7 @@ Video Collection Document + Archive (.zip, .rar etc.) diff --git a/doc/docbook/GuideReferencePixbufLoaders.xml b/doc/docbook/GuideReferencePixbufLoaders.xml index 3b0d3e3d..f1519b99 100644 --- a/doc/docbook/GuideReferencePixbufLoaders.xml +++ b/doc/docbook/GuideReferencePixbufLoaders.xml @@ -3,14 +3,6 @@ Additional pixbuf loaders Geeqie can display files for which there are existing pixbuf loaders. In addition to the standard ones included with most distributions, the following loaders may also be available to you. However, in some cases you are required to compile and install them yourself. -
- svg - librsvg2-common -
-
- wmf - libwmf0.2-7-gtk -
JP2/JPC/JPX/J2K/JPF If your distribution has the libpixbufloader-jasper.so loader, these file formats will also be displayed. diff --git a/doc/docbook/GuideReferenceStandardPlugins.xml b/doc/docbook/GuideReferenceStandardPlugins.xml index 89ec41ca..bfbde9ae 100644 --- a/doc/docbook/GuideReferenceStandardPlugins.xml +++ b/doc/docbook/GuideReferenceStandardPlugins.xml @@ -132,15 +132,4 @@ menu.
-
- Open archive file - - Opens an archive file (of the type .zip, .rar, .cbr, .tar.gz) and extracts the contents into a folder in /tmp. A new Geeqie window is opened in that folder. - - - This item is displayed in the - Plugins - menu. - -
diff --git a/doc/docbook/GuideReferenceSupportedFormats.xml b/doc/docbook/GuideReferenceSupportedFormats.xml index a6c69bde..aef372d0 100644 --- a/doc/docbook/GuideReferenceSupportedFormats.xml +++ b/doc/docbook/GuideReferenceSupportedFormats.xml @@ -1,7 +1,11 @@
Supported File Formats - 3FR, ANI, APM, ARW, BMP, CR2, CRW, CUR, DDS, DNG, ERF, GIF, ICNS, ICO, JPE/JPEG/JPG, JPS, KDC, MEF, MPO, MOS, MRW, NEF, ORF, PEF, PTX, PBM/PGM/PNM/PPM, PNG, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WMF, XBM, XPM, HEIF (primary image only), WebP, DjVu. Animated GIFs are supported. + 3FR, ANI, APM, ARW, BMP, CR2, CRW, CUR, DDS, DNG, ERF, GIF, ICNS, ICO, JPE/JPEG/JPG, JPS, KDC, MEF, MPO, MOS, MRW, NEF, ORF, PEF, PTX, PBM/PGM/PNM/PPM, PNG, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WMF, XBM, XPM, HEIF (primary image only), WebP, DjVu. + Images in archive files (.zip, .tar etc) can be displayed. + + Animated GIFs are supported. + Refer to diff --git a/plugins/Makefile.am b/plugins/Makefile.am index d6493926..5b63d6c9 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -1,5 +1,5 @@ -SUBDIRS = rotate symlink ufraw geocode-parameters export-jpeg tethered-photography camera-import image-crop random-image lens open-archive +SUBDIRS = rotate symlink ufraw geocode-parameters export-jpeg tethered-photography camera-import image-crop random-image lens qq_desktoptemplatedir = $(appdir) qq_desktoptemplate_in_files = template.desktop.in qq_desktoptemplate_DATA = $(qq_desktoptemplate_in_files:.desktop.in=.desktop) diff --git a/plugins/open-archive/Makefile.am b/plugins/open-archive/Makefile.am deleted file mode 100644 index 78638316..00000000 --- a/plugins/open-archive/Makefile.am +++ /dev/null @@ -1,10 +0,0 @@ -dist_gq_bin_SCRIPTS = geeqie-open-archive - -gq_desktopdir = $(appdir)/applications -gq_desktop_in_files = open-archive.desktop.in -gq_desktop_DATA = $(gq_desktop_in_files:.desktop.in=.desktop) -@INTLTOOL_DESKTOP_RULE@ - -EXTRA_DIST = $(gq_desktop_in_files) -CLEANFILES = $(gq_desktop_DATA) - diff --git a/plugins/open-archive/geeqie-open-archive b/plugins/open-archive/geeqie-open-archive deleted file mode 100755 index d5424331..00000000 --- a/plugins/open-archive/geeqie-open-archive +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/bash - -# Extract the contents of an archive file to a -# temporary folder under /tmp. -# -# Open a new Geeqie window pointing to that folder. - -full_path=$(realpath "$1") -filename=$(basename -- "$1") -extension="${filename#*.}" - -case $extension in - - zip) - if [ $(which unzip > /dev/null; echo $? ) = 0 ] - then - rm --recursive --force "/tmp/geeqie-archive/$full_path" - mkdir --parents "/tmp/geeqie-archive/$full_path" - unzip "$full_path" -d "/tmp/geeqie-archive/$full_path" > /dev/null - geeqie --remote --new-window "/tmp/geeqie-archive/$full_path" - else - zenity --title="Geeqie Open Archive" --info --width=300 --text="Utility unzip is not installed" - fi - ;; - - tar.gz) - if [ $(which tar > /dev/null; echo $? ) = 0 ] - then - rm --recursive --force "/tmp/geeqie-archive/$full_path" - mkdir --parents "/tmp/geeqie-archive/$full_path" - tar --extract --gunzip --directory "/tmp/geeqie-archive/$full_path" --file="$full_path" > /dev/null - geeqie --remote --new-window "/tmp/geeqie-archive/$full_path" - else - zenity --title="Geeqie Open Archive" --info --width=300 --text="Utility tar is not installed" - fi - ;; - - cbr | rar) - if [ $(which unrar > /dev/null; echo $? ) = 0 ] - then - rm --recursive --force rf "/tmp/geeqie-archive/$full_path" - mkdir --parents "/tmp/geeqie-archive/$full_path" - unrar "$full_path" "/tmp/geeqie-archive/$full_path" > /dev/null - geeqie --remote --new-window "/tmp/geeqie-archive/$full_path" - else - zenity --title="Geeqie Open Archive" --info --width=300 --text="Utility unrar is not installed" - fi - ;; - - *) - zenity --title="Geeqie Open Archive" --info --width=300 --text="This is not a known archive file type" - ;; -esac - diff --git a/plugins/open-archive/open-archive.desktop.in b/plugins/open-archive/open-archive.desktop.in deleted file mode 100644 index 649199ef..00000000 --- a/plugins/open-archive/open-archive.desktop.in +++ /dev/null @@ -1,16 +0,0 @@ -[Desktop Entry] -Version=1.0 -Type=Application -_Name=Open archive file - -# call the helper script -Exec=geeqie-open-archive %f - -# Desktop files that are usable only in Geeqie should be marked like this: -Categories=X-Geeqie; -OnlyShowIn=X-Geeqie; - -# It can be made verbose -# X-Geeqie-Verbose=true - -Icon=package-x-generic diff --git a/src/Makefile.am b/src/Makefile.am index 09a1b147..db943274 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -307,7 +307,7 @@ geeqie_SOURCES = \ zonedetect.c \ zonedetect.h -geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(TIFF_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS) $(LUA_LIBS) $(CLUTTER_LIBS) $(CLUTTER_GTK_LIBS) $(FFMPEGTHUMBNAILER_LIBS) $(PDF_LIBS) $(HEIF_LIBS) $(WEBP_LIBS) $(DJVU_LIBS) $(J2K_LIBS) $(RAW_LIBS) $(JPEGXL_LIBS) +geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(TIFF_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS) $(LUA_LIBS) $(CLUTTER_LIBS) $(CLUTTER_GTK_LIBS) $(FFMPEGTHUMBNAILER_LIBS) $(PDF_LIBS) $(HEIF_LIBS) $(WEBP_LIBS) $(DJVU_LIBS) $(J2K_LIBS) $(RAW_LIBS) $(JPEGXL_LIBS) $(ARCHIVE_LIBS) EXTRA_DIST = \ $(extra_SLIK) diff --git a/src/filefilter.c b/src/filefilter.c index c0d48424..a45a025f 100644 --- a/src/filefilter.c +++ b/src/filefilter.c @@ -306,10 +306,12 @@ void filter_add_defaults(void) #endif #ifdef HAVE_J2K filter_add_if_missing("jp2", "JPEG 2000", ".jp2", FORMAT_CLASS_IMAGE, FALSE, FALSE, TRUE); +#endif +#ifdef HAVE_ARCHIVE + filter_add_if_missing("zip", "Archive files", ".zip;.rar;.tar;.tar.gz;.cbr;.cbz;.bz2;.lzh;.lza;.7z", FORMAT_CLASS_ARCHIVE, FALSE, FALSE, TRUE); #endif filter_add_if_missing("psd", "Adobe Photoshop Document", ".psd", FORMAT_CLASS_IMAGE, FALSE, FALSE, TRUE); filter_add_if_missing("apng", "Animated Portable Network Graphic", ".apng", FORMAT_CLASS_IMAGE, FALSE, FALSE, TRUE); - filter_add_if_missing("zip", "Archive files", ".zip;.rar;.cbr;tar.gz", FORMAT_CLASS_ARCHIVE, FALSE, FALSE, TRUE); } GList *filter_to_list(const gchar *extensions) diff --git a/src/img-view.c b/src/img-view.c index eb00f7ce..c6a241b0 100644 --- a/src/img-view.c +++ b/src/img-view.c @@ -592,11 +592,27 @@ static void button_cb(ImageWindow *imd, GdkEventButton *event, gpointer data) { ViewWindow *vw = data; GtkWidget *menu; + gchar *dest_dir; + LayoutWindow *lw_new; switch (event->button) { case MOUSE_BUTTON_LEFT: - if (options->image_l_click_video && options->image_l_click_video_editor && imd->image_fd->format_class == FORMAT_CLASS_VIDEO) + if (options->image_l_click_archive && imd->image_fd->format_class == FORMAT_CLASS_ARCHIVE) + { + dest_dir = open_archive(imd->image_fd); + if (dest_dir) + { + lw_new = layout_new_from_default(); + layout_set_path(lw_new, dest_dir); + g_free(dest_dir); + } + else + { + warning_dialog(_("Cannot open archive file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, NULL); + } + } + else if (options->image_l_click_video && options->image_l_click_video_editor && imd->image_fd->format_class == FORMAT_CLASS_VIDEO) { start_editor_from_file(options->image_l_click_video_editor, imd->image_fd); } diff --git a/src/layout_image.c b/src/layout_image.c index 83e4170e..7caccf69 100644 --- a/src/layout_image.c +++ b/src/layout_image.c @@ -1843,11 +1843,27 @@ static void layout_image_button_cb(ImageWindow *imd, GdkEventButton *event, gpoi { LayoutWindow *lw = data; GtkWidget *menu; + LayoutWindow *lw_new; + gchar *dest_dir; switch (event->button) { case MOUSE_BUTTON_LEFT: - if (options->image_l_click_video && options->image_l_click_video_editor && imd->image_fd && imd->image_fd->format_class == FORMAT_CLASS_VIDEO) + if (options->image_l_click_archive && imd-> image_fd && imd->image_fd->format_class == FORMAT_CLASS_ARCHIVE) + { + dest_dir = open_archive(imd->image_fd); + if (dest_dir) + { + lw_new = layout_new_from_default(); + layout_set_path(lw_new, dest_dir); + g_free(dest_dir); + } + else + { + warning_dialog(_("Cannot open archive file"), _("See the Log Window"), GTK_STOCK_DIALOG_WARNING, NULL); + } + } + else if (options->image_l_click_video && options->image_l_click_video_editor && imd-> image_fd && imd->image_fd->format_class == FORMAT_CLASS_VIDEO) { start_editor_from_file(options->image_l_click_video_editor, imd->image_fd); } diff --git a/src/layout_util.c b/src/layout_util.c index d1e1011c..9ed0e3be 100644 --- a/src/layout_util.c +++ b/src/layout_util.c @@ -890,6 +890,25 @@ static void layout_menu_view_in_new_window_cb(GtkAction *action, gpointer data) view_window_new(layout_image_get_fd(lw)); } +static void layout_menu_open_archive_cb(GtkAction *action, gpointer data) +{ + LayoutWindow *lw = data; + LayoutWindow *lw_new; + gchar *dest_dir; + FileData *fd; + + layout_exit_fullscreen(lw); + fd = layout_image_get_fd(lw); + + if (fd->format_class == FORMAT_CLASS_ARCHIVE) + { + dest_dir = open_archive(layout_image_get_fd(lw)); + lw_new = layout_new_from_default(); + layout_set_path(lw_new, dest_dir); + g_free(dest_dir); + } +} + static void layout_menu_fullscreen_cb(GtkAction *action, gpointer data) { LayoutWindow *lw = data; @@ -2225,6 +2244,42 @@ static void layout_menu_windows_menu_cb(GtkWidget *widget, gpointer data) } } +static void layout_menu_view_menu_cb(GtkWidget *widget, gpointer data) +{ + LayoutWindow *lw = data; + GtkWidget *menu; + GtkWidget *sub_menu; + gchar *menu_label; + GList *children, *iter; + gint i; + FileData *fd; + + menu = gtk_ui_manager_get_widget(lw->ui_manager, "/MainMenu/ViewMenu/"); + sub_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu)); + + fd = layout_image_get_fd(lw); + + i = 0; + children = gtk_container_get_children(GTK_CONTAINER(sub_menu)); + for (iter = children; iter != NULL; iter = g_list_next(iter), i++) + { + menu_label = g_strdup(gtk_menu_item_get_label(GTK_MENU_ITEM(iter->data))); + if (g_strcmp0(menu_label, _("Open archive")) == 0) + { + if (fd->format_class == FORMAT_CLASS_ARCHIVE) + { + gtk_widget_set_sensitive(GTK_WIDGET(iter->data), TRUE); + } + else + { + gtk_widget_set_sensitive(GTK_WIDGET(iter->data), FALSE); + } + } + g_free(menu_label); + } + g_list_free(children); +} + static void change_window_id(const gchar *infile, const gchar *outfile) { GFile *in_file; @@ -2390,7 +2445,7 @@ static GtkActionEntry menu_entries[] = { { "OrientationMenu", NULL, N_("_Orientation"), NULL, NULL, NULL }, { "RatingMenu", NULL, N_("_Rating"), NULL, NULL, NULL }, { "PreferencesMenu", NULL, N_("P_references"), NULL, NULL, NULL }, - { "ViewMenu", NULL, N_("_View"), NULL, NULL, NULL }, + { "ViewMenu", NULL, N_("_View"), NULL, NULL, CB(layout_menu_view_menu_cb) }, { "FileDirMenu", NULL, N_("_Files and Folders"), NULL, NULL, NULL }, { "ZoomMenu", NULL, N_("_Zoom"), NULL, NULL, NULL }, { "ColorMenu", NULL, N_("_Color Management"), NULL, NULL, NULL }, @@ -2506,6 +2561,7 @@ static GtkActionEntry menu_entries[] = { { "ConnectZoom33", NULL, N_("Zoom 1:3"), NULL, N_("Connected Zoom 1:3"), CB(layout_menu_connect_zoom_1_3_cb) }, { "ConnectZoom25", NULL, N_("Zoom 1:4"), NULL, N_("Connected Zoom 1:4"), CB(layout_menu_connect_zoom_1_4_cb) }, { "ViewInNewWindow", NULL, N_("_View in new window"), "V", N_("View in new window"), CB(layout_menu_view_in_new_window_cb) }, + { "OpenArchive", GTK_STOCK_OPEN, N_("Open archive"), NULL, N_("Open archive"), CB(layout_menu_open_archive_cb) }, { "FullScreen", GTK_STOCK_FULLSCREEN, N_("F_ull screen"), "F", N_("Full screen"), CB(layout_menu_fullscreen_cb) }, { "FullScreenAlt1", GTK_STOCK_FULLSCREEN, N_("F_ull screen"), "V", N_("Full screen"), CB(layout_menu_fullscreen_cb) }, { "FullScreenAlt2", GTK_STOCK_FULLSCREEN, N_("F_ull screen"), "F11", N_("Full screen"), CB(layout_menu_fullscreen_cb) }, @@ -2727,6 +2783,7 @@ static const gchar *menu_ui_description = " " " " " " +" " " " " " " " diff --git a/src/main.c b/src/main.c index ce535cb0..e7f6b69f 100644 --- a/src/main.c +++ b/src/main.c @@ -271,6 +271,7 @@ gchar *gq_app_dir; gchar *gq_bin_dir; gchar *gq_executable_path; gchar *desktop_file_template; +gchar *instance_identifier; /* *----------------------------------------------------------------------------- @@ -1076,6 +1077,8 @@ static void exit_program_final(void) LayoutWindow *lw = NULL; GList *list; LayoutWindow *tmp_lw; + gchar *archive_dir; + GFile *archive_file; /* make sure that external editors are loaded, we would save incomplete configuration otherwise */ layout_editors_reload_finish(); @@ -1108,6 +1111,27 @@ static void exit_program_final(void) layout_free(lw); } + /* Delete any files/folders in /tmp that have been created by the open archive function */ + archive_dir = g_build_filename(g_get_tmp_dir(), GQ_ARCHIVE_DIR, instance_identifier, NULL); + if (isdir(archive_dir)) + { + archive_file = g_file_new_for_path(archive_dir); + rmdir_recursive(archive_file, NULL, NULL); + g_free(archive_dir); + g_object_unref(archive_file); + } + + /* If there are still sub-dirs created by another instance, this will fail + * but that does not matter */ + archive_dir = g_build_filename(g_get_tmp_dir(), GQ_ARCHIVE_DIR, NULL); + if (isdir(archive_dir)) + { + archive_file = g_file_new_for_path(archive_dir); + g_file_delete(archive_file, NULL, NULL); + g_free(archive_dir); + g_object_unref(archive_file); + } + secure_close(command_line->ssi); gtk_main_quit(); @@ -1349,6 +1373,9 @@ gint main(gint argc, gchar *argv[]) options->disable_gpu = TRUE; } + /* Generate a unique identifier used by the open archive function */ + instance_identifier = g_strdup_printf("%x", g_random_int()); + DEBUG_1("%s main: mkdir_if_not_exists", get_exec_time()); /* these functions don't depend on config file */ mkdir_if_not_exists(get_rc_dir()); diff --git a/src/main.h b/src/main.h index b105cbad..426017c9 100644 --- a/src/main.h +++ b/src/main.h @@ -84,6 +84,7 @@ #define GQ_COLLECTIONS_DIR "collections" #define GQ_TRASH_DIR "trash" #define GQ_WINDOW_LAYOUTS_DIR "layouts" +#define GQ_ARCHIVE_DIR "geeqie-archive" #define GQ_SYSTEM_WIDE_DIR "/etc/" GQ_APPNAME_LC @@ -153,6 +154,7 @@ extern gchar *gq_app_dir; extern gchar *gq_bin_dir; extern gchar *gq_executable_path; extern gchar *desktop_file_template; +extern gchar *instance_identifier; void keyboard_scroll_calc(gint *x, gint *y, GdkEventKey *event); gint key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer data); diff --git a/src/misc.c b/src/misc.c index d8b1440d..50a0a6b6 100644 --- a/src/misc.c +++ b/src/misc.c @@ -22,6 +22,8 @@ #include "misc.h" #include "ui_fileops.h" +#include +#include #include #include @@ -408,4 +410,161 @@ void tree_path_free_wrapper(void *data, void *useradata) gtk_tree_path_free(data); } +/* Copied from the libarchive .repo. examples */ + +static void errmsg(const char *); +static gboolean extract(const char *filename, int do_extract, int flags); +static int copy_data(struct archive *, struct archive *); +static void msg(const char *); +static int verbose = 0; + +gchar *open_archive(FileData *fd) +{ + int flags; + gchar *current_dir; + gchar *destination_dir; + gboolean success; + + destination_dir = g_build_filename(g_get_tmp_dir(), GQ_ARCHIVE_DIR, instance_identifier, fd->path, NULL); + + recursive_mkdir_if_not_exists(destination_dir, 0755); + + current_dir = g_get_current_dir(); + chdir(destination_dir); + + flags = ARCHIVE_EXTRACT_TIME; + success = extract(fd->path, 1, flags); + + chdir(current_dir); + g_free(current_dir); + + if (!success) + { + g_free(destination_dir); + destination_dir = NULL; + } + + return destination_dir; +} + +static gboolean extract(const char *filename, int do_extract, int flags) +{ + struct archive *a; + struct archive *ext; + struct archive_entry *entry; + int r; + + a = archive_read_new(); + ext = archive_write_disk_new(); + archive_write_disk_set_options(ext, flags); + archive_write_disk_set_standard_lookup(ext); + archive_read_support_filter_all(a); + archive_read_support_format_all(a); + + if (filename != NULL && strcmp(filename, "-") == 0) + { + filename = NULL; + } + if ((r = archive_read_open_filename(a, filename, 10240))) + { + errmsg(archive_error_string(a)); + errmsg("\n"); + return(FALSE); + } + for (;;) + { + int needcr = 0; + + r = archive_read_next_header(a, &entry); + if (r == ARCHIVE_EOF) + { + break; + } + if (r != ARCHIVE_OK) + { + errmsg(archive_error_string(a)); + errmsg("\n"); + return(FALSE); + } + if (verbose && do_extract) + { + msg("x "); + } + if (verbose || !do_extract) + { + msg(archive_entry_pathname(entry)); + msg(" "); + needcr = 1; + } + if (do_extract) + { + r = archive_write_header(ext, entry); + if (r != ARCHIVE_OK) + { + errmsg(archive_error_string(a)); + needcr = 1; + } + else + { + r = copy_data(a, ext); + if (r != ARCHIVE_OK) + { + needcr = 1; + } + } + } + if (needcr) + { + msg("\n"); + } + } + archive_read_close(a); + archive_read_free(a); + + archive_write_close(ext); + archive_write_free(ext); + return(TRUE); +} + +static int +copy_data(struct archive *ar, struct archive *aw) +{ + int r; + const void *buff; + size_t size; + int64_t offset; + + for (;;) + { + r = archive_read_data_block(ar, &buff, &size, &offset); + if (r == ARCHIVE_EOF) + return (ARCHIVE_OK); + if (r != ARCHIVE_OK) + { + errmsg(archive_error_string(ar)); + return (r); + } + r = archive_write_data_block(aw, buff, size, offset); + if (r != ARCHIVE_OK) + { + errmsg(archive_error_string(ar)); + return (r); + } + } +} + +static void msg(const char *m) +{ + log_printf("%s \n", m); +} + +static void errmsg(const char *m) +{ + if (m == NULL) + { + m = "Error: No error description provided.\n"; + } + log_printf("%s \n", m); +} + /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/misc.h b/src/misc.h index e7ade952..c5626820 100644 --- a/src/misc.h +++ b/src/misc.h @@ -33,5 +33,6 @@ gchar *convert_rating_to_stars(gint rating); gchar *get_symbolic_link(const gchar *path_utf8); gint get_cpu_cores(void); void tree_path_free_wrapper(void *data, void *useradata); +gchar *open_archive(FileData *fd); #endif /* MISC_H */ /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/options.c b/src/options.c index 355aac56..c51df5bc 100644 --- a/src/options.c +++ b/src/options.c @@ -141,6 +141,7 @@ ConfOptions *init_options(ConfOptions *options) options->lazy_image_sync = FALSE; options->mousewheel_scrolls = FALSE; options->image_lm_click_nav = TRUE; + options->image_l_click_archive = FALSE; options->image_l_click_video = FALSE; options->image_l_click_video_editor = NULL; options->open_recent_list_maxsize = 10; diff --git a/src/options.h b/src/options.h index ce050551..31b34e76 100644 --- a/src/options.h +++ b/src/options.h @@ -31,6 +31,7 @@ struct _ConfOptions gboolean place_dialogs_under_mouse; gboolean mousewheel_scrolls; gboolean image_lm_click_nav; + gboolean image_l_click_archive; gboolean image_l_click_video; gchar *image_l_click_video_editor; gboolean show_icon_names; diff --git a/src/preferences.c b/src/preferences.c index 47ebd294..6ce4eb23 100644 --- a/src/preferences.c +++ b/src/preferences.c @@ -328,6 +328,7 @@ static void config_window_apply(void) options->mousewheel_scrolls = c_options->mousewheel_scrolls; options->image_lm_click_nav = c_options->image_lm_click_nav; + options->image_l_click_archive = c_options->image_l_click_archive; options->image_l_click_video = c_options->image_l_click_video; options->image_l_click_video_editor = c_options->image_l_click_video_editor; @@ -3477,6 +3478,8 @@ static void config_tab_behavior(GtkWidget *notebook) options->mousewheel_scrolls, &c_options->mousewheel_scrolls); pref_checkbox_new_int(group, _("Navigation by left or middle click on image"), options->image_lm_click_nav, &c_options->image_lm_click_nav); + pref_checkbox_new_int(group, _("Open archive by left click on image"), + options->image_l_click_archive, &c_options->image_l_click_archive); pref_checkbox_new_int(group, _("Play video by left click on image"), options->image_l_click_video, &c_options->image_l_click_video); table = pref_table_new(group, 2, 1, FALSE, FALSE); diff --git a/src/rcfile.c b/src/rcfile.c index 3cae3130..1a71400b 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -330,6 +330,7 @@ static void write_global_attributes(GString *outstr, gint indent) WRITE_NL(); WRITE_BOOL(*options, mousewheel_scrolls); WRITE_NL(); WRITE_BOOL(*options, image_lm_click_nav); + WRITE_NL(); WRITE_BOOL(*options, image_l_click_archive); WRITE_NL(); WRITE_BOOL(*options, image_l_click_video); WRITE_NL(); WRITE_CHAR(*options, image_l_click_video_editor); WRITE_NL(); WRITE_INT(*options, open_recent_list_maxsize); @@ -825,6 +826,7 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar ** if (READ_BOOL(*options, mousewheel_scrolls)) continue; if (READ_BOOL(*options, image_lm_click_nav)) continue; + if (READ_BOOL(*options, image_l_click_archive)) continue; if (READ_BOOL(*options, image_l_click_video)) continue; if (READ_CHAR(*options, image_l_click_video_editor)) continue; diff --git a/src/toolbar.c b/src/toolbar.c index 9d229c8c..6aaff5d1 100644 --- a/src/toolbar.c +++ b/src/toolbar.c @@ -97,6 +97,7 @@ static const UseableToolbarItems useable_toolbar_items[] = { {"Delete", N_("Delete"), GTK_STOCK_DELETE}, {"CloseWindow", N_("Close Window"), GTK_STOCK_CLOSE}, {"PanView", N_("Pan view"), PIXBUF_INLINE_ICON_PANORAMA}, + {"OpenArchive", N_("Open Archive"), PIXBUF_INLINE_ARCHIVE}, {"SelectAll", N_("Select all"), PIXBUF_INLINE_ICON_SELECT_ALL}, {"SelectNone", N_("Select none"), PIXBUF_INLINE_ICON_SELECT_NONE}, {"SelectInvert", N_("Select invert"), PIXBUF_INLINE_ICON_SELECT_INVERT}, diff --git a/src/ui_fileops.c b/src/ui_fileops.c index d14c0ce2..2c35490d 100644 --- a/src/ui_fileops.c +++ b/src/ui_fileops.c @@ -1141,4 +1141,26 @@ gboolean download_web_file(const gchar *text, gboolean minimized, gpointer data) return ret; } + +gboolean rmdir_recursive(GFile *file, GCancellable *cancellable, GError **error) +{ + g_autoptr(GFileEnumerator) enumerator = NULL; + + enumerator = g_file_enumerate_children(file, G_FILE_ATTRIBUTE_STANDARD_NAME, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, NULL); + + while (enumerator != NULL) + { + GFile *child; + + if (!g_file_enumerator_iterate(enumerator, NULL, &child, cancellable, error)) + return FALSE; + if (child == NULL) + break; + if (!rmdir_recursive(child, cancellable, error)) + return FALSE; + } + + return g_file_delete(file, cancellable, error); +} + /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/ui_fileops.h b/src/ui_fileops.h index 84c50683..0122467b 100644 --- a/src/ui_fileops.h +++ b/src/ui_fileops.h @@ -118,5 +118,6 @@ gchar *md5_text_from_file_utf8(const gchar *path, const gchar *error_text); gboolean md5_get_digest_from_file_utf8(const gchar *path, guchar digest[16]); gboolean download_web_file(const gchar *text, gboolean minimized, gpointer data); +gboolean rmdir_recursive(GFile *file, GCancellable *cancellable, GError **error); #endif /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */