Skip to content

Commit

Permalink
[FIX] clipboard: invalidate clipboard when inserting col/row before c…
Browse files Browse the repository at this point in the history
…ut zone

Adding or removing col/rows before/inside a cut area will now invalidate
the clipboard. 266221d only fixed col/rows inserted inside the cut area.

Ideally we would want to adapt the clipboard zone instead of invalidating
the clipboard, but it's a bigger change that won't be done in 15.0 (task 2712533).

Odoo task 2862400

closes odoo/o-spreadsheet#1431

X-original-commit: c0d8ae7e3914a9cf89a4ca8593f067db01fa3c3f
Signed-off-by: Pierre Rousseau (pro) <[email protected]>
  • Loading branch information
Topdev97 committed Jun 14, 2022
1 parent 17c9463 commit 5573bef
Show file tree
Hide file tree
Showing 2 changed files with 223 additions and 73 deletions.
62 changes: 35 additions & 27 deletions src/plugins/ui/clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,35 @@ export class ClipboardPlugin extends UIPlugin {
break;
}
case "ADD_COLUMNS_ROWS": {
// If we add a col/row inside a cut clipped area, we invalidate the clipboard
const isClipboardDirty = this.isAddRowColDirtyingClipboard(
cmd.base,
cmd.position,
this.status = "invisible";

// If we add a col/row inside or before the cut area, we invalidate the clipboard
if (this.state?.operation !== "CUT") {
return;
}
const isClipboardDirty = this.isColRowDirtyingClipboard(
cmd.position === "before" ? cmd.base : cmd.base + 1,
cmd.dimension
);
if (this.state && this.state.operation == "CUT" && isClipboardDirty) {
if (isClipboardDirty) {
this.state = undefined;
}
break;
}
case "REMOVE_COLUMNS_ROWS": {
this.status = "invisible";

// If we remove a col/row inside or before the cut area, we invalidate the clipboard
if (this.state?.operation !== "CUT") {
return;
}
for (let el of cmd.elements) {
const isClipboardDirty = this.isColRowDirtyingClipboard(el, cmd.dimension);
if (isClipboardDirty) {
this.state = undefined;
break;
}
}
this.status = "invisible";
break;
}
Expand Down Expand Up @@ -634,31 +654,19 @@ export class ClipboardPlugin extends UIPlugin {
}

/**
* Check if a ADD_COLUMNS_ROWS command is affecting an area inside the clipboard
* Check if a col/row added/removed at the given position is dirtying the clipboard
*/
private isAddRowColDirtyingClipboard(
base: number,
position: "before" | "after",
dimension: Dimension
) {
private isColRowDirtyingClipboard(position: number, dimension: Dimension) {
if (!this.state || !this.state.zones) return false;
const sheet = this.getters.getActiveSheet();
const affectedZone: Zone = {
top: 0,
bottom: sheet.rows.length - 1,
left: 0,
right: sheet.cols.length - 1,
};
if (dimension === "COL") {
const affectedCol = position === "before" ? base - 1 : base + 1;
affectedZone.left = affectedCol;
affectedZone.right = affectedCol;
} else {
const affectedRow = position === "before" ? base - 1 : base + 1;
affectedZone.top = affectedRow;
affectedZone.bottom = affectedRow;
for (let zone of this.state.zones) {
if (dimension === "COL" && position <= zone.right) {
return true;
}
if (dimension === "ROW" && position <= zone.bottom) {
return true;
}
}
return this.isZoneOverlapClippedZone(this.state.zones, affectedZone);
return false;
}

// ---------------------------------------------------------------------------
Expand Down
234 changes: 188 additions & 46 deletions tests/plugins/clipboard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1625,59 +1625,201 @@ describe("clipboard: pasting outside of sheet", () => {
expect(getCellContent(model, "D3")).toBe("Patrick");
});

test("adding a column inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "B1", "2");
describe("add col/row can invalidate the clipboard of cut", () => {
test("adding a column before a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "B1", "2");

model.dispatch("CUT", { target: target("A1:B1") });
addColumns(model, "after", "A", 1);
model.dispatch("PASTE", { target: [toZone("A2")] });
expect(getCellContent(model, "A1")).toBe("1");
expect(getCellContent(model, "C1")).toBe("2");
expect(getCellContent(model, "A2")).toBe("");
expect(getCellContent(model, "C2")).toBe("");
});
model.dispatch("CUT", { target: target("A1:B1") });
addColumns(model, "before", "A", 1);
model.dispatch("PASTE", { target: [toZone("A2")] });
expect(getCellContent(model, "B1")).toBe("1");
expect(getCellContent(model, "C1")).toBe("2");
expect(getCellContent(model, "A2")).toBe("");
expect(getCellContent(model, "B2")).toBe("");
expect(getCellContent(model, "C2")).toBe("");
});

test("adding multipe columns inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "B1", "2");
test("adding a column after a cut zone is not invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "B1", "2");

model.dispatch("CUT", { target: target("A1:B1") });
addColumns(model, "after", "A", 5);
model.dispatch("PASTE", { target: [toZone("A2")] });
expect(getCellContent(model, "A1")).toBe("1");
expect(getCellContent(model, "G1")).toBe("2");
expect(getCellContent(model, "A2")).toBe("");
expect(getCellContent(model, "C2")).toBe("");
});
model.dispatch("CUT", { target: target("A1:B1") });
addColumns(model, "after", "B", 1);
model.dispatch("PASTE", { target: [toZone("A2")] });
expect(getCellContent(model, "A1")).toBe("");
expect(getCellContent(model, "B1")).toBe("");
expect(getCellContent(model, "A2")).toBe("1");
expect(getCellContent(model, "B2")).toBe("2");
});

test("adding a row inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "A2", "2");
test("adding a column inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "B1", "2");

model.dispatch("CUT", { target: target("A1:A2") });
addRows(model, "after", 0, 1);
model.dispatch("PASTE", { target: [toZone("C1")] });
expect(getCellContent(model, "A1")).toBe("1");
expect(getCellContent(model, "A3")).toBe("2");
expect(getCellContent(model, "C1")).toBe("");
expect(getCellContent(model, "C3")).toBe("");
model.dispatch("CUT", { target: target("A1:B1") });
addColumns(model, "after", "A", 1);
model.dispatch("PASTE", { target: [toZone("A2")] });
expect(getCellContent(model, "A1")).toBe("1");
expect(getCellContent(model, "C1")).toBe("2");
expect(getCellContent(model, "A2")).toBe("");
expect(getCellContent(model, "C2")).toBe("");
});

test("adding multipe columns inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "B1", "2");

model.dispatch("CUT", { target: target("A1:B1") });
addColumns(model, "after", "A", 5);
model.dispatch("PASTE", { target: [toZone("A2")] });
expect(getCellContent(model, "A1")).toBe("1");
expect(getCellContent(model, "G1")).toBe("2");
expect(getCellContent(model, "A2")).toBe("");
expect(getCellContent(model, "C2")).toBe("");
});

test("adding a row before a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "A2", "2");

model.dispatch("CUT", { target: target("A1:A2") });
addRows(model, "before", 0, 1);
model.dispatch("PASTE", { target: [toZone("C1")] });
expect(getCellContent(model, "A2")).toBe("1");
expect(getCellContent(model, "A3")).toBe("2");
expect(getCellContent(model, "C1")).toBe("");
expect(getCellContent(model, "C2")).toBe("");
expect(getCellContent(model, "C3")).toBe("");
});

