sc/Library_sc.mk                              |    1 
 sc/source/ui/docshell/dbdocfun.cxx            |  366 -----------------------
 sc/source/ui/inc/operation/QueryOperation.hxx |   39 ++
 sc/source/ui/operation/QueryOperation.cxx     |  407 ++++++++++++++++++++++++++
 4 files changed, 450 insertions(+), 363 deletions(-)

New commits:
commit a52ebf89d56d41490f6405b66d85134d50a4826b
Author:     Tomaž Vajngerl <[email protected]>
AuthorDate: Wed Feb 18 18:25:18 2026 +0900
Commit:     Miklos Vajna <[email protected]>
CommitDate: Wed Feb 18 15:12:03 2026 +0100

    sc: Introduce QueryOperation class deriving from sc::Operation
    
    Move the implementation from ScDBDocFunc into sc::QueryOperation.
    No functional change was done.
    
    Change-Id: I5edd52b72f2daf35d98cad9be1822b53085ac58f
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199603
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index bd76aadd9249..ea8223498747 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -539,6 +539,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
     sc/source/ui/operation/SetNormalStringOperation \
     sc/source/ui/operation/SetStringOperation \
     sc/source/ui/operation/SetValueOperation \
+    sc/source/ui/operation/QueryOperation \
     sc/source/ui/operation/SortOperation \
     sc/source/ui/pagedlg/areasdlg \
     sc/source/ui/pagedlg/tphfedit \
diff --git a/sc/source/ui/docshell/dbdocfun.cxx 
b/sc/source/ui/docshell/dbdocfun.cxx
index ba79111acad3..6119fbac4c34 100644
--- a/sc/source/ui/docshell/dbdocfun.cxx
+++ b/sc/source/ui/docshell/dbdocfun.cxx
@@ -59,6 +59,7 @@
 #include <ChartTools.hxx>
 #include <SheetViewOperationsTester.hxx>
 #include <operation/SortOperation.hxx>
+#include <operation/QueryOperation.hxx>
 
 #include <memory>
 
