xref: /aoo42x/main/sw/source/core/doc/docfly.cxx (revision 64b14621)
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_sw.hxx"
26 
27 #include <hintids.hxx>
28 #include <svl/itemiter.hxx>
29 #include <svx/svdobj.hxx>
30 #include <svx/svdpage.hxx>
31 #include <svx/svdmodel.hxx>
32 #include <svx/svdocapt.hxx>
33 #include <svx/svdmark.hxx>
34 #include <fmtfsize.hxx>
35 #include <fmtornt.hxx>
36 #include <fmtsrnd.hxx>
37 #include <dcontact.hxx>
38 
39 #include <ndgrf.hxx>
40 #include <doc.hxx>
41 #include <IDocumentUndoRedo.hxx>
42 #include <ndindex.hxx>
43 #include <docary.hxx>
44 #include <fmtcntnt.hxx>
45 #include <fmtanchr.hxx>
46 #include <txtflcnt.hxx>
47 #include <fmtflcnt.hxx>
48 #include <txtfrm.hxx>
49 #include <pagefrm.hxx>
50 #include <rootfrm.hxx>
51 #include <flyfrms.hxx>
52 #include <frmtool.hxx>
53 #include <frmfmt.hxx>
54 #include <ndtxt.hxx>
55 #include <pam.hxx>
56 #include <tblsel.hxx>
57 #include <swundo.hxx>
58 #include <swtable.hxx>
59 #include <crstate.hxx>
60 #include <UndoCore.hxx>
61 #include <UndoAttribute.hxx>
62 #include <fmtcnct.hxx>
63 #include <dflyobj.hxx>
64 #include <undoflystrattr.hxx>
65 #include <switerator.hxx>
66 
67 //UUUU
68 #include <svx/xbtmpit.hxx>
69 #include <svx/xflftrit.hxx>
70 
71 extern sal_uInt16 GetHtmlMode( const SwDocShell* );
72 
73 
74 using namespace ::com::sun::star;
75 
76 sal_uInt16 SwDoc::GetFlyCount( FlyCntType eType ) const
77 {
78 	const SwSpzFrmFmts& rFmts = *GetSpzFrmFmts();
79 	sal_uInt16 nSize = rFmts.Count();
80 	sal_uInt16 nCount = 0;
81 	const SwNodeIndex* pIdx;
82 	for ( sal_uInt16 i = 0; i < nSize; i++)
83 	{
84 		const SwFrmFmt* pFlyFmt = rFmts[ i ];
85 		if( RES_FLYFRMFMT == pFlyFmt->Which()
86 			&& 0 != ( pIdx = pFlyFmt->GetCntnt().GetCntntIdx() )
87 			&& pIdx->GetNodes().IsDocNodes()
88 			)
89 		{
90 			const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
91 
92 			switch( eType )
93 			{
94 			case FLYCNTTYPE_FRM:
95 				if(!pNd->IsNoTxtNode())
96 					nCount++;
97 				break;
98 
99 			case FLYCNTTYPE_GRF:
100 				if( pNd->IsGrfNode() )
101 					nCount++;
102 				break;
103 
104 			case FLYCNTTYPE_OLE:
105 				if(pNd->IsOLENode())
106 					nCount++;
107 				break;
108 
109 			default:
110 				nCount++;
111 			}
112 		}
113 	}
114 	return nCount;
115 }
116 
117 // If you change this, also update SwXFrameEnumeration in unocoll.
118 SwFrmFmt* SwDoc::GetFlyNum( sal_uInt16 nIdx, FlyCntType eType )
119 {
120 	SwSpzFrmFmts& rFmts = *GetSpzFrmFmts();
121 	SwFrmFmt* pRetFmt = 0;
122 	sal_uInt16 nSize = rFmts.Count();
123 	const SwNodeIndex* pIdx;
124 	sal_uInt16 nCount = 0;
125 	for( sal_uInt16 i = 0; !pRetFmt && i < nSize; ++i )
126 	{
127 		SwFrmFmt* pFlyFmt = rFmts[ i ];
128 		if( RES_FLYFRMFMT == pFlyFmt->Which()
129 			&& 0 != ( pIdx = pFlyFmt->GetCntnt().GetCntntIdx() )
130 			&& pIdx->GetNodes().IsDocNodes()
131 			)
132 		{
133 			const SwNode* pNd = GetNodes()[ pIdx->GetIndex() + 1 ];
134 			switch( eType )
135 			{
136 			case FLYCNTTYPE_FRM:
137 				if( !pNd->IsNoTxtNode() && nIdx == nCount++)
138 					pRetFmt = pFlyFmt;
139 				break;
140 			case FLYCNTTYPE_GRF:
141 				if(pNd->IsGrfNode() && nIdx == nCount++ )
142 					pRetFmt = pFlyFmt;
143 				break;
144 			case FLYCNTTYPE_OLE:
145 				if(pNd->IsOLENode() && nIdx == nCount++)
146 					pRetFmt = pFlyFmt;
147 				break;
148 			default:
149 				if(nIdx == nCount++)
150 					pRetFmt = pFlyFmt;
151 			}
152 		}
153 	}
154 	return pRetFmt;
155 }
156 
157 Point lcl_FindAnchorLayPos( SwDoc& rDoc, const SwFmtAnchor& rAnch,
158 							const SwFrmFmt* pFlyFmt )
159 {
160 	Point aRet;
161 	if( rDoc.GetCurrentViewShell() )	//swmod 071107//swmod 071225
162 		switch( rAnch.GetAnchorId() )
163 		{
164         case FLY_AS_CHAR:
165 			if( pFlyFmt && rAnch.GetCntntAnchor() )
166 			{
167 				const SwFrm* pOld = ((SwFlyFrmFmt*)pFlyFmt)->GetFrm( &aRet, sal_False );
168 				if( pOld )
169 					aRet = pOld->Frm().Pos();
170 			}
171 			break;
172 
173         case FLY_AT_PARA:
174         case FLY_AT_CHAR: // LAYER_IMPL
175 			if( rAnch.GetCntntAnchor() )
176 			{
177 				const SwPosition *pPos = rAnch.GetCntntAnchor();
178 				const SwCntntNode* pNd = pPos->nNode.GetNode().GetCntntNode();
179 				const SwFrm* pOld = pNd ? pNd->getLayoutFrm( rDoc.GetCurrentLayout(), &aRet, 0, sal_False ) : 0;
180 				if( pOld )
181 					aRet = pOld->Frm().Pos();
182 			}
183 			break;
184 
185 		case FLY_AT_FLY: // LAYER_IMPL
186 			if( rAnch.GetCntntAnchor() )
187 			{
188 				const SwFlyFrmFmt* pFmt = (SwFlyFrmFmt*)rAnch.GetCntntAnchor()->
189 												nNode.GetNode().GetFlyFmt();
190 				const SwFrm* pOld = pFmt ? pFmt->GetFrm( &aRet, sal_False ) : 0;
191 				if( pOld )
192 					aRet = pOld->Frm().Pos();
193 			}
194 			break;
195 
196         case FLY_AT_PAGE:
197 			{
198 				sal_uInt16 nPgNum = rAnch.GetPageNum();
199 				const SwPageFrm *pPage = (SwPageFrm*)rDoc.GetCurrentLayout()->Lower();
200 				for( sal_uInt16 i = 1; (i <= nPgNum) && pPage; ++i,
201 									pPage = (const SwPageFrm*)pPage->GetNext() )
202 					if( i == nPgNum )
203 					{
204 						aRet = pPage->Frm().Pos();
205 						break;
206 					}
207 			}
208 			break;
209 		default:
210 			break;
211 		}
212 	return aRet;
213 }
214 
215 #define MAKEFRMS 0
216 #define IGNOREANCHOR 1
217 #define DONTMAKEFRMS 2
218 
219 sal_Int8 SwDoc::SetFlyFrmAnchor( SwFrmFmt& rFmt, SfxItemSet& rSet, sal_Bool bNewFrms )
220 {
221 	//Ankerwechsel sind fast immer in alle 'Richtungen' erlaubt.
222 	//Ausnahme: Absatz- bzw. Zeichengebundene Rahmen duerfen wenn sie in
223 	//Kopf-/Fusszeilen stehen nicht Seitengebunden werden.
224 	const SwFmtAnchor &rOldAnch = rFmt.GetAnchor();
225 	const RndStdIds nOld = rOldAnch.GetAnchorId();
226 
227 	SwFmtAnchor aNewAnch( (SwFmtAnchor&)rSet.Get( RES_ANCHOR ) );
228 	RndStdIds nNew = aNewAnch.GetAnchorId();
229 
230 	// ist der neue ein gueltiger Anker?
231 	if( !aNewAnch.GetCntntAnchor() && (FLY_AT_FLY == nNew ||
232         (FLY_AT_PARA == nNew) || (FLY_AS_CHAR == nNew) ||
233         (FLY_AT_CHAR == nNew) ))
234     {
235         return IGNOREANCHOR;
236     }
237 
238 	if( nOld == nNew )
239         return DONTMAKEFRMS;
240 
241 
242 	Point aOldAnchorPos( ::lcl_FindAnchorLayPos( *this, rOldAnch, &rFmt ));
243 	Point aNewAnchorPos( ::lcl_FindAnchorLayPos( *this, aNewAnch, 0 ));
244 
245 	//Die alten Frms vernichten. Dabei werden die Views implizit gehidet und
246 	//doppeltes hiden waere so eine art Show!
247 	rFmt.DelFrms();
248 
249     if ( FLY_AS_CHAR == nOld )
250 	{
251 		//Bei InCntnt's wird es spannend: Das TxtAttribut muss vernichtet
252 		//werden. Leider reisst dies neben den Frms auch noch das Format mit
253 		//in sein Grab. Um dass zu unterbinden loesen wir vorher die
254 		//Verbindung zwischen Attribut und Format.
255 		const SwPosition *pPos = rOldAnch.GetCntntAnchor();
256 		SwTxtNode *pTxtNode = pPos->nNode.GetNode().GetTxtNode();
257 		ASSERT( pTxtNode->HasHints(), "Missing FlyInCnt-Hint." );
258 		const xub_StrLen nIdx = pPos->nContent.GetIndex();
259         SwTxtAttr * const  pHnt =
260             pTxtNode->GetTxtAttrForCharAt( nIdx, RES_TXTATR_FLYCNT );
261 		ASSERT( pHnt && pHnt->Which() == RES_TXTATR_FLYCNT,
262 					"Missing FlyInCnt-Hint." );
263 		ASSERT( pHnt && pHnt->GetFlyCnt().GetFrmFmt() == &rFmt,
264 					"Wrong TxtFlyCnt-Hint." );
265         const_cast<SwFmtFlyCnt&>(pHnt->GetFlyCnt()).SetFlyFmt();
266 
267 		//Die Verbindung ist geloest, jetzt muss noch das Attribut vernichtet
268 		//werden.
269         pTxtNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIdx, nIdx );
270     }
271 
272 	//Endlich kann das Attribut gesetzt werden. Es muss das erste Attribut
273 	//sein; Undo depends on it!
274     rFmt.SetFmtAttr( aNewAnch );
275 
276 	//Positionskorrekturen
277 	const SfxPoolItem* pItem;
278 	switch( nNew )
279 	{
280     case FLY_AS_CHAR:
281 			//Wenn keine Positionsattribute hereinkommen, dann muss dafuer
282 			//gesorgt werden, das keine unerlaubte automatische Ausrichtung
283 			//bleibt.
284 		{
285 			const SwPosition *pPos = aNewAnch.GetCntntAnchor();
286 			SwTxtNode *pNd = pPos->nNode.GetNode().GetTxtNode();
287 			ASSERT( pNd, "Crsr steht nicht auf TxtNode." );
288 
289             SwFmtFlyCnt aFmt( static_cast<SwFlyFrmFmt*>(&rFmt) );
290             pNd->InsertItem( aFmt, pPos->nContent.GetIndex(), 0 );
291         }
292 
293 		if( SFX_ITEM_SET != rSet.GetItemState( RES_VERT_ORIENT, sal_False, &pItem ))
294 		{
295 			SwFmtVertOrient aOldV( rFmt.GetVertOrient() );
296 			sal_Bool bSet = sal_True;
297 			switch( aOldV.GetVertOrient() )
298 			{
299             case text::VertOrientation::LINE_TOP:     aOldV.SetVertOrient( text::VertOrientation::TOP );   break;
300             case text::VertOrientation::LINE_CENTER:  aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
301             case text::VertOrientation::LINE_BOTTOM:  aOldV.SetVertOrient( text::VertOrientation::BOTTOM); break;
302             case text::VertOrientation::NONE:         aOldV.SetVertOrient( text::VertOrientation::CENTER); break;
303 			default:
304 				bSet = sal_False;
305 			}
306 			if( bSet )
307 				rSet.Put( aOldV );
308 		}
309 		break;
310 
311     case FLY_AT_PARA:
312     case FLY_AT_CHAR: // LAYER_IMPL
313     case FLY_AT_FLY: // LAYER_IMPL
314     case FLY_AT_PAGE:
315 		{
316 			//Wenn keine Positionsattribute hereinschneien korrigieren wir
317 			//die Position so, dass die Dokumentkoordinaten des Flys erhalten
318 			//bleiben.
319 			//Chg: Wenn sich in den Positionsattributen lediglich die
320             //Ausrichtung veraendert (text::RelOrientation::FRAME vs. text::RelOrientation::PRTAREA), dann wird die
321 			//Position ebenfalls korrigiert.
322 			if( SFX_ITEM_SET != rSet.GetItemState( RES_HORI_ORIENT, sal_False, &pItem ))
323 				pItem = 0;
324 
325 			SwFmtHoriOrient aOldH( rFmt.GetHoriOrient() );
326 
327             if( text::HoriOrientation::NONE == aOldH.GetHoriOrient() && ( !pItem ||
328 				aOldH.GetPos() == ((SwFmtHoriOrient*)pItem)->GetPos() ))
329 			{
330                 SwTwips nPos = (FLY_AS_CHAR == nOld) ? 0 : aOldH.GetPos();
331 				nPos += aOldAnchorPos.X() - aNewAnchorPos.X();
332 
333 				if( pItem )
334 				{
335 					SwFmtHoriOrient* pH = (SwFmtHoriOrient*)pItem;
336 					aOldH.SetHoriOrient( pH->GetHoriOrient() );
337 					aOldH.SetRelationOrient( pH->GetRelationOrient() );
338 				}
339 				aOldH.SetPos( nPos );
340 				rSet.Put( aOldH );
341 			}
342 
343 			if( SFX_ITEM_SET != rSet.GetItemState( RES_VERT_ORIENT, sal_False, &pItem ))
344 				pItem = 0;
345 			SwFmtVertOrient aOldV( rFmt.GetVertOrient() );
346 
347             // OD 2004-05-14 #i28922# - correction: compare <aOldV.GetVertOrient()
348             // with <text::VertOrientation::NONE>
349             if( text::VertOrientation::NONE == aOldV.GetVertOrient() && (!pItem ||
350 				aOldV.GetPos() == ((SwFmtVertOrient*)pItem)->GetPos() ) )
351 			{
352                 SwTwips nPos = (FLY_AS_CHAR == nOld) ? 0 : aOldV.GetPos();
353 				nPos += aOldAnchorPos.Y() - aNewAnchorPos.Y();
354 				if( pItem )
355 				{
356 					SwFmtVertOrient* pV = (SwFmtVertOrient*)pItem;
357 					aOldV.SetVertOrient( pV->GetVertOrient() );
358 					aOldV.SetRelationOrient( pV->GetRelationOrient() );
359 				}
360 				aOldV.SetPos( nPos );
361 				rSet.Put( aOldV );
362 			}
363 		}
364 		break;
365 	default:
366 		break;
367 	}
368 
369 	if( bNewFrms )
370 		rFmt.MakeFrms();
371 
372     return MAKEFRMS;
373 }
374 
375 static bool
376 lcl_SetFlyFrmAttr(SwDoc & rDoc,
377         sal_Int8 (SwDoc::*pSetFlyFrmAnchor)(SwFrmFmt &, SfxItemSet &, sal_Bool),
378         SwFrmFmt & rFlyFmt, SfxItemSet & rSet)
379 {
380     // #i32968# Inserting columns in the frame causes MakeFrmFmt to put two
381     // objects of type SwUndoFrmFmt on the undo stack. We don't want them.
382     ::sw::UndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo());
383 
384 	//Ist das Ankerattribut dabei? Falls ja ueberlassen wir die Verarbeitung
385 	//desselben einer Spezialmethode. Sie Returnt sal_True wenn der Fly neu
386 	//erzeugt werden muss (z.B. weil ein Wechsel des FlyTyps vorliegt).
387     sal_Int8 const nMakeFrms =
388         (SFX_ITEM_SET == rSet.GetItemState( RES_ANCHOR, sal_False ))
389              ?  (rDoc.*pSetFlyFrmAnchor)( rFlyFmt, rSet, sal_False )
390              :  DONTMAKEFRMS;
391 
392 	const SfxPoolItem* pItem;
393 	SfxItemIter aIter( rSet );
394 	SfxItemSet aTmpSet( rDoc.GetAttrPool(), aFrmFmtSetRange );
395 	sal_uInt16 nWhich = aIter.GetCurItem()->Which();
396 	do {
397 		switch( nWhich )
398 		{
399 		case RES_FILL_ORDER:
400 		case RES_BREAK:
401 		case RES_PAGEDESC:
402 		case RES_CNTNT:
403 		case RES_FOOTER:
404             OSL_ENSURE(false, ":-) unknown Attribute for Fly.");
405 			// kein break;
406 		case RES_CHAIN:
407 			rSet.ClearItem( nWhich );
408 			break;
409 		case RES_ANCHOR:
410             if( DONTMAKEFRMS != nMakeFrms )
411 				break;
412 
413 		default:
414 			if( !IsInvalidItem( aIter.GetCurItem() ) && ( SFX_ITEM_SET !=
415 				rFlyFmt.GetAttrSet().GetItemState( nWhich, sal_True, &pItem ) ||
416 				*pItem != *aIter.GetCurItem() ))
417 				aTmpSet.Put( *aIter.GetCurItem() );
418 			break;
419 		}
420 
421 		if( aIter.IsAtEnd() )
422 			break;
423 
424 	} while( 0 != ( nWhich = aIter.NextItem()->Which() ) );
425 
426 	if( aTmpSet.Count() )
427         rFlyFmt.SetFmtAttr( aTmpSet );
428 
429     if( MAKEFRMS == nMakeFrms )
430 		rFlyFmt.MakeFrms();
431 
432     return aTmpSet.Count() || MAKEFRMS == nMakeFrms;
433 }
434 
435 void SwDoc::CheckForUniqueItemForLineFillNameOrIndex(SfxItemSet& rSet)
436 {
437     SdrModel* pDrawModel = GetOrCreateDrawModel();
438     SfxItemIter aIter(rSet);
439 
440     for(const SfxPoolItem* pItem = aIter.FirstItem(); pItem; pItem = aIter.NextItem())
441     {
442         const SfxPoolItem* pResult = pItem;
443 
444         switch(pItem->Which())
445         {
446             case XATTR_FILLBITMAP:
447             {
448                 pResult = static_cast< const XFillBitmapItem* >(pItem)->checkForUniqueItem(pDrawModel);
449                 break;
450             }
451             case XATTR_LINEDASH:
452             {
453                 pResult = static_cast< const XLineDashItem* >(pItem)->checkForUniqueItem(pDrawModel);
454                 break;
455             }
456             case XATTR_LINESTART:
457             {
458                 pResult = static_cast< const XLineStartItem* >(pItem)->checkForUniqueItem(pDrawModel);
459                 break;
460             }
461             case XATTR_LINEEND:
462             {
463                 pResult = static_cast< const XLineEndItem* >(pItem)->checkForUniqueItem(pDrawModel);
464                 break;
465             }
466             case XATTR_FILLGRADIENT:
467             {
468                 pResult = static_cast< const XFillGradientItem* >(pItem)->checkForUniqueItem(pDrawModel);
469                 break;
470             }
471             case XATTR_FILLFLOATTRANSPARENCE:
472             {
473                 pResult = static_cast< const XFillFloatTransparenceItem* >(pItem)->checkForUniqueItem(pDrawModel);
474                 break;
475             }
476             case XATTR_FILLHATCH:
477             {
478                 pResult = static_cast< const XFillHatchItem* >(pItem)->checkForUniqueItem(pDrawModel);
479                 break;
480             }
481         }
482 
483         if(pResult != pItem)
484         {
485             rSet.Put(*pResult);
486             delete pResult;
487         }
488     }
489 }
490 
491 sal_Bool SwDoc::SetFlyFrmAttr( SwFrmFmt& rFlyFmt, SfxItemSet& rSet )
492 {
493 	if( !rSet.Count() )
494 		return sal_False;
495 
496     ::std::auto_ptr<SwUndoFmtAttrHelper> pSaveUndo;
497 
498     if (GetIDocumentUndoRedo().DoesUndo())
499     {
500         GetIDocumentUndoRedo().ClearRedo(); // AppendUndo far below, so leave it
501         pSaveUndo.reset( new SwUndoFmtAttrHelper( rFlyFmt ) );
502     }
503 
504     //UUUU Need to check for unique item for DrawingLayer items of type NameOrIndex
505     // and evtl. correct that item to ensure unique names for that type. This call may
506     // modify/correct entries inside of the given SfxItemSet
507     CheckForUniqueItemForLineFillNameOrIndex(rSet);
508 
509     bool const bRet =
510         lcl_SetFlyFrmAttr(*this, &SwDoc::SetFlyFrmAnchor, rFlyFmt, rSet);
511 
512     if ( pSaveUndo.get() )
513     {
514         if ( pSaveUndo->GetUndo() )
515         {
516             GetIDocumentUndoRedo().AppendUndo( pSaveUndo->ReleaseUndo() );
517         }
518     }
519 
520 	SetModified();
521 
522     return bRet;
523 }
524 
525 // --> OD 2009-07-20 #i73249#
526 void SwDoc::SetFlyFrmTitle( SwFlyFrmFmt& rFlyFrmFmt,
527                             const String& sNewTitle )
528 {
529     if ( rFlyFrmFmt.GetObjTitle() == sNewTitle )
530     {
531         return;
532     }
533 
534     ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
535 
536     if (GetIDocumentUndoRedo().DoesUndo())
537     {
538         GetIDocumentUndoRedo().AppendUndo( new SwUndoFlyStrAttr( rFlyFrmFmt,
539                                           UNDO_FLYFRMFMT_TITLE,
540                                           rFlyFrmFmt.GetObjTitle(),
541                                           sNewTitle ) );
542     }
543 
544     rFlyFrmFmt.SetObjTitle( sNewTitle, true );
545 
546     SetModified();
547 }
548 
549 void SwDoc::SetFlyFrmDescription( SwFlyFrmFmt& rFlyFrmFmt,
550                                   const String& sNewDescription )
551 {
552     if ( rFlyFrmFmt.GetObjDescription() == sNewDescription )
553     {
554         return;
555     }
556 
557     ::sw::DrawUndoGuard const drawUndoGuard(GetIDocumentUndoRedo());
558 
559     if (GetIDocumentUndoRedo().DoesUndo())
560     {
561         GetIDocumentUndoRedo().AppendUndo( new SwUndoFlyStrAttr( rFlyFrmFmt,
562                                           UNDO_FLYFRMFMT_DESCRIPTION,
563                                           rFlyFrmFmt.GetObjDescription(),
564                                           sNewDescription ) );
565     }
566 
567     rFlyFrmFmt.SetObjDescription( sNewDescription, true );
568 
569     SetModified();
570 }
571 // <--
572 
573 sal_Bool SwDoc::SetFrmFmtToFly( SwFrmFmt& rFmt, SwFrmFmt& rNewFmt,
574 							SfxItemSet* pSet, sal_Bool bKeepOrient )
575 {
576 	sal_Bool bChgAnchor = sal_False, bFrmSz = sal_False;
577 
578 	const SwFmtFrmSize aFrmSz( rFmt.GetFrmSize() );
579 	const SwFmtVertOrient aVert( rFmt.GetVertOrient() );
580 	const SwFmtHoriOrient aHori( rFmt.GetHoriOrient() );
581 
582 	SwUndoSetFlyFmt* pUndo = 0;
583     bool const bUndo = GetIDocumentUndoRedo().DoesUndo();
584     if (bUndo)
585     {
586         pUndo = new SwUndoSetFlyFmt( rFmt, rNewFmt );
587         GetIDocumentUndoRedo().AppendUndo(pUndo);
588     }
589 
590     // #i32968# Inserting columns in the section causes MakeFrmFmt to put
591     // 2 objects of type SwUndoFrmFmt on the undo stack. We don't want them.
592     ::sw::UndoGuard const undoGuard(GetIDocumentUndoRedo());
593 
594 	//Erstmal die Spalten setzen, sonst gibts nix als Aerger mit dem
595 	//Set/Reset/Abgleich usw.
596 	const SfxPoolItem* pItem;
597 	if( SFX_ITEM_SET != rNewFmt.GetAttrSet().GetItemState( RES_COL ))
598         rFmt.ResetFmtAttr( RES_COL );
599 
600 	if( rFmt.DerivedFrom() != &rNewFmt )
601 	{
602 		rFmt.SetDerivedFrom( &rNewFmt );
603 
604 		// 1. wenn nicht automatisch -> ignorieren, sonst -> wech
605 		// 2. wech damit, MB!
606 		if( SFX_ITEM_SET == rNewFmt.GetAttrSet().GetItemState( RES_FRM_SIZE, sal_False ))
607 		{
608             rFmt.ResetFmtAttr( RES_FRM_SIZE );
609 			bFrmSz = sal_True;
610 		}
611 
612 		const SfxItemSet* pAsk = pSet;
613 		if( !pAsk ) pAsk = &rNewFmt.GetAttrSet();
614 		if( SFX_ITEM_SET == pAsk->GetItemState( RES_ANCHOR, sal_False, &pItem )
615 			&& ((SwFmtAnchor*)pItem)->GetAnchorId() !=
616 				rFmt.GetAnchor().GetAnchorId() )
617 		{
618             if( pSet )
619                 bChgAnchor = MAKEFRMS == SetFlyFrmAnchor( rFmt, *pSet, sal_False );
620 			else
621 			{
622 				//JP 23.04.98: muss den FlyFmt-Range haben, denn im SetFlyFrmAnchor
623 				//				werden Attribute in diesen gesetzt!
624 				SfxItemSet aFlySet( *rNewFmt.GetAttrSet().GetPool(),
625 									rNewFmt.GetAttrSet().GetRanges() );
626 				aFlySet.Put( *pItem );
627                 bChgAnchor = MAKEFRMS == SetFlyFrmAnchor( rFmt, aFlySet, sal_False);
628 			}
629         }
630 	}
631 
632 	//Hori und Vert nur dann resetten, wenn in der Vorlage eine
633 	//automatische Ausrichtung eingestellt ist, anderfalls den alten Wert
634 	//wieder hineinstopfen.
635 	//JP 09.06.98: beim Update der RahmenVorlage sollte der Fly NICHT
636 	//				seine Orientierng verlieren (diese wird nicht geupdatet!)
637     //OS: #96584# text::HoriOrientation::NONE and text::VertOrientation::NONE are allowed now
638 	if (!bKeepOrient)
639 	{
640         rFmt.ResetFmtAttr(RES_VERT_ORIENT);
641         rFmt.ResetFmtAttr(RES_HORI_ORIENT);
642 	}
643 
644     rFmt.ResetFmtAttr( RES_PRINT, RES_SURROUND );
645     rFmt.ResetFmtAttr( RES_LR_SPACE, RES_UL_SPACE );
646     rFmt.ResetFmtAttr( RES_BACKGROUND, RES_COL );
647     rFmt.ResetFmtAttr( RES_URL, RES_EDIT_IN_READONLY );
648 
649 	if( !bFrmSz )
650         rFmt.SetFmtAttr( aFrmSz );
651 
652 	if( bChgAnchor )
653 		rFmt.MakeFrms();
654 
655 	if( pUndo )
656         pUndo->DeRegisterFromFormat( rFmt );
657 
658 	SetModified();
659 
660 	return bChgAnchor;
661 }
662 
663 void SwDoc::GetGrfNms( const SwFlyFrmFmt& rFmt, String* pGrfName,
664 						String* pFltName ) const
665 {
666 	SwNodeIndex aIdx( *rFmt.GetCntnt().GetCntntIdx(), 1 );
667 	const SwGrfNode* pGrfNd = aIdx.GetNode().GetGrfNode();
668 	if( pGrfNd && pGrfNd->IsLinkedFile() )
669 		pGrfNd->GetFileFilterNms( pGrfName, pFltName );
670 }
671 
672 sal_Bool SwDoc::ChgAnchor( const SdrMarkList& _rMrkList,
673                            RndStdIds _eAnchorType,
674                            const sal_Bool _bSameOnly,
675                            const sal_Bool _bPosCorr )
676 {
677 	ASSERT( GetCurrentLayout(), "Ohne Layout geht gar nichts" );	//swmod 080218
678 
679     if ( !_rMrkList.GetMarkCount() ||
680          _rMrkList.GetMark( 0 )->GetMarkedSdrObj()->GetUpGroup() )
681     {
682         return false;
683     }
684 
685     GetIDocumentUndoRedo().StartUndo( UNDO_INSATTR, NULL );
686 
687 	sal_Bool bUnmark = sal_False;
688     for ( sal_uInt16 i = 0; i < _rMrkList.GetMarkCount(); ++i )
689 	{
690         SdrObject* pObj = _rMrkList.GetMark( i )->GetMarkedSdrObj();
691 		if ( !pObj->ISA(SwVirtFlyDrawObj) )
692 		{
693             SwDrawContact* pContact = static_cast<SwDrawContact*>(GetUserCall(pObj));
694 
695             // OD 27.06.2003 #108784# - consider, that drawing object has
696             // no user call. E.g.: a 'virtual' drawing object is disconnected by
697             // the anchor type change of the 'master' drawing object.
698             // Continue with next selected object and assert, if this isn't excepted.
699             if ( !pContact )
700             {
701 #ifdef DBG_UTIL
702                 bool bNoUserCallExcepted =
703                         pObj->ISA(SwDrawVirtObj) &&
704                         !static_cast<SwDrawVirtObj*>(pObj)->IsConnected();
705                 ASSERT( bNoUserCallExcepted, "SwDoc::ChgAnchor(..) - no contact at selected drawing object" );
706 #endif
707                 continue;
708             }
709 
710             // OD 2004-03-29 #i26791#
711             const SwFrm* pOldAnchorFrm = pContact->GetAnchorFrm( pObj );
712             const SwFrm* pNewAnchorFrm = pOldAnchorFrm;
713 
714             // --> OD 2006-03-01 #i54336#
715             // Instead of only keeping the index position for an as-character
716             // anchored object the complete <SwPosition> is kept, because the
717             // anchor index position could be moved, if the object again is
718             // anchored as character.
719 //            xub_StrLen nIndx = STRING_NOTFOUND;
720             const SwPosition* pOldAsCharAnchorPos( 0L );
721             const RndStdIds eOldAnchorType = pContact->GetAnchorId();
722             if ( !_bSameOnly && eOldAnchorType == FLY_AS_CHAR )
723             {
724                 pOldAsCharAnchorPos = new SwPosition( pContact->GetCntntAnchor() );
725             }
726             // <--
727 
728             if ( _bSameOnly )
729                 _eAnchorType = eOldAnchorType;
730 
731             SwFmtAnchor aNewAnch( _eAnchorType );
732             Rectangle aObjRect( pContact->GetAnchoredObj( pObj )->GetObjRect().SVRect() );
733             const Point aPt( aObjRect.TopLeft() );
734 
735             switch ( _eAnchorType )
736 			{
737             case FLY_AT_PARA:
738             case FLY_AT_CHAR:
739                 {
740                     const Point aNewPoint = pOldAnchorFrm &&
741                                             ( pOldAnchorFrm->IsVertical() ||
742                                               pOldAnchorFrm->IsRightToLeft() )
743                                             ? aObjRect.TopRight()
744                                             : aPt;
745 
746                     // OD 18.06.2003 #108784# - allow drawing objects in header/footer
747                     pNewAnchorFrm = ::FindAnchor( pOldAnchorFrm, aNewPoint, false );
748                     if ( pNewAnchorFrm->IsTxtFrm() && ((SwTxtFrm*)pNewAnchorFrm)->IsFollow() )
749                     {
750                         pNewAnchorFrm = ((SwTxtFrm*)pNewAnchorFrm)->FindMaster();
751                     }
752                     if ( pNewAnchorFrm->IsProtected() )
753                     {
754                         pNewAnchorFrm = 0;
755                     }
756                     else
757                     {
758                         SwPosition aPos( *((SwCntntFrm*)pNewAnchorFrm)->GetNode() );
759                         aNewAnch.SetType( _eAnchorType );
760                         aNewAnch.SetAnchor( &aPos );
761                     }
762                 }
763 				break;
764 
765 			case FLY_AT_FLY: // LAYER_IMPL
766 				{
767 					//Ausgehend von der linken oberen Ecke des Fly den
768 					//dichtesten SwFlyFrm suchen.
769 					SwFrm *pTxtFrm;
770 					{
771 						SwCrsrMoveState aState( MV_SETONLYTEXT );
772 						SwPosition aPos( GetNodes() );
773 						Point aPoint( aPt );
774 						aPoint.X() -= 1;
775 						GetCurrentLayout()->GetCrsrOfst( &aPos, aPoint, &aState );
776                         // OD 20.06.2003 #108784# - consider that drawing objects
777                         // can be in header/footer. Thus, <GetFrm()> by left-top-corner
778                         pTxtFrm = aPos.nNode.GetNode().
779                                         GetCntntNode()->getLayoutFrm( GetCurrentLayout(), &aPt, 0, sal_False );
780 					}
781 					const SwFrm *pTmp = ::FindAnchor( pTxtFrm, aPt );
782                     pNewAnchorFrm = pTmp->FindFlyFrm();
783                     if( pNewAnchorFrm && !pNewAnchorFrm->IsProtected() )
784 					{
785                         const SwFrmFmt *pTmpFmt = ((SwFlyFrm*)pNewAnchorFrm)->GetFmt();
786 						const SwFmtCntnt& rCntnt = pTmpFmt->GetCntnt();
787 						SwPosition aPos( *rCntnt.GetCntntIdx() );
788 						aNewAnch.SetAnchor( &aPos );
789 						break;
790 					}
791 
792                     aNewAnch.SetType( FLY_AT_PAGE );
793 					// no break
794 				}
795             case FLY_AT_PAGE:
796 				{
797                     pNewAnchorFrm = GetCurrentLayout()->Lower();
798                     while ( pNewAnchorFrm && !pNewAnchorFrm->Frm().IsInside( aPt ) )
799                         pNewAnchorFrm = pNewAnchorFrm->GetNext();
800                     if ( !pNewAnchorFrm )
801 						continue;
802 
803                     aNewAnch.SetPageNum( ((SwPageFrm*)pNewAnchorFrm)->GetPhyPageNum());
804 				}
805 				break;
806             case FLY_AS_CHAR:
807                 if( _bSameOnly )    // Positions/Groessenaenderung
808 				{
809                     if( !pOldAnchorFrm )
810                     {
811                         pContact->ConnectToLayout();
812                         pOldAnchorFrm = pContact->GetAnchorFrm();
813                     }
814                     ((SwTxtFrm*)pOldAnchorFrm)->Prepare();
815 				}
816 				else 			// Ankerwechsel
817 				{
818                     // OD 18.06.2003 #108784# - allow drawing objects in header/footer
819                     pNewAnchorFrm = ::FindAnchor( pOldAnchorFrm, aPt, false );
820                     if( pNewAnchorFrm->IsProtected() )
821 					{
822                         pNewAnchorFrm = 0;
823 						break;
824 					}
825 
826 					bUnmark = ( 0 != i );
827 					Point aPoint( aPt );
828 					aPoint.X() -= 1;	// nicht im DrawObj landen!!
829                     aNewAnch.SetType( FLY_AS_CHAR );
830                     SwPosition aPos( *((SwCntntFrm*)pNewAnchorFrm)->GetNode() );
831                     if ( pNewAnchorFrm->Frm().IsInside( aPoint ) )
832 					{
833 					// es muss ein TextNode gefunden werden, denn nur dort
834 					// ist ein inhaltsgebundenes DrawObjekt zu verankern
835 						SwCrsrMoveState aState( MV_SETONLYTEXT );
836 						GetCurrentLayout()->GetCrsrOfst( &aPos, aPoint, &aState );	//swmod 080218
837 					}
838 					else
839 					{
840 						SwCntntNode &rCNd = (SwCntntNode&)
841                             *((SwCntntFrm*)pNewAnchorFrm)->GetNode();
842                         if ( pNewAnchorFrm->Frm().Bottom() < aPt.Y() )
843 							rCNd.MakeStartIndex( &aPos.nContent );
844 						else
845 							rCNd.MakeEndIndex( &aPos.nContent );
846 					}
847 					aNewAnch.SetAnchor( &aPos );
848 					SetAttr( aNewAnch, *pContact->GetFmt() );
849                     // OD 2004-04-13 #i26791# - adjust vertical positioning to
850                     // 'center to baseline'
851                     SetAttr( SwFmtVertOrient( 0, text::VertOrientation::CENTER, text::RelOrientation::FRAME ), *pContact->GetFmt() );
852                     SwTxtNode *pNd = aPos.nNode.GetNode().GetTxtNode();
853                     ASSERT( pNd, "Cursor not positioned at TxtNode." );
854 
855                     SwFmtFlyCnt aFmt( pContact->GetFmt() );
856                     pNd->InsertItem( aFmt, aPos.nContent.GetIndex(), 0 );
857                 }
858                 break;
859 			default:
860 				ASSERT( !this, "unexpected AnchorId." );
861 			}
862 
863             if ( (FLY_AS_CHAR != _eAnchorType) &&
864                  pNewAnchorFrm &&
865                  ( !_bSameOnly || pNewAnchorFrm != pOldAnchorFrm ) )
866 			{
867                 // OD 2004-04-06 #i26791# - Direct object positioning no longer
868                 // needed. Apply of attributes (method call <SetAttr(..)>) takes
869                 // care of the invalidation of the object position.
870                 SetAttr( aNewAnch, *pContact->GetFmt() );
871                 if ( _bPosCorr )
872                 {
873                     // --> OD 2004-08-24 #i33313# - consider not connected
874                     // 'virtual' drawing objects
875                     if ( pObj->ISA(SwDrawVirtObj) &&
876                          !static_cast<SwDrawVirtObj*>(pObj)->IsConnected() )
877                     {
878                         SwRect aNewObjRect( aObjRect );
879                         static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( 0L ))
880                                         ->AdjustPositioningAttr( pNewAnchorFrm,
881                                                                  &aNewObjRect );
882 
883                     }
884                     else
885                     {
886                         static_cast<SwAnchoredDrawObject*>(pContact->GetAnchoredObj( pObj ))
887                                     ->AdjustPositioningAttr( pNewAnchorFrm );
888                     }
889                 }
890             }
891 
892             // --> OD 2006-03-01 #i54336#
893             if ( pNewAnchorFrm && pOldAsCharAnchorPos )
894 			{
895 				//Bei InCntnt's wird es spannend: Das TxtAttribut muss vernichtet
896 				//werden. Leider reisst dies neben den Frms auch noch das Format mit
897 				//in sein Grab. Um dass zu unterbinden loesen wir vorher die
898 				//Verbindung zwischen Attribut und Format.
899                 const xub_StrLen nIndx( pOldAsCharAnchorPos->nContent.GetIndex() );
900                 SwTxtNode* pTxtNode( pOldAsCharAnchorPos->nNode.GetNode().GetTxtNode() );
901                 ASSERT( pTxtNode, "<SwDoc::ChgAnchor(..)> - missing previous anchor text node for as-character anchored object" );
902                 ASSERT( pTxtNode->HasHints(), "Missing FlyInCnt-Hint." );
903                 SwTxtAttr * const pHnt =
904                     pTxtNode->GetTxtAttrForCharAt( nIndx, RES_TXTATR_FLYCNT );
905                 const_cast<SwFmtFlyCnt&>(pHnt->GetFlyCnt()).SetFlyFmt();
906 
907 				//Die Verbindung ist geloest, jetzt muss noch das Attribut vernichtet
908 				//werden.
909                 pTxtNode->DeleteAttributes( RES_TXTATR_FLYCNT, nIndx, nIndx );
910                 delete pOldAsCharAnchorPos;
911 			}
912             // <--
913 		}
914 	}
915 
916     GetIDocumentUndoRedo().EndUndo( UNDO_END, NULL );
917 	SetModified();
918 
919 	return bUnmark;
920 }
921 
922 
923 int SwDoc::Chainable( const SwFrmFmt &rSource, const SwFrmFmt &rDest )
924 {
925 	//Die Source darf noch keinen Follow haben.
926 	const SwFmtChain &rOldChain = rSource.GetChain();
927 	if ( rOldChain.GetNext() )
928 		return SW_CHAIN_SOURCE_CHAINED;
929 
930 	//Ziel darf natuerlich nicht gleich Source sein und es
931 	//darf keine geschlossene Kette entstehen.
932 	const SwFrmFmt *pFmt = &rDest;
933 	do {
934 		if( pFmt == &rSource )
935 			return SW_CHAIN_SELF;
936 		pFmt = pFmt->GetChain().GetNext();
937 	} while ( pFmt );
938 
939 	//Auch eine Verkettung von Innen nach aussen oder von aussen
940 	//nach innen ist nicht zulaessig.
941 	if( rDest.IsLowerOf( rSource ) || rSource .IsLowerOf( rDest ) )
942 		return SW_CHAIN_SELF;
943 
944 	//Das Ziel darf noch keinen Master haben.
945 	const SwFmtChain &rChain = rDest.GetChain();
946 	if( rChain.GetPrev() )
947 		return SW_CHAIN_IS_IN_CHAIN;
948 
949 	//Das Ziel muss leer sein.
950 	const SwNodeIndex* pCntIdx = rDest.GetCntnt().GetCntntIdx();
951 	if( !pCntIdx )
952 		return SW_CHAIN_NOT_FOUND;
953 
954 	SwNodeIndex aNxtIdx( *pCntIdx, 1 );
955 	const SwTxtNode* pTxtNd = aNxtIdx.GetNode().GetTxtNode();
956 	if( !pTxtNd )
957 		return SW_CHAIN_NOT_FOUND;
958 
959     const sal_uLong nFlySttNd = pCntIdx->GetIndex();
960 	if( 2 != ( pCntIdx->GetNode().EndOfSectionIndex() - nFlySttNd ) ||
961 		pTxtNd->GetTxt().Len() )
962 		return SW_CHAIN_NOT_EMPTY;
963 
964 	sal_uInt16 nArrLen = GetSpzFrmFmts()->Count();
965 	for( sal_uInt16 n = 0; n < nArrLen; ++n )
966 	{
967 		const SwFmtAnchor& rAnchor = (*GetSpzFrmFmts())[ n ]->GetAnchor();
968         sal_uLong nTstSttNd;
969         // OD 11.12.2003 #i20622# - to-frame anchored objects are allowed.
970         if ( ((rAnchor.GetAnchorId() == FLY_AT_PARA) ||
971               (rAnchor.GetAnchorId() == FLY_AT_CHAR)) &&
972 			 0 != rAnchor.GetCntntAnchor() &&
973 			 nFlySttNd <= ( nTstSttNd =
974 			 			rAnchor.GetCntntAnchor()->nNode.GetIndex() ) &&
975              nTstSttNd < nFlySttNd + 2 )
976 		{
977 			return SW_CHAIN_NOT_EMPTY;
978 		}
979 	}
980 
981 	//Auf die richtige Area muessen wir auch noch einen Blick werfen.
982 	//Beide Flys muessen im selben Bereich (Body, Head/Foot, Fly) sitzen
983 	//Wenn die Source nicht der selektierte Rahmen ist, so reicht es
984 	//Wenn ein passender gefunden wird (Der Wunsch kann z.B. von der API
985 	//kommen).
986 
987 	// both in the same fly, header, footer or on the page?
988 	const SwFmtAnchor &rSrcAnchor = rSource.GetAnchor(),
989 					  &rDstAnchor = rDest.GetAnchor();
990 	sal_uLong nEndOfExtras = GetNodes().GetEndOfExtras().GetIndex();
991 	sal_Bool bAllowed = sal_False;
992     if ( FLY_AT_PAGE == rSrcAnchor.GetAnchorId() )
993     {
994         if ( (FLY_AT_PAGE == rDstAnchor.GetAnchorId()) ||
995 			( rDstAnchor.GetCntntAnchor() &&
996 			  rDstAnchor.GetCntntAnchor()->nNode.GetIndex() > nEndOfExtras ))
997 			bAllowed = sal_True;
998 	}
999 	else if( rSrcAnchor.GetCntntAnchor() && rDstAnchor.GetCntntAnchor() )
1000 	{
1001 		const SwNodeIndex &rSrcIdx = rSrcAnchor.GetCntntAnchor()->nNode,
1002 						    &rDstIdx = rDstAnchor.GetCntntAnchor()->nNode;
1003 		const SwStartNode* pSttNd = 0;
1004 		if( rSrcIdx == rDstIdx ||
1005 			( !pSttNd &&
1006 				0 != ( pSttNd = rSrcIdx.GetNode().FindFlyStartNode() ) &&
1007 				pSttNd == rDstIdx.GetNode().FindFlyStartNode() ) ||
1008 			( !pSttNd &&
1009 				0 != ( pSttNd = rSrcIdx.GetNode().FindFooterStartNode() ) &&
1010 				pSttNd == rDstIdx.GetNode().FindFooterStartNode() ) ||
1011 			( !pSttNd &&
1012 				0 != ( pSttNd = rSrcIdx.GetNode().FindHeaderStartNode() ) &&
1013 				pSttNd == rDstIdx.GetNode().FindHeaderStartNode() ) ||
1014 			( !pSttNd && rDstIdx.GetIndex() > nEndOfExtras &&
1015 							rSrcIdx.GetIndex() > nEndOfExtras ))
1016 			bAllowed = sal_True;
1017 	}
1018 
1019 	return bAllowed ? SW_CHAIN_OK : SW_CHAIN_WRONG_AREA;
1020 }
1021 
1022 int SwDoc::Chain( SwFrmFmt &rSource, const SwFrmFmt &rDest )
1023 {
1024 	int nErr = Chainable( rSource, rDest );
1025 	if ( !nErr )
1026     {
1027         GetIDocumentUndoRedo().StartUndo( UNDO_CHAINE, NULL );
1028 
1029 		SwFlyFrmFmt& rDestFmt = (SwFlyFrmFmt&)rDest;
1030 
1031 		//Follow an den Master haengen.
1032 		SwFmtChain aChain = rDestFmt.GetChain();
1033 		aChain.SetPrev( &(SwFlyFrmFmt&)rSource );
1034 		SetAttr( aChain, rDestFmt );
1035 
1036 		SfxItemSet aSet( GetAttrPool(), RES_FRM_SIZE, RES_FRM_SIZE,
1037 										RES_CHAIN,  RES_CHAIN, 0 );
1038 
1039 		//Follow an den Master haengen.
1040 		aChain.SetPrev( &(SwFlyFrmFmt&)rSource );
1041 		SetAttr( aChain, rDestFmt );
1042 
1043 		//Master an den Follow haengen und dafuer sorgen, dass der Master
1044 		//eine fixierte Hoehe hat.
1045 		aChain = rSource.GetChain();
1046 		aChain.SetNext( &rDestFmt );
1047 		aSet.Put( aChain );
1048 
1049 		SwFmtFrmSize aSize( rSource.GetFrmSize() );
1050         if ( aSize.GetHeightSizeType() != ATT_FIX_SIZE )
1051 		{
1052 			SwFlyFrm *pFly = SwIterator<SwFlyFrm,SwFmt>::FirstElement( rSource );
1053 			if ( pFly )
1054 				aSize.SetHeight( pFly->Frm().Height() );
1055             aSize.SetHeightSizeType( ATT_FIX_SIZE );
1056 			aSet.Put( aSize );
1057 		}
1058 		SetAttr( aSet, rSource );
1059 
1060         GetIDocumentUndoRedo().EndUndo( UNDO_CHAINE, NULL );
1061     }
1062 	return nErr;
1063 }
1064 
1065 void SwDoc::Unchain( SwFrmFmt &rFmt )
1066 {
1067 	SwFmtChain aChain( rFmt.GetChain() );
1068 	if ( aChain.GetNext() )
1069     {
1070         GetIDocumentUndoRedo().StartUndo( UNDO_UNCHAIN, NULL );
1071 		SwFrmFmt *pFollow = aChain.GetNext();
1072 		aChain.SetNext( 0 );
1073 		SetAttr( aChain, rFmt );
1074 		aChain = pFollow->GetChain();
1075 		aChain.SetPrev( 0 );
1076 		SetAttr( aChain, *pFollow );
1077         GetIDocumentUndoRedo().EndUndo( UNDO_UNCHAIN, NULL );
1078     }
1079 }
1080 
1081 
1082 
1083