Merge remote-tracking branch 'github/merge-requests/479'
authorKlaus Ethgen <Klaus@Ethgen.de>
Sat, 8 Jul 2017 09:29:51 +0000 (10:29 +0100)
committerKlaus Ethgen <Klaus@Ethgen.de>
Sat, 8 Jul 2017 09:29:51 +0000 (10:29 +0100)
* github/merge-requests/479:
  Update README.md
  Update README.md
  Rename README to README.md
  Rename README to README.md

98 files changed:
.gitignore
.travis.yml [new file with mode: 0644]
Makefile.am
README.md
configure.in
doc/docbook/GuideCollections.xml
doc/docbook/GuideEditorsConfig.xml
doc/docbook/GuideImageSearchFindingDuplicates.xml
doc/docbook/GuideImageSearchSearch.xml
doc/docbook/GuideMainWindowImagePane.xml
doc/docbook/GuideMainWindowMenus.xml
doc/docbook/GuideMainWindowStatusBar.xml
doc/docbook/GuideOptionsBehavior.xml
doc/docbook/GuideOptionsGeneral.xml
doc/docbook/GuideOptionsImage.xml
doc/docbook/GuideOptionsLayout.xml
doc/docbook/GuideOptionsMetadata.xml
doc/docbook/GuideOptionsWindow.xml
doc/docbook/GuideReference.xml
doc/docbook/GuideReferenceCommandLine.xml
doc/docbook/GuideReferenceConfig.xml
doc/docbook/GuideReferenceDecodeLatLong.xml [new file with mode: 0644]
doc/docbook/GuideReferenceKeyboardShortcuts.xml
doc/docbook/GuideReferenceTags.xml [new file with mode: 0644]
doc/docbook/GuideSidebarsInfo.xml
doc/docbook/GuideSidebarsSortManager.xml
geeqie.spec.in
plugins/rotate/geeqie-rotate
src/Makefile.am
src/bar.c
src/bar_comment.c
src/bar_gps.c
src/bar_keywords.c
src/bar_keywords.h
src/bar_sort.c
src/cache_maint.c
src/cache_maint.h
src/cellrenderericon.c
src/collect.c
src/debug.c
src/debug.h
src/dupe.c
src/exif.c
src/exif.h
src/exiv2.cc
src/filedata.c
src/image-overlay.c
src/image.c
src/img-view.c
src/layout.c
src/layout_image.c
src/layout_image.h
src/layout_util.c
src/logwindow.c
src/logwindow.h
src/main.c
src/menu.c
src/menu.h
src/metadata.c
src/metadata.h
src/misc.c
src/misc.h
src/options.c
src/options.h
src/pan-view/Makefile.am
src/pan-view/pan-calendar.c
src/pan-view/pan-folder.c
src/pan-view/pan-grid.c
src/pan-view/pan-timeline.c
src/pan-view/pan-types.h
src/pan-view/pan-view-filter.c [new file with mode: 0644]
src/pan-view/pan-view-filter.h [new file with mode: 0644]
src/pan-view/pan-view-search.c [new file with mode: 0644]
src/pan-view/pan-view-search.h [new file with mode: 0644]
src/pan-view/pan-view.c
src/pan-view/pan-view.h
src/pixbuf-renderer.c
src/pixbuf-renderer.h
src/preferences.c
src/print.c
src/rcfile.c
src/remote.c
src/renderer-tiles.c
src/search.c
src/secure_save.c
src/typedefs.h
src/ui_fileops.c
src/ui_fileops.h
src/ui_pathsel.c
src/utilops.c
src/view_file.c
src/view_file/Makefile.am [new file with mode: 0644]
src/view_file/view_file_common.c [new file with mode: 0644]
src/view_file/view_file_icon.c [moved from src/view_file_icon.c with 77% similarity]
src/view_file/view_file_icon.h [moved from src/view_file_icon.h with 96% similarity]
src/view_file/view_file_list.c [moved from src/view_file_list.c with 99% similarity]
src/view_file/view_file_list.h [moved from src/view_file_list.h with 97% similarity]
src/window.c

index 4b906f6..8654fd2 100644 (file)
@@ -73,6 +73,10 @@ Makefile.in
 /src/pan-view/.deps
 /src/pan-view/.dirstamp
 
+# /src/view_file/
+/src/view_file/.deps
+/src/view_file/.dirstamp
+
 /build-stamp
 /debian/geeqie*
 /debian/files
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..6229169
--- /dev/null
@@ -0,0 +1,32 @@
+language: c
+os: linux
+dist: trusty
+sudo: required
+compiler:
+  - gcc
+  - clang
+addons:
+  apt:
+    sources:
+      - trusty
+    packages:
+      - autoconf
+      - automake
+      - autotools-dev
+      - build-essential
+      - debhelper
+      - dh-autoreconf
+      - dpkg-dev
+      - gnome-doc-utils
+      - gnome-doc-utils
+      - imagemagick
+      - intltool
+      - libexiv2-dev
+      - libgtk2.0-dev
+      - libjpeg-dev
+      - liblcms2-dev
+      - liblircclient-dev
+      - liblua5.1-0-dev
+      - libtiff-dev
+      - libtool
+script: (CFLAGS= ./autogen.sh) && make
index 610c55a..cd03ccd 100644 (file)
@@ -8,7 +8,7 @@ DIST_SUBDIRS = src po doc plugins
 man_MANS = geeqie.1
 
 readmedir = @readmedir@
-readme_DATA = README COPYING ChangeLog TODO README.lirc
+readme_DATA = README.md COPYING ChangeLog TODO README.lirc
 
 desktopdir = $(datadir)/applications
 desktop_in_files = geeqie.desktop.in
index d77d216..6b8fba5 100644 (file)
--- a/README.md
+++ b/README.md
@@ -12,6 +12,8 @@
 
 This is Geeqie, a successor of GQview.
 
+[![Build Status](https://api.travis-ci.org/BestImageViewer/geeqie.svg?branch=master)](https://travis-ci.org/BestImageViewer/geeqie)
+
 Geeqie has been forked from GQview project, because it was not possible to
 contact GQview author and the only maintainer. Geeqie projects goal is to move
 the development forward and also integrate the existing patches.
@@ -39,293 +41,295 @@ open an issue on Github (https://github.com/BestImageViewer/geeqie/issues)
 
 ## Requirements
 
-  Required libraries:
+### Required libraries:
     GTK+ 3.00
-               www.gtk.org
-               enabled by default
-               disable with configure option: --disable-gtk3
+        www.gtk.org
+        enabled by default
+        disable with configure option: --disable-gtk3
     or
     GTK+ 2.20
-               disabled by default when GTK+3 libraries are found.
-               enable with configure option: --disable-gtk3
-              optional items map display and GPU acceleration are not available
-              with GTK2
+        disabled by default when GTK+3 libraries are found.
+        enable with configure option: --disable-gtk3
+        optional items map display and GPU acceleration are not available
+        with GTK2
 
-              Note: GTK+3 is still somehow experimental. It is needed for some
-              features but we have several complains about the GTK+3 usability.
+        Note: GTK+3 is still somehow experimental. It is needed for some
+        features but we have several complains about the GTK+3 usability.
 
-              So if you need a stable version, you are advised to compile it
-              with GTK+2. If you want to play with the cool new features, use
-              GTK+3.
+        So if you need a stable version, you are advised to compile it
+        with GTK+2. If you want to play with the cool new features, use
+        GTK+3.
 
-  Optional libraries:
+### Optional libraries:
     lcms2 2.0
     or
     lcms 1.14
-               www.littlecms.com
-               for color management support
-               enabled by default
-               disable with configure option: --disable-lcms
+        www.littlecms.com
+        for color management support
+        enabled by default
+        disable with configure option: --disable-lcms
 
     exiv2 0.11
-               www.exiv2.org
-               for enhanced exif support
-               enabled by default
-               disable with configure option: --disable-exiv2
+        www.exiv2.org
+        for enhanced exif support
+        enabled by default
+        disable with configure option: --disable-exiv2
 
     lirc
-               www.lirc.org
-               for remote control support
-               enabled by default
-               disable with configure option: --disable-lirc
+        www.lirc.org
+        for remote control support
+        enabled by default
+        disable with configure option: --disable-lirc
 
     libchamplain-gtk 0.12
     libchamplain 0.12
     libclutter 1.0
-               wiki.gnome.org/Projects/libchamplain
-               for map display
-               disabled by default
-               enable with configure option: --enable-map
-               enabling will also enable GPU acceleration
+        wiki.gnome.org/Projects/libchamplain
+        for map display
+        disabled by default
+        enable with configure option: --enable-map
+        enabling will also enable GPU acceleration
 
     libclutter 1.0
-               www.clutter-project.org
-               for GPU acceleration (a check-box on Preferences/Image must also be ticked)
-               disabled by default
-               enable with configure option: --enable-gpu-accel
-               explicitly disabling will also disable the map feature
+        www.clutter-project.org
+        for GPU acceleration (a check-box on Preferences/Image must also be ticked)
+        disabled by default
+        enable with configure option: --enable-gpu-accel
+        explicitly disabling will also disable the map feature
 
     lua 5.1
-               www.lua.org
-               support for lua scripting
-               enabled by default
-               disable with configure option: --disable-lua
+        www.lua.org
+        support for lua scripting
+        enabled by default
+        disable with configure option: --disable-lua
 
 ## Notes and changes for this release            [section:release_notes]
 
 See NEWS file.
 
-  Code hackers:
+### Code hackers:
 
-    If you plan on making any major changes to the code that will be offered for
-    inclusion to the main source, please contact us first - so that we can avoid
-    duplication of effort.
+If you plan on making any major changes to the code that will be offered for
+inclusion to the main source, please contact us first - so that we can avoid
+duplication of effort.
                                                          The Geeqie Team
 
-  Known bugs:
+### Known bugs:
 
-    See the Geeqie Bug Tracker at https://github.com/BestImageViewer/geeqie/issues
+See the Geeqie Bug Tracker at https://github.com/BestImageViewer/geeqie/issues
 
 ## Installation
 
-  Compilation: ./autogen.sh ; make
-  Show compile options: ./autogen.sh --help
-  General install: make install
-  Removal: make uninstall
+Compilation: `./autogen.sh ; make`
+
+Show compile options: `./autogen.sh --help`
+
+General install: `make install`
+
+Removal: `make uninstall`
 
 ## Description / Features
 
-  Geeqie is a graphics file viewer. Basic features:
-
-- Single click image viewing / navigation.
-- Zoom functions.
-- Thumbnails, with optional caching and .xvpics support.
-- Multiple file selection for move, copy, delete, rename, drag and drop.
-- Drag and drop.
-- Slideshow.
-- Full screen.
-- Ability to open images in external editors (configurable).
-- Collections.
-- Comparison of images to find duplicates by name, size, date,
-  dimensions, or image content similarity.
-  - Rotation invariant detection
-  - EXIF support.
-  - support for stereoscopic images
-    - input: side-by-side (JPS) and MPO format
-    - output: single image, anaglyph, SBS, mirror, SBS half size (3DTV)
+Geeqie is a graphics file viewer. Basic features:
+
+* Single click image viewing / navigation.
+* Zoom functions.
+* Thumbnails, with optional caching and .xvpics support.
+* Multiple file selection for move, copy, delete, rename, drag and drop.
+* Drag and drop.
+* Slideshow.
+* Full screen.
+* Ability to open images in external editors (configurable).
+* Collections.
+* Comparison of images to find duplicates by name, size, date, dimensions, or image content similarity.
+    * Rotation invariant detection
+* EXIF support.
+* support for stereoscopic images
+    * input: side-by-side (JPS) and MPO format
+    * output: single image, anaglyph, SBS, mirror, SBS half size (3DTV)
 
 ## Credits                                             [section:credits]
 
-  Translations:
-
-     Grzegorz Kowal <g_kowal@poczta.onet.pl>
-     Zbigniew Chyla <cyba@gnome.pl>
-     Emil Nowak <emil5@go2.pl>
-     Wit Wilinski <wit.wilinski@gmail.com>
-     Tomasz Golinski <tomaszg@math.uwb.edu.pl>
-         for Polish translation
-
-     Christopher R. Gabriel <cgabriel@pluto.linux.it>
-     Di Maggio Salvatore <Salvatore.Dimaggio@bologna.marelli.it>
-     Costantino <inverness1ATvirgilio.it>
-         for Italian translation
+### Translations:
+
+    Grzegorz Kowal <g_kowal@poczta.onet.pl>
+    Zbigniew Chyla <cyba@gnome.pl>
+    Emil Nowak <emil5@go2.pl>
+    Wit Wilinski <wit.wilinski@gmail.com>
+    Tomasz Golinski <tomaszg@math.uwb.edu.pl>
+        for Polish translation
+
+    Christopher R. Gabriel <cgabriel@pluto.linux.it>
+    Di Maggio Salvatore <Salvatore.Dimaggio@bologna.marelli.it>
+    Costantino <inverness1ATvirgilio.it>
+        for Italian translation
 
-     Sandokan <cortex@nextra.sk>
-     Ivan Priesol <priesol@iris-sk.sk>
-     Mgr. Peter Tuharsky <tuharsky@misbb.sk>
-         for Slovak translation
-
-     Rodrigo Sancho Senosiain <ruy_ikari@bigfoot.com>
-     Ariel Fermani <the_end@bbs.frc.utn.edu.ar>
-         for Spanish translation
+    Sandokan <cortex@nextra.sk>
+    Ivan Priesol <priesol@iris-sk.sk>
+    Mgr. Peter Tuharsky <tuharsky@misbb.sk>
+        for Slovak translation
+
+    Rodrigo Sancho Senosiain <ruy_ikari@bigfoot.com>
+    Ariel Fermani <the_end@bbs.frc.utn.edu.ar>
+        for Spanish translation
 
-     Laurent Monin <i18n@norz.org>
-     Eric Lassauge <lassauge@users.sf.net>
-     Jean-Pierre Pedron <jppedron@club-internet.fr>
-     Pascal Bleser <pascal.bleser@atosorigin.com>
-     Nicolas Boos <nicolas.boos@wanadoo.fr>
-     Nicolas Bonifas <nicolas_bonifas@users.sf.net>
-         for French translation
+    Laurent Monin <i18n@norz.org>
+    Eric Lassauge <lassauge@users.sf.net>
+    Jean-Pierre Pedron <jppedron@club-internet.fr>
+    Pascal Bleser <pascal.bleser@atosorigin.com>
+    Nicolas Boos <nicolas.boos@wanadoo.fr>
+    Nicolas Bonifas <nicolas_bonifas@users.sf.net>
+        for French translation
 
-     Fâtih Demir <kabalak@gmx.net>
-         for Turkish translation
+    Fâtih Demir <kabalak@gmx.net>
+        for Turkish translation
 
-     Kam Tik <kamtik@hongkong.com>
-     Abel Cheung <deaddog@deaddog.ws>
-     S.J. Luo <crystal@mickey.ee.nctu.edu.tw>
-     Levin <zjlevin@hotmail.com>
-         for Traditional Chinese (Big5) translation
+    Kam Tik <kamtik@hongkong.com>
+    Abel Cheung <deaddog@deaddog.ws>
+    S.J. Luo <crystal@mickey.ee.nctu.edu.tw>
+    Levin <zjlevin@hotmail.com>
+        for Traditional Chinese (Big5) translation
 
-     Junichi Uekawa <dancer@debian.org>
-     Oleg Andrjushenko <oandr@itec.cn.ua>
-     Michael Bravo <mbravo@tag-ltd.spb.ru>
-     Sergey Pinaev <dfo@antex.ru>
-         for Russian translation
+    Junichi Uekawa <dancer@debian.org>
+    Oleg Andrjushenko <oandr@itec.cn.ua>
+    Michael Bravo <mbravo@tag-ltd.spb.ru>
+    Sergey Pinaev <dfo@antex.ru>
+        for Russian translation
 
-     Guilherme M. Schroeder <slump@ieg.com.br>
-     Vitor Fernandes <vitor_fernandes@SoftHome.net>
-     Herval Ribeiro de Azevêdo <heraze@gmail.com>
-         for Brazilian Portuguese translation
+    Guilherme M. Schroeder <slump@ieg.com.br>
+    Vitor Fernandes <vitor_fernandes@SoftHome.net>
+    Herval Ribeiro de Azevêdo <heraze@gmail.com>
+        for Brazilian Portuguese translation
 
-     Shingo Akagaki <akagaki@ece.numazu-ct.ac.jp>
-     Yuuki Ninomiya <gm@debian.or.jp>
-     Sato Satoru <ss@gnome.gr.jp>
-     Takeshi AIHANA <aihana@gnome.gr.jp>
-         for Japanese translation
+    Shingo Akagaki <akagaki@ece.numazu-ct.ac.jp>
+    Yuuki Ninomiya <gm@debian.or.jp>
+    Sato Satoru <ss@gnome.gr.jp>
+    Takeshi AIHANA <aihana@gnome.gr.jp>
+        for Japanese translation
 
-     Matthias Warkus <mawarkus@t-online.de>
-     Thomas Klausner <wiz@danbala.ifoer.tuwien.ac.at>
-     Danny Milosavljevic <danny_milo@yahoo.com>
-     Ronny Steiner <Post@SIRSteiner.de>
-     Klaus Ethgen <Klaus@Ethgen.de>
-         for German translation
+    Matthias Warkus <mawarkus@t-online.de>
+    Thomas Klausner <wiz@danbala.ifoer.tuwien.ac.at>
+    Danny Milosavljevic <danny_milo@yahoo.com>
+    Ronny Steiner <Post@SIRSteiner.de>
+    Klaus Ethgen <Klaus@Ethgen.de>
+        for German translation
 
-     Matej Erman <matej.erman@guest.arnes.si>
-         for Slovene translation
+    Matej Erman <matej.erman@guest.arnes.si>
+        for Slovene translation
 
-     MÃtyÃs Tibor <templar@tempi.scene.hu>
-     Koblinger Egmont <egmont@uhulinux.hu>
-     Sári Gábor <saga@externet.hu>
-         for Hungarian translation
+    MÃtyÃs Tibor <templar@tempi.scene.hu>
+    Koblinger Egmont <egmont@uhulinux.hu>
+    Sári Gábor <saga@externet.hu>
+        for Hungarian translation
 
-     Wu Yulun <migr@operamail.com>
-     Charles Wang <charlesw1234cn@yahoo.com.cn>
-         for simplified Chinese translation
+    Wu Yulun <migr@operamail.com>
+    Charles Wang <charlesw1234cn@yahoo.com.cn>
+        for simplified Chinese translation
 
-     H.J.Visser <H.J.Visser@harrie.mine.nu>
-     Tino Meinen <a.t.meinen@chello.nl>
-         for Dutch translation
+    H.J.Visser <H.J.Visser@harrie.mine.nu>
+    Tino Meinen <a.t.meinen@chello.nl>
+        for Dutch translation
 
-     Lauri Nurmi <lanurmi@iki.fi>
-         for Finnish translation
+    Lauri Nurmi <lanurmi@iki.fi>
+        for Finnish translation
 
-     Ilmar Kerm <ikerm@hot.ee>
-         for Estonian translation
+    Ilmar Kerm <ikerm@hot.ee>
+        for Estonian translation
 
-     Volodymyr M. Lisivka <lvm@mystery.lviv.net>
-         for Ukrainian translation
+    Volodymyr M. Lisivka <lvm@mystery.lviv.net>
+        for Ukrainian translation
 
-     Birger Langkjer <birger.langkjer@image.dk>
-         for Danish translation
+    Birger Langkjer <birger.langkjer@image.dk>
+        for Danish translation
 
-     Torgeir Ness Sundli <torgeir@mp3bil.no>
-         for Norwegian translation
+    Torgeir Ness Sundli <torgeir@mp3bil.no>
+        for Norwegian translation
 
-     Jan Raska <jan.raska@tiscali.cz>
-     Michal Bukovjan <bukm@centrum.cz>
-         for Czech translation
+    Jan Raska <jan.raska@tiscali.cz>
+    Michal Bukovjan <bukm@centrum.cz>
+        for Czech translation
 
-     Phanumas Thanyaboon <maskung@hotmail.com>
-         for Thai translation
+    Phanumas Thanyaboon <maskung@hotmail.com>
+        for Thai translation
 
-     Harald Ersch <hersch@romatsa.ro>
-         for Romanian translation
+    Harald Ersch <hersch@romatsa.ro>
+        for Romanian translation
 
-     pclouds <pclouds@vnlinux.org>
-         for Vietnamese translation
+    pclouds <pclouds@vnlinux.org>
+        for Vietnamese translation
 
-     Tedi Heriyanto <tedi_h@gmx.net>
-         for Indonesian translation
+    Tedi Heriyanto <tedi_h@gmx.net>
+        for Indonesian translation
 
-     Vladimir Petrov <vladux@mail.bg>
-         for Bulgarian translation
+    Vladimir Petrov <vladux@mail.bg>
+        for Bulgarian translation
 
-     Hans Öfverbeck <hans.ofverbeck@home.se>
-         for Swedish translation
+    Hans Öfverbeck <hans.ofverbeck@home.se>
+        for Swedish translation
 
-     Youssef Assad <youssef@devcabal.org>
-         for Arabic translation
+    Youssef Assad <youssef@devcabal.org>
+        for Arabic translation
 
-     catux.org <mecatxis@ya.com>
-         for Catalan translation
+    catux.org <mecatxis@ya.com>
+        for Catalan translation
 
-     Hyun-Jin Moon <moonhyunjin@gmail.com
-         for Korean translation
+    Hyun-Jin Moon <moonhyunjin@gmail.com
+        for Korean translation
 
-     Pavel Piatruk <berserker@neolocation.com>
-         for Belarusian translation
+    Pavel Piatruk <berserker@neolocation.com>
+        for Belarusian translation
 
-     Piarres Beobide <pi@beobide.net>
-         for Basque translation
+    Piarres Beobide <pi@beobide.net>
+        for Basque translation
 
-     Antonio C. Codazzi <f_sophia@libero.it>
-         for Esperanto translation
+    Antonio C. Codazzi <f_sophia@libero.it>
+        for Esperanto translation
 
-     Nikos Papadopoulos
-        for Greek translation
+    Nikos Papadopoulos
+        for Greek translation
 
- Fixes, additions, and patches:
+### Fixes, additions, and patches:
 
-     Eric Swalens
-     Quy Tonthat
-         for initial Exif support.
+    Eric Swalens
+    Quy Tonthat
+        for initial Exif support.
 
-     Daniel M. German <dmgerman at uvic.ca>
-         for Canon raw image support.
+    Daniel M. German <dmgerman at uvic.ca>
+        for Canon raw image support.
 
-     Lars Ellenberg
-         for Fujifilm raw image support.
+    Lars Ellenberg
+        for Fujifilm raw image support.
 
-     Diederen Damien <D.Diederen@student.ulg.ac.be>
-         for .xvpics thumbnail reading support.
+    Diederen Damien <D.Diederen@student.ulg.ac.be>
+        for .xvpics thumbnail reading support.
 
-     Nick Rusnov <nick@grawk.net>
-     Ryan Murray <rmurray@debian.org>
-         for man page.
+    Nick Rusnov <nick@grawk.net>
+    Ryan Murray <rmurray@debian.org>
+        for man page.
 
-     Martin Pool <mbp@samba.org>
-         for sort by number, misc. improvements.
+    Martin Pool <mbp@samba.org>
+        for sort by number, misc. improvements.
 
-     Jörg Mensmann <joerg.mensmann@gmx.de>
-         for Xinerama support patch.
+    Jörg Mensmann <joerg.mensmann@gmx.de>
+        for Xinerama support patch.
 
-     Vadim Shliakhov
-         for miscellaneous fixes.
+    Vadim Shliakhov
+        for miscellaneous fixes.
 
-     Uwe Ohse
-         for Exif enhancements, histogram and other patches
+    Uwe Ohse
+        for Exif enhancements, histogram and other patches
 
-     Timo Horstschäfer
-         for customizable fullscreen overlay
+    Timo Horstschäfer
+        for customizable fullscreen overlay
 
-     Michael Mokeev
-         for print related enhancements
+    Michael Mokeev
+        for print related enhancements
 
-     Carles Pina i Estany
-         for copy path to clipboard patch
+    Carles Pina i Estany
+        for copy path to clipboard patch
 
-     Kjell Morgenstern
-         for random slide show speedup patch
+    Kjell Morgenstern
+        for random slide show speedup patch
 
-     And...
-         Thanks to the users for finding Geeqie useful.
+    And...
+        Thanks to the users for finding Geeqie useful.
index f9d0e79..9ab7cab 100644 (file)
@@ -25,13 +25,16 @@ AC_CONFIG_SRCDIR([src/main.c])
 
 AC_CONFIG_AUX_DIR(auxdir)
 # Require Automake 1.14 for %reldir% support
-AM_INIT_AUTOMAKE([1.14.1 subdir-objects])
+AM_INIT_AUTOMAKE([1.14.1 subdir-objects foreign])
 
 AC_CONFIG_HEADER([config.h])
 
 # Only for developers
 AM_MAINTAINER_MODE
 
+# Silent build for automake >= 1.11
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
 AC_ARG_ENABLE(developer, [
 Development options:
 AC_HELP_STRING([--enable-developer], [turn on developers mode [default=no]])],
index e8a30a2..fc3b1a2 100644 (file)
     </menuchoice>\r
     . A dialog will appear requesting the location of the collection file. A window will the open containing the selected collection. Recently opened or saved collections can also be opened by selecting the collection's file name from the Open recent sub menu of the file menu.\r
   </para>\r
+  <para>\r
+    To delete or rename a collection, select\r
+    <menuchoice>\r
+      <shortcut>\r
+        <keycombo>\r
+          <keycap>O</keycap>\r
+        </keycombo>\r
+      </shortcut>\r
+      <guimenu>File</guimenu>\r
+      <guimenuitem>Open collection</guimenuitem>\r
+    </menuchoice>\r
+    , right-click on the collection you wish to delete or rename, and then select\r
+    <emphasis role="strong">Delete</emphasis>\r
+    or\r
+    <emphasis role="strong">Rename</emphasis>\r
+    .\r
+  </para>\r
   <para>The collection window consists of a pane to display the contents of the collection, and a status bar along the bottom of the window.</para>\r
   <para />\r
   <section id="Iconpane">\r
     </note>\r
     <para />\r
   </section>\r
+  <section id="AppendImages">\r
+    <title>Append Images</title>\r
+    <para>Right-click menu items permit images to be appended from either another collection or from the currently selected folder.</para>\r
+    <para />\r
+  </section>\r
+  <section id="RandomizedSort">\r
+    <title>Randomized Sort</title>\r
+    <para>\r
+      Right-click menu item\r
+      <emphasis role="strong">Sort</emphasis>\r
+      , in addition to the standard sort methods, permits images to be randomly arranged.\r
+    </para>\r
+    <para />\r
+  </section>\r
 </section>\r
index 5d23f90..7d71e83 100644 (file)
       <programlisting>X-Geeqie-Filter=true</programlisting>\r
     </para>\r
     <para>\r
-      When the external editor is executed, you are presented with a dialogue which enables a path to be selected. The path selected, appended by the filename currently being processed, is made available to the editor script either as an environment variable or via a call to geeqie --remote. The following code demonstrates the use of both methods:\r
+      Desktop files containing this entry will be displayed in the Folder action list of the\r
+      <link linkend="CopyMoveandLink" endterm="titleGuideSidebarsSortManager" />\r
+      . The path of the bookmark clicked will be used by the desktop file.\r
+      <para />\r
+      If the desktop file is called from the menu, when the external editor is executed you are presented with a dialogue which enables a path to be selected. The path selected, appended by the filename currently being processed, is made available to the editor script either as an environment variable or via a call to geeqie --remote. The following code demonstrates the use of both methods:\r
       <programlisting xml:space="preserve">\r
         for file in "$@"\r
         do\r
index b963f33..29606d0 100644 (file)
     <title>Ignore Rotation</title>\r
     <para>When checked, the rotational orientation of images will be ignored.</para>\r
   </section>\r
+  <section id="Sort">\r
+    <title>Sort</title>\r
+    <para>\r
+      The normal sort order is for groups (in the case of Similarity checks) with the highest number of near-100% matches to be at the top of the list.\r
+      <para />\r
+      If this box is checked, groups with the lowest number of matches are placed at the top of the list.\r
+    </para>\r
+  </section>\r
   <section id="Comparetwofilesets">\r
     <title>Compare two file sets</title>\r
     <para>Sometimes it is useful to compare one group of files to another, different group of files. Enable this check box to compare two groups of files. When enabled, a second list will appear and files can be added to this list using the same methods for the main list.</para>\r
     </para>\r
     <para />\r
   </section>\r
-</section>
+</section>\r
index ed5a89e..f1719ea 100644 (file)
               </guiicon>\r
             </guibutton>\r
           </code>\r
-          button displays a pop up calendar to enter the date\r
+          button displays a pop up calendar to enter the date.\r
+          <para />\r
+          The\r
+          <emphasis role="strong">Exif date</emphasis>\r
+          checkbox permits searches to be made on the exif date of images. If an image does not have an exif date, it will default to 01 January 1970.\r
         </listitem>\r
       </varlistentry>\r
       <varlistentry>\r
         </term>\r
         <listitem>The search will match if the file's associated keywords match all, match any, or exclude the entered keywords, depending on the method selected from the drop down menu. Keywords can be separated with a space, comma, or tab character.</listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <guilabel>Geocoded position</guilabel>\r
+        </term>\r
+        <listitem>\r
+          The search will match if the file's GPS position is less than or greater than the selected distance from the specified position, or is not geocoded, depending on the method selected from the drop down menu.\r
+          The search location can be specified by\r
+          <itemizedlist>\r
+            <listitem>\r
+              Type in a latitude/longitude in the format\r
+              <code>89.123 179.123</code>\r
+            </listitem>\r
+            <listitem>Drag-and-drop a geocoded image onto the search box</listitem>\r
+            <listitem>If Geeqie's map is displayed, a left-click on the map will store the latitude/longitude under the mouse cursor into the clipboard. It can then be pasted into the search box.</listitem>\r
+            <listitem>Copy-and-paste (in some circumstances drag-and-drop) the result of an Internet search.</listitem>\r
+          </itemizedlist>\r
+          <note>\r
+            In this last case, the result of a search may contain the latitude/longitude embedded in the URL. This may be automatically decoded with the help of an external file:-\r
+            <programlisting xml:space="preserve">~/.config/geeqie/geocode-parameters.awk</programlisting>\r
+            See\r
+            <link linkend="GuideReferenceDecodeLatLong">Decoding Latitude and Longitude</link>\r
+            for details on how to create this file.\r
+          </note>\r
+        </listitem>\r
+      </varlistentry>\r
     </variablelist>\r
     <para />\r
     <para />\r
     <para>Drag and drop can be initiated with the primary or middle mouse buttons. Dragging a file that is selected will include all selected files in the drag. Dragging a file that is not selected will first change the selection to the dragged file, and clear the previous selection.</para>\r
     <para />\r
   </section>\r
-</section>
+</section>\r
index 6d24cea..c91526d 100644 (file)
@@ -48,7 +48,6 @@
     </para>\r
     <para />\r
   </section>\r
-\r
   <section id="Imagepanningandmanipulation">\r
     <title>Image panning and manipulation</title>\r
     <para>When the image size is larger than the image pane, the visible region can be changed with the keyboard or mouse. The keyboard can also be used to adjust the orientation of the image.</para>\r
         <para>To start the smooth scroll function hold down the Shift key and click the primary mouse button. An icon will appear at the location of the mouse pointer, the mouse can then be positioned to pan the image. The image will pan in the direction of the mouse pointer relative to the icon. The pan speed will increase in proportion to the distance that the pointer is from the center of the icon. To end the smooth scroll function click the primary mouse button within the image pane.</para>\r
         <para />\r
       </section>\r
+      <section id="MouseWheelMode">\r
+        <title>Mouse Wheel Mode</title>\r
+        <para>A single click on the mouse middle button will toggle the function of the mouse wheel between "change image" and "change zoom".</para>\r
+        <note>\r
+          <para>\r
+            This option cannot be used if either the Preference\r
+            <emphasis>Mouse wheel scrolls image</emphasis>\r
+            or\r
+            <emphasis>Navigation by left or middle click on image</emphasis>\r
+            are selected.\r
+          </para>\r
+        </note>\r
+        <para>In Split View mode, each window can be clicked individually so that, in Quad View for example, Connected Zoom can be on 2, 3 or all 4 windows.</para>\r
+      </section>\r
     </section>\r
   </section>\r
   <section id="Navigation">\r
index fd4df7b..a0ee585 100644 (file)
           </warning>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <shortcut>\r
+              <keycombo>\r
+                <keycap>[</keycap>\r
+              </keycombo>\r
+            </shortcut>\r
+            <guimenu>Orientation</guimenu>\r
+            <guimenuitem>Rotate counterclockwise</guimenuitem>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>Rotates the current image counterclockwise 90 degrees, does not modify the file on disk.</para>\r
+        </listitem>\r
+      </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <shortcut>\r
+              <keycombo>\r
+                <keycap>Shift</keycap>\r
+                <keycap>R</keycap>\r
+              </keycombo>\r
+            </shortcut>\r
+            <guimenu>Orientation</guimenu>\r
+            <guimenuitem>Rotate 180</guimenuitem>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>Rotates the current image 180 degrees, does not modify the file on disk.</para>\r
+        </listitem>\r
+      </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <guimenu>Orientation</guimenu>\r
+            <guimenuitem>Write orientation to file</guimenuitem>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>\r
+            Saves the current image orientation to the disk file. This operation can only be carried out on jpeg, tiff and png files. Jpeg rotations are lossless.\r
+            <note>\r
+              The Preferences/Metadata option "Write altered image orientation to the metadata" must be off.\r
+              <para />\r
+              Exiftran and mogrify must be installed.\r
+            </note>\r
+          </para>\r
+        </listitem>\r
+      </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <guimenu>Orientation</guimenu>\r
+            <guimenuitem>Write orientation to file (preserve timestamp)</guimenuitem>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>As above, but the file date and time are preserved.</para>\r
+        </listitem>\r
+      </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <guimenu>Rating</guimenu>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>Set a Rating value for each image.</para>\r
+        </listitem>\r
+      </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <shortcut>\r
+              <keycombo>\r
+                <keycap>Alt+Keypad+n</keycap>\r
+              </keycombo>\r
+            </shortcut>\r
+            <guimenu>Rating</guimenu>\r
+            <guimenuitem>n</guimenuitem>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>"n" is in the range 0 to 5. Sets the Rating value for the image.</para>\r
+        </listitem>\r
+      </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <menuchoice>\r
+            <shortcut>\r
+              <keycombo>\r
+                <keycap>Alt+Keypad+Minus</keycap>\r
+              </keycombo>\r
+            </shortcut>\r
+            <guimenu>Rating</guimenu>\r
+            <guimenuitem>-1</guimenuitem>\r
+          </menuchoice>\r
+        </term>\r
+        <listitem>\r
+          <para>Sets the Rating value to -1 for the image.</para>\r
+        </listitem>\r
+      </varlistentry>\r
       <varlistentry>\r
         <term>\r
           <menuchoice>\r
           <para>Toggles display of the Sort Manager sidebar.</para>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <guimenu>Hide Bars</guimenu>\r
+        </term>\r
+        <listitem>\r
+          <para>This item is intended to be used by its shortcut key - by default this is the backtick key. When pressed, the info sidebar, sort manager and file list will be hidden (if displayed). Pressing backtick again will revert the display to its previous layout.</para>\r
+        </listitem>\r
+      </varlistentry>\r
       <varlistentry>\r
         <term>\r
           <guimenu>Toggle slideshow</guimenu>\r
index 255fc60..d749b37 100644 (file)
     <para>This section displays the dimensions (width x height) and file byte size of the image that is active in the image pane. When the format of the file in the image pane can not be determined the dimensions will show as “(0 x 0)”, in addition “(no read permision)” may appear if the file permissions do not allow reading the contents of the file.</para>\r
     <para />\r
   </section>\r
-  <section id="Zoom">\r
-    <title>Zoom</title>\r
-    <para>This section displays the current zoom ratio. A ratio of 1:1 is the image's original size. When the left number is larger the image is displayed larger than original size, when the right number is larger the image is displayed smaller.</para>\r
-    <para>A tilde (~) appears within the ratio display when the zoom is set to fit the image within the display area. In this zoom mode the ratio is automatically adjusted, and the displayed ratio may not be the actual ratio because the status bar display rounds the actual value to the nearest tenth (0.1).</para>\r
-    <para />\r
-  </section>\r
   <section id="Buttons">\r
     <title>Buttons</title>\r
     <para>Statusbar buttons corresponds to selected menu action.</para>\r
     <para />\r
+    <section id="Zoom">\r
+      <title>Zoom</title>\r
+      <para>The button label displays the current zoom ratio. A ratio of 1:1 is the image's original size. When the left number is larger the image is displayed larger than original size, when the right number is larger the image is displayed smaller.</para>\r
+      <para>A tilde (~) appears within the ratio display when the zoom is set to fit the image within the display area. In this zoom mode the ratio is automatically adjusted, and the displayed ratio may not be the actual ratio because the status bar display rounds the actual value to the nearest tenth (0.1).</para>\r
+      <para />\r
+      <para>Clicking this button permits control of the behavior of the zoom and scroll settings used when changing the displayed image.</para>\r
+      <variablelist>\r
+        <varlistentry>\r
+          <term>\r
+            <guilabel>Zoom to original size</guilabel>\r
+          </term>\r
+          <listitem>\r
+            <para>The new image is set to its original size.</para>\r
+          </listitem>\r
+        </varlistentry>\r
+        <varlistentry>\r
+          <term>\r
+            <guilabel>Fit image to window</guilabel>\r
+          </term>\r
+          <listitem>\r
+            <para>The new image's zoom is changed so that the image will fit within the current view area.</para>\r
+          </listitem>\r
+        </varlistentry>\r
+        <varlistentry>\r
+          <term>\r
+            <guilabel>Leave zoom at previous setting</guilabel>\r
+          </term>\r
+          <listitem>\r
+            <para>The zoom setting is unchanged, the new image will be scaled the same as the previous image.</para>\r
+          </listitem>\r
+        </varlistentry>\r
+      </variablelist>\r
+      <variablelist>\r
+        <varlistentry>\r
+          <term>\r
+            <guilabel>Scroll to top left corner</guilabel>\r
+          </term>\r
+          <listitem>\r
+            <para>The new image is displayed from top left corner.</para>\r
+          </listitem>\r
+        </varlistentry>\r
+        <varlistentry>\r
+          <term>\r
+            <guilabel>Scroll to image center</guilabel>\r
+          </term>\r
+          <listitem>\r
+            <para>The new image is centered</para>\r
+          </listitem>\r
+        </varlistentry>\r
+        <varlistentry>\r
+          <term>\r
+            <guilabel>Keep the region from previous iamge</guilabel>\r
+          </term>\r
+          <listitem>\r
+            <para>The new image is positioned as the previous one, whenever possible.</para>\r
+          </listitem>\r
+        </varlistentry>\r
+      </variablelist>\r
+    </section>\r
     <section id="ExifRotate">\r
       <title>Exif rotate</title>\r
       <para>\r
         <guiicon>\r
           <inlinegraphic fileref="gtk-color-picker.png" />\r
         </guiicon>\r
-        Toggles the auto-rotation of images if they have Exif orientation data. \r
+        Toggles the auto-rotation of images if they have Exif orientation data.\r
         Refer also to the\r
         <link linkend="HowdotheExifauto-rotationcommandswork">FAQ</link>\r
         .\r
index 36ad068..ec414d9 100644 (file)
           <para>When renaming a single file, this will allow the rename entry to appear directly over the original filename.</para>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <guilabel>List directory view uses single click to enter</guilabel>\r
+        </term>\r
+        <listitem>\r
+          <para>If selected, a single click will enter a directory, rather than the GTK+ default of a double click.</para>\r
+        </listitem>\r
+      </varlistentry>\r
       <varlistentry>\r
         <term>\r
           <guilabel>Recent folder list maximum size</guilabel>\r
           <para>This will cause image panning speed to gradually accelerate when holding down the keyboard arrows.</para>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <guilabel>Keyboard scrolling step multiplier:</guilabel>\r
+        </term>\r
+        <listitem>\r
+          <para>Defines the amount to pan an image when using the keyboard arrows.</para>\r
+        </listitem>\r
+      </varlistentry>\r
       <varlistentry>\r
         <term>\r
           <guilabel>Mouse wheel scrolls image</guilabel>\r
           </para>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <guilabel>Navigation by left or middle click on image</guilabel>\r
+        </term>\r
+        <listitem>\r
+          <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
     </variablelist>\r
   </section>\r
   <section id="Debugging">\r
           <para>Displayed when compiled with the option --enable-debug-log. This defines the verbosity of debug info sent to console and log window (0 disables the debug output).</para>\r
         </listitem>\r
       </varlistentry>\r
+      <varlistentry>\r
+        <term>\r
+          <guilabel>Log Window max. lines</guilabel>\r
+        </term>\r
+        <listitem>\r
+          <para>The maximum number of data lines to be displayed. The window will show the most recent data.</para>\r
+        </listitem>\r
+      </varlistentry>\r
     </variablelist>\r
   </section>\r
 </section>\r
index 92d4b68..a24dc44 100644 (file)
@@ -68,7 +68,8 @@
         <listitem>\r
           <para>\r
             Enable this to save thumbnails to disk, subsequent requests for a thumbnail will be faster. Thumbnails are cached into:\r
-            <programlisting>$HOME/.cache/.geeqie/thumbnails</programlisting>\r
+            <programlisting>$XDG_CACHE_HOME/geeqie/thumbnails/</programlisting>\r
+            <programlisting>($~/.cache/geeqie/thumbnails/)</programlisting>\r
             Refer to\r
             <link linkend="GuideReferenceThumbnails">Thumbnails Reference</link>\r
             for additional details.\r
       </varlistentry>\r
     </variablelist>\r
   </section>\r
+  <section id="InfoSidebar">\r
+    <title>Info Sidebar component heights</title>\r
+    <para>\r
+      The heights of the following components can be set individually:\r
+      <itemizedlist>\r
+        <listitem>Keywords</listitem>\r
+        <listitem>Title</listitem>\r
+        <listitem>Comments</listitem>\r
+      </itemizedlist>\r
+    </para>\r
+    <note>\r
+      <para>Geeqie must be restarted for changes to take effect.</para>\r
+    </note>\r
+    <variablelist />\r
+  </section>\r
 </section>\r
index 076b8f2..7cac9ae 100644 (file)
@@ -79,7 +79,7 @@
           <para>\r
             Enable this to allow Geeqie to increase the image size for images that are smaller than the current view area when the zoom is set to\r
             <emphasis>Fit image to window</emphasis>\r
-            .\r
+            . The value in the adjoining spin box sets the maximum size permitted in percent i.e. 100% is full-size.\r
           </para>\r
         </listitem>\r
       </varlistentry>\r
       </varlistentry>\r
     </variablelist>\r
   </section>\r
-  <section id="Whennewimageisselected">\r
-    <title>When new image is selected</title>\r
-    <para>This controls the behavior of the zoom and scroll settings when changing the displayed image.</para>\r
-    <variablelist>\r
-      <varlistentry>\r
-        <term>\r
-          <guilabel>Zoom to original size</guilabel>\r
-        </term>\r
-        <listitem>\r
-          <para>The new image is set to it's original size.</para>\r
-        </listitem>\r
-      </varlistentry>\r
-      <varlistentry>\r
-        <term>\r
-          <guilabel>Fit image to window</guilabel>\r
-        </term>\r
-        <listitem>\r
-          <para>The new image's zoom is changed so that the image will fit within the current view area.</para>\r
-        </listitem>\r
-      </varlistentry>\r
-      <varlistentry>\r
-        <term>\r
-          <guilabel>Leave zoom at previous setting</guilabel>\r
-        </term>\r
-        <listitem>\r
-          <para>The zoom setting is unchanged, the new image will be scaled the same as the previous image.</para>\r
-        </listitem>\r
-      </varlistentry>\r
-    </variablelist>\r
-    <variablelist>\r
-      <varlistentry>\r
-        <term>\r
-          <guilabel>Scroll to top left corner</guilabel>\r
-        </term>\r
-        <listitem>\r
-          <para>The new image is displayed from top left corner.</para>\r
-        </listitem>\r
-      </varlistentry>\r
-      <varlistentry>\r
-        <term>\r
-          <guilabel>Scroll to image center</guilabel>\r
-        </term>\r
-        <listitem>\r
-          <para>The new image is centered</para>\r
-        </listitem>\r
-      </varlistentry>\r
-      <varlistentry>\r
-        <term>\r
-          <guilabel>Keep the region from previous iamge</guilabel>\r
-        </term>\r
-        <listitem>\r
-          <para>The new image is positioned as the previous one, whenever possible.</para>\r
-        </listitem>\r
-      </varlistentry>\r
-    </variablelist>\r
-  </section>\r
   <section id="Appearance">\r
     <title>Appearance</title>\r
     <variablelist>\r
         </listitem>\r
       </varlistentry>\r
     </variablelist>\r
+    <variablelist>\r
+      <varlistentry>\r
+        <term>\r
+          <guilabel>Alpha channel color 1/2</guilabel>\r
+        </term>\r
+        <listitem>\r
+          <para>These two colors define the checkerboard background used when images with an alpha channel are displayed.</para>\r
+        </listitem>\r
+      </varlistentry>\r
+    </variablelist>\r
   </section>\r
   <section id="Convenience">\r
     <title>Convenience</title>\r
     <variablelist>\r
-       <varlistentry>\r
+      <varlistentry>\r
         <term>\r
           <guilabel>Auto rotate proofs using EXIF information</guilabel>\r
         </term>\r
index 1fe1156..c56c416 100644 (file)
       </term>\r
       <listitem>\r
         <para>This may be set for each instance of Geeqie.</para>\r
+        <itemizedlist spacing="compact">\r
+          <listitem>\r
+            <para>No change: Use the Current Working Directory. If called from a system menu rather than the command line, the Home directory will probably be used.</para>\r
+          </listitem>\r
+          <listitem>\r
+            <para>Restore last path: Use the folder in use when Geeqie was previously closed.</para>\r
+          </listitem>\r
+          <listitem>\r
+            <para>Home path: Use the folder specified in the Home Path box.</para>\r
+          </listitem>\r
+        </itemizedlist>\r
       </listitem>\r
     </varlistentry>\r
     <varlistentry>\r
index 2a315a6..5844eaa 100644 (file)
           If checked, open a confirmation dialogue before writing to the file\r
         </para>\r
       </listitem>\r
+      <listitem>\r
+        <para>\r
+          <guilabel>Create sidecar files named image.ext.xmp (as opposed to image.xmp)</guilabel>\r
+          <para />\r
+          If checked, new sidecar files will use image.ext.xmp naming scheme\r
+        </para>\r
+      </listitem>\r
     </itemizedlist>\r
   </section>\r
   <section id="WriteToGeeqiePrivateFiles">\r
index 15327fd..cfcf70f 100644 (file)
         </listitem>\r
       </varlistentry>\r
     </variablelist>\r
+    <variablelist>\r
+      <varlistentry>\r
+        <term>\r
+          <guilabel>Use saved window positions also for new windows</guilabel>\r
+        </term>\r
+        <listitem>\r
+          <para>\r
+            A new window opened by menu\r
+            <menuchoice>\r
+              <shortcut>\r
+                <keycombo>\r
+                  <keycap>Ctrl</keycap>\r
+                  <keycap>N</keycap>\r
+                </keycombo>\r
+              </shortcut>\r
+              <guimenu>File</guimenu>\r
+              <guimenuitem>New Window</guimenuitem>\r
+            </menuchoice>\r
+            will use the saved window positions also.\r
+          </para>\r
+        </listitem>\r
+      </varlistentry>\r
+    </variablelist>\r
     <variablelist>\r
       <varlistentry>\r
         <term>\r
           </row>\r
           <row>\r
             <entry>comment</entry>\r
-            <entry>Image comment from metadata</entry>\r
+            <entry>Image comment from XMP metadata</entry>\r
+          </row>\r
+          <row>\r
+            <entry>imagecomment</entry>\r
+            <entry>JPEG image comment</entry>\r
           </row>\r
           <row>\r
             <entry>&lt;meta_tag&gt;</entry>\r
index e12d928..1ef6fbf 100644 (file)
@@ -9,6 +9,8 @@
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceConfig.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceLIRC.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceStandards.xml" />\r
+  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceTags.xml" />\r
   <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceSupportedFormats.xml" />\r
+  <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="GuideReferenceDecodeLatLong.xml" />\r
   <para />\r
-</chapter>
+</chapter>\r
index da17bef..0076433 100644 (file)
@@ -9,7 +9,7 @@
   <table frame="all">\r
     <tgroup cols="3" rowsep="1" colsep="1">\r
       <thead rowsep="1" colsep="1">\r
-        <row >\r
+        <row>\r
           <entry>Short Option</entry>\r
           <entry>Long Option</entry>\r
           <entry>Description</entry>\r
           <entry>--debug[=&lt;level&gt;]</entry>\r
           <entry>Turn on debugging output (when compiled with Debug enabled). &lt;level&gt; is 0 to 4.</entry>\r
         </row>\r
+        <row>\r
+          <entry>-g:&lt;regexp&gt;</entry>\r
+          <entry>--grep:&lt;regexp&gt;</entry>\r
+          <entry>Filter debug output with regular expression</entry>\r
+        </row>\r
+        <row>\r
+          <entry>+w</entry>\r
+          <entry>--show-log-window</entry>\r
+          <entry>Display log window</entry>\r
+        </row>\r
+        <row>\r
+          <entry>-o:&lt;file&gt;</entry>\r
+          <entry>--log-file:&lt;file&gt;</entry>\r
+          <entry>Save log data to file</entry>\r
+        </row>\r
         <row>\r
           <entry />\r
           <entry>--alternate</entry>\r
   <section id="Remotecommands">\r
     <title>Remote commands</title>\r
     <para>The --remote command line option will send all entered commands to an existing Geeqie process, a new process will be started if one does not exist. These are the additional commands that can be used with the remote command:</para>\r
-  <table frame="all">
-    <tgroup cols="3" rowsep="1" colsep="1">
-      <thead rowsep="1" colsep="1">\r
-\r
+    <table frame="all">\r
+      <tgroup cols="3" rowsep="1" colsep="1">\r
+        <thead rowsep="1" colsep="1">\r
           <row>\r
             <entry>Short Option</entry>\r
             <entry>Long Option</entry>\r
             <entry>Description</entry>\r
-          </row></thead>\r
-        <tbody>          <row>\r
+          </row>\r
+        </thead>\r
+        <tbody>\r
+          <row>\r
             <entry>-n</entry>\r
             <entry>--next</entry>\r
             <entry>Change main window to display next image.</entry>\r
           <row>\r
             <entry />\r
             <entry>file:&lt;file&gt;</entry>\r
-            <entry>Change main window to display &lt;file&gt;</entry>\r
+            <entry>Open  &lt;file&gt; and bring Geeqie window to the top</entry>\r
+          </row>\r
+          <row>\r
+            <entry />\r
+            <entry>File:&lt;file&gt;</entry>\r
+            <entry>Open  &lt;file&gt; and do not bring Geeqie window to the top</entry>\r
           </row>\r
           <row>\r
             <entry />\r
             <entry>raise</entry>\r
             <entry>Bring the geeqie window to the top</entry>\r
           </row>\r
+          <row>\r
+            <entry>-ct:clear|clean</entry>\r
+            <entry>--cache-thumbs:clear|clean</entry>\r
+            <entry>clear or clean thumbnail cache</entry>\r
+          </row>\r
+          <row>\r
+            <entry>-cs:clear|clean</entry>\r
+            <entry>--cache-shared:clear|clean</entry>\r
+            <entry>clear or clean shared thumbnail cache</entry>\r
+          </row>\r
+          <row>\r
+            <entry>-cm</entry>\r
+            <entry>--cache-metadata</entry>\r
+            <entry>clean the metadata cache</entry>\r
+          </row>\r
+          <row>\r
+            <entry>-cr:&lt;folder&gt;</entry>\r
+            <entry>--cache-render:&lt;folder&gt;</entry>\r
+            <entry>render thumbnails</entry>\r
+          </row>\r
+          <row>\r
+            <entry>-crr:&lt;folder&gt;</entry>\r
+            <entry>--cache-render-recurse:&lt;folder&gt;</entry>\r
+            <entry>render thumbnails recursively</entry>\r
+          </row>\r
+          <row>\r
+            <entry>-crs:&lt;folder&gt;</entry>\r
+            <entry>--cache-render-shared:&lt;folder&gt;</entry>\r
+            <entry>\r
+              render thumbnails\r
+              <footnote id='ref2'>\r
+                <para>If standard thumbnail cache is not enabled, this command will be ignored.</para>\r
+              </footnote>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>-crsr:&lt;folder&gt;</entry>\r
+            <entry>--cache-render-shared-recurse:&lt;folder&gt;</entry>\r
+            <entry>render thumbnails recursively</entry>\r
+          </row>\r
         </tbody>\r
       </tgroup>\r
     </table>\r
     <para />\r
   </section>\r
-</section>
+</section>\r
index 6be6984..d78fc16 100644 (file)
@@ -2,9 +2,14 @@
 <section id="GuideReferenceConfig">\r
   <title id="titleGuideReferenceConfig">Configuration Files and Locations</title>\r
   <para>The following data lists the locations Geeqie uses for various actions. The uppercase symbols are environment variables. If they are not set on your system the fallback locations are listed in parentheses.</para>\r
+  <para>\r
+    Geqqie will first attempt to load a configuration file from:\r
+    <programlisting xml:space="preserve">/etc/geeqie/geeqierc.xml</programlisting>\r
+  </para>\r
+  <para>It will then continue with the following locations.</para>\r
   <para>\r
     Most of Geeqie's configuration files are contained in the folder, and sub-folders of:\r
-    <programlisting xml:space="preserve">$HOME/$XDG_CONFIG_HOME/geeqie/</programlisting>\r
+    <programlisting xml:space="preserve">$XDG_CONFIG_HOME/geeqie/</programlisting>\r
     <programlisting xml:space="preserve">($~/.config/geeqie/)</programlisting>\r
   </para>\r
   <para>\r
@@ -36,8 +41,9 @@
     <programlisting xml:space="preserve">.../accels</programlisting>\r
   </para>\r
   <para>\r
-    The default location for Collections is in the folder:\r
-    <programlisting xml:space="preserve">$HOME/.local/share/geeqie/collections</programlisting>\r
+    The location for Collections is in the folder:\r
+    <programlisting xml:space="preserve">$XDG_DATA_HOME/geeqie/collections</programlisting>\r
+    <programlisting xml:space="preserve">($~/.local/share/geeqie/collections)</programlisting>\r
   </para>\r
   <para>\r
     The lirc\r
diff --git a/doc/docbook/GuideReferenceDecodeLatLong.xml b/doc/docbook/GuideReferenceDecodeLatLong.xml
new file mode 100644 (file)
index 0000000..22b4c6a
--- /dev/null
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<section id="GuideReferenceDecodeLatLong">\r
+  <title id="titleGuideReferenceDecodeLatLong">Decoding Latitude and Longitude</title>\r
+  <para>This section is relevent to geocode searches and the map display of Geeqie.</para>\r
+  <para>\r
+    The result of some internet or other searches for placenames can contain a latitude and longitude embedded in a text string. For example an openstreetmap.org search can give a URL such as:\r
+    <para />\r
+    <code>https://www.openstreetmap.org/search?query=51.5542%2C-0.1816#map=12/51.5542/-0.1818</code>\r
+  </para>\r
+  <para>\r
+    If you paste such a string into the search box, the latitude/longitude can be automatically extracted and used as the origin of the search.\r
+    <para />\r
+    You may also drag-and-drop a URL of this type onto the map to cause the map to be re-centered.\r
+    <para />\r
+    To enable this feature, create the file\r
+    <para />\r
+    <code>~/.config/geeqie/geocode-parameters.awk</code>\r
+    <para />\r
+    and copy the following text into it:\r
+  </para>\r
+  <para>\r
+    <programlisting xml:space="preserve">\r
+      # Store this file in:\r
+      # ~/.config/geeqie/geocode-parameters.awk\r
+      #\r
+      # This file is used to decode the results of internet or other searches\r
+      # to extract a geo-position from a text string. \r
+      # To include other searches, follow the examples below and\r
+      # ensure the returned value is either in the format:\r
+      # 89.123 179.123\r
+      # or\r
+      # Error: $0\r
+      #\r
+      \r
+      function check_parameters(latitude, longitude)\r
+      {\r
+      # Ensure the parameters are numbers    \r
+      if ((latitude == (latitude+0)) &amp;&amp; (longitude == (longitude+0)))\r
+      {\r
+      if (latitude &gt;= -90 &amp;&amp; latitude &lt;= 90 &amp;&amp;\r
+      longitude &gt;= -180 &amp;&amp; longitude &lt;= 180)\r
+      {\r
+      return latitude " " longitude\r
+      }\r
+      else\r
+      {\r
+      return "Error: " latitude " " longitude\r
+      }\r
+      }\r
+      else\r
+      {\r
+      return "Error: " latitude " " longitude\r
+      }\r
+      }\r
+      \r
+      # This awk file is accessed by an internal function.\r
+      # The call is of the format:\r
+      # echo "string_to_be_searched" | awk -f geocode-parameters.awk\r
+      #\r
+      # Search the input string for known formats.\r
+      {\r
+      if (index($0, "http://www.geonames.org/maps/google_"))\r
+      {\r
+      # This is a drag-and-drop or copy-paste from a geonames.org search\r
+      # in the format e.g.\r
+      # http://www.geonames.org/maps/google_51.513_-0.092.html\r
+      \r
+      gsub(/http:\/\/www.geonames.org\/maps\/google_/, "")\r
+      gsub(/.html/, "")\r
+      gsub(/_/, " ")\r
+      print check_parameters($1, $2)\r
+      }\r
+      \r
+      else if (index($0, "https://www.openstreetmap.org/search?query="))\r
+      {\r
+      # This is a copy-paste from an openstreetmap.org search\r
+      # in the format e.g.\r
+      # https://www.openstreetmap.org/search?query=51.4878%2C-0.1353#map=11/51.4880/-0.1356\r
+      \r
+      gsub(/https:\/\/www.openstreetmap.org\/search\?query=/, "")\r
+      gsub(/#map=.*/, "")\r
+      gsub(/%2C/, " ")\r
+      print check_parameters($1, $2)\r
+      }\r
+      \r
+      else if (index($0, "https://www.openstreetmap.org/#map="))\r
+      {\r
+      # This is a copy-paste from an openstreetmap.org search\r
+      # in the format e.g.\r
+      # https://www.openstreetmap.org/#map=5/18.271/16.084\r
+      \r
+      gsub(/https:\/\/www.openstreetmap.org\/#map=[^\/]*/,"")\r
+      gsub(/\//," ")\r
+      print check_parameters($1, $2)\r
+      }\r
+      \r
+      else if (index($0, "https://www.google.com/maps/"))\r
+      {\r
+      # This is a copy-paste from a google.com maps search\r
+      # in the format e.g.\r
+      # https://www.google.com/maps/place/London,+UK/@51.5283064,-0.3824815,10z/data=....\r
+      \r
+      gsub(/https:\/\/www.google.com\/maps.*@/,"")\r
+      sub(/,/," ")\r
+      gsub(/,.*/,"")\r
+      print check_parameters($1, $2)\r
+      }\r
+      \r
+      else if (index($0,".html"))\r
+      {\r
+      # This is an unknown html address\r
+      \r
+      print "Error: " $0\r
+      }\r
+      \r
+      else if (index($0,"http"))\r
+      {\r
+      # This is an unknown html address\r
+      \r
+      print "Error: " $0\r
+      }\r
+      \r
+      else if (index($0, ","))\r
+      {\r
+      # This is assumed to be a simple lat/long of the format:\r
+      # 89.123,179.123\r
+      \r
+      split($0, latlong, ",")\r
+      print check_parameters(latlong[1], latlong[2])\r
+      }\r
+      \r
+      else\r
+      {\r
+      # This is assumed to be a simple lat/long of the format:\r
+      # 89.123 179.123\r
+      \r
+      split($0, latlong, " ")\r
+      print check_parameters(latlong[1], latlong[2])\r
+      }\r
+      }\r
+\r
+    </programlisting>\r
+  </para>\r
+</section>\r
index d36c197..c6cd6c2 100644 (file)
           <row>\r
             <entry colsep="0">\r
               <emphasis role="strong">Image viewing</emphasis>\r
-              (image has keyboard focus)\r
+              (image has keyboard focus. Refer also to\r
+              <link linkend="MouseWheelMode">Mouse Wheel Mode</link>\r
+              )\r
             </entry>\r
             <entry colsep="0" />\r
             <entry colsep="0" />\r
             <entry />\r
             <entry>Toggle slideshow.</entry>\r
           </row>\r
+          <row>\r
+            <entry>\r
+              <keycap>E</keycap>\r
+            </entry>\r
+            <entry />\r
+            <entry>Split panes horizontal.</entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <keycap>U</keycap>\r
+            </entry>\r
+            <entry />\r
+            <entry>Split panes vertical.</entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <keycap>Y</keycap>\r
+            </entry>\r
+            <entry />\r
+            <entry>Single pane.</entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <code>Alt + Right Arrow</code>\r
+            </entry>\r
+            <entry />\r
+            <entry>Next split pane.</entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <code>Alt + Left Arrow</code>\r
+            </entry>\r
+            <entry />\r
+            <entry>Previous split pane.</entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <code>Alt + Up Arrow</code>\r
+            </entry>\r
+            <entry />\r
+            <entry>Up or previous split pane.</entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <code>Alt + Down Arrow</code>\r
+            </entry>\r
+            <entry />\r
+            <entry>Down or next split pane.</entry>\r
+          </row>\r
         </tbody>\r
       </tgroup>\r
     </table>\r
             <entry />\r
             <entry>Close window.</entry>\r
           </row>\r
+          <row>\r
+            <entry />\r
+            <entry>\r
+              <code>Ctrl + shift + right-click</code>\r
+            </entry>\r
+            <entry>Show debug info.</entry>\r
+          </row>\r
         </tbody>\r
       </tgroup>\r
     </table>\r
diff --git a/doc/docbook/GuideReferenceTags.xml b/doc/docbook/GuideReferenceTags.xml
new file mode 100644 (file)
index 0000000..c2b0167
--- /dev/null
@@ -0,0 +1,747 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<section id="GuideReferenceXmpExif">\r
+  <title id="titleGuideReferenceXmpExif">XMP, Exif and IPTC</title>\r
+  <para>The tables below show the XMP, Exif and IPTC tags used by Geeqie.</para>\r
+  <section id="preformatted">\r
+    <title>Geeqie pre-formatted tags</title>\r
+    <table frame="all">\r
+      <title>Pre-formatted tags</title>\r
+      <tgroup cols="3" rowsep="1" colsep="1">\r
+        <thead>\r
+          <row>\r
+            <entry>\r
+              <para>Geeqie Tag</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exif tags used</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Description</para>\r
+            </entry>\r
+          </row>\r
+        </thead>\r
+        <tbody>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.Camera</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.Image.Make\r
+                <para />\r
+                Exif.Image.Model\r
+                <para />\r
+                Exif.Image.Software\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Camera and software name</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.DateTime</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.Photo.DateTimeOriginal\r
+                <para />\r
+                Exif.Photo.SubSecTimeOriginal\r
+                <para />\r
+                Exif.Image.DateTime\r
+                <para />\r
+                Exif.Photo.SubSecTime\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Image Date</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.ShutterSpeed</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.Photo.ExposureTime\r
+                <para />\r
+                Exif.Photo.ShutterSpeedValue\r
+\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Shutter speed in seconds (e.g. 1/60s)</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.Aperture</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                <para>\r
+                  Exif.Photo.Fnumber\r
+                  <para />\r
+                  Exif.Photo.ApertureValue\r
+                </para>\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Aperture (e.g. f5.6)</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.ExposureBias</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exif.Photo.ExposureBiasValue</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exposure bias (e.g. +0.3)</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.ISOSpeedRating</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exif.Photo.ISOSpeedRatings</para>\r
+            </entry>\r
+            <entry>\r
+              <para>ISO sensitivity (e.g. 100)</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.FocalLength</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exif.Photo.FocalLength</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Focal length</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.FocalLength35mmFilm</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.Photo.FocalLengthIn35mmFilm\r
+                <para />\r
+                Exif.Photo.FocalLength\r
+                <para />\r
+                Exif.Photo.FocalPlaneXResolution\r
+                <para />\r
+                Exif.Photo.FocalPlaneYResolution\r
+                <para />\r
+                Exif.Photo.FocalPlaneResolutionUnit\r
+                <para />\r
+                Exif.Photo.PixelXDimension\r
+                <para />\r
+                Exif.Photo.PixelYDimension\r
+\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Focal length 35mm</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.SubjectDistance</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exif.Photo.SubjectDistance</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Subject distance</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.Flash</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exif.Photo.Flash</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Flash</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.Resolution</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.Image.Xresolution\r
+                <para />\r
+                Exif.Image.Yresolution\r
+                <para />\r
+                Exif.Image.ResolutionUnit\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Resolution</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.ColorProfile</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.Image.InterColorProfile\r
+                <para />\r
+                Exif.Photo.ColorSpace\r
+                <para />\r
+                Exif.Iop.InteroperabilityIndex\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Color profile</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.GPSAltitude</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.GPSInfo.GPSAltitudeRef\r
+                <para />\r
+                Exif.GPSInfo.GPSAltitude\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Altitude above sea level</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.GPSPosition</para>\r
+            </entry>\r
+            <entry>\r
+              <para>\r
+                Exif.GPSInfo.GPSLatitude\r
+                <para />\r
+                Exif.GPSInfo.GPSLatitudeRef\r
+                <para />\r
+                Exif.GPSInfo.GPSLongitude\r
+                <para />\r
+                Exif.GPSInfo.GPSLongitudeRef\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Latitude, Longitude</para>\r
+            </entry>\r
+          </row>\r
+        </tbody>\r
+      </tgroup>\r
+    </table>\r
+  </section>\r
+  <section id="infosidebar">\r
+    <title>Info sidebar panes</title>\r
+    <table frame="all">\r
+      <title>Fixed panes</title>\r
+      <tgroup cols="2" rowsep="1" colsep="1">\r
+        <thead>\r
+          <row>\r
+            <entry>\r
+              <para>Tag</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Pane title</para>\r
+            </entry>\r
+          </row>\r
+        </thead>\r
+        <tbody>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.description</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Comment</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.subject</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Keywords</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.title</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Title</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.xmp.Rating</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Rating</para>\r
+            </entry>\r
+          </row>\r
+        </tbody>\r
+      </tgroup>\r
+    </table>\r
+    <table frame="all">\r
+      <title>Location and GPS pane</title>\r
+      <tgroup cols="2" rowsep="1" colsep="1">\r
+        <thead>\r
+          <row>\r
+            <entry>\r
+              <para>Tag</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Field</para>\r
+            </entry>\r
+          </row>\r
+        </thead>\r
+        <tbody>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.GPSPosition</para>\r
+            </entry>\r
+            <entry>\r
+              <para>GPS position</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.GPSAltitude</para>\r
+            </entry>\r
+            <entry>\r
+              <para>GPS altitiude</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.Country</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Country</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>\r
+                <para>Xmp.iptc.CountryCode</para>\r
+              </para>\r
+            </entry>\r
+            <entry>\r
+              <para>Country Code</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.State</para>\r
+            </entry>\r
+            <entry>\r
+              <para>State</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.City</para>\r
+            </entry>\r
+            <entry>\r
+              <para>City</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.iptc.Location</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Location</para>\r
+            </entry>\r
+          </row>\r
+        </tbody>\r
+      </tgroup>\r
+    </table>\r
+    <table frame="all">\r
+      <title>Copyright pane</title>\r
+      <tgroup cols="2" rowsep="1" colsep="1">\r
+        <thead>\r
+          <row>\r
+            <entry>\r
+              <para>Tag</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Field</para>\r
+            </entry>\r
+          </row>\r
+        </thead>\r
+        <tbody>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.creator</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Creator</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.contributor</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Contributor</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.rights</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Rights</para>\r
+            </entry>\r
+          </row>\r
+        </tbody>\r
+      </tgroup>\r
+    </table>\r
+    <table frame="all">\r
+      <title>Exif pane</title>\r
+      <tgroup cols="2" rowsep="1" colsep="1">\r
+        <thead>\r
+          <row>\r
+            <entry>\r
+              <para>Tag</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Field</para>\r
+            </entry>\r
+          </row>\r
+        </thead>\r
+        <tbody>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.Camera</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Camera</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.DateTime</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Date</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.ShutterSpeed</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Shutter speed</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.Aperture</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Aperture</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.ExposureBias</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exposure bias</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.ISOSpeedRating</para>\r
+            </entry>\r
+            <entry>\r
+              <para>ISO sensitivity</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.FocalLength</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Focal length</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.FocalLength35mmFilm</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Focal length 35mm</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.SubjectDistance</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Subject distance</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.Flash</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Flash</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.Resolution</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Resolution</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>formatted.ColorProfile</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Color profile</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Exif.Photo.ExposureProgram</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exposure Program</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Exif.Photo.MeteringMode</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Metering Mode</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Exif.Photo.LightSource</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Light Source</para>\r
+            </entry>\r
+          </row>\r
+        </tbody>\r
+      </tgroup>\r
+    </table>\r
+  </section>\r
+  <section id="exifiptc">\r
+    <title>Exif IPTC alternative keys</title>\r
+    <table frame="all">\r
+      <title>Conversion table</title>\r
+      <tgroup cols="2" rowsep="1" colsep="1">\r
+        <thead>\r
+          <row>\r
+            <entry>\r
+              <para>Tag</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Field</para>\r
+            </entry>\r
+          </row>\r
+        </thead>\r
+        <tbody>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.tiff.Orientation</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Exif.Image.Orientation</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.title</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.ObjectName</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.Urgency</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Urgency</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.Category</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Category</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.SupplementalCategory</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.SuppCategory</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.subject</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Keywords</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.iptc.Location</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.LocationName</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.Instruction</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.SpecialInstructions</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.DateCreated</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.DateCreated</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.creator</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Byline</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.AuthorsPosition</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.BylineTitle</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.City</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.City</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.State</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.ProvinceState</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.iptc.CountryCode</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.CountryCode</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.Country</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.CountryName</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.TransmissionReference</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.TransmissionReference</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.Headline</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Headline</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.Credit</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Credit</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.Source</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Source</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.rights</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Copyright</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.dc.description</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Caption</para>\r
+            </entry>\r
+          </row>\r
+          <row>\r
+            <entry>\r
+              <para>Xmp.photoshop.CaptionWriter</para>\r
+            </entry>\r
+            <entry>\r
+              <para>Iptc.Application2.Writer</para>\r
+            </entry>\r
+          </row>\r
+        </tbody>\r
+      </tgroup>\r
+    </table>\r
+  </section>\r
+</section>\r
index 869d986..0853b98 100644 (file)
       <link linkend="GuideOptionsMetadata" endterm="titleGuideOptionsMetadata" />\r
       .\r
     </para>\r
+    <para>\r
+      If the right-click menu item\r
+      <guimenu>Revert</guimenu>\r
+      is executed at any time after a\r
+      <guimenu>Show all</guimenu>\r
+      or\r
+      <guimenu>Collapse all</guimenu>\r
+      , the keyword layout will be restored to the state prior to the\r
+      <guimenu>Show all</guimenu>\r
+      or\r
+      <guimenu>Collapse all</guimenu>\r
+      .\r
+      <para />\r
+      If the right-click menu item\r
+      <guimenu>Revert</guimenu>\r
+      is executed at any time before a\r
+      <guimenu>Show all</guimenu>\r
+      or\r
+      <guimenu>Collapse all</guimenu>\r
+      , the keyword layout will be restored to the state existing at start-up.\r
+    </para>\r
+    <note>\r
+      <para>\r
+        The selections in the sub-menu\r
+        <guimenu>On any change</guimenu>\r
+        will affect the operation of the\r
+        <guimenu>Revert</guimenu>\r
+        option.\r
+      </para>\r
+    </note>\r
     <para />\r
   </section>\r
   <section id="Listpanes-ExifFileinfoCopyrightLocationandGPS">\r
       <ulink url="https://www.openstreetmap.org">openstreetmap.org</ulink>\r
       . To use this facility, Geeqie must have been compiled with the --enable-map option.\r
     </para>\r
-    <para>Maps are useful when working with geocoded images. All geocoded images in the currently displayed folder will show as small icons on the map. Clicking the icon will expand the icon to show an image thumbnail, plus other pre-defined image data.</para>\r
-    <para>Right-click on the map will show other map options.</para>\r
+    <itemizedlist spacing="compact">\r
+      <listitem>\r
+        <para>\r
+          Image location display\r
+          <para />\r
+          All geocoded images in the currently displayed folder will show as small icons on the map. Clicking the icon will expand the icon to show an image thumbnail, plus other pre-defined image data.\r
+        </para>\r
+        <para>Right-click on the map will show other map options.</para>\r
+        <para>Middle-click controls the map-centering function.</para>\r
+      </listitem>\r
+      <listitem>\r
+        <para>\r
+          Geo-coded search\r
+          <para />\r
+          Left-click stores the latitude/longitude under the cursor into the clipboard. This may be used to define the origin of a\r
+          <link linkend="Searchparameters">geocode search</link>\r
+          .\r
+        </para>\r
+      </listitem>\r
+      <listitem>\r
+        <para>\r
+          Geo-coding Images\r
+          <para />\r
+          If an image is not geocoded, the filename or icon on the file pane can be dragged-and-dropped onto the map. The image latitude and longitude xmp meta-data will be updated to the drop position on the map.\r
+        </para>\r
+      </listitem>\r
+      <listitem>\r
+        <para>\r
+          Map Centering\r
+          <para />\r
+          If an internet URL containg a valid latitude and longitude is dropped on the map, the map will be re-centered on that location. The zoom level will not change. Refer to\r
+          <link linkend="GuideReferenceDecodeLatLong">Decoding Latitude and Longitude</link>\r
+          .\r
+        </para>\r
+      </listitem>\r
+    </itemizedlist>\r
   </section>\r
 </section>\r
index 6b8f3f5..c2e77da 100644 (file)
       <para />\r
     </section>\r
     <section id="CopyMoveandLink">\r
-      <title>Copy, Move, and Link</title>\r
-      <para>These are the available operations to use on the active image, they should be self explanatory.</para>\r
+      <title>Copy, Move, Link and Desktop actions</title>\r
+      <para>\r
+        Copy, Move and Symbolic Link are available operations to use on the active image; they should be self explanatory. In some cases, Desktop files are also listed. This is detailed in\r
+        <link linkend="Geeqieextensions" endterm="titleGuideEditorsConfig" />\r
+      </para>\r
       <para />\r
     </section>\r
     <section id="Addbutton">\r
index c79f47a..2393d2f 100644 (file)
@@ -81,7 +81,7 @@ make install DESTDIR=%{buildroot}
 %suse_update_desktop_file -n geeqie
 %endif
 
-%__install -m 644 AUTHORS COPYING ChangeLog NEWS README README.lirc "%{buildroot}/%{_docdir}/%{docname}/"
+%__install -m 644 AUTHORS COPYING ChangeLog NEWS README.md README.lirc "%{buildroot}/%{_docdir}/%{docname}/"
 
 %clean
 %__rm -rf "%{buildroot}"
index 1c1184e..ec82cc0 100755 (executable)
@@ -47,12 +47,83 @@ rotate()
     esac
 }
 
+rotate_image_file()
+{
+       ext=`echo "${3##*.}" |tr "[:upper:]" "[:lower:]"`
+       [ "x$ext" = "x" ] && return 4 #no extension
+
+       case "$ext" in
+       jpg|jpeg)
+               exiftran -i "$1" "$3"
+               return 0
+               ;;
+
+       tif|tiff|png)
+               mogrify $2 "$3"
+               return 0
+               ;;
+
+       *)      #not supported
+               return 4
+               ;;
+       esac
+}
+
 get_sidecars=
 if [ "x$1" = "x-g" ] ; then
     get_sidecars=yes
     shift
 fi
 
+rotate_image_file=
+rotation=
+if [ "x$1" = "x-r" ] ; then
+       rotate_image_file=yes
+       shift
+       rotation="$1"
+       shift
+fi
+
+preserve_mtime=
+if [ "x$1" = "x-t" ] ; then
+       preserve_mtime=yes
+       shift
+fi
+
+if [ -n "$rotation" ] ; then
+       if [ "x$rotation" = "x0" ] ; then
+               exit 0
+       fi
+       if [ "x$rotation" = "x2" ] ; then
+               mogrify_param="-flop"
+               exiftran_param="-F"
+       fi
+       if [ "x$rotation" = "x3" ] ; then
+               mogrify_param="-rotate 180"
+               exiftran_param="-1"
+       fi
+       if [ "x$rotation" = "x4" ] ; then
+               mogrify_param="-flip"
+               exiftran_param="-f"
+       fi
+       if [ "x$rotation" = "x5" ] ; then
+               mogrify_param="-transpose"
+               exiftran_param="-t"
+       fi
+       if [ "x$rotation" = "x6" ] ; then
+               mogrify_param="-rotate 90"
+               exiftran_param="-9"
+       fi
+       if [ "x$rotation" = "x7" ] ; then
+               mogrify_param="-transverse"
+               exiftran_param="-T"
+       fi
+       if [ "x$rotation" = "x8" ] ; then
+               mogrify_param="-rotate -90"
+               exiftran_param="-2"
+       fi
+fi
+
 # iterate over files on commandline
 for file in "$@" ; do
     if [ -n "$get_sidecars" ] ; then
@@ -63,7 +134,25 @@ for file in "$@" ; do
             rotate "$sidecar"
         done
     else
-        rotate "$file"
+               if [ -n "$rotate_image_file" ] ; then
+                       if [ -n "$preserve_mtime" ] ; then
+                               mtime=`mktemp /tmp/geeqie-rotate.XXXXXXXXXX` || exit 3
+                               touch --reference="$file" "$mtime"
+                       fi
+
+                       rotate_image_file "$exiftran_param" "$mogrify_param" "$file"
+                       ret=$?
+
+                       if [ -n "$preserve_mtime" ] ; then
+                               touch --reference="$mtime" "$file"
+                               rm "$mtime"
+                       fi
+                       if [ $ret -eq 4 ] ; then
+                               exit 4
+                       fi
+               else
+                       rotate "$file"
+               fi
     fi
 done
 
index 3900a39..15bdf1b 100644 (file)
@@ -81,12 +81,14 @@ extra_SLIK = \
        $(extra_ICONS)
 
 include $(srcdir)/pan-view/Makefile.am
+include $(srcdir)/view_file/Makefile.am
 
 bin_PROGRAMS = geeqie
 
 geeqie_SOURCES = \
        $(module_SLIK)  \
        $(module_pan_view)      \
+       $(module_view_file)     \
        ClayRGB1998.icc \
        ClayRGB1998_icc.h \
        advanced_exif.c \
@@ -251,10 +253,6 @@ geeqie_SOURCES = \
        view_dir_tree.h \
        view_file.c     \
        view_file.h     \
-       view_file_list.c        \
-       view_file_list.h        \
-       view_file_icon.c        \
-       view_file_icon.h        \
        window.c        \
        window.h        \
        lua.c           \
index 1cd5534..8088071 100644 (file)
--- a/src/bar.c
+++ b/src/bar.c
@@ -83,6 +83,14 @@ static const gchar default_config_comment[] =
 "        </bar>"
 "    </layout>"
 "</gq>";
+static const gchar default_config_rating[] =
+"<gq>"
+"    <layout id = '_current_'>"
+"        <bar>"
+"            <pane_comment id = 'rating' expanded = 'true' key = '" RATING_KEY "' height = '10' />"
+"        </bar>"
+"    </layout>"
+"</gq>";
 
 static const gchar default_config_exif[] =
 "<gq>"
@@ -176,6 +184,7 @@ static const KnownPanes known_panes[] = {
        {PANE_COMMENT,          "title",        N_("Title"),            default_config_title},
        {PANE_KEYWORDS,         "keywords",     N_("Keywords"),         default_config_keywords},
        {PANE_COMMENT,          "comment",      N_("Comment"),          default_config_comment},
+       {PANE_COMMENT,          "rating",       N_("Rating"),           default_config_rating},
        {PANE_EXIF,             "exif",         N_("Exif"),             default_config_exif},
 /* other pre-configured panes */
        {PANE_EXIF,             "file_info",    N_("File info"),        default_config_file_info},
@@ -196,6 +205,7 @@ struct _BarData
        GtkWidget *vbox;
        FileData *fd;
        GtkWidget *label_file_name;
+       GtkWidget *add_button;
 
        LayoutWindow *lw;
        gint width;
@@ -322,6 +332,27 @@ static void bar_menu_popup(GtkWidget *widget)
        gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, bar, 0, GDK_CURRENT_TIME);
 }
 
+static void bar_menu_add_popup(GtkWidget *widget)
+{
+       GtkWidget *menu;
+       GtkWidget *bar;
+       const KnownPanes *pane = known_panes;
+
+       bar = widget;
+
+       menu = popup_menu_short_lived();
+
+       while (pane->id)
+               {
+               GtkWidget *item;
+               item = menu_item_add_stock(menu, _(pane->title), GTK_STOCK_ADD, G_CALLBACK(bar_expander_add_cb), bar);
+               g_object_set_data(G_OBJECT(item), "pane_add_id", pane->id);
+               pane++;
+               }
+
+       gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, bar, 0, GDK_CURRENT_TIME);
+}
+
 
 static gboolean bar_menu_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
 {
@@ -333,6 +364,12 @@ static gboolean bar_menu_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer
        return FALSE;
 }
 
+static gboolean bar_menu_add_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data)
+{
+       bar_menu_add_popup(widget);
+       return TRUE;
+}
+
 
 static void bar_pane_set_fd_cb(GtkWidget *expander, gpointer data)
 {
@@ -539,7 +576,7 @@ void bar_add(GtkWidget *bar, GtkWidget *pane)
 
 void bar_populate_default(GtkWidget *bar)
 {
-       const gchar *populate_id[] = {"histogram", "title", "keywords", "comment", "exif", NULL};
+       const gchar *populate_id[] = {"histogram", "title", "keywords", "comment", "rating", "exif", NULL};
        const gchar **id = populate_id;
 
        while (*id)
@@ -605,6 +642,8 @@ GtkWidget *bar_new(LayoutWindow *lw)
        BarData *bd;
        GtkWidget *box;
        GtkWidget *scrolled;
+       GtkWidget *tbar;
+       GtkWidget *add_box;
 
        bd = g_new0(BarData, 1);
 
@@ -646,6 +685,14 @@ GtkWidget *bar_new(LayoutWindow *lw)
        gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled), bd->vbox);
        gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(scrolled))), GTK_SHADOW_NONE);
 
+       add_box = gtk_vbox_new(FALSE, 0);
+       gtk_box_pack_end(GTK_BOX(bd->widget), add_box, FALSE, FALSE, 0);
+       tbar = pref_toolbar_new(add_box, GTK_TOOLBAR_ICONS);
+       bd->add_button = pref_toolbar_button(tbar, GTK_STOCK_ADD, NULL, FALSE,
+                                            _("Add Pane"),
+                                            G_CALLBACK(bar_menu_add_cb), bd);
+       gtk_widget_show(add_box);
+
 #ifdef HAVE_LIBCHAMPLAIN_GTK
        g_signal_connect(G_OBJECT(gtk_bin_get_child(GTK_BIN(scrolled))), "unrealize", G_CALLBACK(bar_unrealize_clutter_fix_cb), NULL);
 #endif
index a8883c0..b5eb290 100644 (file)
@@ -167,6 +167,19 @@ static void bar_pane_comment_write_config(GtkWidget *pane, GString *outstr, gint
        pcd = g_object_get_data(G_OBJECT(pane), "pane_data");
        if (!pcd) return;
 
+       if (!g_strcmp0(pcd->pane.id, "title"))
+               {
+               pcd->height = options->info_title.height;
+               }
+       if (!g_strcmp0(pcd->pane.id, "comment"))
+               {
+               pcd->height = options->info_comment.height;
+               }
+       if (!g_strcmp0(pcd->pane.id, "rating"))
+               {
+               pcd->height = options->info_rating.height;
+               }
+
        WRITE_NL(); WRITE_STRING("<pane_comment ");
        write_char_option(outstr, indent, "id", pcd->pane.id);
        write_char_option(outstr, indent, "title", gtk_label_get_text(GTK_LABEL(pcd->pane.title)));
@@ -294,6 +307,19 @@ GtkWidget *bar_pane_comment_new_from_config(const gchar **attribute_names, const
                log_printf("unknown attribute %s = %s\n", option, value);
                }
 
+       if (!g_strcmp0(id, "title"))
+               {
+               options->info_title.height = height;
+               }
+       if (!g_strcmp0(id, "comment"))
+               {
+               options->info_comment.height = height;
+               }
+       if (!g_strcmp0(id, "rating"))
+               {
+               options->info_rating.height = height;
+               }
+
        bar_pane_translate_title(PANE_COMMENT, id, &title);
        ret = bar_pane_comment_new(id, title, key, expanded, height);
        g_free(title);
index a112be6..fa0213d 100644 (file)
 #include "layout.h"
 #include "metadata.h"
 #include "menu.h"
+#include "misc.h"
 #include "rcfile.h"
 #include "thumb.h"
 #include "ui_menu.h"
+#include "uri_utils.h"
+#include "ui_utildlg.h"
 
 #include <clutter-gtk/clutter-gtk.h>
 #include <champlain/champlain.h>
@@ -72,8 +75,189 @@ struct _PaneGPSData
        gint selection_count;
        gboolean centre_map_checked;
        gboolean enable_markers_checked;
+       gdouble dest_latitude;
+       gdouble dest_longitude;
+       GList *geocode_list;
 };
 
+/*
+ *-------------------------------------------------------------------
+ * drag-and-drop
+ *-------------------------------------------------------------------
+ */
+enum {
+       TARGET_APP_COLLECTION_MEMBER,
+       TARGET_APP_EXIF_ENTRY,
+       TARGET_APP_KEYWORD_PATH,
+       TARGET_URI_LIST,
+       TARGET_TEXT_PLAIN
+};
+
+static GtkTargetEntry bar_pane_gps_drop_types[] = {
+       { "text/uri-list", 0, TARGET_URI_LIST },
+       { "text/plain", 0, TARGET_TEXT_PLAIN }
+};
+static gint n_gps_entry_drop_types = 2;
+
+static void bar_pane_gps_close_cancel_cb(GenericDialog *gd, gpointer data)
+{
+       PaneGPSData *pgd = data;
+
+       g_list_free(pgd->geocode_list);
+}
+
+static void bar_pane_gps_close_save_cb(GenericDialog *gd, gpointer data)
+{
+       PaneGPSData *pgd = data;
+       FileData *fd;
+       GList *work;
+
+       work = g_list_first(pgd->geocode_list);
+       while (work)
+               {
+               fd = work->data;
+               if (fd->name && !fd->parent)
+                       {
+                       work = work->next;
+                       metadata_write_GPS_coord(fd, "Xmp.exif.GPSLatitude", pgd->dest_latitude);
+                       metadata_write_GPS_coord(fd, "Xmp.exif.GPSLongitude", pgd->dest_longitude);
+                       }
+               }
+       g_list_free(work);
+       g_list_free(pgd->geocode_list);
+}
+
+ static void bar_pane_gps_dnd_receive(GtkWidget *pane, GdkDragContext *context,
+                                                                         gint x, gint y,
+                                                                         GtkSelectionData *selection_data, guint info,
+                                                                         guint time, gpointer data)
+{
+       PaneGPSData *pgd;
+       GenericDialog *gd;
+       FileData *fd, *fd_found;
+       GList *work, *list;
+       gint count, geocoded_count;
+       gdouble latitude, longitude;
+       GString *message;
+       gchar *location;
+       gchar **latlong;
+
+       pgd = g_object_get_data(G_OBJECT(pane), "pane_data");
+       if (!pgd) return;
+
+       if (info == TARGET_URI_LIST)
+               {
+               pgd->dest_longitude = champlain_view_x_to_longitude(CHAMPLAIN_VIEW(pgd->gps_view), x);
+               pgd->dest_latitude = champlain_view_y_to_latitude(CHAMPLAIN_VIEW(pgd->gps_view), y);
+
+               count = 0;
+               geocoded_count = 0;
+               pgd->geocode_list = NULL;
+
+               list = uri_filelist_from_gtk_selection_data(selection_data);
+
+               if (list)
+                       {
+                       work = list;
+                       while (work)
+                               {
+                               fd = work->data;
+                               work = work->next;
+                               if (fd->name && !fd->parent)
+                                       {
+                                       count++;
+                                       pgd->geocode_list = g_list_append(pgd->geocode_list, fd);
+                                       latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+                                       longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+                                       if (latitude != 1000 && longitude != 1000)
+                                               {
+                                               geocoded_count++;
+                                               }
+                                       }
+                               }
+                       g_list_free(work);
+
+                       if(count)
+                               {
+                               message = g_string_new("");
+                               if (count == 1)
+                                       {
+                                       fd_found = g_list_first(pgd->geocode_list)->data;
+                                       g_string_append_printf(message,
+                                                       _("\nDo you want to geocode image %s?"), fd_found->name);
+                                       }
+                               else
+                                       {
+                                       g_string_append_printf(message,
+                                                       _("\nDo you want to geocode %i images?"), count);
+                                       }
+                               if (geocoded_count == 1 && count == 1)
+                                       {
+                                       g_string_append_printf(message,
+                                                       _("\nThis image is already geocoded!"));
+                                       }
+                               else if (geocoded_count == 1 && count > 1)
+                                       {
+                                       g_string_append_printf(message,
+                                                       _("\nOne image is already geocoded!"));
+                                       }
+                               else if (geocoded_count > 1 && count > 1)
+                                       {
+                                       g_string_append_printf(message,
+                                                       _("\n%i Images are already geocoded!"), geocoded_count);
+                                       }
+
+                               location = g_strdup_printf("%lf %lf", pgd->dest_latitude,
+                                                                                                               pgd->dest_longitude);
+                               g_string_append_printf(message, _("\n\nPosition: %s \n"), location);
+
+                               gd = generic_dialog_new(_("Geocode images"),
+                                                       "geocode_images", NULL, TRUE,
+                                                       bar_pane_gps_close_cancel_cb, pgd);
+                               generic_dialog_add_message(gd, GTK_STOCK_DIALOG_QUESTION,
+                                                       _("Write lat/long to meta-data?"),
+                                                       message->str);
+
+                               generic_dialog_add_button(gd, GTK_STOCK_SAVE, NULL,
+                                                                                               bar_pane_gps_close_save_cb, TRUE);
+
+                               gtk_widget_show(gd->dialog);
+                               g_free(location);
+                               g_string_free(message, TRUE);
+                               }
+                       }
+               }
+
+       if (info == TARGET_TEXT_PLAIN)
+               {
+               location = decode_geo_parameters((gchar *)gtk_selection_data_get_data(selection_data));
+               if (!(g_strstr_len(location,-1,"Error")))
+                       {
+                       latlong = g_strsplit(location, " ", 2);
+                       champlain_view_center_on(CHAMPLAIN_VIEW(pgd->gps_view),
+                                                       g_ascii_strtod(latlong[0],NULL),
+                                                       g_ascii_strtod(latlong[1],NULL));
+                       g_strfreev(latlong);
+                       }
+               g_free(location);
+               }
+
+       return;
+}
+
+static void bar_pane_gps_dnd_init(gpointer data)
+{
+       PaneGPSData *pgd = data;
+
+       gtk_drag_dest_set(pgd->widget,
+                         GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP,
+                         bar_pane_gps_drop_types, n_gps_entry_drop_types,
+                         GDK_ACTION_COPY | GDK_ACTION_MOVE);
+       g_signal_connect(G_OBJECT(pgd->widget), "drag_data_received",
+                        G_CALLBACK(bar_pane_gps_dnd_receive), NULL);
+
+}
+
 static gboolean bar_gps_draw_direction (ClutterCanvas *canvas,
                                cairo_t *cr, gpointer data)
 {
@@ -649,7 +833,7 @@ static GtkWidget *bar_pane_gps_menu(PaneGPSData *pgd)
  */
 void bar_pane_gps_map_centreing(PaneGPSData *pgd)
 {
-       GtkWidget *dialog;
+       GenericDialog *gd;
        GString *message = g_string_new("");
 
        if (pgd->centre_map_checked)
@@ -663,16 +847,14 @@ void bar_pane_gps_map_centreing(PaneGPSData *pgd)
                pgd->centre_map_checked = TRUE;
                }
 
-       dialog = gtk_message_dialog_new(NULL,
-                                                         GTK_DIALOG_DESTROY_WITH_PARENT,
-                                                         GTK_MESSAGE_INFO,
-                                                         GTK_BUTTONS_CLOSE,
-                                                         "%s", message->str);
-       gtk_window_set_title(GTK_WINDOW(dialog), _("Map Centreing"));
-       gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
-       gtk_dialog_run(GTK_DIALOG(dialog));
+       gd = generic_dialog_new(_("Map centering"),
+                               "map_centering", NULL, TRUE, NULL, pgd);
+       generic_dialog_add_message(gd, GTK_STOCK_DIALOG_INFO,
+                               "Map Centering", message->str);
+       generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, NULL, TRUE);
+
+       gtk_widget_show(gd->dialog);
 
-       gtk_widget_destroy(dialog);
        g_string_free(message, TRUE);
 }
 
@@ -680,6 +862,8 @@ static gboolean bar_pane_gps_map_keypress_cb(GtkWidget *widget, GdkEventButton *
 {
        PaneGPSData *pgd = data;
        GtkWidget *menu;
+       GtkClipboard *clipboard;
+       gchar *geo_coords;
 
        if (bevent->button == MOUSE_BUTTON_RIGHT)
                {
@@ -694,7 +878,17 @@ static gboolean bar_pane_gps_map_keypress_cb(GtkWidget *widget, GdkEventButton *
                }
        else if (bevent->button == MOUSE_BUTTON_LEFT)
                {
-               return FALSE;
+               clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
+               geo_coords = g_strdup_printf("%lf %lf",
+                                                       champlain_view_y_to_latitude(
+                                                               CHAMPLAIN_VIEW(pgd->gps_view),bevent->y),
+                                                       champlain_view_x_to_longitude(
+                                                               CHAMPLAIN_VIEW(pgd->gps_view),bevent->x));
+               gtk_clipboard_set_text(clipboard, geo_coords, -1);
+
+               g_free(geo_coords);
+
+               return TRUE;
                }
        else
                {
@@ -807,6 +1001,8 @@ GtkWidget *bar_pane_gps_new(const gchar *id, const gchar *title, const gchar *ma
        g_signal_connect(pgd->gps_view, "notify::zoom-level", G_CALLBACK(bar_pane_gps_view_state_changed_cb), pgd);
        g_signal_connect(G_OBJECT(slider), "value-changed", G_CALLBACK(bar_pane_gps_slider_changed_cb), pgd);
 
+       bar_pane_gps_dnd_init(pgd);
+
        file_data_register_notify_func(bar_pane_gps_notify_cb, pgd, NOTIFY_PRIORITY_LOW);
 
        pgd->create_markers_id = 0;
index 96376d3..0f3f436 100644 (file)
@@ -123,6 +123,8 @@ struct _PaneKeywordsData
        FileData *fd;
        gchar *key;
        gint height;
+
+       GList *expanded_rows;
 };
 
 typedef struct _ConfDialogData ConfDialogData;
@@ -247,20 +249,69 @@ void bar_pane_keywords_set_fd(GtkWidget *pane, FileData *fd)
        bar_pane_keywords_update(pkd);
 }
 
+void bar_keyword_tree_get_expanded_cb(GtkTreeView *keyword_treeview, GtkTreePath *path,  gpointer data)
+{
+       GList **expanded = data;
+       GtkTreeModel *model;
+       GtkTreeIter iter;
+       gchar *path_string;
+
+       model = gtk_tree_view_get_model(GTK_TREE_VIEW(keyword_treeview));
+       gtk_tree_model_get_iter(model, &iter, path);
+
+       path_string = gtk_tree_model_get_string_from_iter(model, &iter);
+
+       *expanded = g_list_append(*expanded, g_strdup(path_string));
+       g_free(path_string);
+}
+
+static void bar_pane_keywords_entry_write_config(gchar *entry, GString *outstr, gint indent)
+{
+       struct {
+               gchar *path;
+       } expand;
+
+       expand.path = entry;
+
+       WRITE_NL(); WRITE_STRING("<expanded ");
+       WRITE_CHAR(expand, path);
+       WRITE_STRING("/>");
+}
+
 static void bar_pane_keywords_write_config(GtkWidget *pane, GString *outstr, gint indent)
 {
        PaneKeywordsData *pkd;
+       GList *path_expanded = NULL;
 
        pkd = g_object_get_data(G_OBJECT(pane), "pane_data");
        if (!pkd) return;
 
+       pkd->height = options->info_keywords.height;
+
        WRITE_NL(); WRITE_STRING("<pane_keywords ");
        write_char_option(outstr, indent, "id", pkd->pane.id);
        write_char_option(outstr, indent, "title", gtk_label_get_text(GTK_LABEL(pkd->pane.title)));
        WRITE_BOOL(pkd->pane, expanded);
        WRITE_CHAR(*pkd, key);
        WRITE_INT(*pkd, height);
-       WRITE_STRING("/>");
+       WRITE_STRING(">");
+       indent++;
+
+       gtk_tree_view_map_expanded_rows(GTK_TREE_VIEW(pkd->keyword_treeview),
+                                                               (bar_keyword_tree_get_expanded_cb), &path_expanded);
+
+       g_list_first(path_expanded);
+       while (path_expanded)
+               {
+               bar_pane_keywords_entry_write_config(path_expanded->data, outstr, indent);
+               g_free(path_expanded->data);
+               path_expanded = path_expanded->next;
+               }
+       g_list_free(path_expanded);
+
+       indent--;
+       WRITE_NL();
+       WRITE_STRING("</pane_keywords>");
 }
 
 gint bar_pane_keywords_event(GtkWidget *bar, GdkEvent *event)
@@ -1013,6 +1064,11 @@ static void bar_pane_keywords_show_all_cb(GtkWidget *menu_widget, gpointer data)
 
        GtkTreeModel *keyword_tree;
 
+       string_list_free(pkd->expanded_rows);
+       pkd->expanded_rows = NULL;
+       gtk_tree_view_map_expanded_rows(GTK_TREE_VIEW(pkd->keyword_treeview),
+                                                               (bar_keyword_tree_get_expanded_cb), &pkd->expanded_rows);
+
        pkd->hide_unchecked = FALSE;
 
        model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
@@ -1024,6 +1080,28 @@ static void bar_pane_keywords_show_all_cb(GtkWidget *menu_widget, gpointer data)
        bar_keyword_tree_sync(pkd);
 }
 
+static void bar_pane_keywords_revert_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       GList *work;
+       GtkTreePath *tree_path;
+       gchar *path;
+
+       gtk_tree_view_collapse_all(GTK_TREE_VIEW(pkd->keyword_treeview));
+
+       work = pkd->expanded_rows;
+       while (work)
+               {
+               path = work->data;
+               tree_path = gtk_tree_path_new_from_string(path);
+               gtk_tree_view_expand_to_path(GTK_TREE_VIEW(pkd->keyword_treeview), tree_path);
+               work = work->next;
+               gtk_tree_path_free(tree_path);
+               }
+
+       bar_keyword_tree_sync(pkd);
+}
+
 static void bar_pane_keywords_expand_checked_cb(GtkWidget *menu_widget, gpointer data)
 {
        PaneKeywordsData *pkd = data;
@@ -1033,6 +1111,34 @@ static void bar_pane_keywords_expand_checked_cb(GtkWidget *menu_widget, gpointer
        gtk_tree_model_foreach(model, bar_keyword_tree_expand_if_set_cb, pkd);
 }
 
+static void bar_pane_keywords_collapse_all_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+
+       string_list_free(pkd->expanded_rows);
+       pkd->expanded_rows = NULL;
+       gtk_tree_view_map_expanded_rows(GTK_TREE_VIEW(pkd->keyword_treeview),
+                                                               (bar_keyword_tree_get_expanded_cb), &pkd->expanded_rows);
+
+       gtk_tree_view_collapse_all(GTK_TREE_VIEW(pkd->keyword_treeview));
+
+       bar_keyword_tree_sync(pkd);
+}
+
+static void bar_pane_keywords_revert_hidden_cb(GtkWidget *menu_widget, gpointer data)
+{
+       PaneKeywordsData *pkd = data;
+       GtkTreeModel *model;
+       GtkTreeModel *keyword_tree;
+
+       model = gtk_tree_view_get_model(GTK_TREE_VIEW(pkd->keyword_treeview));
+       keyword_tree = gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(model));
+
+       keyword_revert_hidden_in(GTK_TREE_STORE(keyword_tree), model);
+
+       bar_keyword_tree_sync(pkd);
+}
+
 static void bar_pane_keywords_collapse_unchecked_cb(GtkWidget *menu_widget, gpointer data)
 {
        PaneKeywordsData *pkd = data;
@@ -1217,7 +1323,12 @@ static void bar_pane_keywords_menu_popup(GtkWidget *widget, PaneKeywordsData *pk
        menu_item_add(menu, _("Expand checked"), G_CALLBACK(bar_pane_keywords_expand_checked_cb), pkd);
        menu_item_add(menu, _("Collapse unchecked"), G_CALLBACK(bar_pane_keywords_collapse_unchecked_cb), pkd);
        menu_item_add(menu, _("Hide unchecked"), G_CALLBACK(bar_pane_keywords_hide_unchecked_cb), pkd);
+       menu_item_add(menu, _("Revert all hidden"), G_CALLBACK(bar_pane_keywords_revert_hidden_cb), pkd);
+       menu_item_add_divider(menu);
        menu_item_add(menu, _("Show all"), G_CALLBACK(bar_pane_keywords_show_all_cb), pkd);
+       menu_item_add(menu, _("Collapse all"), G_CALLBACK(bar_pane_keywords_collapse_all_cb), pkd);
+       menu_item_add(menu, _("Revert"), G_CALLBACK(bar_pane_keywords_revert_cb), pkd);
+       menu_item_add_divider(menu);
 
        submenu = gtk_menu_new();
        item = menu_item_add(menu, _("On any change"), NULL, NULL);
@@ -1263,7 +1374,8 @@ static void bar_pane_keywords_destroy(GtkWidget *widget, gpointer data)
 {
        PaneKeywordsData *pkd = data;
 
-        if (pkd->click_tpath) gtk_tree_path_free(pkd->click_tpath);
+       string_list_free(pkd->expanded_rows);
+       if (pkd->click_tpath) gtk_tree_path_free(pkd->click_tpath);
        if (pkd->idle_id) g_source_remove(pkd->idle_id);
        file_data_unregister_notify_func(bar_pane_keywords_notify_cb, pkd);
 
@@ -1300,6 +1412,7 @@ static GtkWidget *bar_pane_keywords_new(const gchar *id, const gchar *title, con
        pkd->key = g_strdup(key);
 
        pkd->expand_checked = TRUE;
+       pkd->expanded_rows = NULL;
 
        hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
 
@@ -1448,6 +1561,7 @@ GtkWidget *bar_pane_keywords_new_from_config(const gchar **attribute_names, cons
                log_printf("unknown attribute %s = %s\n", option, value);
                }
 
+       options->info_keywords.height = height;
        bar_pane_translate_title(PANE_KEYWORDS, id, &title);
        ret = bar_pane_keywords_new(id, title, key, expanded, height);
        g_free(id);
@@ -1491,4 +1605,29 @@ void bar_pane_keywords_update_from_config(GtkWidget *pane, const gchar **attribu
 }
 
 
+void bar_pane_keywords_entry_add_from_config(GtkWidget *pane, const gchar **attribute_names, const gchar **attribute_values)
+{
+       PaneKeywordsData *pkd;
+       gchar *path = NULL;
+       GtkTreePath *tree_path;
+
+       pkd = g_object_get_data(G_OBJECT(pane), "pane_data");
+       if (!pkd) return;
+
+       while (*attribute_names)
+               {
+               const gchar *option = *attribute_names++;
+               const gchar *value = *attribute_values++;
+
+               if (READ_CHAR_FULL("path", path))
+                       {
+                       tree_path = gtk_tree_path_new_from_string(path);
+                       gtk_tree_view_expand_to_path(GTK_TREE_VIEW(pkd->keyword_treeview), tree_path);
+                       gtk_tree_path_free(tree_path);
+                       pkd->expanded_rows = g_list_append(pkd->expanded_rows, g_strdup(path));
+                       continue;
+                       }
+               log_printf("unknown attribute %s = %s\n", option, value);
+               }
+}
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index 149ddbc..1c2583f 100644 (file)
@@ -24,7 +24,7 @@
 
 GtkWidget *bar_pane_keywords_new_from_config(const gchar **attribute_names, const gchar **attribute_values);
 void bar_pane_keywords_update_from_config(GtkWidget *pane, const gchar **attribute_names, const gchar **attribute_values);
-
+void bar_pane_keywords_entry_add_from_config(GtkWidget *pane, const gchar **attribute_names, const gchar **attribute_values);
 /* used in search.c */
 GList *keyword_list_pull(GtkWidget *text_widget);
 
index 02c8e22..509f7a3 100644 (file)
@@ -85,8 +85,8 @@ struct _SortData
        GtkWidget *undo_button;
        SortActionType undo_action;
        GList *undo_src_list;
-       gchar *undo_src;
-       gchar *undo_dest;
+       GList *undo_dest_list;
+       gchar *undo_collection;
 };
 
 
@@ -94,7 +94,7 @@ struct _SortData
 #define SORT_KEY_COLLECTIONS "sort_manager_collections"
 
 
-static void bar_sort_undo_set(SortData *sd, GList *src_list, FileData *src, const gchar *dest);
+static void bar_sort_undo_set(SortData *sd, GList *src_list, const gchar *dest);
 static void bar_sort_add_close(SortData *sd);
 
 
@@ -165,7 +165,7 @@ static void bar_sort_mode_sync(SortData *sd, SortModeType mode)
 
        bar_sort_add_close(sd);
 
-       bar_sort_undo_set(sd, NULL, NULL, NULL);
+       bar_sort_undo_set(sd, NULL, NULL);
 }
 
 static void bar_sort_mode_cb(GtkWidget *combo, gpointer data)
@@ -183,28 +183,42 @@ static void bar_sort_mode_cb(GtkWidget *combo, gpointer data)
 }
 
 /* this takes control of src_list */
-static void bar_sort_undo_set(SortData *sd, GList *src_list, FileData *src, const gchar *dest)
+static void bar_sort_undo_set(SortData *sd, GList *src_list, const gchar *dest)
 {
        string_list_free(sd->undo_src_list);
        sd->undo_src_list = filelist_to_path_list(src_list);
 
-       g_free(sd->undo_src);
-       sd->undo_src = src ? g_strdup(src->path) : NULL;
-       g_free(sd->undo_dest);
-       sd->undo_dest = g_strdup(dest);
+       if (src_list)
+               {
+               /* we should create the undo_dest_list to use it later... */
+               string_list_free(sd->undo_dest_list);
+               sd->undo_dest_list=NULL;
+
+               GList *work = sd->undo_src_list;
+               while(work)
+                       {
+                       gchar *filename =  g_strdup(filename_from_path(work->data));
+                       gchar *dest_path = g_build_filename(g_strdup(dest), filename, NULL);
+                       sd->undo_dest_list = g_list_prepend(sd->undo_dest_list, g_strdup(dest_path));
+                       work = work->next;
+                       }
+               sd->undo_dest_list = g_list_reverse(sd->undo_dest_list);
+               }
 
        sd->undo_action = sd->action;
 
        if (sd->undo_button)
                {
                gtk_widget_set_sensitive(sd->undo_button,
-                                        ((sd->undo_src_list || sd->undo_src) && sd->undo_dest) );
+                                       ((sd->undo_src_list ) && sd->undo_dest_list));
                }
 }
 
 static void bar_sort_undo_folder(SortData *sd, GtkWidget *button)
 {
-       if (!sd->undo_src || !sd->undo_dest) return;
+       gchar *origin;
+
+       if (!(sd->undo_src_list && sd->undo_dest_list)) return;
 
        switch (sd->undo_action)
                {
@@ -212,30 +226,57 @@ static void bar_sort_undo_folder(SortData *sd, GtkWidget *button)
                        {
                        GList *list;
                        gchar *src_dir;
+                       gchar *src_path;
 
-                       list = g_list_append(NULL, file_data_new_group(sd->undo_dest));
-                       src_dir = remove_level_from_path(sd->undo_src);
-                       file_util_move_simple(list, src_dir, sd->lw->window);
-                       g_free(src_dir);
+                       if (sd->undo_src_list)
+                               {
+                               GList *work = NULL;
+
+                               src_path = g_strdup(sd->undo_src_list->data);
+                               src_dir = remove_level_from_path(src_path);
+                               list = sd->undo_dest_list;
+                               while (list)
+                                       {
+                                       work = g_list_prepend(work, file_data_new_group(list->data));
+                                       list=list->next;
+                                       }
+                               file_util_move_simple(work, src_dir, sd->lw->window);
+                               g_free(src_dir);
+                               g_free(src_path);
+                               }
                        }
                        break;
+
                case BAR_SORT_COPY:
-                       file_util_delete(file_data_new_group(sd->undo_dest), NULL, button);
+               case BAR_SORT_FILTER:
+                       if (sd->undo_src_list)
+                               {
+                               GList *delete_list;
+                               GList *work = NULL;
+
+                               delete_list = sd->undo_dest_list;
+                               while (delete_list)
+                                       {
+                                       work = g_list_append(work, file_data_new_group(delete_list->data));
+                                       delete_list = delete_list->next;
+                                       }
+                               file_util_delete(NULL, work, button);
+                               }
                        break;
+
                default:
-                       /* undo external command */
-                       file_util_delete(file_data_new_group(sd->undo_dest), NULL, button);
                        break;
                }
 
        layout_refresh(sd->lw);
+       origin = (sd->undo_src_list)->data;
 
-       if (isfile(sd->undo_src))
+       if (isfile(origin))
                {
-               layout_image_set_fd(sd->lw, file_data_new_group(sd->undo_src));
+               layout_image_set_fd(sd->lw, file_data_new_group(origin));
                }
 
-       bar_sort_undo_set(sd, NULL, NULL, NULL);
+       bar_sort_undo_set(sd, NULL, NULL);
 }
 
 static void bar_sort_undo_collection(SortData *sd)
@@ -246,13 +287,12 @@ static void bar_sort_undo_collection(SortData *sd)
        while (work)
                {
                gchar *source;
-
                source = work->data;
                work = work->next;
-               collect_manager_remove(file_data_new_group(source), sd->undo_dest);
+               collect_manager_remove(file_data_new_group(source), sd->undo_collection);
                }
 
-       bar_sort_undo_set(sd, NULL, NULL, NULL);
+       bar_sort_undo_set(sd, NULL,  NULL);
 }
 
 static void bar_sort_undo_cb(GtkWidget *button, gpointer data)
@@ -271,38 +311,40 @@ static void bar_sort_undo_cb(GtkWidget *button, gpointer data)
 
 static void bar_sort_bookmark_select_folder(SortData *sd, FileData *source, const gchar *path)
 {
-       GList *list;
-       gchar *dest_path;
+       GList *orig_list;
+       GList *action_list;
+       GList *undo_src_list;
 
        if (!isdir(path)) return;
 
-       dest_path = g_build_filename(path, source->name, NULL);
-       bar_sort_undo_set(sd, NULL, source, dest_path);
+       orig_list = layout_selection_list(sd->lw);
+       action_list = orig_list;
+       undo_src_list = orig_list;
+       orig_list = NULL;
 
-       list = g_list_append(NULL, file_data_ref(source));
+       bar_sort_undo_set(sd, undo_src_list, path);
 
        switch (sd->action)
                {
                case BAR_SORT_COPY:
-                       file_util_copy_simple(list, path, sd->lw->window);
-                       list = NULL;
+                       file_util_copy_simple(action_list, path, sd->lw->window);
+                       action_list = NULL;
                        layout_image_next(sd->lw);
                        break;
+
                case BAR_SORT_MOVE:
-                       file_util_move_simple(list, path, sd->lw->window);
-                       list = NULL;
+                       file_util_move_simple(action_list, path, sd->lw->window);
+                       action_list = NULL;
                        break;
+
                case BAR_SORT_FILTER:
-                       file_util_start_filter_from_filelist(sd->filter_key, list, path, sd->lw->window);
-                       list = NULL;
+                       file_util_start_filter_from_filelist(sd->filter_key, action_list, path, sd->lw->window);
                        layout_image_next(sd->lw);
                        break;
+
                default:
                        break;
                }
-
-       g_list_free(list);
-       g_free(dest_path);
 }
 
 static void bar_sort_bookmark_select_collection(SortData *sd, FileData *source, const gchar *path)
@@ -323,11 +365,12 @@ static void bar_sort_bookmark_select_collection(SortData *sd, FileData *source,
 
        if (!list)
                {
-               bar_sort_undo_set(sd, NULL, NULL, NULL);
+               bar_sort_undo_set(sd, NULL, NULL);
                return;
                }
 
-       bar_sort_undo_set(sd, list, NULL, path);
+       bar_sort_undo_set(sd, list, path);
+       sd->undo_collection = g_strdup(path);
 
        while (list)
                {
@@ -558,8 +601,9 @@ static void bar_sort_destroy(GtkWidget *widget, gpointer data)
        bar_sort_add_close(sd);
 
        g_free(sd->filter_key);
-       g_free(sd->undo_src);
-       g_free(sd->undo_dest);
+       string_list_free(sd->undo_src_list);
+       string_list_free(sd->undo_dest_list);
+       g_free(sd->undo_collection);
        g_free(sd);
 }
 
@@ -594,8 +638,9 @@ static GtkWidget *bar_sort_new(LayoutWindow *lw, SortActionType action,
                }
 
        sd->selection = selection;
-       sd->undo_src = NULL;
-       sd->undo_dest = NULL;
+       sd->undo_src_list = NULL;
+       sd->undo_dest_list = NULL;
+       sd->undo_collection = NULL;
 
        sd->vbox = gtk_vbox_new(FALSE, PREF_PAD_GAP);
        g_object_set_data(G_OBJECT(sd->vbox), "bar_sort_data", sd);
index 230ce10..ed92b54 100644 (file)
@@ -48,6 +48,7 @@ struct _CMData
        GtkWidget *button_close;
        gboolean clear;
        gboolean metadata;
+       gboolean remote;
 };
 
 #define PURGE_DIALOG_WIDTH 400
@@ -117,11 +118,14 @@ static void cache_maintain_home_stop(CMData *cm)
                cm->idle_id = 0;
                }
 
-       gtk_entry_set_text(GTK_ENTRY(cm->entry), _("done"));
-       spinner_set_interval(cm->spinner, -1);
+       if (!cm->remote)
+               {
+               gtk_entry_set_text(GTK_ENTRY(cm->entry), _("done"));
+               spinner_set_interval(cm->spinner, -1);
 
-       gtk_widget_set_sensitive(cm->button_stop, FALSE);
-       gtk_widget_set_sensitive(cm->button_close, TRUE);
+               gtk_widget_set_sensitive(cm->button_stop, FALSE);
+               gtk_widget_set_sensitive(cm->button_close, TRUE);
+               }
 }
 
 static gboolean cache_maintain_home_cb(gpointer data)
@@ -134,6 +138,7 @@ static gboolean cache_maintain_home_cb(gpointer data)
        gboolean still_have_a_file = TRUE;
        gsize base_length;
        const gchar *cache_folder;
+       gboolean filter_disable;
 
        if (cm->metadata)
                {
@@ -158,6 +163,13 @@ static gboolean cache_maintain_home_cb(gpointer data)
 
        DEBUG_1("purge chk (%d) \"%s\"", (cm->clear && !cm->metadata), fd->path);
 
+/**
+ * It is necessary to disable the file filter when clearing the cache,
+ * otherwise the .sim (file similarity) files are not deleted.
+ */
+       filter_disable = options->file_filter.disable;
+       options->file_filter.disable = TRUE;
+
        if (g_list_find(cm->done_list, fd) == NULL)
                {
                cm->done_list = g_list_prepend(cm->done_list, fd);
@@ -194,6 +206,8 @@ static gboolean cache_maintain_home_cb(gpointer data)
                                }
                        }
                }
+       options->file_filter.disable = filter_disable;
+
        filelist_free(list);
 
        cm->list = g_list_concat(dlist, cm->list);
@@ -224,7 +238,7 @@ static gboolean cache_maintain_home_cb(gpointer data)
                file_data_unref(fd);
                }
 
-       if (cm->list)
+       if (cm->list && !cm->remote)
                {
                const gchar *buf;
 
@@ -292,6 +306,7 @@ void cache_maintain_home(gboolean metadata, gboolean clear, GtkWidget *parent)
        cm->done_list = NULL;
        cm->clear = clear;
        cm->metadata = metadata;
+       cm->remote = FALSE;
 
        if (metadata)
                {
@@ -339,6 +354,41 @@ void cache_maintain_home(gboolean metadata, gboolean clear, GtkWidget *parent)
        cm->idle_id = g_idle_add(cache_maintain_home_cb, cm);
 }
 
+void cache_maintain_home_remote(gboolean metadata, gboolean clear)
+{
+       CMData *cm;
+       GList *dlist;
+       FileData *dir_fd;
+       const gchar *cache_folder;
+
+       if (metadata)
+               {
+               cache_folder = get_metadata_cache_dir();
+               }
+       else
+               {
+               cache_folder = get_thumbnails_cache_dir();
+               }
+
+       dir_fd = file_data_new_dir(cache_folder);
+       if (!filelist_read(dir_fd, NULL, &dlist))
+               {
+               file_data_unref(dir_fd);
+               return;
+               }
+
+       dlist = g_list_append(dlist, dir_fd);
+
+       cm = g_new0(CMData, 1);
+       cm->list = dlist;
+       cm->done_list = NULL;
+       cm->clear = clear;
+       cm->metadata = metadata;
+       cm->remote = TRUE;
+
+       cm->idle_id = g_idle_add(cache_maintain_home_cb, cm);
+}
+
 static void cache_file_move(const gchar *src, const gchar *dest)
 {
        if (!dest || !src || !isfile(src)) return;
@@ -526,6 +576,8 @@ struct _CleanData
        gboolean local;
        gboolean recurse;
 
+       gboolean remote;
+
        guint idle_id; /* event source id */
 };
 
@@ -555,14 +607,16 @@ static void cache_manager_render_close_cb(GenericDialog *fd, gpointer data)
 static void cache_manager_render_finish(CleanData *cd)
 {
        cache_manager_render_reset(cd);
+       if (!cd->remote)
+               {
+               gtk_entry_set_text(GTK_ENTRY(cd->progress), _("done"));
+               spinner_set_interval(cd->spinner, -1);
 
-       gtk_entry_set_text(GTK_ENTRY(cd->progress), _("done"));
-       spinner_set_interval(cd->spinner, -1);
-
-       gtk_widget_set_sensitive(cd->group, TRUE);
-       gtk_widget_set_sensitive(cd->button_start, TRUE);
-       gtk_widget_set_sensitive(cd->button_stop, FALSE);
-       gtk_widget_set_sensitive(cd->button_close, TRUE);
+               gtk_widget_set_sensitive(cd->group, TRUE);
+               gtk_widget_set_sensitive(cd->button_start, TRUE);
+               gtk_widget_set_sensitive(cd->button_stop, FALSE);
+               gtk_widget_set_sensitive(cd->button_close, TRUE);
+               }
 }
 
 static void cache_manager_render_stop_cb(GenericDialog *fd, gpointer data)
@@ -624,7 +678,10 @@ static gboolean cache_manager_render_file(CleanData *cd)
                success = thumb_loader_start((ThumbLoader *)cd->tl, fd);
                if (success)
                        {
-                       gtk_entry_set_text(GTK_ENTRY(cd->progress), fd->path);
+                       if (!cd->remote)
+                               {
+                               gtk_entry_set_text(GTK_ENTRY(cd->progress), fd->path);
+                               }
                        }
                else
                        {
@@ -660,26 +717,62 @@ static void cache_manager_render_start_cb(GenericDialog *fd, gpointer data)
        CleanData *cd = data;
        gchar *path;
 
-       if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
+       if(!cd->remote)
+               {
+               if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
+               }
 
        path = remove_trailing_slash((gtk_entry_get_text(GTK_ENTRY(cd->entry))));
        parse_out_relatives(path);
 
        if (!isdir(path))
                {
-               warning_dialog(_("Invalid folder"),
-                               _("The specified folder can not be found."),
-                              GTK_STOCK_DIALOG_WARNING, cd->gd->dialog);
+               if (!cd->remote)
+                       {
+                       warning_dialog(_("Invalid folder"),
+                       _("The specified folder can not be found."),
+                       GTK_STOCK_DIALOG_WARNING, cd->gd->dialog);
+                       }
+               else
+                       {
+                       log_printf("The specified folder can not be found: %s\n", path);
+                       }
                }
        else
                {
                FileData *dir_fd;
-               gtk_widget_set_sensitive(cd->group, FALSE);
-               gtk_widget_set_sensitive(cd->button_start, FALSE);
-               gtk_widget_set_sensitive(cd->button_stop, TRUE);
-               gtk_widget_set_sensitive(cd->button_close, FALSE);
+               if(!cd->remote)
+                       {
+                       gtk_widget_set_sensitive(cd->group, FALSE);
+                       gtk_widget_set_sensitive(cd->button_start, FALSE);
+                       gtk_widget_set_sensitive(cd->button_stop, TRUE);
+                       gtk_widget_set_sensitive(cd->button_close, FALSE);
 
-               spinner_set_interval(cd->spinner, SPINNER_SPEED);
+                       spinner_set_interval(cd->spinner, SPINNER_SPEED);
+                       }
+               dir_fd = file_data_new_dir(path);
+               cache_manager_render_folder(cd, dir_fd);
+               file_data_unref(dir_fd);
+               while (cache_manager_render_file(cd));
+               }
+
+       g_free(path);
+}
+
+static void cache_manager_render_start_render_remote(CleanData *cd, const gchar *user_path)
+{
+       gchar *path;
+
+       path = remove_trailing_slash(user_path);
+       parse_out_relatives(path);
+
+       if (!isdir(path))
+               {
+               log_printf("The specified folder can not be found: %s\n", path);
+               }
+       else
+               {
+               FileData *dir_fd;
 
                dir_fd = file_data_new_dir(path);
                cache_manager_render_folder(cd, dir_fd);
@@ -698,6 +791,7 @@ static void cache_manager_render_dialog(GtkWidget *widget, const gchar *path)
        GtkWidget *button;
 
        cd = g_new0(CleanData, 1);
+       cd->remote = FALSE;
 
        cd->gd = generic_dialog_new(_("Create thumbnails"),
                                    "create_thumbnails",
@@ -750,8 +844,17 @@ static void cache_manager_render_dialog(GtkWidget *widget, const gchar *path)
        gtk_widget_show(cd->gd->dialog);
 }
 
+void cache_manager_render_remote(const gchar *path, gboolean recurse, gboolean local)
+{
+       CleanData *cd;
 
+       cd = g_new0(CleanData, 1);
+       cd->recurse = recurse;
+       cd->local = local;
+       cd->remote = TRUE;
 
+       cache_manager_render_start_render_remote(cd, path);
+}
 
 static void cache_manager_standard_clean_close_cb(GenericDialog *gd, gpointer data)
 {
@@ -768,12 +871,14 @@ static void cache_manager_standard_clean_close_cb(GenericDialog *gd, gpointer da
 
 static void cache_manager_standard_clean_done(CleanData *cd)
 {
-       gtk_widget_set_sensitive(cd->button_stop, FALSE);
-       gtk_widget_set_sensitive(cd->button_close, TRUE);
-
-       gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress), 1.0);
-       gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("done"));
+       if (!cd->remote)
+               {
+               gtk_widget_set_sensitive(cd->button_stop, FALSE);
+               gtk_widget_set_sensitive(cd->button_close, TRUE);
 
+               gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress), 1.0);
+               gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("done"));
+               }
        if (cd->idle_id)
                {
                g_source_remove(cd->idle_id);
@@ -811,10 +916,13 @@ static gint cache_manager_standard_clean_clear_cb(gpointer data)
                file_data_unref(next_fd);
 
                cd->count_done++;
-               if (cd->count_total != 0)
+               if (!cd->remote)
                        {
-                       gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress),
-                                                     (gdouble)cd->count_done / cd->count_total);
+                       if (cd->count_total != 0)
+                               {
+                               gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress),
+                                                             (gdouble)cd->count_done / cd->count_total);
+                               }
                        }
 
                return TRUE;
@@ -838,10 +946,13 @@ static void cache_manager_standard_clean_valid_cb(const gchar *path, gboolean va
                        }
 
                cd->count_done++;
-               if (cd->count_total != 0)
+               if (!cd->remote)
                        {
-                       gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress),
-                                                     (gdouble)cd->count_done / cd->count_total);
+                       if (cd->count_total != 0)
+                               {
+                               gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(cd->progress),
+                                                             (gdouble)cd->count_done / cd->count_total);
+                               }
                        }
                }
 
@@ -863,20 +974,23 @@ static void cache_manager_standard_clean_valid_cb(const gchar *path, gboolean va
                }
 }
 
-static void cache_manager_standard_clean_start_cb(GenericDialog *gd, gpointer data)
+static void cache_manager_standard_clean_start(GenericDialog *gd, gpointer data)
 {
        CleanData *cd = data;
        GList *list;
        gchar *path;
        FileData *dir_fd;
 
-       if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
+       if (!cd->remote)
+       {
+               if (cd->list || !gtk_widget_get_sensitive(cd->button_start)) return;
 
-       gtk_widget_set_sensitive(cd->button_start, FALSE);
-       gtk_widget_set_sensitive(cd->button_stop, TRUE);
-       gtk_widget_set_sensitive(cd->button_close, FALSE);
+               gtk_widget_set_sensitive(cd->button_start, FALSE);
+               gtk_widget_set_sensitive(cd->button_stop, TRUE);
+               gtk_widget_set_sensitive(cd->button_close, FALSE);
 
-       gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("running..."));
+               gtk_progress_bar_set_text(GTK_PROGRESS_BAR(cd->progress), _("running..."));
+       }
 
        path = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, THUMB_FOLDER_NORMAL, NULL);
        dir_fd = file_data_new_dir(path);
@@ -913,6 +1027,11 @@ static void cache_manager_standard_clean_start_cb(GenericDialog *gd, gpointer da
                }
 }
 
+static void cache_manager_standard_clean_start_cb(GenericDialog *gd, gpointer data)
+{
+       cache_manager_standard_clean_start(gd, data);
+}
+
 static void cache_manager_standard_process(GtkWidget *widget, gboolean clear)
 {
        CleanData *cd;
@@ -921,6 +1040,7 @@ static void cache_manager_standard_process(GtkWidget *widget, gboolean clear)
 
        cd = g_new0(CleanData, 1);
        cd->clear = clear;
+       cd->remote = FALSE;
 
        if (clear)
                {
@@ -963,6 +1083,20 @@ static void cache_manager_standard_process(GtkWidget *widget, gboolean clear)
        gtk_widget_show(cd->gd->dialog);
 }
 
+void cache_manager_standard_process_remote(gboolean clear)
+{
+       CleanData *cd;
+
+       cd = g_new0(CleanData, 1);
+       cd->clear = clear;
+       cd->days = 30;
+       cd->tl = NULL;
+       cd->idle_id = 0;
+       cd->remote = TRUE;
+
+       cache_manager_standard_clean_start(NULL, cd);
+}
+
 static void cache_manager_standard_clean_cb(GtkWidget *widget, gpointer data)
 {
        cache_manager_standard_process(widget, FALSE);
@@ -1042,11 +1176,8 @@ static GtkWidget *cache_manager_location_label(GtkWidget *group, const gchar *su
 {
        GtkWidget *label;
        gchar *buf;
-       gchar *path;
 
-       path = g_build_filename(homedir(), subdir, NULL);
-       buf = g_strdup_printf(_("Location: %s"), path);
-       g_free(path);
+       buf = g_strdup_printf(_("Location: %s"), subdir);
        label = pref_label_new(group, buf);
        g_free(buf);
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
@@ -1061,6 +1192,7 @@ void cache_manager_show(void)
        GtkWidget *button;
        GtkWidget *table;
        GtkSizeGroup *sizegroup;
+       gchar *path;
 
        if (cache_manager)
                {
@@ -1100,12 +1232,14 @@ void cache_manager_show(void)
        button = pref_table_button(table, 0, 1, GTK_STOCK_DELETE, _("Clear cache"), FALSE,
                                   G_CALLBACK(cache_manager_main_clear_cb), cache_manager);
        gtk_size_group_add_widget(sizegroup, button);
-       pref_table_label(table, 1, 1, _("Delete all cached thumbnails."), 0.0);
+       pref_table_label(table, 1, 1, _("Delete all cached data."), 0.0);
 
 
        group = pref_group_new(gd->vbox, FALSE, _("Shared thumbnail cache"), GTK_ORIENTATION_VERTICAL);
 
-       cache_manager_location_label(group, THUMB_FOLDER_GLOBAL);
+       path = g_build_filename(homedir(), THUMB_FOLDER_GLOBAL, NULL);
+       cache_manager_location_label(group, path);
+       g_free(path);
 
        table = pref_table_new(group, 2, 2, FALSE, FALSE);
 
index a2eb5e3..d839496 100644 (file)
@@ -27,6 +27,8 @@ void cache_maintain_home(gboolean metadata, gboolean clear, GtkWidget *parent);
 void cache_notify_cb(FileData *fd, NotifyType type, gpointer data);
 void cache_manager_show(void);
 
-
+void cache_maintain_home_remote(gboolean metadata, gboolean clear);
+void cache_manager_standard_process_remote(gboolean clear);
+void cache_manager_render_remote(const gchar *path, gboolean recurse, gboolean local);
 #endif
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index 7de28db..5f42e1c 100644 (file)
@@ -683,7 +683,11 @@ gqv_cell_renderer_icon_render(GtkCellRenderer              *cell,
        GdkPixbuf *pixbuf;
        const gchar *text;
        GdkRectangle cell_rect;
+#if GTK_CHECK_VERSION(3,0,0)
+       GtkStateFlags state;
+#else
        GtkStateType state;
+#endif
        gint xpad, ypad;
 
 
@@ -712,16 +716,28 @@ gqv_cell_renderer_icon_render(GtkCellRenderer             *cell,
        if ((flags & GTK_CELL_RENDERER_SELECTED) == GTK_CELL_RENDERER_SELECTED)
                {
                if (gtk_widget_has_focus(widget))
+#if GTK_CHECK_VERSION(3,0,0)
+                       state = GTK_STATE_FLAG_SELECTED;
+               else
+                       state = GTK_STATE_FLAG_ACTIVE;
+#else
                        state = GTK_STATE_SELECTED;
                else
                        state = GTK_STATE_ACTIVE;
+#endif
                }
        else
                {
                if (gtk_widget_get_state(widget) == GTK_STATE_INSENSITIVE)
+#if GTK_CHECK_VERSION(3,0,0)
+                       state = GTK_STATE_FLAG_INSENSITIVE;
+               else
+                       state = GTK_STATE_FLAG_NORMAL;
+#else
                        state = GTK_STATE_INSENSITIVE;
                else
                        state = GTK_STATE_NORMAL;
+#endif
                }
 
 #if GTK_CHECK_VERSION(3,0,0)
@@ -824,11 +840,17 @@ gqv_cell_renderer_icon_render(GtkCellRenderer             *cell,
                        for (i = 0; i < cellicon->num_marks; i++)
                                {
 #if GTK_CHECK_VERSION(3,0,0)
+#if GTK_CHECK_VERSION(3,14,0)
+                               state &= ~(GTK_STATE_FLAG_CHECKED);
+
+                               if ((cellicon->marks & (1 << i)))
+                                       state |= GTK_STATE_FLAG_CHECKED;
+#else
                                state &= ~(GTK_STATE_FLAG_ACTIVE);
 
                                if ((cellicon->marks & (1 << i)))
                                        state |= GTK_STATE_FLAG_ACTIVE;
-
+#endif
                                cairo_save (cr);
 
                                cairo_rectangle(cr,
@@ -842,10 +864,35 @@ gqv_cell_renderer_icon_render(GtkCellRenderer             *cell,
 
                                gtk_style_context_add_class(context, GTK_STYLE_CLASS_CHECK);
 
+                               gtk_style_context_add_class(context, "marks");
+                               GtkStyleProvider *provider;
+                               provider = (GtkStyleProvider *)gtk_css_provider_new();
+                               gtk_css_provider_load_from_data(GTK_CSS_PROVIDER(provider),
+                                               ".marks {\n"
+                                               "border-color: #808080;\n"
+                                               "border-style: solid;\n"
+                                               "border-width: 1px;\n"
+                                               "border-radius: 0px;\n"
+                                               "}\n"
+                                               ,-1, NULL);
+                               gtk_style_context_add_provider(context, provider,
+                                                       GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
+
                                gtk_render_check(context, cr,
                                         pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
                                         pix_rect.y,
                                         TOGGLE_WIDTH, TOGGLE_WIDTH);
+                               gtk_render_frame(context, cr,
+                                        pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
+                                        pix_rect.y,
+                                        TOGGLE_WIDTH, TOGGLE_WIDTH);
+
+                               if (cellicon->focused && gtk_widget_has_focus(widget))
+                                       {
+                                       gtk_render_focus(context, cr,
+                                               pix_rect.x + i * TOGGLE_SPACING + (TOGGLE_WIDTH - TOGGLE_SPACING) / 2,
+                                               pix_rect.y, TOGGLE_WIDTH, TOGGLE_WIDTH);
+                                       }
                                gtk_style_context_restore(context);
                                cairo_restore(cr);
 
@@ -859,21 +906,18 @@ gqv_cell_renderer_icon_render(GtkCellRenderer             *cell,
 #endif
                                }
                        }
-                }
+               }
 
+#if !GTK_CHECK_VERSION(3,0,0)
        if (cellicon->focused && gtk_widget_has_focus(widget))
                {
-#if GTK_CHECK_VERSION(3,0,0)
-#else
                gtk_paint_focus(gtk_widget_get_style(widget), window,
                                state,
                                cell_area, widget,
                                "cellrendererfocus",
                                cell_area->x, cell_area->y,
                                cell_area->width, cell_area->height);
-#endif
                }
-#if !GTK_CHECK_VERSION(3,0,0)
        cairo_destroy(cr);
 #endif
 }
index 28879cc..695133a 100644 (file)
@@ -1178,8 +1178,8 @@ CollectWindow *collection_window_new(const gchar *path)
 
        if (options->save_window_positions && path && collection_load_only_geometry(cw->cd, path))
                {
-               /* FIXME: x, y is not implemented */
                gtk_window_set_default_size(GTK_WINDOW(cw->window), cw->cd->window_w, cw->cd->window_h);
+               gtk_window_move(GTK_WINDOW(cw->window), cw->cd->window_x, cw->cd->window_y);
                }
        else
                {
index 6134ad7..f44d993 100644 (file)
 #include "ui_fileops.h"
 
 #include <glib/gprintf.h>
+#include <regex.h>
 
 /*
  * Logging functions
  */
+static gchar *regexp = NULL;
 
 static gboolean log_msg_cb(gpointer data)
 {
@@ -46,6 +48,62 @@ static gboolean log_normal_cb(gpointer data)
        return FALSE;
 }
 
+void log_domain_print_message(const gchar *domain, gchar *buf)
+{
+       gchar *buf_nl;
+       regex_t regex;
+       gint ret_comp, ret_exec;
+
+       buf_nl = g_strconcat(buf, "\n", NULL);
+
+       if (regexp && command_line)
+               {
+                       ret_comp = regcomp(&regex, regexp, 0);
+                       if (!ret_comp)
+                               {
+                               ret_exec = regexec(&regex, buf_nl, 0, NULL, 0);
+
+                               if (!ret_exec)
+                                       {
+                                       print_term(buf_nl);
+                                       if (strcmp(domain, DOMAIN_INFO) == 0)
+                                               g_idle_add(log_normal_cb, buf_nl);
+                                       else
+                                               g_idle_add(log_msg_cb, buf_nl);
+                                       }
+                               regfree(&regex);
+                               }
+               }
+       else
+               {
+               print_term(buf_nl);
+               if (strcmp(domain, DOMAIN_INFO) == 0)
+                       g_idle_add(log_normal_cb, buf_nl);
+               else
+                       g_idle_add(log_msg_cb, buf_nl);
+               }
+       g_free(buf);
+}
+
+void log_domain_print_debug(const gchar *domain, const gchar *file_name,
+                                                                       int line_number, const gchar *format, ...)
+{
+       va_list ap;
+       gchar *message;
+       gchar *location;
+       gchar *buf;
+
+       va_start(ap, format);
+       message = g_strdup_vprintf(format, ap);
+       va_end(ap);
+
+       location = g_strdup_printf("%s:%d:", file_name, line_number);
+       buf = g_strconcat(location, message, NULL);
+       log_domain_print_message(domain,buf);
+       g_free(location);
+       g_free(message);
+}
+
 void log_domain_printf(const gchar *domain, const gchar *format, ...)
 {
        va_list ap;
@@ -55,12 +113,7 @@ void log_domain_printf(const gchar *domain, const gchar *format, ...)
        buf = g_strdup_vprintf(format, ap);
        va_end(ap);
 
-       print_term(buf);
-       if (strcmp(domain, DOMAIN_INFO) == 0)
-               g_idle_add(log_normal_cb, buf);
-       else
-               g_idle_add(log_msg_cb, buf);
-
+       log_domain_print_message(domain, buf);
 }
 
 /*
@@ -152,5 +205,15 @@ void init_exec_time(void)
        get_exec_time();
 }
 
+void set_regexp(gchar *cmd_regexp)
+{
+       regexp = g_strdup(cmd_regexp);
+}
+
+gchar *get_regexp(void)
+{
+       return g_strdup(regexp);
+}
+
 #endif /* DEBUG */
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index 733c9a1..d8a34b9 100644 (file)
@@ -27,6 +27,8 @@
 #define DOMAIN_INFO  "info"
 
 void log_domain_printf(const gchar *domain, const gchar *format, ...) G_GNUC_PRINTF(2, 3);
+void log_domain_print_debug(const gchar *domain, const gchar *file_name,
+                                                       int line_number, const gchar *format, ...) G_GNUC_PRINTF(4, 5);
 #define log_printf(...) log_domain_printf(DOMAIN_INFO, __VA_ARGS__)
 
 #ifdef DEBUG
@@ -34,6 +36,8 @@ void log_domain_printf(const gchar *domain, const gchar *format, ...) G_GNUC_PRI
 #define DEBUG_LEVEL_MIN 0
 #define DEBUG_LEVEL_MAX 4
 
+void set_regexp(gchar *regexp);
+gchar *get_regexp(void);
 gint get_debug_level(void);
 void set_debug_level(gint new_level);
 void debug_level_add(gint delta);
@@ -46,14 +50,21 @@ void init_exec_time(void);
                                gint debug_level = get_debug_level(); \
                                if (debug_level >= (n))         \
                                        {               \
-                                       if (debug_level != 1) log_domain_printf(DOMAIN_DEBUG, "%s:%d: ", __FILE__, __LINE__); \
-                                       log_domain_printf(DOMAIN_DEBUG, __VA_ARGS__); \
-                                       log_domain_printf(DOMAIN_DEBUG, "\n"); \
+                                       if (debug_level != 1) \
+                                       { \
+                                               log_domain_print_debug(DOMAIN_DEBUG, __FILE__, __LINE__, __VA_ARGS__); \
+                                       } \
+                                       else \
+                                               { \
+                                               log_domain_printf(DOMAIN_DEBUG, __VA_ARGS__); \
+                                               } \
                                        } \
                                } while (0)
 
 #else /* DEBUG */
 
+#define get_regexp() (0)
+#define set_regexp(regexp) do { } while(0)
 #define get_debug_level() (0)
 #define set_debug_level(new_level) do { } while(0)
 #define debug_level_add(delta) do { } while(0)
index 08c801b..932606e 100644 (file)
@@ -1066,6 +1066,20 @@ static void dupe_match_sort_groups(GList *list)
                }
 }
 
+static gint dupe_match_totals_sort_cb(gconstpointer a, gconstpointer b)
+{
+       DupeItem *da = (DupeItem *)a;
+       DupeItem *db = (DupeItem *)b;
+
+       if (g_list_length(da->group) > g_list_length(db->group)) return -1;
+       if (g_list_length(da->group) < g_list_length(db->group)) return 1;
+
+       if (da->group_rank < db->group_rank) return -1;
+       if (da->group_rank > db->group_rank) return 1;
+
+       return 0;
+}
+
 static gint dupe_match_rank_sort_cb(gconstpointer a, gconstpointer b)
 {
        DupeItem *da = (DupeItem *)a;
@@ -1099,6 +1113,15 @@ static GList *dupe_match_rank_sort(GList *source_list)
        return g_list_sort(list, dupe_match_rank_sort_cb);
 }
 
+/* returns allocated GList of dupes sorted by totals */
+static GList *dupe_match_totals_sort(GList *source_list)
+{
+       source_list = g_list_sort(source_list, dupe_match_totals_sort_cb);
+
+       source_list = g_list_first(source_list);
+       return g_list_reverse(source_list);
+}
+
 static void dupe_match_rank(DupeWindow *dw)
 {
        GList *list;
@@ -1116,6 +1139,11 @@ static void dupe_match_rank(DupeWindow *dw)
        if (required_debug_level(2)) dupe_match_print_list(list);
 
        list = dupe_match_rank_sort(list);
+       if (options->sort_totals)
+               {
+               list = dupe_match_totals_sort(list);
+               }
+       if (required_debug_level(2)) dupe_match_print_list(list);
 
        g_list_free(dw->dupes);
        dw->dupes = list;
@@ -2648,6 +2676,15 @@ static void dupe_second_set_toggle_cb(GtkWidget *widget, gpointer data)
        dupe_window_recompare(dw);
 }
 
+static void dupe_sort_totals_toggle_cb(GtkWidget *widget, gpointer data)
+{
+       DupeWindow *dw = data;
+
+       options->sort_totals = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
+       dupe_window_recompare(dw);
+
+}
+
 /*
  *-------------------------------------------------------------------
  * match type menu
@@ -3077,11 +3114,11 @@ static gboolean dupe_window_keypress_cb(GtkWidget *widget, GdkEventKey *event, g
                                        }
                                break;
                        case '1':
-                               options->duplicates_select_type == DUPE_SELECT_GROUP1;
+                               options->duplicates_select_type = DUPE_SELECT_GROUP1;
                                dupe_listview_select_dupes(dw, DUPE_SELECT_GROUP1);
                                break;
                        case '2':
-                               options->duplicates_select_type == DUPE_SELECT_GROUP2;
+                               options->duplicates_select_type = DUPE_SELECT_GROUP2;
                                dupe_listview_select_dupes(dw, DUPE_SELECT_GROUP2);
                                break;
                        case GDK_KEY_Menu:
@@ -3331,6 +3368,14 @@ DupeWindow *dupe_window_new()
        gtk_container_add(GTK_CONTAINER(frame), dw->status_label);
        gtk_widget_show(dw->status_label);
 
+       button = gtk_check_button_new_with_label(_("Sort"));
+       gtk_widget_set_tooltip_text(GTK_WIDGET(button), "Sort by group totals");
+       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), options->sort_totals);
+       g_signal_connect(G_OBJECT(button), "toggled",
+                        G_CALLBACK(dupe_sort_totals_toggle_cb), dw);
+       gtk_box_pack_start(GTK_BOX(status_box), button, FALSE, FALSE, PREF_PAD_SPACE);
+       gtk_widget_show(button);
+
        label = gtk_label_new(_("Custom Threshold"));
        gtk_box_pack_start(GTK_BOX(status_box), label, FALSE, FALSE, PREF_PAD_SPACE);
        gtk_widget_show(label);
index 0478466..872b9cc 100644 (file)
@@ -1105,6 +1105,18 @@ guchar *exif_get_color_profile(ExifData *exif, guint *data_len)
 }
 
 
+gchar* exif_get_image_comment(FileData* fd)
+{
+       log_printf("%s", _("Can't get image comment: not compiled with Exiv2.\n"));
+       return g_strdup("");
+}
+
+void exif_set_image_comment(FileData* fd, const gchar* comment)
+{
+       log_printf("%s", _("Can't set image comment: not compiled with Exiv2.\n"));
+}
+
+
 /*
  *-------------------------------------------------------------------
  * misc
index 804b805..06dec25 100644 (file)
@@ -158,6 +158,10 @@ void exif_add_jpeg_color_profile(ExifData *exif, guchar *cp_data, guint cp_lengt
 
 gboolean exif_jpeg_parse_color(ExifData *exif, guchar *data, guint size);
 
+/* support for so called "jpeg comment" */
+gchar* exif_get_image_comment(FileData* fd);
+void exif_set_image_comment(FileData* fd, const gchar* comment);
+
 /*raw support */
 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height);
 void exif_free_preview(guchar *buf);
index 49b8e07..0ad5c61 100644 (file)
@@ -158,6 +158,10 @@ struct _ExifData
        virtual void add_jpeg_color_profile(unsigned char *cp_data, guint cp_length) = 0;
 
        virtual guchar *get_jpeg_color_profile(guint *data_len) = 0;
+
+       virtual std::string image_comment() const = 0;
+
+       virtual void set_image_comment(const std::string& comment) = 0;
 };
 
 // This allows read-only access to the original metadata
@@ -280,6 +284,17 @@ public:
                }
                return NULL;
        }
+
+       virtual std::string image_comment() const
+       {
+               return image_.get() ? image_->comment() : "";
+       }
+
+       virtual void set_image_comment(const std::string& comment)
+       {
+               if (image_.get())
+                       image_->setComment(comment);
+       }
 };
 
 extern "C" {
@@ -415,6 +430,16 @@ public:
        {
                return imageData_->get_jpeg_color_profile(data_len);
        }
+
+       virtual std::string image_comment() const
+       {
+               return imageData_->image_comment();
+       }
+
+       virtual void set_image_comment(const std::string& comment)
+       {
+               imageData_->set_image_comment(comment);
+       }
 };
 
 
@@ -1133,6 +1158,23 @@ guchar *exif_get_color_profile(ExifData *exif, guint *data_len)
        return ret;
 }
 
+gchar* exif_get_image_comment(FileData* fd)
+{
+       if (!fd || !fd->exif)
+               return g_strdup("");
+
+       return g_strdup(fd->exif->image_comment().c_str());
+}
+
+void exif_set_image_comment(FileData* fd, const gchar* comment)
+{
+       if (!fd || !fd->exif)
+               return;
+
+       fd->exif->set_image_comment(comment ? comment : "");
+}
+
+
 #if EXIV2_TEST_VERSION(0,17,90)
 
 guchar *exif_get_preview(ExifData *exif, guint *data_len, gint requested_width, gint requested_height)
index 34b545a..b690e81 100644 (file)
@@ -427,6 +427,8 @@ static FileData *file_data_new(const gchar *path_utf8, struct stat *st, gboolean
        fd->mode = st->st_mode;
        fd->ref = 1;
        fd->magick = FD_MAGICK;
+       fd->exifdate = 0;
+       fd->rating = 0;
 
        if (disable_sidecars) fd->disable_grouping = TRUE;
 
@@ -465,21 +467,6 @@ FileData *file_data_new_simple(const gchar *path_utf8)
        return fd;
 }
 
-void init_exif_time_data(GList *files)
-{
-       FileData *file;
-       DEBUG_1("%s init_exif_time_data: ...", get_exec_time());
-       while (files)
-               {
-               file = files->data;
-
-               if (file)
-                       file->exifdate = 0;
-
-               files = files->next;
-               }
-}
-
 void read_exif_time_data(FileData *file)
 {
        if (file->exifdate > 0)
@@ -528,6 +515,24 @@ void set_exif_time_data(GList *files)
                }
 }
 
+void set_rating_data(GList *files)
+{
+       gchar *rating_str;
+       DEBUG_1("%s set_rating_data: ...", get_exec_time());
+
+       while (files)
+               {
+               FileData *file = files->data;
+               rating_str = metadata_read_string(file, RATING_KEY, METADATA_PLAIN);
+               if (rating_str )
+                       {
+                       file->rating = atoi(rating_str);
+                       g_free(rating_str);
+                       }
+               files = files->next;
+               }
+}
+
 FileData *file_data_new_no_grouping(const gchar *path_utf8)
 {
        struct stat st;
@@ -605,6 +610,7 @@ static void file_data_free(FileData *fd)
        g_free(fd->original_path);
        g_free(fd->collate_key_name);
        g_free(fd->collate_key_name_nocase);
+       g_free(fd->extended_extension);
        if (fd->thumb_pixbuf) g_object_unref(fd->thumb_pixbuf);
        histmap_free(fd->histmap);
 
@@ -932,6 +938,8 @@ static void file_data_disconnect_sidecar_file(FileData *target, FileData *sfd)
 
        target->sidecar_files = g_list_remove(target->sidecar_files, sfd);
        sfd->parent = NULL;
+       g_free(sfd->extended_extension);
+       sfd->extended_extension = NULL;
 
        file_data_unref(target);
        file_data_unref(sfd);
@@ -1037,6 +1045,11 @@ gint filelist_sort_compare_filedata(FileData *fa, FileData *fb)
                        if (fa->exifdate > fb->exifdate) return 1;
                        /* fall back to name */
                        break;
+               case SORT_RATING:
+                       if (fa->rating < fb->rating) return -1;
+                       if (fa->rating > fb->rating) return 1;
+                       /* fall back to name */
+                       break;
 #ifdef HAVE_STRVERSCMP
                case SORT_NUMBER:
                        ret = strverscmp(fa->name, fb->name);
@@ -1092,6 +1105,10 @@ GList *filelist_sort(GList *list, SortType method, gboolean ascend)
                {
                set_exif_time_data(list);
                }
+       if (method == SORT_RATING)
+               {
+               set_rating_data(list);
+               }
        return filelist_sort_full(list, method, ascend, (GCompareFunc) filelist_sort_file_cb);
 }
 
@@ -1119,6 +1136,37 @@ static GList * file_data_basename_hash_insert(GHashTable *basename_hash, FileDat
 
        list = g_hash_table_lookup(basename_hash, basename);
 
+       if (!list)
+               {
+               DEBUG_1("TG: basename_hash not found for %s",fd->path);
+               const gchar *parent_extension = registered_extension_from_path(basename);
+
+               if (parent_extension)
+                       {
+                       DEBUG_1("TG: parent extension %s",parent_extension);
+                       gchar *parent_basename = g_strndup(basename, parent_extension - basename);
+                       DEBUG_1("TG: parent basename %s",parent_basename);
+                       FileData *parent_fd = g_hash_table_lookup(file_data_pool, basename);
+                       if (parent_fd)
+                               {
+                               DEBUG_1("TG: parent fd found");
+                               list = g_hash_table_lookup(basename_hash, parent_basename);
+                               if (!g_list_find(list, parent_fd))
+                                       {
+                                       DEBUG_1("TG: parent fd doesn't fit");
+                                       g_free(parent_basename);
+                                       list = NULL;
+                                       }
+                               else
+                                       {
+                                       g_free(basename);
+                                       basename = parent_basename;
+                                       fd->extended_extension = g_strconcat(parent_extension, fd->extension, NULL);
+                                       }
+                               }
+                       }
+               }
+
        if (!g_list_find(list, fd))
                {
                list = g_list_insert_sorted(list, file_data_ref(fd), file_data_sort_by_ext);
@@ -1131,6 +1179,11 @@ static GList * file_data_basename_hash_insert(GHashTable *basename_hash, FileDat
        return list;
 }
 
+static void file_data_basename_hash_insert_cb(gpointer fd, gpointer basename_hash)
+{
+       file_data_basename_hash_insert((GHashTable *)basename_hash, (FileData *)fd);
+}
+
 static void file_data_basename_hash_remove_list(gpointer key, gpointer value, gpointer data)
 {
        filelist_free((GList *)value);
@@ -1195,6 +1248,7 @@ static gboolean filelist_read_real(const gchar *dir_path, GList **files, GList *
        gchar *pathl;
        GList *dlist = NULL;
        GList *flist = NULL;
+       GList *xmp_files = NULL;
        gint (*stat_func)(const gchar *path, struct stat *buf);
        GHashTable *basename_hash = NULL;
 
@@ -1252,7 +1306,10 @@ static gboolean filelist_read_real(const gchar *dir_path, GList **files, GList *
                                        flist = g_list_prepend(flist, fd);
                                        if (fd->sidecar_priority && !fd->disable_grouping)
                                                {
-                                               file_data_basename_hash_insert(basename_hash, fd);
+                                               if (strcmp(fd->extension, ".xmp") != 0)
+                                                       file_data_basename_hash_insert(basename_hash, fd);
+                                               else
+                                                       xmp_files = g_list_append(xmp_files, fd);
                                                }
                                        }
                                }
@@ -1271,6 +1328,12 @@ static gboolean filelist_read_real(const gchar *dir_path, GList **files, GList *
 
        g_free(pathl);
 
+       if (xmp_files)
+               {
+               g_list_foreach(xmp_files,file_data_basename_hash_insert_cb,basename_hash);
+               g_list_free(xmp_files);
+               }
+
        if (dirs) *dirs = dlist;
 
        if (files)
@@ -1281,9 +1344,6 @@ static gboolean filelist_read_real(const gchar *dir_path, GList **files, GList *
                }
        if (basename_hash) file_data_basename_hash_free(basename_hash);
 
-       // Call a separate function to initialize the exif datestamps for the found files..
-       if (files) init_exif_time_data(*files);
-
        return TRUE;
 }
 
@@ -1531,22 +1591,29 @@ gchar *file_data_get_sidecar_path(FileData *fd, gboolean existing_only)
        if (!file_data_can_write_sidecar(fd)) return NULL;
 
        work = fd->parent ? fd->parent->sidecar_files : fd->sidecar_files;
+       gchar *extended_extension = g_strconcat(fd->parent ? fd->parent->extension : fd->extension, ".xmp", NULL);
        while (work)
                {
                FileData *sfd = work->data;
                work = work->next;
-               if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0)
+               if (g_ascii_strcasecmp(sfd->extension, ".xmp") == 0 || g_ascii_strcasecmp(sfd->extension, extended_extension) == 0)
                        {
                        sidecar_path = g_strdup(sfd->path);
                        break;
                        }
                }
+       g_free(extended_extension);
 
        if (!existing_only && !sidecar_path)
                {
-               gchar *base = g_strndup(fd->path, fd->extension - fd->path);
-               sidecar_path = g_strconcat(base, ".xmp", NULL);
-               g_free(base);
+               if (options->metadata.sidecar_extended_name)
+                       sidecar_path = g_strconcat(fd->path, ".xmp", NULL);
+               else
+                       {
+                       gchar *base = g_strndup(fd->path, fd->extension - fd->path);
+                       sidecar_path = g_strconcat(base, ".xmp", NULL);
+                       g_free(base);
+                       }
                }
 
        return sidecar_path;
@@ -1670,7 +1737,7 @@ gboolean file_data_register_mark_func(gint n, FileDataGetMarkFunc get_mark_func,
         file_data_mark_func_data[n] = data;
         file_data_destroy_mark_func[n] = notify;
 
-        if (get_mark_func)
+       if (get_mark_func && file_data_pool)
                {
                /* this effectively changes all known files */
                g_hash_table_foreach(file_data_pool, file_data_notify_mark_func, NULL);
@@ -2095,11 +2162,11 @@ static void file_data_update_ci_dest(FileData *fd, const gchar *dest_path)
 
 static void file_data_update_ci_dest_preserve_ext(FileData *fd, const gchar *dest_path)
 {
-       const gchar *extension = extension_from_path(fd->change->source);
+       const gchar *extension = registered_extension_from_path(fd->change->source);
        gchar *base = remove_extension_from_path(dest_path);
        gchar *old_path = fd->change->dest;
 
-       fd->change->dest = g_strconcat(base, extension, NULL);
+       fd->change->dest = g_strconcat(base, fd->extended_extension ? fd->extended_extension : extension, NULL);
        file_data_update_planned_change_hash(fd, old_path, fd->change->dest);
 
        g_free(old_path);
@@ -2372,7 +2439,7 @@ gint file_data_verify_ci(FileData *fd, GList *list)
 
                if (!same)
                        {
-                       const gchar *dest_ext = extension_from_path(fd->change->dest);
+                       const gchar *dest_ext = registered_extension_from_path(fd->change->dest);
                        if (!dest_ext) dest_ext = "";
                        if (!options->file_filter.disable_file_extension_checks)
                                {
index bcc5666..7afe1a4 100644 (file)
@@ -336,6 +336,10 @@ static gchar *image_osd_mkinfo(const gchar *str, ImageWindow *imd, GHashTable *v
                        {
                        data = metadata_read_string(imd->image_fd, COMMENT_KEY, METADATA_PLAIN);
                        }
+               else if (strcmp(name, "imagecomment") == 0)
+                       {
+                       data = exif_get_image_comment(imd->image_fd);
+                       }
 #ifdef HAVE_LUA
                else if (strncmp(name, "lua/", 4) == 0)
                        {
index 2adb4f1..e04037f 100644 (file)
@@ -56,6 +56,10 @@ static void image_cache_set(ImageWindow *imd, FileData *fd);
 static void image_click_cb(PixbufRenderer *pr, GdkEventButton *event, gpointer data)
 {
        ImageWindow *imd = data;
+       if (!options->image_lm_click_nav && event->button == MOUSE_BUTTON_MIDDLE)
+               {
+               imd->mouse_wheel_mode = !imd->mouse_wheel_mode;
+               }
 
        if (imd->func_button)
                {
@@ -1739,6 +1743,7 @@ static void image_options_set(ImageWindow *imd)
                                        "window_limit_value", options->image.max_window_size,
                                        "autofit_limit", options->image.limit_autofit_size,
                                        "autofit_limit_value", options->image.max_autofit_size,
+                                       "enlargement_limit_value", options->image.max_enlargement_size,
 
                                        NULL);
 
@@ -1936,7 +1941,7 @@ ImageWindow *image_new(gboolean frame)
 
        image_set_selectable(imd, 0);
 
-       g_signal_connect(G_OBJECT(imd->pr), "clicked",
+       g_signal_connect(G_OBJECT(imd->pr), "button_press_event",
                         G_CALLBACK(image_click_cb), imd);
        g_signal_connect(G_OBJECT(imd->pr), "scroll_notify",
                         G_CALLBACK(image_scroll_notify_cb), imd);
index 35b0ca3..31b3170 100644 (file)
@@ -600,7 +600,8 @@ static void scroll_cb(ImageWindow *imd, GdkEventScroll *event, gpointer data)
 {
        ViewWindow *vw = data;
 
-       if (event->state & GDK_CONTROL_MASK)
+       if ((event->state & GDK_CONTROL_MASK) ||
+                               (imd->mouse_wheel_mode && !options->image_lm_click_nav))
                {
                switch (event->direction)
                        {
@@ -905,7 +906,9 @@ static ViewWindow *real_view_window_new(FileData *fd, GList *list, CollectionDat
        req_size.height = h;
        gtk_widget_size_allocate(GTK_WIDGET(vw->window), &req_size);
 
+#if !GTK_CHECK_VERSION(3,0,0)
        gtk_widget_set_size_request(vw->imd->pr, w, h);
+#endif
 
        gtk_widget_show(vw->window);
 
@@ -1271,8 +1274,9 @@ static GtkWidget *view_popup_menu(ViewWindow *vw)
                         G_CALLBACK(view_popup_menu_destroy_cb), editmenu_fd_list);
        item = submenu_add_edit(menu, NULL, G_CALLBACK(view_edit_cb), vw, editmenu_fd_list);
        menu_item_add_divider(item);
+#if !GTK_CHECK_VERSION(3,0,0)
        menu_item_add(item, _("Set as _wallpaper"), G_CALLBACK(view_wallpaper_cb), vw);
-
+#endif
        submenu_add_alter(menu, G_CALLBACK(view_alter_cb), vw);
 
        menu_item_add_stock(menu, _("View in _new window"), GTK_STOCK_NEW, G_CALLBACK(view_new_window_cb), vw);
index 83cafd1..692a2ae 100644 (file)
@@ -30,6 +30,7 @@
 #include "layout_config.h"
 #include "layout_image.h"
 #include "layout_util.h"
+#include "logwindow.h"
 #include "menu.h"
 #include "pixbuf-renderer.h"
 #include "pixbuf_util.h"
@@ -428,6 +429,113 @@ static GtkWidget *layout_sort_button(LayoutWindow *lw)
        return button;
 }
 
+static void layout_zoom_menu_cb(GtkWidget *widget, gpointer data)
+{
+       ZoomMode mode;
+
+       if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
+
+       mode = (ZoomMode)GPOINTER_TO_INT(data);
+       options->image.zoom_mode = mode;
+}
+
+static void layout_scroll_menu_cb(GtkWidget *widget, gpointer data)
+{
+       guint scroll_type;
+
+       if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
+
+       scroll_type = GPOINTER_TO_UINT(data);
+       options->image.scroll_reset_method = scroll_type;
+       image_options_sync();
+}
+
+static void layout_zoom_menu_hide_cb(GtkWidget *widget, gpointer data)
+{
+       /* destroy the menu */
+       g_object_unref(widget);
+}
+
+static void layout_zoom_button_press_cb(GtkWidget *widget, gpointer data)
+{
+       LayoutWindow *lw = data;
+       GtkWidget *menu;
+       GdkEvent *event;
+       guint32 etime;
+
+       menu = submenu_add_zoom(NULL, G_CALLBACK(layout_zoom_menu_cb),
+                       lw, FALSE, FALSE, TRUE, options->image.zoom_mode);
+
+       /* take ownership of menu */
+#ifdef GTK_OBJECT_FLOATING
+       /* GTK+ < 2.10 */
+       g_object_ref(G_OBJECT(menu));
+       gtk_object_sink(GTK_OBJECT(menu));
+#else
+       /* GTK+ >= 2.10 */
+       g_object_ref_sink(G_OBJECT(menu));
+#endif
+
+       menu_item_add_divider(menu);
+
+       menu_item_add_radio(menu, _("Scroll to top left corner"),
+                       GUINT_TO_POINTER(SCROLL_RESET_TOPLEFT),
+                       options->image.scroll_reset_method == SCROLL_RESET_TOPLEFT,
+                       G_CALLBACK(layout_scroll_menu_cb),
+                       GUINT_TO_POINTER(SCROLL_RESET_TOPLEFT));
+       menu_item_add_radio(menu, _("Scroll to image center"),
+                       GUINT_TO_POINTER(SCROLL_RESET_CENTER),
+                       options->image.scroll_reset_method == SCROLL_RESET_CENTER,
+                       G_CALLBACK(layout_scroll_menu_cb),
+                       GUINT_TO_POINTER(SCROLL_RESET_CENTER));
+       menu_item_add_radio(menu, _("Keep the region from previous image"),
+                       GUINT_TO_POINTER(SCROLL_RESET_NOCHANGE),
+                       options->image.scroll_reset_method == SCROLL_RESET_NOCHANGE,
+                       G_CALLBACK(layout_scroll_menu_cb),
+                       GUINT_TO_POINTER(SCROLL_RESET_NOCHANGE));
+
+       g_signal_connect(G_OBJECT(menu), "selection_done",
+                        G_CALLBACK(layout_zoom_menu_hide_cb), NULL);
+
+       event = gtk_get_current_event();
+       if (event)
+               {
+               etime = gdk_event_get_time(event);
+               gdk_event_free(event);
+               }
+       else
+               {
+               etime = 0;
+               }
+
+       gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, etime);
+}
+
+static GtkWidget *layout_zoom_button(LayoutWindow *lw, GtkWidget *box, gint size, gboolean expand)
+{
+       GtkWidget *button;
+       GtkWidget *frame;
+
+
+       frame = gtk_frame_new(NULL);
+       if (size) gtk_widget_set_size_request(frame, size, -1);
+       gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+
+       gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0);
+
+       gtk_widget_show(frame);
+
+       button = gtk_button_new_with_label("1:1");
+       g_signal_connect(G_OBJECT(button), "clicked",
+                        G_CALLBACK(layout_zoom_button_press_cb), lw);
+       gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+
+       gtk_container_add(GTK_CONTAINER(frame), button);
+       gtk_widget_show(button);
+
+       return button;
+}
+
 /*
  *-----------------------------------------------------------------------------
  * status bar
@@ -527,7 +635,7 @@ void layout_status_update_image(LayoutWindow *lw)
 
        if (!n)
                {
-               gtk_label_set_text(GTK_LABEL(lw->info_zoom), "");
+               gtk_button_set_label(GTK_BUTTON(lw->info_zoom), "");
                gtk_label_set_text(GTK_LABEL(lw->info_details), "");
                }
        else
@@ -536,7 +644,7 @@ void layout_status_update_image(LayoutWindow *lw)
                gchar *b;
 
                text = image_zoom_get_as_text(lw->image);
-               gtk_label_set_text(GTK_LABEL(lw->info_zoom), text);
+               gtk_button_set_label(GTK_BUTTON(lw->info_zoom), text);
                g_free(text);
 
                b = image_get_fd(lw->image) ? text_from_size(image_get_fd(lw->image)->size) : g_strdup("0");
@@ -666,8 +774,10 @@ static void layout_status_setup(LayoutWindow *lw, GtkWidget *box, gboolean small
        gtk_widget_show(toolbar_frame);
        gtk_widget_show(toolbar);
        gtk_box_pack_end(GTK_BOX(hbox), toolbar_frame, FALSE, FALSE, 0);
-       lw->info_zoom = layout_status_label(NULL, hbox, FALSE, ZOOM_LABEL_WIDTH, FALSE);
-       gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_zoom), _("Image zoom level"));
+       lw->info_zoom = layout_zoom_button(lw, hbox, ZOOM_LABEL_WIDTH, TRUE);
+       gtk_widget_set_tooltip_text(GTK_WIDGET(lw->info_zoom), _("Select zoom mode"));
+       gtk_widget_show(lw->info_zoom);
+
        if (small_format)
                {
                hbox = gtk_hbox_new(FALSE, 0);
@@ -1234,6 +1344,26 @@ gboolean layout_geometry_get_tools(LayoutWindow *lw, gint *x, gint *y, gint *w,
        return TRUE;
 }
 
+gboolean layout_geometry_get_log_window(LayoutWindow *lw, gint *x, gint *y,
+                                                                                                               gint *w, gint *h)
+{
+       GdkWindow *window;
+
+       if (!layout_valid(&lw)) return FALSE;
+
+       if (!lw->log_window)
+               {
+               return FALSE;
+               }
+
+       window = gtk_widget_get_window(lw->log_window);
+       gdk_window_get_root_origin(window, x, y);
+       *w = gdk_window_get_width(window);
+       *h = gdk_window_get_height(window);
+
+       return TRUE;
+}
+
 static void layout_tools_geometry_sync(LayoutWindow *lw)
 {
        layout_geometry_get_tools(lw, &lw->options.float_window.x, &lw->options.float_window.y,
@@ -2057,6 +2187,10 @@ void layout_sync_options_with_current_state(LayoutWindow *lw)
 
        g_free(lw->options.last_path);
        lw->options.last_path = g_strdup(layout_get_path(lw));
+
+       layout_geometry_get_log_window(lw, &lw->options.log_window.x, &lw->options.log_window.y,
+                                        &lw->options.log_window.w, &lw->options.log_window.h);
+
 }
 
 void layout_apply_options(LayoutWindow *lw, LayoutOptions *lop)
@@ -2330,6 +2464,12 @@ void layout_write_attributes(LayoutOptions *layout, GString *outstr, gint indent
        WRITE_NL(); WRITE_INT(*layout, image_overlay.histogram_channel);
        WRITE_NL(); WRITE_INT(*layout, image_overlay.histogram_mode);
 
+       WRITE_NL(); WRITE_INT(*layout, log_window.x);
+       WRITE_NL(); WRITE_INT(*layout, log_window.y);
+       WRITE_NL(); WRITE_INT(*layout, log_window.w);
+       WRITE_NL(); WRITE_INT(*layout, log_window.h);
+       WRITE_SEPARATOR();
+
        WRITE_NL(); WRITE_BOOL(*layout, animate);
 }
 
@@ -2402,6 +2542,11 @@ void layout_load_attributes(LayoutOptions *layout, const gchar **attribute_names
                if (READ_INT(*layout, image_overlay.histogram_channel)) continue;
                if (READ_INT(*layout, image_overlay.histogram_mode)) continue;
 
+               if (READ_INT(*layout, log_window.x)) continue;
+               if (READ_INT(*layout, log_window.y)) continue;
+               if (READ_INT(*layout, log_window.w)) continue;
+               if (READ_INT(*layout, log_window.h)) continue;
+
                if (READ_BOOL(*layout, animate)) continue;
 
                log_printf("unknown attribute %s = %s\n", option, value);
@@ -2486,7 +2631,7 @@ LayoutWindow *layout_new_from_config(const gchar **attribute_names, const gchar
 
        if (use_commandline && command_line->startup_full_screen) layout_image_full_screen_start(lw);
        if (use_commandline && command_line->startup_in_slideshow) layout_image_slideshow_start(lw);
-
+       if (use_commandline && command_line->log_window_show) log_window_new(lw);
 
        g_free(path);
        free_layout_options_content(&lop);
index 63da73b..8289cb7 100644 (file)
@@ -668,8 +668,9 @@ static GtkWidget *layout_image_pop_menu(LayoutWindow *lw)
        submenu = submenu_add_edit(menu, &item, G_CALLBACK(li_pop_menu_edit_cb), lw, editmenu_fd_list);
        if (!path) gtk_widget_set_sensitive(item, FALSE);
        menu_item_add_divider(submenu);
+#if !GTK_CHECK_VERSION(3,0,0)
        menu_item_add(submenu, _("Set as _wallpaper"), G_CALLBACK(li_pop_menu_wallpaper_cb), lw);
-
+#endif
        item = submenu_add_alter(menu, G_CALLBACK(li_pop_menu_alter_cb), lw);
 
        item = menu_item_add_stock(menu, _("View in _new window"), GTK_STOCK_NEW, G_CALLBACK(li_pop_menu_new_cb), lw);
@@ -1007,12 +1008,12 @@ void layout_image_zoom_adjust_at_point(LayoutWindow *lw, gdouble increment, gint
                {
                image_zoom_adjust_at_point(lw->full_screen->imd, increment, x, y);
                }
-
-       if (!connect_zoom) return;
+       if (!connect_zoom && !lw->split_mode) return;
 
        for (i = 0; i < MAX_SPLIT_IMAGES; i++)
                {
-               if (lw->split_images[i] && lw->split_images[i] != lw->image)
+               if (lw->split_images[i] && lw->split_images[i] != lw->image &&
+                                               lw->split_images[i]->mouse_wheel_mode)
                        image_zoom_adjust_at_point(lw->split_images[i], increment, x, y);
                }
 }
@@ -1069,7 +1070,6 @@ void layout_image_alter_orientation(LayoutWindow *lw, AlterType type)
        GtkTreePath *tpath;
        FileData *fd_n;
        GtkTreeIter iter;
-       IconData *id;
 
        if (!lw || !lw->vf) return;
 
@@ -1088,8 +1088,7 @@ void layout_image_alter_orientation(LayoutWindow *lw, AlterType type)
                {
                if (lw->vf->type == FILEVIEW_ICON)
                        {
-                       id = work->data;
-                       fd_n = id->fd;
+                       fd_n = work->data;
                        work = work->next;
                        }
                else
@@ -1104,6 +1103,54 @@ void layout_image_alter_orientation(LayoutWindow *lw, AlterType type)
                }
 }
 
+static void image_alter_rating(FileData *fd_n, const gchar *rating)
+{
+       metadata_write_string(fd_n, RATING_KEY, rating);
+}
+
+void layout_image_rating(LayoutWindow *lw, const gchar *rating)
+{
+       if (!layout_valid(&lw)) return;
+
+       GtkTreeModel *store;
+       GList *work;
+       GtkTreeSelection *selection;
+       GtkTreePath *tpath;
+       FileData *fd_n;
+       GtkTreeIter iter;
+
+       if (!lw || !lw->vf) return;
+
+       if (lw->vf->type == FILEVIEW_ICON)
+               {
+               if (!VFICON(lw->vf)->selection) return;
+               work = VFICON(lw->vf)->selection;
+               }
+       else
+               {
+               selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(lw->vf->listview));
+               work = gtk_tree_selection_get_selected_rows(selection, &store);
+               }
+
+       while (work)
+               {
+               if (lw->vf->type == FILEVIEW_ICON)
+                       {
+                       fd_n = work->data;
+                       work = work->next;
+                       }
+               else
+                       {
+                       tpath = work->data;
+                       gtk_tree_model_get_iter(store, &iter, tpath);
+                       gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
+                       work = work->next;
+                       }
+
+               image_alter_rating(fd_n, rating);
+               }
+}
+
 void layout_image_reset_orientation(LayoutWindow *lw)
 {
        ImageWindow *imd= lw->image;
@@ -1639,6 +1686,11 @@ static void layout_image_button_cb(ImageWindow *imd, GdkEventButton *event, gpoi
        switch (event->button)
                {
                case MOUSE_BUTTON_LEFT:
+                       if (event->type == GDK_2BUTTON_PRESS)
+                               {
+                               if (lw->full_screen)
+                                       layout_image_full_screen_stop(lw);
+                               }
                        if (options->image_lm_click_nav && lw->split_mode == SPLIT_NONE)
                                layout_image_next(lw);
                        break;
@@ -1672,7 +1724,8 @@ static void layout_image_scroll_cb(ImageWindow *imd, GdkEventScroll *event, gpoi
                }
 
 
-       if (event->state & GDK_CONTROL_MASK)
+       if ((event->state & GDK_CONTROL_MASK) ||
+                               (imd->mouse_wheel_mode && !options->image_lm_click_nav))
                {
                switch (event->direction)
                        {
@@ -1999,16 +2052,19 @@ static void layout_image_setup_split_common(LayoutWindow *lw, gint n)
                                GList *work = g_list_last(layout_selection_list(lw));
                                gint j = 0;
 
-                               if (work) work = work->prev;
-
                                while (work && j < i)
                                        {
                                        FileData *fd = work->data;
                                        work = work->prev;
 
-                                       j++;
-                                       if (!fd || !*fd->path) continue;
+                                       if (!fd || !*fd->path || fd->parent ||
+                                                                               fd == lw->split_images[0]->image_fd)
+                                               {
+                                               continue;
+                                               }
                                        img_fd = fd;
+
+                                       j++;
                                        }
                                }
 
index 54c2822..347c547 100644 (file)
@@ -64,6 +64,8 @@ void layout_image_alter_orientation(LayoutWindow *lw, AlterType type);
 void layout_image_set_desaturate(LayoutWindow *lw, gboolean desaturate);
 gboolean layout_image_get_desaturate(LayoutWindow *lw);
 
+void layout_image_rating(LayoutWindow *lw, const gchar *rating);
+
 /*
 gint layout_image_stereo_get(LayoutWindow *lw);
 void layout_image_stereo_set(LayoutWindow *lw, gint stereo_mode);
index 65fc1d5..106f8dc 100644 (file)
@@ -51,6 +51,7 @@
 #include "ui_tabcomp.h"
 #include "utilops.h"
 #include "view_dir.h"
+#include "view_file.h"
 #include "window.h"
 #include "metadata.h"
 #include "desktop_file.h"
 #include "keymap_template.c"
 
 #define MENU_EDIT_ACTION_OFFSET 16
+#define FILE_COLUMN_POINTER 0
 
 static gboolean layout_bar_enabled(LayoutWindow *lw);
 static gboolean layout_bar_sort_enabled(LayoutWindow *lw);
+static void layout_bars_hide_toggle(LayoutWindow *lw);
 static void layout_util_sync_views(LayoutWindow *lw);
 
 /*
@@ -352,6 +355,55 @@ static void layout_menu_alter_90_cb(GtkAction *action, gpointer data)
        layout_image_alter_orientation(lw, ALTER_ROTATE_90);
 }
 
+static void layout_menu_rating_0_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_rating(lw, "0");
+}
+
+static void layout_menu_rating_1_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_rating(lw, "1");
+}
+
+static void layout_menu_rating_2_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_rating(lw, "2");
+}
+
+static void layout_menu_rating_3_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_rating(lw, "3");
+}
+
+static void layout_menu_rating_4_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_rating(lw, "4");
+}
+
+static void layout_menu_rating_5_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_rating(lw, "5");
+}
+
+static void layout_menu_rating_m1_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_image_rating(lw, "-1");
+}
+
 static void layout_menu_alter_90cc_cb(GtkAction *action, gpointer data)
 {
        LayoutWindow *lw = data;
@@ -402,6 +454,99 @@ static void layout_menu_exif_rotate_cb(GtkToggleAction *action, gpointer data)
        layout_image_reset_orientation(lw);
 }
 
+static void layout_menu_write_rotate(GtkToggleAction *action, gpointer data, gboolean keep_date)
+{
+       LayoutWindow *lw = data;
+       GtkTreeModel *store;
+       GList *work;
+       GtkTreeSelection *selection;
+       GtkTreePath *tpath;
+       FileData *fd_n;
+       GtkTreeIter iter;
+       gchar *rotation;
+       gchar *command;
+       gint run_result;
+       GenericDialog *gd;
+       GString *message;
+
+       if (!layout_valid(&lw)) return;
+
+       if (!lw || !lw->vf) return;
+
+       if (lw->vf->type == FILEVIEW_ICON)
+               {
+               if (!VFICON(lw->vf)->selection) return;
+               work = VFICON(lw->vf)->selection;
+               }
+       else
+               {
+               selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(lw->vf->listview));
+               work = gtk_tree_selection_get_selected_rows(selection, &store);
+               }
+
+       while (work)
+               {
+               if (lw->vf->type == FILEVIEW_ICON)
+                       {
+                       fd_n = work->data;
+                       work = work->next;
+                       }
+               else
+                       {
+                       tpath = work->data;
+                       gtk_tree_model_get_iter(store, &iter, tpath);
+                       gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &fd_n, -1);
+                       work = work->next;
+                       }
+
+               rotation = g_strdup_printf("%d", fd_n->user_orientation);
+               command = g_strconcat(GQ_BIN_DIR, "/geeqie-rotate -r ", rotation,
+                                                                                                       keep_date ? " -t " : " ", fd_n->path, NULL);
+
+               run_result = WEXITSTATUS(runcmd(command));
+               if (!run_result)
+                       {
+                       fd_n->user_orientation = 0;
+                       }
+               else
+                       {
+                       message = g_string_new("");
+                       message = g_string_append(message, _("Operation failed:\n"));
+
+                       if (run_result == 3)
+                               message = g_string_append(message, _("Cannot create tmp file"));
+                       else
+                               {
+                               message = g_string_append(message, _("File: "));
+                               message = g_string_append(message, fd_n->name);
+                               }
+
+                       gd = generic_dialog_new(_("Image orientation"),
+                       "Image orientation", NULL, TRUE, NULL, NULL);
+                       generic_dialog_add_message(gd, GTK_STOCK_DIALOG_ERROR,
+                       "Image orientation", message->str);
+                       generic_dialog_add_button(gd, GTK_STOCK_OK, NULL, NULL, TRUE);
+
+                       gtk_widget_show(gd->dialog);
+
+                       g_string_free(message, TRUE);
+                       }
+
+               g_free(rotation);
+               g_free(command);
+               }
+}
+
+static void layout_menu_write_rotate_keep_date_cb(GtkToggleAction *action, gpointer data)
+{
+       layout_menu_write_rotate(action, data, TRUE);
+}
+
+static void layout_menu_write_rotate_cb(GtkToggleAction *action, gpointer data)
+{
+       layout_menu_write_rotate(action, data, FALSE);
+}
+
 static void layout_menu_config_cb(GtkAction *action, gpointer data)
 {
        LayoutWindow *lw = data;
@@ -637,12 +782,20 @@ static void layout_menu_list_cb(GtkRadioAction *action, GtkRadioAction *current,
        layout_views_set(lw, lw->options.dir_view_type, (FileViewType) gtk_radio_action_get_current_value(action));
 }
 
-static void layout_menu_view_dir_as_cb(GtkRadioAction *action, GtkRadioAction *current, gpointer data)
+static void layout_menu_view_dir_as_cb(GtkToggleAction *action,  gpointer data)
 {
        LayoutWindow *lw = data;
 
        layout_exit_fullscreen(lw);
-       layout_views_set(lw, (DirViewType) gtk_radio_action_get_current_value(action), lw->options.file_view_type);
+
+       if (gtk_toggle_action_get_active(action))
+               {
+               layout_views_set(lw, DIRVIEW_TREE, lw->options.file_view_type);
+               }
+       else
+               {
+               layout_views_set(lw, DIRVIEW_LIST, lw->options.file_view_type);
+               }
 }
 
 static void layout_menu_view_in_new_window_cb(GtkAction *action, gpointer data)
@@ -842,6 +995,13 @@ static void layout_menu_bar_sort_cb(GtkToggleAction *action, gpointer data)
        layout_bar_sort_toggle(lw);
 }
 
+static void layout_menu_hide_bars_cb(GtkToggleAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+
+       layout_bars_hide_toggle(lw);
+}
+
 static void layout_menu_slideshow_cb(GtkToggleAction *action, gpointer data)
 {
        LayoutWindow *lw = data;
@@ -1082,7 +1242,7 @@ static void layout_menu_log_window_cb(GtkAction *action, gpointer data)
        LayoutWindow *lw = data;
 
        layout_exit_fullscreen(lw);
-       log_window_new();
+       log_window_new(lw);
 }
 
 
@@ -1219,6 +1379,64 @@ static void layout_menu_image_next_cb(GtkAction *action, gpointer data)
        layout_image_next(lw);
 }
 
+static void layout_menu_split_pane_next_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+       gint active_frame;
+
+       active_frame = lw->active_split_image;
+
+       if (active_frame < MAX_SPLIT_IMAGES-1 && lw->split_images[active_frame+1] )
+               {
+               active_frame++;
+               }
+       else
+               {
+               active_frame = 0;
+               }
+       layout_image_activate(lw, active_frame, FALSE);
+}
+
+static void layout_menu_split_pane_prev_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+       gint active_frame;
+
+       active_frame = lw->active_split_image;
+
+       if (active_frame >=1 && lw->split_images[active_frame-1] )
+               {
+               active_frame--;
+               }
+       else
+               {
+               active_frame = MAX_SPLIT_IMAGES-1;
+               while (!lw->split_images[active_frame])
+                       {
+                       active_frame--;
+                       }
+               }
+       layout_image_activate(lw, active_frame, FALSE);
+}
+
+static void layout_menu_split_pane_updown_cb(GtkAction *action, gpointer data)
+{
+       LayoutWindow *lw = data;
+       gint active_frame;
+
+       active_frame = lw->active_split_image;
+
+       if (lw->split_images[MAX_SPLIT_IMAGES-1] )
+               {
+               active_frame = active_frame ^ 2;
+               }
+       else
+               {
+               active_frame = active_frame ^ 1;
+               }
+       layout_image_activate(lw, active_frame, FALSE);
+}
+
 static void layout_menu_image_last_cb(GtkAction *action, gpointer data)
 {
        LayoutWindow *lw = data;
@@ -1476,6 +1694,7 @@ static GtkActionEntry menu_entries[] = {
   { "EditMenu",                NULL,                   N_("_Edit"),                            NULL,                   NULL,                                   NULL },
   { "SelectMenu",      NULL,                   N_("_Select"),                          NULL,                   NULL,                                   NULL },
   { "OrientationMenu", NULL,                   N_("_Orientation"),                     NULL,                   NULL,                                   NULL },
+  { "RatingMenu",      NULL,                   N_("_Rating"),                                  NULL,                   NULL,                                   NULL },
   { "ExternalMenu",    NULL,                   N_("E_xternal Editors"),                NULL,                   NULL,                                   NULL },
   { "PreferencesMenu", NULL,                   N_("P_references"),                     NULL,                   NULL,                                   NULL },
   { "ViewMenu",                NULL,                   N_("_View"),                            NULL,                   NULL,                                   NULL },
@@ -1521,6 +1740,13 @@ static GtkActionEntry menu_entries[] = {
   { "CloseWindow",     GTK_STOCK_CLOSE,        N_("C_lose window"),                    "<control>W",           N_("Close window"),                     CB(layout_menu_close_cb) },
   { "Quit",            GTK_STOCK_QUIT,         N_("_Quit"),                            "<control>Q",           N_("Quit"),                             CB(layout_menu_exit_cb) },
   { "RotateCW",                NULL,                   N_("_Rotate clockwise"),                "bracketright",         N_("Rotate clockwise"),                 CB(layout_menu_alter_90_cb) },
+  { "Rating0",         NULL,                   N_("_Rating 0"),        "<alt>KP_0",    N_("Rating 0"),                 CB(layout_menu_rating_0_cb) },
+  { "Rating1",         NULL,                   N_("_Rating 1"),        "<alt>KP_1",    N_("Rating 1"),                 CB(layout_menu_rating_1_cb) },
+  { "Rating2",         NULL,                   N_("_Rating 2"),        "<alt>KP_2",    N_("Rating 2"),                 CB(layout_menu_rating_2_cb) },
+  { "Rating3",         NULL,                   N_("_Rating 3"),        "<alt>KP_3",    N_("Rating 3"),                 CB(layout_menu_rating_3_cb) },
+  { "Rating4",         NULL,                   N_("_Rating 4"),        "<alt>KP_4",    N_("Rating 4"),                 CB(layout_menu_rating_4_cb) },
+  { "Rating5",         NULL,                   N_("_Rating 5"),        "<alt>KP_5",    N_("Rating 5"),                 CB(layout_menu_rating_5_cb) },
+  { "RatingM1",                NULL,                   N_("_Rating -1"),       "<alt>KP_Subtract",     N_("Rating -1"),        CB(layout_menu_rating_m1_cb) },
   { "RotateCCW",       NULL,                   N_("Rotate _counterclockwise"),         "bracketleft",          N_("Rotate counterclockwise"),          CB(layout_menu_alter_90cc_cb) },
   { "Rotate180",       NULL,                   N_("Rotate 1_80"),                      "<shift>R",             N_("Rotate 180"),                       CB(layout_menu_alter_180_cb) },
   { "Mirror",          NULL,                   N_("_Mirror"),                          "<shift>M",             N_("Mirror"),                           CB(layout_menu_alter_mirror_cb) },
@@ -1587,6 +1813,12 @@ static GtkActionEntry menu_entries[] = {
   { "LogWindow",       NULL,                   N_("_Log Window"),                      NULL,                   N_("Log Window"),                       CB(layout_menu_log_window_cb) },
   { "ExifWin",         NULL,                   N_("_Exif window"),                     "<control>E",           N_("Exif window"),                      CB(layout_menu_bar_exif_cb) },
   { "StereoCycle",     NULL,                   N_("_Cycle through stereo modes"),      NULL,                   N_("Cycle through stereo modes"),       CB(layout_menu_stereo_mode_next_cb) },
+  { "SplitNextPane",   NULL,                   N_("_Next Pane"),       "<alt>Right",                   N_("Next Pane"),        CB(layout_menu_split_pane_next_cb) },
+  { "SplitPreviousPane",       NULL,                   N_("_Previous Pane"),   "<alt>Left",                    N_("Previous Pane"),    CB(layout_menu_split_pane_prev_cb) },
+  { "SplitUpPane",     NULL,                   N_("_Up Pane"), "<alt>Up",                      N_("Up Pane"),  CB(layout_menu_split_pane_updown_cb) },
+  { "SplitDownPane",   NULL,                   N_("_Down Pane"),       "<alt>Down",                    N_("Down Pane"),        CB(layout_menu_split_pane_updown_cb) },
+  { "WriteRotation",   NULL,                   N_("_Write orientation to file"),               NULL,           N_("Write orientation to file"),                        CB(layout_menu_write_rotate_cb) },
+  { "WriteRotationKeepDate",   NULL,                   N_("_Write orientation to file (preserve timestamp)"),                  NULL,           N_("Write orientation to file (preserve timestamp)"),                   CB(layout_menu_write_rotate_keep_date_cb) },
 
 };
 
@@ -1598,6 +1830,7 @@ static GtkToggleActionEntry menu_toggle_entries[] = {
   { "HideToolbar",     NULL,                   N_("Hide tool_bar"),                    NULL,                   N_("Hide toolbar"),                     CB(layout_menu_toolbar_cb),      FALSE  },
   { "SBar",            NULL,                   N_("_Info sidebar"),                    "<control>K",           N_("Info sidebar"),                     CB(layout_menu_bar_cb),          FALSE  },
   { "SBarSort",                NULL,                   N_("Sort _manager"),                    "<shift>S",             N_("Sort manager"),                     CB(layout_menu_bar_sort_cb),     FALSE  },
+  { "HideBars",                NULL,                   N_("Hide Bars"),                        "grave",                N_("Hide Bars"),                        CB(layout_menu_hide_bars_cb),    FALSE  },
   { "SlideShow",       GTK_STOCK_MEDIA_PLAY,   N_("Toggle _slideshow"),                "S",                    N_("Toggle slideshow"),                 CB(layout_menu_slideshow_cb),    FALSE  },
   { "UseColorProfiles",        GTK_STOCK_SELECT_COLOR, N_("Use _color profiles"),              NULL,                   N_("Use color profiles"),               CB(layout_color_menu_enable_cb), FALSE},
   { "UseImageProfile", NULL,                   N_("Use profile from _image"),          NULL,                   N_("Use profile from image"),           CB(layout_color_menu_use_image_cb), FALSE},
@@ -1614,9 +1847,8 @@ static GtkRadioActionEntry menu_radio_entries[] = {
   { "ViewIcons",       NULL,                   N_("I_cons"),                           "<control>I",           N_("View Images as Icons"),             FILEVIEW_ICON }
 };
 
-static GtkRadioActionEntry menu_view_dir_radio_entries[] = {
-  { "FolderList",      NULL,                   N_("Folder Li_st"),                     "<meta>L",              N_("View Folders as List"),             DIRVIEW_LIST },
-  { "FolderTree",      NULL,                   N_("Folder T_ree"),                     "<control>T",           N_("View Folders as Tree"),             DIRVIEW_TREE },
+static GtkToggleActionEntry menu_view_dir_toggle_entries[] = {
+  { "FolderTree",      NULL,                   N_("T_oggle Folder View"),                      "<control>T",           N_("Toggle Folders View"),              CB(layout_menu_view_dir_as_cb),FALSE },
 };
 
 static GtkRadioActionEntry menu_split_radio_entries[] = {
@@ -1727,6 +1959,19 @@ static const gchar *menu_ui_description =
 "        <separator/>"
 "        <menuitem action='ExifRotate'/>"
 "        <separator/>"
+"        <menuitem action='WriteRotation'/>"
+"        <menuitem action='WriteRotationKeepDate'/>"
+"        <separator/>"
+"      </menu>"
+"      <menu action='RatingMenu'>"
+"        <menuitem action='Rating0'/>"
+"        <menuitem action='Rating1'/>"
+"        <menuitem action='Rating2'/>"
+"        <menuitem action='Rating3'/>"
+"        <menuitem action='Rating4'/>"
+"        <menuitem action='Rating5'/>"
+"        <menuitem action='RatingM1'/>"
+"        <separator/>"
 "      </menu>"
 "      <menuitem action='SaveMetadata'/>"
 "      <placeholder name='PropertiesSection'/>"
@@ -1739,7 +1984,9 @@ static const gchar *menu_ui_description =
 "      </menu>"
 "      <placeholder name='PreferencesSection'/>"
 "      <separator/>"
+#if !GTK_CHECK_VERSION(3,0,0)
 "      <menuitem action='Wallpaper'/>"
+#endif
 "      <separator/>"
 "    </menu>"
 "    <menu action='ViewMenu'>"
@@ -1749,7 +1996,6 @@ static const gchar *menu_ui_description =
 "      <placeholder name='WindowSection'/>"
 "      <separator/>"
 "      <menu action='FileDirMenu'>"
-"        <menuitem action='FolderList'/>"
 "        <menuitem action='FolderTree'/>"
 "        <placeholder name='FolderSection'/>"
 "        <separator/>"
@@ -1797,6 +2043,11 @@ static const gchar *menu_ui_description =
 "        <menuitem action='SplitVertical'/>"
 "        <menuitem action='SplitQuad'/>"
 "        <menuitem action='SplitSingle'/>"
+"        <separator/>"
+"        <menuitem action='SplitNextPane'/>"
+"        <menuitem action='SplitPreviousPane'/>"
+"        <menuitem action='SplitUpPane'/>"
+"        <menuitem action='SplitDownPane'/>"
 "      </menu>"
 "      <menu action='StereoMenu'>"
 "        <menuitem action='StereoAuto'/>"
@@ -1839,6 +2090,7 @@ static const gchar *menu_ui_description =
 "      <separator/>"
 "      <menuitem action='SBar'/>"
 "      <menuitem action='SBarSort'/>"
+"      <menuitem action='HideBars'/>"
 "      <menuitem action='ShowInfoPixel'/>"
 "      <placeholder name='ToolsSection'/>"
 "      <separator/>"
@@ -2175,9 +2427,9 @@ void layout_actions_setup(LayoutWindow *lw)
        gtk_action_group_add_radio_actions(lw->action_group,
                                           menu_split_radio_entries, G_N_ELEMENTS(menu_split_radio_entries),
                                           0, G_CALLBACK(layout_menu_split_cb), lw);
-       gtk_action_group_add_radio_actions(lw->action_group,
-                                          menu_view_dir_radio_entries, DIRVIEW_LAST + 1 /* count */,
-                                          0, G_CALLBACK(layout_menu_view_dir_as_cb), lw);
+       gtk_action_group_add_toggle_actions(lw->action_group,
+                                          menu_view_dir_toggle_entries, G_N_ELEMENTS(menu_view_dir_toggle_entries),
+                                           lw);
        gtk_action_group_add_radio_actions(lw->action_group,
                                           menu_color_radio_entries, COLOR_PROFILE_FILE + COLOR_PROFILE_INPUTS,
                                           0, G_CALLBACK(layout_color_menu_input_cb), lw);
@@ -2453,11 +2705,20 @@ static void layout_util_sync_views(LayoutWindow *lw)
        if (!lw->action_group) return;
 
        action = gtk_action_group_get_action(lw->action_group, "FolderTree");
-       gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), lw->options.dir_view_type);
+       gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(action), lw->options.dir_view_type);
 
        action = gtk_action_group_get_action(lw->action_group, "SplitSingle");
        gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), lw->split_mode);
 
+       action = gtk_action_group_get_action(lw->action_group, "SplitNextPane");
+       gtk_action_set_sensitive(action, !(lw->split_mode == SPLIT_NONE));
+       action = gtk_action_group_get_action(lw->action_group, "SplitPreviousPane");
+       gtk_action_set_sensitive(action, !(lw->split_mode == SPLIT_NONE));
+       action = gtk_action_group_get_action(lw->action_group, "SplitUpPane");
+       gtk_action_set_sensitive(action, !(lw->split_mode == SPLIT_NONE));
+       action = gtk_action_group_get_action(lw->action_group, "SplitDownPane");
+       gtk_action_set_sensitive(action, !(lw->split_mode == SPLIT_NONE));
+
        action = gtk_action_group_get_action(lw->action_group, "ViewIcons");
        gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), lw->options.file_view_type);
 
@@ -2509,6 +2770,13 @@ static void layout_util_sync_views(LayoutWindow *lw)
        action = gtk_action_group_get_action(lw->action_group, "ConnectZoomMenu");
        gtk_action_set_sensitive(action, lw->split_mode != SPLIT_NONE);
 
+       action = gtk_action_group_get_action(lw->action_group, "WriteRotation");
+       gtk_action_set_sensitive(action, !(runcmd("which exiftran >/dev/null") ||
+                                                       runcmd("which mogrify >/dev/null") || options->metadata.write_orientation));
+       action = gtk_action_group_get_action(lw->action_group, "WriteRotationKeepDate");
+       gtk_action_set_sensitive(action, !(runcmd("which exiftran >/dev/null") ||
+                                                       runcmd("which mogrify >/dev/null") || options->metadata.write_orientation));
+
        action = gtk_action_group_get_action(lw->action_group, "StereoAuto");
        gtk_radio_action_set_current_value(GTK_RADIO_ACTION(action), layout_image_stereo_pixbuf_get(lw));
 
@@ -2706,6 +2974,39 @@ void layout_bar_sort_toggle(LayoutWindow *lw)
        layout_util_sync_views(lw);
 }
 
+static void layout_bars_hide_toggle(LayoutWindow *lw)
+{
+       if (lw->options.bars_state.hidden)
+               {
+               lw->options.bars_state.hidden = FALSE;
+               if (lw->options.bars_state.sort)
+                       {
+                       gtk_widget_show(lw->bar_sort);
+                       }
+               if (lw->options.bars_state.info)
+                       {
+                       gtk_widget_show(lw->bar);
+                       }
+               layout_tools_float_set(lw, lw->options.tools_float,
+                                                                       lw->options.bars_state.tools_hidden);
+               }
+       else
+               {
+               lw->options.bars_state.hidden = TRUE;
+               lw->options.bars_state.sort = layout_bar_sort_enabled(lw);
+               lw->options.bars_state.info = layout_bar_enabled(lw);
+               lw->options.bars_state.tools_float = lw->options.tools_float;
+               lw->options.bars_state.tools_hidden = lw->options.tools_hidden;
+
+               gtk_widget_hide(lw->bar);
+               if (lw->bar_sort)
+                       gtk_widget_hide(lw->bar_sort);
+               layout_tools_float_set(lw, lw->options.tools_float, TRUE);
+               }
+
+       layout_util_sync_views(lw);
+}
+
 void layout_bars_new_image(LayoutWindow *lw)
 {
        layout_bar_new_image(lw);
index e70e9e4..14f41b4 100644 (file)
@@ -22,6 +22,8 @@
 #include "logwindow.h"
 
 #include "misc.h"
+#include "secure_save.h"
+#include "ui_misc.h"
 #include "window.h"
 
 #include <gdk/gdkkeysyms.h>
@@ -38,6 +40,11 @@ struct _LogWindow
        GdkColor colors[LOG_COUNT];
 
        guint lines;
+       GtkWidget *regexp_box;
+       GtkWidget *bar;
+       GtkWidget *pause;
+       GtkWidget *wrap;
+       GtkWidget *debug_level;
 };
 
 typedef struct _LogDef LogDef;
@@ -70,7 +77,43 @@ static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
        return FALSE;
 }
 
-static LogWindow *log_window_create(void)
+
+static void log_window_pause_cb(GtkWidget *widget, gpointer data)
+{
+       options->log_window.paused = !options->log_window.paused;
+}
+
+static void log_window_line_wrap_cb(GtkWidget *widget, gpointer data)
+{
+       LogWindow *logwin = data;
+
+       options->log_window.line_wrap = !options->log_window.line_wrap;
+
+       if (options->log_window.line_wrap)
+               {
+               gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(logwin->text), GTK_WRAP_WORD);
+               }
+       else
+               {
+               gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(logwin->text), GTK_WRAP_NONE);
+               }
+}
+
+static void log_window_regexp_cb(GtkWidget *text_entry, gpointer data)
+{
+       gchar *new_regexp;
+
+       new_regexp = g_strdup(gtk_entry_get_text(GTK_ENTRY(text_entry)));
+       set_regexp(new_regexp);
+       g_free(new_regexp);
+}
+
+static void log_window_debug_spin_cb(GtkSpinButton *debug_level, gpointer data)
+{
+       set_debug_level(gtk_spin_button_get_value(debug_level));
+}
+
+static LogWindow *log_window_create(LayoutWindow *lw)
 {
        LogWindow *logwin;
        GtkWidget *window;
@@ -78,11 +121,21 @@ static LogWindow *log_window_create(void)
        GtkWidget *text;
        GtkTextBuffer *buffer;
        GtkTextIter iter;
+       GtkWidget *button;
+       GtkWidget *win_vbox;
+       GtkWidget *textbox;
+       GtkWidget *hbox;
 
        logwin = g_new0(LogWindow, 1);
 
        window = window_new(GTK_WINDOW_TOPLEVEL, "log", NULL, NULL, _("Log"));
-       gtk_widget_set_size_request(window, 520, 400);
+       win_vbox = gtk_vbox_new(FALSE, PREF_PAD_SPACE);
+       gtk_container_add(GTK_CONTAINER(window), win_vbox);
+       gtk_widget_show(win_vbox);
+
+       gtk_widget_set_size_request(window, lw->options.log_window.w, lw->options.log_window.h);
+       gtk_window_move(GTK_WINDOW(window), lw->options.log_window.x, lw->options.log_window.y);
+
        g_signal_connect(G_OBJECT(window), "delete_event",
                         G_CALLBACK(gtk_widget_hide_on_delete), NULL);
        g_signal_connect(G_OBJECT(window), "key_press_event",
@@ -96,9 +149,31 @@ static LogWindow *log_window_create(void)
                                       GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
                                            GTK_SHADOW_IN);
-       gtk_container_add(GTK_CONTAINER(window), scrolledwin);
+
+       gtk_container_add(GTK_CONTAINER(win_vbox), scrolledwin);
        gtk_widget_show(scrolledwin);
 
+       hbox = pref_box_new(win_vbox, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
+
+       gtk_widget_show(hbox);
+       logwin->debug_level = pref_spin_new_mnemonic(hbox, _("Debug level:"), NULL,
+                         0, 4, 1, 1, get_debug_level(),G_CALLBACK(log_window_debug_spin_cb),
+                         logwin->debug_level );
+
+       logwin->pause = pref_button_new(hbox, NULL, "Pause", FALSE,
+                                          G_CALLBACK(log_window_pause_cb), NULL);
+
+       logwin->wrap = pref_button_new(hbox, NULL, "Line wrap", FALSE,
+                                          G_CALLBACK(log_window_line_wrap_cb), logwin);
+
+       pref_label_new(hbox, "Filter regexp");
+
+       textbox = gtk_entry_new();
+       gtk_container_add(GTK_CONTAINER(hbox), textbox);
+       gtk_widget_show(textbox);
+       g_signal_connect(G_OBJECT(textbox), "activate",
+                        G_CALLBACK(log_window_regexp_cb), logwin);
+
        text = gtk_text_view_new();
        gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
        gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
@@ -112,7 +187,8 @@ static LogWindow *log_window_create(void)
        logwin->scrolledwin = scrolledwin;
        logwin->text = text;
        logwin->lines = 1;
-
+       logwin->regexp_box = textbox;
+       lw->log_window = logwin->window;
        return logwin;
 }
 
@@ -168,6 +244,7 @@ static void log_window_show(LogWindow *logwin)
        GtkTextView *text = GTK_TEXT_VIEW(logwin->text);
        GtkTextBuffer *buffer;
        GtkTextMark *mark;
+       gchar *regexp;
 
        g_assert(logwin != NULL);
 
@@ -178,15 +255,22 @@ static void log_window_show(LogWindow *logwin)
        gtk_window_present(GTK_WINDOW(logwin->window));
 
        log_window_append("", LOG_NORMAL); // to flush memorized lines
+
+       regexp = g_strdup(get_regexp());
+       if (regexp != NULL)
+               {
+               gtk_entry_set_text(GTK_ENTRY(logwin->regexp_box), regexp);
+               g_free(regexp);
+               }
 }
 
-void log_window_new(void)
+void log_window_new(LayoutWindow *lw)
 {
        if (logwindow == NULL)
                {
                LogWindow *logwin;
 
-               logwin = log_window_create();
+               logwin = log_window_create(lw);
                log_window_init(logwin);
                logwindow = logwin;
                }
@@ -220,7 +304,6 @@ void log_window_append(const gchar *str, LogType type)
        GtkTextView *text;
        GtkTextBuffer *buffer;
        GtkTextIter iter;
-       guint line_limit = 1000; //FIXME: option
        static GList *memory = NULL;
 
        if (logwindow == NULL)
@@ -233,7 +316,7 @@ void log_window_append(const gchar *str, LogType type)
 
                        memory = g_list_prepend(memory, msg);
 
-                       while (g_list_length(memory) >= line_limit)
+                       while (g_list_length(memory) >= options->log_window_lines)
                                {
                                GList *work = g_list_last(memory);
                                LogMsg *oldest_msg = work->data;
@@ -248,13 +331,13 @@ void log_window_append(const gchar *str, LogType type)
        text = GTK_TEXT_VIEW(logwindow->text);
        buffer = gtk_text_view_get_buffer(text);
 
-       if (line_limit > 0 && logwindow->lines >= line_limit)
+       if (options->log_window_lines > 0 && logwindow->lines >= options->log_window_lines)
                {
                GtkTextIter start, end;
 
                gtk_text_buffer_get_start_iter(buffer, &start);
                end = start;
-               gtk_text_iter_forward_lines(&end, logwindow->lines - line_limit);
+               gtk_text_iter_forward_lines(&end, logwindow->lines - options->log_window_lines);
                gtk_text_buffer_delete(buffer, &start, &end);
                }
 
@@ -278,12 +361,15 @@ void log_window_append(const gchar *str, LogType type)
 
        log_window_insert_text(buffer, &iter, str, logdefs[type].tag);
 
-       if (gtk_widget_get_visible(GTK_WIDGET(text)))
+       if (!options->log_window.paused)
                {
-               GtkTextMark *mark;
+               if (gtk_widget_get_visible(GTK_WIDGET(text)))
+                       {
+                       GtkTextMark *mark;
 
-               mark = gtk_text_buffer_get_mark(buffer, "end");
-               gtk_text_view_scroll_mark_onscreen(text, mark);
+                       mark = gtk_text_buffer_get_mark(buffer, "end");
+                       gtk_text_view_scroll_mark_onscreen(text, mark);
+                       }
                }
 
        logwindow->lines = gtk_text_buffer_get_line_count(buffer);
index e92f042..a29cd7b 100644 (file)
@@ -30,7 +30,7 @@ typedef enum
        LOG_COUNT
 } LogType;
 
-void log_window_new(void);
+void log_window_new(LayoutWindow *lw);
 
 void log_window_append(const gchar *str, LogType type);
 
index 3901bd0..5f8c81e 100644 (file)
@@ -226,6 +226,7 @@ static void parse_command_line(gint argc, gchar *argv[])
 
        command_line->argc = argc;
        command_line->argv = argv;
+       command_line->regexp = NULL;
 
        if (argc > 1)
                {
@@ -301,6 +302,27 @@ static void parse_command_line(gint argc, gchar *argv[])
                                        remote_list = remote_build_list(remote_list, argc - i, &argv[i], &remote_errors);
                                        }
                                }
+                       else if ((strcmp(cmd_line, "+w") == 0) ||
+                                               strcmp(cmd_line, "--show-log-window") == 0)
+                               {
+                               command_line->log_window_show = TRUE;
+                               }
+                       else if (strncmp(cmd_line, "-o:", 3) == 0)
+                               {
+                               command_line->log_file = g_strdup(cmd_line + 3);
+                               }
+                       else if (strncmp(cmd_line, "--log-file:", 11) == 0)
+                               {
+                               command_line->log_file = g_strdup(cmd_line + 11);
+                               }
+                       else if (strncmp(cmd_line, "-g:", 3) == 0)
+                               {
+                               set_regexp(g_strdup(cmd_line+3));
+                               }
+                       else if (strncmp(cmd_line, "-grep:", 6) == 0)
+                               {
+                               set_regexp(g_strdup(cmd_line+3));
+                               }
                        else if (strcmp(cmd_line, "-rh") == 0 ||
                                 strcmp(cmd_line, "--remote-help") == 0)
                                {
@@ -340,7 +362,10 @@ static void parse_command_line(gint argc, gchar *argv[])
                                print_term(_("  -rh,--remote-help                print remote command list\n"));
 #ifdef DEBUG
                                print_term(_("      --debug[=level]              turn on debug output\n"));
+                               print_term(_("  -g:<regexp>, --grep:<regexp>     filter debug output\n"));
 #endif
+                               print_term(_("  +w, --show-log-window            show log window\n"));
+                               print_term(_("  -o:<file>, --log-file:<file>     save log data to file\n"));
                                print_term(_("  -v, --version                    print version info\n"));
                                print_term(_("  -h, --help                       show this message\n\n"));
 
@@ -639,6 +664,8 @@ static void exit_program_final(void)
                layout_free(lw);
                }
 
+       secure_close(command_line->ssi);
+
        gtk_main_quit();
 }
 
@@ -871,6 +898,15 @@ gint main(gint argc, gchar *argv[])
                        }
                }
 
+       if (command_line->log_file)
+               {
+               gchar *pathl;
+               gchar *path = g_strdup(command_line->log_file);
+
+               pathl = path_from_utf8(path);
+               command_line->ssi = secure_open(pathl);
+               }
+
        if (command_line->cmd_list ||
            (command_line->startup_command_line_collection && command_line->collection_list))
                {
index ffd4195..0e22198 100644 (file)
@@ -158,6 +158,9 @@ gchar *sort_type_get_text(SortType method)
                case SORT_NUMBER:
                        return _("Sort by number");
                        break;
+               case SORT_RATING:
+                       return _("Sort by rating");
+                       break;
                case SORT_NAME:
                default:
                        return _("Sort by name");
@@ -205,6 +208,7 @@ GtkWidget *submenu_add_sort(GtkWidget *menu, GCallback func, gpointer data,
        submenu_add_sort_item(submenu, func, SORT_CTIME, show_current, type);
        submenu_add_sort_item(submenu, func, SORT_EXIFTIME, show_current, type);
        submenu_add_sort_item(submenu, func, SORT_SIZE, show_current, type);
+       submenu_add_sort_item(submenu, func, SORT_RATING, show_current, type);
        if (include_path) submenu_add_sort_item(submenu, func, SORT_PATH, show_current, type);
        if (include_none) submenu_add_sort_item(submenu, func, SORT_NONE, show_current, type);
 
@@ -220,6 +224,80 @@ GtkWidget *submenu_add_sort(GtkWidget *menu, GCallback func, gpointer data,
        return submenu;
 }
 
+gchar *zoom_type_get_text(ZoomMode method)
+{
+       switch (method)
+               {
+               case ZOOM_RESET_ORIGINAL:
+                       return _("Zoom to original size");
+                       break;
+               case ZOOM_RESET_FIT_WINDOW:
+                       return _("Fit image to window");
+                       break;
+               case ZOOM_RESET_NONE:
+                       return _("Leave Zoom at previous setting");
+                       break;
+               default:
+                       return _("Zoom to original size");
+                       break;
+               }
+
+       return "";
+}
+
+static GtkWidget *submenu_add_zoom_item(GtkWidget *menu,
+                                       GCallback func, ZoomMode mode,
+                                       gboolean show_current, ZoomMode show_mode)
+{
+       GtkWidget *item;
+
+       if (show_current)
+               {
+               item = menu_item_add_radio(menu,
+                                          zoom_type_get_text(mode), GINT_TO_POINTER((gint)mode), (mode == show_mode),
+                                          func, GINT_TO_POINTER((gint)mode));
+               }
+       else
+               {
+               item = menu_item_add(menu, zoom_type_get_text(mode),
+                                    func, GINT_TO_POINTER((gint)mode));
+               }
+
+       return item;
+}
+
+GtkWidget *submenu_add_zoom(GtkWidget *menu, GCallback func, gpointer data,
+                           gboolean include_none, gboolean include_path,
+                           gboolean show_current, ZoomMode mode)
+{
+       GtkWidget *submenu;
+
+       if (!menu)
+               {
+               submenu = gtk_menu_new();
+               g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
+               }
+       else
+               {
+               submenu = menu;
+               }
+
+       submenu_add_zoom_item(submenu, func, ZOOM_RESET_ORIGINAL, show_current, mode);
+       submenu_add_zoom_item(submenu, func, ZOOM_RESET_FIT_WINDOW, show_current, mode);
+       submenu_add_zoom_item(submenu, func, ZOOM_RESET_NONE, show_current, mode);
+
+       if (menu)
+               {
+               GtkWidget *item;
+
+               item = menu_item_add(menu, _("Zoom"), NULL, NULL);
+               gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
+               return item;
+               }
+
+       return submenu;
+}
+
 /*
  *-----------------------------------------------------------------------------
  * altering
index 5e7c987..aec4fd4 100644 (file)
@@ -31,7 +31,9 @@ gchar *sort_type_get_text(SortType method);
 GtkWidget *submenu_add_sort(GtkWidget *menu, GCallback func, gpointer data,
                            gboolean include_none, gboolean include_path,
                            gboolean show_current, SortType type);
-
+GtkWidget *submenu_add_zoom(GtkWidget *menu, GCallback func, gpointer data,
+                           gboolean include_none, gboolean include_path,
+                           gboolean show_current, ZoomMode mode);
 gchar *alter_type_get_text(AlterType type);
 GtkWidget *submenu_add_alter(GtkWidget *menu, GCallback func, gpointer data);
 
index 721f12a..563b7b8 100644 (file)
@@ -302,7 +302,7 @@ gboolean metadata_write_perform(FileData *fd)
        g_assert(fd->change);
 
        if (fd->change->dest &&
-           strcmp(extension_from_path(fd->change->dest), GQ_CACHE_EXT_METADATA) == 0)
+           strcmp(registered_extension_from_path(fd->change->dest), GQ_CACHE_EXT_METADATA) == 0)
                {
                success = metadata_legacy_write(fd);
                if (success) metadata_legacy_delete(fd, fd->change->dest);
@@ -833,6 +833,46 @@ gboolean metadata_append_string(FileData *fd, const gchar *key, const char *valu
                }
 }
 
+gboolean metadata_write_GPS_coord(FileData *fd, const gchar *key, gdouble value)
+{
+       gint deg;
+       gdouble min;
+       gdouble param;
+       char *coordinate;
+       char *ref;
+       gboolean ok = TRUE;
+
+       param = value;
+       if (param < 0)
+               param = -param;
+       deg = param;
+       min = (param * 60) - (deg * 60);
+       if (g_strcmp0(key, "Xmp.exif.GPSLongitude") == 0)
+               if (value < 0)
+                       ref = "W";
+               else
+                       ref = "E";
+       else if (g_strcmp0(key, "Xmp.exif.GPSLatitude") == 0)
+               if (value < 0)
+                       ref = "S";
+               else
+                       ref = "N";
+       else
+               {
+               log_printf("unknown GPS parameter key '%s'\n", key);
+               ok = FALSE;
+               }
+
+       if (ok)
+               {
+               coordinate = g_strdup_printf("%i,%lf,%s", deg, min, ref);
+               metadata_write_string(fd, key, coordinate );
+               g_free(coordinate);
+               }
+
+       return ok;
+}
+
 gboolean metadata_append_list(FileData *fd, const gchar *key, const GList *values)
 {
        GList *list = metadata_read_list(fd, key, METADATA_PLAIN);
@@ -1076,6 +1116,14 @@ gchar *keyword_get_name(GtkTreeModel *keyword_tree, GtkTreeIter *iter)
        return name;
 }
 
+gchar *keyword_get_mark(GtkTreeModel *keyword_tree, GtkTreeIter *iter)
+{
+       gchar *mark_str;
+
+       gtk_tree_model_get(keyword_tree, iter, KEYWORD_COLUMN_MARK, &mark_str, -1);
+       return mark_str;
+}
+
 gchar *keyword_get_casefold(GtkTreeModel *keyword_tree, GtkTreeIter *iter)
 {
        gchar *casefold;
@@ -1557,6 +1605,20 @@ void keyword_show_all_in(GtkTreeStore *keyword_tree, gpointer id)
        gtk_tree_model_foreach(GTK_TREE_MODEL(keyword_tree), keyword_show_all_in_cb, id);
 }
 
+static gboolean keyword_revert_hidden_in_cb(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
+{
+       if (keyword_is_hidden_in(GTK_TREE_MODEL(keyword_tree), iter, data))
+               {
+               keyword_show_in(GTK_TREE_STORE(model), iter, data);
+               }
+       return FALSE;
+}
+
+void keyword_revert_hidden_in(GtkTreeStore *keyword_tree, gpointer id)
+{
+       gtk_tree_model_foreach(GTK_TREE_MODEL(keyword_tree), keyword_revert_hidden_in_cb, id);
+}
+
 static void keyword_hide_unset_in_recursive(GtkTreeStore *keyword_tree, GtkTreeIter *iter_ptr, gpointer id, GList *keywords)
 {
        GtkTreeIter iter = *iter_ptr;
@@ -1705,12 +1767,19 @@ static void keyword_tree_node_write_config(GtkTreeModel *keyword_tree, GtkTreeIt
                {
                GtkTreeIter children;
                gchar *name;
+               gchar *mark_str;
 
                WRITE_NL(); WRITE_STRING("<keyword ");
                name = keyword_get_name(keyword_tree, &iter);
                write_char_option(outstr, indent, "name", name);
                g_free(name);
                write_bool_option(outstr, indent, "kw", keyword_get_is_keyword(keyword_tree, &iter));
+               mark_str = keyword_get_mark(keyword_tree, &iter);
+               if (mark_str && mark_str[0])
+                       {
+                       write_char_option(outstr, indent, "mark", mark_str);
+                       }
+
                if (gtk_tree_model_iter_children(keyword_tree, &children, &iter))
                        {
                        WRITE_STRING(">");
@@ -1745,6 +1814,7 @@ GtkTreeIter *keyword_add_from_config(GtkTreeStore *keyword_tree, GtkTreeIter *pa
 {
        gchar *name = NULL;
        gboolean is_kw = TRUE;
+       gchar *mark_str = NULL;
 
        while (*attribute_names)
                {
@@ -1753,6 +1823,7 @@ GtkTreeIter *keyword_add_from_config(GtkTreeStore *keyword_tree, GtkTreeIter *pa
 
                if (READ_CHAR_FULL("name", name)) continue;
                if (READ_BOOL_FULL("kw", is_kw)) continue;
+               if (READ_CHAR_FULL("mark", mark_str)) continue;
 
                log_printf("unknown attribute %s = %s\n", option, value);
                }
@@ -1765,6 +1836,13 @@ GtkTreeIter *keyword_add_from_config(GtkTreeStore *keyword_tree, GtkTreeIter *pa
                        gtk_tree_store_append(keyword_tree, &iter, parent);
                        }
                keyword_set(keyword_tree, &iter, name, is_kw);
+
+               if (mark_str)
+                       {
+                       meta_data_connect_mark_with_keyword(GTK_TREE_MODEL(keyword_tree),
+                                                                                       &iter, (gint)atoi(mark_str) - 1);
+                       }
+
                g_free(name);
                return gtk_tree_iter_copy(&iter);
                }
index 5b2c06d..ec56579 100644 (file)
@@ -25,6 +25,7 @@
 #define COMMENT_KEY "Xmp.dc.description"
 #define KEYWORD_KEY "Xmp.dc.subject"
 #define ORIENTATION_KEY "Xmp.tiff.Orientation"
+#define RATING_KEY "Xmp.xmp.Rating"
 
 void metadata_cache_free(FileData *fd);
 
@@ -46,6 +47,7 @@ gchar *metadata_read_string(FileData *fd, const gchar *key, MetadataFormat forma
 guint64 metadata_read_int(FileData *fd, const gchar *key, guint64 fallback);
 gdouble metadata_read_GPS_coord(FileData *fd, const gchar *key, gdouble fallback);
 gdouble metadata_read_GPS_direction(FileData *fd, const gchar *key, gdouble fallback);
+gboolean metadata_write_GPS_coord(FileData *fd, const gchar *key, gdouble value);
 
 gboolean metadata_append_string(FileData *fd, const gchar *key, const char *value);
 gboolean metadata_append_list(FileData *fd, const gchar *key, const GList *values);
@@ -71,6 +73,7 @@ void meta_data_connect_mark_with_keyword(GtkTreeModel *keyword_tree, GtkTreeIter
 
 
 gchar *keyword_get_name(GtkTreeModel *keyword_tree, GtkTreeIter *iter);
+gchar *keyword_get_mark(GtkTreeModel *keyword_tree, GtkTreeIter *iter);
 gchar *keyword_get_casefold(GtkTreeModel *keyword_tree, GtkTreeIter *iter);
 gboolean keyword_get_is_keyword(GtkTreeModel *keyword_tree, GtkTreeIter *iter);
 
@@ -98,6 +101,7 @@ void keyword_hide_in(GtkTreeStore *keyword_tree, GtkTreeIter *iter, gpointer id)
 void keyword_show_in(GtkTreeStore *keyword_tree, GtkTreeIter *iter, gpointer id);
 gboolean keyword_is_hidden_in(GtkTreeModel *keyword_tree, GtkTreeIter *iter, gpointer id);
 void keyword_show_all_in(GtkTreeStore *keyword_tree, gpointer id);
+void keyword_revert_hidden_in(GtkTreeStore *keyword_tree, gpointer id);
 void keyword_hide_unset_in(GtkTreeStore *keyword_tree, gpointer id, GList *keywords);
 void keyword_show_set_in(GtkTreeStore *keyword_tree, gpointer id, GList *keywords);
 
index ca17483..f0817c1 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "main.h"
 #include "misc.h"
+#include "ui_fileops.h"
 
 gdouble get_zoom_increment(void)
 {
@@ -123,6 +124,51 @@ gchar *expand_tilde(const gchar *filename)
 #endif
 }
 
+/* Search for latitude/longitude parameters in a string
+ */
+
+#define GEOCODE_NAME "geocode-parameters.awk"
+#define BUFSIZE 128
+
+gchar *decode_geo_parameters(const gchar *input_text)
+{
+       gchar *message;
+       gchar *path = g_build_filename(get_rc_dir(), GEOCODE_NAME, NULL);
+       gchar *cmd = g_strconcat("echo \'", input_text, "\'  | awk -f ", path, NULL);
+
+       if (g_file_test(path, G_FILE_TEST_EXISTS))
+               {
+               gchar buf[BUFSIZE];
+               FILE *fp;
+
+               if ((fp = popen(cmd, "r")) == NULL)
+                       {
+                       message = g_strconcat("Error: opening pipe\n", input_text, NULL);
+                       }
+               else
+                       {
+                       while (fgets(buf, BUFSIZE, fp))
+                               {
+                               DEBUG_1("Output: %s", buf);
+                               }
+
+                       message = g_strconcat(buf, NULL);
+
+                       if(pclose(fp))
+                               {
+                               message = g_strconcat("Error: Command not found or exited with error status\n", input_text, NULL);
+                               }
+                       }
+               }
+       else
+               {
+               message = g_strconcat(input_text, NULL);
+               }
+
+       g_free(path);
+       g_free(cmd);
+       return message;
+}
 
 /* Run a command like system() but may output debug messages. */
 int runcmd(gchar *cmd)
index dac9fc2..1be461e 100644 (file)
@@ -26,6 +26,7 @@ gchar *utf8_validate_or_convert(const gchar *text);
 gint utf8_compare(const gchar *s1, const gchar *s2, gboolean case_sensitive);
 gchar *expand_tilde(const gchar *filename);
 int runcmd(gchar *cmd);
+gchar *decode_geo_parameters(const gchar *input_text);
 
 #endif /* MISC_H */
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index c49396b..a79dc28 100644 (file)
@@ -48,6 +48,7 @@ ConfOptions *init_options(ConfOptions *options)
        options->dnd_icon_size = 48;
        options->duplicates_similarity_threshold = 99;
        options->rot_invariant_sim = TRUE;
+       options->sort_totals = FALSE;
 
        options->file_filter.disable = FALSE;
        options->file_filter.show_dot_directory = FALSE;
@@ -77,6 +78,15 @@ ConfOptions *init_options(ConfOptions *options)
        options->fullscreen.screen = -1;
 
        memset(&options->image.border_color, 0, sizeof(options->image.border_color));
+       memset(&options->image.alpha_color_1, 0, sizeof(options->image.alpha_color_1));
+       memset(&options->image.alpha_color_2, 0, sizeof(options->image.alpha_color_2));
+/* alpha channel checkerboard background (same as gimp) */
+       options->image.alpha_color_1.red = 0x009999;
+       options->image.alpha_color_1.green = 0x009999;
+       options->image.alpha_color_1.blue = 0x009999;
+       options->image.alpha_color_2.red = 0x006666;
+       options->image.alpha_color_2.green = 0x006666;
+       options->image.alpha_color_2.blue = 0x006666;
        options->image.enable_read_ahead = TRUE;
        options->image.exif_rotate_enable = TRUE;
        options->image.exif_proof_rotate_enable = TRUE;
@@ -84,6 +94,7 @@ ConfOptions *init_options(ConfOptions *options)
        options->image.limit_autofit_size = FALSE;
        options->image.limit_window_size = TRUE;
        options->image.max_autofit_size = 100;
+       options->image.max_enlargement_size = 900;
        options->image.max_window_size = 90;
        options->image.scroll_reset_method = SCROLL_RESET_NOCHANGE;
        options->image.tile_cache_max = 10;
@@ -131,6 +142,7 @@ ConfOptions *init_options(ConfOptions *options)
        options->metadata.confirm_on_dir_change = TRUE;
        options->metadata.keywords_case_sensitive = FALSE;
        options->metadata.write_orientation = TRUE;
+       options->metadata.sidecar_extended_name = FALSE;
 
        options->show_icon_names = TRUE;
 
@@ -158,6 +170,10 @@ ConfOptions *init_options(ConfOptions *options)
        options->stereo.fixed_x2 = 0;
        options->stereo.fixed_y2 = 1125;
 
+       options->log_window_lines = 1000;
+       options->log_window.line_wrap = TRUE;
+       options->log_window.paused = FALSE;
+
        return options;
 }
 
@@ -227,6 +243,7 @@ LayoutOptions *init_layout_options(LayoutOptions *options)
        options->main_window.w = 720;
        options->main_window.x = 0;
        options->main_window.y = 0;
+       options->folder_window.vdivider_pos = 100;
        options->order = g_strdup("123");
        options->show_directory_date = FALSE;
        options->show_marks = FALSE;
@@ -240,6 +257,9 @@ LayoutOptions *init_layout_options(LayoutOptions *options)
        options->image_overlay.histogram_mode = 1;
        options->image_overlay.state = OSD_SHOW_NOTHING;
        options->animate = FALSE;
+       options->bars_state.hidden = FALSE;
+       options->log_window.w = 520;
+       options->log_window.h = 400;
        return options;
 }
 
index 9423679..ac996dd 100644 (file)
@@ -45,6 +45,7 @@ struct _ConfOptions
        gboolean duplicates_thumbnails;
        guint duplicates_select_type;
        gboolean rot_invariant_sim;
+       gboolean sort_totals;
 
        gint open_recent_list_maxsize;
        gint dnd_icon_size;
@@ -53,6 +54,25 @@ struct _ConfOptions
        gboolean use_saved_window_positions_for_new_windows;
        gboolean tools_restore_state;
 
+       gint log_window_lines;
+
+       /* info sidebar component heights */
+       struct {
+               gint height;
+       } info_comment;
+
+       struct {
+               gint height;
+       } info_keywords;
+
+       struct {
+               gint height;
+       } info_title;
+
+       struct {
+               gint height;
+       } info_rating;
+
        /* file ops */
        struct {
                gboolean enable_in_place_rename;
@@ -74,6 +94,7 @@ struct _ConfOptions
                gint max_window_size;
                gboolean limit_autofit_size;
                gint max_autofit_size;
+               gint max_enlargement_size;
 
                gint tile_cache_max;    /* in megabytes */
                gint image_cache_max;   /* in megabytes */
@@ -89,6 +110,8 @@ struct _ConfOptions
                gboolean use_custom_border_color_in_fullscreen;
                gboolean use_custom_border_color;
                GdkColor border_color;
+               GdkColor alpha_color_1;
+               GdkColor alpha_color_2;
        } image;
 
        /* thumbnails */
@@ -210,6 +233,7 @@ struct _ConfOptions
                gboolean confirm_on_dir_change;
                gboolean keywords_case_sensitive;
                gboolean write_orientation;
+               gboolean sidecar_extended_name;
        } metadata;
 
        /* Stereo */
@@ -235,6 +259,20 @@ struct _ConfOptions
                        gboolean fs_temp_disable;
                } tmp;
        } stereo;
+
+       /* copy move rename */
+       struct {
+               gint auto_start;
+               gchar *auto_end;
+               gint auto_padding;
+               gint formatted_start;
+       } cp_mv_rn;
+
+       /* log window */
+       struct {
+               gboolean paused;
+               gboolean line_wrap;
+       } log_window;
 };
 
 ConfOptions *options;
index 70c9423..2401b5e 100644 (file)
@@ -13,4 +13,9 @@ module_pan_view = \
        %D%/pan-util.c  \
        %D%/pan-util.h  \
        %D%/pan-view.c  \
-       %D%/pan-view.h
+       %D%/pan-view.h  \
+       %D%/pan-view-filter.c   \
+       %D%/pan-view-filter.h   \
+       %D%/pan-view-search.c   \
+       %D%/pan-view-search.h
+
index 91aa003..3400fbb 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "pan-util.h"
 #include "pan-view.h"
+#include "pan-view-filter.h"
 #include "pixbuf_util.h"
 
 #define PAN_CAL_POPUP_COLOR 220, 220, 220
@@ -200,6 +201,7 @@ void pan_calendar_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *he
        gint end_month = 0;
 
        list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
+       pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
 
        if (pw->cache_list && pw->exif_date_enable)
                {
index 65ac306..95748ee 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "pan-item.h"
 #include "pan-util.h"
+#include "pan-view-filter.h"
 
 static void pan_flower_size(PanWindow *pw, gint *width, gint *height)
 {
@@ -242,6 +243,8 @@ static FlowerGroup *pan_flower_group(PanWindow *pw, FileData *dir_fd, gint x, gi
        f = filelist_sort(f, SORT_NAME, TRUE);
        d = filelist_sort(d, SORT_NAME, TRUE);
 
+       pan_filter_fd_list(&f, pw->filter_ui->filter_elements);
+
        pi_box = pan_item_text_new(pw, x, y, dir_fd->path, PAN_TEXT_ATTR_NONE,
                                   PAN_TEXT_BORDER_SIZE,
                                   PAN_TEXT_COLOR, 255);
@@ -386,6 +389,8 @@ static void pan_folder_tree_path(PanWindow *pw, FileData *dir_fd,
        f = filelist_sort(f, SORT_NAME, TRUE);
        d = filelist_sort(d, SORT_NAME, TRUE);
 
+       pan_filter_fd_list(&f, pw->filter_ui->filter_elements);
+
        *x = PAN_BOX_BORDER + ((*level) * MAX(PAN_BOX_BORDER, PAN_THUMB_GAP));
 
        pi_box = pan_item_text_new(pw, *x, *y, dir_fd->path, PAN_TEXT_ATTR_NONE,
index b441b83..5d28e4b 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "pan-item.h"
 #include "pan-util.h"
+#include "pan-view-filter.h"
 
 void pan_grid_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
 {
@@ -35,6 +36,7 @@ void pan_grid_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height
        gint next_y;
 
        list = pan_list_tree(dir_fd, SORT_NAME, TRUE, pw->ignore_symlinks);
+       pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
 
        grid_size = (gint)sqrt((gdouble)g_list_length(list));
        if (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE)
index 9a96420..1162795 100644 (file)
 #include "pan-item.h"
 #include "pan-util.h"
 #include "pan-view.h"
+#include "pan-view-filter.h"
 
 void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *height)
 {
        GList *list;
        GList *work;
        gint x, y;
-       time_t tc;
+       time_t group_start_date;
        gint total;
        gint count;
        PanItem *pi_month = NULL;
@@ -41,6 +42,7 @@ void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *he
        gint y_height;
 
        list = pan_list_tree(dir_fd, SORT_NONE, TRUE, pw->ignore_symlinks);
+       pan_filter_fd_list(&list, pw->filter_ui->filter_elements);
 
        if (pw->cache_list && pw->exif_date_enable)
                {
@@ -61,7 +63,8 @@ void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *he
        day_start = month_start;
        x_width = 0;
        y_height = 0;
-       tc = 0;
+       group_start_date = 0;
+       // total and count are used to enforce a stride of PAN_GROUP_MAX thumbs.
        total = 0;
        count = 0;
        work = list;
@@ -73,13 +76,15 @@ void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *he
                fd = work->data;
                work = work->next;
 
-               if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_DAY))
+               if (!pan_date_compare(fd->date, group_start_date, PAN_DATE_LENGTH_DAY))
                        {
+                       // FD starts a new day group.
                        GList *needle;
                        gchar *buf;
 
-                       if (!pan_date_compare(fd->date, tc, PAN_DATE_LENGTH_MONTH))
+                       if (!pan_date_compare(fd->date, group_start_date, PAN_DATE_LENGTH_MONTH))
                                {
+                               // FD starts a new month group.
                                pi_day = NULL;
 
                                if (pi_month)
@@ -114,7 +119,7 @@ void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *he
 
                        if (pi_day) x = pi_day->x + pi_day->width + PAN_BOX_BORDER;
 
-                       tc = fd->date;
+                       group_start_date = fd->date;
                        total = 1;
                        count = 0;
 
@@ -124,7 +129,7 @@ void pan_timeline_compute(PanWindow *pw, FileData *dir_fd, gint *width, gint *he
                                FileData *nfd;
 
                                nfd = needle->data;
-                               if (pan_date_compare(nfd->date, tc, PAN_DATE_LENGTH_DAY))
+                               if (pan_date_compare(nfd->date, group_start_date, PAN_DATE_LENGTH_DAY))
                                        {
                                        needle = needle->next;
                                        total++;
index 5318b38..9fd8916 100644 (file)
@@ -169,6 +169,19 @@ struct _PanItem {
        gboolean queued;
 };
 
+typedef struct _PanViewSearchUi PanViewSearchUi;
+struct _PanViewSearchUi
+{
+       GtkWidget *search_box;
+       GtkWidget *search_entry;
+       GtkWidget *search_label;
+       GtkWidget *search_button;
+       GtkWidget *search_button_arrow;
+};
+
+// Defined in pan-view-filter.h
+typedef struct _PanViewFilterUi PanViewFilterUi;
+
 typedef struct _PanWindow PanWindow;
 struct _PanWindow
 {
@@ -182,11 +195,8 @@ struct _PanWindow
        GtkWidget *label_message;
        GtkWidget *label_zoom;
 
-       GtkWidget *search_box;
-       GtkWidget *search_entry;
-       GtkWidget *search_label;
-       GtkWidget *search_button;
-       GtkWidget *search_button_arrow;
+       PanViewSearchUi *search_ui;
+       PanViewFilterUi *filter_ui;
 
        GtkWidget *date_button;
 
diff --git a/src/pan-view/pan-view-filter.c b/src/pan-view/pan-view-filter.c
new file mode 100644 (file)
index 0000000..5865d66
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-view-filter.h"
+
+#include "image.h"
+#include "metadata.h"
+#include "pan-item.h"
+#include "pan-util.h"
+#include "pan-view.h"
+#include "ui_fileops.h"
+#include "ui_tabcomp.h"
+#include "ui_misc.h"
+
+PanViewFilterUi *pan_filter_ui_new(PanWindow *pw)
+{
+       PanViewFilterUi *ui = g_new0(PanViewFilterUi, 1);
+       GtkWidget *combo;
+       GtkWidget *hbox;
+
+       /* Since we're using the GHashTable as a HashSet (in which key and value pointers
+        * are always identical), specifying key _and_ value destructor callbacks will
+        * cause a double-free.
+        */
+       {
+               GtkTreeIter iter;
+               ui->filter_mode_model = gtk_list_store_new(3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING);
+               gtk_list_store_append(ui->filter_mode_model, &iter);
+               gtk_list_store_set(ui->filter_mode_model, &iter,
+                                  0, PAN_VIEW_FILTER_REQUIRE, 1, _("Require"), 2, _("R"), -1);
+               gtk_list_store_append(ui->filter_mode_model, &iter);
+               gtk_list_store_set(ui->filter_mode_model, &iter,
+                                  0, PAN_VIEW_FILTER_EXCLUDE, 1, _("Exclude"), 2, _("E"), -1);
+               gtk_list_store_append(ui->filter_mode_model, &iter);
+               gtk_list_store_set(ui->filter_mode_model, &iter,
+                                  0, PAN_VIEW_FILTER_INCLUDE, 1, _("Include"), 2, _("I"), -1);
+               gtk_list_store_append(ui->filter_mode_model, &iter);
+               gtk_list_store_set(ui->filter_mode_model, &iter,
+                                  0, PAN_VIEW_FILTER_GROUP, 1, _("Group"), 2, _("G"), -1);
+
+               ui->filter_mode_combo = gtk_combo_box_new_with_model(GTK_TREE_MODEL(ui->filter_mode_model));
+               gtk_combo_box_set_focus_on_click(GTK_COMBO_BOX(ui->filter_mode_combo), FALSE);
+               gtk_combo_box_set_active(GTK_COMBO_BOX(ui->filter_mode_combo), 0);
+
+               GtkCellRenderer *render = gtk_cell_renderer_text_new();
+               gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(ui->filter_mode_combo), render, TRUE);
+               gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(ui->filter_mode_combo), render, "text", 1, NULL);
+       }
+
+       // Build the actual filter UI.
+       ui->filter_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+       pref_spacer(ui->filter_box, 0);
+       pref_label_new(ui->filter_box, _("Keyword Filter:"));
+
+       gtk_box_pack_start(GTK_BOX(ui->filter_box), ui->filter_mode_combo, TRUE, TRUE, 0);
+       gtk_widget_show(ui->filter_mode_combo);
+
+       hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
+       gtk_box_pack_start(GTK_BOX(ui->filter_box), hbox, TRUE, TRUE, 0);
+       gtk_widget_show(hbox);
+
+       combo = tab_completion_new_with_history(&ui->filter_entry, "", "pan_view_filter", -1,
+                                               pan_filter_activate_cb, pw);
+       gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
+       gtk_widget_show(combo);
+
+       // TODO(xsdg): Figure out whether it's useful to keep this label around.
+       ui->filter_label = gtk_label_new("");
+       //gtk_box_pack_start(GTK_BOX(hbox), ui->filter_label, FALSE, FALSE, 0);
+       //gtk_widget_show(ui->filter_label);
+
+       ui->filter_kw_hbox = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+       gtk_box_pack_start(GTK_BOX(hbox), ui->filter_kw_hbox, TRUE, TRUE, 0);
+       gtk_widget_show(ui->filter_kw_hbox);
+
+       // Build the spin-button to show/hide the filter UI.
+       ui->filter_button = gtk_toggle_button_new();
+       gtk_button_set_relief(GTK_BUTTON(ui->filter_button), GTK_RELIEF_NONE);
+       gtk_button_set_focus_on_click(GTK_BUTTON(ui->filter_button), FALSE);
+       hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
+       gtk_container_add(GTK_CONTAINER(ui->filter_button), hbox);
+       gtk_widget_show(hbox);
+       ui->filter_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
+       gtk_box_pack_start(GTK_BOX(hbox), ui->filter_button_arrow, FALSE, FALSE, 0);
+       gtk_widget_show(ui->filter_button_arrow);
+       pref_label_new(hbox, _("Filter"));
+
+       g_signal_connect(G_OBJECT(ui->filter_button), "clicked",
+                        G_CALLBACK(pan_filter_toggle_cb), pw);
+
+       return ui;
+}
+
+void pan_filter_ui_destroy(PanViewFilterUi **ui_ptr)
+{
+       if (ui_ptr == NULL || *ui_ptr == NULL) return;
+
+       // Note that g_clear_pointer handles already-NULL pointers.
+       //g_clear_pointer(&(*ui_ptr)->filter_kw_table, g_hash_table_destroy);
+
+       g_free(*ui_ptr);
+       *ui_ptr = NULL;
+}
+
+static void pan_filter_status(PanWindow *pw, const gchar *text)
+{
+       gtk_label_set_text(GTK_LABEL(pw->filter_ui->filter_label), (text) ? text : "");
+}
+
+static void pan_filter_kw_button_cb(GtkButton *widget, gpointer data)
+{
+       PanFilterCallbackState *cb_state = data;
+       PanWindow *pw = cb_state->pw;
+       PanViewFilterUi *ui = pw->filter_ui;
+
+       // TODO(xsdg): Fix filter element pointed object memory leak.
+       ui->filter_elements = g_list_delete_link(ui->filter_elements, cb_state->filter_element);
+       gtk_widget_destroy(GTK_WIDGET(widget));
+       g_free(cb_state);
+
+       pan_filter_status(pw, _("Removed keyword…"));
+       pan_layout_update(pw);
+}
+
+void pan_filter_activate_cb(const gchar *text, gpointer data)
+{
+       GtkWidget *kw_button;
+       PanWindow *pw = data;
+       PanViewFilterUi *ui = pw->filter_ui;
+       GtkTreeIter iter;
+
+       if (!text) return;
+
+       // Get all relevant state and reset UI.
+       gtk_combo_box_get_active_iter(GTK_COMBO_BOX(ui->filter_mode_combo), &iter);
+       gtk_entry_set_text(GTK_ENTRY(ui->filter_entry), "");
+       tab_completion_append_to_history(ui->filter_entry, text);
+
+       // Add new filter element.
+       PanViewFilterElement *element = g_new0(PanViewFilterElement, 1);
+       gtk_tree_model_get(GTK_TREE_MODEL(ui->filter_mode_model), &iter, 0, &element->mode, -1);
+       element->keyword = g_strdup(text);
+       if (g_strcmp0(text, g_regex_escape_string(text, -1)))
+               {
+               // It's an actual regex, so compile
+               element->kw_regex = g_regex_new(text, G_REGEX_ANCHORED | G_REGEX_OPTIMIZE, G_REGEX_MATCH_ANCHORED, NULL);
+               }
+       ui->filter_elements = g_list_append(ui->filter_elements, element);
+
+       // Get the short version of the mode value.
+       gchar *short_mode;
+       gtk_tree_model_get(GTK_TREE_MODEL(ui->filter_mode_model), &iter, 2, &short_mode, -1);
+
+       // Create the button.
+       // TODO(xsdg): Use MVC so that the button list is an actual representation of the GList
+       gchar *label = g_strdup_printf("(%s) %s", short_mode, text);
+       kw_button = gtk_button_new_with_label(label);
+       g_clear_pointer(&label, g_free);
+
+       gtk_box_pack_start(GTK_BOX(ui->filter_kw_hbox), kw_button, FALSE, FALSE, 0);
+       gtk_widget_show(kw_button);
+
+       PanFilterCallbackState *cb_state = g_new0(PanFilterCallbackState, 1);
+       cb_state->pw = pw;
+       cb_state->filter_element = g_list_last(ui->filter_elements);
+
+       g_signal_connect(G_OBJECT(kw_button), "clicked",
+                        G_CALLBACK(pan_filter_kw_button_cb), cb_state);
+
+       pan_layout_update(pw);
+}
+
+void pan_filter_activate(PanWindow *pw)
+{
+       gchar *text;
+
+       text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->filter_ui->filter_entry)));
+       pan_filter_activate_cb(text, pw);
+       g_free(text);
+}
+
+void pan_filter_toggle_cb(GtkWidget *button, gpointer data)
+{
+       PanWindow *pw = data;
+       PanViewFilterUi *ui = pw->filter_ui;
+       gboolean visible;
+
+       visible = gtk_widget_get_visible(ui->filter_box);
+       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
+
+       if (visible)
+               {
+               gtk_widget_hide(ui->filter_box);
+               gtk_arrow_set(GTK_ARROW(ui->filter_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
+               }
+       else
+               {
+               gtk_widget_show(ui->filter_box);
+               gtk_arrow_set(GTK_ARROW(ui->filter_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+               gtk_widget_grab_focus(ui->filter_entry);
+               }
+}
+
+void pan_filter_toggle_visible(PanWindow *pw, gboolean enable)
+{
+       PanViewFilterUi *ui = pw->filter_ui;
+       if (pw->fs) return;
+
+       if (enable)
+               {
+               if (gtk_widget_get_visible(ui->filter_box))
+                       {
+                       gtk_widget_grab_focus(ui->filter_entry);
+                       }
+               else
+                       {
+                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->filter_button), TRUE);
+                       }
+               }
+       else
+               {
+               if (gtk_widget_get_visible(ui->filter_entry))
+                       {
+                       if (gtk_widget_has_focus(ui->filter_entry))
+                               {
+                               gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
+                               }
+                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->filter_button), FALSE);
+                       }
+               }
+}
+
+static gboolean pan_view_list_contains_kw_pattern(GList *haystack, PanViewFilterElement *filter, gchar **found_kw)
+{
+       if (filter->kw_regex)
+               {
+               // regex compile succeeded; attempt regex match.
+               GList *work = g_list_first(haystack);
+               while (work)
+                       {
+                       gchar *keyword = work->data;
+                       work = work->next;
+                       if (g_regex_match(filter->kw_regex, keyword, 0x0, NULL))
+                               {
+                               if (found_kw) *found_kw = keyword;
+                               return TRUE;
+                               }
+                       }
+               return FALSE;
+               }
+       else
+               {
+               // regex compile failed; fall back to exact string match.
+               GList *found_elem = g_list_find_custom(haystack, filter->keyword, (GCompareFunc)g_strcmp0);
+               if (found_elem && found_kw) *found_kw = found_elem->data;
+               return !!found_elem;
+               }
+}
+
+gboolean pan_filter_fd_list(GList **fd_list, GList *filter_elements)
+{
+       GList *work;
+       gboolean modified = FALSE;
+       GHashTable *seen_kw_table = NULL;
+
+       if (!fd_list || !*fd_list || !filter_elements) return modified;
+
+       // seen_kw_table is only valid in this scope, so don't take ownership of any strings.
+       seen_kw_table = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
+
+       work = *fd_list;
+       while (work)
+               {
+               FileData *fd = work->data;
+               GList *last_work = work;
+               work = work->next;
+
+               // TODO(xsdg): OPTIMIZATION Do the search inside of metadata.c to avoid a
+               // bunch of string list copies.
+               GList *img_keywords = metadata_read_list(fd, KEYWORD_KEY, METADATA_PLAIN);
+
+               // TODO(xsdg): OPTIMIZATION Determine a heuristic for when to linear-search the
+               // keywords list, and when to build a hash table for the image's keywords.
+               gboolean should_reject = FALSE;
+               gchar *group_kw = NULL;
+               GList *filter_element = filter_elements;
+               while (filter_element)
+                       {
+                       PanViewFilterElement *filter = filter_element->data;
+                       filter_element = filter_element->next;
+                       gchar *found_kw = NULL;
+                       gboolean has_kw = pan_view_list_contains_kw_pattern(img_keywords, filter, &found_kw);
+
+                       switch (filter->mode)
+                               {
+                               case PAN_VIEW_FILTER_REQUIRE:
+                                       should_reject |= !has_kw;
+                                       break;
+                               case PAN_VIEW_FILTER_EXCLUDE:
+                                       should_reject |= has_kw;
+                                       break;
+                               case PAN_VIEW_FILTER_INCLUDE:
+                                       if (has_kw) should_reject = FALSE;
+                                       break;
+                               case PAN_VIEW_FILTER_GROUP:
+                                       if (has_kw)
+                                               {
+                                               if (g_hash_table_contains(seen_kw_table, found_kw))
+                                                       {
+                                                       should_reject = TRUE;
+                                                       }
+                                               else if (group_kw == NULL)
+                                                       {
+                                                       group_kw = found_kw;
+                                                       }
+                                               }
+                                       break;
+                               }
+                       }
+
+               if (!should_reject && group_kw != NULL) g_hash_table_add(seen_kw_table, group_kw);
+
+               group_kw = NULL;  // group_kw references an item from img_keywords.
+               string_list_free(img_keywords);
+
+               if (should_reject)
+                       {
+                       *fd_list = g_list_delete_link(*fd_list, last_work);
+                       modified = TRUE;
+                       }
+               }
+
+       g_hash_table_destroy(seen_kw_table);
+       return modified;
+}
diff --git a/src/pan-view/pan-view-filter.h b/src/pan-view/pan-view-filter.h
new file mode 100644 (file)
index 0000000..f3e3e11
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_VIEW_FILTER_H
+#define PAN_VIEW_PAN_VIEW_FILTER_H
+
+#include "main.h"
+#include "pan-types.h"
+
+typedef enum {
+       PAN_VIEW_FILTER_REQUIRE,
+       PAN_VIEW_FILTER_EXCLUDE,
+       PAN_VIEW_FILTER_INCLUDE,
+       PAN_VIEW_FILTER_GROUP
+} PanViewFilterMode;
+
+typedef struct _PanViewFilterElement PanViewFilterElement;
+struct _PanViewFilterElement
+{
+       PanViewFilterMode mode;
+       gchar *keyword;
+       GRegex *kw_regex;
+};
+
+typedef struct _PanFilterCallbackState PanFilterCallbackState;
+struct _PanFilterCallbackState
+{
+       PanWindow *pw;
+       GList *filter_element;
+};
+
+struct _PanViewFilterUi
+{
+       GtkWidget *filter_box;
+       GtkWidget *filter_entry;
+       GtkWidget *filter_label;
+       GtkWidget *filter_button;
+       GtkWidget *filter_button_arrow;
+       GtkWidget *filter_kw_hbox;
+       GtkListStore *filter_mode_model;
+       GtkWidget *filter_mode_combo;
+       GList *filter_elements;  // List of PanViewFilterElement.
+};
+
+void pan_filter_toggle_visible(PanWindow *pw, gboolean enable);
+void pan_filter_activate(PanWindow *pw);
+void pan_filter_activate_cb(const gchar *text, gpointer data);
+void pan_filter_toggle_cb(GtkWidget *button, gpointer data);
+
+// Creates a new PanViewFilterUi instance and returns it.
+PanViewFilterUi *pan_filter_ui_new(PanWindow *pw);
+
+// Destroys the specified PanViewFilterUi and sets the pointer to NULL.
+void pan_filter_ui_destroy(PanViewFilterUi **ui);
+
+gboolean pan_filter_fd_list(GList **fd_list, GList *filter_elements);
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
diff --git a/src/pan-view/pan-view-search.c b/src/pan-view/pan-view-search.c
new file mode 100644 (file)
index 0000000..5ddef13
--- /dev/null
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "pan-view-search.h"
+
+#include "image.h"
+#include "pan-calendar.h"
+#include "pan-item.h"
+#include "pan-util.h"
+#include "pan-view.h"
+#include "ui_tabcomp.h"
+#include "ui_misc.h"
+
+PanViewSearchUi *pan_search_ui_new(PanWindow *pw)
+{
+       PanViewSearchUi *ui = g_new0(PanViewSearchUi, 1);
+       GtkWidget *combo;
+       GtkWidget *hbox;
+
+       // Build the actual search UI.
+       ui->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+       pref_spacer(ui->search_box, 0);
+       pref_label_new(ui->search_box, _("Find:"));
+
+       hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
+       gtk_box_pack_start(GTK_BOX(ui->search_box), hbox, TRUE, TRUE, 0);
+       gtk_widget_show(hbox);
+
+       combo = tab_completion_new_with_history(&ui->search_entry, "", "pan_view_search", -1,
+                                               pan_search_activate_cb, pw);
+       gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
+       gtk_widget_show(combo);
+
+       ui->search_label = gtk_label_new("");
+       gtk_box_pack_start(GTK_BOX(hbox), ui->search_label, TRUE, TRUE, 0);
+       gtk_widget_show(ui->search_label);
+
+       // Build the spin-button to show/hide the search UI.
+       ui->search_button = gtk_toggle_button_new();
+       gtk_button_set_relief(GTK_BUTTON(ui->search_button), GTK_RELIEF_NONE);
+       gtk_button_set_focus_on_click(GTK_BUTTON(ui->search_button), FALSE);
+       hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
+       gtk_container_add(GTK_CONTAINER(ui->search_button), hbox);
+       gtk_widget_show(hbox);
+       ui->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
+       gtk_box_pack_start(GTK_BOX(hbox), ui->search_button_arrow, FALSE, FALSE, 0);
+       gtk_widget_show(ui->search_button_arrow);
+       pref_label_new(hbox, _("Find"));
+
+       g_signal_connect(G_OBJECT(ui->search_button), "clicked",
+                        G_CALLBACK(pan_search_toggle_cb), pw);
+
+       return ui;
+}
+
+void pan_search_ui_destroy(PanViewSearchUi **ui_ptr)
+{
+       if (ui_ptr == NULL || *ui_ptr == NULL) return;
+
+       g_free(*ui_ptr);
+       *ui_ptr = NULL;
+}
+
+static void pan_search_status(PanWindow *pw, const gchar *text)
+{
+       gtk_label_set_text(GTK_LABEL(pw->search_ui->search_label), (text) ? text : "");
+}
+
+static gint pan_search_by_path(PanWindow *pw, const gchar *path)
+{
+       PanItem *pi;
+       GList *list;
+       GList *found;
+       PanItemType type;
+       gchar *buf;
+
+       type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+
+       list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
+       if (!list) return FALSE;
+
+       found = g_list_find(list, pw->click_pi);
+       if (found && found->next)
+               {
+               found = found->next;
+               pi = found->data;
+               }
+       else
+               {
+               pi = list->data;
+               }
+
+       pan_info_update(pw, pi);
+       image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
+
+       buf = g_strdup_printf("%s ( %d / %d )",
+                             (path[0] == G_DIR_SEPARATOR) ? _("path found") : _("filename found"),
+                             g_list_index(list, pi) + 1,
+                             g_list_length(list));
+       pan_search_status(pw, buf);
+       g_free(buf);
+
+       g_list_free(list);
+
+       return TRUE;
+}
+
+static gboolean pan_search_by_partial(PanWindow *pw, const gchar *text)
+{
+       PanItem *pi;
+       GList *list;
+       GList *found;
+       PanItemType type;
+       gchar *buf;
+
+       type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+
+       list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
+       if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
+       if (!list)
+               {
+               gchar *needle;
+
+               needle = g_utf8_strdown(text, -1);
+               list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
+               g_free(needle);
+               }
+       if (!list) return FALSE;
+
+       found = g_list_find(list, pw->click_pi);
+       if (found && found->next)
+               {
+               found = found->next;
+               pi = found->data;
+               }
+       else
+               {
+               pi = list->data;
+               }
+
+       pan_info_update(pw, pi);
+       image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
+
+       buf = g_strdup_printf("%s ( %d / %d )",
+                             _("partial match"),
+                             g_list_index(list, pi) + 1,
+                             g_list_length(list));
+       pan_search_status(pw, buf);
+       g_free(buf);
+
+       g_list_free(list);
+
+       return TRUE;
+}
+
+static gboolean valid_date_separator(gchar c)
+{
+       return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
+}
+
+static GList *pan_search_by_date_val(PanWindow *pw, PanItemType type,
+                                    gint year, gint month, gint day,
+                                    const gchar *key)
+{
+       GList *list = NULL;
+       GList *work;
+
+       work = g_list_last(pw->list_static);
+       while (work)
+               {
+               PanItem *pi;
+
+               pi = work->data;
+               work = work->prev;
+
+               if (pi->fd && (pi->type == type || type == PAN_ITEM_NONE) &&
+                   ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
+                       {
+                       struct tm *tl;
+
+                       tl = localtime(&pi->fd->date);
+                       if (tl)
+                               {
+                               gint match;
+
+                               match = (tl->tm_year == year - 1900);
+                               if (match && month >= 0) match = (tl->tm_mon == month - 1);
+                               if (match && day > 0) match = (tl->tm_mday == day);
+
+                               if (match) list = g_list_prepend(list, pi);
+                               }
+                       }
+               }
+
+       return g_list_reverse(list);
+}
+
+static gboolean pan_search_by_date(PanWindow *pw, const gchar *text)
+{
+       PanItem *pi = NULL;
+       GList *list = NULL;
+       GList *found;
+       gint year;
+       gint month = -1;
+       gint day = -1;
+       gchar *ptr;
+       gchar *mptr;
+       struct tm *lt;
+       time_t t;
+       gchar *message;
+       gchar *buf;
+       gchar *buf_count;
+
+       if (!text) return FALSE;
+
+       ptr = (gchar *)text;
+       while (*ptr != '\0')
+               {
+               if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
+               ptr++;
+               }
+
+       t = time(NULL);
+       if (t == -1) return FALSE;
+       lt = localtime(&t);
+       if (!lt) return FALSE;
+
+       if (valid_date_separator(*text))
+               {
+               year = -1;
+               mptr = (gchar *)text;
+               }
+       else
+               {
+               year = (gint)strtol(text, &mptr, 10);
+               if (mptr == text) return FALSE;
+               }
+
+       if (*mptr != '\0' && valid_date_separator(*mptr))
+               {
+               gchar *dptr;
+
+               mptr++;
+               month = strtol(mptr, &dptr, 10);
+               if (dptr == mptr)
+                       {
+                       if (valid_date_separator(*dptr))
+                               {
+                               month = lt->tm_mon + 1;
+                               dptr++;
+                               }
+                       else
+                               {
+                               month = -1;
+                               }
+                       }
+               if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
+                       {
+                       gchar *eptr;
+                       dptr++;
+                       day = strtol(dptr, &eptr, 10);
+                       if (dptr == eptr)
+                               {
+                               day = lt->tm_mday;
+                               }
+                       }
+               }
+
+       if (year == -1)
+               {
+               year = lt->tm_year + 1900;
+               }
+       else if (year < 100)
+               {
+               if (year > 70)
+                       year+= 1900;
+               else
+                       year+= 2000;
+               }
+
+       if (year < 1970 ||
+           month < -1 || month == 0 || month > 12 ||
+           day < -1 || day == 0 || day > 31) return FALSE;
+
+       t = pan_date_to_time(year, month, day);
+       if (t < 0) return FALSE;
+
+       if (pw->layout == PAN_LAYOUT_CALENDAR)
+               {
+               list = pan_search_by_date_val(pw, PAN_ITEM_BOX, year, month, day, "day");
+               }
+       else
+               {
+               PanItemType type;
+
+               type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
+               list = pan_search_by_date_val(pw, type, year, month, day, NULL);
+               }
+
+       if (list)
+               {
+               found = g_list_find(list, pw->search_pi);
+               if (found && found->next)
+                       {
+                       found = found->next;
+                       pi = found->data;
+                       }
+               else
+                       {
+                       pi = list->data;
+                       }
+               }
+
+       pw->search_pi = pi;
+
+       if (pw->layout == PAN_LAYOUT_CALENDAR && pi && pi->type == PAN_ITEM_BOX)
+               {
+               pan_info_update(pw, NULL);
+               pan_calendar_update(pw, pi);
+               image_scroll_to_point(pw->imd,
+                                     pi->x + pi->width / 2,
+                                     pi->y + pi->height / 2, 0.5, 0.5);
+               }
+       else if (pi)
+               {
+               pan_info_update(pw, pi);
+               image_scroll_to_point(pw->imd,
+                                     pi->x - PAN_BOX_BORDER * 5 / 2,
+                                     pi->y, 0.0, 0.5);
+               }
+
+       if (month > 0)
+               {
+               buf = pan_date_value_string(t, PAN_DATE_LENGTH_MONTH);
+               if (day > 0)
+                       {
+                       gchar *tmp;
+                       tmp = buf;
+                       buf = g_strdup_printf("%d %s", day, tmp);
+                       g_free(tmp);
+                       }
+               }
+       else
+               {
+               buf = pan_date_value_string(t, PAN_DATE_LENGTH_YEAR);
+               }
+
+       if (pi)
+               {
+               buf_count = g_strdup_printf("( %d / %d )",
+                                           g_list_index(list, pi) + 1,
+                                           g_list_length(list));
+               }
+       else
+               {
+               buf_count = g_strdup_printf("(%s)", _("no match"));
+               }
+
+       message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
+       g_free(buf);
+       g_free(buf_count);
+       pan_search_status(pw, message);
+       g_free(message);
+
+       g_list_free(list);
+
+       return TRUE;
+}
+
+void pan_search_activate_cb(const gchar *text, gpointer data)
+{
+       PanWindow *pw = data;
+
+       if (!text) return;
+
+       tab_completion_append_to_history(pw->search_ui->search_entry, text);
+
+       if (pan_search_by_path(pw, text)) return;
+
+       if ((pw->layout == PAN_LAYOUT_TIMELINE ||
+            pw->layout == PAN_LAYOUT_CALENDAR) &&
+           pan_search_by_date(pw, text))
+               {
+               return;
+               }
+
+       if (pan_search_by_partial(pw, text)) return;
+
+       pan_search_status(pw, _("no match"));
+}
+
+void pan_search_activate(PanWindow *pw)
+{
+       gchar *text;
+
+       text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_ui->search_entry)));
+       pan_search_activate_cb(text, pw);
+       g_free(text);
+}
+
+void pan_search_toggle_cb(GtkWidget *button, gpointer data)
+{
+       PanWindow *pw = data;
+       PanViewSearchUi *ui = pw->search_ui;
+       gboolean visible;
+
+       visible = gtk_widget_get_visible(ui->search_box);
+       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
+
+       if (visible)
+               {
+               gtk_widget_hide(ui->search_box);
+               gtk_arrow_set(GTK_ARROW(ui->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
+               }
+       else
+               {
+               gtk_widget_show(ui->search_box);
+               gtk_arrow_set(GTK_ARROW(ui->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
+               gtk_widget_grab_focus(ui->search_entry);
+               }
+}
+
+void pan_search_toggle_visible(PanWindow *pw, gboolean enable)
+{
+       PanViewSearchUi *ui = pw->search_ui;
+       if (pw->fs) return;
+
+       if (enable)
+               {
+               if (gtk_widget_get_visible(ui->search_box))
+                       {
+                       gtk_widget_grab_focus(ui->search_entry);
+                       }
+               else
+                       {
+                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->search_button), TRUE);
+                       }
+               }
+       else
+               {
+               if (gtk_widget_get_visible(ui->search_entry))
+                       {
+                       if (gtk_widget_has_focus(ui->search_entry))
+                               {
+                               gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
+                               }
+                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ui->search_button), FALSE);
+                       }
+               }
+}
diff --git a/src/pan-view/pan-view-search.h b/src/pan-view/pan-view-search.h
new file mode 100644 (file)
index 0000000..88784b7
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2006 John Ellis
+ * Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ * Author: John Ellis
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef PAN_VIEW_PAN_VIEW_SEARCH_H
+#define PAN_VIEW_PAN_VIEW_SEARCH_H
+
+#include "main.h"
+#include "pan-types.h"
+
+void pan_search_toggle_visible(PanWindow *pw, gboolean enable);
+void pan_search_activate(PanWindow *pw);
+void pan_search_activate_cb(const gchar *text, gpointer data);
+void pan_search_toggle_cb(GtkWidget *button, gpointer data);
+
+// Creates a new PanViewSearchUi instance and returns it.
+PanViewSearchUi *pan_search_ui_new(PanWindow *pw);
+
+// Destroys the specified PanViewSearchUi and sets the pointer to NULL.
+void pan_search_ui_destroy(PanViewSearchUi **ui);
+
+#endif
+/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index db70b65..59c4cc0 100644 (file)
@@ -38,6 +38,8 @@
 #include "pan-item.h"
 #include "pan-timeline.h"
 #include "pan-util.h"
+#include "pan-view-filter.h"
+#include "pan-view-search.h"
 #include "pixbuf-renderer.h"
 #include "pixbuf_util.h"
 #include "thumb.h"
@@ -78,9 +80,6 @@ static void pan_layout_update_idle(PanWindow *pw);
 
 static void pan_fullscreen_toggle(PanWindow *pw, gboolean force_off);
 
-static void pan_search_toggle_visible(PanWindow *pw, gboolean enable);
-static void pan_search_activate(PanWindow *pw);
-
 static void pan_window_close(PanWindow *pw);
 
 static GtkWidget *pan_popup_menu(PanWindow *pw);
@@ -1072,7 +1071,7 @@ static void pan_layout_update_idle(PanWindow *pw)
                }
 }
 
-static void pan_layout_update(PanWindow *pw)
+void pan_layout_update(PanWindow *pw)
 {
        pan_window_message(pw, _("Sorting images..."));
        pan_layout_update_idle(pw);
@@ -1134,7 +1133,8 @@ static gboolean pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, g
        imd_widget = gtk_container_get_focus_child(GTK_CONTAINER(pw->imd->widget));
        focused = (pw->fs || (imd_widget && gtk_widget_has_focus(imd_widget)));
        on_entry = (gtk_widget_has_focus(pw->path_entry) ||
-                   gtk_widget_has_focus(pw->search_entry));
+                   gtk_widget_has_focus(pw->search_ui->search_entry) ||
+                   gtk_widget_has_focus(pw->filter_ui->filter_entry));
 
        if (focused)
                {
@@ -1248,6 +1248,7 @@ static gboolean pan_window_key_press_cb(GtkWidget *widget, GdkEventKey *event, g
 
                if (stop_signal) return stop_signal;
 
+               // Don't steal characters from entry boxes.
                if (!on_entry)
                        {
                        stop_signal = TRUE;
@@ -1326,7 +1327,7 @@ static void pan_info_add_exif(PanTextAlignment *ta, FileData *fd)
 }
 
 
-static void pan_info_update(PanWindow *pw, PanItem *pi)
+void pan_info_update(PanWindow *pw, PanItem *pi)
 {
        PanTextAlignment *ta;
        PanItem *pbox;
@@ -1450,399 +1451,6 @@ static void pan_info_update(PanWindow *pw, PanItem *pi)
 }
 
 
-/*
- *-----------------------------------------------------------------------------
- * search
- *-----------------------------------------------------------------------------
- */
-
-static void pan_search_status(PanWindow *pw, const gchar *text)
-{
-       gtk_label_set_text(GTK_LABEL(pw->search_label), (text) ? text : "");
-}
-
-static gint pan_search_by_path(PanWindow *pw, const gchar *path)
-{
-       PanItem *pi;
-       GList *list;
-       GList *found;
-       PanItemType type;
-       gchar *buf;
-
-       type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
-
-       list = pan_item_find_by_path(pw, type, path, FALSE, FALSE);
-       if (!list) return FALSE;
-
-       found = g_list_find(list, pw->click_pi);
-       if (found && found->next)
-               {
-               found = found->next;
-               pi = found->data;
-               }
-       else
-               {
-               pi = list->data;
-               }
-
-       pan_info_update(pw, pi);
-       image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
-
-       buf = g_strdup_printf("%s ( %d / %d )",
-                             (path[0] == G_DIR_SEPARATOR) ? _("path found") : _("filename found"),
-                             g_list_index(list, pi) + 1,
-                             g_list_length(list));
-       pan_search_status(pw, buf);
-       g_free(buf);
-
-       g_list_free(list);
-
-       return TRUE;
-}
-
-static gboolean pan_search_by_partial(PanWindow *pw, const gchar *text)
-{
-       PanItem *pi;
-       GList *list;
-       GList *found;
-       PanItemType type;
-       gchar *buf;
-
-       type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
-
-       list = pan_item_find_by_path(pw, type, text, TRUE, FALSE);
-       if (!list) list = pan_item_find_by_path(pw, type, text, FALSE, TRUE);
-       if (!list)
-               {
-               gchar *needle;
-
-               needle = g_utf8_strdown(text, -1);
-               list = pan_item_find_by_path(pw, type, needle, TRUE, TRUE);
-               g_free(needle);
-               }
-       if (!list) return FALSE;
-
-       found = g_list_find(list, pw->click_pi);
-       if (found && found->next)
-               {
-               found = found->next;
-               pi = found->data;
-               }
-       else
-               {
-               pi = list->data;
-               }
-
-       pan_info_update(pw, pi);
-       image_scroll_to_point(pw->imd, pi->x + pi->width / 2, pi->y + pi->height / 2, 0.5, 0.5);
-
-       buf = g_strdup_printf("%s ( %d / %d )",
-                             _("partial match"),
-                             g_list_index(list, pi) + 1,
-                             g_list_length(list));
-       pan_search_status(pw, buf);
-       g_free(buf);
-
-       g_list_free(list);
-
-       return TRUE;
-}
-
-static gboolean valid_date_separator(gchar c)
-{
-       return (c == '/' || c == '-' || c == ' ' || c == '.' || c == ',');
-}
-
-static GList *pan_search_by_date_val(PanWindow *pw, PanItemType type,
-                                    gint year, gint month, gint day,
-                                    const gchar *key)
-{
-       GList *list = NULL;
-       GList *work;
-
-       work = g_list_last(pw->list_static);
-       while (work)
-               {
-               PanItem *pi;
-
-               pi = work->data;
-               work = work->prev;
-
-               if (pi->fd && (pi->type == type || type == PAN_ITEM_NONE) &&
-                   ((!key && !pi->key) || (key && pi->key && strcmp(key, pi->key) == 0)))
-                       {
-                       struct tm *tl;
-
-                       tl = localtime(&pi->fd->date);
-                       if (tl)
-                               {
-                               gint match;
-
-                               match = (tl->tm_year == year - 1900);
-                               if (match && month >= 0) match = (tl->tm_mon == month - 1);
-                               if (match && day > 0) match = (tl->tm_mday == day);
-
-                               if (match) list = g_list_prepend(list, pi);
-                               }
-                       }
-               }
-
-       return g_list_reverse(list);
-}
-
-static gboolean pan_search_by_date(PanWindow *pw, const gchar *text)
-{
-       PanItem *pi = NULL;
-       GList *list = NULL;
-       GList *found;
-       gint year;
-       gint month = -1;
-       gint day = -1;
-       gchar *ptr;
-       gchar *mptr;
-       struct tm *lt;
-       time_t t;
-       gchar *message;
-       gchar *buf;
-       gchar *buf_count;
-
-       if (!text) return FALSE;
-
-       ptr = (gchar *)text;
-       while (*ptr != '\0')
-               {
-               if (!g_unichar_isdigit(*ptr) && !valid_date_separator(*ptr)) return FALSE;
-               ptr++;
-               }
-
-       t = time(NULL);
-       if (t == -1) return FALSE;
-       lt = localtime(&t);
-       if (!lt) return FALSE;
-
-       if (valid_date_separator(*text))
-               {
-               year = -1;
-               mptr = (gchar *)text;
-               }
-       else
-               {
-               year = (gint)strtol(text, &mptr, 10);
-               if (mptr == text) return FALSE;
-               }
-
-       if (*mptr != '\0' && valid_date_separator(*mptr))
-               {
-               gchar *dptr;
-
-               mptr++;
-               month = strtol(mptr, &dptr, 10);
-               if (dptr == mptr)
-                       {
-                       if (valid_date_separator(*dptr))
-                               {
-                               month = lt->tm_mon + 1;
-                               dptr++;
-                               }
-                       else
-                               {
-                               month = -1;
-                               }
-                       }
-               if (dptr != mptr && *dptr != '\0' && valid_date_separator(*dptr))
-                       {
-                       gchar *eptr;
-                       dptr++;
-                       day = strtol(dptr, &eptr, 10);
-                       if (dptr == eptr)
-                               {
-                               day = lt->tm_mday;
-                               }
-                       }
-               }
-
-       if (year == -1)
-               {
-               year = lt->tm_year + 1900;
-               }
-       else if (year < 100)
-               {
-               if (year > 70)
-                       year+= 1900;
-               else
-                       year+= 2000;
-               }
-
-       if (year < 1970 ||
-           month < -1 || month == 0 || month > 12 ||
-           day < -1 || day == 0 || day > 31) return FALSE;
-
-       t = pan_date_to_time(year, month, day);
-       if (t < 0) return FALSE;
-
-       if (pw->layout == PAN_LAYOUT_CALENDAR)
-               {
-               list = pan_search_by_date_val(pw, PAN_ITEM_BOX, year, month, day, "day");
-               }
-       else
-               {
-               PanItemType type;
-
-               type = (pw->size > PAN_IMAGE_SIZE_THUMB_LARGE) ? PAN_ITEM_IMAGE : PAN_ITEM_THUMB;
-               list = pan_search_by_date_val(pw, type, year, month, day, NULL);
-               }
-
-       if (list)
-               {
-               found = g_list_find(list, pw->search_pi);
-               if (found && found->next)
-                       {
-                       found = found->next;
-                       pi = found->data;
-                       }
-               else
-                       {
-                       pi = list->data;
-                       }
-               }
-
-       pw->search_pi = pi;
-
-       if (pw->layout == PAN_LAYOUT_CALENDAR && pi && pi->type == PAN_ITEM_BOX)
-               {
-               pan_info_update(pw, NULL);
-               pan_calendar_update(pw, pi);
-               image_scroll_to_point(pw->imd,
-                                     pi->x + pi->width / 2,
-                                     pi->y + pi->height / 2, 0.5, 0.5);
-               }
-       else if (pi)
-               {
-               pan_info_update(pw, pi);
-               image_scroll_to_point(pw->imd,
-                                     pi->x - PAN_BOX_BORDER * 5 / 2,
-                                     pi->y, 0.0, 0.5);
-               }
-
-       if (month > 0)
-               {
-               buf = pan_date_value_string(t, PAN_DATE_LENGTH_MONTH);
-               if (day > 0)
-                       {
-                       gchar *tmp;
-                       tmp = buf;
-                       buf = g_strdup_printf("%d %s", day, tmp);
-                       g_free(tmp);
-                       }
-               }
-       else
-               {
-               buf = pan_date_value_string(t, PAN_DATE_LENGTH_YEAR);
-               }
-
-       if (pi)
-               {
-               buf_count = g_strdup_printf("( %d / %d )",
-                                           g_list_index(list, pi) + 1,
-                                           g_list_length(list));
-               }
-       else
-               {
-               buf_count = g_strdup_printf("(%s)", _("no match"));
-               }
-
-       message = g_strdup_printf("%s %s %s", _("Date:"), buf, buf_count);
-       g_free(buf);
-       g_free(buf_count);
-       pan_search_status(pw, message);
-       g_free(message);
-
-       g_list_free(list);
-
-       return TRUE;
-}
-
-static void pan_search_activate_cb(const gchar *text, gpointer data)
-{
-       PanWindow *pw = data;
-
-       if (!text) return;
-
-       tab_completion_append_to_history(pw->search_entry, text);
-
-       if (pan_search_by_path(pw, text)) return;
-
-       if ((pw->layout == PAN_LAYOUT_TIMELINE ||
-            pw->layout == PAN_LAYOUT_CALENDAR) &&
-           pan_search_by_date(pw, text))
-               {
-               return;
-               }
-
-       if (pan_search_by_partial(pw, text)) return;
-
-       pan_search_status(pw, _("no match"));
-}
-
-static void pan_search_activate(PanWindow *pw)
-{
-       gchar *text;
-
-       text = g_strdup(gtk_entry_get_text(GTK_ENTRY(pw->search_entry)));
-       pan_search_activate_cb(text, pw);
-       g_free(text);
-}
-
-static void pan_search_toggle_cb(GtkWidget *button, gpointer data)
-{
-       PanWindow *pw = data;
-       gboolean visible;
-
-       visible = gtk_widget_get_visible(pw->search_box);
-       if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)) == visible) return;
-
-       if (visible)
-               {
-               gtk_widget_hide(pw->search_box);
-               gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_UP, GTK_SHADOW_NONE);
-               }
-       else
-               {
-               gtk_widget_show(pw->search_box);
-               gtk_arrow_set(GTK_ARROW(pw->search_button_arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE);
-               gtk_widget_grab_focus(pw->search_entry);
-               }
-}
-
-static void pan_search_toggle_visible(PanWindow *pw, gboolean enable)
-{
-       if (pw->fs) return;
-
-       if (enable)
-               {
-               if (gtk_widget_get_visible(pw->search_box))
-                       {
-                       gtk_widget_grab_focus(pw->search_entry);
-                       }
-               else
-                       {
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), TRUE);
-                       }
-               }
-       else
-               {
-               if (gtk_widget_get_visible(pw->search_entry))
-                       {
-                       if (gtk_widget_has_focus(pw->search_entry))
-                               {
-                               gtk_widget_grab_focus(GTK_WIDGET(pw->imd->widget));
-                               }
-                       gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pw->search_button), FALSE);
-                       }
-               }
-}
-
-
 /*
  *-----------------------------------------------------------------------------
  * main window
@@ -2129,6 +1737,8 @@ static void pan_window_close(PanWindow *pw)
                }
 
        pan_fullscreen_toggle(pw, TRUE);
+       pan_search_ui_destroy(&pw->search_ui);
+       pan_filter_ui_destroy(&pw->filter_ui);
        gtk_widget_destroy(pw->window);
 
        pan_window_items_free(pw);
@@ -2274,24 +1884,12 @@ static void pan_window_new_real(FileData *dir_fd)
 
        /* find bar */
 
-       pw->search_box = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
-       gtk_box_pack_start(GTK_BOX(vbox), pw->search_box, FALSE, FALSE, 2);
+       pw->search_ui = pan_search_ui_new(pw);
+       gtk_box_pack_start(GTK_BOX(vbox), pw->search_ui->search_box, FALSE, FALSE, 2);
 
-       pref_spacer(pw->search_box, 0);
-       pref_label_new(pw->search_box, _("Find:"));
-
-       hbox = gtk_hbox_new(TRUE, PREF_PAD_SPACE);
-       gtk_box_pack_start(GTK_BOX(pw->search_box), hbox, TRUE, TRUE, 0);
-       gtk_widget_show(hbox);
-
-       combo = tab_completion_new_with_history(&pw->search_entry, "", "pan_view_search", -1,
-                                               pan_search_activate_cb, pw);
-       gtk_box_pack_start(GTK_BOX(hbox), combo, TRUE, TRUE, 0);
-       gtk_widget_show(combo);
-
-       pw->search_label = gtk_label_new("");
-       gtk_box_pack_start(GTK_BOX(hbox), pw->search_label, TRUE, TRUE, 0);
-       gtk_widget_show(pw->search_label);
+    /* filter bar */
+    pw->filter_ui = pan_filter_ui_new(pw);
+    gtk_box_pack_start(GTK_BOX(vbox), pw->filter_ui->filter_box, FALSE, FALSE, 2);
 
        /* status bar */
 
@@ -2320,21 +1918,13 @@ static void pan_window_new_real(FileData *dir_fd)
        gtk_container_add(GTK_CONTAINER(frame), pw->label_zoom);
        gtk_widget_show(pw->label_zoom);
 
-       pw->search_button = gtk_toggle_button_new();
-       gtk_button_set_relief(GTK_BUTTON(pw->search_button), GTK_RELIEF_NONE);
-       gtk_button_set_focus_on_click(GTK_BUTTON(pw->search_button), FALSE);
-       hbox = gtk_hbox_new(FALSE, PREF_PAD_GAP);
-       gtk_container_add(GTK_CONTAINER(pw->search_button), hbox);
-       gtk_widget_show(hbox);
-       pw->search_button_arrow = gtk_arrow_new(GTK_ARROW_UP, GTK_SHADOW_NONE);
-       gtk_box_pack_start(GTK_BOX(hbox), pw->search_button_arrow, FALSE, FALSE, 0);
-       gtk_widget_show(pw->search_button_arrow);
-       pref_label_new(hbox, _("Find"));
-
-       gtk_box_pack_end(GTK_BOX(box), pw->search_button, FALSE, FALSE, 0);
-       gtk_widget_show(pw->search_button);
-       g_signal_connect(G_OBJECT(pw->search_button), "clicked",
-                        G_CALLBACK(pan_search_toggle_cb), pw);
+       // Add the "Find" button to the status bar area.
+       gtk_box_pack_end(GTK_BOX(box), pw->search_ui->search_button, FALSE, FALSE, 0);
+       gtk_widget_show(pw->search_ui->search_button);
+
+       // Add the "Filter" button to the status bar area.
+       gtk_box_pack_end(GTK_BOX(box), pw->filter_ui->filter_button, FALSE, FALSE, 0);
+       gtk_widget_show(pw->filter_ui->filter_button);
 
        g_signal_connect(G_OBJECT(pw->window), "delete_event",
                         G_CALLBACK(pan_window_delete_cb), pw);
index 7a7ddc7..a17c6ab 100644 (file)
@@ -25,6 +25,7 @@
 #include "main.h"
 #include "pan-types.h"
 
+void pan_layout_update(PanWindow *pw);
 GList *pan_layout_intersect(PanWindow *pw, gint x, gint y, gint width, gint height);
 void pan_layout_resize(PanWindow *pw);
 
@@ -32,6 +33,7 @@ void pan_cache_sync_date(PanWindow *pw, GList *list);
 
 GList *pan_cache_sort(GList *list, SortType method, gboolean ascend);
 
+void pan_info_update(PanWindow *pw, PanItem *pi);
 
 #endif
 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */
index f736ccc..07de0e7 100644 (file)
@@ -105,7 +105,8 @@ enum {
        PROP_WINDOW_LIMIT,
        PROP_WINDOW_LIMIT_VALUE,
        PROP_AUTOFIT_LIMIT,
-       PROP_AUTOFIT_LIMIT_VALUE
+       PROP_AUTOFIT_LIMIT_VALUE,
+       PROP_ENLARGEMENT_LIMIT_VALUE
 };
 
 typedef enum {
@@ -329,6 +330,16 @@ static void pixbuf_renderer_class_init(PixbufRendererClass *class)
                                                          100,
                                                          G_PARAM_READABLE | G_PARAM_WRITABLE));
 
+       g_object_class_install_property(gobject_class,
+                                       PROP_ENLARGEMENT_LIMIT_VALUE,
+                                       g_param_spec_uint("enlargement_limit_value",
+                                                         "Size increase limit of image when autofitting",
+                                                         NULL,
+                                                         100,
+                                                         999,
+                                                         500,
+                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
+
 
        signals[SIGNAL_ZOOM] =
                g_signal_new("zoom",
@@ -530,6 +541,9 @@ static void pixbuf_renderer_set_property(GObject *object, guint prop_id,
                case PROP_AUTOFIT_LIMIT_VALUE:
                        pr->autofit_limit_size = g_value_get_uint(value);
                        break;
+               case PROP_ENLARGEMENT_LIMIT_VALUE:
+                       pr->enlargement_limit_size = g_value_get_uint(value);
+                       break;
                default:
                        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
                        break;
@@ -593,6 +607,9 @@ static void pixbuf_renderer_get_property(GObject *object, guint prop_id,
                case PROP_AUTOFIT_LIMIT_VALUE:
                        g_value_set_uint(value, pr->autofit_limit_size);
                        break;
+               case PROP_ENLARGEMENT_LIMIT_VALUE:
+                       g_value_set_uint(value, pr->enlargement_limit_size);
+                       break;
                default:
                        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
                        break;
@@ -1665,6 +1682,17 @@ static gboolean pr_zoom_clamp(PixbufRenderer *pr, gdouble zoom,
                                scale = scale * factor;
                                }
 
+                       if (pr->zoom_expand)
+                               {
+                               gdouble factor = (gdouble)pr->enlargement_limit_size / 100;
+                               if (scale > factor)
+                                       {
+                                       w = w * factor / scale;
+                                       h = h * factor / scale;
+                                       scale = factor;
+                                       }
+                               }
+
                        if (w < 1) w = 1;
                        if (h < 1) h = 1;
                        }
@@ -1723,10 +1751,10 @@ static void pr_zoom_sync(PixbufRenderer *pr, gdouble zoom,
                }
 
        if (force) clamp_flags |= PR_ZOOM_INVALIDATE;
+       (void) pr_parent_window_resize(pr, pr->width, pr->height);
        if (!pr_zoom_clamp(pr, zoom, clamp_flags)) return;
 
        (void) pr_size_clamp(pr);
-       (void) pr_parent_window_resize(pr, pr->width, pr->height);
 
        if (force && new)
                {
index f81f585..c399e1f 100644 (file)
@@ -29,9 +29,7 @@
 #define IS_PIXBUF_RENDERER_CLASS(klass)        (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_PIXBUF_RENDERER))
 #define PIXBUF_RENDERER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_PIXBUF_RENDERER, PixbufRendererClass))
 
-/* alpha channel checkerboard background (same as gimp) */
-#define PR_ALPHA_CHECK1 0x00999999
-#define PR_ALPHA_CHECK2 0x00666666
+/* alpha channel checkerboard (same as gimp) */
 #define PR_ALPHA_CHECK_SIZE 16
 /* when scaling image to below this size, use nearest pixel for scaling
  * (below about 4, the other scale types become slow generating their conversion tables)
@@ -160,6 +158,7 @@ struct _PixbufRenderer
 
        gboolean autofit_limit;
        gint autofit_limit_size;
+       gint enlargement_limit_size;
 
        GdkColor color;
 
index 669fb73..1e7ad4c 100644 (file)
@@ -236,6 +236,7 @@ static void config_window_apply(void)
        options->image.max_window_size = c_options->image.max_window_size;
        options->image.limit_autofit_size = c_options->image.limit_autofit_size;
        options->image.max_autofit_size = c_options->image.max_autofit_size;
+       options->image.max_enlargement_size = c_options->image.max_enlargement_size;
        options->image.use_clutter_renderer = c_options->image.use_clutter_renderer;
        options->progressive_key_scrolling = c_options->progressive_key_scrolling;
        options->keyboard_scroll_step = c_options->keyboard_scroll_step;
@@ -297,6 +298,9 @@ static void config_window_apply(void)
                view_window_colors_update();
                }
 
+       options->image.alpha_color_1 = c_options->image.alpha_color_1;
+       options->image.alpha_color_2 = c_options->image.alpha_color_2;
+
        options->fullscreen.screen = c_options->fullscreen.screen;
        options->fullscreen.clean_flip = c_options->fullscreen.clean_flip;
        options->fullscreen.disable_saver = c_options->fullscreen.disable_saver;
@@ -334,6 +338,7 @@ static void config_window_apply(void)
        options->metadata.save_legacy_format = c_options->metadata.save_legacy_format;
        options->metadata.sync_grouped_files = c_options->metadata.sync_grouped_files;
        options->metadata.confirm_write = c_options->metadata.confirm_write;
+       options->metadata.sidecar_extended_name = c_options->metadata.sidecar_extended_name;
        options->metadata.confirm_timeout = c_options->metadata.confirm_timeout;
        options->metadata.confirm_after_timeout = c_options->metadata.confirm_after_timeout;
        options->metadata.confirm_on_image_change = c_options->metadata.confirm_on_image_change;
@@ -362,6 +367,11 @@ static void config_window_apply(void)
        options->stereo.fixed_x2 = c_options->stereo.fixed_x2;
        options->stereo.fixed_y2 = c_options->stereo.fixed_y2;
 
+       options->info_keywords.height = c_options->info_keywords.height;
+       options->info_title.height = c_options->info_title.height;
+       options->info_comment.height = c_options->info_comment.height;
+       options->info_rating.height = c_options->info_rating.height;
+
 #ifdef DEBUG
        set_debug_level(debug_c);
 #endif
@@ -441,7 +451,11 @@ static void config_window_ok_cb(GtkWidget *widget, gpointer data)
 
 static void config_window_apply_cb(GtkWidget *widget, gpointer data)
 {
+       LayoutWindow *lw;
+       lw = layout_window_list->data;
+
        config_window_apply();
+       layout_util_sync(lw);
 }
 
 static void config_window_save_cb(GtkWidget *widget, gpointer data)
@@ -1409,6 +1423,7 @@ static GtkWidget *scrolled_notebook_page(GtkWidget *notebook, const gchar *title
 static void config_tab_general(GtkWidget *notebook)
 {
        GtkWidget *vbox;
+       GtkWidget *hbox;
        GtkWidget *group;
        GtkWidget *subgroup;
        GtkWidget *button;
@@ -1463,6 +1478,22 @@ static void config_tab_general(GtkWidget *notebook)
 
        pref_checkbox_new_int(group, _("Refresh on file change"),
                              options->update_on_time_change, &c_options->update_on_time_change);
+
+       group = pref_group_new(vbox, FALSE, _("Info sidebar heights"), GTK_ORIENTATION_VERTICAL);
+       pref_label_new(group, _("NOTE! Geeqie must be restarted for changes to take effect"));
+       hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
+       pref_spin_new_int(hbox, _("Keywords:"), NULL,
+                                1, 9999, 1,
+                                options->info_keywords.height, &c_options->info_keywords.height);
+       pref_spin_new_int(hbox, _("Title:"), NULL,
+                                1, 9999, 1,
+                                options->info_title.height, &c_options->info_title.height);
+       pref_spin_new_int(hbox, _("Comment:"), NULL,
+                                1, 9999, 1,
+                                options->info_comment.height, &c_options->info_comment.height);
+       pref_spin_new_int(hbox, _("Rating:"), NULL,
+                                1, 9999, 1,
+                                options->info_rating.height, &c_options->info_rating.height);
 }
 
 /* image tab */
@@ -1474,6 +1505,7 @@ static void config_tab_image(GtkWidget *notebook)
        GtkWidget *group;
        GtkWidget *button;
        GtkWidget *ct_button;
+       GtkWidget *enlargement_button;
        GtkWidget *table;
        GtkWidget *spin;
 
@@ -1492,11 +1524,16 @@ static void config_tab_image(GtkWidget *notebook)
        pref_checkbox_new_int(group, _("Two pass rendering (apply HQ zoom and color correction in second pass)"),
                              options->image.zoom_2pass, &c_options->image.zoom_2pass);
 
-       pref_checkbox_new_int(group, _("Allow enlargement of image for zoom to fit"),
+       hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
+       enlargement_button = pref_checkbox_new_int(hbox, _("Allow enlargement of image for zoom to fit (max. size in %)"),
                              options->image.zoom_to_fit_allow_expand, &c_options->image.zoom_to_fit_allow_expand);
+       spin = pref_spin_new_int(hbox, NULL, NULL,
+                                100, 999, 1,
+                                options->image.max_enlargement_size, &c_options->image.max_enlargement_size);
+       pref_checkbox_link_sensitivity(enlargement_button, spin);
 
        hbox = pref_box_new(group, FALSE, GTK_ORIENTATION_HORIZONTAL, PREF_PAD_SPACE);
-       ct_button = pref_checkbox_new_int(hbox, _("Limit image size when autofitting (%):"),
+       ct_button = pref_checkbox_new_int(hbox, _("Limit image size when autofitting (% of window):"),
                                          options->image.limit_autofit_size, &c_options->image.limit_autofit_size);
        spin = pref_spin_new_int(hbox, NULL, NULL,
                                 10, 150, 1,
@@ -1509,33 +1546,6 @@ static void config_tab_image(GtkWidget *notebook)
                             G_CALLBACK(zoom_increment_cb), NULL);
        gtk_spin_button_set_update_policy(GTK_SPIN_BUTTON(spin), GTK_UPDATE_ALWAYS);
 
-       group = pref_group_new(vbox, FALSE, _("When new image is selected:"), GTK_ORIENTATION_HORIZONTAL);
-
-       vbox2 = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
-       c_options->image.zoom_mode = options->image.zoom_mode;
-       button = pref_radiobutton_new(vbox2, NULL, _("Zoom to original size"),
-                                     (options->image.zoom_mode == ZOOM_RESET_ORIGINAL),
-                                     G_CALLBACK(zoom_mode_cb), GINT_TO_POINTER(ZOOM_RESET_ORIGINAL));
-       button = pref_radiobutton_new(vbox2, button, _("Fit image to window"),
-                                     (options->image.zoom_mode == ZOOM_RESET_FIT_WINDOW),
-                                     G_CALLBACK(zoom_mode_cb), GINT_TO_POINTER(ZOOM_RESET_FIT_WINDOW));
-       button = pref_radiobutton_new(vbox2, button, _("Leave Zoom at previous setting"),
-                                     (options->image.zoom_mode == ZOOM_RESET_NONE),
-                                     G_CALLBACK(zoom_mode_cb), GINT_TO_POINTER(ZOOM_RESET_NONE));
-
-       vbox2 = pref_box_new(group, TRUE, GTK_ORIENTATION_VERTICAL, PREF_PAD_SPACE);
-       c_options->image.scroll_reset_method = options->image.scroll_reset_method;
-       button = pref_radiobutton_new(vbox2, NULL, _("Scroll to top left corner"),
-                                     (options->image.scroll_reset_method == SCROLL_RESET_TOPLEFT),
-                                     G_CALLBACK(scroll_reset_cb), GINT_TO_POINTER(SCROLL_RESET_TOPLEFT));
-       button = pref_radiobutton_new(vbox2, button, _("Scroll to image center"),
-                                     (options->image.scroll_reset_method == SCROLL_RESET_CENTER),
-                                     G_CALLBACK(scroll_reset_cb), GINT_TO_POINTER(SCROLL_RESET_CENTER));
-       button = pref_radiobutton_new(vbox2, button, _("Keep the region from previous image"),
-                                     (options->image.scroll_reset_method == SCROLL_RESET_NOCHANGE),
-                                     G_CALLBACK(scroll_reset_cb), GINT_TO_POINTER(SCROLL_RESET_NOCHANGE));
-
-
        group = pref_group_new(vbox, FALSE, _("Appearance"), GTK_ORIENTATION_VERTICAL);
 
        pref_checkbox_new_int(group, _("Use custom border color in window mode"),
@@ -1547,6 +1557,17 @@ static void config_tab_image(GtkWidget *notebook)
        pref_color_button_new(group, _("Border color"), &options->image.border_color,
                              G_CALLBACK(pref_color_button_set_cb), &c_options->image.border_color);
 
+       c_options->image.border_color = options->image.border_color;
+
+       pref_color_button_new(group, _("Alpha channel color 1"), &options->image.alpha_color_1,
+                             G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_1);
+
+       pref_color_button_new(group, _("Alpha channel color 2"), &options->image.alpha_color_2,
+                             G_CALLBACK(pref_color_button_set_cb), &c_options->image.alpha_color_2);
+
+       c_options->image.alpha_color_1 = options->image.alpha_color_1;
+       c_options->image.alpha_color_2 = options->image.alpha_color_2;
+
        group = pref_group_new(vbox, FALSE, _("Convenience"), GTK_ORIENTATION_VERTICAL);
 
        pref_checkbox_new_int(group, _("Auto rotate proofs using Exif information"),
@@ -1876,7 +1897,7 @@ static void config_tab_metadata(GtkWidget *notebook)
        label = pref_label_new(group, _("Metadata are written in the following order. The process ends after first success."));
        gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 
-       ct_button = pref_checkbox_new_int(group, _("1) Save metadata in image files, resp. sidecar files, according to the XMP standard"),
+       ct_button = pref_checkbox_new_int(group, _("1) Save metadata in image files, or sidecar files, according to the XMP standard"),
                              options->metadata.save_in_image_file, &c_options->metadata.save_in_image_file);
 #ifndef HAVE_EXIV2
        gtk_widget_set_sensitive(ct_button, FALSE);
@@ -1908,6 +1929,9 @@ static void config_tab_metadata(GtkWidget *notebook)
        pref_checkbox_new_int(hbox, _("Ask before writing to image files"),
                              options->metadata.confirm_write, &c_options->metadata.confirm_write);
 
+       pref_checkbox_new_int(hbox, _("Create sidecar files named image.ext.xmp (as opposed to image.xmp)"),
+                             options->metadata.sidecar_extended_name, &c_options->metadata.sidecar_extended_name);
+
        group = pref_group_new(vbox, FALSE, _("Step 2 and 3: write to Geeqie private files"), GTK_ORIENTATION_VERTICAL);
 #ifndef HAVE_EXIV2
        gtk_widget_set_sensitive(group, FALSE);
@@ -2172,6 +2196,9 @@ static void config_tab_behavior(GtkWidget *notebook)
 
        pref_spin_new_int(group, _("Debug level:"), NULL,
                          DEBUG_LEVEL_MIN, DEBUG_LEVEL_MAX, 1, get_debug_level(), &debug_c);
+
+       pref_spin_new_int(group, _("Log Window max. lines:"), NULL,
+                         1, 99999, 1, options->log_window_lines, &options->log_window_lines);
 #endif
 }
 
index bd61835..539ce64 100644 (file)
@@ -23,6 +23,7 @@
 #include "print.h"
 
 #include "filedata.h"
+#include "filefilter.h"
 #include "image.h"
 #include "image-load.h"
 #include "pixbuf_util.h"
@@ -1449,7 +1450,7 @@ static gboolean print_job_rgb_page_new(PrintWindow *pw, gint page)
                const gchar *ext;
                gchar *base;
 
-               ext = extension_from_path(pw->output_path);
+               ext = registered_extension_from_path(pw->output_path);
 
                if (ext)
                        {
index c86bf46..9ed1342 100644 (file)
@@ -322,6 +322,7 @@ static void write_global_attributes(GString *outstr, gint indent)
        WRITE_NL(); WRITE_UINT(*options, duplicates_select_type);
        WRITE_NL(); WRITE_BOOL(*options, duplicates_thumbnails);
        WRITE_NL(); WRITE_BOOL(*options, rot_invariant_sim);
+       WRITE_NL(); WRITE_BOOL(*options, sort_totals);
        WRITE_SEPARATOR();
 
        WRITE_NL(); WRITE_BOOL(*options, mousewheel_scrolls);
@@ -334,6 +335,8 @@ static void write_global_attributes(GString *outstr, gint indent)
        WRITE_NL(); WRITE_BOOL(*options, use_saved_window_positions_for_new_windows);
        WRITE_NL(); WRITE_BOOL(*options, tools_restore_state);
 
+       WRITE_NL(); WRITE_UINT(*options, log_window_lines);
+
        /* File operations Options */
        WRITE_NL(); WRITE_BOOL(*options, file_ops.enable_in_place_rename);
        WRITE_NL(); WRITE_BOOL(*options, file_ops.confirm_delete);
@@ -358,6 +361,7 @@ static void write_global_attributes(GString *outstr, gint indent)
        WRITE_NL(); WRITE_INT(*options, image.max_window_size);
        WRITE_NL(); WRITE_BOOL(*options, image.limit_autofit_size);
        WRITE_NL(); WRITE_INT(*options, image.max_autofit_size);
+       WRITE_NL(); WRITE_INT(*options, image.max_enlargement_size);
        WRITE_NL(); WRITE_UINT(*options, image.scroll_reset_method);
        WRITE_NL(); WRITE_INT(*options, image.tile_cache_max);
        WRITE_NL(); WRITE_INT(*options, image.image_cache_max);
@@ -366,6 +370,8 @@ static void write_global_attributes(GString *outstr, gint indent)
        WRITE_NL(); WRITE_BOOL(*options, image.use_custom_border_color);
        WRITE_NL(); WRITE_BOOL(*options, image.use_custom_border_color_in_fullscreen);
        WRITE_NL(); WRITE_COLOR(*options, image.border_color);
+       WRITE_NL(); WRITE_COLOR(*options, image.alpha_color_1);
+       WRITE_NL(); WRITE_COLOR(*options, image.alpha_color_2);
        WRITE_NL(); WRITE_BOOL(*options, image.use_clutter_renderer);
 
        /* Thumbnails Options */
@@ -442,6 +448,7 @@ static void write_global_attributes(GString *outstr, gint indent)
        WRITE_NL(); WRITE_BOOL(*options, metadata.save_legacy_format);
        WRITE_NL(); WRITE_BOOL(*options, metadata.sync_grouped_files);
        WRITE_NL(); WRITE_BOOL(*options, metadata.confirm_write);
+       WRITE_NL(); WRITE_BOOL(*options, metadata.sidecar_extended_name);
        WRITE_NL(); WRITE_INT(*options, metadata.confirm_timeout);
        WRITE_NL(); WRITE_BOOL(*options, metadata.confirm_after_timeout);
        WRITE_NL(); WRITE_BOOL(*options, metadata.confirm_on_image_change);
@@ -458,6 +465,12 @@ static void write_global_attributes(GString *outstr, gint indent)
        WRITE_NL(); WRITE_INT(*options, stereo.fixed_y1);
        WRITE_NL(); WRITE_INT(*options, stereo.fixed_x2);
        WRITE_NL(); WRITE_INT(*options, stereo.fixed_y2);
+
+       /* copy move rename */
+       WRITE_NL(); WRITE_INT(*options, cp_mv_rn.auto_start);
+       WRITE_NL(); WRITE_INT(*options, cp_mv_rn.auto_padding);
+       WRITE_NL(); WRITE_CHAR(*options, cp_mv_rn.auto_end);
+       WRITE_NL(); WRITE_INT(*options, cp_mv_rn.formatted_start);
 }
 
 static void write_color_profile(GString *outstr, gint indent)
@@ -602,6 +615,7 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar **
                if (READ_UINT_CLAMP(*options, duplicates_select_type, 0, DUPE_SELECT_GROUP2)) continue;
                if (READ_BOOL(*options, duplicates_thumbnails)) continue;
                if (READ_BOOL(*options, rot_invariant_sim)) continue;
+               if (READ_BOOL(*options, sort_totals)) continue;
 
                if (READ_BOOL(*options, progressive_key_scrolling)) continue;
                if (READ_UINT_CLAMP(*options, keyboard_scroll_step, 1, 32)) continue;
@@ -617,6 +631,8 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar **
                if (READ_BOOL(*options, use_saved_window_positions_for_new_windows)) continue;
                if (READ_BOOL(*options, tools_restore_state)) continue;
 
+               if (READ_INT(*options, log_window_lines)) continue;
+
                /* Properties dialog options */
                if (READ_CHAR(*options, properties.tabs_order)) continue;
 
@@ -629,6 +645,7 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar **
                if (READ_INT(*options, image.max_window_size)) continue;
                if (READ_BOOL(*options, image.limit_autofit_size)) continue;
                if (READ_INT(*options, image.max_autofit_size)) continue;
+               if (READ_INT(*options, image.max_enlargement_size)) continue;
                if (READ_UINT_CLAMP(*options, image.scroll_reset_method, 0, PR_SCROLL_RESET_COUNT - 1)) continue;
                if (READ_INT(*options, image.tile_cache_max)) continue;
                if (READ_INT(*options, image.image_cache_max)) continue;
@@ -639,6 +656,8 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar **
                if (READ_BOOL(*options, image.use_custom_border_color)) continue;
                if (READ_BOOL(*options, image.use_custom_border_color_in_fullscreen)) continue;
                if (READ_COLOR(*options, image.border_color)) continue;
+               if (READ_COLOR(*options, image.alpha_color_1)) continue;
+               if (READ_COLOR(*options, image.alpha_color_2)) continue;
                if (READ_BOOL(*options, image.use_clutter_renderer)) continue;
 
                /* Thumbnails options */
@@ -720,6 +739,7 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar **
                if (READ_BOOL(*options, metadata.save_legacy_format)) continue;
                if (READ_BOOL(*options, metadata.sync_grouped_files)) continue;
                if (READ_BOOL(*options, metadata.confirm_write)) continue;
+               if (READ_BOOL(*options, metadata.sidecar_extended_name)) continue;
                if (READ_BOOL(*options, metadata.confirm_after_timeout)) continue;
                if (READ_INT(*options, metadata.confirm_timeout)) continue;
                if (READ_BOOL(*options, metadata.confirm_on_image_change)) continue;
@@ -737,6 +757,12 @@ static gboolean load_global_params(const gchar **attribute_names, const gchar **
                if (READ_INT(*options, stereo.fixed_x2)) continue;
                if (READ_INT(*options, stereo.fixed_y2)) continue;
 
+               /* copy move rename */
+               if (READ_INT(*options, cp_mv_rn.auto_start))  continue;
+               if (READ_INT(*options, cp_mv_rn.auto_padding)) continue;
+               if (READ_CHAR(*options, cp_mv_rn.auto_end)) continue;
+               if (READ_INT(*options, cp_mv_rn.formatted_start)) continue;
+
                /* Dummy options */
                if (READ_DUMMY(*options, image.dither_quality, "deprecated since 2012-08-13")) continue;
 
@@ -940,6 +966,22 @@ static void options_parse_pane_exif(GQParserData *parser_data, GMarkupParseConte
                }
 }
 
+static void options_parse_pane_keywords(GQParserData *parser_data, GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer data, GError **error)
+{
+       GtkWidget *pane = data;
+
+       if (g_ascii_strcasecmp(element_name, "expanded") == 0)
+               {
+               bar_pane_keywords_entry_add_from_config(pane, attribute_names, attribute_values);
+               options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+               }
+       else
+               {
+               log_printf("unexpected in <pane_keywords>: <%s>\n", element_name);
+               options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+               }
+}
+
 static void options_parse_bar(GQParserData *parser_data, GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer data, GError **error)
 {
        GtkWidget *bar = data;
@@ -1015,7 +1057,7 @@ static void options_parse_bar(GQParserData *parser_data, GMarkupParseContext *co
                        pane = bar_pane_keywords_new_from_config(attribute_names, attribute_values);
                        bar_add(bar, pane);
                        }
-               options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
+               options_parse_func_push(parser_data, options_parse_pane_keywords, NULL, pane);
                }
        else if (g_ascii_strcasecmp(element_name, "clear") == 0)
                {
@@ -1055,12 +1097,17 @@ static void options_parse_layout(GQParserData *parser_data, GMarkupParseContext
 
                options_parse_func_push(parser_data, options_parse_bar, NULL, lw->bar);
                }
+#if 0
+/* FIXME: The sort manager and desktop files are set up in the idle loop.
+ * Setup is not yet completed when the layout is first displayed.
+ */
        else if (g_ascii_strcasecmp(element_name, "bar_sort") == 0)
                {
                GtkWidget *bar = bar_sort_new_from_config(lw, attribute_names, attribute_values);
                layout_bar_sort_set(lw, bar);
                options_parse_func_push(parser_data, options_parse_leaf, NULL, NULL);
                }
+#endif
        else if (g_ascii_strcasecmp(element_name, "toolbar") == 0)
                {
                options_parse_func_push(parser_data, options_parse_toolbar_and_statusbar, NULL, NULL);
index 9147bf9..e32f272 100644 (file)
@@ -22,6 +22,7 @@
 #include "main.h"
 #include "remote.h"
 
+#include "cache_maint.h"
 #include "collect.h"
 #include "filedata.h"
 #include "image.h"
@@ -51,6 +52,7 @@
 
 static RemoteConnection *remote_client_open(const gchar *path);
 static gint remote_client_send(RemoteConnection *rc, const gchar *text);
+static void gr_raise(const gchar *text, GIOChannel *channel, gpointer data);
 
 
 typedef struct _RemoteClient RemoteClient;
@@ -429,6 +431,54 @@ static void gr_slideshow_start_rec(const gchar *text, GIOChannel *channel, gpoin
        layout_image_slideshow_start_from_list(NULL, list);
 }
 
+static void gr_cache_thumb(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       if (!g_strcmp0(text, "clear"))
+               cache_maintain_home_remote(FALSE, TRUE);
+       else if (!g_strcmp0(text, "clean"))
+               cache_maintain_home_remote(FALSE, FALSE);
+}
+
+static void gr_cache_shared(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       if (!g_strcmp0(text, "clear"))
+               cache_manager_standard_process_remote(TRUE);
+       else if (!g_strcmp0(text, "clean"))
+               cache_manager_standard_process_remote(FALSE);
+}
+
+static void gr_cache_metadata(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       cache_maintain_home_remote(TRUE, FALSE);
+}
+
+static void gr_cache_clear(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       cache_maintain_home_remote(FALSE, TRUE);
+}
+
+static void gr_cache_render(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       cache_manager_render_remote(text, FALSE, FALSE);
+}
+
+static void gr_cache_render_recurse(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       cache_manager_render_remote(text, TRUE, FALSE);
+}
+
+static void gr_cache_render_standard(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       if(options->thumbnails.spec_standard)
+               cache_manager_render_remote(text, FALSE, TRUE);
+}
+
+static void gr_cache_render_standard_recurse(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       if(options->thumbnails.spec_standard)
+               cache_manager_render_remote(text, TRUE, TRUE);
+}
+
 static void gr_slideshow_toggle(const gchar *text, GIOChannel *channel, gpointer data)
 {
        layout_image_slideshow_toggle(NULL);
@@ -495,7 +545,7 @@ static void gr_quit(const gchar *text, GIOChannel *channel, gpointer data)
        g_idle_add(gr_quit_idle_cb, NULL);
 }
 
-static void gr_file_load(const gchar *text, GIOChannel *channel, gpointer data)
+static void gr_file_load_no_raise(const gchar *text, GIOChannel *channel, gpointer data)
 {
        gchar *filename = expand_tilde(text);
 
@@ -523,12 +573,22 @@ static void gr_file_load(const gchar *text, GIOChannel *channel, gpointer data)
        g_free(filename);
 }
 
+static void gr_file_load(const gchar *text, GIOChannel *channel, gpointer data)
+{
+       gr_file_load_no_raise(text, channel, data);
+
+       gr_raise(text, channel, data);
+}
+
 static void gr_file_tell(const gchar *text, GIOChannel *channel, gpointer data)
 {
        LayoutWindow *lw = NULL; /* NULL to force layout_valid() to do some magic */
        if (!layout_valid(&lw)) return;
        if (image_get_path(lw->image))
-               printf_term("%s %s\n", GQ_APPNAME, image_get_path(lw->image));
+               {
+               g_io_channel_write_chars(channel, image_get_path(lw->image), -1, NULL, NULL);
+               g_io_channel_write_chars(channel, "\n", -1, NULL, NULL);
+               }
 }
 
 static void gr_config_load(const gchar *text, GIOChannel *channel, gpointer data)
@@ -674,12 +734,20 @@ static RemoteCommandEntry remote_commands[] = {
        { NULL, "--config-load:",       gr_config_load,         TRUE,  FALSE, N_("<FILE>"), N_("load configuration from FILE") },
        { NULL, "--get-sidecars:",      gr_get_sidecars,        TRUE,  FALSE, N_("<FILE>"), N_("get list of sidecars of FILE") },
        { NULL, "--get-destination:",   gr_get_destination,     TRUE,  FALSE, N_("<FILE>"), N_("get destination path of FILE") },
-       { NULL, "file:",                gr_file_load,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE") },
+       { NULL, "file:",                gr_file_load,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE, bring Geeqie window to the top") },
+       { NULL, "File:",                gr_file_load_no_raise,  TRUE,  FALSE, N_("<FILE>"), N_("open FILE, do not bring Geeqie window to the top") },
        { NULL, "--tell",               gr_file_tell,           FALSE, FALSE, NULL, N_("print filename of current image") },
        { NULL, "view:",                gr_file_view,           TRUE,  FALSE, N_("<FILE>"), N_("open FILE in new window") },
        { NULL, "--list-clear",         gr_list_clear,          FALSE, FALSE, NULL, N_("clear command line collection list") },
        { NULL, "--list-add:",          gr_list_add,            TRUE,  FALSE, N_("<FILE>"), N_("add FILE to command line collection list") },
        { NULL, "raise",                gr_raise,               FALSE, FALSE, NULL, N_("bring the Geeqie window to the top") },
+       { "-ct:", "--cache-thumbs:",    gr_cache_thumb,         TRUE, FALSE, N_("clear|clean"), N_("clear or clean thumbnail cache") },
+       { "-cs:", "--cache-shared:",    gr_cache_shared,        TRUE, FALSE, N_("clear|clean"), N_("clear or clean shared thumbnail cache") },
+       { "-cm","--cache-metadata",      gr_cache_metadata,               FALSE, FALSE, NULL, N_("    clean the metadata cache") },
+       { "-cr:", "--cache-render:",    gr_cache_render,        TRUE, FALSE, N_("<folder>  "), N_(" render thumbnails") },
+       { "-crr:", "--cache-render-recurse:", gr_cache_render_recurse, TRUE, FALSE, N_("<folder> "), N_("render thumbnails recursively") },
+       { "-crs:", "--cache-render-shared:", gr_cache_render_standard, TRUE, FALSE, N_("<folder> "), N_(" render thumbnails (see Help)") },
+       { "-crsr:", "--cache-render-shared-recurse:", gr_cache_render_standard_recurse, TRUE, FALSE, N_("<folder>"), N_(" render thumbnails recursively (see Help)") },
        { NULL, NULL, NULL, FALSE, FALSE, NULL }
 };
 
index c6d87cb..6a034e1 100644 (file)
@@ -1272,7 +1272,13 @@ static void rt_tile_get_region(gboolean has_alpha,
                                         scale_x, scale_y,
                                         interp_type,
                                         255, check_x, check_y,
-                                        PR_ALPHA_CHECK_SIZE, PR_ALPHA_CHECK1, PR_ALPHA_CHECK2);
+                                        PR_ALPHA_CHECK_SIZE,
+                                        ((options->image.alpha_color_1.red << 8 & 0x00FF0000) +
+                                        (options->image.alpha_color_1.green & 0x00FF00) +
+                                        (options->image.alpha_color_1.blue >> 8 & 0x00FF)),
+                                        ((options->image.alpha_color_2.red << 8 & 0x00FF0000) +
+                                        (options->image.alpha_color_2.green & 0x00FF00) +
+                                        (options->image.alpha_color_2.blue >> 8 & 0x00FF)));
                }
 }
 
index a858ac7..a2f53b3 100644 (file)
@@ -32,6 +32,7 @@
 #include "image-load.h"
 #include "img-view.h"
 #include "layout.h"
+#include "math.h"
 #include "menu.h"
 #include "metadata.h"
 #include "misc.h"
@@ -60,7 +61,6 @@
 #define SEARCH_BUFFER_MATCH_MISS 1
 #define SEARCH_BUFFER_FLUSH_SIZE 99
 
-
 typedef enum {
        SEARCH_MATCH_NONE,
        SEARCH_MATCH_EQUAL,
@@ -138,6 +138,11 @@ struct _SearchData
        GtkWidget *menu_comment;
        GtkWidget *entry_comment;
 
+       GtkWidget *check_rating;
+       GtkWidget *menu_rating;
+       GtkWidget *spin_rating;
+       GtkWidget *spin_rating_end;
+
        FileData *search_dir_fd;
        gboolean   search_path_recurse;
        gchar *search_name;
@@ -159,6 +164,8 @@ struct _SearchData
        CacheData *search_similarity_cd;
        GList *search_keyword_list;
        gchar *search_comment;
+       gint   search_rating;
+       gint   search_rating_end;
        gboolean   search_comment_match_case;
        gboolean   search_date_exif;
 
@@ -170,6 +177,8 @@ struct _SearchData
        MatchType match_dimensions;
        MatchType match_keywords;
        MatchType match_comment;
+       MatchType match_rating;
+       MatchType match_gps;
 
        gboolean match_name_enable;
        gboolean match_size_enable;
@@ -178,6 +187,7 @@ struct _SearchData
        gboolean match_similarity_enable;
        gboolean match_keywords_enable;
        gboolean match_comment_enable;
+       gboolean match_rating_enable;
 
        GList *search_folder_list;
        GList *search_done_list;
@@ -199,6 +209,18 @@ struct _SearchData
        ThumbLoader *thumb_loader;
        gboolean thumb_enable;
        FileData *thumb_fd;
+
+       /* Used for lat/long coordinate search
+       */
+       gint search_gps;
+       gdouble search_lat, search_lon;
+       GtkWidget *entry_gps_coord;
+       GtkWidget *check_gps;
+       GtkWidget *spin_gps;
+       GtkWidget *units_gps;
+       GtkWidget *menu_gps;
+       gboolean match_gps_enable;
+
 };
 
 typedef struct _MatchFileData MatchFileData;
@@ -253,6 +275,20 @@ static const MatchList text_search_menu_comment[] = {
        { N_("miss"),           SEARCH_MATCH_NONE }
 };
 
+
+static const MatchList text_search_menu_rating[] = {
+       { N_("equal to"),       SEARCH_MATCH_EQUAL },
+       { N_("less than"),      SEARCH_MATCH_UNDER },
+       { N_("greater than"),   SEARCH_MATCH_OVER },
+       { N_("between"),        SEARCH_MATCH_BETWEEN }
+};
+
+static const MatchList text_search_menu_gps[] = {
+       { N_("not geocoded"),   SEARCH_MATCH_NONE },
+       { N_("less than"),      SEARCH_MATCH_UNDER },
+       { N_("greater than"),   SEARCH_MATCH_OVER }
+};
+
 static GList *search_window_list = NULL;
 
 
@@ -1356,6 +1392,12 @@ static GtkTargetEntry result_drag_types[] = {
 };
 static gint n_result_drag_types = 2;
 
+static GtkTargetEntry result_drop_types[] = {
+       { "text/uri-list", 0, TARGET_URI_LIST },
+       { "text/plain", 0, TARGET_TEXT_PLAIN }
+};
+static gint n_result_drop_types = 2;
+
 static void search_dnd_data_set(GtkWidget *widget, GdkDragContext *context,
                                GtkSelectionData *selection_data, guint info,
                                guint time, gpointer data)
@@ -1402,6 +1444,45 @@ static void search_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointe
                }
 }
 
+static void search_gps_dnd_received_cb(GtkWidget *pane, GdkDragContext *context,
+                                                                               gint x, gint y,
+                                                                               GtkSelectionData *selection_data, guint info,
+                                                                               guint time, gpointer data)
+{
+       SearchData *sd = data;
+       GList *list;
+       gdouble latitude, longitude;
+       FileData *fd;
+
+       if (info == TARGET_URI_LIST)
+               {
+               list = uri_filelist_from_gtk_selection_data(selection_data);
+
+               /* If more than one file, use only the first file in a list.
+               */
+               if (list != NULL)
+                       {
+                       fd = list->data;
+                       latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+                       longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+                       if (latitude != 1000 && longitude != 1000)
+                               {
+                               gtk_entry_set_text(GTK_ENTRY(sd->entry_gps_coord),
+                                                       g_strdup_printf("%lf %lf", latitude, longitude));
+                               }
+                       else
+                               {
+                               gtk_entry_set_text(GTK_ENTRY(sd->entry_gps_coord), "Image is not geocoded");
+                               }
+                       }
+               }
+
+       if (info == TARGET_TEXT_PLAIN)
+               {
+               gtk_entry_set_text(GTK_ENTRY(sd->entry_gps_coord),"");
+               }
+}
+
 static void search_dnd_init(SearchData *sd)
 {
        gtk_drag_source_set(sd->result_view, GDK_BUTTON1_MASK | GDK_BUTTON2_MASK,
@@ -1411,6 +1492,14 @@ static void search_dnd_init(SearchData *sd)
                         G_CALLBACK(search_dnd_data_set), sd);
        g_signal_connect(G_OBJECT(sd->result_view), "drag_begin",
                         G_CALLBACK(search_dnd_begin), sd);
+
+       gtk_drag_dest_set(GTK_WIDGET(sd->entry_gps_coord),
+                                        GTK_DEST_DEFAULT_ALL,
+                                         result_drop_types, n_result_drop_types,
+                                        GDK_ACTION_COPY);
+
+       g_signal_connect(G_OBJECT(sd->entry_gps_coord), "drag_data_received",
+                                       G_CALLBACK(search_gps_dnd_received_cb), sd);
 }
 
 /*
@@ -1890,8 +1979,88 @@ static gboolean search_file_next(SearchData *sd)
                        }
                }
 
-       if ((match || extra_only) &&
-           (sd->match_dimensions_enable || sd->match_similarity_enable))
+       if (match && sd->match_rating_enable)
+               {
+               tested = TRUE;
+               match = FALSE;
+               gint rating;
+
+               rating = metadata_read_int(fd, RATING_KEY, 0);
+               if (sd->match_rating == SEARCH_MATCH_EQUAL)
+                       {
+                       match = (rating == sd->search_rating);
+                       }
+               else if (sd->match_rating == SEARCH_MATCH_UNDER)
+                       {
+                       match = (rating < sd->search_rating);
+                       }
+               else if (sd->match_rating == SEARCH_MATCH_OVER)
+                       {
+                       match = (rating > sd->search_rating);
+                       }
+               else if (sd->match_rating == SEARCH_MATCH_BETWEEN)
+                       {
+                       match = MATCH_IS_BETWEEN(rating, sd->search_rating, sd->search_rating_end);
+                       }
+               }
+
+       if (match && sd->match_gps_enable)
+               {
+               /* Calculate the distance the image is from the specified origin.
+               * This is a standard algorithm. A simplified one may be faster.
+               */
+               #define RADIANS  0.0174532925
+               #define KM_EARTH_RADIUS 6371
+               #define MILES_EARTH_RADIUS 3959
+               #define NAUTICAL_MILES_EARTH_RADIUS 3440
+
+               gdouble latitude, longitude, range, conversion;
+
+               if (g_strcmp0(gtk_combo_box_text_get_active_text(
+                                               GTK_COMBO_BOX_TEXT(sd->units_gps)), _("km")) == 0)
+                       {
+                       conversion = KM_EARTH_RADIUS;
+                       }
+               else if (g_strcmp0(gtk_combo_box_text_get_active_text(
+                                               GTK_COMBO_BOX_TEXT(sd->units_gps)), _("miles")) == 0)
+                       {
+                       conversion = MILES_EARTH_RADIUS;
+                       }
+               else
+                       {
+                       conversion = NAUTICAL_MILES_EARTH_RADIUS;
+                       }
+
+               tested = TRUE;
+               match = FALSE;
+
+               latitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLatitude", 1000);
+               longitude = metadata_read_GPS_coord(fd, "Xmp.exif.GPSLongitude", 1000);
+               if (latitude != 1000 && longitude != 1000)
+                       {
+                       range = conversion * acos(sin(latitude * RADIANS) *
+                                               sin(sd->search_lat * RADIANS) + cos(latitude * RADIANS) *
+                                               cos(sd->search_lat * RADIANS) * cos((sd->search_lon -
+                                               longitude) * RADIANS));
+                       if (sd->match_gps == SEARCH_MATCH_UNDER)
+                               {
+                               if (sd->search_gps >= range)
+                                       match = TRUE;
+                               }
+                       else if (sd->match_gps == SEARCH_MATCH_OVER)
+                               {
+                               if (sd->search_gps < range)
+                                       match = TRUE;
+                               }
+                       }
+               else if (sd->match_gps == SEARCH_MATCH_NONE)
+                       {
+                       match = TRUE;
+                       }
+               }
+
+       if ((match || extra_only) && (sd->match_dimensions_enable ||
+                                                               sd ->match_similarity_enable))
                {
                tested = TRUE;
 
@@ -2122,6 +2291,7 @@ static void search_start_cb(GtkWidget *widget, gpointer data)
        SearchData *sd = data;
        GtkTreeViewColumn *column;
        gchar *path;
+       gchar *entry_text;
 
        if (sd->search_folder_list)
                {
@@ -2152,6 +2322,32 @@ static void search_start_cb(GtkWidget *widget, gpointer data)
                tab_completion_append_to_history(sd->entry_similarity, sd->search_similarity_path);
                }
 
+       /* Check the coordinate entry.
+       * If the result is not sensible, it should get blocked.
+       */
+       if (sd->match_gps_enable)
+               {
+               if (sd->match_gps != SEARCH_MATCH_NONE)
+                       {
+                       entry_text = decode_geo_parameters(gtk_entry_get_text(
+                                                                               GTK_ENTRY(sd->entry_gps_coord)));
+
+                       sd->search_lat = 1000;
+                       sd->search_lon = 1000;
+                       sscanf(entry_text," %lf  %lf ", &sd->search_lat, &sd->search_lon );
+                       if (!(entry_text != NULL && !g_strstr_len(entry_text, -1, "Error") &&
+                                               sd->search_lat >= -90 && sd->search_lat <= 90 &&
+                                               sd->search_lon >= -180 && sd->search_lon <= 180))
+                               {
+                               file_util_warning_dialog(_(
+                                               "Entry does not contain a valid lat/long value"),
+                                                       entry_text, GTK_STOCK_DIALOG_WARNING, sd->window);
+                               return;
+                               }
+                       g_free(entry_text);
+                       }
+               }
+
        string_list_free(sd->search_keyword_list);
        sd->search_keyword_list = keyword_list_pull(sd->entry_keywords);
 
@@ -2384,6 +2580,16 @@ static void menu_choice_size_cb(GtkWidget *combo, gpointer data)
                                (sd->match_size == SEARCH_MATCH_BETWEEN));
 }
 
+static void menu_choice_rating_cb(GtkWidget *combo, gpointer data)
+{
+       SearchData *sd = data;
+
+       if (!menu_choice_get_match_type(combo, &sd->match_rating)) return;
+
+       menu_choice_set_visible(gtk_widget_get_parent(sd->spin_rating_end),
+                               (sd->match_rating == SEARCH_MATCH_BETWEEN));
+}
+
 static void menu_choice_date_cb(GtkWidget *combo, gpointer data)
 {
        SearchData *sd = data;
@@ -2425,6 +2631,16 @@ static void menu_choice_spin_cb(GtkAdjustment *adjustment, gpointer data)
        *value = (gint)gtk_adjustment_get_value(adjustment);
 }
 
+static void menu_choice_gps_cb(GtkWidget *combo, gpointer data)
+{
+       SearchData *sd = data;
+
+       if (!menu_choice_get_match_type(combo, &sd->match_gps)) return;
+
+       menu_choice_set_visible(gtk_widget_get_parent(sd->spin_gps),
+                                       (sd->match_gps != SEARCH_MATCH_NONE));
+}
+
 static GtkWidget *menu_spin(GtkWidget *box, gdouble min, gdouble max, gint value,
                            GCallback func, gpointer data)
 {
@@ -2602,11 +2818,15 @@ void search_new(FileData *dir_fd, FileData *example_file)
        sd->match_dimensions = SEARCH_MATCH_EQUAL;
        sd->match_keywords = SEARCH_MATCH_ALL;
        sd->match_comment = SEARCH_MATCH_CONTAINS;
+       sd->match_rating = SEARCH_MATCH_EQUAL;
 
        sd->match_name_enable = TRUE;
 
        sd->search_similarity = 95;
 
+       sd->search_gps = 1;
+       sd->match_gps = SEARCH_MATCH_NONE;
+
        if (example_file)
                {
                sd->search_similarity_path = g_strdup(example_file->path);
@@ -2767,6 +2987,51 @@ void search_new(FileData *dir_fd, FileData *example_file)
        pref_checkbox_new_int(hbox, _("Match case"),
                              sd->search_comment_match_case, &sd->search_comment_match_case);
 
+       /* Search for image rating */
+       hbox = menu_choice(sd->box_search, &sd->check_rating, &sd->menu_rating,
+                          _("Image rating is"), &sd->match_rating_enable,
+                          text_search_menu_rating, sizeof(text_search_menu_rating) / sizeof(MatchList),
+                          G_CALLBACK(menu_choice_rating_cb), sd);
+       sd->spin_size = menu_spin(hbox, -1, 5, sd->search_rating,
+                                 G_CALLBACK(menu_choice_spin_cb), &sd->search_rating);
+       hbox2 = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+       gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0);
+       pref_label_new(hbox2, _("and"));
+       sd->spin_rating_end = menu_spin(hbox2, -1, 5, sd->search_rating_end,
+                                     G_CALLBACK(menu_choice_spin_cb), &sd->search_rating_end);
+
+       /* Search for images within a specified range of a lat/long coordinate
+       */
+       hbox = menu_choice(sd->box_search, &sd->check_gps, &sd->menu_gps,
+                          _("Image is"), &sd->match_gps_enable,
+                          text_search_menu_gps, sizeof(text_search_menu_gps) / sizeof(MatchList),
+                          G_CALLBACK(menu_choice_gps_cb), sd);
+
+       hbox2 = gtk_hbox_new(FALSE, PREF_PAD_SPACE);
+       gtk_box_pack_start(GTK_BOX(hbox), hbox2, FALSE, FALSE, 0);
+       sd->spin_gps = menu_spin(hbox2, 1, 9999, sd->search_gps,
+                                                                  G_CALLBACK(menu_choice_spin_cb), &sd->search_gps);
+
+       sd->units_gps = gtk_combo_box_text_new();
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sd->units_gps), _("km"));
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sd->units_gps), _("miles"));
+       gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(sd->units_gps), _("n.m."));
+       gtk_box_pack_start(GTK_BOX(hbox2), sd->units_gps, FALSE, FALSE, 0);
+       gtk_combo_box_set_active(GTK_COMBO_BOX(sd->units_gps), 0);
+       gtk_widget_set_tooltip_text(sd->units_gps, "kilometres, miles or nautical miles");
+       gtk_widget_show(sd->units_gps);
+
+       pref_label_new(hbox2, _("from"));
+
+       sd->entry_gps_coord = gtk_entry_new();
+       gtk_editable_set_editable(GTK_EDITABLE(sd->entry_gps_coord), TRUE);
+       gtk_widget_set_has_tooltip(sd->entry_gps_coord, TRUE);
+       gtk_widget_set_tooltip_text(sd->entry_gps_coord, _("Enter a coordinate in the form:\n89.123 179.456\nor drag-and-drop a geo-coded image\nor left-click on the map and paste\nor cut-and-paste or drag-and-drop\nan internet search URL\nSee the Help file"));
+       gtk_box_pack_start(GTK_BOX(hbox2), sd->entry_gps_coord, TRUE, TRUE, 0);
+       gtk_widget_set_sensitive(sd->entry_gps_coord, TRUE);
+
+       gtk_widget_show(sd->entry_gps_coord);
+
        /* Done the types of searches */
 
        scrolled = gtk_scrolled_window_new(NULL, NULL);
index c12b602..85eff68 100644 (file)
@@ -298,7 +298,6 @@ secure_close(SecureSaveInfo *ssi)
                                utime(ssi->tmp_file_name, &tb);
                                }
                        }
-               DEBUG_3("rename %s -> %s", ssi->tmp_file_name, ssi->file_name);
                if (rename(ssi->tmp_file_name, ssi->file_name) == -1) {
                        ret = errno;
                        secsave_errno = SS_ERR_RENAME;
index 11e1e99..9d7db51 100644 (file)
@@ -66,7 +66,8 @@ typedef enum {
        SORT_CTIME,
        SORT_PATH,
        SORT_NUMBER,
-       SORT_EXIFTIME
+       SORT_EXIFTIME,
+       SORT_RATING
 } SortType;
 
 typedef enum {
@@ -251,6 +252,13 @@ typedef enum {
 
 #define MAX_SPLIT_IMAGES 4
 
+typedef enum {
+       SELECTION_NONE          = 0,
+       SELECTION_SELECTED      = 1 << 0,
+       SELECTION_PRELIGHT      = 1 << 1,
+       SELECTION_FOCUS         = 1 << 2
+} SelectionType;
+
 typedef struct _ImageLoader ImageLoader;
 typedef struct _ThumbLoader ThumbLoader;
 
@@ -517,6 +525,8 @@ struct _ImageWindow
        gint orientation;
        gboolean desaturate;
        gint user_stereo;
+
+       gboolean mouse_wheel_mode;
 };
 
 #define FILEDATA_MARKS_SIZE 6
@@ -536,6 +546,7 @@ struct _FileData {
        gchar *path;
        const gchar *name;
        const gchar *extension;
+       gchar *extended_extension;
        gchar *collate_key_name;
        gchar *collate_key_name_nocase;
        gint64 size;
@@ -570,6 +581,9 @@ struct _FileData {
        time_t exifdate;
        GHashTable *modified_xmp; // hash table which contains unwritten xmp metadata in format: key->list of string values
        GList *cached_metadata;
+       gint rating;
+
+       SelectionType selected;  // Used by view_file_icon.
 };
 
 struct _LayoutOptions
@@ -620,10 +634,26 @@ struct _LayoutOptions
                gint histogram_mode;
        } image_overlay;
 
+       struct {
+               gint w;
+               gint h;
+               gint x;
+               gint y;
+               gboolean paused;
+       } log_window;
+
        gboolean tools_float;
        gboolean tools_hidden;
        gboolean toolbar_hidden;
 
+       struct {
+               gboolean info;
+               gboolean sort;
+               gboolean tools_float;
+               gboolean tools_hidden;
+               gboolean hidden;
+       } bars_state;
+
        gchar *home_path;
        gchar *last_path;
 
@@ -753,6 +783,8 @@ struct _LayoutWindow
        GtkWidget *exif_window;
 
        AnimationData *animation;
+
+       GtkWidget *log_window;
 };
 
 struct _ViewDir
@@ -800,6 +832,7 @@ struct _ViewDirInfoTree
 struct _ViewFile
 {
        FileViewType type;
+       // TODO(xsdg): Turn this into a union (see VFLIST and VFICON from view_file.h).
        gpointer info;
 
        GtkWidget *widget;
@@ -853,20 +886,6 @@ struct _ViewFileInfoList
        guint select_idle_id; /* event source id */
 };
 
-typedef enum {
-       SELECTION_NONE          = 0,
-       SELECTION_SELECTED      = 1 << 0,
-       SELECTION_PRELIGHT      = 1 << 1,
-       SELECTION_FOCUS         = 1 << 2
-} SelectionType;
-
-typedef struct _IconData IconData;
-struct _IconData
-{
-       SelectionType selected;
-       FileData *fd;
-};
-
 struct _ViewFileInfoIcon
 {
        /* table stuff */
@@ -874,15 +893,15 @@ struct _ViewFileInfoIcon
        gint rows;
 
        GList *selection;
-       struct _IconData *prev_selection;
+       FileData *prev_selection;
 
        GtkWidget *tip_window;
        guint tip_delay_id; /* event source id */
-       struct _IconData *tip_id;
+       FileData *tip_fd;
 
-       struct _IconData *click_id;
+       FileData *click_fd;
 
-       struct _IconData *focus_id;
+       FileData *focus_fd;
        gint focus_row;
        gint focus_column;
 
@@ -965,11 +984,15 @@ struct _CommandLine
        gboolean startup_command_line_collection;
        gboolean tools_hide;
        gboolean tools_show;
+       gboolean log_window_show;
        gchar *path;
        gchar *file;
        GList *cmd_list;
        GList *collection_list;
        gchar *geometry;
+       gchar *regexp;
+       gchar *log_file;
+       SecureSaveInfo *ssi;
 };
 
 #endif
index c75ead5..c4466fc 100644 (file)
@@ -41,6 +41,9 @@
 #include "ui_utildlg.h"        /* for locale warning dialog */
 #include "md5-util.h"
 
+#include "filefilter.h"
+#include "secure_save.h"
+
 /*
  *-----------------------------------------------------------------------------
  * generic file information and manipulation routines (public)
@@ -55,6 +58,8 @@ void print_term(const gchar *text_utf8)
 
        text_l = g_locale_from_utf8(text_utf8, -1, NULL, NULL, NULL);
        fputs((text_l) ? text_l : text_utf8, stderr);
+       if(command_line && command_line->ssi)
+               secure_fputs(command_line->ssi, (text_l) ? text_l : text_utf8);
        g_free(text_l);
 }
 
@@ -697,7 +702,7 @@ gchar *unique_filename_simple(const gchar *path)
        name = filename_from_path(path);
        if (!name) return NULL;
 
-       ext = extension_from_path(name);
+       ext = registered_extension_from_path(name);
 
        if (!ext)
                {
@@ -729,24 +734,14 @@ const gchar *filename_from_path(const gchar *path)
 
 gchar *remove_level_from_path(const gchar *path)
 {
-       gint p = 0, n = -1;
+       const gchar *base;
 
        if (!path) return NULL;
 
-       while (path[p])
-               {
-               if (path[p] == G_DIR_SEPARATOR) n = p;
-               p++;
-               }
-       if (n <= 0) n++;
-
-       return g_strndup(path, (gsize) n);
-}
+       base = strrchr(path, G_DIR_SEPARATOR);
+       if (base) return g_strndup(path, strlen(path)-strlen(base));
 
-const gchar *extension_from_path(const gchar *path)
-{
-       if (!path) return NULL;
-       return strrchr(path, '.');
+       return NULL;
 }
 
 gboolean file_extension_match(const gchar *path, const gchar *ext)
@@ -766,18 +761,13 @@ gboolean file_extension_match(const gchar *path, const gchar *ext)
 
 gchar *remove_extension_from_path(const gchar *path)
 {
-       gint p = 0, n = -1;
+       const gchar *reg_ext;
 
        if (!path) return NULL;
 
-       while (path[p])
-               {
-               if (path[p] == '.') n = p;
-               p++;
-               }
-       if (n < 0) n = p;
+       reg_ext = registered_extension_from_path(path);
 
-       return g_strndup(path, (gsize) n);
+       return g_strndup(path, strlen(path) - (reg_ext == NULL ? 0 : strlen(reg_ext)));
 }
 
 void parse_out_relatives(gchar *path)
index eb25885..8f30502 100644 (file)
@@ -91,7 +91,6 @@ gchar *unique_filename_simple(const gchar *path);
 const gchar *filename_from_path(const gchar *path);
 gchar *remove_level_from_path(const gchar *path);
 
-const gchar *extension_from_path(const gchar *path);
 gchar *remove_extension_from_path(const gchar *path);
 
 gboolean file_extension_match(const gchar *path, const gchar *ext);
index e5fc70a..88f3924 100644 (file)
@@ -786,6 +786,7 @@ static void dest_new_dir_cb(GtkWidget *widget, gpointer data)
 
                tree_edit_by_path(GTK_TREE_VIEW(dd->d_view), dd->right_click_path, 0, text,
                                  dest_view_rename_cb, dd);
+               gtk_entry_set_text(GTK_ENTRY(dd->entry), path);
                }
 
        g_free(path);
@@ -1145,6 +1146,9 @@ GtkWidget *path_selection_new_with_files(GtkWidget *entry, const gchar *path,
                store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
 
                dd->filter_combo = gtk_combo_box_new_with_model_and_entry(GTK_TREE_MODEL(store));
+               gtk_combo_box_set_entry_text_column(GTK_COMBO_BOX(dd->filter_combo),
+                                                                                                               FILTER_COLUMN_FILTER);
+
                g_object_unref(store);
                gtk_cell_layout_clear(GTK_CELL_LAYOUT(dd->filter_combo));
                renderer = gtk_cell_renderer_text_new();
index 599244e..1cb2632 100644 (file)
@@ -225,7 +225,7 @@ static gint filename_base_length(const gchar *name)
                {
                const gchar *ext;
 
-               ext = extension_from_path(name);
+               ext = registered_extension_from_path(name);
                if (ext) n -= strlen(ext);
                }
 
@@ -1205,7 +1205,7 @@ static gchar *file_util_rename_multiple_auto_format_name(const gchar *format, co
                parsed = tmp;
                }
 
-       ext = extension_from_path(name);
+       ext = registered_extension_from_path(name);
 
        middle = strchr(parsed, '*');
        if (middle)
@@ -1291,13 +1291,19 @@ static void file_util_rename_preview_update(UtilityData *ud)
 
                format = gtk_entry_get_text(GTK_ENTRY(ud->format_entry));
 
+               g_free(options->cp_mv_rn.auto_end);
+               options->cp_mv_rn.auto_end = g_strdup(end);
+               options->cp_mv_rn.auto_padding = padding;
+
                if (mode == UTILITY_RENAME_FORMATTED)
                        {
                        start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ud->format_spin));
+                       options->cp_mv_rn.formatted_start = start_n;
                        }
                else
                        {
                        start_n = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(ud->auto_spin_start));
+                       options->cp_mv_rn.auto_start = start_n;
                        }
 
                store = gtk_tree_view_get_model(GTK_TREE_VIEW(ud->listview));
@@ -1687,19 +1693,19 @@ static void file_util_dialog_init_source_dest(UtilityData *ud, gboolean second_i
        box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
 
        ud->auto_spin_start = pref_spin_new(box2, NULL, NULL,
-                                           0.0, 1000000.0, 1.0, 0, 1.0,
+                                           0.0, 1000000.0, 1.0, 0, options->cp_mv_rn.auto_start,
                                            G_CALLBACK(file_util_rename_preview_adj_cb), ud);
 
        box2 = furm_simple_vlabel(hbox, _("End text"), TRUE);
 
-       combo = history_combo_new(&ud->auto_entry_end, "", "numerical_rename_suffix", -1);
+       combo = history_combo_new(&ud->auto_entry_end, options->cp_mv_rn.auto_end, "numerical_rename_suffix", -1);
        g_signal_connect(G_OBJECT(ud->auto_entry_end), "changed",
                         G_CALLBACK(file_util_rename_preview_entry_cb), ud);
        gtk_box_pack_start(GTK_BOX(box2), combo, TRUE, TRUE, 0);
        gtk_widget_show(combo);
 
        ud->auto_spin_pad = pref_spin_new(page, _("Padding:"), NULL,
-                                         1.0, 8.0, 1.0, 0, 1.0,
+                                         1.0, 8.0, 1.0, 0, options->cp_mv_rn.auto_padding,
                                          G_CALLBACK(file_util_rename_preview_adj_cb), ud);
 
        page = gtk_vbox_new(FALSE, PREF_PAD_GAP);
@@ -1719,7 +1725,7 @@ static void file_util_dialog_init_source_dest(UtilityData *ud, gboolean second_i
        box2 = furm_simple_vlabel(hbox, _("Start #"), FALSE);
 
        ud->format_spin = pref_spin_new(box2, NULL, NULL,
-                                       0.0, 1000000.0, 1.0, 0, 1.0,
+                                       0.0, 1000000.0, 1.0, 0, options->cp_mv_rn.formatted_start,
                                        G_CALLBACK(file_util_rename_preview_adj_cb), ud);
 
 //     gtk_combo_box_set_active(GTK_COMBO_BOX(ud->combo_type), 0); /* callback will take care of the rest */
index a2d6cd7..cd4cf09 100644 (file)
@@ -31,8 +31,8 @@
 #include "ui_menu.h"
 #include "ui_fileops.h"
 #include "utilops.h"
-#include "view_file_list.h"
-#include "view_file_icon.h"
+#include "view_file/view_file_list.h"
+#include "view_file/view_file_icon.h"
 
 /*
  *-----------------------------------------------------------------------------
@@ -555,7 +555,7 @@ GtkWidget *vf_pop_menu(ViewFile *vf)
                active = (VFLIST(vf)->click_fd != NULL);
                break;
        case FILEVIEW_ICON:
-               active = (VFICON(vf)->click_id != NULL);
+               active = (VFICON(vf)->click_fd != NULL);
                break;
        }
 
@@ -1083,7 +1083,7 @@ void vf_notify_cb(FileData *fd, NotifyType type, gpointer data)
        if (!refresh)
                {
                gchar *base = remove_level_from_path(fd->path);
-               refresh = (strcmp(base, vf->dir_fd->path) == 0);
+               refresh = (g_strcmp0(base, vf->dir_fd->path) == 0);
                g_free(base);
                }
 
@@ -1092,14 +1092,14 @@ void vf_notify_cb(FileData *fd, NotifyType type, gpointer data)
                if (!refresh && fd->change->dest)
                        {
                        gchar *dest_base = remove_level_from_path(fd->change->dest);
-                       refresh = (strcmp(dest_base, vf->dir_fd->path) == 0);
+                       refresh = (g_strcmp0(dest_base, vf->dir_fd->path) == 0);
                        g_free(dest_base);
                        }
 
                if (!refresh && fd->change->source)
                        {
                        gchar *source_base = remove_level_from_path(fd->change->source);
-                       refresh = (strcmp(source_base, vf->dir_fd->path) == 0);
+                       refresh = (g_strcmp0(source_base, vf->dir_fd->path) == 0);
                        g_free(source_base);
                        }
                }
diff --git a/src/view_file/Makefile.am b/src/view_file/Makefile.am
new file mode 100644 (file)
index 0000000..6837e72
--- /dev/null
@@ -0,0 +1,5 @@
+module_view_file = \
+       %D%/view_file_icon.c    \
+       %D%/view_file_icon.h    \
+       %D%/view_file_list.c    \
+       %D%/view_file_list.h
diff --git a/src/view_file/view_file_common.c b/src/view_file/view_file_common.c
new file mode 100644 (file)
index 0000000..959aa66
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  This file is a part of Geeqie project (http://www.geeqie.org/).
+ *  Copyright (C) 2008 - 2016 The Geeqie Team
+ *
+ *  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.
+ */
+
+guint vfcommon_count(ViewFile *vf, gint64 *bytes)
+{
+       if (bytes)
+               {
+               gint64 b = 0;
+               GList *work;
+
+               work = vf->list;
+               while (work)
+                       {
+                       FileData *fd = work->data;
+                       work = work->next;
+
+                       b += fd->size;
+                       }
+
+               *bytes = b;
+               }
+
+       return g_list_length(vf->list);
+}
+
similarity index 77%
rename from src/view_file_icon.c
rename to src/view_file/view_file_icon.c
index 579a170..6fbc7d2 100644 (file)
@@ -60,61 +60,10 @@ enum {
        FILE_COLUMN_COUNT
 };
 
-static gint vficon_index_by_id(ViewFile *vf, IconData *in_id);
-
-static IconData *vficon_icon_data(ViewFile *vf, FileData *fd)
-{
-       IconData *id = NULL;
-       GList *work;
-
-       if (!fd) return NULL;
-       work = vf->list;
-       while (work && !id)
-               {
-               IconData *chk = work->data;
-               work = work->next;
-               if (chk->fd == fd) id = chk;
-               }
-       return id;
-}
-
-static void iconlist_free(GList *list)
-{
-       GList *work = list;
-       while (work)
-               {
-               IconData *id = work->data;
-               file_data_unref(id->fd);
-               g_free(id);
-               work = work->next;
-               }
-
-       g_list_free(list);
-
-}
-
-gint iconlist_sort_file_cb(gpointer a, gpointer b)
-{
-       IconData *ida = a;
-       IconData *idb = b;
-       return filelist_sort_compare_filedata(ida->fd, idb->fd);
-}
-
-GList *iconlist_sort(GList *list, SortType method, gboolean ascend)
-{
-       return filelist_sort_full(list, method, ascend, (GCompareFunc) iconlist_sort_file_cb);
-}
-
-GList *iconlist_insert_sort(GList *list, IconData *id, SortType method, gboolean ascend)
-{
-       return filelist_insert_sort_full(list, id, method, ascend, (GCompareFunc) iconlist_sort_file_cb);
-}
-
-
 static void vficon_toggle_filenames(ViewFile *vf);
-static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter);
+static void vficon_selection_remove(ViewFile *vf, FileData *id, SelectionType mask, GtkTreeIter *iter);
 static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relative);
-static void vficon_set_focus(ViewFile *vf, IconData *id);
+static void vficon_set_focus(ViewFile *vf, FileData *fd);
 static void vficon_populate_at_new_size(ViewFile *vf, gint w, gint h, gboolean force);
 
 
@@ -131,23 +80,23 @@ GList *vficon_selection_get_one(ViewFile *vf, FileData *fd)
 
 GList *vficon_pop_menu_file_list(ViewFile *vf)
 {
-       if (!VFICON(vf)->click_id) return NULL;
+       if (!VFICON(vf)->click_fd) return NULL;
 
-       if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
+       if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
                {
                return vf_selection_get_list(vf);
                }
 
-       return vficon_selection_get_one(vf, VFICON(vf)->click_id->fd);
+       return vficon_selection_get_one(vf, VFICON(vf)->click_fd);
 }
 
 void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data)
 {
        ViewFile *vf = data;
 
-       if (!VFICON(vf)->click_id) return;
+       if (!VFICON(vf)->click_fd) return;
 
-       if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
+       if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
                {
                GList *list;
 
@@ -157,7 +106,7 @@ void vficon_pop_menu_view_cb(GtkWidget *widget, gpointer data)
                }
        else
                {
-               view_window_new(VFICON(vf)->click_id->fd);
+               view_window_new(VFICON(vf)->click_fd);
                }
 }
 
@@ -185,8 +134,8 @@ void vficon_pop_menu_refresh_cb(GtkWidget *widget, gpointer data)
 void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data)
 {
        ViewFile *vf = data;
-       vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
-       VFICON(vf)->click_id = NULL;
+       vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
+       VFICON(vf)->click_fd = NULL;
        vf->popup = NULL;
 }
 
@@ -196,15 +145,15 @@ void vficon_popup_destroy_cb(GtkWidget *widget, gpointer data)
  *-------------------------------------------------------------------
  */
 
-static void vficon_send_layout_select(ViewFile *vf, IconData *id)
+static void vficon_send_layout_select(ViewFile *vf, FileData *fd)
 {
        FileData *read_ahead_fd = NULL;
        FileData *sel_fd;
        FileData *cur_fd;
 
-       if (!vf->layout || !id || !id->fd) return;
+       if (!vf->layout || !fd) return;
 
-       sel_fd = id->fd;
+       sel_fd = fd;
 
        cur_fd = layout_image_get_fd(vf->layout);
        if (sel_fd == cur_fd) return; /* no change */
@@ -213,7 +162,7 @@ static void vficon_send_layout_select(ViewFile *vf, IconData *id)
                {
                gint row;
 
-               row = g_list_index(vf->list, id);
+               row = g_list_index(vf->list, fd);
                if (row > vficon_index_by_fd(vf, cur_fd) &&
                    (guint) (row + 1) < vf_count(vf, NULL))
                        {
@@ -257,11 +206,11 @@ static gint vficon_get_icon_width(ViewFile *vf)
  *-------------------------------------------------------------------
  */
 
-static gboolean vficon_find_position(ViewFile *vf, IconData *id, gint *row, gint *col)
+static gboolean vficon_find_position(ViewFile *vf, FileData *fd, gint *row, gint *col)
 {
        gint n;
 
-       n = g_list_index(vf->list, id);
+       n = g_list_index(vf->list, fd);
 
        if (n < 0) return FALSE;
 
@@ -271,20 +220,20 @@ static gboolean vficon_find_position(ViewFile *vf, IconData *id, gint *row, gint
        return TRUE;
 }
 
-static gboolean vficon_find_iter(ViewFile *vf, IconData *id, GtkTreeIter *iter, gint *column)
+static gboolean vficon_find_iter(ViewFile *vf, FileData *fd, GtkTreeIter *iter, gint *column)
 {
        GtkTreeModel *store;
        gint row, col;
 
        store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-       if (!vficon_find_position(vf, id, &row, &col)) return FALSE;
+       if (!vficon_find_position(vf, fd, &row, &col)) return FALSE;
        if (!gtk_tree_model_iter_nth_child(store, iter, NULL, row)) return FALSE;
        if (column) *column = col;
 
        return TRUE;
 }
 
-static IconData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter *iter)
+static FileData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter *iter)
 {
        GtkTreeModel *store;
        GtkTreeIter p;
@@ -307,7 +256,7 @@ static IconData *vficon_find_data(ViewFile *vf, gint row, gint col, GtkTreeIter
        return NULL;
 }
 
-static IconData *vficon_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
+static FileData *vficon_find_data_by_coord(ViewFile *vf, gint x, gint y, GtkTreeIter *iter)
 {
        GtkTreePath *tpath;
        GtkTreeViewColumn *column;
@@ -346,21 +295,19 @@ static void vficon_mark_toggled_cb(GtkCellRendererToggle *cell, gchar *path_str,
        gint column;
        GList *list;
        guint toggled_mark;
-       IconData *id;
+       FileData *fd;
 
        store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
-       if (!path || !gtk_tree_model_get_iter(store, &row, path))
-               return;
+       if (!path || !gtk_tree_model_get_iter(store, &row, path)) return;
 
        gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
 
        column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(cell), "column_number"));
        g_object_get(G_OBJECT(cell), "toggled_mark", &toggled_mark, NULL);
 
-       id = g_list_nth_data(list, column);
-       if (id)
+       fd = g_list_nth_data(list, column);
+       if (fd)
                {
-               FileData *fd = id->fd;
                file_data_set_mark(fd, toggled_mark, !file_data_get_mark(fd, toggled_mark));
                }
 }
@@ -394,14 +341,14 @@ static void tip_show(ViewFile *vf)
        gdk_window_get_pointer(gtk_tree_view_get_bin_window(GTK_TREE_VIEW(vf->listview)), &x, &y, NULL);
 #endif
 
-       VFICON(vf)->tip_id = vficon_find_data_by_coord(vf, x, y, NULL);
-       if (!VFICON(vf)->tip_id) return;
+       VFICON(vf)->tip_fd = vficon_find_data_by_coord(vf, x, y, NULL);
+       if (!VFICON(vf)->tip_fd) return;
 
        VFICON(vf)->tip_window = gtk_window_new(GTK_WINDOW_POPUP);
        gtk_window_set_resizable(GTK_WINDOW(VFICON(vf)->tip_window), FALSE);
        gtk_container_set_border_width(GTK_CONTAINER(VFICON(vf)->tip_window), 2);
 
-       label = gtk_label_new(VFICON(vf)->tip_id->fd->name);
+       label = gtk_label_new(VFICON(vf)->tip_fd->name);
 
        g_object_set_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label", label);
        gtk_container_add(GTK_CONTAINER(VFICON(vf)->tip_window), label);
@@ -473,7 +420,7 @@ static void tip_unschedule(ViewFile *vf)
                }
 }
 
-static void tip_update(ViewFile *vf, IconData *id)
+static void tip_update(ViewFile *vf, FileData *fd)
 {
 #if GTK_CHECK_VERSION(3,0,0)
        GdkDisplay *display = gdk_display_get_default();
@@ -492,13 +439,13 @@ static void tip_update(ViewFile *vf, IconData *id)
 #endif
                gtk_window_move(GTK_WINDOW(VFICON(vf)->tip_window), x + 16, y + 16);
 
-               if (id != VFICON(vf)->tip_id)
+               if (fd != VFICON(vf)->tip_fd)
                        {
                        GtkWidget *label;
 
-                       VFICON(vf)->tip_id = id;
+                       VFICON(vf)->tip_fd = fd;
 
-                       if (!VFICON(vf)->tip_id)
+                       if (!VFICON(vf)->tip_fd)
                                {
                                tip_hide(vf);
                                tip_schedule(vf);
@@ -506,7 +453,7 @@ static void tip_update(ViewFile *vf, IconData *id)
                                }
 
                        label = g_object_get_data(G_OBJECT(VFICON(vf)->tip_window), "tip_label");
-                       gtk_label_set_text(GTK_LABEL(label), VFICON(vf)->tip_id->fd->name);
+                       gtk_label_set_text(GTK_LABEL(label), VFICON(vf)->tip_fd->name);
                        }
                }
        else
@@ -528,15 +475,15 @@ static void vficon_dnd_get(GtkWidget *widget, GdkDragContext *context,
        ViewFile *vf = data;
        GList *list = NULL;
 
-       if (!VFICON(vf)->click_id) return;
+       if (!VFICON(vf)->click_fd) return;
 
-       if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
+       if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
                {
                list = vf_selection_get_list(vf);
                }
        else
                {
-               list = g_list_append(NULL, file_data_ref(VFICON(vf)->click_id->fd));
+               list = g_list_append(NULL, file_data_ref(VFICON(vf)->click_fd));
                }
 
        if (!list) return;
@@ -551,11 +498,10 @@ static void vficon_drag_data_received(GtkWidget *entry_widget, GdkDragContext *c
        ViewFile *vf = data;
 
        if (info == TARGET_TEXT_PLAIN) {
-               IconData *id = vficon_find_data_by_coord(vf, x, y, NULL);
+               FileData *fd = vficon_find_data_by_coord(vf, x, y, NULL);
 
-               if (id && id->fd) {
+               if (fd) {
                        /* Add keywords to file */
-                       FileData *fd = id->fd;
                        gchar *str = (gchar *) gtk_selection_data_get_text(selection);
                        GList *kw_list = string_to_keywords_list(str);
 
@@ -572,16 +518,16 @@ static void vficon_dnd_begin(GtkWidget *widget, GdkDragContext *context, gpointe
 
        tip_unschedule(vf);
 
-       if (VFICON(vf)->click_id && VFICON(vf)->click_id->fd->thumb_pixbuf)
+       if (VFICON(vf)->click_fd && VFICON(vf)->click_fd->thumb_pixbuf)
                {
                gint items;
 
-               if (VFICON(vf)->click_id->selected & SELECTION_SELECTED)
+               if (VFICON(vf)->click_fd->selected & SELECTION_SELECTED)
                        items = g_list_length(VFICON(vf)->selection);
                else
                        items = 1;
 
-               dnd_set_drag_icon(widget, context, VFICON(vf)->click_id->fd->thumb_pixbuf, items);
+               dnd_set_drag_icon(widget, context, VFICON(vf)->click_fd->thumb_pixbuf, items);
                }
 }
 
@@ -589,7 +535,7 @@ static void vficon_dnd_end(GtkWidget *widget, GdkDragContext *context, gpointer
 {
        ViewFile *vf = data;
 
-       vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
+       vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
 
        if (gdk_drag_context_get_selected_action(context) == GDK_ACTION_MOVE)
                {
@@ -624,16 +570,15 @@ void vficon_dnd_init(ViewFile *vf)
  *-------------------------------------------------------------------
  */
 
-static void vficon_selection_set(ViewFile *vf, IconData *id, SelectionType value, GtkTreeIter *iter)
+static void vficon_selection_set(ViewFile *vf, FileData *fd, SelectionType value, GtkTreeIter *iter)
 {
        GtkTreeModel *store;
        GList *list;
 
-       if (!id) return;
-
+       if (!fd) return;
 
-       if (id->selected == value) return;
-       id->selected = value;
+       if (fd->selected == value) return;
+       fd->selected = value;
 
        store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
        if (iter)
@@ -645,7 +590,7 @@ static void vficon_selection_set(ViewFile *vf, IconData *id, SelectionType value
                {
                GtkTreeIter row;
 
-               if (vficon_find_iter(vf, id, &row, NULL))
+               if (vficon_find_iter(vf, fd, &row, NULL))
                        {
                        gtk_tree_model_get(store, &row, FILE_COLUMN_POINTER, &list, -1);
                        if (list) gtk_list_store_set(GTK_LIST_STORE(store), &row, FILE_COLUMN_POINTER, list, -1);
@@ -653,18 +598,18 @@ static void vficon_selection_set(ViewFile *vf, IconData *id, SelectionType value
                }
 }
 
-static void vficon_selection_add(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter)
+static void vficon_selection_add(ViewFile *vf, FileData *fd, SelectionType mask, GtkTreeIter *iter)
 {
-       if (!id) return;
+       if (!fd) return;
 
-       vficon_selection_set(vf, id, id->selected | mask, iter);
+       vficon_selection_set(vf, fd, fd->selected | mask, iter);
 }
 
-static void vficon_selection_remove(ViewFile *vf, IconData *id, SelectionType mask, GtkTreeIter *iter)
+static void vficon_selection_remove(ViewFile *vf, FileData *fd, SelectionType mask, GtkTreeIter *iter)
 {
-       if (!id) return;
+       if (!fd) return;
 
-       vficon_selection_set(vf, id, id->selected & ~mask, iter);
+       vficon_selection_set(vf, fd, fd->selected & ~mask, iter);
 }
 
 void vficon_marks_set(ViewFile *vf, gint enable)
@@ -687,12 +632,12 @@ static void vficon_verify_selections(ViewFile *vf)
        work = VFICON(vf)->selection;
        while (work)
                {
-               IconData *id = work->data;
+               FileData *fd = work->data;
                work = work->next;
 
-               if (vficon_index_by_id(vf, id) >= 0) continue;
+               if (vficon_index_by_fd(vf, fd) >= 0) continue;
 
-               VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
+               VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
                }
 }
 
@@ -706,11 +651,11 @@ void vficon_select_all(ViewFile *vf)
        work = vf->list;
        while (work)
                {
-               IconData *id = work->data;
+               FileData *fd = work->data;
                work = work->next;
 
-               VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
-               vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
+               VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
+               vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
                }
 
        vf_send_update(vf);
@@ -723,10 +668,10 @@ void vficon_select_none(ViewFile *vf)
        work = VFICON(vf)->selection;
        while (work)
                {
-               IconData *id = work->data;
+               FileData *fd = work->data;
                work = work->next;
 
-               vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
+               vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
                }
 
        g_list_free(VFICON(vf)->selection);
@@ -742,61 +687,61 @@ void vficon_select_invert(ViewFile *vf)
        work = vf->list;
        while (work)
                {
-               IconData *id = work->data;
+               FileData *fd = work->data;
                work = work->next;
 
-               if (id->selected & SELECTION_SELECTED)
+               if (fd->selected & SELECTION_SELECTED)
                        {
-                       VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
-                       vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
+                       VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
+                       vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
                        }
                else
                        {
-                       VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
-                       vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
+                       VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
+                       vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
                        }
                }
 
        vf_send_update(vf);
 }
 
-static void vficon_select(ViewFile *vf, IconData *id)
+static void vficon_select(ViewFile *vf, FileData *fd)
 {
-       VFICON(vf)->prev_selection = id;
+       VFICON(vf)->prev_selection = fd;
 
-       if (!id || id->selected & SELECTION_SELECTED) return;
+       if (!fd || fd->selected & SELECTION_SELECTED) return;
 
-       VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, id);
-       vficon_selection_add(vf, id, SELECTION_SELECTED, NULL);
+       VFICON(vf)->selection = g_list_append(VFICON(vf)->selection, fd);
+       vficon_selection_add(vf, fd, SELECTION_SELECTED, NULL);
 
        vf_send_update(vf);
 }
 
-static void vficon_unselect(ViewFile *vf, IconData *id)
+static void vficon_unselect(ViewFile *vf, FileData *fd)
 {
-       VFICON(vf)->prev_selection = id;
+       VFICON(vf)->prev_selection = fd;
 
-       if (!id || !(id->selected & SELECTION_SELECTED) ) return;
+       if (!fd || !(fd->selected & SELECTION_SELECTED) ) return;
 
-       VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, id);
-       vficon_selection_remove(vf, id, SELECTION_SELECTED, NULL);
+       VFICON(vf)->selection = g_list_remove(VFICON(vf)->selection, fd);
+       vficon_selection_remove(vf, fd, SELECTION_SELECTED, NULL);
 
        vf_send_update(vf);
 }
 
-static void vficon_select_util(ViewFile *vf, IconData *id, gboolean select)
+static void vficon_select_util(ViewFile *vf, FileData *fd, gboolean select)
 {
        if (select)
                {
-               vficon_select(vf, id);
+               vficon_select(vf, fd);
                }
        else
                {
-               vficon_unselect(vf, id);
+               vficon_unselect(vf, fd);
                }
 }
 
-static void vficon_select_region_util(ViewFile *vf, IconData *start, IconData *end, gboolean select)
+static void vficon_select_region_util(ViewFile *vf, FileData *start, FileData *end, gboolean select)
 {
        gint row1, col1;
        gint row2, col2;
@@ -811,20 +756,19 @@ static void vficon_select_region_util(ViewFile *vf, IconData *start, IconData *e
        if (!options->collections.rectangular_selection)
                {
                GList *work;
-               IconData *id;
 
                if (g_list_index(vf->list, start) > g_list_index(vf->list, end))
                        {
-                       id = start;
+                       FileData *tmp = start;
                        start = end;
-                       end = id;
+                       end = tmp;
                        }
 
                work = g_list_find(vf->list, start);
                while (work)
                        {
-                       id = work->data;
-                       vficon_select_util(vf, id, select);
+                       FileData *fd = work->data;
+                       vficon_select_util(vf, fd, select);
 
                        if (work->data != end)
                                work = work->next;
@@ -834,6 +778,7 @@ static void vficon_select_region_util(ViewFile *vf, IconData *start, IconData *e
                return;
                }
 
+       // rectangular_selection==true.
        if (row2 < row1)
                {
                t = row1;
@@ -853,19 +798,19 @@ static void vficon_select_region_util(ViewFile *vf, IconData *start, IconData *e
                {
                for (j = col1; j <= col2; j++)
                        {
-                       IconData *id = vficon_find_data(vf, i, j, NULL);
-                       if (id) vficon_select_util(vf, id, select);
+                       FileData *fd = vficon_find_data(vf, i, j, NULL);
+                       if (fd) vficon_select_util(vf, fd, select);
                        }
                }
 }
 
 gboolean vficon_index_is_selected(ViewFile *vf, gint row)
 {
-       IconData *id = g_list_nth_data(vf->list, row);
+       FileData *fd = g_list_nth_data(vf->list, row);
 
-       if (!id) return FALSE;
+       if (!fd) return FALSE;
 
-       return (id->selected & SELECTION_SELECTED);
+       return (fd->selected & SELECTION_SELECTED);
 }
 
 guint vficon_selection_count(ViewFile *vf, gint64 *bytes)
@@ -878,8 +823,7 @@ guint vficon_selection_count(ViewFile *vf, gint64 *bytes)
                work = VFICON(vf)->selection;
                while (work)
                        {
-                       IconData *id = work->data;
-                       FileData *fd = id->fd;
+                       FileData *fd = work->data;
                        g_assert(fd->magick == FD_MAGICK);
                        b += fd->size;
 
@@ -900,8 +844,7 @@ GList *vficon_selection_get_list(ViewFile *vf)
        work = VFICON(vf)->selection;
        while (work)
                {
-               IconData *id = work->data;
-               FileData *fd = id->fd;
+               FileData *fd = work->data;
                g_assert(fd->magick == FD_MAGICK);
 
                list = g_list_prepend(list, file_data_ref(fd));
@@ -937,33 +880,18 @@ GList *vficon_selection_get_list_by_index(ViewFile *vf)
        return g_list_reverse(list);
 }
 
-static void vficon_select_by_id(ViewFile *vf, IconData *id)
+void vficon_select_by_fd(ViewFile *vf, FileData *fd)
 {
-       if (!id) return;
+       if (!fd) return;
+       if (!g_list_find(vf->list, fd)) return;
 
-       if (!(id->selected & SELECTION_SELECTED))
+       if (!(fd->selected & SELECTION_SELECTED))
                {
                vf_select_none(vf);
-               vficon_select(vf, id);
+               vficon_select(vf, fd);
                }
 
-       vficon_set_focus(vf, id);
-}
-
-void vficon_select_by_fd(ViewFile *vf, FileData *fd)
-{
-       IconData *id = NULL;
-       GList *work;
-
-       if (!fd) return;
-       work = vf->list;
-       while (work && !id)
-               {
-               IconData *chk = work->data;
-               work = work->next;
-               if (chk->fd == fd) id = chk;
-               }
-       vficon_select_by_id(vf, id);
+       vficon_set_focus(vf, fd);
 }
 
 void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
@@ -976,14 +904,13 @@ void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
        work = vf->list;
        while (work)
                {
-               IconData *id = work->data;
-               FileData *fd = id->fd;
+               FileData *fd = work->data;
                gboolean mark_val, selected;
 
                g_assert(fd->magick == FD_MAGICK);
 
                mark_val = file_data_get_mark(fd, n);
-               selected = (id->selected & SELECTION_SELECTED);
+               selected = fd->selected & SELECTION_SELECTED;
 
                switch (mode)
                        {
@@ -997,7 +924,7 @@ void vficon_mark_to_selection(ViewFile *vf, gint mark, MarkToSelectionMode mode)
                                break;
                        }
 
-               vficon_select_util(vf, id, selected);
+               vficon_select_util(vf, fd, selected);
 
                work = work->next;
                }
@@ -1034,7 +961,7 @@ void vficon_selection_to_mark(ViewFile *vf, gint mark, SelectionToMarkMode mode)
 static void vficon_select_closest(ViewFile *vf, FileData *sel_fd)
 {
        GList *work;
-       IconData *id = NULL;
+       FileData *fd = NULL;
 
        if (sel_fd->parent) sel_fd = sel_fd->parent;
        work = vf->list;
@@ -1042,10 +969,8 @@ static void vficon_select_closest(ViewFile *vf, FileData *sel_fd)
        while (work)
                {
                gint match;
-               FileData *fd;
 
-               id = work->data;
-               fd = id->fd;
+               fd = work->data;
                work = work->next;
 
                match = filelist_sort_compare_filedata_full(fd, sel_fd, vf->sort_method, vf->sort_ascend);
@@ -1053,10 +978,10 @@ static void vficon_select_closest(ViewFile *vf, FileData *sel_fd)
                if (match >= 0) break;
                }
 
-       if (id)
+       if (fd)
                {
-               vficon_select(vf, id);
-               vficon_send_layout_select(vf, id);
+               vficon_select(vf, fd);
+               vficon_send_layout_select(vf, fd);
                }
 }
 
@@ -1150,36 +1075,46 @@ static void vficon_move_focus(ViewFile *vf, gint row, gint col, gboolean relativ
        vficon_set_focus(vf, vficon_find_data(vf, new_row, new_col, NULL));
 }
 
-static void vficon_set_focus(ViewFile *vf, IconData *id)
+static void vficon_set_focus(ViewFile *vf, FileData *fd)
 {
        GtkTreeIter iter;
        gint row, col;
 
-       if (g_list_find(vf->list, VFICON(vf)->focus_id))
+       if (g_list_find(vf->list, VFICON(vf)->focus_fd))
                {
-               if (id == VFICON(vf)->focus_id)
+               if (fd == VFICON(vf)->focus_fd)
                        {
                        /* ensure focus row col are correct */
-                       vficon_find_position(vf, VFICON(vf)->focus_id, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column);
+                       vficon_find_position(vf, VFICON(vf)->focus_fd, &VFICON(vf)->focus_row, &VFICON(vf)->focus_column);
+#if GTK_CHECK_VERSION(3,0,0)
+/* FIXME: Refer to issue #467 on Github. The thumbnail position is not
+ * preserved when the icon view is refreshed. Caused by an unknown call from
+ * the idle loop. This patch hides the problem.
+ */
+                       if (vficon_find_iter(vf, VFICON(vf)->focus_fd, &iter, NULL))
+                               {
+                               tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
+                               }
+#endif
                        return;
                        }
-               vficon_selection_remove(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL);
+               vficon_selection_remove(vf, VFICON(vf)->focus_fd, SELECTION_FOCUS, NULL);
                }
 
-       if (!vficon_find_position(vf, id, &row, &col))
+       if (!vficon_find_position(vf, fd, &row, &col))
                {
-               VFICON(vf)->focus_id = NULL;
+               VFICON(vf)->focus_fd = NULL;
                VFICON(vf)->focus_row = -1;
                VFICON(vf)->focus_column = -1;
                return;
                }
 
-       VFICON(vf)->focus_id = id;
+       VFICON(vf)->focus_fd = fd;
        VFICON(vf)->focus_row = row;
        VFICON(vf)->focus_column = col;
-       vficon_selection_add(vf, VFICON(vf)->focus_id, SELECTION_FOCUS, NULL);
+       vficon_selection_add(vf, VFICON(vf)->focus_fd, SELECTION_FOCUS, NULL);
 
-       if (vficon_find_iter(vf, VFICON(vf)->focus_id, &iter, NULL))
+       if (vficon_find_iter(vf, VFICON(vf)->focus_fd, &iter, NULL))
                {
                GtkTreePath *tpath;
                GtkTreeViewColumn *column;
@@ -1231,7 +1166,7 @@ static void vfi_menu_position_cb(GtkMenu *menu, gint *x, gint *y, gboolean *push
        GtkTreePath *tpath;
        gint cw, ch;
 
-       if (!vficon_find_iter(vf, VFICON(vf)->click_id, &iter, &column)) return;
+       if (!vficon_find_iter(vf, VFICON(vf)->click_fd, &iter, &column)) return;
        store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
        tpath = gtk_tree_model_get_path(store, &iter);
        tree_view_get_cell_clamped(GTK_TREE_VIEW(vf->listview), tpath, column, FALSE, x, y, &cw, &ch);
@@ -1245,7 +1180,7 @@ gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer dat
        ViewFile *vf = data;
        gint focus_row = 0;
        gint focus_col = 0;
-       IconData *id;
+       FileData *fd;
        gboolean stop_signal;
 
        stop_signal = TRUE;
@@ -1278,38 +1213,38 @@ gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer dat
                        focus_col = VFICON(vf)->columns - 1 - VFICON(vf)->focus_column;
                        break;
                case GDK_KEY_space:
-                       id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
-                       if (id)
+                       fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
+                       if (fd)
                                {
-                               VFICON(vf)->click_id = id;
+                               VFICON(vf)->click_fd = fd;
                                if (event->state & GDK_CONTROL_MASK)
                                        {
                                        gint selected;
 
-                                       selected = id->selected & SELECTION_SELECTED;
+                                       selected = fd->selected & SELECTION_SELECTED;
                                        if (selected)
                                                {
-                                               vficon_unselect(vf, id);
+                                               vficon_unselect(vf, fd);
                                                }
                                        else
                                                {
-                                               vficon_select(vf, id);
-                                               vficon_send_layout_select(vf, id);
+                                               vficon_select(vf, fd);
+                                               vficon_send_layout_select(vf, fd);
                                                }
                                        }
                                else
                                        {
                                        vf_select_none(vf);
-                                       vficon_select(vf, id);
-                                       vficon_send_layout_select(vf, id);
+                                       vficon_select(vf, fd);
+                                       vficon_send_layout_select(vf, fd);
                                        }
                                }
                        break;
                case GDK_KEY_Menu:
-                       id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
-                       VFICON(vf)->click_id = id;
+                       fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
+                       VFICON(vf)->click_fd = fd;
 
-                       vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
+                       vficon_selection_add(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
                        tip_unschedule(vf);
 
                        vf->popup = vf_pop_menu(vf);
@@ -1322,38 +1257,38 @@ gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer dat
 
        if (focus_row != 0 || focus_col != 0)
                {
-               IconData *new_id;
-               IconData *old_id;
+               FileData *new_fd;
+               FileData *old_fd;
 
-               old_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
+               old_fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
                vficon_move_focus(vf, focus_row, focus_col, TRUE);
-               new_id = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
+               new_fd = vficon_find_data(vf, VFICON(vf)->focus_row, VFICON(vf)->focus_column, NULL);
 
-               if (new_id != old_id)
+               if (new_fd != old_fd)
                        {
                        if (event->state & GDK_SHIFT_MASK)
                                {
                                if (!options->collections.rectangular_selection)
                                        {
-                                       vficon_select_region_util(vf, old_id, new_id, FALSE);
+                                       vficon_select_region_util(vf, old_fd, new_fd, FALSE);
                                        }
                                else
                                        {
-                                       vficon_select_region_util(vf, VFICON(vf)->click_id, old_id, FALSE);
+                                       vficon_select_region_util(vf, VFICON(vf)->click_fd, old_fd, FALSE);
                                        }
-                               vficon_select_region_util(vf, VFICON(vf)->click_id, new_id, TRUE);
-                               vficon_send_layout_select(vf, new_id);
+                               vficon_select_region_util(vf, VFICON(vf)->click_fd, new_fd, TRUE);
+                               vficon_send_layout_select(vf, new_fd);
                                }
                        else if (event->state & GDK_CONTROL_MASK)
                                {
-                               VFICON(vf)->click_id = new_id;
+                               VFICON(vf)->click_fd = new_fd;
                                }
                        else
                                {
-                               VFICON(vf)->click_id = new_id;
+                               VFICON(vf)->click_fd = new_fd;
                                vf_select_none(vf);
-                               vficon_select(vf, new_id);
-                               vficon_send_layout_select(vf, new_id);
+                               vficon_select(vf, new_fd);
+                               vficon_send_layout_select(vf, new_fd);
                                }
                        }
                }
@@ -1375,10 +1310,10 @@ gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer dat
 static gboolean vficon_motion_cb(GtkWidget *widget, GdkEventMotion *event, gpointer data)
 {
        ViewFile *vf = data;
-       IconData *id;
+       FileData *fd;
 
-       id = vficon_find_data_by_coord(vf, (gint)event->x, (gint)event->y, NULL);
-       tip_update(vf, id);
+       fd = vficon_find_data_by_coord(vf, (gint)event->x, (gint)event->y, NULL);
+       tip_update(vf, fd);
 
        return FALSE;
 }
@@ -1387,14 +1322,14 @@ gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer dat
 {
        ViewFile *vf = data;
        GtkTreeIter iter;
-       IconData *id;
+       FileData *fd;
 
        tip_unschedule(vf);
 
-       id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
+       fd = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
 
-       VFICON(vf)->click_id = id;
-       vficon_selection_add(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter);
+       VFICON(vf)->click_fd = fd;
+       vficon_selection_add(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, &iter);
 
        switch (bevent->button)
                {
@@ -1404,10 +1339,9 @@ gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer dat
                                gtk_widget_grab_focus(vf->listview);
                                }
 
-                       if (bevent->type == GDK_2BUTTON_PRESS &&
-                           vf->layout)
+                       if (bevent->type == GDK_2BUTTON_PRESS && vf->layout)
                                {
-                               vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, &iter);
+                               vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, &iter);
                                layout_image_full_screen_start(vf->layout);
                                }
                        break;
@@ -1426,43 +1360,43 @@ gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer d
 {
        ViewFile *vf = data;
        GtkTreeIter iter;
-       IconData *id = NULL;
+       FileData *fd = NULL;
        gboolean was_selected;
 
        tip_schedule(vf);
 
        if ((gint)bevent->x != 0 || (gint)bevent->y != 0)
                {
-               id = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
+               fd = vficon_find_data_by_coord(vf, (gint)bevent->x, (gint)bevent->y, &iter);
                }
 
-       if (VFICON(vf)->click_id)
+       if (VFICON(vf)->click_fd)
                {
-               vficon_selection_remove(vf, VFICON(vf)->click_id, SELECTION_PRELIGHT, NULL);
+               vficon_selection_remove(vf, VFICON(vf)->click_fd, SELECTION_PRELIGHT, NULL);
                }
 
-       if (!id || VFICON(vf)->click_id != id) return TRUE;
+       if (!fd || VFICON(vf)->click_fd != fd) return TRUE;
 
-       was_selected = !!(id->selected & SELECTION_SELECTED);
+       was_selected = !!(fd->selected & SELECTION_SELECTED);
 
        switch (bevent->button)
                {
                case MOUSE_BUTTON_LEFT:
                        {
-                       vficon_set_focus(vf, id);
+                       vficon_set_focus(vf, fd);
 
                        if (bevent->state & GDK_CONTROL_MASK)
                                {
                                gboolean select;
 
-                               select = !(id->selected & SELECTION_SELECTED);
+                               select = !(fd->selected & SELECTION_SELECTED);
                                if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
                                        {
-                                       vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, select);
+                                       vficon_select_region_util(vf, VFICON(vf)->prev_selection, fd, select);
                                        }
                                else
                                        {
-                                       vficon_select_util(vf, id, select);
+                                       vficon_select_util(vf, fd, select);
                                        }
                                }
                        else
@@ -1471,11 +1405,11 @@ gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer d
 
                                if ((bevent->state & GDK_SHIFT_MASK) && VFICON(vf)->prev_selection)
                                        {
-                                       vficon_select_region_util(vf, VFICON(vf)->prev_selection, id, TRUE);
+                                       vficon_select_region_util(vf, VFICON(vf)->prev_selection, fd, TRUE);
                                        }
                                else
                                        {
-                                       vficon_select_util(vf, id, TRUE);
+                                       vficon_select_util(vf, fd, TRUE);
                                        was_selected = FALSE;
                                        }
                                }
@@ -1483,16 +1417,16 @@ gboolean vficon_release_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer d
                        break;
                case MOUSE_BUTTON_MIDDLE:
                        {
-                       vficon_select_util(vf, id, !(id->selected & SELECTION_SELECTED));
+                       vficon_select_util(vf, fd, !(fd->selected & SELECTION_SELECTED));
                        }
                        break;
                default:
                        break;
                }
 
-       if (!was_selected && (id->selected & SELECTION_SELECTED))
+       if (!was_selected && (fd->selected & SELECTION_SELECTED))
                {
-               vficon_send_layout_select(vf, id);
+               vficon_send_layout_select(vf, fd);
                }
 
        return TRUE;
@@ -1556,7 +1490,7 @@ static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_positio
        GtkTreeModel *store;
        GtkTreePath *tpath;
        GList *work;
-       IconData *visible_id = NULL;
+       FileData *visible_fd = NULL;
        gint r, c;
        gboolean valid;
        GtkTreeIter iter;
@@ -1575,7 +1509,7 @@ static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_positio
                gtk_tree_path_free(tpath);
 
                gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
-               if (list) visible_id = list->data;
+               if (list) visible_fd = list->data;
                }
 
 
@@ -1638,20 +1572,20 @@ static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_positio
 
                while (list)
                        {
-                       IconData *id;
+                       FileData *fd;
 
                        if (work)
                                {
-                               id = work->data;
+                               fd = work->data;
                                work = work->next;
                                c++;
                                }
                        else
                                {
-                               id = NULL;
+                               fd = NULL;
                                }
 
-                       list->data = id;
+                       list->data = fd;
                        list = list->next;
                        }
                if (valid) valid = gtk_tree_model_iter_next(store, &iter);
@@ -1669,7 +1603,7 @@ static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_positio
 
        VFICON(vf)->rows = r;
 
-       if (visible_id &&
+       if (visible_fd &&
            gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
                {
                GtkTreeIter iter;
@@ -1679,8 +1613,8 @@ static void vficon_populate(ViewFile *vf, gboolean resize, gboolean keep_positio
                gtk_tree_path_free(tpath);
 
                gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
-               if (g_list_find(list, visible_id) == NULL &&
-                   vficon_find_iter(vf, visible_id, &iter, NULL))
+               if (g_list_find(list, visible_fd) == NULL &&
+                   vficon_find_iter(vf, visible_fd, &iter, NULL))
                        {
                        tree_view_row_make_visible(GTK_TREE_VIEW(vf->listview), &iter, FALSE);
                        }
@@ -1746,8 +1680,7 @@ void vficon_thumb_progress_count(GList *list, gint *count, gint *done)
        GList *work = list;
        while (work)
                {
-               IconData *id = work->data;
-               FileData *fd = id->fd;
+               FileData *fd = work->data;
                work = work->next;
 
                if (fd->thumb_pixbuf) (*done)++;
@@ -1761,7 +1694,8 @@ void vficon_set_thumb_fd(ViewFile *vf, FileData *fd)
        GtkTreeIter iter;
        GList *list;
 
-       if (!vficon_find_iter(vf, vficon_icon_data(vf, fd), &iter, NULL)) return;
+       if (!g_list_find(vf->list, fd)) return;
+       if (!vficon_find_iter(vf, fd, &iter, NULL)) return;
 
        store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
 
@@ -1769,12 +1703,12 @@ void vficon_set_thumb_fd(ViewFile *vf, FileData *fd)
        gtk_list_store_set(GTK_LIST_STORE(store), &iter, FILE_COLUMN_POINTER, list, -1);
 }
 
-
+/* Returns the next fd without a loaded pixbuf, so the thumb-loader can load the pixbuf for it. */
 FileData *vficon_thumb_next_fd(ViewFile *vf)
 {
        GtkTreePath *tpath;
-       FileData *fd = NULL;
 
+       /* First see if there are visible files that don't have a loaded thumb... */
        if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(vf->listview), 0, 0, &tpath, NULL, NULL, NULL))
                {
                GtkTreeModel *store;
@@ -1784,40 +1718,36 @@ FileData *vficon_thumb_next_fd(ViewFile *vf)
                store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
                gtk_tree_model_get_iter(store, &iter, tpath);
                gtk_tree_path_free(tpath);
+               tpath = NULL;
 
-               while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
+               while (valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
                        {
                        GList *list;
-
                        gtk_tree_model_get(store, &iter, FILE_COLUMN_POINTER, &list, -1);
 
-                       while (!fd && list)
+                       // TODO(xsdg): for loop here.
+                       for (; list; list = list->next)
                                {
-                               IconData *id = list->data;
-                               if (id && !id->fd->thumb_pixbuf) fd = id->fd;
-                               list = list->next;
+                               FileData *fd = list->data;
+                               if (fd && !fd->thumb_pixbuf) return fd;
                                }
 
                        valid = gtk_tree_model_iter_next(store, &iter);
                        }
                }
 
-       /* then find first undone */
-
-       if (!fd)
+       /* Then iterate through the entire list to load all of them. */
+       GList *work;
+       for (work = vf->list; work; work = work->next)
                {
-               GList *work = vf->list;
-               while (work && !fd)
-                       {
-                       IconData *id = work->data;
-                       FileData *fd_p = id->fd;
-                       work = work->next;
+               FileData *fd = work->data;
 
-                       if (!fd_p->thumb_pixbuf) fd = fd_p;
-                       }
+               // Note: This implementation differs from view_file_list.c because sidecar files are not
+               // distinct list elements here, as they are in the list view.
+               if (!fd->thumb_pixbuf) return fd;
                }
 
-       return fd;
+       return NULL;
 }
 
 void vficon_thumb_reset_all(ViewFile *vf)
@@ -1826,8 +1756,7 @@ void vficon_thumb_reset_all(ViewFile *vf)
 
        while (work)
                {
-               IconData *id = work->data;
-               FileData *fd = id->fd;
+               FileData *fd = work->data;
                if (fd->thumb_pixbuf)
                        {
                        g_object_unref(fd->thumb_pixbuf);
@@ -1846,10 +1775,10 @@ void vficon_thumb_reset_all(ViewFile *vf)
 
 FileData *vficon_index_get_data(ViewFile *vf, gint row)
 {
-       IconData *id;
+       FileData *fd;
 
-       id = g_list_nth_data(vf->list, row);
-       return id ? id->fd : NULL;
+       fd = g_list_nth_data(vf->list, row);
+       return fd ? fd : NULL;
 }
 
 
@@ -1863,8 +1792,7 @@ gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
        work = vf->list;
        while (work)
                {
-               IconData *id = work->data;
-               FileData *fd = id->fd;
+               FileData *fd = work->data;
                if (fd == in_fd) return p;
                work = work->next;
                p++;
@@ -1873,25 +1801,6 @@ gint vficon_index_by_fd(ViewFile *vf, FileData *in_fd)
        return -1;
 }
 
-static gint vficon_index_by_id(ViewFile *vf, IconData *in_id)
-{
-       gint p = 0;
-       GList *work;
-
-       if (!in_id) return -1;
-
-       work = vf->list;
-       while (work)
-               {
-               IconData *id = work->data;
-               if (id == in_id) return p;
-               work = work->next;
-               p++;
-               }
-
-       return -1;
-}
-
 guint vficon_count(ViewFile *vf, gint64 *bytes)
 {
        if (bytes)
@@ -1902,8 +1811,7 @@ guint vficon_count(ViewFile *vf, gint64 *bytes)
                work = vf->list;
                while (work)
                        {
-                       IconData *id = work->data;
-                       FileData *fd = id->fd;
+                       FileData *fd = work->data;
                        work = work->next;
 
                        b += fd->size;
@@ -1923,8 +1831,7 @@ GList *vficon_get_list(ViewFile *vf)
        work = vf->list;
        while (work)
                {
-               IconData *id = work->data;
-               FileData *fd = id->fd;
+               FileData *fd = work->data;
                work = work->next;
 
                list = g_list_prepend(list, file_data_ref(fd));
@@ -1942,13 +1849,13 @@ GList *vficon_get_list(ViewFile *vf)
 static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
 {
        gboolean ret = TRUE;
-       GList *work, *work_fd;
-       IconData *focus_id;
-       GList *new_filelist = NULL;
+       GList *work, *new_work;
+       FileData *focus_fd;
        FileData *first_selected = NULL;
-       GList *new_iconlist = NULL;
+       GList *new_filelist = NULL;
+       GList *new_fd_list = NULL;
 
-       focus_id = VFICON(vf)->focus_id;
+       focus_fd = VFICON(vf)->focus_fd;
 
        if (vf->dir_fd)
                {
@@ -1956,44 +1863,39 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
                new_filelist = file_data_filter_marks_list(new_filelist, vf_marks_get_filter(vf));
                }
 
-       vf->list = iconlist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
+       vf->list = filelist_sort(vf->list, vf->sort_method, vf->sort_ascend); /* the list might not be sorted if there were renames */
        new_filelist = filelist_sort(new_filelist, vf->sort_method, vf->sort_ascend);
 
        if (VFICON(vf)->selection)
                {
-               first_selected = ((IconData *)(VFICON(vf)->selection->data))->fd;
+               first_selected = VFICON(vf)->selection->data;
                file_data_ref(first_selected);
                g_list_free(VFICON(vf)->selection);
                VFICON(vf)->selection = NULL;
-
-
                }
 
-       /* check for same files from old_list */
+       /* iterate old list and new list, looking for differences */
        work = vf->list;
-       work_fd = new_filelist;
-       while (work || work_fd)
+       new_work = new_filelist;
+       while (work || new_work)
                {
-               IconData *id = NULL;
                FileData *fd = NULL;
                FileData *new_fd = NULL;
                gint match;
 
-               if (work && work_fd)
+               if (work && new_work)
                        {
-                       id = work->data;
-                       fd = id->fd;
-
-                       new_fd = work_fd->data;
+                       fd = work->data;
+                       new_fd = new_work->data;
 
                        if (fd == new_fd)
                                {
                                /* not changed, go to next */
                                work = work->next;
-                               work_fd = work_fd->next;
-                               if (id->selected & SELECTION_SELECTED)
+                               new_work = new_work->next;
+                               if (fd->selected & SELECTION_SELECTED)
                                        {
-                                       VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, id);
+                                       VFICON(vf)->selection = g_list_prepend(VFICON(vf)->selection, fd);
                                        }
                                continue;
                                }
@@ -2003,13 +1905,14 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
                        }
                else if (work)
                        {
-                       id = work->data;
-                       fd = id->fd;
+                       /* old item was deleted */
+                       fd = work->data;
                        match = -1;
                        }
-               else /* work_fd */
+               else
                        {
-                       new_fd = work_fd->data;
+                       /* new item was added */
+                       new_fd = new_work->data;
                        match = 1;
                        }
 
@@ -2018,32 +1921,33 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
                        /* file no longer exists, delete from vf->list */
                        GList *to_delete = work;
                        work = work->next;
-                       if (id == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL;
-                       if (id == VFICON(vf)->click_id) VFICON(vf)->click_id = NULL;
+                       if (fd == VFICON(vf)->prev_selection) VFICON(vf)->prev_selection = NULL;
+                       if (fd == VFICON(vf)->click_fd) VFICON(vf)->click_fd = NULL;
                        file_data_unref(fd);
-                       g_free(id);
                        vf->list = g_list_delete_link(vf->list, to_delete);
                        }
                else
                        {
                        /* new file, add to vf->list */
-                       id = g_new0(IconData, 1);
-
-                       id->selected = SELECTION_NONE;
-                       id->fd = file_data_ref(new_fd);
+                       file_data_ref(new_fd);
+                       new_fd->selected = SELECTION_NONE;
                        if (work)
-                               vf->list = g_list_insert_before(vf->list, work, id);
+                               {
+                               vf->list = g_list_insert_before(vf->list, work, new_fd);
+                               }
                        else
-                               new_iconlist = g_list_prepend(new_iconlist, id); /* it is faster to append all new entries together later */
+                               {
+                               /* it is faster to append all new entries together later */
+                               new_fd_list = g_list_prepend(new_fd_list, new_fd);
+                               }
 
-                       work_fd = work_fd->next;
+                       new_work = new_work->next;
                        }
-
                }
 
-       if (new_iconlist)
+       if (new_fd_list)
                {
-               vf->list = g_list_concat(vf->list, g_list_reverse(new_iconlist));
+               vf->list = g_list_concat(vf->list, g_list_reverse(new_fd_list));
                }
 
        VFICON(vf)->selection = g_list_reverse(VFICON(vf)->selection);
@@ -2060,9 +1964,9 @@ static gboolean vficon_refresh_real(ViewFile *vf, gboolean keep_position)
        file_data_unref(first_selected);
 
        /* attempt to keep focus on same icon when refreshing */
-       if (focus_id && g_list_find(vf->list, focus_id))
+       if (focus_fd && g_list_find(vf->list, focus_fd))
                {
-               vficon_set_focus(vf, focus_id);
+               vficon_set_focus(vf, focus_fd);
                }
 
        return ret;
@@ -2090,7 +1994,7 @@ static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer
                                GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer data)
 {
        GList *list;
-       IconData *id;
+       FileData *fd;
        ColumnData *cd = data;
        ViewFile *vf = cd->vf;
 
@@ -2098,9 +2002,9 @@ static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer
 
        gtk_tree_model_get(tree_model, iter, FILE_COLUMN_POINTER, &list, -1);
 
-       id = g_list_nth_data(list, cd->number);
+       fd = g_list_nth_data(list, cd->number);
 
-       if (id)
+       if (fd)
                {
                GdkColor color_fg;
                GdkColor color_bg;
@@ -2109,23 +2013,23 @@ static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer
                gchar *link;
                GtkStateType state = GTK_STATE_NORMAL;
 
-               g_assert(id->fd->magick == FD_MAGICK);
+               g_assert(fd->magick == FD_MAGICK);
 
-               link = islink(id->fd->path) ? GQ_LINK_STR : "";
-               if (id->fd->sidecar_files)
+               link = islink(fd->path) ? GQ_LINK_STR : "";
+               if (fd->sidecar_files)
                        {
-                       gchar *sidecars = file_data_sc_list_to_string(id->fd);
-                       name_sidecars = g_strdup_printf("%s%s %s", link, id->fd->name, sidecars);
+                       gchar *sidecars = file_data_sc_list_to_string(fd);
+                       name_sidecars = g_strdup_printf("%s%s %s", link, fd->name, sidecars);
                        g_free(sidecars);
                        }
                else
                        {
-                       gchar *disabled_grouping = id->fd->disable_grouping ? _(" [NO GROUPING]") : "";
-                       name_sidecars = g_strdup_printf("%s%s%s", link, id->fd->name, disabled_grouping);
+                       gchar *disabled_grouping = fd->disable_grouping ? _(" [NO GROUPING]") : "";
+                       name_sidecars = g_strdup_printf("%s%s%s", link, fd->name, disabled_grouping);
                        }
 
                style = gtk_widget_get_style(vf->listview);
-               if (id->selected & SELECTION_SELECTED)
+               if (fd->selected & SELECTION_SELECTED)
                        {
                        state = GTK_STATE_SELECTED;
                        }
@@ -2133,20 +2037,20 @@ static void vficon_cell_data_cb(GtkTreeViewColumn *tree_column, GtkCellRenderer
                memcpy(&color_fg, &style->text[state], sizeof(color_fg));
                memcpy(&color_bg, &style->base[state], sizeof(color_bg));
 
-               if (id->selected & SELECTION_PRELIGHT)
+               if (fd->selected & SELECTION_PRELIGHT)
                        {
                        shift_color(&color_bg, -1, 0);
                        }
 
-               g_object_set(cell,      "pixbuf", id->fd->thumb_pixbuf,
+               g_object_set(cell,      "pixbuf", fd->thumb_pixbuf,
                                        "text", name_sidecars,
-                                       "marks", file_data_get_marks(id->fd),
+                                       "marks", file_data_get_marks(fd),
                                        "show_marks", vf->marks_enabled,
                                        "cell-background-gdk", &color_bg,
                                        "cell-background-set", TRUE,
                                        "foreground-gdk", &color_fg,
                                        "foreground-set", TRUE,
-                                       "has-focus", (VFICON(vf)->focus_id == id), NULL);
+                                       "has-focus", (VFICON(vf)->focus_fd == fd), NULL);
                g_free(name_sidecars);
                }
        else
@@ -2210,13 +2114,13 @@ gboolean vficon_set_fd(ViewFile *vf, FileData *dir_fd)
        g_list_free(VFICON(vf)->selection);
        VFICON(vf)->selection = NULL;
 
-       iconlist_free(vf->list);
+       g_list_free(vf->list);
        vf->list = NULL;
 
        /* NOTE: populate will clear the store for us */
        ret = vficon_refresh_real(vf, FALSE);
 
-       VFICON(vf)->focus_id = NULL;
+       VFICON(vf)->focus_fd = NULL;
        vficon_move_focus(vf, 0, 0, FALSE);
 
        return ret;
@@ -2234,7 +2138,7 @@ void vficon_destroy_cb(GtkWidget *widget, gpointer data)
 
        vf_thumb_cleanup(vf);
 
-       iconlist_free(vf->list);
+       g_list_free(vf->list);
        g_list_free(VFICON(vf)->selection);
 }
 
similarity index 96%
rename from src/view_file_icon.h
rename to src/view_file/view_file_icon.h
index 086491b..0ca75b5 100644 (file)
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#ifndef VIEW_FILE_ICON_H
-#define VIEW_FILE_ICON_H
+#ifndef VIEW_FILE_VIEW_FILE_ICON_H
+#define VIEW_FILE_VIEW_FILE_ICON_H
+
+#include "filedata.h"
 
 gboolean vficon_press_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data);
 gboolean vficon_press_cb(GtkWidget *widget, GdkEventButton *bevent, gpointer data);
similarity index 99%
rename from src/view_file_list.c
rename to src/view_file/view_file_list.c
index bee6bfc..38be2a9 100644 (file)
@@ -1102,6 +1102,7 @@ FileData *vflist_thumb_next_fd(ViewFile *vf)
                store = gtk_tree_view_get_model(GTK_TREE_VIEW(vf->listview));
                gtk_tree_model_get_iter(store, &iter, tpath);
                gtk_tree_path_free(tpath);
+               tpath = NULL;
 
                while (!fd && valid && tree_view_row_get_visibility(GTK_TREE_VIEW(vf->listview), &iter, FALSE) == 0)
                        {
similarity index 97%
rename from src/view_file_list.h
rename to src/view_file/view_file_list.h
index ca9e75d..38bc829 100644 (file)
@@ -19,9 +19,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-#ifndef VIEW_FILE_LIST_H
-#define VIEW_FILE_LIST_H
-
+#ifndef VIEW_FILE_VIEW_FILE_LIST_H
+#define VIEW_FILE_VIEW_FILE_LIST_H
 
 #include "filedata.h"
 
index 45782d6..6ccd2ad 100644 (file)
@@ -257,7 +257,7 @@ void help_window_show(const gchar *key)
                return;
                }
 
-       path = g_build_filename(GQ_HELPDIR, "README", NULL);
+       path = g_build_filename(GQ_HELPDIR, "README.md", NULL);
        help_window = help_window_new(_("Help"), "help", path, key);
        g_free(path);