1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <editeng/tstpitem.hxx> 21 #include <editeng/colritem.hxx> 22 #include <editeng/fontitem.hxx> 23 #include <editeng/crossedoutitem.hxx> 24 #include <editeng/fhgtitem.hxx> 25 #include <editeng/flditem.hxx> 26 #include <editeng/postitem.hxx> 27 #include <editeng/kernitem.hxx> 28 #include <editeng/wrlmitem.hxx> 29 #include <editeng/wghtitem.hxx> 30 #include <editeng/udlnitem.hxx> 31 #include <editeng/cmapitem.hxx> 32 #include <editeng/contouritem.hxx> 33 #include <editeng/escapementitem.hxx> 34 #include <editeng/shdditem.hxx> 35 #include <editeng/autokernitem.hxx> 36 #include <editeng/langitem.hxx> 37 #include <editeng/emphasismarkitem.hxx> 38 #include <editeng/charscaleitem.hxx> 39 #include <editeng/charreliefitem.hxx> 40 #include <editeng/xmlcnitm.hxx> 41 #include <editeng/editids.hrc> 42 #include <editeng/editdata.hxx> 43 #include <editeng/lrspitem.hxx> 44 #include <editeng/ulspitem.hxx> 45 #include <editeng/lspcitem.hxx> 46 47 #include <editdoc.hxx> 48 #include "editdbg.hxx" 49 #include <editeng/eerdll.hxx> 50 #include <eerdll2.hxx> 51 #include "impedit.hxx" 52 53 #include <rtl/ustrbuf.hxx> 54 #include <sal/log.hxx> 55 #include <osl/diagnose.h> 56 57 #include <svl/grabbagitem.hxx> 58 #include <tools/stream.hxx> 59 #include <tools/debug.hxx> 60 #include <com/sun/star/i18n/ScriptType.hpp> 61 #include <libxml/xmlwriter.h> 62 63 #include <algorithm> 64 #include <cassert> 65 #include <limits> 66 #include <memory> 67 #include <set> 68 #include <string_view> 69 70 using namespace ::com::sun::star; 71 72 73 sal_uInt16 GetScriptItemId( sal_uInt16 nItemId, SvtScriptType nScriptType ) 74 { 75 sal_uInt16 nId = nItemId; 76 77 if ( ( nScriptType == SvtScriptType::ASIAN ) || 78 ( nScriptType == SvtScriptType::COMPLEX ) ) 79 { 80 switch ( nItemId ) 81 { 82 case EE_CHAR_LANGUAGE: 83 nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_LANGUAGE_CJK : EE_CHAR_LANGUAGE_CTL; 84 break; 85 case EE_CHAR_FONTINFO: 86 nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_FONTINFO_CJK : EE_CHAR_FONTINFO_CTL; 87 break; 88 case EE_CHAR_FONTHEIGHT: 89 nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_FONTHEIGHT_CJK : EE_CHAR_FONTHEIGHT_CTL; 90 break; 91 case EE_CHAR_WEIGHT: 92 nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_WEIGHT_CJK : EE_CHAR_WEIGHT_CTL; 93 break; 94 case EE_CHAR_ITALIC: 95 nId = ( nScriptType == SvtScriptType::ASIAN ) ? EE_CHAR_ITALIC_CJK : EE_CHAR_ITALIC_CTL; 96 break; 97 } 98 } 99 100 return nId; 101 } 102 103 bool IsScriptItemValid( sal_uInt16 nItemId, short nScriptType ) 104 { 105 bool bValid = true; 106 107 switch ( nItemId ) 108 { 109 case EE_CHAR_LANGUAGE: 110 bValid = nScriptType == i18n::ScriptType::LATIN; 111 break; 112 case EE_CHAR_LANGUAGE_CJK: 113 bValid = nScriptType == i18n::ScriptType::ASIAN; 114 break; 115 case EE_CHAR_LANGUAGE_CTL: 116 bValid = nScriptType == i18n::ScriptType::COMPLEX; 117 break; 118 case EE_CHAR_FONTINFO: 119 bValid = nScriptType == i18n::ScriptType::LATIN; 120 break; 121 case EE_CHAR_FONTINFO_CJK: 122 bValid = nScriptType == i18n::ScriptType::ASIAN; 123 break; 124 case EE_CHAR_FONTINFO_CTL: 125 bValid = nScriptType == i18n::ScriptType::COMPLEX; 126 break; 127 case EE_CHAR_FONTHEIGHT: 128 bValid = nScriptType == i18n::ScriptType::LATIN; 129 break; 130 case EE_CHAR_FONTHEIGHT_CJK: 131 bValid = nScriptType == i18n::ScriptType::ASIAN; 132 break; 133 case EE_CHAR_FONTHEIGHT_CTL: 134 bValid = nScriptType == i18n::ScriptType::COMPLEX; 135 break; 136 case EE_CHAR_WEIGHT: 137 bValid = nScriptType == i18n::ScriptType::LATIN; 138 break; 139 case EE_CHAR_WEIGHT_CJK: 140 bValid = nScriptType == i18n::ScriptType::ASIAN; 141 break; 142 case EE_CHAR_WEIGHT_CTL: 143 bValid = nScriptType == i18n::ScriptType::COMPLEX; 144 break; 145 case EE_CHAR_ITALIC: 146 bValid = nScriptType == i18n::ScriptType::LATIN; 147 break; 148 case EE_CHAR_ITALIC_CJK: 149 bValid = nScriptType == i18n::ScriptType::ASIAN; 150 break; 151 case EE_CHAR_ITALIC_CTL: 152 bValid = nScriptType == i18n::ScriptType::COMPLEX; 153 break; 154 } 155 156 return bValid; 157 } 158 159 const SfxItemInfo aItemInfos[EDITITEMCOUNT] = { 160 { SID_ATTR_FRAMEDIRECTION, true }, // EE_PARA_WRITINGDIR 161 { 0, true }, // EE_PARA_XMLATTRIBS 162 { SID_ATTR_PARA_HANGPUNCTUATION, true }, // EE_PARA_HANGINGPUNCTUATION 163 { SID_ATTR_PARA_FORBIDDEN_RULES, true }, // EE_PARA_FORBIDDENRULES 164 { SID_ATTR_PARA_SCRIPTSPACE, true }, // EE_PARA_ASIANCJKSPACING 165 { SID_ATTR_NUMBERING_RULE, true }, // EE_PARA_NUMBULL 166 { 0, true }, // EE_PARA_HYPHENATE 167 { 0, true }, // EE_PARA_BULLETSTATE 168 { 0, true }, // EE_PARA_OUTLLRSPACE 169 { SID_ATTR_PARA_OUTLLEVEL, true }, // EE_PARA_OUTLLEVEL 170 { SID_ATTR_PARA_BULLET, true }, // EE_PARA_BULLET 171 { SID_ATTR_LRSPACE, true }, // EE_PARA_LRSPACE 172 { SID_ATTR_ULSPACE, true }, // EE_PARA_ULSPACE 173 { SID_ATTR_PARA_LINESPACE, true }, // EE_PARA_SBL 174 { SID_ATTR_PARA_ADJUST, true }, // EE_PARA_JUST 175 { SID_ATTR_TABSTOP, true }, // EE_PARA_TABS 176 { SID_ATTR_ALIGN_HOR_JUSTIFY_METHOD, true }, // EE_PARA_JUST_METHOD 177 { SID_ATTR_ALIGN_VER_JUSTIFY, true }, // EE_PARA_VER_JUST 178 { SID_ATTR_CHAR_COLOR, true }, // EE_CHAR_COLOR 179 { SID_ATTR_CHAR_FONT, true }, // EE_CHAR_FONTINFO 180 { SID_ATTR_CHAR_FONTHEIGHT, true }, // EE_CHAR_FONTHEIGHT 181 { SID_ATTR_CHAR_SCALEWIDTH, true }, // EE_CHAR_FONTWIDTH 182 { SID_ATTR_CHAR_WEIGHT, true }, // EE_CHAR_WEIGHT 183 { SID_ATTR_CHAR_UNDERLINE, true }, // EE_CHAR_UNDERLINE 184 { SID_ATTR_CHAR_STRIKEOUT, true }, // EE_CHAR_STRIKEOUT 185 { SID_ATTR_CHAR_POSTURE, true }, // EE_CHAR_ITALIC 186 { SID_ATTR_CHAR_CONTOUR, true }, // EE_CHAR_OUTLINE 187 { SID_ATTR_CHAR_SHADOWED, true }, // EE_CHAR_SHADOW 188 { SID_ATTR_CHAR_ESCAPEMENT, true }, // EE_CHAR_ESCAPEMENT 189 { SID_ATTR_CHAR_AUTOKERN, true }, // EE_CHAR_PAIRKERNING 190 { SID_ATTR_CHAR_KERNING, true }, // EE_CHAR_KERNING 191 { SID_ATTR_CHAR_WORDLINEMODE, true }, // EE_CHAR_WLM 192 { SID_ATTR_CHAR_LANGUAGE, true }, // EE_CHAR_LANGUAGE 193 { SID_ATTR_CHAR_CJK_LANGUAGE, true }, // EE_CHAR_LANGUAGE_CJK 194 { SID_ATTR_CHAR_CTL_LANGUAGE, true }, // EE_CHAR_LANGUAGE_CTL 195 { SID_ATTR_CHAR_CJK_FONT, true }, // EE_CHAR_FONTINFO_CJK 196 { SID_ATTR_CHAR_CTL_FONT, true }, // EE_CHAR_FONTINFO_CTL 197 { SID_ATTR_CHAR_CJK_FONTHEIGHT, true }, // EE_CHAR_FONTHEIGHT_CJK 198 { SID_ATTR_CHAR_CTL_FONTHEIGHT, true }, // EE_CHAR_FONTHEIGHT_CTL 199 { SID_ATTR_CHAR_CJK_WEIGHT, true }, // EE_CHAR_WEIGHT_CJK 200 { SID_ATTR_CHAR_CTL_WEIGHT, true }, // EE_CHAR_WEIGHT_CTL 201 { SID_ATTR_CHAR_CJK_POSTURE, true }, // EE_CHAR_ITALIC_CJK 202 { SID_ATTR_CHAR_CTL_POSTURE, true }, // EE_CHAR_ITALIC_CTL 203 { SID_ATTR_CHAR_EMPHASISMARK, true }, // EE_CHAR_EMPHASISMARK 204 { SID_ATTR_CHAR_RELIEF, true }, // EE_CHAR_RELIEF 205 { 0, true }, // EE_CHAR_RUBI_DUMMY 206 { 0, true }, // EE_CHAR_XMLATTRIBS 207 { SID_ATTR_CHAR_OVERLINE, true }, // EE_CHAR_OVERLINE 208 { SID_ATTR_CHAR_CASEMAP, true }, // EE_CHAR_CASEMAP 209 { SID_ATTR_CHAR_GRABBAG, true }, // EE_CHAR_GRABBAG 210 { SID_ATTR_CHAR_BACK_COLOR, true }, // EE_CHAR_BKGCOLOR 211 { 0, true }, // EE_FEATURE_TAB 212 { 0, true }, // EE_FEATURE_LINEBR 213 { SID_ATTR_CHAR_CHARSETCOLOR, true }, // EE_FEATURE_NOTCONV 214 { SID_FIELD, false }, // EE_FEATURE_FIELD 215 }; 216 217 EditCharAttrib* MakeCharAttrib( SfxItemPool& rPool, const SfxPoolItem& rAttr, sal_Int32 nS, sal_Int32 nE ) 218 { 219 // Create a new attribute in the pool 220 const SfxPoolItem& rNew = rPool.Put( rAttr ); 221 222 EditCharAttrib* pNew = nullptr; 223 switch( rNew.Which() ) 224 { 225 case EE_CHAR_LANGUAGE: 226 case EE_CHAR_LANGUAGE_CJK: 227 case EE_CHAR_LANGUAGE_CTL: 228 { 229 pNew = new EditCharAttribLanguage( static_cast<const SvxLanguageItem&>(rNew), nS, nE ); 230 } 231 break; 232 case EE_CHAR_COLOR: 233 { 234 pNew = new EditCharAttribColor( static_cast<const SvxColorItem&>(rNew), nS, nE ); 235 } 236 break; 237 case EE_CHAR_FONTINFO: 238 case EE_CHAR_FONTINFO_CJK: 239 case EE_CHAR_FONTINFO_CTL: 240 { 241 pNew = new EditCharAttribFont( static_cast<const SvxFontItem&>(rNew), nS, nE ); 242 } 243 break; 244 case EE_CHAR_FONTHEIGHT: 245 case EE_CHAR_FONTHEIGHT_CJK: 246 case EE_CHAR_FONTHEIGHT_CTL: 247 { 248 pNew = new EditCharAttribFontHeight( static_cast<const SvxFontHeightItem&>(rNew), nS, nE ); 249 } 250 break; 251 case EE_CHAR_FONTWIDTH: 252 { 253 pNew = new EditCharAttribFontWidth( static_cast<const SvxCharScaleWidthItem&>(rNew), nS, nE ); 254 } 255 break; 256 case EE_CHAR_WEIGHT: 257 case EE_CHAR_WEIGHT_CJK: 258 case EE_CHAR_WEIGHT_CTL: 259 { 260 pNew = new EditCharAttribWeight( static_cast<const SvxWeightItem&>(rNew), nS, nE ); 261 } 262 break; 263 case EE_CHAR_UNDERLINE: 264 { 265 pNew = new EditCharAttribUnderline( static_cast<const SvxUnderlineItem&>(rNew), nS, nE ); 266 } 267 break; 268 case EE_CHAR_OVERLINE: 269 { 270 pNew = new EditCharAttribOverline( static_cast<const SvxOverlineItem&>(rNew), nS, nE ); 271 } 272 break; 273 case EE_CHAR_EMPHASISMARK: 274 { 275 pNew = new EditCharAttribEmphasisMark( static_cast<const SvxEmphasisMarkItem&>(rNew), nS, nE ); 276 } 277 break; 278 case EE_CHAR_RELIEF: 279 { 280 pNew = new EditCharAttribRelief( static_cast<const SvxCharReliefItem&>(rNew), nS, nE ); 281 } 282 break; 283 case EE_CHAR_STRIKEOUT: 284 { 285 pNew = new EditCharAttribStrikeout( static_cast<const SvxCrossedOutItem&>(rNew), nS, nE ); 286 } 287 break; 288 case EE_CHAR_ITALIC: 289 case EE_CHAR_ITALIC_CJK: 290 case EE_CHAR_ITALIC_CTL: 291 { 292 pNew = new EditCharAttribItalic( static_cast<const SvxPostureItem&>(rNew), nS, nE ); 293 } 294 break; 295 case EE_CHAR_OUTLINE: 296 { 297 pNew = new EditCharAttribOutline( static_cast<const SvxContourItem&>(rNew), nS, nE ); 298 } 299 break; 300 case EE_CHAR_SHADOW: 301 { 302 pNew = new EditCharAttribShadow( static_cast<const SvxShadowedItem&>(rNew), nS, nE ); 303 } 304 break; 305 case EE_CHAR_ESCAPEMENT: 306 { 307 pNew = new EditCharAttribEscapement( static_cast<const SvxEscapementItem&>(rNew), nS, nE ); 308 } 309 break; 310 case EE_CHAR_PAIRKERNING: 311 { 312 pNew = new EditCharAttribPairKerning( static_cast<const SvxAutoKernItem&>(rNew), nS, nE ); 313 } 314 break; 315 case EE_CHAR_KERNING: 316 { 317 pNew = new EditCharAttribKerning( static_cast<const SvxKerningItem&>(rNew), nS, nE ); 318 } 319 break; 320 case EE_CHAR_WLM: 321 { 322 pNew = new EditCharAttribWordLineMode( static_cast<const SvxWordLineModeItem&>(rNew), nS, nE ); 323 } 324 break; 325 case EE_CHAR_XMLATTRIBS: 326 { 327 pNew = new EditCharAttrib( rNew, nS, nE ); // Attribute is only for holding XML information... 328 } 329 break; 330 case EE_CHAR_CASEMAP: 331 { 332 pNew = new EditCharAttribCaseMap( static_cast<const SvxCaseMapItem&>(rNew), nS, nE ); 333 } 334 break; 335 case EE_CHAR_GRABBAG: 336 { 337 pNew = new EditCharAttribGrabBag( static_cast<const SfxGrabBagItem&>(rNew), nS, nE ); 338 } 339 break; 340 case EE_FEATURE_TAB: 341 { 342 pNew = new EditCharAttribTab( static_cast<const SfxVoidItem&>(rNew), nS ); 343 } 344 break; 345 case EE_FEATURE_LINEBR: 346 { 347 pNew = new EditCharAttribLineBreak( static_cast<const SfxVoidItem&>(rNew), nS ); 348 } 349 break; 350 case EE_FEATURE_FIELD: 351 { 352 pNew = new EditCharAttribField( static_cast<const SvxFieldItem&>(rNew), nS ); 353 } 354 break; 355 case EE_CHAR_BKGCOLOR: 356 { 357 pNew = new EditCharAttribBackgroundColor( static_cast<const SvxBackgroundColorItem&>(rNew), nS, nE ); 358 } 359 break; 360 default: 361 { 362 OSL_FAIL( "Invalid Attribute!" ); 363 } 364 } 365 return pNew; 366 } 367 368 TextPortionList::TextPortionList() 369 { 370 } 371 372 TextPortionList::~TextPortionList() 373 { 374 Reset(); 375 } 376 377 void TextPortionList::Reset() 378 { 379 maPortions.clear(); 380 } 381 382 void TextPortionList::DeleteFromPortion(sal_Int32 nDelFrom) 383 { 384 assert((nDelFrom < static_cast<sal_Int32>(maPortions.size())) || ((nDelFrom == 0) && maPortions.empty())); 385 PortionsType::iterator it = maPortions.begin(); 386 std::advance(it, nDelFrom); 387 maPortions.erase(it, maPortions.end()); 388 } 389 390 sal_Int32 TextPortionList::Count() const 391 { 392 return static_cast<sal_Int32>(maPortions.size()); 393 } 394 395 const TextPortion& TextPortionList::operator[](sal_Int32 nPos) const 396 { 397 return *maPortions[nPos].get(); 398 } 399 400 TextPortion& TextPortionList::operator[](sal_Int32 nPos) 401 { 402 return *maPortions[nPos].get(); 403 } 404 405 void TextPortionList::Append(TextPortion* p) 406 { 407 maPortions.push_back(std::unique_ptr<TextPortion>(p)); 408 } 409 410 void TextPortionList::Insert(sal_Int32 nPos, TextPortion* p) 411 { 412 maPortions.insert(maPortions.begin()+nPos, std::unique_ptr<TextPortion>(p)); 413 } 414 415 void TextPortionList::Remove(sal_Int32 nPos) 416 { 417 maPortions.erase(maPortions.begin()+nPos); 418 } 419 420 namespace { 421 422 class FindTextPortionByAddress 423 { 424 const TextPortion* mp; 425 public: 426 explicit FindTextPortionByAddress(const TextPortion* p) : mp(p) {} 427 bool operator() (const std::unique_ptr<TextPortion>& v) const 428 { 429 return v.get() == mp; 430 } 431 }; 432 433 } 434 435 sal_Int32 TextPortionList::GetPos(const TextPortion* p) const 436 { 437 PortionsType::const_iterator it = 438 std::find_if(maPortions.begin(), maPortions.end(), FindTextPortionByAddress(p)); 439 440 if (it == maPortions.end()) 441 return std::numeric_limits<sal_Int32>::max(); // not found. 442 443 return std::distance(maPortions.begin(), it); 444 } 445 446 sal_Int32 TextPortionList::FindPortion( 447 sal_Int32 nCharPos, sal_Int32& nPortionStart, bool bPreferStartingPortion) const 448 { 449 // When nCharPos at portion limit, the left portion is found 450 sal_Int32 nTmpPos = 0; 451 sal_Int32 n = maPortions.size(); 452 for (sal_Int32 i = 0; i < n; ++i) 453 { 454 const TextPortion& rPortion = *maPortions[i].get(); 455 nTmpPos = nTmpPos + rPortion.GetLen(); 456 if ( nTmpPos >= nCharPos ) 457 { 458 // take this one if we don't prefer the starting portion, or if it's the last one 459 if ( ( nTmpPos != nCharPos ) || !bPreferStartingPortion || ( i == n-1 ) ) 460 { 461 nPortionStart = nTmpPos - rPortion.GetLen(); 462 return i; 463 } 464 } 465 } 466 OSL_FAIL( "FindPortion: Not found!" ); 467 return n - 1; 468 } 469 470 sal_Int32 TextPortionList::GetStartPos(sal_Int32 nPortion) 471 { 472 sal_Int32 nPos = 0; 473 for (sal_Int32 i = 0; i < nPortion; ++i) 474 { 475 const TextPortion& rPortion = *maPortions[i].get(); 476 nPos = nPos + rPortion.GetLen(); 477 } 478 return nPos; 479 } 480 481 ExtraPortionInfo::ExtraPortionInfo() 482 : nOrgWidth(0) 483 , nWidthFullCompression(0) 484 , nPortionOffsetX(0) 485 , nMaxCompression100thPercent(0) 486 , nAsianCompressionTypes(AsianCompressionFlags::Normal) 487 , bFirstCharIsRightPunktuation(false) 488 , bCompressed(false) 489 , lineBreaksList() 490 { 491 } 492 493 ExtraPortionInfo::~ExtraPortionInfo() 494 { 495 } 496 497 void ExtraPortionInfo::SaveOrgDXArray( const long* pDXArray, sal_Int32 nLen ) 498 { 499 if (pDXArray) 500 { 501 pOrgDXArray.reset(new long[nLen]); 502 memcpy( pOrgDXArray.get(), pDXArray, nLen * sizeof(long) ); 503 } 504 else 505 pOrgDXArray.reset(); 506 } 507 508 ParaPortion::ParaPortion( ContentNode* pN ) : 509 pNode(pN), 510 nHeight(0), 511 nInvalidPosStart(0), 512 nFirstLineOffset(0), 513 nBulletX(0), 514 nInvalidDiff(0), 515 bInvalid(true), 516 bSimple(false), 517 bVisible(true), 518 bForceRepaint(false) 519 { 520 } 521 522 ParaPortion::~ParaPortion() 523 { 524 } 525 526 void ParaPortion::MarkInvalid( sal_Int32 nStart, sal_Int32 nDiff ) 527 { 528 if ( !bInvalid ) 529 { 530 // nInvalidPosEnd = nStart; // ??? => CreateLines 531 nInvalidPosStart = ( nDiff >= 0 ) ? nStart : ( nStart + nDiff ); 532 nInvalidDiff = nDiff; 533 } 534 else 535 { 536 // Simple tap in succession 537 if ( ( nDiff > 0 ) && ( nInvalidDiff > 0 ) && 538 ( ( nInvalidPosStart+nInvalidDiff ) == nStart ) ) 539 { 540 nInvalidDiff = nInvalidDiff + nDiff; 541 } 542 // Simple delete in succession 543 else if ( ( nDiff < 0 ) && ( nInvalidDiff < 0 ) && ( nInvalidPosStart == nStart ) ) 544 { 545 nInvalidPosStart = nInvalidPosStart + nDiff; 546 nInvalidDiff = nInvalidDiff + nDiff; 547 } 548 else 549 { 550 // nInvalidPosEnd = pNode->Len(); 551 DBG_ASSERT( ( nDiff >= 0 ) || ( (nStart+nDiff) >= 0 ), "MarkInvalid: Diff out of Range" ); 552 nInvalidPosStart = std::min( nInvalidPosStart, ( nDiff < 0 ? nStart+nDiff : nDiff ) ); 553 nInvalidDiff = 0; 554 bSimple = false; 555 } 556 } 557 bInvalid = true; 558 aScriptInfos.clear(); 559 aWritingDirectionInfos.clear(); 560 } 561 562 void ParaPortion::MarkSelectionInvalid( sal_Int32 nStart ) 563 { 564 if ( !bInvalid ) 565 { 566 nInvalidPosStart = nStart; 567 } 568 else 569 { 570 nInvalidPosStart = std::min( nInvalidPosStart, nStart ); 571 } 572 nInvalidDiff = 0; 573 bInvalid = true; 574 bSimple = false; 575 aScriptInfos.clear(); 576 aWritingDirectionInfos.clear(); 577 } 578 579 sal_Int32 ParaPortion::GetLineNumber( sal_Int32 nIndex ) const 580 { 581 SAL_WARN_IF( !aLineList.Count(), "editeng", "Empty ParaPortion in GetLine!" ); 582 DBG_ASSERT( bVisible, "Why GetLine() on an invisible paragraph?" ); 583 584 for ( sal_Int32 nLine = 0; nLine < aLineList.Count(); nLine++ ) 585 { 586 if ( aLineList[nLine].IsIn( nIndex ) ) 587 return nLine; 588 } 589 590 // Then it should be at the end of the last line! 591 DBG_ASSERT( nIndex == aLineList[ aLineList.Count() - 1 ].GetEnd(), "Index dead wrong!" ); 592 return (aLineList.Count()-1); 593 } 594 595 void ParaPortion::SetVisible( bool bMakeVisible ) 596 { 597 bVisible = bMakeVisible; 598 } 599 600 void ParaPortion::CorrectValuesBehindLastFormattedLine( sal_Int32 nLastFormattedLine ) 601 { 602 sal_Int32 nLines = aLineList.Count(); 603 DBG_ASSERT( nLines, "CorrectPortionNumbersFromLine: Empty Portion?" ); 604 if ( nLastFormattedLine < ( nLines - 1 ) ) 605 { 606 const EditLine& rLastFormatted = aLineList[ nLastFormattedLine ]; 607 const EditLine& rUnformatted = aLineList[ nLastFormattedLine+1 ]; 608 sal_Int32 nPortionDiff = rUnformatted.GetStartPortion() - rLastFormatted.GetEndPortion(); 609 sal_Int32 nTextDiff = rUnformatted.GetStart() - rLastFormatted.GetEnd(); 610 nTextDiff++; // LastFormatted->GetEnd() was included => 1 deducted too much! 611 612 // The first unformatted must begin exactly one Portion behind the last 613 // of the formatted: 614 // If the modified line was split into one portion, can 615 // nLastEnd > nNextStart! 616 int nPDiff = -( nPortionDiff-1 ); 617 int nTDiff = -( nTextDiff-1 ); 618 if ( nPDiff || nTDiff ) 619 { 620 for ( sal_Int32 nL = nLastFormattedLine+1; nL < nLines; nL++ ) 621 { 622 EditLine& rLine = aLineList[ nL ]; 623 624 rLine.GetStartPortion() = rLine.GetStartPortion() + nPDiff; 625 rLine.GetEndPortion() = rLine.GetEndPortion() + nPDiff; 626 627 rLine.GetStart() = rLine.GetStart() + nTDiff; 628 rLine.GetEnd() = rLine.GetEnd() + nTDiff; 629 630 rLine.SetValid(); 631 } 632 } 633 } 634 DBG_ASSERT( aLineList[ aLineList.Count()-1 ].GetEnd() == pNode->Len(), "CorrectLines: The end is not right!" ); 635 } 636 637 // Shared reverse lookup acceleration pieces ... 638 639 namespace { 640 641 template<typename Array, typename Val> 642 sal_Int32 FastGetPos(const Array& rArray, const Val* p, sal_Int32& rLastPos) 643 { 644 sal_Int32 nArrayLen = rArray.size(); 645 646 // Through certain filter code-paths we do a lot of appends, which in 647 // turn call GetPos - creating some N^2 nightmares. If we have a 648 // non-trivially large list, do a few checks from the end first. 649 if (rLastPos > 16 && nArrayLen > 16) 650 { 651 sal_Int32 nEnd; 652 if (rLastPos > nArrayLen - 2) 653 nEnd = nArrayLen; 654 else 655 nEnd = rLastPos + 2; 656 657 for (sal_Int32 nIdx = rLastPos - 2; nIdx < nEnd; ++nIdx) 658 { 659 if (rArray.at(nIdx).get() == p) 660 { 661 rLastPos = nIdx; 662 return nIdx; 663 } 664 } 665 } 666 // The world's lamest linear search from svarray... 667 for (sal_Int32 nIdx = 0; nIdx < nArrayLen; ++nIdx) 668 if (rArray.at(nIdx).get() == p) 669 return rLastPos = nIdx; 670 671 // XXX "not found" condition for sal_Int32 indexes 672 return EE_PARA_NOT_FOUND; 673 } 674 675 } 676 677 ParaPortionList::ParaPortionList() : nLastCache( 0 ) 678 { 679 } 680 681 ParaPortionList::~ParaPortionList() 682 { 683 } 684 685 sal_Int32 ParaPortionList::GetPos(const ParaPortion* p) const 686 { 687 return FastGetPos(maPortions, p, nLastCache); 688 } 689 690 ParaPortion* ParaPortionList::operator [](sal_Int32 nPos) 691 { 692 return 0 <= nPos && nPos < static_cast<sal_Int32>(maPortions.size()) ? maPortions[nPos].get() : nullptr; 693 } 694 695 const ParaPortion* ParaPortionList::operator [](sal_Int32 nPos) const 696 { 697 return 0 <= nPos && nPos < static_cast<sal_Int32>(maPortions.size()) ? maPortions[nPos].get() : nullptr; 698 } 699 700 std::unique_ptr<ParaPortion> ParaPortionList::Release(sal_Int32 nPos) 701 { 702 if (nPos < 0 || static_cast<sal_Int32>(maPortions.size()) <= nPos) 703 { 704 SAL_WARN( "editeng", "ParaPortionList::Release - out of bounds pos " << nPos); 705 return nullptr; 706 } 707 std::unique_ptr<ParaPortion> p = std::move(maPortions[nPos]); 708 maPortions.erase(maPortions.begin()+nPos); 709 return p; 710 } 711 712 void ParaPortionList::Remove(sal_Int32 nPos) 713 { 714 if (nPos < 0 || static_cast<sal_Int32>(maPortions.size()) <= nPos) 715 { 716 SAL_WARN( "editeng", "ParaPortionList::Remove - out of bounds pos " << nPos); 717 return; 718 } 719 maPortions.erase(maPortions.begin()+nPos); 720 } 721 722 void ParaPortionList::Insert(sal_Int32 nPos, std::unique_ptr<ParaPortion> p) 723 { 724 if (nPos < 0 || static_cast<sal_Int32>(maPortions.size()) < nPos) 725 { 726 SAL_WARN( "editeng", "ParaPortionList::Insert - out of bounds pos " << nPos); 727 return; 728 } 729 maPortions.insert(maPortions.begin()+nPos, std::move(p)); 730 } 731 732 void ParaPortionList::Append(std::unique_ptr<ParaPortion> p) 733 { 734 maPortions.push_back(std::move(p)); 735 } 736 737 sal_Int32 ParaPortionList::Count() const 738 { 739 size_t nSize = maPortions.size(); 740 if (nSize > SAL_MAX_INT32) 741 { 742 SAL_WARN( "editeng", "ParaPortionList::Count - overflow " << nSize); 743 return SAL_MAX_INT32; 744 } 745 return nSize; 746 } 747 748 void ParaPortionList::Reset() 749 { 750 maPortions.clear(); 751 } 752 753 long ParaPortionList::GetYOffset(const ParaPortion* pPPortion) const 754 { 755 long nHeight = 0; 756 for (const auto & rPortion : maPortions) 757 { 758 const ParaPortion* pTmpPortion = rPortion.get(); 759 if ( pTmpPortion == pPPortion ) 760 return nHeight; 761 nHeight += pTmpPortion->GetHeight(); 762 } 763 OSL_FAIL( "GetYOffset: Portion not found" ); 764 return nHeight; 765 } 766 767 sal_Int32 ParaPortionList::FindParagraph(long nYOffset) const 768 { 769 long nY = 0; 770 for (size_t i = 0, n = maPortions.size(); i < n; ++i) 771 { 772 nY += maPortions[i]->GetHeight(); // should also be correct even in bVisible! 773 if ( nY > nYOffset ) 774 return i <= SAL_MAX_INT32 ? static_cast<sal_Int32>(i) : SAL_MAX_INT32; 775 } 776 return EE_PARA_NOT_FOUND; 777 } 778 779 const ParaPortion* ParaPortionList::SafeGetObject(sal_Int32 nPos) const 780 { 781 return 0 <= nPos && nPos < static_cast<sal_Int32>(maPortions.size()) ? maPortions[nPos].get() : nullptr; 782 } 783 784 ParaPortion* ParaPortionList::SafeGetObject(sal_Int32 nPos) 785 { 786 return 0 <= nPos && nPos < static_cast<sal_Int32>(maPortions.size()) ? maPortions[nPos].get() : nullptr; 787 } 788 789 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 790 void 791 ParaPortionList::DbgCheck(ParaPortionList const& rParas, EditDoc const& rDoc) 792 { 793 assert(rParas.Count() == rDoc.Count()); 794 for (sal_Int32 i = 0; i < rParas.Count(); ++i) 795 { 796 assert(rParas.SafeGetObject(i) != nullptr); 797 assert(rParas.SafeGetObject(i)->GetNode() != nullptr); 798 assert(rParas.SafeGetObject(i)->GetNode() == rDoc.GetObject(i)); 799 } 800 } 801 #endif 802 803 ContentAttribsInfo::ContentAttribsInfo( const SfxItemSet& rParaAttribs ) : 804 aPrevParaAttribs( rParaAttribs) 805 { 806 } 807 808 void ContentAttribsInfo::RemoveAllCharAttribsFromPool(SfxItemPool& rPool) const 809 { 810 for (const std::unique_ptr<EditCharAttrib>& rAttrib : aPrevCharAttribs) 811 rPool.Remove(*rAttrib->GetItem()); 812 } 813 814 void ContentAttribsInfo::AppendCharAttrib(EditCharAttrib* pNew) 815 { 816 aPrevCharAttribs.push_back(std::unique_ptr<EditCharAttrib>(pNew)); 817 } 818 819 void ConvertItem( std::unique_ptr<SfxPoolItem>& rPoolItem, MapUnit eSourceUnit, MapUnit eDestUnit ) 820 { 821 DBG_ASSERT( eSourceUnit != eDestUnit, "ConvertItem - Why?!" ); 822 823 switch ( rPoolItem->Which() ) 824 { 825 case EE_PARA_LRSPACE: 826 { 827 assert(dynamic_cast<const SvxLRSpaceItem *>(rPoolItem.get()) != nullptr); 828 SvxLRSpaceItem& rItem = static_cast<SvxLRSpaceItem&>(*rPoolItem); 829 rItem.SetTextFirstLineOfst( sal::static_int_cast< short >( OutputDevice::LogicToLogic( rItem.GetTextFirstLineOfst(), eSourceUnit, eDestUnit ) ) ); 830 rItem.SetTextLeft( OutputDevice::LogicToLogic( rItem.GetTextLeft(), eSourceUnit, eDestUnit ) ); 831 rItem.SetRight( OutputDevice::LogicToLogic( rItem.GetRight(), eSourceUnit, eDestUnit ) ); 832 } 833 break; 834 case EE_PARA_ULSPACE: 835 { 836 assert(dynamic_cast<const SvxULSpaceItem *>(rPoolItem.get()) != nullptr); 837 SvxULSpaceItem& rItem = static_cast<SvxULSpaceItem&>(*rPoolItem); 838 rItem.SetUpper( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetUpper(), eSourceUnit, eDestUnit ) ) ); 839 rItem.SetLower( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetLower(), eSourceUnit, eDestUnit ) ) ); 840 } 841 break; 842 case EE_PARA_SBL: 843 { 844 assert(dynamic_cast<const SvxLineSpacingItem *>(rPoolItem.get()) != nullptr); 845 SvxLineSpacingItem& rItem = static_cast<SvxLineSpacingItem&>(*rPoolItem); 846 // SetLineHeight changes also eLineSpace! 847 if ( rItem.GetLineSpaceRule() == SvxLineSpaceRule::Min ) 848 rItem.SetLineHeight( sal::static_int_cast< sal_uInt16 >( OutputDevice::LogicToLogic( rItem.GetLineHeight(), eSourceUnit, eDestUnit ) ) ); 849 } 850 break; 851 case EE_PARA_TABS: 852 { 853 assert(dynamic_cast<const SvxTabStopItem *>(rPoolItem.get()) != nullptr); 854 SvxTabStopItem& rItem = static_cast<SvxTabStopItem&>(*rPoolItem); 855 SvxTabStopItem* pNewItem(new SvxTabStopItem(EE_PARA_TABS)); 856 for ( sal_uInt16 i = 0; i < rItem.Count(); i++ ) 857 { 858 const SvxTabStop& rTab = rItem[i]; 859 SvxTabStop aNewStop( OutputDevice::LogicToLogic( rTab.GetTabPos(), eSourceUnit, eDestUnit ), rTab.GetAdjustment(), rTab.GetDecimal(), rTab.GetFill() ); 860 pNewItem->Insert( aNewStop ); 861 } 862 rPoolItem.reset(pNewItem); 863 } 864 break; 865 case EE_CHAR_FONTHEIGHT: 866 case EE_CHAR_FONTHEIGHT_CJK: 867 case EE_CHAR_FONTHEIGHT_CTL: 868 { 869 assert(dynamic_cast<const SvxFontHeightItem *>(rPoolItem.get()) != nullptr); 870 SvxFontHeightItem& rItem = static_cast<SvxFontHeightItem&>(*rPoolItem); 871 rItem.SetHeight( OutputDevice::LogicToLogic( rItem.GetHeight(), eSourceUnit, eDestUnit ) ); 872 } 873 break; 874 } 875 } 876 877 void ConvertAndPutItems( SfxItemSet& rDest, const SfxItemSet& rSource, const MapUnit* pSourceUnit, const MapUnit* pDestUnit ) 878 { 879 const SfxItemPool* pSourcePool = rSource.GetPool(); 880 const SfxItemPool* pDestPool = rDest.GetPool(); 881 882 for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ ) 883 { 884 // If possible go through SlotID ... 885 886 sal_uInt16 nSourceWhich = nWhich; 887 sal_uInt16 nSlot = pDestPool->GetTrueSlotId( nWhich ); 888 if ( nSlot ) 889 { 890 sal_uInt16 nW = pSourcePool->GetTrueWhich( nSlot ); 891 if ( nW ) 892 nSourceWhich = nW; 893 } 894 895 if ( rSource.GetItemState( nSourceWhich, false ) == SfxItemState::SET ) 896 { 897 MapUnit eSourceUnit = pSourceUnit ? *pSourceUnit : pSourcePool->GetMetric( nSourceWhich ); 898 MapUnit eDestUnit = pDestUnit ? *pDestUnit : pDestPool->GetMetric( nWhich ); 899 if ( eSourceUnit != eDestUnit ) 900 { 901 std::unique_ptr<SfxPoolItem> pItem(rSource.Get( nSourceWhich ).Clone()); 902 ConvertItem( pItem, eSourceUnit, eDestUnit ); 903 pItem->SetWhich(nWhich); 904 rDest.Put( std::move(pItem) ); 905 } 906 else 907 { 908 rDest.Put( rSource.Get( nSourceWhich ).CloneSetWhich(nWhich) ); 909 } 910 } 911 } 912 } 913 914 EditLine::EditLine() : 915 nTxtWidth(0), 916 nStartPosX(0), 917 nStart(0), 918 nEnd(0), 919 nStartPortion(0), // to be able to tell the difference between a line 920 // without Portions from one with the Portion number 0 921 nEndPortion(0), 922 nHeight(0), 923 nTxtHeight(0), 924 nMaxAscent(0), 925 bHangingPunctuation(false), 926 bInvalid(true) 927 { 928 } 929 930 EditLine::EditLine( const EditLine& r ) : 931 nTxtWidth(0), 932 nStartPosX(0), 933 nStart(r.nStart), 934 nEnd(r.nEnd), 935 nStartPortion(r.nStartPortion), 936 nEndPortion(r.nEndPortion), 937 nHeight(0), 938 nTxtHeight(0), 939 nMaxAscent(0), 940 bHangingPunctuation(r.bHangingPunctuation), 941 bInvalid(true) 942 { 943 } 944 945 EditLine::~EditLine() 946 { 947 } 948 949 950 EditLine* EditLine::Clone() const 951 { 952 EditLine* pL = new EditLine; 953 pL->aPositions = aPositions; 954 pL->nStartPosX = nStartPosX; 955 pL->nStart = nStart; 956 pL->nEnd = nEnd; 957 pL->nStartPortion = nStartPortion; 958 pL->nEndPortion = nEndPortion; 959 pL->nHeight = nHeight; 960 pL->nTxtWidth = nTxtWidth; 961 pL->nTxtHeight = nTxtHeight; 962 pL->nMaxAscent = nMaxAscent; 963 964 return pL; 965 } 966 967 bool operator == ( const EditLine& r1, const EditLine& r2 ) 968 { 969 if ( r1.nStart != r2.nStart ) 970 return false; 971 972 if ( r1.nEnd != r2.nEnd ) 973 return false; 974 975 if ( r1.nStartPortion != r2.nStartPortion ) 976 return false; 977 978 if ( r1.nEndPortion != r2.nEndPortion ) 979 return false; 980 981 return true; 982 } 983 984 EditLine& EditLine::operator = ( const EditLine& r ) 985 { 986 nEnd = r.nEnd; 987 nStart = r.nStart; 988 nEndPortion = r.nEndPortion; 989 nStartPortion = r.nStartPortion; 990 return *this; 991 } 992 993 994 void EditLine::SetHeight( sal_uInt16 nH, sal_uInt16 nTxtH ) 995 { 996 nHeight = nH; 997 nTxtHeight = ( nTxtH ? nTxtH : nH ); 998 } 999 1000 void EditLine::SetStartPosX( long start ) 1001 { 1002 if (start > 0) 1003 nStartPosX = start; 1004 else 1005 nStartPosX = 0; 1006 } 1007 1008 Size EditLine::CalcTextSize( ParaPortion& rParaPortion ) 1009 { 1010 Size aSz; 1011 Size aTmpSz; 1012 1013 DBG_ASSERT( rParaPortion.GetTextPortions().Count(), "GetTextSize before CreatePortions !" ); 1014 1015 for ( sal_Int32 n = nStartPortion; n <= nEndPortion; n++ ) 1016 { 1017 TextPortion& rPortion = rParaPortion.GetTextPortions()[n]; 1018 switch ( rPortion.GetKind() ) 1019 { 1020 case PortionKind::TEXT: 1021 case PortionKind::FIELD: 1022 case PortionKind::HYPHENATOR: 1023 { 1024 aTmpSz = rPortion.GetSize(); 1025 aSz.AdjustWidth(aTmpSz.Width() ); 1026 if ( aSz.Height() < aTmpSz.Height() ) 1027 aSz.setHeight( aTmpSz.Height() ); 1028 } 1029 break; 1030 case PortionKind::TAB: 1031 { 1032 aSz.AdjustWidth(rPortion.GetSize().Width() ); 1033 } 1034 break; 1035 case PortionKind::LINEBREAK: break; 1036 } 1037 } 1038 1039 SetHeight( static_cast<sal_uInt16>(aSz.Height()) ); 1040 return aSz; 1041 } 1042 1043 EditLineList::EditLineList() 1044 { 1045 } 1046 1047 EditLineList::~EditLineList() 1048 { 1049 Reset(); 1050 } 1051 1052 void EditLineList::Reset() 1053 { 1054 maLines.clear(); 1055 } 1056 1057 void EditLineList::DeleteFromLine(sal_Int32 nDelFrom) 1058 { 1059 assert(nDelFrom <= (static_cast<sal_Int32>(maLines.size()) - 1)); 1060 LinesType::iterator it = maLines.begin(); 1061 std::advance(it, nDelFrom); 1062 maLines.erase(it, maLines.end()); 1063 } 1064 1065 sal_Int32 EditLineList::FindLine(sal_Int32 nChar, bool bInclEnd) 1066 { 1067 sal_Int32 n = maLines.size(); 1068 for (sal_Int32 i = 0; i < n; ++i) 1069 { 1070 const EditLine& rLine = *maLines[i].get(); 1071 if ( (bInclEnd && (rLine.GetEnd() >= nChar)) || 1072 (rLine.GetEnd() > nChar) ) 1073 { 1074 return i; 1075 } 1076 } 1077 1078 DBG_ASSERT( !bInclEnd, "Line not found: FindLine" ); 1079 return n - 1; 1080 } 1081 1082 sal_Int32 EditLineList::Count() const 1083 { 1084 return maLines.size(); 1085 } 1086 1087 const EditLine& EditLineList::operator[](sal_Int32 nPos) const 1088 { 1089 return *maLines[nPos].get(); 1090 } 1091 1092 EditLine& EditLineList::operator[](sal_Int32 nPos) 1093 { 1094 return *maLines[nPos].get(); 1095 } 1096 1097 void EditLineList::Append(EditLine* p) 1098 { 1099 maLines.push_back(std::unique_ptr<EditLine>(p)); 1100 } 1101 1102 void EditLineList::Insert(sal_Int32 nPos, EditLine* p) 1103 { 1104 maLines.insert(maLines.begin()+nPos, std::unique_ptr<EditLine>(p)); 1105 } 1106 1107 EditPaM::EditPaM() : pNode(nullptr), nIndex(0) {} 1108 EditPaM::EditPaM(ContentNode* p, sal_Int32 n) : pNode(p), nIndex(n) {} 1109 1110 1111 void EditPaM::SetNode(ContentNode* p) 1112 { 1113 pNode = p; 1114 } 1115 1116 bool EditPaM::DbgIsBuggy( EditDoc const & rDoc ) const 1117 { 1118 return !pNode || 1119 rDoc.GetPos( pNode ) >= rDoc.Count() || 1120 nIndex > pNode->Len(); 1121 } 1122 1123 bool EditSelection::DbgIsBuggy( EditDoc const & rDoc ) const 1124 { 1125 return aStartPaM.DbgIsBuggy( rDoc ) || aEndPaM.DbgIsBuggy( rDoc ); 1126 } 1127 1128 EditSelection::EditSelection() 1129 { 1130 } 1131 1132 EditSelection::EditSelection( const EditPaM& rStartAndAnd ) : 1133 aStartPaM(rStartAndAnd), 1134 aEndPaM(rStartAndAnd) 1135 { 1136 } 1137 1138 EditSelection::EditSelection( const EditPaM& rStart, const EditPaM& rEnd ) : 1139 aStartPaM(rStart), 1140 aEndPaM(rEnd) 1141 { 1142 } 1143 1144 EditSelection& EditSelection::operator = ( const EditPaM& rPaM ) 1145 { 1146 aStartPaM = rPaM; 1147 aEndPaM = rPaM; 1148 return *this; 1149 } 1150 1151 void EditSelection::Adjust( const EditDoc& rNodes ) 1152 { 1153 DBG_ASSERT( aStartPaM.GetIndex() <= aStartPaM.GetNode()->Len(), "Index out of range in Adjust(1)" ); 1154 DBG_ASSERT( aEndPaM.GetIndex() <= aEndPaM.GetNode()->Len(), "Index out of range in Adjust(2)" ); 1155 1156 const ContentNode* pStartNode = aStartPaM.GetNode(); 1157 const ContentNode* pEndNode = aEndPaM.GetNode(); 1158 1159 sal_Int32 nStartNode = rNodes.GetPos( pStartNode ); 1160 sal_Int32 nEndNode = rNodes.GetPos( pEndNode ); 1161 1162 DBG_ASSERT( nStartNode != SAL_MAX_INT32, "Node out of range in Adjust(1)" ); 1163 DBG_ASSERT( nEndNode != SAL_MAX_INT32, "Node out of range in Adjust(2)" ); 1164 1165 const bool bSwap = ( nStartNode > nEndNode ) || 1166 ( ( nStartNode == nEndNode ) && 1167 ( aStartPaM.GetIndex() > aEndPaM.GetIndex() ) ); 1168 1169 if ( bSwap ) 1170 { 1171 EditPaM aTmpPaM( aStartPaM ); 1172 aStartPaM = aEndPaM; 1173 aEndPaM = aTmpPaM; 1174 } 1175 } 1176 1177 bool operator == ( const EditPaM& r1, const EditPaM& r2 ) 1178 { 1179 return ( r1.GetNode() == r2.GetNode() ) && 1180 ( r1.GetIndex() == r2.GetIndex() ); 1181 } 1182 1183 bool operator != ( const EditPaM& r1, const EditPaM& r2 ) 1184 { 1185 return !( r1 == r2 ); 1186 } 1187 1188 ContentNode::ContentNode( SfxItemPool& rPool ) : aContentAttribs( rPool ) 1189 { 1190 } 1191 1192 ContentNode::ContentNode( const OUString& rStr, const ContentAttribs& rContentAttribs ) : 1193 maString(rStr), aContentAttribs(rContentAttribs) 1194 { 1195 } 1196 1197 ContentNode::~ContentNode() 1198 { 1199 } 1200 1201 void ContentNode::ExpandAttribs( sal_Int32 nIndex, sal_Int32 nNew, SfxItemPool& rItemPool ) 1202 { 1203 if ( !nNew ) 1204 return; 1205 1206 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1207 CharAttribList::DbgCheckAttribs(aCharAttribList); 1208 #endif 1209 1210 // Since features are treated differently than normal character attributes, 1211 // but can also affect the order of the start list. // In every if ..., in the next (n) opportunities due to bFeature or 1212 // an existing special case, must (n-1) opportunities be provided with 1213 // bResort. The most likely possibility receives no bResort, so that is 1214 // not sorted anew when all attributes are the same. 1215 bool bResort = false; 1216 bool bExpandedEmptyAtIndexNull = false; 1217 1218 sal_Int32 nAttr = 0; 1219 CharAttribList::AttribsType& rAttribs = aCharAttribList.GetAttribs(); 1220 EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr); 1221 while ( pAttrib ) 1222 { 1223 if ( pAttrib->GetEnd() >= nIndex ) 1224 { 1225 // Move all attributes behind the insertion point... 1226 if ( pAttrib->GetStart() > nIndex ) 1227 { 1228 pAttrib->MoveForward( nNew ); 1229 } 1230 // 0: Expand empty attribute, if at insertion point 1231 else if ( pAttrib->IsEmpty() ) 1232 { 1233 // Do not check Index, an empty one could only be there 1234 // When later checking it anyhow: 1235 // Special case: Start == 0; AbsLen == 1, nNew = 1 1236 // => Expand, because of paragraph break! 1237 // Start <= nIndex, End >= nIndex => Start=End=nIndex! 1238 // if ( pAttrib->GetStart() == nIndex ) 1239 pAttrib->Expand( nNew ); 1240 bResort = true; 1241 if ( pAttrib->GetStart() == 0 ) 1242 bExpandedEmptyAtIndexNull = true; 1243 } 1244 // 1: Attribute starts before, goes to index ... 1245 else if ( pAttrib->GetEnd() == nIndex ) // Start must be before 1246 { 1247 // Only expand when there is no feature 1248 // and if not in exclude list! 1249 // Otherwise, a UL will go on until a new ULDB, expanding both 1250 // if ( !pAttrib->IsFeature() && !rExclList.FindAttrib( pAttrib->Which() ) ) 1251 if ( !pAttrib->IsFeature() && !aCharAttribList.FindEmptyAttrib( pAttrib->Which(), nIndex ) ) 1252 { 1253 if ( !pAttrib->IsEdge() ) 1254 pAttrib->Expand( nNew ); 1255 } 1256 else 1257 bResort = true; 1258 } 1259 // 2: Attribute starts before, goes past the Index... 1260 else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) ) 1261 { 1262 DBG_ASSERT( !pAttrib->IsFeature(), "Large Feature?!" ); 1263 pAttrib->Expand( nNew ); 1264 } 1265 // 3: Attribute starts on index... 1266 else if ( pAttrib->GetStart() == nIndex ) 1267 { 1268 if ( pAttrib->IsFeature() ) 1269 { 1270 pAttrib->MoveForward( nNew ); 1271 bResort = true; 1272 } 1273 else 1274 { 1275 bool bExpand = false; 1276 if ( nIndex == 0 ) 1277 { 1278 bExpand = true; 1279 if( bExpandedEmptyAtIndexNull ) 1280 { 1281 // Check if this kind of attribute was empty and expanded here... 1282 sal_uInt16 nW = pAttrib->GetItem()->Which(); 1283 for ( sal_Int32 nA = 0; nA < nAttr; nA++ ) 1284 { 1285 const EditCharAttrib& r = *aCharAttribList.GetAttribs()[nA].get(); 1286 if ( ( r.GetStart() == 0 ) && ( r.GetItem()->Which() == nW ) ) 1287 { 1288 bExpand = false; 1289 break; 1290 } 1291 } 1292 1293 } 1294 } 1295 if ( bExpand ) 1296 { 1297 pAttrib->Expand( nNew ); 1298 bResort = true; 1299 } 1300 else 1301 { 1302 pAttrib->MoveForward( nNew ); 1303 } 1304 } 1305 } 1306 } 1307 1308 if ( pAttrib->IsEdge() ) 1309 pAttrib->SetEdge(false); 1310 1311 DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" ); 1312 1313 DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribute distorted!" ); 1314 DBG_ASSERT( ( pAttrib->GetEnd() <= Len() ), "Expand: Attribute larger than paragraph!" ); 1315 if ( pAttrib->IsEmpty() ) 1316 { 1317 OSL_FAIL( "Empty Attribute after ExpandAttribs?" ); 1318 bResort = true; 1319 rItemPool.Remove( *pAttrib->GetItem() ); 1320 rAttribs.erase(rAttribs.begin()+nAttr); 1321 --nAttr; 1322 } 1323 ++nAttr; 1324 pAttrib = GetAttrib(rAttribs, nAttr); 1325 } 1326 1327 if ( bResort ) 1328 aCharAttribList.ResortAttribs(); 1329 1330 if (mpWrongList) 1331 { 1332 bool bSep = ( maString[ nIndex ] == ' ' ) || IsFeature( nIndex ); 1333 mpWrongList->TextInserted( nIndex, nNew, bSep ); 1334 } 1335 1336 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1337 CharAttribList::DbgCheckAttribs(aCharAttribList); 1338 #endif 1339 } 1340 1341 void ContentNode::CollapseAttribs( sal_Int32 nIndex, sal_Int32 nDeleted, SfxItemPool& rItemPool ) 1342 { 1343 if ( !nDeleted ) 1344 return; 1345 1346 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1347 CharAttribList::DbgCheckAttribs(aCharAttribList); 1348 #endif 1349 1350 // Since features are treated differently than normal character attributes, 1351 // but can also affect the order of the start list 1352 bool bResort = false; 1353 sal_Int32 nEndChanges = nIndex+nDeleted; 1354 1355 sal_Int32 nAttr = 0; 1356 CharAttribList::AttribsType& rAttribs = aCharAttribList.GetAttribs(); 1357 EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr); 1358 while ( pAttrib ) 1359 { 1360 bool bDelAttr = false; 1361 if ( pAttrib->GetEnd() >= nIndex ) 1362 { 1363 // Move all Attribute behind the insert point... 1364 if ( pAttrib->GetStart() >= nEndChanges ) 1365 { 1366 pAttrib->MoveBackward( nDeleted ); 1367 } 1368 // 1. Delete Internal attributes... 1369 else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) ) 1370 { 1371 // Special case: Attribute covers the area exactly 1372 // => keep as empty Attribute. 1373 if ( !pAttrib->IsFeature() && ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) ) 1374 { 1375 pAttrib->GetEnd() = nIndex; // empty 1376 bResort = true; 1377 } 1378 else 1379 bDelAttr = true; 1380 } 1381 // 2. Attribute starts earlier, ends inside or behind it ... 1382 else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) ) 1383 { 1384 DBG_ASSERT( !pAttrib->IsFeature(), "Collapsing Feature!" ); 1385 if ( pAttrib->GetEnd() <= nEndChanges ) // ends inside 1386 pAttrib->GetEnd() = nIndex; 1387 else 1388 pAttrib->Collaps( nDeleted ); // ends behind 1389 } 1390 // 3. Attribute starts inside, ending behind ... 1391 else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) ) 1392 { 1393 // Features not allowed to expand! 1394 if ( pAttrib->IsFeature() ) 1395 { 1396 pAttrib->MoveBackward( nDeleted ); 1397 bResort = true; 1398 } 1399 else 1400 { 1401 pAttrib->GetStart() = nEndChanges; 1402 pAttrib->MoveBackward( nDeleted ); 1403 } 1404 } 1405 } 1406 DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" ); 1407 1408 DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collapse: Attribute distorted!" ); 1409 DBG_ASSERT( ( pAttrib->GetEnd() <= Len()) || bDelAttr, "Collapse: Attribute larger than paragraph!" ); 1410 if ( bDelAttr ) 1411 { 1412 bResort = true; 1413 rItemPool.Remove( *pAttrib->GetItem() ); 1414 rAttribs.erase(rAttribs.begin()+nAttr); 1415 nAttr--; 1416 } 1417 else if ( pAttrib->IsEmpty() ) 1418 aCharAttribList.SetHasEmptyAttribs(true); 1419 1420 nAttr++; 1421 pAttrib = GetAttrib(rAttribs, nAttr); 1422 } 1423 1424 if ( bResort ) 1425 aCharAttribList.ResortAttribs(); 1426 1427 if (mpWrongList) 1428 mpWrongList->TextDeleted(nIndex, nDeleted); 1429 1430 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1431 CharAttribList::DbgCheckAttribs(aCharAttribList); 1432 #endif 1433 } 1434 1435 void ContentNode::CopyAndCutAttribs( ContentNode* pPrevNode, SfxItemPool& rPool, bool bKeepEndingAttribs ) 1436 { 1437 assert(pPrevNode); 1438 1439 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1440 CharAttribList::DbgCheckAttribs(aCharAttribList); 1441 CharAttribList::DbgCheckAttribs(pPrevNode->aCharAttribList); 1442 #endif 1443 1444 sal_Int32 nCut = pPrevNode->Len(); 1445 1446 sal_Int32 nAttr = 0; 1447 CharAttribList::AttribsType& rPrevAttribs = pPrevNode->GetCharAttribs().GetAttribs(); 1448 EditCharAttrib* pAttrib = GetAttrib(rPrevAttribs, nAttr); 1449 while ( pAttrib ) 1450 { 1451 if ( pAttrib->GetEnd() < nCut ) 1452 { 1453 // remain unchanged... 1454 ; 1455 } 1456 else if ( pAttrib->GetEnd() == nCut ) 1457 { 1458 // must be copied as an empty attributes. 1459 if ( bKeepEndingAttribs && !pAttrib->IsFeature() && !aCharAttribList.FindAttrib( pAttrib->GetItem()->Which(), 0 ) ) 1460 { 1461 EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, 0 ); 1462 assert(pNewAttrib); 1463 aCharAttribList.InsertAttrib( pNewAttrib ); 1464 } 1465 } 1466 else if ( pAttrib->IsInside( nCut ) || ( !nCut && !pAttrib->GetStart() && !pAttrib->IsFeature() ) ) 1467 { 1468 // If cut is done right at the front then the attribute must be 1469 // kept! Has to be copied and changed. 1470 EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, pAttrib->GetEnd()-nCut ); 1471 assert(pNewAttrib); 1472 aCharAttribList.InsertAttrib( pNewAttrib ); 1473 pAttrib->GetEnd() = nCut; 1474 } 1475 else 1476 { 1477 // Move all attributes in the current node (this) 1478 CharAttribList::AttribsType::iterator it = rPrevAttribs.begin() + nAttr; 1479 aCharAttribList.InsertAttrib(it->release()); 1480 rPrevAttribs.erase(it); 1481 pAttrib->MoveBackward( nCut ); 1482 nAttr--; 1483 } 1484 nAttr++; 1485 pAttrib = GetAttrib(rPrevAttribs, nAttr); 1486 } 1487 1488 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1489 CharAttribList::DbgCheckAttribs(aCharAttribList); 1490 CharAttribList::DbgCheckAttribs(pPrevNode->aCharAttribList); 1491 #endif 1492 } 1493 1494 void ContentNode::AppendAttribs( ContentNode* pNextNode ) 1495 { 1496 assert(pNextNode); 1497 1498 sal_Int32 nNewStart = maString.getLength(); 1499 1500 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1501 CharAttribList::DbgCheckAttribs(aCharAttribList); 1502 CharAttribList::DbgCheckAttribs(pNextNode->aCharAttribList); 1503 #endif 1504 1505 sal_Int32 nAttr = 0; 1506 CharAttribList::AttribsType& rNextAttribs = pNextNode->GetCharAttribs().GetAttribs(); 1507 EditCharAttrib* pAttrib = GetAttrib(rNextAttribs, nAttr); 1508 while ( pAttrib ) 1509 { 1510 // Move all attributes in the current node (this) 1511 bool bMelted = false; 1512 if ( ( pAttrib->GetStart() == 0 ) && ( !pAttrib->IsFeature() ) ) 1513 { 1514 // Attributes can possibly be summarized as: 1515 sal_Int32 nTmpAttr = 0; 1516 EditCharAttrib* pTmpAttrib = GetAttrib( aCharAttribList.GetAttribs(), nTmpAttr ); 1517 while ( !bMelted && pTmpAttrib ) 1518 { 1519 if ( pTmpAttrib->GetEnd() == nNewStart ) 1520 { 1521 if (pTmpAttrib->Which() == pAttrib->Which()) 1522 { 1523 // prevent adding 2 0-length attributes at same position 1524 if ((*(pTmpAttrib->GetItem()) == *(pAttrib->GetItem())) 1525 || (0 == pAttrib->GetLen())) 1526 { 1527 pTmpAttrib->GetEnd() = 1528 pTmpAttrib->GetEnd() + pAttrib->GetLen(); 1529 rNextAttribs.erase(rNextAttribs.begin()+nAttr); 1530 // Unsubscribe from the pool?! 1531 bMelted = true; 1532 } 1533 else if (0 == pTmpAttrib->GetLen()) 1534 { 1535 aCharAttribList.Remove(nTmpAttr); 1536 --nTmpAttr; // to cancel later increment... 1537 } 1538 } 1539 } 1540 ++nTmpAttr; 1541 pTmpAttrib = GetAttrib( aCharAttribList.GetAttribs(), nTmpAttr ); 1542 } 1543 } 1544 1545 if ( !bMelted ) 1546 { 1547 pAttrib->GetStart() = pAttrib->GetStart() + nNewStart; 1548 pAttrib->GetEnd() = pAttrib->GetEnd() + nNewStart; 1549 CharAttribList::AttribsType::iterator it = rNextAttribs.begin() + nAttr; 1550 aCharAttribList.InsertAttrib(it->release()); 1551 rNextAttribs.erase(it); 1552 } 1553 pAttrib = GetAttrib(rNextAttribs, nAttr); 1554 } 1555 // For the Attributes that just moved over: 1556 rNextAttribs.clear(); 1557 1558 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 1559 CharAttribList::DbgCheckAttribs(aCharAttribList); 1560 CharAttribList::DbgCheckAttribs(pNextNode->aCharAttribList); 1561 #endif 1562 } 1563 1564 void ContentNode::CreateDefFont() 1565 { 1566 // First use the information from the style ... 1567 SfxStyleSheet* pS = aContentAttribs.GetStyleSheet(); 1568 if ( pS ) 1569 CreateFont( GetCharAttribs().GetDefFont(), pS->GetItemSet() ); 1570 1571 // ... then iron out the hard paragraph formatting... 1572 CreateFont( GetCharAttribs().GetDefFont(), 1573 GetContentAttribs().GetItems(), pS == nullptr ); 1574 } 1575 1576 void ContentNode::SetStyleSheet( SfxStyleSheet* pS, const SvxFont& rFontFromStyle ) 1577 { 1578 aContentAttribs.SetStyleSheet( pS ); 1579 1580 1581 // First use the information from the style ... 1582 GetCharAttribs().GetDefFont() = rFontFromStyle; 1583 // ... then iron out the hard paragraph formatting... 1584 CreateFont( GetCharAttribs().GetDefFont(), 1585 GetContentAttribs().GetItems(), pS == nullptr ); 1586 } 1587 1588 void ContentNode::SetStyleSheet( SfxStyleSheet* pS, bool bRecalcFont ) 1589 { 1590 aContentAttribs.SetStyleSheet( pS ); 1591 if ( bRecalcFont ) 1592 CreateDefFont(); 1593 } 1594 1595 bool ContentNode::IsFeature( sal_Int32 nPos ) const 1596 { 1597 return maString[nPos] == CH_FEATURE; 1598 } 1599 1600 sal_Int32 ContentNode::Len() const 1601 { 1602 return maString.getLength(); 1603 } 1604 1605 sal_uLong ContentNode::GetExpandedLen() const 1606 { 1607 sal_uLong nLen = maString.getLength(); 1608 1609 // Fields can be longer than the placeholder in the Node 1610 const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs(); 1611 for (sal_Int32 nAttr = rAttrs.size(); nAttr; ) 1612 { 1613 const EditCharAttrib& rAttr = *rAttrs[--nAttr].get(); 1614 if (rAttr.Which() == EE_FEATURE_FIELD) 1615 { 1616 nLen += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength(); 1617 --nLen; // Standalone, to avoid corner cases when previous getLength() returns 0 1618 } 1619 } 1620 1621 return nLen; 1622 } 1623 1624 OUString ContentNode::GetExpandedText(sal_Int32 nStartPos, sal_Int32 nEndPos) const 1625 { 1626 if ( nEndPos < 0 || nEndPos > Len() ) 1627 nEndPos = Len(); 1628 1629 DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" ); 1630 1631 sal_Int32 nIndex = nStartPos; 1632 OUStringBuffer aStr; 1633 const EditCharAttrib* pNextFeature = GetCharAttribs().FindFeature( nIndex ); 1634 while ( nIndex < nEndPos ) 1635 { 1636 sal_Int32 nEnd = nEndPos; 1637 if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) ) 1638 nEnd = pNextFeature->GetStart(); 1639 else 1640 pNextFeature = nullptr; // Feature does not interest the below 1641 1642 DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" ); 1643 //!! beware of sub string length of -1 1644 if (nEnd > nIndex) 1645 aStr.append( std::u16string_view(GetString()).substr(nIndex, nEnd - nIndex) ); 1646 1647 if ( pNextFeature ) 1648 { 1649 switch ( pNextFeature->GetItem()->Which() ) 1650 { 1651 case EE_FEATURE_TAB: aStr.append( "\t" ); 1652 break; 1653 case EE_FEATURE_LINEBR: aStr.append( "\x0A" ); 1654 break; 1655 case EE_FEATURE_FIELD: 1656 aStr.append( static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue() ); 1657 break; 1658 default: OSL_FAIL( "What feature?" ); 1659 } 1660 pNextFeature = GetCharAttribs().FindFeature( ++nEnd ); 1661 } 1662 nIndex = nEnd; 1663 } 1664 return aStr.makeStringAndClear(); 1665 } 1666 1667 void ContentNode::UnExpandPosition( sal_Int32 &rPos, bool bBiasStart ) 1668 { 1669 sal_Int32 nOffset = 0; 1670 1671 const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs(); 1672 for (size_t nAttr = 0; nAttr < rAttrs.size(); ++nAttr ) 1673 { 1674 const EditCharAttrib& rAttr = *rAttrs[nAttr].get(); 1675 assert (!(nAttr < rAttrs.size() - 1) || 1676 rAttrs[nAttr]->GetStart() <= rAttrs[nAttr + 1]->GetStart()); 1677 1678 nOffset = rAttr.GetStart(); 1679 1680 if (nOffset >= rPos) // happens after the position 1681 return; 1682 1683 sal_Int32 nChunk = 0; 1684 if (rAttr.Which() == EE_FEATURE_FIELD) 1685 { 1686 nChunk += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength(); 1687 nChunk--; // Character representing the field in the string 1688 1689 if (nOffset + nChunk >= rPos) // we're inside the field 1690 { 1691 if (bBiasStart) 1692 rPos = rAttr.GetStart(); 1693 else 1694 rPos = rAttr.GetEnd(); 1695 return; 1696 } 1697 // Adjust for the position 1698 rPos -= nChunk; 1699 } 1700 } 1701 assert (rPos <= Len()); 1702 } 1703 1704 /* 1705 * Fields are represented by a single character in the underlying string 1706 * and/or selection, however, they can be expanded to the full value of 1707 * the field. When we're dealing with selection / offsets however we need 1708 * to deal in character positions inside the real (unexpanded) string. 1709 * This method maps us back to character offsets. 1710 */ 1711 void ContentNode::UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos ) 1712 { 1713 UnExpandPosition( rStartPos, true ); 1714 UnExpandPosition( rEndPos, false ); 1715 } 1716 1717 void ContentNode::SetChar(sal_Int32 nPos, sal_Unicode c) 1718 { 1719 maString = maString.replaceAt(nPos, 1, OUString(c)); 1720 } 1721 1722 void ContentNode::Insert(const OUString& rStr, sal_Int32 nPos) 1723 { 1724 maString = maString.replaceAt(nPos, 0, rStr); 1725 } 1726 1727 void ContentNode::Append(const OUString& rStr) 1728 { 1729 maString += rStr; 1730 } 1731 1732 void ContentNode::Erase(sal_Int32 nPos) 1733 { 1734 maString = maString.copy(0, nPos); 1735 } 1736 1737 void ContentNode::Erase(sal_Int32 nPos, sal_Int32 nCount) 1738 { 1739 maString = maString.replaceAt(nPos, nCount, ""); 1740 } 1741 1742 OUString ContentNode::Copy(sal_Int32 nPos) const 1743 { 1744 return maString.copy(nPos); 1745 } 1746 1747 OUString ContentNode::Copy(sal_Int32 nPos, sal_Int32 nCount) const 1748 { 1749 return maString.copy(nPos, nCount); 1750 } 1751 1752 sal_Unicode ContentNode::GetChar(sal_Int32 nPos) const 1753 { 1754 return maString[nPos]; 1755 } 1756 1757 void ContentNode::EnsureWrongList() 1758 { 1759 if (!mpWrongList) 1760 CreateWrongList(); 1761 } 1762 1763 WrongList* ContentNode::GetWrongList() 1764 { 1765 return mpWrongList.get(); 1766 } 1767 1768 const WrongList* ContentNode::GetWrongList() const 1769 { 1770 return mpWrongList.get(); 1771 } 1772 1773 void ContentNode::SetWrongList( WrongList* p ) 1774 { 1775 mpWrongList.reset(p); 1776 } 1777 1778 void ContentNode::CreateWrongList() 1779 { 1780 SAL_WARN_IF( mpWrongList && !mpWrongList->empty(), "editeng", "WrongList already exist!"); 1781 if (!mpWrongList || !mpWrongList->empty()) 1782 mpWrongList.reset(new WrongList); 1783 } 1784 1785 void ContentNode::DestroyWrongList() 1786 { 1787 mpWrongList.reset(); 1788 } 1789 1790 void ContentNode::dumpAsXml(xmlTextWriterPtr pWriter) const 1791 { 1792 xmlTextWriterStartElement(pWriter, BAD_CAST("ContentNode")); 1793 xmlTextWriterWriteAttribute(pWriter, BAD_CAST("maString"), BAD_CAST(maString.toUtf8().getStr())); 1794 aContentAttribs.dumpAsXml(pWriter); 1795 aCharAttribList.dumpAsXml(pWriter); 1796 xmlTextWriterEndElement(pWriter); 1797 } 1798 1799 1800 ContentAttribs::ContentAttribs( SfxItemPool& rPool ) 1801 : pStyle(nullptr) 1802 , aAttribSet( rPool, svl::Items<EE_PARA_START, EE_CHAR_END>{} ) 1803 { 1804 } 1805 1806 1807 SvxTabStop ContentAttribs::FindTabStop( sal_Int32 nCurPos, sal_uInt16 nDefTab ) 1808 { 1809 const SvxTabStopItem& rTabs = GetItem( EE_PARA_TABS ); 1810 for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ ) 1811 { 1812 const SvxTabStop& rTab = rTabs[i]; 1813 if ( rTab.GetTabPos() > nCurPos ) 1814 return rTab; 1815 } 1816 1817 // Determine DefTab ... 1818 SvxTabStop aTabStop; 1819 const sal_Int32 x = nCurPos / nDefTab + 1; 1820 aTabStop.GetTabPos() = nDefTab * x; 1821 return aTabStop; 1822 } 1823 1824 void ContentAttribs::SetStyleSheet( SfxStyleSheet* pS ) 1825 { 1826 bool bStyleChanged = ( pStyle != pS ); 1827 pStyle = pS; 1828 // Only when other style sheet, not when current style sheet modified 1829 if ( pStyle && bStyleChanged ) 1830 { 1831 // Selectively remove the attributes from the paragraph formatting 1832 // which are specified in the style, so that the attributes of the 1833 // style can have an affect. 1834 const SfxItemSet& rStyleAttribs = pStyle->GetItemSet(); 1835 for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ ) 1836 { 1837 // Don't change bullet on/off 1838 if ( ( nWhich != EE_PARA_BULLETSTATE ) && ( rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET ) ) 1839 aAttribSet.ClearItem( nWhich ); 1840 } 1841 } 1842 } 1843 1844 const SfxPoolItem& ContentAttribs::GetItem( sal_uInt16 nWhich ) const 1845 { 1846 // Hard paragraph attributes take precedence! 1847 const SfxItemSet* pTakeFrom = &aAttribSet; 1848 if ( pStyle && ( aAttribSet.GetItemState( nWhich, false ) != SfxItemState::SET ) ) 1849 pTakeFrom = &pStyle->GetItemSet(); 1850 1851 return pTakeFrom->Get( nWhich ); 1852 } 1853 1854 bool ContentAttribs::HasItem( sal_uInt16 nWhich ) const 1855 { 1856 bool bHasItem = false; 1857 if ( aAttribSet.GetItemState( nWhich, false ) == SfxItemState::SET ) 1858 bHasItem = true; 1859 else if ( pStyle && pStyle->GetItemSet().GetItemState( nWhich ) == SfxItemState::SET ) 1860 bHasItem = true; 1861 1862 return bHasItem; 1863 } 1864 1865 void ContentAttribs::dumpAsXml(xmlTextWriterPtr pWriter) const 1866 { 1867 xmlTextWriterStartElement(pWriter, BAD_CAST("ContentAttribs")); 1868 xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("style"), "%s", pStyle->GetName().toUtf8().getStr()); 1869 aAttribSet.dumpAsXml(pWriter); 1870 xmlTextWriterEndElement(pWriter); 1871 } 1872 1873 1874 ItemList::ItemList() : CurrentItem( 0 ) 1875 { 1876 } 1877 1878 const SfxPoolItem* ItemList::First() 1879 { 1880 CurrentItem = 0; 1881 return aItemPool.empty() ? nullptr : aItemPool[ 0 ]; 1882 } 1883 1884 const SfxPoolItem* ItemList::Next() 1885 { 1886 if ( CurrentItem + 1 < static_cast<sal_Int32>(aItemPool.size()) ) 1887 { 1888 ++CurrentItem; 1889 return aItemPool[ CurrentItem ]; 1890 } 1891 return nullptr; 1892 } 1893 1894 void ItemList::Insert( const SfxPoolItem* pItem ) 1895 { 1896 aItemPool.push_back( pItem ); 1897 CurrentItem = aItemPool.size() - 1; 1898 } 1899 1900 1901 EditDoc::EditDoc( SfxItemPool* pPool ) : 1902 nLastCache(0), 1903 pItemPool(pPool ? pPool : new EditEngineItemPool()), 1904 nDefTab(DEFTAB), 1905 bIsVertical(false), 1906 bIsTopToBottomVert(false), 1907 bIsFixedCellHeight(false), 1908 bOwnerOfPool(pPool == nullptr), 1909 bModified(false) 1910 { 1911 // Don't create an empty node, Clear() will be called in EditEngine-CTOR 1912 }; 1913 1914 EditDoc::~EditDoc() 1915 { 1916 ImplDestroyContents(); 1917 if ( bOwnerOfPool ) 1918 SfxItemPool::Free(pItemPool); 1919 } 1920 1921 namespace { 1922 1923 class RemoveEachItemFromPool 1924 { 1925 EditDoc& mrDoc; 1926 public: 1927 explicit RemoveEachItemFromPool(EditDoc& rDoc) : mrDoc(rDoc) {} 1928 void operator() (const std::unique_ptr<ContentNode>& rNode) 1929 { 1930 mrDoc.RemoveItemsFromPool(*rNode); 1931 } 1932 }; 1933 1934 struct ClearSpellErrorsHandler 1935 { 1936 void operator() (std::unique_ptr<ContentNode> const & rNode) 1937 { 1938 rNode->DestroyWrongList(); 1939 } 1940 }; 1941 1942 } 1943 1944 void EditDoc::ImplDestroyContents() 1945 { 1946 std::for_each(maContents.begin(), maContents.end(), RemoveEachItemFromPool(*this)); 1947 maContents.clear(); 1948 } 1949 1950 void EditDoc::RemoveItemsFromPool(const ContentNode& rNode) 1951 { 1952 for (sal_Int32 nAttr = 0; nAttr < rNode.GetCharAttribs().Count(); ++nAttr) 1953 { 1954 const EditCharAttrib& rAttr = *rNode.GetCharAttribs().GetAttribs()[nAttr].get(); 1955 GetItemPool().Remove(*rAttr.GetItem()); 1956 } 1957 } 1958 1959 void CreateFont( SvxFont& rFont, const SfxItemSet& rSet, bool bSearchInParent, SvtScriptType nScriptType ) 1960 { 1961 vcl::Font aPrevFont( rFont ); 1962 rFont.SetAlignment( ALIGN_BASELINE ); 1963 rFont.SetTransparent( true ); 1964 1965 sal_uInt16 nWhich_FontInfo = GetScriptItemId( EE_CHAR_FONTINFO, nScriptType ); 1966 sal_uInt16 nWhich_Language = GetScriptItemId( EE_CHAR_LANGUAGE, nScriptType ); 1967 sal_uInt16 nWhich_FontHeight = GetScriptItemId( EE_CHAR_FONTHEIGHT, nScriptType ); 1968 sal_uInt16 nWhich_Weight = GetScriptItemId( EE_CHAR_WEIGHT, nScriptType ); 1969 sal_uInt16 nWhich_Italic = GetScriptItemId( EE_CHAR_ITALIC, nScriptType ); 1970 1971 if ( bSearchInParent || ( rSet.GetItemState( nWhich_FontInfo ) == SfxItemState::SET ) ) 1972 { 1973 const SvxFontItem& rFontItem = static_cast<const SvxFontItem&>(rSet.Get( nWhich_FontInfo )); 1974 rFont.SetFamilyName( rFontItem.GetFamilyName() ); 1975 rFont.SetFamily( rFontItem.GetFamily() ); 1976 rFont.SetPitch( rFontItem.GetPitch() ); 1977 rFont.SetCharSet( rFontItem.GetCharSet() ); 1978 } 1979 if ( bSearchInParent || ( rSet.GetItemState( nWhich_Language ) == SfxItemState::SET ) ) 1980 rFont.SetLanguage( static_cast<const SvxLanguageItem&>(rSet.Get( nWhich_Language )).GetLanguage() ); 1981 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_COLOR ) == SfxItemState::SET ) ) 1982 rFont.SetColor( rSet.Get( EE_CHAR_COLOR ).GetValue() ); 1983 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_BKGCOLOR ) == SfxItemState::SET ) ) 1984 rFont.SetFillColor( rSet.Get( EE_CHAR_BKGCOLOR ).GetValue() ); 1985 if ( bSearchInParent || ( rSet.GetItemState( nWhich_FontHeight ) == SfxItemState::SET ) ) 1986 rFont.SetFontSize( Size( rFont.GetFontSize().Width(), static_cast<const SvxFontHeightItem&>(rSet.Get( nWhich_FontHeight ) ).GetHeight() ) ); 1987 if ( bSearchInParent || ( rSet.GetItemState( nWhich_Weight ) == SfxItemState::SET ) ) 1988 rFont.SetWeight( static_cast<const SvxWeightItem&>(rSet.Get( nWhich_Weight )).GetWeight() ); 1989 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_UNDERLINE ) == SfxItemState::SET ) ) 1990 rFont.SetUnderline( rSet.Get( EE_CHAR_UNDERLINE ).GetLineStyle() ); 1991 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_OVERLINE ) == SfxItemState::SET ) ) 1992 rFont.SetOverline( rSet.Get( EE_CHAR_OVERLINE ).GetLineStyle() ); 1993 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_STRIKEOUT ) == SfxItemState::SET ) ) 1994 rFont.SetStrikeout( rSet.Get( EE_CHAR_STRIKEOUT ).GetStrikeout() ); 1995 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_CASEMAP ) == SfxItemState::SET ) ) 1996 rFont.SetCaseMap( rSet.Get( EE_CHAR_CASEMAP ).GetCaseMap() ); 1997 if ( bSearchInParent || ( rSet.GetItemState( nWhich_Italic ) == SfxItemState::SET ) ) 1998 rFont.SetItalic( static_cast<const SvxPostureItem&>(rSet.Get( nWhich_Italic )).GetPosture() ); 1999 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_OUTLINE ) == SfxItemState::SET ) ) 2000 rFont.SetOutline( rSet.Get( EE_CHAR_OUTLINE ).GetValue() ); 2001 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_SHADOW ) == SfxItemState::SET ) ) 2002 rFont.SetShadow( rSet.Get( EE_CHAR_SHADOW ).GetValue() ); 2003 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_ESCAPEMENT ) == SfxItemState::SET ) ) 2004 { 2005 const SvxEscapementItem& rEsc = rSet.Get( EE_CHAR_ESCAPEMENT ); 2006 2007 sal_uInt16 const nProp = rEsc.GetProportionalHeight(); 2008 rFont.SetPropr( static_cast<sal_uInt8>(nProp) ); 2009 2010 short nEsc = rEsc.GetEsc(); 2011 if ( nEsc == DFLT_ESC_AUTO_SUPER ) 2012 nEsc = 100 - nProp; 2013 else if ( nEsc == DFLT_ESC_AUTO_SUB ) 2014 nEsc = sal::static_int_cast< short >( -( 100 - nProp ) ); 2015 rFont.SetEscapement( nEsc ); 2016 } 2017 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_PAIRKERNING ) == SfxItemState::SET ) ) 2018 rFont.SetKerning( rSet.Get( EE_CHAR_PAIRKERNING ).GetValue() ? FontKerning::FontSpecific : FontKerning::NONE ); 2019 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_KERNING ) == SfxItemState::SET ) ) 2020 rFont.SetFixKerning( rSet.Get( EE_CHAR_KERNING ).GetValue() ); 2021 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_WLM ) == SfxItemState::SET ) ) 2022 rFont.SetWordLineMode( rSet.Get( EE_CHAR_WLM ).GetValue() ); 2023 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_EMPHASISMARK ) == SfxItemState::SET ) ) 2024 rFont.SetEmphasisMark( rSet.Get( EE_CHAR_EMPHASISMARK ).GetEmphasisMark() ); 2025 if ( bSearchInParent || ( rSet.GetItemState( EE_CHAR_RELIEF ) == SfxItemState::SET ) ) 2026 rFont.SetRelief( rSet.Get( EE_CHAR_RELIEF ).GetValue() ); 2027 2028 // Operator == compares the individual members of the font if the impl pointer is 2029 // not equal. If all members are the same, this assignment makes 2030 // sure that both also point to the same internal instance of the font. 2031 // To avoid this assignment, you would need to check in 2032 // every if statement above whether or not the new value differs from the 2033 // old value before making an assignment. 2034 if ( rFont == aPrevFont ) 2035 rFont = aPrevFont; // => The same ImpPointer for IsSameInstance 2036 } 2037 2038 void EditDoc::CreateDefFont( bool bUseStyles ) 2039 { 2040 SfxItemSet aTmpSet( GetItemPool(), svl::Items<EE_PARA_START, EE_CHAR_END>{} ); 2041 CreateFont( aDefFont, aTmpSet ); 2042 aDefFont.SetVertical( IsVertical() ); 2043 aDefFont.SetOrientation( IsVertical() ? (IsTopToBottom() ? 2700 : 900) : 0 ); 2044 2045 for ( sal_Int32 nNode = 0; nNode < Count(); nNode++ ) 2046 { 2047 ContentNode* pNode = GetObject( nNode ); 2048 pNode->GetCharAttribs().GetDefFont() = aDefFont; 2049 if ( bUseStyles ) 2050 pNode->CreateDefFont(); 2051 } 2052 } 2053 2054 sal_Int32 EditDoc::GetPos(const ContentNode* p) const 2055 { 2056 return FastGetPos(maContents, p, nLastCache); 2057 } 2058 2059 const ContentNode* EditDoc::GetObject(sal_Int32 nPos) const 2060 { 2061 return 0 <= nPos && nPos < static_cast<sal_Int32>(maContents.size()) ? maContents[nPos].get() : nullptr; 2062 } 2063 2064 ContentNode* EditDoc::GetObject(sal_Int32 nPos) 2065 { 2066 return 0 <= nPos && nPos < static_cast<sal_Int32>(maContents.size()) ? maContents[nPos].get() : nullptr; 2067 } 2068 2069 const ContentNode* EditDoc::operator[](sal_Int32 nPos) const 2070 { 2071 return GetObject(nPos); 2072 } 2073 2074 ContentNode* EditDoc::operator[](sal_Int32 nPos) 2075 { 2076 return GetObject(nPos); 2077 } 2078 2079 void EditDoc::Insert(sal_Int32 nPos, ContentNode* p) 2080 { 2081 if (nPos < 0 || nPos == SAL_MAX_INT32) 2082 { 2083 SAL_WARN( "editeng", "EditDoc::Insert - overflow pos " << nPos); 2084 return; 2085 } 2086 maContents.insert(maContents.begin()+nPos, std::unique_ptr<ContentNode>(p)); 2087 } 2088 2089 void EditDoc::Remove(sal_Int32 nPos) 2090 { 2091 if (nPos < 0 || nPos >= static_cast<sal_Int32>(maContents.size())) 2092 { 2093 SAL_WARN( "editeng", "EditDoc::Remove - out of bounds pos " << nPos); 2094 return; 2095 } 2096 maContents.erase(maContents.begin() + nPos); 2097 } 2098 2099 void EditDoc::Release(sal_Int32 nPos) 2100 { 2101 if (nPos < 0 || nPos >= static_cast<sal_Int32>(maContents.size())) 2102 { 2103 SAL_WARN( "editeng", "EditDoc::Release - out of bounds pos " << nPos); 2104 return; 2105 } 2106 (void)maContents[nPos].release(); 2107 maContents.erase(maContents.begin() + nPos); 2108 } 2109 2110 sal_Int32 EditDoc::Count() const 2111 { 2112 size_t nSize = maContents.size(); 2113 if (nSize > SAL_MAX_INT32) 2114 { 2115 SAL_WARN( "editeng", "EditDoc::Count - overflow " << nSize); 2116 return SAL_MAX_INT32; 2117 } 2118 return nSize; 2119 } 2120 2121 OUString EditDoc::GetSepStr( LineEnd eEnd ) 2122 { 2123 if ( eEnd == LINEEND_CR ) 2124 return "\015"; // 0x0d 2125 if ( eEnd == LINEEND_LF ) 2126 return "\012"; // 0x0a 2127 return "\015\012"; // 0x0d, 0x0a 2128 } 2129 2130 OUString EditDoc::GetText( LineEnd eEnd ) const 2131 { 2132 const sal_Int32 nNodes = Count(); 2133 if (nNodes == 0) 2134 return OUString(); 2135 2136 const OUString aSep = EditDoc::GetSepStr( eEnd ); 2137 const sal_Int32 nSepSize = aSep.getLength(); 2138 const sal_uInt32 nLen = GetTextLen() + (nNodes - 1)*nSepSize; 2139 2140 OUStringBuffer aBuffer(nLen + 16); // leave some slack 2141 2142 for ( sal_Int32 nNode = 0; nNode < nNodes; nNode++ ) 2143 { 2144 if ( nSepSize && nNode>0 ) 2145 { 2146 aBuffer.append(aSep); 2147 } 2148 aBuffer.append(GetParaAsString( GetObject(nNode) )); 2149 } 2150 2151 return aBuffer.makeStringAndClear(); 2152 } 2153 2154 OUString EditDoc::GetParaAsString( sal_Int32 nNode ) const 2155 { 2156 return GetParaAsString( GetObject( nNode ) ); 2157 } 2158 2159 OUString EditDoc::GetParaAsString( 2160 const ContentNode* pNode, sal_Int32 nStartPos, sal_Int32 nEndPos) 2161 { 2162 return pNode->GetExpandedText(nStartPos, nEndPos); 2163 } 2164 2165 EditPaM EditDoc::GetStartPaM() const 2166 { 2167 ContentNode* p = const_cast<ContentNode*>(GetObject(0)); 2168 return EditPaM(p, 0); 2169 } 2170 2171 EditPaM EditDoc::GetEndPaM() const 2172 { 2173 ContentNode* pLastNode = const_cast<ContentNode*>(GetObject(Count()-1)); 2174 return EditPaM( pLastNode, pLastNode->Len() ); 2175 } 2176 2177 sal_uLong EditDoc::GetTextLen() const 2178 { 2179 sal_uLong nLen = 0; 2180 for ( sal_Int32 nNode = 0; nNode < Count(); nNode++ ) 2181 { 2182 const ContentNode* pNode = GetObject( nNode ); 2183 nLen += pNode->GetExpandedLen(); 2184 } 2185 return nLen; 2186 } 2187 2188 EditPaM EditDoc::Clear() 2189 { 2190 ImplDestroyContents(); 2191 2192 ContentNode* pNode = new ContentNode( GetItemPool() ); 2193 Insert(0, pNode); 2194 2195 CreateDefFont(false); 2196 2197 SetModified(false); 2198 2199 return EditPaM( pNode, 0 ); 2200 } 2201 2202 void EditDoc::ClearSpellErrors() 2203 { 2204 std::for_each(maContents.begin(), maContents.end(), ClearSpellErrorsHandler()); 2205 } 2206 2207 void EditDoc::SetModified( bool b ) 2208 { 2209 bModified = b; 2210 if ( bModified ) 2211 { 2212 aModifyHdl.Call( nullptr ); 2213 } 2214 } 2215 2216 EditPaM EditDoc::RemoveText() 2217 { 2218 // Keep the old ItemSet, to keep the chart Font. 2219 ContentNode* pPrevFirstNode = GetObject(0); 2220 SfxStyleSheet* pPrevStyle = pPrevFirstNode->GetStyleSheet(); 2221 SfxItemSet aPrevSet( pPrevFirstNode->GetContentAttribs().GetItems() ); 2222 vcl::Font aPrevFont( pPrevFirstNode->GetCharAttribs().GetDefFont() ); 2223 2224 ImplDestroyContents(); 2225 2226 ContentNode* pNode = new ContentNode( GetItemPool() ); 2227 Insert(0, pNode); 2228 2229 pNode->SetStyleSheet(pPrevStyle, false); 2230 pNode->GetContentAttribs().GetItems().Set( aPrevSet ); 2231 pNode->GetCharAttribs().GetDefFont() = aPrevFont; 2232 2233 SetModified(true); 2234 2235 return EditPaM( pNode, 0 ); 2236 } 2237 2238 EditPaM EditDoc::InsertText( EditPaM aPaM, const OUString& rStr ) 2239 { 2240 DBG_ASSERT( rStr.indexOf( 0x0A ) == -1, "EditDoc::InsertText: Newlines prohibited in paragraph!" ); 2241 DBG_ASSERT( rStr.indexOf( 0x0D ) == -1, "EditDoc::InsertText: Newlines prohibited in paragraph!" ); 2242 DBG_ASSERT( rStr.indexOf( '\t' ) == -1, "EditDoc::InsertText: Newlines prohibited in paragraph!" ); 2243 assert(aPaM.GetNode()); 2244 2245 aPaM.GetNode()->Insert( rStr, aPaM.GetIndex() ); 2246 aPaM.GetNode()->ExpandAttribs( aPaM.GetIndex(), rStr.getLength(), GetItemPool() ); 2247 aPaM.SetIndex( aPaM.GetIndex() + rStr.getLength() ); 2248 2249 SetModified( true ); 2250 2251 return aPaM; 2252 } 2253 2254 EditPaM EditDoc::InsertParaBreak( EditPaM aPaM, bool bKeepEndingAttribs ) 2255 { 2256 assert(aPaM.GetNode()); 2257 ContentNode* pCurNode = aPaM.GetNode(); 2258 sal_Int32 nPos = GetPos( pCurNode ); 2259 OUString aStr = aPaM.GetNode()->Copy( aPaM.GetIndex() ); 2260 aPaM.GetNode()->Erase( aPaM.GetIndex() ); 2261 2262 // the paragraph attributes... 2263 ContentAttribs aContentAttribs( aPaM.GetNode()->GetContentAttribs() ); 2264 2265 // for a new paragraph we like to have the bullet/numbering visible by default 2266 aContentAttribs.GetItems().Put( SfxBoolItem( EE_PARA_BULLETSTATE, true) ); 2267 2268 // ContentNode constructor copies also the paragraph attributes 2269 ContentNode* pNode = new ContentNode( aStr, aContentAttribs ); 2270 2271 // Copy the Default Font 2272 pNode->GetCharAttribs().GetDefFont() = aPaM.GetNode()->GetCharAttribs().GetDefFont(); 2273 SfxStyleSheet* pStyle = aPaM.GetNode()->GetStyleSheet(); 2274 if ( pStyle ) 2275 { 2276 OUString aFollow( pStyle->GetFollow() ); 2277 if ( !aFollow.isEmpty() && ( aFollow != pStyle->GetName() ) ) 2278 { 2279 SfxStyleSheetBase* pNext = pStyle->GetPool()->Find( aFollow, pStyle->GetFamily() ); 2280 pNode->SetStyleSheet( static_cast<SfxStyleSheet*>(pNext) ); 2281 } 2282 } 2283 2284 // Character attributes may need to be copied or trimmed: 2285 pNode->CopyAndCutAttribs( aPaM.GetNode(), GetItemPool(), bKeepEndingAttribs ); 2286 2287 Insert(nPos+1, pNode); 2288 2289 SetModified(true); 2290 2291 aPaM.SetNode( pNode ); 2292 aPaM.SetIndex( 0 ); 2293 return aPaM; 2294 } 2295 2296 EditPaM EditDoc::InsertFeature( EditPaM aPaM, const SfxPoolItem& rItem ) 2297 { 2298 assert(aPaM.GetNode()); 2299 2300 aPaM.GetNode()->Insert( OUString(CH_FEATURE), aPaM.GetIndex() ); 2301 aPaM.GetNode()->ExpandAttribs( aPaM.GetIndex(), 1, GetItemPool() ); 2302 2303 // Create a feature-attribute for the feature... 2304 EditCharAttrib* pAttrib = MakeCharAttrib( GetItemPool(), rItem, aPaM.GetIndex(), aPaM.GetIndex()+1 ); 2305 assert(pAttrib); 2306 aPaM.GetNode()->GetCharAttribs().InsertAttrib( pAttrib ); 2307 2308 SetModified( true ); 2309 2310 aPaM.SetIndex( aPaM.GetIndex() + 1 ); 2311 return aPaM; 2312 } 2313 2314 EditPaM EditDoc::ConnectParagraphs( ContentNode* pLeft, ContentNode* pRight ) 2315 { 2316 const EditPaM aPaM( pLeft, pLeft->Len() ); 2317 2318 // First the attributes, otherwise nLen will not be correct! 2319 pLeft->AppendAttribs( pRight ); 2320 // then the Text... 2321 pLeft->Append(pRight->GetString()); 2322 2323 // the one to the right disappears. 2324 RemoveItemsFromPool(*pRight); 2325 sal_Int32 nRight = GetPos( pRight ); 2326 Remove( nRight ); 2327 2328 SetModified(true); 2329 2330 return aPaM; 2331 } 2332 2333 void EditDoc::RemoveChars( EditPaM aPaM, sal_Int32 nChars ) 2334 { 2335 // Maybe remove Features! 2336 aPaM.GetNode()->Erase( aPaM.GetIndex(), nChars ); 2337 aPaM.GetNode()->CollapseAttribs( aPaM.GetIndex(), nChars, GetItemPool() ); 2338 2339 SetModified( true ); 2340 } 2341 2342 void EditDoc::InsertAttribInSelection( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, const SfxPoolItem& rPoolItem ) 2343 { 2344 assert(pNode); 2345 DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribute too large!" ); 2346 2347 // for Optimization: 2348 // This ends at the beginning of the selection => can be expanded 2349 EditCharAttrib* pEndingAttrib = nullptr; 2350 // This starts at the end of the selection => can be expanded 2351 EditCharAttrib* pStartingAttrib = nullptr; 2352 2353 DBG_ASSERT( nStart <= nEnd, "Small miscalculations in InsertAttribInSelection" ); 2354 2355 RemoveAttribs( pNode, nStart, nEnd, pStartingAttrib, pEndingAttrib, rPoolItem.Which() ); 2356 2357 if ( pStartingAttrib && pEndingAttrib && 2358 ( *(pStartingAttrib->GetItem()) == rPoolItem ) && 2359 ( *(pEndingAttrib->GetItem()) == rPoolItem ) ) 2360 { 2361 // Will become a large Attribute. 2362 pEndingAttrib->GetEnd() = pStartingAttrib->GetEnd(); 2363 GetItemPool().Remove( *(pStartingAttrib->GetItem()) ); 2364 pNode->GetCharAttribs().Remove(pStartingAttrib); 2365 } 2366 else if ( pStartingAttrib && ( *(pStartingAttrib->GetItem()) == rPoolItem ) ) 2367 pStartingAttrib->GetStart() = nStart; 2368 else if ( pEndingAttrib && ( *(pEndingAttrib->GetItem()) == rPoolItem ) ) 2369 pEndingAttrib->GetEnd() = nEnd; 2370 else 2371 InsertAttrib( rPoolItem, pNode, nStart, nEnd ); 2372 2373 if ( pStartingAttrib ) 2374 pNode->GetCharAttribs().ResortAttribs(); 2375 2376 SetModified(true); 2377 } 2378 2379 bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, sal_uInt16 nWhich ) 2380 { 2381 EditCharAttrib* pStarting; 2382 EditCharAttrib* pEnding; 2383 return RemoveAttribs( pNode, nStart, nEnd, pStarting, pEnding, nWhich ); 2384 } 2385 2386 bool EditDoc::RemoveAttribs( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, EditCharAttrib*& rpStarting, EditCharAttrib*& rpEnding, sal_uInt16 nWhich ) 2387 { 2388 2389 assert(pNode); 2390 DBG_ASSERT( nEnd <= pNode->Len(), "InsertAttrib: Attribute too large!" ); 2391 2392 // This ends at the beginning of the selection => can be expanded 2393 rpEnding = nullptr; 2394 // This starts at the end of the selection => can be expanded 2395 rpStarting = nullptr; 2396 2397 bool bChanged = false; 2398 2399 DBG_ASSERT( nStart <= nEnd, "Small miscalculations in InsertAttribInSelection" ); 2400 2401 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2402 CharAttribList::DbgCheckAttribs(pNode->GetCharAttribs()); 2403 #endif 2404 2405 // iterate over the attributes ... 2406 sal_Int32 nAttr = 0; 2407 CharAttribList::AttribsType& rAttribs = pNode->GetCharAttribs().GetAttribs(); 2408 EditCharAttrib* pAttr = GetAttrib(rAttribs, nAttr); 2409 while ( pAttr ) 2410 { 2411 bool bRemoveAttrib = false; 2412 sal_uInt16 nAttrWhich = pAttr->Which(); 2413 if ( ( nAttrWhich < EE_FEATURE_START ) && ( !nWhich || ( nAttrWhich == nWhich ) ) ) 2414 { 2415 // Attribute starts in Selection 2416 if ( ( pAttr->GetStart() >= nStart ) && ( pAttr->GetStart() <= nEnd ) ) 2417 { 2418 bChanged = true; 2419 if ( pAttr->GetEnd() > nEnd ) 2420 { 2421 pAttr->GetStart() = nEnd; // then it starts after this 2422 rpStarting = pAttr; 2423 if ( nWhich ) 2424 break; // There can be no further attributes here 2425 } 2426 else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) ) 2427 { 2428 // Delete feature only if on the exact spot 2429 bRemoveAttrib = true; 2430 } 2431 } 2432 2433 // Attribute ends in Selection 2434 else if ( ( pAttr->GetEnd() >= nStart ) && ( pAttr->GetEnd() <= nEnd ) ) 2435 { 2436 bChanged = true; 2437 if ( ( pAttr->GetStart() < nStart ) && !pAttr->IsFeature() ) 2438 { 2439 pAttr->GetEnd() = nStart; // then it ends here 2440 rpEnding = pAttr; 2441 } 2442 else if ( !pAttr->IsFeature() || ( pAttr->GetStart() == nStart ) ) 2443 { 2444 // Delete feature only if on the exact spot 2445 bRemoveAttrib = true; 2446 } 2447 } 2448 // Attribute overlaps the selection 2449 else if ( ( pAttr->GetStart() <= nStart ) && ( pAttr->GetEnd() >= nEnd ) ) 2450 { 2451 bChanged = true; 2452 if ( pAttr->GetStart() == nStart ) 2453 { 2454 pAttr->GetStart() = nEnd; 2455 rpStarting = pAttr; 2456 if ( nWhich ) 2457 break; // There can be further attributes! 2458 } 2459 else if ( pAttr->GetEnd() == nEnd ) 2460 { 2461 pAttr->GetEnd() = nStart; 2462 rpEnding = pAttr; 2463 if ( nWhich ) 2464 break; // There can be further attributes! 2465 } 2466 else // Attribute must be split ... 2467 { 2468 sal_Int32 nOldEnd = pAttr->GetEnd(); 2469 pAttr->GetEnd() = nStart; 2470 rpEnding = pAttr; 2471 InsertAttrib( *pAttr->GetItem(), pNode, nEnd, nOldEnd ); 2472 if ( nWhich ) 2473 break; // There can be further attributes! 2474 } 2475 } 2476 } 2477 if ( bRemoveAttrib ) 2478 { 2479 DBG_ASSERT( ( pAttr != rpStarting ) && ( pAttr != rpEnding ), "Delete and retain the same attribute?" ); 2480 DBG_ASSERT( !pAttr->IsFeature(), "RemoveAttribs: Remove a feature?!" ); 2481 GetItemPool().Remove( *pAttr->GetItem() ); 2482 rAttribs.erase(rAttribs.begin()+nAttr); 2483 nAttr--; 2484 } 2485 nAttr++; 2486 pAttr = GetAttrib(rAttribs, nAttr); 2487 } 2488 2489 if ( bChanged ) 2490 { 2491 // char attributes need to be sorted by start again 2492 pNode->GetCharAttribs().ResortAttribs(); 2493 SetModified(true); 2494 } 2495 2496 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2497 CharAttribList::DbgCheckAttribs(pNode->GetCharAttribs()); 2498 #endif 2499 2500 return bChanged; 2501 } 2502 2503 void EditDoc::InsertAttrib( const SfxPoolItem& rPoolItem, ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd ) 2504 { 2505 // This method no longer checks whether a corresponding attribute already 2506 // exists at this place! 2507 EditCharAttrib* pAttrib = MakeCharAttrib( GetItemPool(), rPoolItem, nStart, nEnd ); 2508 assert(pAttrib); 2509 pNode->GetCharAttribs().InsertAttrib( pAttrib ); 2510 2511 SetModified( true ); 2512 } 2513 2514 void EditDoc::InsertAttrib( ContentNode* pNode, sal_Int32 nStart, sal_Int32 nEnd, const SfxPoolItem& rPoolItem ) 2515 { 2516 if ( nStart != nEnd ) 2517 { 2518 InsertAttribInSelection( pNode, nStart, nEnd, rPoolItem ); 2519 } 2520 else 2521 { 2522 // Check whether already a new attribute with WhichId exists at this place: 2523 CharAttribList& rAttrList = pNode->GetCharAttribs(); 2524 EditCharAttrib* pAttr = rAttrList.FindEmptyAttrib( rPoolItem.Which(), nStart ); 2525 if ( pAttr ) 2526 { 2527 // Remove attribute... 2528 rAttrList.Remove(pAttr); 2529 } 2530 2531 // check whether 'the same' attribute exist at this place. 2532 pAttr = rAttrList.FindAttrib( rPoolItem.Which(), nStart ); 2533 if ( pAttr ) 2534 { 2535 if ( pAttr->IsInside( nStart ) ) // split 2536 { 2537 // check again if really splitting, or return ! 2538 sal_Int32 nOldEnd = pAttr->GetEnd(); 2539 pAttr->GetEnd() = nStart; 2540 EditCharAttrib* pNew = MakeCharAttrib( GetItemPool(), *(pAttr->GetItem()), nStart, nOldEnd ); 2541 rAttrList.InsertAttrib(pNew); 2542 } 2543 else if ( pAttr->GetEnd() == nStart ) 2544 { 2545 DBG_ASSERT( !pAttr->IsEmpty(), "Still an empty attribute?" ); 2546 // Check if exactly the same attribute 2547 if ( *(pAttr->GetItem()) == rPoolItem ) 2548 return; 2549 } 2550 } 2551 InsertAttrib( rPoolItem, pNode, nStart, nStart ); 2552 } 2553 2554 SetModified( true ); 2555 } 2556 2557 void EditDoc::FindAttribs( ContentNode* pNode, sal_Int32 nStartPos, sal_Int32 nEndPos, SfxItemSet& rCurSet ) 2558 { 2559 assert(pNode); 2560 DBG_ASSERT( nStartPos <= nEndPos, "Invalid region!" ); 2561 2562 sal_uInt16 nAttr = 0; 2563 EditCharAttrib* pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr ); 2564 // No Selection... 2565 if ( nStartPos == nEndPos ) 2566 { 2567 while ( pAttr && ( pAttr->GetStart() <= nEndPos) ) 2568 { 2569 const SfxPoolItem* pItem = nullptr; 2570 // Attribute is about... 2571 if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() > nStartPos ) ) 2572 pItem = pAttr->GetItem(); 2573 // Attribute ending here is not empty 2574 else if ( ( pAttr->GetStart() < nStartPos ) && ( pAttr->GetEnd() == nStartPos ) ) 2575 { 2576 if ( !pNode->GetCharAttribs().FindEmptyAttrib( pAttr->GetItem()->Which(), nStartPos ) ) 2577 pItem = pAttr->GetItem(); 2578 } 2579 // Attribute ending here is empty 2580 else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() == nStartPos ) ) 2581 { 2582 pItem = pAttr->GetItem(); 2583 } 2584 // Attribute starts here 2585 else if ( ( pAttr->GetStart() == nStartPos ) && ( pAttr->GetEnd() > nStartPos ) ) 2586 { 2587 if ( nStartPos == 0 ) // special case 2588 pItem = pAttr->GetItem(); 2589 } 2590 2591 if ( pItem ) 2592 { 2593 sal_uInt16 nWhich = pItem->Which(); 2594 if ( rCurSet.GetItemState( nWhich ) == SfxItemState::DEFAULT ) 2595 { 2596 rCurSet.Put( *pItem ); 2597 } 2598 else if ( rCurSet.GetItemState( nWhich ) == SfxItemState::SET ) 2599 { 2600 const SfxPoolItem& rItem = rCurSet.Get( nWhich ); 2601 if ( rItem != *pItem ) 2602 { 2603 rCurSet.InvalidateItem( nWhich ); 2604 } 2605 } 2606 } 2607 nAttr++; 2608 pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr ); 2609 } 2610 } 2611 else // Selection 2612 { 2613 while ( pAttr && ( pAttr->GetStart() < nEndPos) ) 2614 { 2615 const SfxPoolItem* pItem = nullptr; 2616 // Attribute is about... 2617 if ( ( pAttr->GetStart() <= nStartPos ) && ( pAttr->GetEnd() >= nEndPos ) ) 2618 pItem = pAttr->GetItem(); 2619 // Attribute starts right in the middle ... 2620 else if ( pAttr->GetStart() >= nStartPos ) 2621 { 2622 // !!! pItem = pAttr->GetItem(); 2623 // PItem is simply not enough, since one for example in case 2624 // of Shadow, would never find an unequal item, since such a 2625 // item represents its presence by absence! 2626 // If (...) 2627 // It needs to be examined on exactly the same attribute at the 2628 // break point, which is quite expensive. 2629 // Since optimization is done when inserting the attributes 2630 // this case does not appear so fast... 2631 // So based on the need for speed: 2632 rCurSet.InvalidateItem( pAttr->GetItem()->Which() ); 2633 2634 } 2635 // Attribute ends in the middle of it ... 2636 else if ( pAttr->GetEnd() > nStartPos ) 2637 { 2638 rCurSet.InvalidateItem( pAttr->GetItem()->Which() ); 2639 } 2640 2641 if ( pItem ) 2642 { 2643 sal_uInt16 nWhich = pItem->Which(); 2644 if ( rCurSet.GetItemState( nWhich ) == SfxItemState::DEFAULT ) 2645 { 2646 rCurSet.Put( *pItem ); 2647 } 2648 else if ( rCurSet.GetItemState( nWhich ) == SfxItemState::SET ) 2649 { 2650 const SfxPoolItem& rItem = rCurSet.Get( nWhich ); 2651 if ( rItem != *pItem ) 2652 { 2653 rCurSet.InvalidateItem( nWhich ); 2654 } 2655 } 2656 } 2657 nAttr++; 2658 pAttr = GetAttrib( pNode->GetCharAttribs().GetAttribs(), nAttr ); 2659 } 2660 } 2661 } 2662 2663 void EditDoc::dumpAsXml(xmlTextWriterPtr pWriter) const 2664 { 2665 bool bOwns = false; 2666 if (!pWriter) 2667 { 2668 pWriter = xmlNewTextWriterFilename("editdoc.xml", 0); 2669 xmlTextWriterSetIndent(pWriter,1); 2670 xmlTextWriterSetIndentString(pWriter, BAD_CAST(" ")); 2671 xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr); 2672 bOwns = true; 2673 } 2674 2675 xmlTextWriterStartElement(pWriter, BAD_CAST("EditDoc")); 2676 for (auto const & i : maContents) 2677 { 2678 i->dumpAsXml(pWriter); 2679 } 2680 xmlTextWriterEndElement(pWriter); 2681 2682 if (bOwns) 2683 { 2684 xmlTextWriterEndDocument(pWriter); 2685 xmlFreeTextWriter(pWriter); 2686 } 2687 } 2688 2689 2690 namespace { 2691 2692 struct LessByStart 2693 { 2694 bool operator() (const std::unique_ptr<EditCharAttrib>& left, const std::unique_ptr<EditCharAttrib>& right) const 2695 { 2696 return left->GetStart() < right->GetStart(); 2697 } 2698 }; 2699 2700 } 2701 2702 CharAttribList::CharAttribList() 2703 : aAttribs() 2704 , aDefFont() 2705 , bHasEmptyAttribs(false) 2706 { 2707 } 2708 2709 CharAttribList::~CharAttribList() 2710 { 2711 } 2712 2713 void CharAttribList::InsertAttrib( EditCharAttrib* pAttrib ) 2714 { 2715 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! 2716 // optimize: binary search? ! 2717 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!! 2718 2719 // Maybe just simply iterate backwards: 2720 // The most common and critical case: Attributes are already sorted 2721 // (InsertBinTextObject!) binary search would not be optimal here. 2722 // => Would bring something! 2723 2724 const sal_Int32 nStart = pAttrib->GetStart(); // may be better for Comp.Opt. 2725 2726 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2727 CharAttribList::DbgCheckAttribs(*this); 2728 #endif 2729 2730 if ( pAttrib->IsEmpty() ) 2731 bHasEmptyAttribs = true; 2732 2733 bool bInsert(true); 2734 for (sal_Int32 i = 0, n = aAttribs.size(); i < n; ++i) 2735 { 2736 const EditCharAttrib& rCurAttrib = *aAttribs[i].get(); 2737 if (rCurAttrib.GetStart() > nStart) 2738 { 2739 aAttribs.insert(aAttribs.begin()+i, std::unique_ptr<EditCharAttrib>(pAttrib)); 2740 bInsert = false; 2741 break; 2742 } 2743 } 2744 2745 if (bInsert) aAttribs.push_back(std::unique_ptr<EditCharAttrib>(pAttrib)); 2746 2747 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2748 CharAttribList::DbgCheckAttribs(*this); 2749 #endif 2750 } 2751 2752 void CharAttribList::ResortAttribs() 2753 { 2754 std::sort(aAttribs.begin(), aAttribs.end(), LessByStart()); 2755 2756 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2757 CharAttribList::DbgCheckAttribs(*this); 2758 #endif 2759 } 2760 2761 void CharAttribList::OptimizeRanges( SfxItemPool& rItemPool ) 2762 { 2763 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2764 CharAttribList::DbgCheckAttribs(*this); 2765 #endif 2766 for (sal_Int32 i = 0; i < static_cast<sal_Int32>(aAttribs.size()); ++i) 2767 { 2768 EditCharAttrib& rAttr = *aAttribs[i].get(); 2769 for (sal_Int32 nNext = i+1; nNext < static_cast<sal_Int32>(aAttribs.size()); ++nNext) 2770 { 2771 EditCharAttrib& rNext = *aAttribs[nNext].get(); 2772 if (!rAttr.IsFeature() && rNext.GetStart() == rAttr.GetEnd() && rNext.Which() == rAttr.Which()) 2773 { 2774 if (*rNext.GetItem() == *rAttr.GetItem()) 2775 { 2776 rAttr.GetEnd() = rNext.GetEnd(); 2777 rItemPool.Remove(*rNext.GetItem()); 2778 aAttribs.erase(aAttribs.begin()+nNext); 2779 } 2780 break; // only 1 attr with same which can start here. 2781 } 2782 else if (rNext.GetStart() > rAttr.GetEnd()) 2783 { 2784 break; 2785 } 2786 } 2787 } 2788 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2789 CharAttribList::DbgCheckAttribs(*this); 2790 #endif 2791 } 2792 2793 sal_Int32 CharAttribList::Count() const 2794 { 2795 return aAttribs.size(); 2796 } 2797 2798 const EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos ) const 2799 { 2800 // Backwards, if one ends where the next starts. 2801 // => The starting one is the valid one ... 2802 AttribsType::const_reverse_iterator it = std::find_if(aAttribs.rbegin(), aAttribs.rend(), 2803 [&nWhich, &nPos](const AttribsType::value_type& rxAttr) { 2804 return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); }); 2805 if (it != aAttribs.rend()) 2806 { 2807 const EditCharAttrib& rAttr = **it; 2808 return &rAttr; 2809 } 2810 return nullptr; 2811 } 2812 2813 EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos ) 2814 { 2815 // Backwards, if one ends where the next starts. 2816 // => The starting one is the valid one ... 2817 AttribsType::reverse_iterator it = std::find_if(aAttribs.rbegin(), aAttribs.rend(), 2818 [&nWhich, &nPos](AttribsType::value_type& rxAttr) { 2819 return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); }); 2820 if (it != aAttribs.rend()) 2821 { 2822 EditCharAttrib& rAttr = **it; 2823 return &rAttr; 2824 } 2825 return nullptr; 2826 } 2827 2828 const EditCharAttrib* CharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_Int32 nFromPos ) const 2829 { 2830 assert(nWhich); 2831 for (auto const& attrib : aAttribs) 2832 { 2833 const EditCharAttrib& rAttr = *attrib; 2834 if (rAttr.GetStart() >= nFromPos && rAttr.Which() == nWhich) 2835 return &rAttr; 2836 } 2837 return nullptr; 2838 } 2839 2840 bool CharAttribList::HasAttrib( sal_Int32 nStartPos, sal_Int32 nEndPos ) const 2841 { 2842 return std::any_of(aAttribs.rbegin(), aAttribs.rend(), 2843 [&nStartPos, &nEndPos](const AttribsType::value_type& rxAttr) { 2844 return rxAttr->GetStart() < nEndPos && rxAttr->GetEnd() > nStartPos; }); 2845 } 2846 2847 2848 namespace { 2849 2850 class FindByAddress 2851 { 2852 const EditCharAttrib* mpAttr; 2853 public: 2854 explicit FindByAddress(const EditCharAttrib* p) : mpAttr(p) {} 2855 bool operator() (const std::unique_ptr<EditCharAttrib>& r) const 2856 { 2857 return r.get() == mpAttr; 2858 } 2859 }; 2860 2861 } 2862 2863 void CharAttribList::Remove(const EditCharAttrib* p) 2864 { 2865 AttribsType::iterator it = std::find_if(aAttribs.begin(), aAttribs.end(), FindByAddress(p)); 2866 if (it != aAttribs.end()) 2867 aAttribs.erase(it); 2868 } 2869 2870 void CharAttribList::Remove(sal_Int32 nPos) 2871 { 2872 if (nPos >= static_cast<sal_Int32>(aAttribs.size())) 2873 return; 2874 2875 aAttribs.erase(aAttribs.begin()+nPos); 2876 } 2877 2878 void CharAttribList::SetHasEmptyAttribs(bool b) 2879 { 2880 bHasEmptyAttribs = b; 2881 } 2882 2883 bool CharAttribList::HasBoundingAttrib( sal_Int32 nBound ) const 2884 { 2885 // Backwards, if one ends where the next starts. 2886 // => The starting one is the valid one ... 2887 AttribsType::const_reverse_iterator it = aAttribs.rbegin(), itEnd = aAttribs.rend(); 2888 for (; it != itEnd; ++it) 2889 { 2890 const EditCharAttrib& rAttr = **it; 2891 if (rAttr.GetEnd() < nBound) 2892 return false; 2893 2894 if (rAttr.GetStart() == nBound || rAttr.GetEnd() == nBound) 2895 return true; 2896 } 2897 return false; 2898 } 2899 2900 EditCharAttrib* CharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_Int32 nPos ) 2901 { 2902 if ( !bHasEmptyAttribs ) 2903 return nullptr; 2904 2905 for (const std::unique_ptr<EditCharAttrib>& rAttr : aAttribs) 2906 { 2907 if (rAttr->GetStart() == nPos && rAttr->GetEnd() == nPos && rAttr->Which() == nWhich) 2908 return rAttr.get(); 2909 } 2910 return nullptr; 2911 } 2912 2913 namespace { 2914 2915 class FindByStartPos 2916 { 2917 sal_Int32 mnPos; 2918 public: 2919 explicit FindByStartPos(sal_Int32 nPos) : mnPos(nPos) {} 2920 bool operator() (const std::unique_ptr<EditCharAttrib>& r) const 2921 { 2922 return r->GetStart() >= mnPos; 2923 } 2924 }; 2925 2926 } 2927 2928 const EditCharAttrib* CharAttribList::FindFeature( sal_Int32 nPos ) const 2929 { 2930 // First, find the first attribute that starts at or after specified position. 2931 AttribsType::const_iterator it = 2932 std::find_if(aAttribs.begin(), aAttribs.end(), FindByStartPos(nPos)); 2933 2934 if (it == aAttribs.end()) 2935 // All attributes are before the specified position. 2936 return nullptr; 2937 2938 // And find the first attribute with feature. 2939 it = std::find_if(it, aAttribs.end(), [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsFeature(); } ); 2940 return it == aAttribs.end() ? nullptr : it->get(); 2941 } 2942 2943 namespace { 2944 2945 class RemoveEmptyAttrItem 2946 { 2947 SfxItemPool& mrItemPool; 2948 public: 2949 explicit RemoveEmptyAttrItem(SfxItemPool& rPool) : mrItemPool(rPool) {} 2950 void operator() (const std::unique_ptr<EditCharAttrib>& r) 2951 { 2952 if (r->IsEmpty()) 2953 mrItemPool.Remove(*r->GetItem()); 2954 } 2955 }; 2956 2957 } 2958 2959 void CharAttribList::DeleteEmptyAttribs( SfxItemPool& rItemPool ) 2960 { 2961 std::for_each(aAttribs.begin(), aAttribs.end(), RemoveEmptyAttrItem(rItemPool)); 2962 aAttribs.erase( std::remove_if(aAttribs.begin(), aAttribs.end(), [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsEmpty(); } ), aAttribs.end() ); 2963 bHasEmptyAttribs = false; 2964 } 2965 2966 #if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG 2967 void CharAttribList::DbgCheckAttribs(CharAttribList const& rAttribs) 2968 { 2969 std::set<std::pair<sal_Int32, sal_uInt16>> zero_set; 2970 for (const std::unique_ptr<EditCharAttrib>& rAttr : rAttribs.aAttribs) 2971 { 2972 assert(rAttr->GetStart() <= rAttr->GetEnd()); 2973 assert(!rAttr->IsFeature() || rAttr->GetLen() == 1); 2974 if (0 == rAttr->GetLen()) 2975 { 2976 // not sure if 0-length attributes allowed at all in non-empty para? 2977 assert(zero_set.insert(std::make_pair(rAttr->GetStart(), rAttr->Which())).second && "duplicate 0-length attribute detected"); 2978 } 2979 } 2980 CheckOrderedList(rAttribs.GetAttribs()); 2981 } 2982 #endif 2983 2984 void CharAttribList::dumpAsXml(xmlTextWriterPtr pWriter) const 2985 { 2986 xmlTextWriterStartElement(pWriter, BAD_CAST("CharAttribList")); 2987 for (auto const & i : aAttribs) { 2988 i->dumpAsXml(pWriter); 2989 } 2990 xmlTextWriterEndElement(pWriter); 2991 } 2992 2993 EditEngineItemPool::EditEngineItemPool() 2994 : SfxItemPool( "EditEngineItemPool", EE_ITEMS_START, EE_ITEMS_END, 2995 aItemInfos, nullptr ) 2996 { 2997 m_xDefItems = EditDLL::Get().GetGlobalData()->GetDefItems(); 2998 SetDefaults(m_xDefItems->getDefaults()); 2999 } 3000 3001 EditEngineItemPool::~EditEngineItemPool() 3002 { 3003 ClearDefaults(); 3004 } 3005 3006 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 3007
