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