// #i33700# // Set shadow distance defaults as PoolDefaultItems. Details see bug.
rPool.SetUserDefaultItem(makeSdrShadowXDistItem(300));
rPool.SetUserDefaultItem(makeSdrShadowYDistItem(300));
// default for script spacing depends on locale, see SdDrawDocument ctor in sd
LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); if (MsLangId::isKorean(eOfficeLanguage) || eOfficeLanguage == LANGUAGE_JAPANESE)
{ // secondary is edit engine pool
rPool.GetSecondaryPool()->SetUserDefaultItem( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) );
}
SetStyleSheetPool(pDocument ? pDocument->GetStyleSheetPool() : new ScStyleSheetPool(rPool, pDocument));
SdrLayerAdmin& rAdmin = GetLayerAdmin();
rAdmin.NewLayer(u"vorne"_ustr, SC_LAYER_FRONT.get());
rAdmin.NewLayer(u"hinten"_ustr, SC_LAYER_BACK.get());
rAdmin.NewLayer(u"intern"_ustr, SC_LAYER_INTERN.get()); // tdf#140252 use same name as in ctor of SdrLayerAdmin
rAdmin.NewLayer(rAdmin.GetControlLayerName(), SC_LAYER_CONTROLS.get());
rAdmin.NewLayer(u"hidden"_ustr, SC_LAYER_HIDDEN.get());
// Set link for URL-Fields
ScModule* pScMod = ScModule::get();
Outliner& rOutliner = GetDrawOutliner();
rOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) );
rOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool()));
sal_uInt16 nCount = GetPageCount(); for (sal_uInt16 i=0; i<nCount && !bFound; i++) if (GetPage(i)->GetObjCount())
bFound = true;
return bFound;
}
SdrModel* ScDrawLayer::AllocModel() const
{ // Allocated model (for clipboard etc) must not have a pointer // to the original model's document, pass NULL as document: auto pNewModel = std::make_unique<ScDrawLayer>(nullptr, aName); auto pNewPool = static_cast<ScStyleSheetPool*>(pNewModel->GetStyleSheetPool());
pNewPool->CopyUsedGraphicStylesFrom(GetStyleSheetPool());
return pNewModel.release();
}
bool ScDrawLayer::ScAddPage( SCTAB nTab )
{ if (bDrawIsInUndo) returnfalse; // not inserted
void ScDrawLayer::ScRemovePage( SCTAB nTab )
{ if (bDrawIsInUndo) return;
Broadcast( ScTabDeletedHint( nTab ) ); if (bRecording)
{
SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
AddCalcUndo(std::make_unique<SdrUndoDelPage>(*pPage)); // Undo-Action becomes the page owner
RemovePage( static_cast<sal_uInt16>(nTab) ); // just deliver, not deleting
} else
DeletePage( static_cast<sal_uInt16>(nTab) ); // just get rid of it
if ( rSize != pPage->GetSize() )
{
pPage->SetSize( rSize );
Broadcast( ScTabSizeChangedHint( static_cast<SCTAB>(nPageNo) ) ); // SetWorkArea() on the views
}
// Do not call RecalcPos while loading, because row height is not finished, when SetPageSize // is called first time. Instead the objects are initialized from ScXMLImport::endDocument() and // RecalcPos is called from there. if (!pDoc || pDoc->IsImportingXML()) return;
// Implement Detective lines (adjust to new heights / widths) // even if size is still the same // (individual rows/columns can have been changed))
tools::Rectangle lcl_UpdateCalcPoly(basegfx::B2DPolygon &rCalcPoly, int nWhichPoint, const Point &rPos)
{
rCalcPoly.setB2DPoint(nWhichPoint, basegfx::B2DPoint(rPos.X(), rPos.Y()));
basegfx::B2DRange aRange(basegfx::utils::getRange(rCalcPoly)); return tools::Rectangle(static_cast<tools::Long>(aRange.getMinX()), static_cast<tools::Long>(aRange.getMinY()), static_cast<tools::Long>(aRange.getMaxX()), static_cast<tools::Long>(aRange.getMaxY()));
}
bool lcl_AreRectanglesApproxEqual(const tools::Rectangle& rRectA, const tools::Rectangle& rRectB)
{ // Twips <-> Hmm conversions introduce +-1 differences although there are no real changes in the object. // Therefore test with == is not appropriate in some cases. if (std::abs(rRectA.Left() - rRectB.Left()) > 1) returnfalse; if (std::abs(rRectA.Top() - rRectB.Top()) > 1) returnfalse; if (std::abs(rRectA.Right() - rRectB.Right()) > 1) returnfalse; if (std::abs(rRectA.Bottom() - rRectB.Bottom()) > 1) returnfalse; returntrue;
}
void lcl_SetLogicRectFromAnchor(SdrObject* pObj, const ScDrawObjData& rAnchor, const ScDocument* pDoc)
{ // This is only used during initialization. At that time, shape handling is always LTR. No need // to consider negative page. if (!pObj || !pDoc || !rAnchor.maEnd.IsValid() || !rAnchor.maStart.IsValid()) return;
// In case of a vertical mirrored custom shape, LibreOffice uses internally an additional 180deg // in aGeo.nRotationAngle and in turn has a different logic rectangle position. We remove flip, // set the logic rectangle, and apply flip again. You cannot simple use a 180deg-rotated // rectangle, because custom shape mirroring is internally applied after the other // transformations. constbool bNeedsMirrorYCorrection = lcl_NeedsMirrorYCorrection(pObj); // remember state if (bNeedsMirrorYCorrection)
{ const tools::Rectangle aRect(pObj->GetSnapRect()); const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1); const Point aRight(aLeft.X() + 1000, aLeft.Y());
pObj->NbcMirror(aLeft, aRight);
}
// Build full sized logic rectangle from start and end given in anchor. const tools::Rectangle aStartCellRect(
pDoc->GetMMRect(rAnchor.maStart.Col(), rAnchor.maStart.Row(), rAnchor.maStart.Col(),
rAnchor.maStart.Row(), rAnchor.maStart.Tab(), false/*bHiddenAsZero*/));
Point aStartPoint(aStartCellRect.Left(), aStartCellRect.Top());
aStartPoint.AdjustX(rAnchor.maStartOffset.getX());
aStartPoint.AdjustY(rAnchor.maStartOffset.getY());
Point aEndPoint(aEndCellRect.Left(), aEndCellRect.Top());
aEndPoint.AdjustX(rAnchor.maEndOffset.getX());
aEndPoint.AdjustY(rAnchor.maEndOffset.getY());
// Set this as new, full sized logical rectangle
tools::Rectangle aNewRectangle(aStartPoint, aEndPoint);
aNewRectangle.Normalize(); if (!lcl_AreRectanglesApproxEqual(pObj->GetLogicRect(), aNewRectangle))
pObj->NbcSetLogicRect(lcl_makeSafeRectangle(aNewRectangle));
// The shape has the correct logical rectangle now. Reapply the above removed mirroring. if (bNeedsMirrorYCorrection)
{ const tools::Rectangle aRect(pObj->GetSnapRect()); const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1); const Point aRight(aLeft.X() + 1000, aLeft.Y());
pObj->NbcMirror(aLeft, aRight);
}
}
// this sets the needed changed position (translation)
aRect.SetPos(aPos);
if (bCanResize)
{ // all this stuff is additional stuff to evtl. not only translate the // range (Rectangle), but also check for and evtl. do corrections for it's size const tools::Rectangle aLastCellRect(rData.getLastCellRect());
// If the row was hidden before, or we don't have a valid cell rect, calculate the // new rect based on the end point. // Also when the end point is set, we need to consider it. if (rData.mbWasInHiddenRow || aLastCellRect.IsEmpty() || nRow1 != nRow2 || nCol1 != nCol2)
{
Point aEnd(pDoc->GetColOffset(nCol2, nTab2, /*bHiddenAsZero*/true),
pDoc->GetRowOffset(nRow2, nTab2, /*bHiddenAsZero*/true));
aEnd.setX(convertTwipToMm100(aEnd.X()));
aEnd.setY(convertTwipToMm100(aEnd.Y()));
aEnd += lcl_calcAvailableDiff(*pDoc, nCol2, nRow2, nTab2, rData.maEndOffset);
aRect = tools::Rectangle(aPos, aEnd);
} elseif (!aLastCellRect.IsEmpty())
{ // We calculate based on the last cell rect to be able to scale the image // as much as the cell was scaled. // Still, we keep the image in its current cell (to keep start anchor == end anchor) const tools::Rectangle aCurrentCellRect(GetCellRect(*GetDocument(), rData.maStart, true));
tools::Long nCurrentWidth(aCurrentCellRect.GetWidth());
tools::Long nCurrentHeight(aCurrentCellRect.GetHeight()); const tools::Long nLastWidth(aLastCellRect.GetWidth()); const tools::Long nLastHeight(aLastCellRect.GetHeight());
// tdf#116931 Avoid and correct nifty numerical problems with the integer // based and converted values (GetCellRect uses multiplies with HMM_PER_TWIPS) if(nCurrentWidth + 1 == nLastWidth || nCurrentWidth == nLastWidth + 1)
{
nCurrentWidth = nLastWidth;
}
if(bIsGrowing) // cell is growing larger
{ // To actually grow the image, we need to take the max
fWidthFactor = std::max(fWidthFactor, fHeightFactor);
} elseif(bIsShrinking) // cell is growing smaller, take the min
{
fWidthFactor = std::min(fWidthFactor, fHeightFactor);
}
// We don't want the image to become larger than the current cell
fWidthFactor = fHeightFactor = std::min(fWidthFactor, fMaxFactor);
}
if(bIsSizeChanged)
{ // tdf#116931 re-organized scaling (if needed) // Check if we need to scale at all. Always scale on growing. bool bNeedToScale(bIsGrowing);
if(!bNeedToScale && bIsShrinking)
{ // Check if original still fits into space. Do *not* forget to // compare with evtl. numerically corrected aCurrentCellRect constbool bFitsInX(aRect.Right() <= aCurrentCellRect.Left() + nCurrentWidth); constbool bFitsInY(aRect.Bottom() <= aCurrentCellRect.Top() + nCurrentHeight);
// If the image still fits in the smaller cell, don't resize it at all
bNeedToScale = (!bFitsInX || !bFitsInY);
}
if(bNeedToScale)
{ // tdf#116931 use transformations now. Translation is already applied // (see aRect.SetPos above), so only scale needs to be applied - relative // to *new* CellRect (which is aCurrentCellRect). // Prepare scale relative to top-left of aCurrentCellRect
basegfx::B2DHomMatrix aChange;
void ScDrawLayer::InitializeCellAnchoredObj(SdrObject* pObj, ScDrawObjData& rData)
{ // This is called from ScXMLImport::endDocument() if (!pDoc || !pObj) return; if (!rData.getShapeRect().IsEmpty()) return; // already initialized, should not happen if (rData.meType == ScDrawObjData::CellNote || rData.meType == ScDrawObjData::ValidationCircle
|| rData.meType == ScDrawObjData::DetectiveArrow) return; // handled in RecalcPos
// Prevent multiple broadcasts during the series of changes. bool bWasLocked = pObj->getSdrModelFromSdrObject().isLocked();
pObj->getSdrModelFromSdrObject().setLock(true);
// rNoRotatedAnchor refers in its start and end addresses and its start and end offsets to // the logic rectangle of the object. The values are so, as if no hidden columns and rows // exists and if it is a LTR sheet. These values are directly used for XML in ODF file.
ScDrawObjData& rNoRotatedAnchor = *GetNonRotatedObjData(pObj, true/*bCreate*/);
// From XML import, rData contains temporarily the anchor information as they are given in // XML. Copy it to rNoRotatedAnchor, where it belongs. rData will later contain the anchor // of the transformed object as visible on screen.
rNoRotatedAnchor.maStart = rData.maStart;
rNoRotatedAnchor.maEnd = rData.maEnd;
rNoRotatedAnchor.maStartOffset = rData.maStartOffset;
rNoRotatedAnchor.maEndOffset = rData.maEndOffset;
SCCOL nCol1 = rNoRotatedAnchor.maStart.Col();
SCROW nRow1 = rNoRotatedAnchor.maStart.Row();
SCTAB nTab1 = rNoRotatedAnchor.maStart.Tab(); // Used as parameter several times
// Object has coordinates relative to left/top of containing cell in XML. Change object to // absolute coordinates as internally used. const tools::Rectangle aRect(
pDoc->GetMMRect(nCol1, nRow1, nCol1, nRow1, nTab1, false/*bHiddenAsZero*/)); const Size aShift(aRect.Left(), aRect.Top());
pObj->NbcMove(aShift);
const ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObj); if (aAnchorType == SCA_CELL_RESIZE)
{ if (pObj->GetObjIdentifier() == SdrObjKind::Line)
{ // Horizontal lines might have wrong start and end anchor because of erroneously applied // 180deg rotation (tdf#137446). Other lines have wrong end anchor. Coordinates in // object are correct. Use them for recreating the anchor. const basegfx::B2DPolygon aPoly( static_cast<SdrPathObj*>(pObj)->GetPathPoly().getB2DPolygon(0)); const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0)); const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1)); const Point aPointLT(basegfx::fround<tools::Long>(std::min(aB2DPoint0.getX(), aB2DPoint1.getX())),
basegfx::fround<tools::Long>(std::min(aB2DPoint0.getY(), aB2DPoint1.getY()))); const Point aPointRB(basegfx::fround<tools::Long>(std::max(aB2DPoint0.getX(), aB2DPoint1.getX())),
basegfx::fround<tools::Long>(std::max(aB2DPoint0.getY(), aB2DPoint1.getY()))); const tools::Rectangle aObjRect(aPointLT, aPointRB);
GetCellAnchorFromPosition(aObjRect, rNoRotatedAnchor, *pDoc, nTab1, false/*bHiddenAsZero*/);
} elseif (pObj->GetObjIdentifier() == SdrObjKind::Measure)
{ // Measure lines might have got wrong start and end anchor from XML import. Recreate // anchor from start and end point.
SdrMeasureObj* pMeasureObj = static_cast<SdrMeasureObj*>(pObj); // tdf#137576. The logic rectangle has likely no current values here, but only the // 1cm x 1cm default size. The call of TakeUnrotatedSnapRect is currently (LO 7.2) // the only way to force a recalc of the logic rectangle.
tools::Rectangle aObjRect;
pMeasureObj->TakeUnrotatedSnapRect(aObjRect);
GetCellAnchorFromPosition(aObjRect, rNoRotatedAnchor, *pDoc, rData.maStart.Tab(), false/*bHiddenAsZero*/);
} elseif (pObj->IsResizeProtect())
{ // tdf#154005: This is a workaround for documents created with LO 6 and older.
rNoRotatedAnchor.mbResizeWithCell = false;
rData.mbResizeWithCell = false;
UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1, true/*bUseLogicRect*/);
} elseif (pObj->GetObjIdentifier() == SdrObjKind::Group)
{ // nothing to do.
} else
{ // In case there are hidden rows or cols, versions 7.0 and earlier have written width and // height in file so that hidden row or col are count as zero. XML import bases the // logical rectangle of the object on it. Shapes have at least wrong size, when row or col // are shown. We try to regenerate the logic rectangle as far as possible from the anchor. // ODF specifies anyway, that the size has to be ignored, if end cell attributes exist.
lcl_SetLogicRectFromAnchor(pObj, rNoRotatedAnchor, pDoc);
}
} else// aAnchorType == SCA_CELL
{ // XML has no end cell address in this case. We generate it from position.
UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1, true/*bUseLogicRect*/);
}
// Make sure maShapeRect of rNoRotatedAnchor is not empty. Method ScDrawView::Notify() // needs it to detect a change in object geometry. For example a 180deg rotation effects only // logic rect.
rNoRotatedAnchor.setShapeRect(GetDocument(), pObj->GetLogicRect(), true);
// Start and end addresses and offsets in rData refer to the actual snap rectangle of the // shape. We initialize them here based on the "full" sized object. Adaptation to reduced size // (by hidden row/col) is done later in RecalcPos.
GetCellAnchorFromPosition(pObj->GetSnapRect(), rData, *pDoc, nTab1, false/*bHiddenAsZero*/);
// As of ODF 1.3 strict there is no attribute to store whether an object is hidden. So a "visible" // object might actually be hidden by being in hidden row or column. We detect it here. // Note, that visibility by hidden row or column refers to the snap rectangle. if (pObj->IsVisible()
&& (pDoc->RowHidden(rData.maStart.Row(), rData.maStart.Tab())
|| pDoc->ColHidden(rData.maStart.Col(), rData.maStart.Tab())))
pObj->SetVisible(false);
// Set visibility. ToDo: Really used?
rNoRotatedAnchor.setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible());
// And set maShapeRect in rData. It stores not only the current rectangles, but currently, // existence of maShapeRect is the flag for initialization is done.
rData.setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible());
if (rData.meType == ScDrawObjData::CellNote)
{
OSL_ENSURE( rData.maStart.IsValid(), "ScDrawLayer::RecalcPos - invalid position for cell note" ); /* #i109372# On insert/remove rows/columns/cells: Updating the caption positionmustnotbedone,ifthecellcontainingthenotehasnot beenmovedyetinthedocument.Thecallingcodenowpassesan
additional boolean stating if the cells are already moved. */ /* tdf #152081 Do not change hidden objects. That would produce zero height
or width and loss of caption.*/ if (bUpdateNoteCaptionPos && pObj->IsVisible())
{ /* When inside an undo action, there may be pending note captions wherecellnoteisalreadydeleted(thusdocumentcannotfind thenoteobjectanymore).Thecaptionwillbedeletedlater
with drawing undo. */ if( ScPostIt* pNote = pDoc->GetNote( rData.maStart ) )
pNote->UpdateCaptionPos( rData.maStart );
} return;
}
if (rData.meType == ScDrawObjData::ValidationCircle)
{ // Validation circle for detective.
rData.setShapeRect(GetDocument(), pObj->GetLogicRect());
// rData.maStart should contain the address of the be validated cell.
tools::Rectangle aRect = GetCellRect(*GetDocument(), rData.maStart, true);
aRect.AdjustLeft( -250 );
aRect.AdjustRight(250 );
aRect.AdjustTop( -70 );
aRect.AdjustBottom(70 ); if ( bNegativePage )
MirrorRectRTL( aRect );
if ( pObj->GetLogicRect() != aRect )
{ if (bRecording)
AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect)); // maStart has the meaning of "to be validated cell" in a validation circle. For usual // drawing objects it has the meaning "left/top of logic/snap rect". Because the rectangle // is expanded above, SetLogicRect() will set maStart to one cell left and one cell above // of the to be validated cell. We need to backup the old value and restore it.
ScAddress aBackup(rData.maStart);
pObj->SetLogicRect(rData.getShapeRect());
rData.maStart = aBackup;
}
} elseif (rData.meType == ScDrawObjData::DetectiveArrow)
{
rData.setShapeRect(GetDocument(), pObj->GetLogicRect());
basegfx::B2DPolygon aCalcPoly;
Point aOrigStartPos(pObj->GetPoint(0));
Point aOrigEndPos(pObj->GetPoint(1));
aCalcPoly.append(basegfx::B2DPoint(aOrigStartPos.X(), aOrigStartPos.Y()));
aCalcPoly.append(basegfx::B2DPoint(aOrigEndPos.X(), aOrigEndPos.Y())); //TODO: do not create multiple Undos for one object (last one can be omitted then)
SCCOL nLastCol;
SCROW nLastRow; if( bValid1 )
{
Point aPos( pDoc->GetColOffset( nCol1, nTab1 ), pDoc->GetRowOffset( nRow1, nTab1 ) ); if (!pDoc->ColHidden(nCol1, nTab1, nullptr, &nLastCol))
aPos.AdjustX(pDoc->GetColWidth( nCol1, nTab1 ) / 4 ); if (!pDoc->RowHidden(nRow1, nTab1, nullptr, &nLastRow))
aPos.AdjustY(pDoc->GetRowHeight( nRow1, nTab1 ) / 2 );
aPos.setX(convertTwipToMm100(aPos.X()));
aPos.setY(convertTwipToMm100(aPos.Y()));
Point aStartPos = aPos; if ( bNegativePage )
aStartPos.setX( -aStartPos.X() ); // don't modify aPos - used below if ( pObj->GetPoint( 0 ) != aStartPos )
{ if (bRecording)
AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
if( !bValid1 )
{
Point aStartPos( aPos.X() - DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET ); if (aStartPos.X() < 0)
aStartPos.AdjustX(2 * DET_ARROW_OFFSET); if (aStartPos.Y() < 0)
aStartPos.AdjustY(2 * DET_ARROW_OFFSET); if ( bNegativePage )
aStartPos.setX( -aStartPos.X() ); if ( pObj->GetPoint( 0 ) != aStartPos )
{ if (bRecording)
AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 0, aStartPos));
pObj->SetPoint( aStartPos, 0 );
}
}
}
} // end ScDrawObjData::DetectiveArrow else// start ScDrawObjData::DrawingObject
{ // Do not change hidden objects. That would produce zero height or width and loss of offsets. if (!pObj->IsVisible()) return;
// Prevent multiple broadcasts during the series of changes. bool bWasLocked = pObj->getSdrModelFromSdrObject().isLocked();
pObj->getSdrModelFromSdrObject().setLock(true);
// ToDo: Implement NbcSetSnapRect of SdrMeasureObj. Then this can be removed.
tools::Long nOldWidth = aOld.GetWidth();
tools::Long nOldHeight = aOld.GetHeight(); if (pObj->IsPolyObj() && nOldWidth && nOldHeight)
{ // Polyline objects need special treatment.
Size aSizeMove(aNew.Left()-aOld.Left(), aNew.Top()-aOld.Top());
pObj->NbcMove(aSizeMove);
rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(rData.getShapeRect()), pObj->IsVisible()); if (pObj->GetObjIdentifier() == SdrObjKind::CustomShape)
pObj->AdjustToMaxRect(rData.getShapeRect()); else
pObj->SetSnapRect(rData.getShapeRect());
// The shape rectangle in the 'unrotated' anchor needs to be updated to the changed // object geometry. It is used in adjustAnchoredPosition() in ScDrawView::Notify().
rNoRotatedAnchor.setShapeRect(pDoc, pObj->GetLogicRect(), pObj->IsVisible());
}
} else
{ const Point aPos(rData.getShapeRect().TopLeft()); if ( pObj->GetRelativePos() != aPos )
{ if (bRecording)
AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
pObj->SetRelativePos( aPos );
rNoRotatedAnchor.setShapeRect(pDoc, pObj->GetLogicRect(), pObj->IsVisible());
}
} /* *Ifwewerenotallowedresizetheobject,thentheendcellanchor *ispossiblyincorrectnow,andiftheobjecthasnoend-cell(e.g. *missinginoriginal.xml)wearealsoforcedtogenerateone
*/ bool bEndAnchorIsBad = !bValid2 || pObj->IsResizeProtect(); if (bEndAnchorIsBad)
{ // update 'rotated' anchor
ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, rData, *pDoc, nTab1, false); // update 'unrotated' anchor
ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1 );
}
// End prevent multiple broadcasts during the series of changes.
pObj->getSdrModelFromSdrObject().setLock(bWasLocked); if (!bWasLocked)
pObj->BroadcastObjectChange();
} // end ScDrawObjData::DrawingObject
}
if ( bNegativePage )
{
nStartX = -nStartX; // positions are negative, swap start/end so the same comparisons work
nEndX = -nEndX;
::std::swap( nStartX, nEndX );
}
const SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab));
OSL_ENSURE(pPage,"Page not found"); if (pPage)
{
SdrObjListIter aIter( pPage, SdrIterMode::Flat ); while (SdrObject* pObject = aIter.Next())
{ //TODO: test Flags (hidden?)
tools::Rectangle aObjRect = pObject->GetCurrentBoundRect(); bool bFit = true; if ( !bSetHor && ( aObjRect.Right() < nStartX || aObjRect.Left() > nEndX ) )
bFit = false; if ( !bSetVer && ( aObjRect.Bottom() < nStartY || aObjRect.Top() > nEndY ) )
bFit = false; // #i104716# don't include hidden note objects if ( bFit && pObject->GetLayer() != SC_LAYER_HIDDEN )
{ if (bSetHor)
{ if (aObjRect.Left() < nStartX) nStartX = aObjRect.Left(); if (aObjRect.Right() > nEndX) nEndX = aObjRect.Right();
} if (bSetVer)
{ if (aObjRect.Top() < nStartY) nStartY = aObjRect.Top(); if (aObjRect.Bottom() > nEndY) nEndY = aObjRect.Bottom();
}
bAny = true;
}
}
}
if ( bNegativePage )
{
nStartX = -nStartX; // reverse transformation, so the same cell address calculation works
nEndX = -nEndX;
::std::swap( nStartX, nEndX );
}
if (bAny)
{
OSL_ENSURE( nStartX<=nEndX && nStartY<=nEndY, "Start/End wrong in ScDrawLayer::GetPrintArea" );
SdrObjListIter aIter( pPage, SdrIterMode::Flat ); while (SdrObject* pObject = aIter.Next())
{ // do not delete note caption, they are always handled by the cell note // TODO: detective objects are still deleted, is this desired? if (!IsNoteCaption( pObject ))
{
tools::Rectangle aObjRect = pObject->GetCurrentBoundRect();
ScRange aRange = pDoc->GetRange(nTab, aObjRect); bool bObjectInMarkArea =
aMarkBound.Contains(aObjRect) && rMark.IsAllMarked(aRange); const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObject);
ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObject); bool bObjectAnchoredToMarkedCell
= ((aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE)
&& pObjData && pObjData->maStart.IsValid()
&& rMark.IsCellMarked(pObjData->maStart.Col(),
pObjData->maStart.Row())); if (bObjectInMarkArea || bObjectAnchoredToMarkedCell)
{
ppObj.push_back(pObject);
}
}
}
// Delete objects (backwards)
if (bRecording) for (auto p : ppObj)
AddCalcUndo(std::make_unique<SdrUndoDelObj>(*p));
for (auto p : ppObj)
pPage->RemoveObject(p->GetOrdNum());
}
} else
{
OSL_FAIL("pPage?");
}
}
}
void ScDrawLayer::CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const tools::Rectangle& rRange )
{ // copy everything in the specified range into the same page (sheet) in the clipboard doc
SdrPage* pSrcPage = GetPage(static_cast<sal_uInt16>(nTab)); if (!pSrcPage) return;
SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat ); while (SdrObject* pOldObject = aIter.Next())
{ // do not copy internal objects (detective) and note captions if (pOldObject->GetLayer() == SC_LAYER_INTERN) continue;
const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject); if (IsNoteCaption(pObjData)) continue;
// Catch objects where the object itself is inside the rectangle to be copied. bool bObjectInArea = rRange.Contains(pOldObject->GetCurrentBoundRect()); // Catch objects whose anchor is inside the rectangle to be copied. if (!bObjectInArea && pObjData)
bObjectInArea = aClipRange.Contains(pObjData->maStart); if (!bObjectInArea) continue;
if (!pDestModel)
{
pDestModel = pClipDoc->GetDrawLayer(); // does the document already have a drawing layer? if (!pDestModel)
{ // allocate drawing layer in clipboard document only if there are objects to copy
OSL_ENSURE(pDestPage, "no page"); if (pDestPage)
{ // Clone to target SdrModel
rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*pDestModel));
uno::Reference< chart2::XChartDocument > xOldChart( ScChartHelper::GetChartFromSdrObject( pOldObject ) ); if(!xOldChart.is())//#i110034# do not move charts as they lose all their data references otherwise
{ if (pObjData)
{ // The object is anchored to cell. The position is determined by the start // address. Copying into the clipboard does not change the anchor. // ToDo: Adapt Offset relative to anchor cell size for cell anchored. // ToDo: Adapt Offset and size for cell-anchored with resize objects. // ToDo: Exclude object from resize if disallowed at object.
} else
{ // The object is anchored to page. We make its position so, that the // cell behind the object will have the same address in clipboard document as // in source document. So we will be able to reconstruct the original cell // address from position when pasting the object.
tools::Rectangle aObjRect = pOldObject->GetSnapRect();
ScRange aPseudoAnchor = pDoc->GetRange(nTab, aObjRect, true/*bHiddenAsZero*/);
tools::Rectangle aSourceCellRect
= GetCellRect(*pDoc, aPseudoAnchor.aStart, false/*bMergedCell*/);
tools::Rectangle aDestCellRect
= GetCellRect(*pClipDoc, aPseudoAnchor.aStart, false);
Point aMove = aDestCellRect.TopLeft() - aSourceCellRect.TopLeft();
pNewObject->NbcMove(Size(aMove.getX(), aMove.getY()));
}
}
pDestPage->InsertObject(pNewObject.get());
// Store the chart's source data to the clipboard document, even when it's out of the // copied range. It will be ignored when pasted to the same document; when pasted to // another document, ScDocument::mpClipParam will provide the actually copied ranges, // and the data copied here will be used to break connection and switch to own data // in ScDrawLayer::CopyFromClip. if (xOldChart && !xOldChart->hasInternalDataProvider())
{
sc::CopyToClipContext aCxt(*pClipDoc, false, true);
OUString aChartName = static_cast<SdrOle2Obj*>(pOldObject)->GetPersistName();
std::vector<ScRangeList> aRangesVector;
pDoc->GetChartRanges(aChartName, aRangesVector, *pDoc); for (const ScRangeList& ranges : aRangesVector)
{ for (const ScRange& r : ranges)
{ for (SCTAB i = r.aStart.Tab(); i <= r.aEnd.Tab(); ++i)
{
ScTable* pTab = pDoc->FetchTable(i);
ScTable* pClipTab = pClipDoc->FetchTable(i); if (!pTab || !pClipTab) continue;
pTab->CopyToClip(aCxt, r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(),
r.aEnd.Row(), pClipTab);
}
}
}
}
// no undo needed in clipboard document // charts are not updated
}
}
}
staticbool lcl_IsAllInRange( const ::std::vector< ScRangeList >& rRangesVector, constScRange& rClipRange )
{ // check if every range of rRangesVector is completely in rClipRange
for( const ScRangeList& rRanges : rRangesVector )
{ for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ )
{ const ScRange & rRange = rRanges[ i ]; if ( !rClipRange.Contains( rRange ) )
{ returnfalse; // at least one range is not valid
}
}
}
ScDocument* pClipDoc = pClipModel->GetDocument(); if (!pClipDoc) return; // Can this happen? And if yes, what to do?
SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat ); if (!aIter.Count()) return; // no objects at all. Nothing to do.
// a clipboard document and its source share the same document item pool, // so the pointers can be compared to see if this is copy&paste within // the same document bool bSameDoc = pDoc->GetPool() == pClipDoc->GetPool(); bool bDestClip = pDoc->IsClipboard(); // Happens while transposing. ToDo: Other cases?
//#i110034# charts need correct sheet names for xml range conversion during load //so the target sheet name is temporarily renamed (if we have any SdrObjects)
OUString aDestTabName; bool bRestoreDestTabName = false; if (!bSameDoc && !bDestClip)
{
OUString aSourceTabName; if (pClipDoc->GetName(nSourceTab, aSourceTabName) && pDoc->GetName(nDestTab, aDestTabName)
&& aSourceTabName != aDestTabName && pDoc->ValidNewTabName(aSourceTabName))
{
bRestoreDestTabName = pDoc->RenameTab( nDestTab, aSourceTabName );
}
}
// We are going to make all rectangle calculations on LTR, so determine whether doc is RTL. bool bSourceRTL = pClipDoc->IsLayoutRTL(nSourceTab); bool bDestRTL = pDoc->IsLayoutRTL(nDestTab);
while (SdrObject* pOldObject = aIter.Next())
{ // ToDO: Can this happen? Such objects should not be in the clipboard document. // do not copy internal objects (detective) and note captions if ((pOldObject->GetLayer() == SC_LAYER_INTERN) || IsNoteCaption(pOldObject)) continue;
// 'aIter' considers all objects on pSrcPage. But ScDocument::CopyBlockFromClip, which is used // for filtered data, acts not on the total range but only on parts of it. So we need to look, // whether an object is really contained in the current rSourceRange. // For cell anchored objects we use the start address of the anchor, for page anchored objects // we use the cell range behind the bounding box of the shape.
ScAddress aSrcObjStart; const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject); if (pObjData) // Object is anchored to cell.
{
aSrcObjStart = (*pObjData).maStart;
} else// Object is anchored to page.
{
aSrcObjStart = pClipDoc->GetRange(nSourceTab, pOldObject->GetCurrentBoundRect()).aStart;
} if (!rSourceRange.Contains(aSrcObjStart)) continue; // If object is anchored to a filtered cell, we will not copy it, because filtered rows are // eliminated in paste. Copying would produce hidden objects which can only be accessed per // macro. if (pObjData && pClipDoc->RowFiltered((*pObjData).maStart.Row(), nSourceTab)) continue;
// Copy style sheet auto pStyleSheet = pOldObject->GetStyleSheet(); if (pStyleSheet && !bSameDoc)
pDoc->GetStyleSheetPool()->CopyStyleFrom(pClipModel->GetStyleSheetPool(),
pStyleSheet->GetName(),
pStyleSheet->GetFamily(), true);
rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*this));
tools::Rectangle aObjRect = pOldObject->GetSnapRect(); if (bSourceRTL)
{
MirrorRTL(pNewObject.get()); // We make the calculations in LTR.
MirrorRectRTL(aObjRect);
}
bool bCanResize = IsResizeWithCell(*pOldObject) && !pOldObject->IsResizeProtect(); // We do not resize charts or other OLE objects and do not resize when transposing.
bCanResize &= pOldObject->GetObjIdentifier() != SdrObjKind::OLE2;
bCanResize &= !bTransposing && !pClipDoc->GetClipParam().isTransposed(); if (bCanResize)
{ // Filtered rows are eliminated on paste. Filtered cols do not exist as of May 2023. // Collapsed or hidden rows/cols are shown on paste. // Idea: First calculate top left cell and bottom right cell of pasted object. Then // calculate the object corners inside these cell and from that build new snap rectangle. // We assume that pObjData is valid and pObjData and aObjRect correspond to each other // in the source document.
// Start cell of object in source and destination. The case of a filtered start cell is // already excluded above. aSrcObjStart = (*pObjData).maStart is already done above. // If filtered rows exist in the total range, the range was divided into blocks which // do not contain filtered rows. So the rows between start of aSourceRange and object // start row do not contain filtered rows.
SCROW nStartRowDiff = aSrcObjStart.Row() - rSourceRange.aStart.Row();
SCCOL nStartColDiff = aSrcObjStart.Col() - rSourceRange.aStart.Col();
ScAddress aDestObjStart = rDestRange.aStart;
aDestObjStart.IncCol(nStartColDiff);
aDestObjStart.IncRow(nStartRowDiff);
// End cell of object in source and destination. We look at the amount of rows/cols to be // added to get object end cell from object start cell.
ScAddress aSrcObjEnd = (*pObjData).maEnd;
SCCOL nColsToAdd = aSrcObjEnd.Col() - aSrcObjStart.Col();
SCROW nRowsToAdd
= pClipDoc->CountNonFilteredRows(aSrcObjStart.Row(), aSrcObjEnd.Row(), nSourceTab)
- 1;
ScAddress aDestObjEnd = aDestObjStart;
aDestObjEnd.IncCol(nColsToAdd);
aDestObjEnd.IncRow(nRowsToAdd);
// Position of object inside start and end cell in source. We describe the distance from // cell corner to object corner as ratio of offset to cell width/height. // We cannot use GetCellRect method, because that uses bHiddenAsZero=true.
Point aSrcObjTopLeftOffset = (*pObjData).maStartOffset;
tools::Rectangle aSrcStartRect
= pClipDoc->GetMMRect(aSrcObjStart.Col(), aSrcObjStart.Row(), aSrcObjStart.Col(),
aSrcObjStart.Row(), nSourceTab, false/*bHiddenAsZero*/); if (bSourceRTL)
MirrorRectRTL(aSrcStartRect); double fStartXRatio
= aSrcStartRect.getOpenWidth() == 0
? 1.0
: double(aSrcObjTopLeftOffset.X()) / double(aSrcStartRect.getOpenWidth()); double fStartYRatio
= aSrcStartRect.getOpenHeight() == 0
? 1.0
: double(aSrcObjTopLeftOffset.Y()) / double(aSrcStartRect.getOpenHeight());
Point aSrcObjBottomRightOffset = (*pObjData).maEndOffset;
tools::Rectangle aSrcEndRect
= pClipDoc->GetMMRect(aSrcObjEnd.Col(), aSrcObjEnd.Row(), aSrcObjEnd.Col(),
aSrcObjEnd.Row(), nSourceTab, false/*bHiddenAsZero*/); if (bSourceRTL)
MirrorRectRTL(aSrcEndRect); double fEndXRatio
= aSrcEndRect.getOpenWidth() == 0
? 1.0
: double(aSrcObjBottomRightOffset.X()) / double(aSrcEndRect.getOpenWidth()); double fEndYRatio
= aSrcEndRect.getOpenHeight() == 0
? 1.0
: double(aSrcObjBottomRightOffset.Y()) / double(aSrcEndRect.getOpenHeight()); // The end cell given in pObjData might be filtered. In that case the object is cut at // the lower cell edge. The offset is as large as the cell. if (pClipDoc->RowFiltered(aSrcObjEnd.Row(), nSourceTab))
fEndYRatio = 1.0;
// Position of object inside start and end cell in destination
tools::Rectangle aDestStartRect
= GetCellRect(*pDoc, aDestObjStart, false/*bMergedCell*/); if (bDestRTL)
MirrorRectRTL(aDestStartRect);
Point aDestObjTopLeftOffset(fStartXRatio * aDestStartRect.getOpenWidth(),
fStartYRatio * aDestStartRect.getOpenHeight());
Point aDestObjTopLeft = aDestStartRect.TopLeft() + aDestObjTopLeftOffset;
tools::Rectangle aDestEndRect = GetCellRect(*pDoc, aDestObjEnd, false/*bMergedCell*/); if (bDestRTL)
MirrorRectRTL(aDestEndRect);
Point aDestObjBottomRightOffset(fEndXRatio * aDestEndRect.getOpenWidth(),
fEndYRatio * aDestEndRect.getOpenHeight());
Point aDestObjBottomRight = aDestEndRect.TopLeft() + aDestObjBottomRightOffset;
// Fit new object into destination rectangle
tools::Rectangle aNewObjRect(aDestObjTopLeft, aDestObjBottomRight);
aNewObjRect = lcl_makeSafeRectangle(aNewObjRect); if (pNewObject->GetObjIdentifier() == SdrObjKind::CustomShape)
pNewObject->AdjustToMaxRect(aNewObjRect); else
pNewObject->SetSnapRect(aNewObjRect);
} else
{ // We determine the MM-distance of the new object from its start cell in destination from // the ratio of offset to cell width/height. Thus the object still starts in this cell // even if the destination cell has different size. Otherwise we might lose objects when // transposing.
// Position of object inside start cell in destination
tools::Rectangle aDestStartRect
= GetCellRect(*pDoc, aDestObjStart, false/*bMergedCell*/); if (bDestRTL)
MirrorRectRTL(aDestStartRect);
Point aDestObjTopLeftOffset(fStartXRatio * aDestStartRect.getOpenWidth(),
fStartYRatio * aDestStartRect.getOpenHeight());
Point aDestObjTopLeft = aDestStartRect.TopLeft() + aDestObjTopLeftOffset;
// Move new object to new position
Point aMoveBy = aDestObjTopLeft - aObjRect.TopLeft();
pNewObject->NbcMove(Size(aMoveBy.getX(), aMoveBy.getY()));
}
if (bDestRTL)
MirrorRTL(pNewObject.get());
// Changing object position or size does not automatically change its anchor. if (IsCellAnchored(*pOldObject))
SetCellAnchoredFromPosition(*pNewObject, *pDoc, nDestTab,
IsResizeWithCell(*pOldObject));
// InsertObject includes broadcasts // MakeNameUnique makes the pasted objects accessible via Navigator. if (bDestClip)
pDestPage->InsertObject(pNewObject.get()); else
{ if (bRecording)
pDoc->EnableUndo(false);
pDestPage->InsertObjectThenMakeNameUnique(pNewObject.get()); if (bRecording)
pDoc->EnableUndo(true);
}
if (bRecording)
AddCalcUndo(std::make_unique<SdrUndoInsertObj>(*pNewObject));
// always lose references when pasting into a clipboard document (transpose) if ((bInSourceRange || bSameDoc) && !bDestClip)
{ if (bInSourceRange)
{ if (rDestPos != aClipRange.aStart)
{ // update the data ranges to the new (copied) position if (lcl_MoveRanges(aRangesVector, aClipRange, rDestPos, *pDoc))
pDoc->SetChartRanges(aChartName, aRangesVector);
}
} else
{ // leave the ranges unchanged
}
} else
{ // pasting into a new document without the complete source data // -> break connection to source data and switch to own data
uno::Reference<chart::XChartDocument> xOldChartDoc(
ScChartHelper::GetChartFromSdrObject(pOldObject), uno::UNO_QUERY);
uno::Reference<chart::XChartDocument> xNewChartDoc(xNewChart,
uno::UNO_QUERY); if (xOldChartDoc.is() && xNewChartDoc.is())
xNewChartDoc->attachData(xOldChartDoc->getData());
// (see ScDocument::UpdateChartListenerCollection, PastingDrawFromOtherDoc)
}
}
}
}
}
}
// don't mirror OLE or graphics, otherwise ask the object // if it can be mirrored bool bCanMirror = ( nIdent != SdrObjKind::Graphic && nIdent != SdrObjKind::OLE2 ); if (bCanMirror)
{
SdrObjTransformInfoRec aInfo;
pObj->TakeObjInfo( aInfo );
bCanMirror = aInfo.bMirror90Allowed;
}
if (bCanMirror)
{
ScDrawObjData* pData = GetObjData(pObj); if (pData) // cell anchored
{ // Remember values from positive side. const tools::Rectangle aOldSnapRect = pObj->GetSnapRect(); const tools::Rectangle aOldLogicRect = pObj->GetLogicRect(); // Generate noRotate anchor if missing.
ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj); if (!pNoRotatedAnchor)
{
ScDrawObjData aNoRotateAnchor; const tools::Rectangle aLogicRect(pObj->GetLogicRect());
GetCellAnchorFromPosition(aLogicRect, aNoRotateAnchor,
*pDoc, pData->maStart.Tab());
aNoRotateAnchor.mbResizeWithCell = pData->mbResizeWithCell;
SetNonRotatedAnchor(*pObj, aNoRotateAnchor);
pNoRotatedAnchor = GetNonRotatedObjData(pObj);
assert(pNoRotatedAnchor);
} // Mirror object at vertical axis
Point aRef1( 0, 0 );
Point aRef2( 0, 1 ); if (bRecording)
AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
pObj->Mirror( aRef1, aRef2 );
// Adapt offsets in pNoRotatedAnchor so, that object will be moved to current position in // save and reload. const tools::Long nInverseShift = aOldSnapRect.Left() + aOldSnapRect.Right(); const Point aLogicLT = pObj->GetLogicRect().TopLeft(); const Point aMirroredLogicLT = aLogicLT + Point(nInverseShift, 0); const Point aOffsetDiff = aMirroredLogicLT - aOldLogicRect.TopLeft(); // new Offsets
pNoRotatedAnchor->maStartOffset += aOffsetDiff;
pNoRotatedAnchor->maEndOffset += aOffsetDiff;
} else// page anchored
{
Point aRef1( 0, 0 );
Point aRef2( 0, 1 ); if (bRecording)
AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) );
pObj->Mirror( aRef1, aRef2 );
}
} else
{ // Move instead of mirroring: // New start position is negative of old end position // -> move by sum of start and end position
tools::Rectangle aObjRect = pObj->GetSnapRect();
Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 ); if (bRecording)
AddCalcUndo( std::make_unique<SdrUndoMoveObj>( *pObj, aMoveSize ) );
pObj->Move( aMoveSize );
}
// for cell anchored objects adapt rectangles in anchors
ScDrawObjData* pData = GetObjData(pObj); if (pData)
{
pData->setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible());
ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj, true/*bCreate*/);
pNoRotatedAnchor->setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible());
}
}
OUString ScDrawLayer::GetVisibleName( const SdrObject* pObj )
{
OUString aName = pObj->GetName(); if ( pObj->GetObjIdentifier() == SdrObjKind::OLE2 )
{ // For OLE, the user defined name (GetName) is used // if it's not empty (accepting possibly duplicate names), // otherwise the persist name is used so every object appears // in the Navigator at all.
staticbool IsNamedObject( const SdrObject* pObj, std::u16string_view rName )
{ // sal_True if rName is the object's Name or PersistName // (used to find a named object)
/* The index passed to GetNewGraphicName() will be set to theusedindexineachcall.Thispreventstherepeatedsearch
for all names from 1 to current index. */
tools::Long nCounter = 0;
while (SdrObject* pObject = aIter.Next()) if ( pObject->GetObjIdentifier() == SdrObjKind::Graphic && pObject->GetName().isEmpty())
pObject->SetName( GetNewGraphicName( &nCounter ) );
}
}
}
// absolutely necessary to set flag, ScDrawLayer::RecalcPos expects it. if ( ScDrawObjData* pAnchor = GetObjData( &rObj ) )
{
pAnchor->setShapeRect(&rDoc, rObj.GetSnapRect());
}
// - keep also an anchor in terms of the Logic ( untransformed ) object // because that's what we stored ( and still do ) to xml
// Vertical flipped custom shapes need special treatment, see comment in // lcl_SetLogicRectFromAnchor
tools::Rectangle aObjRect2; if (lcl_NeedsMirrorYCorrection(&rObj))
{ const tools::Rectangle aRect(rObj.GetSnapRect()); const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1); const Point aRight(aLeft.X() + 1000, aLeft.Y());
rObj.NbcMirror(aLeft, aRight);
aObjRect2 = rObj.GetLogicRect();
rObj.NbcMirror(aLeft, aRight);
} elseif (rObj.GetObjIdentifier() == SdrObjKind::Measure)
{ // tdf#137576. A SdrMeasureObj might have a wrong logic rect here. TakeUnrotatedSnapRect // calculates the current unrotated snap rectangle, sets logic rectangle and returns it. static_cast<SdrMeasureObj&>(rObj).TakeUnrotatedSnapRect(aObjRect2);
} else
aObjRect2 = rObj.GetLogicRect();
// Values in XML are so as if it is a LTR sheet. The object is shifted to negative page on loading // so that the snap rectangle appears mirrored. For transformed objects the shifted logic rectangle // is not the mirrored LTR rectangle. We calculate the mirrored LTR rectangle here. if (rDoc.IsNegativePage(nTab))
{ const tools::Rectangle aSnapRect(rObj.GetSnapRect());
aObjRect2.Move(Size(-aSnapRect.Left() - aSnapRect.Right(), 0));
MirrorRectRTL(aObjRect2);
}
aNoRotatedAnchor.mbResizeWithCell = bResizeWithCell;
SetNonRotatedAnchor( rObj, aNoRotatedAnchor); // And update maShapeRect. It is used in adjustAnchoredPosition() in ScDrawView::Notify(). if (ScDrawObjData* pAnchor = GetNonRotatedObjData(&rObj))
{
pAnchor->setShapeRect(&rDoc, rObj.GetLogicRect());
}
}
bool ScDrawLayer::IsCellAnchored( const SdrObject& rObj )
{ // Cell anchored object always has a user data, to store the anchor cell // info. If it doesn't then it's page-anchored. // tdf#140866: Caption objects anchor position are handled differently. return GetFirstUserDataOfType(&rObj, SC_UD_OBJDATA) != nullptr
&& rObj.GetObjIdentifier() != SdrObjKind::Caption;
}
bool ScDrawLayer::IsResizeWithCell( const SdrObject& rObj )
{ // Cell anchored object always has a user data, to store the anchor cell // info. If it doesn't then it's page-anchored.
ScDrawObjData* pDrawObjData = GetObjData(const_cast<SdrObject*>(&rObj)); if (!pDrawObjData) returnfalse;
bool ScDrawLayer::HasObjectsAnchoredInRange(const ScRange& rRange)
{ // This only works for one table at a time
assert(rRange.aStart.Tab() == rRange.aEnd.Tab());
SdrObjListIter aIter( pPage, SdrIterMode::Flat ); while (SdrObject* pObject = aIter.Next())
{ if (!dynamic_cast<SdrCaptionObj*>(pObject)) // Caption objects are handled differently
{
ScDrawObjData* pObjData = GetObjData(pObject); if (pObjData && rRange.Contains(pObjData->maStart)) // Object is in given range 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.