From d4c5f90c8700668d984b5f190a86a7ac9f0c66ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Fri, 9 Oct 2026 02:53:55 -0400 Subject: [PATCH] ElyTable: an AutoFilter on marked rows keeps the total below out (R2.9) --- agents.md | 1 + crates/ely-lok/src/client/tests.rs | 1 + crates/ely-lok/src/client/tests/filter.rs | 47 ++++++++ ...calc-autofilter-keeps-a-marked-block.patch | 110 ++++++++++++++++++ 4 files changed, 159 insertions(+) create mode 100644 crates/ely-lok/src/client/tests/filter.rs create mode 100644 patches/libreoffice/0040-lok-calc-autofilter-keeps-a-marked-block.patch diff --git a/agents.md b/agents.md index 735b324..285679b 100644 --- a/agents.md +++ b/agents.md @@ -263,3 +263,4 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely - 2026-10-09(用户定:“打开方式”里不得出现 LibreOffice):系统会登记磁盘上能找到的 .app、挂载镜像里的 .app 与运行过的包,构建目录里的 `LibreOffice.app` 与测试挂载的官方镜像曾因此成为 xlsx 等的默认程序。现构建后由 `scripts/inert-lo.sh` 去掉其全部文件与 URL 声明、快速查看与聚焦插件,注销它,并在 `engines/` 放 `.metadata_never_index`;Ely 只取其 Frameworks 与 Resources。任何脚本与测试都不挂载 LibreOffice 镜像:LOK 探测用本构建的内核,单测所需的官方内核由 `scripts/extract-official-lo.sh` 以 7zz 解成无包的文件夹。`tests/scripts/lo-unregistered.sh` 以 NSWorkspace 核对 docx、xlsx、pptx、odt、csv 的“打开方式”里没有 LibreOffice。 - 2026-10-09(用户报:ElyTable 点格跳到别处、拖不出选区):Calc 在 `sc_print_twips_msgs` 下以打印 twips(行高原值)报光标、选区与 `.uno:SheetGeometryData`,图块与鼠标却在图块 twips(每行按 `ScViewData::ToPixel` 截成整像素),两者随行号漂移(5000 行处约 140 行)。视图对外一律打印 twips;`LokView::set_geometry` 收下工作表几何后,`sheet::Axis::tile`、`print` 照内核的截断换算(每像素 twips 取图块的像素与 twips 之比),绘制、显示、跳转与冻结都先换成图块 twips,鼠标以图块 twips 发出(`tile_point`),拖动句柄仍为打印 twips;`.uno:` 读回与应用(表头、填充柄)照旧用打印 twips,表头的起点取 `LokView::scrolled_start`。内核读鼠标用的比例原取最后一块图块,worker 现于每次 `setClientVisibleArea` 前以 `setClientZoom` 告知图块比例,点击不再依赖先画过图块。 - 2026-10-09(用户报:ElyPDF 改字报“这种字体画不出 V”):文档自带的子集字体常无可用的 Unicode 表(LibreOffice 导出的子集表为占位,0x00 起逐一对应字形号),按 Unicode 找字形一律失败。现以“文档自带字体”写字时,从该页字体资源里找到同一字体(`pdf_load_font` 按对象缓存,与文字层的字体同一对象),按文字提取的读法(ToUnicode 或 cid_to_ucs、`pdf_font_cid_to_gid`)倒出 Unicode 到字形号的表,写字只用页面上已有的字形;新字体的 `/ToUnicode` 以这张表重写(MuPDF 按子集的占位表生成的那份会把新字读成乱码)。页面上没有的字仍报缺字。 +- 2026-10-09:ElyTable 的自动筛选(R2.9)。内核每次打开筛选列表都把未命名的筛选区向下扩到紧邻的数据行(为接上后来追加的行),分不出选区时就在下方的合计行,合计因此进了列表、随数据一起被隐藏。补丁 `0040-lok-calc-autofilter-keeps-a-marked-block.patch`:LOK 下对跨行的选区开自动筛选时,筛选区记下“选定的行”(不存入文件),此后不再扩展;单格开筛选照旧取整个数据区。已知限制:存后重开的文件不带此记号,其筛选区照内核原样扩展。 diff --git a/crates/ely-lok/src/client/tests.rs b/crates/ely-lok/src/client/tests.rs index 6358fa4..48c989a 100644 --- a/crates/ely-lok/src/client/tests.rs +++ b/crates/ely-lok/src/client/tests.rs @@ -6,6 +6,7 @@ mod analysis; mod charts; mod check; mod commands; +mod filter; mod format; mod formula; mod handouts; diff --git a/crates/ely-lok/src/client/tests/filter.rs b/crates/ely-lok/src/client/tests/filter.rs new file mode 100644 index 0000000..c7cb3cf --- /dev/null +++ b/crates/ely-lok/src/client/tests/filter.rs @@ -0,0 +1,47 @@ +//! An AutoFilter keeps to the marked block (TABLE-08). + +use super::sheet::open_book; +use super::*; +use crate::sheet; + +/// A header, three numbers and their total right under them. +const BLOCK: &str = r#" + + + Name + 1 + 2 + 3 + 6 + +"#; + +/// The filter entries after an AutoFilter on `marked`. +fn entries_after(marked: &str) -> Vec { + let (_dir, mut worker, doc) = open_book(BLOCK); + worker.run(&doc, "cell.goto", &[marked]).expect("marks the cells"); + worker.run(&doc, "data.autofilter", &[]).expect("filters"); + let query = sheet::cell_query(sheet::FILTER, 0, 0); + let json = worker.values(&doc, &query).expect("the entries"); + sheet::filter_values(&json).expect("parses").entries.into_iter().map(|e| e.text).collect() +} + +#[test] +fn an_autofilter_on_a_marked_block_leaves_the_row_below_out() { + if install().is_none() { + eprintln!("SKIP: no pinned LOK build"); + return; + } + assert_eq!(entries_after("A1:A4"), ["1", "2", "3"], "the total below stays out"); +} + +#[test] +fn an_autofilter_on_one_cell_takes_the_data_area() { + if install().is_none() { + eprintln!("SKIP: no pinned LOK build"); + return; + } + assert_eq!(entries_after("A1"), ["1", "2", "3", "6"], "the whole column of data"); +} diff --git a/patches/libreoffice/0040-lok-calc-autofilter-keeps-a-marked-block.patch b/patches/libreoffice/0040-lok-calc-autofilter-keeps-a-marked-block.patch new file mode 100644 index 0000000..8c78c3c --- /dev/null +++ b/patches/libreoffice/0040-lok-calc-autofilter-keeps-a-marked-block.patch @@ -0,0 +1,110 @@ +LOK Calc: an AutoFilter on marked rows keeps to those rows + +Each time the filter list opens, GetFilterEntries grows an unnamed +filter range to the data rows right under it (ExtendDataArea and +ExtendBackColorArea), so appended rows join. It cannot tell them from +a totals row that was there when the user marked the block: the +total joined the filter, showed in its list and hid with the data. +Under LOK, an AutoFilter set on a marked block that spans rows keeps +that block: ScDBData remembers it (not saved) and never extends. + +diff --git a/sc/inc/dbdata.hxx b/sc/inc/dbdata.hxx +index 6d80216b4..54515b138 100644 +--- a/sc/inc/dbdata.hxx ++++ b/sc/inc/dbdata.hxx +@@ -119,6 +119,7 @@ private: + + sal_uInt16 nIndex; ///< unique index formulas + bool bAutoFilter; ///< AutoFilter? (not saved) ++ bool bMarkedRows; ///< rows were marked; never extend (not saved) + bool bModified; ///< is set/cleared for/by(?) UpdateReference + + ::std::vector< OUString > maTableColumnNames; ///< names of table columns +@@ -244,6 +245,9 @@ public: + bool HasAutoFilter() const { return bAutoFilter; } + void SetAutoFilter(bool bSet) { bAutoFilter = bSet; } + ++ bool HasMarkedRows() const { return bMarkedRows; } ++ void SetMarkedRows(bool bSet) { bMarkedRows = bSet; } ++ + bool IsModified() const { return bModified; } + void SetModified(bool bMod) { bModified = bMod; } + +diff --git a/sc/source/core/tool/dbdata.cxx b/sc/source/core/tool/dbdata.cxx +index b1d714c55..e7c5a16c1 100644 +--- a/sc/source/core/tool/dbdata.cxx ++++ b/sc/source/core/tool/dbdata.cxx +@@ -75,6 +75,7 @@ ScDBData::ScDBData( const OUString& rName, + bDBSelection(false), + nIndex (0), + bAutoFilter (false), ++ bMarkedRows (false), + bModified (false), + mbTableColumnNamesDirty(true), + nFilteredRowCount(SCSIZE_MAX) +@@ -110,6 +111,7 @@ ScDBData::ScDBData( const ScDBData& rData ) : + bDBSelection (rData.bDBSelection), + nIndex (rData.nIndex), + bAutoFilter (rData.bAutoFilter), ++ bMarkedRows (rData.bMarkedRows), + bModified (rData.bModified), + maTableColumnNames (rData.maTableColumnNames), + maTableColumnAttributes(rData.maTableColumnAttributes), +@@ -146,6 +148,7 @@ ScDBData::ScDBData( const OUString& rName, const ScDBData& rData ) : + bDBSelection (rData.bDBSelection), + nIndex (rData.nIndex), + bAutoFilter (rData.bAutoFilter), ++ bMarkedRows (rData.bMarkedRows), + bModified (rData.bModified), + maTableColumnNames (rData.maTableColumnNames), + maTableColumnAttributes(rData.maTableColumnAttributes), +@@ -192,6 +195,7 @@ ScDBData& ScDBData::operator= (const ScDBData& rData) + bDBSelection = rData.bDBSelection; + nIndex = rData.nIndex; + bAutoFilter = rData.bAutoFilter; ++ bMarkedRows = rData.bMarkedRows; + nFilteredRowCount = rData.nFilteredRowCount; + + if (bHeaderRangeDiffers) +@@ -646,6 +650,8 @@ bool ScDBData::UpdateReference(const ScDocument& rDoc, UpdateRefMode eUpdateRefM + + void ScDBData::ExtendDataArea(const ScDocument& rDoc) + { ++ if (bMarkedRows) ++ return; + // Extend the DB area to include data rows immediately below. + SCCOL nOldCol1 = nStartCol, nOldCol2 = nEndCol; + SCROW nOldEndRow = nEndRow; +@@ -665,6 +671,8 @@ void ScDBData::ExtendDataArea(const ScDocument& rDoc) + + void ScDBData::ExtendBackColorArea(const ScDocument& rDoc) + { ++ if (bMarkedRows) ++ return; + // Extend the DB area to include data rows immediately below. + SCCOL nOldCol1 = nStartCol, nOldCol2 = nEndCol; + SCROW nOldEndRow = nEndRow; +diff --git a/sc/source/ui/view/dbfunc.cxx b/sc/source/ui/view/dbfunc.cxx +index db3d23d86..8140ce4d0 100644 +--- a/sc/source/ui/view/dbfunc.cxx ++++ b/sc/source/ui/view/dbfunc.cxx +@@ -18,6 +18,7 @@ + */ + + #include ++#include + #include + #include + #include +@@ -286,6 +287,11 @@ void ScDBFunc::ToggleAutoFilter() + return; + + pDBData->SetByRow( true ); //! undo, retrieve beforehand ?? ++ // LOK: marked rows are the range, as in Excel. ++ ScRange aMarked; ++ pDBData->SetMarkedRows(comphelper::LibreOfficeKit::isActive() ++ && rViewData.GetSimpleArea(aMarked) == SC_MARK_SIMPLE ++ && aMarked.aStart.Row() != aMarked.aEnd.Row()); + pDBData->GetQueryParam( aParam ); + + SCCOL nCol;