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
&rCont
, sal_uInt16 nCnt
)
86 SwFrame
* pLower
= rCont
.Lower();
87 assert( pLower
&& pLower
->IsColumnFrame() &&
88 "no columns to remove." );
90 SwColumnFrame
*pColumn
= static_cast<SwColumnFrame
*>(pLower
);
91 sw_RemoveFootnotes( pColumn
, true, true );
92 while ( pColumn
->GetNext() )
94 OSL_ENSURE( pColumn
->GetNext()->IsColumnFrame(),
95 "neighbor of ColumnFrame is no ColumnFrame." );
96 pColumn
= static_cast<SwColumnFrame
*>(pColumn
->GetNext());
98 for ( sal_uInt16 i
= 0; i
< nCnt
; ++i
)
100 SwColumnFrame
*pTmp
= static_cast<SwColumnFrame
*>(pColumn
->GetPrev());
102 SwFrame::DestroyFrame(pColumn
); //format is going to be destroyed in the DTor if needed.
107 static SwLayoutFrame
* lcl_FindColumns( SwLayoutFrame
*pLay
, sal_uInt16 nCount
)
109 SwFrame
*pCol
= pLay
->Lower();
111 if ( pLay
->IsPageFrame() )
112 pCol
= static_cast<SwPageFrame
*>(pLay
)->FindBodyCont()->Lower();
114 if ( pCol
&& pCol
->IsColumnFrame() )
116 SwFrame
*pTmp
= pCol
;
118 for ( i
= 0; pTmp
; pTmp
= pTmp
->GetNext(), ++i
)
120 return i
== nCount
? static_cast<SwLayoutFrame
*>(pCol
) : nullptr;
125 static bool lcl_AddColumns( SwLayoutFrame
*pCont
, sal_uInt16 nCount
)
127 SwDoc
*pDoc
= pCont
->GetFormat()->GetDoc();
128 const bool bMod
= pDoc
->getIDocumentState().IsModified();
130 //Formats should be shared whenever possible. If a neighbour already has
131 //the same column settings we can add them to the same format.
132 //The neighbour can be searched using the format, however the owner of the
133 //attribute depends on the frame type.
134 SwLayoutFrame
*pAttrOwner
= pCont
;
135 if ( pCont
->IsBodyFrame() )
136 pAttrOwner
= pCont
->FindPageFrame();
137 SwLayoutFrame
*pNeighbourCol
= nullptr;
138 SwIterator
<SwLayoutFrame
,SwFormat
> aIter( *pAttrOwner
->GetFormat() );
139 SwLayoutFrame
*pNeighbour
= aIter
.First();
142 SwFrame
*pCol
= pCont
->Lower();
143 if ( pCol
&& pCol
->IsColumnFrame() )
144 for ( nAdd
= 1; pCol
; pCol
= pCol
->GetNext(), ++nAdd
)
148 if ( nullptr != (pNeighbourCol
= lcl_FindColumns( pNeighbour
, nCount
+nAdd
)) &&
149 pNeighbourCol
!= pCont
)
151 pNeighbourCol
= nullptr;
152 pNeighbour
= aIter
.Next();
156 SwTwips nMax
= pCont
->IsPageBodyFrame() ?
157 pCont
->FindPageFrame()->GetMaxFootnoteHeight() : LONG_MAX
;
161 SwFrame
*pTmp
= pCont
->Lower();
164 pTmp
= pTmp
->GetNext();
165 pNeighbourCol
= static_cast<SwLayoutFrame
*>(pNeighbourCol
->GetNext());
167 for ( sal_uInt16 i
= 0; i
< nCount
; ++i
)
169 SwColumnFrame
*pTmpCol
= new SwColumnFrame( pNeighbourCol
->GetFormat(), pCont
);
170 pTmpCol
->SetMaxFootnoteHeight( nMax
);
171 pTmpCol
->InsertBefore( pCont
, nullptr );
172 pNeighbourCol
= static_cast<SwLayoutFrame
*>(pNeighbourCol
->GetNext());
178 // tdf#103359, like #i32968# Inserting columns in the section causes MakeFrameFormat to put
179 // nCount objects of type SwUndoFrameFormat on the undo stack. We don't want them.
180 ::sw::UndoGuard
const undoGuard(pDoc
->GetIDocumentUndoRedo());
181 for ( sal_uInt16 i
= 0; i
< nCount
; ++i
)
183 SwFrameFormat
*pFormat
= pDoc
->MakeFrameFormat(OUString(), pDoc
->GetDfltFrameFormat());
184 SwColumnFrame
*pTmp
= new SwColumnFrame( pFormat
, pCont
);
185 pTmp
->SetMaxFootnoteHeight( nMax
);
186 pTmp
->Paste( pCont
);
191 pDoc
->getIDocumentState().ResetModified();
195 /** add or remove columns from a layoutframe.
197 * Normally, a layoutframe with a column attribute of 1 or 0 columns contains
198 * no columnframe. However, a sectionframe with "footnotes at the end" needs
203 * @param bChgFootnote if true, the columnframe will be inserted or removed, if necessary.
205 void SwLayoutFrame::ChgColumns( const SwFormatCol
&rOld
, const SwFormatCol
&rNew
,
206 const bool bChgFootnote
)
208 if ( rOld
.GetNumCols() <= 1 && rNew
.GetNumCols() <= 1 && !bChgFootnote
)
211 // If current lower is a no text frame, then columns are not allowed
212 if ( Lower() && Lower()->IsNoTextFrame() &&
213 rNew
.GetNumCols() > 1 )
218 sal_uInt16 nNewNum
, nOldNum
= 1;
219 if( Lower() && Lower()->IsColumnFrame() )
221 SwFrame
* pCol
= Lower();
222 while( nullptr != (pCol
=pCol
->GetNext()) )
225 nNewNum
= rNew
.GetNumCols();
230 bAtEnd
= static_cast<SwSectionFrame
*>(this)->IsAnyNoteAtEnd();
234 //Setting the column width is only needed for new formats.
235 bool bAdjustAttributes
= nOldNum
!= rOld
.GetNumCols();
237 //The content is saved and restored if the column count is different.
238 SwFrame
*pSave
= nullptr;
239 if( nOldNum
!= nNewNum
|| bChgFootnote
)
241 SwDoc
*pDoc
= GetFormat()->GetDoc();
242 OSL_ENSURE( pDoc
, "FrameFormat doesn't return a document." );
243 // SaveContent would also suck up the content of the footnote container
244 // and store it within the normal text flow.
245 if( IsPageBodyFrame() )
246 pDoc
->getIDocumentLayoutAccess().GetCurrentLayout()->RemoveFootnotes( static_cast<SwPageFrame
*>(GetUpper()) );
247 pSave
= ::SaveContent( this );
249 //If columns exist, they get deleted if a column count of 0 or 1 is requested.
250 if ( nNewNum
== 1 && !bAtEnd
)
252 ::lcl_RemoveColumns( *this, nOldNum
);
254 SetFrameFormat( pDoc
->GetDfltFrameFormat() );
256 GetFormat()->SetFormatAttr( SwFormatFillOrder() );
258 ::RestoreContent( pSave
, this, nullptr );
264 SetFrameFormat( pDoc
->GetColumnContFormat() );
266 GetFormat()->SetFormatAttr( SwFormatFillOrder( ATT_LEFT_TO_RIGHT
) );
267 if( !Lower() || !Lower()->IsColumnFrame() )
270 if ( nOldNum
> nNewNum
)
272 ::lcl_RemoveColumns( *this, nOldNum
- nNewNum
);
273 bAdjustAttributes
= true;
275 else if( nOldNum
< nNewNum
)
277 sal_uInt16 nAdd
= nNewNum
- nOldNum
;
278 bAdjustAttributes
= lcl_AddColumns( this, nAdd
);
282 if ( !bAdjustAttributes
)
284 if ( rOld
.GetLineWidth() != rNew
.GetLineWidth() ||
285 rOld
.GetWishWidth() != rNew
.GetWishWidth() ||
286 rOld
.IsOrtho() != rNew
.IsOrtho() )
287 bAdjustAttributes
= true;
290 const size_t nCount
= std::min( rNew
.GetColumns().size(), rOld
.GetColumns().size() );
291 for ( size_t i
= 0; i
< nCount
; ++i
)
292 if ( !(rOld
.GetColumns()[i
] == rNew
.GetColumns()[i
]) )
294 bAdjustAttributes
= true;
300 //The columns can now be easily adjusted.
301 AdjustColumns( &rNew
, bAdjustAttributes
);
303 //Don't restore the content before. An earlier restore would trigger useless
304 //actions during setup.
307 SwFrame
* pLower
= Lower();
308 assert(pLower
&& pLower
->IsLayoutFrame());
309 SwFrame
* pLowerLower
= static_cast<SwLayoutFrame
*>(pLower
)->Lower();
310 assert(pLowerLower
&& pLowerLower
->IsLayoutFrame() && "no column body."); // ColumnFrames contain BodyFrames
311 ::RestoreContent( pSave
, static_cast<SwLayoutFrame
*>(pLowerLower
), nullptr );
315 void SwLayoutFrame::AdjustColumns( const SwFormatCol
*pAttr
, bool bAdjustAttributes
)
317 SwFrame
* pLower
= Lower();
321 if( !pLower
->GetNext() )
323 pLower
->ChgSize( getFramePrintArea().SSize() );
327 SwRectFnSet
fnRect(this);
329 //If we have a pointer or we have to configure an attribute, we set the
330 //column widths in any case. Otherwise we check if a configuration is needed.
333 pAttr
= &GetFormat()->GetCol();
334 if ( !bAdjustAttributes
)
336 tools::Long nAvail
= fnRect
.GetWidth(getFramePrintArea());
337 for ( SwLayoutFrame
*pCol
= static_cast<SwLayoutFrame
*>(pLower
);
339 pCol
= static_cast<SwLayoutFrame
*>(pCol
->GetNext()) )
340 nAvail
-= fnRect
.GetWidth(pCol
->getFrameArea());
346 //The columns can now be easily adjusted.
347 //The widths get counted so we can give the reminder to the last one.
348 SwTwips nAvail
= fnRect
.GetWidth(getFramePrintArea());
349 const bool bLine
= pAttr
->GetLineAdj() != COLADJ_NONE
;
350 const sal_uInt16 nMin
= bLine
? sal_uInt16( 20 + ( pAttr
->GetLineWidth() / 2) ) : 0;
352 const bool bR2L
= IsRightToLeft();
353 SwFrame
*pCol
= bR2L
? GetLastLower() : pLower
;
356 // bOrtho means we have to adjust the column frames manually. Otherwise
357 // we may use the values returned by CalcColWidth:
358 const bool bOrtho
= pAttr
->IsOrtho() && pAttr
->GetNumCols() > 0;
359 tools::Long nGutter
= 0;
361 for ( sal_uInt16 i
= 0; i
< pAttr
->GetNumCols() && pCol
; ++i
) //i118878, value returned by GetNumCols() can't be trusted
365 const SwTwips nWidth
= i
== (pAttr
->GetNumCols() - 1) ?
367 pAttr
->CalcColWidth( i
, sal_uInt16( fnRect
.GetWidth(getFramePrintArea()) ) );
369 const Size aColSz
= fnRect
.IsVert() ?
370 Size( getFramePrintArea().Width(), nWidth
) :
371 Size( nWidth
, getFramePrintArea().Height() );
373 pCol
->ChgSize( aColSz
);
375 // With this, the ColumnBodyFrames from page columns gets adjusted and
376 // their bFixHeight flag is set so they won't shrink/grow.
377 // Don't use the flag with frame columns because BodyFrames in frame
378 // columns can grow/shrink.
380 static_cast<SwLayoutFrame
*>(pCol
)->Lower()->ChgSize( aColSz
);
385 if ( bOrtho
|| bAdjustAttributes
)
387 const SwColumn
*pC
= &pAttr
->GetColumns()[i
];
388 const SwAttrSet
* pSet
= pCol
->GetAttrSet();
389 SvxLRSpaceItem
aLR( pSet
->GetLRSpace() );
391 //In order to have enough space for the separation lines, we have to
392 //take them into account here. Every time two columns meet we
393 //calculate a clearance of 20 + half the pen width on the left or
394 //right side, respectively.
395 const sal_uInt16 nLeft
= pC
->GetLeft();
396 const sal_uInt16 nRight
= pC
->GetRight();
398 aLR
.SetLeft(SvxIndentValue::twips(nLeft
));
399 aLR
.SetRight(SvxIndentValue::twips(nRight
));
405 aLR
.SetRight(SvxIndentValue::twips(std::max(nRight
, nMin
)));
407 else if ( i
== pAttr
->GetNumCols() - 1 )
409 aLR
.SetLeft(SvxIndentValue::twips(std::max(nLeft
, nMin
)));
413 aLR
.SetLeft(SvxIndentValue::twips(std::max(nLeft
, nMin
)));
414 aLR
.SetRight(SvxIndentValue::twips(std::max(nRight
, nMin
)));
418 if ( bAdjustAttributes
)
420 SvxULSpaceItem
aUL( pSet
->GetULSpace() );
424 static_cast<SwLayoutFrame
*>(pCol
)->GetFormat()->SetFormatAttr( aLR
);
425 static_cast<SwLayoutFrame
*>(pCol
)->GetFormat()->SetFormatAttr( aUL
);
428 nGutter
+= aLR
.ResolveLeft({}) + aLR
.ResolveRight({});
431 pCol
= bR2L
? pCol
->GetPrev() : pCol
->GetNext();
437 tools::Long nInnerWidth
= ( nAvail
- nGutter
) / pAttr
->GetNumCols();
439 for( sal_uInt16 i
= 0; i
< pAttr
->GetNumCols() && pCol
; pCol
= pCol
->GetNext(), ++i
) //i118878, value returned by GetNumCols() can't be trusted
442 if ( i
== pAttr
->GetNumCols() - 1 )
446 SvxLRSpaceItem
aLR( pCol
->GetAttrSet()->GetLRSpace() );
447 nWidth
= nInnerWidth
+ aLR
.ResolveLeft({}) + aLR
.ResolveRight({});
452 const Size aColSz
= fnRect
.IsVert() ?
453 Size( getFramePrintArea().Width(), nWidth
) :
454 Size( nWidth
, getFramePrintArea().Height() );
456 pCol
->ChgSize( aColSz
);
459 static_cast<SwLayoutFrame
*>(pCol
)->Lower()->ChgSize( aColSz
);
465 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */