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