Commit Graph
2215 Commits
Author SHA1 Message Date
Brandon Thomas db4fd82595 Merge pull request #612 from MarekSpodBiedry:issue-546 2026-10-12 23:21:33 -04:00
Brandon Thomas a85638ddd1 Merge pull request #611 from MarekSpodBiedry:issue-547 2026-10-12 23:21:32 -04:00
Brandon Thomas 4164ca557e Merge pull request #609 from MarekSpodBiedry:issue-552 2026-10-12 23:21:30 -04:00
Brandon Thomas 51b1b031d4 Merge pull request #608 from MarekSpodBiedry:issue-554 2026-10-12 23:21:29 -04:00
Brandon Thomas 28c5bf1b06 Merge pull request #607 from MarekSpodBiedry:issue-556 2026-10-12 23:21:28 -04:00
Brandon Thomas cd985b7d49 Merge pull request #606 from MarekSpodBiedry:issue-557 2026-10-12 23:21:27 -04:00
Brandon Thomas 8f99be0f2e Merge pull request #605 from MarekSpodBiedry:issue-558 2026-10-12 23:21:25 -04:00
Marco Arnulfo 522ede0fca Save an XLSX opened under an unknown extension back to its own file as XLSX (#878) 2026-10-13 05:14:30 +02: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
CouchRivandClaude Opus 5.5 151a279da8 Conditional formats: hex colours in a custom style, no silently empty rules
home.conditionalFormat deserialized `style` straight into the model's
Style, so a programmatic caller had to write colours as {"Rgb": 16773836}:
"#FFF2CC", which format_range, colorScale and dataBar take, was refused
with a serde message naming internal types. A key the struct doesn't have
(`style: {preset: …}`, `fillColor`) was dropped, which saved a rule with an
empty format that matches and shows nothing. And a cellIs `value` already
written as a string literal ("\"Closed\"", as in the file and openpyxl)
was quoted again into the text "Closed" with its quotes, so it never
matched.

`style` colours (font, fill fg/bg, borders) may now be "#RRGGBB",
{theme, tint} or "auto", and `fill` may be a colour alone (a solid fill);
a fill colour with no pattern is solid. Unknown keys and a non-object
`style` are refused. A cellIs value that is one string literal is kept.
The model's own shape keeps working.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 23:00:09 -04:00
CouchRivandClaude Opus 5.5 9a816f80f7 Format: Row Height and Column Width say pixels, and take points like Excel
home.rowHeight and home.columnWidth take pixels at 96 dpi, the model's unit
(header drags and the Row Height dialog send pixels), but their parameter docs
said points. An agent asking for a 30 pt row got `ht="22.5"` in the file, and
the range check accepted 500 while its message said "between 0 and 409 points".

- The params docs say pixels: `height: 20 (pixels) | points: 15`,
  `width: 64 (pixels) | chars: 8.43`, and `{width: 64 (pixels)}` for
  home.defaultWidth. Pixel values behave as before.
- home.rowHeight takes `points`, Excel's unit (Row Height dialog, `ht` in the
  file), as home.columnWidth already takes `chars`.
- Range errors give the limit in the unit passed: 546 pixels (409.5 points),
  409.5 points, 1800 pixels, 255 characters. `chars` is checked against 255,
  Excel's limit and the widest column a file holds (it went to 256.4 through
  the pixel limit).
- The model's size comments say pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:54:25 -04:00
CouchRivandClaude Opus 5.5 46b2fe2f7a Row, column and freeze commands act on the sheet they are given
view.freezePanes, home.rowHeight, home.columnWidth, the AutoFit, Hide and
Unhide row/column commands and home.defaultWidth always changed the active
sheet: a `sheet` parameter was silently ignored, so a programmatic caller
laying out a sheet that isn't active changed the wrong one, and `Sheet!`
prefixes on `rows`/`cols` were refused as "not a range".

