/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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.bGroupActive[0] && !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 ) ); } // 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( pDocSh, aRange, pDBData->GetName(), false ) ); pDBData->SetAutoFilter(false); // remove filter (incl. Paint / Undo) SCSIZE nEC = aParam.GetEntryCount(); for (SCSIZE i=0; iGetUndoManager()->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 xBox(Application::CreateMessageDialog(pViewData->GetDialogParent(), VclMessageType::Question, VclButtonsType::YesNo, ScResId(STR_MSSG_MAKEAUTOFILTER_0))); // header from first row? 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, nCol, nRow, nTab, aParam] (sal_Int32 nResult) { if (nResult == RET_YES) { pDBData->SetHeader( true ); //! Undo ?? } ApplyAutoFilter(pDocSh, pViewData, pDBData, nCol, nRow, nTab, aParam); }); } else ApplyAutoFilter(pDocSh, pViewData, pDBData, nCol, nRow, nTab, aParam); } else { std::shared_ptr 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, SCCOL nCol, SCROW nRow, SCTAB nTab, ScQueryParam aParam) { ScDocument& rDoc = pViewData->GetDocument(); ScRange aRange; pDBData->GetArea(aRange); pDocSh->GetUndoManager()->AddUndoAction( std::make_unique(pDocSh, aRange, pDBData->GetName(), true)); pDBData->SetAutoFilter(true); for (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(); 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( 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: */