From bdce41689afc1a2c2774668badbe209eb599b234 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 18 Oct 2026 22:16:17 +0000
Subject: [PATCH 1/6] Initial plan
From 03c5a2576eae3b3363377fc4d5c1c9e824559c3c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 18 Oct 2026 22:22:57 +0000
Subject: [PATCH 2/6] Fix Godley popup undo and redo history
Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
---
.../src/app/managers/GodleyMenuManager.ts | 14 ++--
.../godley-widget-view.component.ts | 5 ++
model/godleyIcon.cc | 3 +
model/godleyTableWindow.cc | 64 ++++++++++++++++---
test/00/godleyTable.sh | 25 ++++++++
5 files changed, 98 insertions(+), 13 deletions(-)
diff --git a/gui-js/apps/minsky-electron/src/app/managers/GodleyMenuManager.ts b/gui-js/apps/minsky-electron/src/app/managers/GodleyMenuManager.ts
index c837ce598..26a184a4e 100644
--- a/gui-js/apps/minsky-electron/src/app/managers/GodleyMenuManager.ts
+++ b/gui-js/apps/minsky-electron/src/app/managers/GodleyMenuManager.ts
@@ -20,7 +20,7 @@ export class GodleyMenuManager {
const godley = new GodleyIcon(minsky.namedItems.elem(itemInfo.id));
const menu = Menu.buildFromTemplate([
scope.getGodleyFileMenuItem(window, godley),
- scope.getGodleyEditMenuItem(godley),
+ scope.getGodleyEditMenuItem(window, godley),
scope.getGodleyViewMenuItem(window),
scope.getGodleyOptionsMenuItem(window),
new MenuItem({
@@ -188,6 +188,7 @@ export class GodleyMenuManager {
}
private static getGodleyEditMenuItem(
+ window: Electron.BrowserWindow,
godley: GodleyIcon
) {
return new MenuItem({
@@ -196,12 +197,18 @@ export class GodleyMenuManager {
{
label: 'Undo',
accelerator: 'CmdOrCtrl + z',
- click: () => {godley.popup.undo(1);},
+ click: async () => {
+ await godley.popup.undo(1);
+ GodleyMenuManager.refresh(window);
+ },
},
{
label: 'Redo',
accelerator: 'CmdOrCtrl + y',
- click: () => {godley.popup.undo(-1);},
+ click: async () => {
+ await godley.popup.undo(-1);
+ GodleyMenuManager.refresh(window);
+ },
},
{
label: 'Title',
@@ -281,4 +288,3 @@ export class GodleyMenuManager {
menu.popup();
}
}
-
diff --git a/gui-js/libs/ui-components/src/lib/godley-widget-view/godley-widget-view.component.ts b/gui-js/libs/ui-components/src/lib/godley-widget-view/godley-widget-view.component.ts
index c37714693..ef12d4c47 100644
--- a/gui-js/libs/ui-components/src/lib/godley-widget-view/godley-widget-view.component.ts
+++ b/gui-js/libs/ui-components/src/lib/godley-widget-view/godley-widget-view.component.ts
@@ -256,6 +256,7 @@ export class GodleyWidgetViewComponent implements OnDestroy, OnInit, AfterViewIn
async onRowAdd(i) {
await this.finishEditing();
+ await this.namedItemSubCommand.pushHistory();
this.godleyIcon.table.insertRow(i+3);
await this.hardRefresh();
}
@@ -268,24 +269,28 @@ export class GodleyWidgetViewComponent implements OnDestroy, OnInit, AfterViewIn
async onRowMove(i, n) {
await this.finishEditing();
+ await this.namedItemSubCommand.pushHistory();
this.godleyIcon.table.moveRow(i+2,n);
await this.hardRefresh();
}
async onColumnAdd(i) {
await this.finishEditing();
+ await this.namedItemSubCommand.pushHistory();
this.godleyIcon.table.insertCol(i+2);
await this.hardRefresh();
}
async onColumnDelete(i) {
await this.finishEditing();
+ await this.namedItemSubCommand.pushHistory();
this.godleyIcon.table.deleteCol(i+2);
await this.hardRefresh();
}
async onColumnMove(i, n) {
await this.finishEditing();
+ await this.namedItemSubCommand.pushHistory();
this.godleyIcon.table.moveCol(i+1,n);
await this.hardRefresh();
}
diff --git a/model/godleyIcon.cc b/model/godleyIcon.cc
index 7661d5f0b..eff02cb63 100644
--- a/model/godleyIcon.cc
+++ b/model/godleyIcon.cc
@@ -224,6 +224,7 @@ namespace minsky
void GodleyIcon::setCell(int row, int col, const string& newVal)
{
if (!table.cellInTable(row,col)) return;
+ popup.pushHistory();
// if this operation is clearing an initial condition cell, set it to 0
string& c=table.cell(row,col);
if (newVal.empty() && !c.empty() && table.initialConditionRow(row))
@@ -241,6 +242,7 @@ namespace minsky
{
if (row>0 && row<=table.rows())
{
+ popup.pushHistory();
table.deleteRow(row-1);
// if shared column data is deleted, remove it from the other tables too
for (size_t col=1; col
0) m_godleyIcon.table.insertCol(c+1);
+ if (c>0)
+ {
+ pushHistory();
+ m_godleyIcon.table.insertCol(c+1);
+ }
}
void GodleyTableEditor::importStockVar(const string& name, double x)
@@ -870,6 +879,7 @@ namespace minsky
{
if (c>0 && size_t(c)=0)
- m_godleyIcon.table.deleteCol(c+1);
+ {
+ pushHistory();
+ m_godleyIcon.table.deleteCol(c+1);
+ }
}
void GodleyTableEditor::addFlow(double y)
@@ -897,8 +910,11 @@ namespace minsky
void GodleyTableEditor::addFlowByRow(int r)
{
- if (r>0)
- m_godleyIcon.table.insertRow(r+1);
+ if (r>0)
+ {
+ pushHistory();
+ m_godleyIcon.table.insertRow(r+1);
+ }
}
void GodleyTableEditor::deleteFlow(double y)
@@ -910,7 +926,10 @@ namespace minsky
void GodleyTableEditor::deleteFlowByRow(int r)
{
if (r>1) // Cannot delete flow in Initial Conditions row. For ticket 1064
- m_godleyIcon.deleteRow(r+1);
+ {
+ pushHistory();
+ m_godleyIcon.deleteRow(r+1);
+ }
}
namespace {
@@ -1013,25 +1032,52 @@ namespace {
void GodleyTableEditor::pushHistory()
{
- while (history.size()>maxHistory) history.pop_front();
+ if (historyPtr0 && history[historyPtr-1]==m_godleyIcon.table)
+ return;
+ history.erase(history.begin()+historyPtr, history.end());
+ }
// Perform deep comparison of Godley tables in history to avoid spurious noAssetClass columns from arising during undo. For ticket 1118.
if (history.empty() || !(history.back()==m_godleyIcon.table)) {
history.push_back(m_godleyIcon.table);
}
+ while (history.size()>maxHistory)
+ {
+ history.pop_front();
+ if (historyPtr>0) --historyPtr;
+ }
historyPtr=history.size();
}
void GodleyTableEditor::undo(int changes)
{
- if (historyPtr==history.size())
- pushHistory();
- historyPtr-=changes;
+ const bool pendingChange=historyPtr==0 ||
+ !(history[historyPtr-1]==m_godleyIcon.table);
+ if (pendingChange)
+ {
+ pushHistory();
+ if (changes<0) return;
+ }
+ if (changes>0 && static_cast(changes)>=historyPtr)
+ return;
+ if (changes<0 &&
+ static_cast(-static_cast(changes))>
+ history.size()-historyPtr)
+ return;
+ if (changes>0)
+ historyPtr-=changes;
+ else if (changes<0)
+ historyPtr+=-static_cast(changes);
if (historyPtr > 0 && historyPtr <= history.size())
{
auto& d=history[historyPtr-1];
// Perform deep comparison of Godley tables in history to avoid spurious noAssetClass columns from arising during undo. For ticket 1118.
if (d.getData().empty()) return; // should not happen
m_godleyIcon.table=d;
+ m_godleyIcon.update();
+ adjustWidgets();
+ requestRedrawCanvas();
}
}
diff --git a/test/00/godleyTable.sh b/test/00/godleyTable.sh
index b6c20e8af..bb2477eab 100755
--- a/test/00/godleyTable.sh
+++ b/test/00/godleyTable.sh
@@ -75,6 +75,31 @@ assert minsky.canvas.item.table.rowSum(1)=='0'
assert minsky.canvas.item.table.rowSum(2)=='b'
assert minsky.canvas.item.table.getVariables()()==['a', 'b']
+# popup undo/redo tracks cell edits and discards stale redo states
+originalCell = item.table.getCell(2, 1)
+item.setCell(2, 1, 'changed')
+item.popup.undo(1)
+assert item.table.getCell(2, 1)==originalCell
+item.popup.undo(-1)
+assert item.table.getCell(2, 1)=='changed'
+item.popup.undo(1)
+item.setCell(2, 1, 'branched')
+item.popup.undo(-1)
+assert item.table.getCell(2, 1)=='branched'
+item.popup.undo(1)
+assert item.table.getCell(2, 1)==originalCell
+item.popup.undo(-1)
+assert item.table.getCell(2, 1)=='branched'
+
+# row edits made through the popup are also undoable
+rows = item.table.rows()
+item.popup.addFlowByRow(rows-1)
+assert item.table.rows()==rows+1
+item.popup.undo(1)
+assert item.table.rows()==rows
+item.popup.undo(-1)
+assert item.table.rows()==rows+1
+
minsky.canvas.deleteItem()
assert minsky.model.numItems()==0
EOF
From 4268541643f105fc29ebada294e9a875e3db054c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Oct 2026 00:34:59 +0000
Subject: [PATCH 3/6] Keep Godley redo history after table refresh
Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
---
model/godleyTableWindow.cc | 4 +++-
test/00/godleyTable.sh | 9 +++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/model/godleyTableWindow.cc b/model/godleyTableWindow.cc
index 88b9c05e4..d2c6bd230 100644
--- a/model/godleyTableWindow.cc
+++ b/model/godleyTableWindow.cc
@@ -1074,8 +1074,10 @@ namespace {
auto& d=history[historyPtr-1];
// Perform deep comparison of Godley tables in history to avoid spurious noAssetClass columns from arising during undo. For ticket 1118.
if (d.getData().empty()) return; // should not happen
- m_godleyIcon.table=d;
+ const GodleyTable restoredTable=d;
+ m_godleyIcon.table=restoredTable;
m_godleyIcon.update();
+ m_godleyIcon.table=restoredTable;
adjustWidgets();
requestRedrawCanvas();
}
diff --git a/test/00/godleyTable.sh b/test/00/godleyTable.sh
index bb2477eab..5d69abe62 100755
--- a/test/00/godleyTable.sh
+++ b/test/00/godleyTable.sh
@@ -91,6 +91,15 @@ assert item.table.getCell(2, 1)==originalCell
item.popup.undo(-1)
assert item.table.getCell(2, 1)=='branched'
+# updating after undo must not change a restored blank initial condition
+item.popup.pushHistory()
+item.table.setCell(1, 1, '')
+item.setCell(2, 1, 'changed')
+item.popup.undo(1)
+assert item.table.getCell(1, 1)==''
+item.popup.undo(-1)
+assert item.table.getCell(2, 1)=='changed'
+
# row edits made through the popup are also undoable
rows = item.table.rows()
item.popup.addFlowByRow(rows-1)
From dd37bd63a25986e631d8c76664890c5108cfa8ac Mon Sep 17 00:00:00 2001
From: Russell Standish
Date: Tue, 20 Oct 2026 14:34:43 +1100
Subject: [PATCH 4/6] bugfix: undo of godley table clearing initial conditions
should clear stock var's initial conditions.
---
gui-js/package-lock.json | 63 --------------------------------------
model/godleyIcon.cc | 8 ++---
model/godleyIcon.h | 3 +-
model/godleyTableWindow.cc | 3 +-
4 files changed, 7 insertions(+), 70 deletions(-)
diff --git a/gui-js/package-lock.json b/gui-js/package-lock.json
index 01b266512..339a74ff4 100644
--- a/gui-js/package-lock.json
+++ b/gui-js/package-lock.json
@@ -14170,21 +14170,6 @@
}
}
},
- "node_modules/html-encoding-sniffer/node_modules/@noble/hashes": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz",
- "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 20.19.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -16826,21 +16811,6 @@
}
}
},
- "node_modules/jsdom/node_modules/@noble/hashes": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz",
- "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 20.19.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
"node_modules/jsdom/node_modules/lru-cache": {
"version": "11.5.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz",
@@ -24986,21 +24956,6 @@
}
}
},
- "node_modules/whatwg-url/node_modules/@noble/hashes": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz",
- "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 20.19.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
"node_modules/which": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
@@ -25308,24 +25263,6 @@
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"license": "ISC"
},
- "node_modules/yaml": {
- "version": "2.9.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz",
- "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "peer": true,
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/eemeli"
- }
- },
"node_modules/yargs": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz",
diff --git a/model/godleyIcon.cc b/model/godleyIcon.cc
index eff02cb63..508076c33 100644
--- a/model/godleyIcon.cc
+++ b/model/godleyIcon.cc
@@ -304,7 +304,7 @@ namespace minsky
return r;
}
- void GodleyIcon::update()
+ void GodleyIcon::update(bool preserveTable)
{
updateVars(m_stockVars, table.getColumnVariables(), VariableType::stock);
updateVars(m_flowVars, table.getVariables(), VariableType::flow);
@@ -332,16 +332,16 @@ namespace minsky
fc1.coef*=fc.coef;
v.init(fc1.str());
}
- v.godleyOverridden=true;
}
+ else if (preserveTable)
+ v.init("");
else
{
// populate cell with current variable's initial value
const FlowCoef fc(v.init());
table.cell(r,c)=fc.str();
- v.godleyOverridden=true;
}
-
+ v.godleyOverridden=true;
}
if (m_variableDisplay)
diff --git a/model/godleyIcon.h b/model/godleyIcon.h
index 69e52604a..32acd5f82 100644
--- a/model/godleyIcon.h
+++ b/model/godleyIcon.h
@@ -121,7 +121,8 @@ namespace minsky
std::vector summarise() const;
/// updates the variable lists with the Godley table
- void update();
+ /// @param preserveTable - do no populate table's initial conditions from variable init conditions
+ void update(bool preserveTable=false);
GodleyIcon* clone() const override {
auto r=new GodleyIcon(*this);
diff --git a/model/godleyTableWindow.cc b/model/godleyTableWindow.cc
index d2c6bd230..b3e37dd4e 100644
--- a/model/godleyTableWindow.cc
+++ b/model/godleyTableWindow.cc
@@ -1076,8 +1076,7 @@ namespace {
if (d.getData().empty()) return; // should not happen
const GodleyTable restoredTable=d;
m_godleyIcon.table=restoredTable;
- m_godleyIcon.update();
- m_godleyIcon.table=restoredTable;
+ m_godleyIcon.update(true);
adjustWidgets();
requestRedrawCanvas();
}
From dbec42327fafdb03009226b22a543567cedbf26d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Oct 2026 04:01:46 +0000
Subject: [PATCH 5/6] Commit active Godley edit before undo
Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
---
.../src/app/events/electron.events.ts | 9 ++++++++
.../src/app/managers/GodleyMenuManager.ts | 21 ++++++++++---------
.../shared/src/lib/constants/constants.ts | 2 ++
.../godley-widget-view.component.ts | 4 ++++
4 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/gui-js/apps/minsky-electron/src/app/events/electron.events.ts b/gui-js/apps/minsky-electron/src/app/events/electron.events.ts
index 73e725692..34c16da71 100644
--- a/gui-js/apps/minsky-electron/src/app/events/electron.events.ts
+++ b/gui-js/apps/minsky-electron/src/app/events/electron.events.ts
@@ -150,6 +150,15 @@ ipcMain.handle(
}
);
+ipcMain.handle(
+ events.GODLEY_POPUP_UNDO,
+ async (event, payload: { itemId: string; changes: number }) => {
+ const godley = new GodleyIcon(minsky.namedItems.elem(payload.itemId));
+ await godley.popup.undo(payload.changes);
+ event.sender.send(events.GODLEY_POPUP_REFRESH);
+ }
+);
+
ipcMain.on(
events.REFRESH_ALL_GODLEY_POPUPS, async () => {
WindowManager.refreshAllGodleyPopups();
diff --git a/gui-js/apps/minsky-electron/src/app/managers/GodleyMenuManager.ts b/gui-js/apps/minsky-electron/src/app/managers/GodleyMenuManager.ts
index 26a184a4e..8950cfaf3 100644
--- a/gui-js/apps/minsky-electron/src/app/managers/GodleyMenuManager.ts
+++ b/gui-js/apps/minsky-electron/src/app/managers/GodleyMenuManager.ts
@@ -20,7 +20,7 @@ export class GodleyMenuManager {
const godley = new GodleyIcon(minsky.namedItems.elem(itemInfo.id));
const menu = Menu.buildFromTemplate([
scope.getGodleyFileMenuItem(window, godley),
- scope.getGodleyEditMenuItem(window, godley),
+ scope.getGodleyEditMenuItem(window, godley, itemInfo.id),
scope.getGodleyViewMenuItem(window),
scope.getGodleyOptionsMenuItem(window),
new MenuItem({
@@ -189,7 +189,8 @@ export class GodleyMenuManager {
private static getGodleyEditMenuItem(
window: Electron.BrowserWindow,
- godley: GodleyIcon
+ godley: GodleyIcon,
+ itemId: string
) {
return new MenuItem({
label: 'Edit',
@@ -197,18 +198,18 @@ export class GodleyMenuManager {
{
label: 'Undo',
accelerator: 'CmdOrCtrl + z',
- click: async () => {
- await godley.popup.undo(1);
- GodleyMenuManager.refresh(window);
- },
+ click: () => window.webContents?.send(events.GODLEY_POPUP_UNDO_REQUEST, {
+ itemId,
+ changes: 1,
+ }),
},
{
label: 'Redo',
accelerator: 'CmdOrCtrl + y',
- click: async () => {
- await godley.popup.undo(-1);
- GodleyMenuManager.refresh(window);
- },
+ click: () => window.webContents?.send(events.GODLEY_POPUP_UNDO_REQUEST, {
+ itemId,
+ changes: -1,
+ }),
},
{
label: 'Title',
diff --git a/gui-js/libs/shared/src/lib/constants/constants.ts b/gui-js/libs/shared/src/lib/constants/constants.ts
index 5471e1d10..b473fbd87 100644
--- a/gui-js/libs/shared/src/lib/constants/constants.ts
+++ b/gui-js/libs/shared/src/lib/constants/constants.ts
@@ -35,6 +35,8 @@ export const events = {
GET_CURRENT_WINDOW: 'get-current-window',
GET_PREFERENCES: 'get-preferences',
GODLEY_POPUP_REFRESH: 'godley-popup-refresh',
+ GODLEY_POPUP_UNDO: 'godley-popup-undo',
+ GODLEY_POPUP_UNDO_REQUEST: 'godley-popup-undo-request',
GODLEY_VIEW_MOUSEDOWN: 'godley-view-mousedown',
GODLEY_VIEW_IMPORT_STOCK: 'godley-view-import-stock',
HELP_FOR: 'help-for',
diff --git a/gui-js/libs/ui-components/src/lib/godley-widget-view/godley-widget-view.component.ts b/gui-js/libs/ui-components/src/lib/godley-widget-view/godley-widget-view.component.ts
index ef12d4c47..f01e2991d 100644
--- a/gui-js/libs/ui-components/src/lib/godley-widget-view/godley-widget-view.component.ts
+++ b/gui-js/libs/ui-components/src/lib/godley-widget-view/godley-widget-view.component.ts
@@ -104,6 +104,10 @@ export class GodleyWidgetViewComponent implements OnDestroy, OnInit, AfterViewIn
this.windowId = (await this.electronService.getCurrentWindow()).id;
if(!this.canvasMode) {
+ this.electronService.on(events.GODLEY_POPUP_UNDO_REQUEST, async (e, payload) => {
+ await this.finishEditing();
+ await this.electronService.invoke(events.GODLEY_POPUP_UNDO, payload);
+ });
this.electronService.on(events.GODLEY_POPUP_REFRESH, async e => {
await this.hardRefresh();
});
From 486227f2424778ceca42ca168da5a22031307ebb Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 20 Oct 2026 04:03:09 +0000
Subject: [PATCH 6/6] Make Godley cross-column moves a single undo step
Co-authored-by: highperformancecoder <3075825+highperformancecoder@users.noreply.github.com>
---
model/godleyIcon.cc | 12 +++++++++---
model/godleyIcon.h | 1 +
test/testMinsky.cc | 19 +++++++++++++++++++
3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/model/godleyIcon.cc b/model/godleyIcon.cc
index 508076c33..189c792be 100644
--- a/model/godleyIcon.cc
+++ b/model/godleyIcon.cc
@@ -222,9 +222,15 @@ namespace minsky
}
void GodleyIcon::setCell(int row, int col, const string& newVal)
+ {
+ setCellImpl(row, col, newVal, true);
+ }
+
+ void GodleyIcon::setCellImpl(int row, int col, const string& newVal,
+ bool recordHistory)
{
if (!table.cellInTable(row,col)) return;
- popup.pushHistory();
+ if (recordHistory) popup.pushHistory();
// if this operation is clearing an initial condition cell, set it to 0
string& c=table.cell(row,col);
if (newVal.empty() && !c.empty() && table.initialConditionRow(row))
@@ -263,8 +269,8 @@ namespace minsky
// create a copy here, as setCell may resize the array, changing
// what the reference referred to.
const string oldVal=table.cell(args.srcRow, args.srcCol);
- setCell(args.destRow, args.destCol, oldVal);
- setCell(args.srcRow, args.srcCol, "");
+ setCellImpl(args.destRow, args.destCol, oldVal, false);
+ setCellImpl(args.srcRow, args.srcCol, "", false);
}
else // within a column, just move the data without affecting any linked column
{
diff --git a/model/godleyIcon.h b/model/godleyIcon.h
index 32acd5f82..17b5a5b81 100644
--- a/model/godleyIcon.h
+++ b/model/godleyIcon.h
@@ -57,6 +57,7 @@ namespace minsky
}
double titleOffs() const {return !table.title.empty()? 15*zoomFactor(): 0;}
+ void setCellImpl(int row, int col, const string& val, bool recordHistory);
public:
static SVGRenderer svgRenderer; ///< SVG icon to display when not in editor mode
diff --git a/test/testMinsky.cc b/test/testMinsky.cc
index 5e8aa0551..61ff2f006 100644
--- a/test/testMinsky.cc
+++ b/test/testMinsky.cc
@@ -959,6 +959,25 @@ TEST(TensorOps, evalOpEvaluate)
EXPECT_EQ("",godley1.cell(2,2));
}
+ TEST_F(MinskySuite, godleyCrossColumnMoveUndo)
+ {
+ auto g=new GodleyIcon;
+ model->addItem(g);
+ g->table.resize(2,3);
+ g->table.cell(0,1)="a";
+ g->table.cell(0,2)="b";
+ g->table.cell(1,1)="flow";
+ g->popup.pushHistory();
+
+ g->moveCell({1,1,1,2});
+ EXPECT_EQ("",g->table.cell(1,1));
+ EXPECT_EQ("flow",g->table.cell(1,2));
+
+ g->popup.undo(1);
+ EXPECT_EQ("flow",g->table.cell(1,1));
+ EXPECT_EQ("",g->table.cell(1,2));
+ }
+
TEST_F(MinskySuite,godleyRowSums)
{
auto g1=new GodleyIcon; model->addItem(g1);