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