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

1251 lines
40 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <com/sun/star/embed/XEmbeddedObject.hpp>
#include <svx/svditer.hxx>
#include <svx/svdograf.hxx>
#include <svx/svdogrp.hxx>
#include <svx/svdoole2.hxx>
#include <svx/svdouno.hxx>
#include <svx/svdpage.hxx>
#include <svx/svdpagv.hxx>
#include <svx/svdundo.hxx>
#include <svx/svdocapt.hxx>
#include <svx/sdrpaintwindow.hxx>
#include <sfx2/bindings.hxx>
#include <sfx2/viewfrm.hxx>
#include <svx/sdrundomanager.hxx>
#include <svx/xfillit0.hxx>
#include <svx/xbtmpit.hxx>
#include <comphelper/lok.hxx>
#include <sfx2/lokhelper.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <svx/sdr/contact/objectcontactofpageview.hxx>
#include <svx/sdr/contact/viewobjectcontact.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/sdrpagewindow.hxx>
#include <tools/UnitConversion.hxx>
#include <osl/diagnose.h>
#include <drawview.hxx>
#include <global.hxx>
#include <viewdata.hxx>
#include <document.hxx>
#include <drawutil.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <tabvwsh.hxx>
#include <client.hxx>
#include <scmod.hxx>
#include <drwlayer.hxx>
#include <docsh.hxx>
#include <viewuno.hxx>
#include <userdat.hxx>
#include <postit.hxx>
#include <undocell.hxx>
#include <gridwin.hxx>
#include <sc.hrc>
using namespace com::sun::star;
#define SC_HANDLESIZE_BIG 9
void ScDrawView::Construct()
{
EnableExtendedKeyInputDispatcher(false);
EnableExtendedMouseEventDispatcher(false);
SetFrameDragSingles();
SetMinMoveDistancePixel( 2 );
SetHitTolerancePixel( 2 );
if (pViewData)
{
SCTAB nViewTab = pViewData->GetTabNo();
ShowSdrPage(GetModel().GetPage(nViewTab));
bool bEx = pViewData->GetViewShell()->IsDrawSelMode();
bool bProt = rDoc.IsTabProtected( nViewTab ) ||
pViewData->GetSfxDocShell()->IsReadOnly();
SdrLayer* pLayer;
SdrLayerAdmin& rAdmin = GetModel().GetLayerAdmin();
pLayer = rAdmin.GetLayerPerID(SC_LAYER_BACK);
if (pLayer)
SetLayerLocked( pLayer->GetName(), bProt || !bEx );
pLayer = rAdmin.GetLayerPerID(SC_LAYER_INTERN);
if (pLayer)
SetLayerLocked( pLayer->GetName() );
pLayer = rAdmin.GetLayerPerID(SC_LAYER_FRONT);
if (pLayer)
{
SetLayerLocked( pLayer->GetName(), bProt );
SetActiveLayer( pLayer->GetName() ); // set active layer to FRONT
}
pLayer = rAdmin.GetLayerPerID(SC_LAYER_CONTROLS);
if (pLayer)
SetLayerLocked( pLayer->GetName(), bProt );
pLayer = rAdmin.GetLayerPerID(SC_LAYER_HIDDEN);
if (pLayer)
{
SetLayerLocked( pLayer->GetName(), bProt );
SetLayerVisible( pLayer->GetName(), false);
}
SetSwapAsynchron();
}
else
{
ShowSdrPage(GetModel().GetPage(nTab));
}
UpdateUserViewOptions();
RecalcScale();
UpdateWorkArea();
bInConstruct = false;
}
void ScDrawView::ImplClearCalcDropMarker()
{
pDropMarker.reset();
}
ScDrawView::~ScDrawView()
{
ImplClearCalcDropMarker();
}
void ScDrawView::AddCustomHdl()
{
const SdrMarkList &rMarkList = GetMarkedObjectList();
const size_t nCount = rMarkList.GetMarkCount();
for(size_t nPos=0; nPos<nCount; ++nPos )
{
SdrObject* pObj = rMarkList.GetMark(nPos)->GetMarkedSdrObj();
if (ScDrawObjData *pAnchor = ScDrawLayer::GetObjDataTab(pObj, nTab))
{
if (ScTabView* pView = pViewData->GetView())
pView->CreateAnchorHandles(maHdlList, pAnchor->maStart);
}
}
}
void ScDrawView::InvalidateAttribs()
{
if (!pViewData) return;
SfxBindings& rBindings = pViewData->GetBindings();
// true status values:
rBindings.InvalidateAll( true );
}
void ScDrawView::InvalidateDrawTextAttrs()
{
if (!pViewData) return;
SfxBindings& rBindings = pViewData->GetBindings();
// cjk/ctl font items have no configured slots,
// need no invalidate
rBindings.Invalidate( SID_ATTR_CHAR_FONT );
rBindings.Invalidate( SID_ATTR_CHAR_FONTHEIGHT );
rBindings.Invalidate( SID_ATTR_CHAR_WEIGHT );
rBindings.Invalidate( SID_ATTR_CHAR_POSTURE );
rBindings.Invalidate( SID_ATTR_CHAR_UNDERLINE );
rBindings.Invalidate( SID_ULINE_VAL_NONE );
rBindings.Invalidate( SID_ULINE_VAL_SINGLE );
rBindings.Invalidate( SID_ULINE_VAL_DOUBLE );
rBindings.Invalidate( SID_ULINE_VAL_DOTTED );
rBindings.Invalidate( SID_ATTR_CHAR_OVERLINE );
rBindings.Invalidate( SID_ATTR_CHAR_COLOR );
rBindings.Invalidate( SID_ATTR_CHAR_BACK_COLOR );
rBindings.Invalidate( SID_ATTR_PARA_ADJUST_LEFT );
rBindings.Invalidate( SID_ATTR_PARA_ADJUST_RIGHT );
rBindings.Invalidate( SID_ATTR_PARA_ADJUST_BLOCK );
rBindings.Invalidate( SID_ATTR_PARA_ADJUST_CENTER);
rBindings.Invalidate( SID_ALIGNLEFT );
rBindings.Invalidate( SID_ALIGNCENTERHOR );
rBindings.Invalidate( SID_ALIGNRIGHT );
rBindings.Invalidate( SID_ALIGNBLOCK );
rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_10 );
rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_15 );
rBindings.Invalidate( SID_ATTR_PARA_LINESPACE_20 );
rBindings.Invalidate( SID_SET_SUPER_SCRIPT );
rBindings.Invalidate( SID_SET_SUB_SCRIPT );
rBindings.Invalidate( SID_ATTR_CHAR_KERNING );
rBindings.Invalidate( SID_ATTR_CHAR_STRIKEOUT );
rBindings.Invalidate( SID_ATTR_CHAR_SHADOWED );
rBindings.Invalidate( SID_TEXTDIRECTION_LEFT_TO_RIGHT );
rBindings.Invalidate( SID_TEXTDIRECTION_TOP_TO_BOTTOM );
rBindings.Invalidate( SID_ATTR_PARA_LEFT_TO_RIGHT );
rBindings.Invalidate( SID_ATTR_PARA_RIGHT_TO_LEFT );
rBindings.Invalidate( SID_TABLE_VERT_NONE );
rBindings.Invalidate( SID_TABLE_VERT_CENTER );
rBindings.Invalidate( SID_TABLE_VERT_BOTTOM );
// pseudo slots for Format menu
rBindings.Invalidate( SID_ALIGN_ANY_LEFT );
rBindings.Invalidate( SID_ALIGN_ANY_HCENTER );
rBindings.Invalidate( SID_ALIGN_ANY_RIGHT );
rBindings.Invalidate( SID_ALIGN_ANY_JUSTIFIED );
}
void ScDrawView::SetMarkedToLayer( SdrLayerID nLayerNo )
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
if (rMarkList.GetMarkCount() == 0)
return;
// #i11702# use SdrUndoObjectLayerChange for undo
// STR_UNDO_SELATTR is "Attributes" - should use a different text later
BegUndo( ScResId( STR_UNDO_SELATTR ) );
const SdrMarkList& rMark = GetMarkedObjectList();
const size_t nCount = rMark.GetMarkCount();
for (size_t i=0; i<nCount; ++i)
{
SdrObject* pObj = rMark.GetMark(i)->GetMarkedSdrObj();
assert(pObj);
if ( dynamic_cast<const SdrUnoObj*>( pObj) == nullptr && (pObj->GetLayer() != SC_LAYER_INTERN) )
{
AddUndo( std::make_unique<SdrUndoObjectLayerChange>( *pObj, pObj->GetLayer(), nLayerNo) );
pObj->SetLayer( nLayerNo );
}
}
EndUndo();
// repaint is done in SetLayer
pViewData->GetDocShell()->SetDrawModified();
// check mark list now instead of later in a timer
CheckMarked();
MarkListHasChanged();
}
bool ScDrawView::HasMarkedControl() const
{
SdrObjListIter aIter( GetMarkedObjectList() );
for( SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next() )
if( dynamic_cast<const SdrUnoObj*>( pObj) != nullptr )
return true;
return false;
}
bool ScDrawView::HasMarkedInternal() const
{
// internal objects should not be inside a group, but who knows...
SdrObjListIter aIter( GetMarkedObjectList() );
for( SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next() )
if( pObj->GetLayer() == SC_LAYER_INTERN )
return true;
return false;
}
void ScDrawView::UpdateWorkArea()
{
SdrPage* pPage = GetModel().GetPage(static_cast<sal_uInt16>(nTab));
if (pPage)
{
Size aPageSize( pPage->GetSize() );
tools::Rectangle aNewArea( Point(), aPageSize );
if ( aPageSize.Width() < 0 )
{
// RTL: from max.negative (left) to zero (right)
aNewArea.SetRight( 0 );
aNewArea.SetLeft( aPageSize.Width() + 1 );
}
SetWorkArea( aNewArea );
}
else
{
OSL_FAIL("Page not found");
}
}
void ScDrawView::DoCut()
{
DoCopy();
BegUndo( ScResId( STR_UNDO_CUT ) );
DeleteMarked(); // In this View - not affected by 505f change
EndUndo();
}
void ScDrawView::GetScale( Fraction& rFractX, Fraction& rFractY ) const
{
rFractX = aScaleX;
rFractY = aScaleY;
}
void ScDrawView::RecalcScale()
{
double nPPTX;
double nPPTY;
Fraction aZoomX(1,1);
Fraction aZoomY(1,1);
if (pViewData)
{
nTab = pViewData->GetTabNo();
nPPTX = pViewData->GetPPTX();
nPPTY = pViewData->GetPPTY();
aZoomX = pViewData->GetZoomX();
aZoomY = pViewData->GetZoomY();
}
else
{
Point aLogic = pDev->LogicToPixel(Point(1000,1000), MapMode(MapUnit::MapTwip));
nPPTX = aLogic.X() / 1000.0;
nPPTY = aLogic.Y() / 1000.0;
//! Zoom, handed over ???
}
SCCOL nEndCol = 0;
SCROW nEndRow = 0;
rDoc.GetTableArea( nTab, nEndCol, nEndRow );
if (nEndCol<20)
nEndCol = 20;
if (nEndRow<20)
nEndRow = 20;
ScDrawUtil::CalcScale(
rDoc, nTab, 0, 0, nEndCol, nEndRow, pDev, aZoomX, aZoomY, nPPTX, nPPTY,
aScaleX, aScaleY);
// clear all evtl existing GridOffset vectors
resetGridOffsetsForAllSdrPageViews();
SdrPageView* pPV = GetSdrPageView();
if ( !(pViewData && pPV) )
return;
if ( SdrPage* pPage = pPV->GetPage() )
{
for (const rtl::Reference<SdrObject>& pObj : *pPage)
// Align objects to nearest grid position
SyncForGrid( pObj.get() );
}
}
void ScDrawView::DoConnect(SdrOle2Obj* pOleObj)
{
if ( pViewData )
pViewData->GetViewShell()->ConnectObject( pOleObj );
}
void ScDrawView::MarkListHasChanged()
{
FmFormView::MarkListHasChanged();
ScTabViewShell* pViewSh = pViewData->GetViewShell();
const SdrMarkList& rMarkList = GetMarkedObjectList();
ScModule* pScMod = ScModule::get();
// #i110829# remove the cell selection only if drawing objects are selected
if ( !bInConstruct && rMarkList.GetMarkCount() )
{
pViewSh->Unmark(); // remove cell selection
// end cell edit mode if drawing objects are selected
pScMod->InputEnterHandler();
}
// deactivate IP
bool bUnoRefDialog = pScMod->IsRefDialogOpen() && pScMod->GetCurRefDlgId() == WID_SIMPLE_REF;
ScClient* pClient = static_cast<ScClient*>( pViewSh->GetIPClient() );
if ( pClient && pClient->IsObjectInPlaceActive() && !bUnoRefDialog )
{
// do not display the handles for ViewShell::Activate from the Reset2Open
pClient->DeactivateObject();
// replacing image ole graphics is now done in ScClient::UIActivate
}
// Select Ole object?
SdrOle2Obj* pOle2Obj = nullptr;
SdrGrafObj* pGrafObj = nullptr;
const size_t nMarkCount = rMarkList.GetMarkCount();
if ( nMarkCount == 0 && !pViewData->GetViewShell()->IsDrawSelMode() && !bInConstruct )
{
// relock layers that may have been unlocked before
LockBackgroundLayer(true);
LockInternalLayer();
}
bool bSubShellSet = false;
if (nMarkCount == 1)
{
SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj();
if (pObj->GetObjIdentifier() == SdrObjKind::OLE2)
{
pOle2Obj = static_cast<SdrOle2Obj*>(pObj);
if (!ScDocument::IsChart(pObj) )
pViewSh->SetOleObjectShell(true);
else
pViewSh->SetChartShell(true);
bSubShellSet = true;
}
else if (pObj->GetObjIdentifier() == SdrObjKind::Graphic)
{
pGrafObj = static_cast<SdrGrafObj*>(pObj);
pViewSh->SetGraphicShell(true);
bSubShellSet = true;
}
else if (pObj->GetObjIdentifier() == SdrObjKind::Media)
{
pViewSh->SetMediaShell(true);
bSubShellSet = true;
}
else if (pObj->GetObjIdentifier() != SdrObjKind::Text // prevent switching to the drawing shell
|| !pViewSh->IsDrawTextShell()) // when creating a text object @#70206#
{
pViewSh->SetDrawShell(true);
}
}
if ( nMarkCount && !bSubShellSet )
{
bool bOnlyControls = true;
bool bOnlyGraf = true;
for (size_t i=0; i<nMarkCount; ++i)
{
SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj();
if ( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObj) )
{
const SdrObjList *pLst = pObjGroup->GetSubList();
const size_t nListCount = pLst->GetObjCount();
if ( nListCount == 0 )
{
// An empty group (may occur during Undo) is no control or graphics object.
// Creating the form shell during undo would lead to problems with the undo manager.
bOnlyControls = false;
bOnlyGraf = false;
}
for ( size_t j = 0; j < nListCount; ++j )
{
SdrObject *pSubObj = pLst->GetObj( j );
assert(pSubObj);
if (dynamic_cast<const SdrUnoObj*>( pSubObj) == nullptr)
bOnlyControls = false;
if (pSubObj->GetObjIdentifier() != SdrObjKind::Graphic)
bOnlyGraf = false;
if ( !bOnlyControls && !bOnlyGraf ) break;
}
}
else
{
if (dynamic_cast<const SdrUnoObj*>( pObj) == nullptr)
bOnlyControls = false;
if (pObj->GetObjIdentifier() != SdrObjKind::Graphic)
bOnlyGraf = false;
}
if ( !bOnlyControls && !bOnlyGraf ) break;
}
if(bOnlyControls)
{
pViewSh->SetDrawFormShell(true); // now UNO controls
}
else if(bOnlyGraf)
{
pViewSh->SetGraphicShell(true);
}
else if(nMarkCount>1)
{
pViewSh->SetDrawShell(true);
}
}
// adjust verbs
SfxViewFrame& rViewFrame = pViewSh->GetViewFrame();
bool bOle = pViewSh->GetViewFrame().GetFrame().IsInPlace();
uno::Sequence< embed::VerbDescriptor > aVerbs;
if ( pOle2Obj && !bOle )
{
const uno::Reference < embed::XEmbeddedObject >& xObj = pOle2Obj->GetObjRef();
OSL_ENSURE( xObj.is(), "SdrOle2Obj without ObjRef" );
if (xObj.is())
aVerbs = xObj->getSupportedVerbs();
}
pViewSh->SetVerbs( aVerbs );
// image map editor
if ( pOle2Obj )
UpdateIMap( pOle2Obj );
else if ( pGrafObj )
UpdateIMap( pGrafObj );
InvalidateAttribs(); // after the image map editor update
InvalidateDrawTextAttrs();
for(sal_uInt32 a(0); a < PaintWindowCount(); a++)
{
SdrPaintWindow* pPaintWindow = GetPaintWindow(a);
OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
if(OUTDEV_WINDOW == rOutDev.GetOutDevType())
{
rOutDev.GetOwnerWindow()->PaintImmediately();
}
}
// uno object for view returns drawing objects as selection,
// so it must notify its SelectionChangeListeners
SfxFrame& rFrame = rViewFrame.GetFrame();
uno::Reference<frame::XController> xController = rFrame.GetController();
if (xController.is())
{
ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
if (pImp)
pImp->SelectionChanged();
}
// update selection transfer object
pViewSh->CheckSelectionTransfer();
}
bool ScDrawView::SdrBeginTextEdit(
SdrObject* pObj,
SdrPageView* pPV,
vcl::Window* pWinL,
bool bIsNewObj,
SdrOutliner* pGivenOutliner,
OutlinerView* pGivenOutlinerView,
bool bDontDeleteOutliner,
bool bOnlyOneView,
bool bGrabFocus )
{
const bool bRet = FmFormView::SdrBeginTextEdit(
pObj, pPV, pWinL, bIsNewObj,
pGivenOutliner, pGivenOutlinerView, bDontDeleteOutliner,
bOnlyOneView, bGrabFocus );
ScTabViewShell* pViewSh = pViewData->GetViewShell();
if (comphelper::LibreOfficeKit::isActive())
{
if (OutlinerView* pView = GetTextEditOutlinerView())
{
tools::Rectangle aRectangle = pView->GetOutputArea();
if (pWinL && pWinL->GetMapMode().GetMapUnit() == MapUnit::Map100thMM)
{
aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip);
}
OString sRectangle = aRectangle.toString();
SfxLokHelper::notifyOtherViews(pViewSh, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRectangle);
}
}
SfxFrame& rFrame = pViewSh->GetViewFrame().GetFrame();
uno::Reference< frame::XController > xController = rFrame.GetController();
if (xController.is())
{
ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
if (pImp)
pImp->SelectionChanged();
}
return bRet;
}
SdrEndTextEditKind ScDrawView::SdrEndTextEdit( bool bDontDeleteReally )
{
const SdrEndTextEditKind eRet = FmFormView::SdrEndTextEdit( bDontDeleteReally );
ScTabViewShell* pViewSh = pViewData->GetViewShell();
if (comphelper::LibreOfficeKit::isActive())
SfxLokHelper::notifyOtherViews(pViewSh, LOK_CALLBACK_VIEW_LOCK, "rectangle", "EMPTY"_ostr);
SfxFrame& rFrame = pViewSh->GetViewFrame().GetFrame();
uno::Reference< frame::XController > xController = rFrame.GetController();
if (xController.is())
{
ScTabViewObj* pImp = dynamic_cast<ScTabViewObj*>( xController.get() );
if (pImp)
pImp->SelectionChanged();
}
return eRet;
}
void ScDrawView::ModelHasChanged()
{
SdrObject* pEditObj = GetTextEditObject();
if ( pEditObj && !pEditObj->IsInserted() && pViewData )
{
// SdrObjEditView::ModelHasChanged will end text edit in this case,
// so make sure the EditEngine's undo manager is no longer used.
pViewData->GetViewShell()->SetDrawTextUndo(nullptr);
SetCreateMode(); // don't leave FuText in a funny state
}
FmFormView::ModelHasChanged();
}
void ScDrawView::UpdateUserViewOptions()
{
if (!pViewData)
return;
const ScViewOptions& rOpt = pViewData->GetOptions();
const ScGridOptions& rGrid = rOpt.GetGridOptions();
SetDragStripes( rOpt.GetOption( VOPT_HELPLINES ) );
SetMarkHdlSizePixel( SC_HANDLESIZE_BIG );
SetGridVisible( rGrid.GetGridVisible() );
SetSnapEnabled( rGrid.GetUseGridSnap() );
SetGridSnap( rGrid.GetUseGridSnap() );
Fraction aFractX( rGrid.GetFieldDrawX(), rGrid.GetFieldDivisionX() + 1 );
Fraction aFractY( rGrid.GetFieldDrawY(), rGrid.GetFieldDivisionY() + 1 );
SetSnapGridWidth( aFractX, aFractY );
SetGridCoarse( Size( rGrid.GetFieldDrawX(), rGrid.GetFieldDrawY() ) );
SetGridFine( Size( rGrid.GetFieldDrawX() / (rGrid.GetFieldDivisionX() + 1),
rGrid.GetFieldDrawY() / (rGrid.GetFieldDivisionY() + 1) ) );
}
SdrObject* ScDrawView::GetObjectByName(std::u16string_view rName)
{
ScDocShell* pShell = rDoc.GetDocumentShell();
if (pShell)
{
SdrModel& rDrawLayer = GetModel();
sal_uInt16 nTabCount = rDoc.GetTableCount();
for (sal_uInt16 i=0; i<nTabCount; i++)
{
SdrPage* pPage = rDrawLayer.GetPage(i);
DBG_ASSERT(pPage,"Page ?");
if (pPage)
{
SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
SdrObject* pObject = aIter.Next();
while (pObject)
{
if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
{
return pObject;
}
pObject = aIter.Next();
}
}
}
}
return nullptr;
}
//realize multi-selection of objects
void ScDrawView::SelectCurrentViewObject( std::u16string_view rName )
{
sal_uInt16 nObjectTab = 0;
SdrObject* pFound = nullptr;
ScDocShell* pShell = rDoc.GetDocumentShell();
if (pShell)
{
SdrModel& rDrawLayer = GetModel();
sal_uInt16 nTabCount = rDoc.GetTableCount();
for (sal_uInt16 i=0; i<nTabCount && !pFound; i++)
{
SdrPage* pPage = rDrawLayer.GetPage(i);
DBG_ASSERT(pPage,"Page ?");
if (pPage)
{
SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
SdrObject* pObject = aIter.Next();
while (pObject && !pFound)
{
if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
{
pFound = pObject;
nObjectTab = i;
}
pObject = aIter.Next();
}
}
}
}
if ( !pFound )
return;
ScTabView* pView = pViewData->GetView();
if ( nObjectTab != nTab ) // switch sheet
pView->SetTabNo( nObjectTab );
DBG_ASSERT( nTab == nObjectTab, "Switching sheets did not work" );
pView->ScrollToObject( pFound );
if ( pFound->GetLayer() == SC_LAYER_BACK &&
!pViewData->GetViewShell()->IsDrawSelMode() &&
!rDoc.IsTabProtected( nTab ) &&
!pViewData->GetSfxDocShell()->IsReadOnly() )
{
SdrLayer* pLayer = GetModel().GetLayerAdmin().GetLayerPerID(SC_LAYER_BACK);
if (pLayer)
SetLayerLocked( pLayer->GetName(), false );
}
SdrPageView* pPV = GetSdrPageView();
const bool bUnMark = IsObjMarked(pFound);
MarkObj( pFound, pPV, bUnMark);
}
bool ScDrawView::SelectObject( std::u16string_view rName )
{
UnmarkAll();
SCTAB nObjectTab = 0;
SdrObject* pFound = nullptr;
ScDocShell* pShell = rDoc.GetDocumentShell();
if (pShell)
{
SdrModel& rDrawLayer = GetModel();
SCTAB nTabCount = rDoc.GetTableCount();
for (SCTAB i=0; i<nTabCount && !pFound; i++)
{
SdrPage* pPage = rDrawLayer.GetPage(static_cast<sal_uInt16>(i));
OSL_ENSURE(pPage,"Page ?");
if (pPage)
{
SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups );
SdrObject* pObject = aIter.Next();
while (pObject && !pFound)
{
if ( ScDrawLayer::GetVisibleName( pObject ) == rName )
{
pFound = pObject;
nObjectTab = i;
}
pObject = aIter.Next();
}
}
}
}
if ( pFound )
{
ScTabView* pView = pViewData->GetView();
if ( nObjectTab != nTab ) // switch sheet
pView->SetTabNo( nObjectTab );
OSL_ENSURE( nTab == nObjectTab, "Switching sheets did not work" );
pView->ScrollToObject( pFound );
/* To select an object on the background layer, the layer has to
be unlocked even if exclusive drawing selection mode is not active
(this is reversed in MarkListHasChanged when nothing is selected) */
if ( pFound->GetLayer() == SC_LAYER_BACK &&
!pViewData->GetViewShell()->IsDrawSelMode() &&
!rDoc.IsTabProtected( nTab ) &&
!pViewData->GetSfxDocShell()->IsReadOnly() )
{
LockBackgroundLayer(false);
}
SdrPageView* pPV = GetSdrPageView();
MarkObj( pFound, pPV );
}
return ( pFound != nullptr );
}
//If object is marked , return true , else return false .
bool ScDrawView::GetObjectIsMarked( const SdrObject* pObject )
{
bool bisMarked = false;
if (pObject )
{
bisMarked = IsObjMarked(pObject);
}
return bisMarked;
}
bool ScDrawView::InsertObjectSafe(SdrObject* pObj, SdrPageView& rPV)
{
SdrInsertFlags nOptions=SdrInsertFlags::NONE;
// Do not change marks when the ole object is active
// (for Drop from ole object would otherwise be deactivated in the middle of ExecuteDrag!)
if (pViewData)
{
SfxInPlaceClient* pClient = pViewData->GetViewShell()->GetIPClient();
if ( pClient && pClient->IsObjectInPlaceActive() )
nOptions |= SdrInsertFlags::DONTMARK;
}
return InsertObjectAtView(pObj, rPV, nOptions);
}
SdrObject* ScDrawView::GetMarkedNoteCaption( ScDrawObjData** ppCaptData )
{
const SdrMarkList& rMarkList = GetMarkedObjectList();
if( pViewData && (rMarkList.GetMarkCount() == 1) )
{
SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj();
if( ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( pObj, pViewData->GetTabNo() ) )
{
if( ppCaptData ) *ppCaptData = pCaptData;
return pObj;
}
}
return nullptr;
}
void ScDrawView::LockCalcLayer( SdrLayerID nLayer, bool bLock )
{
SdrLayer* pLockLayer = GetModel().GetLayerAdmin().GetLayerPerID( nLayer );
if( pLockLayer && (IsLayerLocked( pLockLayer->GetName() ) != bLock) )
SetLayerLocked( pLockLayer->GetName(), bLock );
}
void ScDrawView::MakeVisible( const tools::Rectangle& rRect, vcl::Window& rWin )
{
//! Evaluate rWin properly
//! change zoom if necessary
if ( pViewData && pViewData->GetActiveWin() == &rWin )
pViewData->GetView()->MakeVisible( rRect );
}
SfxViewShell* ScDrawView::GetSfxViewShell() const
{
return pViewData->GetViewShell();
}
void ScDrawView::DeleteMarked()
{
// try to delete a note caption object with its cell note in the Calc document
ScDrawObjData* pCaptData = nullptr;
if( SdrObject* pCaptObj = GetMarkedNoteCaption( &pCaptData ) )
{
ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer();
ScDocShell* pDocShell = pViewData ? pViewData->GetDocShell() : nullptr;
SfxUndoManager* pUndoMgr = pDocShell ? pDocShell->GetUndoManager() : nullptr;
bool bUndo = pDrawLayer && pDocShell && pUndoMgr && rDoc.IsUndoEnabled();
// remove the cell note from document, we are its owner now
std::unique_ptr<ScPostIt> pNote = rDoc.ReleaseNote( pCaptData->maStart );
OSL_ENSURE( pNote, "ScDrawView::DeleteMarked - cell note missing in document" );
if( pNote )
{
// rescue note data for undo (with pointer to caption object)
ScNoteData aNoteData = pNote->GetNoteData();
OSL_ENSURE( aNoteData.mxCaption.get() == pCaptObj, "ScDrawView::DeleteMarked - caption object does not match" );
// collect the drawing undo action created while deleting the note
if( bUndo )
pDrawLayer->BeginCalcUndo(false);
// delete the note (already removed from document above)
pNote.reset();
// add the undo action for the note
if( bUndo )
pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( *pDocShell, pCaptData->maStart, aNoteData, false, pDrawLayer->GetCalcUndo() ) );
// repaint the cell to get rid of the note marker
if( pDocShell )
pDocShell->PostPaintCell( pCaptData->maStart );
// done, return now to skip call of FmFormView::DeleteMarked()
return;
}
}
FmFormView::DeleteMarked();
}
SdrEndTextEditKind ScDrawView::ScEndTextEdit()
{
bool bIsTextEdit = IsTextEdit();
SdrEndTextEditKind eKind = SdrEndTextEdit();
if (bIsTextEdit)
pViewData->GetViewShell()->SetDrawTextUndo(nullptr); // the "normal" undo manager
return eKind;
}
void ScDrawView::MarkDropObj( SdrObject* pObj )
{
if ( pDropMarkObj != pObj )
{
pDropMarkObj = pObj;
ImplClearCalcDropMarker();
if(pDropMarkObj)
{
pDropMarker.reset( new SdrDropMarkerOverlay(*this, *pDropMarkObj) );
}
}
}
// In order to counteract the effects of rounding due to the nature of how the
// grid positions are calculated and drawn we calculate the offset needed at the
// current zoom to be applied to an SrdObject when it is drawn in order to make
// sure that it's position relative to the nearest cell anchor doesn't change.
// Of course not all shape(s)/control(s) are cell anchored, if the
// object doesn't have a cell anchor we synthesise a temporary anchor.
void ScDrawView::SyncForGrid( SdrObject* pObj )
{
// process members of a group shape separately
if ( auto pObjGroup = dynamic_cast<const SdrObjGroup*>( pObj) )
{
SdrObjList *pLst = pObjGroup->GetSubList();
for (const rtl::Reference<SdrObject>& pChild : *pLst)
SyncForGrid( pChild.get() );
}
ScSplitPos eWhich = pViewData->GetActivePart();
ScGridWindow* pGridWin = pViewData->GetActiveWin();
ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj );
if ( !pGridWin )
return;
ScAddress aOldStt;
if( pData && pData->maStart.IsValid())
{
aOldStt = pData->maStart;
}
else
{
// Page anchored object so...
// synthesise an anchor ( but don't attach it to
// the object as we want to maintain page anchoring )
ScDrawObjData aAnchor;
const tools::Rectangle aObjRect(pObj->GetLogicRect());
ScDrawLayer::GetCellAnchorFromPosition(
aObjRect,
aAnchor,
rDoc,
GetTab());
aOldStt = aAnchor.maStart;
}
MapMode aDrawMode = pGridWin->GetDrawMapMode();
// find pos anchor position
Point aOldPos( rDoc.GetColOffset( aOldStt.Col(), aOldStt.Tab() ), rDoc.GetRowOffset( aOldStt.Row(), aOldStt.Tab() ) );
aOldPos = convertTwipToMm100(aOldPos);
// find position of same point on the screen ( e.g. grid )
Point aCurPos = pViewData->GetScrPos( aOldStt.Col(), aOldStt.Row(), eWhich, true );
Point aCurPosHmm = pGridWin->PixelToLogic(aCurPos, aDrawMode );
Point aGridOff = aCurPosHmm - aOldPos;
// fdo#63878 Fix the X position for RTL Sheet
if( rDoc.IsNegativePage( GetTab() ) && !comphelper::LibreOfficeKit::isActive() )
aGridOff.setX( aCurPosHmm.getX() + aOldPos.getX() );
}
void ScDrawView::resetGridOffsetsForAllSdrPageViews()
{
SdrPageView* pPageView(GetSdrPageView());
if(nullptr == pPageView)
return;
for(sal_uInt32 a(0); a < pPageView->PageWindowCount(); a++)
{
SdrPageWindow* pPageWindow(pPageView->GetPageWindow(a));
assert(pPageWindow && "SdrView::SetMasterPagePaintCaching: Corrupt SdrPageWindow list (!)");
if(nullptr != pPageWindow)
{
sdr::contact::ObjectContact& rObjectContact(pPageWindow->GetObjectContact());
if(rObjectContact.supportsGridOffsets())
{
rObjectContact.resetAllGridOffsets();
}
}
}
}
bool ScDrawView::calculateGridOffsetForSdrObject(
SdrObject& rSdrObject,
basegfx::B2DVector& rTarget) const
{
if (comphelper::LibreOfficeKit::isActive() &&
!comphelper::LibreOfficeKit::isCompatFlagSet(
comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
return false;
ScGridWindow* pGridWin(pViewData->GetActiveWin());
if(nullptr == pGridWin)
{
return false;
}
ScDrawObjData* pData(ScDrawLayer::GetObjData(&rSdrObject));
ScAddress aOldStt;
if(nullptr != pData && pData->maStart.IsValid())
{
aOldStt = pData->maStart;
}
else
{
// Page anchored object so...
// synthesise an anchor ( but don't attach it to
// the object as we want to maintain page anchoring )
ScDrawObjData aAnchor;
const tools::Rectangle aObjRect(rSdrObject.GetLogicRect());
ScDrawLayer::GetCellAnchorFromPosition(
aObjRect,
aAnchor,
rDoc,
GetTab());
aOldStt = aAnchor.maStart;
}
MapMode aDrawMode = pGridWin->GetDrawMapMode();
// find pos anchor position
Point aOldPos(rDoc.GetColOffset(aOldStt.Col(), aOldStt.Tab()), rDoc.GetRowOffset(aOldStt.Row(), aOldStt.Tab()));
aOldPos = convertTwipToMm100(aOldPos);
// find position of same point on the screen ( e.g. grid )
ScSplitPos eWhich(pViewData->GetActivePart());
Point aCurPos(pViewData->GetScrPos(aOldStt.Col(), aOldStt.Row(), eWhich, true));
Point aCurPosHmm(pGridWin->PixelToLogic(aCurPos, aDrawMode));
Point aGridOff(aCurPosHmm - aOldPos);
bool bLOKActive = comphelper::LibreOfficeKit::isActive();
bool bNegativePage = rDoc.IsNegativePage(GetTab());
// fdo#63878 Fix the X position for RTL Sheet
if(bNegativePage && !bLOKActive)
{
aGridOff.setX(aCurPosHmm.getX() + aOldPos.getX());
}
rTarget.setX(bNegativePage && bLOKActive ? -aGridOff.X() : aGridOff.X());
rTarget.setY(aGridOff.Y());
return true;
}
bool ScDrawView::calculateGridOffsetForB2DRange(
const basegfx::B2DRange& rB2DRange,
basegfx::B2DVector& rTarget) const
{
ScGridWindow* pGridWin(pViewData->GetActiveWin());
if(nullptr == pGridWin || rB2DRange.isEmpty())
{
return false;
}
// No SdrObject, so synthesise an anchor ( but don't attach it to
// the object as we want to maintain page anchoring )
ScDrawObjData aAnchor;
const tools::Rectangle aRectangle(
basegfx::fround<tools::Long>(rB2DRange.getMinX()), basegfx::fround<tools::Long>(rB2DRange.getMinY()),
basegfx::fround<tools::Long>(rB2DRange.getMaxX()), basegfx::fround<tools::Long>(rB2DRange.getMaxY()));
ScDrawLayer::GetCellAnchorFromPosition(
aRectangle,
aAnchor,
rDoc,
GetTab());
ScAddress aOldStt(aAnchor.maStart);
MapMode aDrawMode = pGridWin->GetDrawMapMode();
// find pos anchor position
Point aOldPos(rDoc.GetColOffset(aOldStt.Col(), aOldStt.Tab()), rDoc.GetRowOffset(aOldStt.Row(), aOldStt.Tab()));
aOldPos = convertTwipToMm100(aOldPos);
// find position of same point on the screen ( e.g. grid )
ScSplitPos eWhich(pViewData->GetActivePart());
Point aCurPos(pViewData->GetScrPos(aOldStt.Col(), aOldStt.Row(), eWhich, true));
Point aCurPosHmm(pGridWin->PixelToLogic(aCurPos, aDrawMode));
Point aGridOff(aCurPosHmm - aOldPos);
bool bLOKActive = comphelper::LibreOfficeKit::isActive();
bool bNegativePage = rDoc.IsNegativePage(GetTab());
// fdo#63878 Fix the X position for RTL Sheet
if(bNegativePage && !bLOKActive)
{
aGridOff.setX(aCurPosHmm.getX() + aOldPos.getX());
}
rTarget.setX(bLOKActive && bNegativePage ? -aGridOff.X() : aGridOff.X());
rTarget.setY(aGridOff.Y());
return true;
}
// Create a new view-local UndoManager manager for Calc
std::unique_ptr<SdrUndoManager> ScDrawView::createLocalTextUndoManager()
{
std::unique_ptr<SdrUndoManager> pUndoManager(new SdrUndoManager);
ScDocShell* pDocShell = pViewData ? pViewData->GetDocShell() : nullptr;
pUndoManager->SetDocShell(pDocShell);
return pUndoManager;
}
// #i123922# helper to apply a Graphic to an existing SdrObject
SdrObject* ScDrawView::ApplyGraphicToObject(
SdrObject& rHitObject,
const Graphic& rGraphic,
const OUString& rBeginUndoText,
const OUString& rFile)
{
if(auto pGrafHitObj = dynamic_cast< SdrGrafObj* >(&rHitObject))
{
rtl::Reference<SdrGrafObj> pNewGrafObj = SdrObject::Clone(*pGrafHitObj, rHitObject.getSdrModelFromSdrObject());
pNewGrafObj->SetGraphic(rGraphic);
BegUndo(rBeginUndoText);
ReplaceObjectAtView(&rHitObject, *GetSdrPageView(), pNewGrafObj.get());
// set in all cases - the Clone() will have copied an existing link (!)
pNewGrafObj->SetGraphicLink( rFile );
EndUndo();
return pNewGrafObj.get();
}
else if(rHitObject.IsClosedObj() && !dynamic_cast< SdrOle2Obj* >(&rHitObject))
{
AddUndo(std::make_unique<SdrUndoAttrObj>(rHitObject));
SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLBITMAP> aSet(GetModel().GetItemPool());
aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP));
aSet.Put(XFillBitmapItem(OUString(), rGraphic));
rHitObject.SetMergedItemSetAndBroadcast(aSet);
return &rHitObject;
}
return nullptr;
}
// Own derivation of ObjectContact to allow on-demand calculation of
// GridOffset for non-linear ViewToDevice transformation (calc)
namespace sdr::contact
{
namespace {
class ObjectContactOfScDrawView final : public ObjectContactOfPageView
{
private:
// The ScDrawView to work on
const ScDrawView& mrScDrawView;
public:
explicit ObjectContactOfScDrawView(
const ScDrawView& rScDrawView,
SdrPageWindow& rPageWindow,
const char* pDebugName);
virtual bool supportsGridOffsets() const override;
virtual void calculateGridOffsetForViewObjectContact(
basegfx::B2DVector& rTarget,
const ViewObjectContact& rClient) const override;
virtual void calculateGridOffsetForB2DRange(
basegfx::B2DVector& rTarget,
const basegfx::B2DRange& rB2DRange) const override;
};
}
ObjectContactOfScDrawView::ObjectContactOfScDrawView(
const ScDrawView& rScDrawView,
SdrPageWindow& rPageWindow,
const char* pDebugName)
: ObjectContactOfPageView(rPageWindow, pDebugName),
mrScDrawView(rScDrawView)
{
}
bool ObjectContactOfScDrawView::supportsGridOffsets() const
{
// Except when scPrintTwipsMsgs flag is active,
// Calc in LOK mode directly sets pixel-aligned logical coordinates for draw-objects.
if (comphelper::LibreOfficeKit::isActive() &&
!comphelper::LibreOfficeKit::isCompatFlagSet(
comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs))
return false;
// no GridOffset support for printer
if(isOutputToPrinter())
{
return false;
}
// no GridOffset support for PDF export
if(isOutputToPDFFile())
{
return false;
}
// yes - we support it
return true;
}
void ObjectContactOfScDrawView::calculateGridOffsetForViewObjectContact(
basegfx::B2DVector& rTarget,
const ViewObjectContact& rClient) const
{
// Here the on-demand calculation happens. Try to access the SdrObject involved
SdrObject* pTargetSdrObject(rClient.GetViewContact().TryToGetSdrObject());
if(nullptr != pTargetSdrObject)
{
mrScDrawView.calculateGridOffsetForSdrObject(
*pTargetSdrObject,
rTarget);
}
}
void ObjectContactOfScDrawView::calculateGridOffsetForB2DRange(
basegfx::B2DVector& rTarget,
const basegfx::B2DRange& rB2DRange) const
{
// Here the on-demand calculation happens. Try to access the SdrObject involved
if(!rB2DRange.isEmpty())
{
mrScDrawView.calculateGridOffsetForB2DRange(
rB2DRange,
rTarget);
}
}
}
// Create own derivation of ObjectContact for calc
sdr::contact::ObjectContact* ScDrawView::createViewSpecificObjectContact(
SdrPageWindow& rPageWindow,
const char* pDebugName) const
{
return new sdr::contact::ObjectContactOfScDrawView(
*this,
rPageWindow,
pDebugName);
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */