/* -*- 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: */