xref: /trunk/main/sw/source/core/layout/colfrm.cxx (revision d1af8f7d)
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 "cntfrm.hxx"
29 #include "doc.hxx"
30 
31 #include "hintids.hxx"
32 #include <editeng/ulspitem.hxx>
33 #include <editeng/lrspitem.hxx>
34 #include <fmtclds.hxx>
35 #include <fmtfordr.hxx>
36 #include <frmfmt.hxx>
37 #include <node.hxx>
38 #include "frmtool.hxx"
39 #include "colfrm.hxx"
40 #include "pagefrm.hxx"
41 #include "bodyfrm.hxx"	 // ColumnFrms jetzt mit BodyFrm
42 #include "rootfrm.hxx"   // wg. RemoveFtns
43 #include "sectfrm.hxx"	 // wg. FtnAtEnd-Flag
44 #include "switerator.hxx"
45 
46 // ftnfrm.cxx:
47 void lcl_RemoveFtns( SwFtnBossFrm* pBoss, sal_Bool bPageOnly, sal_Bool bEndNotes );
48 
49 
50 /*************************************************************************
51 |*
52 |*	SwColumnFrm::SwColumnFrm()
53 |*
54 |*	Ersterstellung		MA ??
55 |*	Letzte Aenderung	AMA 30. Oct 98
56 |*
57 |*************************************************************************/
SwColumnFrm(SwFrmFmt * pFmt,SwFrm * pSib)58 SwColumnFrm::SwColumnFrm( SwFrmFmt *pFmt, SwFrm* pSib ):
59 	SwFtnBossFrm( pFmt, pSib )
60 {
61     nType = FRMC_COLUMN;
62 	SwBodyFrm* pColBody = new SwBodyFrm( pFmt->GetDoc()->GetDfltFrmFmt(), pSib );
63 	pColBody->InsertBehind( this, 0 ); // ColumnFrms jetzt mit BodyFrm
64 	SetMaxFtnHeight( LONG_MAX );
65 }
66 
~SwColumnFrm()67 SwColumnFrm::~SwColumnFrm()
68 {
69 	SwFrmFmt *pFmt = GetFmt();
70 	SwDoc *pDoc;
71 	if ( !(pDoc = pFmt->GetDoc())->IsInDtor() && pFmt->IsLastDepend() )
72 	{
73 		//Ich bin der einzige, weg mit dem Format.
74 		//Vorher ummelden, damit die Basisklasse noch klarkommt.
75 		pDoc->GetDfltFrmFmt()->Add( this );
76 		pDoc->DelFrmFmt( pFmt );
77 	}
78 }
79 
80 /*************************************************************************
81 |*
82 |*	SwLayoutFrm::ChgColumns()
83 |*
84 |*	Ersterstellung		MA 11. Feb. 93
85 |*	Letzte Aenderung	MA 12. Oct. 98
86 |*
87 |*************************************************************************/
88 
lcl_RemoveColumns(SwLayoutFrm * pCont,sal_uInt16 nCnt)89 void MA_FASTCALL lcl_RemoveColumns( SwLayoutFrm *pCont, sal_uInt16 nCnt )
90 {
91 	ASSERT( pCont && pCont->Lower() && pCont->Lower()->IsColumnFrm(),
92 			"Keine Spalten zu entfernen." );
93 
94 	SwColumnFrm *pColumn = (SwColumnFrm*)pCont->Lower();
95 	::lcl_RemoveFtns( pColumn, sal_True, sal_True );
96 	while ( pColumn->GetNext() )
97 	{
98 		ASSERT( pColumn->GetNext()->IsColumnFrm(),
99 				"Nachbar von ColFrm kein ColFrm." );
100 		pColumn = (SwColumnFrm*)pColumn->GetNext();
101 	}
102 	for ( sal_uInt16 i = 0; i < nCnt; ++i )
103 	{
104 		SwColumnFrm *pTmp = (SwColumnFrm*)pColumn->GetPrev();
105 		pColumn->Cut();
106 		delete pColumn;	//Format wird ggf. im DTor mit vernichtet.
107 		pColumn = pTmp;
108 	}
109 }
110 
lcl_FindColumns(SwLayoutFrm * pLay,sal_uInt16 nCount)111 SwLayoutFrm * MA_FASTCALL lcl_FindColumns( SwLayoutFrm *pLay, sal_uInt16 nCount )
112 {
113 	SwFrm *pCol = pLay->Lower();
114 	if ( pLay->IsPageFrm() )
115 		pCol = ((SwPageFrm*)pLay)->FindBodyCont()->Lower();
116 
117 	if ( pCol && pCol->IsColumnFrm() )
118 	{
119 		SwFrm *pTmp = pCol;
120 		sal_uInt16 i;
121 		for ( i = 0; pTmp; pTmp = pTmp->GetNext(), ++i )
122 			/* do nothing */;
123 		return i == nCount ? (SwLayoutFrm*)pCol : 0;
124 	}
125 	return 0;
126 }
127 
128 
lcl_AddColumns(SwLayoutFrm * pCont,sal_uInt16 nCount)129 static sal_Bool lcl_AddColumns( SwLayoutFrm *pCont, sal_uInt16 nCount )
130 {
131 	SwDoc *pDoc = pCont->GetFmt()->GetDoc();
132 	const sal_Bool bMod = pDoc->IsModified();
133 
134 	//Format sollen soweit moeglich geshared werden. Wenn es also schon einen
135 	//Nachbarn mit den selben Spalteneinstellungen gibt, so koennen die
136 	//Spalten an die selben Formate gehaengt werden.
137 	//Der Nachbar kann ueber das Format gesucht werden, wer der Owner des Attributes
138 	//ist, ist allerdings vom Frametyp abhaengig.
139 	SwLayoutFrm *pAttrOwner = pCont;
140 	if ( pCont->IsBodyFrm() )
141 		pAttrOwner = pCont->FindPageFrm();
142 	SwLayoutFrm *pNeighbourCol = 0;
143 	SwIterator<SwLayoutFrm,SwFmt> aIter( *pAttrOwner->GetFmt() );
144 	SwLayoutFrm *pNeighbour = aIter.First();
145 
146 	sal_uInt16 nAdd = 0;
147 	SwFrm *pCol = pCont->Lower();
148 	if ( pCol && pCol->IsColumnFrm() )
149 		for ( nAdd = 1; pCol; pCol = pCol->GetNext(), ++nAdd )
150 			/* do nothing */;
151 	while ( pNeighbour )
152 	{
153 		if ( 0 != (pNeighbourCol = lcl_FindColumns( pNeighbour, nCount+nAdd )) &&
154 			 pNeighbourCol != pCont )
155 			break;
156 		pNeighbourCol = 0;
157 		pNeighbour = aIter.Next();
158 	}
159 
160 	sal_Bool bRet;
161 	SwTwips nMax = pCont->IsPageBodyFrm() ?
162 				   pCont->FindPageFrm()->GetMaxFtnHeight() : LONG_MAX;
163 	if ( pNeighbourCol )
164 	{
165 		bRet = sal_False;
166 		SwFrm *pTmp = pCont->Lower();
167 		while ( pTmp )
168 		{
169 			pTmp = pTmp->GetNext();
170 			pNeighbourCol = (SwLayoutFrm*)pNeighbourCol->GetNext();
171 		}
172 		for ( sal_uInt16 i = 0; i < nCount; ++i )
173 		{
174             SwColumnFrm *pTmpCol = new SwColumnFrm( pNeighbourCol->GetFmt(), pCont );
175             pTmpCol->SetMaxFtnHeight( nMax );
176             pTmpCol->InsertBefore( pCont, NULL );
177 			pNeighbourCol = (SwLayoutFrm*)pNeighbourCol->GetNext();
178 		}
179 	}
180 	else
181 	{
182 		bRet = sal_True;
183 		for ( sal_uInt16 i = 0; i < nCount; ++i )
184 		{
185 			SwFrmFmt *pFmt = pDoc->MakeFrmFmt( aEmptyStr, pDoc->GetDfltFrmFmt());
186 			SwColumnFrm *pTmp = new SwColumnFrm( pFmt, pCont );
187 			pTmp->SetMaxFtnHeight( nMax );
188 			pTmp->Paste( pCont );
189 		}
190 	}
191 
192 	if ( !bMod )
193 		pDoc->ResetModified();
194 	return bRet;
195 }
196 
197 /*-----------------21.09.99 15:42-------------------
198  * ChgColumns() adds or removes columns from a layoutframe.
199  * Normally, a layoutframe with a column attribut of 1 or 0 columns contains
200  * no columnframe. However, a sectionframe with "footnotes at the end" needs
201  * a columnframe. If the bChgFtn-flag is set, the columnframe will be inserted
202  * or remove, if necessary.
203  * --------------------------------------------------*/
204 
ChgColumns(const SwFmtCol & rOld,const SwFmtCol & rNew,const sal_Bool bChgFtn)205 void SwLayoutFrm::ChgColumns( const SwFmtCol &rOld, const SwFmtCol &rNew,
206 	const sal_Bool bChgFtn )
207 {
208 	if ( rOld.GetNumCols() <= 1 && rNew.GetNumCols() <= 1 && !bChgFtn )
209 		return;
210     // --> OD 2009-08-12 #i97379#
211     // If current lower is a no text frame, then columns are not allowed
212     if ( Lower() && Lower()->IsNoTxtFrm() &&
213          rNew.GetNumCols() > 1 )
214     {
215         return;
216     }
217     // <--
218 
219 	sal_uInt16 nNewNum,	nOldNum = 1;
220 	if( Lower() && Lower()->IsColumnFrm() )
221 	{
222 		SwFrm* pCol = Lower();
223 		while( 0 != (pCol=pCol->GetNext()) )
224 			++nOldNum;
225 	}
226 	nNewNum = rNew.GetNumCols();
227 	if( !nNewNum )
228 		++nNewNum;
229 	sal_Bool bAtEnd;
230 	if( IsSctFrm() )
231 		bAtEnd = ((SwSectionFrm*)this)->IsAnyNoteAtEnd();
232 	else
233 		bAtEnd = sal_False;
234 
235 	//Einstellung der Spaltenbreiten ist nur bei neuen Formaten notwendig.
236 	sal_Bool bAdjustAttributes = nOldNum != rOld.GetNumCols();
237 
238 	//Wenn die Spaltenanzahl unterschiedlich ist, wird der Inhalt
239 	//gesichert und restored.
240 	SwFrm *pSave = 0;
241 	if( nOldNum != nNewNum || bChgFtn )
242 	{
243 		SwDoc *pDoc = GetFmt()->GetDoc();
244 		ASSERT( pDoc, "FrmFmt gibt kein Dokument her." );
245 		// SaveCntnt wuerde auch den Inhalt der Fussnotencontainer aufsaugen
246 		// und im normalen Textfluss unterbringen.
247 		if( IsPageBodyFrm() )
248 			pDoc->GetCurrentLayout()->RemoveFtns( (SwPageFrm*)GetUpper(), sal_True, sal_False );	//swmod 080218
249 		pSave = ::SaveCntnt( this );
250 
251 		//Wenn Spalten existieren, jetzt aber eine Spaltenanzahl von
252 		//0 oder eins gewuenscht ist, so werden die Spalten einfach vernichtet.
253 		if ( nNewNum == 1 && !bAtEnd )
254 		{
255 			::lcl_RemoveColumns( this, nOldNum );
256 			if ( IsBodyFrm() )
257 				SetFrmFmt( pDoc->GetDfltFrmFmt() );
258 			else
259                 GetFmt()->SetFmtAttr( SwFmtFillOrder() );
260 			if ( pSave )
261                 ::RestoreCntnt( pSave, this, 0, true );
262             return;
263 		}
264 		if ( nOldNum == 1 )
265 		{
266 			if ( IsBodyFrm() )
267 				SetFrmFmt( pDoc->GetColumnContFmt() );
268 			else
269                 GetFmt()->SetFmtAttr( SwFmtFillOrder( ATT_LEFT_TO_RIGHT ) );
270 			if( !Lower() || !Lower()->IsColumnFrm() )
271 				--nOldNum;
272 		}
273 		if ( nOldNum > nNewNum )
274 		{
275 			::lcl_RemoveColumns( this, nOldNum - nNewNum );
276 			bAdjustAttributes = sal_True;
277 		}
278 		else if( nOldNum < nNewNum )
279 		{
280 			sal_uInt16 nAdd = nNewNum - nOldNum;
281 			bAdjustAttributes = lcl_AddColumns( this, nAdd );
282 		}
283 	}
284 
285 	if ( !bAdjustAttributes )
286 	{
287 		if ( rOld.GetLineWidth() 	!= rNew.GetLineWidth() ||
288 			 rOld.GetWishWidth()  	!= rNew.GetWishWidth() ||
289 			 rOld.IsOrtho()			!= rNew.IsOrtho() )
290 			bAdjustAttributes = sal_True;
291 		else
292 		{
293 			sal_uInt16 nCount = Min( rNew.GetColumns().Count(), rOld.GetColumns().Count() );
294 			for ( sal_uInt16 i = 0; i < nCount; ++i )
295 				if ( !(*rOld.GetColumns()[i] == *rNew.GetColumns()[i]) )
296 				{
297 					bAdjustAttributes = sal_True;
298 					break;
299 				}
300 		}
301 	}
302 
303 	//Sodele, jetzt koennen die Spalten bequem eingestellt werden.
304 	AdjustColumns( &rNew, bAdjustAttributes );
305 
306 	//Erst jetzt den Inhalt restaurieren. Ein frueheres Restaurieren wuerde
307 	//unnuetzte Aktionen beim Einstellen zur Folge haben.
308 	if ( pSave )
309 	{
310 		ASSERT( Lower() && Lower()->IsLayoutFrm() &&
311 				((SwLayoutFrm*)Lower())->Lower() &&
312 				((SwLayoutFrm*)Lower())->Lower()->IsLayoutFrm(),
313 				"Gesucht: Spaltenbody (Tod oder Lebend)." );   // ColumnFrms jetzt mit BodyFrm
314         ::RestoreCntnt( pSave, (SwLayoutFrm*)((SwLayoutFrm*)Lower())->Lower(), 0, true );
315     }
316 }
317 
318 /*************************************************************************
319 |*
320 |*	SwLayoutFrm::AdjustColumns()
321 |*
322 |*	Ersterstellung		MA 19. Jan. 99
323 |*	Letzte Aenderung	MA 19. Jan. 99
324 |*
325 |*************************************************************************/
326 
AdjustColumns(const SwFmtCol * pAttr,sal_Bool bAdjustAttributes)327 void SwLayoutFrm::AdjustColumns( const SwFmtCol *pAttr, sal_Bool bAdjustAttributes )
328 {
329 	if( !Lower()->GetNext() )
330 	{
331 		Lower()->ChgSize( Prt().SSize() );
332 		return;
333 	}
334 
335     const sal_Bool bVert = IsVertical();
336     //Badaa: 2008-04-18 * Support for Classical Mongolian Script (SCMS) joint with Jiayanmin
337 	SwRectFn fnRect = bVert ? ( IsVertLR() ? fnRectVertL2R : fnRectVert ) : fnRectHori;
338 
339 	//Ist ein Pointer da, oder sollen wir die Attribute einstellen,
340 	//so stellen wir auf jeden Fall die Spaltenbreiten ein. Andernfalls
341 	//checken wir, ob eine Einstellung notwendig ist.
342 	if ( !pAttr )
343 	{
344 		pAttr = &GetFmt()->GetCol();
345 		if ( !bAdjustAttributes )
346 		{
347             long nAvail = (Prt().*fnRect->fnGetWidth)();
348 			for ( SwLayoutFrm *pCol = (SwLayoutFrm*)Lower();
349 				  pCol;
350 				  pCol = (SwLayoutFrm*)pCol->GetNext() )
351                 nAvail -= (pCol->Frm().*fnRect->fnGetWidth)();
352 			if ( !nAvail )
353 				return;
354 		}
355 	}
356 
357 	//Sodele, jetzt koennen die Spalten bequem eingestellt werden.
358 	//Die Breiten werden mitgezaehlt, damit wir dem letzten den Rest geben
359 	//koennen.
360     SwTwips nAvail = (Prt().*fnRect->fnGetWidth)();
361     const sal_Bool bLine = pAttr->GetLineAdj() != COLADJ_NONE;
362     const sal_uInt16 nMin = bLine ? sal_uInt16( 20 + ( pAttr->GetLineWidth() / 2) ) : 0;
363 
364     const sal_Bool bR2L = IsRightToLeft();
365     SwFrm *pCol = bR2L ? GetLastLower() : Lower();
366 
367     // --> FME 2004-07-16 #i27399#
368     // bOrtho means we have to adjust the column frames manually. Otherwise
369     // we may use the values returned by CalcColWidth:
370     const sal_Bool bOrtho = pAttr->IsOrtho() && pAttr->GetNumCols() > 0;
371     long nGutter = 0;
372     // <--
373 
374     for ( sal_uInt16 i = 0; i < pAttr->GetNumCols() && pCol; ++i ) //i118878, value returned by GetNumCols() can't be trusted
375 	{
376         if( !bOrtho )
377         {
378             const SwTwips nWidth = i == (pAttr->GetNumCols() - 1) ?
379                                    nAvail :
380                                    pAttr->CalcColWidth( i, sal_uInt16( (Prt().*fnRect->fnGetWidth)() ) );
381 
382             const Size aColSz = bVert ?
383                                 Size( Prt().Width(), nWidth ) :
384                                 Size( nWidth, Prt().Height() );
385 
386             pCol->ChgSize( aColSz );
387 
388             // Hierdurch werden die ColumnBodyFrms von Seitenspalten angepasst und
389             // ihr bFixHeight-Flag wird gesetzt, damit sie nicht schrumpfen/wachsen.
390             // Bei Rahmenspalten hingegen soll das Flag _nicht_ gesetzt werden,
391             // da BodyFrms in Rahmenspalten durchaus wachsen/schrumpfen duerfen.
392             if( IsBodyFrm() )
393                 ((SwLayoutFrm*)pCol)->Lower()->ChgSize( aColSz );
394 
395             nAvail -= nWidth;
396         }
397 
398         if ( bOrtho || bAdjustAttributes )
399 		{
400             const SwColumn *pC = pAttr->GetColumns()[i];
401             const SwAttrSet* pSet = pCol->GetAttrSet();
402 			SvxLRSpaceItem aLR( pSet->GetLRSpace() );
403 
404             //Damit die Trennlinien Platz finden, muessen sie hier
405 			//Beruecksichtigung finden. Ueberall wo zwei Spalten aufeinanderstossen
406 			//wird jeweils rechts bzw. links ein Sicherheitsabstand von 20 plus
407 			//der halben Penbreite einkalkuliert.
408             const sal_uInt16 nLeft = pC->GetLeft();
409             const sal_uInt16 nRight = pC->GetRight();
410 
411             aLR.SetLeft ( nLeft );
412             aLR.SetRight( nRight );
413 
414             if ( bLine )
415             {
416                 if ( i == 0 )
417                 {
418                     aLR.SetRight( Max( nRight, nMin ) );
419                 }
420                 else if ( i == pAttr->GetNumCols() - 1 )
421                 {
422                     aLR.SetLeft ( Max( nLeft, nMin ) );
423                 }
424                 else
425                 {
426                     aLR.SetLeft ( Max( nLeft,  nMin ) );
427                     aLR.SetRight( Max( nRight, nMin ) );
428                 }
429             }
430 
431             if ( bAdjustAttributes )
432             {
433     			SvxULSpaceItem aUL( pSet->GetULSpace() );
434                 aUL.SetUpper( pC->GetUpper());
435                 aUL.SetLower( pC->GetLower());
436 
437                 ((SwLayoutFrm*)pCol)->GetFmt()->SetFmtAttr( aLR );
438                 ((SwLayoutFrm*)pCol)->GetFmt()->SetFmtAttr( aUL );
439             }
440 
441             nGutter += aLR.GetLeft() + aLR.GetRight();
442 		}
443 
444         pCol = bR2L ? pCol->GetPrev() : pCol->GetNext();
445     }
446 
447     if( bOrtho )
448     {
449         long nInnerWidth = ( nAvail - nGutter ) / pAttr->GetNumCols();
450         pCol = Lower();
451         for( sal_uInt16 i = 0; i < pAttr->GetNumCols() && pCol; pCol = pCol->GetNext(), ++i ) //i118878, value returned by GetNumCols() can't be trusted
452         {
453             SwTwips nWidth;
454             if ( i == pAttr->GetNumCols() - 1 )
455                 nWidth = nAvail;
456             else
457             {
458                 SvxLRSpaceItem aLR( pCol->GetAttrSet()->GetLRSpace() );
459                 nWidth = nInnerWidth + aLR.GetLeft() + aLR.GetRight();
460             }
461             if( nWidth < 0 )
462                 nWidth = 0;
463 
464             const Size aColSz = bVert ?
465                                 Size( Prt().Width(), nWidth ) :
466                                 Size( nWidth, Prt().Height() );
467 
468             pCol->ChgSize( aColSz );
469 
470             if( IsBodyFrm() )
471                 ((SwLayoutFrm*)pCol)->Lower()->ChgSize( aColSz );
472 
473             nAvail -= nWidth;
474         }
475     }
476 }
477 
478 
479 
480 
481 
482