mirror of
https://github.com/NohamR/Reclass.git
synced 2026-05-10 19:59:21 +00:00
Add saved sources with quick-switch in source picker
Source picker now remembers loaded files and attached processes below a separator with checkmarks. Clicking a saved source instantly switches back to it, preserving base addresses per-source. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -289,18 +289,79 @@ void RcxController::connectEditor(RcxEditor* editor) {
|
||||
break;
|
||||
}
|
||||
case EditTarget::Source: {
|
||||
if (text == QStringLiteral("File")) {
|
||||
if (text.startsWith(QStringLiteral("#saved:"))) {
|
||||
int idx = text.mid(7).toInt();
|
||||
switchToSavedSource(idx);
|
||||
} else if (text == QStringLiteral("File")) {
|
||||
auto* w = qobject_cast<QWidget*>(parent());
|
||||
QString path = QFileDialog::getOpenFileName(w, "Load Binary Data", {}, "All Files (*)");
|
||||
if (!path.isEmpty()) m_doc->loadData(path);
|
||||
if (!path.isEmpty()) {
|
||||
// Save current source's base address before switching
|
||||
if (m_activeSourceIdx >= 0 && m_activeSourceIdx < m_savedSources.size())
|
||||
m_savedSources[m_activeSourceIdx].baseAddress = m_doc->tree.baseAddress;
|
||||
|
||||
m_doc->loadData(path);
|
||||
|
||||
// Check if this file is already saved
|
||||
int existingIdx = -1;
|
||||
for (int i = 0; i < m_savedSources.size(); i++) {
|
||||
if (m_savedSources[i].kind == QStringLiteral("File")
|
||||
&& m_savedSources[i].filePath == path) {
|
||||
existingIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (existingIdx >= 0) {
|
||||
m_activeSourceIdx = existingIdx;
|
||||
m_doc->tree.baseAddress = m_savedSources[existingIdx].baseAddress;
|
||||
} else {
|
||||
SavedSourceEntry entry;
|
||||
entry.kind = QStringLiteral("File");
|
||||
entry.displayName = QFileInfo(path).fileName();
|
||||
entry.filePath = path;
|
||||
entry.baseAddress = m_doc->tree.baseAddress;
|
||||
m_savedSources.append(entry);
|
||||
m_activeSourceIdx = m_savedSources.size() - 1;
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
else if (text == QStringLiteral("Process")) {
|
||||
#ifdef _WIN32
|
||||
auto* w = qobject_cast<QWidget*>(parent());
|
||||
ProcessPicker picker(w);
|
||||
if (picker.exec() == QDialog::Accepted) {
|
||||
attachToProcess(picker.selectedProcessId(),
|
||||
picker.selectedProcessName());
|
||||
// Save current source's base address before switching
|
||||
if (m_activeSourceIdx >= 0 && m_activeSourceIdx < m_savedSources.size())
|
||||
m_savedSources[m_activeSourceIdx].baseAddress = m_doc->tree.baseAddress;
|
||||
|
||||
uint32_t pid = picker.selectedProcessId();
|
||||
QString procName = picker.selectedProcessName();
|
||||
attachToProcess(pid, procName);
|
||||
|
||||
// Check if this process is already saved
|
||||
int existingIdx = -1;
|
||||
for (int i = 0; i < m_savedSources.size(); i++) {
|
||||
if (m_savedSources[i].kind == QStringLiteral("Process")
|
||||
&& m_savedSources[i].pid == pid) {
|
||||
existingIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (existingIdx >= 0) {
|
||||
m_activeSourceIdx = existingIdx;
|
||||
m_savedSources[existingIdx].baseAddress = m_doc->tree.baseAddress;
|
||||
} else {
|
||||
SavedSourceEntry entry;
|
||||
entry.kind = QStringLiteral("Process");
|
||||
entry.displayName = procName;
|
||||
entry.pid = pid;
|
||||
entry.processName = procName;
|
||||
entry.baseAddress = m_doc->tree.baseAddress;
|
||||
m_savedSources.append(entry);
|
||||
m_activeSourceIdx = m_savedSources.size() - 1;
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -396,6 +457,7 @@ void RcxController::refresh() {
|
||||
editor->restoreViewState(vs);
|
||||
}
|
||||
applySelectionOverlays();
|
||||
pushSavedSourcesToEditors();
|
||||
updateCommandRow();
|
||||
}
|
||||
|
||||
@@ -1006,6 +1068,42 @@ void RcxController::attachToProcess(uint32_t pid, const QString& processName) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void RcxController::switchToSavedSource(int idx) {
|
||||
if (idx < 0 || idx >= m_savedSources.size()) return;
|
||||
if (idx == m_activeSourceIdx) return;
|
||||
|
||||
// Save current source's base address before switching
|
||||
if (m_activeSourceIdx >= 0 && m_activeSourceIdx < m_savedSources.size())
|
||||
m_savedSources[m_activeSourceIdx].baseAddress = m_doc->tree.baseAddress;
|
||||
|
||||
m_activeSourceIdx = idx;
|
||||
const auto& entry = m_savedSources[idx];
|
||||
|
||||
if (entry.kind == QStringLiteral("File")) {
|
||||
m_doc->loadData(entry.filePath);
|
||||
m_doc->tree.baseAddress = entry.baseAddress;
|
||||
refresh();
|
||||
} else if (entry.kind == QStringLiteral("Process")) {
|
||||
#ifdef _WIN32
|
||||
attachToProcess(entry.pid, entry.processName);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void RcxController::pushSavedSourcesToEditors() {
|
||||
QVector<SavedSourceDisplay> display;
|
||||
display.reserve(m_savedSources.size());
|
||||
for (int i = 0; i < m_savedSources.size(); i++) {
|
||||
SavedSourceDisplay d;
|
||||
d.text = QStringLiteral("%1 '%2'")
|
||||
.arg(m_savedSources[i].kind, m_savedSources[i].displayName);
|
||||
d.active = (i == m_activeSourceIdx);
|
||||
display.append(d);
|
||||
}
|
||||
for (auto* editor : m_editors)
|
||||
editor->setSavedSources(display);
|
||||
}
|
||||
|
||||
void RcxController::handleMarginClick(RcxEditor* editor, int margin,
|
||||
int line, Qt::KeyboardModifiers) {
|
||||
const LineMeta* lm = editor->metaForLine(line);
|
||||
|
||||
@@ -48,6 +48,17 @@ private:
|
||||
Command m_cmd;
|
||||
};
|
||||
|
||||
// ── Saved source entry ──
|
||||
|
||||
struct SavedSourceEntry {
|
||||
QString kind; // "File" or "Process"
|
||||
QString displayName; // filename or process name
|
||||
QString filePath; // for File sources
|
||||
uint32_t pid = 0; // for Process sources
|
||||
QString processName; // for Process sources
|
||||
uint64_t baseAddress = 0;
|
||||
};
|
||||
|
||||
// ── Controller ──
|
||||
|
||||
class RcxController : public QObject {
|
||||
@@ -96,10 +107,16 @@ private:
|
||||
int m_anchorLine = -1;
|
||||
bool m_suppressRefresh = false;
|
||||
|
||||
// ── Saved sources for quick-switch ──
|
||||
QVector<SavedSourceEntry> m_savedSources;
|
||||
int m_activeSourceIdx = -1;
|
||||
|
||||
void connectEditor(RcxEditor* editor);
|
||||
void handleMarginClick(RcxEditor* editor, int margin, int line, Qt::KeyboardModifiers mods);
|
||||
void updateCommandRow();
|
||||
void attachToProcess(uint32_t pid, const QString& processName);
|
||||
void switchToSavedSource(int idx);
|
||||
void pushSavedSourcesToEditors();
|
||||
};
|
||||
|
||||
} // namespace rcx
|
||||
|
||||
@@ -1453,6 +1453,17 @@ void RcxEditor::showSourcePicker() {
|
||||
menu.addAction("File");
|
||||
menu.addAction("Process");
|
||||
|
||||
// Saved sources below separator (with checkmarks)
|
||||
if (!m_savedSourceDisplay.isEmpty()) {
|
||||
menu.addSeparator();
|
||||
for (int i = 0; i < m_savedSourceDisplay.size(); i++) {
|
||||
auto* act = menu.addAction(m_savedSourceDisplay[i].text);
|
||||
act->setCheckable(true);
|
||||
act->setChecked(m_savedSourceDisplay[i].active);
|
||||
act->setData(i);
|
||||
}
|
||||
}
|
||||
|
||||
int lineH = (int)m_sci->SendScintilla(QsciScintillaBase::SCI_TEXTHEIGHT, 0);
|
||||
int x = (int)m_sci->SendScintilla(QsciScintillaBase::SCI_POINTXFROMPOSITION,
|
||||
0, m_editState.posStart);
|
||||
@@ -1463,7 +1474,10 @@ void RcxEditor::showSourcePicker() {
|
||||
QAction* sel = menu.exec(pos);
|
||||
if (sel) {
|
||||
auto info = endInlineEdit();
|
||||
emit inlineEditCommitted(info.nodeIdx, info.subLine, info.target, sel->text());
|
||||
QString text = sel->text();
|
||||
if (sel->data().isValid())
|
||||
text = QStringLiteral("#saved:") + QString::number(sel->data().toInt());
|
||||
emit inlineEditCommitted(info.nodeIdx, info.subLine, info.target, text);
|
||||
} else {
|
||||
cancelInlineEdit();
|
||||
}
|
||||
|
||||
11
src/editor.h
11
src/editor.h
@@ -9,6 +9,11 @@ class QsciLexerCPP;
|
||||
|
||||
namespace rcx {
|
||||
|
||||
struct SavedSourceDisplay {
|
||||
QString text;
|
||||
bool active = false;
|
||||
};
|
||||
|
||||
class RcxEditor : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@@ -45,6 +50,9 @@ public:
|
||||
// Custom type names (struct types from the tree) shown in type picker
|
||||
void setCustomTypeNames(const QStringList& names) { m_customTypeNames = names; }
|
||||
|
||||
// Saved sources for quick-switch in source picker
|
||||
void setSavedSources(const QVector<SavedSourceDisplay>& sources) { m_savedSourceDisplay = sources; }
|
||||
|
||||
signals:
|
||||
void marginClicked(int margin, int line, Qt::KeyboardModifiers mods);
|
||||
void contextMenuRequested(int line, int nodeIdx, int subLine, QPoint globalPos);
|
||||
@@ -109,6 +117,9 @@ private:
|
||||
// ── Custom type names for type picker ──
|
||||
QStringList m_customTypeNames;
|
||||
|
||||
// ── Saved sources for quick-switch ──
|
||||
QVector<SavedSourceDisplay> m_savedSourceDisplay;
|
||||
|
||||
// ── Reentrancy guards ──
|
||||
bool m_clampingSelection = false;
|
||||
bool m_updatingComment = false;
|
||||
|
||||
Reference in New Issue
Block a user