xref: /trunk/main/sw/source/core/layout/colfrm.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
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