xref: /AOO41X/main/sc/source/core/data/postit.cxx (revision 2ab8e59e44d5950d9c68a72781ee6b8163bb05bd)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_sc.hxx"
26 
27 #include "postit.hxx"
28 
29 #include <rtl/ustrbuf.hxx>
30 #include <unotools/useroptions.hxx>
31 #include <svx/svdpage.hxx>
32 #include <svx/svdocapt.hxx>
33 #include <editeng/outlobj.hxx>
34 #include <editeng/editobj.hxx>
35 #include <basegfx/polygon/b2dpolygon.hxx>
36 
37 #include "scitems.hxx"
38 #include <svx/xlnstit.hxx>
39 #include <svx/xlnstwit.hxx>
40 #include <svx/xlnstcit.hxx>
41 #include <svx/sxcecitm.hxx>
42 #include <svx/xflclit.hxx>
43 #include <svx/sdshitm.hxx>
44 #include <svx/sdsxyitm.hxx>
45 
46 #include "document.hxx"
47 #include "docpool.hxx"
48 #include "patattr.hxx"
49 #include "cell.hxx"
50 #include "drwlayer.hxx"
51 #include "userdat.hxx"
52 #include "detfunc.hxx"
53 
54 using ::rtl::OUString;
55 using ::rtl::OUStringBuffer;
56 
57 // ============================================================================
58 
59 namespace {
60 
61 const long SC_NOTECAPTION_WIDTH             =  2900;    /// Default width of note caption textbox.
62 const long SC_NOTECAPTION_MAXWIDTH_TEMP     = 12000;    /// Maximum width of temporary note caption textbox.
63 const long SC_NOTECAPTION_HEIGHT            =  1800;    /// Default height of note caption textbox.
64 const long SC_NOTECAPTION_CELLDIST          =   600;    /// Default distance of note captions to border of anchor cell.
65 const long SC_NOTECAPTION_OFFSET_Y          = -1500;    /// Default Y offset of note captions to top border of anchor cell.
66 const long SC_NOTECAPTION_OFFSET_X          =  1500;    /// Default X offset of note captions to left border of anchor cell.
67 const long SC_NOTECAPTION_BORDERDIST_TEMP   =   100;    /// Distance of temporary note captions to visible sheet area.
68 
69 // ============================================================================
70 
71 /** Static helper functions for caption objects. */
72 class ScCaptionUtil
73 {
74 public:
75     /** Moves the caption object to the correct layer according to passed visibility. */
76     static void         SetCaptionLayer( SdrCaptionObj& rCaption, bool bShown );
77     /** Sets basic caption settings required for note caption objects. */
78     static void         SetBasicCaptionSettings( SdrCaptionObj& rCaption, bool bShown );
79     /** Stores the cell position of the note in the user data area of the caption. */
80     static void         SetCaptionUserData( SdrCaptionObj& rCaption, const ScAddress& rPos );
81     /** Sets all default formatting attributes to the caption object. */
82     static void         SetDefaultItems( SdrCaptionObj& rCaption, ScDocument& rDoc );
83     /** Updates caption item set according to the passed item set while removing shadow items. */
84     static void         SetCaptionItems( SdrCaptionObj& rCaption, const SfxItemSet& rItemSet );
85 };
86 
87 // ----------------------------------------------------------------------------
88 
89 void ScCaptionUtil::SetCaptionLayer( SdrCaptionObj& rCaption, bool bShown )
90 {
91     SdrLayerID nLayer = bShown ? SC_LAYER_INTERN : SC_LAYER_HIDDEN;
92     if( nLayer != rCaption.GetLayer() )
93         rCaption.SetLayer( nLayer );
94 }
95 
96 void ScCaptionUtil::SetBasicCaptionSettings( SdrCaptionObj& rCaption, bool bShown )
97 {
98     ScDrawLayer::SetAnchor( &rCaption, SCA_PAGE );
99     SetCaptionLayer( rCaption, bShown );
100     rCaption.SetFixedTail();
101     rCaption.SetSpecialTextBoxShadow();
102 }
103 
104 void ScCaptionUtil::SetCaptionUserData( SdrCaptionObj& rCaption, const ScAddress& rPos )
105 {
106     // pass true to ScDrawLayer::GetObjData() to create the object data entry
107     ScDrawObjData* pObjData = ScDrawLayer::GetObjData( &rCaption, true );
108     OSL_ENSURE( pObjData, "ScCaptionUtil::SetCaptionUserData - missing drawing object user data" );
109     pObjData->maStart = rPos;
110     pObjData->mbNote = true;
111 }
112 
113 void ScCaptionUtil::SetDefaultItems( SdrCaptionObj& rCaption, ScDocument& rDoc )
114 {
115     SfxItemSet aItemSet = rCaption.GetMergedItemSet();
116 
117     // caption tail arrow
118     ::basegfx::B2DPolygon aTriangle;
119     aTriangle.append( ::basegfx::B2DPoint( 10.0,  0.0 ) );
120     aTriangle.append( ::basegfx::B2DPoint(  0.0, 30.0 ) );
121     aTriangle.append( ::basegfx::B2DPoint( 20.0, 30.0 ) );
122     aTriangle.setClosed( true );
123     /*  #99319# Line ends are now created with an empty name. The
124         checkForUniqueItem() method then finds a unique name for the item's
125         value. */
126     aItemSet.Put( XLineStartItem( String::EmptyString(), ::basegfx::B2DPolyPolygon( aTriangle ) ) );
127     aItemSet.Put( XLineStartWidthItem( 200 ) );
128     aItemSet.Put( XLineStartCenterItem( sal_False ) );
129     aItemSet.Put( XFillStyleItem( XFILL_SOLID ) );
130     aItemSet.Put( XFillColorItem( String::EmptyString(), ScDetectiveFunc::GetCommentColor() ) );
131     aItemSet.Put( SdrCaptionEscDirItem( SDRCAPT_ESCBESTFIT ) );
132 
133     // shadow
134     /*  SdrShadowItem has sal_False, instead the shadow is set for the
135         rectangle only with SetSpecialTextBoxShadow() when the object is
136         created (item must be set to adjust objects from older files). */
137     aItemSet.Put( SdrShadowItem( sal_False ) );
138     aItemSet.Put( SdrShadowXDistItem( 100 ) );
139     aItemSet.Put( SdrShadowYDistItem( 100 ) );
140 
141     // text attributes
142     aItemSet.Put( SdrTextLeftDistItem( 100 ) );
143     aItemSet.Put( SdrTextRightDistItem( 100 ) );
144     aItemSet.Put( SdrTextUpperDistItem( 100 ) );
145     aItemSet.Put( SdrTextLowerDistItem( 100 ) );
146     aItemSet.Put( SdrTextAutoGrowWidthItem( sal_False ) );
147     aItemSet.Put( SdrTextAutoGrowHeightItem( sal_True ) );
148     // #78943# use the default cell style to be able to modify the caption font
149     const ScPatternAttr& rDefPattern = static_cast< const ScPatternAttr& >( rDoc.GetPool()->GetDefaultItem( ATTR_PATTERN ) );
150     rDefPattern.FillEditItemSet( &aItemSet );
151 
152     rCaption.SetMergedItemSet( aItemSet );
153 }
154 
155 void ScCaptionUtil::SetCaptionItems( SdrCaptionObj& rCaption, const SfxItemSet& rItemSet )
156 {
157     // copy all items
158     rCaption.SetMergedItemSet( rItemSet );
159     // reset shadow items
160     rCaption.SetMergedItem( SdrShadowItem( sal_False ) );
161     rCaption.SetMergedItem( SdrShadowXDistItem( 100 ) );
162     rCaption.SetMergedItem( SdrShadowYDistItem( 100 ) );
163     rCaption.SetSpecialTextBoxShadow();
164 }
165 
166 // ============================================================================
167 
168 /** Helper for creation and manipulation of caption drawing objects independent
169     from cell annotations. */
170 class ScCaptionCreator
171 {
172 public:
173     /** Create a new caption. The caption will not be inserted into the document. */
174     explicit            ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, bool bShown, bool bTailFront );
175     /** Manipulate an existing caption. */
176     explicit            ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption );
177 
178     /** Returns the drawing layer page of the sheet contained in maPos. */
179     SdrPage*            GetDrawPage();
180     /** Returns the caption drawing obejct. */
181     inline SdrCaptionObj* GetCaption() { return mpCaption; }
182 
183     /** Moves the caption inside the passed rectangle. Uses page area if 0 is passed. */
184     void                FitCaptionToRect( const Rectangle* pVisRect = 0 );
185     /** Places the caption inside the passed rectangle, tries to keep the cell rectangle uncovered. Uses page area if 0 is passed. */
186     void                AutoPlaceCaption( const Rectangle* pVisRect = 0 );
187     /** Updates caption tail and textbox according to current cell position. Uses page area if 0 is passed. */
188     void                UpdateCaptionPos( const Rectangle* pVisRect = 0 );
189 
190 protected:
191     /** Helper constructor for derived classes. */
192     explicit            ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos );
193 
194     /** Calculates the caption tail position according to current cell position. */
195     Point               CalcTailPos( bool bTailFront );
196     /** Implements creation of the caption object. The caption will not be inserted into the document. */
197     void                CreateCaption( bool bShown, bool bTailFront );
198 
199 private:
200     /** Initializes all members. */
201     void                Initialize();
202     /** Returns the passed rectangle if existing, page rectangle otherwise. */
203     inline const Rectangle& GetVisRect( const Rectangle* pVisRect ) const { return pVisRect ? *pVisRect : maPageRect; }
204 
205 private:
206     ScDocument&         mrDoc;
207     ScAddress           maPos;
208     SdrCaptionObj*      mpCaption;
209     Rectangle           maPageRect;
210     Rectangle           maCellRect;
211     bool                mbNegPage;
212 };
213 
214 // ----------------------------------------------------------------------------
215 
216 ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, bool bShown, bool bTailFront ) :
217     mrDoc( rDoc ),
218     maPos( rPos ),
219     mpCaption( 0 )
220 {
221     Initialize();
222     CreateCaption( bShown, bTailFront );
223 }
224 
225 ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption ) :
226     mrDoc( rDoc ),
227     maPos( rPos ),
228     mpCaption( &rCaption )
229 {
230     Initialize();
231 }
232 
233 ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos ) :
234     mrDoc( rDoc ),
235     maPos( rPos ),
236     mpCaption( 0 )
237 {
238     Initialize();
239 }
240 
241 SdrPage* ScCaptionCreator::GetDrawPage()
242 {
243     ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
244     return pDrawLayer ? pDrawLayer->GetPage( static_cast< sal_uInt16 >( maPos.Tab() ) ) : 0;
245 }
246 
247 void ScCaptionCreator::FitCaptionToRect( const Rectangle* pVisRect )
248 {
249     const Rectangle& rVisRect = GetVisRect( pVisRect );
250 
251     // tail position
252     Point aTailPos = mpCaption->GetTailPos();
253     aTailPos.X() = ::std::max( ::std::min( aTailPos.X(), rVisRect.Right() ), rVisRect.Left() );
254     aTailPos.Y() = ::std::max( ::std::min( aTailPos.Y(), rVisRect.Bottom() ), rVisRect.Top() );
255     mpCaption->SetTailPos( aTailPos );
256 
257     // caption rectangle
258     Rectangle aCaptRect = mpCaption->GetLogicRect();
259     Point aCaptPos = aCaptRect.TopLeft();
260     // move textbox inside right border of visible area
261     aCaptPos.X() = ::std::min< long >( aCaptPos.X(), rVisRect.Right() - aCaptRect.GetWidth() );
262     // move textbox inside left border of visible area (this may move it outside on right side again)
263     aCaptPos.X() = ::std::max< long >( aCaptPos.X(), rVisRect.Left() );
264     // move textbox inside bottom border of visible area
265     aCaptPos.Y() = ::std::min< long >( aCaptPos.Y(), rVisRect.Bottom() - aCaptRect.GetHeight() );
266     // move textbox inside top border of visible area (this may move it outside on bottom side again)
267     aCaptPos.Y() = ::std::max< long >( aCaptPos.Y(), rVisRect.Top() );
268     // update caption
269     aCaptRect.SetPos( aCaptPos );
270     mpCaption->SetLogicRect( aCaptRect );
271 }
272 
273 void ScCaptionCreator::AutoPlaceCaption( const Rectangle* pVisRect )
274 {
275     const Rectangle& rVisRect = GetVisRect( pVisRect );
276 
277     // caption rectangle
278     Rectangle aCaptRect = mpCaption->GetLogicRect();
279     long nWidth = aCaptRect.GetWidth();
280     long nHeight = aCaptRect.GetHeight();
281 
282     // n***Space contains available space between border of visible area and cell
283     long nLeftSpace = maCellRect.Left() - rVisRect.Left() + 1;
284     long nRightSpace = rVisRect.Right() - maCellRect.Right() + 1;
285     long nTopSpace = maCellRect.Top() - rVisRect.Top() + 1;
286     long nBottomSpace = rVisRect.Bottom() - maCellRect.Bottom() + 1;
287 
288     // nNeeded*** contains textbox dimensions plus needed distances to cell or border of visible area
289     long nNeededSpaceX = nWidth + SC_NOTECAPTION_CELLDIST;
290     long nNeededSpaceY = nHeight + SC_NOTECAPTION_CELLDIST;
291 
292     // bFitsWidth*** == true means width of textbox fits into horizontal free space of visible area
293     bool bFitsWidthLeft = nNeededSpaceX <= nLeftSpace;      // text box width fits into the width left of cell
294     bool bFitsWidthRight = nNeededSpaceX <= nRightSpace;    // text box width fits into the width right of cell
295     bool bFitsWidth = nWidth <= rVisRect.GetWidth();        // text box width fits into width of visible area
296 
297     // bFitsHeight*** == true means height of textbox fits into vertical free space of visible area
298     bool bFitsHeightTop = nNeededSpaceY <= nTopSpace;       // text box height fits into the height above cell
299     bool bFitsHeightBottom = nNeededSpaceY <= nBottomSpace; // text box height fits into the height below cell
300     bool bFitsHeight = nHeight <= rVisRect.GetHeight();     // text box height fits into height of visible area
301 
302     // bFits*** == true means the textbox fits completely into free space of visible area
303     bool bFitsLeft = bFitsWidthLeft && bFitsHeight;
304     bool bFitsRight = bFitsWidthRight && bFitsHeight;
305     bool bFitsTop = bFitsWidth && bFitsHeightTop;
306     bool bFitsBottom = bFitsWidth && bFitsHeightBottom;
307 
308     Point aCaptPos;
309     // use left/right placement if possible, or if top/bottom placement not possible
310     if( bFitsLeft || bFitsRight || (!bFitsTop && !bFitsBottom) )
311     {
312         // prefer left in RTL sheet and right in LTR sheets
313         bool bPreferLeft = bFitsLeft && (mbNegPage || !bFitsRight);
314         bool bPreferRight = bFitsRight && (!mbNegPage || !bFitsLeft);
315         // move to left, if left is preferred, or if neither left nor right fit and there is more space to the left
316         if( bPreferLeft || (!bPreferRight && (nLeftSpace > nRightSpace)) )
317             aCaptPos.X() = maCellRect.Left() - SC_NOTECAPTION_CELLDIST - nWidth;
318         else // to right
319             aCaptPos.X() = maCellRect.Right() + SC_NOTECAPTION_CELLDIST;
320         // Y position according to top cell border
321         aCaptPos.Y() = maCellRect.Top() + SC_NOTECAPTION_OFFSET_Y;
322     }
323     else    // top or bottom placement
324     {
325         // X position
326         aCaptPos.X() = maCellRect.Left() + SC_NOTECAPTION_OFFSET_X;
327         // top placement, if possible
328         if( bFitsTop )
329             aCaptPos.Y() = maCellRect.Top() - SC_NOTECAPTION_CELLDIST - nHeight;
330         else    // bottom placement
331             aCaptPos.Y() = maCellRect.Bottom() + SC_NOTECAPTION_CELLDIST;
332     }
333 
334     // update textbox position in note caption object
335     aCaptRect.SetPos( aCaptPos );
336     mpCaption->SetLogicRect( aCaptRect );
337     FitCaptionToRect( pVisRect );
338 }
339 
340 void ScCaptionCreator::UpdateCaptionPos( const Rectangle* pVisRect )
341 {
342     ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
343 
344     // update caption position
345     const Point& rOldTailPos = mpCaption->GetTailPos();
346     Point aTailPos = CalcTailPos( false );
347     if( rOldTailPos != aTailPos )
348     {
349         // create drawing undo action
350         if( pDrawLayer && pDrawLayer->IsRecording() )
351             pDrawLayer->AddCalcUndo( pDrawLayer->GetSdrUndoFactory().CreateUndoGeoObject( *mpCaption ) );
352         // calculate new caption rectangle (#i98141# handle LTR<->RTL switch correctly)
353         Rectangle aCaptRect = mpCaption->GetLogicRect();
354         long nDiffX = (rOldTailPos.X() >= 0) ? (aCaptRect.Left() - rOldTailPos.X()) : (rOldTailPos.X() - aCaptRect.Right());
355         if( mbNegPage ) nDiffX = -nDiffX - aCaptRect.GetWidth();
356         long nDiffY = aCaptRect.Top() - rOldTailPos.Y();
357         aCaptRect.SetPos( aTailPos + Point( nDiffX, nDiffY ) );
358         // set new tail position and caption rectangle
359         mpCaption->SetTailPos( aTailPos );
360         mpCaption->SetLogicRect( aCaptRect );
361         // fit caption into draw page
362         FitCaptionToRect( pVisRect );
363     }
364 
365     // update cell position in caption user data
366     ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( mpCaption, maPos.Tab() );
367     if( pCaptData && (maPos != pCaptData->maStart) )
368     {
369         // create drawing undo action
370         if( pDrawLayer && pDrawLayer->IsRecording() )
371             pDrawLayer->AddCalcUndo( new ScUndoObjData( mpCaption, pCaptData->maStart, pCaptData->maEnd, maPos, pCaptData->maEnd ) );
372         // set new position
373         pCaptData->maStart = maPos;
374     }
375 }
376 
377 Point ScCaptionCreator::CalcTailPos( bool bTailFront )
378 {
379     // tail position
380     bool bTailLeft = bTailFront != mbNegPage;
381     Point aTailPos = bTailLeft ? maCellRect.TopLeft() : maCellRect.TopRight();
382     // move caption point 1/10 mm inside cell
383     if( bTailLeft ) aTailPos.X() += 10; else aTailPos.X() -= 10;
384     aTailPos.Y() += 10;
385     return aTailPos;
386 }
387 
388 void ScCaptionCreator::CreateCaption( bool bShown, bool bTailFront )
389 {
390     // create the caption drawing object
391     Rectangle aTextRect( Point( 0 , 0 ), Size( SC_NOTECAPTION_WIDTH, SC_NOTECAPTION_HEIGHT ) );
392     Point aTailPos = CalcTailPos( bTailFront );
393     mpCaption = new SdrCaptionObj( aTextRect, aTailPos );
394     // basic caption settings
395     ScCaptionUtil::SetBasicCaptionSettings( *mpCaption, bShown );
396 }
397 
398 void ScCaptionCreator::Initialize()
399 {
400     maCellRect = ScDrawLayer::GetCellRect( mrDoc, maPos, true );
401     mbNegPage = mrDoc.IsNegativePage( maPos.Tab() );
402     if( SdrPage* pDrawPage = GetDrawPage() )
403     {
404         maPageRect = Rectangle( Point( 0, 0 ), pDrawPage->GetSize() );
405         /*  #i98141# SdrPage::GetSize() returns negative width in RTL mode.
406             The call to Rectangle::Adjust() orders left/right coordinate
407             accordingly. */
408         maPageRect.Justify();
409     }
410 }
411 
412 // ============================================================================
413 
414 /** Helper for creation of permanent caption drawing objects for cell notes. */
415 class ScNoteCaptionCreator : public ScCaptionCreator
416 {
417 public:
418     /** Create a new caption object and inserts it into the document. */
419     explicit            ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScNoteData& rNoteData );
420     /** Manipulate an existing caption. */
421     explicit            ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption, bool bShown );
422 };
423 
424 // ----------------------------------------------------------------------------
425 
426 ScNoteCaptionCreator::ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScNoteData& rNoteData ) :
427     ScCaptionCreator( rDoc, rPos )  // use helper c'tor that does not create the caption yet
428 {
429     SdrPage* pDrawPage = GetDrawPage();
430     OSL_ENSURE( pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - no drawing page" );
431     if( pDrawPage )
432     {
433         // create the caption drawing object
434         CreateCaption( rNoteData.mbShown, false );
435         rNoteData.mpCaption = GetCaption();
436         OSL_ENSURE( rNoteData.mpCaption, "ScNoteCaptionCreator::ScNoteCaptionCreator - missing caption object" );
437         if( rNoteData.mpCaption )
438         {
439             // store note position in user data of caption object
440             ScCaptionUtil::SetCaptionUserData( *rNoteData.mpCaption, rPos );
441             // insert object into draw page
442             pDrawPage->InsertObject( rNoteData.mpCaption );
443         }
444     }
445 }
446 
447 ScNoteCaptionCreator::ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption, bool bShown ) :
448     ScCaptionCreator( rDoc, rPos, rCaption )
449 {
450     SdrPage* pDrawPage = GetDrawPage();
451     OSL_ENSURE( pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - no drawing page" );
452     OSL_ENSURE( rCaption.GetPage() == pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - wrong drawing page in caption" );
453     if( pDrawPage && (rCaption.GetPage() == pDrawPage) )
454     {
455         // store note position in user data of caption object
456         ScCaptionUtil::SetCaptionUserData( rCaption, rPos );
457         // basic caption settings
458         ScCaptionUtil::SetBasicCaptionSettings( rCaption, bShown );
459         // set correct tail position
460         rCaption.SetTailPos( CalcTailPos( false ) );
461     }
462 }
463 
464 } // namespace
465 
466 // ============================================================================
467 
468 struct ScCaptionInitData
469 {
470     typedef ::std::auto_ptr< SfxItemSet >           SfxItemSetPtr;
471     typedef ::std::auto_ptr< OutlinerParaObject >   OutlinerParaObjPtr;
472 
473     SfxItemSetPtr       mxItemSet;          /// Caption object formatting.
474     OutlinerParaObjPtr  mxOutlinerObj;      /// Text object with all text portion formatting.
475     ::rtl::OUString     maSimpleText;       /// Simple text without formatting.
476     Point               maCaptionOffset;    /// Caption position relative to cell corner.
477     Size                maCaptionSize;      /// Size of the caption object.
478     bool                mbDefaultPosSize;   /// True = use default position and size for caption.
479 
480     explicit            ScCaptionInitData();
481 };
482 
483 // ----------------------------------------------------------------------------
484 
485 ScCaptionInitData::ScCaptionInitData() :
486     mbDefaultPosSize( true )
487 {
488 }
489 
490 // ============================================================================
491 
492 ScNoteData::ScNoteData( bool bShown ) :
493     mpCaption( 0 ),
494     mbShown( bShown )
495 {
496 }
497 
498 ScNoteData::~ScNoteData()
499 {
500 }
501 
502 // ============================================================================
503 
504 ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, bool bShown ) :
505     mrDoc( rDoc ),
506     maNoteData( bShown )
507 {
508     AutoStamp();
509     CreateCaption( rPos );
510 }
511 
512 ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, const ScPostIt& rNote ) :
513     mrDoc( rDoc ),
514     maNoteData( rNote.maNoteData )
515 {
516     maNoteData.mpCaption = 0;
517     CreateCaption( rPos, rNote.maNoteData.mpCaption );
518 }
519 
520 ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, const ScNoteData& rNoteData, bool bAlwaysCreateCaption ) :
521     mrDoc( rDoc ),
522     maNoteData( rNoteData )
523 {
524     if( bAlwaysCreateCaption || maNoteData.mbShown )
525         CreateCaptionFromInitData( rPos );
526 }
527 
528 ScPostIt::~ScPostIt()
529 {
530     RemoveCaption();
531 }
532 
533 ScPostIt* ScPostIt::Clone( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, bool bCloneCaption ) const
534 {
535     CreateCaptionFromInitData( rOwnPos );
536     return bCloneCaption ? new ScPostIt( rDestDoc, rDestPos, *this ) : new ScPostIt( rDestDoc, rDestPos, maNoteData, false );
537 }
538 
539 void ScPostIt::AutoStamp()
540 {
541     maNoteData.maDate = ScGlobal::pLocaleData->getDate( Date() );
542     maNoteData.maAuthor = SvtUserOptions().GetID();
543 }
544 
545 const OutlinerParaObject* ScPostIt::GetOutlinerObject() const
546 {
547     if( maNoteData.mpCaption )
548         return maNoteData.mpCaption->GetOutlinerParaObject();
549     if( maNoteData.mxInitData.get() )
550         return maNoteData.mxInitData->mxOutlinerObj.get();
551     return 0;
552 }
553 
554 const EditTextObject* ScPostIt::GetEditTextObject() const
555 {
556     const OutlinerParaObject* pOPO = GetOutlinerObject();
557     return pOPO ? &pOPO->GetTextObject() : 0;
558 }
559 
560 OUString ScPostIt::GetText() const
561 {
562     if( const EditTextObject* pEditObj = GetEditTextObject() )
563     {
564         OUStringBuffer aBuffer;
565         for( sal_uInt16 nPara = 0, nParaCount = pEditObj->GetParagraphCount(); nPara < nParaCount; ++nPara )
566         {
567             if( nPara > 0 )
568                 aBuffer.append( sal_Unicode( '\n' ) );
569             aBuffer.append( pEditObj->GetText( nPara ) );
570         }
571         return aBuffer.makeStringAndClear();
572     }
573     if( maNoteData.mxInitData.get() )
574         return maNoteData.mxInitData->maSimpleText;
575     return OUString();
576 }
577 
578 bool ScPostIt::HasMultiLineText() const
579 {
580     if( const EditTextObject* pEditObj = GetEditTextObject() )
581         return pEditObj->GetParagraphCount() > 1;
582     if( maNoteData.mxInitData.get() )
583         return maNoteData.mxInitData->maSimpleText.indexOf( '\n' ) >= 0;
584     return false;
585 }
586 
587 void ScPostIt::SetText( const ScAddress& rPos, const OUString& rText )
588 {
589     CreateCaptionFromInitData( rPos );
590     if( maNoteData.mpCaption )
591         maNoteData.mpCaption->SetText( rText );
592 }
593 
594 SdrCaptionObj* ScPostIt::GetOrCreateCaption( const ScAddress& rPos ) const
595 {
596     CreateCaptionFromInitData( rPos );
597     return maNoteData.mpCaption;
598 }
599 
600 void ScPostIt::ForgetCaption()
601 {
602     /*  This function is used in undo actions to give up the responsibility for
603         the caption object which is handled by separate drawing undo actions. */
604     maNoteData.mpCaption = 0;
605     maNoteData.mxInitData.reset();
606 }
607 
608 void ScPostIt::ShowCaption( const ScAddress& rPos, bool bShow )
609 {
610     CreateCaptionFromInitData( rPos );
611     // no separate drawing undo needed, handled completely inside ScUndoShowHideNote
612     maNoteData.mbShown = bShow;
613     if( maNoteData.mpCaption )
614         ScCaptionUtil::SetCaptionLayer( *maNoteData.mpCaption, bShow );
615 }
616 
617 void ScPostIt::ShowCaptionTemp( const ScAddress& rPos, bool bShow )
618 {
619     CreateCaptionFromInitData( rPos );
620     if( maNoteData.mpCaption )
621         ScCaptionUtil::SetCaptionLayer( *maNoteData.mpCaption, maNoteData.mbShown || bShow );
622 }
623 
624 void ScPostIt::UpdateCaptionPos( const ScAddress& rPos )
625 {
626     CreateCaptionFromInitData( rPos );
627     if( maNoteData.mpCaption )
628     {
629         ScCaptionCreator aCreator( mrDoc, rPos, *maNoteData.mpCaption );
630         aCreator.UpdateCaptionPos();
631     }
632 }
633 
634 // private --------------------------------------------------------------------
635 
636 void ScPostIt::CreateCaptionFromInitData( const ScAddress& rPos ) const
637 {
638     OSL_ENSURE( maNoteData.mpCaption || maNoteData.mxInitData.get(), "ScPostIt::CreateCaptionFromInitData - need caption object or initial caption data" );
639     if( maNoteData.mxInitData.get() )
640     {
641         /*  This function is called from ScPostIt::Clone() when copying cells
642             to the clipboard/undo document, and when copying cells from the
643             clipboard/undo document. The former should always be called first,
644             so if called in an clipboard/undo document, the caption should have
645             been created already. */
646         OSL_ENSURE( !mrDoc.IsUndo() && !mrDoc.IsClipboard(), "ScPostIt::CreateCaptionFromInitData - note caption should not be created in undo/clip documents" );
647 
648         /*  #i104915# Never try to create notes in Undo document, leads to
649             crash due to missing document members (e.g. row height array). */
650         if( !maNoteData.mpCaption && !mrDoc.IsUndo() )
651         {
652             // ScNoteCaptionCreator c'tor creates the caption and inserts it into the document and maNoteData
653             ScNoteCaptionCreator aCreator( mrDoc, rPos, maNoteData );
654             if( maNoteData.mpCaption )
655             {
656                 ScCaptionInitData& rInitData = *maNoteData.mxInitData;
657 
658                 // transfer ownership of outliner object to caption, or set simple text
659                 OSL_ENSURE( rInitData.mxOutlinerObj.get() || (rInitData.maSimpleText.getLength() > 0),
660                     "ScPostIt::CreateCaptionFromInitData - need either outliner para object or simple text" );
661                 if( rInitData.mxOutlinerObj.get() )
662                     maNoteData.mpCaption->SetOutlinerParaObject( rInitData.mxOutlinerObj.release() );
663                 else
664                     maNoteData.mpCaption->SetText( rInitData.maSimpleText );
665 
666                 // copy all items or set default items; reset shadow items
667                 ScCaptionUtil::SetDefaultItems( *maNoteData.mpCaption, mrDoc );
668                 if( rInitData.mxItemSet.get() )
669                     ScCaptionUtil::SetCaptionItems( *maNoteData.mpCaption, *rInitData.mxItemSet );
670 
671                 // set position and size of the caption object
672                 if( rInitData.mbDefaultPosSize )
673                 {
674                     // set other items and fit caption size to text
675                     maNoteData.mpCaption->SetMergedItem( SdrTextMinFrameWidthItem( SC_NOTECAPTION_WIDTH ) );
676                     maNoteData.mpCaption->SetMergedItem( SdrTextMaxFrameWidthItem( SC_NOTECAPTION_MAXWIDTH_TEMP ) );
677                     maNoteData.mpCaption->AdjustTextFrameWidthAndHeight();
678                     aCreator.AutoPlaceCaption();
679                 }
680                 else
681                 {
682                     Rectangle aCellRect = ScDrawLayer::GetCellRect( mrDoc, rPos, true );
683                     bool bNegPage = mrDoc.IsNegativePage( rPos.Tab() );
684                     long nPosX = bNegPage ? (aCellRect.Left() - rInitData.maCaptionOffset.X()) : (aCellRect.Right() + rInitData.maCaptionOffset.X());
685                     long nPosY = aCellRect.Top() + rInitData.maCaptionOffset.Y();
686                     Rectangle aCaptRect( Point( nPosX, nPosY ), rInitData.maCaptionSize );
687                     maNoteData.mpCaption->SetLogicRect( aCaptRect );
688                     aCreator.FitCaptionToRect();
689                 }
690             }
691         }
692         // forget the initial caption data struct
693         maNoteData.mxInitData.reset();
694     }
695 }
696 
697 void ScPostIt::CreateCaption( const ScAddress& rPos, const SdrCaptionObj* pCaption )
698 {
699     OSL_ENSURE( !maNoteData.mpCaption, "ScPostIt::CreateCaption - unexpected caption object found" );
700     maNoteData.mpCaption = 0;
701 
702     /*  #i104915# Never try to create notes in Undo document, leads to
703         crash due to missing document members (e.g. row height array). */
704     OSL_ENSURE( !mrDoc.IsUndo(), "ScPostIt::CreateCaption - note caption should not be created in undo documents" );
705     if( mrDoc.IsUndo() )
706         return;
707 
708     // drawing layer may be missing, if a note is copied into a clipboard document
709     if( mrDoc.IsClipboard() )
710         mrDoc.InitDrawLayer();
711 
712     // ScNoteCaptionCreator c'tor creates the caption and inserts it into the document and maNoteData
713     ScNoteCaptionCreator aCreator( mrDoc, rPos, maNoteData );
714     if( maNoteData.mpCaption )
715     {
716         // clone settings of passed caption
717         if( pCaption )
718         {
719             // copy edit text object (object must be inserted into page already)
720             if( OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() )
721                 maNoteData.mpCaption->SetOutlinerParaObject( new OutlinerParaObject( *pOPO ) );
722             // copy formatting items (after text has been copied to apply font formatting)
723             maNoteData.mpCaption->SetMergedItemSetAndBroadcast( pCaption->GetMergedItemSet() );
724             // move textbox position relative to new cell, copy textbox size
725             Rectangle aCaptRect = pCaption->GetLogicRect();
726             Point aDist = maNoteData.mpCaption->GetTailPos() - pCaption->GetTailPos();
727             aCaptRect.Move( aDist.X(), aDist.Y() );
728             maNoteData.mpCaption->SetLogicRect( aCaptRect );
729             aCreator.FitCaptionToRect();
730         }
731         else
732         {
733             // set default formatting and default position
734             ScCaptionUtil::SetDefaultItems( *maNoteData.mpCaption, mrDoc );
735             aCreator.AutoPlaceCaption();
736         }
737 
738         // create undo action
739         if( ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer() )
740             if( pDrawLayer->IsRecording() )
741                 pDrawLayer->AddCalcUndo( pDrawLayer->GetSdrUndoFactory().CreateUndoNewObject( *maNoteData.mpCaption ) );
742     }
743 }
744 
745 void ScPostIt::RemoveCaption()
746 {
747 
748     /*  Remove caption object only, if this note is its owner (e.g. notes in
749         undo documents refer to captions in original document, do not remove
750         them from drawing layer here). */
751     ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer();
752     if( maNoteData.mpCaption && (pDrawLayer == maNoteData.mpCaption->GetModel()) )
753     {
754         OSL_ENSURE( pDrawLayer, "ScPostIt::RemoveCaption - object without drawing layer" );
755         SdrPage* pDrawPage = maNoteData.mpCaption->GetPage();
756         OSL_ENSURE( pDrawPage, "ScPostIt::RemoveCaption - object without drawing page" );
757         if( pDrawPage )
758         {
759             pDrawPage->RecalcObjOrdNums();
760             // create drawing undo action (before removing the object to have valid draw page in undo action)
761             bool bRecording = ( pDrawLayer && pDrawLayer->IsRecording() );
762             if( bRecording )
763                 pDrawLayer->AddCalcUndo( pDrawLayer->GetSdrUndoFactory().CreateUndoDeleteObject( *maNoteData.mpCaption ) );
764             // remove the object from the drawing page, delete if undo is disabled
765             SdrObject* pObj = pDrawPage->RemoveObject( maNoteData.mpCaption->GetOrdNum() );
766             if( !bRecording )
767                 SdrObject::Free( pObj );
768         }
769     }
770     maNoteData.mpCaption = 0;
771 }
772 
773 // ============================================================================
774 
775 void ScNoteUtil::UpdateCaptionPositions( ScDocument& rDoc, const ScRange& rRange )
776 {
777     // do not use ScCellIterator, it skips filtered and subtotal cells
778     for( ScAddress aPos( rRange.aStart ); aPos.Tab() <= rRange.aEnd.Tab(); aPos.IncTab() )
779         for( aPos.SetCol( rRange.aStart.Col() ); aPos.Col() <= rRange.aEnd.Col(); aPos.IncCol() )
780             for( aPos.SetRow( rRange.aStart.Row() ); aPos.Row() <= rRange.aEnd.Row(); aPos.IncRow() )
781                 if( ScPostIt* pNote = rDoc.GetNote( aPos ) )
782                     pNote->UpdateCaptionPos( aPos );
783 }
784 
785 SdrCaptionObj* ScNoteUtil::CreateTempCaption(
786         ScDocument& rDoc, const ScAddress& rPos, SdrPage& rDrawPage,
787         const OUString& rUserText, const Rectangle& rVisRect, bool bTailFront )
788 {
789     OUStringBuffer aBuffer( rUserText );
790     // add plain text of invisible (!) cell note (no formatting etc.)
791     SdrCaptionObj* pNoteCaption = 0;
792     const ScPostIt* pNote = rDoc.GetNote( rPos );
793     if( pNote && !pNote->IsCaptionShown() )
794     {
795         if( aBuffer.getLength() > 0 )
796             aBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM( "\n--------\n" ) ).append( pNote->GetText() );
797         pNoteCaption = pNote->GetOrCreateCaption( rPos );
798     }
799 
800     // create a caption if any text exists
801     if( !pNoteCaption && (aBuffer.getLength() == 0) )
802         return 0;
803 
804     // prepare visible rectangle (add default distance to all borders)
805     Rectangle aVisRect(
806         rVisRect.Left() + SC_NOTECAPTION_BORDERDIST_TEMP,
807         rVisRect.Top() + SC_NOTECAPTION_BORDERDIST_TEMP,
808         rVisRect.Right() - SC_NOTECAPTION_BORDERDIST_TEMP,
809         rVisRect.Bottom() - SC_NOTECAPTION_BORDERDIST_TEMP );
810 
811     // create the caption object
812     ScCaptionCreator aCreator( rDoc, rPos, true, bTailFront );
813     SdrCaptionObj* pCaption = aCreator.GetCaption();
814 
815     // insert caption into page (needed to set caption text)
816     rDrawPage.InsertObject( pCaption );
817 
818     // clone the edit text object, unless user text is present, then set this text
819     if( pNoteCaption && (rUserText.getLength() == 0) )
820     {
821         if( OutlinerParaObject* pOPO = pNoteCaption->GetOutlinerParaObject() )
822             pCaption->SetOutlinerParaObject( new OutlinerParaObject( *pOPO ) );
823         // set formatting (must be done after setting text) and resize the box to fit the text
824         pCaption->SetMergedItemSetAndBroadcast( pNoteCaption->GetMergedItemSet() );
825         Rectangle aCaptRect( pCaption->GetLogicRect().TopLeft(), pNoteCaption->GetLogicRect().GetSize() );
826         pCaption->SetLogicRect( aCaptRect );
827     }
828     else
829     {
830         // if pNoteCaption is null, then aBuffer contains some text
831         pCaption->SetText( aBuffer.makeStringAndClear() );
832         ScCaptionUtil::SetDefaultItems( *pCaption, rDoc );
833         // adjust caption size to text size
834         long nMaxWidth = ::std::min< long >( aVisRect.GetWidth() * 2 / 3, SC_NOTECAPTION_MAXWIDTH_TEMP );
835         pCaption->SetMergedItem( SdrTextAutoGrowWidthItem( sal_True ) );
836         pCaption->SetMergedItem( SdrTextMinFrameWidthItem( SC_NOTECAPTION_WIDTH ) );
837         pCaption->SetMergedItem( SdrTextMaxFrameWidthItem( nMaxWidth ) );
838         pCaption->SetMergedItem( SdrTextAutoGrowHeightItem( sal_True ) );
839         pCaption->AdjustTextFrameWidthAndHeight();
840     }
841 
842     // move caption into visible area
843     aCreator.AutoPlaceCaption( &aVisRect );
844     return pCaption;
845 }
846 
847 ScPostIt* ScNoteUtil::CreateNoteFromCaption(
848         ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption, bool bShown )
849 {
850     ScNoteData aNoteData( bShown );
851     aNoteData.mpCaption = &rCaption;
852     ScPostIt* pNote = new ScPostIt( rDoc, rPos, aNoteData, false );
853     pNote->AutoStamp();
854     rDoc.TakeNote( rPos, pNote );
855     // if pNote still points to the note after TakeNote(), insertion was successful
856     if( pNote )
857     {
858         // ScNoteCaptionCreator c'tor updates the caption object to be part of a note
859         ScNoteCaptionCreator aCreator( rDoc, rPos, rCaption, bShown );
860     }
861     return pNote;
862 }
863 
864 ScPostIt* ScNoteUtil::CreateNoteFromObjectData(
865         ScDocument& rDoc, const ScAddress& rPos, SfxItemSet* pItemSet,
866         OutlinerParaObject* pOutlinerObj, const Rectangle& rCaptionRect,
867         bool bShown, bool bAlwaysCreateCaption )
868 {
869     OSL_ENSURE( pItemSet && pOutlinerObj, "ScNoteUtil::CreateNoteFromObjectData - item set and outliner object expected" );
870     ScNoteData aNoteData( bShown );
871     aNoteData.mxInitData.reset( new ScCaptionInitData );
872     ScCaptionInitData& rInitData = *aNoteData.mxInitData;
873     rInitData.mxItemSet.reset( pItemSet );
874     rInitData.mxOutlinerObj.reset( pOutlinerObj );
875 
876     // convert absolute caption position to relative position
877     rInitData.mbDefaultPosSize = rCaptionRect.IsEmpty();
878     if( !rInitData.mbDefaultPosSize )
879     {
880         Rectangle aCellRect = ScDrawLayer::GetCellRect( rDoc, rPos, true );
881         bool bNegPage = rDoc.IsNegativePage( rPos.Tab() );
882         rInitData.maCaptionOffset.X() = bNegPage ? (aCellRect.Left() - rCaptionRect.Right()) : (rCaptionRect.Left() - aCellRect.Right());
883         rInitData.maCaptionOffset.Y() = rCaptionRect.Top() - aCellRect.Top();
884         rInitData.maCaptionSize = rCaptionRect.GetSize();
885     }
886 
887     /*  Create the note and insert it into the document. If the note is
888         visible, the caption object will be created automatically. */
889     ScPostIt* pNote = new ScPostIt( rDoc, rPos, aNoteData, bAlwaysCreateCaption );
890     pNote->AutoStamp();
891     rDoc.TakeNote( rPos, pNote );
892     // if pNote still points to the note after TakeNote(), insertion was successful
893     return pNote;
894 }
895 
896 ScPostIt* ScNoteUtil::CreateNoteFromString(
897         ScDocument& rDoc, const ScAddress& rPos, const OUString& rNoteText,
898         bool bShown, bool bAlwaysCreateCaption )
899 {
900     ScPostIt* pNote = 0;
901     if( rNoteText.getLength() > 0 )
902     {
903         ScNoteData aNoteData( bShown );
904         aNoteData.mxInitData.reset( new ScCaptionInitData );
905         ScCaptionInitData& rInitData = *aNoteData.mxInitData;
906         rInitData.maSimpleText = rNoteText;
907         rInitData.mbDefaultPosSize = true;
908 
909         /*  Create the note and insert it into the document. If the note is
910             visible, the caption object will be created automatically. */
911         pNote = new ScPostIt( rDoc, rPos, aNoteData, bAlwaysCreateCaption );
912         pNote->AutoStamp();
913         rDoc.TakeNote( rPos, pNote );
914         // if pNote still points to the note after TakeNote(), insertion was successful
915     }
916     return pNote;
917 }
918 
919 // ============================================================================
920