They now take `sheet` (name or index) like the commands that already use
target_sheet, accept a `Sheet!` prefix on `rows`/`cols` (and on Freeze
Panes' `cell`, which was refused), and refuse a prefix that contradicts
`sheet`. The selection belongs to the active sheet, so another sheet needs
explicit lines or a cell. Freeze Top Row, Freeze First Column and Unfreeze
Panes take `sheet` too. Without `sheet`, nothing changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 22:50:48 -04:00
Marco Arnulfo 9bb85ab9af Refuse to save to an unknown file extension instead of writing XLSX under it (fixes #878) 2026-10-13 04:34:03 +02:00
neilcawse 95012e03b4 UI: a cut's text pastes again once the clipboard has held other text 2026-10-12 21:19:05 -04:00
Marco Arnulfo dab4aa5e79 XLSX: write this-row structured references as [[#This Row],[Col]] (fixes #864)
[@Col] is only Excel's display shorthand. Written verbatim into cell formulas and calculatedColumnFormula, it made Excel repair the file and drop the tables.
2026-10-13 02:58:56 +02:00
nihilist-narwhal 34b39e2f3c Merge remote-tracking branch 'origin/main' into fix-function-name-arguments-m 2026-10-12 19:57:06 -05:00
nihilist-narwhal c930bf7872 Merge Across: keep only the first value of each row
Merge & Center and Merge Cells kept only the upper-left value, but
Merge Across left the other values of each row in place, hidden under
the merge and still counted: with 1 2 / 3 4 in A1:B2, SUM(A1:B2) stayed
10 after Merge Across, where Excel gives 4. Each row's merged area now
keeps only its first value, as the other merges do.
2026-10-12 19:52:59 -05:00
nihilist-narwhal 9da4e4cdf7 Manage Rules: don't index past the list after a delete
home.manageRules took the number of rules once, before deleting, so
{"delete":0,"moveUp":1} with two rules swapped past the end of the
shortened list and panicked (index out of bounds; the session's guard
kept the workbook). The count is now taken after each step.
2026-10-12 19:52:59 -05:00
nihilist-narwhal 6b6aa82be1 Long operator chains: evaluate them without the depth limit
A formula such as =A1+A2+…+A500 nests one level per operator, and the
evaluator recursed once per level, so past its depth limit (about 400
operators) the result was #CALC!. Excel allows such formulas up to its
8,192-character limit. Chains of arithmetic, comparison and & operators
are now folded in a loop.
2026-10-12 19:52:59 -05:00
nihilist-narwhal b5a82554a9 Fill Series: a falling growth series stops at its stop value
A growth series with a step below 1 stopped at once when given a stop
value, since the stop was only checked for rising series. It now bounds
the series in the direction it runs.
2026-10-12 19:52:59 -05:00
nihilist-narwhal 56617b07af AutoFill: times, quarters, ordinals and mixed sources
Following Excel's AutoFill series:
- a single time steps by an hour (9:00, 10:00, 11:00); it was copied.
- Qtr3 and Quarter3 wrap after 4 like Q3; they counted on to Qtr5.
- 1st Period continues as 2nd Period, 3rd Period; it gave 2st, 3st.
- a source without a common pattern (Product 1, On backorder) continues
  each cell's own series; it was copied as is.
2026-10-12 19:52:59 -05:00
neilcawseandClaude Opus 5.5 317ec4388c XLS: array formulas fill their range on recalculation
A legacy array formula in a .xls file ({=FREQUENCY(...)}, {=TREND(...)}) is
stored as one ARRAY record plus a FORMULA record per cell of its range, each
with the cached result. The importer attached the formula to the anchor cell
but kept the cached values in the other cells as plain constants. On
recalculation the formula then found its range occupied and returned #SPILL!.
The .xlsx reader already drops those cached results; the .xls reader now does
the same once the formula is attached, and keeps the values when the formula
could not be imported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:52:03 -04:00
neilcawse 4d654c5185 UI: Cmd/Ctrl+V doesn't re-paste a pasted cut's clipboard text either 2026-10-12 20:51:39 -04:00
neilcawse fd1ed6649b UI: Paste doesn't re-paste a pasted cut's clipboard text; Paste keytip (H,V,P) pastes the system clipboard 2026-10-12 20:51:39 -04:00
neilcawse cc65915d16 UI: keytip Copy/Cut publish to the system clipboard; ribbon Paste keeps a newer programmatic copy 2026-10-12 20:51:39 -04:00
neilcawseandClaude Opus 5.5 fa1dcd73e2 UI: Paste button and menus paste the system clipboard; freeze regression test (#273)
In 0.5.0 a click anywhere on the Home ribbon's Paste button (label,
icon or dropdown arrow) froze the app for good. home() called
paste.secondary_clicked() inside ctx.data_mut(); after any click egui
answers that with ctx.input(), which takes the context lock the closure
already holds, so the UI thread waited on itself. Sampling the hung
0.5.0 process on macOS shows the main thread parked in
RawRwLock::lock_exclusive_slow under Context::write <- data_mut <-
ribbon::home. #272 moved the query out of the closure; it landed after
the 0.5.0 release, which is the build both reporters used.

No test ever clicked the Paste button itself, which is how this
shipped. The new test clicks it, then opens its menu with a secondary
click and picks Paste Values, on a worker thread with a timeout so a
regression fails instead of hanging the run. With #272 reverted it
fails (egui's debug lock check panics after 10 s); with it, it passes.
The Paste button gets an accessible "Paste" name so the test (and
screen readers) can find it, the same way Pictures and Border presets
are named.

With the freeze gone, the button still did not do what it says: it
pasted GridCraft's own copy only (GridState::system_clipboard was never
assigned), so text copied in another app gave "Nothing to paste." The
ribbon button, its options menu's Paste, the cell and header context
menus and the macOS Edit menu now read the system clipboard through a
new paste_text host service and paste it like Cmd/Ctrl+V does, falling
back to GridCraft's own copy when there is no text (or no service, as
on the web). Asking egui for the clipboard instead (RequestPaste) was
tried and stalled the macOS event loop until the next input event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 20:51:39 -04:00
Marco Arnulfo 2dd832e2d5 XLSX: XML maps survive a save, with their table columns and mapped cells (part of #860) 2026-10-13 02:45:40 +02:00
Marco Arnulfo 5564856feb PivotTables: the PivotTable Fields pane follows the interface language (fixes #858)
The pane's labels, field menus and buttons were hardcoded English strings that never went through i18n, so switching to Spanish left the whole pane in English. They now go through Language::tr, with Spanish entries in the catalog. The Summarize by / Show as items are keyed as whole labels so each one can be translated.
2026-10-13 02:41:49 +02:00
Eduardo Soares 26c6e3df44 #833: pin hidden-row formatting, grow-only restore branch, tighten tests 2026-10-12 20:48:43 -03:00
Eduardo Soares 1086e4aab8 #833: opened files grow non-custom rows to their tallest font 2026-10-12 20:47:31 -03:00
Eduardo Soares a0a17b154c #833: whole-row/column formatting re-fits row heights 2026-10-12 20:47:31 -03:00
NetterSilver c5c1763774 XLSX: keep chart sheets on open and save (#727)
Chart sheets (a sheet that holds one chart; Move Chart > New sheet, F11) were
skipped on open, and their chart sheet, drawing, chart, style and colour parts
were dropped with them because the pass-through treats those kinds as content
GridCraft rewrites. Saving a workbook lost every chart sheet.

They are now kept as they were: the <sheet> entry (name, visibility and its
place among the worksheets), the chartsheet part and everything behind it,
byte for byte, written back in place. The workbook's other sheet positions
(active tab, print areas, scoped names) are computed with the chart sheets
counted. A chart sheet is left out on save when the chart is no longer
valid for the workbook: a sheet it plots was deleted or renamed, or a
worksheet took its name.
2026-10-13 01:45:40 +02:00
derechopiano cf7138b5e1 UI: Show rule-specific data validation fields 2026-10-13 00:31:48 +01:00
NetterSilver 39b3d55f8a CSV export: refuse a file over 512 MiB instead of expanding the whole used range (#528)
write_csv visited every position of the used range and appended to one growing
String with no limit, so two cells far apart (A1 and XFD1048576) asked for
about 16 GiB of commas. The output is now capped at 512 MiB: a lower bound
(delimiters and line ends of every row) is checked before anything is
allocated, and the text written so far is checked after each row. Over the
cap, write_csv returns IoError::TooLarge naming the rows, columns and limit,
and Save As / Export CSV and TSV report it without writing a file. Accepted
exports are unchanged. write_csv now returns a Result.
2026-10-13 01:16:05 +02:00
Marco Arnulfo 68a3eabe20 Sheet tabs: All Sheets navigator with a filter field (fixes #839) 2026-10-13 00:25:32 +02:00
Marco Arnulfo 443301f9ee UI: Enter pastes the copied range and ends copy mode (fixes #845) 2026-10-13 00:21:25 +02:00
Bryan MarkandClaude Opus 5.5 a38c58ef48 Merge branch 'main' into recent-modified
Brings in upstream main (0.6.1), which fixes the two CSV tests this
branch failed CI on.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 07:20:22 +09:00
Marco Arnulfo 9c3db37ca2 UI: AutoComplete folds case beyond ASCII and cuts the proposed rest on characters (#842) 2026-10-13 00:18:55 +02:00
Marco Arnulfo a7e22d299f Grid: horizontal scrollbar in the sheet tab bar (fixes #840) 2026-10-13 00:14:40 +02:00
Marco Arnulfo 7a8235a586 UI: AutoComplete for cell values can be turned off, Backspace and Escape drop the proposal (fixes #842) 2026-10-13 00:13:35 +02:00
Marco Arnulfo f25bded4ef Closing the window asks to save unsaved workbooks (fixes #838, fixes #841) 2026-10-13 00:12:20 +02:00
Marco Arnulfo 1f19bf4a27 Grid: Shift+arrows in point mode extend from the pointed cell, also up and left (fixes #835) 2026-10-12 23:28:26 +02:00
bmh129andClaude Opus 5.5 0153457580 View ribbon: lay out the Show group's checkboxes in two columns
The Show group stacked Ruler, Formula Bar, Gridlines and Headings in
one column, but the ribbon body fits only three rows, so Headings was
cut off at the bottom and could not be reached. The four checkboxes now
sit in two columns of two (Ruler/Gridlines, Formula Bar/Headings), as in
Excel's Show group. Checked in English and German, which has the
longest labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:03:47 -05:00
Marco Arnulfo 52b4f873c7 XLSX: a hyperlink to another file is saved as an external relationship (fixes #800) 2026-10-12 22:57:20 +02:00
Marco Arnulfo 484612f638 XLS: formulas that use a skipped defined name keep their saved results (fixes #803) 2026-10-12 22:56:40 +02:00
Marco Arnulfo 5406fc5629 XLSX: Save As into another folder keeps links on the same workbook (fixes #801)
Excel stores the path of a linked workbook relative to the file that uses it, and rewrites it when the file is saved into another folder. The path was kept as text, so after Save As it named a file next to the new copy: a missing link, or another workbook with the same name. Relative externalLinkPath targets are now resolved against the folder the workbook was read from and written relative to the folder it is saved into (absolute when there is no relative path, on another drive).
2026-10-12 22:55:53 +02:00
Marco Arnulfo 6939d95cae Formula bar: hide _xludf. like Excel, keep it when the edit is confirmed unchanged (#726) 2026-10-12 22:53:10 +02:00
Marco Arnulfo 75305dfedf XLSX: keep the workbook's default font on save (fixes #805)
Cell format 0 always mapped to the model's built-in Calibri 11 default, so the file's Normal font was replaced on save and every default-formatted cell changed size. The Normal style now takes the font of cell format 0, and a Normal font in the theme's body face is written with scheme="minor" again.
2026-10-12 22:51:41 +02:00
Marco Arnulfo 930fa1cada XLSX: keep fileVersion and calcFeatures in workbook.xml on save (fixes #799)
Excel uses the cached cell values of a saved file only when workbook.xml has both <fileVersion> and the calcFeatures extension; without them it recalculates everything on open, and lookups into closed workbooks come back as #CALC!. Both are now read into the workbook's kept content and written back in schema order: fileVersion first, the calcFeatures ext in <extLst> last. A workbook that had neither gets neither.
2026-10-12 22:49:34 +02:00
Marco Arnulfo 9a58f2d086 XLS: a formula kept as its saved value keeps a text result (fixes #802) 2026-10-12 22:40:01 +02:00