1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_editeng.hxx" 30 31 #include <vcl/wrkwin.hxx> 32 #include <vcl/dialog.hxx> 33 #include <vcl/msgbox.hxx> 34 #include <vcl/svapp.hxx> 35 #include <svl/smplhint.hxx> 36 37 #include <tools/rtti.hxx> 38 #include <editeng/lspcitem.hxx> 39 #include <editeng/adjitem.hxx> 40 #include <editeng/tstpitem.hxx> 41 42 #include <editdoc.hxx> 43 #include <impedit.hxx> 44 #include <editdbg.hxx> 45 46 #include <editeng/numitem.hxx> 47 48 #include <editeng/akrnitem.hxx> 49 #include <editeng/cntritem.hxx> 50 #include <editeng/colritem.hxx> 51 #include <editeng/crsditem.hxx> 52 #include <editeng/escpitem.hxx> 53 #include <editeng/fhgtitem.hxx> 54 #include <editeng/fontitem.hxx> 55 #include <editeng/kernitem.hxx> 56 #include <editeng/lrspitem.hxx> 57 #include <editeng/postitem.hxx> 58 #include <editeng/shdditem.hxx> 59 #include <editeng/udlnitem.hxx> 60 #include <editeng/ulspitem.hxx> 61 #include <editeng/wghtitem.hxx> 62 #include <editeng/wrlmitem.hxx> 63 #include <editeng/charscaleitem.hxx> 64 65 #include <vcl/svapp.hxx> // Fuer AppWindow... 66 67 DBG_NAME( EE_ParaPortion ) 68 69 SV_IMPL_VARARR( CharPosArray, sal_Int32 ); 70 71 /* 72 73 sal_Bool EditStyleSheet::HasStyleAsAnyParent( SfxStyleSheet& rStyle ) 74 { 75 if ( GetParent() == rStyle.GetName() ) 76 return sal_True; 77 78 if ( GetParent().Len() && ( GetParent() != GetName() ) ) 79 { 80 EditStyleSheet* pS = (EditStyleSheet*)GetPool().Find( GetParent(), rStyle.GetFamily() ); 81 if ( pS ) 82 return pS->HasStyleAsAnyParent( rStyle ); 83 } 84 return sal_False; 85 } 86 87 */ 88 89 // ------------------------------------------------------------------------- 90 // class TextPortionList 91 // ------------------------------------------------------------------------- 92 TextPortionList::TextPortionList() 93 { 94 } 95 96 TextPortionList::~TextPortionList() 97 { 98 Reset(); 99 } 100 101 void TextPortionList::Reset() 102 { 103 for ( sal_uInt16 nPortion = 0; nPortion < Count(); nPortion++ ) 104 delete GetObject( nPortion ); 105 Remove( 0, Count() ); 106 } 107 108 void TextPortionList::DeleteFromPortion( sal_uInt16 nDelFrom ) 109 { 110 DBG_ASSERT( ( nDelFrom < Count() ) || ( (nDelFrom == 0) && (Count() == 0) ), "DeleteFromPortion: Out of range" ); 111 for ( sal_uInt16 nP = nDelFrom; nP < Count(); nP++ ) 112 delete GetObject( nP ); 113 Remove( nDelFrom, Count()-nDelFrom ); 114 } 115 116 sal_uInt16 TextPortionList::FindPortion( sal_uInt16 nCharPos, sal_uInt16& nPortionStart, sal_Bool bPreferStartingPortion ) 117 { 118 // Bei nCharPos an Portion-Grenze wird die linke Portion gefunden 119 sal_uInt16 nTmpPos = 0; 120 for ( sal_uInt16 nPortion = 0; nPortion < Count(); nPortion++ ) 121 { 122 TextPortion* pPortion = GetObject( nPortion ); 123 nTmpPos = nTmpPos + pPortion->GetLen(); 124 if ( nTmpPos >= nCharPos ) 125 { 126 // take this one if we don't prefer the starting portion, or if it's the last one 127 if ( ( nTmpPos != nCharPos ) || !bPreferStartingPortion || ( nPortion == Count() - 1 ) ) 128 { 129 nPortionStart = nTmpPos - pPortion->GetLen(); 130 return nPortion; 131 } 132 } 133 } 134 DBG_ERROR( "FindPortion: Nicht gefunden!" ); 135 return ( Count() - 1 ); 136 } 137 138 sal_uInt16 TextPortionList::GetStartPos( sal_uInt16 nPortion ) 139 { 140 sal_uInt16 nPos = 0; 141 for ( sal_uInt16 n = 0; n < nPortion; n++ ) 142 { 143 TextPortion* pPortion = GetObject( n ); 144 nPos = nPos + pPortion->GetLen(); 145 } 146 return nPos; 147 } 148 149 150 // ------------------------------------------------------------------------- 151 // class ExtraPortionInfo 152 // ------------------------------------------------------------------------- 153 154 ExtraPortionInfo::ExtraPortionInfo() 155 { 156 nOrgWidth = 0; 157 nWidthFullCompression = 0; 158 nMaxCompression100thPercent = 0; 159 nAsianCompressionTypes = 0; 160 nPortionOffsetX = 0; 161 bFirstCharIsRightPunktuation = sal_False; 162 bCompressed = sal_False; 163 pOrgDXArray = NULL; 164 } 165 166 ExtraPortionInfo::~ExtraPortionInfo() 167 { 168 delete[] pOrgDXArray; 169 } 170 171 void ExtraPortionInfo::SaveOrgDXArray( const sal_Int32* pDXArray, sal_uInt16 nLen ) 172 { 173 delete[] pOrgDXArray; 174 pOrgDXArray = new sal_Int32[nLen]; 175 memcpy( pOrgDXArray, pDXArray, nLen*sizeof(sal_Int32) ); 176 } 177 178 void ExtraPortionInfo::DestroyOrgDXArray() 179 { 180 delete[] pOrgDXArray; 181 pOrgDXArray = NULL; 182 } 183 184 185 // ------------------------------------------------------------------------- 186 // class ParaPortion 187 // ------------------------------------------------------------------------- 188 ParaPortion::ParaPortion( ContentNode* pN ) 189 { 190 DBG_CTOR( EE_ParaPortion, 0 ); 191 192 pNode = pN; 193 bInvalid = sal_True; 194 bVisible = sal_True; 195 bSimple = sal_False; 196 bForceRepaint = sal_False; 197 nInvalidPosStart = 0; 198 nInvalidDiff = 0; 199 nHeight = 0; 200 nFirstLineOffset = 0; 201 nBulletX = 0; 202 } 203 204 ParaPortion::~ParaPortion() 205 { 206 DBG_DTOR( EE_ParaPortion, 0 ); 207 } 208 209 void ParaPortion::MarkInvalid( sal_uInt16 nStart, short nDiff ) 210 { 211 if ( bInvalid == sal_False ) 212 { 213 // nInvalidPosEnd = nStart; // ??? => CreateLines 214 nInvalidPosStart = ( nDiff >= 0 ) ? nStart : ( nStart + nDiff ); 215 nInvalidDiff = nDiff; 216 } 217 else 218 { 219 // Einfaches hintereinander tippen 220 if ( ( nDiff > 0 ) && ( nInvalidDiff > 0 ) && 221 ( ( nInvalidPosStart+nInvalidDiff ) == nStart ) ) 222 { 223 nInvalidDiff = nInvalidDiff + nDiff; 224 } 225 // Einfaches hintereinander loeschen 226 else if ( ( nDiff < 0 ) && ( nInvalidDiff < 0 ) && ( nInvalidPosStart == nStart ) ) 227 { 228 nInvalidPosStart = nInvalidPosStart + nDiff; 229 nInvalidDiff = nInvalidDiff + nDiff; 230 } 231 else 232 { 233 // nInvalidPosEnd = pNode->Len(); 234 DBG_ASSERT( ( nDiff >= 0 ) || ( (nStart+nDiff) >= 0 ), "MarkInvalid: Diff out of Range" ); 235 nInvalidPosStart = Min( nInvalidPosStart, (sal_uInt16) ( nDiff < 0 ? nStart+nDiff : nDiff ) ); 236 nInvalidDiff = 0; 237 bSimple = sal_False; 238 } 239 } 240 bInvalid = sal_True; 241 aScriptInfos.clear(); 242 aWritingDirectionInfos.clear(); 243 } 244 245 void ParaPortion::MarkSelectionInvalid( sal_uInt16 nStart, sal_uInt16 /* nEnd */ ) 246 { 247 if ( bInvalid == sal_False ) 248 { 249 nInvalidPosStart = nStart; 250 // nInvalidPosEnd = nEnd; 251 } 252 else 253 { 254 nInvalidPosStart = Min( nInvalidPosStart, nStart ); 255 // nInvalidPosEnd = pNode->Len(); 256 } 257 nInvalidDiff = 0; 258 bInvalid = sal_True; 259 bSimple = sal_False; 260 aScriptInfos.clear(); 261 aWritingDirectionInfos.clear(); 262 } 263 264 sal_uInt16 ParaPortion::GetLineNumber( sal_uInt16 nIndex ) 265 { 266 DBG_ASSERTWARNING( aLineList.Count(), "Leere ParaPortion in GetLine!" ); 267 DBG_ASSERT( bVisible, "Wozu GetLine() bei einem unsichtbaren Absatz?" ); 268 269 for ( sal_uInt16 nLine = 0; nLine < aLineList.Count(); nLine++ ) 270 { 271 if ( aLineList[nLine]->IsIn( nIndex ) ) 272 return nLine; 273 } 274 275 // Dann sollte es am Ende der letzten Zeile sein! 276 DBG_ASSERT( nIndex == aLineList[ aLineList.Count() - 1 ]->GetEnd(), "Index voll daneben!" ); 277 return (aLineList.Count()-1); 278 } 279 280 void ParaPortion::SetVisible( sal_Bool bMakeVisible ) 281 { 282 bVisible = bMakeVisible; 283 } 284 285 void ParaPortion::CorrectValuesBehindLastFormattedLine( sal_uInt16 nLastFormattedLine ) 286 { 287 sal_uInt16 nLines = aLineList.Count(); 288 DBG_ASSERT( nLines, "CorrectPortionNumbersFromLine: Leere Portion?" ); 289 if ( nLastFormattedLine < ( nLines - 1 ) ) 290 { 291 const EditLine* pLastFormatted = aLineList[ nLastFormattedLine ]; 292 const EditLine* pUnformatted = aLineList[ nLastFormattedLine+1 ]; 293 short nPortionDiff = pUnformatted->GetStartPortion() - pLastFormatted->GetEndPortion(); 294 short nTextDiff = pUnformatted->GetStart() - pLastFormatted->GetEnd(); 295 nTextDiff++; // LastFormatted->GetEnd() war incl. => 1 zuviel abgezogen! 296 297 // Die erste unformatierte muss genau eine Portion hinter der letzten der 298 // formatierten beginnen: 299 // Wenn in der geaenderten Zeile eine Portion gesplittet wurde, 300 // kann nLastEnd > nNextStart sein! 301 int nPDiff = -( nPortionDiff-1 ); 302 int nTDiff = -( nTextDiff-1 ); 303 if ( nPDiff || nTDiff ) 304 { 305 for ( sal_uInt16 nL = nLastFormattedLine+1; nL < nLines; nL++ ) 306 { 307 EditLine* pLine = aLineList[ nL ]; 308 309 pLine->GetStartPortion() = sal::static_int_cast< sal_uInt16 >( 310 pLine->GetStartPortion() + nPDiff); 311 pLine->GetEndPortion() = sal::static_int_cast< sal_uInt16 >( 312 pLine->GetEndPortion() + nPDiff); 313 314 pLine->GetStart() = sal::static_int_cast< sal_uInt16 >( 315 pLine->GetStart() + nTDiff); 316 pLine->GetEnd() = sal::static_int_cast< sal_uInt16 >( 317 pLine->GetEnd() + nTDiff); 318 319 pLine->SetValid(); 320 } 321 } 322 } 323 DBG_ASSERT( aLineList[ aLineList.Count()-1 ]->GetEnd() == pNode->Len(), "CorrectLines: Ende stimmt nicht!" ); 324 } 325 326 // Shared reverse lookup acceleration pieces ... 327 328 static sal_uInt16 FastGetPos( const VoidPtr *pPtrArray, sal_uInt16 nPtrArrayLen, 329 VoidPtr pPtr, sal_uInt16 &rLastPos ) 330 { 331 // Through certain filter code-paths we do a lot of appends, which in 332 // turn call GetPos - creating some N^2 nightmares. If we have a 333 // non-trivially large list, do a few checks from the end first. 334 if( rLastPos > 16 ) 335 { 336 sal_uInt16 nEnd; 337 if (rLastPos > nPtrArrayLen - 2) 338 nEnd = nPtrArrayLen; 339 else 340 nEnd = rLastPos + 2; 341 342 for( sal_uInt16 nIdx = rLastPos - 2; nIdx < nEnd; nIdx++ ) 343 { 344 if( pPtrArray[ nIdx ] == pPtr ) 345 { 346 rLastPos = nIdx; 347 return nIdx; 348 } 349 } 350 } 351 // The world's lamest linear search from svarray ... 352 for( sal_uInt16 nIdx = 0; nIdx < nPtrArrayLen; nIdx++ ) 353 if (pPtrArray[ nIdx ] == pPtr ) 354 return rLastPos = nIdx; 355 return USHRT_MAX; 356 } 357 358 // ------------------------------------------------------------------------- 359 // class ParaPortionList 360 // ------------------------------------------------------------------------- 361 ParaPortionList::ParaPortionList() : nLastCache( 0 ) 362 { 363 } 364 365 ParaPortionList::~ParaPortionList() 366 { 367 Reset(); 368 } 369 370 sal_uInt16 ParaPortionList::GetPos( const ParaPortionPtr &rPtr ) const 371 { 372 return FastGetPos( reinterpret_cast<const VoidPtr *>( GetData() ), 373 Count(), static_cast<VoidPtr>( rPtr ), 374 ((ParaPortionList *)this)->nLastCache ); 375 } 376 377 sal_uInt16 ContentList::GetPos( const ContentNodePtr &rPtr ) const 378 { 379 return FastGetPos( reinterpret_cast<const VoidPtr *>( GetData() ), 380 Count(), static_cast<VoidPtr>( rPtr ), 381 ((ContentList *)this)->nLastCache ); 382 } 383 384 void ParaPortionList::Reset() 385 { 386 for ( sal_uInt16 nPortion = 0; nPortion < Count(); nPortion++ ) 387 delete GetObject( nPortion ); 388 Remove( 0, Count() ); 389 } 390 391 long ParaPortionList::GetYOffset( ParaPortion* pPPortion ) 392 { 393 long nHeight = 0; 394 for ( sal_uInt16 nPortion = 0; nPortion < Count(); nPortion++ ) 395 { 396 ParaPortion* pTmpPortion = GetObject(nPortion); 397 if ( pTmpPortion == pPPortion ) 398 return nHeight; 399 nHeight += pTmpPortion->GetHeight(); 400 } 401 DBG_ERROR( "GetYOffset: Portion nicht gefunden" ); 402 return nHeight; 403 } 404 405 sal_uInt16 ParaPortionList::FindParagraph( long nYOffset ) 406 { 407 long nY = 0; 408 for ( sal_uInt16 nPortion = 0; nPortion < Count(); nPortion++ ) 409 { 410 nY += GetObject(nPortion)->GetHeight(); // sollte auch bei !bVisible richtig sein! 411 if ( nY > nYOffset ) 412 return nPortion; 413 } 414 return 0xFFFF; // solte mal ueber EE_PARA_NOT_FOUND erreicht werden! 415 } 416 417 void ParaPortionList::DbgCheck( EditDoc& 418 #ifdef DBG_UTIL 419 rDoc 420 #endif 421 ) 422 { 423 #ifdef DBG_UTIL 424 DBG_ASSERT( Count() == rDoc.Count(), "ParaPortionList::DbgCheck() - Count() ungleich!" ); 425 for ( sal_uInt16 i = 0; i < Count(); i++ ) 426 { 427 DBG_ASSERT( SaveGetObject(i), "ParaPortionList::DbgCheck() - Null-Pointer in Liste!" ); 428 DBG_ASSERT( GetObject(i)->GetNode(), "ParaPortionList::DbgCheck() - Null-Pointer in Liste(2)!" ); 429 DBG_ASSERT( GetObject(i)->GetNode() == rDoc.GetObject(i), "ParaPortionList::DbgCheck() - Eintraege kreuzen sich!" ); 430 } 431 #endif 432 } 433 434 435 ContentAttribsInfo::ContentAttribsInfo( const SfxItemSet& rParaAttribs ) : 436 aPrevParaAttribs( rParaAttribs) 437 { 438 } 439 440 441 void ConvertItem( SfxPoolItem& rPoolItem, MapUnit eSourceUnit, MapUnit eDestUnit ) 442 { 443 DBG_ASSERT( eSourceUnit != eDestUnit, "ConvertItem - Why?!" ); 444 445 switch ( rPoolItem.Which() ) 446 { 447 case EE_PARA_LRSPACE: 448 { 449 DBG_ASSERT( rPoolItem.IsA( TYPE( SvxLRSpaceItem ) ), "ConvertItem: Ungueltiges Item!" ); 450 SvxLRSpaceItem& rItem = (SvxLRSpaceItem&)rPoolItem; 451 rItem.SetTxtFirstLineOfst( sal::static_int_cast< short >( OutputDevice::LogicToLogic( rItem.GetTxtFirstLineOfst(), eSourceUnit, eDestUnit ) ) ); 452 rItem.SetTxtLeft( OutputDevice::LogicToLogic( rItem.GetTxtLeft(), eSourceUnit, eDestUnit ) ); 453 // rItem.SetLeft( OutputDevice::LogicToLogic( rItem.GetLeft(), eSourceUnit, eDestUnit ) ); // #96298# SetLeft manipulates nTxtLeft! 454 rItem.SetRight( OutputDevice::LogicToLogic( rItem.GetRight(), eSourceUnit, eDestUnit ) ); 455 } 456 break; 457 case EE_PARA_ULSPACE: 458 { 459 DBG_ASSERT( rPoolItem.IsA( TYPE( SvxULSpaceItem ) ), "ConvertItem: Ungueltiges Item!" ); 460 SvxULSpaceItem& rItem = (SvxULSpaceItem&)rPoolItem; 461 rItem.SetUpper( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetUpper(), eSourceUnit, eDestUnit ) ) ); 462 rItem.SetLower( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetLower(), eSourceUnit, eDestUnit ) ) ); 463 } 464 break; 465 case EE_PARA_SBL: 466 { 467 DBG_ASSERT( rPoolItem.IsA( TYPE( SvxLineSpacingItem ) ), "ConvertItem: Ungueltiges Item!" ); 468 SvxLineSpacingItem& rItem = (SvxLineSpacingItem&)rPoolItem; 469 // #96298# SetLineHeight changes also eLineSpace! 470 if ( rItem.GetLineSpaceRule() == SVX_LINE_SPACE_MIN ) 471 rItem.SetLineHeight( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetLineHeight(), eSourceUnit, eDestUnit ) ) ); 472 } 473 break; 474 case EE_PARA_TABS: 475 { 476 DBG_ASSERT( rPoolItem.IsA( TYPE( SvxTabStopItem ) ), "ConvertItem: Ungueltiges Item!" ); 477 SvxTabStopItem& rItem = (SvxTabStopItem&)rPoolItem; 478 SvxTabStopItem aNewItem( EE_PARA_TABS ); 479 for ( sal_uInt16 i = 0; i < rItem.Count(); i++ ) 480 { 481 const SvxTabStop& rTab = rItem[i]; 482 SvxTabStop aNewStop( OutputDevice::LogicToLogic( rTab.GetTabPos(), eSourceUnit, eDestUnit ), rTab.GetAdjustment(), rTab.GetDecimal(), rTab.GetFill() ); 483 aNewItem.Insert( aNewStop ); 484 } 485 rItem = aNewItem; 486 } 487 break; 488 case EE_CHAR_FONTHEIGHT: 489 case EE_CHAR_FONTHEIGHT_CJK: 490 case EE_CHAR_FONTHEIGHT_CTL: 491 { 492 DBG_ASSERT( rPoolItem.IsA( TYPE( SvxFontHeightItem ) ), "ConvertItem: Ungueltiges Item!" ); 493 SvxFontHeightItem& rItem = (SvxFontHeightItem&)rPoolItem; 494 rItem.SetHeight( OutputDevice::LogicToLogic( rItem.GetHeight(), eSourceUnit, eDestUnit ) ); 495 } 496 break; 497 } 498 } 499 500 void ConvertAndPutItems( SfxItemSet& rDest, const SfxItemSet& rSource, const MapUnit* pSourceUnit, const MapUnit* pDestUnit ) 501 { 502 const SfxItemPool* pSourcePool = rSource.GetPool(); 503 const SfxItemPool* pDestPool = rDest.GetPool(); 504 505 for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ ) 506 { 507 // Wenn moeglich ueber SlotID gehen... 508 509 sal_uInt16 nSourceWhich = nWhich; 510 sal_uInt16 nSlot = pDestPool->GetTrueSlotId( nWhich ); 511 if ( nSlot ) 512 { 513 sal_uInt16 nW = pSourcePool->GetTrueWhich( nSlot ); 514 if ( nW ) 515 nSourceWhich = nW; 516 } 517 518 if ( rSource.GetItemState( nSourceWhich, sal_False ) == SFX_ITEM_ON ) 519 { 520 MapUnit eSourceUnit = pSourceUnit ? *pSourceUnit : (MapUnit)pSourcePool->GetMetric( nSourceWhich ); 521 MapUnit eDestUnit = pDestUnit ? *pDestUnit : (MapUnit)pDestPool->GetMetric( nWhich ); 522 if ( eSourceUnit != eDestUnit ) 523 { 524 SfxPoolItem* pItem = rSource.Get( nSourceWhich ).Clone(); 525 // pItem->SetWhich( nWhich ); 526 ConvertItem( *pItem, eSourceUnit, eDestUnit ); 527 rDest.Put( *pItem, nWhich ); 528 delete pItem; 529 } 530 else 531 { 532 rDest.Put( rSource.Get( nSourceWhich ), nWhich ); 533 } 534 } 535 else 536 { 537 // MT 3.3.99: Waere so eigentlich richtig, aber schon seit Jahren nicht so... 538 // rDest.ClearItem( nWhich ); 539 } 540 } 541 } 542 543