Update ooo320-m1
[ooovba.git] / sw / source / core / text / widorp.cxx
blobd7ea1cf5cb2898178075cf9e8813aed3f8d13e97
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: widorp.cxx,v $
10 * $Revision: 1.22.214.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sw.hxx"
35 #include "hintids.hxx"
37 #include "layfrm.hxx"
38 #include "ftnboss.hxx"
39 #include "ndtxt.hxx"
40 #include "paratr.hxx"
41 #include <svx/orphitem.hxx>
42 #include <svx/widwitem.hxx>
43 #include <svx/keepitem.hxx>
44 #include <svx/spltitem.hxx>
45 #include <frmatr.hxx>
46 #include <txtftn.hxx>
47 #include <fmtftn.hxx>
48 #include <rowfrm.hxx>
50 #include "txtcfg.hxx"
51 #include "widorp.hxx"
52 #include "txtfrm.hxx"
53 #include "itrtxt.hxx"
54 #include "sectfrm.hxx" //SwSectionFrm
55 #include "ftnfrm.hxx"
57 #undef WIDOWTWIPS
60 /*************************************************************************
61 * inline IsNastyFollow()
62 *************************************************************************/
63 // Ein Follow, der auf der selben Seite steht, wie sein Master ist nasty.
64 inline sal_Bool IsNastyFollow( const SwTxtFrm *pFrm )
66 ASSERT( !pFrm->IsFollow() || !pFrm->GetPrev() ||
67 ((const SwTxtFrm*)pFrm->GetPrev())->GetFollow() == pFrm,
68 "IsNastyFollow: Was ist denn hier los?" );
69 return pFrm->IsFollow() && pFrm->GetPrev();
72 /*************************************************************************
73 * SwTxtFrmBreak::SwTxtFrmBreak()
74 *************************************************************************/
76 SwTxtFrmBreak::SwTxtFrmBreak( SwTxtFrm *pNewFrm, const SwTwips nRst )
77 : nRstHeight(nRst), pFrm(pNewFrm)
79 SWAP_IF_SWAPPED( pFrm )
80 SWRECTFN( pFrm )
81 nOrigin = (pFrm->*fnRect->fnGetPrtTop)();
82 SwSectionFrm* pSct;
83 bKeep = !pFrm->IsMoveable() || IsNastyFollow( pFrm ) ||
84 ( pFrm->IsInSct() && (pSct=pFrm->FindSctFrm())->Lower()->IsColumnFrm()
85 && !pSct->MoveAllowed( pFrm ) ) ||
86 !pFrm->GetTxtNode()->GetSwAttrSet().GetSplit().GetValue() ||
87 pFrm->GetTxtNode()->GetSwAttrSet().GetKeep().GetValue();
88 bBreak = sal_False;
90 if( !nRstHeight && !pFrm->IsFollow() && pFrm->IsInFtn() && pFrm->HasPara() )
92 nRstHeight = pFrm->GetFtnFrmHeight();
93 nRstHeight += (pFrm->Prt().*fnRect->fnGetHeight)() -
94 (pFrm->Frm().*fnRect->fnGetHeight)();
95 if( nRstHeight < 0 )
96 nRstHeight = 0;
99 UNDO_SWAP( pFrm )
102 /* BP 18.6.93: Widows.
103 * Im Gegensatz zur ersten Implementierung werden die Widows nicht
104 * mehr vorausschauend berechnet, sondern erst beim Formatieren des
105 * gesplitteten Follows festgestellt. Im Master faellt die Widows-
106 * Berechnung also generell weg (nWidows wird manipuliert).
107 * Wenn der Follow feststellt, dass die Widowsregel zutrifft,
108 * verschickt er an seinen Vorgaenger ein Prepare.
109 * Ein besonderes Problem ergibt sich, wenn die Widows zuschlagen,
110 * aber im Master noch ein paar Zeilen zur Verfuegung stehen.
114 /*************************************************************************
115 * SwTxtFrmBreak::IsInside()
116 *************************************************************************/
118 /* BP(22.07.92): Berechnung von Witwen und Waisen.
119 * Die Methode liefert sal_True zurueck, wenn eine dieser Regelung zutrifft.
121 * Eine Schwierigkeit gibt es im Zusammenhang mit Widows und
122 * unterschiedlichen Formaten zwischen Master- und Folgeframes:
123 * Beispiel: Wenn die erste Spalte 3cm und die zweite 4cm breit ist
124 * und Widows auf sagen wir 3 gesetzt ist, so ist erst bei der Formatierung
125 * des Follows entscheidbar, ob die Widowsbedingung einhaltbar ist oder
126 * nicht. Leider ist davon abhaengig, ob der Absatz als Ganzes auf die
127 * naechste Seite rutscht.
130 sal_Bool SwTxtFrmBreak::IsInside( SwTxtMargin &rLine ) const
132 sal_Bool bFit = sal_False;
134 SWAP_IF_SWAPPED( pFrm )
135 SWRECTFN( pFrm )
136 // nOrigin is an absolut value, rLine referes to the swapped situation.
138 SwTwips nTmpY;
139 if ( pFrm->IsVertical() )
140 nTmpY = pFrm->SwitchHorizontalToVertical( rLine.Y() + rLine.GetLineHeight() );
141 else
142 nTmpY = rLine.Y() + rLine.GetLineHeight();
144 SwTwips nLineHeight = (*fnRect->fnYDiff)( nTmpY , nOrigin );
146 // 7455 und 6114: Raum fuer die Umrandung unten einkalkulieren.
147 nLineHeight += (pFrm->*fnRect->fnGetBottomMargin)();
149 if( nRstHeight )
150 bFit = nRstHeight >= nLineHeight;
151 else
153 // Der Frm besitzt eine Hoehe, mit der er auf die Seite passt.
154 SwTwips nHeight =
155 (*fnRect->fnYDiff)( (pFrm->GetUpper()->*fnRect->fnGetPrtBottom)(), nOrigin );
157 // Wenn sich alles innerhalb des bestehenden Frames abspielt,
158 // ist das Ergebnis sal_True;
159 bFit = nHeight >= nLineHeight;
160 if( !bFit )
162 // Die LineHeight sprengt die aktuelle Frm-Hoehe.
163 // Nun rufen wir ein Probe-Grow, um zu ermitteln, ob der
164 // Frame um den gewuenschten Bereich wachsen wuerde.
165 nHeight += pFrm->GrowTst( LONG_MAX );
167 // Das Grow() returnt die Hoehe, um die der Upper des TxtFrm
168 // den TxtFrm wachsen lassen wuerde.
169 // Der TxtFrm selbst darf wachsen wie er will.
170 bFit = nHeight >= nLineHeight;
174 UNDO_SWAP( pFrm );
176 return bFit;
179 /*************************************************************************
180 * SwTxtFrmBreak::IsBreakNow()
181 *************************************************************************/
183 sal_Bool SwTxtFrmBreak::IsBreakNow( SwTxtMargin &rLine )
185 SWAP_IF_SWAPPED( pFrm )
187 // bKeep ist staerker als IsBreakNow()
188 // Ist noch genug Platz ?
189 if( bKeep || IsInside( rLine ) )
190 bBreak = sal_False;
191 else
193 /* Diese Klasse geht davon aus, dass der SwTxtMargin von Top nach Bottom
194 * durchgearbeitet wird. Aus Performancegruenden wird in folgenden
195 * Faellen der Laden fuer das weitere Aufspalten dicht gemacht:
196 * Wenn eine einzige Zeile nicht mehr passt.
197 * Sonderfall: bei DummyPortions ist LineNr == 1, obwohl wir splitten
198 * wollen.
200 // 6010: DropLines mit einbeziehen
202 sal_Bool bFirstLine = 1 == rLine.GetLineNr() && !rLine.GetPrev();
203 bBreak = sal_True;
204 if( ( bFirstLine && pFrm->GetIndPrev() )
205 || ( rLine.GetLineNr() <= rLine.GetDropLines() ) )
207 bKeep = sal_True;
208 bBreak = sal_False;
210 else if(bFirstLine && pFrm->IsInFtn() && !pFrm->FindFtnFrm()->GetPrev())
212 SwLayoutFrm* pTmp = pFrm->FindFtnBossFrm()->FindBodyCont();
213 if( !pTmp || !pTmp->Lower() )
214 bBreak = sal_False;
218 UNDO_SWAP( pFrm )
220 return bBreak;
223 // OD 2004-02-27 #106629# - no longer inline
224 void SwTxtFrmBreak::SetRstHeight( const SwTxtMargin &rLine )
226 // OD, FME 2004-02-27 #106629# - consider bottom margin
227 SWRECTFN( pFrm )
228 nRstHeight = (pFrm->*fnRect->fnGetBottomMargin)();
229 if ( bVert )
230 nRstHeight += nOrigin - pFrm->SwitchHorizontalToVertical( rLine.Y() );
231 else
232 nRstHeight += rLine.Y() - nOrigin;
235 /*************************************************************************
236 * WidowsAndOrphans::WidowsAndOrphans()
237 *************************************************************************/
239 WidowsAndOrphans::WidowsAndOrphans( SwTxtFrm *pNewFrm, const SwTwips nRst,
240 sal_Bool bChkKeep )
241 : SwTxtFrmBreak( pNewFrm, nRst ), nWidLines( 0 ), nOrphLines( 0 )
243 SWAP_IF_SWAPPED( pFrm )
245 if( bKeep )
247 // 5652: bei Absaetzen, die zusammengehalten werden sollen und
248 // groesser sind als die Seite wird bKeep aufgehoben.
249 if( bChkKeep && !pFrm->GetPrev() && !pFrm->IsInFtn() &&
250 pFrm->IsMoveable() &&
251 ( !pFrm->IsInSct() || pFrm->FindSctFrm()->MoveAllowed(pFrm) ) )
252 bKeep = sal_False;
253 //Auch bei gesetztem Keep muessen Orphans beachtet werden,
254 //z.B. bei verketteten Rahmen erhaelt ein Follow im letzten Rahmen ein Keep,
255 //da er nicht (vorwaerts) Moveable ist,
256 //er darf aber trotzdem vom Master Zeilen anfordern wg. der Orphanregel.
257 if( pFrm->IsFollow() )
258 nWidLines = pFrm->GetTxtNode()->GetSwAttrSet().GetWidows().GetValue();
260 else
262 const SwAttrSet& rSet = pFrm->GetTxtNode()->GetSwAttrSet();
263 const SvxOrphansItem &rOrph = rSet.GetOrphans();
264 if ( rOrph.GetValue() > 1 )
265 nOrphLines = rOrph.GetValue();
266 if ( pFrm->IsFollow() )
267 nWidLines = rSet.GetWidows().GetValue();
271 if ( bKeep || nWidLines || nOrphLines )
273 bool bResetFlags = false;
275 if ( pFrm->IsInTab() )
277 // For compatibility reasons, we disable Keep/Widows/Orphans
278 // inside splittable row frames:
279 if ( pFrm->GetNextCellLeaf( MAKEPAGE_NONE ) || pFrm->IsInFollowFlowRow() )
281 const SwFrm* pTmpFrm = pFrm->GetUpper();
282 while ( !pTmpFrm->IsRowFrm() )
283 pTmpFrm = pTmpFrm->GetUpper();
284 if ( static_cast<const SwRowFrm*>(pTmpFrm)->IsRowSplitAllowed() )
285 bResetFlags = true;
289 if( pFrm->IsInFtn() && !pFrm->GetIndPrev() )
291 // Innerhalb von Fussnoten gibt es gute Gruende, das Keep-Attribut und
292 // die Widows/Orphans abzuschalten.
293 SwFtnFrm *pFtn = pFrm->FindFtnFrm();
294 sal_Bool bFt = !pFtn->GetAttr()->GetFtn().IsEndNote();
295 if( !pFtn->GetPrev() &&
296 pFtn->FindFtnBossFrm( bFt ) != pFtn->GetRef()->FindFtnBossFrm( bFt )
297 && ( !pFrm->IsInSct() || pFrm->FindSctFrm()->MoveAllowed(pFrm) ) )
299 bResetFlags = true;
303 if ( bResetFlags )
305 bKeep = sal_False;
306 nOrphLines = 0;
307 nWidLines = 0;
311 UNDO_SWAP( pFrm )
314 /*************************************************************************
315 * WidowsAndOrphans::FindBreak()
316 *************************************************************************/
318 /* Die Find*-Methoden suchen nicht nur, sondern stellen den SwTxtMargin auf
319 * die Zeile ein, wo der Absatz gebrochen werden soll und kuerzen ihn dort.
320 * FindBreak()
323 sal_Bool WidowsAndOrphans::FindBreak( SwTxtFrm *pFrame, SwTxtMargin &rLine,
324 sal_Bool bHasToFit )
326 // OD 2004-02-25 #i16128# - Why member <pFrm> _*and*_ parameter <pFrame>??
327 // Thus, assertion on situation, that these are different to figure out why.
328 ASSERT( pFrm == pFrame, "<WidowsAndOrphans::FindBreak> - pFrm != pFrame" );
330 SWAP_IF_SWAPPED( pFrm )
332 sal_Bool bRet = sal_True;
333 MSHORT nOldOrphans = nOrphLines;
334 if( bHasToFit )
335 nOrphLines = 0;
336 rLine.Bottom();
337 // OD 2004-02-25 #i16128# - method renamed
338 if( !IsBreakNowWidAndOrp( rLine ) )
339 bRet = sal_False;
340 if( !FindWidows( pFrame, rLine ) )
342 sal_Bool bBack = sal_False;
343 // OD 2004-02-25 #i16128# - method renamed
344 while( IsBreakNowWidAndOrp( rLine ) )
346 if( rLine.PrevLine() )
347 bBack = sal_True;
348 else
349 break;
351 // Eigentlich werden bei HasToFit Schusterjungen (Orphans) nicht
352 // beruecksichtigt, wenn allerdings Dummy-Lines im Spiel sind und
353 // die Orphansregel verletzt wird, machen wir mal eine Ausnahme:
354 // Wir lassen einfach eine Dummyline zurueck und wandern mit dem Text
355 // komplett auf die naechste Seite/Spalte.
356 if( rLine.GetLineNr() <= nOldOrphans &&
357 rLine.GetInfo().GetParaPortion()->IsDummy() &&
358 ( ( bHasToFit && bRet ) || IsBreakNow( rLine ) ) )
359 rLine.Top();
361 rLine.TruncLines( sal_True );
362 bRet = bBack;
364 nOrphLines = nOldOrphans;
366 UNDO_SWAP( pFrm )
368 return bRet;
371 /*************************************************************************
372 * WidowsAndOrphans::FindWidows()
373 *************************************************************************/
375 /* FindWidows positioniert den SwTxtMargin des Masters auf die umzubrechende
376 * Zeile, indem der Follow formatiert und untersucht wird.
377 * Liefert sal_True zurueck, wenn die Widows-Regelung in Kraft tritt,
378 * d.h. der Absatz _zusammengehalten_ werden soll !
381 sal_Bool WidowsAndOrphans::FindWidows( SwTxtFrm *pFrame, SwTxtMargin &rLine )
383 ASSERT( ! pFrame->IsVertical() || ! pFrame->IsSwapped(),
384 "WidowsAndOrphans::FindWidows with swapped frame" )
386 if( !nWidLines || !pFrame->IsFollow() )
387 return sal_False;
389 rLine.Bottom();
391 // Wir koennen noch was abzwacken
392 SwTxtFrm *pMaster = pFrame->FindMaster();
393 ASSERT(pMaster, "+WidowsAndOrphans::FindWidows: Widows in a master?");
394 if( !pMaster )
395 return sal_False;
397 // 5156: Wenn die erste Zeile des Follows nicht passt, wird der Master
398 // wohl voll mit Dummies sein. In diesem Fall waere ein PREP_WIDOWS fatal.
399 if( pMaster->GetOfst() == pFrame->GetOfst() )
400 return sal_False;
402 // Resthoehe des Masters
403 SWRECTFN( pFrame )
405 const SwTwips nDocPrtTop = (pFrame->*fnRect->fnGetPrtTop)();
406 SwTwips nOldHeight;
407 SwTwips nTmpY = rLine.Y() + rLine.GetLineHeight();
409 if ( bVert )
411 nTmpY = pFrame->SwitchHorizontalToVertical( nTmpY );
412 nOldHeight = -(pFrame->Prt().*fnRect->fnGetHeight)();
414 else
415 nOldHeight = (pFrame->Prt().*fnRect->fnGetHeight)();
417 const SwTwips nChg = (*fnRect->fnYDiff)( nTmpY, nDocPrtTop + nOldHeight );
419 // Unterhalb der Widows-Schwelle...
420 if( rLine.GetLineNr() >= nWidLines )
422 // 8575: Follow to Master I
423 // Wenn der Follow *waechst*, so besteht fuer den Master die Chance,
424 // Zeilen entgegenzunehmen, die er vor Kurzem gezwungen war an den
425 // Follow abzugeben: Prepare(Need); diese Abfrage unterhalb von nChg!
426 // (0W, 2O, 2M, 2F) + 1F = 3M, 2F
427 if( rLine.GetLineNr() > nWidLines && pFrame->IsJustWidow() )
429 // Wenn der Master gelockt ist, so hat er vermutlich gerade erst
430 // eine Zeile an uns abgegeben, diese geben nicht zurueck, nur
431 // weil bei uns daraus mehrere geworden sind (z.B. durch Rahmen).
432 if( !pMaster->IsLocked() && pMaster->GetUpper() )
434 const SwTwips nTmpRstHeight = (pMaster->Frm().*fnRect->fnBottomDist)
435 ( (pMaster->GetUpper()->*fnRect->fnGetPrtBottom)() );
436 if ( nTmpRstHeight >=
437 SwTwips(rLine.GetInfo().GetParaPortion()->Height() ) )
439 pMaster->Prepare( PREP_ADJUST_FRM );
440 pMaster->_InvalidateSize();
441 pMaster->InvalidatePage();
445 pFrame->SetJustWidow( sal_False );
447 return sal_False;
450 // 8575: Follow to Master II
451 // Wenn der Follow *schrumpft*, so besteht fuer den Master die Chance,
452 // den kompletten Orphan zu inhalieren.
453 // (0W, 2O, 2M, 1F) - 1F = 3M, 0F -> PREP_ADJUST_FRM
454 // (0W, 2O, 3M, 2F) - 1F = 2M, 2F -> PREP_WIDOWS
456 if( 0 > nChg && !pMaster->IsLocked() && pMaster->GetUpper() )
458 SwTwips nTmpRstHeight = (pMaster->Frm().*fnRect->fnBottomDist)
459 ( (pMaster->GetUpper()->*fnRect->fnGetPrtBottom)() );
460 if( nTmpRstHeight >= SwTwips(rLine.GetInfo().GetParaPortion()->Height() ) )
462 pMaster->Prepare( PREP_ADJUST_FRM );
463 pMaster->_InvalidateSize();
464 pMaster->InvalidatePage();
465 pFrame->SetJustWidow( sal_False );
466 return sal_False;
470 // Master to Follow
471 // Wenn der Follow nach seiner Formatierung weniger Zeilen enthaelt
472 // als Widows, so besteht noch die Chance, einige Zeilen des Masters
473 // abzuzwacken. Wenn dadurch die Orphans-Regel des Masters in Kraft
474 // tritt muss im CalcPrep() des Master-Frame der Frame so vergroessert
475 // werden, dass er nicht mehr auf seine urspruengliche Seite passt.
476 // Wenn er noch ein paar Zeilen entbehren kann, dann muss im CalcPrep()
477 // ein Shrink() erfolgen, der Follow mit dem Widows rutscht dann auf
478 // die Seite des Masters, haelt sich aber zusammen, so dass er (endlich)
479 // auf die naechste Seite rutscht. - So die Theorie!
482 // Wir fordern nur noch ein Zeile zur Zeit an, weil eine Zeile des Masters
483 // bei uns durchaus mehrere Zeilen ergeben koennten.
484 // Dafuer behaelt CalcFollow solange die Kontrolle, bis der Follow alle
485 // notwendigen Zeilen bekommen hat.
486 MSHORT nNeed = 1; // frueher: nWidLines - rLine.GetLineNr();
488 // Special case: Master cannot give lines to follow
489 // --> FME 2008-09-16 #i91421#
490 if ( !pMaster->GetIndPrev() )
492 ULONG nLines = pMaster->GetThisLines();
493 if(nLines == 0 && pMaster->HasPara())
495 const SwParaPortion *pMasterPara = pMaster->GetPara();
496 if(pMasterPara && pMasterPara->GetNext())
497 nLines = 2;
499 if( nLines <= nNeed )
500 return sal_False;
503 pMaster->Prepare( PREP_WIDOWS, (void*)&nNeed );
504 return sal_True;
507 /*************************************************************************
508 * WidowsAndOrphans::WouldFit()
509 *************************************************************************/
511 sal_Bool WidowsAndOrphans::WouldFit( SwTxtMargin &rLine, SwTwips &rMaxHeight, sal_Bool bTst )
513 // Here it does not matter, if pFrm is swapped or not.
514 // IsInside() takes care for itself
516 // Wir erwarten, dass rLine auf der letzten Zeile steht!!
517 ASSERT( !rLine.GetNext(), "WouldFit: aLine::Bottom missed!" );
518 MSHORT nLineCnt = rLine.GetLineNr();
520 // Erstmal die Orphansregel und den Initialenwunsch erfuellen ...
521 const MSHORT nMinLines = Max( GetOrphansLines(), rLine.GetDropLines() );
522 if ( nLineCnt < nMinLines )
523 return sal_False;
525 rLine.Top();
526 SwTwips nLineSum = rLine.GetLineHeight();
528 while( nMinLines > rLine.GetLineNr() )
530 DBG_LOOP;
531 if( !rLine.NextLine() )
532 return sal_False;
533 nLineSum += rLine.GetLineHeight();
536 // Wenn wir jetzt schon nicht mehr passen ...
537 if( !IsInside( rLine ) )
538 return sal_False;
540 // Jetzt noch die Widows-Regel ueberpruefen
541 if( !nWidLines && !pFrm->IsFollow() )
543 // I.A. brauchen Widows nur ueberprueft werden, wenn wir ein Follow
544 // sind. Bei WouldFit muss aber auch fuer den Master die Regel ueber-
545 // prueft werden, weil wir ja gerade erst die Trennstelle ermitteln.
546 // Im Ctor von WidowsAndOrphans wurde nWidLines aber nur fuer Follows
547 // aus dem AttrSet ermittelt, deshalb holen wir es hier nach:
548 const SwAttrSet& rSet = pFrm->GetTxtNode()->GetSwAttrSet();
549 nWidLines = rSet.GetWidows().GetValue();
552 // Sind nach Orphans/Initialen noch genug Zeilen fuer die Widows uebrig?
553 // #111937#: If we are currently doing a test formatting, we may not
554 // consider the widows rule for two reasons:
555 // 1. The columns may have different widths.
556 // Widow lines would have wrong width.
557 // 2. Test formatting is only done up to the given space.
558 // we do not have any lines for widows at all.
559 if( bTst || nLineCnt - nMinLines >= GetWidowsLines() )
561 if( rMaxHeight >= nLineSum )
563 rMaxHeight -= nLineSum;
564 return sal_True;
567 return sal_False;