rawTherapee/rtgui/exifpanel.cc
Hombre 8b2eac9a3d Pipette and "On Preview Widgets" branch. See issue 227
The pipette part is already working quite nice but need to be finished. The widgets part needs more work...
2014-01-21 23:37:36 +01:00

559 lines
20 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee 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 3 of the License, or
* (at your option) any later version.
*
* RawTherapee 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 RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include "exifpanel.h"
#include "../rtengine/safegtk.h"
#include "guiutils.h"
#include "rtimage.h"
using namespace rtengine;
using namespace rtengine::procparams;
using namespace rtexif;
ExifPanel::ExifPanel () : idata(NULL) {
recursiveOp = true;
exifTree = Gtk::manage(new Gtk::TreeView());
scrolledWindow = Gtk::manage(new Gtk::ScrolledWindow());
exifTree->set_headers_visible(false);
exifTree->set_rules_hint(false);
exifTree->set_reorderable(false);
exifTree->set_enable_search(true);
exifTree->get_selection()->set_mode (Gtk::SELECTION_MULTIPLE);
scrolledWindow->set_border_width(2);
scrolledWindow->set_shadow_type(Gtk::SHADOW_NONE);
scrolledWindow->set_policy(Gtk::POLICY_ALWAYS, Gtk::POLICY_ALWAYS);
scrolledWindow->property_window_placement().set_value(Gtk::CORNER_TOP_LEFT);
scrolledWindow->add(*exifTree);
exifTreeModel = Gtk::TreeStore::create(exifColumns);
exifTree->set_model (exifTreeModel);
delicon = safe_create_from_file ("gtk-close.png");
keepicon = safe_create_from_file ("gtk-apply.png");
editicon = safe_create_from_file ("gtk-add.png");
Gtk::TreeView::Column *viewcol = Gtk::manage(new Gtk::TreeView::Column ("Field Name"));
Gtk::CellRendererPixbuf* render_pb = Gtk::manage(new Gtk::CellRendererPixbuf ());
Gtk::CellRendererText *render_txt = Gtk::manage(new Gtk::CellRendererText());
viewcol->pack_start (*render_pb, false);
viewcol->pack_start (*render_txt, true);
viewcol->add_attribute (*render_pb, "pixbuf", exifColumns.icon);
viewcol->add_attribute (*render_txt, "markup", exifColumns.field);
render_pb->property_ypad() = 0;
render_txt->property_ypad() = 0;
render_pb->property_yalign() = 0;
render_txt->property_yalign() = 0;
exifTree->append_column (*viewcol);
Gtk::TreeView::Column *viewcolv = Gtk::manage(new Gtk::TreeView::Column ("Value"));
Gtk::CellRendererText *render_txtv = Gtk::manage(new Gtk::CellRendererText());
viewcolv->pack_start (*render_txtv, true);
viewcolv->add_attribute (*render_txtv, "markup", exifColumns.value);
render_txtv->property_ypad() = 0;
exifTree->append_column (*viewcolv);
pack_start (*scrolledWindow);
Gtk::HBox* buttons1 = Gtk::manage(new Gtk::HBox ());
Gtk::HBox* buttons2 = Gtk::manage(new Gtk::HBox ());
remove = Gtk::manage(new Gtk::Button (M("EXIFPANEL_REMOVE")));
remove->set_image (*Gtk::manage(new Gtk::Image (delicon)));
remove->set_tooltip_text (M("EXIFPANEL_REMOVEHINT"));
buttons1->pack_start (*remove);
keep = Gtk::manage(new Gtk::Button (M("EXIFPANEL_KEEP")));
keep->set_image (*Gtk::manage(new Gtk::Image (keepicon)));
keep->set_tooltip_text (M("EXIFPANEL_KEEPHINT"));
buttons1->pack_start (*keep);
add = Gtk::manage(new Gtk::Button (M("EXIFPANEL_ADDEDIT")));
add->set_image (*Gtk::manage(new Gtk::Image (editicon)));
add->set_tooltip_text (M("EXIFPANEL_ADDEDITHINT"));
buttons1->pack_start (*add);
reset = Gtk::manage(new Gtk::Button (M("EXIFPANEL_RESET")));
reset->set_image (*Gtk::manage(new RTImage ("gtk-undo-ltr.png", "gtk-undo-rtl.png")));
reset->set_tooltip_text (M("EXIFPANEL_RESETHINT"));
buttons2->pack_start (*reset);
resetAll = Gtk::manage(new Gtk::Button (M("EXIFPANEL_RESETALL")));
resetAll->set_image (*Gtk::manage(new RTImage ("gtk-undoall-ltr.png", "gtk-undoall-rtl.png")));
resetAll->set_tooltip_text (M("EXIFPANEL_RESETALLHINT"));
buttons2->pack_start (*resetAll);
pack_end (*buttons2, Gtk::PACK_SHRINK);
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));
remove->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::removePressed) );
keep->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::keepPressed) );
reset->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::resetPressed) );
resetAll->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::resetAllPressed) );
add->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::addPressed) );
show_all ();
}
ExifPanel::~ExifPanel () {
}
void ExifPanel::read (const ProcParams* pp, const ParamsEdited* pedited) {
disableListener ();
changeList = pp->exif;
setImageData (idata);
applyChangeList ();
exifSelectionChanged ();
enableListener ();
}
void ExifPanel::write (ProcParams* pp, ParamsEdited* pedited) {
// updateChangeList ();
pp->exif = changeList;
}
void ExifPanel::setDefaults (const ProcParams* defParams, const ParamsEdited* pedited) {
defChangeList = defParams->exif;
}
void ExifPanel::setImageData (const ImageMetaData* id) {
idata = id;
exifTreeModel->clear ();
const std::vector<Tag*>& defTags = ExifManager::getDefaultTIFFTags (NULL);
for (size_t i=0; i<defTags.size(); i++)
if (defTags[i]->nameToString() == "ImageWidth" || defTags[i]->nameToString() == "ImageHeight" || defTags[i]->nameToString() == "BitsPerSample")
addTag (exifTreeModel->children(), defTags[i]->nameToString(), "?", AC_SYSTEM, false);
else
addTag (exifTreeModel->children(), defTags[i]->nameToString(), defTags[i]->valueToString(), AC_SYSTEM, false);
if (id && id->getExifData ()) {
// id->getExifData ()->printAll ();
addDirectory (id->getExifData (), exifTreeModel->children());
}
}
Gtk::TreeModel::Children ExifPanel::addTag (const Gtk::TreeModel::Children& root, Glib::ustring field, Glib::ustring value, rtexif::ActionCode action, bool editable) {
Gtk::TreeModel::Row row = *(exifTreeModel->append(root));
row[exifColumns.action] = action;
row[exifColumns.editable] = editable;
row[exifColumns.edited] = false;
row[exifColumns.field_nopango] = field;
row[exifColumns.value_nopango] = value;
row[exifColumns.orig_value] = value;
if (action==AC_WRITE)
row[exifColumns.icon] = keepicon;
else if (action==AC_DONTWRITE)
row[exifColumns.icon] = delicon;
if (editable) {
row[exifColumns.field] = Glib::ustring("<b>") + escapeHtmlChars(field) + "</b>";
row[exifColumns.value] = Glib::ustring("<b>") + escapeHtmlChars(value) + "</b>";
}
else if (action==AC_SYSTEM) {
row[exifColumns.field] = Glib::ustring("<i>") + escapeHtmlChars(field) + "</i>";
row[exifColumns.value] = Glib::ustring("<i>") + escapeHtmlChars(value) + "</i>";
}
else {
row[exifColumns.field] = escapeHtmlChars(field);
row[exifColumns.value] = escapeHtmlChars(value);
}
return row.children();
}
void ExifPanel::addDirectory (const TagDirectory* dir, Gtk::TreeModel::Children root) {
for (int i=0; i<dir->getCount(); i++) {
Tag* t = (const_cast<TagDirectory*>(dir))->getTagByIndex (i);
if (t->getAttrib() && t->getAttrib()->action==AC_SYSTEM)
continue;
if (t->isDirectory())
for (int j=0; t->getDirectory(j); j++) {
Gtk::TreeModel::Children ch = addTag (root, t->nameToString (j), M("EXIFPANEL_SUBDIRECTORY"), t->getAttrib() ? t->getAttrib()->action : AC_DONTWRITE, t->getAttrib() && t->getAttrib()->editable);
addDirectory (t->getDirectory(j), ch);
}
else
addTag (root, t->nameToString (), t->valueToString (), t->getAttrib() ? (t->getOwnMemory()?t->getAttrib()->action:AC_SYSTEM) : AC_DONTWRITE, t->getAttrib() && t->getAttrib()->editable);
}
}
void ExifPanel::exifSelectionChanged () {
Glib::RefPtr<Gtk::TreeSelection> selection = exifTree->get_selection();
std::vector<Gtk::TreeModel::Path> sel = selection->get_selected_rows();
if (sel.size()>1) {
remove->set_sensitive (1);
keep->set_sensitive (1);
reset->set_sensitive (1);
}
else if (sel.size()==1) {
Gtk::TreeModel::iterator iter = exifTreeModel->get_iter (sel[0]);
if (iter->get_value (exifColumns.action)==AC_SYSTEM) {
remove->set_sensitive (0);
keep->set_sensitive (0);
reset->set_sensitive (0);
}
else if (!iter->children().empty()) {
remove->set_sensitive (1);
keep->set_sensitive (1);
reset->set_sensitive (1);
}
else if (iter->get_value(exifColumns.icon)==delicon) {
remove->set_sensitive (0);
keep->set_sensitive (1);
reset->set_sensitive (1);
}
else if (iter->get_value(exifColumns.icon)==keepicon || iter->get_value(exifColumns.icon)==editicon) {
keep->set_sensitive (0);
remove->set_sensitive (1);
reset->set_sensitive (1);
}
}
else {
remove->set_sensitive (0);
keep->set_sensitive (0);
reset->set_sensitive (0);
}
}
void ExifPanel::delIt (Gtk::TreeModel::iterator iter) {
if (!iter)
return;
if (iter->get_value (exifColumns.action) != AC_SYSTEM)
iter->set_value (exifColumns.icon, delicon);
if (recursiveOp)
for (Gtk::TreeModel::iterator i=iter->children().begin(); i!=iter->children().end(); i++)
delIt (i);
}
void ExifPanel::removePressed () {
std::vector<Gtk::TreeModel::Path> sel = exifTree->get_selection()->get_selected_rows();
for (size_t i=0; i<sel.size(); i++)
delIt (exifTreeModel->get_iter (sel[i]));
exifSelectionChanged ();
updateChangeList ();
notifyListener ();
}
void ExifPanel::keepIt (Gtk::TreeModel::iterator iter) {
if (!iter)
return;
if (iter->get_value (exifColumns.action) != AC_SYSTEM)
iter->set_value (exifColumns.icon, iter->get_value (exifColumns.edited) ? editicon : keepicon);
if (recursiveOp)
for (Gtk::TreeModel::iterator i=iter->children().begin(); i!=iter->children().end(); i++)
keepIt (i);
}
void ExifPanel::keepPressed () {
std::vector<Gtk::TreeModel::Path> sel = exifTree->get_selection()->get_selected_rows();
for (size_t i=0; i<sel.size(); i++)
keepIt (exifTreeModel->get_iter (sel[i]));
exifSelectionChanged ();
updateChangeList ();
notifyListener ();
}
/*void ExifPanel::resetIt (Gtk::TreeModel::iterator iter) {
if (!iter)
return;
if (iter->get_value (exifColumns.action)!=AC_SYSTEM)
iter->set_value (exifColumns.icon, iter->get_value (exifColumns.action) ? keepicon : delicon);
if (iter->get_value (exifColumns.edited)) {
iter->set_value (exifColumns.value, Glib::ustring("<b>") + iter->get_value(exifColumns.orig_value) + "</b>");
iter->set_value (exifColumns.value_nopango, iter->get_value(exifColumns.orig_value));
iter->set_value (exifColumns.edited, false);
}
if (iter->get_value (exifColumns.action)==AC_INVALID)
exifTreeModel->erase (iter);
else
if (recursiveOp)
for (Gtk::TreeModel::iterator i=iter->children().begin(); i!=iter->children().end(); i++)
resetIt (i);
}*/
Gtk::TreeModel::iterator ExifPanel::resetIt (Gtk::TreeModel::iterator iter) {
if (!iter)
return iter;
if (iter->get_value (exifColumns.action)!=AC_SYSTEM)
iter->set_value (exifColumns.icon, iter->get_value (exifColumns.action) ? keepicon : delicon);
if (iter->get_value (exifColumns.edited)) {
iter->set_value (exifColumns.value, Glib::ustring("<b>") + iter->get_value(exifColumns.orig_value) + "</b>");
iter->set_value (exifColumns.value_nopango, iter->get_value(exifColumns.orig_value));
iter->set_value (exifColumns.edited, false);
}
if (iter->get_value (exifColumns.action)==AC_INVALID) {
return exifTreeModel->erase (iter);
}
else
if (recursiveOp) {
Gtk::TreeModel::iterator i = iter->children().begin();
while (i && i != iter->children().end())
i = resetIt (i);
}
return ++iter;
}
void ExifPanel::resetPressed () {
std::vector<Gtk::TreeModel::Path> sel = exifTree->get_selection()->get_selected_rows();
for (size_t i=0; i<sel.size(); i++)
resetIt (exifTreeModel->get_iter (sel[i]));
exifSelectionChanged ();
updateChangeList ();
notifyListener ();
}
void ExifPanel::resetAllPressed () {
setImageData (idata);
changeList = defChangeList;
applyChangeList ();
exifSelectionChanged ();
notifyListener ();
}
void ExifPanel::addPressed () {
Gtk::Dialog* dialog = new Gtk::Dialog (M("EXIFPANEL_ADDTAGDLG_TITLE"), *((Gtk::Window*)get_toplevel()), true, true);
dialog->add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
dialog->add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
Gtk::HBox* hb1 = new Gtk::HBox ();
Gtk::HBox* hb2 = new Gtk::HBox ();
Gtk::Label* tlabel = new Gtk::Label (M("EXIFPANEL_ADDTAGDLG_SELECTTAG")+":");
MyComboBoxText* tcombo = new MyComboBoxText ();
tcombo->append_text ("Artist");
tcombo->append_text ("Copyright");
tcombo->append_text ("ImageDescription");
tcombo->append_text ("Exif.UserComment");
hb1->pack_start (*tlabel, Gtk::PACK_SHRINK, 4);
hb1->pack_start (*tcombo);
Gtk::Label* vlabel = new Gtk::Label (M("EXIFPANEL_ADDTAGDLG_ENTERVALUE")+":");
Gtk::Entry* ventry = new Gtk::Entry ();
hb2->pack_start (*vlabel, Gtk::PACK_SHRINK, 4);
hb2->pack_start (*ventry);
Glib::ustring sel = getSelection (true);
if (sel=="")
tcombo->set_active_text ("Exif.UserComment");
else {
tcombo->set_active_text (sel);
if (tcombo->get_active ()<0) {
tcombo->append_text (sel);
tcombo->set_active_text (sel);
}
ventry->set_text (getSelectedValue ());
}
ventry->set_activates_default (true);
dialog->set_default_response (Gtk::RESPONSE_OK);
dialog->get_vbox()->pack_start (*hb1, Gtk::PACK_SHRINK);
dialog->get_vbox()->pack_start (*hb2, Gtk::PACK_SHRINK, 4);
tlabel->show ();
tcombo->show ();
vlabel->show ();
ventry->show ();
hb1->show ();
hb2->show ();
if (dialog->run ()== Gtk::RESPONSE_OK) {
editTag (exifTreeModel->children(), tcombo->get_active_text(), ventry->get_text());
updateChangeList ();
notifyListener ();
}
delete dialog;
delete tlabel;
delete tcombo;
delete vlabel;
delete ventry;
delete hb1;
delete hb2;
}
void ExifPanel::editTag (Gtk::TreeModel::Children root, Glib::ustring name, Glib::ustring value) {
Glib::ustring::size_type dp = name.find_first_of ('.');
Glib::ustring fseg = name.substr (0,dp);
// look up first segment of the path
Gtk::TreeModel::iterator iter;
for (iter = root.begin(); iter!=root.end(); iter++)
if (iter->get_value (exifColumns.field_nopango) == fseg)
break;
if (iter==root.end() && value!="#keep" && value!="#delete") {
iter = exifTreeModel->append(root);
iter->set_value (exifColumns.field_nopango, fseg);
iter->set_value (exifColumns.action, AC_INVALID);
if (dp==Glib::ustring::npos) {
iter->set_value (exifColumns.value, Glib::ustring("<b>") + value + "</b>");
iter->set_value (exifColumns.value_nopango, value);
iter->set_value (exifColumns.orig_value, value);
iter->set_value (exifColumns.field, Glib::ustring("<b>") + fseg + "</b>");
iter->set_value (exifColumns.edited, true);
iter->set_value (exifColumns.editable, true);
iter->set_value (exifColumns.icon, editicon);
}
else {
iter->set_value (exifColumns.value, Glib::ustring(M("EXIFPANEL_SUBDIRECTORY")));
iter->set_value (exifColumns.value_nopango, Glib::ustring(M("EXIFPANEL_SUBDIRECTORY")));
iter->set_value (exifColumns.field, fseg);
iter->set_value (exifColumns.icon, keepicon);
iter->set_value (exifColumns.orig_value, Glib::ustring(M("EXIFPANEL_SUBDIRECTORY")));
}
}
if (dp==Glib::ustring::npos) {
if (value=="#keep" && iter->get_value (exifColumns.action)!=AC_SYSTEM)
iter->set_value (exifColumns.icon, iter->get_value (exifColumns.edited) ? editicon : keepicon);
else if (value=="#delete" && iter->get_value (exifColumns.action)!=AC_SYSTEM)
iter->set_value (exifColumns.icon, delicon);
else {
iter->set_value (exifColumns.value, Glib::ustring("<b>") + value + "</b>");
iter->set_value (exifColumns.value_nopango, value);
iter->set_value (exifColumns.edited, true);
iter->set_value (exifColumns.icon, editicon);
}
}
else
editTag (iter->children(), name.substr (dp+1, Glib::ustring::npos), value);
}
Glib::ustring ExifPanel::getSelectedValue () {
Glib::RefPtr<Gtk::TreeSelection> selection = exifTree->get_selection();
std::vector<Gtk::TreeModel::Path> rows = selection->get_selected_rows();
if (rows.size()!=1)
return "";
Gtk::TreeModel::iterator iter = exifTreeModel->get_iter (rows[0]);
if (iter)
return iter->get_value (exifColumns.value_nopango);
return "";
}
Glib::ustring ExifPanel::getSelection (bool onlyeditable) {
Glib::RefPtr<Gtk::TreeSelection> selection = exifTree->get_selection();
std::vector<Gtk::TreeModel::Path> rows = selection->get_selected_rows();
if (rows.size()!=1)
return "";
Gtk::TreeModel::iterator iter = exifTreeModel->get_iter (rows[0]);
Glib::ustring ret = "";
bool first = true;
bool editable = false;
while (iter) {
if (first)
ret = iter->get_value (exifColumns.field_nopango);
else
ret = iter->get_value (exifColumns.field_nopango) + "." + ret;
editable = iter->get_value (exifColumns.editable);
iter = iter->parent ();
first = false;
}
if (!editable && onlyeditable)
return "";
return ret;
}
void ExifPanel::updateChangeList (Gtk::TreeModel::Children root, std::string prefix) {
if (prefix!="")
prefix = prefix + ".";
Gtk::TreeModel::iterator iter;
for (iter = root.begin(); iter!=root.end(); iter++) {
if (iter->get_value (exifColumns.edited) == true)
changeList[ prefix+iter->get_value (exifColumns.field_nopango) ] = iter->get_value (exifColumns.value_nopango);
else if (iter->get_value (exifColumns.action) == AC_WRITE && iter->get_value (exifColumns.icon) == delicon)
changeList[ prefix+iter->get_value (exifColumns.field_nopango) ] = "#delete";
else if (iter->get_value (exifColumns.action) == AC_DONTWRITE && iter->get_value (exifColumns.icon) == keepicon)
changeList[ prefix+iter->get_value (exifColumns.field_nopango) ] = "#keep";
if (iter->get_value (exifColumns.icon) == keepicon)
updateChangeList (iter->children(), prefix + iter->get_value (exifColumns.field_nopango));
}
}
void ExifPanel::updateChangeList () {
changeList.clear ();
updateChangeList (exifTreeModel->children(), "");
}
void ExifPanel::applyChangeList () {
for (rtengine::procparams::ExifPairs::iterator i=changeList.begin(); i!=changeList.end(); i++)
editTag (exifTreeModel->children(), i->first, i->second);
}
void ExifPanel::row_activated (const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* column) {
Gtk::TreeModel::iterator iter = exifTreeModel->get_iter (path);
if (iter) {
if (!iter->children().empty())
if (exifTree->row_expanded (path))
exifTree->collapse_row (path);
else
exifTree->expand_row (path, false);
else if (iter->get_value (exifColumns.editable))
addPressed ();
}
}
void ExifPanel::notifyListener () {
if (listener)
listener->panelChanged (EvExif, M("HISTORY_CHANGED"));
}