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: editdoc2.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 <eeng_pch.hxx>
35 #include <svtools/smplhint.hxx>
36 #include <tools/rtti.hxx>
37 #include <svx/lspcitem.hxx>
38 #include <svx/adjitem.hxx>
39 #include <svx/tstpitem.hxx>
41 #include <editdoc.hxx>
42 #include <impedit.hxx>
43 #include <editdbg.hxx>
45 #include <svx/numitem.hxx>
47 #include <svx/akrnitem.hxx>
48 #include <svx/cntritem.hxx>
49 #include <svx/colritem.hxx>
50 #include <svx/crsditem.hxx>
51 #include <svx/escpitem.hxx>
52 #include <svx/fhgtitem.hxx>
53 #include "fontitem.hxx"
54 #include <svx/kernitem.hxx>
55 #include <svx/lrspitem.hxx>
56 #include <svx/postitem.hxx>
57 #include <svx/shdditem.hxx>
58 #include <svx/udlnitem.hxx>
59 #include <svx/ulspitem.hxx>
60 #include <svx/wghtitem.hxx>
61 #include <svx/wrlmitem.hxx>
62 #include <svx/charscaleitem.hxx>
64 #include <vcl/svapp.hxx> // Fuer AppWindow...
66 DBG_NAME( EE_ParaPortion
)
68 SV_IMPL_VARARR( CharPosArray
, sal_Int32
);
72 BOOL EditStyleSheet::HasStyleAsAnyParent( SfxStyleSheet& rStyle )
74 if ( GetParent() == rStyle.GetName() )
77 if ( GetParent().Len() && ( GetParent() != GetName() ) )
79 EditStyleSheet* pS = (EditStyleSheet*)GetPool().Find( GetParent(), rStyle.GetFamily() );
81 return pS->HasStyleAsAnyParent( rStyle );
88 // -------------------------------------------------------------------------
89 // class TextPortionList
90 // -------------------------------------------------------------------------
91 TextPortionList::TextPortionList()
95 TextPortionList::~TextPortionList()
100 void TextPortionList::Reset()
102 for ( USHORT nPortion
= 0; nPortion
< Count(); nPortion
++ )
103 delete GetObject( nPortion
);
104 Remove( 0, Count() );
107 void TextPortionList::DeleteFromPortion( USHORT nDelFrom
)
109 DBG_ASSERT( ( nDelFrom
< Count() ) || ( (nDelFrom
== 0) && (Count() == 0) ), "DeleteFromPortion: Out of range" );
110 for ( USHORT nP
= nDelFrom
; nP
< Count(); nP
++ )
111 delete GetObject( nP
);
112 Remove( nDelFrom
, Count()-nDelFrom
);
115 USHORT
TextPortionList::FindPortion( USHORT nCharPos
, USHORT
& nPortionStart
, BOOL bPreferStartingPortion
)
117 // Bei nCharPos an Portion-Grenze wird die linke Portion gefunden
119 for ( USHORT nPortion
= 0; nPortion
< Count(); nPortion
++ )
121 TextPortion
* pPortion
= GetObject( nPortion
);
122 nTmpPos
= nTmpPos
+ pPortion
->GetLen();
123 if ( nTmpPos
>= nCharPos
)
125 // take this one if we don't prefer the starting portion, or if it's the last one
126 if ( ( nTmpPos
!= nCharPos
) || !bPreferStartingPortion
|| ( nPortion
== Count() - 1 ) )
128 nPortionStart
= nTmpPos
- pPortion
->GetLen();
133 DBG_ERROR( "FindPortion: Nicht gefunden!" );
134 return ( Count() - 1 );
137 USHORT
TextPortionList::GetStartPos( USHORT nPortion
)
140 for ( USHORT n
= 0; n
< nPortion
; n
++ )
142 TextPortion
* pPortion
= GetObject( n
);
143 nPos
= nPos
+ pPortion
->GetLen();
149 // -------------------------------------------------------------------------
150 // class ExtraPortionInfo
151 // -------------------------------------------------------------------------
153 ExtraPortionInfo::ExtraPortionInfo()
156 nWidthFullCompression
= 0;
157 nMaxCompression100thPercent
= 0;
158 nAsianCompressionTypes
= 0;
160 bFirstCharIsRightPunktuation
= FALSE
;
165 ExtraPortionInfo::~ExtraPortionInfo()
167 delete[] pOrgDXArray
;
170 void ExtraPortionInfo::SaveOrgDXArray( const sal_Int32
* pDXArray
, USHORT nLen
)
172 delete[] pOrgDXArray
;
173 pOrgDXArray
= new sal_Int32
[nLen
];
174 memcpy( pOrgDXArray
, pDXArray
, nLen
*sizeof(sal_Int32
) );
177 void ExtraPortionInfo::DestroyOrgDXArray()
179 delete[] pOrgDXArray
;
184 // -------------------------------------------------------------------------
186 // -------------------------------------------------------------------------
187 ParaPortion::ParaPortion( ContentNode
* pN
)
189 DBG_CTOR( EE_ParaPortion
, 0 );
195 bForceRepaint
= FALSE
;
196 nInvalidPosStart
= 0;
199 nFirstLineOffset
= 0;
203 ParaPortion::~ParaPortion()
205 DBG_DTOR( EE_ParaPortion
, 0 );
208 void ParaPortion::MarkInvalid( USHORT nStart
, short nDiff
)
210 if ( bInvalid
== FALSE
)
212 // nInvalidPosEnd = nStart; // ??? => CreateLines
213 nInvalidPosStart
= ( nDiff
>= 0 ) ? nStart
: ( nStart
+ nDiff
);
214 nInvalidDiff
= nDiff
;
218 // Einfaches hintereinander tippen
219 if ( ( nDiff
> 0 ) && ( nInvalidDiff
> 0 ) &&
220 ( ( nInvalidPosStart
+nInvalidDiff
) == nStart
) )
222 nInvalidDiff
= nInvalidDiff
+ nDiff
;
224 // Einfaches hintereinander loeschen
225 else if ( ( nDiff
< 0 ) && ( nInvalidDiff
< 0 ) && ( nInvalidPosStart
== nStart
) )
227 nInvalidPosStart
= nInvalidPosStart
+ nDiff
;
228 nInvalidDiff
= nInvalidDiff
+ nDiff
;
232 // nInvalidPosEnd = pNode->Len();
233 DBG_ASSERT( ( nDiff
>= 0 ) || ( (nStart
+nDiff
) >= 0 ), "MarkInvalid: Diff out of Range" );
234 nInvalidPosStart
= Min( nInvalidPosStart
, (USHORT
) ( nDiff
< 0 ? nStart
+nDiff
: nDiff
) );
240 aScriptInfos
.Remove( 0, aScriptInfos
.Count() );
241 aWritingDirectionInfos
.Remove( 0, aWritingDirectionInfos
.Count() );
242 // aExtraCharInfos.Remove( 0, aExtraCharInfos.Count() );
245 void ParaPortion::MarkSelectionInvalid( USHORT nStart
, USHORT
/* nEnd */ )
247 if ( bInvalid
== FALSE
)
249 nInvalidPosStart
= nStart
;
250 // nInvalidPosEnd = nEnd;
254 nInvalidPosStart
= Min( nInvalidPosStart
, nStart
);
255 // nInvalidPosEnd = pNode->Len();
260 aScriptInfos
.Remove( 0, aScriptInfos
.Count() );
261 aWritingDirectionInfos
.Remove( 0, aWritingDirectionInfos
.Count() );
262 // aExtraCharInfos.Remove( 0, aExtraCharInfos.Count() );
265 USHORT
ParaPortion::GetLineNumber( USHORT nIndex
)
267 DBG_ASSERTWARNING( aLineList
.Count(), "Leere ParaPortion in GetLine!" );
268 DBG_ASSERT( bVisible
, "Wozu GetLine() bei einem unsichtbaren Absatz?" );
270 for ( USHORT nLine
= 0; nLine
< aLineList
.Count(); nLine
++ )
272 if ( aLineList
[nLine
]->IsIn( nIndex
) )
276 // Dann sollte es am Ende der letzten Zeile sein!
277 DBG_ASSERT( nIndex
== aLineList
[ aLineList
.Count() - 1 ]->GetEnd(), "Index voll daneben!" );
278 return (aLineList
.Count()-1);
281 void ParaPortion::SetVisible( BOOL bMakeVisible
)
283 bVisible
= bMakeVisible
;
286 void ParaPortion::CorrectValuesBehindLastFormattedLine( USHORT nLastFormattedLine
)
288 USHORT nLines
= aLineList
.Count();
289 DBG_ASSERT( nLines
, "CorrectPortionNumbersFromLine: Leere Portion?" );
290 if ( nLastFormattedLine
< ( nLines
- 1 ) )
292 const EditLine
* pLastFormatted
= aLineList
[ nLastFormattedLine
];
293 const EditLine
* pUnformatted
= aLineList
[ nLastFormattedLine
+1 ];
294 short nPortionDiff
= pUnformatted
->GetStartPortion() - pLastFormatted
->GetEndPortion();
295 short nTextDiff
= pUnformatted
->GetStart() - pLastFormatted
->GetEnd();
296 nTextDiff
++; // LastFormatted->GetEnd() war incl. => 1 zuviel abgezogen!
298 // Die erste unformatierte muss genau eine Portion hinter der letzten der
299 // formatierten beginnen:
300 // Wenn in der geaenderten Zeile eine Portion gesplittet wurde,
301 // kann nLastEnd > nNextStart sein!
302 int nPDiff
= -( nPortionDiff
-1 );
303 int nTDiff
= -( nTextDiff
-1 );
304 if ( nPDiff
|| nTDiff
)
306 for ( USHORT nL
= nLastFormattedLine
+1; nL
< nLines
; nL
++ )
308 EditLine
* pLine
= aLineList
[ nL
];
310 pLine
->GetStartPortion() = sal::static_int_cast
< USHORT
>(
311 pLine
->GetStartPortion() + nPDiff
);
312 pLine
->GetEndPortion() = sal::static_int_cast
< USHORT
>(
313 pLine
->GetEndPortion() + nPDiff
);
315 pLine
->GetStart() = sal::static_int_cast
< USHORT
>(
316 pLine
->GetStart() + nTDiff
);
317 pLine
->GetEnd() = sal::static_int_cast
< USHORT
>(
318 pLine
->GetEnd() + nTDiff
);
324 DBG_ASSERT( aLineList
[ aLineList
.Count()-1 ]->GetEnd() == pNode
->Len(), "CorrectLines: Ende stimmt nicht!" );
327 // Shared reverse lookup acceleration pieces ...
329 static USHORT
FastGetPos( const VoidPtr
*pPtrArray
, USHORT nPtrArrayLen
,
330 VoidPtr pPtr
, USHORT
&rLastPos
)
332 // Through certain filter code-paths we do a lot of appends, which in
333 // turn call GetPos - creating some N^2 nightmares. If we have a
334 // non-trivially large list, do a few checks from the end first.
338 if (rLastPos
> nPtrArrayLen
- 2)
343 for( USHORT nIdx
= rLastPos
- 2; nIdx
< nEnd
; nIdx
++ )
345 if( pPtrArray
[ nIdx
] == pPtr
)
352 // The world's lamest linear search from svarray ...
353 for( USHORT nIdx
= 0; nIdx
< nPtrArrayLen
; nIdx
++ )
354 if (pPtrArray
[ nIdx
] == pPtr
)
355 return rLastPos
= nIdx
;
359 // -------------------------------------------------------------------------
360 // class ParaPortionList
361 // -------------------------------------------------------------------------
362 ParaPortionList::ParaPortionList() : nLastCache( 0 )
366 ParaPortionList::~ParaPortionList()
371 USHORT
ParaPortionList::GetPos( const ParaPortionPtr
&rPtr
) const
373 return FastGetPos( reinterpret_cast<const VoidPtr
*>( GetData() ),
374 Count(), static_cast<VoidPtr
>( rPtr
),
375 ((ParaPortionList
*)this)->nLastCache
);
378 USHORT
ContentList::GetPos( const ContentNodePtr
&rPtr
) const
380 return FastGetPos( reinterpret_cast<const VoidPtr
*>( GetData() ),
381 Count(), static_cast<VoidPtr
>( rPtr
),
382 ((ContentList
*)this)->nLastCache
);
385 void ParaPortionList::Reset()
387 for ( USHORT nPortion
= 0; nPortion
< Count(); nPortion
++ )
388 delete GetObject( nPortion
);
389 Remove( 0, Count() );
392 long ParaPortionList::GetYOffset( ParaPortion
* pPPortion
)
395 for ( USHORT nPortion
= 0; nPortion
< Count(); nPortion
++ )
397 ParaPortion
* pTmpPortion
= GetObject(nPortion
);
398 if ( pTmpPortion
== pPPortion
)
400 nHeight
+= pTmpPortion
->GetHeight();
402 DBG_ERROR( "GetYOffset: Portion nicht gefunden" );
406 USHORT
ParaPortionList::FindParagraph( long nYOffset
)
409 for ( USHORT nPortion
= 0; nPortion
< Count(); nPortion
++ )
411 nY
+= GetObject(nPortion
)->GetHeight(); // sollte auch bei !bVisible richtig sein!
415 return 0xFFFF; // solte mal ueber EE_PARA_NOT_FOUND erreicht werden!
418 void ParaPortionList::DbgCheck( EditDoc
&
425 DBG_ASSERT( Count() == rDoc
.Count(), "ParaPortionList::DbgCheck() - Count() ungleich!" );
426 for ( USHORT i
= 0; i
< Count(); i
++ )
428 DBG_ASSERT( SaveGetObject(i
), "ParaPortionList::DbgCheck() - Null-Pointer in Liste!" );
429 DBG_ASSERT( GetObject(i
)->GetNode(), "ParaPortionList::DbgCheck() - Null-Pointer in Liste(2)!" );
430 DBG_ASSERT( GetObject(i
)->GetNode() == rDoc
.GetObject(i
), "ParaPortionList::DbgCheck() - Eintraege kreuzen sich!" );
436 ContentAttribsInfo::ContentAttribsInfo( const SfxItemSet
& rParaAttribs
) :
437 aPrevParaAttribs( rParaAttribs
)
442 void ConvertItem( SfxPoolItem
& rPoolItem
, MapUnit eSourceUnit
, MapUnit eDestUnit
)
444 DBG_ASSERT( eSourceUnit
!= eDestUnit
, "ConvertItem - Why?!" );
446 switch ( rPoolItem
.Which() )
448 case EE_PARA_LRSPACE
:
450 DBG_ASSERT( rPoolItem
.IsA( TYPE( SvxLRSpaceItem
) ), "ConvertItem: Ungueltiges Item!" );
451 SvxLRSpaceItem
& rItem
= (SvxLRSpaceItem
&)rPoolItem
;
452 rItem
.SetTxtFirstLineOfst( sal::static_int_cast
< short >( OutputDevice::LogicToLogic( rItem
.GetTxtFirstLineOfst(), eSourceUnit
, eDestUnit
) ) );
453 rItem
.SetTxtLeft( OutputDevice::LogicToLogic( rItem
.GetTxtLeft(), eSourceUnit
, eDestUnit
) );
454 // rItem.SetLeft( OutputDevice::LogicToLogic( rItem.GetLeft(), eSourceUnit, eDestUnit ) ); // #96298# SetLeft manipulates nTxtLeft!
455 rItem
.SetRight( OutputDevice::LogicToLogic( rItem
.GetRight(), eSourceUnit
, eDestUnit
) );
458 case EE_PARA_ULSPACE
:
460 DBG_ASSERT( rPoolItem
.IsA( TYPE( SvxULSpaceItem
) ), "ConvertItem: Ungueltiges Item!" );
461 SvxULSpaceItem
& rItem
= (SvxULSpaceItem
&)rPoolItem
;
462 rItem
.SetUpper( sal::static_int_cast
< USHORT
>( OutputDevice::LogicToLogic( rItem
.GetUpper(), eSourceUnit
, eDestUnit
) ) );
463 rItem
.SetLower( sal::static_int_cast
< USHORT
>( OutputDevice::LogicToLogic( rItem
.GetLower(), eSourceUnit
, eDestUnit
) ) );
468 DBG_ASSERT( rPoolItem
.IsA( TYPE( SvxLineSpacingItem
) ), "ConvertItem: Ungueltiges Item!" );
469 SvxLineSpacingItem
& rItem
= (SvxLineSpacingItem
&)rPoolItem
;
470 // #96298# SetLineHeight changes also eLineSpace!
471 if ( rItem
.GetLineSpaceRule() == SVX_LINE_SPACE_MIN
)
472 rItem
.SetLineHeight( sal::static_int_cast
< USHORT
>( OutputDevice::LogicToLogic( rItem
.GetLineHeight(), eSourceUnit
, eDestUnit
) ) );
477 DBG_ASSERT( rPoolItem
.IsA( TYPE( SvxTabStopItem
) ), "ConvertItem: Ungueltiges Item!" );
478 SvxTabStopItem
& rItem
= (SvxTabStopItem
&)rPoolItem
;
479 SvxTabStopItem
aNewItem( EE_PARA_TABS
);
480 for ( USHORT i
= 0; i
< rItem
.Count(); i
++ )
482 const SvxTabStop
& rTab
= rItem
[i
];
483 SvxTabStop
aNewStop( OutputDevice::LogicToLogic( rTab
.GetTabPos(), eSourceUnit
, eDestUnit
), rTab
.GetAdjustment(), rTab
.GetDecimal(), rTab
.GetFill() );
484 aNewItem
.Insert( aNewStop
);
489 case EE_CHAR_FONTHEIGHT
:
490 case EE_CHAR_FONTHEIGHT_CJK
:
491 case EE_CHAR_FONTHEIGHT_CTL
:
493 DBG_ASSERT( rPoolItem
.IsA( TYPE( SvxFontHeightItem
) ), "ConvertItem: Ungueltiges Item!" );
494 SvxFontHeightItem
& rItem
= (SvxFontHeightItem
&)rPoolItem
;
495 rItem
.SetHeight( OutputDevice::LogicToLogic( rItem
.GetHeight(), eSourceUnit
, eDestUnit
) );
501 void ConvertAndPutItems( SfxItemSet
& rDest
, const SfxItemSet
& rSource
, const MapUnit
* pSourceUnit
, const MapUnit
* pDestUnit
)
503 const SfxItemPool
* pSourcePool
= rSource
.GetPool();
504 const SfxItemPool
* pDestPool
= rDest
.GetPool();
506 for ( USHORT nWhich
= EE_PARA_START
; nWhich
<= EE_CHAR_END
; nWhich
++ )
508 // Wenn moeglich ueber SlotID gehen...
510 USHORT nSourceWhich
= nWhich
;
511 USHORT nSlot
= pDestPool
->GetTrueSlotId( nWhich
);
514 USHORT nW
= pSourcePool
->GetTrueWhich( nSlot
);
519 if ( rSource
.GetItemState( nSourceWhich
, FALSE
) == SFX_ITEM_ON
)
521 MapUnit eSourceUnit
= pSourceUnit
? *pSourceUnit
: (MapUnit
)pSourcePool
->GetMetric( nSourceWhich
);
522 MapUnit eDestUnit
= pDestUnit
? *pDestUnit
: (MapUnit
)pDestPool
->GetMetric( nWhich
);
523 if ( eSourceUnit
!= eDestUnit
)
525 SfxPoolItem
* pItem
= rSource
.Get( nSourceWhich
).Clone();
526 // pItem->SetWhich( nWhich );
527 ConvertItem( *pItem
, eSourceUnit
, eDestUnit
);
528 rDest
.Put( *pItem
, nWhich
);
533 rDest
.Put( rSource
.Get( nSourceWhich
), nWhich
);
538 // MT 3.3.99: Waere so eigentlich richtig, aber schon seit Jahren nicht so...
539 // rDest.ClearItem( nWhich );