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