2 * This file is part of RawTherapee.
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
19 #include <exifpanel.h>
22 using namespace rtengine
;
23 using namespace rtengine::procparams
;
24 using namespace rtexif
;
25 extern Glib::ustring argv0
;
27 ExifPanel::ExifPanel () : idata(NULL
) {
31 exifTree
= Gtk::manage(new Gtk::TreeView());
32 scrolledWindow
= Gtk::manage(new Gtk::ScrolledWindow());
34 exifTree
->set_headers_visible(false);
35 exifTree
->set_rules_hint(false);
36 exifTree
->set_reorderable(false);
37 exifTree
->set_enable_search(true);
38 exifTree
->get_selection()->set_mode (Gtk::SELECTION_MULTIPLE
);
39 scrolledWindow
->set_border_width(2);
40 scrolledWindow
->set_shadow_type(Gtk::SHADOW_NONE
);
41 scrolledWindow
->set_policy(Gtk::POLICY_ALWAYS
, Gtk::POLICY_ALWAYS
);
42 scrolledWindow
->property_window_placement().set_value(Gtk::CORNER_TOP_LEFT
);
43 scrolledWindow
->add(*exifTree
);
45 exifTreeModel
= Gtk::TreeStore::create(exifColumns
);
46 exifTree
->set_model (exifTreeModel
);
48 delicon
= Gdk::Pixbuf::create_from_file (GET_DATA_PATH(argv0
)+"/images/deltags.png");
49 keepicon
= Gdk::Pixbuf::create_from_file (GET_DATA_PATH(argv0
)+"/images/addtags.png");
50 editicon
= Gdk::Pixbuf::create_from_file (GET_DATA_PATH(argv0
)+"/images/logoicon16.png");
52 Gtk::TreeView::Column
*viewcol
= Gtk::manage(new Gtk::TreeView::Column ("Field Name"));
53 Gtk::CellRendererPixbuf
* render_pb
= Gtk::manage(new Gtk::CellRendererPixbuf ());
54 Gtk::CellRendererText
*render_txt
= Gtk::manage(new Gtk::CellRendererText());
55 viewcol
->pack_start (*render_pb
, false);
56 viewcol
->pack_start (*render_txt
, true);
57 viewcol
->add_attribute (*render_pb
, "pixbuf", exifColumns
.icon
);
58 viewcol
->add_attribute (*render_txt
, "markup", exifColumns
.field
);
60 render_pb
->property_ypad() = 0;
61 render_txt
->property_ypad() = 0;
62 render_pb
->property_yalign() = 0;
63 render_txt
->property_yalign() = 0;
65 exifTree
->append_column (*viewcol
);
67 Gtk::TreeView::Column
*viewcolv
= Gtk::manage(new Gtk::TreeView::Column ("Value"));
68 Gtk::CellRendererText
*render_txtv
= Gtk::manage(new Gtk::CellRendererText());
69 viewcolv
->pack_start (*render_txtv
, true);
70 viewcolv
->add_attribute (*render_txtv
, "markup", exifColumns
.value
);
72 render_txtv
->property_ypad() = 0;
74 exifTree
->append_column (*viewcolv
);
76 pack_start (*scrolledWindow
);
78 Gtk::HBox
* buttons1
= Gtk::manage(new Gtk::HBox ());
79 Gtk::HBox
* buttons2
= Gtk::manage(new Gtk::HBox ());
81 remove
= Gtk::manage(new Gtk::Button (M("EXIFPANEL_REMOVE")));
82 remove
->set_image (*Gtk::manage(new Gtk::Image (delicon
)));
83 remove
->set_tooltip_text (M("EXIFPANEL_REMOVEHINT"));
84 buttons1
->pack_start (*remove
);
86 keep
= Gtk::manage(new Gtk::Button (M("EXIFPANEL_KEEP")));
87 keep
->set_image (*Gtk::manage(new Gtk::Image (keepicon
)));
88 keep
->set_tooltip_text (M("EXIFPANEL_KEEPHINT"));
89 buttons1
->pack_start (*keep
);
91 add
= Gtk::manage(new Gtk::Button (M("EXIFPANEL_ADDEDIT")));
92 add
->set_image (*Gtk::manage(new Gtk::Image (editicon
)));
93 add
->set_tooltip_text (M("EXIFPANEL_ADDEDITHINT"));
94 buttons1
->pack_start (*add
);
96 reset
= Gtk::manage(new Gtk::Button (M("EXIFPANEL_RESET")));
97 reset
->set_image (*Gtk::manage(new Gtk::Image (Gtk::StockID ("gtk-undo"), Gtk::IconSize (2))));
98 reset
->set_tooltip_text (M("EXIFPANEL_RESETHINT"));
99 buttons2
->pack_start (*reset
);
101 resetAll
= Gtk::manage(new Gtk::Button (M("EXIFPANEL_RESETALL")));
102 resetAll
->set_image (*Gtk::manage(new Gtk::Image (GET_DATA_PATH(argv0
)+"/images/gtk-undo-ltr.png")));
103 resetAll
->set_tooltip_text (M("EXIFPANEL_RESETALLHINT"));
104 buttons2
->pack_start (*resetAll
);
106 pack_end (*buttons2
, Gtk::PACK_SHRINK
);
107 pack_end (*buttons1
, Gtk::PACK_SHRINK
);
109 exifTree
->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &ExifPanel::exifSelectionChanged
));
110 exifTree
->signal_row_activated().connect(sigc::mem_fun(*this, &ExifPanel::row_activated
));
112 remove
->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::removePressed
) );
113 keep
->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::keepPressed
) );
114 reset
->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::resetPressed
) );
115 resetAll
->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::resetAllPressed
) );
116 add
->signal_clicked().connect( sigc::mem_fun(*this, &ExifPanel::addPressed
) );
121 ExifPanel::~ExifPanel () {
124 void ExifPanel::read (const ProcParams
* pp
, const ParamsEdited
* pedited
) {
128 changeList
= pp
->exif
;
129 setImageData (idata
);
131 exifSelectionChanged ();
136 void ExifPanel::write (ProcParams
* pp
, ParamsEdited
* pedited
) {
138 // updateChangeList ();
139 pp
->exif
= changeList
;
142 void ExifPanel::setDefaults (const ProcParams
* defParams
, const ParamsEdited
* pedited
) {
144 defChangeList
= defParams
->exif
;
147 void ExifPanel::setImageData (const ImageMetaData
* id
) {
150 exifTreeModel
->clear ();
152 const std::vector
<Tag
*>& defTags
= ExifManager::getDefaultTIFFTags (NULL
);
153 for (int i
=0; i
<defTags
.size(); i
++)
154 if (defTags
[i
]->nameToString() == "ImageWidth" || defTags
[i
]->nameToString() == "ImageHeight" || defTags
[i
]->nameToString() == "BitsPerSample")
155 addTag (exifTreeModel
->children(), defTags
[i
]->nameToString(), "?", SYSTEM
, false);
157 addTag (exifTreeModel
->children(), defTags
[i
]->nameToString(), defTags
[i
]->valueToString(), SYSTEM
, false);
159 if (id
&& id
->getExifData ()) {
160 // id->getExifData ()->printAll ();
161 addDirectory (id
->getExifData (), exifTreeModel
->children());
165 Gtk::TreeModel::Children
ExifPanel::addTag (const Gtk::TreeModel::Children
& root
, Glib::ustring field
, Glib::ustring value
, int action
, bool editable
) {
167 Gtk::TreeModel::Row row
= *(exifTreeModel
->append(root
));
168 row
[exifColumns
.action
] = action
;
169 row
[exifColumns
.editable
] = editable
;
170 row
[exifColumns
.edited
] = false;
171 row
[exifColumns
.field_nopango
] = field
;
172 row
[exifColumns
.value_nopango
] = value
;
173 row
[exifColumns
.orig_value
] = value
;
176 row
[exifColumns
.icon
] = keepicon
;
177 else if (action
==DONTWRITE
)
178 row
[exifColumns
.icon
] = delicon
;
181 row
[exifColumns
.field
] = Glib::ustring("<b>") + field
+ "</b>";
182 row
[exifColumns
.value
] = Glib::ustring("<b>") + value
+ "</b>";
184 else if (action
==SYSTEM
) {
185 row
[exifColumns
.field
] = Glib::ustring("<i>") + field
+ "</i>";
186 row
[exifColumns
.value
] = Glib::ustring("<i>") + value
+ "</i>";
189 row
[exifColumns
.field
] = field
;
190 row
[exifColumns
.value
] = value
;
193 return row
.children();
196 void ExifPanel::addDirectory (const TagDirectory
* dir
, Gtk::TreeModel::Children root
) {
198 for (int i
=0; i
<dir
->getCount(); i
++) {
199 Tag
* t
= ((TagDirectory
*)dir
)->getTagByIndex (i
);
200 if (t
->getAttrib() && t
->getAttrib()->action
==SYSTEM
)
202 if (t
->isDirectory())
203 for (int j
=0; t
->getDirectory(j
); j
++) {
204 Gtk::TreeModel::Children ch
= addTag (root
, t
->nameToString (j
), M("EXIFPANEL_SUBDIRECTORY"), t
->getAttrib() ? t
->getAttrib()->action
: 0, t
->getAttrib() && t
->getAttrib()->editable
);
205 addDirectory (t
->getDirectory(j
), ch
);
208 addTag (root
, t
->nameToString (), t
->valueToString (), t
->getAttrib() ? t
->getAttrib()->action
: 0, t
->getAttrib() && t
->getAttrib()->editable
);
212 void ExifPanel::exifSelectionChanged () {
214 Glib::RefPtr
<Gtk::TreeSelection
> selection
= exifTree
->get_selection();
215 std::vector
<Gtk::TreeModel::Path
> sel
= selection
->get_selected_rows();
217 remove
->set_sensitive (1);
218 keep
->set_sensitive (1);
219 reset
->set_sensitive (1);
221 else if (sel
.size()==1) {
222 Gtk::TreeModel::iterator iter
= exifTreeModel
->get_iter (sel
[0]);
223 if (iter
->get_value (exifColumns
.action
)==SYSTEM
) {
224 remove
->set_sensitive (0);
225 keep
->set_sensitive (0);
226 reset
->set_sensitive (0);
228 else if (iter
->children().size()>0) {
229 remove
->set_sensitive (1);
230 keep
->set_sensitive (1);
231 reset
->set_sensitive (1);
233 else if (iter
->get_value(exifColumns
.icon
)==delicon
) {
234 remove
->set_sensitive (0);
235 keep
->set_sensitive (1);
236 reset
->set_sensitive (1);
238 else if (iter
->get_value(exifColumns
.icon
)==keepicon
|| iter
->get_value(exifColumns
.icon
)==editicon
) {
239 keep
->set_sensitive (0);
240 remove
->set_sensitive (1);
241 reset
->set_sensitive (1);
245 remove
->set_sensitive (0);
246 keep
->set_sensitive (0);
247 reset
->set_sensitive (0);
251 void ExifPanel::delIt (Gtk::TreeModel::iterator iter
) {
256 if (iter
->get_value (exifColumns
.action
) != SYSTEM
)
257 iter
->set_value (exifColumns
.icon
, delicon
);
259 for (Gtk::TreeModel::iterator i
=iter
->children().begin(); i
!=iter
->children().end(); i
++)
263 void ExifPanel::removePressed () {
265 std::vector
<Gtk::TreeModel::Path
> sel
= exifTree
->get_selection()->get_selected_rows();
266 for (int i
=0; i
<sel
.size(); i
++)
267 delIt (exifTreeModel
->get_iter (sel
[i
]));
269 exifSelectionChanged ();
274 void ExifPanel::keepIt (Gtk::TreeModel::iterator iter
) {
279 if (iter
->get_value (exifColumns
.action
) != SYSTEM
)
280 iter
->set_value (exifColumns
.icon
, iter
->get_value (exifColumns
.edited
) ? editicon
: keepicon
);
282 for (Gtk::TreeModel::iterator i
=iter
->children().begin(); i
!=iter
->children().end(); i
++)
286 void ExifPanel::keepPressed () {
288 std::vector
<Gtk::TreeModel::Path
> sel
= exifTree
->get_selection()->get_selected_rows();
289 for (int i
=0; i
<sel
.size(); i
++)
290 keepIt (exifTreeModel
->get_iter (sel
[i
]));
292 exifSelectionChanged ();
297 /*void ExifPanel::resetIt (Gtk::TreeModel::iterator iter) {
302 if (iter->get_value (exifColumns.action)!=SYSTEM)
303 iter->set_value (exifColumns.icon, iter->get_value (exifColumns.action) ? keepicon : delicon);
304 if (iter->get_value (exifColumns.edited)) {
305 iter->set_value (exifColumns.value, Glib::ustring("<b>") + iter->get_value(exifColumns.orig_value) + "</b>");
306 iter->set_value (exifColumns.value_nopango, iter->get_value(exifColumns.orig_value));
307 iter->set_value (exifColumns.edited, false);
309 if (iter->get_value (exifColumns.action)==100)
310 exifTreeModel->erase (iter);
313 for (Gtk::TreeModel::iterator i=iter->children().begin(); i!=iter->children().end(); i++)
316 Gtk::TreeModel::iterator
ExifPanel::resetIt (Gtk::TreeModel::iterator iter
) {
321 if (iter
->get_value (exifColumns
.action
)!=SYSTEM
)
322 iter
->set_value (exifColumns
.icon
, iter
->get_value (exifColumns
.action
) ? keepicon
: delicon
);
323 if (iter
->get_value (exifColumns
.edited
)) {
324 iter
->set_value (exifColumns
.value
, Glib::ustring("<b>") + iter
->get_value(exifColumns
.orig_value
) + "</b>");
325 iter
->set_value (exifColumns
.value_nopango
, iter
->get_value(exifColumns
.orig_value
));
326 iter
->set_value (exifColumns
.edited
, false);
328 if (iter
->get_value (exifColumns
.action
)==100) {
329 return exifTreeModel
->erase (iter
);
333 Gtk::TreeModel::iterator i
= iter
->children().begin();
334 while (i
&& i
!= iter
->children().end())
339 void ExifPanel::resetPressed () {
341 std::vector
<Gtk::TreeModel::Path
> sel
= exifTree
->get_selection()->get_selected_rows();
342 for (int i
=0; i
<sel
.size(); i
++)
343 resetIt (exifTreeModel
->get_iter (sel
[i
]));
345 exifSelectionChanged ();
350 void ExifPanel::resetAllPressed () {
352 setImageData (idata
);
353 changeList
= defChangeList
;
355 exifSelectionChanged ();
359 void ExifPanel::addPressed () {
361 Gtk::Dialog
* dialog
= new Gtk::Dialog (M("EXIFPANEL_ADDTAGDLG_TITLE"), *((Gtk::Window
*)get_toplevel()), true, true);
362 dialog
->add_button (Gtk::Stock::OK
, Gtk::RESPONSE_OK
);
363 dialog
->add_button (Gtk::Stock::CANCEL
, Gtk::RESPONSE_CANCEL
);
365 Gtk::HBox
* hb1
= new Gtk::HBox ();
366 Gtk::HBox
* hb2
= new Gtk::HBox ();
368 Gtk::Label
* tlabel
= new Gtk::Label (M("EXIFPANEL_ADDTAGDLG_SELECTTAG")+":");
369 Gtk::ComboBoxText
* tcombo
= new Gtk::ComboBoxText ();
371 tcombo
->append_text ("Artist");
372 tcombo
->append_text ("Copyright");
373 tcombo
->append_text ("ImageDescription");
374 tcombo
->append_text ("Exif.UserComment");
376 hb1
->pack_start (*tlabel
, Gtk::PACK_SHRINK
, 4);
377 hb1
->pack_start (*tcombo
);
379 Gtk::Label
* vlabel
= new Gtk::Label (M("EXIFPANEL_ADDTAGDLG_ENTERVALUE")+":");
380 Gtk::Entry
* ventry
= new Gtk::Entry ();
381 hb2
->pack_start (*vlabel
, Gtk::PACK_SHRINK
, 4);
382 hb2
->pack_start (*ventry
);
384 Glib::ustring sel
= getSelection (true);
386 tcombo
->set_active_text ("Exif.UserComment");
388 tcombo
->set_active_text (sel
);
389 if (tcombo
->get_active ()<0) {
390 tcombo
->append_text (sel
);
391 tcombo
->set_active_text (sel
);
393 ventry
->set_text (getSelectedValue ());
396 ventry
->set_activates_default (true);
397 dialog
->set_default_response (Gtk::RESPONSE_OK
);
398 dialog
->get_vbox()->pack_start (*hb1
, Gtk::PACK_SHRINK
);
399 dialog
->get_vbox()->pack_start (*hb2
, Gtk::PACK_SHRINK
, 4);
407 if (dialog
->run ()== Gtk::RESPONSE_OK
) {
408 editTag (exifTreeModel
->children(), tcombo
->get_active_text(), ventry
->get_text());
422 void ExifPanel::editTag (Gtk::TreeModel::Children root
, Glib::ustring name
, Glib::ustring value
) {
424 Glib::ustring::size_type dp
= name
.find_first_of ('.');
425 Glib::ustring fseg
= name
.substr (0,dp
);
426 // look up first segment of the path
427 Gtk::TreeModel::iterator iter
;
428 for (iter
= root
.begin(); iter
!=root
.end(); iter
++)
429 if (iter
->get_value (exifColumns
.field_nopango
) == fseg
)
432 if (iter
==root
.end() && value
!="#keep" && value
!="#delete") {
433 iter
= exifTreeModel
->append(root
);
434 iter
->set_value (exifColumns
.field_nopango
, fseg
);
435 iter
->set_value (exifColumns
.action
, 100);
436 if (dp
==Glib::ustring::npos
) {
437 iter
->set_value (exifColumns
.value
, Glib::ustring("<b>") + value
+ "</b>");
438 iter
->set_value (exifColumns
.value_nopango
, value
);
439 iter
->set_value (exifColumns
.orig_value
, value
);
440 iter
->set_value (exifColumns
.field
, Glib::ustring("<b>") + fseg
+ "</b>");
441 iter
->set_value (exifColumns
.edited
, true);
442 iter
->set_value (exifColumns
.editable
, true);
443 iter
->set_value (exifColumns
.icon
, editicon
);
446 iter
->set_value (exifColumns
.value
, Glib::ustring(M("EXIFPANEL_SUBDIRECTORY")));
447 iter
->set_value (exifColumns
.value_nopango
, Glib::ustring(M("EXIFPANEL_SUBDIRECTORY")));
448 iter
->set_value (exifColumns
.field
, fseg
);
449 iter
->set_value (exifColumns
.icon
, keepicon
);
450 iter
->set_value (exifColumns
.orig_value
, Glib::ustring(M("EXIFPANEL_SUBDIRECTORY")));
454 if (dp
==Glib::ustring::npos
) {
455 if (value
=="#keep" && iter
->get_value (exifColumns
.action
)!=SYSTEM
)
456 iter
->set_value (exifColumns
.icon
, iter
->get_value (exifColumns
.edited
) ? editicon
: keepicon
);
457 else if (value
=="#delete" && iter
->get_value (exifColumns
.action
)!=SYSTEM
)
458 iter
->set_value (exifColumns
.icon
, delicon
);
460 iter
->set_value (exifColumns
.value
, Glib::ustring("<b>") + value
+ "</b>");
461 iter
->set_value (exifColumns
.value_nopango
, value
);
462 iter
->set_value (exifColumns
.edited
, true);
463 iter
->set_value (exifColumns
.icon
, editicon
);
467 editTag (iter
->children(), name
.substr (dp
+1, Glib::ustring::npos
), value
);
470 Glib::ustring
ExifPanel::getSelectedValue () {
472 Glib::RefPtr
<Gtk::TreeSelection
> selection
= exifTree
->get_selection();
473 std::vector
<Gtk::TreeModel::Path
> rows
= selection
->get_selected_rows();
476 Gtk::TreeModel::iterator iter
= exifTreeModel
->get_iter (rows
[0]);
478 return iter
->get_value (exifColumns
.value_nopango
);
482 Glib::ustring
ExifPanel::getSelection (bool onlyeditable
) {
484 Glib::RefPtr
<Gtk::TreeSelection
> selection
= exifTree
->get_selection();
485 std::vector
<Gtk::TreeModel::Path
> rows
= selection
->get_selected_rows();
489 Gtk::TreeModel::iterator iter
= exifTreeModel
->get_iter (rows
[0]);
491 Glib::ustring ret
= "";
493 bool editable
= false;
496 ret
= iter
->get_value (exifColumns
.field_nopango
);
498 ret
= iter
->get_value (exifColumns
.field_nopango
) + "." + ret
;
499 editable
= iter
->get_value (exifColumns
.editable
);
500 iter
= iter
->parent ();
503 if (!editable
&& onlyeditable
)
508 void ExifPanel::updateChangeList (Gtk::TreeModel::Children root
, std::string prefix
) {
511 prefix
= prefix
+ ".";
513 Gtk::TreeModel::iterator iter
;
514 for (iter
= root
.begin(); iter
!=root
.end(); iter
++) {
515 if (iter
->get_value (exifColumns
.edited
) == true) {
517 ec
.field
= prefix
+ iter
->get_value (exifColumns
.field_nopango
);
518 ec
.value
= iter
->get_value (exifColumns
.value_nopango
);
519 changeList
.push_back (ec
);
521 else if (iter
->get_value (exifColumns
.action
) == WRITE
&& iter
->get_value (exifColumns
.icon
) == delicon
) {
523 ec
.field
= prefix
+ iter
->get_value (exifColumns
.field_nopango
);
524 ec
.value
= "#delete";
525 changeList
.push_back (ec
);
527 else if (iter
->get_value (exifColumns
.action
) == DONTWRITE
&& iter
->get_value (exifColumns
.icon
) == keepicon
) {
529 ec
.field
= prefix
+ iter
->get_value (exifColumns
.field_nopango
);
531 changeList
.push_back (ec
);
533 if (iter
->get_value (exifColumns
.icon
) == keepicon
)
534 updateChangeList (iter
->children(), prefix
+ iter
->get_value (exifColumns
.field_nopango
));
538 void ExifPanel::updateChangeList () {
541 updateChangeList (exifTreeModel
->children(), "");
544 void ExifPanel::applyChangeList () {
546 for (int i
=0; i
<changeList
.size(); i
++)
547 editTag (exifTreeModel
->children(), changeList
[i
].field
, changeList
[i
].value
);
550 void ExifPanel::row_activated (const Gtk::TreeModel::Path
& path
, Gtk::TreeViewColumn
* column
) {
552 Gtk::TreeModel::iterator iter
= exifTreeModel
->get_iter (path
);
554 if (iter
->children().size()>0)
555 if (exifTree
->row_expanded (path
))
556 exifTree
->collapse_row (path
);
558 exifTree
->expand_row (path
, false);
559 else if (iter
->get_value (exifColumns
.editable
))
565 void ExifPanel::notifyListener () {
568 listener
->panelChanged (EvExif
, M("HISTORY_CHANGED"));