1 #include "imp.hpp"
2 #include "util/str_util.hpp"
3 #include "util/util.hpp"
4 #include "util/geom_util.hpp"
5 #include "util/selection_util.hpp"
6 #include "pool/entity.hpp"
7 #include "pool/part.hpp"
8 #include "block/block.hpp"
9 #include "board/board_junction.hpp"
10 
11 namespace horizon {
hud_update()12 void ImpBase::hud_update()
13 {
14     if (core->tool_is_active())
15         return;
16 
17     auto sel = canvas->get_selection();
18     if (sel.size()) {
19         std::string hud_text = get_hud_text(sel);
20         trim(hud_text);
21         hud_text += "\n\n";
22         auto text_generic = ImpBase::get_hud_text(sel);
23         trim(text_generic);
24         hud_text += text_generic;
25         trim(hud_text);
26         main_window->hud_update(hud_text);
27     }
28     else {
29         main_window->hud_update("");
30     }
31 }
32 
get_hud_text(std::set<SelectableRef> & sel)33 std::string ImpBase::get_hud_text(std::set<SelectableRef> &sel)
34 {
35     std::string s;
36     if (sel_count_type(sel, ObjectType::LINE)) {
37         auto n = sel_count_type(sel, ObjectType::LINE);
38         s += "\n\n<b>" + std::to_string(n) + " " + object_descriptions.at(ObjectType::LINE).get_name_for_n(n)
39              + "</b>\n";
40         std::set<int> layers;
41         int64_t length = 0;
42         for (const auto &it : sel) {
43             if (it.type == ObjectType::LINE) {
44                 const auto li = core->get_line(it.uuid);
45                 layers.insert(li->layer);
46                 length += (li->from->position - li->to->position).magd();
47             }
48         }
49         s += "Layers: ";
50         for (auto layer : layers) {
51             s += core->get_layer_provider().get_layers().at(layer).name + " ";
52         }
53         s += "\nTotal length: " + dim_to_string(length, false);
54         sel_erase_type(sel, ObjectType::LINE);
55     }
56 
57     // Display the length if a single edge of a polygon is given
58     if (auto it = sel_find_exactly_one(sel, ObjectType::POLYGON_EDGE)) {
59         s += "\n\n<b>" + object_descriptions.at(ObjectType::POLYGON_EDGE).name + "</b>\n";
60         const auto li = core->get_polygon(it->uuid);
61         const auto pair = li->get_vertices_for_edge(it->vertex);
62         const int64_t length = (li->vertices.at(pair.first).position - li->vertices.at(pair.second).position).magd();
63         s += "Layer: ";
64         s += core->get_layer_provider().get_layers().at(li->layer).name + " ";
65         s += "\nLength: " + dim_to_string(length, false);
66         if (preferences.hud_debug)
67             s += "\nVertex: " + std::to_string(it->vertex);
68         sel_erase_type(sel, ObjectType::POLYGON_EDGE);
69     }
70 
71     if (auto it = sel_find_exactly_one(sel, ObjectType::POLYGON_VERTEX)) {
72         s += "\n\n<b>" + object_descriptions.at(ObjectType::POLYGON_VERTEX).name + "</b>\n";
73         const auto poly = core->get_polygon(it->uuid);
74         s += coord_to_string(poly->vertices.at(it->vertex).position);
75         if (preferences.hud_debug)
76             s += "\nVertex: " + std::to_string(it->vertex);
77         sel_erase_type(sel, ObjectType::POLYGON_VERTEX);
78     }
79 
80     if (preferences.hud_debug) {
81         if (auto it = sel_find_exactly_one(sel, ObjectType::TEXT)) {
82             const auto txt = core->get_text(it->uuid);
83             s += "\n\n<b>Text:</b>\n";
84             if (txt->overridden) {
85                 s += "Overriden: " + txt->text_override;
86             }
87             else {
88                 s += "Not overridden";
89             }
90         }
91     }
92 
93     if (auto it = sel_find_exactly_one(sel, ObjectType::TEXT)) {
94         const auto text = core->get_text(it->uuid);
95         const auto txt = Glib::ustring(text->text);
96         auto regex = Glib::Regex::create(R"((https?:\/\/|file:\/\/\/?)([\w\.-]+)(\/\S+)?)");
97         Glib::MatchInfo ma;
98         if (regex->match(txt, ma)) {
99             s += "\n\n<b>Text with links</b>\n";
100             do {
101                 auto url = ma.fetch(0);
102                 auto regex_file = Glib::Regex::create(R"(file:\/\/?(\S+\/|)([^\/\s]+))");
103                 Glib::MatchInfo ma_f;
104                 auto name = ma.fetch(2);
105                 if (regex_file->match(url, ma_f)) {
106                     name = ma_f.fetch(2);
107                 }
108                 if (ma.fetch(1).compare("file://") == 0) {
109                     url = url.replace(0, 7, "file://" + Glib::path_get_dirname(core->get_filename()) + "/");
110                 }
111                 s += "<a href=\"" + Glib::Markup::escape_text(url) + "\" title=\""
112                      + Glib::Markup::escape_text(Glib::Markup::escape_text(url)) + "\">" + name + "</a>\n";
113             } while (ma.next());
114             sel_erase_type(sel, ObjectType::TEXT);
115         }
116     }
117 
118     if (preferences.hud_debug) {
119         if (auto it = sel_find_exactly_one(sel, ObjectType::JUNCTION)) {
120             const auto ju = core->get_junction(it->uuid);
121             s += "\n\n<b>Junction:</b>\n";
122             s += "Layers " + std::to_string(ju->layer.start()) + " — " + std::to_string(ju->layer.end()) + "\n";
123             const Net *net = nullptr;
124             if (auto ju_b = dynamic_cast<BoardJunction *>(ju))
125                 net = ju_b->net;
126             if (net)
127                 s += "Net: " + core->get_block()->get_net_name(net->uuid);
128             else
129                 s += "No net";
130             sel_erase_type(sel, ObjectType::JUNCTION);
131         }
132     }
133 
134     // Display the delta if two items of these types are selected
135     for (const ObjectType type : {ObjectType::HOLE, ObjectType::POLYGON_VERTEX, ObjectType::JUNCTION}) {
136         if (sel_count_type(sel, type) == 2) {
137             s += "\n\n<b>2 " + object_descriptions.at(type).name_pl + "</b>";
138             std::vector<Coordi> positions;
139             for (const auto &iter : sel) {
140                 if (iter.type == type) {
141                     if (type == ObjectType::POLYGON_VERTEX) {
142                         const auto poly = core->get_polygon(iter.uuid);
143                         const auto vertex = &poly->vertices.at(iter.vertex);
144                         positions.push_back(vertex->position);
145                     }
146                     else if (type == ObjectType::HOLE) {
147                         const auto hole = core->get_hole(iter.uuid);
148                         positions.push_back(hole->placement.shift);
149                     }
150                     else if (type == ObjectType::JUNCTION) {
151                         const auto junction = core->get_junction(iter.uuid);
152                         positions.push_back(junction->position);
153                     }
154                     else {
155                         assert(false); // unreachable
156                     }
157                 }
158             }
159             assert(positions.size() == 2);
160             const auto delta = positions.at(1) - positions.at(0);
161             s += "\n" + coord_to_string(delta, true);
162             sel_erase_type(sel, type);
163         }
164     }
165 
166     trim(s);
167     if (sel.size()) {
168         s += "\n\n<b>Others:</b>\n";
169         for (const auto &it : object_descriptions) {
170             auto n = std::count_if(sel.begin(), sel.end(), [&it](const auto &a) { return a.type == it.first; });
171             if (n) {
172                 s += std::to_string(n) + " " + it.second.get_name_for_n(n) + "\n";
173             }
174         }
175         sel.clear();
176     }
177     return s;
178 }
179 
get_hud_text_for_net(const Net * net)180 std::string ImpBase::get_hud_text_for_net(const Net *net)
181 {
182     if (!net)
183         return "No net";
184 
185     std::string s = "Net: " + core->get_block()->get_net_name(net->uuid) + "\n";
186     s += "Net class " + net->net_class->name + "\n";
187     if (net->is_power)
188         s += "is power net";
189 
190     trim(s);
191     return s;
192 }
193 
get_hud_text_for_component(const Component * comp)194 std::string ImpBase::get_hud_text_for_component(const Component *comp)
195 {
196     const Part *part = comp->part;
197     if (!part) {
198         std::string s = "No part\n";
199         s += "Entity: " + comp->entity->name;
200         return s;
201     }
202     else {
203         std::string s = "MPN: " + Glib::Markup::escape_text(part->get_MPN()) + "\n";
204         s += "Manufacturer: " + Glib::Markup::escape_text(part->get_manufacturer()) + "\n";
205         s += "Package: " + Glib::Markup::escape_text(part->package->name) + "\n";
206         if (part->get_description().size())
207             s += Glib::Markup::escape_text(part->get_description()) + "\n";
208         if (part->get_datasheet().size())
209             s += "<a href=\"" + Glib::Markup::escape_text(part->get_datasheet()) + "\" title=\""
210                  + Glib::Markup::escape_text(Glib::Markup::escape_text(part->get_datasheet())) + "\">Datasheet</a>\n";
211 
212         const auto block = core->get_block();
213         if (comp->group)
214             s += "Group: " + Glib::Markup::escape_text(block->get_group_name(comp->group)) + "\n";
215         if (comp->tag)
216             s += "Tag: " + Glib::Markup::escape_text(block->get_tag_name(comp->tag)) + "\n";
217 
218         trim(s);
219         return s;
220     }
221 }
222 
223 } // namespace horizon
224