1 #include "symbol_pin_names_window.hpp"
2 #include "pool/gate.hpp"
3 #include "pool/entity.hpp"
4 #include "schematic/schematic_symbol.hpp"
5 #include "util/util.hpp"
6 #include "util/str_util.hpp"
7 #include "util/gtk_util.hpp"
8 #include "common/object_descr.hpp"
9 #include "util/csv.hpp"
10 #include "core/tool_data_window.hpp"
11 #include "imp/imp_interface.hpp"
12 #include "util/changeable.hpp"
13 #include <iostream>
14 #include <deque>
15 #include <algorithm>
16
17 namespace horizon {
18
19 class PinNamesBox : public Gtk::FlowBox, public Changeable {
20 public:
PinNamesBox(Component * c,const UUIDPath<2> & p,Glib::RefPtr<Gtk::SizeGroup> sg_combo)21 PinNamesBox(Component *c, const UUIDPath<2> &p, Glib::RefPtr<Gtk::SizeGroup> sg_combo)
22 : Gtk::FlowBox(), comp(c), path(p), pin(comp->entity->gates.at(path.at(0)).unit->pins.at(path.at(1)))
23 {
24 set_homogeneous(true);
25 set_selection_mode(Gtk::SELECTION_NONE);
26 set_min_children_per_line(2);
27
28 {
29 auto primary_button = Gtk::manage(new Gtk::CheckButton(pin.primary_name + " (primary)"));
30 if (comp->pin_names.count(path))
31 primary_button->set_active(comp->pin_names.at(path).count(-1));
32 primary_button->signal_toggled().connect([this, primary_button] {
33 add_remove_name(-1, primary_button->get_active());
34 s_signal_changed.emit();
35 });
36 sg_combo->add_widget(*primary_button);
37 add(*primary_button);
38 }
39
40 {
41 int i = 0;
42 for (const auto &pin_name : pin.names) {
43 auto cb = Gtk::manage(new Gtk::CheckButton(pin_name));
44 if (comp->pin_names.count(path))
45 cb->set_active(comp->pin_names.at(path).count(i));
46 cb->signal_toggled().connect([this, i, cb] {
47 add_remove_name(i, cb->get_active());
48 s_signal_changed.emit();
49 });
50 i++;
51 sg_combo->add_widget(*cb);
52 add(*cb);
53 }
54 }
55
56 {
57 auto cbox = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL));
58 custom_cb = Gtk::manage(new Gtk::CheckButton(""));
59 cbox->pack_start(*custom_cb, false, false, 0);
60 custom_entry = Gtk::manage(new Gtk::Entry);
61 if (comp->custom_pin_names.count(path))
62 custom_entry->set_text(comp->custom_pin_names.at(path));
63 cbox->pack_start(*custom_entry, true, true, 0);
64 custom_cb->signal_toggled().connect([this] {
65 custom_entry->set_sensitive(custom_cb->get_active());
66 add_remove_name(-2, custom_cb->get_active());
67 s_signal_changed.emit();
68 });
69 custom_entry->set_sensitive(false);
70 custom_entry->set_placeholder_text("Custom");
71 custom_entry->signal_changed().connect([this] {
72 comp->custom_pin_names[path] = custom_entry->get_text();
73 s_signal_changed.emit();
74 });
75 if (comp->pin_names.count(path))
76 custom_cb->set_active(comp->pin_names.at(path).count(-2));
77 sg_combo->add_widget(*cbox);
78 add(*cbox);
79 }
80 }
81
get_pin() const82 const Pin &get_pin() const
83 {
84 return pin;
85 }
86
set_custom_name(const std::string & name)87 void set_custom_name(const std::string &name)
88 {
89 if (!name.size())
90 return;
91 custom_entry->set_text(name);
92 custom_cb->set_active(true);
93 }
94
95 private:
96 Component *comp;
97 UUIDPath<2> path;
98 const Pin &pin;
99 Gtk::Entry *custom_entry = nullptr;
100 Gtk::CheckButton *custom_cb = nullptr;
101
add_remove_name(int name,bool add)102 void add_remove_name(int name, bool add)
103 {
104 if (add) {
105 comp->pin_names[path].insert(name);
106 }
107 else {
108 comp->pin_names[path].erase(name);
109 }
110 }
111 };
112
113 class GatePinRow : public Gtk::Box {
114 public:
GatePinRow(const std::string & label,Glib::RefPtr<Gtk::SizeGroup> sg_label)115 GatePinRow(const std::string &label, Glib::RefPtr<Gtk::SizeGroup> sg_label)
116 : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 16)
117 {
118 auto la = Gtk::manage(new Gtk::Label(label));
119 la->set_xalign(0);
120 sg_label->add_widget(*la);
121 pack_start(*la, false, false, 0);
122 }
123
add_box(PinNamesBox * b)124 void add_box(PinNamesBox *b)
125 {
126 box = b;
127 pack_start(*b, true, true, 0);
128 }
129
get_box()130 PinNamesBox *get_box()
131 {
132 return box;
133 }
134
135 private:
136 PinNamesBox *box = nullptr;
137 };
138
139 class GatePinEditor : public Gtk::ListBox, public Changeable {
140 public:
GatePinEditor(Component * c,const Gate * g)141 GatePinEditor(Component *c, const Gate *g) : Gtk::ListBox(), comp(c), gate(g)
142 {
143 sg = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
144 sg_combo = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
145 set_header_func(&header_func_separator);
146 set_selection_mode(Gtk::SELECTION_SINGLE);
147 std::deque<const Pin *> pins_sorted;
148 for (const auto &it : gate->unit->pins) {
149 pins_sorted.push_back(&it.second);
150 }
151 std::sort(pins_sorted.begin(), pins_sorted.end(),
152 [](const auto &a, const auto &b) { return strcmp_natural(a->primary_name, b->primary_name) < 0; });
153 boxes.reserve(pins_sorted.size());
154 for (const auto it : pins_sorted) {
155 auto box = Gtk::manage(new GatePinRow(it->primary_name, sg));
156
157 auto bu = Gtk::manage(new PinNamesBox(comp, UUIDPath<2>(gate->uuid, it->uuid), sg_combo));
158 bu->signal_changed().connect([this] { s_signal_changed.emit(); });
159 bu->show();
160 box->add_box(bu);
161
162 box->set_margin_start(16);
163 box->set_margin_end(8);
164 box->set_margin_top(4);
165 box->set_margin_bottom(4);
166
167 boxes.push_back(bu);
168 insert(*box, -1);
169 }
170 }
171 Component *comp;
172 const Gate *gate;
173
get_boxes()174 const std::vector<PinNamesBox *> &get_boxes()
175 {
176 return boxes;
177 }
178
179 private:
180 Glib::RefPtr<Gtk::SizeGroup> sg;
181 Glib::RefPtr<Gtk::SizeGroup> sg_combo;
182 std::vector<PinNamesBox *> boxes;
183 };
184
handle_import()185 void SymbolPinNamesWindow::handle_import()
186 {
187 GtkFileChooserNative *native =
188 gtk_file_chooser_native_new("Open", GTK_WINDOW(gobj()), GTK_FILE_CHOOSER_ACTION_OPEN, "_Open", "_Cancel");
189 auto chooser = Glib::wrap(GTK_FILE_CHOOSER(native));
190 auto filter = Gtk::FileFilter::create();
191 filter->set_name("CSV documents");
192 filter->add_pattern("*.csv");
193 filter->add_pattern("*.CSV");
194 chooser->add_filter(filter);
195
196 if (gtk_native_dialog_run(GTK_NATIVE_DIALOG(native)) == GTK_RESPONSE_ACCEPT) {
197 auto filename = chooser->get_filename();
198 try {
199 auto ifs = make_ifstream(filename);
200 if (!ifs.is_open()) {
201 throw std::runtime_error("file " + filename + " not opened");
202 }
203 CSV::Csv csv;
204 ifs >> csv;
205 ifs.close();
206 csv.expand(2);
207 auto &boxes = editor->get_boxes();
208 for (const auto &it : csv) {
209 std::string primary_name = it.at(0);
210 std::string custom_name = it.at(1);
211 trim(primary_name);
212 trim(custom_name);
213 auto box = std::find_if(boxes.begin(), boxes.end(),
214 [&primary_name](auto &x) { return x->get_pin().primary_name == primary_name; });
215 if (box != boxes.end()) {
216 (*box)->set_custom_name(custom_name);
217 }
218 }
219 }
220 catch (const std::exception &e) {
221 Gtk::MessageDialog md(*this, "Error importing", false /* use_markup */, Gtk::MESSAGE_ERROR,
222 Gtk::BUTTONS_OK);
223 md.set_secondary_text(e.what());
224 md.run();
225 }
226 catch (...) {
227 Gtk::MessageDialog md(*this, "Error importing", false /* use_markup */, Gtk::MESSAGE_ERROR,
228 Gtk::BUTTONS_OK);
229 md.set_secondary_text("unknown error");
230 md.run();
231 }
232 }
233 }
234
SymbolPinNamesWindow(Gtk::Window * parent,ImpInterface * intf,SchematicSymbol & s)235 SymbolPinNamesWindow::SymbolPinNamesWindow(Gtk::Window *parent, ImpInterface *intf, SchematicSymbol &s)
236 : ToolWindow(parent, intf), sym(s)
237 {
238 set_title("Symbol " + s.component->refdes + s.gate->suffix + " pin names");
239 set_default_size(300, 700);
240 auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL, 0));
241
242 auto mode_combo = Gtk::manage(new Gtk::ComboBoxText());
243
244 for (const auto &it : object_descriptions.at(ObjectType::SCHEMATIC_SYMBOL)
245 .properties.at(ObjectProperty::ID::PIN_NAME_DISPLAY)
246 .enum_items) {
247 mode_combo->append(std::to_string(it.first), it.second);
248 }
249 mode_combo->set_active_id(std::to_string(static_cast<int>(sym.pin_display_mode)));
250 mode_combo->signal_changed().connect([this, mode_combo] {
251 sym.pin_display_mode = static_cast<SchematicSymbol::PinDisplayMode>(std::stoi(mode_combo->get_active_id()));
252 emit_event(ToolDataWindow::Event::UPDATE);
253 });
254
255 auto box2 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 4));
256 box2->property_margin() = 8;
257 box->pack_start(*box2, false, false, 0);
258 box2->pack_start(*mode_combo, true, true, 0);
259
260 auto import_button = Gtk::manage(new Gtk::Button("Import custom pin names"));
261 import_button->signal_clicked().connect(sigc::mem_fun(*this, &SymbolPinNamesWindow::handle_import));
262 box2->pack_start(*import_button, false, false, 0);
263
264 auto sep = Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL));
265 box->pack_start(*sep, false, false, 0);
266
267 editor = Gtk::manage(new GatePinEditor(sym.component, sym.gate));
268 editor->signal_changed().connect([this] { emit_event(ToolDataWindow::Event::UPDATE); });
269 editor->signal_selected_rows_changed().connect([this] { emit_event(ToolDataWindow::Event::UPDATE); });
270 auto sc = Gtk::manage(new Gtk::ScrolledWindow());
271 sc->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
272 sc->add(*editor);
273 box->pack_start(*sc, true, true, 0);
274
275
276 add(*box);
277
278 show_all();
279 }
280
ensure_row_visible(GtkListBox * box,GtkListBoxRow * row)281 static void ensure_row_visible(GtkListBox *box, GtkListBoxRow *row)
282 {
283 GtkWidget *header;
284 gint y, height;
285 GtkAllocation allocation;
286
287 GtkAdjustment *adjustment = gtk_list_box_get_adjustment(box);
288
289 gtk_widget_get_allocation(GTK_WIDGET(row), &allocation);
290 y = allocation.y;
291 height = allocation.height;
292
293 /* If the row has a header, we want to ensure that it is visible as well. */
294 header = gtk_list_box_row_get_header(row);
295 if (GTK_IS_WIDGET(header) && gtk_widget_is_drawable(header)) {
296 gtk_widget_get_allocation(header, &allocation);
297 y = allocation.y;
298 height += allocation.height;
299 }
300
301 gtk_adjustment_clamp_page(adjustment, y, y + height);
302 }
303
go_to_pin(const UUID & uu)304 void SymbolPinNamesWindow::go_to_pin(const UUID &uu)
305 {
306 for (auto ed : editor->get_boxes()) {
307 if (ed->get_pin().uuid == uu) {
308 editor->unselect_all();
309 auto row = dynamic_cast<Gtk::ListBoxRow *>(ed->get_ancestor(GTK_TYPE_LIST_BOX_ROW));
310 editor->select_row(*row);
311 ensure_row_visible(editor->gobj(), row->gobj());
312 break;
313 }
314 }
315 }
316
get_selected_pin()317 UUID SymbolPinNamesWindow::get_selected_pin()
318 {
319 auto row = editor->get_selected_row();
320 if (row) {
321 auto row2 = dynamic_cast<GatePinRow *>(row->get_child());
322 return row2->get_box()->get_pin().uuid;
323 }
324 else {
325 return UUID();
326 }
327 }
328
329 } // namespace horizon
330