Addl fix #299: File Compression and Archiving
authorColin Clark <colin.clark@cclark.uk>
Fri, 17 Sep 2021 15:05:33 +0000 (16:05 +0100)
committerColin Clark <colin.clark@cclark.uk>
Fri, 17 Sep 2021 15:05:33 +0000 (16:05 +0100)
https://github.com/BestImageViewer/geeqie/issues/299

Use libarchive to extract the files, instead of a plugin.

27 files changed:
README.md
configure.ac
doc/docbook/GuideOptionsBehavior.xml
doc/docbook/GuideOptionsFiltering.xml
doc/docbook/GuideReferencePixbufLoaders.xml
doc/docbook/GuideReferenceStandardPlugins.xml
doc/docbook/GuideReferenceSupportedFormats.xml
plugins/Makefile.am
plugins/open-archive/Makefile.am [deleted file]
plugins/open-archive/geeqie-open-archive [deleted file]
plugins/open-archive/open-archive.desktop.in [deleted file]
src/Makefile.am
src/filefilter.c
src/img-view.c
src/layout_image.c
src/layout_util.c
src/main.c
src/main.h
src/misc.c
src/misc.h
src/options.c
src/options.h
src/preferences.c
src/rcfile.c
src/toolbar.c
src/ui_fileops.c
src/ui_fileops.h

index 779893f..b5b23f0 100644 (file)
--- 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
 
index ff6d44d..c4c1bcd 100644 (file)
@@ -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
index 1d3ae3b..3459f4d 100644 (file)
           <para>If selected, mouse left-click will select the next image; mouse middle-click will select the previous image.</para>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <guilabel>Open archive by left click on image</guilabel>\r
+        </term>\r
+        <listitem>\r
+          <para>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.</para>\r
+        </listitem>\r
+      </varlistentry>\r
       <varlistentry>\r
         <term>\r
           <guilabel>Play video by left click on image</guilabel>\r
index 64e977c..da7861c 100644 (file)
               <listitem>Video</listitem>\r
               <listitem>Collection</listitem>\r
               <listitem>Document</listitem>\r
+              <listitem>Archive (.zip, .rar etc.)</listitem>\r
             </itemizedlist>\r
           </listitem>\r
         </varlistentry>\r
index 3b0d3e3..f1519b9 100644 (file)
@@ -3,14 +3,6 @@
   <title id="titleGuideReferencePixbufLoaders">Additional pixbuf loaders</title>\r
   <para>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.</para>\r
   <para>However, in some cases you are required to compile and install them yourself.</para>\r
-  <section id="svg">\r
-    <title>svg</title>\r
-    <para>librsvg2-common</para>\r
-  </section>\r
-  <section id="wmf">\r
-    <title>wmf</title>\r
-    <para>libwmf0.2-7-gtk</para>\r
-  </section>\r
   <section id="jp2">\r
     <title>JP2/JPC/JPX/J2K/JPF</title>\r
     <para>If your distribution has the libpixbufloader-jasper.so loader, these file formats will also be displayed.</para>\r
index 89ec41c..bfbde9a 100644 (file)
       menu.\r
     </para>\r
   </section>\r
-  <section id="OpenArchive">\r
-    <title>Open archive file</title>\r
-    <para>\r
-      Opens an archive file (of the type .zip, .rar, .cbr, .tar.gz) and extracts the contents into a folder in <code>/tmp</code>. A new Geeqie window is opened in that folder.\r
-    </para>\r
-    <para>\r
-      This item is displayed in the\r
-      <emphasis role="strong">Plugins</emphasis>\r
-      menu.\r
-    </para>\r
-  </section>\r
 </section>\r
index a6c69bd..aef372d 100644 (file)
@@ -1,7 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>\r
 <section id="GuideReferenceSupportedFormats">\r
   <title id="titleGuideReferenceSupportedFormats">Supported File Formats</title>\r
-  <para>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.</para>\r
+  <para>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.</para>\r
+  <para>Images in archive files (.zip, .tar etc) can be displayed.\r
+  </para>\r
+  <para>Animated GIFs are supported.\r
+  </para>\r
   <para>\r
     Refer to\r
     <link linkend="GuideReferencePixbufLoaders" endterm="titleGuideReferencePixbufLoaders" />\r
index d649392..5b63d6c 100644 (file)
@@ -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 (file)
index 7863831..0000000
+++ /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 (executable)
index d542433..0000000
+++ /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 (file)
index 649199e..0000000
+++ /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
index 09a1b14..db94327 100644 (file)
@@ -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)
index c0d4842..a45a025 100644 (file)
@@ -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)
index eb00f7c..c6a241b 100644 (file)
@@ -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);
                                }
index 83e4170..7caccf6 100644 (file)
@@ -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);
                                }
index d1e1011..9ed0e3b 100644 (file)
@@ -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"),              "<control>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 =
 "      <menuitem action='ViewInNewWindow'/>"
 "      <menuitem action='PanView'/>"
 "      <menuitem action='ExifWin'/>"
+"      <menuitem action='OpenArchive'/>"
 "      <placeholder name='WindowSection'/>"
 "      <separator/>"
 "      <menu action='FileDirMenu'>"
index ce535cb..e7f6b69 100644 (file)
@@ -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());
index b105cba..426017c 100644 (file)
@@ -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);
index d8b1440..50a0a6b 100644 (file)
@@ -22,6 +22,8 @@
 #include "misc.h"
 #include "ui_fileops.h"
 
+#include <archive.h>
+#include <archive_entry.h>
 #include <langinfo.h>
 #include <locale.h>
 
@@ -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: */
index e7ade95..c562682 100644 (file)
@@ -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: */
index 355aac5..c51df5b 100644 (file)
@@ -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;
index ce05055..31b34e7 100644 (file)
@@ -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;
index 47ebd29..6ce4eb2 100644 (file)
@@ -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);
index 3cae313..1a71400 100644 (file)
@@ -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;
 
index 9d229c8..6aaff5d 100644 (file)
@@ -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},
index d14c0ce..2c35490 100644 (file)
@@ -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: */
index 84c5068..0122467 100644 (file)
@@ -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: */