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