merge the formfield patch from ooo-build
[ooovba.git] / svx / source / editeng / editdoc2.cxx
blobe0d5fc27e9a22440130b955d99c2bf37cf1a0b6d
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 $
10 * $Revision: 1.19 $
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() )
75 return TRUE;
77 if ( GetParent().Len() && ( GetParent() != GetName() ) )
79 EditStyleSheet* pS = (EditStyleSheet*)GetPool().Find( GetParent(), rStyle.GetFamily() );
80 if ( pS )
81 return pS->HasStyleAsAnyParent( rStyle );
83 return FALSE;
88 // -------------------------------------------------------------------------
89 // class TextPortionList
90 // -------------------------------------------------------------------------
91 TextPortionList::TextPortionList()
95 TextPortionList::~TextPortionList()
97 Reset();
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
118 USHORT nTmpPos = 0;
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();
129 return nPortion;
133 DBG_ERROR( "FindPortion: Nicht gefunden!" );
134 return ( Count() - 1 );
137 USHORT TextPortionList::GetStartPos( USHORT nPortion )
139 USHORT nPos = 0;
140 for ( USHORT n = 0; n < nPortion; n++ )
142 TextPortion* pPortion = GetObject( n );
143 nPos = nPos + pPortion->GetLen();
145 return nPos;
149 // -------------------------------------------------------------------------
150 // class ExtraPortionInfo
151 // -------------------------------------------------------------------------
153 ExtraPortionInfo::ExtraPortionInfo()
155 nOrgWidth = 0;
156 nWidthFullCompression = 0;
157 nMaxCompression100thPercent = 0;
158 nAsianCompressionTypes = 0;
159 nPortionOffsetX = 0;
160 bFirstCharIsRightPunktuation = FALSE;
161 bCompressed = FALSE;
162 pOrgDXArray = NULL;
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;
180 pOrgDXArray = NULL;
184 // -------------------------------------------------------------------------
185 // class ParaPortion
186 // -------------------------------------------------------------------------
187 ParaPortion::ParaPortion( ContentNode* pN )
189 DBG_CTOR( EE_ParaPortion, 0 );
191 pNode = pN;
192 bInvalid = TRUE;
193 bVisible = TRUE;
194 bSimple = FALSE;
195 bForceRepaint = FALSE;
196 nInvalidPosStart = 0;
197 nInvalidDiff = 0;
198 nHeight = 0;
199 nFirstLineOffset = 0;
200 nBulletX = 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;
216 else
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;
230 else
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 ) );
235 nInvalidDiff = 0;
236 bSimple = FALSE;
239 bInvalid = TRUE;
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;
252 else
254 nInvalidPosStart = Min( nInvalidPosStart, nStart );
255 // nInvalidPosEnd = pNode->Len();
257 nInvalidDiff = 0;
258 bInvalid = TRUE;
259 bSimple = FALSE;
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 ) )
273 return nLine;
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);
320 pLine->SetValid();
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.
335 if( rLastPos > 16 )
337 USHORT nEnd;
338 if (rLastPos > nPtrArrayLen - 2)
339 nEnd = nPtrArrayLen;
340 else
341 nEnd = rLastPos + 2;
343 for( USHORT nIdx = rLastPos - 2; nIdx < nEnd; nIdx++ )
345 if( pPtrArray[ nIdx ] == pPtr )
347 rLastPos = nIdx;
348 return nIdx;
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;
356 return USHRT_MAX;
359 // -------------------------------------------------------------------------
360 // class ParaPortionList
361 // -------------------------------------------------------------------------
362 ParaPortionList::ParaPortionList() : nLastCache( 0 )
366 ParaPortionList::~ParaPortionList()
368 Reset();
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 )
394 long nHeight = 0;
395 for ( USHORT nPortion = 0; nPortion < Count(); nPortion++ )
397 ParaPortion* pTmpPortion = GetObject(nPortion);
398 if ( pTmpPortion == pPPortion )
399 return nHeight;
400 nHeight += pTmpPortion->GetHeight();
402 DBG_ERROR( "GetYOffset: Portion nicht gefunden" );
403 return nHeight;
406 USHORT ParaPortionList::FindParagraph( long nYOffset )
408 long nY = 0;
409 for ( USHORT nPortion = 0; nPortion < Count(); nPortion++ )
411 nY += GetObject(nPortion)->GetHeight(); // sollte auch bei !bVisible richtig sein!
412 if ( nY > nYOffset )
413 return nPortion;
415 return 0xFFFF; // solte mal ueber EE_PARA_NOT_FOUND erreicht werden!
418 void ParaPortionList::DbgCheck( EditDoc&
419 #ifdef DBG_UTIL
420 rDoc
421 #endif
424 #ifdef DBG_UTIL
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!" );
432 #endif
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 ) );
457 break;
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 ) ) );
465 break;
466 case EE_PARA_SBL:
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 ) ) );
474 break;
475 case EE_PARA_TABS:
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 );
486 rItem = aNewItem;
488 break;
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 ) );
497 break;
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 );
512 if ( nSlot )
514 USHORT nW = pSourcePool->GetTrueWhich( nSlot );
515 if ( nW )
516 nSourceWhich = nW;
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 );
529 delete pItem;
531 else
533 rDest.Put( rSource.Get( nSourceWhich ), nWhich );
536 else
538 // MT 3.3.99: Waere so eigentlich richtig, aber schon seit Jahren nicht so...
539 // rDest.ClearItem( nWhich );