1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_sw.hxx" 26 27 #include <ctype.h> 28 #include <hintids.hxx> 29 #include <hints.hxx> 30 #include <svtools/filter.hxx> 31 32 #include <vcl/graph.hxx> 33 #include <svl/urihelper.hxx> 34 #include <editeng/boxitem.hxx> 35 #include <editeng/boxitem.hxx> 36 #include <editeng/wghtitem.hxx> 37 #include <editeng/cmapitem.hxx> 38 #include <editeng/cntritem.hxx> 39 #include <editeng/postitem.hxx> 40 #include <editeng/crsditem.hxx> 41 #include <svl/stritem.hxx> 42 #include <unotools/charclass.hxx> 43 #include <txtftn.hxx> 44 #include <fmtpdsc.hxx> 45 #include <fmtftn.hxx> 46 #include <fmtanchr.hxx> 47 #include <fmtrfmrk.hxx> 48 #include <fmtclds.hxx> 49 #include <fmtfld.hxx> 50 #include <fmtfsize.hxx> 51 #include <fmthdft.hxx> 52 #include <fmtcntnt.hxx> 53 #include <redline.hxx> 54 #include <pam.hxx> 55 #include <doc.hxx> 56 #include <ndtxt.hxx> 57 #include <frmatr.hxx> 58 #include <fldbas.hxx> // RES_SETEXPFLD 59 #include <charatr.hxx> // class SwFmtRefMark 60 #include <swtable.hxx> // class SwTableLines, ... 61 #include <tox.hxx> 62 #include <expfld.hxx> // SwExpField 63 #include <section.hxx> // class SwSection 64 #include <tblsel.hxx> // class SwSelBoxes 65 #include <pagedesc.hxx> 66 #include <docsh.hxx> // class SwDocSh 67 #include <fltshell.hxx> 68 #include <viewsh.hxx> 69 #include <shellres.hxx> 70 71 72 #define MAX_FIELDLEN 64000 73 74 using namespace com::sun::star; 75 76 static SwCntntNode* GetCntntNode(SwDoc* pDoc, SwNodeIndex& rIdx, sal_Bool bNext) 77 { 78 SwCntntNode * pCNd = rIdx.GetNode().GetCntntNode(); 79 if(!pCNd && 0 == (pCNd = bNext ? pDoc->GetNodes().GoNext(&rIdx) 80 : pDoc->GetNodes().GoPrevious(&rIdx))) 81 { 82 pCNd = bNext ? pDoc->GetNodes().GoPrevious(&rIdx) 83 : pDoc->GetNodes().GoNext(&rIdx); 84 ASSERT(pCNd, "kein ContentNode gefunden"); 85 } 86 return pCNd; 87 } 88 89 // ------ Stack-Eintrag fuer die gesamten - Attribute vom Text ----------- 90 SwFltStackEntry::SwFltStackEntry(const SwPosition& rStartPos, SfxPoolItem* pHt ) : 91 nMkNode(rStartPos.nNode, -1), 92 //Modify here for #119405, by easyfan, 2012-05-24 93 nPtNode(nMkNode),mnStartCP(-1),mnEndCP(-1),bIsParaEnd(false) 94 //End of modification, by easyfan 95 { 96 // Anfang vom Bereich merken 97 nMkCntnt = rStartPos.nContent.GetIndex(); 98 pAttr = pHt; // speicher eine Kopie vom Attribut 99 bOld = sal_False; // used for marking Attributes *before* skipping field results 100 bLocked = sal_True; // locke das Attribut --> darf erst 101 bCopied = sal_False; // gesetzt werden, wenn es wieder geunlocked ist 102 bConsumedByField = sal_False; 103 } 104 105 SwFltStackEntry::SwFltStackEntry(const SwFltStackEntry& rEntry) : 106 nMkNode(rEntry.nMkNode), 107 nPtNode(rEntry.nPtNode) 108 { 109 pAttr = rEntry.pAttr->Clone(); 110 nMkCntnt= rEntry.nMkCntnt; 111 bOld = rEntry.bOld; 112 bLocked = bCopied = sal_True; // when rEntry were NOT bLocked we would never have been called 113 bConsumedByField = rEntry.bConsumedByField; 114 //Modify here for #119405, by chengjh, 2012-08-16 115 mnStartCP= rEntry.mnStartCP; 116 mnEndCP = rEntry.mnEndCP; 117 bIsParaEnd = rEntry.bIsParaEnd; 118 //End 119 } 120 121 122 SwFltStackEntry::~SwFltStackEntry() 123 { 124 // Attribut kam zwar als Pointer, wird aber hier geloescht 125 if (pAttr) 126 delete pAttr; 127 } 128 129 void SwFltStackEntry::SetEndPos(const SwPosition& rEndPos) 130 { 131 // Attribut freigeben und das Ende merken. 132 // Alles mit sal_uInt16's, weil sonst beim Einfuegen von neuem Text an der 133 // Cursor-Position auch der Bereich vom Attribut weiter 134 // verschoben wird. 135 // Das ist aber nicht das gewollte! 136 bLocked = sal_False; // freigeben und das ENDE merken 137 nPtNode = rEndPos.nNode.GetIndex()-1; 138 nPtCntnt = rEndPos.nContent.GetIndex(); 139 } 140 //Modify here for #119405, by chengjh, 2012-08-16 141 //The only position of 0x0D will not be able to make regin in the old logic 142 //because it is beyond the length of para...need special consideration here. 143 bool SwFltStackEntry::IsAbleMakeRegion() 144 { 145 SwCntntNode *const pCntntNode( 146 SwNodeIndex(nMkNode, +1).GetNode().GetCntntNode()); 147 if ((nMkNode.GetIndex() == nPtNode.GetIndex()) && (nMkCntnt == nPtCntnt) 148 && ((0 != nPtCntnt) || (pCntntNode && (0 != pCntntNode->Len()))) 149 && ((RES_TXTATR_FIELD != pAttr->Which()) 150 && !(bIsParaEnd && pCntntNode && pCntntNode->IsTxtNode() && 0 != pCntntNode->Len() ))) 151 { 152 return false; 153 } 154 155 return true; 156 } 157 //End 158 sal_Bool SwFltStackEntry::MakeRegion(SwDoc* pDoc, SwPaM& rRegion, sal_Bool bCheck ) 159 { 160 // does this range actually contain something? 161 // empty range is allowed if at start of empty paragraph 162 // fields are special: never have range, so leave them 163 //Modify here for #119405, by chengjh, 2012-08-16 164 //Revised the code and move the code segment to defined function 165 if ( !IsAbleMakeRegion() ) 166 { 167 return sal_False; 168 } 169 //End 170 // !!! Die Content-Indizies beziehen sich immer auf den Node !!! 171 rRegion.GetPoint()->nNode = nMkNode.GetIndex() + 1; 172 SwCntntNode* pCNd = GetCntntNode(pDoc, rRegion.GetPoint()->nNode, sal_True); 173 rRegion.GetPoint()->nContent.Assign(pCNd, nMkCntnt); 174 rRegion.SetMark(); 175 if( nMkNode != nPtNode ) 176 { 177 rRegion.GetPoint()->nNode = nPtNode.GetIndex() + 1; 178 pCNd = GetCntntNode(pDoc, rRegion.GetPoint()->nNode, sal_False); 179 } 180 rRegion.GetPoint()->nContent.Assign(pCNd, nPtCntnt); 181 #if OSL_DEBUG_LEVEL > 1 182 ASSERT( CheckNodesRange( rRegion.Start()->nNode, 183 rRegion.End()->nNode, sal_True ), 184 "Attribut oder AEhnliches ueber Bereichs-Grenzen" ); 185 #endif 186 if( bCheck ) 187 return CheckNodesRange( rRegion.Start()->nNode, 188 rRegion.End()->nNode, sal_True ); 189 else 190 return sal_True; 191 } 192 193 SwFltControlStack::SwFltControlStack(SwDoc* pDo, sal_uLong nFieldFl) 194 : nFieldFlags(nFieldFl), bHasSdOD(true) 195 ,bSdODChecked(false), pDoc(pDo), bIsEndStack(false) 196 //End 197 198 { 199 } 200 201 202 SwFltControlStack::~SwFltControlStack() 203 { 204 ASSERT(!Count(), "noch Attribute auf dem Stack"); 205 } 206 207 // MoveAttrs() ist fuer folgendes Problem: 208 // Wenn ueber den Stack ein Feld wie z.B. "Variable setzen" gesetzt wird, 209 // verschiebt sich der Text um ein \xff - Zeichen, und alle folgenden 210 // Attribute stimmen in ihrer Position nicht mehr. 211 // Dann muss MoveAttrs() nach dem Setzen des Attributes ins Doc gerufen werden, 212 // so dass alle Attribut-Positionen, 213 // die im selben Absatz weiter hinten stehen, um 1 Zeichen weiter 214 // nach rechts verschoben werden. 215 void SwFltControlStack::MoveAttrs( const SwPosition& rPos ) 216 { 217 sal_uInt16 nCnt = static_cast< sal_uInt16 >(Count()); 218 SwFltStackEntry* pEntry; 219 sal_uLong nPosNd = rPos.nNode.GetIndex(); 220 sal_uInt16 nPosCt = rPos.nContent.GetIndex() - 1; 221 222 for (sal_uInt16 i=0; i < nCnt; i++){ 223 pEntry = (*this)[ i ]; 224 if(( pEntry->nMkNode.GetIndex() + 1 == nPosNd ) 225 &&( pEntry->nMkCntnt >= nPosCt )){ 226 pEntry->nMkCntnt++; 227 ASSERT( pEntry->nMkCntnt 228 <= pDoc->GetNodes()[nPosNd]->GetCntntNode()->Len(), 229 "Attribut-Anfang hinter Zeilenende" ); 230 } 231 if(( pEntry->nPtNode.GetIndex() + 1 == nPosNd ) 232 &&( pEntry->nPtCntnt >= nPosCt )){ 233 pEntry->nPtCntnt++; 234 ASSERT( pEntry->nPtCntnt 235 <= pDoc->GetNodes()[nPosNd]->GetCntntNode()->Len(), 236 "Attribut-Ende hinter Zeilenende" ); 237 } 238 } 239 } 240 241 void SwFltControlStack::MarkAllAttrsOld() 242 { 243 sal_uInt16 nCnt = static_cast< sal_uInt16 >(Count()); 244 for (sal_uInt16 i=0; i < nCnt; i++) 245 (*this)[ i ]->bOld = sal_True; 246 } 247 248 void SwFltControlStack::NewAttr(const SwPosition& rPos, const SfxPoolItem & rAttr ) 249 { 250 SwFltStackEntry *pTmp = new SwFltStackEntry(rPos, rAttr.Clone() ); 251 //Modify here for #119405, by easyfan, 2012-05-24 252 pTmp->SetStartCP(GetCurrAttrCP()); 253 //End of modification, by easyfan 254 sal_uInt16 nWhich = pTmp->pAttr->Which(); 255 SetAttr(rPos, nWhich);// Ende von evtl. gleichen Attributen auf dem Stack 256 // Setzen, damit sich die Attribute nicht auf 257 // dem Stack haeufen 258 maEntries.push_back(pTmp); 259 } 260 261 void SwFltControlStack::DeleteAndDestroy(Entries::size_type nCnt) 262 { 263 ASSERT(nCnt < maEntries.size(), "Out of range!"); 264 if (nCnt < maEntries.size()) 265 { 266 myEIter aElement = maEntries.begin() + nCnt; 267 delete *aElement; 268 maEntries.erase(aElement); 269 } 270 //Modify for #119405 by chengjh, 2012-08-16 271 //Clear the para end position recorded in reader intermittently for the least impact on loading performance 272 //Because the attributes handled based on the unit of para 273 if ( Count() == 0 ) 274 { 275 ClearParaEndPosition(); 276 bHasSdOD = true; 277 bSdODChecked = false; 278 } 279 //End 280 } 281 282 // SwFltControlStack::StealAttr() loescht Attribute des angegebenen Typs vom Stack. 283 // Als nAttrId sind erlaubt: 0 fuer alle, oder ein spezieller Typ. 284 // Damit erscheinen sie nicht in der Doc-Struktur. Dabei werden nur die 285 // Attribute entfernt, die im selben Absatz wie pPos stehen. 286 // Wird fuer Grafik-Apos -> Grafiken benutzt. 287 void SwFltControlStack::StealAttr(const SwPosition* pPos, sal_uInt16 nAttrId /* = 0 */) 288 { 289 sal_uInt16 nCnt = static_cast< sal_uInt16 >(Count()); 290 291 SwFltStackEntry* pEntry; 292 293 while (nCnt) 294 { 295 nCnt --; 296 pEntry = (*this)[ nCnt ]; 297 if (pEntry->nPtNode.GetIndex()+1 == pPos->nNode.GetIndex() && 298 (!nAttrId || nAttrId == pEntry->pAttr->Which())) 299 DeleteAndDestroy(nCnt); // loesche aus dem Stack 300 } 301 } 302 303 // SwFltControlStack::KillUnlockedAttr() loescht alle Attribute vom Stack, 304 // welche punktuell auf pPos aufgespannt sind. 305 // Damit erscheinen sie nicht in der Doc-Struktur. 306 // Wird im WW Import benoetigt zum ignorieren der auf dem 0x0c Section- 307 // Break-Symbol gesetzten Attribute. 308 void SwFltControlStack::KillUnlockedAttrs(const SwPosition& pPos) 309 { 310 SwNodeIndex aAktNode( pPos.nNode, -1 ); 311 sal_uInt16 nAktIdx = pPos.nContent.GetIndex(); 312 313 sal_uInt16 nCnt = static_cast< sal_uInt16 >(Count()); 314 SwFltStackEntry* pEntry; 315 while( nCnt ) 316 { 317 nCnt --; 318 pEntry = (*this)[ nCnt ]; 319 if( !pEntry->bOld 320 && !pEntry->bLocked 321 && (pEntry->nMkNode == aAktNode) 322 && (pEntry->nMkCntnt == nAktIdx ) 323 && (pEntry->nPtNode == aAktNode) 324 && (pEntry->nPtCntnt == nAktIdx )) 325 { 326 DeleteAndDestroy( nCnt ); // loesche aus dem Stack 327 } 328 } 329 } 330 331 // Alle gelockten Attribute freigeben (unlocken) und das Ende setzen, 332 // alle anderen im Document setzen und wieder aus dem Stack loeschen 333 // Returned, ob das gesuchte Attribut / die gesuchten Attribute 334 // ueberhaupt auf dem Stack standen 335 void SwFltControlStack::SetAttr(const SwPosition& rPos, sal_uInt16 nAttrId, 336 sal_Bool bTstEnde, long nHand, sal_Bool consumedByField ) 337 { 338 ASSERT(!nAttrId || 339 (POOLATTR_BEGIN <= nAttrId && POOLATTR_END > nAttrId) || 340 (RES_FLTRATTR_BEGIN <= nAttrId && RES_FLTRATTR_END > nAttrId), 341 "Falsche Id fuers Attribut") 342 343 sal_uInt16 nCnt = static_cast< sal_uInt16 >(Count()); 344 345 SwFltStackEntry* pEntry; 346 347 for (sal_uInt16 i=0; i < nCnt; i++) 348 { 349 pEntry = (*this)[ i ]; 350 if (pEntry->bLocked) 351 { 352 // setze das Ende vom Attribut 353 bool bF = false; 354 if (!nAttrId ){ 355 bF = true; 356 }else if( nAttrId == pEntry->pAttr->Which()){ 357 if( nAttrId != RES_FLTR_BOOKMARK ){ // Handle abfragen 358 bF = true; 359 }else if( nHand == ((SwFltBookmark*)(pEntry->pAttr))->GetHandle() ) 360 { 361 bF = true; 362 } 363 } 364 if (bF) { 365 pEntry->bConsumedByField = consumedByField; 366 pEntry->SetEndPos(rPos); 367 //Modify here for #119405, by easyfan, 2012-05-24 368 pEntry->SetEndCP(GetCurrAttrCP()); 369 //End of modification, by easyfan 370 } 371 continue; 372 } 373 374 // ist die Endposition die Cursor-Position, dann noch nicht 375 // ins Dokument setzen, es muss noch Text folgen; 376 // ausser am Dokumentende. (Attribut-Expandierung !!) 377 // Beim Ende-Stack niemals ausser am DocEnde reinsetzen 378 if (bTstEnde) 379 { 380 if (bIsEndStack || pEntry->nPtNode.GetIndex()+1 == 381 rPos.nNode.GetIndex()) 382 continue; 383 } 384 SetAttrInDoc(rPos, pEntry); 385 DeleteAndDestroy(i); // loesche aus dem Stack 386 i--; nCnt--; // Danach rutschen alle folgenden nach unten 387 } 388 } 389 390 static void MakePoint(SwFltStackEntry* pEntry, SwDoc* pDoc, SwPaM& rRegion) 391 { 392 // der Anker ist der Point vom Pam. Dieser wird beim Einfugen 393 // von Text usw. veraendert; darum wird er auf dem Stack 394 // gespeichert. Das Attribut muss nur noch im Format 395 // gesetzt werden. 396 rRegion.DeleteMark(); 397 rRegion.GetPoint()->nNode = pEntry->nMkNode.GetIndex() + 1; 398 SwCntntNode* pCNd = GetCntntNode(pDoc, rRegion.GetPoint()->nNode, sal_True); 399 rRegion.GetPoint()->nContent.Assign(pCNd, pEntry->nMkCntnt); 400 } 401 402 // MakeBookRegionOrPoint() ist wie MakeRegionOrPoint, aber die besonderen 403 // Beschraenkungen von Bookmarks in Tabellen werden beachtet. 404 // ( Anfang und Ende muessen in selber Zelle sein ) 405 static void MakeBookRegionOrPoint(SwFltStackEntry* pEntry, SwDoc* pDoc, 406 SwPaM& rRegion, sal_Bool bCheck ) 407 { 408 if (pEntry->MakeRegion(pDoc, rRegion, bCheck )){ 409 // sal_Bool b1 = rNds[rRegion.GetPoint()->nNode]->FindTableNode() != 0; 410 // const SwStartNode* p1 = rNds[rRegion.GetPoint()->nNode]->FindTableBoxStartNode(); 411 // const SwStartNode* p2 = rNds[rRegion.GetMark()->nNode]->FindTableBoxStartNode(); 412 if (rRegion.GetPoint()->nNode.GetNode().FindTableBoxStartNode() 413 != rRegion.GetMark()->nNode.GetNode().FindTableBoxStartNode()) 414 { 415 rRegion.Exchange(); // Ungueltiger Bereich 416 rRegion.DeleteMark(); // -> beide auf Mark 417 } 418 }else{ 419 MakePoint(pEntry, pDoc, rRegion); 420 } 421 } 422 423 #if OSL_DEBUG_LEVEL > 1 424 extern sal_Bool CheckNodesRange( const SwNodeIndex& rStt, 425 const SwNodeIndex& rEnd, sal_Bool bChkSection ); 426 #endif 427 428 // IterateNumrulePiece() sucht von rTmpStart bis rEnd den ersten 429 // fuer Numrules gueltigen Bereich heraus. 430 // 431 // rNds sind die Doc-Nodes 432 // rEnd ist Bereichs-Ende, 433 // rTmpStart ist ReinRaus-Parameter: Anfang des zu untersuchenden Bereiches rein, 434 // Anfang des gueltigen Bereichs raus 435 // rTmpEnd ist raus-Parameter 436 // Return-Bool ist sal_True fuer gueltigen Bereich 437 static sal_Bool IterateNumrulePiece( const SwNodeIndex& rEnd, 438 SwNodeIndex& rTmpStart, SwNodeIndex& rTmpEnd ) 439 { 440 while( ( rTmpStart <= rEnd ) 441 && !( rTmpStart.GetNode().IsTxtNode() ) ) // suche gueltigen Anfang 442 rTmpStart++; 443 444 rTmpEnd = rTmpStart; 445 while( ( rTmpEnd <= rEnd ) 446 && ( rTmpEnd.GetNode().IsTxtNode() ) ) // suche gueltiges Ende + 1 447 rTmpEnd++; 448 449 rTmpEnd--; // gueltiges Ende 450 451 return rTmpStart <= rTmpEnd; // gueltig ? 452 } 453 //Modify for #119405 by chengjh, 2012-08-16 454 //***This function will check whether there is existing individual attribute positon for 0x0D***/ 455 //The check will happen only once for a paragraph during loading 456 bool SwFltControlStack::HasSdOD() 457 { 458 sal_uInt16 nCnt = static_cast< sal_uInt16 >(Count()); 459 460 SwFltStackEntry* pEntry = 0; 461 462 bool bRet = false; 463 464 for (sal_uInt16 i=0; i < nCnt; i++) 465 { 466 pEntry = (*this)[ i ]; 467 if ( pEntry && pEntry->mnStartCP == pEntry->mnEndCP ) 468 { 469 if ( CheckSdOD(pEntry->mnStartCP,pEntry->mnEndCP) ) 470 { 471 bRet = true; 472 break; 473 } 474 } 475 } 476 477 return bRet; 478 } 479 //End 480 void SwFltControlStack::SetAttrInDoc(const SwPosition& rTmpPos, SwFltStackEntry* pEntry) 481 { 482 SwPaM aRegion( rTmpPos ); 483 484 switch(pEntry->pAttr->Which()) 485 { 486 case RES_FLTR_ANCHOR: 487 { 488 SwFrmFmt* pFmt = ((SwFltAnchor*)pEntry->pAttr)->GetFrmFmt(); 489 if (pFmt != NULL) 490 { 491 MakePoint(pEntry, pDoc, aRegion); 492 SwFmtAnchor aAnchor(pFmt->GetAnchor()); 493 aAnchor.SetAnchor(aRegion.GetPoint()); 494 pFmt->SetFmtAttr(aAnchor); 495 // Damit die Frames bei Einfuegen in existierendes Doc 496 // erzeugt werden (erst nach Setzen des Ankers!): 497 if(pDoc->GetCurrentViewShell() //swmod 071108//swmod 071225 498 && (FLY_AT_PARA == pFmt->GetAnchor().GetAnchorId())) 499 { 500 pFmt->MakeFrms(); 501 } 502 } 503 } 504 break; 505 case RES_FLTR_STYLESHEET: 506 break; 507 case RES_TXTATR_FIELD: 508 break; 509 case RES_TXTATR_TOXMARK: 510 break; 511 case RES_FLTR_NUMRULE: // Numrule 'reinsetzen 512 { 513 const String& rNumNm = ((SfxStringItem*)pEntry->pAttr)->GetValue(); 514 SwNumRule* pRul = pDoc->FindNumRulePtr( rNumNm ); 515 if( pRul ) 516 { 517 if( pEntry->MakeRegion(pDoc, aRegion, sal_True)) 518 { 519 SwNodeIndex aTmpStart( aRegion.Start()->nNode ); 520 SwNodeIndex aTmpEnd( aTmpStart ); 521 SwNodeIndex& rRegEndNd = aRegion.End()->nNode; 522 while( IterateNumrulePiece( rRegEndNd, 523 aTmpStart, aTmpEnd ) ) 524 { 525 SwPaM aTmpPam( aTmpStart, aTmpEnd ); 526 // --> OD 2008-03-17 #refactorlists# 527 // no start of a new list 528 pDoc->SetNumRule( aTmpPam, *pRul, false ); 529 // <-- 530 531 aTmpStart = aTmpEnd; // Start fuer naechstes Teilstueck 532 aTmpStart++; 533 } 534 } 535 else 536 pDoc->DelNumRule( rNumNm ); 537 } 538 } 539 break; 540 case RES_FLTR_NUMRULE_NUM: 541 break; 542 case RES_FLTR_BOOKMARK: 543 { 544 SwFltBookmark* pB = (SwFltBookmark*)pEntry->pAttr; 545 const String& rName = ((SwFltBookmark*)pEntry->pAttr)->GetName(); 546 547 if (IsFlagSet(BOOK_TO_VAR_REF)) 548 { 549 SwFieldType* pFT = pDoc->GetFldType(RES_SETEXPFLD, rName, false); 550 if (!pFT) 551 { 552 SwSetExpFieldType aS(pDoc, rName, nsSwGetSetExpType::GSE_STRING); 553 pFT = pDoc->InsertFldType(aS); 554 } 555 SwSetExpField aFld((SwSetExpFieldType*)pFT, pB->GetValSys()); 556 aFld.SetSubType( nsSwExtendedSubType::SUB_INVISIBLE ); 557 MakePoint(pEntry, pDoc, aRegion); 558 pDoc->InsertPoolItem(aRegion, SwFmtFld(aFld), 0); 559 MoveAttrs( *(aRegion.GetPoint()) ); 560 } 561 if ( ( !IsFlagSet(HYPO) || IsFlagSet(BOOK_AND_REF) ) && 562 !pEntry->bConsumedByField ) 563 { 564 MakeBookRegionOrPoint(pEntry, pDoc, aRegion, sal_True); 565 // #120879# - create a cross reference heading bookmark if appropriate. 566 const IDocumentMarkAccess::MarkType eBookmarkType = 567 ( pB->IsTOCBookmark() && 568 IDocumentMarkAccess::IsLegalPaMForCrossRefHeadingBookmark( aRegion ) ) 569 ? IDocumentMarkAccess::CROSSREF_HEADING_BOOKMARK 570 : IDocumentMarkAccess::BOOKMARK; 571 pDoc->getIDocumentMarkAccess()->makeMark( aRegion, rName, eBookmarkType ); 572 } 573 } 574 break; 575 case RES_FLTR_TOX: 576 { 577 MakePoint(pEntry, pDoc, aRegion); 578 579 SwPosition* pPoint = aRegion.GetPoint(); 580 581 SwFltTOX* pTOXAttr = (SwFltTOX*)pEntry->pAttr; 582 583 // test if on this node there had been a pagebreak BEFORE the 584 // tox attribut was put on the stack 585 SfxItemSet aBkSet( pDoc->GetAttrPool(), RES_PAGEDESC, RES_BREAK ); 586 SwCntntNode* pNd = 0; 587 if( !pTOXAttr->HadBreakItem() || !pTOXAttr->HadPageDescItem() ) 588 { 589 pNd = pPoint->nNode.GetNode().GetCntntNode(); 590 if( pNd ) 591 { 592 const SfxItemSet* pSet = pNd->GetpSwAttrSet(); 593 const SfxPoolItem* pItem; 594 if( pSet ) 595 { 596 if( !pTOXAttr->HadBreakItem() 597 && SFX_ITEM_SET == pSet->GetItemState( RES_BREAK, sal_False, &pItem ) ) 598 { 599 aBkSet.Put( *pItem ); 600 pNd->ResetAttr( RES_BREAK ); 601 } 602 if( !pTOXAttr->HadPageDescItem() 603 && SFX_ITEM_SET == pSet->GetItemState( RES_PAGEDESC, sal_False, &pItem ) ) 604 { 605 aBkSet.Put( *pItem ); 606 pNd->ResetAttr( RES_PAGEDESC ); 607 } 608 } 609 } 610 } 611 612 delete pTOXAttr->GetBase(); 613 614 // set (aboved saved and removed) the break item at the node following the TOX 615 if( aBkSet.Count() ) 616 pNd->SetAttr( aBkSet ); 617 } 618 break; 619 case RES_FLTR_SECTION: 620 MakePoint(pEntry, pDoc, aRegion); // bislang immer Point==Mark 621 pDoc->InsertSwSection(aRegion, 622 *(static_cast<SwFltSection*>(pEntry->pAttr))->GetSectionData(), 623 0, 0, false); 624 delete (((SwFltSection*)pEntry->pAttr)->GetSectionData()); 625 break; 626 case RES_FLTR_REDLINE: 627 { 628 if (pEntry->MakeRegion(pDoc, aRegion, sal_True)) 629 { 630 pDoc->SetRedlineMode((RedlineMode_t)( nsRedlineMode_t::REDLINE_ON 631 | nsRedlineMode_t::REDLINE_SHOW_INSERT 632 | nsRedlineMode_t::REDLINE_SHOW_DELETE )); 633 SwFltRedline& rFltRedline = *((SwFltRedline*)pEntry->pAttr); 634 635 if( USHRT_MAX != rFltRedline.nAutorNoPrev ) 636 { 637 SwRedlineData aData(rFltRedline.eTypePrev, 638 rFltRedline.nAutorNoPrev, 639 rFltRedline.aStampPrev, 640 aEmptyStr, 641 0 642 ); 643 pDoc->AppendRedline(new SwRedline(aData, aRegion), true); 644 } 645 SwRedlineData aData(rFltRedline.eType, 646 rFltRedline.nAutorNo, 647 rFltRedline.aStamp, 648 aEmptyStr, 649 0 650 ); 651 pDoc->AppendRedline( new SwRedline(aData, aRegion), true ); 652 pDoc->SetRedlineMode((RedlineMode_t)( nsRedlineMode_t::REDLINE_NONE 653 | nsRedlineMode_t::REDLINE_SHOW_INSERT 654 | nsRedlineMode_t::REDLINE_SHOW_DELETE )); 655 } 656 } 657 break; 658 default: 659 { 660 //Modify here for #119405, by chengjh, 2012-08-16 661 //Revised for more complex situations should be considered 662 if ( !bSdODChecked ) 663 { 664 bHasSdOD = HasSdOD(); 665 bSdODChecked = true; 666 } 667 sal_Int32 nStart = pEntry->GetStartCP(); 668 sal_Int32 nEnd = pEntry->GetEndCP(); 669 if (nStart != -1 && nEnd != -1 && nEnd >= nStart ) 670 { 671 pEntry->SetIsParaEnd( IsParaEndInCPs(nStart,nEnd,bHasSdOD) ); 672 } 673 //End 674 if (pEntry->MakeRegion(pDoc, aRegion, sal_False)) 675 { 676 //Modify here for #119405, by easyfan, 2012-05-24 677 //Refined 2012-08-16 678 if (pEntry->IsParaEnd()) 679 { 680 pDoc->InsertPoolItem(aRegion, *pEntry->pAttr, 0,true); 681 }else 682 { 683 pDoc->InsertPoolItem(aRegion, *pEntry->pAttr, 0); 684 } 685 //End 686 } 687 } 688 break; 689 } 690 } 691 692 SfxPoolItem* SwFltControlStack::GetFmtStackAttr(sal_uInt16 nWhich, sal_uInt16 * pPos) 693 { 694 SwFltStackEntry* pEntry; 695 sal_uInt16 nSize = static_cast< sal_uInt16 >(Count()); 696 697 while (nSize) 698 { 699 // ist es das gesuchte Attribut ? (gueltig sind nur gelockte, 700 // also akt. gesetzte Attribute!!) 701 if ((pEntry = (*this)[ --nSize ])->bLocked && 702 pEntry->pAttr->Which() == nWhich) 703 { 704 if (pPos) 705 *pPos = nSize; 706 return (SfxPoolItem*)pEntry->pAttr; // Ok, dann Ende 707 } 708 } 709 return 0; 710 } 711 712 const SfxPoolItem* SwFltControlStack::GetOpenStackAttr(const SwPosition& rPos, sal_uInt16 nWhich) 713 { 714 SwFltStackEntry* pEntry; 715 sal_uInt16 nSize = static_cast< sal_uInt16 >(Count()); 716 SwNodeIndex aAktNode( rPos.nNode, -1 ); 717 sal_uInt16 nAktIdx = rPos.nContent.GetIndex(); 718 719 while (nSize) 720 { 721 pEntry = (*this)[ --nSize ]; 722 if( pEntry->bLocked 723 && (pEntry->pAttr->Which() == nWhich) 724 && (pEntry->nMkNode == aAktNode) 725 && (pEntry->nMkCntnt == nAktIdx )) 726 { 727 return (SfxPoolItem*)pEntry->pAttr; 728 } 729 } 730 return 0; 731 } 732 733 const SfxPoolItem* SwFltControlStack::GetFmtAttr(const SwPosition& rPos, sal_uInt16 nWhich) 734 { 735 SfxPoolItem* pHt = GetFmtStackAttr(nWhich); 736 if (pHt) 737 return (const SfxPoolItem*)pHt; 738 739 // im Stack ist das Attribut nicht vorhanden, also befrage das Dokument 740 // SwCntntNode * pNd = rPaM.GetCntntNode(); 741 SwCntntNode * pNd = rPos.nNode.GetNode().GetCntntNode(); 742 743 if (!pNd) // kein ContentNode, dann das dflt. Attribut 744 return &pDoc->GetAttrPool().GetDefaultItem(nWhich); 745 return &pNd->GetAttr(nWhich); 746 } 747 748 void SwFltControlStack::Delete(const SwPaM &rPam) 749 { 750 const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End(); 751 752 if( !rPam.HasMark() || *pStt >= *pEnd ) 753 return; 754 755 SwNodeIndex aStartNode(pStt->nNode, -1); 756 sal_uInt16 nStartIdx = pStt->nContent.GetIndex(); 757 SwNodeIndex aEndNode(pEnd->nNode, -1); 758 sal_uInt16 nEndIdx = pEnd->nContent.GetIndex(); 759 760 //We don't support deleting content that is over one node, or removing a node. 761 ASSERT(aEndNode == aStartNode, "nodes must be the same, or this method extended"); 762 if (aEndNode != aStartNode) 763 return; 764 765 for (sal_uInt16 nSize = static_cast< sal_uInt16 >(Count()); nSize > 0;) 766 { 767 SwFltStackEntry* pEntry = (*this)[--nSize]; 768 769 bool bEntryStartAfterSelStart = 770 (pEntry->nMkNode == aStartNode && pEntry->nMkCntnt >= nStartIdx); 771 772 bool bEntryStartBeforeSelEnd = 773 (pEntry->nMkNode == aEndNode && pEntry->nMkCntnt <= nEndIdx); 774 775 bool bEntryEndAfterSelStart = false; 776 bool bEntryEndBeforeSelEnd = false; 777 if (!pEntry->bLocked) 778 { 779 bEntryEndAfterSelStart = 780 (pEntry->nPtNode == aStartNode && pEntry->nPtCntnt >= nStartIdx); 781 782 bEntryEndBeforeSelEnd = 783 (pEntry->nPtNode == aEndNode && pEntry->nPtCntnt <= nEndIdx); 784 } 785 786 bool bTotallyContained = false; 787 if ( 788 bEntryStartAfterSelStart && bEntryStartBeforeSelEnd && 789 bEntryEndAfterSelStart && bEntryEndBeforeSelEnd 790 ) 791 { 792 bTotallyContained = true; 793 } 794 795 if (bTotallyContained) 796 { 797 //after start, before end, delete 798 DeleteAndDestroy(nSize); 799 continue; 800 } 801 802 xub_StrLen nCntntDiff = nEndIdx - nStartIdx; 803 804 //to be adjusted 805 if (bEntryStartAfterSelStart) 806 { 807 if (bEntryStartBeforeSelEnd) 808 { 809 //move start to new start 810 pEntry->nMkNode = aStartNode; 811 pEntry->nMkCntnt = nStartIdx; 812 } 813 else 814 pEntry->nMkCntnt = pEntry->nMkCntnt - nCntntDiff; 815 } 816 817 if (bEntryEndAfterSelStart) 818 { 819 if (bEntryEndBeforeSelEnd) 820 { 821 pEntry->nPtNode = aStartNode; 822 pEntry->nPtCntnt = nStartIdx; 823 } 824 else 825 pEntry->nPtCntnt = pEntry->nPtCntnt - nCntntDiff; 826 } 827 828 //That's what locked is, end equal to start, and nPtCntnt is invalid 829 if (pEntry->bLocked) 830 pEntry->nPtNode = pEntry->nMkNode; 831 } 832 } 833 834 //------ hier stehen die Methoden von SwFltAnchor ----------- 835 SwFltAnchor::SwFltAnchor(SwFrmFmt* pFmt) : 836 SfxPoolItem(RES_FLTR_ANCHOR), pFrmFmt(pFmt) 837 { 838 pClient = new SwFltAnchorClient(this); 839 pFrmFmt->Add(pClient); 840 } 841 842 SwFltAnchor::SwFltAnchor(const SwFltAnchor& rCpy) : 843 SfxPoolItem(RES_FLTR_ANCHOR), pFrmFmt(rCpy.pFrmFmt) 844 { 845 pClient = new SwFltAnchorClient(this); 846 pFrmFmt->Add(pClient); 847 } 848 849 SwFltAnchor::~SwFltAnchor() 850 { 851 delete pClient; 852 } 853 854 void SwFltAnchor::SetFrmFmt(SwFrmFmt * _pFrmFmt) 855 { 856 pFrmFmt = _pFrmFmt; 857 } 858 859 const SwFrmFmt * SwFltAnchor::GetFrmFmt() const 860 { 861 return pFrmFmt; 862 } 863 864 SwFrmFmt * SwFltAnchor::GetFrmFmt() 865 { 866 return pFrmFmt; 867 } 868 869 int SwFltAnchor::operator==(const SfxPoolItem& rItem) const 870 { 871 return pFrmFmt == ((SwFltAnchor&)rItem).pFrmFmt; 872 } 873 874 SfxPoolItem* __EXPORT SwFltAnchor::Clone(SfxItemPool*) const 875 { 876 return new SwFltAnchor(*this); 877 } 878 879 // SwFltAnchorClient 880 881 SwFltAnchorClient::SwFltAnchorClient(SwFltAnchor * pFltAnchor) 882 : m_pFltAnchor(pFltAnchor) 883 { 884 } 885 886 void SwFltAnchorClient::Modify(const SfxPoolItem *, const SfxPoolItem * pNew) 887 { 888 if (pNew->Which() == RES_FMT_CHG) 889 { 890 const SwFmtChg * pFmtChg = dynamic_cast<const SwFmtChg *> (pNew); 891 892 if (pFmtChg != NULL) 893 { 894 SwFrmFmt * pFrmFmt = dynamic_cast<SwFrmFmt *> (pFmtChg->pChangedFmt); 895 896 if (pFrmFmt != NULL) 897 m_pFltAnchor->SetFrmFmt(pFrmFmt); 898 } 899 } 900 } 901 902 //------ hier stehen die Methoden von SwFltRedline ----------- 903 int SwFltRedline::operator==(const SfxPoolItem& rItem) const 904 { 905 return this == &rItem; 906 } 907 908 SfxPoolItem* SwFltRedline::Clone( SfxItemPool* ) const 909 { 910 return new SwFltRedline(*this); 911 } 912 913 //------ hier stehen die Methoden von SwFltBookmark ----------- 914 SwFltBookmark::SwFltBookmark( const String& rNa, const String& rVa, 915 long nHand, const bool bIsTOCBookmark ) 916 : SfxPoolItem( RES_FLTR_BOOKMARK ) 917 , mnHandle( nHand ) 918 , maName( rNa ) 919 , maVal( rVa ) 920 , mbIsTOCBookmark( bIsTOCBookmark ) 921 { 922 // eSrc: CHARSET_DONTKNOW fuer keine UEbersetzung bei operator << 923 // Upcase wird immer gemacht. 924 // bei XXXStack.NewAttr(...) wird nie eine UEbersetzung vorgenommen. 925 // ansonsten: uebergebener Src-Charset fuer aName 926 // im Filter eingestellter Src-Charset fuer aVal ( Text ) 927 928 if ( IsTOCBookmark() ) 929 { 930 maName = IDocumentMarkAccess::GetCrossRefHeadingBookmarkNamePrefix(); 931 maName += rNa; 932 } 933 } 934 935 SwFltBookmark::SwFltBookmark(const SwFltBookmark& rCpy) 936 : SfxPoolItem( RES_FLTR_BOOKMARK ) 937 , mnHandle( rCpy.mnHandle ) 938 , maName( rCpy.maName ) 939 , maVal( rCpy.maVal ) 940 , mbIsTOCBookmark( rCpy.mbIsTOCBookmark ) 941 { 942 } 943 944 int SwFltBookmark::operator==(const SfxPoolItem& rItem) const 945 { 946 return ( maName == ((SwFltBookmark&)rItem).maName) 947 && (mnHandle == ((SwFltBookmark&)rItem).mnHandle); 948 } 949 950 SfxPoolItem* SwFltBookmark::Clone(SfxItemPool*) const 951 { 952 return new SwFltBookmark(*this); 953 } 954 955 //------ hier stehen die Methoden von SwFltTOX ----------- 956 957 SwFltTOX::SwFltTOX(SwTOXBase* pBase, sal_uInt16 _nCols) 958 : SfxPoolItem(RES_FLTR_TOX), pTOXBase(pBase), nCols( _nCols ), 959 bHadBreakItem( sal_False ), bHadPageDescItem( sal_False ) 960 { 961 } 962 963 SwFltTOX::SwFltTOX(const SwFltTOX& rCpy) 964 : SfxPoolItem(RES_FLTR_TOX), pTOXBase(rCpy.pTOXBase), nCols( rCpy.nCols ), 965 bHadBreakItem( rCpy.bHadBreakItem ), bHadPageDescItem( rCpy.bHadPageDescItem ) 966 { 967 } 968 969 int SwFltTOX::operator==(const SfxPoolItem& rItem) const 970 { 971 return pTOXBase == ((SwFltTOX&)rItem).pTOXBase; 972 } 973 974 SfxPoolItem* SwFltTOX::Clone(SfxItemPool*) const 975 { 976 return new SwFltTOX(*this); 977 } 978 979 //------ hier stehen die Methoden von SwFltSwSection ----------- 980 981 SwFltSection::SwFltSection(SwSectionData *const pSect) 982 : SfxPoolItem(RES_FLTR_SECTION) 983 , m_pSection(pSect) 984 { 985 } 986 987 SwFltSection::SwFltSection(const SwFltSection& rCpy) 988 : SfxPoolItem(RES_FLTR_SECTION) 989 , m_pSection(rCpy.m_pSection) 990 { 991 } 992 993 int SwFltSection::operator==(const SfxPoolItem& rItem) const 994 { 995 return m_pSection == ((SwFltSection&)rItem).m_pSection; 996 } 997 998 SfxPoolItem* __EXPORT SwFltSection::Clone(SfxItemPool*) const 999 { 1000 return new SwFltSection(*this); 1001 } 1002 1003 /////////////////////////////////////////////////////////////////////// 1004 // 1005 // hier beginnt der von mdt erzeugte code. dieser ist eine shell auf 1006 // der writer-seite nach moeglichkeit bald fuer alle filter. die ganze 1007 // schwierigkeit, texte & formatattribute einzufuegen, die positionen 1008 // zu verwalten, styles & kopf/fuszzeilen etc. 1009 // 1010 1011 //////////////////////////////////////////////////////////// SwFltShell 1012 SwFltShell::SwFltShell(SwDoc* pDoc, SwPaM& rPaM, const String& rBaseURL, sal_Bool bNew, sal_uLong nFieldFl) : 1013 pCurrentPageDesc(0), 1014 pSavedPos(0), 1015 eSubMode(None), 1016 nAktStyle(0), 1017 aStack(pDoc, nFieldFl), 1018 aEndStack(pDoc, nFieldFl), 1019 pPaM(new SwPaM(*(rPaM.GetPoint()))), 1020 sBaseURL(rBaseURL), 1021 nPageDescOffset(GetDoc().GetPageDescCnt()), 1022 eSrcCharSet(RTL_TEXTENCODING_MS_1252), 1023 bNewDoc(bNew), 1024 bStdPD(sal_False), 1025 bProtect(sal_False) 1026 { 1027 memset( pColls, 0, sizeof( pColls ) ); 1028 pOutDoc = new SwFltOutDoc( *pDoc, pPaM, aStack, aEndStack ); 1029 pOut = pOutDoc; 1030 1031 if( !bNewDoc ){ // in ein Dokument einfuegen ? 1032 // Da immer ganze Zeile eingelesen werden, muessen 1033 // evtl. Zeilen eingefuegt / aufgebrochen werden 1034 const SwPosition* pPos = pPaM->GetPoint(); 1035 const SwTxtNode* pSttNd = pPos->nNode.GetNode().GetTxtNode(); 1036 sal_uInt16 nCntPos = pPos->nContent.GetIndex(); 1037 if( nCntPos && pSttNd->GetTxt().Len() ) 1038 // EinfuegePos nicht in leerer Zeile 1039 pDoc->SplitNode( *pPos, false ); // neue Zeile erzeugen 1040 if( pSttNd->GetTxt().Len() ){ // EinfuegePos nicht am Ende der Zeile 1041 pDoc->SplitNode( *pPos, false ); // neue Zeile 1042 pPaM->Move( fnMoveBackward ); // gehe in leere Zeile 1043 } 1044 1045 // verhinder das Einlesen von Tabellen in Fussnoten / Tabellen 1046 sal_uLong nNd = pPos->nNode.GetIndex(); 1047 sal_Bool bReadNoTbl = 0 != pSttNd->FindTableNode() || 1048 ( nNd < pDoc->GetNodes().GetEndOfInserts().GetIndex() && 1049 pDoc->GetNodes().GetEndOfInserts().StartOfSectionIndex() < nNd ); 1050 if( bReadNoTbl ) 1051 pOutDoc->SetReadNoTable(); 1052 } 1053 pCurrentPageDesc = &((SwPageDesc&)const_cast<const SwDoc *>(pDoc) 1054 ->GetPageDesc( 0 )); // Standard 1055 1056 } 1057 1058 SwFltShell::~SwFltShell() 1059 { 1060 sal_uInt16 i; 1061 1062 if (eSubMode == Style) 1063 EndStyle(); 1064 if( pOutDoc->IsInTable() ) // falls nicht ordentlich abgeschlossen 1065 EndTable(); 1066 if( pOutDoc->IsInFly() ) 1067 EndFly(); 1068 1069 GetDoc().SetUpdateExpFldStat(true); 1070 GetDoc().SetInitDBFields(sal_True); 1071 aStack.SetAttr(*pPaM->GetPoint(), 0, sal_False); 1072 aStack.SetAttr(*pPaM->GetPoint(), 0, sal_False); 1073 aEndStack.SetAttr(*pPaM->GetPoint(), 0, sal_False); 1074 aEndStack.SetAttr(*pPaM->GetPoint(), 0, sal_False); 1075 if( bProtect ){ // Das ganze Doc soll geschuetzt sein 1076 1077 SwDoc& rDoc = GetDoc(); 1078 // 1. SectionFmt und Section anlegen 1079 SwSectionFmt* pSFmt = rDoc.MakeSectionFmt( 0 ); 1080 SwSectionData aSectionData( CONTENT_SECTION, String::CreateFromAscii( 1081 RTL_CONSTASCII_STRINGPARAM("PMW-Protect") )); 1082 aSectionData.SetProtectFlag( true ); 1083 // 2. Start- und EndIdx suchen 1084 const SwNode* pEndNd = &rDoc.GetNodes().GetEndOfContent(); 1085 SwNodeIndex aEndIdx( *pEndNd, -1L ); 1086 const SwStartNode* pSttNd = pEndNd->StartOfSectionNode(); 1087 SwNodeIndex aSttIdx( *pSttNd, 1L ); // +1 -> hinter StartNode 1088 // Section einfuegen 1089 // Section einfuegen 1090 rDoc.GetNodes().InsertTextSection( 1091 aSttIdx, *pSFmt, aSectionData, 0, &aEndIdx, false ); 1092 1093 if( !IsFlagSet(SwFltControlStack::DONT_HARD_PROTECT) ){ 1094 SwDocShell* pDocSh = rDoc.GetDocShell(); 1095 if( pDocSh ) 1096 pDocSh->SetReadOnlyUI( sal_True ); 1097 } 1098 } 1099 // Pagedescriptoren am Dokument updaten (nur so werden auch die 1100 // linken Seiten usw. eingestellt). 1101 1102 GetDoc().ChgPageDesc( 0, 1103 const_cast<const SwDoc &>(GetDoc()). 1104 GetPageDesc( 0 )); // PageDesc "Standard" 1105 for (i=nPageDescOffset;i<GetDoc().GetPageDescCnt();i++) 1106 { 1107 const SwPageDesc& rPD = const_cast<const SwDoc &>(GetDoc()). 1108 GetPageDesc(i); 1109 GetDoc().ChgPageDesc(i, rPD); 1110 } 1111 1112 delete pPaM; 1113 for (i=0; i<sizeof(pColls)/sizeof(*pColls); i++) 1114 if( pColls[i] ) 1115 delete pColls[i]; 1116 delete pOutDoc; 1117 } 1118 1119 SwFltShell& SwFltShell::operator << ( const String& rStr ) 1120 { 1121 ASSERT(eSubMode != Style, "char insert while in style-mode"); 1122 GetDoc().InsertString( *pPaM, rStr ); 1123 return *this; 1124 } 1125 1126 void SwFltShell::ConvertUStr( String& rInOut ) 1127 { 1128 GetAppCharClass().toUpper( rInOut ); 1129 } 1130 1131 // QuoteString() wandelt CRs abhaengig von nFieldIniFlags in '\n' oder "\0x0d" 1132 String SwFltShell::QuoteStr( const String& rIn ) 1133 { 1134 String sOut( rIn ); 1135 sal_Bool bAllowCr = aStack.IsFlagSet( SwFltControlStack::ALLOW_FLD_CR ); 1136 1137 for( xub_StrLen n = 0; n < sOut.Len(); ++n ) 1138 { 1139 switch( sOut.GetChar( n ) ) 1140 { 1141 case 0x0a: 1142 sOut.Erase( n, 1 ); // 0xd 0xa wird zu \n 1143 break; 1144 1145 case 0x0b: 1146 case 0x0c: 1147 case 0x0d: 1148 if( bAllowCr ) 1149 sOut.SetChar( n, '\n' ); 1150 break; 1151 } 1152 } 1153 return sOut; 1154 } 1155 1156 SwFltShell& SwFltShell::operator << ( const sal_Unicode c ) 1157 { 1158 ASSERT( eSubMode != Style, "char insert while in style-mode"); 1159 GetDoc().InsertString( *pPaM, c ); 1160 return *this; 1161 } 1162 1163 SwFltShell& SwFltShell::AddError( const sal_Char* pErr ) 1164 { 1165 String aName( String::CreateFromAscii( 1166 RTL_CONSTASCII_STRINGPARAM( "ErrorTag" ))); 1167 SwFieldType* pFT = GetDoc().GetFldType( RES_SETEXPFLD, aName, false ); 1168 if( pFT == 0) 1169 { 1170 SwSetExpFieldType aS(&GetDoc(), aName, nsSwGetSetExpType::GSE_STRING); 1171 pFT = GetDoc().InsertFldType(aS); 1172 } 1173 SwSetExpField aFld( (SwSetExpFieldType*)pFT, 1174 String::CreateFromAscii( pErr )); 1175 //, VVF_INVISIBLE 1176 GetDoc().InsertPoolItem(*pPaM, SwFmtFld(aFld), 0); 1177 return *this; 1178 } 1179 1180 SwFltShell& SwFltShell::operator << (Graphic& rGraphic) 1181 { 1182 // embedded Grafik !! 1183 GetDoc().Insert(*pPaM, aEmptyStr, aEmptyStr, &rGraphic, NULL, NULL, NULL); 1184 return *this; 1185 } 1186 1187 void SwFltShell::NextParagraph() 1188 { 1189 GetDoc().AppendTxtNode(*pPaM->GetPoint()); 1190 } 1191 1192 void SwFltShell::NextPage() 1193 { 1194 NextParagraph(); 1195 GetDoc().InsertPoolItem(*pPaM, 1196 SvxFmtBreakItem(SVX_BREAK_PAGE_BEFORE, RES_BREAK), 0); 1197 } 1198 1199 SwFltShell& SwFltShell::AddGraphic( const String& rPicName ) 1200 { 1201 // embedded: 1202 GraphicFilter* pFilter = GraphicFilter::GetGraphicFilter(); 1203 Graphic aGraphic; 1204 // one of: GFF_NOT GFF_BMP GFF_GIF GFF_JPG GFF_PCD GFF_PCX GFF_PNG 1205 // GFF_TIF GFF_XBM GFF_DXF GFF_MET GFF_PCT GFF_SGF GFF_SVM GFF_WMF 1206 // GFF_SGV GFF_XXX 1207 INetURLObject aDir( 1208 URIHelper::SmartRel2Abs( 1209 INetURLObject(GetBaseURL()), rPicName, 1210 URIHelper::GetMaybeFileHdl()) ); 1211 switch (pFilter->ImportGraphic(aGraphic, aDir)) 1212 { 1213 case GRFILTER_OK: 1214 *this << aGraphic; 1215 break; 1216 case GRFILTER_OPENERROR: 1217 case GRFILTER_IOERROR: 1218 case GRFILTER_FORMATERROR: 1219 case GRFILTER_VERSIONERROR: 1220 case GRFILTER_FILTERERROR: 1221 case GRFILTER_ABORT: 1222 case GRFILTER_TOOBIG: 1223 default: 1224 AddError( "picture import error" ); 1225 break; 1226 } 1227 return *this; 1228 } 1229 1230 SwFltShell& SwFltShell::SetStyle( sal_uInt16 nStyle ) 1231 { 1232 SwFltFormatCollection* p = pColls[ nStyle ]; 1233 1234 if (p) 1235 { 1236 if( !pOutDoc->IsInTable() && nStyle != nAktStyle ) 1237 { 1238 if( pColls[nAktStyle]->IsInFly() && pOutDoc->IsInFly() ) 1239 pOutDoc->EndFly(); 1240 if( p->IsInFly() ) 1241 p->BeginStyleFly( pOutDoc ); 1242 } 1243 GetDoc().SetTxtFmtColl(*pPaM, p->GetColl()); 1244 nAktStyle = nStyle; 1245 } 1246 else 1247 { 1248 ASSERT( sal_False, "Ungueltiger SwFltStyleCode" ); 1249 } 1250 return *this; 1251 } 1252 1253 SwFltShell& SwFltShell::operator << (SwFltBookmark& aBook) 1254 { 1255 ConvertUStr( aBook.maName ); 1256 aBook.maVal = QuoteStr(aBook.maVal); 1257 aEndStack.NewAttr(*pPaM->GetPoint(), aBook); 1258 return *this; 1259 } 1260 1261 void SwFltShell::SetBookEnd(long nHandle) 1262 { 1263 aEndStack.SetAttr( *pPaM->GetPoint(), RES_FLTR_BOOKMARK, sal_True, nHandle ); 1264 } 1265 1266 SwFltShell& SwFltShell::EndItem( sal_uInt16 nAttrId ) 1267 { 1268 switch( nAttrId ) 1269 { 1270 case RES_FLTR_BOOKMARK: 1271 ASSERT( sal_False, "Falscher Aufruf fuer Bookmark-Ende" ); 1272 break; 1273 1274 case RES_FLTR_TOX: 1275 aEndStack.SetAttr(*pPaM->GetPoint(), nAttrId); 1276 break; 1277 1278 default: 1279 aStack.SetAttr(*pPaM->GetPoint(), nAttrId); 1280 break; 1281 } 1282 return *this; 1283 } 1284 1285 SwFltShell& SwFltShell::operator << (const SwField& rField) 1286 { 1287 GetDoc().InsertPoolItem(*pPaM, SwFmtFld(rField), 0); 1288 return *this; 1289 } 1290 1291 /*virtual*/ SwFltOutBase& SwFltOutDoc::operator << (const SfxPoolItem& rItem) 1292 { 1293 rStack.NewAttr(*pPaM->GetPoint(), rItem); 1294 return *this; 1295 } 1296 1297 /*virtual*/ SwFltOutBase& SwFltFormatCollection::operator << 1298 (const SfxPoolItem& rItem) 1299 { 1300 pColl->SetFmtAttr(rItem); 1301 return *this; 1302 } 1303 1304 const SfxPoolItem& SwFltOutDoc::GetAttr(sal_uInt16 nWhich) 1305 { 1306 return *rStack.GetFmtAttr(*pPaM->GetPoint(), nWhich); 1307 } 1308 1309 const SfxPoolItem& SwFltFormatCollection::GetAttr(sal_uInt16 nWhich) 1310 { 1311 return GetColl()->GetFmtAttr(nWhich); // mit Parents 1312 } 1313 1314 // GetNodeOrStyAttr holt Attribute fuer Toggle- und Modify-Attribute: 1315 // Bei Formatdefinitionen aus dem altuellen Style mit Parents 1316 // sonst aus dem Node mit Parents 1317 // Im Stack wird nicht nachgesehen 1318 1319 const SfxPoolItem& SwFltOutDoc::GetNodeOrStyAttr(sal_uInt16 nWhich) 1320 { 1321 SwCntntNode * pNd = pPaM->GetPoint()->nNode.GetNode().GetCntntNode(); 1322 if (pNd) // ContentNode: Attribut mit Parent 1323 return pNd->GetAttr(nWhich); 1324 else // kein ContentNode, dann das dflt. Attribut 1325 return GetDoc().GetAttrPool().GetDefaultItem(nWhich); 1326 } 1327 1328 const SfxPoolItem& SwFltFormatCollection::GetNodeOrStyAttr(sal_uInt16 nWhich) 1329 { 1330 return GetColl()->GetFmtAttr(nWhich); // mit Parents 1331 } 1332 1333 const SfxPoolItem& SwFltShell::GetNodeOrStyAttr(sal_uInt16 nWhich) 1334 { 1335 return pOut->GetNodeOrStyAttr( nWhich ); 1336 } 1337 1338 const SfxPoolItem& SwFltShell::GetAttr(sal_uInt16 nWhich) 1339 { 1340 return pOut->GetAttr( nWhich ); 1341 } 1342 1343 const SfxPoolItem& SwFltShell::GetFlyFrmAttr(sal_uInt16 nWhich) 1344 { 1345 return pOut->GetFlyFrmAttr( nWhich ); 1346 } 1347 1348 SwFieldType* SwFltShell::GetSysFldType(sal_uInt16 eWhich) 1349 { 1350 return GetDoc().GetSysFldType(eWhich); 1351 } 1352 1353 sal_Bool SwFltShell::GetWeightBold() 1354 { 1355 return ((SvxWeightItem&)GetNodeOrStyAttr(RES_CHRATR_WEIGHT)).GetWeight() 1356 != WEIGHT_NORMAL; 1357 } 1358 1359 sal_Bool SwFltShell::GetPostureItalic() 1360 { 1361 return ((SvxPostureItem&)GetNodeOrStyAttr(RES_CHRATR_POSTURE)).GetPosture() 1362 != ITALIC_NONE; 1363 } 1364 1365 sal_Bool SwFltShell::GetCrossedOut() 1366 { 1367 return ((SvxCrossedOutItem&)GetNodeOrStyAttr(RES_CHRATR_CROSSEDOUT)) 1368 .GetStrikeout() != STRIKEOUT_NONE; 1369 } 1370 1371 sal_Bool SwFltShell::GetContour() 1372 { 1373 return ((SvxContourItem&)GetNodeOrStyAttr(RES_CHRATR_CONTOUR)).GetValue(); 1374 } 1375 1376 sal_Bool SwFltShell::GetCaseKapitaelchen() 1377 { 1378 return ((SvxCaseMapItem&)GetNodeOrStyAttr(RES_CHRATR_CASEMAP)) 1379 .GetCaseMap() == SVX_CASEMAP_KAPITAELCHEN; 1380 } 1381 1382 sal_Bool SwFltShell::GetCaseVersalien() 1383 { 1384 return ((SvxCaseMapItem&)GetNodeOrStyAttr(RES_CHRATR_CASEMAP)) 1385 .GetCaseMap() == SVX_CASEMAP_VERSALIEN; 1386 } 1387 1388 //------------------------------------------------------------------------- 1389 // Tabellen 1390 //------------------------------------------------------------------------- 1391 1392 SwFltOutBase::~SwFltOutBase() 1393 { 1394 } 1395 1396 SwFltOutBase::SwFltOutBase(SwDoc& rDocu) 1397 : rDoc(rDocu), eFlyAnchor(FLY_AT_PARA), bFlyAbsPos(false) 1398 { 1399 } 1400 1401 const SfxPoolItem& SwFltOutBase::GetCellAttr(sal_uInt16 nWhich) 1402 { 1403 ASSERT(sal_False, "GetCellAttr ausserhalb von normalem Text"); 1404 return GetDoc().GetAttrPool().GetDefaultItem(nWhich); 1405 } 1406 1407 sal_Bool SwFltOutBase::BeginTable() 1408 { 1409 ASSERT(sal_False, "BeginTable ausserhalb von normalem Text"); 1410 return sal_False; 1411 } 1412 1413 void SwFltOutBase::NextTableCell() 1414 { 1415 ASSERT(sal_False, "NextTableCell ausserhalb von normalem Text"); 1416 } 1417 1418 void SwFltOutBase::NextTableRow() 1419 { 1420 ASSERT(sal_False, "NextTableRow ausserhalb von normalem Text"); 1421 } 1422 1423 void SwFltOutBase::SetTableWidth(SwTwips /*nW*/) 1424 { 1425 ASSERT(sal_False, "SetTableWidth ausserhalb von normalem Text"); 1426 } 1427 1428 void SwFltOutBase::SetTableOrient(sal_Int16 /*eOri*/) 1429 { 1430 ASSERT(sal_False, "SetTableOrient ausserhalb von normalem Text"); 1431 } 1432 1433 void SwFltOutBase::SetCellWidth(SwTwips /*nWidth*/, sal_uInt16 /*nCell*/) 1434 { 1435 ASSERT(sal_False, "SetCellWidth ausserhalb von normalem Text"); 1436 } 1437 1438 void SwFltOutBase::SetCellHeight(SwTwips /*nH*/) 1439 { 1440 ASSERT(sal_False, "SetCellHeight ausserhalb von normalem Text"); 1441 } 1442 1443 void SwFltOutBase::SetCellBorder(const SvxBoxItem& /*rFmtBox*/, sal_uInt16 /*nCell*/) 1444 { 1445 ASSERT(sal_False, "SetCellBorder ausserhalb von normalem Text"); 1446 } 1447 1448 void SwFltOutBase::SetCellSpace(sal_uInt16 /*nSp*/) 1449 { 1450 ASSERT(sal_False, "SetCellSpace ausserhalb von normalem Text"); 1451 } 1452 1453 void SwFltOutBase::DeleteCell(sal_uInt16 /*nCell*/) 1454 { 1455 ASSERT(sal_False, "DeleteCell ausserhalb von normalem Text"); 1456 } 1457 1458 void SwFltOutBase::EndTable() 1459 { 1460 ASSERT(sal_False, "EndTable ausserhalb von normalem Text"); 1461 } 1462 1463 /*virtual*/ sal_Bool SwFltOutDoc::IsInTable() 1464 { 1465 return pTable != 0; 1466 }; 1467 1468 sal_Bool SwFltOutDoc::BeginTable() 1469 { 1470 if(bReadNoTbl) 1471 return sal_False; 1472 1473 if (pTable){ 1474 ASSERT(sal_False, "BeginTable in Table"); 1475 return sal_False; 1476 } 1477 // Alle Attribute schliessen, da sonst Attribute 1478 // entstehen koennen, die in Flys reinragen 1479 rStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 1480 rEndStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 1481 1482 // create table: 1483 ASSERT(pTabSavedPos == NULL, "SwFltOutDoc"); 1484 pTabSavedPos = new SwPosition(*pPaM->GetPoint()); 1485 pTable = GetDoc().InsertTable( 1486 SwInsertTableOptions( tabopts::HEADLINE_NO_BORDER, 1 ), 1487 *pTabSavedPos, 1, 1, text::HoriOrientation::LEFT, 0, 0, sal_False, sal_False ); // TODO MULTIHEADER 1488 nTableWidth = 0; 1489 ((SwTable*)pTable)->LockModify(); // Nichts automatisch anpassen! 1490 // set pam in 1. table cell 1491 usTableX = 1492 usTableY = 0; 1493 SeekCell(usTableY, usTableX, sal_True); 1494 return sal_True; 1495 } 1496 1497 SwTableBox* SwFltOutDoc::GetBox(sal_uInt16 ny, sal_uInt16 nx /*= USHRT_MAX */) 1498 { 1499 if(!pTable){ 1500 ASSERT(pTable, "GetBox ohne Tabelle"); 1501 return 0; 1502 } 1503 if( nx == USHRT_MAX ) // aktuelle Zelle 1504 nx = usTableX; 1505 1506 // get structs to table cells 1507 const SwTableLines* pTableLines = &pTable->GetTabLines(); 1508 if(!pTableLines){ 1509 ASSERT(sal_False, "SwFltOutDoc:GetBox:pTableLines"); 1510 return 0; 1511 } 1512 if( ny >= pTableLines->Count() ){ // Notbremse 1513 ASSERT( sal_False, "SwFltOutDoc:GetBox:ny >= Count()"); 1514 ny = pTableLines->Count() - 1; 1515 } 1516 SwTableLine* pTableLine = (*pTableLines)[ny]; 1517 if(!pTableLine){ 1518 ASSERT(sal_False, "SwFltOutDoc:GetBox:pTableLine"); 1519 return 0; 1520 } 1521 SwTableBoxes* pTableBoxes = &pTableLine->GetTabBoxes(); 1522 if(!pTableBoxes){ 1523 ASSERT(sal_False, "SwFltOutDoc:GetBox:pTableBoxes"); 1524 return 0; 1525 } 1526 if( nx >= pTableBoxes->Count() ){ // Notbremse 1527 ASSERT(sal_False, "SwFltOutDoc:GetBox:nx >= Count()"); 1528 nx = pTableBoxes->Count() - 1; 1529 } 1530 SwTableBox* pTableBox = (*pTableBoxes)[nx]; 1531 1532 ASSERT(pTableBox != 0, "SwFltOutDoc:GetBox:pTableBox"); 1533 return pTableBox; 1534 } 1535 1536 void SwFltOutDoc::NextTableCell() 1537 { 1538 if(!pTable){ 1539 ASSERT(pTable, "NextTableCell ohne Tabelle"); 1540 return; 1541 } 1542 const SwTableLines* pTableLines = &pTable->GetTabLines(); 1543 SwTableLine* pTableLine = (*pTableLines)[usTableY]; 1544 SwTableBoxes* pTableBoxes = &pTableLine->GetTabBoxes(); 1545 SwTableBox* pTableBox = (*pTableBoxes)[usTableX]; 1546 ASSERT(pTableBox != 0, "SwFltOutDoc:NextTableCell:pTableBox"); 1547 if(!pTableBox) 1548 return; 1549 //#pragma message(__FILE__ "(?) : Sw's const problem") 1550 // insert cells: 1551 if (++usTableX >= pTableBoxes->Count()) 1552 GetDoc().GetNodes().InsBoxen( 1553 GetDoc().IsIdxInTbl(pPaM->GetPoint()->nNode), 1554 pTableLine, 1555 (SwTableBoxFmt*)pTableBox->GetFrmFmt(), 1556 GetDoc().GetTxtCollFromPool(RES_POOLCOLL_STANDARD, false ), 1557 0, 1558 pTableBoxes->Count()); 1559 SeekCell(usTableY, usTableX, sal_True); 1560 pTableBox = (*pTableBoxes)[usTableX]; 1561 ASSERT(pTableBox != 0, "SwFltOutDoc:pTableBox"); 1562 if(pTableBox) 1563 (*pTableBoxes)[usTableX]->ClaimFrmFmt(); 1564 } 1565 1566 void SwFltOutDoc::NextTableRow() 1567 { 1568 SwTableBox* pTableBox = GetBox(usTableY, 0); 1569 if (pTableBox) 1570 { 1571 // duplicate row: 1572 SwSelBoxes aSelBoxes; 1573 aSelBoxes.Insert( pTableBox ); 1574 GetDoc().InsertRow(aSelBoxes); 1575 usTableX = 0; 1576 SeekCell(++usTableY, usTableX, sal_True); 1577 GetDoc().SetTxtFmtColl(*pPaM, 1578 GetDoc().GetTxtCollFromPool(RES_POOLCOLL_STANDARD, false )); 1579 } 1580 } 1581 1582 void SwFltOutDoc::SetTableWidth(SwTwips nSwWidth) 1583 { 1584 if(!pTable){ 1585 ASSERT(pTable, "SetTableWidth ohne Tabelle"); 1586 return; 1587 } 1588 ASSERT( nSwWidth > MINLAY, "Tabellenbreite <= MINLAY" ); 1589 if( nSwWidth != nTableWidth ){ 1590 if( nTableWidth ) // Nicht beim ersten Setzen 1591 SplitTable(); 1592 pTable->GetFrmFmt()->SetFmtAttr( SwFmtFrmSize(ATT_VAR_SIZE, nSwWidth)); 1593 nTableWidth = nSwWidth; 1594 } 1595 } 1596 1597 void SwFltOutDoc::SetTableOrient(sal_Int16 eOri) 1598 { 1599 if(!pTable){ 1600 ASSERT(pTable, "SetTableOrient ohne Tabelle"); 1601 return; 1602 } 1603 pTable->GetFrmFmt()->SetFmtAttr( SwFmtHoriOrient( 0, eOri )); 1604 } 1605 1606 void SwFltOutDoc::SetCellWidth(SwTwips nWidth, sal_uInt16 nCell /* = USHRT_MAX */ ) 1607 { 1608 if(!pTable){ 1609 ASSERT(pTable, "SetCellWidth ohne Tabelle"); 1610 return; 1611 } 1612 ASSERT( nWidth > MINLAY, "Tabellenzellenbreite <= MINLAY" ); 1613 if (nWidth < MINLAY) 1614 nWidth = MINLAY; 1615 1616 SwTableBox* pTableBox = GetBox(usTableY, nCell); 1617 if(pTableBox && pTableBox->GetFrmFmt() ){ 1618 SwFmtFrmSize aFmtFrmSize(ATT_FIX_SIZE); 1619 aFmtFrmSize.SetWidth(nWidth); 1620 pTableBox->GetFrmFmt()->SetFmtAttr(aFmtFrmSize); 1621 } 1622 } 1623 1624 void SwFltOutDoc::SetCellHeight(SwTwips nHeight) 1625 { 1626 if(!pTable){ 1627 ASSERT(pTable, "SetCellHeight ohne Tabelle"); 1628 return; 1629 } 1630 1631 const SwTableLines* pTableLines = &pTable->GetTabLines(); 1632 SwTableLine* pTableLine = (*pTableLines)[usTableY]; 1633 SwFmtFrmSize aFmtFrmSize(ATT_MIN_SIZE, 0, 0); 1634 if (nHeight < MINLAY) 1635 nHeight = MINLAY; 1636 aFmtFrmSize.SetHeight(nHeight); 1637 pTableLine->GetFrmFmt()->SetFmtAttr(aFmtFrmSize); 1638 } 1639 1640 const SfxPoolItem& SwFltOutDoc::GetCellAttr(sal_uInt16 nWhich) 1641 { 1642 if (!pTable){ 1643 ASSERT(pTable, "GetCellAttr ohne Table"); 1644 return GetDoc().GetAttrPool().GetDefaultItem(nWhich); 1645 } 1646 1647 SwTableBox* pTableBox = GetBox(usTableY, usTableX); 1648 if(!pTableBox) 1649 return GetDoc().GetAttrPool().GetDefaultItem(nWhich); 1650 return pTableBox->GetFrmFmt()->GetFmtAttr( nWhich ); 1651 } 1652 1653 void SwFltOutDoc::SetCellBorder(const SvxBoxItem& rFmtBox, 1654 sal_uInt16 nCell /* = USHRT_MAX */ ) 1655 { 1656 SwTableBox* pTableBox = GetBox(usTableY, nCell); 1657 if(pTableBox) 1658 pTableBox->GetFrmFmt()->SetFmtAttr(rFmtBox); 1659 } 1660 1661 // nicht aktiviert !!! 1662 void SwFltOutDoc::SetCellSpace(sal_uInt16 nDist) 1663 { 1664 if(!pTable){ 1665 ASSERT(pTable, "SetCellSpace ohne Tabelle"); 1666 return; 1667 } 1668 SwTableBox* pTableBox = GetBox(usTableY, usTableX); 1669 if(!pTableBox) 1670 return; 1671 1672 SvxBoxItem aFmtBox( *((SvxBoxItem*) 1673 &pTableBox->GetFrmFmt()->GetFmtAttr( RES_BOX ))); 1674 1675 // versteh ich nich, sven: if (!nDist) nDist = 18; // ca. 0.03 cm 1676 if (nDist > 42) // max. 0.7 mm 1677 nDist = 42; 1678 else 1679 if (nDist < MIN_BORDER_DIST) 1680 nDist = MIN_BORDER_DIST; 1681 aFmtBox.SetDistance(nDist); 1682 pTableBox->GetFrmFmt()->SetFmtAttr(aFmtBox); 1683 } 1684 1685 void SwFltOutDoc::DeleteCell(sal_uInt16 nCell /* = USHRT_MAX */) 1686 { 1687 SwTableBox* pTableBox = GetBox(usTableY, nCell); 1688 if(pTableBox){ 1689 SwSelBoxes aSelBoxes; 1690 aSelBoxes.Insert( pTableBox ); 1691 GetDoc().DeleteRowCol(aSelBoxes); 1692 usTableX--; 1693 } 1694 } 1695 1696 void SwFltOutDoc::SplitTable() 1697 { 1698 if(!pTable) 1699 { 1700 ASSERT(pTable, "SplitTable ohne Tabelle"); 1701 return; 1702 } 1703 SwTableBox* pAktBox = GetBox(usTableY, usTableX); 1704 SwTableBox* pSplitBox = GetBox(usTableY - 1, 0); 1705 GetDoc().GetNodes().SplitTable(SwNodeIndex(*pSplitBox->GetSttNd()), false); 1706 pTable = &pAktBox->GetSttNd()->FindTableNode()->GetTable(); 1707 usTableY = 0; 1708 } 1709 1710 void SwFltOutDoc::EndTable() 1711 { 1712 if (!pTable){ 1713 ASSERT(pTable, "EndTable ohne Table"); 1714 return; 1715 } 1716 // Alle Attribute schliessen, da sonst Attribute 1717 // entstehen koennen, die in Flys reinragen 1718 rStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 1719 rEndStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 1720 1721 if (GetDoc().GetCurrentViewShell()){ //swmod 071108//swmod 071225 1722 SwTableNode* pTableNode = GetDoc().IsIdxInTbl( 1723 pPaM->GetPoint()->nNode); 1724 pTableNode->DelFrms(); 1725 pTableNode->MakeFrms(&pPaM->GetPoint()->nNode); 1726 } 1727 1728 *pPaM->GetPoint() = *pTabSavedPos; // restore Cursor 1729 delete pTabSavedPos; 1730 pTabSavedPos = 0; 1731 ((SwTable*)pTable)->UnlockModify(); // Test, nuetzt nichts gegen Assert 1732 pTable = 0; 1733 nTableWidth = 0; 1734 } 1735 1736 sal_Bool SwFltOutDoc::SeekCell(short nRow, short nCol, sal_Bool bPam) 1737 { 1738 // get structs to table cells 1739 const SwTableLines* pTableLines = &pTable->GetTabLines(); 1740 SwTableLine* pTableLine = (*pTableLines)[usTableY]; 1741 SwTableBoxes* pTableBoxes = &pTableLine->GetTabBoxes(); 1742 SwTableBox* pTableBox = (*pTableBoxes)[usTableX]; 1743 1744 if ((sal_uInt16)nRow >= pTableLines->Count()) 1745 { 1746 ASSERT((sal_uInt16)nRow >= pTableLines->Count(), "SwFltOutDoc"); 1747 return sal_False; 1748 } 1749 pTableLine = (*pTableLines)[nRow]; 1750 pTableBoxes = &pTableLine->GetTabBoxes(); 1751 if (nCol >= pTableBoxes->Count()) 1752 return sal_False; 1753 pTableBox = (*pTableBoxes)[nCol]; 1754 if( !pTableBox->GetSttNd() ) 1755 { 1756 ASSERT(pTableBox->GetSttNd(), "SwFltOutDoc"); 1757 return sal_False; 1758 } 1759 if(bPam) 1760 { 1761 pPaM->GetPoint()->nNode = pTableBox->GetSttIdx() + 1; 1762 pPaM->GetPoint()->nContent.Assign(pPaM->GetCntntNode(), 0); 1763 //#pragma message(__FILE__ "(?) : Sw's const problem") 1764 #if OSL_DEBUG_LEVEL > 1 1765 const SwTxtFmtColl* p = GetDoc().GetDfltTxtFmtColl(); 1766 p = GetDoc().GetTxtCollFromPool(RES_POOLCOLL_STANDARD, false ); 1767 #endif 1768 GetDoc().SetTxtFmtColl(*pPaM, 1769 GetDoc().GetTxtCollFromPool(RES_POOLCOLL_STANDARD, false )); 1770 } 1771 return sal_True; 1772 } 1773 1774 1775 //----------------------------------------------------------------------------- 1776 // Flys in SwFltOutBase 1777 //----------------------------------------------------------------------------- 1778 1779 SfxItemSet* SwFltOutBase::NewFlyDefaults() 1780 { 1781 // Unbedingt noetige Standardwerte setzen ( falls diese Werte nicht 1782 // spaeter explizit gesetzt werden ) 1783 1784 SfxItemSet* p = new SfxItemSet( GetDoc().GetAttrPool(), 1785 RES_FRMATR_BEGIN, RES_FRMATR_END-1 ); 1786 SwFmtFrmSize aSz( ATT_VAR_SIZE, MINFLY, MINFLY ); 1787 // Default: Breite 100% ( = PMW:Auto ) 1788 aSz.SetWidthPercent( 100 ); // Hoehe: Auto 1789 p->Put( aSz ); 1790 p->Put( SwFmtHoriOrient( 0, text::HoriOrientation::NONE, text::RelOrientation::FRAME )); 1791 return p; 1792 } 1793 1794 sal_Bool SwFltOutBase::BeginFly( RndStdIds eAnchor /*= FLY_AT_PARA*/, 1795 sal_Bool bAbsolutePos /*= sal_False*/, 1796 const SfxItemSet* 1797 #ifdef DBG_UTIL 1798 pMoreAttrs /*= 0*/ 1799 #endif 1800 ) 1801 { 1802 ASSERT(!pMoreAttrs, "SwFltOutBase:BeginFly mit pMoreAttrs" ); 1803 eFlyAnchor = eAnchor; 1804 bFlyAbsPos = bAbsolutePos; // Bloedsinn eigentlich 1805 return sal_True; 1806 } 1807 1808 /*virtual*/ void SwFltOutBase::SetFlyAnchor( RndStdIds eAnchor ) 1809 { 1810 if( !IsInFly() ){ 1811 ASSERT( sal_False, "SetFlyAnchor() ohne Fly" ); 1812 return; 1813 } 1814 if ( eAnchor == FLY_AS_CHAR ){ 1815 ASSERT( sal_False, "SetFlyAnchor( FLY_AS_CHAR ) nicht implementiert" ); 1816 return; 1817 } 1818 SwFmtAnchor& rAnchor = (SwFmtAnchor&)GetFlyFrmAttr( RES_ANCHOR ); 1819 rAnchor.SetType( eAnchor ); 1820 } 1821 1822 void SwFltOutBase::EndFly() 1823 { 1824 if( bFlyAbsPos ){ 1825 // hier muessen die absoluten Positionen am Fly noch in 1826 // die Writer-Koordinaten umgerechnet werden. 1827 } 1828 } 1829 1830 //----------------------------------------------------------------------------- 1831 // Flys in SwFltDoc 1832 //----------------------------------------------------------------------------- 1833 1834 /* virtual */ sal_Bool SwFltOutDoc::IsInFly() 1835 { 1836 return pFly != 0; 1837 }; 1838 1839 SwFrmFmt* SwFltOutDoc::MakeFly( RndStdIds eAnchor, SfxItemSet* pSet ) 1840 { 1841 pFly = (SwFlyFrmFmt*)GetDoc().MakeFlySection( eAnchor, pPaM->GetPoint(), 1842 pSet ); 1843 return pFly; 1844 } 1845 1846 sal_Bool SwFltOutDoc::BeginFly( RndStdIds eAnchor /*= FLY_AT_PARA*/, 1847 sal_Bool bAbsolutePos /*= sal_False*/, 1848 const SfxItemSet* pMoreAttrs /*= 0*/ ) 1849 1850 { 1851 SwFltOutBase::BeginFly( eAnchor, bAbsolutePos, 0 ); 1852 SfxItemSet* pSet = NewFlyDefaults(); 1853 1854 // Alle Attribute schliessen, da sonst Attribute entstehen koennen, 1855 // die in Flys reinragen 1856 rStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 1857 rEndStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 1858 1859 // create Fly: 1860 ASSERT(pFlySavedPos == NULL, "BeginFly in Fly"); // rekursiv geht noch nicht 1861 pFlySavedPos = new SwPosition(*pPaM->GetPoint()); 1862 1863 1864 SwFmtAnchor aAnchor( eAnchor, 1 ); 1865 1866 // Wenn Fly-Attribute im Style waren, dann jetzt als Defaults reinsetzen 1867 if (pMoreAttrs) 1868 pSet->Put(*pMoreAttrs); 1869 1870 // dieses NICHT bei Seitengebundenem Fly mit Seiten-NUMMER ! 1871 aAnchor.SetAnchor(pPaM->GetPoint()); // braucht erstaunlicherweise 1872 // den Stack nicht 1873 // aStack.NewAttr( *pPaM->GetPoint(), SwFltAnchor( pFly ) ); 1874 1875 pSet->Put( aAnchor ); 1876 SwFrmFmt* pF = MakeFly( eAnchor, pSet ); 1877 delete pSet; 1878 1879 // set pam in Fly 1880 const SwFmtCntnt& rCntnt = pF->GetCntnt(); 1881 ASSERT( rCntnt.GetCntntIdx(), "Kein Inhalt vorbereitet." ); 1882 pPaM->GetPoint()->nNode = rCntnt.GetCntntIdx()->GetIndex() + 1; 1883 SwCntntNode *pNode = pPaM->GetCntntNode(); 1884 pPaM->GetPoint()->nContent.Assign( pNode, 0 ); 1885 1886 return sal_True; 1887 } 1888 1889 /*virtual*/ void SwFltOutDoc::SetFlyFrmAttr(const SfxPoolItem& rAttr) 1890 { 1891 if (pFly){ 1892 pFly->SetFmtAttr( rAttr ); 1893 }else{ 1894 ASSERT(pFly, "SetFlyAttr ohne Doc-Fly"); 1895 return; 1896 } 1897 } 1898 1899 /*virtual*/ const SfxPoolItem& SwFltOutDoc::GetFlyFrmAttr(sal_uInt16 nWhich) 1900 { 1901 if (pFly){ 1902 return pFly->GetFmtAttr( nWhich ); 1903 }else{ 1904 ASSERT(pFly, "GetFlyAttr ohne Fly"); 1905 return GetDoc().GetAttrPool().GetDefaultItem(nWhich); 1906 } 1907 } 1908 1909 void SwFltOutDoc::EndFly() 1910 { 1911 if( pTable ){ 1912 ASSERT( sal_False, "SwFltOutDoc::EndFly() in Table" ); 1913 return; 1914 } 1915 // Alle Attribute schliessen, da sonst Attribute 1916 // entstehen koennen, die aus Flys rausragen 1917 rStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 1918 rEndStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 1919 1920 *pPaM->GetPoint() = *pFlySavedPos; // restore Cursor 1921 delete pFlySavedPos; 1922 pFlySavedPos = 0; 1923 SwFltOutBase::EndFly(); 1924 pFly = 0; 1925 } 1926 1927 //----------------------------------------------------------------------------- 1928 // Flys in SwFltFormatCollection 1929 //----------------------------------------------------------------------------- 1930 /*virtual*/ sal_Bool SwFltFormatCollection::IsInFly() 1931 { 1932 return bHasFly; 1933 }; 1934 1935 /*virtual*/ void SwFltFormatCollection::SetFlyFrmAttr(const SfxPoolItem& rAttr) 1936 { 1937 if (!pFlyAttrs) 1938 pFlyAttrs = new SfxItemSet( GetDoc().GetAttrPool(), 1939 RES_FRMATR_BEGIN, RES_FRMATR_END-1 ); 1940 pFlyAttrs->Put( rAttr ); 1941 } 1942 1943 /*virtual*/ const SfxPoolItem& SwFltFormatCollection::GetFlyFrmAttr(sal_uInt16 nWhich) 1944 { 1945 // ASSERT( pFlyAttrs, "GetFlyFrmAttr ohne Coll-FlyAttrs" ); 1946 if( pFlyAttrs ) 1947 return pFlyAttrs->Get( nWhich, sal_False ); 1948 else 1949 return GetDoc().GetAttrPool().GetDefaultItem(nWhich); 1950 } 1951 1952 sal_Bool SwFltFormatCollection::BeginFly( RndStdIds eAnchor /*= FLY_AT_PARA*/, 1953 sal_Bool bAbsolutePos /*= sal_False*/, 1954 const SfxItemSet* pMoreAttrs /*= 0*/ ) 1955 1956 { 1957 SwFltOutBase::BeginFly( eAnchor, bAbsolutePos, pMoreAttrs ); 1958 bHasFly = sal_True; 1959 return sal_True; 1960 } 1961 1962 void SwFltFormatCollection::EndFly() // Wird nie aufgerufen 1963 { 1964 } 1965 1966 sal_Bool SwFltFormatCollection::BeginStyleFly( SwFltOutDoc* pOutDoc ) 1967 { 1968 ASSERT( pOutDoc, "BeginStyleFly ohne pOutDoc" ); 1969 ASSERT( pOutDoc && !pOutDoc->IsInFly(), "BeginStyleFly in Fly" ); 1970 if( pOutDoc && !pOutDoc->IsInFly() ) 1971 return pOutDoc->BeginFly( eFlyAnchor, bFlyAbsPos, pFlyAttrs ); 1972 else 1973 return sal_False; 1974 } 1975 1976 //----------------------------------------------------------------------------- 1977 // Flys in SwFltShell 1978 //----------------------------------------------------------------------------- 1979 1980 sal_Bool SwFltShell::BeginFly( RndStdIds eAnchor /*= FLY_AT_PARA*/, 1981 sal_Bool bAbsolutePos /*= sal_False*/ ) 1982 1983 { 1984 if (pOut->IsInFly()){ 1985 ASSERT(sal_False, "BeginFly in Fly"); 1986 return sal_False; 1987 } 1988 if (pOutDoc->IsInTable()){ 1989 ASSERT(sal_False, "BeginFly in Table"); 1990 return sal_False; 1991 } 1992 pOut->BeginFly( eAnchor, bAbsolutePos, pColls[nAktStyle]->GetpFlyAttrs() ); 1993 eSubMode = Fly; 1994 return sal_True; 1995 } 1996 1997 void SwFltShell::SetFlyXPos( short nXPos, sal_Int16 eHRel /*= text::RelOrientation::FRAME*/, 1998 sal_Int16 eHAlign /*= text::HoriOrientation::NONE*/ ) 1999 { 2000 SetFlyFrmAttr( SwFmtHoriOrient( nXPos, eHAlign, eHRel ) ); 2001 } 2002 2003 void SwFltShell::SetFlyYPos( short nYPos, sal_Int16 eVRel /*= text::RelOrientation::FRAME*/, 2004 sal_Int16 eVAlign /*= text::VertOrientation::NONE*/ ) 2005 { 2006 SetFlyFrmAttr( SwFmtVertOrient( nYPos, eVAlign, eVRel ) ); 2007 } 2008 2009 2010 void SwFltShell::EndFly() 2011 { 2012 if (!pOut->IsInFly()){ 2013 ASSERT(sal_False, "EndFly ohne Fly"); 2014 return; 2015 } 2016 if (pOutDoc->IsInTable()){ // Table verschraenkt mit Fly macht keinen Sinn 2017 ASSERT(sal_False, "EndFly in Table ( verschraenkt )"); 2018 EndTable(); // -> Table beenden 2019 } 2020 pOut->EndFly(); 2021 eSubMode = None; 2022 } 2023 2024 //----------------------------------------------------------------------------- 2025 // Fussnoten 2026 //----------------------------------------------------------------------------- 2027 2028 void SwFltShell::BeginFootnote() 2029 { 2030 if( pOut->IsInFly() ){ // Passiert z.B. bei Fussnote in Fly 2031 ASSERT(sal_False, "Fussnote in Fly nicht erlaubt"); 2032 return; 2033 } 2034 if( pOutDoc->IsInTable() ){ 2035 ASSERT(sal_False, "Fussnote in Table z.Zt. nicht erlaubt"); 2036 return; 2037 } 2038 2039 // Alle Attribute schliessen, da sonst Attribute entstehen koennen, 2040 // die in Fussnoten reinragen 2041 aStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 2042 // aEndStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 2043 // EndStack erstmal nicht zwangs-Schliessen, damit Bookmarks ueber 2044 // Fussnoten im PMW uebernommen werden 2045 2046 SwFmtFtn aFtn; 2047 GetDoc().InsertPoolItem(*pPaM, aFtn, 0); 2048 ASSERT(pSavedPos == NULL, "SwFltShell"); 2049 pSavedPos = new SwPosition(*pPaM->GetPoint()); 2050 pPaM->Move(fnMoveBackward, fnGoCntnt); 2051 SwTxtNode* pTxt = pPaM->GetNode()->GetTxtNode(); 2052 SwTxtAttr *const pFN = pTxt->GetTxtAttrForCharAt( 2053 pPaM->GetPoint()->nContent.GetIndex(), RES_TXTATR_FTN); 2054 if( !pFN ){ // Passiert z.B. bei Fussnote in Fly 2055 ASSERT(pFN, "Probleme beim Anlegen des Fussnoten-Textes"); 2056 return; 2057 } 2058 const SwNodeIndex* pStartIndex = ((SwTxtFtn*)pFN)->GetStartNode(); 2059 ASSERT(pStartIndex, "Probleme beim Anlegen des Fussnoten-Textes"); 2060 pPaM->GetPoint()->nNode = pStartIndex->GetIndex() + 1; 2061 pPaM->GetPoint()->nContent.Assign(pPaM->GetCntntNode(), 0); 2062 eSubMode = Footnote; 2063 } 2064 2065 void SwFltShell::EndFootnote() 2066 { 2067 if(!pSavedPos) 2068 return; 2069 // Alle Attribute schliessen, da sonst Attribute 2070 // entstehen koennen, die aus Fussnoten rausragen 2071 aStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 2072 // aEndStack.SetAttr( *pPaM->GetPoint(), 0, sal_False ); 2073 // EndStack erstmal nicht zwangs-Schliessen, damit Bookmarks ueber 2074 // Fussnoten im PMW uebernommen werden 2075 2076 *pPaM->GetPoint() = *pSavedPos; // restore Cursor 2077 delete pSavedPos; 2078 pSavedPos = 0; 2079 } 2080 2081 void SwFltShell::BeginHeader(SwPageDesc* /*pPD*/) 2082 { 2083 SwFrmFmt* pFmt = &pCurrentPageDesc->GetMaster( 2084 ); //(bUseLeft) ? &pCurrentPageDesc->GetLeft() : 2085 SwFrmFmt* pHdFtFmt; 2086 pFmt->SetFmtAttr(SwFmtHeader(sal_True)); 2087 pHdFtFmt = (SwFrmFmt*)pFmt->GetHeader().GetHeaderFmt(); 2088 const SwNodeIndex* pStartIndex = pHdFtFmt->GetCntnt().GetCntntIdx(); 2089 if (!pStartIndex) 2090 return; 2091 ASSERT(pSavedPos == NULL, "SwFltShell"); 2092 pSavedPos = new SwPosition(*pPaM->GetPoint()); 2093 pPaM->GetPoint()->nNode = pStartIndex->GetIndex() + 1; 2094 pPaM->GetPoint()->nContent.Assign(pPaM->GetCntntNode(), 0); 2095 eSubMode = Header; 2096 } 2097 2098 void SwFltShell::BeginFooter(SwPageDesc* /*pPD*/) 2099 { 2100 SwFrmFmt* pFmt = &pCurrentPageDesc->GetMaster( 2101 ); //(bUseLeft) ? &pCurrentPageDesc->GetLeft() : 2102 SwFrmFmt* pHdFtFmt; 2103 pFmt->SetFmtAttr(SwFmtFooter(sal_True)); 2104 pHdFtFmt = (SwFrmFmt*)pFmt->GetFooter().GetFooterFmt(); 2105 const SwNodeIndex* pStartIndex = pHdFtFmt->GetCntnt().GetCntntIdx(); 2106 if (!pStartIndex) 2107 return; 2108 ASSERT(pSavedPos == NULL, "SwFltShell"); 2109 pSavedPos = new SwPosition(*pPaM->GetPoint()); 2110 pPaM->GetPoint()->nNode = pStartIndex->GetIndex() + 1; 2111 pPaM->GetPoint()->nContent.Assign(pPaM->GetCntntNode(), 0); 2112 eSubMode = Footer; 2113 } 2114 2115 void SwFltShell::EndHeaderFooter() 2116 { 2117 *pPaM->GetPoint() = *pSavedPos; // restore Cursor 2118 delete pSavedPos; 2119 pSavedPos = 0; 2120 } 2121 2122 SwPageDesc* SwFltShell::MakePageDesc(SwPageDesc* pFirstPageDesc) 2123 { 2124 if(bStdPD) // keine Neuen PageDescs 2125 return pCurrentPageDesc; 2126 2127 sal_Bool bFollow = (pFirstPageDesc != 0); 2128 SwPageDesc* pNewPD; 2129 sal_uInt16 nPos; 2130 if (bFollow && pFirstPageDesc->GetFollow() != pFirstPageDesc) 2131 return pFirstPageDesc; // Fehler: hat schon Follow 2132 // Erkennung doppelter Namen fehlt noch (Wahrscheinlichkeit 2133 // fuer dopp. Namen ist gering) 2134 2135 nPos = GetDoc().MakePageDesc( ViewShell::GetShellRes()->GetPageDescName( 2136 GetDoc().GetPageDescCnt(), sal_False, bFollow ), 2137 pFirstPageDesc, sal_False ); 2138 2139 pNewPD = &((SwPageDesc&)const_cast<const SwDoc &>(GetDoc()). 2140 GetPageDesc(nPos)); 2141 if (bFollow) 2142 { // Dieser ist der folgende von pPageDesc 2143 pFirstPageDesc->SetFollow(pNewPD); 2144 pNewPD->SetFollow(pNewPD); 2145 } 2146 else 2147 { 2148 GetDoc().InsertPoolItem( *pPaM, SwFmtPageDesc( pNewPD ), 0 ); 2149 } 2150 pNewPD->WriteUseOn( // alle Seiten 2151 (UseOnPage)(nsUseOnPage::PD_ALL | nsUseOnPage::PD_HEADERSHARE | nsUseOnPage::PD_FOOTERSHARE)); 2152 return pNewPD; 2153 } 2154 2155 ///////////////////////////////////////////////// SwFltFormatCollection 2156 SwFltFormatCollection::SwFltFormatCollection( 2157 SwDoc& _rDoc, RES_POOL_COLLFMT_TYPE nType ) : 2158 SwFltOutBase(_rDoc), 2159 pColl(_rDoc.GetTxtCollFromPool( static_cast< sal_uInt16 >(nType), false )), 2160 pFlyAttrs( 0 ), 2161 bHasFly( sal_False ) 2162 { 2163 Reset(); // Default-Attrs loeschen und Auto-Flag 2164 } 2165 2166 SwFltFormatCollection::SwFltFormatCollection( 2167 SwDoc& _rDoc, const String& rName ) : 2168 SwFltOutBase(_rDoc), 2169 pFlyAttrs( 0 ), 2170 bHasFly( sal_False ) 2171 { 2172 pColl = _rDoc.MakeTxtFmtColl(rName, (SwTxtFmtColl*)_rDoc.GetDfltTxtFmtColl()); 2173 Reset(); // Default-Attrs loeschen und Auto-Flag 2174 } 2175 2176 void SwFltShell::NextStyle(sal_uInt16 nWhich, sal_uInt16 nNext) 2177 { 2178 ASSERT(pColls[nWhich], "Next style for noexistent style" ); 2179 ASSERT(pColls[nNext], "Next style to noexistent style" ); 2180 if( pColls[nWhich] && pColls[nNext] ) 2181 pColls[nWhich]->GetColl()->SetNextTxtFmtColl( 2182 *pColls[nNext]->GetColl() ); 2183 } 2184 2185 // UpdatePageDescs muss am Ende des Einlesevorganges aufgerufen werden, damit 2186 // der Writer den Inhalt der Pagedescs wirklich akzeptiert 2187 void UpdatePageDescs(SwDoc &rDoc, sal_uInt16 nInPageDescOffset) 2188 { 2189 // Pagedescriptoren am Dokument updaten (nur so werden auch die 2190 // linken Seiten usw. eingestellt). 2191 2192 // PageDesc "Standard" 2193 rDoc.ChgPageDesc(0, const_cast<const SwDoc &>(rDoc).GetPageDesc(0)); 2194 2195 // PageDescs "Konvert..." 2196 for (sal_uInt16 i = nInPageDescOffset; i < rDoc.GetPageDescCnt(); ++i) 2197 rDoc.ChgPageDesc(i, const_cast<const SwDoc &>(rDoc).GetPageDesc(i)); 2198 } 2199 2200 /* vi:set tabstop=4 shiftwidth=4 expandtab: */ 2201