1
0
Fork 0
libreoffice/sc/source/ui/view/dbfunc.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

467 lines
16 KiB
C++

/* -*- 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/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <scitems.hxx>
#include <sfx2/bindings.hxx>
#include <vcl/svapp.hxx>
#include <vcl/weld.hxx>
#include <unotools/charclass.hxx>
#include <osl/diagnose.h>
#include <dbfunc.hxx>
#include <docsh.hxx>
#include <attrib.hxx>
#include <sc.hrc>
#include <undodat.hxx>
#include <dbdata.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <global.hxx>
#include <dbdocfun.hxx>
#include <editable.hxx>
#include <queryentry.hxx>
#include <markdata.hxx>
#include <tabvwsh.hxx>
#include <sortparam.hxx>
ScDBFunc::ScDBFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ) :
ScViewFunc( pParent, rDocSh, pViewShell )
{
}
ScDBFunc::~ScDBFunc()
{
}
// auxiliary functions
void ScDBFunc::GotoDBArea( const OUString& rDBName )
{
ScDocument& rDoc = GetViewData().GetDocument();
ScDBCollection* pDBCol = rDoc.GetDBCollection();
ScDBData* pData = pDBCol->getNamedDBs().findByUpperName(ScGlobal::getCharClass().uppercase(rDBName));
if (!pData)
return;
SCTAB nTab = 0;
SCCOL nStartCol = 0;
SCROW nStartRow = 0;
SCCOL nEndCol = 0;
SCROW nEndRow = 0;
pData->GetArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow );
SetTabNo( nTab );
MoveCursorAbs( nStartCol, nStartRow, SC_FOLLOW_JUMP,
false, false ); // bShift,bControl
DoneBlockMode();
InitBlockMode( nStartCol, nStartRow, nTab );
MarkCursor( nEndCol, nEndRow, nTab );
SelectionChanged();
}
// search current datarange for sort / filter
ScDBData* ScDBFunc::GetDBData( bool bMark, ScGetDBMode eMode, ScGetDBSelection eSel )
{
ScDocShell* pDocSh = GetViewData().GetDocShell();
ScDBData* pData = nullptr;
ScRange aRange;
ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED )
{
bool bShrinkColumnsOnly = false;
if (eSel == ScGetDBSelection::RowDown)
{
// Don't alter row range, additional rows may have been selected on
// purpose to append data, or to have a fake header row.
bShrinkColumnsOnly = true;
// Select further rows only if only one row or a portion thereof is
// selected.
if (aRange.aStart.Row() != aRange.aEnd.Row())
{
// If an area is selected shrink that to the actual used
// columns, don't draw filter buttons for empty columns.
eSel = ScGetDBSelection::ShrinkToUsedData;
}
else if (aRange.aStart.Col() == aRange.aEnd.Col())
{
// One cell only, if it is not marked obtain entire used data
// area.
const ScMarkData& rMarkData = GetViewData().GetMarkData();
if (!(rMarkData.IsMarked() || rMarkData.IsMultiMarked()))
eSel = ScGetDBSelection::Keep;
}
}
switch (eSel)
{
case ScGetDBSelection::ShrinkToUsedData:
case ScGetDBSelection::RowDown:
{
// Shrink the selection to actual used area.
ScDocument& rDoc = pDocSh->GetDocument();
SCCOL nCol1 = aRange.aStart.Col(), nCol2 = aRange.aEnd.Col();
SCROW nRow1 = aRange.aStart.Row(), nRow2 = aRange.aEnd.Row();
bool bShrunk;
rDoc.ShrinkToUsedDataArea( bShrunk, aRange.aStart.Tab(),
nCol1, nRow1, nCol2, nRow2, bShrinkColumnsOnly);
if (bShrunk)
{
aRange.aStart.SetCol(nCol1);
aRange.aEnd.SetCol(nCol2);
aRange.aStart.SetRow(nRow1);
aRange.aEnd.SetRow(nRow2);
}
}
break;
default:
; // nothing
}
pData = pDocSh->GetDBData( aRange, eMode, eSel );
}
else if ( eMode != SC_DB_OLD )
pData = pDocSh->GetDBData(
ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(),
GetViewData().GetTabNo() ),
eMode, ScGetDBSelection::Keep );
if (!pData)
return nullptr;
if (bMark)
{
ScRange aFound;
pData->GetArea(aFound);
MarkRange( aFound, false );
}
return pData;
}
ScDBData* ScDBFunc::GetAnonymousDBData()
{
ScDocShell* pDocSh = GetViewData().GetDocShell();
ScRange aRange;
ScMarkType eMarkType = GetViewData().GetSimpleArea(aRange);
if (eMarkType != SC_MARK_SIMPLE && eMarkType != SC_MARK_SIMPLE_FILTERED)
return nullptr;
// Expand to used data area if not explicitly marked.
const ScMarkData& rMarkData = GetViewData().GetMarkData();
if (!rMarkData.IsMarked() && !rMarkData.IsMultiMarked())
{
SCCOL nCol1 = aRange.aStart.Col();
SCCOL nCol2 = aRange.aEnd.Col();
SCROW nRow1 = aRange.aStart.Row();
SCROW nRow2 = aRange.aEnd.Row();
pDocSh->GetDocument().GetDataArea(aRange.aStart.Tab(), nCol1, nRow1, nCol2, nRow2, false, false);
aRange.aStart.SetCol(nCol1);
aRange.aStart.SetRow(nRow1);
aRange.aEnd.SetCol(nCol2);
aRange.aEnd.SetRow(nRow2);
}
return pDocSh->GetAnonymousDBData(aRange);
}
// main functions
// Sort
void ScDBFunc::UISort( const ScSortParam& rSortParam )
{
ScDocShell* pDocSh = GetViewData().GetDocShell();
ScDocument& rDoc = pDocSh->GetDocument();
SCTAB nTab = GetViewData().GetTabNo();
ScDBData* pDBData = rDoc.GetDBAtArea( nTab, rSortParam.nCol1, rSortParam.nRow1,
rSortParam.nCol2, rSortParam.nRow2 );
if (!pDBData)
{
OSL_FAIL( "Sort: no DBData" );
return;
}
ScSubTotalParam aSubTotalParam;
pDBData->GetSubTotalParam( aSubTotalParam );
if (aSubTotalParam.aGroups[0].bActive && !aSubTotalParam.bRemoveOnly)
{
// repeat subtotals, with new sortorder
DoSubTotals( aSubTotalParam, true/*bRecord*/, &rSortParam );
}
else
{
Sort( rSortParam ); // just sort
}
}
void ScDBFunc::Sort( const ScSortParam& rSortParam, bool bRecord, bool bPaint )
{
ScDocShell* pDocSh = GetViewData().GetDocShell();
SCTAB nTab = GetViewData().GetTabNo();
ScDBDocFunc aDBDocFunc( *pDocSh );
bool bSuccess = aDBDocFunc.Sort( nTab, rSortParam, bRecord, bPaint, false );
if ( bSuccess && !rSortParam.bInplace )
{
// mark target
ScRange aDestRange( rSortParam.nDestCol, rSortParam.nDestRow, rSortParam.nDestTab,
rSortParam.nDestCol + rSortParam.nCol2 - rSortParam.nCol1,
rSortParam.nDestRow + rSortParam.nRow2 - rSortParam.nRow1,
rSortParam.nDestTab );
MarkRange( aDestRange );
}
ResetAutoSpellForContentChange();
}
// filters
void ScDBFunc::Query( const ScQueryParam& rQueryParam, const ScRange* pAdvSource, bool bRecord )
{
ScDocShell* pDocSh = GetViewData().GetDocShell();
SCTAB nTab = GetViewData().GetTabNo();
ScDBDocFunc aDBDocFunc( *pDocSh );
bool bSuccess = aDBDocFunc.Query( nTab, rQueryParam, pAdvSource, bRecord, false );
if (!bSuccess)
return;
bool bCopy = !rQueryParam.bInplace;
if (bCopy)
{
// mark target range (data base range has been set up if applicable)
ScDocument& rDoc = pDocSh->GetDocument();
ScDBData* pDestData = rDoc.GetDBAtCursor(
rQueryParam.nDestCol, rQueryParam.nDestRow,
rQueryParam.nDestTab, ScDBDataPortion::TOP_LEFT );
if (pDestData)
{
ScRange aDestRange;
pDestData->GetArea(aDestRange);
MarkRange( aDestRange );
}
}
if (!bCopy)
{
ScTabViewShell::notifyAllViewsSheetGeomInvalidation(
GetViewData().GetViewShell(),
false /* bColumns */, true /* bRows */,
false /* bSizes*/, true /* bHidden */, true /* bFiltered */,
false /* bGroups */, nTab);
UpdateScrollBars(ROW_HEADER);
SelectionChanged(); // for attribute states (filtered rows are ignored)
}
GetViewData().GetBindings().Invalidate( SID_UNFILTER );
}
// autofilter-buttons show / hide
void ScDBFunc::ToggleAutoFilter()
{
ScViewData* pViewData = &GetViewData();
ScDocShell* pDocSh = pViewData->GetDocShell();
ScQueryParam aParam;
ScDocument& rDoc = pViewData->GetDocument();
ScDBData* pDBData = GetDBData(false, SC_DB_AUTOFILTER, ScGetDBSelection::RowDown);
pDBData->SetByRow( true ); //! undo, retrieve beforehand ??
pDBData->GetQueryParam( aParam );
SCCOL nCol;
SCROW nRow = aParam.nRow1;
SCTAB nTab = pViewData->GetTabNo();
ScMF nFlag;
bool bHasAuto = true;
bool bHeader = pDBData->HasHeader();
//! instead retrieve from DB-range?
for (nCol=aParam.nCol1; nCol<=aParam.nCol2 && bHasAuto; nCol++)
{
nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
if ( !(nFlag & ScMF::Auto) )
bHasAuto = false;
}
if (bHasAuto) // remove
{
// hide filter buttons
for (nCol=aParam.nCol1; nCol<=aParam.nCol2; nCol++)
{
nFlag = rDoc.GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG )->GetValue();
rDoc.ApplyAttr( nCol, nRow, nTab, ScMergeFlagAttr( nFlag & ~ScMF::Auto ) );
aParam.RemoveAllEntriesByField(nCol);
}
// use a list action for the AutoFilter buttons (ScUndoAutoFilter) and the filter operation
OUString aUndo = ScResId( STR_UNDO_QUERY );
pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, pViewData->GetViewShell()->GetViewShellId() );
ScRange aRange;
pDBData->GetArea( aRange );
pDocSh->GetUndoManager()->AddUndoAction(
std::make_unique<ScUndoAutoFilter>( pDocSh, aRange, pDBData->GetName(), false ) );
pDBData->SetAutoFilter(false);
aParam.bDuplicate = true;
Query( aParam, nullptr, true );
pDocSh->GetUndoManager()->LeaveListAction();
ScDBFunc::ModifiedAutoFilter(pDocSh);
}
else // show filter buttons
{
if ( !rDoc.IsBlockEmpty( aParam.nCol1, aParam.nRow1,
aParam.nCol2, aParam.nRow2, nTab ) )
{
if (!bHeader)
{
std::shared_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pViewData->GetDialogParent(),
VclMessageType::Question,
VclButtonsType::YesNo,
// header from first row?
ScResId(STR_MSSG_MAKEAUTOFILTER_0),
SfxViewShell::Current()));
xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); // "StarCalc"
xBox->set_default_response(RET_YES);
xBox->SetInstallLOKNotifierHdl(LINK(this, ScDBFunc, InstallLOKNotifierHdl));
xBox->runAsync(xBox, [pDocSh, pViewData, pDBData, nRow, nTab, aParam] (sal_Int32 nResult) {
if (nResult == RET_YES)
{
pDBData->SetHeader( true ); //! Undo ??
}
ApplyAutoFilter(pDocSh, pViewData, pDBData, nRow, nTab, aParam);
});
}
else
ApplyAutoFilter(pDocSh, pViewData, pDBData, nRow, nTab, aParam);
}
else
{
std::shared_ptr<weld::MessageDialog> xErrorBox(Application::CreateMessageDialog(pViewData->GetDialogParent(),
VclMessageType::Warning, VclButtonsType::Ok,
ScResId(STR_ERR_AUTOFILTER)));
xErrorBox->SetInstallLOKNotifierHdl(LINK(this, ScDBFunc, InstallLOKNotifierHdl));
xErrorBox->runAsync(xErrorBox, [] (sal_Int32) {});
}
}
}
IMPL_STATIC_LINK_NOARG(ScDBFunc, InstallLOKNotifierHdl, void*, vcl::ILibreOfficeKitNotifier*)
{
return GetpApp();
}
void ScDBFunc::ApplyAutoFilter(ScDocShell* pDocSh, ScViewData* pViewData, ScDBData* pDBData,
SCROW nRow, SCTAB nTab, const ScQueryParam& aParam)
{
ScDocument& rDoc = pViewData->GetDocument();
ScRange aRange;
pDBData->GetArea(aRange);
pDocSh->GetUndoManager()->AddUndoAction(
std::make_unique<ScUndoAutoFilter>(pDocSh, aRange, pDBData->GetName(), true));
pDBData->SetAutoFilter(true);
for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2; nCol++)
{
ScMF nFlag = rDoc.GetAttr(nCol, nRow, nTab, ATTR_MERGE_FLAG)->GetValue();
rDoc.ApplyAttr(nCol, nRow, nTab, ScMergeFlagAttr(nFlag | ScMF::Auto));
}
pDocSh->PostPaint(ScRange(aParam.nCol1, nRow, nTab, aParam.nCol2, nRow, nTab),
PaintPartFlags::Grid);
ScDBFunc::ModifiedAutoFilter(pDocSh);
}
void ScDBFunc::ModifiedAutoFilter(ScDocShell* pDocSh)
{
ScDocShellModificator aModificator(*pDocSh);
aModificator.SetDocumentModified();
if (SfxBindings* pBindings = pDocSh->GetViewBindings())
{
pBindings->Invalidate(SID_AUTO_FILTER);
pBindings->Invalidate(SID_AUTOFILTER_HIDE);
}
}
// just hide, no data change
void ScDBFunc::HideAutoFilter()
{
ScDocShell* pDocSh = GetViewData().GetDocShell();
ScDocShellModificator aModificator( *pDocSh );
ScDocument& rDoc = pDocSh->GetDocument();
ScDBData* pDBData = GetDBData( false );
SCTAB nTab;
SCCOL nCol1, nCol2;
SCROW nRow1, nRow2;
pDBData->GetArea(nTab, nCol1, nRow1, nCol2, nRow2);
for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++)
{
ScMF nFlag = rDoc.GetAttr( nCol, nRow1, nTab, ATTR_MERGE_FLAG )->GetValue();
rDoc.ApplyAttr( nCol, nRow1, nTab, ScMergeFlagAttr( nFlag & ~ScMF::Auto ) );
}
ScRange aRange;
pDBData->GetArea( aRange );
pDocSh->GetUndoManager()->AddUndoAction(
std::make_unique<ScUndoAutoFilter>( pDocSh, aRange, pDBData->GetName(), false ) );
pDBData->SetAutoFilter(false);
pDocSh->PostPaint(ScRange(nCol1, nRow1, nTab, nCol2, nRow1, nTab), PaintPartFlags::Grid );
aModificator.SetDocumentModified();
SfxBindings& rBindings = GetViewData().GetBindings();
rBindings.Invalidate( SID_AUTO_FILTER );
rBindings.Invalidate( SID_AUTOFILTER_HIDE );
}
// Re-Import
bool ScDBFunc::ImportData( const ScImportParam& rParam )
{
ScDocument& rDoc = GetViewData().GetDocument();
ScEditableTester aTester( rDoc, GetViewData().GetTabNo(), rParam.nCol1,rParam.nRow1,
rParam.nCol2,rParam.nRow2 );
if ( !aTester.IsEditable() )
{
ErrorMessage(aTester.GetMessageId());
return false;
}
ScDBDocFunc aDBDocFunc( *GetViewData().GetDocShell() );
return aDBDocFunc.DoImport( GetViewData().GetTabNo(), rParam, nullptr );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */