1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <libxml/xmlwriter.h>
22 #include <editeng/ulspitem.hxx>
23 #include <osl/diagnose.h>
24 #include <fmtclds.hxx>
25 #include <fmtfordr.hxx>
28 #include <frmtool.hxx>
30 #include <pagefrm.hxx>
31 #include <bodyfrm.hxx>
32 #include <rootfrm.hxx>
33 #include <sectfrm.hxx>
36 #include <IDocumentState.hxx>
37 #include <IDocumentLayoutAccess.hxx>
38 #include <IDocumentUndoRedo.hxx>
40 SwColumnFrame::SwColumnFrame( SwFrameFormat
*pFormat
, SwFrame
* pSib
):
41 SwFootnoteBossFrame( pFormat
, pSib
)
43 mnFrameType
= SwFrameType::Column
;
44 SwBodyFrame
* pColBody
= new SwBodyFrame( pFormat
->GetDoc()->GetDfltFrameFormat(), pSib
);
45 pColBody
->InsertBehind( this, nullptr ); // ColumnFrames now with BodyFrame
46 SetMaxFootnoteHeight( LONG_MAX
);
49 void SwColumnFrame::DestroyImpl()
51 SwFrameFormat
*pFormat
= GetFormat();
53 if ( !(pDoc
= pFormat
->GetDoc())->IsInDtor() && pFormat
->HasOnlyOneListener() )
55 //I'm the only one, delete the format.
56 //Get default format before, so the base class can cope with it.
57 pDoc
->GetDfltFrameFormat()->Add( this );
58 // tdf#134009, like #i32968# avoid SwUndoFrameFormatDelete creation,
59 // the format is owned by the SwColumnFrame, see lcl_AddColumns()
60 ::sw::UndoGuard
const ug(pDoc
->GetIDocumentUndoRedo());
61 pDoc
->DelFrameFormat( pFormat
);
64 SwFootnoteBossFrame::DestroyImpl();
67 SwColumnFrame::~SwColumnFrame()
71 void SwColumnFrame::dumpAsXml(xmlTextWriterPtr writer
) const
73 (void)xmlTextWriterStartElement(writer
, reinterpret_cast<const xmlChar
*>("column"));
74 dumpAsXmlAttributes(writer
);
76 (void)xmlTextWriterStartElement(writer
, BAD_CAST("infos"));
77 dumpInfosAsXml(writer
);
78 (void)xmlTextWriterEndElement(writer
);
79 dumpChildrenAsXml(writer
);
81 (void)xmlTextWriterEndElement(writer
);
84 static void lcl_RemoveColumns( SwLayoutFrame
*pCont
, sal_uInt16 nCnt
)
86 OSL_ENSURE( pCont
&& pCont
->Lower() && pCont
->Lower()->IsColumnFrame(),
87 "no columns to remove." );
89 SwColumnFrame
*pColumn
= static_cast<SwColumnFrame
*>(pCont
->Lower());
90 sw_RemoveFootnotes( pColumn
, true, true );
91 while ( pColumn
->GetNext() )
93 OSL_ENSURE( pColumn
->GetNext()->IsColumnFrame(),
94 "neighbor of ColumnFrame is no ColumnFrame." );
95 pColumn
= static_cast<SwColumnFrame
*>(pColumn
->GetNext());
97 for ( sal_uInt16 i
= 0; i
< nCnt
; ++i
)
99 SwColumnFrame
*pTmp
= static_cast<SwColumnFrame
*>(pColumn
->GetPrev());
101 SwFrame::DestroyFrame(pColumn
); //format is going to be destroyed in the DTor if needed.
106 static SwLayoutFrame
* lcl_FindColumns( SwLayoutFrame
*pLay
, sal_uInt16 nCount
)
108 SwFrame
*pCol
= pLay
->Lower();
109 if ( pLay
->IsPageFrame() )
110 pCol
= static_cast<SwPageFrame
*>(pLay
)->FindBodyCont()->Lower();
112 if ( pCol
&& pCol
->IsColumnFrame() )
114 SwFrame
*pTmp
= pCol
;
116 for ( i
= 0; pTmp
; pTmp
= pTmp
->GetNext(), ++i
)
118 return i
== nCount
? static_cast<SwLayoutFrame
*>(pCol
) : nullptr;
123 static bool lcl_AddColumns( SwLayoutFrame
*pCont
, sal_uInt16 nCount
)
125 SwDoc
*pDoc
= pCont
->GetFormat()->GetDoc();
126 const bool bMod
= pDoc
->getIDocumentState().IsModified();
128 //Formats should be shared whenever possible. If a neighbour already has
129 //the same column settings we can add them to the same format.
130 //The neighbour can be searched using the format, however the owner of the
131 //attribute depends on the frame type.
132 SwLayoutFrame
*pAttrOwner
= pCont
;
133 if ( pCont
->IsBodyFrame() )
134 pAttrOwner
= pCont
->FindPageFrame();
135 SwLayoutFrame
*pNeighbourCol
= nullptr;
136 SwIterator
<SwLayoutFrame
,SwFormat
> aIter( *pAttrOwner
->GetFormat() );
137 SwLayoutFrame
*pNeighbour
= aIter
.First();
140 SwFrame
*pCol
= pCont
->Lower();
141 if ( pCol
&& pCol
->IsColumnFrame() )
142 for ( nAdd
= 1; pCol
; pCol
= pCol
->GetNext(), ++nAdd
)
146 if ( nullptr != (pNeighbourCol
= lcl_FindColumns( pNeighbour
, nCount
+nAdd
)) &&
147 pNeighbourCol
!= pCont
)
149 pNeighbourCol
= nullptr;
150 pNeighbour
= aIter
.Next();
154 SwTwips nMax
= pCont
->IsPageBodyFrame() ?
155 pCont
->FindPageFrame()->GetMaxFootnoteHeight() : LONG_MAX
;
159 SwFrame
*pTmp
= pCont
->Lower();
162 pTmp
= pTmp
->GetNext();
163 pNeighbourCol
= static_cast<SwLayoutFrame
*>(pNeighbourCol
->GetNext());
165 for ( sal_uInt16 i
= 0; i
< nCount
; ++i
)
167 SwColumnFrame
*pTmpCol
= new SwColumnFrame( pNeighbourCol
->GetFormat(), pCont
);
168 pTmpCol
->SetMaxFootnoteHeight( nMax
);
169 pTmpCol
->InsertBefore( pCont
, nullptr );
170 pNeighbourCol
= static_cast<SwLayoutFrame
*>(pNeighbourCol
->GetNext());
176 // tdf#103359, like #i32968# Inserting columns in the section causes MakeFrameFormat to put
177 // nCount objects of type SwUndoFrameFormat on the undo stack. We don't want them.
178 ::sw::UndoGuard
const undoGuard(pDoc
->GetIDocumentUndoRedo());
179 for ( sal_uInt16 i
= 0; i
< nCount
; ++i
)
181 SwFrameFormat
*pFormat
= pDoc
->MakeFrameFormat(OUString(), pDoc
->GetDfltFrameFormat());
182 SwColumnFrame
*pTmp
= new SwColumnFrame( pFormat
, pCont
);
183 pTmp
->SetMaxFootnoteHeight( nMax
);
184 pTmp
->Paste( pCont
);
189 pDoc
->getIDocumentState().ResetModified();
193 /** add or remove columns from a layoutframe.
195 * Normally, a layoutframe with a column attribute of 1 or 0 columns contains
196 * no columnframe. However, a sectionframe with "footnotes at the end" needs
201 * @param bChgFootnote if true, the columnframe will be inserted or removed, if necessary.
203 void SwLayoutFrame::ChgColumns( const SwFormatCol
&rOld
, const SwFormatCol
&rNew
,
204 const bool bChgFootnote
)
206 if ( rOld
.GetNumCols() <= 1 && rNew
.GetNumCols() <= 1 && !bChgFootnote
)
209 // If current lower is a no text frame, then columns are not allowed
210 if ( Lower() && Lower()->IsNoTextFrame() &&
211 rNew
.GetNumCols() > 1 )
216 sal_uInt16 nNewNum
, nOldNum
= 1;
217 if( Lower() && Lower()->IsColumnFrame() )
219 SwFrame
* pCol
= Lower();
220 while( nullptr != (pCol
=pCol
->GetNext()) )
223 nNewNum
= rNew
.GetNumCols();
228 bAtEnd
= static_cast<SwSectionFrame
*>(this)->IsAnyNoteAtEnd();
232 //Setting the column width is only needed for new formats.
233 bool bAdjustAttributes
= nOldNum
!= rOld
.GetNumCols();
235 //The content is saved and restored if the column count is different.
236 SwFrame
*pSave
= nullptr;
237 if( nOldNum
!= nNewNum
|| bChgFootnote
)
239 SwDoc
*pDoc
= GetFormat()->GetDoc();
240 OSL_ENSURE( pDoc
, "FrameFormat doesn't return a document." );
241 // SaveContent would also suck up the content of the footnote container
242 // and store it within the normal text flow.
243 if( IsPageBodyFrame() )
244 pDoc
->getIDocumentLayoutAccess().GetCurrentLayout()->RemoveFootnotes( static_cast<SwPageFrame
*>(GetUpper()) );
245 pSave
= ::SaveContent( this );
247 //If columns exist, they get deleted if a column count of 0 or 1 is requested.
248 if ( nNewNum
== 1 && !bAtEnd
)
250 ::lcl_RemoveColumns( this, nOldNum
);
252 SetFrameFormat( pDoc
->GetDfltFrameFormat() );
254 GetFormat()->SetFormatAttr( SwFormatFillOrder() );
256 ::RestoreContent( pSave
, this, nullptr );
262 SetFrameFormat( pDoc
->GetColumnContFormat() );
264 GetFormat()->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT
) );
265 if( !Lower() || !Lower()->IsColumnFrame() )
268 if ( nOldNum
> nNewNum
)
270 ::lcl_RemoveColumns( this, nOldNum
- nNewNum
);
271 bAdjustAttributes
= true;
273 else if( nOldNum
< nNewNum
)
275 sal_uInt16 nAdd
= nNewNum
- nOldNum
;
276 bAdjustAttributes
= lcl_AddColumns( this, nAdd
);
280 if ( !bAdjustAttributes
)
282 if ( rOld
.GetLineWidth() != rNew
.GetLineWidth() ||
283 rOld
.GetWishWidth() != rNew
.GetWishWidth() ||
284 rOld
.IsOrtho() != rNew
.IsOrtho() )
285 bAdjustAttributes
= true;
288 const size_t nCount
= std::min( rNew
.GetColumns().size(), rOld
.GetColumns().size() );
289 for ( size_t i
= 0; i
< nCount
; ++i
)
290 if ( !(rOld
.GetColumns()[i
] == rNew
.GetColumns()[i
]) )
292 bAdjustAttributes
= true;
298 //The columns can now be easily adjusted.
299 AdjustColumns( &rNew
, bAdjustAttributes
);
301 //Don't restore the content before. An earlier restore would trigger useless
302 //actions during setup.
305 OSL_ENSURE( Lower() && Lower()->IsLayoutFrame() &&
306 static_cast<SwLayoutFrame
*>(Lower())->Lower() &&
307 static_cast<SwLayoutFrame
*>(Lower())->Lower()->IsLayoutFrame(),
308 "no column body." ); // ColumnFrames contain BodyFrames
309 ::RestoreContent( pSave
, static_cast<SwLayoutFrame
*>(static_cast<SwLayoutFrame
*>(Lower())->Lower()), nullptr );
313 void SwLayoutFrame::AdjustColumns( const SwFormatCol
*pAttr
, bool bAdjustAttributes
)
315 if( !Lower()->GetNext() )
317 Lower()->ChgSize( getFramePrintArea().SSize() );
321 const bool bVert
= IsVertical();
323 SwRectFn fnRect
= bVert
? ( IsVertLR() ? (IsVertLRBT() ? fnRectVertL2RB2T
: fnRectVertL2R
) : fnRectVert
) : fnRectHori
;
325 //If we have a pointer or we have to configure an attribute, we set the
326 //column widths in any case. Otherwise we check if a configuration is needed.
329 pAttr
= &GetFormat()->GetCol();
330 if ( !bAdjustAttributes
)
332 tools::Long nAvail
= (getFramePrintArea().*fnRect
->fnGetWidth
)();
333 for ( SwLayoutFrame
*pCol
= static_cast<SwLayoutFrame
*>(Lower());
335 pCol
= static_cast<SwLayoutFrame
*>(pCol
->GetNext()) )
336 nAvail
-= (pCol
->getFrameArea().*fnRect
->fnGetWidth
)();
342 //The columns can now be easily adjusted.
343 //The widths get counted so we can give the reminder to the last one.
344 SwTwips nAvail
= (getFramePrintArea().*fnRect
->fnGetWidth
)();
345 const bool bLine
= pAttr
->GetLineAdj() != COLADJ_NONE
;
346 const sal_uInt16 nMin
= bLine
? sal_uInt16( 20 + ( pAttr
->GetLineWidth() / 2) ) : 0;
348 const bool bR2L
= IsRightToLeft();
349 SwFrame
*pCol
= bR2L
? GetLastLower() : Lower();
352 // bOrtho means we have to adjust the column frames manually. Otherwise
353 // we may use the values returned by CalcColWidth:
354 const bool bOrtho
= pAttr
->IsOrtho() && pAttr
->GetNumCols() > 0;
355 tools::Long nGutter
= 0;
357 for ( sal_uInt16 i
= 0; i
< pAttr
->GetNumCols() && pCol
; ++i
) //i118878, value returned by GetNumCols() can't be trusted
361 const SwTwips nWidth
= i
== (pAttr
->GetNumCols() - 1) ?
363 pAttr
->CalcColWidth( i
, sal_uInt16( (getFramePrintArea().*fnRect
->fnGetWidth
)() ) );
365 const Size aColSz
= bVert
?
366 Size( getFramePrintArea().Width(), nWidth
) :
367 Size( nWidth
, getFramePrintArea().Height() );
369 pCol
->ChgSize( aColSz
);
371 // With this, the ColumnBodyFrames from page columns gets adjusted and
372 // their bFixHeight flag is set so they won't shrink/grow.
373 // Don't use the flag with frame columns because BodyFrames in frame
374 // columns can grow/shrink.
376 static_cast<SwLayoutFrame
*>(pCol
)->Lower()->ChgSize( aColSz
);
381 if ( bOrtho
|| bAdjustAttributes
)
383 const SwColumn
*pC
= &pAttr
->GetColumns()[i
];
384 const SwAttrSet
* pSet
= pCol
->GetAttrSet();
385 SvxLRSpaceItem
aLR( pSet
->GetLRSpace() );
387 //In order to have enough space for the separation lines, we have to
388 //take them into account here. Every time two columns meet we
389 //calculate a clearance of 20 + half the pen width on the left or
390 //right side, respectively.
391 const sal_uInt16 nLeft
= pC
->GetLeft();
392 const sal_uInt16 nRight
= pC
->GetRight();
394 aLR
.SetLeft ( nLeft
);
395 aLR
.SetRight( nRight
);
401 aLR
.SetRight( std::max( nRight
, nMin
) );
403 else if ( i
== pAttr
->GetNumCols() - 1 )
405 aLR
.SetLeft ( std::max( nLeft
, nMin
) );
409 aLR
.SetLeft ( std::max( nLeft
, nMin
) );
410 aLR
.SetRight( std::max( nRight
, nMin
) );
414 if ( bAdjustAttributes
)
416 SvxULSpaceItem
aUL( pSet
->GetULSpace() );
420 static_cast<SwLayoutFrame
*>(pCol
)->GetFormat()->SetFormatAttr( aLR
);
421 static_cast<SwLayoutFrame
*>(pCol
)->GetFormat()->SetFormatAttr( aUL
);
424 nGutter
+= aLR
.GetLeft() + aLR
.GetRight();
427 pCol
= bR2L
? pCol
->GetPrev() : pCol
->GetNext();
433 tools::Long nInnerWidth
= ( nAvail
- nGutter
) / pAttr
->GetNumCols();
435 for( sal_uInt16 i
= 0; i
< pAttr
->GetNumCols() && pCol
; pCol
= pCol
->GetNext(), ++i
) //i118878, value returned by GetNumCols() can't be trusted
438 if ( i
== pAttr
->GetNumCols() - 1 )
442 SvxLRSpaceItem
aLR( pCol
->GetAttrSet()->GetLRSpace() );
443 nWidth
= nInnerWidth
+ aLR
.GetLeft() + aLR
.GetRight();
448 const Size aColSz
= bVert
?
449 Size( getFramePrintArea().Width(), nWidth
) :
450 Size( nWidth
, getFramePrintArea().Height() );
452 pCol
->ChgSize( aColSz
);
455 static_cast<SwLayoutFrame
*>(pCol
)->Lower()->ChgSize( aColSz
);
461 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */