1 #include "edit_stackup.hpp"
2 #include <iostream>
3 #include <deque>
4 #include <algorithm>
5 #include "board/board.hpp"
6 #include "board/board_layers.hpp"
7 #include "widgets/spin_button_dim.hpp"
8 #include "util/util.hpp"
9 #include "document/idocument_board.hpp"
10
11 namespace horizon {
12
13 class StackupLayerEditor : public Gtk::Box {
14 public:
15 StackupLayerEditor(EditStackupDialog &parent, int la, bool cu);
16 SpinButtonDim *sp = nullptr;
17 int layer;
18 bool copper;
19 };
20
StackupLayerEditor(EditStackupDialog & parent,int ly,bool cu)21 StackupLayerEditor::StackupLayerEditor(EditStackupDialog &parent, int ly, bool cu)
22 : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 10), layer(ly), copper(cu)
23 {
24 auto colorbox = Gtk::manage(new Gtk::DrawingArea);
25 colorbox->set_size_request(20, -1);
26 colorbox->show();
27 colorbox->signal_draw().connect([this](const Cairo::RefPtr<Cairo::Context> &cr) -> bool {
28 if (copper) {
29 cr->set_source_rgb(1, .8, 0);
30 }
31 else {
32 cr->set_source_rgb(.2, .15, 0);
33 }
34 cr->paint();
35 return true;
36 });
37 pack_start(*colorbox, false, false, 0);
38
39 std::string label_str = parent.board.get_layers().at(layer).name;
40 if (cu) {
41 label_str += " (Copper)";
42 }
43 else {
44 label_str += " (Substrate)";
45 }
46
47 auto la = Gtk::manage(new Gtk::Label(label_str));
48 parent.sg_layer_name->add_widget(*la);
49 la->set_xalign(0);
50 la->show();
51 pack_start(*la, false, false, 0);
52
53 sp = Gtk::manage(new SpinButtonDim());
54 sp->set_range(0, 10_mm);
55 sp->show();
56
57 set_margin_start(8);
58 set_margin_end(8);
59 set_margin_top(4);
60 set_margin_bottom(4);
61
62 pack_start(*sp, true, true, 0);
63 }
64
65
EditStackupDialog(Gtk::Window * parent,IDocumentBoard & c)66 EditStackupDialog::EditStackupDialog(Gtk::Window *parent, IDocumentBoard &c)
67 : Gtk::Dialog("Edit Stackup", *parent, Gtk::DialogFlags::DIALOG_MODAL | Gtk::DialogFlags::DIALOG_USE_HEADER_BAR),
68 core(c), board(*core.get_board())
69 {
70 add_button("Cancel", Gtk::ResponseType::RESPONSE_CANCEL);
71 auto ok_button = add_button("OK", Gtk::ResponseType::RESPONSE_OK);
72 ok_button->signal_clicked().connect(sigc::mem_fun(*this, &EditStackupDialog::ok_clicked));
73 set_default_response(Gtk::ResponseType::RESPONSE_OK);
74 set_default_size(400, 300);
75
76 sg_layer_name = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL);
77
78 auto box = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
79 auto box2 = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 8));
80 box2->property_margin() = 8;
81 auto la = Gtk::manage(new Gtk::Label("Inner Layers"));
82 la->get_style_context()->add_class("dim-label");
83 box2->pack_start(*la, false, false, 0);
84
85 sp_n_inner_layers = Gtk::manage(new Gtk::SpinButton());
86 sp_n_inner_layers->set_range(0, 4);
87 sp_n_inner_layers->set_digits(0);
88 sp_n_inner_layers->set_increments(1, 1);
89 sp_n_inner_layers->set_value(board.get_n_inner_layers());
90 sp_n_inner_layers->signal_value_changed().connect(sigc::mem_fun(*this, &EditStackupDialog::update_layers));
91 box2->pack_start(*sp_n_inner_layers, true, true, 0);
92
93 box->pack_start(*box2, false, false, 0);
94
95 {
96 auto sep = Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL));
97 box->pack_start(*sep, false, false, 0);
98 }
99
100 auto sc = Gtk::manage(new Gtk::ScrolledWindow);
101 sc->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
102
103 lb = Gtk::manage(new Gtk::ListBox);
104 lb->set_selection_mode(Gtk::SELECTION_NONE);
105 sc->add(*lb);
106
107 box->pack_start(*sc, true, true, 0);
108
109 get_content_area()->pack_start(*box, true, true, 0);
110 get_content_area()->set_border_width(0);
111
112 update_layers();
113
114 show_all();
115 }
116
update_layers()117 void EditStackupDialog::update_layers()
118 {
119 auto n_inner_layers = sp_n_inner_layers->get_value_as_int();
120 board.set_n_inner_layers(n_inner_layers);
121 for (auto ch : lb->get_children()) {
122 auto ed = dynamic_cast<StackupLayerEditor *>(dynamic_cast<Gtk::ListBoxRow *>(ch)->get_child());
123 saved[{ed->layer, ed->copper}] = ed->sp->get_value_as_int();
124 delete ch;
125 }
126 auto ed_top = Gtk::manage(new StackupLayerEditor(*this, BoardLayers::TOP_COPPER, true));
127 if (saved.count({BoardLayers::TOP_COPPER, true}))
128 ed_top->sp->set_value(saved.at({BoardLayers::TOP_COPPER, true}));
129 else if (board.stackup.count(BoardLayers::TOP_COPPER))
130 ed_top->sp->set_value(board.stackup.at(BoardLayers::TOP_COPPER).thickness);
131 lb->add(*ed_top);
132
133 auto ed_top_sub = Gtk::manage(new StackupLayerEditor(*this, BoardLayers::TOP_COPPER, false));
134 if (saved.count({BoardLayers::TOP_COPPER, false}))
135 ed_top_sub->sp->set_value(saved.at({BoardLayers::TOP_COPPER, false}));
136 else if (board.stackup.count(BoardLayers::TOP_COPPER))
137 ed_top_sub->sp->set_value(board.stackup.at(BoardLayers::TOP_COPPER).substrate_thickness);
138 lb->add(*ed_top_sub);
139
140 for (int i = 0; i < n_inner_layers; i++) {
141 int layer = -i - 1;
142 for (const auto cu : {true, false}) {
143 auto ed = Gtk::manage(new StackupLayerEditor(*this, layer, cu));
144 if (saved.count({layer, cu}))
145 ed->sp->set_value(saved.at({layer, cu}));
146 else if (board.stackup.count(layer))
147 ed->sp->set_value(cu ? board.stackup.at(layer).thickness : board.stackup.at(layer).substrate_thickness);
148 lb->add(*ed);
149 }
150 }
151
152
153 auto ed_bottom = Gtk::manage(new StackupLayerEditor(*this, BoardLayers::BOTTOM_COPPER, true));
154 if (saved.count({BoardLayers::BOTTOM_COPPER, true}))
155 ed_bottom->sp->set_value(saved.at({BoardLayers::BOTTOM_COPPER, true}));
156 else if (board.stackup.count(BoardLayers::BOTTOM_COPPER))
157 ed_bottom->sp->set_value(board.stackup.at(BoardLayers::BOTTOM_COPPER).thickness);
158 lb->add(*ed_bottom);
159
160 lb->show_all();
161 }
162
ok_clicked()163 void EditStackupDialog::ok_clicked()
164 {
165 auto n_inner_layers = sp_n_inner_layers->get_value_as_int();
166 board.set_n_inner_layers(n_inner_layers);
167 for (auto ch : lb->get_children()) {
168 auto ed = dynamic_cast<StackupLayerEditor *>(dynamic_cast<Gtk::ListBoxRow *>(ch)->get_child());
169 if (ed->copper) {
170 board.stackup.at(ed->layer).thickness = ed->sp->get_value_as_int();
171 }
172 else {
173 board.stackup.at(ed->layer).substrate_thickness = ed->sp->get_value_as_int();
174 }
175 }
176 map_erase_if(board.tracks, [this](const auto &x) { return board.get_layers().count(x.second.layer) == 0; });
177 map_erase_if(board.polygons, [this](const auto &x) { return board.get_layers().count(x.second.layer) == 0; });
178 map_erase_if(board.lines, [this](const auto &x) { return board.get_layers().count(x.second.layer) == 0; });
179 map_erase_if(board.texts, [this](const auto &x) { return board.get_layers().count(x.second.layer) == 0; });
180 core.get_fab_output_settings().update_for_board(board);
181 board.update_pdf_export_settings(core.get_pdf_export_settings());
182 board.expand_flags = Board::EXPAND_VIAS | Board::EXPAND_PACKAGES; // expand inner layers of padstacks
183 }
184 } // namespace horizon
185