| 1033 | } |
| 1034 | |
| 1035 | void RcxController::showContextMenu(RcxEditor* editor, int line, int nodeIdx, |
| 1036 | int subLine, const QPoint& globalPos) { |
| 1037 | auto icon = [](const char* name) { return QIcon(QStringLiteral(":/vsicons/%1").arg(name)); }; |
| 1038 | |
| 1039 | const bool hasNode = nodeIdx >= 0 && nodeIdx < m_doc->tree.nodes.size(); |
| 1040 | |
| 1041 | // Selection policy |
| 1042 | if (hasNode) { |
| 1043 | uint64_t clickedId = m_doc->tree.nodes[nodeIdx].id; |
| 1044 | if (!m_selIds.contains(clickedId)) { |
| 1045 | m_selIds.clear(); |
| 1046 | m_selIds.insert(clickedId); |
| 1047 | m_anchorLine = line; |
| 1048 | applySelectionOverlays(); |
| 1049 | } |
| 1050 | } |
| 1051 | |
| 1052 | // Multi-select batch actions at top |
| 1053 | if (hasNode && m_selIds.size() > 1) { |
| 1054 | QMenu menu; |
| 1055 | int count = m_selIds.size(); |
| 1056 | QSet<uint64_t> ids = m_selIds; |
| 1057 | menu.addAction(icon("trash.svg"), QString("Delete %1 nodes").arg(count), [this, ids]() { |
| 1058 | QVector<int> indices; |
| 1059 | for (uint64_t id : ids) { |
| 1060 | int idx = m_doc->tree.indexOfId(id); |
| 1061 | if (idx >= 0) indices.append(idx); |
| 1062 | } |
| 1063 | batchRemoveNodes(indices); |
| 1064 | }); |
| 1065 | menu.addAction(icon("symbol-structure.svg"), QString("Change type of %1 nodes...").arg(count), |
| 1066 | [this, ids]() { |
| 1067 | QStringList types; |
| 1068 | for (const auto& e : kKindMeta) types << e.name; |
| 1069 | bool ok; |
| 1070 | QString sel = QInputDialog::getItem(nullptr, "Change Type", "Type:", |
| 1071 | types, 0, false, &ok); |
| 1072 | if (ok) { |
| 1073 | QVector<int> indices; |
| 1074 | for (uint64_t id : ids) { |
| 1075 | int idx = m_doc->tree.indexOfId(id); |
| 1076 | if (idx >= 0) indices.append(idx); |
| 1077 | } |
| 1078 | batchChangeKind(indices, kindFromString(sel)); |
| 1079 | } |
| 1080 | }); |
| 1081 | menu.exec(globalPos); |
| 1082 | return; |
| 1083 | } |
| 1084 | |
| 1085 | QMenu menu; |
| 1086 | |
| 1087 | // ── Node-specific actions (only when clicking on a node) ── |
| 1088 | if (hasNode) { |
| 1089 | const Node& node = m_doc->tree.nodes[nodeIdx]; |
| 1090 | uint64_t nodeId = node.id; |
| 1091 | uint64_t parentId = node.parentId; |
| 1092 |
nothing calls this directly
no test coverage detected