Commit Graph
77 Commits
Author SHA1 Message Date
aiongg 24e49d18ad Batch: Subtotal puts summary rows below their detail in the same undo step (#2482) 2026-10-25 04:27:21 +00:00
dotCooCoo 118cd15d93 Outline: Settings puts summary rows above and summary columns left of their detail
Data > Outline > Settings now has Summary rows below detail and Summary
columns to right of detail. With one cleared, each group's - / + button
sits on the line before the group, its bracket runs down to the last
detail line, and Hide Detail, Show Detail, the level buttons and
Ungroup mark or clear the collapsed flag on that line. Changing a
direction moves the flags of collapsed groups to their new summary
lines.

- model: Sheet::summary_below and summary_right (on by default, also
  for older JSON).
- xlsx: read and written as sheetPr/outlinePr summaryBelow and
  summaryRight; files that set them no longer lose them on save.
- engine: data.outlineSettings {sheet?, summaryBelow?, summaryRight?},
  undoable; outline::groups_with places the summary line.
- ui: a Settings button in the Data tab's Outline group opens the
  dialog; the outline band follows the setting.
2026-10-24 11:34:35 -07:00
Brandon ThomasandClaude Opus 5.5 7cf44d0fbb Integration and review fixes for #2367, #2369 and #2387
- #2369: its test joins the single ui-egui integration-test binary (tests/it), as main now builds them.
- #2367: a cell set to empty text is a blank entry and doesn't grow the AutoFilter (CodeRabbit).
- #2387: MOD's range check also covers the sign-corrected remainder (CodeRabbit).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:05:43 -04:00
MarekSpodBiedry 599762f534 AutoFilter: grow the range when a value is entered in the row below it (#2342) 2026-10-23 17:36:39 +02:00
aiongg ca1cdd4229 Integration: track t13
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/engine/src/cmd/data.rs
#	crates/xlsx/src/tests/mod.rs
#	docs/chart-parity.md
#	docs/file-format-parity.md
#	docs/function-parity.md
#	docs/gaps.md
#	docs/localization-parity.md
#	docs/target-app-parity.md
2026-10-22 03:05:18 +00:00
aiongg 6dda6be2c6 Integration: track t09
# Conflicts:
#	crates/engine/src/tests.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/pt.rs
#	docs/file-format-parity.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-22 03:04:21 +00:00
aiongg 470a2ae2c1 Merge PR #1765: Sheet protection: obey the Sort and Use AutoFilter boxes
Conflicts:
- crates/engine/src/cmd/data.rs: do_sort keeps #1284's sheet parameter and checks Sorting on it; toggle_filter checks Filtering after #1285's data_target
- crates/engine/src/tests.rs: adjacent tests, both kept
2026-10-22 02:37:16 +00:00
aiongg 6b0411091a Merge PR #1559: Sort and de-duplicate data.textToColumns fixed-width breaks
Conflicts:
- crates/engine/src/cmd/data.rs: adjacent test insertions, both kept
2026-10-22 02:35:56 +00:00
aiongg 524ab89a1a Merge PR #1379: engine: subtotal honours header:false instead of dropping the first data row
Conflicts:
- crates/engine/src/cmd/data.rs: subtotal keeps #1285's data_target and sheet-qualified calls with #1379's header/first logic; grand-total range starts at first; both tests kept (#1299 filter blanks, #1379 subtotal)
2026-10-22 02:35:51 +00:00
aiongg aab44d2c23 Merge PR #1535: engine: sort and remove duplicates move the rows' notes and hyperlinks
# Conflicts:
#	crates/engine/src/tests.rs
2026-10-22 02:35:40 +00:00
aiongg 4fd4d39aa0 Merge PR #1308: engine: refuse removeDuplicates with no valid column instead of deleting all rows
Conflicts:
- crates/engine/src/cmd/data.rs: keeps #1308's column guard after cols is built; drops its later 'let sheet' (#1285 supplies sheet)
2026-10-22 02:35:34 +00:00
aiongg 77b861392a Merge PR #1299: Filter: count empty text and ="" results as (Blanks) 2026-10-22 02:35:26 +00:00
aiongg d691e95f6d Merge PR #1285: Remove Duplicates, Filter and Subtotal: a Sheet! prefix on the range works on that sheet 2026-10-22 02:35:24 +00:00
aiongg 87df1947f9 Merge PR #1284: Sort: a Sheet! prefix on the range sorts that sheet, not the active one 2026-10-22 02:35:21 +00:00
aiongg cd5d21740a Merge PR #1326: Reject a list data validation that has no formula1 (#1303)
Conflicts:
- crates/engine/src/cmd/data.rs: kept both; the missing-formula1 guard comes right after the kind match, before the operator and list_source handling
2026-10-22 02:35:14 +00:00
aiongg 708c82cf2c Integration: #1059 delete_areas accepts a single row or column
home.deleteRows {rows:"2"} and home.deleteColumns {cols:"B"} failed with
"not a range"; delete_areas now parses with parse_lines. The PR's test
covers both.
2026-10-22 02:35:09 +00:00
aiongg 0b573362bc Merge PR #1059: rows/cols parameters: accept a single row or column 2026-10-22 02:34:51 +00:00
aiongg f8f7193f52 Merge PR #1084: Data validation: a list typed without quotes is saved as a quoted list, and an unknown type is refused 2026-10-22 02:34:32 +00:00
Hamza e5aeea7a94 Sheet protection: obey the Sort and Use AutoFilter boxes
`SheetProtection` carries eleven permissions. `format_cells` is checked in
`apply_style`, and an edit to a locked cell is caught after the fact by
`check_protection`, but `sort` and `autofilter` had no reader anywhere outside
`sheet_read.rs`, `sheet_write.rs` and a round-trip test: they only ever
travelled through the file. So a protected sheet could still be sorted, and
Ctrl+Shift+L still put an AutoFilter on it.

`refuse_unless_allowed` sits beside `locked_cell_error`, with three call sites:
`do_sort` (which both Sort… and the A-to-Z buttons come through), `toggle_filter`
and `filter_by`.

Where the box decides is worth knowing. Sorting a range of locked cells was
already refused, by the locked-cell guard rather than the permission, because a
sort moves values between locked cells — which is Excel's rule too ("users
cannot sort ranges that contain locked cells on a protected worksheet,
regardless of this setting"). So the Sort box decides for a range that has been
unlocked, while an AutoFilter changes no cell's contents and the box is the only
thing that ever decided it. The test asserts both, and that an unprotected sheet
still sorts and filters.

Not fixed here: Delete Rows and Delete Columns are refused even when their boxes
are ticked, because deleting removes locked cells and trips the same post-hoc
guard, while Insert Rows and Insert Columns work. Letting the guard know which
operation ran is a change to code every command passes through and wants its own
PR.
2026-10-19 22:30:36 +05:00
Brandon Thomas 8d562e9b56 Merge pull request #1879 from Silvertree2010:fix/h2804-custom-autofilter-is-less-than-l 2026-10-19 12:21:34 -04:00
HaSh3003 5eae222fbc Outline: + shows zero-width hidden columns at the default width (fixes #1714)
Templates hide grouped columns as width="0" hidden="1". Show Detail and the level buttons only cleared hidden, so the columns stayed invisible; they now reset a zero size, as Unhide does.
2026-10-19 18:21:54 +03:00
NetterSilverandClaude Haiku 5.5 4f3853d4a9 Hide blank and text rows for numeric ordering custom filters
Found while testing crates/engine/src/cmd/data.rs:550 (custom_ok, used from apply_filter at :623)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-18 20:25:07 +02:00
NetterSilverandClaude Haiku 5.5 2bb222aea6 Sort and de-duplicate data.textToColumns fixed-width breaks
Found while testing crates/engine/src/cmd/data.rs:826

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 17:48:47 +02:00
midasmatterhorn f8cbfb7cb6 engine: sort and remove duplicates move the rows' notes and hyperlinks
do_sort moved cells, formulas, cell pictures, row heights and floating
object anchors through the sort permutation, and remove_duplicates moved
cells and pictures through the compaction, but neither touched
Sheet::comments or Sheet::hyperlinks (keyed by CellRef): a note that
described one record stayed behind and annotated whatever landed on that
row — 'late payment' on a paid invoice — and a note on a row removed by
Remove Duplicates annotated blank cells. Sheet::comments/hyperlinks are
exactly the range-scoped side data that row and column inserts already
remap and edit.clear already clears, so the omission looks unintended.

Both maps now travel through the same permutation the cells use (the
sort collects before re-inserting, so a swap cannot resurrect an entry
from a destination that is also a source; Remove Duplicates compacts the
kept rows and drops the rest).

Regression test: sort_and_remove_duplicates_move_the_rows_notes_and_links
— on main the note and link stay on their old cells; with the fix they
travel with their record.
2026-10-15 17:40:18 +03:00
midasmatterhorn 2c23b61908 engine: subtotal honours header:false instead of dropping the first data row 2026-10-14 23:51:59 +03:00
MarekSpodBiedry ef894c20e5 Reject a list data validation that has no formula1 (#1303) 2026-10-14 19:31:01 +02:00
midasmatterhorn 450ef1aea0 engine: refuse removeDuplicates with no valid column instead of deleting all rows 2026-10-14 18:01:42 +03:00
MarekSpodBiedry 139098e174 Filter: count empty text and ="" results as (Blanks) (#1294) 2026-10-14 15:44:31 +02:00
Marco Arnulfo 39602e6ce1 Filter and Subtotal: a Sheet! prefix on the range works on that sheet, not the active one (refs #1283) 2026-10-14 13:00:17 +02:00
Marco Arnulfo 13691e95e5 Remove Duplicates: a Sheet! prefix on the range works on that sheet, not the active one (refs #1283) 2026-10-14 12:51:05 +02:00
Marco Arnulfo be1af2d6ac Sort: a Sheet! prefix on the range sorts that sheet, not the active one (fixes #1283) 2026-10-14 12:08:41 +02:00
nihilist-narwhal 9939819ee3 rows/cols parameters: accept a single row or column
home.insertRows {"rows":"3"} and home.insertColumns {"cols":"B"}
failed with "not a range": only spans such as 3:3 or B:B parsed. The
same went for deleting, hiding, sizing and grouping rows and columns.
A single row number or column letter now means that one line.
2026-10-13 06:54:31 -05:00
Brandon Thomas 2f33d43546 Merge main into #591
# Conflicts:
#	crates/engine/src/cmd/data.rs
2026-10-13 00:41:59 -04:00
Brandon Thomas 6dd8f7abe8 Merge main into #796
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests.rs
2026-10-13 00:08:27 -04:00
Brandon Thomas 89f9e24aad Merge pull request #664 from richard1912:fix/top10-filter-threshold 2026-10-12 23:48:43 -04:00
Brandon Thomas 50853e3def Merge pull request #647 from MarekSpodBiedry:issue-508 2026-10-12 23:48:36 -04:00
Brandon Thomas e154960ce3 Merge pull request #602 from MarekSpodBiedry:issue-562 2026-10-12 23:35:51 -04:00
CouchRivandClaude Opus 5.5 1a6979c948 Data validation: refuse an unknown type, operator or error style
data.validation took any `type`, `operator` or `errorStyle` and fell back
to a default: a typo such as `type: "lst"` saved an "any value" rule
(`type="none"`), which validates nothing, and an unknown operator became
`between`. Each is now an error that lists the accepted values; `any`
stays a type, and the operator aliases (`greaterThan`…) still work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:32:31 -04:00
Brandon Thomas 487f736c8f Merge pull request #624 from MarekSpodBiedry:issue-534 2026-10-12 23:21:47 -04:00
CouchRivandClaude Opus 5.5 180928c001 Data validation: a list typed without quotes is saved as a quoted list
data.validation {type: "list", formula1: "Open,In progress,Closed"}, which is
how Excel's Source box takes a list, worked inside GridCraft but was saved as
<formula1>Open,In progress,Closed</formula1>. In a file an inline list is a
string literal, "Open,In progress,Closed" (what Excel writes and what our own
reader expects); unquoted, formula1 reads as a formula of names.

- validation quotes such a source when the rule is created, doubling inner
  quotes, using the test list_items already used to tell a literal list from
  a reference or a name (now is_literal_list). A source typed with = (a
  formula), a reference, a name, a sheet-qualified range and an already quoted
  list are stored as before.
- list_items reads a quoted list as a string literal: "" inside stands for
  one quote, so an item with a quote reads back the same after the change and
  from a file.

Creating the rule is the one place that still knows whether the user typed =,
so the XLSX writer is left alone: it can't tell OFFSET(...) entered as a
formula from a literal list, and a rule read from a file is already quoted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:08:25 -04:00
vangel76andClaude Sonnet 5.5 2be04524b3 Calc: SUBTOTAL(10x) recalculates when Row Height, AutoFit Row Height or Ungroup reveal rows
home.rowHeight, home.autofitRowHeight and data.ungroup change row hidden
state without marking the edit structural, so SUBTOTAL(101-111) kept a stale
value. Found by the command-sequence fuzz (seed 3).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 21:09:27 +02:00
RichardandClaude Opus 5.5 ae54c84bc9 Key the cached Top 10 cutoff by criterion, not column (#661)
Two Top 10 criteria on one column (possible in native JSON files) shared one
cache entry, so the last cutoff overwrote the first. Reported by @derechopiano.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ut3hr3a13szpQdQ7gbYbfc
2026-10-13 00:10:54 +11:00
RichardandClaude Opus 5.5 bf9a202222 Compute the Top 10 filter threshold once per column, not once per row (#661)
apply_filter cloned and sorted the column's numbers for every row it tested,
O(n^2 log n). The threshold is now computed once per criterion with the other
column stats. Adds a test for top/bottom items and percent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ut3hr3a13szpQdQ7gbYbfc
2026-10-12 20:29:00 +11:00
MarekSpodBiedry 5334ae5cbc Keep manually hidden and collapsed rows when removing an AutoFilter (#508) 2026-10-12 08:39:12 +02:00
MarekSpodBiedry 125f460cef Paste Special: copy the validation rule with the Validation and All options (#562) 2026-10-12 08:36:13 +02:00
MarekSpodBiedry 76d3879a94 Fix data.filterBy inside a table installing the sheet AutoFilter instead of toggling the table button (#547) 2026-10-12 08:35:51 +02:00
MarekSpodBiedry c13cda2229 Column sort rejects a zero row key with a parameter error instead of panicking (#534) 2026-10-12 08:31:04 +02:00
MarekSpodBiedry a4c6ac3eff Reject out-of-range numeric filterBy columns instead of overflowing (#573) 2026-10-12 08:27:08 +02:00
Brandon Thomas dcd469d12e Merge pull request #419 from neilcawse/fix/function-fixes
Functions: MODE/VALUE/SUMPRODUCT text args, ADDRESS [Book]Sheet, Excel-style lookup bisection, SUBTOTAL skips filtered rows

# Conflicts:
#	docs/file-format-parity.md
#	docs/function-parity.md
2026-10-11 18:05:41 -04:00
neilcawseandClaude Opus 5.5 53690bace0 Functions: oracle fixes - MODE/VALUE/SUMPRODUCT text args, ADDRESS [Book]Sheet, Excel-style lookup bisection, SUBTOTAL skips filtered rows (table filter criteria kept)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:39:31 -04:00