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 &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 &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