1 #include "parameter_set_editor.hpp"
2 #include "spin_button_dim.hpp"
3 #include "common/common.hpp"
4 
5 namespace horizon {
6 
header_fun(Gtk::ListBoxRow * row,Gtk::ListBoxRow * before)7 static void header_fun(Gtk::ListBoxRow *row, Gtk::ListBoxRow *before)
8 {
9     if (before && !row->get_header()) {
10         auto ret = Gtk::manage(new Gtk::Separator);
11         row->set_header(*ret);
12     }
13 }
14 
15 class ParameterEditor : public Gtk::Box {
16 public:
ParameterEditor(ParameterID id,ParameterSetEditor * pse)17     ParameterEditor(ParameterID id, ParameterSetEditor *pse)
18         : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 8), parameter_id(id), parent(pse)
19     {
20         auto la = Gtk::manage(new Gtk::Label(parameter_id_to_name(id)));
21         la->get_style_context()->add_class("dim-label");
22         la->set_xalign(1);
23         parent->sg_label->add_widget(*la);
24         pack_start(*la, false, false, 0);
25 
26         sp = Gtk::manage(new SpinButtonDim());
27         sp->set_range(-1e9, 1e9);
28         sp->set_value(parent->parameter_set->at(id));
29         sp->signal_value_changed().connect([this] {
30             (*parent->parameter_set)[parameter_id] = sp->get_value_as_int();
31             parent->s_signal_changed.emit();
32         });
33         sp->signal_key_press_event().connect([this](GdkEventKey *ev) {
34             if (ev->keyval == GDK_KEY_Return && (ev->state & GDK_SHIFT_MASK)) {
35                 sp->activate();
36                 parent->s_signal_apply_all.emit(parameter_id);
37                 parent->s_signal_changed.emit();
38                 return true;
39             }
40             return false;
41         });
42         sp->signal_activate().connect([this] {
43             auto widgets = parent->listbox->get_children();
44             auto my_row = sp->get_ancestor(GTK_TYPE_LIST_BOX_ROW);
45             auto next = std::find(widgets.begin(), widgets.end(), my_row) + 1;
46             if (next < widgets.end()) {
47                 auto e = dynamic_cast<ParameterEditor *>(dynamic_cast<Gtk::ListBoxRow *>(*next)->get_child());
48                 e->sp->grab_focus();
49             }
50             else {
51                 parent->s_signal_activate_last.emit();
52             }
53         });
54         pack_start(*sp, true, true, 0);
55 
56         auto delete_button = Gtk::manage(new Gtk::Button());
57         delete_button->set_image_from_icon_name("list-remove-symbolic", Gtk::ICON_SIZE_BUTTON);
58         pack_start(*delete_button, false, false, 0);
59         delete_button->signal_clicked().connect([this] {
60             parent->s_signal_remove_extra_widget.emit(parameter_id);
61             parent->s_signal_apply_all_toggled.emit(parameter_id, false);
62             parent->parameter_set->erase(parameter_id);
63             parent->s_signal_changed.emit();
64             delete this->get_parent();
65         });
66 
67         for (auto extra_widget : parent->s_signal_create_extra_widget.emit(id)) {
68             if (auto ch = dynamic_cast<Changeable *>(extra_widget)) {
69                 ch->signal_changed().connect([this] { parent->s_signal_changed.emit(); });
70             }
71             pack_start(*extra_widget, false, false, 0);
72         }
73         if (auto bu = parent->create_apply_all_button(id)) {
74             apply_all_toggle_button = dynamic_cast<Gtk::ToggleButton *>(bu);
75             pack_start(*bu, false, false, 0);
76         }
77 
78         set_margin_start(4);
79         set_margin_end(4);
80         set_margin_top(4);
81         set_margin_bottom(4);
82         show_all();
83     }
84     SpinButtonDim *sp = nullptr;
85     Gtk::ToggleButton *apply_all_toggle_button = nullptr;
86 
87 
88     const ParameterID parameter_id;
89 
90 private:
91     ParameterSetEditor *parent;
92 };
93 
ParameterSetEditor(ParameterSet * ps,bool populate_init)94 ParameterSetEditor::ParameterSetEditor(ParameterSet *ps, bool populate_init)
95     : Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0), parameter_set(ps)
96 {
97     add_button = Gtk::manage(new Gtk::MenuButton());
98     add_button->set_label("Add parameter");
99     add_button->set_halign(Gtk::ALIGN_START);
100     add_button->set_margin_bottom(10);
101     add_button->set_margin_top(10);
102     add_button->set_margin_start(10);
103     add_button->set_margin_end(10);
104     add_button->show();
105     pack_start(*add_button, false, false, 0);
106 
107 
108     add_button->set_menu(menu);
109     menu.signal_show().connect(sigc::mem_fun(*this, &ParameterSetEditor::update_menu));
110     for (int i = 1; i < (static_cast<int>(ParameterID::N_PARAMETERS)); i++) {
111         auto id = static_cast<ParameterID>(i);
112         auto item = Gtk::manage(new Gtk::MenuItem(parameter_id_to_name(id)));
113         item->signal_activate().connect([this, id] {
114             (*parameter_set)[id] = 0.5_mm;
115             auto pe = Gtk::manage(new ParameterEditor(id, this));
116             listbox->add(*pe);
117             s_signal_changed.emit();
118         });
119         menu.append(*item);
120         menu_items.emplace(id, *item);
121     }
122 
123     auto sc = Gtk::manage(new Gtk::ScrolledWindow());
124     sc->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
125     listbox = Gtk::manage(new Gtk::ListBox());
126     listbox->set_selection_mode(Gtk::SELECTION_NONE);
127     listbox->set_header_func(&header_fun);
128     sc->add(*listbox);
129 
130     sg_label = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
131     if (populate_init)
132         populate();
133 
134     sc->show_all();
135     {
136         auto sep = Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL));
137         sep->show();
138         pack_start(*sep, false, false, 0);
139     }
140     pack_start(*sc, true, true, 0);
141 }
142 
set_has_apply_all(const std::string & tooltip_text)143 void ParameterSetEditor::set_has_apply_all(const std::string &tooltip_text)
144 {
145     apply_all_tooltip_text.emplace(tooltip_text);
146 }
147 
set_has_apply_all_toggle(const std::string & tooltip_text)148 void ParameterSetEditor::set_has_apply_all_toggle(const std::string &tooltip_text)
149 {
150     apply_all_tooltip_text.emplace(tooltip_text);
151     apply_all_toggle = true;
152 }
153 
154 
set_button_margin_left(int margin)155 void ParameterSetEditor::set_button_margin_left(int margin)
156 {
157     add_button->set_margin_start(margin);
158 }
159 
populate()160 void ParameterSetEditor::populate()
161 {
162     for (auto &it : *parameter_set) {
163         auto pe = Gtk::manage(new ParameterEditor(it.first, this));
164         listbox->add(*pe);
165     }
166     listbox->show_all();
167 }
168 
focus_first()169 void ParameterSetEditor::focus_first()
170 {
171     auto widgets = listbox->get_children();
172     if (widgets.size()) {
173         auto w = dynamic_cast<ParameterEditor *>(dynamic_cast<Gtk::ListBoxRow *>(widgets.front())->get_child());
174         w->sp->grab_focus();
175     }
176 }
177 
add_or_set_parameter(ParameterID param,int64_t value)178 void ParameterSetEditor::add_or_set_parameter(ParameterID param, int64_t value)
179 {
180     if (parameter_set->count(param)) {
181         auto rows = listbox->get_children();
182         for (auto row : rows) {
183             auto w = dynamic_cast<ParameterEditor *>(dynamic_cast<Gtk::ListBoxRow *>(row)->get_child());
184             if (w->parameter_id == param)
185                 w->sp->set_value(value);
186         }
187     }
188     else {
189         (*parameter_set)[param] = value;
190         auto pe = Gtk::manage(new ParameterEditor(param, this));
191         listbox->add(*pe);
192     }
193     s_signal_changed.emit();
194 }
195 
update_menu()196 void ParameterSetEditor::update_menu()
197 {
198     for (int i = 1; i < (static_cast<int>(ParameterID::N_PARAMETERS)); i++) {
199         auto id = static_cast<ParameterID>(i);
200         menu_items.at(id).set_visible(parameter_set->count(id) == 0);
201     }
202 }
203 
create_apply_all_button(ParameterID id)204 Gtk::Widget *ParameterSetEditor::create_apply_all_button(ParameterID id)
205 {
206     if (!apply_all_tooltip_text)
207         return nullptr;
208     if (apply_all_toggle) {
209         auto w = Gtk::manage(new Gtk::ToggleButton("All"));
210         w->set_tooltip_text(apply_all_tooltip_text.value());
211         w->signal_clicked().connect([this, id, w] { s_signal_apply_all_toggled.emit(id, w->get_active()); });
212         return w;
213     }
214     else {
215         auto w = Gtk::manage(new Gtk::Button());
216         w->set_image_from_icon_name("object-select-symbolic", Gtk::ICON_SIZE_BUTTON);
217         w->set_tooltip_text(apply_all_tooltip_text.value());
218         w->signal_clicked().connect([this, id] { s_signal_apply_all.emit(id); });
219         return w;
220     }
221 }
222 
set_apply_all(std::set<ParameterID> params)223 void ParameterSetEditor::set_apply_all(std::set<ParameterID> params)
224 {
225     auto rows = listbox->get_children();
226     for (auto row : rows) {
227         auto w = dynamic_cast<ParameterEditor *>(dynamic_cast<Gtk::ListBoxRow *>(row)->get_child());
228         if (w->apply_all_toggle_button)
229             w->apply_all_toggle_button->set_active(params.count(w->parameter_id));
230     }
231 }
232 
233 } // namespace horizon
234