1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: txtrange.cxx,v $
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_svx.hxx"
34 #include "txtrange.hxx"
36 #include <tools/poly.hxx>
37 #include <tools/debug.hxx>
38 #include <basegfx/polygon/b2dpolygon.hxx>
39 #include <basegfx/polygon/b2dpolygontools.hxx>
41 /*************************************************************************
43 |* TextRanger::TextRanger()
46 |* Ersterstellung 20.01.97
47 |* Letzte Aenderung 20.01.97 AMA
49 *************************************************************************/
52 #pragma optimize ( "", off )
55 TextRanger::TextRanger( const basegfx::B2DPolyPolygon
& rPolyPolygon
, const basegfx::B2DPolyPolygon
* pLinePolyPolygon
,
56 USHORT nCacheSz
, USHORT nLft
, USHORT nRght
, BOOL bSimpl
, BOOL bInnr
,
59 nCacheSize( nCacheSz
),
71 bFlag3
= bFlag4
= bFlag5
= bFlag6
= bFlag7
= FALSE
;
73 pRangeArr
= new Range
[ nCacheSize
];
74 pCache
= new SvLongsPtr
[ nCacheSize
];
75 memset( pRangeArr
, 0, nCacheSize
* sizeof( Range
) );
76 memset( pCache
, 0, nCacheSize
* sizeof( SvLongsPtr
) );
77 sal_uInt32
nCount(rPolyPolygon
.count());
78 mpPolyPolygon
= new PolyPolygon( (sal_uInt16
)nCount
);
80 for(sal_uInt32
i(0L); i
< nCount
; i
++)
82 const basegfx::B2DPolygon
aCandidate(rPolyPolygon
.getB2DPolygon(i
).getDefaultAdaptiveSubdivision());
83 nPointCount
+= aCandidate
.count();
84 mpPolyPolygon
->Insert( Polygon(aCandidate
), (sal_uInt16
)i
);
87 if( pLinePolyPolygon
)
89 nCount
= pLinePolyPolygon
->count();
90 mpLinePolyPolygon
= new PolyPolygon();
92 for(sal_uInt32
i(0L); i
< nCount
; i
++)
94 const basegfx::B2DPolygon
aCandidate(pLinePolyPolygon
->getB2DPolygon(i
).getDefaultAdaptiveSubdivision());
95 nPointCount
+= aCandidate
.count();
96 mpLinePolyPolygon
->Insert( Polygon(aCandidate
), (sal_uInt16
)i
);
100 mpLinePolyPolygon
= NULL
;
104 #pragma optimize ( "", on )
107 /*************************************************************************
109 |* TextRanger::~TextRanger()
112 |* Ersterstellung 20.01.97
113 |* Letzte Aenderung 20.01.97 AMA
115 *************************************************************************/
117 TextRanger::~TextRanger()
119 for( USHORT i
= 0; i
< nCacheSize
; ++i
)
123 delete mpPolyPolygon
;
124 delete mpLinePolyPolygon
;
127 /*-----------------17.11.00 09:49-------------------
128 * TextRanger::SetVertical(..)
129 * If there's is a change in the writing direction,
130 * the cache has to be cleared.
131 * --------------------------------------------------*/
133 void TextRanger::SetVertical( BOOL bNew
)
135 if( IsVertical() != bNew
)
138 for( USHORT i
= 0; i
< nCacheSize
; ++i
)
140 memset( pRangeArr
, 0, nCacheSize
* sizeof( Range
) );
141 memset( pCache
, 0, nCacheSize
* sizeof( SvLongsPtr
) );
145 /*************************************************************************
150 |* Ersterstellung 20.01.97
151 |* Letzte Aenderung 20.01.97 AMA
153 *************************************************************************/
159 TextRanger
*pTextRanger
;
180 void NoteRange( BOOL bToggle
);
181 long Cut( long nY
, const Point
& rPt1
, const Point
& rPt2
);
183 void _NoteFarPoint( long nPx
, long nPyDiff
, long nDiff
);
184 void NoteFarPoint( long nPx
, long nPyDiff
, long nDiff
)
185 { if( nDiff
) _NoteFarPoint( nPx
, nPyDiff
, nDiff
); }
186 long CalcMax( const Point
& rPt1
, const Point
& rPt2
, long nRange
, long nFar
);
187 void CheckCut( const Point
& rLst
, const Point
& rNxt
);
188 inline long A( const Point
& rP
) const { return bRotate
? rP
.Y() : rP
.X(); }
189 inline long B( const Point
& rP
) const { return bRotate
? rP
.X() : rP
.Y(); }
191 SvxBoundArgs( TextRanger
* pRanger
, SvLongs
*pLong
, const Range
& rRange
);
192 void NotePoint( const long nA
) { NoteMargin( nA
- nStart
, nA
+ nEnd
); }
193 void NoteMargin( const long nL
, const long nR
)
194 { if( nMin
> nL
) nMin
= nL
; if( nMax
< nR
) nMax
= nR
; }
195 USHORT
Area( const Point
& rPt
);
196 void NoteUpLow( long nA
, const BYTE nArea
);
197 void Calc( const PolyPolygon
& rPoly
);
198 void Concat( const PolyPolygon
* pPoly
);
200 void NoteLast() { if( bMultiple
) NoteRange( nAct
== nFirst
); }
201 void SetClosed( const BOOL bNew
){ bClosed
= bNew
; }
202 BOOL
IsClosed() const { return bClosed
; }
203 void SetConcat( const BOOL bNew
){ bConcat
= bNew
; }
204 BOOL
IsConcat() const { return bConcat
; }
205 BYTE
GetAct() const { return nAct
; }
208 SvxBoundArgs::SvxBoundArgs( TextRanger
* pRanger
, SvLongs
*pLong
,
209 const Range
& rRange
)
210 : aBoolArr( 4, 4 ), pLongArr( pLong
), pTextRanger( pRanger
),
211 nTop( rRange
.Min() ), nBottom( rRange
.Max() ),
212 bInner( pRanger
->IsInner() ), bMultiple( bInner
|| !pRanger
->IsSimple() ),
213 bConcat( FALSE
), bRotate( pRanger
->IsVertical() )
217 nStart
= pRanger
->GetUpper();
218 nEnd
= pRanger
->GetLower();
219 nLowDiff
= pRanger
->GetLeft();
220 nUpDiff
= pRanger
->GetRight();
224 nStart
= pRanger
->GetLeft();
225 nEnd
= pRanger
->GetRight();
226 nLowDiff
= pRanger
->GetUpper();
227 nUpDiff
= pRanger
->GetLower();
229 nUpper
= nTop
- nUpDiff
;
230 nLower
= nBottom
+ nLowDiff
;
231 pLongArr
->Remove( 0, pLongArr
->Count() );
234 long SvxBoundArgs::CalcMax( const Point
& rPt1
, const Point
& rPt2
,
235 long nRange
, long nFarRange
)
237 double nDa
= Cut( nRange
, rPt1
, rPt2
) - Cut( nFarRange
, rPt1
, rPt2
);
248 nB
= nRange
+ nDa
* ( nFarRange
- nRange
) / sqrt( nB
);
252 bNote
= nB
> B(rPt1
);
254 bNote
= nB
< B(rPt1
);
256 return( long( nB
) );
260 void SvxBoundArgs::CheckCut( const Point
& rLst
, const Point
& rNxt
)
263 NotePoint( Cut( nBottom
, rLst
, rNxt
) );
265 NotePoint( Cut( nTop
, rLst
, rNxt
) );
266 if( rLst
.X() != rNxt
.X() && rLst
.Y() != rNxt
.Y() )
269 if( nLowDiff
&& ( ( nCut
& 1 ) || nLast
== 1 || nNext
== 1 ) )
271 nYps
= CalcMax( rLst
, rNxt
, nBottom
, nLower
);
273 _NoteFarPoint( Cut( nYps
, rLst
, rNxt
), nLower
-nYps
, nLowDiff
);
275 if( nUpDiff
&& ( ( nCut
& 2 ) || nLast
== 2 || nNext
== 2 ) )
277 nYps
= CalcMax( rLst
, rNxt
, nTop
, nUpper
);
279 _NoteFarPoint( Cut( nYps
, rLst
, rNxt
), nYps
-nUpper
, nUpDiff
);
284 void SvxBoundArgs::_NoteFarPoint( long nPa
, long nPbDiff
, long nDiff
)
287 double nQuot
= 2 * nDiff
- nPbDiff
;
289 nQuot
= sqrt( nQuot
);
291 nTmpA
= nPa
- long( nStart
* nQuot
);
292 nPbDiff
= nPa
+ long( nEnd
* nQuot
);
293 NoteMargin( nTmpA
, nPbDiff
);
296 void SvxBoundArgs::NoteRange( BOOL bToggle
)
298 DBG_ASSERT( nMax
>= nMin
|| bInner
, "NoteRange: Min > Max?");
304 USHORT nCount
= pLongArr
->Count();
305 DBG_ASSERT( nCount
== 2 * aBoolArr
.Count(), "NoteRange: Incompatible Sizes" );
306 while( nIdx
< nCount
&& (*pLongArr
)[ nIdx
] < nMin
)
308 BOOL bOdd
= nIdx
% 2 ? TRUE
: FALSE
;
309 // Kein Ueberlappung mit vorhandenen Intervallen?
310 if( nIdx
== nCount
|| ( !bOdd
&& nMax
< (*pLongArr
)[ nIdx
] ) )
311 { // Dann wird ein neues eingefuegt ...
312 pLongArr
->Insert( nMin
, nIdx
);
313 pLongArr
->Insert( nMax
, nIdx
+ 1 );
314 aBoolArr
.Insert( bToggle
, nIdx
/ 2 );
317 { // ein vorhandes Intervall erweitern ...
318 USHORT nMaxIdx
= nIdx
;
319 // Wenn wir auf einer linken Intervallgrenze gelandet sind, muss diese
320 // auf nMin gesenkt werden.
324 (*pLongArr
)[ nIdx
] = nMin
;
325 while( nMaxIdx
< nCount
&& (*pLongArr
)[ nMaxIdx
] < nMax
)
327 DBG_ASSERT( nMaxIdx
> nIdx
|| nMin
== nMax
, "NoteRange: Funny Situation." );
332 // Wenn wir auf einer rechten Intervallgrenze landen, muss diese
333 // auf nMax angehoben werden.
335 (*pLongArr
)[ nMaxIdx
-- ] = nMax
;
336 // Jetzt werden eventuell noch Intervalle verschmolzen
337 USHORT nDiff
= nMaxIdx
- nIdx
;
338 nMaxIdx
= nIdx
/ 2; // Ab hier ist nMaxIdx der Index im BoolArray.
341 (*pLongArr
).Remove( nIdx
+ 1, nDiff
);
343 USHORT nStop
= nMaxIdx
+ nDiff
;
344 for( USHORT i
= nMaxIdx
; i
< nStop
; ++i
)
345 bToggle
^= aBoolArr
[ i
];
346 aBoolArr
.Remove( nMaxIdx
, nDiff
);
348 DBG_ASSERT( nMaxIdx
< aBoolArr
.Count(), "NoteRange: Too much deleted" );
349 aBoolArr
[ nMaxIdx
] ^= bToggle
;
353 void SvxBoundArgs::Calc( const PolyPolygon
& rPoly
)
357 for( USHORT i
= 0; i
< rPoly
.Count(); ++i
)
359 const Polygon
& rPol
= rPoly
[ i
];
360 nCount
= rPol
.GetSize();
363 const Point
& rNull
= rPol
[ 0 ];
364 SetClosed( IsConcat() || ( rNull
== rPol
[ nCount
- 1 ] ) );
365 nLast
= Area( rNull
);
374 // Der erste Punkt des Polygons liegt innerhalb der Zeile.
377 if( bMultiple
|| !nAct
)
383 NoteFarPoint( A(rNull
), nLower
- B(rNull
), nLowDiff
);
385 NoteFarPoint( A(rNull
), B(rNull
) - nUpper
, nUpDiff
);
389 if( bMultiple
|| !nAct
)
396 NotePoint( A(rNull
) );
398 nFirst
= 0; // In welcher Richtung wird die Zeile verlassen?
399 nAct
= 3; // Wir sind z.Z. innerhalb der Zeile.
406 const Point
& rLast
= rPol
[ nIdx
- 1 ];
409 const Point
& rNext
= rPol
[ nIdx
];
410 nNext
= Area( rNext
);
411 nCut
= nNext
^ nLast
;
412 USHORT nOldAct
= nAct
;
414 CheckCut( rLast
, rNext
);
417 NoteUpLow( Cut( nLower
, rLast
, rNext
), 2 );
418 if( nAct
&& nAct
!= nOldAct
)
421 CheckCut( rLast
, rNext
);
426 NoteUpLow( Cut( nUpper
, rLast
, rNext
), 1 );
427 if( nAct
&& nAct
!= nOldAct
)
428 CheckCut( rLast
, rNext
);
432 if( !( nNext
& 12 ) )
436 if( !( nNext
& 12 ) )
439 NotePoint( A(rNext
) );
441 NoteFarPoint( A(rNext
), nLower
-B(rNext
), nLowDiff
);
443 NoteFarPoint( A(rNext
), B(rNext
)-nUpper
, nUpDiff
);
446 if( ++nIdx
== nCount
&& !IsClosed() )
448 if( !( nNext
& 12 ) )
454 if( bMultiple
&& IsConcat() )
463 DBG_ASSERT( pLongArr
->Count() == 0, "I said: Simple!" );
468 long nTmpMin
, nTmpMax
;
470 nTmpMin
= nMin
+ 2 * nStart
;
471 nTmpMax
= nMax
- 2 * nEnd
;
472 if( nTmpMin
<= nTmpMax
)
474 pLongArr
->Insert( nTmpMin
, 0 );
475 pLongArr
->Insert( nTmpMax
, 1 );
481 pLongArr
->Insert( nMin
, 0 );
482 pLongArr
->Insert( nMax
, 1 );
486 else if( !IsConcat() )
490 void SvxBoundArgs::Add()
493 USHORT nCount
= aBoolArr
.Count();
494 if( nCount
&& ( !bInner
|| !pTextRanger
->IsSimple() ) )
496 BOOL bDelete
= aBoolArr
[ 0 ];
499 for( USHORT nBoolIdx
= 1; nBoolIdx
< nCount
; ++nBoolIdx
)
504 while( nBoolIdx
< nCount
&& !aBoolArr
[ nBoolIdx
++ ] &&
505 (!bInner
|| nBoolIdx
< nCount
) )
507 pLongArr
->Remove( nLongIdx
, next
);
509 nBoolIdx
= nBoolIdx
- next
;
510 nCount
= nCount
- next
;
511 aBoolArr
.Remove( nBoolIdx
, next
);
513 aBoolArr
[ nBoolIdx
- 1 ] = FALSE
;
514 #if OSL_DEBUG_LEVEL > 1
519 bDelete
= nBoolIdx
< nCount
&& aBoolArr
[ nBoolIdx
];
521 DBG_ASSERT( nLongIdx
== 2*nBoolIdx
+1, "BoundArgs: Array-Idx Confusion" );
522 DBG_ASSERT( aBoolArr
.Count()*2 == pLongArr
->Count(),
523 "BoundArgs: Array-Count: Confusion" );
526 if( 0 != ( nCount
= pLongArr
->Count() ) )
530 pLongArr
->Remove( 0, 1 );
531 pLongArr
->Remove( pLongArr
->Count() - 1, 1 );
533 // Hier wird die Zeile beim "einfachen" Konturumfluss im Innern
534 // in ein grosses Rechteck zusammengefasst.
535 // Zur Zeit (April 1999) wertet die EditEngine nur das erste Rechteck
536 // aus, falls sie eines Tages in der Lage ist, eine Zeile in mehreren
537 // Teilen auszugeben, kann es sinnvoll sein, die folgenden Zeilen
539 if( pTextRanger
->IsSimple() && pLongArr
->Count() > 2 )
540 pLongArr
->Remove( 1, pLongArr
->Count() - 2 );
546 void SvxBoundArgs::Concat( const PolyPolygon
* pPoly
)
549 DBG_ASSERT( pPoly
, "Nothing to do?" );
550 SvLongs
*pOld
= pLongArr
;
551 pLongArr
= new SvLongs( 2, 8 );
552 aBoolArr
.Remove( 0, aBoolArr
.Count() );
555 USHORT nCount
= pLongArr
->Count();
558 BOOL bSubtract
= pTextRanger
->IsInner();
561 USHORT nOldCount
= pOld
->Count();
562 if( nIdx
== nOldCount
)
563 { // Am Ende des alten Arrays angelangt...
565 pOld
->Insert( pLongArr
, nIdx
, i
, USHRT_MAX
);
568 long nLeft
= (*pLongArr
)[ i
++ ];
569 long nRight
= (*pLongArr
)[ i
++ ];
570 USHORT nLeftPos
= nIdx
+ 1;
571 while( nLeftPos
< nOldCount
&& nLeft
> (*pOld
)[ nLeftPos
] )
573 if( nLeftPos
>= nOldCount
)
574 { // Das aktuelle Intervall gehoert ans Ende des alten Arrays...
576 pOld
->Insert( pLongArr
, nOldCount
, i
- 2, USHRT_MAX
);
579 USHORT nRightPos
= nLeftPos
- 1;
580 while( nRightPos
< nOldCount
&& nRight
>= (*pOld
)[ nRightPos
] )
582 if( nRightPos
< nLeftPos
)
583 { // Das aktuelle Intervall gehoert zwischen zwei alte Intervalle
585 pOld
->Insert( pLongArr
, nRightPos
, i
- 2, i
);
586 nIdx
= nRightPos
+ 2;
588 else if( bSubtract
) // Subtrahieren ggf. Trennen
591 if( nLeft
> ( nOld
= (*pOld
)[ nLeftPos
- 1 ] ) )
592 { // Jetzt spalten wir den linken Teil ab...
593 if( nLeft
- 1 > nOld
)
595 pOld
->Insert( nOld
, nLeftPos
- 1 );
596 pOld
->Insert( nLeft
- 1, nLeftPos
);
601 if( nRightPos
- nLeftPos
> 1 )
602 pOld
->Remove( nLeftPos
, nRightPos
- nLeftPos
- 1 );
603 if( ++nRight
>= ( nOld
= (*pOld
)[ nLeftPos
] ) )
604 pOld
->Remove( nLeftPos
- 1, 2 );
606 (*pOld
)[ nLeftPos
- 1 ] = nRight
;
610 if( nLeft
< (*pOld
)[ nLeftPos
- 1 ] )
611 (*pOld
)[ nLeftPos
- 1 ] = nLeft
;
612 if( nRight
> (*pOld
)[ nRightPos
- 1 ] )
613 (*pOld
)[ nRightPos
- 1 ] = nRight
;
614 if( nRightPos
- nLeftPos
> 1 )
615 pOld
->Remove( nLeftPos
, nRightPos
- nLeftPos
- 1 );
623 /*************************************************************************
624 * SvxBoundArgs::Area ermittelt den Bereich, in dem sich der Punkt befindet
625 * 0 = innerhalb der Zeile
626 * 1 = unterhalb, aber innerhalb der oberen Randes
627 * 2 = oberhalb, aber innerhalb der unteren Randes
628 * 5 = unterhalb des oberen Randes
629 *10 = oberhalb des unteren Randes
630 *************************************************************************/
632 USHORT
SvxBoundArgs::Area( const Point
& rPt
)
650 /*************************************************************************
651 * lcl_Cut berechnet die X-Koordinate der Strecke (Pt1-Pt2) auf der
653 * Vorausgesetzt wird, dass einer der Punkte oberhalb und der andere
654 * unterhalb der Y-Koordinate liegt.
655 *************************************************************************/
657 long SvxBoundArgs::Cut( long nB
, const Point
& rPt1
, const Point
& rPt2
)
659 if( pTextRanger
->IsVertical() )
661 double nQuot
= nB
- rPt1
.X();
662 nQuot
/= ( rPt2
.X() - rPt1
.X() );
663 nQuot
*= ( rPt2
.Y() - rPt1
.Y() );
664 return long( rPt1
.Y() + nQuot
);
666 double nQuot
= nB
- rPt1
.Y();
667 nQuot
/= ( rPt2
.Y() - rPt1
.Y() );
668 nQuot
*= ( rPt2
.X() - rPt1
.X() );
669 return long( rPt1
.X() + nQuot
);
672 void SvxBoundArgs::NoteUpLow( long nA
, const BYTE nArea
)
676 NoteMargin( nA
, nA
);
679 NoteRange( nArea
!= nAct
);
693 SvLongsPtr
TextRanger::GetTextRanges( const Range
& rRange
)
695 DBG_ASSERT( rRange
.Min() || rRange
.Max(), "Zero-Range not allowed, Bye Bye" );
697 while( nIndex
< nCacheSize
&& rRange
!= pRangeArr
[ nIndex
] )
699 if( nIndex
>= nCacheSize
)
702 nCacheIdx
%= nCacheSize
;
703 pRangeArr
[ nCacheIdx
] = rRange
;
704 if( !pCache
[ nCacheIdx
] )
705 pCache
[ nCacheIdx
] = new SvLongs( 2, 8 );
707 SvxBoundArgs
aArg( this, pCache
[ nCacheIdx
], rRange
);
708 aArg
.Calc( *mpPolyPolygon
);
709 if( mpLinePolyPolygon
)
710 aArg
.Concat( mpLinePolyPolygon
);
712 return pCache
[ nIndex
];
715 const Rectangle
& TextRanger::_GetBoundRect()
717 DBG_ASSERT( 0 == pBound
, "Don't call twice." );
718 pBound
= new Rectangle( mpPolyPolygon
->GetBoundRect() );