1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20 #include <memory>
21 #include <postit.hxx>
22
23 #include <rtl/ustrbuf.hxx>
24 #include <sal/log.hxx>
25 #include <unotools/useroptions.hxx>
26 #include <svx/svdpage.hxx>
27 #include <svx/svdocapt.hxx>
28 #include <editeng/outlobj.hxx>
29 #include <editeng/editobj.hxx>
30 #include <basegfx/polygon/b2dpolygon.hxx>
31 #include <osl/diagnose.h>
32
33 #include <scitems.hxx>
34 #include <svx/xfillit0.hxx>
35 #include <svx/xlnstit.hxx>
36 #include <svx/xlnstwit.hxx>
37 #include <svx/xlnstcit.hxx>
38 #include <svx/sxcecitm.hxx>
39 #include <svx/xflclit.hxx>
40 #include <svx/sdshitm.hxx>
41 #include <svx/sdsxyitm.hxx>
42 #include <svx/sdtditm.hxx>
43 #include <svx/sdtagitm.hxx>
44 #include <svx/sdtmfitm.hxx>
45 #include <tools/gen.hxx>
46
47 #include <document.hxx>
48 #include <docpool.hxx>
49 #include <patattr.hxx>
50 #include <drwlayer.hxx>
51 #include <userdat.hxx>
52 #include <detfunc.hxx>
53 #include <editutil.hxx>
54
55 using namespace com::sun::star;
56
57 namespace {
58
59 const tools::Long SC_NOTECAPTION_WIDTH = 2900; /// Default width of note caption textbox.
60 const tools::Long SC_NOTECAPTION_MAXWIDTH_TEMP = 12000; /// Maximum width of temporary note caption textbox.
61 const tools::Long SC_NOTECAPTION_HEIGHT = 1800; /// Default height of note caption textbox.
62 const tools::Long SC_NOTECAPTION_CELLDIST = 600; /// Default distance of note captions to border of anchor cell.
63 const tools::Long SC_NOTECAPTION_OFFSET_Y = -1500; /// Default Y offset of note captions to top border of anchor cell.
64 const tools::Long SC_NOTECAPTION_OFFSET_X = 1500; /// Default X offset of note captions to left border of anchor cell.
65 const tools::Long SC_NOTECAPTION_BORDERDIST_TEMP = 100; /// Distance of temporary note captions to visible sheet area.
66
67 /** Static helper functions for caption objects. */
68 class ScCaptionUtil
69 {
70 public:
71 /** Moves the caption object to the correct layer according to passed visibility. */
72 static void SetCaptionLayer( SdrCaptionObj& rCaption, bool bShown );
73 /** Sets basic caption settings required for note caption objects. */
74 static void SetBasicCaptionSettings( SdrCaptionObj& rCaption, bool bShown );
75 /** Stores the cell position of the note in the user data area of the caption. */
76 static void SetCaptionUserData( SdrCaptionObj& rCaption, const ScAddress& rPos );
77 /** Sets all default formatting attributes to the caption object. */
78 static void SetDefaultItems( SdrCaptionObj& rCaption, ScDocument& rDoc, const SfxItemSet* pExtraItemSet );
79 };
80
SetCaptionLayer(SdrCaptionObj & rCaption,bool bShown)81 void ScCaptionUtil::SetCaptionLayer( SdrCaptionObj& rCaption, bool bShown )
82 {
83 SdrLayerID nLayer = bShown ? SC_LAYER_INTERN : SC_LAYER_HIDDEN;
84 if( nLayer != rCaption.GetLayer() )
85 rCaption.SetLayer( nLayer );
86 }
87
SetBasicCaptionSettings(SdrCaptionObj & rCaption,bool bShown)88 void ScCaptionUtil::SetBasicCaptionSettings( SdrCaptionObj& rCaption, bool bShown )
89 {
90 SetCaptionLayer( rCaption, bShown );
91 rCaption.SetFixedTail();
92 rCaption.SetSpecialTextBoxShadow();
93 }
94
SetCaptionUserData(SdrCaptionObj & rCaption,const ScAddress & rPos)95 void ScCaptionUtil::SetCaptionUserData( SdrCaptionObj& rCaption, const ScAddress& rPos )
96 {
97 // pass true to ScDrawLayer::GetObjData() to create the object data entry
98 ScDrawObjData* pObjData = ScDrawLayer::GetObjData( &rCaption, true );
99 OSL_ENSURE( pObjData, "ScCaptionUtil::SetCaptionUserData - missing drawing object user data" );
100 pObjData->maStart = rPos;
101 pObjData->meType = ScDrawObjData::CellNote;
102 }
103
SetDefaultItems(SdrCaptionObj & rCaption,ScDocument & rDoc,const SfxItemSet * pExtraItemSet)104 void ScCaptionUtil::SetDefaultItems( SdrCaptionObj& rCaption, ScDocument& rDoc, const SfxItemSet* pExtraItemSet )
105 {
106 SfxItemSet aItemSet = rCaption.GetMergedItemSet();
107
108 // caption tail arrow
109 ::basegfx::B2DPolygon aTriangle;
110 aTriangle.append( ::basegfx::B2DPoint( 10.0, 0.0 ) );
111 aTriangle.append( ::basegfx::B2DPoint( 0.0, 30.0 ) );
112 aTriangle.append( ::basegfx::B2DPoint( 20.0, 30.0 ) );
113 aTriangle.setClosed( true );
114 /* Line ends are now created with an empty name. The
115 checkForUniqueItem() method then finds a unique name for the item's
116 value. */
117 aItemSet.Put( XLineStartItem( OUString(), ::basegfx::B2DPolyPolygon( aTriangle ) ) );
118 aItemSet.Put( XLineStartWidthItem( 200 ) );
119 aItemSet.Put( XLineStartCenterItem( false ) );
120 aItemSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) );
121 aItemSet.Put( XFillColorItem( OUString(), ScDetectiveFunc::GetCommentColor() ) );
122 aItemSet.Put( SdrCaptionEscDirItem( SdrCaptionEscDir::BestFit ) );
123
124 // shadow
125 /* SdrShadowItem has sal_False, instead the shadow is set for the
126 rectangle only with SetSpecialTextBoxShadow() when the object is
127 created (item must be set to adjust objects from older files). */
128 aItemSet.Put( makeSdrShadowItem( false ) );
129 aItemSet.Put( makeSdrShadowXDistItem( 100 ) );
130 aItemSet.Put( makeSdrShadowYDistItem( 100 ) );
131
132 // text attributes
133 aItemSet.Put( makeSdrTextLeftDistItem( 100 ) );
134 aItemSet.Put( makeSdrTextRightDistItem( 100 ) );
135 aItemSet.Put( makeSdrTextUpperDistItem( 100 ) );
136 aItemSet.Put( makeSdrTextLowerDistItem( 100 ) );
137 aItemSet.Put( makeSdrTextAutoGrowWidthItem( false ) );
138 aItemSet.Put( makeSdrTextAutoGrowHeightItem( true ) );
139 // use the default cell style to be able to modify the caption font
140 const ScPatternAttr& rDefPattern = rDoc.GetPool()->GetDefaultItem( ATTR_PATTERN );
141 rDefPattern.FillEditItemSet( &aItemSet );
142
143 if (pExtraItemSet)
144 {
145 /* Updates caption item set according to the passed item set while removing shadow items. */
146
147 aItemSet.Put(*pExtraItemSet);
148 // reset shadow items
149 aItemSet.Put( makeSdrShadowItem( false ) );
150 aItemSet.Put( makeSdrShadowXDistItem( 100 ) );
151 aItemSet.Put( makeSdrShadowYDistItem( 100 ) );
152 }
153
154 rCaption.SetMergedItemSet( aItemSet );
155
156 if (pExtraItemSet)
157 rCaption.SetSpecialTextBoxShadow();
158 }
159
160 /** Helper for creation and manipulation of caption drawing objects independent
161 from cell annotations. */
162 class ScCaptionCreator
163 {
164 public:
165 /** Create a new caption. The caption will not be inserted into the document. */
166 explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, bool bTailFront );
167 /** Manipulate an existing caption. */
168 explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, const ScCaptionPtr& xCaption );
169
170 /** Returns the drawing layer page of the sheet contained in maPos. */
171 SdrPage* GetDrawPage();
172 /** Returns the caption drawing object. */
GetCaption()173 ScCaptionPtr & GetCaption() { return mxCaption; }
174
175 /** Moves the caption inside the passed rectangle. Uses page area if 0 is passed. */
176 void FitCaptionToRect( const tools::Rectangle* pVisRect = nullptr );
177 /** Places the caption inside the passed rectangle, tries to keep the cell rectangle uncovered. Uses page area if 0 is passed. */
178 void AutoPlaceCaption( const tools::Rectangle* pVisRect = nullptr );
179 /** Updates caption tail and textbox according to current cell position. Uses page area if 0 is passed. */
180 void UpdateCaptionPos();
181
182 protected:
183 /** Helper constructor for derived classes. */
184 explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos );
185
186 /** Calculates the caption tail position according to current cell position. */
187 Point CalcTailPos( bool bTailFront );
188 /** Implements creation of the caption object. The caption will not be inserted into the document. */
189 void CreateCaption( bool bShown, bool bTailFront );
190
191 private:
192 /** Initializes all members. */
193 void Initialize();
194 /** Returns the passed rectangle if existing, page rectangle otherwise. */
GetVisRect(const tools::Rectangle * pVisRect) const195 const tools::Rectangle& GetVisRect( const tools::Rectangle* pVisRect ) const { return pVisRect ? *pVisRect : maPageRect; }
196
197 private:
198 ScDocument& mrDoc;
199 ScAddress maPos;
200 ScCaptionPtr mxCaption;
201 tools::Rectangle maPageRect;
202 tools::Rectangle maCellRect;
203 bool mbNegPage;
204 };
205
ScCaptionCreator(ScDocument & rDoc,const ScAddress & rPos,bool bTailFront)206 ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, bool bTailFront ) :
207 mrDoc( rDoc ),
208 maPos( rPos )
209 {
210 Initialize();
211 CreateCaption( true/*bShown*/, bTailFront );
212 }
213
ScCaptionCreator(ScDocument & rDoc,const ScAddress & rPos,const ScCaptionPtr & xCaption)214 ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, const ScCaptionPtr& xCaption ) :
215 mrDoc( rDoc ),
216 maPos( rPos ),
217 mxCaption( xCaption )
218 {
219 Initialize();
220 }
221
ScCaptionCreator(ScDocument & rDoc,const ScAddress & rPos)222 ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos ) :
223 mrDoc( rDoc ),
224 maPos( rPos )
225 {
226 Initialize();
227 }
228
GetDrawPage()229 SdrPage* ScCaptionCreator::GetDrawPage()
230 {
231 ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
232 return pDrawLayer ? pDrawLayer->GetPage( static_cast< sal_uInt16 >( maPos.Tab() ) ) : nullptr;
233 }
234
FitCaptionToRect(const tools::Rectangle * pVisRect)235 void ScCaptionCreator::FitCaptionToRect( const tools::Rectangle* pVisRect )
236 {
237 const tools::Rectangle& rVisRect = GetVisRect( pVisRect );
238
239 // tail position
240 Point aTailPos = mxCaption->GetTailPos();
241 aTailPos.setX( ::std::clamp( aTailPos.X(), rVisRect.Left(), rVisRect.Right() ) );
242 aTailPos.setY( ::std::clamp( aTailPos.Y(), rVisRect.Top(), rVisRect.Bottom() ) );
243 mxCaption->SetTailPos( aTailPos );
244
245 // caption rectangle
246 tools::Rectangle aCaptRect = mxCaption->GetLogicRect();
247 Point aCaptPos = aCaptRect.TopLeft();
248 // move textbox inside right border of visible area
249 aCaptPos.setX( ::std::min< tools::Long >( aCaptPos.X(), rVisRect.Right() - aCaptRect.GetWidth() ) );
250 // move textbox inside left border of visible area (this may move it outside on right side again)
251 aCaptPos.setX( ::std::max< tools::Long >( aCaptPos.X(), rVisRect.Left() ) );
252 // move textbox inside bottom border of visible area
253 aCaptPos.setY( ::std::min< tools::Long >( aCaptPos.Y(), rVisRect.Bottom() - aCaptRect.GetHeight() ) );
254 // move textbox inside top border of visible area (this may move it outside on bottom side again)
255 aCaptPos.setY( ::std::max< tools::Long >( aCaptPos.Y(), rVisRect.Top() ) );
256 // update caption
257 aCaptRect.SetPos( aCaptPos );
258 mxCaption->SetLogicRect( aCaptRect );
259 }
260
AutoPlaceCaption(const tools::Rectangle * pVisRect)261 void ScCaptionCreator::AutoPlaceCaption( const tools::Rectangle* pVisRect )
262 {
263 const tools::Rectangle& rVisRect = GetVisRect( pVisRect );
264
265 // caption rectangle
266 tools::Rectangle aCaptRect = mxCaption->GetLogicRect();
267 tools::Long nWidth = aCaptRect.GetWidth();
268 tools::Long nHeight = aCaptRect.GetHeight();
269
270 // n***Space contains available space between border of visible area and cell
271 tools::Long nLeftSpace = maCellRect.Left() - rVisRect.Left() + 1;
272 tools::Long nRightSpace = rVisRect.Right() - maCellRect.Right() + 1;
273 tools::Long nTopSpace = maCellRect.Top() - rVisRect.Top() + 1;
274 tools::Long nBottomSpace = rVisRect.Bottom() - maCellRect.Bottom() + 1;
275
276 // nNeeded*** contains textbox dimensions plus needed distances to cell or border of visible area
277 tools::Long nNeededSpaceX = nWidth + SC_NOTECAPTION_CELLDIST;
278 tools::Long nNeededSpaceY = nHeight + SC_NOTECAPTION_CELLDIST;
279
280 // bFitsWidth*** == true means width of textbox fits into horizontal free space of visible area
281 bool bFitsWidthLeft = nNeededSpaceX <= nLeftSpace; // text box width fits into the width left of cell
282 bool bFitsWidthRight = nNeededSpaceX <= nRightSpace; // text box width fits into the width right of cell
283 bool bFitsWidth = nWidth <= rVisRect.GetWidth(); // text box width fits into width of visible area
284
285 // bFitsHeight*** == true means height of textbox fits into vertical free space of visible area
286 bool bFitsHeightTop = nNeededSpaceY <= nTopSpace; // text box height fits into the height above cell
287 bool bFitsHeightBottom = nNeededSpaceY <= nBottomSpace; // text box height fits into the height below cell
288 bool bFitsHeight = nHeight <= rVisRect.GetHeight(); // text box height fits into height of visible area
289
290 // bFits*** == true means the textbox fits completely into free space of visible area
291 bool bFitsLeft = bFitsWidthLeft && bFitsHeight;
292 bool bFitsRight = bFitsWidthRight && bFitsHeight;
293 bool bFitsTop = bFitsWidth && bFitsHeightTop;
294 bool bFitsBottom = bFitsWidth && bFitsHeightBottom;
295
296 Point aCaptPos;
297 // use left/right placement if possible, or if top/bottom placement not possible
298 if( bFitsLeft || bFitsRight || (!bFitsTop && !bFitsBottom) )
299 {
300 // prefer left in RTL sheet and right in LTR sheets
301 bool bPreferLeft = bFitsLeft && (mbNegPage || !bFitsRight);
302 bool bPreferRight = bFitsRight && (!mbNegPage || !bFitsLeft);
303 // move to left, if left is preferred, or if neither left nor right fit and there is more space to the left
304 if( bPreferLeft || (!bPreferRight && (nLeftSpace > nRightSpace)) )
305 aCaptPos.setX( maCellRect.Left() - SC_NOTECAPTION_CELLDIST - nWidth );
306 else // to right
307 aCaptPos.setX( maCellRect.Right() + SC_NOTECAPTION_CELLDIST );
308 // Y position according to top cell border
309 aCaptPos.setY( maCellRect.Top() + SC_NOTECAPTION_OFFSET_Y );
310 }
311 else // top or bottom placement
312 {
313 // X position
314 aCaptPos.setX( maCellRect.Left() + SC_NOTECAPTION_OFFSET_X );
315 // top placement, if possible
316 if( bFitsTop )
317 aCaptPos.setY( maCellRect.Top() - SC_NOTECAPTION_CELLDIST - nHeight );
318 else // bottom placement
319 aCaptPos.setY( maCellRect.Bottom() + SC_NOTECAPTION_CELLDIST );
320 }
321
322 // update textbox position in note caption object
323 aCaptRect.SetPos( aCaptPos );
324 mxCaption->SetLogicRect( aCaptRect );
325 FitCaptionToRect( pVisRect );
326 }
327
UpdateCaptionPos()328 void ScCaptionCreator::UpdateCaptionPos()
329 {
330 ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
331
332 // update caption position
333 const Point& rOldTailPos = mxCaption->GetTailPos();
334 Point aTailPos = CalcTailPos( false );
335 if( rOldTailPos != aTailPos )
336 {
337 // create drawing undo action
338 if( pDrawLayer && pDrawLayer->IsRecording() )
339 pDrawLayer->AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *mxCaption ) );
340 // calculate new caption rectangle (#i98141# handle LTR<->RTL switch correctly)
341 tools::Rectangle aCaptRect = mxCaption->GetLogicRect();
342 tools::Long nDiffX = (rOldTailPos.X() >= 0) ? (aCaptRect.Left() - rOldTailPos.X()) : (rOldTailPos.X() - aCaptRect.Right());
343 if( mbNegPage ) nDiffX = -nDiffX - aCaptRect.GetWidth();
344 tools::Long nDiffY = aCaptRect.Top() - rOldTailPos.Y();
345 aCaptRect.SetPos( aTailPos + Point( nDiffX, nDiffY ) );
346 // set new tail position and caption rectangle
347 mxCaption->SetTailPos( aTailPos );
348 mxCaption->SetLogicRect( aCaptRect );
349 // fit caption into draw page
350 FitCaptionToRect();
351 }
352
353 // update cell position in caption user data
354 ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( mxCaption.get(), maPos.Tab() );
355 if( pCaptData && (maPos != pCaptData->maStart) )
356 {
357 // create drawing undo action
358 if( pDrawLayer && pDrawLayer->IsRecording() )
359 pDrawLayer->AddCalcUndo( std::make_unique<ScUndoObjData>( mxCaption.get(), pCaptData->maStart, pCaptData->maEnd, maPos, pCaptData->maEnd ) );
360 // set new position
361 pCaptData->maStart = maPos;
362 }
363 }
364
CalcTailPos(bool bTailFront)365 Point ScCaptionCreator::CalcTailPos( bool bTailFront )
366 {
367 // tail position
368 bool bTailLeft = bTailFront != mbNegPage;
369 Point aTailPos = bTailLeft ? maCellRect.TopLeft() : maCellRect.TopRight();
370 // move caption point 1/10 mm inside cell
371 if( bTailLeft ) aTailPos.AdjustX(10 ); else aTailPos.AdjustX( -10 );
372 aTailPos.AdjustY(10);
373 return aTailPos;
374 }
375
CreateCaption(bool bShown,bool bTailFront)376 void ScCaptionCreator::CreateCaption( bool bShown, bool bTailFront )
377 {
378 // create the caption drawing object
379 tools::Rectangle aTextRect( Point( 0 , 0 ), Size( SC_NOTECAPTION_WIDTH, SC_NOTECAPTION_HEIGHT ) );
380 Point aTailPos = CalcTailPos( bTailFront );
381 mxCaption.reset(
382 new SdrCaptionObj(
383 *mrDoc.GetDrawLayer(), // TTTT should ret a ref?
384 aTextRect,
385 aTailPos));
386 // basic caption settings
387 ScCaptionUtil::SetBasicCaptionSettings( *mxCaption, bShown );
388 }
389
Initialize()390 void ScCaptionCreator::Initialize()
391 {
392 maCellRect = ScDrawLayer::GetCellRect( mrDoc, maPos, true );
393 mbNegPage = mrDoc.IsNegativePage( maPos.Tab() );
394 if( SdrPage* pDrawPage = GetDrawPage() )
395 {
396 maPageRect = tools::Rectangle( Point( 0, 0 ), pDrawPage->GetSize() );
397 /* #i98141# SdrPage::GetSize() returns negative width in RTL mode.
398 The call to Rectangle::Adjust() orders left/right coordinate
399 accordingly. */
400 maPageRect.Justify();
401 }
402 }
403
404 /** Helper for creation of permanent caption drawing objects for cell notes. */
405 class ScNoteCaptionCreator : public ScCaptionCreator
406 {
407 public:
408 /** Create a new caption object and inserts it into the document. */
409 explicit ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScNoteData& rNoteData );
410 /** Manipulate an existing caption. */
411 explicit ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScCaptionPtr& xCaption, bool bShown );
412 };
413
ScNoteCaptionCreator(ScDocument & rDoc,const ScAddress & rPos,ScNoteData & rNoteData)414 ScNoteCaptionCreator::ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScNoteData& rNoteData ) :
415 ScCaptionCreator( rDoc, rPos ) // use helper c'tor that does not create the caption yet
416 {
417 SdrPage* pDrawPage = GetDrawPage();
418 OSL_ENSURE( pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - no drawing page" );
419 if( !pDrawPage )
420 return;
421
422 // create the caption drawing object
423 CreateCaption( rNoteData.mbShown, false );
424 rNoteData.mxCaption = GetCaption();
425 OSL_ENSURE( rNoteData.mxCaption, "ScNoteCaptionCreator::ScNoteCaptionCreator - missing caption object" );
426 if( rNoteData.mxCaption )
427 {
428 // store note position in user data of caption object
429 ScCaptionUtil::SetCaptionUserData( *rNoteData.mxCaption, rPos );
430 // insert object into draw page
431 pDrawPage->InsertObject( rNoteData.mxCaption.get() );
432 }
433 }
434
ScNoteCaptionCreator(ScDocument & rDoc,const ScAddress & rPos,ScCaptionPtr & xCaption,bool bShown)435 ScNoteCaptionCreator::ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScCaptionPtr& xCaption, bool bShown ) :
436 ScCaptionCreator( rDoc, rPos, xCaption )
437 {
438 SdrPage* pDrawPage = GetDrawPage();
439 OSL_ENSURE( pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - no drawing page" );
440 OSL_ENSURE( xCaption->getSdrPageFromSdrObject() == pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - wrong drawing page in caption" );
441 if( pDrawPage && (xCaption->getSdrPageFromSdrObject() == pDrawPage) )
442 {
443 // store note position in user data of caption object
444 ScCaptionUtil::SetCaptionUserData( *xCaption, rPos );
445 // basic caption settings
446 ScCaptionUtil::SetBasicCaptionSettings( *xCaption, bShown );
447 // set correct tail position
448 xCaption->SetTailPos( CalcTailPos( false ) );
449 }
450 }
451
452 } // namespace
453
ScCaptionPtr()454 ScCaptionPtr::ScCaptionPtr() :
455 mpHead(nullptr), mpNext(nullptr), mpCaption(nullptr), mbNotOwner(false)
456 {
457 }
458
ScCaptionPtr(SdrCaptionObj * p)459 ScCaptionPtr::ScCaptionPtr( SdrCaptionObj* p ) :
460 mpHead(nullptr), mpNext(nullptr), mpCaption(p), mbNotOwner(false)
461 {
462 if (p)
463 {
464 newHead();
465 }
466 }
467
ScCaptionPtr(const ScCaptionPtr & r)468 ScCaptionPtr::ScCaptionPtr( const ScCaptionPtr& r ) :
469 mpHead(r.mpHead), mpCaption(r.mpCaption), mbNotOwner(false)
470 {
471 if (r.mpCaption)
472 {
473 assert(r.mpHead);
474 r.incRef();
475 // Insert into list.
476 mpNext = r.mpNext;
477 r.mpNext = this;
478 }
479 else
480 {
481 assert(!r.mpHead);
482 mpNext = nullptr;
483 }
484 }
485
ScCaptionPtr(ScCaptionPtr && r)486 ScCaptionPtr::ScCaptionPtr(ScCaptionPtr&& r) noexcept
487 : mpHead(r.mpHead), mpNext(r.mpNext), mpCaption(r.mpCaption), mbNotOwner(false)
488 {
489 r.replaceInList( this );
490 r.mpCaption = nullptr;
491 r.mbNotOwner = false;
492 }
493
operator =(ScCaptionPtr && r)494 ScCaptionPtr& ScCaptionPtr::operator=(ScCaptionPtr&& r) noexcept
495 {
496 assert(this != &r);
497
498 mpHead = r.mpHead;
499 mpNext = r.mpNext;
500 mpCaption = r.mpCaption;
501 mbNotOwner = r.mbNotOwner;
502
503 r.replaceInList( this );
504 r.mpCaption = nullptr;
505 r.mbNotOwner = false;
506
507 return *this;
508 }
509
operator =(const ScCaptionPtr & r)510 ScCaptionPtr& ScCaptionPtr::operator=( const ScCaptionPtr& r )
511 {
512 if (this == &r)
513 return *this;
514
515 if (mpCaption == r.mpCaption)
516 {
517 // Two lists for the same caption is bad.
518 assert(!mpCaption || mpHead == r.mpHead);
519 assert(!mpCaption); // assigning same caption pointer within same list is weird
520 // Nullptr captions are not inserted to the list, so nothing to do here
521 // if both are.
522 return *this;
523 }
524
525 // Let's find some weird usage.
526 // Assigning without head doesn't make sense unless it is a nullptr caption.
527 assert(r.mpHead || !r.mpCaption);
528 // A nullptr caption must not be in a list and thus not have a head.
529 assert(!r.mpHead || r.mpCaption);
530 // Same captions were caught above, so here different heads must be present.
531 assert(r.mpHead != mpHead);
532
533 r.incRef();
534 decRefAndDestroy();
535 removeFromList();
536
537 mpCaption = r.mpCaption;
538 mbNotOwner = r.mbNotOwner;
539 // That head is this' master.
540 mpHead = r.mpHead;
541 // Insert into list.
542 mpNext = r.mpNext;
543 r.mpNext = this;
544
545 return *this;
546 }
547
setNotOwner()548 void ScCaptionPtr::setNotOwner()
549 {
550 mbNotOwner = true;
551 }
552
Head(ScCaptionPtr * p)553 ScCaptionPtr::Head::Head( ScCaptionPtr* p ) :
554 mpFirst(p), mnRefs(1)
555 {
556 }
557
newHead()558 void ScCaptionPtr::newHead()
559 {
560 assert(!mpHead);
561 mpHead = new Head(this);
562 }
563
replaceInList(ScCaptionPtr * pNew)564 void ScCaptionPtr::replaceInList(ScCaptionPtr* pNew) noexcept
565 {
566 if (!mpHead && !mpNext)
567 return;
568
569 assert(mpHead);
570 assert(mpCaption == pNew->mpCaption);
571
572 ScCaptionPtr* pThat = mpHead->mpFirst;
573 while (pThat && pThat != this && pThat->mpNext != this)
574 {
575 pThat = pThat->mpNext;
576 }
577 if (pThat && pThat != this)
578 {
579 assert(pThat->mpNext == this);
580 pThat->mpNext = pNew;
581 }
582 pNew->mpNext = mpNext;
583 if (mpHead->mpFirst == this)
584 mpHead->mpFirst = pNew;
585
586 mpHead = nullptr;
587 mpNext = nullptr;
588 }
589
removeFromList()590 void ScCaptionPtr::removeFromList()
591 {
592 if (!mpHead && !mpNext && !mpCaption)
593 return;
594
595 #if OSL_DEBUG_LEVEL > 0
596 oslInterlockedCount nCount = 0;
597 #endif
598 ScCaptionPtr* pThat = (mpHead ? mpHead->mpFirst : nullptr);
599 while (pThat && pThat != this && pThat->mpNext != this)
600 {
601 // Use the walk to check consistency on the fly.
602 assert(pThat->mpHead == mpHead); // all belong to the same
603 assert(pThat->mpHead || !pThat->mpNext); // next without head is bad
604 assert(pThat->mpCaption == mpCaption);
605 pThat = pThat->mpNext;
606 #if OSL_DEBUG_LEVEL > 0
607 ++nCount;
608 #endif
609 }
610 assert(pThat || !mpHead); // not found only if this was standalone
611 if (pThat)
612 {
613 if (pThat != this)
614 {
615 #if OSL_DEBUG_LEVEL > 0
616 // The while loop above was not executed, and for this
617 // (pThat->mpNext) the loop below won't either.
618 ++nCount;
619 #endif
620 pThat->mpNext = mpNext;
621 }
622 #if OSL_DEBUG_LEVEL > 0
623 do
624 {
625 assert(pThat->mpHead == mpHead); // all belong to the same
626 assert(pThat->mpHead || !pThat->mpNext); // next without head is bad
627 assert(pThat->mpCaption == mpCaption);
628 ++nCount;
629 }
630 while ((pThat = pThat->mpNext) != nullptr);
631 #endif
632 }
633 #if OSL_DEBUG_LEVEL > 0
634 // If part of a list then refs were already decremented.
635 assert(nCount == (mpHead ? mpHead->mnRefs + 1 : 0));
636 #endif
637 if (mpHead && mpHead->mpFirst == this)
638 {
639 if (mpNext)
640 mpHead->mpFirst = mpNext;
641 else
642 {
643 // The only one destroys also head.
644 assert(mpHead->mnRefs == 0); // cough
645 delete mpHead; // DEAD now
646 }
647 }
648 mpHead = nullptr;
649 mpNext = nullptr;
650 }
651
reset(SdrCaptionObj * p)652 void ScCaptionPtr::reset( SdrCaptionObj* p )
653 {
654 assert(!p || p != mpCaption);
655 #if OSL_DEBUG_LEVEL > 0
656 if (p)
657 {
658 // Check if we end up with a duplicated management in this list.
659 ScCaptionPtr* pThat = (mpHead ? mpHead->mpFirst : nullptr);
660 while (pThat)
661 {
662 assert(pThat->mpCaption != p);
663 pThat = pThat->mpNext;
664 }
665 }
666 #endif
667 decRefAndDestroy();
668 removeFromList();
669 mpCaption = p;
670 mbNotOwner = false;
671 if (p)
672 {
673 newHead();
674 }
675 }
676
~ScCaptionPtr()677 ScCaptionPtr::~ScCaptionPtr()
678 {
679 decRefAndDestroy();
680 removeFromList();
681 }
682
getRefs() const683 oslInterlockedCount ScCaptionPtr::getRefs() const
684 {
685 return mpHead ? mpHead->mnRefs : 0;
686 }
687
incRef() const688 void ScCaptionPtr::incRef() const
689 {
690 if (mpHead)
691 osl_atomic_increment(&mpHead->mnRefs);
692 }
693
decRef() const694 bool ScCaptionPtr::decRef() const
695 {
696 return mpHead && mpHead->mnRefs > 0 && !osl_atomic_decrement(&mpHead->mnRefs);
697 }
698
decRefAndDestroy()699 void ScCaptionPtr::decRefAndDestroy()
700 {
701 if (!decRef())
702 return;
703
704 assert(mpHead->mpFirst == this); // this must be one and only one
705 assert(!mpNext); // this must be one and only one
706 assert(mpCaption);
707
708 #if 0
709 // Quick workaround for when there are still cases where the caption
710 // pointer is dangling
711 mpCaption = nullptr;
712 mbNotOwner = false;
713 #else
714 // Destroying Draw Undo and some other delete the SdrObject, don't
715 // attempt that twice.
716 if (mbNotOwner)
717 {
718 mpCaption = nullptr;
719 mbNotOwner = false;
720 }
721 else
722 {
723 removeFromDrawPageAndFree( true ); // ignoring Undo
724 if (mpCaption)
725 {
726 // There's no draw page associated so removeFromDrawPageAndFree()
727 // didn't do anything, but still we want to delete the caption
728 // object. release()/dissolve() also resets mpCaption.
729 SdrObject* pObj = release();
730 SdrObject::Free( pObj );
731 }
732 }
733 #endif
734 delete mpHead;
735 mpHead = nullptr;
736 }
737
insertToDrawPage(SdrPage & rDrawPage)738 void ScCaptionPtr::insertToDrawPage( SdrPage& rDrawPage )
739 {
740 assert(mpHead && mpCaption);
741
742 rDrawPage.InsertObject( mpCaption );
743 }
744
removeFromDrawPage(SdrPage & rDrawPage)745 void ScCaptionPtr::removeFromDrawPage( SdrPage& rDrawPage )
746 {
747 assert(mpHead && mpCaption);
748 SdrObject* pObj = rDrawPage.RemoveObject( mpCaption->GetOrdNum() );
749 assert(pObj == mpCaption); (void)pObj;
750 }
751
removeFromDrawPageAndFree(bool bIgnoreUndo)752 void ScCaptionPtr::removeFromDrawPageAndFree( bool bIgnoreUndo )
753 {
754 assert(mpHead && mpCaption);
755 SdrPage* pDrawPage(mpCaption->getSdrPageFromSdrObject());
756 SAL_WARN_IF( !pDrawPage, "sc.core", "ScCaptionPtr::removeFromDrawPageAndFree - object without drawing page");
757 if (!pDrawPage)
758 return;
759
760 pDrawPage->RecalcObjOrdNums();
761 bool bRecording = false;
762 if(!bIgnoreUndo)
763 {
764 ScDrawLayer* pDrawLayer(dynamic_cast< ScDrawLayer* >(&mpCaption->getSdrModelFromSdrObject()));
765 SAL_WARN_IF( !pDrawLayer, "sc.core", "ScCaptionPtr::removeFromDrawPageAndFree - object without drawing layer");
766 // create drawing undo action (before removing the object to have valid draw page in undo action)
767 bRecording = (pDrawLayer && pDrawLayer->IsRecording());
768 if (bRecording)
769 pDrawLayer->AddCalcUndo( std::make_unique<SdrUndoDelObj>( *mpCaption ));
770 }
771 // remove the object from the drawing page, delete if undo is disabled
772 removeFromDrawPage( *pDrawPage );
773 // If called from outside mnRefs must be 1 to delete. If called from
774 // decRefAndDestroy() mnRefs is already 0.
775 if (!bRecording && getRefs() <= 1)
776 {
777 SdrObject* pObj = release();
778 SdrObject::Free( pObj );
779 }
780 }
781
release()782 SdrCaptionObj* ScCaptionPtr::release()
783 {
784 SdrCaptionObj* pTmp = mpCaption;
785 dissolve();
786 return pTmp;
787 }
788
forget()789 void ScCaptionPtr::forget()
790 {
791 decRef();
792 removeFromList();
793 mpCaption = nullptr;
794 mbNotOwner = false;
795 }
796
dissolve()797 void ScCaptionPtr::dissolve()
798 {
799 ScCaptionPtr::Head* pHead = mpHead;
800 ScCaptionPtr* pThat = (mpHead ? mpHead->mpFirst : this);
801 while (pThat)
802 {
803 assert(!pThat->mpNext || pThat->mpHead); // next without head is bad
804 assert(pThat->mpHead == pHead); // same head required within one list
805 ScCaptionPtr* p = pThat->mpNext;
806 pThat->clear();
807 pThat = p;
808 }
809 assert(!mpHead && !mpNext && !mpCaption); // should had been cleared during list walk
810 delete pHead;
811 }
812
clear()813 void ScCaptionPtr::clear()
814 {
815 mpHead = nullptr;
816 mpNext = nullptr;
817 mpCaption = nullptr;
818 mbNotOwner = false;
819 }
820
821 struct ScCaptionInitData
822 {
823 std::unique_ptr< SfxItemSet > mxItemSet; /// Caption object formatting.
824 std::unique_ptr< OutlinerParaObject > mxOutlinerObj; /// Text object with all text portion formatting.
825 OUString maSimpleText; /// Simple text without formatting.
826 Point maCaptionOffset; /// Caption position relative to cell corner.
827 Size maCaptionSize; /// Size of the caption object.
828 bool mbDefaultPosSize; /// True = use default position and size for caption.
829
830 explicit ScCaptionInitData();
831 };
832
ScCaptionInitData()833 ScCaptionInitData::ScCaptionInitData() :
834 mbDefaultPosSize( true )
835 {
836 }
837
ScNoteData(bool bShown)838 ScNoteData::ScNoteData( bool bShown ) :
839 mbShown( bShown )
840 {
841 }
842
843 sal_uInt32 ScPostIt::mnLastPostItId = 1;
844
ScPostIt(ScDocument & rDoc,const ScAddress & rPos,sal_uInt32 nPostItId)845 ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, sal_uInt32 nPostItId ) :
846 mrDoc( rDoc ),
847 maNoteData( false )
848 {
849 mnPostItId = nPostItId == 0 ? mnLastPostItId++ : nPostItId;
850 AutoStamp();
851 CreateCaption( rPos );
852 }
853
ScPostIt(ScDocument & rDoc,const ScAddress & rPos,const ScPostIt & rNote,sal_uInt32 nPostItId)854 ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, const ScPostIt& rNote, sal_uInt32 nPostItId ) :
855 mrDoc( rDoc ),
856 maNoteData( rNote.maNoteData )
857 {
858 mnPostItId = nPostItId == 0 ? mnLastPostItId++ : nPostItId;
859 maNoteData.mxCaption.reset(nullptr);
860 CreateCaption( rPos, rNote.maNoteData.mxCaption.get() );
861 }
862
ScPostIt(ScDocument & rDoc,const ScAddress & rPos,const ScNoteData & rNoteData,bool bAlwaysCreateCaption,sal_uInt32 nPostItId)863 ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, const ScNoteData& rNoteData, bool bAlwaysCreateCaption, sal_uInt32 nPostItId ) :
864 mrDoc( rDoc ),
865 maNoteData( rNoteData )
866 {
867 mnPostItId = nPostItId == 0 ? mnLastPostItId++ : nPostItId;
868 if( bAlwaysCreateCaption || maNoteData.mbShown )
869 CreateCaptionFromInitData( rPos );
870 }
871
~ScPostIt()872 ScPostIt::~ScPostIt()
873 {
874 RemoveCaption();
875 }
876
Clone(const ScAddress & rOwnPos,ScDocument & rDestDoc,const ScAddress & rDestPos,bool bCloneCaption) const877 std::unique_ptr<ScPostIt> ScPostIt::Clone( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, bool bCloneCaption ) const
878 {
879 CreateCaptionFromInitData( rOwnPos );
880 return bCloneCaption ? std::make_unique<ScPostIt>( rDestDoc, rDestPos, *this, mnPostItId ) : std::make_unique<ScPostIt>( rDestDoc, rDestPos, maNoteData, false, mnPostItId );
881 }
882
SetDate(const OUString & rDate)883 void ScPostIt::SetDate( const OUString& rDate )
884 {
885 maNoteData.maDate = rDate;
886 }
887
SetAuthor(const OUString & rAuthor)888 void ScPostIt::SetAuthor( const OUString& rAuthor )
889 {
890 maNoteData.maAuthor = rAuthor;
891 }
892
AutoStamp()893 void ScPostIt::AutoStamp()
894 {
895 maNoteData.maDate = ScGlobal::getLocaleDataPtr()->getDate( Date( Date::SYSTEM ) );
896 maNoteData.maAuthor = SvtUserOptions().GetID();
897 }
898
GetOutlinerObject() const899 const OutlinerParaObject* ScPostIt::GetOutlinerObject() const
900 {
901 if( maNoteData.mxCaption )
902 return maNoteData.mxCaption->GetOutlinerParaObject();
903 if( maNoteData.mxInitData )
904 return maNoteData.mxInitData->mxOutlinerObj.get();
905 return nullptr;
906 }
907
GetEditTextObject() const908 const EditTextObject* ScPostIt::GetEditTextObject() const
909 {
910 const OutlinerParaObject* pOPO = GetOutlinerObject();
911 return pOPO ? &pOPO->GetTextObject() : nullptr;
912 }
913
GetText() const914 OUString ScPostIt::GetText() const
915 {
916 if( const EditTextObject* pEditObj = GetEditTextObject() )
917 {
918 OUStringBuffer aBuffer;
919 ScNoteEditEngine& rEngine = mrDoc.GetNoteEngine();
920 rEngine.SetTextCurrentDefaults(*pEditObj);
921 sal_Int32 nParaCount = rEngine.GetParagraphCount();
922 for( sal_Int32 nPara = 0; nPara < nParaCount; ++nPara )
923 {
924 if( nPara > 0 )
925 aBuffer.append( '\n' );
926 aBuffer.append(rEngine.GetText(nPara));
927 }
928 return aBuffer.makeStringAndClear();
929 }
930 if( maNoteData.mxInitData )
931 return maNoteData.mxInitData->maSimpleText;
932 return OUString();
933 }
934
HasMultiLineText() const935 bool ScPostIt::HasMultiLineText() const
936 {
937 if( const EditTextObject* pEditObj = GetEditTextObject() )
938 return pEditObj->GetParagraphCount() > 1;
939 if( maNoteData.mxInitData )
940 return maNoteData.mxInitData->maSimpleText.indexOf( '\n' ) >= 0;
941 return false;
942 }
943
SetText(const ScAddress & rPos,const OUString & rText)944 void ScPostIt::SetText( const ScAddress& rPos, const OUString& rText )
945 {
946 CreateCaptionFromInitData( rPos );
947 if( maNoteData.mxCaption )
948 maNoteData.mxCaption->SetText( rText );
949 }
950
GetOrCreateCaption(const ScAddress & rPos) const951 SdrCaptionObj* ScPostIt::GetOrCreateCaption( const ScAddress& rPos ) const
952 {
953 CreateCaptionFromInitData( rPos );
954 return maNoteData.mxCaption.get();
955 }
956
ForgetCaption(bool bPreserveData)957 void ScPostIt::ForgetCaption( bool bPreserveData )
958 {
959 if (bPreserveData)
960 {
961 // Used in clipboard when the originating document is destructed to be
962 // able to paste into another document. Caption size and relative
963 // position are not preserved but default created when pasted. Also the
964 // MergedItemSet can not be carried over or it had to be adapted to
965 // defaults and pool. At least preserve the text and outline object if
966 // possible.
967 ScCaptionInitData* pInitData = new ScCaptionInitData;
968 const OutlinerParaObject* pOPO = GetOutlinerObject();
969 if (pOPO)
970 pInitData->mxOutlinerObj.reset( new OutlinerParaObject(*pOPO));
971 pInitData->maSimpleText = GetText();
972
973 maNoteData.mxInitData.reset(pInitData);
974 maNoteData.mxCaption.forget();
975 }
976 else
977 {
978 /* This function is used in undo actions to give up the responsibility for
979 the caption object which is handled by separate drawing undo actions. */
980 maNoteData.mxCaption.forget();
981 maNoteData.mxInitData.reset();
982 }
983 }
984
ShowCaption(const ScAddress & rPos,bool bShow)985 void ScPostIt::ShowCaption( const ScAddress& rPos, bool bShow )
986 {
987 CreateCaptionFromInitData( rPos );
988 // no separate drawing undo needed, handled completely inside ScUndoShowHideNote
989 maNoteData.mbShown = bShow;
990 if( maNoteData.mxCaption )
991 ScCaptionUtil::SetCaptionLayer( *maNoteData.mxCaption, bShow );
992 }
993
ShowCaptionTemp(const ScAddress & rPos,bool bShow)994 void ScPostIt::ShowCaptionTemp( const ScAddress& rPos, bool bShow )
995 {
996 CreateCaptionFromInitData( rPos );
997 if( maNoteData.mxCaption )
998 ScCaptionUtil::SetCaptionLayer( *maNoteData.mxCaption, maNoteData.mbShown || bShow );
999 }
1000
UpdateCaptionPos(const ScAddress & rPos)1001 void ScPostIt::UpdateCaptionPos( const ScAddress& rPos )
1002 {
1003 CreateCaptionFromInitData( rPos );
1004 if( maNoteData.mxCaption )
1005 {
1006 ScCaptionCreator aCreator( mrDoc, rPos, maNoteData.mxCaption );
1007 aCreator.UpdateCaptionPos();
1008 }
1009 }
1010
1011 // private --------------------------------------------------------------------
1012
CreateCaptionFromInitData(const ScAddress & rPos) const1013 void ScPostIt::CreateCaptionFromInitData( const ScAddress& rPos ) const
1014 {
1015 // Captions are not created in Undo documents and only rarely in Clipboard,
1016 // but otherwise we need caption or initial data.
1017 assert((maNoteData.mxCaption || maNoteData.mxInitData) || mrDoc.IsUndo() || mrDoc.IsClipboard());
1018 if( !maNoteData.mxInitData )
1019 return;
1020
1021
1022 /* This function is called from ScPostIt::Clone() when copying cells
1023 to the clipboard/undo document, and when copying cells from the
1024 clipboard/undo document. The former should always be called first,
1025 so if called in a clipboard/undo document, the caption should have
1026 been created already. However, for clipboard in case the
1027 originating document was destructed a new caption has to be
1028 created. */
1029 OSL_ENSURE( !mrDoc.IsUndo() && (!mrDoc.IsClipboard() || !maNoteData.mxCaption),
1030 "ScPostIt::CreateCaptionFromInitData - note caption should not be created in undo/clip documents" );
1031
1032 // going to forget the initial caption data struct when this method returns
1033 auto xInitData = std::move(maNoteData.mxInitData);
1034
1035 /* #i104915# Never try to create notes in Undo document, leads to
1036 crash due to missing document members (e.g. row height array). */
1037 if( maNoteData.mxCaption || mrDoc.IsUndo() )
1038 return;
1039
1040 if (mrDoc.IsClipboard())
1041 mrDoc.InitDrawLayer(); // ensure there is a drawing layer
1042
1043 // ScNoteCaptionCreator c'tor creates the caption and inserts it into the document and maNoteData
1044 ScNoteCaptionCreator aCreator( mrDoc, rPos, maNoteData );
1045 if( !maNoteData.mxCaption )
1046 return;
1047
1048 // Prevent triple change broadcasts of the same object.
1049 bool bWasLocked = maNoteData.mxCaption->getSdrModelFromSdrObject().isLocked();
1050 maNoteData.mxCaption->getSdrModelFromSdrObject().setLock(true);
1051
1052 // transfer ownership of outliner object to caption, or set simple text
1053 OSL_ENSURE( xInitData->mxOutlinerObj || !xInitData->maSimpleText.isEmpty(),
1054 "ScPostIt::CreateCaptionFromInitData - need either outliner para object or simple text" );
1055 if (xInitData->mxOutlinerObj)
1056 maNoteData.mxCaption->SetOutlinerParaObject( std::move(xInitData->mxOutlinerObj) );
1057 else
1058 maNoteData.mxCaption->SetText( xInitData->maSimpleText );
1059
1060 // copy all items or set default items; reset shadow items
1061 ScCaptionUtil::SetDefaultItems( *maNoteData.mxCaption, mrDoc, xInitData->mxItemSet.get() );
1062
1063 // set position and size of the caption object
1064 if( xInitData->mbDefaultPosSize )
1065 {
1066 // set other items and fit caption size to text
1067 maNoteData.mxCaption->SetMergedItem( makeSdrTextMinFrameWidthItem( SC_NOTECAPTION_WIDTH ) );
1068 maNoteData.mxCaption->SetMergedItem( makeSdrTextMaxFrameWidthItem( SC_NOTECAPTION_MAXWIDTH_TEMP ) );
1069 maNoteData.mxCaption->AdjustTextFrameWidthAndHeight();
1070 aCreator.AutoPlaceCaption();
1071 }
1072 else
1073 {
1074 tools::Rectangle aCellRect = ScDrawLayer::GetCellRect( mrDoc, rPos, true );
1075 bool bNegPage = mrDoc.IsNegativePage( rPos.Tab() );
1076 tools::Long nPosX = bNegPage ? (aCellRect.Left() - xInitData->maCaptionOffset.X()) : (aCellRect.Right() + xInitData->maCaptionOffset.X());
1077 tools::Long nPosY = aCellRect.Top() + xInitData->maCaptionOffset.Y();
1078 tools::Rectangle aCaptRect( Point( nPosX, nPosY ), xInitData->maCaptionSize );
1079 maNoteData.mxCaption->SetLogicRect( aCaptRect );
1080 aCreator.FitCaptionToRect();
1081 }
1082
1083 // End prevent triple change broadcasts of the same object.
1084 maNoteData.mxCaption->getSdrModelFromSdrObject().setLock(bWasLocked);
1085 maNoteData.mxCaption->BroadcastObjectChange();
1086 }
1087
CreateCaption(const ScAddress & rPos,const SdrCaptionObj * pCaption)1088 void ScPostIt::CreateCaption( const ScAddress& rPos, const SdrCaptionObj* pCaption )
1089 {
1090 OSL_ENSURE( !maNoteData.mxCaption, "ScPostIt::CreateCaption - unexpected caption object found" );
1091 maNoteData.mxCaption.reset(nullptr);
1092
1093 /* #i104915# Never try to create notes in Undo document, leads to
1094 crash due to missing document members (e.g. row height array). */
1095 OSL_ENSURE( !mrDoc.IsUndo(), "ScPostIt::CreateCaption - note caption should not be created in undo documents" );
1096 if( mrDoc.IsUndo() )
1097 return;
1098
1099 // drawing layer may be missing, if a note is copied into a clipboard document
1100 if( mrDoc.IsClipboard() )
1101 mrDoc.InitDrawLayer();
1102
1103 // ScNoteCaptionCreator c'tor creates the caption and inserts it into the document and maNoteData
1104 ScNoteCaptionCreator aCreator( mrDoc, rPos, maNoteData );
1105 if( !maNoteData.mxCaption )
1106 return;
1107
1108 // clone settings of passed caption
1109 if( pCaption )
1110 {
1111 // copy edit text object (object must be inserted into page already)
1112 if( OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() )
1113 maNoteData.mxCaption->SetOutlinerParaObject( std::make_unique<OutlinerParaObject>( *pOPO ) );
1114 // copy formatting items (after text has been copied to apply font formatting)
1115 maNoteData.mxCaption->SetMergedItemSetAndBroadcast( pCaption->GetMergedItemSet() );
1116 // move textbox position relative to new cell, copy textbox size
1117 tools::Rectangle aCaptRect = pCaption->GetLogicRect();
1118 Point aDist = maNoteData.mxCaption->GetTailPos() - pCaption->GetTailPos();
1119 aCaptRect.Move( aDist.X(), aDist.Y() );
1120 maNoteData.mxCaption->SetLogicRect( aCaptRect );
1121 aCreator.FitCaptionToRect();
1122 }
1123 else
1124 {
1125 // set default formatting and default position
1126 ScCaptionUtil::SetDefaultItems( *maNoteData.mxCaption, mrDoc, nullptr );
1127 aCreator.AutoPlaceCaption();
1128 }
1129
1130 // create undo action
1131 if( ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer() )
1132 if( pDrawLayer->IsRecording() )
1133 pDrawLayer->AddCalcUndo( std::make_unique<SdrUndoNewObj>( *maNoteData.mxCaption ) );
1134 }
1135
RemoveCaption()1136 void ScPostIt::RemoveCaption()
1137 {
1138 if (!maNoteData.mxCaption)
1139 return;
1140
1141 /* Remove caption object only, if this note is its owner (e.g. notes in
1142 undo documents refer to captions in original document, do not remove
1143 them from drawing layer here). */
1144 // TTTT maybe no longer needed - can that still happen?
1145 ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
1146 if (pDrawLayer == &maNoteData.mxCaption->getSdrModelFromSdrObject())
1147 maNoteData.mxCaption.removeFromDrawPageAndFree();
1148
1149 SAL_INFO("sc.core","ScPostIt::RemoveCaption - refs: " << maNoteData.mxCaption.getRefs() <<
1150 " IsUndo: " << mrDoc.IsUndo() << " IsClip: " << mrDoc.IsClipboard() <<
1151 " Dtor: " << mrDoc.IsInDtorClear());
1152
1153 // Forget the caption object if removeFromDrawPageAndFree() did not free it.
1154 if (maNoteData.mxCaption)
1155 {
1156 SAL_INFO("sc.core","ScPostIt::RemoveCaption - forgetting one ref");
1157 maNoteData.mxCaption.forget();
1158 }
1159 }
1160
CreateTempCaption(ScDocument & rDoc,const ScAddress & rPos,SdrPage & rDrawPage,const OUString & rUserText,const tools::Rectangle & rVisRect,bool bTailFront)1161 ScCaptionPtr ScNoteUtil::CreateTempCaption(
1162 ScDocument& rDoc, const ScAddress& rPos, SdrPage& rDrawPage,
1163 const OUString& rUserText, const tools::Rectangle& rVisRect, bool bTailFront )
1164 {
1165 OUStringBuffer aBuffer( rUserText );
1166 // add plain text of invisible (!) cell note (no formatting etc.)
1167 SdrCaptionObj* pNoteCaption = nullptr;
1168 const ScPostIt* pNote = rDoc.GetNote( rPos );
1169 if( pNote && !pNote->IsCaptionShown() )
1170 {
1171 if( !aBuffer.isEmpty() )
1172 aBuffer.append( "\n--------\n" + pNote->GetText() );
1173 pNoteCaption = pNote->GetOrCreateCaption( rPos );
1174 }
1175
1176 // create a caption if any text exists
1177 if( !pNoteCaption && aBuffer.isEmpty() )
1178 return ScCaptionPtr();
1179
1180 // prepare visible rectangle (add default distance to all borders)
1181 tools::Rectangle aVisRect(
1182 rVisRect.Left() + SC_NOTECAPTION_BORDERDIST_TEMP,
1183 rVisRect.Top() + SC_NOTECAPTION_BORDERDIST_TEMP,
1184 rVisRect.Right() - SC_NOTECAPTION_BORDERDIST_TEMP,
1185 rVisRect.Bottom() - SC_NOTECAPTION_BORDERDIST_TEMP );
1186
1187 // create the caption object
1188 ScCaptionCreator aCreator( rDoc, rPos, bTailFront );
1189
1190 // insert caption into page (needed to set caption text)
1191 aCreator.GetCaption().insertToDrawPage( rDrawPage );
1192
1193 SdrCaptionObj* pCaption = aCreator.GetCaption().get(); // just for ease of use
1194
1195 // clone the edit text object, unless user text is present, then set this text
1196 if( pNoteCaption && rUserText.isEmpty() )
1197 {
1198 if( OutlinerParaObject* pOPO = pNoteCaption->GetOutlinerParaObject() )
1199 pCaption->SetOutlinerParaObject( std::make_unique<OutlinerParaObject>( *pOPO ) );
1200 // set formatting (must be done after setting text) and resize the box to fit the text
1201 pCaption->SetMergedItemSetAndBroadcast( pNoteCaption->GetMergedItemSet() );
1202 tools::Rectangle aCaptRect( pCaption->GetLogicRect().TopLeft(), pNoteCaption->GetLogicRect().GetSize() );
1203 pCaption->SetLogicRect( aCaptRect );
1204 }
1205 else
1206 {
1207 // if pNoteCaption is null, then aBuffer contains some text
1208 pCaption->SetText( aBuffer.makeStringAndClear() );
1209 ScCaptionUtil::SetDefaultItems( *pCaption, rDoc, nullptr );
1210 // adjust caption size to text size
1211 tools::Long nMaxWidth = ::std::min< tools::Long >( aVisRect.GetWidth() * 2 / 3, SC_NOTECAPTION_MAXWIDTH_TEMP );
1212 pCaption->SetMergedItem( makeSdrTextAutoGrowWidthItem( true ) );
1213 pCaption->SetMergedItem( makeSdrTextMinFrameWidthItem( SC_NOTECAPTION_WIDTH ) );
1214 pCaption->SetMergedItem( makeSdrTextMaxFrameWidthItem( nMaxWidth ) );
1215 pCaption->SetMergedItem( makeSdrTextAutoGrowHeightItem( true ) );
1216 pCaption->AdjustTextFrameWidthAndHeight();
1217 }
1218
1219 // move caption into visible area
1220 aCreator.AutoPlaceCaption( &aVisRect );
1221
1222 // XXX Note it is already inserted to the draw page.
1223 return aCreator.GetCaption();
1224 }
1225
CreateNoteFromCaption(ScDocument & rDoc,const ScAddress & rPos,SdrCaptionObj * pCaption)1226 ScPostIt* ScNoteUtil::CreateNoteFromCaption(
1227 ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj* pCaption )
1228 {
1229 ScNoteData aNoteData( true/*bShown*/ );
1230 aNoteData.mxCaption.reset( pCaption );
1231 ScPostIt* pNote = new ScPostIt( rDoc, rPos, aNoteData, false );
1232 pNote->AutoStamp();
1233
1234 rDoc.SetNote(rPos, std::unique_ptr<ScPostIt>(pNote));
1235
1236 // ScNoteCaptionCreator c'tor updates the caption object to be part of a note
1237 ScNoteCaptionCreator aCreator( rDoc, rPos, aNoteData.mxCaption, true/*bShown*/ );
1238
1239 return pNote;
1240 }
1241
CreateNoteFromObjectData(ScDocument & rDoc,const ScAddress & rPos,std::unique_ptr<SfxItemSet> pItemSet,OutlinerParaObject * pOutlinerObj,const tools::Rectangle & rCaptionRect,bool bShown)1242 ScPostIt* ScNoteUtil::CreateNoteFromObjectData(
1243 ScDocument& rDoc, const ScAddress& rPos, std::unique_ptr<SfxItemSet> pItemSet,
1244 OutlinerParaObject* pOutlinerObj, const tools::Rectangle& rCaptionRect,
1245 bool bShown )
1246 {
1247 OSL_ENSURE( pItemSet && pOutlinerObj, "ScNoteUtil::CreateNoteFromObjectData - item set and outliner object expected" );
1248 ScNoteData aNoteData( bShown );
1249 aNoteData.mxInitData = std::make_shared<ScCaptionInitData>();
1250 ScCaptionInitData& rInitData = *aNoteData.mxInitData;
1251 rInitData.mxItemSet = std::move(pItemSet);
1252 rInitData.mxOutlinerObj.reset( pOutlinerObj );
1253
1254 // convert absolute caption position to relative position
1255 rInitData.mbDefaultPosSize = rCaptionRect.IsEmpty();
1256 if( !rInitData.mbDefaultPosSize )
1257 {
1258 tools::Rectangle aCellRect = ScDrawLayer::GetCellRect( rDoc, rPos, true );
1259 bool bNegPage = rDoc.IsNegativePage( rPos.Tab() );
1260 rInitData.maCaptionOffset.setX( bNegPage ? (aCellRect.Left() - rCaptionRect.Right()) : (rCaptionRect.Left() - aCellRect.Right()) );
1261 rInitData.maCaptionOffset.setY( rCaptionRect.Top() - aCellRect.Top() );
1262 rInitData.maCaptionSize = rCaptionRect.GetSize();
1263 }
1264
1265 /* Create the note and insert it into the document. If the note is
1266 visible, the caption object will be created automatically. */
1267 ScPostIt* pNote = new ScPostIt( rDoc, rPos, aNoteData, /*bAlwaysCreateCaption*/false, 0/*nPostItId*/ );
1268 pNote->AutoStamp();
1269
1270 rDoc.SetNote(rPos, std::unique_ptr<ScPostIt>(pNote));
1271
1272 return pNote;
1273 }
1274
CreateNoteFromString(ScDocument & rDoc,const ScAddress & rPos,const OUString & rNoteText,bool bShown,bool bAlwaysCreateCaption,sal_uInt32 nPostItId)1275 ScPostIt* ScNoteUtil::CreateNoteFromString(
1276 ScDocument& rDoc, const ScAddress& rPos, const OUString& rNoteText,
1277 bool bShown, bool bAlwaysCreateCaption, sal_uInt32 nPostItId )
1278 {
1279 ScPostIt* pNote = nullptr;
1280 if( !rNoteText.isEmpty() )
1281 {
1282 ScNoteData aNoteData( bShown );
1283 aNoteData.mxInitData = std::make_shared<ScCaptionInitData>();
1284 ScCaptionInitData& rInitData = *aNoteData.mxInitData;
1285 rInitData.maSimpleText = rNoteText;
1286 rInitData.mbDefaultPosSize = true;
1287
1288 /* Create the note and insert it into the document. If the note is
1289 visible, the caption object will be created automatically. */
1290 pNote = new ScPostIt( rDoc, rPos, aNoteData, bAlwaysCreateCaption, nPostItId );
1291 pNote->AutoStamp();
1292 //insert takes ownership
1293 rDoc.SetNote(rPos, std::unique_ptr<ScPostIt>(pNote));
1294 }
1295 return pNote;
1296 }
1297
1298 namespace sc {
1299
NoteEntry(const ScAddress & rPos,const ScPostIt * pNote)1300 NoteEntry::NoteEntry( const ScAddress& rPos, const ScPostIt* pNote ) :
1301 maPos(rPos), mpNote(pNote) {}
1302
1303 }
1304
1305 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1306