diff --git a/src/frontends/wx/wx_frame.cpp b/src/frontends/wx/wx_frame.cpp index ccc4654..c455602 100644 --- a/src/frontends/wx/wx_frame.cpp +++ b/src/frontends/wx/wx_frame.cpp @@ -12,6 +12,7 @@ #include "core/domain/modules.hpp" #include "core/domain/parts.hpp" #include "core/domain/persist.hpp" +#include "core/domain/pins.hpp" #include "core/domain/signal_type.hpp" #include "core/domain/signals.hpp" #include "core/domain/system.hpp" @@ -24,6 +25,7 @@ #include #include +#include #include #include @@ -44,6 +46,34 @@ enum { // Core (UTF-8 std::string) -> wxString, and back for paths. inline wxString wx(const std::string &s) { return wxString::FromUTF8(s.c_str()); } + +// Natural order ("2" < "10", "A2" < "A10") so pin/part lists read intuitively. +bool natural_less(const std::string &a, const std::string &b) { + size_t i = 0, j = 0; + while (i < a.size() && j < b.size()) { + unsigned char ca = a[i], cb = b[j]; + if (std::isdigit(ca) && std::isdigit(cb)) { + size_t i0 = i, j0 = j; + while (i < a.size() && std::isdigit((unsigned char)a[i])) ++i; + while (j < b.size() && std::isdigit((unsigned char)b[j])) ++j; + std::string na = a.substr(i0, i - i0), nb = b.substr(j0, j - j0); + na.erase(0, na.find_first_not_of('0')); // ignore leading zeros + nb.erase(0, nb.find_first_not_of('0')); + if (na.size() != nb.size()) return na.size() < nb.size(); + if (na != nb) return na < nb; + } else { + if (ca != cb) return ca < cb; + ++i; ++j; + } + } + return a.size() < b.size(); +} + +// " (Power)" / " (Gnd)" — only for the meaningful types; "" for Other. +wxString type_suffix(SignalType t) { + return t == SignalType::Other ? wxString() + : " (" + wxString(signal_type_name(t)) + ")"; +} } // namespace EssimFrame::EssimFrame(WxFrontend &fe) @@ -143,17 +173,54 @@ void EssimFrame::RebuildModelView() { wxTreeItemId mid = tree_->AppendItem( root, wx(mname) + wxString::Format(" — %d part(s), %d signal(s)", mp, ms)); + + // Parts → pins (each pin shows the signal it is wired to, or NC). std::vector parts; for (auto &pkv : *m) parts.push_back(pkv.first); - std::sort(parts.begin(), parts.end()); + std::sort(parts.begin(), parts.end(), natural_less); for (const auto &pname : parts) { Part *p = m->get(pname); wxString label = wx(pname) + wxString::Format(" (%d pin(s))", (int)p->size()); if (!p->connector_type.empty()) label += " [" + wx(p->connector_type) + "]"; - tree_->AppendItem(mid, label); + wxTreeItemId pid = tree_->AppendItem(mid, label); + + std::vector pins; + for (auto &nkv : *p) pins.push_back(nkv.first); + std::sort(pins.begin(), pins.end(), natural_less); + for (const auto &pinname : pins) { + Pin *pin = p->get(pinname); + wxString pl = wx(pinname) + " -> "; + if (Signal *s = pin->signal()) { + pl += wx(s->name) + type_suffix(s->type); + } else { + pl += "(NC"; + if (pin->nc_origin == NcOrigin::ImportedUnconnected) + pl += ", imported"; + else if (pin->nc_origin == NcOrigin::DroppedSingleton) + pl += ", dropped"; + pl += ")"; + } + tree_->AppendItem(pid, pl); + } } + + // Signals branch (the per-module net view: type + fan-out). + if (ms > 0) { + wxTreeItemId sid = + tree_->AppendItem(mid, wxString::Format("Signals (%d)", ms)); + std::vector sigs; + for (auto &skv : *m->signals) sigs.push_back(skv.first); + std::sort(sigs.begin(), sigs.end(), natural_less); + for (const auto &sname : sigs) { + Signal *s = m->signals->get(sname); + tree_->AppendItem(sid, wx(sname) + type_suffix(s->type) + + wxString::Format(" — %d pin(s)", (int)s->size())); + } + } + + tree_->Expand(mid); // parts + Signals visible; pins/nets on demand } } tree_->Expand(root);