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;
|
break;
|
||||||
}
|
}
|
||||||
case EditTarget::Source: {
|
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());
|
auto* w = qobject_cast<QWidget*>(parent());
|
||||||
QString path = QFileDialog::getOpenFileName(w, "Load Binary Data", {}, "All Files (*)");
|
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")) {
|
else if (text == QStringLiteral("Process")) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
auto* w = qobject_cast<QWidget*>(parent());
|
auto* w = qobject_cast<QWidget*>(parent());
|
||||||
ProcessPicker picker(w);
|
ProcessPicker picker(w);
|
||||||
if (picker.exec() == QDialog::Accepted) {
|
if (picker.exec() == QDialog::Accepted) {
|
||||||
attachToProcess(picker.selectedProcessId(),
|
// Save current source's base address before switching
|
||||||
picker.selectedProcessName());
|
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
|
#endif
|
||||||
}
|
}
|
||||||
@@ -396,6 +457,7 @@ void RcxController::refresh() {
|
|||||||
editor->restoreViewState(vs);
|
editor->restoreViewState(vs);
|
||||||
}
|
}
|
||||||
applySelectionOverlays();
|
applySelectionOverlays();
|
||||||
|
pushSavedSourcesToEditors();
|
||||||
updateCommandRow();
|
updateCommandRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1006,6 +1068,42 @@ void RcxController::attachToProcess(uint32_t pid, const QString& processName) {
|
|||||||
#endif
|
#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,
|
void RcxController::handleMarginClick(RcxEditor* editor, int margin,
|
||||||
int line, Qt::KeyboardModifiers) {
|
int line, Qt::KeyboardModifiers) {
|
||||||
const LineMeta* lm = editor->metaForLine(line);
|
const LineMeta* lm = editor->metaForLine(line);
|
||||||
|
|||||||
@@ -48,6 +48,17 @@ private:
|
|||||||
Command m_cmd;
|
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 ──
|
// ── Controller ──
|
||||||
|
|
||||||
class RcxController : public QObject {
|
class RcxController : public QObject {
|
||||||
@@ -96,10 +107,16 @@ private:
|
|||||||
int m_anchorLine = -1;
|
int m_anchorLine = -1;
|
||||||
bool m_suppressRefresh = false;
|
bool m_suppressRefresh = false;
|
||||||
|
|
||||||
|
// ── Saved sources for quick-switch ──
|
||||||
|
QVector<SavedSourceEntry> m_savedSources;
|
||||||
|
int m_activeSourceIdx = -1;
|
||||||
|
|
||||||
void connectEditor(RcxEditor* editor);
|
void connectEditor(RcxEditor* editor);
|
||||||
void handleMarginClick(RcxEditor* editor, int margin, int line, Qt::KeyboardModifiers mods);
|
void handleMarginClick(RcxEditor* editor, int margin, int line, Qt::KeyboardModifiers mods);
|
||||||
void updateCommandRow();
|
void updateCommandRow();
|
||||||
void attachToProcess(uint32_t pid, const QString& processName);
|
void attachToProcess(uint32_t pid, const QString& processName);
|
||||||
|
void switchToSavedSource(int idx);
|
||||||
|
void pushSavedSourcesToEditors();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rcx
|
} // namespace rcx
|
||||||
|
|||||||
@@ -1453,6 +1453,17 @@ void RcxEditor::showSourcePicker() {
|
|||||||
menu.addAction("File");
|
menu.addAction("File");
|
||||||
menu.addAction("Process");
|
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 lineH = (int)m_sci->SendScintilla(QsciScintillaBase::SCI_TEXTHEIGHT, 0);
|
||||||
int x = (int)m_sci->SendScintilla(QsciScintillaBase::SCI_POINTXFROMPOSITION,
|
int x = (int)m_sci->SendScintilla(QsciScintillaBase::SCI_POINTXFROMPOSITION,
|
||||||
0, m_editState.posStart);
|
0, m_editState.posStart);
|
||||||
@@ -1463,7 +1474,10 @@ void RcxEditor::showSourcePicker() {
|
|||||||
QAction* sel = menu.exec(pos);
|
QAction* sel = menu.exec(pos);
|
||||||
if (sel) {
|
if (sel) {
|
||||||
auto info = endInlineEdit();
|
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 {
|
} else {
|
||||||
cancelInlineEdit();
|
cancelInlineEdit();
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/editor.h
11
src/editor.h
@@ -9,6 +9,11 @@ class QsciLexerCPP;
|
|||||||
|
|
||||||
namespace rcx {
|
namespace rcx {
|
||||||
|
|
||||||
|
struct SavedSourceDisplay {
|
||||||
|
QString text;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
class RcxEditor : public QWidget {
|
class RcxEditor : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
@@ -45,6 +50,9 @@ public:
|
|||||||
// Custom type names (struct types from the tree) shown in type picker
|
// Custom type names (struct types from the tree) shown in type picker
|
||||||
void setCustomTypeNames(const QStringList& names) { m_customTypeNames = names; }
|
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:
|
signals:
|
||||||
void marginClicked(int margin, int line, Qt::KeyboardModifiers mods);
|
void marginClicked(int margin, int line, Qt::KeyboardModifiers mods);
|
||||||
void contextMenuRequested(int line, int nodeIdx, int subLine, QPoint globalPos);
|
void contextMenuRequested(int line, int nodeIdx, int subLine, QPoint globalPos);
|
||||||
@@ -109,6 +117,9 @@ private:
|
|||||||
// ── Custom type names for type picker ──
|
// ── Custom type names for type picker ──
|
||||||
QStringList m_customTypeNames;
|
QStringList m_customTypeNames;
|
||||||
|
|
||||||
|
// ── Saved sources for quick-switch ──
|
||||||
|
QVector<SavedSourceDisplay> m_savedSourceDisplay;
|
||||||
|
|
||||||
// ── Reentrancy guards ──
|
// ── Reentrancy guards ──
|
||||||
bool m_clampingSelection = false;
|
bool m_clampingSelection = false;
|
||||||
bool m_updatingComment = false;
|
bool m_updatingComment = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user