1 #include "manage_buses.hpp"
2 #include "widgets/net_button.hpp"
3 #include "util/gtk_util.hpp"
4 #include "util/util.hpp"
5 #include "block/block.hpp"
6 #include <iostream>
7 #include <deque>
8 #include <algorithm>
9
10 namespace horizon {
11
12 class BusMemberEditor : public Gtk::Box {
13 public:
BusMemberEditor(Bus & b,Bus::Member & m,Block & bl)14 BusMemberEditor(Bus &b, Bus::Member &m, Block &bl)
15 : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4), bus(b), bus_member(m), block(bl)
16 {
17 set_margin_start(8);
18 set_margin_end(8);
19 set_margin_top(4);
20 set_margin_bottom(4);
21
22 entry = Gtk::manage(new Gtk::Entry());
23 entry->set_text(bus_member.name);
24 entry->set_width_chars(0);
25 entry->signal_changed().connect(sigc::mem_fun(*this, &BusMemberEditor::member_name_changed));
26 pack_start(*entry, true, true, 0);
27
28 auto auto_name_button = Gtk::manage(new Gtk::Button());
29 auto_name_button->set_image_from_icon_name("go-next-symbolic", Gtk::ICON_SIZE_BUTTON);
30 auto_name_button->signal_clicked().connect([this] {
31 bus_member.net->name = bus.name + "_" + bus_member.name;
32 net_button->update();
33 });
34 pack_start(*auto_name_button, false, false, 0);
35
36 net_button = Gtk::manage(new NetButton(block));
37 net_button->set_net(bus_member.net->uuid);
38 net_button->signal_changed().connect(sigc::mem_fun(*this, &BusMemberEditor::bus_net_changed));
39 net_button->set_sensitive(!bus_member.net->has_bus_rippers);
40 pack_start(*net_button, false, false, 0);
41
42
43 auto delbutton = Gtk::manage(new Gtk::Button());
44 delbutton->set_image_from_icon_name("list-remove-symbolic", Gtk::ICON_SIZE_BUTTON);
45 delbutton->set_sensitive(!bus_member.net->has_bus_rippers);
46 // delbutton->get_style_context()->add_class("destructive-action");
47 delbutton->set_margin_start(16);
48 delbutton->signal_clicked().connect(sigc::mem_fun(*this, &BusMemberEditor::bus_member_remove));
49 pack_start(*delbutton, false, false, 0);
50 }
51 Gtk::Entry *entry;
52 NetButton *net_button;
53
54 private:
member_name_changed()55 void member_name_changed()
56 {
57 std::string new_name = entry->get_text();
58 bus_member.name = new_name;
59 }
60
bus_net_changed(const UUID & uu)61 void bus_net_changed(const UUID &uu)
62 {
63 bus_member.net = &block.nets.at(uu);
64 }
65
bus_member_remove()66 void bus_member_remove()
67 {
68 bus.members.erase(bus_member.uuid);
69 delete this->get_parent();
70 }
71
72 Bus &bus;
73 Bus::Member &bus_member;
74 Block █
75 };
76
77 class AddSequenceDialog : public Gtk::Dialog {
78 public:
79 AddSequenceDialog(Gtk::Window *parent);
80
81 Gtk::SpinButton *w_start_index = nullptr;
82 Gtk::SpinButton *w_end_index = nullptr;
83 Gtk::Entry *w_text = nullptr;
84 };
85
AddSequenceDialog(Gtk::Window * parent)86 AddSequenceDialog::AddSequenceDialog(Gtk::Window *parent)
87 : Gtk::Dialog("Add Sequence", *parent, Gtk::DialogFlags::DIALOG_MODAL | Gtk::DialogFlags::DIALOG_USE_HEADER_BAR)
88 {
89 add_button("Cancel", Gtk::ResponseType::RESPONSE_CANCEL);
90 add_button("Add", Gtk::ResponseType::RESPONSE_OK);
91 set_default_response(Gtk::ResponseType::RESPONSE_OK);
92 // set_default_size(400, 300);
93
94 auto grid = Gtk::manage(new Gtk::Grid());
95 grid->set_column_spacing(10);
96 grid->set_row_spacing(10);
97 grid->set_margin_bottom(20);
98 grid->set_margin_top(20);
99 grid->set_margin_end(20);
100 grid->set_margin_start(20);
101 grid->set_halign(Gtk::ALIGN_CENTER);
102
103 int top = 0;
104
105 w_start_index = Gtk::manage(new Gtk::SpinButton);
106 w_start_index->set_range(0, 128);
107 w_start_index->set_increments(1, 10);
108 grid_attach_label_and_widget(grid, "First index", w_start_index, top);
109
110 w_end_index = Gtk::manage(new Gtk::SpinButton);
111 w_end_index->set_range(0, 128);
112 w_end_index->set_increments(1, 10);
113 w_end_index->set_value(7);
114 grid_attach_label_and_widget(grid, "Last index", w_end_index, top);
115
116 w_text = Gtk::manage(new Gtk::Entry);
117 w_text->set_text("D$_P");
118 w_text->set_placeholder_text("$ will be replaced by the index");
119
120 auto textbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 5));
121 textbox->pack_start(*w_text, true, true, 0);
122 auto text_la = Gtk::manage(new Gtk::Label("Use $ for placeholder"));
123 text_la->set_xalign(0);
124 text_la->get_style_context()->add_class("dim-label");
125 Pango::AttrList alist;
126 auto attr_scale = Pango::Attribute::create_attr_scale(0.833);
127 alist.insert(attr_scale);
128 text_la->set_attributes(alist);
129 textbox->pack_start(*text_la, true, true, 0);
130
131 auto text_la2 = grid_attach_label_and_widget(grid, "Template", textbox, top);
132 text_la2->set_valign(Gtk::ALIGN_START);
133
134 auto sg = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_VERTICAL);
135 sg->add_widget(*text_la2);
136 sg->add_widget(*w_text);
137
138 get_content_area()->pack_start(*grid, true, true, 0);
139
140 show_all();
141 }
142
143 class BusEditor : public Gtk::Box {
144 public:
BusEditor(Bus & bu,Block & bl)145 BusEditor(Bus &bu, Block &bl) : Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0), bus(bu), block(bl)
146 {
147 auto labelbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4));
148 labelbox->set_margin_start(10);
149 labelbox->set_margin_end(8);
150 labelbox->set_margin_top(4);
151 labelbox->set_margin_bottom(8);
152 auto la = Gtk::manage(new Gtk::Label("Bus Name"));
153 la->get_style_context()->add_class("dim-label");
154 bus_name_entry = Gtk::manage(new Gtk::Entry());
155 bus_name_entry->set_text(bus.name);
156 bus_name_entry->signal_changed().connect(sigc::mem_fun(*this, &BusEditor::bus_name_changed));
157 labelbox->pack_start(*la, false, false, 0);
158 labelbox->pack_start(*bus_name_entry, true, true, 0);
159
160 pack_start(*labelbox, false, false, 0);
161
162 auto buttonbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 8));
163 buttonbox->set_margin_start(8);
164 buttonbox->set_margin_end(8);
165 buttonbox->set_margin_bottom(8);
166
167 auto add_member_button = Gtk::manage(new Gtk::Button("Add member"));
168 add_member_button->signal_clicked().connect(sigc::mem_fun(*this, &BusEditor::bus_add_member));
169 buttonbox->pack_start(*add_member_button, false, false, 0);
170
171 auto add_seq_button = Gtk::manage(new Gtk::Button("Add sequence"));
172 add_seq_button->signal_clicked().connect(sigc::mem_fun(*this, &BusEditor::bus_add_sequence));
173 buttonbox->pack_start(*add_seq_button, false, false, 0);
174
175 pack_start(*buttonbox, false, false, 0);
176
177 {
178 auto sep = Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL));
179 pack_start(*sep, false, false, 0);
180 }
181
182 auto sc = Gtk::manage(new Gtk::ScrolledWindow());
183 listbox = Gtk::manage(new Gtk::ListBox());
184 listbox->set_header_func(sigc::mem_fun(*this, &BusEditor::header_fun));
185 listbox->set_selection_mode(Gtk::SELECTION_NONE);
186 sc->add(*listbox);
187 pack_start(*sc, true, true, 0);
188
189 std::deque<Bus::Member *> members_sorted;
190 for (auto &it : bus.members) {
191 members_sorted.push_back(&it.second);
192 }
193 std::sort(members_sorted.begin(), members_sorted.end(),
194 [](const auto &a, const auto &b) { return strcmp_natural(a->name, b->name) < 0; });
195
196 sg_entry = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
197 sg_button = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
198 for (auto it : members_sorted) {
199 add_row(*it);
200 }
201 }
202
203 private:
204 Bus &bus;
205 Block █
206 Gtk::Entry *bus_name_entry;
207 Gtk::ListBox *listbox;
208 Glib::RefPtr<Gtk::SizeGroup> sg_entry;
209 Glib::RefPtr<Gtk::SizeGroup> sg_button;
210
add_row(Bus::Member & mem)211 void add_row(Bus::Member &mem)
212 {
213 auto ed = Gtk::manage(new BusMemberEditor(bus, mem, block));
214 sg_entry->add_widget(*ed->entry);
215 sg_button->add_widget(*ed->net_button);
216 listbox->insert(*ed, -1);
217 listbox->show_all();
218 }
219
header_fun(Gtk::ListBoxRow * row,Gtk::ListBoxRow * before)220 void header_fun(Gtk::ListBoxRow *row, Gtk::ListBoxRow *before)
221 {
222 if (before && !row->get_header()) {
223 auto ret = Gtk::manage(new Gtk::Separator);
224 row->set_header(*ret);
225 }
226 }
bus_name_changed()227 void bus_name_changed()
228 {
229 std::string new_name = bus_name_entry->get_text();
230 bus.name = new_name;
231 auto *stack = dynamic_cast<Gtk::Stack *>(get_parent());
232 stack->child_property_title(*this).set_value(new_name);
233 }
bus_add_member()234 void bus_add_member()
235 {
236 auto uu = UUID::random();
237 auto &newmember = bus.members.emplace(uu, uu).first->second;
238 newmember.name = "fixme";
239 uu = UUID::random();
240 Net *newnet = block.insert_net();
241 newnet->name = "changeme";
242 newnet->is_bussed = true;
243 newmember.net = newnet;
244 add_row(newmember);
245 }
bus_add_sequence()246 void bus_add_sequence()
247 {
248 auto top = dynamic_cast<Gtk::Window *>(get_ancestor(GTK_TYPE_WINDOW));
249 AddSequenceDialog dia(top);
250 dia.w_start_index->set_value(seq_first_index);
251 dia.w_end_index->set_value(seq_last_index);
252 if (dia.run() == Gtk::RESPONSE_OK) {
253 seq_first_index = dia.w_start_index->get_value_as_int();
254 seq_last_index = dia.w_end_index->get_value_as_int();
255 const std::string templ = dia.w_text->get_text();
256 const auto dollar_pos = templ.find('$');
257 if (dollar_pos == std::string::npos)
258 return;
259 for (int i = seq_first_index; i <= seq_last_index; i++) {
260 std::string netname = templ;
261 netname.replace(dollar_pos, 1, std::to_string(i));
262
263 auto uu = UUID::random();
264 auto &newmember = bus.members.emplace(uu, uu).first->second;
265 newmember.name = netname;
266 uu = UUID::random();
267 Net *newnet = block.insert_net();
268 newnet->name = bus.name + "_" + netname;
269 newnet->is_bussed = true;
270 newmember.net = newnet;
271 add_row(newmember);
272 }
273 }
274 }
275 int seq_first_index = 0;
276 int seq_last_index = 0;
277 };
278
279
ManageBusesDialog(Gtk::Window * parent,Block & bl)280 ManageBusesDialog::ManageBusesDialog(Gtk::Window *parent, Block &bl)
281 : Gtk::Dialog("Manage buses", *parent, Gtk::DialogFlags::DIALOG_MODAL | Gtk::DialogFlags::DIALOG_USE_HEADER_BAR),
282 block(bl)
283 {
284 add_button("Cancel", Gtk::ResponseType::RESPONSE_CANCEL);
285 add_button("OK", Gtk::ResponseType::RESPONSE_OK);
286 set_default_response(Gtk::ResponseType::RESPONSE_OK);
287 set_default_size(400, 300);
288
289 auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0));
290
291 stack = Gtk::manage(new Gtk::Stack());
292 stack->set_transition_type(Gtk::STACK_TRANSITION_TYPE_SLIDE_RIGHT);
293 stack->set_transition_duration(100);
294 stack->property_visible_child_name().signal_changed().connect(
295 sigc::mem_fun(*this, &ManageBusesDialog::update_bus_removable));
296 auto sidebar = Gtk::manage(new Gtk::StackSidebar());
297 sidebar->set_stack(*stack);
298
299 auto box2 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0));
300 box2->pack_start(*sidebar, true, true, 0);
301
302 auto tb = Gtk::manage(new Gtk::Toolbar());
303 tb->set_icon_size(Gtk::ICON_SIZE_MENU);
304 tb->set_toolbar_style(Gtk::TOOLBAR_ICONS);
305 tb->get_style_context()->add_class("bus-toolbar");
306 {
307 auto tbo = Gtk::manage(new Gtk::ToolButton());
308 tbo->set_icon_name("list-add-symbolic");
309 tbo->signal_clicked().connect(sigc::mem_fun(*this, &ManageBusesDialog::add_bus));
310 // tbo->signal_clicked().connect([this]{s_signal_add_sheet.emit();});
311 tb->insert(*tbo, -1);
312 }
313 {
314 auto tbo = Gtk::manage(new Gtk::ToolButton());
315 tbo->set_icon_name("list-remove-symbolic");
316 tbo->signal_clicked().connect(sigc::mem_fun(*this, &ManageBusesDialog::remove_bus));
317 tb->insert(*tbo, -1);
318 delete_button = tbo;
319 }
320
321
322 box2->pack_start(*tb, false, false, 0);
323
324 box->pack_start(*box2, false, false, 0);
325 box->pack_start(*stack, true, true, 0);
326
327 std::deque<Bus *> buses_sorted;
328 for (auto &it : block.buses) {
329 buses_sorted.push_back(&it.second);
330 }
331 std::sort(buses_sorted.begin(), buses_sorted.end(), [](const auto &a, const auto &b) { return a->name < b->name; });
332
333
334 for (auto it : buses_sorted) {
335 auto ed = Gtk::manage(new BusEditor(*it, block));
336 // auto sc = Gtk::manage(new Gtk::ScrolledWindow());
337 // sc->add(*ed);
338 stack->add(*ed, (std::string)it->uuid, it->name);
339 }
340
341
342 get_content_area()->pack_start(*box, true, true, 0);
343 get_content_area()->set_border_width(0);
344 update_bus_removable();
345 show_all();
346 }
347
remove_bus()348 void ManageBusesDialog::remove_bus()
349 {
350 auto bus_current_uuid = UUID(stack->get_visible_child_name());
351 const auto bus = block.buses.at(bus_current_uuid);
352 if (bus.is_referenced)
353 return;
354 delete stack->get_visible_child();
355 block.buses.erase(bus_current_uuid);
356 }
357
update_bus_removable()358 void ManageBusesDialog::update_bus_removable()
359 {
360 auto vc = stack->get_visible_child_name();
361 if (vc.size()) {
362 auto bus_current_uuid = UUID(stack->get_visible_child_name());
363 const auto bus = block.buses.at(bus_current_uuid);
364 delete_button->set_sensitive(!bus.is_referenced);
365 }
366 else {
367 delete_button->set_sensitive(false);
368 }
369 }
370
add_bus()371 void ManageBusesDialog::add_bus()
372 {
373 auto uu = UUID::random();
374 auto &newbus = block.buses.emplace(uu, uu).first->second;
375 newbus.name = "NEW";
376 auto ed = Gtk::manage(new BusEditor(newbus, block));
377 // auto sc = Gtk::manage(new Gtk::ScrolledWindow());
378 // sc->add(*ed);
379 stack->add(*ed, (std::string)newbus.uuid, newbus.name);
380 stack->show_all();
381 }
382 } // namespace horizon
383