test("adding a row after a cut zone is not invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "A2", "2");

model.dispatch("CUT", { target: target("A1:A2") });
addRows(model, "after", 2, 1);
model.dispatch("PASTE", { target: [toZone("C1")] });
expect(getCellContent(model, "A1")).toBe("");
expect(getCellContent(model, "A2")).toBe("");
expect(getCellContent(model, "C1")).toBe("1");
expect(getCellContent(model, "C2")).toBe("2");
});

test("adding a row inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "A2", "2");

model.dispatch("CUT", { target: target("A1:A2") });
addRows(model, "after", 0, 1);
model.dispatch("PASTE", { target: [toZone("C1")] });
expect(getCellContent(model, "A1")).toBe("1");
expect(getCellContent(model, "A3")).toBe("2");
expect(getCellContent(model, "C1")).toBe("");
expect(getCellContent(model, "C3")).toBe("");
});

test("adding multiple rows inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "A2", "2");

model.dispatch("CUT", { target: target("A1:A2") });
addRows(model, "after", 0, 5);
model.dispatch("PASTE", { target: [toZone("C1")] });
expect(getCellContent(model, "A1")).toBe("1");
expect(getCellContent(model, "A7")).toBe("2");
expect(getCellContent(model, "C1")).toBe("");
expect(getCellContent(model, "C3")).toBe("");
});
});

