1 #include "part_editor.hpp"
2 #include "dialogs/pool_browser_dialog.hpp"
3 #include "widgets/pool_browser_package.hpp"
4 #include "widgets/pool_browser_part.hpp"
5 #include "widgets/tag_entry.hpp"
6 #include <iostream>
7 #include "pool/part.hpp"
8 #include "pool/pool_parametric.hpp"
9 #include "util/util.hpp"
10 #include "util/gtk_util.hpp"
11 #include <glibmm.h>
12 #include "util/pool_completion.hpp"
13 #include "parametric.hpp"
14 #include "pool/ipool.hpp"
15
16 namespace horizon {
17 class EntryWithInheritance : public Gtk::Box {
18 public:
EntryWithInheritance()19 EntryWithInheritance()
20 : Glib::ObjectBase(typeid(EntryWithInheritance)), Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0),
21 p_property_inherit(*this, "inherit"), p_property_can_inherit(*this, "can-inherit")
22 {
23 entry = Gtk::manage(new Gtk::Entry);
24 entry_add_sanitizer(entry);
25 button = Gtk::manage(new Gtk::ToggleButton("Inherit"));
26
27 pack_start(*entry, true, true, 0);
28 pack_start(*button, false, false, 0);
29 get_style_context()->add_class("linked");
30 entry->show();
31 button->show();
32
33 inh_binding = Glib::Binding::bind_property(button->property_active(), property_inherit(),
34 Glib::BINDING_BIDIRECTIONAL);
35 property_inherit().signal_changed().connect([this] {
36 entry->set_sensitive(!property_inherit());
37 if (property_inherit()) {
38 text_this = entry->get_text();
39 entry->set_text(text_inherit);
40 }
41 else {
42 entry->set_text(text_this);
43 }
44 });
45
46 property_can_inherit().signal_changed().connect([this] {
47 button->set_sensitive(property_can_inherit());
48 if (!property_can_inherit()) {
49 property_inherit() = false;
50 }
51 });
52 }
set_text_inherit(const std::string & s)53 void set_text_inherit(const std::string &s)
54 {
55 text_inherit = s;
56 if (property_inherit()) {
57 entry->set_text(text_inherit);
58 }
59 }
60
set_text_this(const std::string & s)61 void set_text_this(const std::string &s)
62 {
63 text_this = s;
64 if (!property_inherit()) {
65 entry->set_text(text_this);
66 }
67 }
68
get_as_pair()69 std::pair<bool, std::string> get_as_pair()
70 {
71 if (property_inherit()) {
72 return {true, text_this};
73 }
74 else {
75 return {false, entry->get_text()};
76 }
77 }
78
79 Gtk::Entry *entry = nullptr;
80 Gtk::ToggleButton *button = nullptr;
81
82
property_inherit()83 Glib::PropertyProxy<bool> property_inherit()
84 {
85 return p_property_inherit.get_proxy();
86 }
property_can_inherit()87 Glib::PropertyProxy<bool> property_can_inherit()
88 {
89 return p_property_can_inherit.get_proxy();
90 }
91
92 private:
93 Glib::Property<bool> p_property_inherit;
94 Glib::Property<bool> p_property_can_inherit;
95 Glib::RefPtr<Glib::Binding> inh_binding;
96 std::string text_inherit;
97 std::string text_this;
98 };
99
100 class OrderableMPNEditor : public Gtk::Box, public Changeable {
101 public:
OrderableMPNEditor(Part & p,const UUID & uu)102 OrderableMPNEditor(Part &p, const UUID &uu) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0), part(p), uuid(uu)
103 {
104 get_style_context()->add_class("linked");
105 entry = Gtk::manage(new Gtk::Entry);
106 entry->show();
107 entry->set_text(part.orderable_MPNs.at(uuid));
108 entry->signal_changed().connect([this] { s_signal_changed.emit(); });
109 entry_add_sanitizer(entry);
110 pack_start(*entry, true, true, 0);
111
112 auto bu = Gtk::manage(new Gtk::Button());
113 bu->set_image_from_icon_name("list-remove-symbolic");
114 bu->show();
115 pack_start(*bu, false, false, 0);
116 bu->signal_clicked().connect([this] {
117 part.orderable_MPNs.erase(uuid);
118 s_signal_changed.emit();
119 delete this;
120 });
121 }
get_MPN()122 std::string get_MPN()
123 {
124 return entry->get_text();
125 }
get_uuid() const126 const UUID &get_uuid() const
127 {
128 return uuid;
129 }
130
focus()131 void focus()
132 {
133 entry->grab_focus();
134 }
135
136 private:
137 Part ∂
138 UUID uuid;
139 Gtk::Entry *entry = nullptr;
140 };
141
142 class FlagEditor : public Gtk::Box, public Changeable {
143 public:
FlagEditor(Part::FlagState & a_state,bool has_inherit)144 FlagEditor(Part::FlagState &a_state, bool has_inherit) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 0), state(a_state)
145 {
146 get_style_context()->add_class("linked");
147 static const std::vector<std::pair<Part::FlagState, std::string>> states = {
148 {Part::FlagState::SET, "Yes"},
149 {Part::FlagState::CLEAR, "No"},
150 {Part::FlagState::INHERIT, "Inherit"},
151 };
152 Gtk::RadioButton *group = nullptr;
153 for (const auto &[st, name] : states) {
154 auto bu = Gtk::manage(new Gtk::RadioButton(name));
155 bu->set_mode(false);
156 if (group)
157 bu->join_group(*group);
158 else
159 group = bu;
160 if (st == Part::FlagState::INHERIT && !has_inherit)
161 bu->set_sensitive(false);
162 if (state == st)
163 bu->set_active(true);
164 bu->show();
165 pack_start(*bu, false, false, 0);
166 Part::FlagState st2 = st;
167 bu->signal_toggled().connect([this, bu, st2] {
168 if (bu->get_active()) {
169 state = st2;
170 s_signal_changed.emit();
171 }
172 });
173 }
174 }
175
176 private:
177 Part::FlagState &state;
178 };
179
PartEditor(BaseObjectType * cobject,const Glib::RefPtr<Gtk::Builder> & x,Part & p,IPool & po,PoolParametric & pp)180 PartEditor::PartEditor(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &x, Part &p, IPool &po,
181 PoolParametric &pp)
182 : Gtk::Box(cobject), part(p), pool(po), pool_parametric(pp)
183 {
184
185 auto add_entry = [x](const char *name) {
186 Gtk::Box *t;
187 x->get_widget(name, t);
188 auto e = Gtk::manage(new EntryWithInheritance);
189 e->show();
190 t->pack_start(*e, true, true, 0);
191 return e;
192 };
193
194 x->get_widget("entity_label", w_entity_label);
195 x->get_widget("package_label", w_package_label);
196 x->get_widget("change_package_button", w_change_package_button);
197 x->get_widget("model_combo", w_model_combo);
198 x->get_widget("model_inherit", w_model_inherit);
199 x->get_widget("base_label", w_base_label);
200 {
201 Gtk::Box *tag_box;
202 x->get_widget("tags", tag_box);
203 w_tags = Gtk::manage(new TagEntry(pool, ObjectType::PART, true));
204 w_tags->show();
205 tag_box->pack_start(*w_tags, true, true, 0);
206 }
207 x->get_widget("tags_inherit", w_tags_inherit);
208 x->get_widget("tags_inherited", w_tags_inherited);
209 x->get_widget("tv_pins", w_tv_pins);
210 x->get_widget("tv_pads", w_tv_pads);
211 x->get_widget("button_map", w_button_map);
212 x->get_widget("button_unmap", w_button_unmap);
213 x->get_widget("button_automap", w_button_automap);
214 x->get_widget("button_select_pin", w_button_select_pin);
215 x->get_widget("button_select_pads", w_button_select_pads);
216 x->get_widget("button_copy_from_other", w_button_copy_from_other);
217 x->get_widget("pin_stat", w_pin_stat);
218 x->get_widget("pad_stat", w_pad_stat);
219 x->get_widget("parametric_box", w_parametric_box);
220 x->get_widget("parametric_table_combo", w_parametric_table_combo);
221 x->get_widget("copy_parametric_from_base", w_parametric_from_base);
222 x->get_widget("orderable_MPNs_label", w_orderable_MPNs_label);
223 x->get_widget("orderable_MPNs_box", w_orderable_MPNs_box);
224 x->get_widget("orderable_MPNs_add_button", w_orderable_MPNs_add_button);
225 x->get_widget("flags_grid", w_flags_grid);
226 x->get_widget("flags_label", w_flags_label);
227 w_parametric_from_base->hide();
228
229 w_entity_label->set_track_visited_links(false);
230 w_entity_label->signal_activate_link().connect(
231 [this](const std::string &url) {
232 s_signal_goto.emit(ObjectType::ENTITY, UUID(url));
233 return true;
234 },
235 false);
236 w_base_label->set_track_visited_links(false);
237 w_base_label->signal_activate_link().connect(
238 [this](const std::string &url) {
239 s_signal_goto.emit(ObjectType::PART, UUID(url));
240 return true;
241 },
242 false);
243 w_package_label->set_track_visited_links(false);
244 w_package_label->signal_activate_link().connect(
245 [this](const std::string &url) {
246 s_signal_goto.emit(ObjectType::PACKAGE, UUID(url));
247 return true;
248 },
249 false);
250
251 w_mpn = add_entry("part_mpn_box");
252 w_value = add_entry("part_value_box");
253 w_manufacturer = add_entry("part_manufacturer_box");
254 w_manufacturer->entry->set_completion(create_pool_manufacturer_completion(pool));
255 w_datasheet = add_entry("part_datasheet_box");
256 w_description = add_entry("part_description_box");
257
258 attr_editors.emplace(horizon::Part::Attribute::MPN, w_mpn);
259 attr_editors.emplace(horizon::Part::Attribute::VALUE, w_value);
260 attr_editors.emplace(horizon::Part::Attribute::MANUFACTURER, w_manufacturer);
261 attr_editors.emplace(horizon::Part::Attribute::DESCRIPTION, w_description);
262 attr_editors.emplace(horizon::Part::Attribute::DATASHEET, w_datasheet);
263
264
265 for (auto &it : attr_editors) {
266 it.second->property_can_inherit() = part.base;
267 it.second->set_text_this(part.attributes.at(it.first).second);
268 if (part.base) {
269 it.second->set_text_inherit(part.base->attributes.at(it.first).second);
270 it.second->property_inherit() = part.attributes.at(it.first).first;
271 }
272 it.second->entry->signal_changed().connect([this, it] {
273 part.attributes[it.first] = it.second->get_as_pair();
274 set_needs_save();
275 });
276 it.second->button->signal_toggled().connect([this, it] {
277 part.attributes[it.first] = it.second->get_as_pair();
278 set_needs_save();
279 });
280 }
281
282 update_entries();
283
284 w_change_package_button->signal_clicked().connect(sigc::mem_fun(*this, &PartEditor::change_package));
285
286 w_tags_inherit->set_active(part.inherit_tags);
287 w_tags_inherit->signal_toggled().connect([this] { set_needs_save(); });
288
289 w_tags->set_tags(part.tags);
290 w_tags->signal_changed().connect([this] {
291 part.tags = w_tags->get_tags();
292 set_needs_save();
293 });
294
295 pin_store = Gtk::ListStore::create(pin_list_columns);
296 pin_store->set_sort_func(pin_list_columns.pin_name,
297 [this](const Gtk::TreeModel::iterator &ia, const Gtk::TreeModel::iterator &ib) {
298 Gtk::TreeModel::Row ra = *ia;
299 Gtk::TreeModel::Row rb = *ib;
300 Glib::ustring a = ra[pin_list_columns.pin_name];
301 Glib::ustring b = rb[pin_list_columns.pin_name];
302 return strcmp_natural(a, b);
303 });
304 w_tv_pins->set_model(pin_store);
305
306 w_tv_pins->append_column("Gate", pin_list_columns.gate_name);
307 w_tv_pins->append_column("Pin", pin_list_columns.pin_name);
308 {
309 auto cr = Gtk::manage(new Gtk::CellRendererPixbuf());
310 cr->property_icon_name() = "object-select-symbolic";
311 cr->property_xalign() = 0;
312 auto tvc = Gtk::manage(new Gtk::TreeViewColumn("Mapped", *cr));
313 tvc->add_attribute(cr->property_visible(), pin_list_columns.mapped);
314 w_tv_pins->append_column(*tvc);
315 }
316
317 w_tv_pins->get_column(0)->set_sort_column(pin_list_columns.gate_name);
318 w_tv_pins->get_column(1)->set_sort_column(pin_list_columns.pin_name);
319
320 pin_store->set_sort_column(pin_list_columns.pin_name, Gtk::SORT_ASCENDING);
321 Glib::signal_timeout().connect_once(
322 sigc::track_obj([this] { pin_store->set_sort_column(pin_list_columns.gate_name, Gtk::SORT_ASCENDING); },
323 *this),
324 10);
325
326 pad_store = Gtk::ListStore::create(pad_list_columns);
327 pad_store->set_sort_func(pad_list_columns.pad_name,
328 [this](const Gtk::TreeModel::iterator &ia, const Gtk::TreeModel::iterator &ib) {
329 Gtk::TreeModel::Row ra = *ia;
330 Gtk::TreeModel::Row rb = *ib;
331 Glib::ustring a = ra[pad_list_columns.pad_name];
332 Glib::ustring b = rb[pad_list_columns.pad_name];
333 return strcmp_natural(a, b);
334 });
335 w_tv_pads->set_model(pad_store);
336 w_tv_pads->append_column("Pad", pad_list_columns.pad_name);
337
338 w_tv_pads->append_column("Gate", pad_list_columns.gate_name);
339 w_tv_pads->append_column("Pin", pad_list_columns.pin_name);
340 w_tv_pads->get_column(0)->set_sort_column(pad_list_columns.pad_name);
341
342 pad_store->set_sort_column(pad_list_columns.pad_name, Gtk::SORT_ASCENDING);
343
344 update_treeview();
345
346 update_map_buttons();
347 w_tv_pads->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &PartEditor::update_map_buttons));
348 w_tv_pins->get_selection()->signal_changed().connect(sigc::mem_fun(*this, &PartEditor::update_map_buttons));
349 w_change_package_button->set_sensitive(!part.base);
350
351 w_button_unmap->signal_clicked().connect([this] {
352 auto sel = w_tv_pads->get_selection();
353 for (auto &path : sel->get_selected_rows()) {
354 auto it = pad_store->get_iter(path);
355 Gtk::TreeModel::Row row = *it;
356 row[pad_list_columns.gate_name] = "";
357 row[pad_list_columns.pin_name] = "";
358 row[pad_list_columns.pin_uuid] = UUID();
359 row[pad_list_columns.gate_uuid] = UUID();
360 }
361 update_mapped();
362 set_needs_save();
363 });
364
365 w_tv_pins->signal_row_activated().connect([this](const Gtk::TreeModel::Path &path, Gtk::TreeViewColumn *col) {
366 auto it_pin = pin_store->get_iter(path);
367 map_pin(it_pin);
368 });
369
370 w_button_map->signal_clicked().connect([this] {
371 auto pin_sel = w_tv_pins->get_selection();
372 auto it_pin = pin_sel->get_selected();
373 map_pin(it_pin);
374 });
375
376 w_button_automap->signal_clicked().connect([this] {
377 auto sel = w_tv_pads->get_selection();
378 for (auto &path : sel->get_selected_rows()) {
379 auto it = pad_store->get_iter(path);
380 Gtk::TreeModel::Row row = *it;
381 Glib::ustring pad_name = row[pad_list_columns.pad_name];
382 for (const auto &it_pin : pin_store->children()) {
383 Gtk::TreeModel::Row row_pin = *it_pin;
384 Glib::ustring pin_name = row_pin[pin_list_columns.pin_name];
385 if (pin_name == pad_name) {
386 row[pad_list_columns.gate_name] = row_pin.get_value(pin_list_columns.gate_name);
387 row[pad_list_columns.pin_name] = row_pin.get_value(pin_list_columns.pin_name);
388 row[pad_list_columns.pin_uuid] = row_pin.get_value(pin_list_columns.pin_uuid);
389 row[pad_list_columns.gate_uuid] = row_pin.get_value(pin_list_columns.gate_uuid);
390 break;
391 }
392 }
393 }
394 update_mapped();
395 set_needs_save();
396 });
397
398 w_button_select_pin->signal_clicked().connect([this] {
399 auto sel = w_tv_pads->get_selection();
400 auto rows = sel->get_selected_rows();
401 if (rows.size() != 1)
402 return;
403 auto it_pad = pad_store->get_iter(rows.front());
404 Gtk::TreeModel::Row row_pad = *it_pad;
405 auto gate_uuid = row_pad[pad_list_columns.gate_uuid];
406 auto pin_uuid = row_pad[pad_list_columns.pin_uuid];
407 for (auto &it : pin_store->children()) {
408 Gtk::TreeModel::Row row = *it;
409 if (row[pin_list_columns.gate_uuid] == gate_uuid && row[pin_list_columns.pin_uuid] == pin_uuid) {
410 w_tv_pins->get_selection()->select(it);
411 tree_view_scroll_to_selection(w_tv_pins);
412 break;
413 }
414 }
415 });
416
417 w_button_select_pads->signal_clicked().connect([this] {
418 auto sel = w_tv_pins->get_selection();
419 auto rows = sel->get_selected_rows();
420 if (rows.size() != 1)
421 return;
422 auto it_pin = pin_store->get_iter(rows.front());
423 Gtk::TreeModel::Row row_pin = *it_pin;
424 auto gate_uuid = row_pin[pin_list_columns.gate_uuid];
425 auto pin_uuid = row_pin[pin_list_columns.pin_uuid];
426
427 w_tv_pads->get_selection()->unselect_all();
428 for (auto &it : pad_store->children()) {
429 Gtk::TreeModel::Row row = *it;
430 if (row[pad_list_columns.gate_uuid] == gate_uuid && row[pad_list_columns.pin_uuid] == pin_uuid) {
431 w_tv_pads->get_selection()->select(it);
432 }
433 }
434 tree_view_scroll_to_selection(w_tv_pads);
435 });
436 w_button_copy_from_other->signal_clicked().connect(sigc::mem_fun(*this, &PartEditor::copy_from_other_part));
437
438 update_mapped();
439 populate_models();
440 w_model_combo->set_active_id((std::string)part.model);
441
442 if (part.base) {
443 w_model_inherit->set_active(part.inherit_model);
444 w_model_inherit->signal_toggled().connect([this] {
445 set_needs_save();
446 update_model_inherit();
447 });
448 update_model_inherit();
449 }
450 else {
451 w_model_inherit->set_sensitive(false);
452 }
453
454 w_model_combo->signal_changed().connect([this] { set_needs_save(); });
455
456 w_parametric_table_combo->append("", "None");
457 for (const auto &it : pool_parametric.get_tables()) {
458 w_parametric_table_combo->append(it.first, it.second.display_name);
459 }
460 w_parametric_table_combo->set_active_id("");
461 if (part.parametric.count("table")) {
462 std::string tab = part.parametric.at("table");
463 if (pool_parametric.get_tables().count(tab)) {
464 w_parametric_table_combo->set_active_id(tab);
465 }
466 }
467 update_parametric_editor();
468 w_parametric_table_combo->signal_changed().connect([this] {
469 set_needs_save();
470 update_parametric_editor();
471 });
472 w_parametric_from_base->set_sensitive(part.base);
473 w_parametric_from_base->signal_clicked().connect([this] {
474 if (part.base) {
475 set_needs_save();
476 }
477 });
478
479 w_orderable_MPNs_add_button->signal_clicked().connect([this] {
480 auto uu = UUID::random();
481 part.orderable_MPNs.emplace(uu, "");
482 auto ed = create_orderable_MPN_editor(uu);
483 ed->focus();
484 set_needs_save();
485 update_orderable_MPNs_label();
486 });
487
488 for (const auto &it : part.orderable_MPNs) {
489 create_orderable_MPN_editor(it.first);
490 }
491 update_orderable_MPNs_label();
492
493 {
494 static const std::map<Part::Flag, std::string> flag_names = {
495 {Part::Flag::BASE_PART, "Base part"},
496 {Part::Flag::EXCLUDE_BOM, "Exclude from BOM"},
497 {Part::Flag::EXCLUDE_PNP, "Exclude from Pick&Place"},
498 };
499 int top = 0;
500 for (const auto &[fl, name] : flag_names) {
501 auto ed = Gtk::manage(new FlagEditor(part.flags.at(fl), part.base));
502 ed->signal_changed().connect([this] {
503 set_needs_save();
504 update_flags_label();
505 });
506 grid_attach_label_and_widget(w_flags_grid, name, ed, top);
507 }
508 }
509 update_flags_label();
510 }
create_orderable_MPN_editor(const UUID & uu)511 class OrderableMPNEditor *PartEditor::create_orderable_MPN_editor(const UUID &uu)
512 {
513 auto ed = Gtk::manage(new OrderableMPNEditor(part, uu));
514 w_orderable_MPNs_box->pack_start(*ed, false, false, 0);
515 ed->signal_changed().connect([this, ed] {
516 if (part.orderable_MPNs.count(ed->get_uuid()))
517 part.orderable_MPNs.at(ed->get_uuid()) = ed->get_MPN();
518 set_needs_save();
519 update_orderable_MPNs_label();
520 });
521 ed->show();
522 return ed;
523 }
524
update_map_buttons()525 void PartEditor::update_map_buttons()
526 {
527 if (part.base) {
528 w_button_map->set_sensitive(false);
529 w_button_unmap->set_sensitive(false);
530 w_button_automap->set_sensitive(false);
531 w_button_copy_from_other->set_sensitive(false);
532 }
533 else {
534 bool can_map =
535 w_tv_pads->get_selection()->count_selected_rows() && w_tv_pins->get_selection()->count_selected_rows();
536 w_button_map->set_sensitive(can_map);
537 w_button_unmap->set_sensitive(w_tv_pads->get_selection()->count_selected_rows());
538 w_button_automap->set_sensitive(w_tv_pads->get_selection()->count_selected_rows());
539 w_button_copy_from_other->set_sensitive(true);
540 }
541 w_button_select_pads->set_sensitive(w_tv_pins->get_selection()->count_selected_rows());
542 w_button_select_pin->set_sensitive(w_tv_pads->get_selection()->count_selected_rows() == 1);
543 }
544
update_orderable_MPNs_label()545 void PartEditor::update_orderable_MPNs_label()
546 {
547 std::string s;
548 for (const auto &[uu, mpn] : part.orderable_MPNs) {
549 s += Glib::Markup::escape_text(mpn) + ", ";
550 }
551 if (s.size()) {
552 s.pop_back();
553 s.pop_back();
554 }
555 else {
556 s = "<i>no orderable MPNs defined</i>";
557 }
558 w_orderable_MPNs_label->set_markup(s);
559 }
560
update_flags_label()561 void PartEditor::update_flags_label()
562 {
563 std::string s;
564 static const std::map<Part::Flag, std::string> flag_names = {
565 {Part::Flag::BASE_PART, "Base part"},
566 {Part::Flag::EXCLUDE_BOM, "No BOM"},
567 {Part::Flag::EXCLUDE_PNP, "No Pick&Place"},
568 };
569 for (const auto &[fl, name] : flag_names) {
570 if (part.get_flag(fl)) {
571 s += Glib::Markup::escape_text(name);
572 s += ", ";
573 }
574 }
575 if (s.size()) {
576 s.pop_back();
577 s.pop_back();
578 }
579 else {
580 s = "<i>none set</i>";
581 }
582 w_flags_label->set_markup(s);
583 }
584
map_pin(Gtk::TreeModel::iterator it_pin)585 void PartEditor::map_pin(Gtk::TreeModel::iterator it_pin)
586 {
587 auto pin_sel = w_tv_pins->get_selection();
588 if (it_pin) {
589 Gtk::TreeModel::Row row_pin = *it_pin;
590 auto sel = w_tv_pads->get_selection();
591 for (auto &path : sel->get_selected_rows()) {
592 auto it = pad_store->get_iter(path);
593 Gtk::TreeModel::Row row = *it;
594 row[pad_list_columns.gate_name] = row_pin.get_value(pin_list_columns.gate_name);
595 row[pad_list_columns.pin_name] = row_pin.get_value(pin_list_columns.pin_name);
596 row[pad_list_columns.pin_uuid] = row_pin.get_value(pin_list_columns.pin_uuid);
597 row[pad_list_columns.gate_uuid] = row_pin.get_value(pin_list_columns.gate_uuid);
598 }
599 if (++it_pin) {
600 pin_sel->select(it_pin);
601 }
602 if (sel->count_selected_rows() == 1) {
603 auto it_pad = pad_store->get_iter(sel->get_selected_rows().at(0));
604 sel->unselect(it_pad);
605 if (++it_pad) {
606 sel->select(it_pad);
607 }
608 }
609 update_mapped();
610 set_needs_save();
611 }
612 }
613
update_model_inherit()614 void PartEditor::update_model_inherit()
615 {
616 auto active = w_model_inherit->get_active();
617 if (active) {
618 w_model_combo->set_active_id((std::string)part.base->model);
619 }
620 w_model_combo->set_sensitive(!active);
621 }
622
append_with_slash(const std::string & s)623 static std::string append_with_slash(const std::string &s)
624 {
625 if (s.size())
626 return " / " + s;
627 else
628 return "";
629 }
630
update_entries()631 void PartEditor::update_entries()
632 {
633
634
635 if (part.base) {
636 w_base_label->set_markup(
637 "<a href=\"" + (std::string)part.base->uuid + "\">"
638 + Glib::Markup::escape_text(part.base->get_MPN() + append_with_slash(part.base->get_manufacturer()))
639 + "</a>");
640 w_entity_label->set_markup("<a href=\"" + (std::string)part.base->entity->uuid + "\">"
641 + Glib::Markup::escape_text(part.base->entity->name
642 + append_with_slash(part.base->entity->manufacturer))
643 + "</a>");
644 w_package_label->set_markup("<a href=\"" + (std::string)part.base->package->uuid + "\">"
645 + Glib::Markup::escape_text(part.base->package->name
646 + append_with_slash(part.base->package->manufacturer))
647 + "</a>");
648 }
649 else {
650 w_base_label->set_text("none");
651 w_entity_label->set_markup(
652 "<a href=\"" + (std::string)part.entity->uuid + "\">"
653 + Glib::Markup::escape_text(part.entity->name + append_with_slash(part.entity->manufacturer)) + "</a>");
654 w_package_label->set_markup(
655 "<a href=\"" + (std::string)part.package->uuid + "\">"
656 + Glib::Markup::escape_text(part.package->name + append_with_slash(part.package->manufacturer))
657 + "</a>");
658 }
659
660 if (part.base) {
661 std::stringstream s;
662 auto tags_from_base = part.base->get_tags();
663 std::copy(tags_from_base.begin(), tags_from_base.end(), std::ostream_iterator<std::string>(s, " "));
664 w_tags_inherited->set_text(s.str());
665 }
666 else {
667 w_tags_inherited->set_text("");
668 }
669
670 w_tags_inherit->set_sensitive(part.base);
671 }
672
change_package()673 void PartEditor::change_package()
674 {
675 auto top = dynamic_cast<Gtk::Window *>(get_ancestor(GTK_TYPE_WINDOW));
676 PoolBrowserDialog dia(top, ObjectType::PACKAGE, pool);
677 if (dia.run() == Gtk::RESPONSE_OK) {
678 set_needs_save();
679 part.package = pool.get_package(dia.get_browser().get_selected());
680 auto ch = pad_store->children();
681 std::set<UUID> pads_exisiting;
682 for (auto it = ch.begin(); it != ch.end();) {
683 Gtk::TreeModel::Row row = *it;
684 auto pad_name = row[pad_list_columns.pad_name];
685 UUID pad_uuid;
686 for (const auto &it_pad : part.package->pads) {
687 if (it_pad.second.name == pad_name) {
688 pad_uuid = it_pad.second.uuid;
689 break;
690 }
691 }
692 if (pad_uuid) {
693 row[pad_list_columns.pad_uuid] = pad_uuid;
694 pads_exisiting.insert(pad_uuid);
695 it++;
696 }
697 else {
698 pad_store->erase(it++);
699 }
700 }
701 for (const auto &it : part.package->pads) {
702 if (pads_exisiting.count(it.first) == 0) {
703 Gtk::TreeModel::Row row = *(pad_store->append());
704 row[pad_list_columns.pad_uuid] = it.first;
705 row[pad_list_columns.pad_name] = it.second.name;
706 }
707 }
708
709 update_entries();
710 update_mapped();
711 populate_models();
712 w_model_combo->set_active_id((std::string)part.package->default_model);
713 set_needs_save();
714 }
715 }
716
reload()717 void PartEditor::reload()
718 {
719 part.update_refs(pool);
720 update_entries();
721 }
722
save()723 void PartEditor::save()
724 {
725 part.inherit_model = w_model_inherit->get_active();
726
727 if (w_model_combo->get_active_row_number() != -1)
728 part.model = UUID(w_model_combo->get_active_id());
729 else
730 part.model = UUID();
731
732 part.inherit_tags = w_tags_inherit->get_active();
733
734 if (parametric_editor) {
735 part.parametric = parametric_editor->get_values();
736 }
737 else {
738 part.parametric.clear();
739 }
740
741 PoolEditorInterface::save();
742 }
743
744
update_treeview()745 void PartEditor::update_treeview()
746 {
747 pin_store->freeze_notify();
748 pad_store->freeze_notify();
749
750 pin_store->clear();
751 pad_store->clear();
752
753 for (const auto &it_gate : part.entity->gates) {
754 for (const auto &it_pin : it_gate.second.unit->pins) {
755 Gtk::TreeModel::Row row = *(pin_store->append());
756 row[pin_list_columns.gate_uuid] = it_gate.first;
757 row[pin_list_columns.gate_name] = it_gate.second.name;
758 row[pin_list_columns.pin_uuid] = it_pin.first;
759 row[pin_list_columns.pin_name] = it_pin.second.primary_name;
760 }
761 }
762
763 for (const auto &it : part.package->pads) {
764 if (it.second.pool_padstack->type != Padstack::Type::MECHANICAL) {
765 Gtk::TreeModel::Row row = *(pad_store->append());
766 row[pad_list_columns.pad_uuid] = it.first;
767 row[pad_list_columns.pad_name] = it.second.name;
768 if (part.pad_map.count(it.first)) {
769 const auto &x = part.pad_map.at(it.first);
770 row[pad_list_columns.gate_uuid] = x.gate->uuid;
771 row[pad_list_columns.gate_name] = x.gate->name;
772 row[pad_list_columns.pin_uuid] = x.pin->uuid;
773 row[pad_list_columns.pin_name] = x.pin->primary_name;
774 }
775 }
776 }
777
778 pad_store->thaw_notify();
779 pin_store->thaw_notify();
780 }
781
update_mapped()782 void PartEditor::update_mapped()
783 {
784 std::set<std::pair<UUID, UUID>> pins_mapped;
785 int n_pads_not_mapped = 0;
786 for (const auto &it : pad_store->children()) {
787 if (it[pad_list_columns.gate_uuid] != UUID()) {
788 pins_mapped.emplace(it[pad_list_columns.gate_uuid], it[pad_list_columns.pin_uuid]);
789 }
790 else {
791 n_pads_not_mapped++;
792 }
793 }
794 for (auto &it : pin_store->children()) {
795 if (pins_mapped.count({it[pin_list_columns.gate_uuid], it[pin_list_columns.pin_uuid]})) {
796 it[pin_list_columns.mapped] = true;
797 }
798 else {
799 it[pin_list_columns.mapped] = false;
800 }
801 }
802 w_pin_stat->set_text(std::to_string(pin_store->children().size() - pins_mapped.size()) + " pins not mapped");
803 w_pad_stat->set_text(std::to_string(n_pads_not_mapped) + " pads not mapped");
804
805 part.pad_map.clear();
806 for (const auto &it : pad_store->children()) {
807 if (it[pad_list_columns.gate_uuid] != UUID() && part.package->pads.count(it[pad_list_columns.pad_uuid])) {
808 const horizon::Gate *gate = &part.entity->gates.at(it[pad_list_columns.gate_uuid]);
809 const horizon::Pin *pin = &gate->unit->pins.at(it[pad_list_columns.pin_uuid]);
810 part.pad_map.emplace(it[pad_list_columns.pad_uuid], Part::PadMapItem(gate, pin));
811 }
812 }
813 }
814
populate_models()815 void PartEditor::populate_models()
816 {
817 w_model_combo->remove_all();
818 for (const auto &it : part.package->models) {
819 w_model_combo->append((std::string)it.first, Glib::path_get_basename(it.second.filename));
820 }
821 }
822
update_parametric_editor()823 void PartEditor::update_parametric_editor()
824 {
825 auto chs = w_parametric_box->get_children();
826 if (chs.size()) {
827 delete chs.back();
828 }
829 parametric_editor = nullptr;
830 auto tab = w_parametric_table_combo->get_active_id();
831 if (pool_parametric.get_tables().count(tab)) {
832 auto ed = Gtk::manage(new ParametricEditor(pool_parametric, tab));
833 ed->show();
834 w_parametric_box->pack_start(*ed, true, true, 0);
835 if (part.parametric.count("table") && part.parametric.at("table") == tab) {
836 ed->update(part.parametric);
837 }
838 parametric_editor = ed;
839 parametric_editor->signal_changed().connect([this] { set_needs_save(); });
840 }
841 }
842
copy_from_other_part()843 void PartEditor::copy_from_other_part()
844 {
845 auto top = dynamic_cast<Gtk::Window *>(get_ancestor(GTK_TYPE_WINDOW));
846 PoolBrowserDialog dia(top, ObjectType::PART, pool);
847 auto &br = dynamic_cast<PoolBrowserPart &>(dia.get_browser());
848 br.set_entity_uuid(part.entity->uuid);
849 if (dia.run() == Gtk::RESPONSE_OK) {
850 auto uu = br.get_selected();
851 auto other_part = pool.get_part(uu);
852 for (auto &it_pad : pad_store->children()) {
853 Gtk::TreeModel::Row row_pad = *it_pad;
854 if (row_pad.get_value(pad_list_columns.gate_uuid) == UUID()) { // only update unmapped pads
855 std::string pad_name = row_pad.get_value(pad_list_columns.pad_name);
856 // find other part mapping
857 for (const auto &it_map_other : other_part->pad_map) {
858 auto pad_uu_other = it_map_other.first;
859 const auto &pad_name_other = other_part->package->pads.at(pad_uu_other).name;
860 if (pad_name_other == pad_name) { // found it
861 row_pad[pad_list_columns.gate_name] = it_map_other.second.gate->name;
862 row_pad[pad_list_columns.pin_name] = it_map_other.second.pin->primary_name;
863 row_pad[pad_list_columns.pin_uuid] = it_map_other.second.pin->uuid;
864 row_pad[pad_list_columns.gate_uuid] = it_map_other.second.gate->uuid;
865 break;
866 }
867 }
868 }
869 }
870 update_mapped();
871 set_needs_save();
872 }
873 }
874
create(Part & p,IPool & po,PoolParametric & pp)875 PartEditor *PartEditor::create(Part &p, IPool &po, PoolParametric &pp)
876 {
877 PartEditor *w;
878 Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create();
879 x->add_from_resource("/org/horizon-eda/horizon/pool-prj-mgr/pool-mgr/editors/part_editor.ui");
880 x->get_widget_derived("part_editor", w, p, po, pp);
881 w->reference();
882 return w;
883 }
884 } // namespace horizon
885