@@ -618,369 +619,8 @@ bool ScDBDocFunc::SortTab(SCTAB nTab, const ScSortParam& 
rSortParam, bool bRecor
 bool ScDBDocFunc::Query( SCTAB nTab, const ScQueryParam& rQueryParam,
                         const ScRange* pAdvSource, bool bRecord, bool bApi )
 {
-    ScDocShellModificator aModificator( rDocShell );
-
-    ScDocument& rDoc = rDocShell.GetDocument();
-
-    ScTabViewShell* pViewSh = rDocShell.GetBestViewShell();
-    if (pViewSh && ScTabViewShell::isAnyEditViewInRange(pViewSh, /*bColumns*/ 
false, rQueryParam.nRow1, rQueryParam.nRow2))
-    {
-        return false;
-    }
-
-    if (bRecord && !rDoc.IsUndoEnabled())
-        bRecord = false;
-    ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rQueryParam.nCol1, 
rQueryParam.nRow1,
-                                                    rQueryParam.nCol2, 
rQueryParam.nRow2 );
-    if (!pDBData)
-    {
-        OSL_FAIL( "Query: no DBData" );
-        return false;
-    }
-
-    //  Change from Inplace to non-Inplace, only then cancel Inplace:
-    //  (only if "Persistent"  is selected in the dialog)
-
-    if ( !rQueryParam.bInplace && pDBData->HasQueryParam() && 
rQueryParam.bDestPers )
-    {
-        ScQueryParam aOldQuery;
-        pDBData->GetQueryParam(aOldQuery);
-        if (aOldQuery.bInplace)
-        {
-            //  cancel old filtering
-
-            SCSIZE nEC = aOldQuery.GetEntryCount();
-            for (SCSIZE i=0; i<nEC; i++)
-                aOldQuery.GetEntry(i).bDoQuery = false;
-            aOldQuery.bDuplicate = true;
-            Query( nTab, aOldQuery, nullptr, bRecord, bApi );
-        }
-    }
-
-    ScQueryParam aLocalParam( rQueryParam );        // for Paint / destination 
range
-    bool bCopy = !rQueryParam.bInplace;             // copied in Table::Query
-    ScDBData* pDestData = nullptr;                  // range to be copied to
-    bool bDoSize = false;                           // adjust destination size 
(insert/delete)
-    SCCOL nFormulaCols = 0;                         // only at bDoSize
-    bool bKeepFmt = false;
-    ScRange aOldDest;
-    ScRange aDestTotal;
-    if ( bCopy && rQueryParam.nDestCol == rQueryParam.nCol1 &&
-                  rQueryParam.nDestRow == rQueryParam.nRow1 && 
rQueryParam.nDestTab == nTab )
-        bCopy = false;
-    SCTAB nDestTab = nTab;
-    if ( bCopy )
-    {
-        aLocalParam.MoveToDest();
-        nDestTab = rQueryParam.nDestTab;
-        if ( !rDoc.ValidColRow( aLocalParam.nCol2, aLocalParam.nRow2 ) )
-        {
-            if (!bApi)
-                rDocShell.ErrorMessage(STR_PASTE_FULL);
-            return false;
-        }
-
-        if (!CheckSheetViewProtection(sc::OperationType::Query))
-            return false;
-
-        ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(rDoc, 
nDestTab,
-                                    aLocalParam.nCol1, aLocalParam.nRow1,
-                                    aLocalParam.nCol2,aLocalParam.nRow2);
-        if (!aTester.IsEditable())
-        {
-            if (!bApi)
-                rDocShell.ErrorMessage(aTester.GetMessageId());
-            return false;
-        }
-
-        pDestData = rDoc.GetDBAtCursor( rQueryParam.nDestCol, 
rQueryParam.nDestRow,
-                                            rQueryParam.nDestTab, 
ScDBDataPortion::TOP_LEFT );
-        if (pDestData)
-        {
-            pDestData->GetArea( aOldDest );
-            aDestTotal=ScRange( rQueryParam.nDestCol,
-                                rQueryParam.nDestRow,
-                                nDestTab,
-                                rQueryParam.nDestCol + rQueryParam.nCol2 - 
rQueryParam.nCol1,
-                                rQueryParam.nDestRow + rQueryParam.nRow2 - 
rQueryParam.nRow1,
-                                nDestTab );
-
-            bDoSize = pDestData->IsDoSize();
-            //  test if formulas need to be filled in (nFormulaCols):
-            if ( bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col() )
-            {
-                SCCOL nTestCol = aOldDest.aEnd.Col() + 1;       // next to the 
range
-                SCROW nTestRow = rQueryParam.nDestRow +
-                                    ( aLocalParam.bHasHeader ? 1 : 0 );
-                while ( nTestCol <= rDoc.MaxCol() &&
-                        rDoc.GetCellType(ScAddress( nTestCol, nTestRow, nTab 
)) == CELLTYPE_FORMULA )
-                {
-                    ++nTestCol;
-                    ++nFormulaCols;
-                }
-            }
-
-            bKeepFmt = pDestData->IsKeepFmt();
-            if ( bDoSize && !rDoc.CanFitBlock( aOldDest, aDestTotal ) )
-            {
-                if (!bApi)
-                    rDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2);     // 
cannot insert rows
-                return false;
-            }
-        }
-    }
-
-    //      execute
-
-    weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() );
-
-    bool bKeepSub = false;                          // repeat existing partial 
results?
-    bool bKeepTotals = false;
-    if (rQueryParam.GetEntry(0).bDoQuery)           // not at cancellation
-    {
-        ScSubTotalParam aSubTotalParam;
-        pDBData->GetSubTotalParam( aSubTotalParam );    // partial results 
exist?
-
-        if (aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)
-            bKeepSub = true;
-
-        if (pDBData->HasTotals() && pDBData->GetTableStyleInfo())
-            bKeepTotals = true;
-    }
-
-    ScDocumentUniquePtr pUndoDoc;
-    std::unique_ptr<ScDBCollection> pUndoDB;
-    const ScRange* pOld = nullptr;
-
-    if ( bRecord )
-    {
-        pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
-        if (bCopy)
-        {
-            pUndoDoc->InitUndo( rDoc, nDestTab, nDestTab, false, true );
-            rDoc.CopyToDocument(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
-                                aLocalParam.nCol2, aLocalParam.nRow2, nDestTab,
-                                InsertDeleteFlags::ALL, false, *pUndoDoc);
-            //  secure attributes in case they were copied along
-
-            if (pDestData)
-            {
-                rDoc.CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, 
*pUndoDoc);
-                pOld = &aOldDest;
-            }
-        }
-        else
-        {
-            pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true );
-            rDoc.CopyToDocument(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), 
rQueryParam.nRow2, nTab,
-                                InsertDeleteFlags::NONE, false, *pUndoDoc);
-        }
-
-        ScDBCollection* pDocDB = rDoc.GetDBCollection();
-        if (!pDocDB->empty())
-            pUndoDB.reset(new ScDBCollection( *pDocDB ));
-
-        rDoc.BeginDrawUndo();
-    }
-
-    std::unique_ptr<ScDocument> pAttribDoc;
-    ScRange aAttribRange;
-    if (pDestData)                                      // delete destination 
range
-    {
-        if ( bKeepFmt )
-        {
-            //  smaller of the end columns, header+1 row
-            aAttribRange = aOldDest;
-            if ( aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col() )
-                aAttribRange.aEnd.SetCol( aDestTotal.aEnd.Col() );
-            aAttribRange.aEnd.SetRow( aAttribRange.aStart.Row() +
-                                        ( aLocalParam.bHasHeader ? 1 : 0 ) );
-
-            //  also for filled-in formulas
-            aAttribRange.aEnd.SetCol( aAttribRange.aEnd.Col() + nFormulaCols );
-
-            pAttribDoc.reset(new ScDocument( SCDOCMODE_UNDO ));
-            pAttribDoc->InitUndo( rDoc, nDestTab, nDestTab, false, true );
-            rDoc.CopyToDocument(aAttribRange, InsertDeleteFlags::ATTRIB, 
false, *pAttribDoc);
-        }
-
-        if ( bDoSize )
-            rDoc.FitBlock( aOldDest, aDestTotal );
-        else
-            rDoc.DeleteAreaTab(aOldDest, InsertDeleteFlags::ALL);         // 
simply delete
-    }
-
-    //  execute filtering on the document
-    SCSIZE nCount = rDoc.Query( nTab, rQueryParam, bKeepSub, bKeepTotals );
-    pDBData->CalcSaveFilteredCount( nCount );
-    if (bCopy)
-    {
-        aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
-        if (!aLocalParam.bHasHeader && nCount > 0)
-            --aLocalParam.nRow2;
-
-        if ( bDoSize )
-        {
-            //  adjust to the real result range
-            //  (this here is always a reduction)
-
-            ScRange aNewDest( aLocalParam.nCol1, aLocalParam.nRow1, nDestTab,
-                                aLocalParam.nCol2, aLocalParam.nRow2, nDestTab 
);
-            rDoc.FitBlock( aDestTotal, aNewDest, false );      // sal_False - 
don't delete
-
-            if ( nFormulaCols > 0 )
-            {
-                //  fill in formulas
-                //! Undo (Query and Repeat) !!!
-
-                ScRange aNewForm( aLocalParam.nCol2+1, aLocalParam.nRow1, 
nDestTab,
-                                  aLocalParam.nCol2+nFormulaCols, 
aLocalParam.nRow2, nDestTab );
-                ScRange aOldForm = aNewForm;
-                aOldForm.aEnd.SetRow( aOldDest.aEnd.Row() );
-                rDoc.FitBlock( aOldForm, aNewForm, false );
-
-                ScMarkData aMark(rDoc.GetSheetLimits());
-                aMark.SelectOneTable(nDestTab);
-                SCROW nFStartY = aLocalParam.nRow1 + ( aLocalParam.bHasHeader 
? 1 : 0 );
-
-                sal_uLong nProgCount = nFormulaCols;
-                nProgCount *= aLocalParam.nRow2 - nFStartY;
-                ScProgress aProgress( rDoc.GetDocumentShell(),
-                        ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true );
-
-                rDoc.Fill( aLocalParam.nCol2+1, nFStartY,
-                            aLocalParam.nCol2+nFormulaCols, nFStartY, 
&aProgress, aMark,
-                            aLocalParam.nRow2 - nFStartY,
-                            FILL_TO_BOTTOM, FILL_SIMPLE );
-            }
-        }
-
-        if ( pAttribDoc )       // copy back the memorized attributes
-        {
-            //  Header
-            if (aLocalParam.bHasHeader)
-            {
-                ScRange aHdrRange = aAttribRange;
-                aHdrRange.aEnd.SetRow( aHdrRange.aStart.Row() );
-                pAttribDoc->CopyToDocument(aHdrRange, 
InsertDeleteFlags::ATTRIB, false, rDoc);
-            }
-
-            //  Data
-            SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
-            SCROW nAttrRow = aAttribRange.aStart.Row() + ( 
aLocalParam.bHasHeader ? 1 : 0 );
-            for (SCCOL nCol = aAttribRange.aStart.Col(); nCol<=nAttrEndCol; 
nCol++)
-            {
-                const ScPatternAttr* pSrcPattern = pAttribDoc->GetPattern(
-                                                    nCol, nAttrRow, nDestTab );
-                OSL_ENSURE(pSrcPattern,"Pattern is 0");
-                if (pSrcPattern)
-                {
-                    rDoc.ApplyPatternAreaTab( nCol, nAttrRow, nCol, 
aLocalParam.nRow2,
-                                                    nDestTab, *pSrcPattern );
-                    const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
-                    if (pStyle)
-                        rDoc.ApplyStyleAreaTab( nCol, nAttrRow, nCol, 
aLocalParam.nRow2,
-                                                    nDestTab, *pStyle );
-                }
-            }
-        }
-    }
-
-    //  saving: Inplace always, otherwise depending on setting
-    //          old Inplace-Filter may have already been removed
-
-    bool bSave = rQueryParam.bInplace || rQueryParam.bDestPers;
-    if (bSave)                                                  // memorize
-    {
-        pDBData->SetQueryParam( rQueryParam );
-        pDBData->SetHeader( rQueryParam.bHasHeader );       //! ???
-        pDBData->SetAdvancedQuerySource( pAdvSource );      // after 
SetQueryParam
-    }
-
-    if (bCopy)                                              // memorize new DB 
range
-    {
-        //  Selection is done afterwards from outside (dbfunc).
-        //  Currently through the DB area at the destination position,
-        //  so a range must be created there in any case.
-
-        ScDBData* pNewData;
-        if (pDestData)
-            pNewData = pDestData;               // range exists -> adjust 
(always!)
-        else                                    // create range
-            pNewData = rDocShell.GetDBData(
-                            ScRange( aLocalParam.nCol1, aLocalParam.nRow1, 
nDestTab,
-                                     aLocalParam.nCol2, aLocalParam.nRow2, 
nDestTab ),
-                            SC_DB_MAKE, ScGetDBSelection::ForceMark );
-
-        if (pNewData)
-        {
-            pNewData->SetArea( nDestTab, aLocalParam.nCol1, aLocalParam.nRow1,
-                                            aLocalParam.nCol2, 
aLocalParam.nRow2 );
-
-            //  query parameter is no longer set at the destination, only 
leads to confusion
-            //  and mistakes with the query parameter at the source range 
(#37187#)
-        }
-        else
-        {
-            OSL_FAIL("Target are not available");
-        }
-    }
-
-    if (!bCopy)
-    {
-        rDoc.InvalidatePageBreaks(nTab);
-        rDoc.UpdatePageBreaks( nTab );
-    }
-
-    // #i23299# Subtotal functions depend on cell's filtered states.
-    ScRange aDirtyRange(0 , aLocalParam.nRow1, nDestTab, rDoc.MaxCol(), 
aLocalParam.nRow2, nDestTab);
-    rDoc.SetSubTotalCellsDirty(aDirtyRange);
-
-    if ( bRecord )
-    {
-        // create undo action after executing, because of drawing layer undo
-        rDocShell.GetUndoManager()->AddUndoAction(
-                    std::make_unique<ScUndoQuery>( &rDocShell, nTab, 
rQueryParam, std::move(pUndoDoc), std::move(pUndoDB),
-                                        pOld, bDoSize, pAdvSource ) );
-    }
-
-    if ( pViewSh )
-    {
-        // could there be horizontal autofilter ?
-        // maybe it would be better to set bColumns to !rQueryParam.bByRow ?
-        // anyway at the beginning the value of bByRow is 'false'
-        // then after the first sort action it becomes 'true'
-        pViewSh->OnLOKShowHideColRow(/*bColumns*/ false, rQueryParam.nRow1 - 
1);
-    }
-
-    if (bCopy)
-    {
-        SCCOL nEndX = aLocalParam.nCol2;
-        SCROW nEndY = aLocalParam.nRow2;
-        if (pDestData)
-        {
-            if ( aOldDest.aEnd.Col() > nEndX )
-                nEndX = aOldDest.aEnd.Col();
-            if ( aOldDest.aEnd.Row() > nEndY )
-                nEndY = aOldDest.aEnd.Row();
-        }
-        if (bDoSize)
-            nEndY = rDoc.MaxRow();
-
-        // remove AutoFilter button flags
-        rDocShell.DBAreaDeleted(nDestTab, aLocalParam.nCol1, 
aLocalParam.nRow1, aLocalParam.nCol2);
-
-        rDocShell.PostPaint(
-            ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, 
nEndY, nDestTab),
-            PaintPartFlags::Grid);
-    }
-    else
-        rDocShell.PostPaint(
-            ScRange(0, rQueryParam.nRow1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), 
nTab),
-            PaintPartFlags::Grid | PaintPartFlags::Left);
-    aModificator.SetDocumentModified();
-
-    return true;
+    sc::QueryOperation aOperation(rDocShell, nTab, rQueryParam, pAdvSource, 
bRecord, bApi);
+    return aOperation.run();
 }
 
 void ScDBDocFunc::DoSubTotals( SCTAB nTab, const ScSubTotalParam& rParam,
diff --git a/sc/source/ui/inc/operation/QueryOperation.hxx 
b/sc/source/ui/inc/operation/QueryOperation.hxx
new file mode 100644
index 000000000000..86027716cfa7
--- /dev/null
+++ b/sc/source/ui/inc/operation/QueryOperation.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include "Operation.hxx"
+#include <sal/types.h>
+#include <address.hxx>
+
+class ScDocShell;
+struct ScQueryParam;
+class ScRange;
+
+namespace sc
+{
+/** Operation which filters/queries cells in a sheet. */
+class QueryOperation : public Operation
+{
+private:
+    ScDocShell& mrDocShell;
+    SCTAB mnTab;
+    ScQueryParam const& mrQueryParam;
+    ScRange const* mpAdvSource;
+
+    bool runImplementation() override;
+
+public:
+    QueryOperation(ScDocShell& rDocShell, SCTAB nTab, ScQueryParam const& 
rQueryParam,
+                   ScRange const* pAdvSource, bool bRecord, bool bApi);
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/operation/QueryOperation.cxx 
b/sc/source/ui/operation/QueryOperation.cxx
new file mode 100644
index 000000000000..7d16dddd6b15
--- /dev/null
+++ b/sc/source/ui/operation/QueryOperation.cxx
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <operation/QueryOperation.hxx>
+
+#include <dbdata.hxx>
+#include <docsh.hxx>
+#include <editable.hxx>
+#include <globstr.hrc>
+#include <tabvwsh.hxx>
+#include <patattr.hxx>
+#include <queryentry.hxx>
+#include <markdata.hxx>
+#include <progress.hxx>
+#include <undodat.hxx>
+#include <scresid.hxx>
+#include <globalnames.hxx>
+#include <SheetViewOperationsTester.hxx>
+
+#include <osl/diagnose.h>
+#include <vcl/weld.hxx>
+
+namespace sc
+{
+QueryOperation::QueryOperation(ScDocShell& rDocShell, SCTAB nTab, ScQueryParam 
const& rQueryParam,
+                               ScRange const* pAdvSource, bool bRecord, bool 
bApi)
+    : Operation(OperationType::Query, bRecord, bApi)
+    , mrDocShell(rDocShell)
+    , mnTab(nTab)
+    , mrQueryParam(rQueryParam)
+    , mpAdvSource(pAdvSource)
+{
+}
+
+bool QueryOperation::runImplementation()
+{
+    ScDocShellModificator aModificator(mrDocShell);
+
+    ScDocument& rDoc = mrDocShell.GetDocument();
+
+    ScTabViewShell* pViewSh = mrDocShell.GetBestViewShell();
+    if (pViewSh
+        && ScTabViewShell::isAnyEditViewInRange(pViewSh, /*bColumns*/ false, 
mrQueryParam.nRow1,
+                                                mrQueryParam.nRow2))
+    {
+        return false;
+    }
+
+    if (mbRecord && !rDoc.IsUndoEnabled())
+        mbRecord = false;
+
+    ScDBData* pDBData = rDoc.GetDBAtArea(mnTab, mrQueryParam.nCol1, 
mrQueryParam.nRow1,
+                                         mrQueryParam.nCol2, 
mrQueryParam.nRow2);
+    if (!pDBData)
+    {
+        OSL_FAIL("Query: no DBData");
+        return false;
+    }
+
+    //  Change from Inplace to non-Inplace, only then cancel Inplace:
+    //  (only if "Persistent"  is selected in the dialog)
+
+    if (!mrQueryParam.bInplace && pDBData->HasQueryParam() && 
mrQueryParam.bDestPers)
+    {
+        ScQueryParam aOldQuery;
+        pDBData->GetQueryParam(aOldQuery);
+        if (aOldQuery.bInplace)
+        {
+            //  cancel old filtering
+
+            SCSIZE nEC = aOldQuery.GetEntryCount();
+            for (SCSIZE i = 0; i < nEC; i++)
+                aOldQuery.GetEntry(i).bDoQuery = false;
+            aOldQuery.bDuplicate = true;
+            QueryOperation aOperation(mrDocShell, mnTab, aOldQuery, nullptr, 
mbRecord, mbApi);
+            aOperation.run();
+        }
+    }
+
+    ScQueryParam aLocalParam(mrQueryParam); // for Paint / destination range
+    bool bCopy = !mrQueryParam.bInplace; // copied in Table::Query
+    ScDBData* pDestData = nullptr; // range to be copied to
+    bool bDoSize = false; // adjust destination size (insert/delete)
+    SCCOL nFormulaCols = 0; // only at bDoSize
+    bool bKeepFmt = false;
+    ScRange aOldDest;
+    ScRange aDestTotal;
+    if (bCopy && mrQueryParam.nDestCol == mrQueryParam.nCol1
+        && mrQueryParam.nDestRow == mrQueryParam.nRow1 && 
mrQueryParam.nDestTab == mnTab)
+        bCopy = false;
+    SCTAB nDestTab = mnTab;
+    if (bCopy)
+    {
+        aLocalParam.MoveToDest();
+        nDestTab = mrQueryParam.nDestTab;
+        if (!rDoc.ValidColRow(aLocalParam.nCol2, aLocalParam.nRow2))
+        {
+            if (!mbApi)
+                mrDocShell.ErrorMessage(STR_PASTE_FULL);
+            return false;
+        }
+
+        if (!checkSheetViewProtection())
+            return false;
+
+        ScEditableTester aTester = ScEditableTester::CreateAndTestBlock(
+            rDoc, nDestTab, aLocalParam.nCol1, aLocalParam.nRow1, 
aLocalParam.nCol2,
+            aLocalParam.nRow2);
+        if (!aTester.IsEditable())
+        {
+            if (!mbApi)
+                mrDocShell.ErrorMessage(aTester.GetMessageId());
+            return false;
+        }
+
+        pDestData = rDoc.GetDBAtCursor(mrQueryParam.nDestCol, 
mrQueryParam.nDestRow,
+                                       mrQueryParam.nDestTab, 
ScDBDataPortion::TOP_LEFT);
+        if (pDestData)
+        {
+            pDestData->GetArea(aOldDest);
+            aDestTotal = ScRange(mrQueryParam.nDestCol, mrQueryParam.nDestRow, 
nDestTab,
+                                 mrQueryParam.nDestCol + mrQueryParam.nCol2 - 
mrQueryParam.nCol1,
+                                 mrQueryParam.nDestRow + mrQueryParam.nRow2 - 
mrQueryParam.nRow1,
+                                 nDestTab);
+
+            bDoSize = pDestData->IsDoSize();
+            //  test if formulas need to be filled in (nFormulaCols):
+            if (bDoSize && aOldDest.aEnd.Col() == aDestTotal.aEnd.Col())
+            {
+                SCCOL nTestCol = aOldDest.aEnd.Col() + 1; // next to the range
+                SCROW nTestRow = mrQueryParam.nDestRow + 
(aLocalParam.bHasHeader ? 1 : 0);
+                while (nTestCol <= rDoc.MaxCol()
+                       && rDoc.GetCellType(ScAddress(nTestCol, nTestRow, 
mnTab))
+                              == CELLTYPE_FORMULA)
+                {
+                    ++nTestCol;
+                    ++nFormulaCols;
+                }
+            }
+
+            bKeepFmt = pDestData->IsKeepFmt();
+            if (bDoSize && !rDoc.CanFitBlock(aOldDest, aDestTotal))
+            {
+                if (!mbApi)
+                    mrDocShell.ErrorMessage(STR_MSSG_DOSUBTOTALS_2); // cannot 
insert rows
+                return false;
+            }
+        }
+    }
+
+    //      execute
+
+    weld::WaitObject aWait(ScDocShell::GetActiveDialogParent());
+
+    bool bKeepSub = false; // repeat existing partial results?
+    bool bKeepTotals = false;
+    if (mrQueryParam.GetEntry(0).bDoQuery) // not at cancellation
+    {
+        ScSubTotalParam aSubTotalParam;
+        pDBData->GetSubTotalParam(aSubTotalParam); // partial results exist?
+
+        if (aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)
+            bKeepSub = true;
+
+        if (pDBData->HasTotals() && pDBData->GetTableStyleInfo())
+            bKeepTotals = true;
+    }
+
+    ScDocumentUniquePtr pUndoDoc;
+    std::unique_ptr<ScDBCollection> pUndoDB;
+    const ScRange* pOld = nullptr;
+
+    if (mbRecord)
+    {
+        pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+        if (bCopy)
+        {
+            pUndoDoc->InitUndo(rDoc, nDestTab, nDestTab, false, true);
+            rDoc.CopyToDocument(aLocalParam.nCol1, aLocalParam.nRow1, 
nDestTab, aLocalParam.nCol2,
+                                aLocalParam.nRow2, nDestTab, 
InsertDeleteFlags::ALL, false,
+                                *pUndoDoc);
+            //  secure attributes in case they were copied along
+
+            if (pDestData)
+            {
+                rDoc.CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, 
*pUndoDoc);
+                pOld = &aOldDest;
+            }
+        }
+        else
+        {
+            pUndoDoc->InitUndo(rDoc, mnTab, mnTab, false, true);
+            rDoc.CopyToDocument(0, mrQueryParam.nRow1, mnTab, rDoc.MaxCol(), 
mrQueryParam.nRow2,
+                                mnTab, InsertDeleteFlags::NONE, false, 
*pUndoDoc);
+        }
+
+        ScDBCollection* pDocDB = rDoc.GetDBCollection();
+        if (!pDocDB->empty())
+            pUndoDB.reset(new ScDBCollection(*pDocDB));
+
+        rDoc.BeginDrawUndo();
+    }
+
+    std::unique_ptr<ScDocument> pAttribDoc;
+    ScRange aAttribRange;
+    if (pDestData) // delete destination range
+    {
+        if (bKeepFmt)
+        {
+            //  smaller of the end columns, header+1 row
+            aAttribRange = aOldDest;
+            if (aAttribRange.aEnd.Col() > aDestTotal.aEnd.Col())
+                aAttribRange.aEnd.SetCol(aDestTotal.aEnd.Col());
+            aAttribRange.aEnd.SetRow(aAttribRange.aStart.Row() + 
(aLocalParam.bHasHeader ? 1 : 0));
+
+            //  also for filled-in formulas
+            aAttribRange.aEnd.SetCol(aAttribRange.aEnd.Col() + nFormulaCols);
+
+            pAttribDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+            pAttribDoc->InitUndo(rDoc, nDestTab, nDestTab, false, true);
+            rDoc.CopyToDocument(aAttribRange, InsertDeleteFlags::ATTRIB, 
false, *pAttribDoc);
+        }
+
+        if (bDoSize)
+            rDoc.FitBlock(aOldDest, aDestTotal);
+        else
+            rDoc.DeleteAreaTab(aOldDest, InsertDeleteFlags::ALL); // simply 
delete
+    }
+
+    //  execute filtering on the document
+    SCSIZE nCount = rDoc.Query(mnTab, mrQueryParam, bKeepSub, bKeepTotals);
+    pDBData->CalcSaveFilteredCount(nCount);
+    if (bCopy)
+    {
+        aLocalParam.nRow2 = aLocalParam.nRow1 + nCount;
+        if (!aLocalParam.bHasHeader && nCount > 0)
+            --aLocalParam.nRow2;
+
+        if (bDoSize)
+        {
+            //  adjust to the real result range
+            //  (this here is always a reduction)
+
+            ScRange aNewDest(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, 
aLocalParam.nCol2,
+                             aLocalParam.nRow2, nDestTab);
+            rDoc.FitBlock(aDestTotal, aNewDest, false); // sal_False - don't 
delete
+
+            if (nFormulaCols > 0)
+            {
+                //  fill in formulas
+                //! Undo (Query and Repeat) !!!
+
+                ScRange aNewForm(aLocalParam.nCol2 + 1, aLocalParam.nRow1, 
nDestTab,
+                                 aLocalParam.nCol2 + nFormulaCols, 
aLocalParam.nRow2, nDestTab);
+                ScRange aOldForm = aNewForm;
+                aOldForm.aEnd.SetRow(aOldDest.aEnd.Row());
+                rDoc.FitBlock(aOldForm, aNewForm, false);
+
+                ScMarkData aMark(rDoc.GetSheetLimits());
+                aMark.SelectOneTable(nDestTab);
+                SCROW nFStartY = aLocalParam.nRow1 + (aLocalParam.bHasHeader ? 
1 : 0);
+
+                sal_uLong nProgCount = nFormulaCols;
+                nProgCount *= aLocalParam.nRow2 - nFStartY;
+                ScProgress aProgress(rDoc.GetDocumentShell(), 
ScResId(STR_FILL_SERIES_PROGRESS),
+                                     nProgCount, true);
+
+                rDoc.Fill(aLocalParam.nCol2 + 1, nFStartY, aLocalParam.nCol2 + 
nFormulaCols,
+                          nFStartY, &aProgress, aMark, aLocalParam.nRow2 - 
nFStartY, FILL_TO_BOTTOM,
+                          FILL_SIMPLE);
+            }
+        }
+
+        if (pAttribDoc) // copy back the memorized attributes
+        {
+            //  Header
+            if (aLocalParam.bHasHeader)
+            {
+                ScRange aHdrRange = aAttribRange;
+                aHdrRange.aEnd.SetRow(aHdrRange.aStart.Row());
+                pAttribDoc->CopyToDocument(aHdrRange, 
InsertDeleteFlags::ATTRIB, false, rDoc);
+            }
+
+            //  Data
+            SCCOL nAttrEndCol = aAttribRange.aEnd.Col();
+            SCROW nAttrRow = aAttribRange.aStart.Row() + 
(aLocalParam.bHasHeader ? 1 : 0);
+            for (SCCOL nCol = aAttribRange.aStart.Col(); nCol <= nAttrEndCol; 
nCol++)
+            {
+                const ScPatternAttr* pSrcPattern = 
pAttribDoc->GetPattern(nCol, nAttrRow, nDestTab);
+                OSL_ENSURE(pSrcPattern, "Pattern is 0");
+                if (pSrcPattern)
+                {
+                    rDoc.ApplyPatternAreaTab(nCol, nAttrRow, nCol, 
aLocalParam.nRow2, nDestTab,
+                                             *pSrcPattern);
+                    const ScStyleSheet* pStyle = pSrcPattern->GetStyleSheet();
+                    if (pStyle)
+                        rDoc.ApplyStyleAreaTab(nCol, nAttrRow, nCol, 
aLocalParam.nRow2, nDestTab,
+                                               *pStyle);
+                }
+            }
+        }
+    }
+
+    //  saving: Inplace always, otherwise depending on setting
+    //          old Inplace-Filter may have already been removed
+
+    bool bSave = mrQueryParam.bInplace || mrQueryParam.bDestPers;
+    if (bSave) // memorize
+    {
+        pDBData->SetQueryParam(mrQueryParam);
+        pDBData->SetHeader(mrQueryParam.bHasHeader); //! ???
+        pDBData->SetAdvancedQuerySource(mpAdvSource); // after SetQueryParam
+    }
+
+    if (bCopy) // memorize new DB range
+    {
+        //  Selection is done afterwards from outside (dbfunc).
+        //  Currently through the DB area at the destination position,
+        //  so a range must be created there in any case.
+
+        ScDBData* pNewData;
+        if (pDestData)
+            pNewData = pDestData; // range exists -> adjust (always!)
+        else // create range
+            pNewData = mrDocShell.GetDBData(ScRange(aLocalParam.nCol1, 
aLocalParam.nRow1, nDestTab,
+                                                    aLocalParam.nCol2, 
aLocalParam.nRow2, nDestTab),
+                                            SC_DB_MAKE, 
ScGetDBSelection::ForceMark);
+
+        if (pNewData)
+        {
+            pNewData->SetArea(nDestTab, aLocalParam.nCol1, aLocalParam.nRow1, 
aLocalParam.nCol2,
+                              aLocalParam.nRow2);
+
+            //  query parameter is no longer set at the destination, only 
leads to confusion
+            //  and mistakes with the query parameter at the source range 
(#37187#)
+        }
+        else
+        {
+            OSL_FAIL("Target are not available");
+        }
+    }
+
+    if (!bCopy)
+    {
+        rDoc.InvalidatePageBreaks(mnTab);
+        rDoc.UpdatePageBreaks(mnTab);
+    }
+
+    // #i23299# Subtotal functions depend on cell's filtered states.
+    ScRange aDirtyRange(0, aLocalParam.nRow1, nDestTab, rDoc.MaxCol(), 
aLocalParam.nRow2, nDestTab);
+    rDoc.SetSubTotalCellsDirty(aDirtyRange);
+
+    if (mbRecord)
+    {
+        // create undo action after executing, because of drawing layer undo
+        mrDocShell.GetUndoManager()->AddUndoAction(
+            std::make_unique<ScUndoQuery>(&mrDocShell, mnTab, mrQueryParam, 
std::move(pUndoDoc),
+                                          std::move(pUndoDB), pOld, bDoSize, 
mpAdvSource));
+    }
+
+    if (pViewSh)
+    {
+        // could there be horizontal autofilter ?
+        // maybe it would be better to set bColumns to !rQueryParam.bByRow ?
+        // anyway at the beginning the value of bByRow is 'false'
+        // then after the first sort action it becomes 'true'
+        pViewSh->OnLOKShowHideColRow(/*bColumns*/ false, mrQueryParam.nRow1 - 
1);
+    }
+
+    if (bCopy)
+    {
+        SCCOL nEndX = aLocalParam.nCol2;
+        SCROW nEndY = aLocalParam.nRow2;
+        if (pDestData)
+        {
+            if (aOldDest.aEnd.Col() > nEndX)
+                nEndX = aOldDest.aEnd.Col();
+            if (aOldDest.aEnd.Row() > nEndY)
+                nEndY = aOldDest.aEnd.Row();
+        }
+        if (bDoSize)
+            nEndY = rDoc.MaxRow();
+
+        // remove AutoFilter button flags
+        mrDocShell.DBAreaDeleted(nDestTab, aLocalParam.nCol1, 
aLocalParam.nRow1, aLocalParam.nCol2);
+
+        mrDocShell.PostPaint(
+            ScRange(aLocalParam.nCol1, aLocalParam.nRow1, nDestTab, nEndX, 
nEndY, nDestTab),
+            PaintPartFlags::Grid);
+    }
+    else
+        mrDocShell.PostPaint(
+            ScRange(0, mrQueryParam.nRow1, mnTab, rDoc.MaxCol(), 
rDoc.MaxRow(), mnTab),
+            PaintPartFlags::Grid | PaintPartFlags::Left);
+    aModificator.SetDocumentModified();
+
+    return true;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to