From 267c6f2ac71f92999e969232431ba04678e7437e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 07:54:39 +0200 Subject: Adding upstream version 4:24.2.0. Signed-off-by: Daniel Baumann --- sc/source/ui/view/gridwin2.cxx | 1312 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1312 insertions(+) create mode 100644 sc/source/ui/view/gridwin2.cxx (limited to 'sc/source/ui/view/gridwin2.cxx') diff --git a/sc/source/ui/view/gridwin2.cxx b/sc/source/ui/view/gridwin2.cxx new file mode 100644 index 0000000000..01f5f39dd7 --- /dev/null +++ b/sc/source/ui/view/gridwin2.cxx @@ -0,0 +1,1312 @@ +/* -*- 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 +#include + +#include +#include + +#include +#include +#include + +using namespace css; +using namespace css::sheet; +using css::sheet::DataPilotFieldOrientation; +using std::vector; + +DataPilotFieldOrientation ScGridWindow::GetDPFieldOrientation( SCCOL nCol, SCROW nRow ) const +{ + ScDocument& rDoc = mrViewData.GetDocument(); + SCTAB nTab = mrViewData.GetTabNo(); + ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab); + if (!pDPObj) + return DataPilotFieldOrientation_HIDDEN; + + DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN; + + // Check for page field first. + if (nCol > 0) + { + // look for the dimension header left of the drop-down arrow + tools::Long nField = pDPObj->GetHeaderDim( ScAddress( nCol-1, nRow, nTab ), nOrient ); + if ( nField >= 0 && nOrient == DataPilotFieldOrientation_PAGE ) + { + bool bIsDataLayout = false; + OUString aFieldName = pDPObj->GetDimName( nField, bIsDataLayout ); + if ( !aFieldName.isEmpty() && !bIsDataLayout ) + return DataPilotFieldOrientation_PAGE; + } + } + + nOrient = DataPilotFieldOrientation_HIDDEN; + + // Now, check for row/column field. + tools::Long nField = pDPObj->GetHeaderDim(ScAddress(nCol, nRow, nTab), nOrient); + if (nField >= 0 && (nOrient == DataPilotFieldOrientation_COLUMN || nOrient == DataPilotFieldOrientation_ROW) ) + { + bool bIsDataLayout = false; + OUString aFieldName = pDPObj->GetDimName(nField, bIsDataLayout); + if (!aFieldName.isEmpty() && !bIsDataLayout) + return nOrient; + } + + return DataPilotFieldOrientation_HIDDEN; +} + +// private method for mouse button handling +bool ScGridWindow::DoPageFieldSelection( SCCOL nCol, SCROW nRow ) +{ + if (GetDPFieldOrientation( nCol, nRow ) == DataPilotFieldOrientation_PAGE) + { + LaunchPageFieldMenu( nCol, nRow ); + return true; + } + return false; +} + +bool ScGridWindow::DoAutoFilterButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt ) +{ + ScDocument& rDoc = mrViewData.GetDocument(); + SCTAB nTab = mrViewData.GetTabNo(); + Point aScrPos = mrViewData.GetScrPos(nCol, nRow, eWhich); + Point aDiffPix = rMEvt.GetPosPixel(); + + aDiffPix -= aScrPos; + bool bLOKActive = comphelper::LibreOfficeKit::isActive(); + bool bLayoutRTL = rDoc.IsLayoutRTL( nTab ); + if ( bLayoutRTL && !bLOKActive ) + aDiffPix.setX( -aDiffPix.X() ); + + tools::Long nSizeX, nSizeY; + mrViewData.GetMergeSizePixel( nCol, nRow, nSizeX, nSizeY ); + // The button height should not use the merged cell height, should still use single row height + nSizeY = ScViewData::ToPixel(rDoc.GetRowHeight(nRow, nTab), mrViewData.GetPPTY()); + Size aScrSize(nSizeX-1, nSizeY-1); + + // Check if the mouse cursor is clicking on the popup arrow box. + mpFilterButton.reset(new ScDPFieldButton(GetOutDev(), &GetSettings().GetStyleSettings(), &mrViewData.GetZoomY(), &rDoc)); + mpFilterButton->setBoundingBox(aScrPos, aScrSize, bLayoutRTL && !bLOKActive); + mpFilterButton->setPopupLeft(bLayoutRTL && bLOKActive ? false : bLayoutRTL); // #i114944# AutoFilter button is left-aligned in RTL + Point aPopupPos; + Size aPopupSize; + mpFilterButton->getPopupBoundingBox(aPopupPos, aPopupSize); + tools::Rectangle aRect(aPopupPos, aPopupSize); + if (aRect.Contains(rMEvt.GetPosPixel())) + { + if ( DoPageFieldSelection( nCol, nRow ) ) + return true; + + bool bFilterActive = IsAutoFilterActive(nCol, nRow, nTab); + mpFilterButton->setHasHiddenMember(bFilterActive); + mpFilterButton->setDrawBaseButton(false); + mpFilterButton->setDrawPopupButton(true); + mpFilterButton->setPopupPressed(true); + mpFilterButton->draw(); + LaunchAutoFilterMenu(nCol, nRow); + return true; + } + + return false; +} + +void ScGridWindow::DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup, bool bMultiField ) +{ + ScDocument& rDoc = mrViewData.GetDocument(); + SCTAB nTab = mrViewData.GetTabNo(); + + ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab); + + if (pDPObj) + { + DataPilotFieldOrientation nOrient = DataPilotFieldOrientation_HIDDEN; + ScAddress aPos( nCol, nRow, nTab ); + ScAddress aDimPos = aPos; + if (!bButton && bPopup && aDimPos.Col() > 0) + // For page field selection cell, the real field position is to the left. + aDimPos.IncCol(-1); + + if (bMultiField && DPTestMultiFieldPopupArrow(rMEvt, aPos, pDPObj)) + { + // Multi-field pop up menu has been launched. Don't activate + // field move or regular popup. + return; + } + + tools::Long nField = pDPObj->GetHeaderDim(aDimPos, nOrient); + if ( nField >= 0 ) + { + bDPMouse = false; + nDPField = nField; + pDragDPObj = pDPObj; + + if (bPopup && DPTestFieldPopupArrow(rMEvt, aPos, aDimPos, pDPObj)) + { + // field name pop up menu has been launched. Don't activate + // field move. + return; + } + + if (bButton) + { + bDPMouse = true; + DPTestMouse( rMEvt, true ); + StartTracking(); + } + } + else if ( pDPObj->IsFilterButton(aPos) ) + { + ReleaseMouse(); // may have been captured in ButtonDown + + ScQueryParam aQueryParam; + SCTAB nSrcTab = 0; + const ScSheetSourceDesc* pDesc = pDPObj->GetSheetDesc(); + OSL_ENSURE(pDesc, "no sheet source for filter button"); + if (pDesc) + { + aQueryParam = pDesc->GetQueryParam(); + nSrcTab = pDesc->GetSourceRange().aStart.Tab(); + } + + SfxItemSetFixed aArgSet( mrViewData.GetViewShell()->GetPool() ); + aArgSet.Put( ScQueryItem( SCITEM_QUERYDATA, &mrViewData, &aQueryParam ) ); + + ScAbstractDialogFactory* pFact = ScAbstractDialogFactory::Create(); + + ScopedVclPtr pDlg( + pFact->CreateScPivotFilterDlg( + mrViewData.GetViewShell()->GetFrameWeld(), aArgSet, nSrcTab)); + if ( pDlg->Execute() == RET_OK ) + { + ScSheetSourceDesc aNewDesc(&rDoc); + if (pDesc) + aNewDesc = *pDesc; + + const ScQueryItem& rQueryItem = pDlg->GetOutputItem(); + aNewDesc.SetQueryParam(rQueryItem.GetQueryData()); + + ScDPObject aNewObj( *pDPObj ); + aNewObj.SetSheetDesc( aNewDesc ); + ScDBDocFunc aFunc( *mrViewData.GetDocShell() ); + aFunc.DataPilotUpdate( pDPObj, &aNewObj, true, false ); + mrViewData.GetView()->CursorPosChanged(); // shells may be switched + } + } + } + else + { + OSL_FAIL("Nothing here"); + } +} + +void ScGridWindow::DoPushPivotToggle( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt ) +{ + bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() ); + + ScDocument& rDoc = mrViewData.GetDocument(); + SCTAB nTab = mrViewData.GetTabNo(); + + ScDPObject* pDPObj = rDoc.GetDPAtCursor(nCol, nRow, nTab); + if (!pDPObj) + return; + + if (!pDPObj->GetSaveData()->GetDrillDown()) + return; + + // Get the geometry of the cell. + Point aScrPos = mrViewData.GetScrPos(nCol, nRow, eWhich); + tools::Long nSizeX, nSizeY; + mrViewData.GetMergeSizePixel(nCol, nRow, nSizeX, nSizeY); + Size aScrSize(nSizeX - 1, nSizeY - 1); + + sal_uInt16 nIndent = 0; + if (const ScIndentItem* pIndentItem = rDoc.GetAttr(nCol, nRow, nTab, ATTR_INDENT)) + nIndent = pIndentItem->GetValue(); + + // Check if the mouse cursor is clicking on the toggle +/- box. + ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY()); + aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL); + aBtn.setDrawToggleButton(true, true, nIndent); + Point aPopupPos; + Size aPopupSize; + aBtn.getToggleBoundingBox(aPopupPos, aPopupSize); + tools::Rectangle aRect(aPopupPos, aPopupSize); + if (aRect.Contains(rMEvt.GetPosPixel())) + { + // Mouse cursor inside the toggle +/- box. + sheet::DataPilotTableHeaderData aData; + ScAddress aCellPos(nCol, nRow, nTab); + pDPObj->GetHeaderPositionData(aCellPos, aData); + ScDPObject aNewObj(*pDPObj); + pDPObj->ToggleDetails(aData, &aNewObj); + ScDBDocFunc aFunc(*mrViewData.GetDocShell()); + aFunc.DataPilotUpdate(pDPObj, &aNewObj, true, false); + } +} + +// Data Pilot interaction + +void ScGridWindow::DPTestMouse( const MouseEvent& rMEvt, bool bMove ) +{ + OSL_ENSURE(pDragDPObj, "pDragDPObj missing"); + + // scroll window if at edges + //! move this to separate method + + bool bTimer = false; + Point aPixel = rMEvt.GetPosPixel(); + + SCCOL nDx = 0; + SCROW nDy = 0; + if ( aPixel.X() < 0 ) + nDx = -1; + if ( aPixel.Y() < 0 ) + nDy = -1; + Size aSize = GetOutputSizePixel(); + if ( aPixel.X() >= aSize.Width() ) + nDx = 1; + if ( aPixel.Y() >= aSize.Height() ) + nDy = 1; + if ( nDx != 0 || nDy != 0 ) + { + UpdateDragRect( false, tools::Rectangle() ); + + if ( nDx != 0) + mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) ); + if ( nDy != 0 ) + mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) ); + + bTimer = true; + } + + SCCOL nPosX; + SCROW nPosY; + mrViewData.GetPosFromPixel( aPixel.X(), aPixel.Y(), eWhich, nPosX, nPosY ); + bool bMouseLeft; + bool bMouseTop; + mrViewData.GetMouseQuadrant( aPixel, eWhich, nPosX, nPosY, bMouseLeft, bMouseTop ); + + ScAddress aPos( nPosX, nPosY, mrViewData.GetTabNo() ); + + tools::Rectangle aPosRect; + DataPilotFieldOrientation nOrient; + tools::Long nDimPos; + bool bHasRange = pDragDPObj->GetHeaderDrag( aPos, bMouseLeft, bMouseTop, nDPField, + aPosRect, nOrient, nDimPos ); + UpdateDragRect( bHasRange && bMove, aPosRect ); + + bool bIsDataLayout; + sal_Int32 nDimFlags = 0; + OUString aDimName = pDragDPObj->GetDimName( nDPField, bIsDataLayout, &nDimFlags ); + bool bAllowed = !bHasRange || ScDPObject::IsOrientationAllowed( nOrient, nDimFlags ); + + if (bMove) // set mouse pointer + { + PointerStyle ePointer = PointerStyle::PivotDelete; + if ( !bAllowed ) + ePointer = PointerStyle::NotAllowed; + else if ( bHasRange ) + switch (nOrient) + { + case DataPilotFieldOrientation_COLUMN: ePointer = PointerStyle::PivotCol; break; + case DataPilotFieldOrientation_ROW: ePointer = PointerStyle::PivotRow; break; + case DataPilotFieldOrientation_PAGE: + case DataPilotFieldOrientation_DATA: ePointer = PointerStyle::PivotField; break; + default: break; + } + SetPointer( ePointer ); + } + else // execute change + { + if (!bHasRange) + nOrient = DataPilotFieldOrientation_HIDDEN; + + if ( bIsDataLayout && ( nOrient != DataPilotFieldOrientation_COLUMN && + nOrient != DataPilotFieldOrientation_ROW ) ) + { + // removing data layout is not allowed + mrViewData.GetView()->ErrorMessage(STR_PIVOT_MOVENOTALLOWED); + } + else if ( bAllowed ) + { + ScDPSaveData aSaveData( *pDragDPObj->GetSaveData() ); + + ScDPSaveDimension* pDim; + if ( bIsDataLayout ) + pDim = aSaveData.GetDataLayoutDimension(); + else + pDim = aSaveData.GetDimensionByName(aDimName); + pDim->SetOrientation( nOrient ); + aSaveData.SetPosition( pDim, nDimPos ); + + //! docfunc method with ScDPSaveData as argument? + + ScDPObject aNewObj( *pDragDPObj ); + aNewObj.SetSaveData( aSaveData ); + ScDBDocFunc aFunc( *mrViewData.GetDocShell() ); + // when dragging fields, allow re-positioning (bAllowMove) + aFunc.DataPilotUpdate( pDragDPObj, &aNewObj, true, false, true ); + mrViewData.GetView()->CursorPosChanged(); // shells may be switched + } + } + + if (bTimer && bMove) + mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event + else + mrViewData.GetView()->ResetTimer(); +} + +bool ScGridWindow::DPTestFieldPopupArrow( + const MouseEvent& rMEvt, const ScAddress& rPos, const ScAddress& rDimPos, ScDPObject* pDPObj) +{ + bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() ); + bool bLOK = comphelper::LibreOfficeKit::isActive(); + + // Get the geometry of the cell. + Point aScrPos = mrViewData.GetScrPos(rPos.Col(), rPos.Row(), eWhich); + tools::Long nSizeX, nSizeY; + mrViewData.GetMergeSizePixel(rPos.Col(), rPos.Row(), nSizeX, nSizeY); + Size aScrSize(nSizeX-1, nSizeY-1); + + // Check if the mouse cursor is clicking on the popup arrow box. + ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY()); + aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL); + aBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now + Point aPopupPos; + Size aPopupSize; + aBtn.getPopupBoundingBox(aPopupPos, aPopupSize); + tools::Rectangle aRect(aPopupPos, aPopupSize); + if (aRect.Contains(rMEvt.GetPosPixel())) + { + // Mouse cursor inside the popup arrow box. Launch the field menu. + DPLaunchFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, rDimPos, pDPObj); + return true; + } + + return false; +} + +bool ScGridWindow::DPTestMultiFieldPopupArrow( + const MouseEvent& rMEvt, const ScAddress& rPos, ScDPObject* pDPObj) +{ + bool bLayoutRTL = mrViewData.GetDocument().IsLayoutRTL( mrViewData.GetTabNo() ); + bool bLOK = comphelper::LibreOfficeKit::isActive(); + + // Get the geometry of the cell. + Point aScrPos = mrViewData.GetScrPos(rPos.Col(), rPos.Row(), eWhich); + tools::Long nSizeX, nSizeY; + mrViewData.GetMergeSizePixel(rPos.Col(), rPos.Row(), nSizeX, nSizeY); + Size aScrSize(nSizeX - 1, nSizeY - 1); + + // Check if the mouse cursor is clicking on the popup arrow box. + ScDPFieldButton aBtn(GetOutDev(), &GetSettings().GetStyleSettings(), &GetMapMode().GetScaleY()); + aBtn.setBoundingBox(aScrPos, aScrSize, bLayoutRTL); + aBtn.setPopupLeft(false); // DataPilot popup is always right-aligned for now + aBtn.setDrawPopupButtonMulti(true); + Point aPopupPos; + Size aPopupSize; + aBtn.getPopupBoundingBox(aPopupPos, aPopupSize); + tools::Rectangle aRect(aPopupPos, aPopupSize); + if (aRect.Contains(rMEvt.GetPosPixel())) + { + DataPilotFieldOrientation nOrient; + pDPObj->GetHeaderDim(rPos, nOrient); + // Mouse cursor inside the popup arrow box. Launch the multi-field menu. + DPLaunchMultiFieldPopupMenu(bLOK ? aScrPos : OutputToScreenPixel(aScrPos), aScrSize, pDPObj, nOrient); + return true; + } + + return false; +} + +namespace { + +struct DPFieldPopupData : public ScCheckListMenuControl::ExtendedData +{ + ScDPLabelData maLabels; + ScDPObject* mpDPObj; + tools::Long mnDim; +}; + +struct DPMultiFieldPopupData : public DPFieldPopupData +{ + std::vector maFieldIndices; + std::vector maFieldNames; +}; + +class DPFieldPopupOKAction : public ScCheckListMenuControl::Action +{ +public: + explicit DPFieldPopupOKAction(ScGridWindow* p) : + mpGridWindow(p) {} + + virtual bool execute() override + { + mpGridWindow->UpdateDPFromFieldPopupMenu(); + return true; + } +private: + VclPtr mpGridWindow; +}; + +class DPFieldChangedAction : public ScCheckListMenuControl::Action +{ +public: + explicit DPFieldChangedAction(ScGridWindow* p) : + mpGridWindow(p) {} + + virtual bool execute() override + { + mpGridWindow->UpdateDPPopupMenuForFieldChange(); + return true; + } +private: + VclPtr mpGridWindow; +}; + +class PopupSortAction : public ScCheckListMenuControl::Action +{ +public: + enum SortType { ASCENDING, DESCENDING, CUSTOM }; + + explicit PopupSortAction(ScDPObject* pDPObject, tools::Long nDimIndex, SortType eType, + sal_uInt16 nUserListIndex, ScTabViewShell* pViewShell) + : mpDPObject(pDPObject) + , mnDimIndex(nDimIndex) + , meType(eType) + , mnUserListIndex(nUserListIndex) + , mpViewShell(pViewShell) + {} + + virtual bool execute() override + { + switch (meType) + { + case ASCENDING: + mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, true); + break; + case DESCENDING: + mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, false); + break; + case CUSTOM: + mpViewShell->DataPilotSort(mpDPObject, mnDimIndex, true, &mnUserListIndex); + break; + default: + ; + } + return true; + } + +private: + ScDPObject* mpDPObject; + tools::Long mnDimIndex; + SortType meType; + sal_uInt16 mnUserListIndex; + ScTabViewShell* mpViewShell; +}; + +} + +void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScreenPosition, const Size& rScreenSize, + const ScAddress& rAddress, ScDPObject* pDPObject) +{ + DataPilotFieldOrientation nOrient; + tools::Long nDimIndex = pDPObject->GetHeaderDim(rAddress, nOrient); + + DPLaunchFieldPopupMenu(rScreenPosition, rScreenSize, nDimIndex, pDPObject); +} + +bool lcl_FillDPFieldPopupData(tools::Long nDimIndex, ScDPObject* pDPObj, + DPFieldPopupData& rDPData, bool& bDimOrientNotPage) +{ + if (!pDPObj) + return false; + + rDPData.mnDim = nDimIndex; + pDPObj->GetSource(); + + bool bIsDataLayout; + OUString aDimName = pDPObj->GetDimName(rDPData.mnDim, bIsDataLayout); + pDPObj->BuildAllDimensionMembers(); + const ScDPSaveData* pSaveData = pDPObj->GetSaveData(); + const ScDPSaveDimension* pDim = pSaveData->GetExistingDimensionByName(aDimName); + if (!pDim) + // This should never happen. + return false; + + bDimOrientNotPage = pDim->GetOrientation() != DataPilotFieldOrientation_PAGE; + + // We need to get the list of field members. + pDPObj->FillLabelData(rDPData.mnDim, rDPData.maLabels); + rDPData.mpDPObj = pDPObj; + + return true; +} + +void ScGridWindow::DPLaunchMultiFieldPopupMenu(const Point& rScreenPosition, const Size& rScreenSize, + ScDPObject* pDPObj, DataPilotFieldOrientation nOrient) +{ + if (!pDPObj) + return; + + pDPObj->GetSource(); + + std::unique_ptr pDPData(new DPMultiFieldPopupData); + pDPObj->GetFieldIdsNames(nOrient, pDPData->maFieldIndices, pDPData->maFieldNames); + + if (pDPData->maFieldIndices.empty()) + return; + + tools::Long nDimIndex = pDPData->maFieldIndices[0]; + + bool bDimOrientNotPage = true; + if (!lcl_FillDPFieldPopupData(nDimIndex, pDPObj, *pDPData, bDimOrientNotPage)) + return; + + mpDPFieldPopup.reset(); + + weld::Window* pPopupParent = GetFrameWeld(); + mpDPFieldPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData, + false, -1, true)); + + mpDPFieldPopup->addFields(pDPData->maFieldNames); + DPSetupFieldPopup(std::move(pDPData), bDimOrientNotPage, pDPObj, true); + + DPConfigFieldPopup(); + + if (IsMouseCaptured()) + ReleaseMouse(); + + tools::Rectangle aCellRect(rScreenPosition, rScreenSize); + mpDPFieldPopup->launch(pPopupParent, aCellRect); +} + +void ScGridWindow::DPPopulateFieldMembers(const ScDPLabelData& rLabelData) +{ + // Populate field members. + size_t n = rLabelData.maMembers.size(); + mpDPFieldPopup->setMemberSize(n); + for (size_t i = 0; i < n; ++i) + { + const ScDPLabelData::Member& rMem = rLabelData.maMembers[i]; + OUString aName = rMem.getDisplayName(); + if (aName.isEmpty()) + // Use special string for an empty name. + mpDPFieldPopup->addMember(ScResId(STR_EMPTYDATA), 0.0, rMem.mbVisible, false); + else + mpDPFieldPopup->addMember(rMem.getDisplayName(), 0.0, rMem.mbVisible, false); + } +} + +void ScGridWindow::DPSetupFieldPopup(std::unique_ptr pDPData, + bool bDimOrientNotPage, ScDPObject* pDPObj, + bool bMultiField) +{ + if (!mpDPFieldPopup || !pDPObj) + return; + + const ScDPLabelData& rLabelData = static_cast(pDPData.get())->maLabels; + const tools::Long nDimIndex = static_cast(pDPData.get())->mnDim; + mpDPFieldPopup->setExtendedData(std::move(pDPData)); + + if (bMultiField) + mpDPFieldPopup->setFieldChangedAction(new DPFieldChangedAction(this)); + + mpDPFieldPopup->setOKAction(new DPFieldPopupOKAction(this)); + DPPopulateFieldMembers(rLabelData); + + if (bDimOrientNotPage) + { + vector aUserSortNames; + ScUserList* pUserList = ScGlobal::GetUserList(); + if (pUserList) + { + size_t n = pUserList->size(); + aUserSortNames.reserve(n); + for (size_t i = 0; i < n; ++i) + { + const ScUserListData& rData = (*pUserList)[i]; + aUserSortNames.push_back(rData.GetString()); + } + } + + // Populate the menus. + ScTabViewShell* pViewShell = mrViewData.GetViewShell(); + mpDPFieldPopup->addMenuItem( + ScResId(STR_MENU_SORT_ASC), + new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::ASCENDING, 0, pViewShell)); + mpDPFieldPopup->addMenuItem( + ScResId(STR_MENU_SORT_DESC), + new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::DESCENDING, 0, pViewShell)); + + ScListSubMenuControl* pSubMenu = mpDPFieldPopup->addSubMenuItem(ScResId(STR_MENU_SORT_CUSTOM), !aUserSortNames.empty(), false); + if (pSubMenu) + { + size_t n = aUserSortNames.size(); + for (size_t i = 0; i < n; ++i) + { + pSubMenu->addMenuItem(aUserSortNames[i], + new PopupSortAction(pDPObj, nDimIndex, PopupSortAction::CUSTOM, sal_uInt16(i), pViewShell)); + } + pSubMenu->resizeToFitMenuItems(); + } + } + + mpDPFieldPopup->initMembers(); +} + +void ScGridWindow::DPConfigFieldPopup() +{ + if (!mpDPFieldPopup) + return; + + ScCheckListMenuControl::Config aConfig; + aConfig.mbAllowEmptySet = false; + aConfig.mbRTL = mrViewData.GetDocument().IsLayoutRTL(mrViewData.GetTabNo()); + mpDPFieldPopup->setConfig(aConfig); +} + +void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScrPos, const Size& rScrSize, + tools::Long nDimIndex, ScDPObject* pDPObj) +{ + std::unique_ptr pDPData(new DPFieldPopupData); + bool bDimOrientNotPage = true; + if (!lcl_FillDPFieldPopupData(nDimIndex, pDPObj, *pDPData, bDimOrientNotPage)) + return; + + mpDPFieldPopup.reset(); + + vcl::ILibreOfficeKitNotifier* pNotifier = nullptr; + if (comphelper::LibreOfficeKit::isActive()) + pNotifier = SfxViewShell::Current(); + + weld::Window* pPopupParent = GetFrameWeld(); + mpDPFieldPopup.reset(new ScCheckListMenuControl(pPopupParent, mrViewData, + false, -1, pNotifier)); + + DPSetupFieldPopup(std::move(pDPData), bDimOrientNotPage, pDPObj); + + DPConfigFieldPopup(); + + if (IsMouseCaptured()) + ReleaseMouse(); + + tools::Rectangle aCellRect(rScrPos, rScrSize); + mpDPFieldPopup->launch(pPopupParent, aCellRect); +} + +void ScGridWindow::UpdateDPPopupMenuForFieldChange() +{ + if (!mpDPFieldPopup) + return; + + DPMultiFieldPopupData* pDPData = static_cast(mpDPFieldPopup->getExtendedData()); + if (!pDPData) + return; + + if (pDPData->maFieldIndices.empty()) + return; + + tools::Long nIndex = mpDPFieldPopup->getField(); + if (nIndex < 0) + return; + + tools::Long nDimIndex = pDPData->maFieldIndices[nIndex]; + if (nDimIndex == pDPData->mnDim) + return; + + bool bDimOrientNotPage = true; + if (!lcl_FillDPFieldPopupData(nDimIndex, pDPData->mpDPObj, *pDPData, bDimOrientNotPage)) + return; + + mpDPFieldPopup->clearMembers(); + + DPPopulateFieldMembers(pDPData->maLabels); + + mpDPFieldPopup->initMembers(); +} + +void ScGridWindow::UpdateDPFromFieldPopupMenu() +{ + typedef std::unordered_map MemNameMapType; + + if (!mpDPFieldPopup) + return; + + DPFieldPopupData* pDPData = static_cast(mpDPFieldPopup->getExtendedData()); + if (!pDPData) + return; + + ScDPObject* pDPObj = pDPData->mpDPObj; + ScDPSaveData* pSaveData = pDPObj->GetSaveData(); + + bool bIsDataLayout; + OUString aDimName = pDPObj->GetDimName(pDPData->mnDim, bIsDataLayout); + ScDPSaveDimension* pDim = pSaveData->GetDimensionByName(aDimName); + if (!pDim) + return; + + // Build a map of layout names to original names. + const ScDPLabelData& rLabelData = pDPData->maLabels; + MemNameMapType aMemNameMap; + for (const auto& rMember : rLabelData.maMembers) + aMemNameMap.emplace(rMember.maLayoutName, rMember.maName); + + // The raw result may contain a mixture of layout names and original names. + ScCheckListMenuControl::ResultType aRawResult; + mpDPFieldPopup->getResult(aRawResult); + + std::unordered_map aResult; + for (const auto& rItem : aRawResult) + { + MemNameMapType::const_iterator itrNameMap = aMemNameMap.find(rItem.aName); + if (itrNameMap == aMemNameMap.end()) + { + // This is an original member name. Use it as-is. + OUString aName = rItem.aName; + if (aName == ScResId(STR_EMPTYDATA)) + // Translate the special empty name into an empty string. + aName.clear(); + + aResult.emplace(aName, rItem.bValid); + } + else + { + // This is a layout name. Get the original member name and use it. + aResult.emplace(itrNameMap->second, rItem.bValid); + } + } + pDim->UpdateMemberVisibility(aResult); + + ScDBDocFunc aFunc(*mrViewData.GetDocShell()); + aFunc.UpdatePivotTable(*pDPObj, true, false); +} + +namespace { + +template +inline +T lcl_getValidValue(T value, T defvalue) +{ + return (value <0) ? defvalue : value; +} + +} // anonymous namespace + +bool ScGridWindow::UpdateVisibleRange() +{ + ScDocument const& rDoc = mrViewData.GetDocument(); + SCCOL nPosX = 0; + SCROW nPosY = 0; + SCCOL nXRight = rDoc.MaxCol(); + SCROW nYBottom = rDoc.MaxRow(); + + if (comphelper::LibreOfficeKit::isActive()) + { + ScTabViewShell* pViewShell = mrViewData.GetViewShell(); + nPosX = lcl_getValidValue(pViewShell->GetLOKStartHeaderCol(), nPosX); + nPosY = lcl_getValidValue(pViewShell->GetLOKStartHeaderRow(), nPosY); + nXRight = lcl_getValidValue(pViewShell->GetLOKEndHeaderCol(), nXRight); + nYBottom = lcl_getValidValue(pViewShell->GetLOKEndHeaderRow(), nYBottom); + } + else + { + nPosX = mrViewData.GetPosX(eHWhich); + nPosY = mrViewData.GetPosY(eVWhich); + nXRight = nPosX + mrViewData.VisibleCellsX(eHWhich); + if (nXRight > rDoc.MaxCol()) + nXRight = rDoc.MaxCol(); + nYBottom = nPosY + mrViewData.VisibleCellsY(eVWhich); + if (nYBottom > rDoc.MaxRow()) + nYBottom = rDoc.MaxRow(); + } + + // Store the current visible range. + return maVisibleRange.set(nPosX, nPosY, nXRight, nYBottom); +} + +void ScGridWindow::DPMouseMove( const MouseEvent& rMEvt ) +{ + DPTestMouse( rMEvt, true ); +} + +void ScGridWindow::DPMouseButtonUp( const MouseEvent& rMEvt ) +{ + bDPMouse = false; + ReleaseMouse(); + + DPTestMouse( rMEvt, false ); + SetPointer( PointerStyle::Arrow ); +} + +void ScGridWindow::UpdateDragRect( bool bShowRange, const tools::Rectangle& rPosRect ) +{ + SCCOL nStartX = ( rPosRect.Left() >= 0 ) ? static_cast(rPosRect.Left()) : SCCOL_MAX; + SCROW nStartY = ( rPosRect.Top() >= 0 ) ? static_cast(rPosRect.Top()) : SCROW_MAX; + SCCOL nEndX = ( rPosRect.Right() >= 0 ) ? static_cast(rPosRect.Right()) : SCCOL_MAX; + SCROW nEndY = ( rPosRect.Bottom() >= 0 ) ? static_cast(rPosRect.Bottom()) : SCROW_MAX; + + if ( bShowRange == bDragRect && nDragStartX == nStartX && nDragEndX == nEndX && + nDragStartY == nStartY && nDragEndY == nEndY ) + { + return; // everything unchanged + } + + if ( bShowRange ) + { + nDragStartX = nStartX; + nDragStartY = nStartY; + nDragEndX = nEndX; + nDragEndY = nEndY; + bDragRect = true; + } + else + bDragRect = false; + + UpdateDragRectOverlay(); +} + +// Page-Break Mode + +sal_uInt16 ScGridWindow::HitPageBreak( const Point& rMouse, ScRange* pSource, + SCCOLROW* pBreak, SCCOLROW* pPrev ) +{ + sal_uInt16 nFound = SC_PD_NONE; // 0 + ScRange aSource; + SCCOLROW nBreak = 0; + SCCOLROW nPrev = 0; + + ScPageBreakData* pPageData = mrViewData.GetView()->GetPageBreakData(); + if ( pPageData ) + { + bool bHori = false; + bool bVert = false; + SCCOL nHitX = 0; + SCROW nHitY = 0; + + tools::Long nMouseX = rMouse.X(); + tools::Long nMouseY = rMouse.Y(); + SCCOL nPosX; + SCROW nPosY; + mrViewData.GetPosFromPixel( nMouseX, nMouseY, eWhich, nPosX, nPosY ); + Point aTL = mrViewData.GetScrPos( nPosX, nPosY, eWhich ); + Point aBR = mrViewData.GetScrPos( nPosX+1, nPosY+1, eWhich ); + + // Horizontal more tolerances as for vertical, because there is more space + if ( nMouseX <= aTL.X() + 4 ) + { + bHori = true; + nHitX = nPosX; + } + else if ( nMouseX >= aBR.X() - 6 ) + { + bHori = true; + nHitX = nPosX+1; // left edge of the next cell + } + if ( nMouseY <= aTL.Y() + 2 ) + { + bVert = true; + nHitY = nPosY; + } + else if ( nMouseY >= aBR.Y() - 4 ) + { + bVert = true; + nHitY = nPosY+1; // upper edge of the next cell + } + + if ( bHori || bVert ) + { + sal_uInt16 nCount = sal::static_int_cast( pPageData->GetCount() ); + for (sal_uInt16 nPos=0; nPosGetData(nPos); + ScRange aRange = rData.GetPrintRange(); + bool bLHit = ( bHori && nHitX == aRange.aStart.Col() ); + bool bRHit = ( bHori && nHitX == aRange.aEnd.Col() + 1 ); + bool bTHit = ( bVert && nHitY == aRange.aStart.Row() ); + bool bBHit = ( bVert && nHitY == aRange.aEnd.Row() + 1 ); + bool bInsideH = ( nPosX >= aRange.aStart.Col() && nPosX <= aRange.aEnd.Col() ); + bool bInsideV = ( nPosY >= aRange.aStart.Row() && nPosY <= aRange.aEnd.Row() ); + + if ( bLHit ) + { + if ( bTHit ) + nFound = SC_PD_RANGE_TL; + else if ( bBHit ) + nFound = SC_PD_RANGE_BL; + else if ( bInsideV ) + nFound = SC_PD_RANGE_L; + } + else if ( bRHit ) + { + if ( bTHit ) + nFound = SC_PD_RANGE_TR; + else if ( bBHit ) + nFound = SC_PD_RANGE_BR; + else if ( bInsideV ) + nFound = SC_PD_RANGE_R; + } + else if ( bTHit && bInsideH ) + nFound = SC_PD_RANGE_T; + else if ( bBHit && bInsideH ) + nFound = SC_PD_RANGE_B; + if (nFound) + aSource = aRange; + + // breaks + + if ( bVert && bInsideH && !nFound ) + { + size_t nRowCount = rData.GetPagesY(); + const SCROW* pRowEnd = rData.GetPageEndY(); + for (size_t nRowPos=0; nRowPos+1= aSize.Width() ) + nDx = 1; + if ( aPos.Y() >= aSize.Height() ) + nDy = 1; + if ( nDx != 0 || nDy != 0 ) + { + if ( bPagebreakDrawn ) // invert + { + bPagebreakDrawn = false; + UpdateDragRectOverlay(); + } + + if ( nDx != 0 ) mrViewData.GetView()->ScrollX( nDx, WhichH(eWhich) ); + if ( nDy != 0 ) mrViewData.GetView()->ScrollY( nDy, WhichV(eWhich) ); + bTimer = true; + } + + // Switching when fixating (so Scrolling works) + + if ( eWhich == mrViewData.GetActivePart() ) //?? + { + if ( mrViewData.GetHSplitMode() == SC_SPLIT_FIX ) + if ( nDx > 0 ) + { + if ( eWhich == SC_SPLIT_TOPLEFT ) + mrViewData.GetView()->ActivatePart( SC_SPLIT_TOPRIGHT ); + else if ( eWhich == SC_SPLIT_BOTTOMLEFT ) + mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT ); + } + + if ( mrViewData.GetVSplitMode() == SC_SPLIT_FIX ) + if ( nDy > 0 ) + { + if ( eWhich == SC_SPLIT_TOPLEFT ) + mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMLEFT ); + else if ( eWhich == SC_SPLIT_TOPRIGHT ) + mrViewData.GetView()->ActivatePart( SC_SPLIT_BOTTOMRIGHT ); + } + } + + // from here new + + // Searching for a position between the cells (before nPosX / nPosY) + SCCOL nPosX; + SCROW nPosY; + mrViewData.GetPosFromPixel( aPos.X(), aPos.Y(), eWhich, nPosX, nPosY ); + bool bLeft, bTop; + mrViewData.GetMouseQuadrant( aPos, eWhich, nPosX, nPosY, bLeft, bTop ); + if ( !bLeft ) ++nPosX; + if ( !bTop ) ++nPosY; + + bool bBreak = ( nPagebreakMouse == SC_PD_BREAK_H || nPagebreakMouse == SC_PD_BREAK_V ); + bool bHide = false; + bool bToEnd = false; + ScRange aDrawRange = aPagebreakSource; + if ( bBreak ) + { + if ( nPagebreakMouse == SC_PD_BREAK_H ) + { + if ( nPosX > aPagebreakSource.aStart.Col() && + nPosX <= aPagebreakSource.aEnd.Col() + 1 ) // to the end is also allowed + { + bToEnd = ( nPosX == aPagebreakSource.aEnd.Col() + 1 ); + aDrawRange.aStart.SetCol( nPosX ); + aDrawRange.aEnd.SetCol( nPosX - 1 ); + } + else + bHide = true; + } + else + { + if ( nPosY > aPagebreakSource.aStart.Row() && + nPosY <= aPagebreakSource.aEnd.Row() + 1 ) // to the end is also allowed + { + bToEnd = ( nPosY == aPagebreakSource.aEnd.Row() + 1 ); + aDrawRange.aStart.SetRow( nPosY ); + aDrawRange.aEnd.SetRow( nPosY - 1 ); + } + else + bHide = true; + } + } + else + { + if ( nPagebreakMouse & SC_PD_RANGE_L ) + aDrawRange.aStart.SetCol( nPosX ); + if ( nPagebreakMouse & SC_PD_RANGE_T ) + aDrawRange.aStart.SetRow( nPosY ); + if ( nPagebreakMouse & SC_PD_RANGE_R ) + { + if ( nPosX > 0 ) + aDrawRange.aEnd.SetCol( nPosX-1 ); + else + bHide = true; + } + if ( nPagebreakMouse & SC_PD_RANGE_B ) + { + if ( nPosY > 0 ) + aDrawRange.aEnd.SetRow( nPosY-1 ); + else + bHide = true; + } + if ( aDrawRange.aStart.Col() > aDrawRange.aEnd.Col() || + aDrawRange.aStart.Row() > aDrawRange.aEnd.Row() ) + bHide = true; + } + + if ( !bPagebreakDrawn || bUp || aDrawRange != aPagebreakDrag ) + { + // draw... + + if ( bPagebreakDrawn ) + { + // invert + bPagebreakDrawn = false; + } + aPagebreakDrag = aDrawRange; + if ( !bUp && !bHide ) + { + // revert + bPagebreakDrawn = true; + } + UpdateDragRectOverlay(); + } + + // when ButtonUp execute the changes + + if ( bUp ) + { + ScViewFunc* pViewFunc = mrViewData.GetView(); + ScDocShell* pDocSh = mrViewData.GetDocShell(); + ScDocument& rDoc = pDocSh->GetDocument(); + SCTAB nTab = mrViewData.GetTabNo(); + bool bUndo (rDoc.IsUndoEnabled()); + + if ( bBreak ) + { + bool bColumn = ( nPagebreakMouse == SC_PD_BREAK_H ); + SCCOLROW nNew = bColumn ? static_cast(nPosX) : static_cast(nPosY); + if ( nNew != nPagebreakBreak ) + { + if (bUndo) + { + OUString aUndo = ScResId( STR_UNDO_DRAG_BREAK ); + pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, mrViewData.GetViewShell()->GetViewShellId() ); + } + + bool bGrow = !bHide && nNew > nPagebreakBreak; + if ( bColumn ) + { + if (rDoc.HasColBreak(static_cast(nPagebreakBreak), nTab) & ScBreakType::Manual) + { + ScAddress aOldAddr( static_cast(nPagebreakBreak), nPosY, nTab ); + pViewFunc->DeletePageBreak( true, true, &aOldAddr, false ); + } + if ( !bHide && !bToEnd ) // not at the end + { + ScAddress aNewAddr( static_cast(nNew), nPosY, nTab ); + pViewFunc->InsertPageBreak( true, true, &aNewAddr, false ); + } + if ( bGrow ) + { + // change last break to hard, and change scaling + bool bManualBreak(rDoc.HasColBreak(static_cast(nPagebreakPrev), nTab) & ScBreakType::Manual); + if ( static_cast(nPagebreakPrev) > aPagebreakSource.aStart.Col() && !bManualBreak ) + { + ScAddress aPrev( static_cast(nPagebreakPrev), nPosY, nTab ); + pViewFunc->InsertPageBreak( true, true, &aPrev, false ); + } + + if (!pDocSh->AdjustPrintZoom( ScRange( + static_cast(nPagebreakPrev),0,nTab, static_cast(nNew-1),0,nTab ) )) + bGrow = false; + } + } + else + { + if (rDoc.HasRowBreak(nPagebreakBreak, nTab) & ScBreakType::Manual) + { + ScAddress aOldAddr( nPosX, nPagebreakBreak, nTab ); + pViewFunc->DeletePageBreak( false, true, &aOldAddr, false ); + } + if ( !bHide && !bToEnd ) // not at the end + { + ScAddress aNewAddr( nPosX, nNew, nTab ); + pViewFunc->InsertPageBreak( false, true, &aNewAddr, false ); + } + if ( bGrow ) + { + // change last break to hard, and change scaling + bool bManualBreak(rDoc.HasRowBreak(nPagebreakPrev, nTab) & ScBreakType::Manual); + if ( nPagebreakPrev > aPagebreakSource.aStart.Row() && !bManualBreak ) + { + ScAddress aPrev( nPosX, nPagebreakPrev, nTab ); + pViewFunc->InsertPageBreak( false, true, &aPrev, false ); + } + + if (!pDocSh->AdjustPrintZoom( ScRange( + 0,nPagebreakPrev,nTab, 0,nNew-1,nTab ) )) + bGrow = false; + } + } + + if (bUndo) + { + pDocSh->GetUndoManager()->LeaveListAction(); + } + + if (!bGrow) // otherwise has already happened in AdjustPrintZoom + { + pViewFunc->UpdatePageBreakData( true ); + pDocSh->SetDocumentModified(); + } + } + } + else if ( bHide || aPagebreakDrag != aPagebreakSource ) + { + // set print range + + OUString aNewRanges; + sal_uInt16 nOldCount = rDoc.GetPrintRangeCount( nTab ); + if ( nOldCount ) + { + for (sal_uInt16 nPos=0; nPosFormat(rDoc, ScRefFlags::VALID); + else if ( !bHide ) + aTemp = aPagebreakDrag.Format(rDoc, ScRefFlags::VALID); + if (!aTemp.isEmpty()) + { + if ( !aNewRanges.isEmpty() ) + aNewRanges += ";"; + aNewRanges += aTemp; + } + } + } + } + else if (!bHide) + aNewRanges = aPagebreakDrag.Format(rDoc, ScRefFlags::VALID); + + pViewFunc->SetPrintRanges( rDoc.IsPrintEntireSheet( nTab ), &aNewRanges, nullptr, nullptr, false ); + } + } + + // Timer for Scrolling + + if (bTimer && !bUp) + mrViewData.GetView()->SetTimer( this, rMEvt ); // repeat event + else + mrViewData.GetView()->ResetTimer(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3