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:
Niklas Haas 2023-01-02 21:27:12 +01:00 committed by GitHub
parent 3423a7ac55
commit 2101b846c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 275 additions and 81 deletions

View File

@ -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

View File

@ -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) {

View File

@ -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);

View File

@ -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),

View File

@ -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)
{

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 ();

View File

@ -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,11 +1237,32 @@ void ThumbBrowserBase::enableTabMode(bool enable)
}
}
void ThumbBrowserBase::initEntry (ThumbBrowserEntryBase* entry)
void ThumbBrowserBase::insertEntry (ThumbBrowserEntryBase* entry)
{
// 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)
{
h = hscroll.get_value ();

View File

@ -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);

View File

@ -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),

View File

@ -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
{
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;

View File

@ -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,20 +719,7 @@ rtengine::IImage8* Thumbnail::upgradeThumbImage (const rtengine::procparams::Pro
void Thumbnail::generateExifDateTimeStrings ()
{
exifString = "";
dateTimeString = "";
if (!cfs.exifValid) {
return;
}
exifString = Glib::ustring::compose ("f/%1 %2s %3%4 %5mm", Glib::ustring(rtengine::FramesData::apertureToString(cfs.fnumber)), Glib::ustring(rtengine::FramesData::shutterToString(cfs.shutter)), M("QINFO_ISO"), cfs.iso, Glib::ustring::format(std::setw(3), std::fixed, std::setprecision(2), cfs.focalLen));
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
}
if (cfs.timeValid) {
std::string dateFormat = options.dateFormat;
std::ostringstream ostr;
bool spec = false;
@ -758,6 +746,25 @@ void Thumbnail::generateExifDateTimeStrings ()
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;
}
exifString = Glib::ustring::compose ("f/%1 %2s %3%4 %5mm", Glib::ustring(rtengine::FramesData::apertureToString(cfs.fnumber)), Glib::ustring(rtengine::FramesData::shutterToString(cfs.shutter)), M("QINFO_ISO"), cfs.iso, Glib::ustring::format(std::setw(3), std::fixed, std::setprecision(2), cfs.focalLen));
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
}
}
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;

View File

@ -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);