From b92e77fb961fa84b4d3e09c37e3ccdd0c340ea0d Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 10 Apr 2020 05:54:40 -0700 Subject: [PATCH] improved support for metadata editing (cherry picked from commit 85da0b51ecf7ece768c0267aead71dd94404d4dc) --- rtdata/images/svg/edit-small.svg | 128 +++++++++++++++++ rtdata/images/svg/edit.svg | 136 ++++++++++++++++++ rtdata/languages/default | 5 +- rtengine/imagedata.cc | 33 +++++ rtengine/imagedata.h | 3 + rtengine/metadata.cc | 11 ++ rtengine/procparams.cc | 154 +++++++++++++-------- rtengine/procparams.h | 46 ++++--- rtengine/simpleprocess.cc | 6 +- rtgui/exifpanel.cc | 227 ++++++++++++++++++++----------- rtgui/exifpanel.h | 19 ++- rtgui/iptcpanel.cc | 8 +- rtgui/paramsedited.cc | 8 +- rtgui/thumbnail.cc | 6 +- rtgui/toolpanelcoord.cc | 8 +- 15 files changed, 615 insertions(+), 183 deletions(-) create mode 100644 rtdata/images/svg/edit-small.svg create mode 100644 rtdata/images/svg/edit.svg diff --git a/rtdata/images/svg/edit-small.svg b/rtdata/images/svg/edit-small.svg new file mode 100644 index 000000000..6306f06df --- /dev/null +++ b/rtdata/images/svg/edit-small.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + image/svg+xml + + + + + Maciej Dworak + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + + + diff --git a/rtdata/images/svg/edit.svg b/rtdata/images/svg/edit.svg new file mode 100644 index 000000000..71f3d0b06 --- /dev/null +++ b/rtdata/images/svg/edit.svg @@ -0,0 +1,136 @@ + + + + + + + + + + + + image/svg+xml + + + + + Maciej Dworak + + + + + + + + RawTherapee icon. + + + + + + + + + + + + + + + + + + + + + diff --git a/rtdata/languages/default b/rtdata/languages/default index 4c0087cd3..a5ce4fa54 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -77,8 +77,7 @@ EXIFPANEL_RESET;Reset EXIFPANEL_RESETALL;Reset All EXIFPANEL_RESETALLHINT;Reset all tags to their original values. EXIFPANEL_RESETHINT;Reset the selected tags to their original values. -EXIFPANEL_SHOWALL;Show all -EXIFPANEL_SUBDIRECTORY;Subdirectory +EXIFPANEL_BASIC_GROUP;Basic EXPORT_BYPASS;Processing steps to bypass EXPORT_BYPASS_ALL;Select / Unselect All EXPORT_BYPASS_DEFRINGE;Bypass Defringe @@ -1548,7 +1547,7 @@ MAIN_TAB_COLOR;Color MAIN_TAB_COLOR_TOOLTIP;Shortcut: Alt-c MAIN_TAB_DETAIL;Detail MAIN_TAB_DETAIL_TOOLTIP;Shortcut: Alt-d -MAIN_TAB_DEVELOP; Batch Edit +MAIN_TAB_DEVELOP; Batch Edit MAIN_TAB_EXIF;Exif MAIN_TAB_EXPORT; Fast Export MAIN_TAB_EXPOSURE;Exposure diff --git a/rtengine/imagedata.cc b/rtengine/imagedata.cc index 42286cd40..52a9fa297 100644 --- a/rtengine/imagedata.cc +++ b/rtengine/imagedata.cc @@ -699,3 +699,36 @@ double FramesMetaData::apertureFromString(std::string s) return std::atof(s.c_str()); } + + +namespace { + +template +void set_exif(Exiv2::ExifData &exif, const std::string &key, T val) +{ + try { + exif[key] = val; + } catch (std::exception &exc) {} +} + +} // namespace + +void FramesData::fillBasicTags(Exiv2::ExifData &exif) const +{ + if (!hasExif()) { + return; + } + set_exif(exif, "Exif.Photo.ISOSpeedRatings", getISOSpeed()); + set_exif(exif, "Exif.Photo.FNumber", Exiv2::DoubleValue(getFNumber())); + //set_exif(exif, "Exif.Photo.ExposureTime", Exiv2::DoubleValue(getShutterSpeed())); + set_exif(exif, "Exif.Photo.ExposureTime", shutterToString(getShutterSpeed())); + set_exif(exif, "Exif.Photo.FocalLength", Exiv2::DoubleValue(getFocalLen())); + set_exif(exif, "Exif.Photo.ExposureBiasValue", Exiv2::DoubleValue(getExpComp())); + set_exif(exif, "Exif.Image.Make", getMake()); + set_exif(exif, "Exif.Image.Model", getModel()); + set_exif(exif, "Exif.Photo.LensModel", getLens()); + char buf[256]; + auto t = getDateTime(); + strftime(buf, 256, "%Y:%m:%d %H:%M:%S", &t); + set_exif(exif, "Exif.Photo.DateTimeOriginal", buf); +} diff --git a/rtengine/imagedata.h b/rtengine/imagedata.h index 306c3b6f9..3a915c15d 100644 --- a/rtengine/imagedata.h +++ b/rtengine/imagedata.h @@ -23,6 +23,7 @@ #include #include "imageio.h" +#include "metadata.h" namespace Glib { @@ -83,6 +84,8 @@ public: std::string getOrientation() const override; Glib::ustring getFileName() const override; int getRating() const override; + + void fillBasicTags(Exiv2::ExifData &exif) const; }; } diff --git a/rtengine/metadata.cc b/rtengine/metadata.cc index af2a7ef8a..d04b979fe 100644 --- a/rtengine/metadata.cc +++ b/rtengine/metadata.cc @@ -26,6 +26,7 @@ #include "metadata.h" #include "settings.h" +#include "imagedata.h" #include "../rtgui/version.h" #include "../rtgui/pathutils.h" @@ -309,6 +310,16 @@ void Exiv2Metadata::remove_unwanted(Exiv2::ExifData &dst) const static const std::vector badpatterns = { "Exif.SubImage" }; + + if (exif_keys_ && !src_.empty()) { + try { + FramesData fd(src_); + fd.fillBasicTags(dst); + } catch (std::exception &exc) { + std::cout << "Error reading metadata from " << src_ + << std::endl; + } + } for (auto it = dst.begin(); it != dst.end(); ) { if (badtags.find(it->key()) != badtags.end()) { diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 5076e147f..06ea2be6a 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -379,33 +379,6 @@ bool saveToKeyfile( return false; } -const std::map exif_keys = { - {"Copyright", "Exif.Image.Copyright"}, - {"Artist", "Exif.Image.Artist"}, - {"ImageDescription", "Exif.Image.ImageDescription"}, - {"Exif.UserComment", "Exif.Photo.UserComment"} -}; - -const std::map iptc_keys = { - {"Title", "Iptc.Application2.ObjectName"}, - {"Category", "Iptc.Application2.Category"}, - {"SupplementalCategories", "Iptc.Application2.SuppCategory"}, - {"Keywords", "Iptc.Application2.Keywords"}, - {"Instructions", "Iptc.Application2.SpecialInstructions"}, - {"DateCreated", "Iptc.Application2.DateCreated"}, - {"Creator", "Iptc.Application2.Byline"}, - {"CreatorJobTitle", "Iptc.Application2.BylineTitle"}, - {"City", "Iptc.Application2.City"}, - {"Province", "Iptc.Application2.ProvinceState"}, - {"Country", "Iptc.Application2.CountryName"}, - {"TransReference", "Iptc.Application2.TransmissionReference"}, - {"Headline", "Iptc.Application2.Headline"}, - {"Credit", "Iptc.Application2.Credit"}, - {"Source", "Iptc.Application2.Source"}, - {"Copyright", "Iptc.Application2.Copyright"}, - {"Caption", "Iptc.Application2.Caption"}, - {"CaptionWriter", "Iptc.Application2.Writer"} -}; } // namespace @@ -5247,23 +5220,6 @@ Glib::ustring RAWParams::getFlatFieldBlurTypeString(FlatFieldBlurType type) } -MetaDataParams::MetaDataParams(): - mode(MetaDataParams::TUNNEL), - exifKeys{"ALL"} -{ -} - -bool MetaDataParams::operator==(const MetaDataParams &other) const -{ - return mode == other.mode - && exifKeys == other.exifKeys; -} - -bool MetaDataParams::operator!=(const MetaDataParams &other) const -{ - return !(*this == other); -} - FilmNegativeParams::FilmNegativeParams() : enabled(false), redRatio(1.36), @@ -5316,6 +5272,90 @@ bool FilmNegativeParams::operator !=(const FilmNegativeParams& other) const return !(*this == other); } + +namespace { + +const std::map exif_keys = { + {"Copyright", "Exif.Image.Copyright"}, + {"Artist", "Exif.Image.Artist"}, + {"ImageDescription", "Exif.Image.ImageDescription"}, + {"Exif.UserComment", "Exif.Photo.UserComment"}, + {"ISOSpeed", "Exif.Photo.ISOSpeedRatings"}, + {"FNumber", "Exif.Photo.FNumber"}, + {"ShutterSpeed", "Exif.Photo.ExposureTime"}, + {"FocalLength", "Exif.Photo.FocalLength"}, + {"ExpComp", "Exif.Photo.ExposureBiasValue"}, + {"Flash", "Exif.Photo.Flash"}, + {"Make", "Exif.Image.Make"}, + {"Model", "Exif.Image.Model"}, + {"Lens", "Exif.Photo.LensModel"}, + {"DateTime", "Exif.Photo.DateTimeOriginal"} +}; + +const std::map iptc_keys = { + {"Title", "Iptc.Application2.ObjectName"}, + {"Category", "Iptc.Application2.Category"}, + {"SupplementalCategories", "Iptc.Application2.SuppCategory"}, + {"Keywords", "Iptc.Application2.Keywords"}, + {"Instructions", "Iptc.Application2.SpecialInstructions"}, + {"DateCreated", "Iptc.Application2.DateCreated"}, + {"Creator", "Iptc.Application2.Byline"}, + {"CreatorJobTitle", "Iptc.Application2.BylineTitle"}, + {"City", "Iptc.Application2.City"}, + {"Province", "Iptc.Application2.ProvinceState"}, + {"Country", "Iptc.Application2.CountryName"}, + {"TransReference", "Iptc.Application2.TransmissionReference"}, + {"Headline", "Iptc.Application2.Headline"}, + {"Credit", "Iptc.Application2.Credit"}, + {"Source", "Iptc.Application2.Source"}, + {"Copyright", "Iptc.Application2.Copyright"}, + {"Caption", "Iptc.Application2.Caption"}, + {"CaptionWriter", "Iptc.Application2.Writer"} +}; + +} // namespace + + +std::vector MetaDataParams::basicExifKeys = { + "Exif.Image.Copyright", + "Exif.Image.Artist", + "Exif.Image.ImageDescription", + "Exif.Photo.UserComment", + "Exif.Image.Make", + "Exif.Image.Model", + "Exif.Photo.LensModel", + "Exif.Photo.FNumber", + "Exif.Photo.ExposureTime", + "Exif.Photo.FocalLength", + "Exif.Photo.ISOSpeedRatings", + "Exif.Photo.ExposureBiasValue", + "Exif.Photo.Flash", + "Exif.Photo.DateTimeOriginal" +}; + + +MetaDataParams::MetaDataParams(): + mode(MetaDataParams::TUNNEL), + exifKeys{"*"}, + exif{}, + iptc{} +{ +} + +bool MetaDataParams::operator==(const MetaDataParams &other) const +{ + return mode == other.mode + && exifKeys == other.exifKeys + && exif == other.exif + && iptc == other.iptc; +} + +bool MetaDataParams::operator!=(const MetaDataParams &other) const +{ + return !(*this == other); +} + + ProcParams::ProcParams() { setDefaults(); @@ -5414,8 +5454,8 @@ void ProcParams::setDefaults() raw = {}; metadata = {}; - exif.clear(); - iptc.clear(); + //exif.clear(); + //iptc.clear(); // -1 means that there's no pp3 data with rank yet. In this case, the // embedded Rating metadata should take precedence. -1 should never be @@ -6876,10 +6916,10 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo for (auto &p : exif_keys) { m[p.second] = p.first; } - for (ExifPairs::const_iterator i = exif.begin(); i != exif.end(); ++i) { - auto it = m.find(i->first); + for (auto &p : metadata.exif) { + auto it = m.find(p.first); if (it != m.end()) { - keyFile.set_string("Exif", it->second, i->second); + keyFile.set_string("Exif", it->second, p.second); } } } @@ -6890,10 +6930,10 @@ int ProcParams::save(const Glib::ustring& fname, const Glib::ustring& fname2, bo for (auto &p : iptc_keys) { m[p.second] = p.first; } - for (IPTCPairs::const_iterator i = iptc.begin(); i != iptc.end(); ++i) { - auto it = m.find(i->first); + for (auto &p : metadata.iptc) { + auto it = m.find(p.first); if (it != m.end()) { - Glib::ArrayHandle values = i->second; + Glib::ArrayHandle values = p.second; keyFile.set_string_list("IPTC", it->second, values); } } @@ -9383,7 +9423,7 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) for (const auto& key : keyFile.get_keys("Exif")) { auto it = exif_keys.find(key); if (it != exif_keys.end()) { - exif[it->second] = keyFile.get_string("Exif", key); + metadata.exif[it->second] = keyFile.get_string("Exif", key); if (pedited) { pedited->exif = true; @@ -9413,16 +9453,16 @@ int ProcParams::load(const Glib::ustring& fname, ParamsEdited* pedited) } auto kk = it->second; - const IPTCPairs::iterator element = iptc.find(kk); + const IPTCPairs::iterator element = metadata.iptc.find(kk); - if (element != iptc.end()) { + if (element != metadata.iptc.end()) { // it already exist so we cleanup the values element->second.clear(); } // TODO: look out if merging Keywords and SupplementalCategories from the procparams chain would be interesting for (const auto& currLoadedTagValue : keyFile.get_string_list("IPTC", key)) { - iptc[kk].push_back(currLoadedTagValue); + metadata.iptc[kk].push_back(currLoadedTagValue); } if (pedited) { @@ -9500,8 +9540,6 @@ bool ProcParams::operator ==(const ProcParams& other) const && rgbCurves == other.rgbCurves && colorToning == other.colorToning && metadata == other.metadata - && exif == other.exif - && iptc == other.iptc && dehaze == other.dehaze && filmNegative == other.filmNegative; } diff --git a/rtengine/procparams.h b/rtengine/procparams.h index dd6872630..d874a9b13 100644 --- a/rtengine/procparams.h +++ b/rtengine/procparams.h @@ -1718,25 +1718,6 @@ struct ColorManagementParams { bool operator !=(const ColorManagementParams& other) const; }; -/** - * Parameters for metadata handling - */ -struct MetaDataParams { - enum Mode { - TUNNEL, - EDIT, - STRIP - }; - Mode mode; - std::vector exifKeys; - - MetaDataParams(); - - bool operator ==(const MetaDataParams &other) const; - bool operator !=(const MetaDataParams &other) const; -}; - - /** * Minimal wrapper allowing forward declaration for representing a key/value for the exif metadata information */ @@ -1841,6 +1822,29 @@ private: std::map> pairs; }; +/** + * Parameters for metadata handling + */ +struct MetaDataParams { + enum Mode { + TUNNEL, + EDIT, + STRIP + }; + Mode mode; + std::vector exifKeys; + ExifPairs exif; + IPTCPairs iptc; + + MetaDataParams(); + + bool operator ==(const MetaDataParams &other) const; + bool operator !=(const MetaDataParams &other) const; + + static std::vector basicExifKeys; +}; + + struct WaveletParams { std::vector ccwcurve; std::vector wavdenoise; @@ -2355,8 +2359,8 @@ public: int ppVersion; ///< Version of the PP file from which the parameters have been read MetaDataParams metadata; ///< Metadata parameters - ExifPairs exif; ///< List of modifications appplied on the exif tags of the input image - IPTCPairs iptc; ///< The IPTC tags and values to be saved to the output image + // ExifPairs exif; ///< List of modifications appplied on the exif tags of the input image + // IPTCPairs iptc; ///< The IPTC tags and values to be saved to the output image /** * The constructor only sets the hand-wired defaults. diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index c8ee5e147..ef5592074 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -1737,9 +1737,9 @@ private: readyImg->setMetadata(std::move(info)); break; case MetaDataParams::EDIT: - info.setExif(params.exif); - info.setIptc(params.iptc); - if (!(params.metadata.exifKeys.size() == 1 && params.metadata.exifKeys[0] == "ALL")) { + info.setExif(params.metadata.exif); + info.setIptc(params.metadata.iptc); + if (!(params.metadata.exifKeys.size() == 1 && params.metadata.exifKeys[0] == "*")) { info.setExifKeys(&(params.metadata.exifKeys)); } readyImg->setMetadata(std::move(info)); diff --git a/rtgui/exifpanel.cc b/rtgui/exifpanel.cc index ba612f615..a130a09b8 100644 --- a/rtgui/exifpanel.cc +++ b/rtgui/exifpanel.cc @@ -34,15 +34,12 @@ using namespace rtengine::procparams; ExifPanel::ExifPanel() : idata(nullptr), changeList(new rtengine::procparams::ExifPairs), - defChangeList(new rtengine::procparams::ExifPairs), - editableTags{ - {"Exif.Photo.UserComment", "User Comment"}, - {"Exif.Image.Artist", "Artist"}, - {"Exif.Image.Copyright", "Copyright"}, - {"Exif.Image.ImageDescription", "Image Description"}, - { "Exif.Photo.LensModel", "Lens Model" } - } + defChangeList(new rtengine::procparams::ExifPairs) { + for (auto &k : MetaDataParams::basicExifKeys) { + editableTags.push_back(std::make_pair(k, "")); + } + set_orientation(Gtk::ORIENTATION_VERTICAL); exifTree = Gtk::manage (new Gtk::TreeView()); scrolledWindow = Gtk::manage (new Gtk::ScrolledWindow()); @@ -60,11 +57,29 @@ ExifPanel::ExifPanel() : exifTreeModel = Gtk::TreeStore::create(exifColumns); exifTree->set_model(exifTreeModel); exifTree->set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_NONE); - //exifTree->set_show_expanders(false); + exifTree->set_show_expanders(false); exifTree->set_tooltip_column(0); + exifTree->set_enable_search(false); - keepicon = RTImage::createPixbufFromFile ("tick-small.png"); - editicon = RTImage::createPixbufFromFile("add-small.png"); + //keepicon = RTImage::createPixbufFromFile("tick-small.png"); + editicon = RTImage::createPixbufFromFile("edit-small.png"); + open_icon_ = RTImage::createPixbufFromFile("expander-open-small.png"); + closed_icon_ = RTImage::createPixbufFromFile("expander-closed-small.png"); + + exif_active_renderer_.property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; + exif_active_renderer_.signal_toggled().connect(sigc::mem_fun(this, &ExifPanel::onKeyActiveToggled)); + exif_active_column_.pack_start(exif_active_renderer_); + exif_active_column_.set_cell_data_func(exif_active_renderer_, sigc::mem_fun(this, &ExifPanel::setKeyActive)); + + exifTree->append_column(exif_active_column_); + + // { + // Gtk::TreeView::Column *c = Gtk::manage(new Gtk::TreeView::Column("")); + // Gtk::CellRendererPixbuf *pb = Gtk::manage(new Gtk::CellRendererPixbuf()); + // c->pack_start(*pb, false); + // c->add_attribute(*pb, "pixbuf", exifColumns.expander_icon); + // exifTree->append_column(*c); + // } Gtk::TreeView::Column *viewcol = Gtk::manage (new Gtk::TreeView::Column ("Field Name")); Gtk::CellRendererPixbuf* render_pb = Gtk::manage (new Gtk::CellRendererPixbuf ()); @@ -85,15 +100,8 @@ ExifPanel::ExifPanel() : render_pb->property_yalign() = 0; render_txt->property_yalign() = 0; - exif_active_renderer_.property_mode() = Gtk::CELL_RENDERER_MODE_ACTIVATABLE; - exif_active_renderer_.signal_toggled().connect(sigc::mem_fun(this, &ExifPanel::onKeyActiveToggled)); - exif_active_column_.pack_start(exif_active_renderer_); - exif_active_column_.set_cell_data_func(exif_active_renderer_, sigc::mem_fun(this, &ExifPanel::setKeyActive)); - - exifTree->append_column(exif_active_column_); - exifTree->append_column(*viewcol); - exifTree->set_expander_column(*viewcol); + //exifTree->set_expander_column(*viewcol); Gtk::TreeView::Column *viewcolv = Gtk::manage(new Gtk::TreeView::Column ("Value")); Gtk::CellRendererText *render_txtv = Gtk::manage(new Gtk::CellRendererText()); @@ -131,14 +139,14 @@ ExifPanel::ExifPanel() : activate_all_ = addbtn("EXIFPANEL_ACTIVATE_ALL_HINT", "tick.png"); activate_none_ = addbtn("EXIFPANEL_ACTIVATE_NONE_HINT", "box.png"); - add = addbtn("EXIFPANEL_ADDEDIT", "add.png"); + add = addbtn("EXIFPANEL_ADDEDIT", "edit.png"); reset = addbtn("EXIFPANEL_RESETHINT", "undo.png", "redo.png"); resetAll = addbtn("EXIFPANEL_RESETALLHINT", "undo-all.png", "redo-all.png"); pack_end (*buttons1, Gtk::PACK_SHRINK); exifTree->get_selection()->signal_changed().connect (sigc::mem_fun (*this, &ExifPanel::exifSelectionChanged)); - // exifTree->signal_row_activated().connect (sigc::mem_fun (*this, &ExifPanel::row_activated)); + exifTree->signal_row_activated().connect(sigc::mem_fun(*this, &ExifPanel::onExifRowActivated)); reset->signal_clicked().connect ( sigc::mem_fun (*this, &ExifPanel::resetPressed) ); resetAll->signal_clicked().connect ( sigc::mem_fun (*this, &ExifPanel::resetAllPressed) ); @@ -146,6 +154,10 @@ ExifPanel::ExifPanel() : activate_all_->signal_clicked().connect(sigc::mem_fun(*this, &ExifPanel::activateAllPressed)); activate_none_->signal_clicked().connect(sigc::mem_fun(*this, &ExifPanel::activateNonePressed)); + exifTree->signal_button_press_event().connect_notify(sigc::mem_fun(*this, &ExifPanel::onExifTreeClick)); + exifTree->signal_row_expanded().connect(sigc::mem_fun(*this, &ExifPanel::onExifRowExpanded)); + exifTree->signal_row_collapsed().connect(sigc::mem_fun(*this, &ExifPanel::onExifRowCollapsed)); + show_all (); } @@ -157,7 +169,7 @@ void ExifPanel::read (const ProcParams* pp, const ParamsEdited* pedited) { disableListener(); - *changeList = pp->exif; + *changeList = pp->metadata.exif; initial_active_keys_.clear(); initial_active_keys_.insert(pp->metadata.exifKeys.begin(), pp->metadata.exifKeys.end()); cur_active_keys_ = initial_active_keys_; @@ -170,54 +182,15 @@ void ExifPanel::read (const ProcParams* pp, const ParamsEdited* pedited) void ExifPanel::write (ProcParams* pp, ParamsEdited* pedited) { - pp->exif = *changeList; - - std::unordered_set prev; - bool all_active = (cur_active_keys_.size() == 1 && *(cur_active_keys_.begin()) == "ALL"); - - if (!all_active) { - prev = cur_active_keys_; - } - - pp->metadata.exifKeys.clear(); - - bool none_active = true; - - auto root = exifTreeModel->children(); - // for (auto &entry : root->children()) { - // Glib::ustring key = entry[exifColumns.key]; - // prev.erase(key); - // if (entry[exifColumns.active]) { - // pp->metadata.exifKeys.push_back(key); - // none_active = false; - // } else { - // all_active = false; - // } - // } - for (auto &group : root->children()) { - for (auto &entry : group.children()) { - std::string key = entry[exifColumns.key]; - prev.erase(key); - if (entry[exifColumns.active]) { - pp->metadata.exifKeys.push_back(key); - none_active = false; - } else { - all_active = false; - } - } - } - - if (all_active) { - pp->metadata.exifKeys = { "ALL" }; - } else if (!none_active) { - pp->metadata.exifKeys.insert(pp->metadata.exifKeys.end(), prev.begin(), prev.end()); - } + pp->metadata.exif = *changeList; + cur_active_keys_ = get_active_keys(); + pp->metadata.exifKeys.assign(cur_active_keys_.begin(), cur_active_keys_.end()); std::sort(pp->metadata.exifKeys.begin(), pp->metadata.exifKeys.end()); } void ExifPanel::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) { - *defChangeList = defParams->exif; + *defChangeList = defParams->metadata.exif; } void ExifPanel::setImageData (const FramesMetaData* id) @@ -240,11 +213,13 @@ void ExifPanel::addTag(const std::string &key, const std::pairchildren(); - for (auto it = root.rbegin(), end = root.rend(); it != end; ++it) { - auto row = *it; + // for (auto it = root.rbegin(), end = root.rend(); it != end; ++it) { + // auto row = *it; + for (auto &row : root) { + // auto row = *it; std::string key = row[exifColumns.key]; if (row[exifColumns.is_group] && key == label.first) { - return it->children(); + return row./*it->*/children(); } } auto it = exifTreeModel->append(root); @@ -253,7 +228,7 @@ void ExifPanel::addTag(const std::string &key, const std::pair"; + row[exifColumns.label] = "" + label.first + ""; row[exifColumns.value_nopango] = ""; row[exifColumns.value] = ""; row[exifColumns.is_group] = true; @@ -275,13 +250,13 @@ void ExifPanel::addTag(const std::string &key, const std::pair Glib::ustring @@ -338,6 +314,9 @@ void ExifPanel::refreshTags() return "(Not shown)"; }; + if (const rtengine::FramesData *fd = dynamic_cast(idata)) { + fd->fillBasicTags(exif); + } for (const auto& p : *changeList) { try { @@ -346,12 +325,18 @@ void ExifPanel::refreshTags() } } - for (const auto& p : editableTags) { - const auto pos = exif.findKey(Exiv2::ExifKey(p.first)); + for (auto& p : editableTags) { + Exiv2::ExifKey k(p.first); + const auto pos = exif.findKey(k); + bool edited = false; + Glib::ustring value = ""; + auto lbl = std::make_pair(M("EXIFPANEL_BASIC_GROUP"), k.tagLabel()); + p.second = k.tagLabel(); if (pos != exif.end() && pos->size()) { - const bool edited = changeList->find(pos->key()) != changeList->end(); - addTag(pos->key(), to_label(*pos), pos->print(&exif), true, edited); + edited = changeList->find(pos->key()) != changeList->end(); + value = pos->print(&exif); } + addTag(p.first, lbl, value, true, edited); } std::set keyset; for (const auto& tag : exif) { @@ -406,6 +391,7 @@ void ExifPanel::resetIt(const Gtk::TreeModel::const_iterator& iter) void ExifPanel::resetPressed() { + cur_active_keys_ = get_active_keys(); std::vector sel = exifTree->get_selection()->get_selected_rows(); @@ -490,10 +476,13 @@ void ExifPanel::addPressed () hb2->show (); if (dialog->run () == Gtk::RESPONSE_OK) { + cur_active_keys_ = get_active_keys(); auto key = editableTags[tcombo->get_active_row_number()].first; const auto value = ventry->get_text(); (*changeList)[key] = value; - cur_active_keys_.insert(key); + if (!all_keys_active()) { + cur_active_keys_.insert(key); + } refreshTags(); notifyListener(); } @@ -556,6 +545,11 @@ void ExifPanel::onKeyActiveToggled(const Glib::ustring &path) for (auto &c : row.children()) { c[exifColumns.active] = b; } + } else if (!b) { + it = row.parent(); + if (it) { + (*it)[exifColumns.active] = b; + } } notifyListener(); } @@ -568,3 +562,78 @@ void ExifPanel::setKeyActive(Gtk::CellRenderer *renderer, const Gtk::TreeModel:: static_cast(renderer)->set_active(row[exifColumns.active]); } + +bool ExifPanel::all_keys_active() const +{ + return (cur_active_keys_.size() == 1 && *(cur_active_keys_.begin()) == "*"); +} + + +std::unordered_set ExifPanel::get_active_keys() const +{ + bool all_active = true; + std::unordered_set ret; + auto root = exifTreeModel->children(); + for (auto &group : root->children()) { + for (auto &entry : group.children()) { + std::string key = entry[exifColumns.key]; + if (entry[exifColumns.active]) { + ret.insert(key); + } else { + all_active = false; + } + } + } + if (all_active) { + ret.clear(); + ret.insert("*"); + } + return ret; +} + +void ExifPanel::onExifTreeClick(GdkEventButton *event) +{ + Gtk::TreeModel::Path pth; + Gtk::TreeViewColumn *col; + int cell_x; + int cell_y; + if (exifTree->get_path_at_pos(event->x, event->y, pth, col, cell_x, cell_y) && col == exifTree->get_column(1) && cell_x <= 22) { + auto it = exifTreeModel->get_iter(pth); + auto row = *it; + if (row[exifColumns.is_group]) { + if (exifTree->row_expanded(pth)) { + exifTree->collapse_row(pth); + } else { + exifTree->expand_row(pth, false); + } + } + } +} + + +void ExifPanel::onExifRowExpanded(const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path) +{ + auto row = *it; + if (row[exifColumns.is_group]) { + row[exifColumns.icon] = open_icon_; + } +} + + +void ExifPanel::onExifRowCollapsed(const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path) +{ + auto row = *it; + if (row[exifColumns.is_group]) { + row[exifColumns.icon] = closed_icon_; + } +} + + +void ExifPanel::onExifRowActivated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column) +{ + auto it = exifTreeModel->get_iter(path); + auto row = *it; + if (row[exifColumns.editable]) { + addPressed(); + } +} diff --git a/rtgui/exifpanel.h b/rtgui/exifpanel.h index 32ce10dc7..da4cc1e37 100644 --- a/rtgui/exifpanel.h +++ b/rtgui/exifpanel.h @@ -50,7 +50,8 @@ private: class ExifColumns : public Gtk::TreeModelColumnRecord { public: - Gtk::TreeModelColumn > icon; + // Gtk::TreeModelColumn> expander_icon; + Gtk::TreeModelColumn> icon; Gtk::TreeModelColumn key; Gtk::TreeModelColumn label; Gtk::TreeModelColumn value; @@ -71,10 +72,13 @@ private: add(editable); add(active); add(is_group); + // add(expander_icon); } }; - Glib::RefPtr keepicon; + //Glib::RefPtr keepicon; Glib::RefPtr editicon; + Glib::RefPtr open_icon_; + Glib::RefPtr closed_icon_; ExifColumns exifColumns; Gtk::TreeView* exifTree; @@ -90,7 +94,7 @@ private: Gtk::CellRendererToggle exif_active_renderer_; Gtk::TreeView::Column exif_active_column_; - const std::vector> editableTags; + std::vector> editableTags; std::unordered_set initial_active_keys_; std::unordered_set cur_active_keys_; @@ -106,7 +110,14 @@ private: void setKeyActive(Gtk::CellRenderer *renderer, const Gtk::TreeModel::iterator &it); void onKeyActiveToggled(const Glib::ustring &path); - + + bool all_keys_active() const; + std::unordered_set get_active_keys() const; + + void onExifTreeClick(GdkEventButton *event); + void onExifRowExpanded(const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path); + void onExifRowCollapsed(const Gtk::TreeModel::iterator &it, const Gtk::TreeModel::Path &path); + void onExifRowActivated(const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *column); public: ExifPanel (); diff --git a/rtgui/iptcpanel.cc b/rtgui/iptcpanel.cc index f476e7513..bee7ec74a 100644 --- a/rtgui/iptcpanel.cc +++ b/rtgui/iptcpanel.cc @@ -467,8 +467,8 @@ void IPTCPanel::read (const ProcParams* pp, const ParamsEdited* pedited) disableListener (); changeList->clear(); - if (!pp->iptc.empty()) { - *changeList = pp->iptc; + if (!pp->metadata.iptc.empty()) { + *changeList = pp->metadata.iptc; } else { *changeList = *embeddedData; } @@ -480,13 +480,13 @@ void IPTCPanel::read (const ProcParams* pp, const ParamsEdited* pedited) void IPTCPanel::write (ProcParams* pp, ParamsEdited* pedited) { - pp->iptc = *changeList; + pp->metadata.iptc = *changeList; } void IPTCPanel::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) { - *defChangeList = defParams->iptc; + *defChangeList = defParams->metadata.iptc; } void IPTCPanel::setImageData (const FramesMetaData* id) diff --git a/rtgui/paramsedited.cc b/rtgui/paramsedited.cc index 8459d4013..98abe2869 100644 --- a/rtgui/paramsedited.cc +++ b/rtgui/paramsedited.cc @@ -6612,15 +6612,15 @@ void ParamsEdited::combine(rtengine::procparams::ProcParams& toEdit, const rteng // Exif changes are added to the existing ones if (exif) { - for (procparams::ExifPairs::const_iterator i = mods.exif.begin(); i != mods.exif.end(); ++i) { - toEdit.exif[i->first] = i->second; + for (procparams::ExifPairs::const_iterator i = mods.metadata.exif.begin(); i != mods.metadata.exif.end(); ++i) { + toEdit.metadata.exif[i->first] = i->second; } } // IPTC changes are added to the existing ones if (iptc) { - for (procparams::IPTCPairs::const_iterator i = mods.iptc.begin(); i != mods.iptc.end(); ++i) { - toEdit.iptc[i->first] = i->second; + for (procparams::IPTCPairs::const_iterator i = mods.metadata.iptc.begin(); i != mods.metadata.iptc.end(); ++i) { + toEdit.metadata.iptc[i->first] = i->second; } } } diff --git a/rtgui/thumbnail.cc b/rtgui/thumbnail.cc index ce5138d28..dfc1bfeb7 100644 --- a/rtgui/thumbnail.cc +++ b/rtgui/thumbnail.cc @@ -1215,7 +1215,7 @@ void Thumbnail::saveMetadata() return; } - if (pparams->exif.empty() && pparams->iptc.empty()) { + if (pparams->metadata.exif.empty() && pparams->metadata.iptc.empty()) { return; } @@ -1224,8 +1224,8 @@ void Thumbnail::saveMetadata() auto xmp = rtengine::Exiv2Metadata::getXmpSidecar(fname); rtengine::Exiv2Metadata meta; meta.xmpData() = std::move(xmp); - meta.setExif(pparams->exif); - meta.setIptc(pparams->iptc); + meta.setExif(pparams->metadata.exif); + meta.setIptc(pparams->metadata.iptc); meta.saveToXmp(fn); if (options.rtSettings.verbose) { std::cout << "saved edited metadata for " << fname << " to " diff --git a/rtgui/toolpanelcoord.cc b/rtgui/toolpanelcoord.cc index 920585aed..fde949606 100644 --- a/rtgui/toolpanelcoord.cc +++ b/rtgui/toolpanelcoord.cc @@ -141,7 +141,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit addfavoritePanel (detailsPanel, dehaze); addfavoritePanel (advancedPanel, wavelet); addfavoritePanel(locallabPanel, locallab); - + addfavoritePanel (transformPanel, crop); addfavoritePanel (transformPanel, resize); addPanel (resize->getPackBox(), prsharpening, 2); @@ -187,7 +187,7 @@ ToolPanelCoordinator::ToolPanelCoordinator (bool batch) : ipc (nullptr), favorit transformPanelSW = Gtk::manage (new MyScrolledWindow ()); rawPanelSW = Gtk::manage (new MyScrolledWindow ()); advancedPanelSW = Gtk::manage (new MyScrolledWindow ()); - locallabPanelSW = Gtk::manage(new MyScrolledWindow()); + locallabPanelSW = Gtk::manage(new MyScrolledWindow()); // load panel endings for (int i = 0; i < 8; i++) { @@ -585,8 +585,8 @@ void ToolPanelCoordinator::profileChange( // Reset IPTC values when switching procparams from the History if (event == rtengine::EvHistoryBrowsed) { - mergedParams->iptc.clear(); - mergedParams->exif.clear(); + mergedParams->metadata.iptc.clear(); + mergedParams->metadata.exif.clear(); } // And apply the partial profile nparams to mergedParams