Implement file sorting in thumbnail view (#6449)
* Use mtime as fallback timestamp for files without EXIF data As suggested in #6449, with date-based sorting it can be useful to have at least *some* sort of time-relevant information for EXIF-less files, to prevent them from falling back to getting sorted alphabetically all the time. This commit simply defaults the file timestamp to the file's mtime as returned by g_stat. For annoying reasons, it doesn't suffice to merely forward the timestamp to the FileData structs - we also need to keep track of it inside FilesData to cover the case of a file with 0 frames in it. * Add DateTime to Thumbnail Putting it here facilitate easier sorting without having to re-construct the DateTime on every comparison. To simplify things moving forwards, use the Glib::DateTime struct right away. This struct also contains timezone information, but we don't currently care about timezone - so just use the local timezone as the best approximation. (Nothing currently depends on getting the timezone right, anyway) In addition to the above, this commit also changes the logic to allow generating datetime strings even for files with missing EXIF (which makes sense as a result of the previous commit allowing the use of mtime instead). * Implement file sorting in thumbnail view For simplicity, I decided to only implement the attributes that I could verily easily reach from the existing metadata exported by Thumbnail. Ideally, I would also like to be able to sort by "last modified" but I'm not sure of the best way to reach this from this place in the code. It's worth pointing out that, with the current implementation, the list will not dynamically re-sort itself until you re-select the sorting method - even if you make changes to the files that would otherwise affect the sorting (e.g. changing the rank while sorting by rank). One might even call this a feature, not a bug, since it prevents thumbnails from moving around while you're trying to re-label them. You can always re-select "sort by ..." from the context menu to force a re-sort. Fixes #3317 Co-authored-by: Thanatomanic <6567747+Thanatomanic@users.noreply.github.com>
This commit is contained in:
parent
3423a7ac55
commit
2101b846c3
@ -166,6 +166,7 @@ FILEBROWSER_POPUPREMOVE;Delete permanently
|
||||
FILEBROWSER_POPUPREMOVEINCLPROC;Delete permanently, including queue-processed version
|
||||
FILEBROWSER_POPUPRENAME;Rename
|
||||
FILEBROWSER_POPUPSELECTALL;Select all
|
||||
FILEBROWSER_POPUPSORTBY;Sort Files
|
||||
FILEBROWSER_POPUPTRASH;Move to trash
|
||||
FILEBROWSER_POPUPUNRANK;Unrank
|
||||
FILEBROWSER_POPUPUNTRASH;Remove from trash
|
||||
@ -2060,6 +2061,13 @@ SAVEDLG_WARNFILENAME;File will be named
|
||||
SHCSELECTOR_TOOLTIP;Click right mouse button to reset the position of those 3 sliders.
|
||||
SOFTPROOF_GAMUTCHECK_TOOLTIP;Highlight pixels with out-of-gamut colors with respect to:\n- the printer profile, if one is set and soft-proofing is enabled,\n- the output profile, if a printer profile is not set and soft-proofing is enabled,\n- the monitor profile, if soft-proofing is disabled.
|
||||
SOFTPROOF_TOOLTIP;Soft-proofing simulates the appearance of the image:\n- when printed, if a printer profile is set in Preferences > Color Management,\n- when viewed on a display that uses the current output profile, if a printer profile is not set.
|
||||
SORT_ASCENDING;Ascending
|
||||
SORT_BY_NAME;By Name
|
||||
SORT_BY_DATE;By Date
|
||||
SORT_BY_EXIF;By EXIF
|
||||
SORT_BY_RANK;By Rank
|
||||
SORT_BY_LABEL;By Color Label
|
||||
SORT_DESCENDING;Descending
|
||||
TC_PRIM_BLUX;Bx
|
||||
TC_PRIM_BLUY;By
|
||||
TC_PRIM_GREX;Gx
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <tiff.h>
|
||||
|
||||
@ -57,7 +58,8 @@ template<typename T>
|
||||
T getFromFrame(
|
||||
const std::vector<std::unique_ptr<FrameData>>& frames,
|
||||
std::size_t frame,
|
||||
const std::function<T (const FrameData&)>& function
|
||||
const std::function<T (const FrameData&)>& function,
|
||||
T defval = {}
|
||||
)
|
||||
{
|
||||
if (frame < frames.size()) {
|
||||
@ -66,7 +68,7 @@ T getFromFrame(
|
||||
if (!frames.empty()) {
|
||||
return function(*frames[0]);
|
||||
}
|
||||
return {};
|
||||
return defval;
|
||||
}
|
||||
|
||||
const std::string& validateUft8(const std::string& str, const std::string& on_error = "???")
|
||||
@ -85,11 +87,21 @@ FramesMetaData* FramesMetaData::fromFile(const Glib::ustring& fname, std::unique
|
||||
return new FramesData(fname, std::move(rml), firstFrameOnly);
|
||||
}
|
||||
|
||||
FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir) :
|
||||
static struct tm timeFromTS(const time_t ts)
|
||||
{
|
||||
#if !defined(WIN32)
|
||||
struct tm tm;
|
||||
return *gmtime_r(&ts, &tm);
|
||||
#else
|
||||
return *gmtime(&ts);
|
||||
#endif
|
||||
}
|
||||
|
||||
FrameData::FrameData(rtexif::TagDirectory* frameRootDir_, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir, time_t ts) :
|
||||
frameRootDir(frameRootDir_),
|
||||
iptc(nullptr),
|
||||
time{},
|
||||
timeStamp{},
|
||||
time(timeFromTS(ts)),
|
||||
timeStamp(ts),
|
||||
iso_speed(0),
|
||||
aperture(0.),
|
||||
focal_len(0.),
|
||||
@ -1068,7 +1080,8 @@ tm FramesData::getDateTime(unsigned int frame) const
|
||||
[](const FrameData& frame_data)
|
||||
{
|
||||
return frame_data.getDateTime();
|
||||
}
|
||||
},
|
||||
modTime
|
||||
);
|
||||
}
|
||||
|
||||
@ -1080,7 +1093,8 @@ time_t FramesData::getDateTimeAsTS(unsigned int frame) const
|
||||
[](const FrameData& frame_data)
|
||||
{
|
||||
return frame_data.getDateTimeAsTS();
|
||||
}
|
||||
},
|
||||
modTimeStamp
|
||||
);
|
||||
}
|
||||
|
||||
@ -1366,6 +1380,11 @@ failure:
|
||||
FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr<RawMetaDataLocation> rml, bool firstFrameOnly) :
|
||||
iptc(nullptr), dcrawFrameCount(0)
|
||||
{
|
||||
GStatBuf statbuf = {};
|
||||
g_stat(fname.c_str(), &statbuf);
|
||||
modTimeStamp = statbuf.st_mtime;
|
||||
modTime = timeFromTS(modTimeStamp);
|
||||
|
||||
if (rml && (rml->exifBase >= 0 || rml->ciffBase >= 0)) {
|
||||
FILE* f = g_fopen(fname.c_str(), "rb");
|
||||
|
||||
@ -1384,7 +1403,7 @@ FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr<RawMetaDataLo
|
||||
|
||||
// creating FrameData
|
||||
for (auto currFrame : exifManager.frames) {
|
||||
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
|
||||
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0), modTimeStamp)));
|
||||
}
|
||||
|
||||
for (auto currRoot : roots) {
|
||||
@ -1410,7 +1429,7 @@ FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr<RawMetaDataLo
|
||||
roots = exifManager.roots;
|
||||
|
||||
for (auto currFrame : exifManager.frames) {
|
||||
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
|
||||
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0), modTimeStamp)));
|
||||
}
|
||||
|
||||
rewind(exifManager.f); // Not sure this is necessary
|
||||
@ -1430,7 +1449,7 @@ FramesData::FramesData(const Glib::ustring& fname, std::unique_ptr<RawMetaDataLo
|
||||
|
||||
// creating FrameData
|
||||
for (auto currFrame : exifManager.frames) {
|
||||
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0))));
|
||||
frames.push_back(std::unique_ptr<FrameData>(new FrameData(currFrame, currFrame->getRoot(), roots.at(0), modTimeStamp)));
|
||||
}
|
||||
|
||||
for (auto currRoot : roots) {
|
||||
|
@ -72,7 +72,7 @@ protected:
|
||||
|
||||
public:
|
||||
|
||||
FrameData (rtexif::TagDirectory* frameRootDir, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir);
|
||||
FrameData (rtexif::TagDirectory* frameRootDir, rtexif::TagDirectory* rootDir, rtexif::TagDirectory* firstRootDir, time_t ts = 0);
|
||||
virtual ~FrameData ();
|
||||
|
||||
bool getPixelShift () const;
|
||||
@ -109,6 +109,8 @@ private:
|
||||
std::vector<rtexif::TagDirectory*> roots;
|
||||
IptcData* iptc;
|
||||
unsigned int dcrawFrameCount;
|
||||
struct tm modTime;
|
||||
time_t modTimeStamp;
|
||||
|
||||
public:
|
||||
explicit FramesData (const Glib::ustring& fname, std::unique_ptr<RawMetaDataLocation> rml = nullptr, bool firstFrameOnly = false);
|
||||
|
@ -34,7 +34,7 @@ bool BatchQueueEntry::iconsLoaded(false);
|
||||
Glib::RefPtr<Gdk::Pixbuf> BatchQueueEntry::savedAsIcon;
|
||||
|
||||
BatchQueueEntry::BatchQueueEntry (rtengine::ProcessingJob* pjob, const rtengine::procparams::ProcParams& pparams, Glib::ustring fname, int prevw, int prevh, Thumbnail* thm, bool overwrite) :
|
||||
ThumbBrowserEntryBase(fname),
|
||||
ThumbBrowserEntryBase(fname, thm),
|
||||
opreview(nullptr),
|
||||
origpw(prevw),
|
||||
origph(prevh),
|
||||
|
@ -167,6 +167,41 @@ FileBrowser::FileBrowser () :
|
||||
pmenu->attach (*Gtk::manage(selall = new Gtk::MenuItem (M("FILEBROWSER_POPUPSELECTALL"))), 0, 1, p, p + 1);
|
||||
p++;
|
||||
|
||||
/***********************
|
||||
* sort
|
||||
***********************/
|
||||
const std::array<std::string, 2> cnameSortOrders = {
|
||||
M("SORT_ASCENDING"),
|
||||
M("SORT_DESCENDING"),
|
||||
};
|
||||
|
||||
const std::array<std::string, Options::SORT_METHOD_COUNT> cnameSortMethods = {
|
||||
M("SORT_BY_NAME"),
|
||||
M("SORT_BY_DATE"),
|
||||
M("SORT_BY_EXIF"),
|
||||
M("SORT_BY_RANK"),
|
||||
M("SORT_BY_LABEL"),
|
||||
};
|
||||
|
||||
pmenu->attach (*Gtk::manage(menuSort = new Gtk::MenuItem (M("FILEBROWSER_POPUPSORTBY"))), 0, 1, p, p + 1);
|
||||
p++;
|
||||
Gtk::Menu* submenuSort = Gtk::manage (new Gtk::Menu ());
|
||||
Gtk::RadioButtonGroup sortOrderGroup, sortMethodGroup;
|
||||
for (size_t i = 0; i < cnameSortOrders.size(); i++) {
|
||||
submenuSort->attach (*Gtk::manage(sortOrder[i] = new Gtk::RadioMenuItem (sortOrderGroup, cnameSortOrders[i])), 0, 1, p, p + 1);
|
||||
p++;
|
||||
sortOrder[i]->set_active (i == options.sortDescending);
|
||||
}
|
||||
submenuSort->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
||||
p++;
|
||||
for (size_t i = 0; i < cnameSortMethods.size(); i++) {
|
||||
submenuSort->attach (*Gtk::manage(sortMethod[i] = new Gtk::RadioMenuItem (sortMethodGroup, cnameSortMethods[i])), 0, 1, p, p + 1);
|
||||
p++;
|
||||
sortMethod[i]->set_active (i == options.sortMethod);
|
||||
}
|
||||
submenuSort->show_all ();
|
||||
menuSort->set_submenu (*submenuSort);
|
||||
|
||||
/***********************
|
||||
* rank
|
||||
***********************/
|
||||
@ -427,6 +462,14 @@ FileBrowser::FileBrowser () :
|
||||
inspect->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), inspect));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
sortOrder[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), sortOrder[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < Options::SORT_METHOD_COUNT; i++) {
|
||||
sortMethod[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), sortMethod[i]));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++) {
|
||||
rank[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), rank[i]));
|
||||
}
|
||||
@ -610,27 +653,7 @@ void FileBrowser::addEntry_ (FileBrowserEntry* entry)
|
||||
entry->getThumbButtonSet()->setButtonListener(this);
|
||||
entry->resize(getThumbnailHeight());
|
||||
entry->filtered = !checkFilter(entry);
|
||||
|
||||
// find place in abc order
|
||||
{
|
||||
MYWRITERLOCK(l, entryRW);
|
||||
|
||||
fd.insert(
|
||||
std::lower_bound(
|
||||
fd.begin(),
|
||||
fd.end(),
|
||||
entry,
|
||||
[](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b)
|
||||
{
|
||||
return *a < *b;
|
||||
}
|
||||
),
|
||||
entry
|
||||
);
|
||||
|
||||
initEntry(entry);
|
||||
}
|
||||
redraw(entry);
|
||||
insertEntry(entry);
|
||||
}
|
||||
|
||||
FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname)
|
||||
@ -724,6 +747,18 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m)
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
if (m == sortOrder[i]) {
|
||||
sortOrderRequested (i);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Options::SORT_METHOD_COUNT; i++)
|
||||
if (m == sortMethod[i]) {
|
||||
sortMethodRequested (i);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
if (m == rank[i]) {
|
||||
rankingRequested (mselected, i);
|
||||
@ -1632,6 +1667,18 @@ void FileBrowser::fromTrashRequested (std::vector<FileBrowserEntry*> tbe)
|
||||
applyFilter (filter);
|
||||
}
|
||||
|
||||
void FileBrowser::sortMethodRequested (int method)
|
||||
{
|
||||
options.sortMethod = Options::SortMethod(method);
|
||||
resort ();
|
||||
}
|
||||
|
||||
void FileBrowser::sortOrderRequested (int order)
|
||||
{
|
||||
options.sortDescending = !!order;
|
||||
resort ();
|
||||
}
|
||||
|
||||
void FileBrowser::rankingRequested (std::vector<FileBrowserEntry*> tbe, int rank)
|
||||
{
|
||||
|
||||
|
@ -83,9 +83,12 @@ protected:
|
||||
Gtk::MenuItem* open;
|
||||
Gtk::MenuItem* inspect;
|
||||
Gtk::MenuItem* selall;
|
||||
Gtk::RadioMenuItem* sortMethod[Options::SORT_METHOD_COUNT];
|
||||
Gtk::RadioMenuItem* sortOrder[2];
|
||||
Gtk::MenuItem* copyTo;
|
||||
Gtk::MenuItem* moveTo;
|
||||
|
||||
Gtk::MenuItem* menuSort;
|
||||
Gtk::MenuItem* menuRank;
|
||||
Gtk::MenuItem* menuLabel;
|
||||
Gtk::MenuItem* menuFileOperations;
|
||||
@ -131,6 +134,8 @@ protected:
|
||||
|
||||
void toTrashRequested (std::vector<FileBrowserEntry*> tbe);
|
||||
void fromTrashRequested (std::vector<FileBrowserEntry*> tbe);
|
||||
void sortMethodRequested (int method);
|
||||
void sortOrderRequested (int order);
|
||||
void rankingRequested (std::vector<FileBrowserEntry*> tbe, int rank);
|
||||
void colorlabelRequested (std::vector<FileBrowserEntry*> tbe, int colorlabel);
|
||||
void requestRanking (int rank);
|
||||
|
@ -45,10 +45,8 @@ Glib::RefPtr<Gdk::Pixbuf> FileBrowserEntry::hdr;
|
||||
Glib::RefPtr<Gdk::Pixbuf> FileBrowserEntry::ps;
|
||||
|
||||
FileBrowserEntry::FileBrowserEntry (Thumbnail* thm, const Glib::ustring& fname)
|
||||
: ThumbBrowserEntryBase (fname), wasInside(false), iatlistener(nullptr), press_x(0), press_y(0), action_x(0), action_y(0), rot_deg(0.0), landscape(true), cropParams(new rtengine::procparams::CropParams), cropgl(nullptr), state(SNormal), crop_custom_ratio(0.f)
|
||||
: ThumbBrowserEntryBase (fname, thm), wasInside(false), iatlistener(nullptr), press_x(0), press_y(0), action_x(0), action_y(0), rot_deg(0.0), landscape(true), cropParams(new rtengine::procparams::CropParams), cropgl(nullptr), state(SNormal), crop_custom_ratio(0.f)
|
||||
{
|
||||
thumbnail = thm;
|
||||
|
||||
feih = new FileBrowserEntryIdleHelper;
|
||||
feih->fbentry = this;
|
||||
feih->destroyed = false;
|
||||
|
@ -685,6 +685,8 @@ void Options::setDefaults()
|
||||
lastICCProfCreatorDir = "";
|
||||
gimpPluginShowInfoDialog = true;
|
||||
maxRecentFolders = 15;
|
||||
sortMethod = SORT_BY_NAME;
|
||||
sortDescending = false;
|
||||
rtSettings.lensfunDbDirectory = ""; // set also in main.cc and main-cli.cc
|
||||
cropGuides = CROP_GUIDE_FULL;
|
||||
cropAutoFit = false;
|
||||
@ -1150,6 +1152,19 @@ void Options::readFromFile(Glib::ustring fname)
|
||||
if (keyFile.has_key("File Browser", "RecentFolders")) {
|
||||
recentFolders = keyFile.get_string_list("File Browser", "RecentFolders");
|
||||
}
|
||||
|
||||
if (keyFile.has_key("File Browser", "SortMethod")) {
|
||||
int v = keyFile.get_integer("File Browser", "SortMethod");
|
||||
if (v < int(0) || v >= int(SORT_METHOD_COUNT)) {
|
||||
sortMethod = SORT_BY_NAME;
|
||||
} else {
|
||||
sortMethod = SortMethod(v);
|
||||
}
|
||||
}
|
||||
|
||||
if (keyFile.has_key("File Browser", "SortDescending")) {
|
||||
sortDescending = keyFile.get_boolean("File Browser", "SortDescending");
|
||||
}
|
||||
}
|
||||
|
||||
if (keyFile.has_group("Clipping Indication")) {
|
||||
@ -2217,6 +2232,8 @@ void Options::saveToFile(Glib::ustring fname)
|
||||
|
||||
keyFile.set_string_list("File Browser", "RecentFolders", temp);
|
||||
}
|
||||
keyFile.set_integer("File Browser", "SortMethod", sortMethod);
|
||||
keyFile.set_boolean("File Browser", "SortDescending", sortDescending);
|
||||
keyFile.set_integer("Clipping Indication", "HighlightThreshold", highlightThreshold);
|
||||
keyFile.set_integer("Clipping Indication", "ShadowThreshold", shadowThreshold);
|
||||
keyFile.set_boolean("Clipping Indication", "BlinkClipped", blinkClipped);
|
||||
|
@ -452,6 +452,17 @@ public:
|
||||
size_t maxRecentFolders; // max. number of recent folders stored in options file
|
||||
std::vector<Glib::ustring> recentFolders; // List containing all recent folders
|
||||
|
||||
enum SortMethod {
|
||||
SORT_BY_NAME,
|
||||
SORT_BY_DATE,
|
||||
SORT_BY_EXIF,
|
||||
SORT_BY_RANK,
|
||||
SORT_BY_LABEL,
|
||||
SORT_METHOD_COUNT,
|
||||
};
|
||||
SortMethod sortMethod; // remembers current state of file browser
|
||||
bool sortDescending;
|
||||
|
||||
|
||||
Options ();
|
||||
|
||||
|
@ -1091,6 +1091,25 @@ bool ThumbBrowserBase::Internal::on_scroll_event (GdkEventScroll* event)
|
||||
}
|
||||
|
||||
|
||||
void ThumbBrowserBase::resort ()
|
||||
{
|
||||
{
|
||||
MYWRITERLOCK(l, entryRW);
|
||||
|
||||
std::sort(
|
||||
fd.begin(),
|
||||
fd.end(),
|
||||
[](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b)
|
||||
{
|
||||
bool lt = a->compare(*b, options.sortMethod);
|
||||
return options.sortDescending ? !lt : lt;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void ThumbBrowserBase::redraw (ThumbBrowserEntryBase* entry)
|
||||
{
|
||||
|
||||
@ -1218,9 +1237,30 @@ void ThumbBrowserBase::enableTabMode(bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
void ThumbBrowserBase::initEntry (ThumbBrowserEntryBase* entry)
|
||||
void ThumbBrowserBase::insertEntry (ThumbBrowserEntryBase* entry)
|
||||
{
|
||||
entry->setOffset ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
|
||||
// find place in sort order
|
||||
{
|
||||
MYWRITERLOCK(l, entryRW);
|
||||
|
||||
fd.insert(
|
||||
std::lower_bound(
|
||||
fd.begin(),
|
||||
fd.end(),
|
||||
entry,
|
||||
[](const ThumbBrowserEntryBase* a, const ThumbBrowserEntryBase* b)
|
||||
{
|
||||
bool lt = a->compare(*b, options.sortMethod);
|
||||
return options.sortDescending ? !lt : lt;
|
||||
}
|
||||
),
|
||||
entry
|
||||
);
|
||||
|
||||
entry->setOffset ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
|
||||
}
|
||||
|
||||
redraw ();
|
||||
}
|
||||
|
||||
void ThumbBrowserBase::getScrollPosition (double& h, double& v)
|
||||
|
@ -208,12 +208,13 @@ public:
|
||||
return fd;
|
||||
}
|
||||
void on_style_updated () override;
|
||||
void resort (); // re-apply sort method
|
||||
void redraw (ThumbBrowserEntryBase* entry = nullptr); // arrange files and draw area
|
||||
void refreshThumbImages (); // refresh thumbnail sizes, re-generate thumbnail images, arrange and draw
|
||||
void refreshQuickThumbImages (); // refresh thumbnail sizes, re-generate thumbnail images, arrange and draw
|
||||
void refreshEditedState (const std::set<Glib::ustring>& efiles);
|
||||
|
||||
void initEntry (ThumbBrowserEntryBase* entry);
|
||||
void insertEntry (ThumbBrowserEntryBase* entry);
|
||||
|
||||
void getScrollPosition (double& h, double& v);
|
||||
void setScrollPosition (double h, double v);
|
||||
|
@ -119,7 +119,7 @@ Glib::ustring getPaddedName(const Glib::ustring& name)
|
||||
|
||||
}
|
||||
|
||||
ThumbBrowserEntryBase::ThumbBrowserEntryBase (const Glib::ustring& fname) :
|
||||
ThumbBrowserEntryBase::ThumbBrowserEntryBase (const Glib::ustring& fname, Thumbnail *thm) :
|
||||
fnlabw(0),
|
||||
fnlabh(0),
|
||||
dtlabw(0),
|
||||
@ -153,7 +153,8 @@ ThumbBrowserEntryBase::ThumbBrowserEntryBase (const Glib::ustring& fname) :
|
||||
bbPreview(nullptr),
|
||||
cursor_type(CSUndefined),
|
||||
collate_name(getPaddedName(dispname).casefold_collate_key()),
|
||||
thumbnail(nullptr),
|
||||
collate_exif(getPaddedName(thm->getExifString()).casefold_collate_key()),
|
||||
thumbnail(thm),
|
||||
filename(fname),
|
||||
selected(false),
|
||||
drawable(false),
|
||||
|
@ -26,6 +26,8 @@
|
||||
#include "guiutils.h"
|
||||
#include "lwbuttonset.h"
|
||||
#include "threadutils.h"
|
||||
#include "options.h"
|
||||
#include "thumbnail.h"
|
||||
|
||||
#include "../rtengine/coord2d.h"
|
||||
|
||||
@ -95,6 +97,7 @@ protected:
|
||||
|
||||
private:
|
||||
const std::string collate_name;
|
||||
const std::string collate_exif;
|
||||
|
||||
public:
|
||||
|
||||
@ -117,7 +120,7 @@ public:
|
||||
bool updatepriority;
|
||||
eWithFilename withFilename;
|
||||
|
||||
explicit ThumbBrowserEntryBase (const Glib::ustring& fname);
|
||||
explicit ThumbBrowserEntryBase (const Glib::ustring& fname, Thumbnail *thm);
|
||||
virtual ~ThumbBrowserEntryBase ();
|
||||
|
||||
void setParent (ThumbBrowserBase* l)
|
||||
@ -174,9 +177,32 @@ public:
|
||||
void setPosition (int x, int y, int w, int h);
|
||||
void setOffset (int x, int y);
|
||||
|
||||
bool operator <(const ThumbBrowserEntryBase& other) const
|
||||
bool compare (const ThumbBrowserEntryBase& other, Options::SortMethod method) const
|
||||
{
|
||||
return collate_name < other.collate_name;
|
||||
int cmp = 0;
|
||||
switch (method){
|
||||
case Options::SORT_BY_NAME:
|
||||
return collate_name < other.collate_name;
|
||||
case Options::SORT_BY_DATE:
|
||||
cmp = thumbnail->getDateTime().compare(other.thumbnail->getDateTime());
|
||||
break;
|
||||
case Options::SORT_BY_EXIF:
|
||||
cmp = collate_exif.compare(other.collate_exif);
|
||||
break;
|
||||
case Options::SORT_BY_RANK:
|
||||
cmp = thumbnail->getRank() - other.thumbnail->getRank();
|
||||
break;
|
||||
case Options::SORT_BY_LABEL:
|
||||
cmp = thumbnail->getColorLabel() - other.thumbnail->getColorLabel();
|
||||
break;
|
||||
case Options::SORT_METHOD_COUNT: abort();
|
||||
}
|
||||
|
||||
// Always fall back to sorting by name
|
||||
if (!cmp)
|
||||
cmp = collate_name.compare(other.collate_name);
|
||||
|
||||
return cmp < 0;
|
||||
}
|
||||
|
||||
virtual void refreshThumbnailImage () = 0;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "../rtengine/procparams.h"
|
||||
#include "../rtengine/rtthumbnail.h"
|
||||
#include <glib/gstdio.h>
|
||||
#include <glibmm/timezone.h>
|
||||
|
||||
#include "../rtengine/dynamicprofile.h"
|
||||
#include "../rtengine/profilestore.h"
|
||||
@ -718,11 +719,44 @@ rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::Pro
|
||||
|
||||
void Thumbnail::generateExifDateTimeStrings ()
|
||||
{
|
||||
if (cfs.timeValid) {
|
||||
std::string dateFormat = options.dateFormat;
|
||||
std::ostringstream ostr;
|
||||
bool spec = false;
|
||||
|
||||
exifString = "";
|
||||
dateTimeString = "";
|
||||
for (size_t i = 0; i < dateFormat.size(); i++)
|
||||
if (spec && dateFormat[i] == 'y') {
|
||||
ostr << cfs.year;
|
||||
spec = false;
|
||||
} else if (spec && dateFormat[i] == 'm') {
|
||||
ostr << (int)cfs.month;
|
||||
spec = false;
|
||||
} else if (spec && dateFormat[i] == 'd') {
|
||||
ostr << (int)cfs.day;
|
||||
spec = false;
|
||||
} else if (dateFormat[i] == '%') {
|
||||
spec = true;
|
||||
} else {
|
||||
ostr << (char)dateFormat[i];
|
||||
spec = false;
|
||||
}
|
||||
|
||||
ostr << " " << (int)cfs.hour;
|
||||
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.min;
|
||||
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.sec;
|
||||
|
||||
dateTimeString = ostr.str ();
|
||||
dateTime = Glib::DateTime::create_local(cfs.year, cfs.month, cfs.day,
|
||||
cfs.hour, cfs.min, cfs.sec);
|
||||
}
|
||||
|
||||
if (!dateTime.gobj() || !cfs.timeValid) {
|
||||
dateTimeString = "";
|
||||
dateTime = Glib::DateTime::create_now_utc(0);
|
||||
}
|
||||
|
||||
if (!cfs.exifValid) {
|
||||
exifString = "";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -731,33 +765,6 @@ void Thumbnail::generateExifDateTimeStrings ()
|
||||
if (options.fbShowExpComp && cfs.expcomp != "0.00" && !cfs.expcomp.empty()) { // don't show exposure compensation if it is 0.00EV;old cache files do not have ExpComp, so value will not be displayed.
|
||||
exifString = Glib::ustring::compose ("%1 %2EV", exifString, cfs.expcomp); // append exposure compensation to exifString
|
||||
}
|
||||
|
||||
std::string dateFormat = options.dateFormat;
|
||||
std::ostringstream ostr;
|
||||
bool spec = false;
|
||||
|
||||
for (size_t i = 0; i < dateFormat.size(); i++)
|
||||
if (spec && dateFormat[i] == 'y') {
|
||||
ostr << cfs.year;
|
||||
spec = false;
|
||||
} else if (spec && dateFormat[i] == 'm') {
|
||||
ostr << (int)cfs.month;
|
||||
spec = false;
|
||||
} else if (spec && dateFormat[i] == 'd') {
|
||||
ostr << (int)cfs.day;
|
||||
spec = false;
|
||||
} else if (dateFormat[i] == '%') {
|
||||
spec = true;
|
||||
} else {
|
||||
ostr << (char)dateFormat[i];
|
||||
spec = false;
|
||||
}
|
||||
|
||||
ostr << " " << (int)cfs.hour;
|
||||
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.min;
|
||||
ostr << ":" << std::setw(2) << std::setfill('0') << (int)cfs.sec;
|
||||
|
||||
dateTimeString = ostr.str ();
|
||||
}
|
||||
|
||||
const Glib::ustring& Thumbnail::getExifString () const
|
||||
@ -772,6 +779,12 @@ const Glib::ustring& Thumbnail::getDateTimeString () const
|
||||
return dateTimeString;
|
||||
}
|
||||
|
||||
const Glib::DateTime& Thumbnail::getDateTime () const
|
||||
{
|
||||
|
||||
return dateTime;
|
||||
}
|
||||
|
||||
void Thumbnail::getAutoWB (double& temp, double& green, double equal, double tempBias)
|
||||
{
|
||||
if (cfs.redAWBMul != -1.0) {
|
||||
@ -802,6 +815,16 @@ int Thumbnail::infoFromImage (const Glib::ustring& fname, std::unique_ptr<rtengi
|
||||
cfs.timeValid = false;
|
||||
cfs.exifValid = false;
|
||||
|
||||
if (idata->getDateTimeAsTS() > 0) {
|
||||
cfs.year = 1900 + idata->getDateTime().tm_year;
|
||||
cfs.month = idata->getDateTime().tm_mon + 1;
|
||||
cfs.day = idata->getDateTime().tm_mday;
|
||||
cfs.hour = idata->getDateTime().tm_hour;
|
||||
cfs.min = idata->getDateTime().tm_min;
|
||||
cfs.sec = idata->getDateTime().tm_sec;
|
||||
cfs.timeValid = true;
|
||||
}
|
||||
|
||||
if (idata->hasExif()) {
|
||||
cfs.shutter = idata->getShutterSpeed ();
|
||||
cfs.fnumber = idata->getFNumber ();
|
||||
@ -814,18 +837,11 @@ int Thumbnail::infoFromImage (const Glib::ustring& fname, std::unique_ptr<rtengi
|
||||
cfs.isPixelShift = idata->getPixelShift ();
|
||||
cfs.frameCount = idata->getFrameCount ();
|
||||
cfs.sampleFormat = idata->getSampleFormat ();
|
||||
cfs.year = 1900 + idata->getDateTime().tm_year;
|
||||
cfs.month = idata->getDateTime().tm_mon + 1;
|
||||
cfs.day = idata->getDateTime().tm_mday;
|
||||
cfs.hour = idata->getDateTime().tm_hour;
|
||||
cfs.min = idata->getDateTime().tm_min;
|
||||
cfs.sec = idata->getDateTime().tm_sec;
|
||||
cfs.timeValid = true;
|
||||
cfs.exifValid = true;
|
||||
cfs.lens = idata->getLens();
|
||||
cfs.camMake = idata->getMake();
|
||||
cfs.camModel = idata->getModel();
|
||||
cfs.rating = idata->getRating();
|
||||
cfs.exifValid = true;
|
||||
|
||||
if (idata->getOrientation() == "Rotate 90 CW") {
|
||||
deg = 90;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <string>
|
||||
|
||||
#include <glibmm/ustring.h>
|
||||
#include <glibmm/datetime.h>
|
||||
|
||||
#include "cacheimagedata.h"
|
||||
#include "threadutils.h"
|
||||
@ -73,6 +74,7 @@ class Thumbnail
|
||||
// exif & date/time strings
|
||||
Glib::ustring exifString;
|
||||
Glib::ustring dateTimeString;
|
||||
Glib::DateTime dateTime;
|
||||
|
||||
bool initial_;
|
||||
|
||||
@ -124,6 +126,7 @@ public:
|
||||
|
||||
const Glib::ustring& getExifString () const;
|
||||
const Glib::ustring& getDateTimeString () const;
|
||||
const Glib::DateTime& getDateTime () const;
|
||||
void getCamWB (double& temp, double& green) const;
|
||||
void getAutoWB (double& temp, double& green, double equal, double tempBias);
|
||||
void getSpotWB (int x, int y, int rect, double& temp, double& green);
|
||||
|
Loading…
x
Reference in New Issue
Block a user