1 /* -*- C++ -*-
2  *  This file is part of RawTherapee.
3  *
4  *  Copyright (c) 2017 Alberto Griggio
5  *
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.
10  *
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.
15  *
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/>.
18  */
19 
20 #include "dynamicprofilepanel.h"
21 #include "multilangmgr.h"
22 #include "../rtengine/profilestore.h"
23 #include "../rtengine/rtengine.h"
24 #include "../rtengine/dynamicprofile.h"
25 #include <sstream>
26 #include <iomanip>
27 
28 
29 //-----------------------------------------------------------------------------
30 // DynamicProfilePanel::EditDialog
31 //-----------------------------------------------------------------------------
32 
EditDialog(const Glib::ustring & title,Gtk::Window & parent)33 DynamicProfilePanel::EditDialog::EditDialog (const Glib::ustring &title, Gtk::Window &parent):
34     Gtk::Dialog (title, parent)
35 {
36     profilepath_ = Gtk::manage (new ProfileStoreComboBox());
37     Gtk::HBox *hb = Gtk::manage (new Gtk::HBox());
38     hb->pack_start (*Gtk::manage (new Gtk::Label (M ("DYNPROFILEEDITOR_PROFILE"))), false, false, 4);
39     hb->pack_start (*profilepath_, true, true, 2);
40     get_content_area()->pack_start (*hb, Gtk::PACK_SHRINK, 4);
41 
42     add_optional (M ("EXIFFILTER_CAMERA"), has_camera_, camera_);
43     add_optional (M ("EXIFFILTER_LENS"), has_lens_, lens_);
44 
45     imagetype_ = Gtk::manage (new MyComboBoxText());
46     imagetype_->append(Glib::ustring("(") + M("DYNPROFILEEDITOR_IMGTYPE_ANY") + ")");
47     imagetype_->append(M("DYNPROFILEEDITOR_IMGTYPE_STD"));
48     imagetype_->append(M("DYNPROFILEEDITOR_IMGTYPE_HDR"));
49     imagetype_->append(M("DYNPROFILEEDITOR_IMGTYPE_PS"));
50     imagetype_->set_active(0);
51     hb = Gtk::manage (new Gtk::HBox());
52     hb->pack_start (*Gtk::manage (new Gtk::Label (M ("EXIFFILTER_IMAGETYPE"))), false, false, 4);
53     hb->pack_start (*imagetype_, true, true, 2);
54     get_content_area()->pack_start (*hb, Gtk::PACK_SHRINK, 4);
55 
56     add_range (M ("EXIFFILTER_ISO"), iso_min_, iso_max_);
57     add_range (M ("EXIFFILTER_APERTURE"), fnumber_min_, fnumber_max_);
58     add_range (M ("EXIFFILTER_FOCALLEN"), focallen_min_, focallen_max_);
59     add_range (M ("EXIFFILTER_SHUTTER"), shutterspeed_min_, shutterspeed_max_);
60     add_range (M ("EXIFFILTER_EXPOSURECOMPENSATION"), expcomp_min_, expcomp_max_);
61 
62     add_button (M ("GENERAL_OK"), 1);
63     add_button (M ("GENERAL_CANCEL"), 2);
64 
65     set_ranges();
66 
67     show_all_children();
68 }
69 
70 
set_rule(const DynamicProfileRule & rule)71 void DynamicProfilePanel::EditDialog::set_rule (
72     const DynamicProfileRule &rule)
73 {
74     iso_min_->set_value (rule.iso.min);
75     iso_max_->set_value (rule.iso.max);
76 
77     fnumber_min_->set_value (rule.fnumber.min);
78     fnumber_max_->set_value (rule.fnumber.max);
79 
80     focallen_min_->set_value (rule.focallen.min);
81     focallen_max_->set_value (rule.focallen.max);
82 
83     shutterspeed_min_->set_value (rule.shutterspeed.min);
84     shutterspeed_max_->set_value (rule.shutterspeed.max);
85 
86     expcomp_min_->set_value (rule.expcomp.min);
87     expcomp_max_->set_value (rule.expcomp.max);
88 
89     has_camera_->set_active (rule.camera.enabled);
90     camera_->set_text (rule.camera.value);
91 
92     has_lens_->set_active (rule.lens.enabled);
93     lens_->set_text (rule.lens.value);
94 
95     if (!rule.imagetype.enabled) {
96         imagetype_->set_active(0);
97     } else if (rule.imagetype.value == "STD") {
98         imagetype_->set_active(1);
99     } else if (rule.imagetype.value == "HDR") {
100         imagetype_->set_active(2);
101     } else if (rule.imagetype.value == "PS") {
102         imagetype_->set_active(3);
103     } else {
104         imagetype_->set_active(0);
105     }
106 
107     profilepath_->updateProfileList();
108 
109     if (!profilepath_->setActiveRowFromFullPath (rule.profilepath)) {
110         profilepath_->setInternalEntry();
111     }
112 }
113 
get_rule()114 DynamicProfileRule DynamicProfilePanel::EditDialog::get_rule()
115 {
116     DynamicProfileRule ret;
117     ret.iso.min = iso_min_->get_value_as_int();
118     ret.iso.max = iso_max_->get_value_as_int();
119 
120     ret.fnumber.min = fnumber_min_->get_value();
121     ret.fnumber.max = fnumber_max_->get_value();
122 
123     ret.focallen.min = focallen_min_->get_value();
124     ret.focallen.max = focallen_max_->get_value();
125 
126     ret.shutterspeed.min = shutterspeed_min_->get_value();
127     ret.shutterspeed.max = shutterspeed_max_->get_value();
128 
129     ret.expcomp.min = expcomp_min_->get_value();
130     ret.expcomp.max = expcomp_max_->get_value();
131 
132     ret.camera.enabled = has_camera_->get_active();
133     ret.camera.value = camera_->get_text();
134 
135     ret.lens.enabled = has_lens_->get_active();
136     ret.lens.value = lens_->get_text();
137 
138     ret.imagetype.enabled = imagetype_->get_active_row_number() > 0;
139     switch (imagetype_->get_active_row_number()) {
140     case 1:
141         ret.imagetype.value = "STD";
142         break;
143     case 2:
144         ret.imagetype.value = "HDR";
145         break;
146     case 3:
147         ret.imagetype.value = "PS";
148         break;
149     default:
150         ret.imagetype.value = "";
151     }
152 
153     ret.profilepath = profilepath_->getFullPathFromActiveRow();
154 
155     return ret;
156 }
157 
set_ranges()158 void DynamicProfilePanel::EditDialog::set_ranges()
159 {
160     DynamicProfileRule default_rule;
161     iso_min_->set_digits (0);
162     iso_max_->set_digits (0);
163     iso_min_->set_increments (1, 10);
164     iso_max_->set_increments (1, 10);
165     iso_min_->set_range (default_rule.iso.min, default_rule.iso.max);
166     iso_max_->set_range (default_rule.iso.min, default_rule.iso.max);
167     iso_min_->set_value (default_rule.iso.min);
168     iso_max_->set_value (default_rule.iso.max);
169 
170 #define DOIT_(name)                                    \
171     name ## _min_->set_digits(1);                      \
172     name ## _max_->set_digits(1);                      \
173     name ## _min_->set_increments(0.1, 1);             \
174     name ## _max_->set_increments(0.1, 1);             \
175     name ## _min_->set_range(default_rule. name .min,  \
176                              default_rule. name .max); \
177     name ## _max_->set_range(default_rule. name .min,  \
178                              default_rule. name .max); \
179     name ## _min_->set_value(default_rule. name .min); \
180     name ## _max_->set_value(default_rule. name .max)
181 
182     DOIT_ (fnumber);
183     DOIT_ (focallen);
184     DOIT_ (shutterspeed);
185     DOIT_ (expcomp);
186 #undef DOIT_
187     shutterspeed_min_->set_digits (4);
188     shutterspeed_max_->set_digits (4);
189 
190     profilepath_->setInternalEntry();
191 }
192 
193 
add_range(const Glib::ustring & name,Gtk::SpinButton * & from,Gtk::SpinButton * & to)194 void DynamicProfilePanel::EditDialog::add_range (const Glib::ustring &name,
195         Gtk::SpinButton *&from, Gtk::SpinButton *&to)
196 {
197     Gtk::HBox *hb = Gtk::manage (new Gtk::HBox());
198     hb->pack_start (*Gtk::manage (new Gtk::Label (name)), false, false, 4);
199     from = Gtk::manage (new Gtk::SpinButton());
200     to = Gtk::manage (new Gtk::SpinButton());
201     from->set_numeric (true);
202     to->set_numeric (true);
203     hb->pack_start (*from, true, true, 2);
204     hb->pack_start (*Gtk::manage (new Gtk::Label (" - ")), false, false, 4);
205     hb->pack_start (*to, true, true, 2);
206     get_content_area()->pack_start (*hb, Gtk::PACK_SHRINK, 4);
207 }
208 
add_optional(const Glib::ustring & name,Gtk::CheckButton * & check,Gtk::Entry * & field)209 void DynamicProfilePanel::EditDialog::add_optional (const Glib::ustring &name, Gtk::CheckButton *&check, Gtk::Entry *&field)
210 {
211     check = Gtk::manage (new Gtk::CheckButton (name));
212     Gtk::HBox *hb = Gtk::manage (new Gtk::HBox());
213     hb->pack_start (*check, Gtk::PACK_SHRINK, 4);
214     field = Gtk::manage (new Gtk::Entry());
215     hb->pack_start (*field, true, true, 2);
216     get_content_area()->pack_start (*hb, Gtk::PACK_SHRINK, 4);
217     field->set_tooltip_text (M ("DYNPROFILEEDITOR_ENTRY_TOOLTIP"));
218 }
219 
220 
221 //-----------------------------------------------------------------------------
222 // DynamicProfilePanel
223 //-----------------------------------------------------------------------------
224 
DynamicProfilePanel()225 DynamicProfilePanel::DynamicProfilePanel():
226     vbox_ (Gtk::ORIENTATION_VERTICAL),
227     button_up_ (M ("DYNPROFILEEDITOR_MOVE_UP")),
228     button_down_ (M ("DYNPROFILEEDITOR_MOVE_DOWN")),
229     button_new_ (M ("DYNPROFILEEDITOR_NEW")),
230     button_edit_ (M ("DYNPROFILEEDITOR_EDIT")),
231     button_delete_ (M ("DYNPROFILEEDITOR_DELETE"))
232 {
233     add (vbox_);
234 
235     treeview_.set_grid_lines (Gtk::TREE_VIEW_GRID_LINES_VERTICAL);
236     scrolledwindow_.add (treeview_);
237 
238     scrolledwindow_.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
239 
240     vbox_.pack_start (scrolledwindow_);
241     vbox_.pack_start (buttonbox_, Gtk::PACK_SHRINK);
242 
243     buttonbox_.pack_start (button_new_, Gtk::PACK_SHRINK);
244     buttonbox_.pack_start (button_edit_, Gtk::PACK_SHRINK);
245     buttonbox_.pack_start (button_delete_, Gtk::PACK_SHRINK);
246     buttonbox_.pack_start (button_up_, Gtk::PACK_SHRINK);
247     buttonbox_.pack_start (button_down_, Gtk::PACK_SHRINK);
248     buttonbox_.set_border_width (5);
249     buttonbox_.set_layout (Gtk::BUTTONBOX_END);
250     button_up_.signal_clicked().connect (
251         sigc::mem_fun (*this, &DynamicProfilePanel::on_button_up));
252     button_down_.signal_clicked().connect (
253         sigc::mem_fun (*this, &DynamicProfilePanel::on_button_down));
254     button_new_.signal_clicked().connect (
255         sigc::mem_fun (*this, &DynamicProfilePanel::on_button_new));
256     button_edit_.signal_clicked().connect (
257         sigc::mem_fun (*this, &DynamicProfilePanel::on_button_edit));
258     button_delete_.signal_clicked().connect (
259         sigc::mem_fun (*this, &DynamicProfilePanel::on_button_delete));
260 
261     treemodel_ = Gtk::ListStore::create (columns_);
262     treeview_.set_model (treemodel_);
263 
264     auto cell = Gtk::manage (new Gtk::CellRendererText());
265     int cols_count = treeview_.append_column ( M ("DYNPROFILEEDITOR_PROFILE"), *cell);
266     auto col = treeview_.get_column (cols_count - 1);
267 
268     if (col) {
269         col->set_cell_data_func (
270             *cell, sigc::mem_fun (
271                 *this, &DynamicProfilePanel::render_profilepath));
272     }
273 
274     cell = Gtk::manage (new Gtk::CellRendererText());
275     cols_count = treeview_.append_column (
276                      M ("EXIFFILTER_CAMERA"), *cell);
277     col = treeview_.get_column (cols_count - 1);
278 
279     if (col) {
280         col->set_cell_data_func (
281             *cell, sigc::mem_fun (
282                 *this, &DynamicProfilePanel::render_camera));
283     }
284 
285     cell = Gtk::manage (new Gtk::CellRendererText());
286     cols_count = treeview_.append_column (M ("EXIFFILTER_LENS"), *cell);
287     col = treeview_.get_column (cols_count - 1);
288 
289     if (col) {
290         col->set_cell_data_func (
291             *cell, sigc::mem_fun (
292                 *this, &DynamicProfilePanel::render_lens));
293     }
294 
295     cell = Gtk::manage (new Gtk::CellRendererText());
296     cols_count = treeview_.append_column (M ("EXIFFILTER_IMAGETYPE"), *cell);
297     col = treeview_.get_column (cols_count - 1);
298 
299     if (col) {
300         col->set_cell_data_func (
301             *cell, sigc::mem_fun (
302                 *this, &DynamicProfilePanel::render_imagetype));
303     }
304 
305     cell = Gtk::manage (new Gtk::CellRendererText());
306     cols_count = treeview_.append_column (M ("EXIFFILTER_ISO"), *cell);
307     col = treeview_.get_column (cols_count - 1);
308 
309     if (col) {
310         col->set_cell_data_func (
311             *cell, sigc::mem_fun (
312                 *this, &DynamicProfilePanel::render_iso));
313     }
314 
315     cell = Gtk::manage (new Gtk::CellRendererText());
316     cols_count = treeview_.append_column (M ("EXIFFILTER_APERTURE"), *cell);
317     col = treeview_.get_column (cols_count - 1);
318 
319     if (col) {
320         col->set_cell_data_func (
321             *cell, sigc::mem_fun (
322                 *this, &DynamicProfilePanel::render_fnumber));
323     }
324 
325     cell = Gtk::manage (new Gtk::CellRendererText());
326     cols_count = treeview_.append_column (M ("EXIFFILTER_FOCALLEN"), *cell);
327     col = treeview_.get_column (cols_count - 1);
328 
329     if (col) {
330         col->set_cell_data_func (
331             *cell, sigc::mem_fun (
332                 *this, &DynamicProfilePanel::render_focallen));
333     }
334 
335     cell = Gtk::manage (new Gtk::CellRendererText());
336     cols_count = treeview_.append_column (M ("EXIFFILTER_SHUTTER"), *cell);
337     col = treeview_.get_column (cols_count - 1);
338 
339     if (col) {
340         col->set_cell_data_func (
341             *cell, sigc::mem_fun (
342                 *this, &DynamicProfilePanel::render_shutterspeed));
343     }
344 
345     cell = Gtk::manage (new Gtk::CellRendererText());
346     cols_count = treeview_.append_column (
347                      M ("EXIFFILTER_EXPOSURECOMPENSATION"), *cell);
348     col = treeview_.get_column (cols_count - 1);
349 
350     if (col) {
351         col->set_cell_data_func (
352             *cell, sigc::mem_fun (
353                 *this, &DynamicProfilePanel::render_expcomp));
354     }
355 
356     show_all_children();
357 
358     for (auto &r : ProfileStore::getInstance()->getRules()) {
359         add_rule (r);
360     }
361 }
362 
363 
update_rule(Gtk::TreeModel::Row row,const DynamicProfileRule & rule)364 void DynamicProfilePanel::update_rule (Gtk::TreeModel::Row row,
365                                        const DynamicProfileRule &rule)
366 {
367     row[columns_.iso] = rule.iso;
368     row[columns_.fnumber] = rule.fnumber;
369     row[columns_.focallen] = rule.focallen;
370     row[columns_.shutterspeed] = rule.shutterspeed;
371     row[columns_.expcomp] = rule.expcomp;
372     row[columns_.camera] = rule.camera;
373     row[columns_.lens] = rule.lens;
374     row[columns_.imagetype] = rule.imagetype;
375     row[columns_.profilepath] = rule.profilepath;
376 }
377 
add_rule(const DynamicProfileRule & rule)378 void DynamicProfilePanel::add_rule (const DynamicProfileRule &rule)
379 {
380     auto row = * (treemodel_->append());
381     update_rule (row, rule);
382 }
383 
384 
to_rule(Gtk::TreeModel::Row row,int serial)385 DynamicProfileRule DynamicProfilePanel::to_rule (Gtk::TreeModel::Row row,
386         int serial)
387 {
388     DynamicProfileRule ret;
389     ret.serial_number = serial;
390     ret.iso = row[columns_.iso];
391     ret.fnumber = row[columns_.fnumber];
392     ret.focallen = row[columns_.focallen];
393     ret.shutterspeed = row[columns_.shutterspeed];
394     ret.expcomp = row[columns_.expcomp];
395     ret.camera = row[columns_.camera];
396     ret.lens = row[columns_.lens];
397     ret.profilepath = row[columns_.profilepath];
398     ret.imagetype = row[columns_.imagetype];
399     return ret;
400 }
401 
402 
render_profilepath(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)403 void DynamicProfilePanel::render_profilepath (
404     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
405 {
406     auto row = *iter;
407     Gtk::CellRendererText *ct = static_cast<Gtk::CellRendererText *> (cell);
408     auto value = row[columns_.profilepath];
409     auto pse = ProfileStore::getInstance()->findEntryFromFullPath (value);
410 
411     if (pse != nullptr) {
412         ct->property_text() = pse->label;
413     } else {
414         ct->property_text() = value;
415     }
416 }
417 
418 
419 #define RENDER_RANGE_(tp, name, tostr)                              \
420     auto row = *iter;                                                   \
421     Gtk::CellRendererText *ct = static_cast<Gtk::CellRendererText *>(cell); \
422     DynamicProfileRule::Range<tp> r = row[columns_. name];         \
423     DynamicProfileRule dflt;                                       \
424     if (r.min > dflt.name.min || r.max < dflt.name.max) {               \
425         auto value = tostr(r.min) + " - " + tostr(r.max);               \
426         ct->property_text() = value;                                    \
427     } else {                                                            \
428         ct->property_text() = "";                                       \
429     }
430 
431 
432 namespace
433 {
434 
435 template <class V>
to_str(V n,int precision=1)436 Glib::ustring to_str (V n, int precision = 1)
437 {
438     std::ostringstream buf;
439     buf << std::setprecision (precision) << std::fixed << n;
440     return buf.str();
441 }
442 
443 } // namespace
444 
render_iso(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)445 void DynamicProfilePanel::render_iso (
446     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
447 {
448     RENDER_RANGE_ (int, iso, to_str);
449 }
450 
451 
render_fnumber(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)452 void DynamicProfilePanel::render_fnumber (
453     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
454 {
455     RENDER_RANGE_ (double, fnumber,
456     [] (double f) {
457         return std::string ("f/") +
458                rtengine::FramesMetaData::apertureToString (f);
459     });
460 }
461 
462 
render_focallen(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)463 void DynamicProfilePanel::render_focallen (
464     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
465 {
466     RENDER_RANGE_ (double, focallen, to_str);
467 }
468 
469 
render_shutterspeed(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)470 void DynamicProfilePanel::render_shutterspeed (
471     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
472 {
473     RENDER_RANGE_ (double, shutterspeed,
474                    rtengine::FramesMetaData::shutterToString);
475 }
476 
477 
render_expcomp(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)478 void DynamicProfilePanel::render_expcomp (
479     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
480 {
481     RENDER_RANGE_ (double, expcomp, to_str);
482 }
483 
484 #undef RENDER_RANGE_
485 
486 #define RENDER_OPTIONAL_(name) \
487     auto row = *iter; \
488     Gtk::CellRendererText *ct = static_cast<Gtk::CellRendererText *>(cell); \
489     DynamicProfileRule::Optional o = row[columns_. name]; \
490     if (o.enabled) { \
491         ct->property_text() = o.value; \
492     } else { \
493         ct->property_text() = ""; \
494     }
495 
render_camera(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)496 void DynamicProfilePanel::render_camera (
497     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
498 {
499     RENDER_OPTIONAL_ (camera);
500 }
501 
502 
render_lens(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)503 void DynamicProfilePanel::render_lens (
504     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
505 {
506     RENDER_OPTIONAL_ (lens);
507 }
508 
render_imagetype(Gtk::CellRenderer * cell,const Gtk::TreeModel::iterator & iter)509 void DynamicProfilePanel::render_imagetype (
510     Gtk::CellRenderer *cell, const Gtk::TreeModel::iterator &iter)
511 {
512     auto row = *iter;
513     Gtk::CellRendererText *ct = static_cast<Gtk::CellRendererText *>(cell);
514     DynamicProfileRule::Optional o = row[columns_.imagetype];
515     if (o.enabled) {
516         ct->property_text() = M(std::string("DYNPROFILEEDITOR_IMGTYPE_") + o.value);
517     } else { \
518         ct->property_text() = "";
519     }
520 }
521 
522 #undef RENDER_OPTIONAL_
523 
on_button_up()524 void DynamicProfilePanel::on_button_up()
525 {
526     auto s = treeview_.get_selection();
527 
528     if (!s->count_selected_rows()) {
529         return;
530     }
531 
532     auto it = s->get_selected();
533 
534     if (it != treemodel_->children().begin()) {
535         auto it2 = it;
536         --it2;
537         treemodel_->iter_swap (it, it2);
538     }
539 }
540 
on_button_down()541 void DynamicProfilePanel::on_button_down()
542 {
543     auto s = treeview_.get_selection();
544 
545     if (!s->count_selected_rows()) {
546         return;
547     }
548 
549     auto it = s->get_selected();
550     auto it2 = it;
551     ++it2;
552 
553     if (it2 != treemodel_->children().end()) {
554         treemodel_->iter_swap (it, it2);
555     }
556 }
557 
558 
on_button_delete()559 void DynamicProfilePanel::on_button_delete()
560 {
561     auto s = treeview_.get_selection();
562 
563     if (!s->count_selected_rows()) {
564         return;
565     }
566 
567     auto it = s->get_selected();
568     treemodel_->erase (it);
569 }
570 
571 
on_button_new()572 void DynamicProfilePanel::on_button_new()
573 {
574     EditDialog d (M ("DYNPROFILEEDITOR_NEW_RULE"),
575                   static_cast<Gtk::Window &> (*get_toplevel()));
576     int status = d.run();
577 
578     if (status == 1) {
579         DynamicProfileRule rule = d.get_rule();
580         add_rule (rule);
581     }
582 }
583 
584 
on_button_edit()585 void DynamicProfilePanel::on_button_edit()
586 {
587     auto s = treeview_.get_selection();
588 
589     if (!s->count_selected_rows()) {
590         return;
591     }
592 
593     EditDialog d (M ("DYNPROFILEEDITOR_EDIT_RULE"),
594                   static_cast<Gtk::Window &> (*get_toplevel()));
595     Gtk::TreeModel::Row row = * (s->get_selected());
596     d.set_rule (to_rule (row));
597     int status = d.run();
598 
599     if (status == 1) {
600         update_rule (row, d.get_rule());
601     }
602 }
603 
604 
save()605 void DynamicProfilePanel::save()
606 {
607     std::vector<DynamicProfileRule> rules;
608     int serial = 1;
609 
610     for (auto row : treemodel_->children()) {
611         rules.emplace_back (to_rule (row, serial++));
612     }
613 
614     ProfileStore::getInstance()->setRules (rules);
615 
616     if (!ProfileStore::getInstance()->storeRules()) {
617         printf ("Error in saving dynamic profile rules\n");
618     } else if (options.rtSettings.verbose) {
619         printf ("Saved %d dynamic profile rules\n", int (rules.size()));
620     }
621 }
622