test("adding multiple rows inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "A1", "1");
setCellContent(model, "A2", "2");
describe("remove col/row can invalidate the clipboard of cut", () => {
test("removing a column before a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "B2", "1");
setCellContent(model, "C2", "2");

model.dispatch("CUT", { target: target("A1:A2") });
addRows(model, "after", 0, 5);
model.dispatch("PASTE", { target: [toZone("C1")] });
expect(getCellContent(model, "A1")).toBe("1");
expect(getCellContent(model, "A7")).toBe("2");
expect(getCellContent(model, "C1")).toBe("");
expect(getCellContent(model, "C3")).toBe("");
model.dispatch("CUT", { target: target("B2:C2") });
deleteColumns(model, ["A"]);
model.dispatch("PASTE", { target: [toZone("D1")] });
expect(getCellContent(model, "A2")).toBe("1");
expect(getCellContent(model, "B2")).toBe("2");
expect(getCellContent(model, "D1")).toBe("");
expect(getCellContent(model, "E1")).toBe("");
});

test("removing a column after a cut zone is not invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "B2", "1");
setCellContent(model, "C2", "2");

model.dispatch("CUT", { target: target("B2:C2") });
deleteColumns(model, ["D"]);
model.dispatch("PASTE", { target: [toZone("D1")] });
expect(getCellContent(model, "B2")).toBe("");
expect(getCellContent(model, "C2")).toBe("");
expect(getCellContent(model, "D1")).toBe("1");
expect(getCellContent(model, "E1")).toBe("2");
});

test("removing a column inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "B2", "1");
setCellContent(model, "C2", "2");

model.dispatch("CUT", { target: target("B2:C2") });
deleteColumns(model, ["C"]);
model.dispatch("PASTE", { target: [toZone("D1")] });
expect(getCellContent(model, "B2")).toBe("1");
expect(getCellContent(model, "D1")).toBe("");
});

test("removing a row before a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "B2", "1");
setCellContent(model, "C2", "2");

model.dispatch("CUT", { target: target("B2:C2") });
deleteRows(model, [0]);
model.dispatch("PASTE", { target: [toZone("D1")] });
expect(getCellContent(model, "B1")).toBe("1");
expect(getCellContent(model, "C1")).toBe("2");
expect(getCellContent(model, "D1")).toBe("");
expect(getCellContent(model, "E1")).toBe("");
});

test("removing a row after a cut zone is not invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "B2", "1");
setCellContent(model, "C2", "2");

model.dispatch("CUT", { target: target("B2:C2") });
deleteRows(model, [3]);
model.dispatch("PASTE", { target: [toZone("D1")] });
expect(getCellContent(model, "B2")).toBe("");
expect(getCellContent(model, "C2")).toBe("");
expect(getCellContent(model, "D1")).toBe("1");
expect(getCellContent(model, "E1")).toBe("2");
});

test("removing a row inside a cut zone is invalidating the clipboard", () => {
const model = new Model();
setCellContent(model, "B2", "1");
setCellContent(model, "B3", "2");

model.dispatch("CUT", { target: target("B2:B3") });
deleteRows(model, [2]);
model.dispatch("PASTE", { target: [toZone("D1")] });
expect(getCellContent(model, "B2")).toBe("1");
expect(getCellContent(model, "D1")).toBe("");
});
});
});

0 comments on commit 5573bef

Please sign in to comment.