/* -*- 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 .
*/
ScExtraEditViewManager::~ScExtraEditViewManager()
{
DBG_ASSERT(nTotalWindows == 0, "ScExtraEditViewManager dtor: some out window has not yet been removed!");
}
mpOtherEditView = pOtherViewShell->GetViewData().GetEditView(eWhich); if (mpOtherEditView != nullptr)
{ for (int i = 0; i < 4; ++i)
{
ScGridWindow* pWin = mpGridWin[i].get(); if (pWin != nullptr)
{
Modifier<ModifierTag>(pWin);
}
}
}
}
template<ScExtraEditViewManager::ModifierTagType ModifierTag> void ScExtraEditViewManager::Modifier(ScGridWindow* /*pWin*/)
{
(void)this;
SAL_WARN("sc", "ScExtraEditViewManager::Modifier<ModifierTag>: non-specialized version should not be invoked.");
}
template<> void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Adder>(ScGridWindow* pWin)
{ if (mpOtherEditView->AddOtherViewWindow(pWin))
++nTotalWindows;
}
template<> void ScExtraEditViewManager::Modifier<ScExtraEditViewManager::Remover>(ScGridWindow* pWin)
{ if (mpOtherEditView->RemoveOtherViewWindow(pWin))
--nTotalWindows;
}
for (sal_uInt16 i = 0; i < 4; i++)
{ if (pGridWin[i] && pGridWin[i]->IsVisible())
pGridWin[i]->UpdateAutoFillMark( bMarked, aMarkRange );
}
for (sal_uInt16 i = 0; i < 2; i++)
{ if (pColBar[i] && pColBar[i]->IsVisible())
pColBar[i]->SetMark( bMarked, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col() ); if (pRowBar[i] && pRowBar[i]->IsVisible())
pRowBar[i]->SetMark( bMarked, aMarkRange.aStart.Row(), aMarkRange.aEnd.Row() );
}
// selection transfer object is checked together with AutoFill marks, // because it has the same requirement of a single continuous block. if (!bFromPaste)
CheckSelectionTransfer(); // update selection transfer object
}
void ScTabView::FakeButtonUp( ScSplitPos eWhich )
{ if (pGridWin[eWhich])
pGridWin[eWhich]->FakeButtonUp();
}
void ScTabView::HideAllCursors()
{ for (VclPtr<ScGridWindow> & pWin : pGridWin)
{ if (pWin && pWin->IsVisible())
{
vcl::Cursor* pCur = pWin->GetCursor(); if (pCur && pCur->IsVisible())
pCur->Hide();
pWin->HideCursor();
}
}
}
void ScTabView::ShowAllCursors()
{ for (VclPtr<ScGridWindow> & pWin : pGridWin)
{ if (pWin && pWin->IsVisible())
{
pWin->ShowCursor();
pWin->CursorChanged();
}
}
}
// FIXME: this is to limit the number of rows handled in the Online // to 1000; this will be removed again when the performance // bottlenecks are sorted out if (comphelper::LibreOfficeKit::isActive())
nPosY = std::min(nPosY, MAXTILEDROW);
Size aNewSize(0, 0); if (pModelObj)
aNewSize = pModelObj->getDocumentSize();
if (pModelObj)
{
ScGridWindow* pGridWindow = aViewData.GetActiveWin(); if (pGridWindow)
{
Size aNewSizePx(aNewSize.Width() * aViewData.GetPPTX(), aNewSize.Height() * aViewData.GetPPTY()); if (aNewSizePx != pGridWindow->GetOutputSizePixel())
pGridWindow->SetOutputSizePixel(aNewSizePx);
}
}
if (aOldSize == aNewSize) return;
// New area extended to the right of the sheet after last column // including overlapping area with aNewRowArea
tools::Rectangle aNewColArea(aOldSize.getWidth(), 0, aNewSize.getWidth(), aNewSize.getHeight()); // New area extended to the bottom of the sheet after last row // excluding overlapping area with aNewColArea
tools::Rectangle aNewRowArea(0, aOldSize.getHeight(), aOldSize.getWidth(), aNewSize.getHeight());
// Only invalidate if spreadsheet extended to the right if (aNewColArea.getOpenWidth())
{
SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), &aNewColArea);
}
// Only invalidate if spreadsheet extended to the bottom if (aNewRowArea.getOpenHeight())
{
SfxLokHelper::notifyInvalidation(aViewData.GetViewShell(), &aNewRowArea);
}
// Provide size in the payload, so clients don't have to // call lok::Document::getDocumentSize().
std::stringstream ss;
ss << aNewSize.Width() << ", " << aNewSize.Height();
OString sSize( ss.str() );
ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(aViewData.GetViewShell()->GetCurrentDocument());
SfxLokHelper::notifyDocumentSizeChanged(aViewData.GetViewShell(), sSize, pModel, false);
}
void ScTabView::CheckSelectionTransfer()
{ if ( !aViewData.IsActive() ) // only for active view return;
ScModule* pScMod = ScModule::get();
ScSelectionTransferObj* pOld = pScMod->GetSelectionTransfer();
rtl::Reference<ScSelectionTransferObj> pNew = ScSelectionTransferObj::CreateFromView( this ); if ( !pNew ) return;
// create new selection
if (pOld)
pOld->ForgetView();
pScMod->SetSelectionTransfer( pNew.get() );
// tdf#124975/tdf#136242 changing the calc selection can trigger removal of the // selection of an open RefDlg dialog, so don't inform the // desktop clipboard of the changed selection if that dialog is open if (!lcl_IsRefDlgActive(aViewData.GetViewShell()->GetViewFrame()))
pNew->CopyToPrimarySelection(); // may delete pOld
// First, see if we can fit the entire hint window in the visible region.
if (nMRight - nMargin >= rHintWndSize.Width())
{ // Right margin is wide enough. if (rFrameWndSize.Height() >= rHintWndSize.Height())
{ // The frame has enough height. Take it.
Point aPos = rCellPos;
aPos.AdjustX(rCellSize.Width() + nMargin ); if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
{ // Push the hint window up a bit to make it fit.
aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
} return aPos;
}
}
if (nMBottom - nMargin >= rHintWndSize.Height())
{ // Bottom margin is high enough. if (rFrameWndSize.Width() >= rHintWndSize.Width())
{ // The frame has enough width. Take it.
Point aPos = rCellPos;
aPos.AdjustY(rCellSize.Height() + nMargin ); if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
{ // Move the hint window to the left to make it fit.
aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
} return aPos;
}
}
if (nMLeft - nMargin >= rHintWndSize.Width())
{ // Left margin is wide enough. if (rFrameWndSize.Height() >= rHintWndSize.Height())
{ // The frame is high enough. Take it.
Point aPos = rCellPos;
aPos.AdjustX( -(rHintWndSize.Width() + nMargin) ); if (aPos.Y() + rHintWndSize.Height() > rFrameWndSize.Height())
{ // Push the hint window up a bit to make it fit.
aPos.setY( rFrameWndSize.Height() - rHintWndSize.Height() );
} return aPos;
}
}
if (nMTop - nMargin >= rHintWndSize.Height())
{ // Top margin is high enough. if (rFrameWndSize.Width() >= rHintWndSize.Width())
{ // The frame is wide enough. Take it.
Point aPos = rCellPos;
aPos.AdjustY( -(rHintWndSize.Height() + nMargin) ); if (aPos.X() + rHintWndSize.Width() > rFrameWndSize.Width())
{ // Move the hint window to the left to make it fit.
aPos.setX( rFrameWndSize.Width() - rHintWndSize.Width() );
} return aPos;
}
}
// The popup doesn't fit in any direction in its entirety. Do our best.
if (nMRight - nMargin >= rHintWndSize.Width())
{ // Right margin is good enough.
Point aPos = rCellPos;
aPos.AdjustX(nMargin + rCellSize.Width() );
aPos.setY( 0 ); return aPos;
}
if (nMBottom - nMargin >= rHintWndSize.Height())
{ // Bottom margin is good enough.
Point aPos = rCellPos;
aPos.AdjustY(nMargin + rCellSize.Height() );
aPos.setX( 0 ); return aPos;
}
if (nMLeft - nMargin >= rHintWndSize.Width())
{ // Left margin is good enough.
Point aPos = rCellPos;
aPos.AdjustX( -(rHintWndSize.Width() + nMargin) );
aPos.setY( 0 ); return aPos;
}
if (nMTop - nMargin >= rHintWndSize.Height())
{ // Top margin is good enough.
Point aPos = rCellPos;
aPos.AdjustY( -(rHintWndSize.Height() + nMargin) );
aPos.setX( 0 ); return aPos;
}
// None of the above. Hopeless. At least try not to cover the current // cell.
Point aPos = rCellPos;
aPos.AdjustX(rCellSize.Width() ); return aPos;
}
}
void ScTabView::TestHintWindow()
{ // show input help window and list drop-down button for validity
Point aHintPos = calcHintWindowPosition(
aPos, Size(nCellSizeX,nCellSizeY), aWinSize, aHintWndSize);
pOverlay->SetPos(pWin->PixelToLogic(aHintPos, pWin->GetDrawMapMode()), pWin->GetDrawMapMode()); for (VclPtr<ScGridWindow> & pWindow : pGridWin)
{ if (!pWindow) continue; if (!pWindow->IsVisible()) continue;
rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = pWindow->getOverlayManager(); if (!xOverlayManager.is()) continue; if (pWindow == pWin)
{
xOverlayManager->add(*pOverlay);
pWindow->updateLOKInputHelp(aTitle, aMessage);
} else
{ //tdf#92530 if the help tip doesn't fit into its allocated area in a split window //scenario, then because here we place it into the other split windows as well the //missing portions will be displayed in the other split windows to form an apparent //single tip, albeit "under" the split lines
Point aOtherPos(pWindow->ScreenToOutputPixel(pWin->OutputToScreenPixel(aHintPos)));
std::unique_ptr<ScOverlayHint> pOtherOverlay(new ScOverlayHint(aTitle, aMessage,
aCommentBack,
aCommentText,
pFrameWin->GetFont()));
Point aFooPos(pWindow->PixelToLogic(aOtherPos, pWindow->GetDrawMapMode()));
pOtherOverlay->SetPos(aFooPos, pWindow->GetDrawMapMode());
xOverlayManager->add(*pOtherOverlay);
mxInputHintOO->append(std::move(pOtherOverlay));
}
}
}
}
// find window that should not be over the cursor static weld::Window* lcl_GetCareWin(SfxViewFrame& rViewFrm)
{ //! also spelling ??? (then set the member variables when calling)
// search & replace if (rViewFrm.HasChildWindow(SID_SEARCH_DLG))
{
SfxChildWindow* pChild = rViewFrm.GetChildWindow(SID_SEARCH_DLG); if (pChild)
{ auto xDlgController = pChild->GetController(); if (xDlgController && xDlgController->getDialog()->get_visible()) return xDlgController->getDialog();
}
}
// apply changes if ( rViewFrm.HasChildWindow(FID_CHG_ACCEPT) )
{
SfxChildWindow* pChild = rViewFrm.GetChildWindow(FID_CHG_ACCEPT); if (pChild)
{ auto xDlgController = pChild->GetController(); if (xDlgController && xDlgController->getDialog()->get_visible()) return xDlgController->getDialog();
}
}
return nullptr;
}
// adjust screen with respect to cursor position
void ScTabView::AlignToCursor( SCCOL nCurX, SCROW nCurY, ScFollowMode eMode, const ScSplitPos* pWhich )
{ // now switch active part here
tools::Long nDenom; if ( eMode == SC_FOLLOW_JUMP_END && nCurX > aViewData.GetRefStartX()
&& nCurY > aViewData.GetRefStartY() )
nDenom = 1; // tdf#154271 Selected cell will be at the bottom corner // to maximize the visible/usable area else
nDenom = 2; // Selected cell will be at the center of the screen, so that // it will be visible. This is useful for search results, etc.
tools::Long nSpaceX = ( aScrSize.Width() - nCellSizeX ) / nDenom;
tools::Long nSpaceY = ( aScrSize.Height() - nCellSizeY ) / nDenom; // nSpaceY: desired start position of cell for FOLLOW_JUMP, modified if dialog interferes
bool bForceNew = false; // force new calculation of JUMP position (vertical only)
// when for instance a search dialog is open, don't put the cursor behind the dialog // if possible, put the row with the cursor above or below the dialog //! not if already completely visible
if ( eMode == SC_FOLLOW_JUMP || eMode == SC_FOLLOW_JUMP_END )
{
weld::Window* pCare = lcl_GetCareWin( aViewData.GetViewShell()->GetViewFrame() ); if (pCare)
{ bool bLimit = false;
tools::Rectangle aDlgPixel;
Size aWinSize;
vcl::Window* pWin = GetActiveWin();
weld::Window* pFrame = pWin ? pWin->GetFrameWeld() : nullptr; int x, y, width, height; if (pFrame && pCare->get_extents_relative_to(*pFrame, x, y, width, height))
{
aDlgPixel = tools::Rectangle(Point(x, y), Size(width, height));
aWinSize = pWin->GetOutputSizePixel(); // dos the dialog cover the GridWin? if ( aDlgPixel.Right() >= 0 && aDlgPixel.Left() < aWinSize.Width() )
{ if ( nCurX < nDeltaX || nCurX >= nDeltaX+nSizeX ||
nCurY < nDeltaY || nCurY >= nDeltaY+nSizeY )
bLimit = true; // scroll anyway else
{ // cursor is on the screen
Point aStart = aViewData.GetScrPos( nCurX, nCurY, eAlign );
tools::Long nCSX, nCSY;
aViewData.GetMergeSizePixel( nCurX, nCurY, nCSX, nCSY );
tools::Rectangle aCursor( aStart, Size( nCSX, nCSY ) ); if ( aCursor.Overlaps( aDlgPixel ) )
bLimit = true; // cell is covered by the dialog
}
}
}
if (nCurX < 0) nCurX = 0; if (nCurY < 0) nCurY = 0; if (nCurX > rDoc.MaxCol()) nCurX = rDoc.MaxCol(); if (nCurY > rDoc.MaxRow()) nCurY = rDoc.MaxRow();
// FIXME: this is to limit the number of rows handled in the Online // to 1000; this will be removed again when the performance // bottlenecks are sorted out if (comphelper::LibreOfficeKit::isActive())
nCurY = std::min(nCurY, MAXTILEDROW);
HideAllCursors();
// switch of active now in AlignToCursor
AlignToCursor( nCurX, nCurY, eMode );
if (bKeepSel)
{
SetCursor( nCurX, nCurY ); // keep selection
// If the cursor is in existing selection, it's a cursor movement by // ENTER or TAB. If not, then it's a new selection during ADD // selection mode.
const ScMarkData& rMark = aViewData.GetMarkData();
ScRangeList aSelList;
rMark.FillRangeListWithMarks(&aSelList, false); if (!aSelList.Contains(ScRange(nCurX, nCurY, aViewData.GetTabNo()))) // Cursor not in existing selection. Start a new selection.
DoneBlockMode(true);
} else
{ if (!bShift)
{ // Remove all marked data on cursor movement unless the Shift is // locked or while editing a formula. It is cheaper to check for // marks first and then formula mode.
ScMarkData& rMark = aViewData.GetMarkData(); bool bMarked = rMark.IsMarked() || rMark.IsMultiMarked(); if (bMarked && !ScModule::get()->IsFormulaMode())
{
rMark.ResetMark();
DoneBlockMode();
InitOwnBlockMode( ScRange( nCurX, nCurY, aViewData.GetTabNo()));
MarkDataChanged();
}
}
// If the cursor has not been moved, the SelectionChanged for canceling the // selection has to happen here individually: if (bSame)
SelectionChanged();
}
if (nMovX < 0 && nOldX == 0)
{ // trying to go left from 1st column if (nMovY == 0) // done, because no vertical move is requested return;
} if (nMovY < 0 && nOldY == 0)
{ // trying to go up from 1st row if (nMovX == 0) // done, because no horizontal move is requested return;
}
aViewData.ResetOldCursor();
if (nMovX != 0 && rDoc.ValidColRow(nCurX,nCurY))
SkipCursorHorizontal(nCurX, nCurY, nOldX, nMovX);
if (nMovY != 0 && rDoc.ValidColRow(nCurX,nCurY))
SkipCursorVertical(nCurX, nCurY, nOldY, nMovY);
// update input line even if cursor was not moved if ( nNewX == nCurX && nNewY == nCurY )
aViewData.UpdateInputHandler(true);
} else
{ // After Tab and Enter back to the starting column again. const SCCOL nTabStartCol = ((nMoveY != 0 && !nMoveX) ? aViewData.GetTabStartCol() : SC_TABSTART_NONE);
rDoc.GetNextPos( nNewX, nNewY, nTab, nMoveX, nMoveY, false, true, rMark, nTabStartCol );
if (!bNew && nTab == aViewData.GetTabNo()) return;
// FormShell would like to be informed before the switch
FmFormShell* pFormSh = aViewData.GetViewShell()->GetFormShell(); if (pFormSh)
{ bool bAllowed = pFormSh->PrepareClose(); if (!bAllowed)
{ //! error message? or does FormShell do it? //! return error flag and cancel actions
return; // FormShell says that it can not be switched
}
}
// not InputEnterHandler due to reference input
ScDocument& rDoc = aViewData.GetDocument();
rDoc.MakeTable( nTab );
// Update pending row heights before switching the sheet, so Reschedule from the progress bar // doesn't paint the new sheet with old heights
aViewData.GetDocShell().UpdatePendingRowHeights( nTab );
SCTAB nTabCount = rDoc.GetTableCount();
SCTAB nOldPos = nTab; while (!rDoc.IsVisible(nTab)) // search for next visible
{ bool bUp = (nTab>=nOldPos); if (bUp)
{
++nTab; if (nTab>=nTabCount)
{
nTab = nOldPos;
bUp = false;
}
}
if (!bUp)
{ if (nTab != 0)
--nTab; else
{
OSL_FAIL("no visible sheets");
rDoc.SetVisible( 0, true );
}
}
}
// #i71490# Deselect drawing objects before changing the sheet number in view data, // so the handling of notes still has the sheet selected on which the notes are.
DrawDeselectAll();
ScModule* pScMod = ScModule::get(); bool bRefMode = pScMod->IsFormulaMode(); if ( !bRefMode ) // query, so that RefMode works when switching sheet
{
DoneBlockMode();
pSelEngine->Reset(); // reset all flags, including locked modifiers
aViewData.SetRefTabNo( nTab );
}
aViewData.SetTabNo( nTab ); if (mpSpellCheckCxt)
mpSpellCheckCxt->setTabNo( nTab ); // UpdateShow before SetCursor, so that UpdateAutoFillMark finds the correct // window (is called from SetCursor)
UpdateShow();
aViewData.GetView()->TestHintWindow();
bool bAllSelected = true; for (SCTAB nSelTab = 0; nSelTab < nTabCount; ++nSelTab)
{ if (!rDoc.IsVisible(nSelTab) || rMark.GetTableSelect(nSelTab))
{ if (nTab == nSelTab) // This tab is already in selection. Keep the current // selection.
bExtendSelection = true;
} else
{
bAllSelected = false; if (bExtendSelection) // We got what we need. No need to stay in the loop. break;
}
} if (bAllSelected && !bNew) // #i6327# if all tables are selected, a selection event (#i6330#) will deselect all // (not if called with bNew to update settings)
bExtendSelection = false;
// recalc zoom-dependent values (before TabChanged, before UpdateEditViewPos)
RefreshZoom(/*bRecalcScale*/false); // no need to call RecalcScale() here, because we will do it in TabChanged()
UpdateVarZoom();
if ( bRefMode ) // hide EditView if necessary (after aViewData.SetTabNo !)
{ for (VclPtr<ScGridWindow> & pWin : pGridWin)
{ if (pWin && pWin->IsVisible())
pWin->UpdateEditViewPos();
}
}
aViewData.GetViewShell()->WindowChanged(); // if the active window has changed
aViewData.ResetOldCursor();
SetCursor( aViewData.GetCurX(), aViewData.GetCurY(), true );
if ( !bUnoRefDialog )
aViewData.GetViewShell()->DisconnectAllClients(); // important for floating frames else
{ // hide / show inplace client
ScClient* pClient = static_cast<ScClient*>(aViewData.GetViewShell()->GetIPClient()); if ( pClient && pClient->IsObjectInPlaceActive() )
{
tools::Rectangle aObjArea = pClient->GetObjArea(); if ( nTab == aViewData.GetRefTabNo() )
{ // move to its original position
SdrOle2Obj* pDrawObj = pClient->GetDrawObj(); if ( pDrawObj )
{
tools::Rectangle aRect = pDrawObj->GetLogicRect();
MapMode aMapMode( MapUnit::Map100thMM );
Size aOleSize = pDrawObj->GetOrigObjSize( &aMapMode );
aRect.SetSize( aOleSize );
aObjArea = aRect;
}
} else
{ // move to an invisible position
if ( bFocus && aViewData.GetActivePart() != eOldActive && !bRefMode )
ActiveGrabFocus(); // grab focus to the pane that's active now
// freeze
bool bResize = false; if ( aViewData.GetHSplitMode() == SC_SPLIT_FIX ) if (aViewData.UpdateFixX())
bResize = true; if ( aViewData.GetVSplitMode() == SC_SPLIT_FIX ) if (aViewData.UpdateFixY())
bResize = true; if (bResize)
RepeatResize();
InvalidateSplit();
if ( aViewData.IsPagebreakMode() )
UpdatePageBreakData(); //! asynchronously ??
// Form Layer must know the visible area of the new sheet // that is why MapMode must already be correct here
SyncGridWindowMapModeFromDrawMapMode();
SetNewVisArea();
if (pThisViewShell->GetInputHandler())
pThisViewShell->GetInputHandler()->UpdateLokReferenceMarks();
}
// TextEditOverlayObject for TextOnOverlay TextEdit. It directly // implements needed EditViewCallbacks and also hosts the // OverlaySelection namespace
{ class ScTextEditOverlayObject : public sdr::overlay::OverlayObject, public EditViewCallbacks
{ // the ScTabView the TextEdit is running at and the ScSplitPos to // identify the associated data
ScTabView& mrScTabView;
ScSplitPos maScSplitPos;
// this separate OverlayObject holds and creates the selection // visualization, so it can be changed/refreshed without changing // the Text or TextEditBackground
std::unique_ptr<sdr::overlay::OverlaySelection> mxOverlayTransparentSelection;
// geometry creation for OverlayObject, in this case the extraction // of edited Text from the setup EditEngine virtual drawinglayer::primitive2d::Primitive2DContainer createOverlayObjectPrimitive2DSequence() override;
public: // create using system selection color & ScTabView
ScTextEditOverlayObject( const Color& rColor,
ScTabView& rScTabView,
ScSplitPos aScSplitPos); virtual ~ScTextEditOverlayObject() override;
// override to mix in TextEditBackground and the Text transformed // as needed virtual drawinglayer::primitive2d::Primitive2DContainer getOverlayObjectPrimitive2DSequence() const override;
// access to OverlaySelection to add to OverlayManager
sdr::overlay::OverlayObject* getOverlaySelection()
{ return mxOverlayTransparentSelection.get();
}
void RefeshTextEditOverlay()
{ // currently just deletes all created stuff, this may // be fine-tuned later if needed
objectChange();
}
};
ScTextEditOverlayObject::ScTextEditOverlayObject( const Color& rColor,
ScTabView& rScTabView,
ScSplitPos aScSplitPos)
: OverlayObject(rColor)
, mrScTabView(rScTabView)
, maScSplitPos(aScSplitPos)
{ // no AA for TextEdit overlay
allowAntiAliase(false);
// establish EditViewCallbacks const ScViewData& rScViewData(mrScTabView.GetViewData());
EditView* pEditView(rScViewData.GetEditView(maScSplitPos));
DBG_ASSERT(nullptr != pEditView, "NO access to EditView in ScTextEditOverlayObject!");
pEditView->setEditViewCallbacks(this);
ScTextEditOverlayObject::~ScTextEditOverlayObject()
{ // delete OverlaySelection - this will also remove itself from // OverlayManager already
mxOverlayTransparentSelection.reset();
// shutdown EditViewCallbacks const ScViewData& rScViewData(mrScTabView.GetViewData());
EditView* pEditView(rScViewData.GetEditView(maScSplitPos));
DBG_ASSERT(nullptr != pEditView, "NO access to EditView in ScTextEditOverlayObject!");
pEditView->setEditViewCallbacks(nullptr);
// remove myself if (getOverlayManager())
getOverlayManager()->remove(*this);
}
drawinglayer::primitive2d::Primitive2DContainer ScTextEditOverlayObject::createOverlayObjectPrimitive2DSequence()
{ // extract primitive representation from active EditEngine
drawinglayer::primitive2d::Primitive2DContainer aRetval;
ScViewData& rScViewData(mrScTabView.GetViewData()); const EditView* pEditView(rScViewData.GetEditView(maScSplitPos));
assert(pEditView && "NO access to EditView in ScTextEditOverlayObject!");
// use no transformations. The result will be in logic coordinates // based on aEditRectangle and the EditEngine setup, see // ScViewData::SetEditEngine
basegfx::B2DHomMatrix aNewTransformA;
basegfx::B2DHomMatrix aNewTransformB;
// get text data in LogicMode
OutputDevice& rOutDev(pEditView->GetOutputDevice()); const MapMode aOrig(rOutDev.GetMapMode());
rOutDev.SetMapMode(rScViewData.GetLogicMode());
void ScTextEditOverlayObject::EditViewInvalidate(const tools::Rectangle& rRect)
{ if (comphelper::LibreOfficeKit::isActive())
{ // UT testPageDownInvalidation from CppunitTest_sc_tiledrendering // *needs* the direct invalidates formerly done in // EditView::InvalidateWindow when no EditViewCallbacks are set
ScGridWindow* pActiveWin(static_cast<ScGridWindow*>(mrScTabView.GetWindowByPos(maScSplitPos)));
pActiveWin->Invalidate(rRect);
}
RefeshTextEditOverlay();
}
void ScTextEditOverlayObject::EditViewSelectionChange()
{
ScViewData& rScViewData(mrScTabView.GetViewData());
EditView* pEditView(rScViewData.GetEditView(maScSplitPos));
assert(pEditView && "NO access to EditView in ScTextEditOverlayObject!");
// get the selection rectangles
std::vector<tools::Rectangle> aRects;
pEditView->GetSelectionRectangles(aRects);
std::vector<basegfx::B2DRange> aLogicRanges;
if (aRects.empty())
{ // if none, reset selection at OverlayObject and we are done
mxOverlayTransparentSelection->setRanges(std::move(aLogicRanges)); return;
}
ScViewData& rScViewData(mrScTabView.GetViewData());
EditView* pEditView(rScViewData.GetEditView(maScSplitPos));
assert(pEditView && "NO access to EditView in ScTextEditOverlayObject!");
// call base implementation to get TextPrimitives in logic coordinates // directly from the setup EditEngine
drawinglayer::primitive2d::Primitive2DContainer aText(
OverlayObject::getOverlayObjectPrimitive2DSequence());
if (aText.empty()) // no Text, done, return result return aRetval;
// remember MapMode and restore at exit - we do not want // to change it outside this method
OutputDevice& rOutDev(pEditView->GetOutputDevice()); const MapMode aOrig(rOutDev.GetMapMode());
// create text edit background based on pixel coordinates // of involved Cells and append
{ const SCCOL nCol1(rScViewData.GetEditStartCol()); const SCROW nRow1(rScViewData.GetEditStartRow()); const SCCOL nCol2(rScViewData.GetEditEndCol()); const SCROW nRow2(rScViewData.GetEditEndRow()); const Point aStart(rScViewData.GetScrPos(nCol1, nRow1, maScSplitPos)); const Point aEnd(rScViewData.GetScrPos(nCol2+1, nRow2+1, maScSplitPos));
if (aStart != aEnd)
{ // create outline polygon. Shrink by 1px due to working // with pixel positions one cell right and below. We are // in discrete (pixel) coordinates with start/end here, // so just subtract '1' from x and y to do that
basegfx::B2DPolyPolygon aOutline(basegfx::utils::createPolygonFromRect(
basegfx::B2DRange(
aStart.X(), aStart.Y(),
aEnd.X() - 1, aEnd.Y() - 1)));
// transform from Pixels to LogicDrawCoordinates
ScGridWindow* pActiveWin(static_cast<ScGridWindow*>(mrScTabView.GetWindowByPos(maScSplitPos)));
rOutDev.SetMapMode(pActiveWin->GetDrawMapMode());
aOutline.transform(rOutDev.GetInverseViewTransformation());
aRetval.push_back(
rtl::Reference<drawinglayer::primitive2d::PolyPolygonColorPrimitive2D>( new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(
std::move(aOutline),
pEditView->GetBackgroundColor().getBColor())));
}
}
// create embedding transformation for Text itself and // append Text
{
basegfx::B2DHomMatrix aTransform;
// transform by TextPaint StartPosition (Top-Left). This corresponds // to aEditRectangle and the EditEngine setup, see // ScViewData::SetEditEngine. Offset is in LogicCoordinates/LogicMode const Point aStartPosition(pEditView->CalculateTextPaintStartPosition());
aTransform.translate(aStartPosition.X(), aStartPosition.Y());
// add text embedded to created transformation
aRetval.push_back(rtl::Reference<drawinglayer::primitive2d::TransformPrimitive2D>( new drawinglayer::primitive2d::TransformPrimitive2D(
aTransform, std::move(aText))));
}
rOutDev.SetMapMode(aOrig); return aRetval;
}
} // end of anonymous namespace
void ScTabView::RefeshTextEditOverlay()
{ // find the ScTextEditOverlayObject in the OverlayGroup and // call refresh there. It is also possible to have separate // holders of that data, so no find/identification would be // needed, but having all associated OverlayObjects in one // group makes handling simple(r). It may also be that the // cursor visualization will be added to that group later for (sal_uInt32 a(0); a < maTextEditOverlayGroup.count(); a++)
{
sdr::overlay::OverlayObject& rOverlayObject(maTextEditOverlayGroup.getOverlayObject(a));
ScTextEditOverlayObject* pScTextEditOverlayObject(dynamic_cast<ScTextEditOverlayObject*>(&rOverlayObject));
if (nullptr != pScTextEditOverlayObject)
{
pScTextEditOverlayObject->RefeshTextEditOverlay();
}
}
}
// for the active part, create edit view even if outside the visible area, // so input isn't lost (and the edit view may be scrolled into the visible area)
// #i26433# during spelling, the spelling view must be active if ( bPosVisible || aViewData.GetActivePart() == aScSplitPos ||
( pSpellingView && aViewData.GetEditView(aScSplitPos) == pSpellingView ) )
{
VclPtr<ScGridWindow> pScGridWindow(pGridWin[aScSplitPos]);
pScGridWindow->HideCursor();
pScGridWindow->DeleteCursorOverlay();
pScGridWindow->DeleteAutoFillOverlay();
pScGridWindow->DeleteCopySourceOverlay();
// MapMode must be set after HideCursor
pScGridWindow->SetMapMode(aViewData.GetLogicMode());
if ( !bPosVisible )
{ // move the edit view area to the real (possibly negative) position, // or hide if completely above or left of the window
pScGridWindow->UpdateEditViewPos();
}
// get OverlayManager and initialize TextEditOnOverlay for it
rtl::Reference<sdr::overlay::OverlayManager> xOverlayManager = pScGridWindow->getOverlayManager();
if (xOverlayManager.is() && aViewData.HasEditView(aScSplitPos))
{ const Color aHilightColor(SvtOptionsDrawinglayer::getHilightColor());
std::unique_ptr<ScTextEditOverlayObject> pNewScTextEditOverlayObject( new ScTextEditOverlayObject(
aHilightColor,
*this,
aScSplitPos));
// add TextEditOverlayObject and the OverlaySelection hosted by it // to the OverlayManager (to make visible) and to the local // reference TextEditOverlayGroup (to access if needed)
xOverlayManager->add(*pNewScTextEditOverlayObject);
xOverlayManager->add(*pNewScTextEditOverlayObject->getOverlaySelection());
maTextEditOverlayGroup.append(std::move(pNewScTextEditOverlayObject));
}
}
}
}
if (aViewData.GetViewShell()->HasAccessibilityObjects())
aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccEnterEditMode));
}
void ScTabView::UpdateEditView()
{ if (aViewData.GetTabNo() != aViewData.GetRefTabNo() && ScModule::get()->IsFormulaMode()) return;
ScSplitPos eActive = aViewData.GetActivePart(); for (sal_uInt16 i = 0; i < 4; i++)
{
ScSplitPos eCurrent = ScSplitPos(i); if (aViewData.HasEditView(eCurrent))
{
EditView* pEditView = aViewData.GetEditView(eCurrent);
// this cleans up all used OverlayObjects for TextEdit, they get deleted // and removed from the OverlayManager what makes them optically disappear
maTextEditOverlayGroup.clear();
// notify accessibility before all things happen if (bNotifyAcc && aViewData.GetViewShell()->HasAccessibilityObjects())
aViewData.GetViewShell()->BroadcastAccessibility(SfxHint(SfxHintId::ScAccLeaveEditMode));
aViewData.ResetEditView(); for (sal_uInt16 i = 0; i < 4; i++)
{ if (pGridWin[i] && bPaint[i] && pGridWin[i]->IsVisible())
{
pGridWin[i]->ShowCursor();
if (comphelper::LibreOfficeKit::isActive())
{
pGridWin[i]->LogicInvalidatePart(&rInvRect, nTab);
// invalidate other views auto lInvalidateWindows =
[nTab, &rInvRect] (ScTabView* pTabView)
{ for (VclPtr<ScGridWindow> const & pWin: pTabView->pGridWin)
{ if (pWin)
pWin->LogicInvalidatePart(&rInvRect, nTab);
}
};
SfxLokHelper::forEachOtherView(GetViewData().GetViewShell(), lInvalidateWindows);
} // #i73567# the cell still has to be repainted else
{ constbool bDoPaint = bExtended || (bAtCursor && !bNoPaint); constbool bDoInvalidate = !bDoPaint && bAtCursor; if (bDoPaint)
{
pGridWin[i]->Draw( nCol1, nRow1, nCol2, nRow2, ScUpdateMode::All );
pGridWin[i]->UpdateSelectionOverlay();
} elseif (bDoInvalidate)
{ // tdf#162651 even if !bNoPaint is set, and there will be a // follow up Draw of the next content, the area blanked out // by the editview which is being removed still needs to be // invalidated. The follow-up Draw of the content may be // optimized to only redraw the area of cells where content // has changed and will be unaware of what bounds this // editview grew to during its editing cycle.
pGridWin[i]->Invalidate(rInvRect);
}
}
}
}
if (pDrawView)
DrawEnableAnim( true );
// GrabFocus always when this View is active and // when the input row has the focus
bool bGrabFocus = false; if (aViewData.IsActive())
{
ScInputHandler* pInputHdl = ScModule::get()->GetInputHdl(); if ( pInputHdl )
{
ScInputWindow* pInputWin = pInputHdl->GetInputWindow(); if (pInputWin && pInputWin->IsInputActive())
bGrabFocus = true;
}
}
if (bGrabFocus)
{ // should be done like this, so that Sfx notice it, but it does not work: //! aViewData.GetViewShell()->GetViewFrame().GetWindow().GrabFocus(); // therefore first like this:
GetActiveWin()->GrabFocus();
}
// cursor query only after GrabFocus
for (sal_uInt16 i = 0; i < 4; i++)
{ if (pGridWin[i] && pGridWin[i]->IsVisible())
{
vcl::Cursor* pCur = pGridWin[i]->GetCursor(); if (pCur && pCur->IsVisible())
pCur->Hide();
if (bPaint[i])
{
pGridWin[i]->UpdateCursorOverlay();
pGridWin[i]->UpdateAutoFillOverlay();
}
}
}
}
for (sal_uInt16 i = 0; i < 4; i++)
{ if (pGridWin[i] && pGridWin[i]->IsVisible())
pGridWin[i]->UpdateFormulas(nStartCol, nStartRow, nEndCol, nEndRow);
}
if ( aViewData.IsPagebreakMode() )
UpdatePageBreakData(); //! asynchronous
bool bIsTiledRendering = comphelper::LibreOfficeKit::isActive(); // UpdateHeaderWidth can fit the GridWindow widths to the frame, something // we don't want in kit-mode where we try and match the GridWindow width // to the tiled area separately if (!bIsTiledRendering)
UpdateHeaderWidth();
// if in edit mode, adjust edit view area because widths/heights may have changed if ( aViewData.HasEditView( aViewData.GetActivePart() ) )
UpdateEditView();
}
Point aStart = aViewData.GetScrPos( nCol1, nRow1, static_cast<ScSplitPos>(i) );
Point aEnd = aViewData.GetScrPos( nCol2+1, nRow2+1, static_cast<ScSplitPos>(i) );
if ( eMode == ScUpdateMode::All )
{ if (nMaxWidthAffectedHintTwip != -1)
{
tools::Long nMaxWidthAffectedHint = ScViewData::ToPixel(nMaxWidthAffectedHintTwip, aViewData.GetPPTX());
// If we know the max text width affected then just invalidate // the max of the cell width and hint of affected cell width // (where affected with is in terms of max width of optimal cell // width for before/after change)
tools::Long nCellWidth = std::abs(aEnd.X() - aStart.X());
aEnd.setX(aStart.getX() + std::max(nCellWidth, nMaxWidthAffectedHint) * nLayoutSign);
} else
{ if (bIsTiledRendering)
{ // When a cell content is deleted we have no clue about // the width of the embedded text. // Anyway, clients will ask only for tiles that overlaps // the visible area. // Remember that wsd expects int and that aEnd.X() is // in pixels and will be converted in twips, before performing // the lok callback, so we need to avoid that an overflow occurs.
aEnd.setX( std::numeric_limits<int>::max() / 1000 );
} else
{
aEnd.setX( bLayoutRTL ? 0 : pGridWin[i]->GetOutputSizePixel().Width() );
}
}
}
aEnd.AdjustX( -nLayoutSign );
aEnd.AdjustY( -1 );
// #i85232# include area below cells (could be done in GetScrPos?) if ( eMode == ScUpdateMode::All && nRow2 >= rDoc.MaxRow() && !bIsTiledRendering )
aEnd.setY( pGridWin[i]->GetOutputSizePixel().Height() );
aStart.AdjustX( -nLayoutSign ); // include change marks
aStart.AdjustY( -1 );
bool bMarkClipped = ScModule::get()->GetColorConfig().GetColorValue(svtools::CALCTEXTOVERFLOW).bIsVisible; if (bMarkClipped)
{ // ScColumn::IsEmptyData has to be optimized for this // (switch to Search() ) //!if ( nCol1 > 0 && !aViewData.GetDocument()->IsBlockEmpty( //! 0, nRow1, nCol1-1, nRow2. //! aViewData.GetTabNo() ) )
tools::Long nMarkPixel = static_cast<tools::Long>( SC_CLIPMARK_SIZE * aViewData.GetPPTX() );
aStart.AdjustX( -(nMarkPixel * nLayoutSign) );
}
// #i79909# Calling UpdateAllOverlays here isn't necessary and would lead to overlay calls from a timer, // with a wrong MapMode if editing in a cell (reference input). // #i80499# Overlays need updates in a lot of cases, e.g. changing row/column size, // or showing/hiding outlines. TODO: selections in inactive windows are vanishing. // #i84689# With relative conditional formats, PaintArea may be called often (for each changed cell), // so UpdateAllOverlays was moved to ScTabViewShell::Notify and is called only if PaintPartFlags::Left/PaintPartFlags::Top // is set (width or height changed).
}
void ScTabView::PaintRangeFinderEntry (const ScRangeFindData* pData, const SCTAB nTab)
{
ScRange aRef = pData->aRef;
aRef.PutInOrder(); // PutInOrder for the queries below
if ( aRef.aStart == aRef.aEnd ) //! ignore sheet?
aViewData.GetDocument().ExtendMerge(aRef);
if (aRef.aStart.Tab() < nTab || aRef.aEnd.Tab() > nTab) return;
void ScTabView::PaintGrid()
{ for (sal_uInt16 i = 0; i < 4; i++)
{ if (pGridWin[i] && pGridWin[i]->IsVisible())
pGridWin[i]->Invalidate();
}
}
// PaintTop - repaint top control elements
void ScTabView::PaintTop()
{ for (sal_uInt16 i = 0; i < 2; i++)
{ if (pColBar[i])
pColBar[i]->Invalidate(); if (pColOutline[i])
pColOutline[i]->Invalidate();
}
}
void ScTabView::CreateAnchorHandles(SdrHdlList& rHdl, const ScAddress& rAddress)
{ for (sal_uInt16 i = 0; i < 4; i++)
{ if(pGridWin[i] && pGridWin[i]->IsVisible())
pGridWin[i]->CreateAnchorHandle(rHdl, rAddress);
}
}
void ScTabView::PaintTopArea( SCCOL nStartCol, SCCOL nEndCol )
{ // pixel position of the left edge
void ScTabView::PaintLeft()
{ for (sal_uInt16 i = 0; i < 2; i++)
{ if (pRowBar[i])
pRowBar[i]->Invalidate(); if (pRowOutline[i])
pRowOutline[i]->Invalidate();
}
}
void ScTabView::PaintLeftArea( SCROW nStartRow, SCROW nEndRow )
{ // pixel position of the upper edge
PaintGrid(); if (bChangedX)
PaintTop(); if (bChangedY)
PaintLeft();
}
void ScTabView::ActivateView( bool bActivate, bool bFirst )
{ if ( bActivate == aViewData.IsActive() && !bFirst )
{ // no assertion anymore - occurs when previously in Drag&Drop switching over // to another document return;
}
// is only called for MDI-(De)Activate // aViewData.Activate behind due to cursor show for KillEditView // don't delete selection - if Activate(false) is set in ViewData, // then the selection is not displayed
// don't cancel reference input, to allow reference // to other document
if (!bRefMode)
{ // pass view to GetInputHdl, this view may not be current anymore
ScInputHandler* pHdl = pScMod->GetInputHdl(aViewData.GetViewShell()); if (pHdl)
pHdl->EnterHandler();
}
}
PaintExtras();
aViewData.Activate(bActivate);
PaintBlock(false); // repaint, selection after active status
if (!bActivate)
HideAllCursors(); // Cursor elseif (!bFirst)
ShowAllCursors();
if (bActivate)
{ if ( bFirst )
{
ScSplitPos eWin = aViewData.GetActivePart();
OSL_ENSURE( pGridWin[eWin], "Corrupted document, not all SplitPos in GridWin" ); if ( !pGridWin[eWin] )
{
eWin = SC_SPLIT_BOTTOMLEFT; if ( !pGridWin[eWin] )
{ short i; for ( i=0; i<4; i++ )
{ if ( pGridWin[i] )
{
eWin = static_cast<ScSplitPos>(i); break; // for
}
}
OSL_ENSURE( i<4, "and BOOM" );
}
aViewData.SetActivePart( eWin );
}
} // do not call GrabFocus from here! // if the document is processed, then Sfx calls GrabFocus in the window of the shell. // if it is a mail body for instance, then it can't get the focus
UpdateInputContext();
} else
pGridWin[aViewData.GetActivePart()]->ClickExtern();
}
if ( bCapture || pGridWin[eWhich]->IsMouseCaptured() )
{ // tracking instead of CaptureMouse, so it can be cancelled cleanly // (SelectionEngine calls CaptureMouse for SetWindow) //! someday SelectionEngine itself should call StartTracking!?!
pGridWin[eWhich]->ReleaseMouse();
pGridWin[eWhich]->StartTracking();
}
// don't switch ViewShell's active window during RefInput, because the focus // might change, and subsequent SetReference calls wouldn't find the right EditView if ( !bRefMode && !bOleActive )
aViewData.GetViewShell()->SetWindow( pGridWin[eWhich] );
if ( bFocus && !aViewData.IsAnyFillMode() && !bRefMode )
{ // GrabFocus only if previously the other GridWindow had the focus // (for instance due to search and replace)
pGridWin[eWhich]->GrabFocus();
}
bInActivatePart = false;
}
void ScTabView::HideListBox()
{ for (VclPtr<ScGridWindow> & pWin : pGridWin)
{ if (pWin)
pWin->ClickExtern();
}
}
void ScTabView::UpdateInputContext()
{
ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get(); if (pWin)
pWin->UpdateInputContext();
if (pTabControl)
pTabControl->UpdateInputContext();
}
// GetGridWidth - width of an output range (for ViewData)
tools::Long ScTabView::GetGridWidth( ScHSplitPos eWhich )
{ // at present only the size of the current pane is synchronized with // the size of the visible area in Online; // as a workaround we use the same width for all panes independently // from the eWhich value if (comphelper::LibreOfficeKit::isActive())
{
ScGridWindow* pGridWindow = aViewData.GetActiveWin(); if (pGridWindow) return pGridWindow->GetSizePixel().Width();
}
// GetGridHeight - height of an output range (for ViewData)
tools::Long ScTabView::GetGridHeight( ScVSplitPos eWhich )
{ // at present only the size of the current pane is synchronized with // the size of the visible area in Online; // as a workaround we use the same height for all panes independently // from the eWhich value if (comphelper::LibreOfficeKit::isActive())
{
ScGridWindow* pGridWindow = aViewData.GetActiveWin(); if (pGridWindow) return pGridWindow->GetSizePixel().Height();
}
void ScTabView::SyncGridWindowMapModeFromDrawMapMode()
{ // AW: Discussed with NN if there is a reason that new map mode was only set for one window, // but is not. Setting only on one window causes the first repaint to have the old mapMode // in three of four views, so the overlay will save the wrong content e.g. when zooming out. // Changing to setting map mode at all windows. for (VclPtr<ScGridWindow> & pWin : pGridWin)
{ if (!pWin) continue;
pWin->SetMapMode(pWin->GetDrawMapMode());
}
}
// To not change too much, use pWin here
ScGridWindow* pWin = pGridWin[aViewData.GetActivePart()].get();
if ( pWin && aViewData.HasEditView( aViewData.GetActivePart() ) )
{ // make sure the EditView's position and size are updated // with the right (logic, not drawing) MapMode
pWin->SetMapMode( aViewData.GetLogicMode() );
UpdateEditView();
}
}
void ScTabView::CheckNeedsRepaint()
{ for (sal_uInt16 i = 0; i < 4; i++)
{ if (pGridWin[i] && pGridWin[i]->IsVisible())
pGridWin[i]->CheckNeedsRepaint();
}
}
bool ScTabView::NeedsRepaint()
{ for (VclPtr<ScGridWindow> & pWin : pGridWin)
{ if (pWin && pWin->IsVisible() && pWin->NeedsRepaint()) returntrue;
} returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.