update credits
[LibreOffice.git] / sw / source / core / doc / docnum.cxx
blob038f01eea263b391fe7918731122978df85f17fe
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <hintids.hxx>
22 #include <rtl/random.h>
23 #include <tools/resid.hxx>
24 #include <editeng/lrspitem.hxx>
25 #include <ftninfo.hxx>
26 #include <ftnidx.hxx>
27 #include <doc.hxx>
28 #include <IDocumentUndoRedo.hxx>
29 #include <pam.hxx>
30 #include <ndtxt.hxx>
31 #include <doctxm.hxx> // pTOXBaseRing
32 #include <poolfmt.hxx>
33 #include <UndoCore.hxx>
34 #include <UndoRedline.hxx>
35 #include <UndoNumbering.hxx>
36 #include <swundo.hxx>
37 #include <SwUndoFmt.hxx>
38 #include <rolbck.hxx>
39 #include <paratr.hxx>
40 #include <docary.hxx>
41 #include <mvsave.hxx>
42 #include <txtfrm.hxx>
43 #include <pamtyp.hxx>
44 #include <redline.hxx>
45 #include <comcore.hrc>
46 #include <editeng/adjustitem.hxx>
47 #include <editeng/frmdiritem.hxx>
48 #include <frmatr.hxx>
49 #include <SwStyleNameMapper.hxx>
50 #include <SwNodeNum.hxx>
51 #include <list.hxx>
52 #include <listfunc.hxx>
53 #include <switerator.hxx>
54 #include <comphelper/string.hxx>
56 #include <map>
58 namespace {
59 static void lcl_ResetIndentAttrs(SwDoc *pDoc, const SwPaM &rPam, sal_uInt16 marker )
61 std::set<sal_uInt16> aResetAttrsArray;
62 aResetAttrsArray.insert( marker );
63 // #i114929#
64 // On a selection setup a corresponding Point-and-Mark in order to get
65 // the indentation attribute reset on all paragraphs touched by the selection
66 if ( rPam.HasMark() &&
67 rPam.End()->nNode.GetNode().GetTxtNode() )
69 SwPaM aPam( rPam.Start()->nNode,
70 rPam.End()->nNode );
71 aPam.Start()->nContent = 0;
72 aPam.End()->nContent = rPam.End()->nNode.GetNode().GetTxtNode()->Len();
73 pDoc->ResetAttrs( aPam, false, aResetAttrsArray );
75 else
77 pDoc->ResetAttrs( rPam, false, aResetAttrsArray );
82 #include <stdlib.h>
85 inline sal_uInt8 GetUpperLvlChg( sal_uInt8 nCurLvl, sal_uInt8 nLevel, sal_uInt16 nMask )
87 if( 1 < nLevel )
89 if( nCurLvl + 1 >= nLevel )
90 nCurLvl -= nLevel - 1;
91 else
92 nCurLvl = 0;
94 return static_cast<sal_uInt8>((nMask - 1) & ~(( 1 << nCurLvl ) - 1));
97 void SwDoc::SetOutlineNumRule( const SwNumRule& rRule )
99 if( mpOutlineRule )
100 (*mpOutlineRule) = rRule;
101 else
103 mpOutlineRule = new SwNumRule( rRule );
105 AddNumRule(mpOutlineRule); // #i36749#
108 mpOutlineRule->SetRuleType( OUTLINE_RULE );
109 mpOutlineRule->SetName( OUString::createFromAscii(
110 SwNumRule::GetOutlineRuleName() ),
111 *this);
113 // assure that the outline numbering rule is an automatic rule
114 mpOutlineRule->SetAutoRule( sal_True );
116 // test whether the optional CharFormats are defined in this Document
117 mpOutlineRule->CheckCharFmts( this );
119 // notify text nodes, which are registered at the outline style, about the
120 // changed outline style
121 SwNumRule::tTxtNodeList aTxtNodeList;
122 mpOutlineRule->GetTxtNodeList( aTxtNodeList );
123 for ( SwNumRule::tTxtNodeList::iterator aIter = aTxtNodeList.begin();
124 aIter != aTxtNodeList.end(); ++aIter )
126 SwTxtNode* pTxtNd = *aIter;
127 pTxtNd->NumRuleChgd();
129 // assure that list level corresponds to outline level
130 if ( pTxtNd->GetTxtColl()->IsAssignedToListLevelOfOutlineStyle() &&
131 pTxtNd->GetAttrListLevel() != pTxtNd->GetTxtColl()->GetAssignedOutlineStyleLevel() )
133 pTxtNd->SetAttrListLevel( pTxtNd->GetTxtColl()->GetAssignedOutlineStyleLevel() );
137 PropagateOutlineRule();
138 mpOutlineRule->SetInvalidRule(sal_True);
139 UpdateNumRule();
141 // update if we have foot notes && numbering by chapter
142 if( !GetFtnIdxs().empty() && FTNNUM_CHAPTER == GetFtnInfo().eNum )
143 GetFtnIdxs().UpdateAllFtn();
145 UpdateExpFlds(NULL, true);
147 SetModified();
150 void SwDoc::PropagateOutlineRule()
152 for (sal_uInt16 n = 0; n < mpTxtFmtCollTbl->size(); n++)
154 SwTxtFmtColl *pColl = (*mpTxtFmtCollTbl)[n];
156 if(pColl->IsAssignedToListLevelOfOutlineStyle())//<-end,zhaojianwei
158 // Check only the list style, which is set at the paragraph style
159 const SwNumRuleItem & rCollRuleItem = pColl->GetNumRule( sal_False );
161 // Check on document setting OUTLINE_LEVEL_YIELDS_OUTLINE_RULE no longer needed.
162 if ( rCollRuleItem.GetValue().isEmpty() )
164 SwNumRule * pMyOutlineRule = GetOutlineNumRule();
166 if (pMyOutlineRule)
168 SwNumRuleItem aNumItem( pMyOutlineRule->GetName() );
170 pColl->SetFmtAttr(aNumItem);
177 // Increase/Decrease
178 bool SwDoc::OutlineUpDown( const SwPaM& rPam, short nOffset )
180 if( GetNodes().GetOutLineNds().empty() || !nOffset )
181 return false;
183 // calculate the range
184 const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
185 const SwNodePtr pSttNd = (SwNodePtr)&rPam.Start()->nNode.GetNode();
186 const SwNodePtr pEndNd = (SwNodePtr)&rPam.End()->nNode.GetNode();
187 sal_uInt16 nSttPos, nEndPos;
189 if( !rOutlNds.Seek_Entry( pSttNd, &nSttPos ) &&
190 !nSttPos-- )
191 // we're not in an "Outline section"
192 return false;
194 if( rOutlNds.Seek_Entry( pEndNd, &nEndPos ) )
195 ++nEndPos;
197 // We now have the wanted range in the OutlineNodes array,
198 // so check now if we're not invalidating sublevels
199 // (stepping over the limits)
200 sal_uInt16 n;
202 // Here we go:
203 // 1. Create the style array:
204 SwTxtFmtColl* aCollArr[ MAXLEVEL ];
205 memset( aCollArr, 0, sizeof( SwTxtFmtColl* ) * MAXLEVEL );
207 for( n = 0; n < mpTxtFmtCollTbl->size(); ++n )
209 if((*mpTxtFmtCollTbl)[ n ]->IsAssignedToListLevelOfOutlineStyle())
211 const int nLevel = (*mpTxtFmtCollTbl)[ n ]->GetAssignedOutlineStyleLevel();
212 aCollArr[ nLevel ] = (*mpTxtFmtCollTbl)[ n ];
213 }//<-end,zhaojianwei
216 /* Find the last occupied level (backward). */
217 for (n = MAXLEVEL - 1; n > 0; n--)
219 if (aCollArr[n] != 0)
220 break;
223 /* If an occupied level is found, choose next level (which IS
224 unoccupied) until a valid level is found. If no occupied level
225 was found n is 0 and aCollArr[0] is 0. In this case no demoting
226 is possible. */
227 if (aCollArr[n] != 0)
229 while (n < MAXLEVEL - 1)
231 n++;
233 SwTxtFmtColl *aTmpColl =
234 GetTxtCollFromPool(static_cast<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + n));
236 if( aTmpColl->IsAssignedToListLevelOfOutlineStyle() &&
237 aTmpColl->GetAssignedOutlineStyleLevel() == n )//<-end,zhaojianwei
239 aCollArr[n] = aTmpColl;
240 break;
245 /* Find the first occupied level (forward). */
246 for (n = 0; n < MAXLEVEL - 1; n++)
248 if (aCollArr[n] != 0)
249 break;
252 /* If an occupied level is found, choose previous level (which IS
253 unoccupied) until a valid level is found. If no occupied level
254 was found n is MAXLEVEL - 1 and aCollArr[MAXLEVEL - 1] is 0. In
255 this case no demoting is possible. */
256 if (aCollArr[n] != 0)
258 while (n > 0)
260 n--;
262 SwTxtFmtColl *aTmpColl =
263 GetTxtCollFromPool(static_cast<sal_uInt16>(RES_POOLCOLL_HEADLINE1 + n));
265 //if (aTmpColl->GetOutlineLevel() == n)//#outline level,zhaojianwei
266 if( aTmpColl->IsAssignedToListLevelOfOutlineStyle() &&
267 aTmpColl->GetAssignedOutlineStyleLevel() == n )//<-end,zhaojianwei
269 aCollArr[n] = aTmpColl;
270 break;
275 /* --> #i13747#
277 Build a move table that states from which level to which other level
278 an outline will be moved.
280 the move table:
281 aMoveArr[n] = m: replace aCollArr[n] with aCollArr[m]
283 int aMoveArr[MAXLEVEL];
284 int nStep; // step size for searching in aCollArr: -1 or 1
285 int nNum; // amount of steps for stepping in aCollArr
287 if (nOffset < 0)
289 nStep = -1;
290 nNum = -nOffset;
292 else
294 nStep = 1;
295 nNum = nOffset;
298 /* traverse aCollArr */
299 for (n = 0; n < MAXLEVEL; n++)
301 /* If outline level n has an assigned paragraph style step
302 nNum steps forwards (nStep == 1) or backwards (nStep ==
303 -1). One step is to go to the next non-null entry in
304 aCollArr in the selected direction. If nNum steps were
305 possible write the index of the entry found to aCollArr[n],
306 i.e. outline level n will be replaced by outline level
307 aCollArr[n].
309 If outline level n has no assigned paragraph style
310 aMoveArr[n] is set to -1.
312 if (aCollArr[n] != NULL)
314 sal_uInt16 m = n;
315 int nCount = nNum;
317 while (nCount > 0 && m + nStep >= 0 && m + nStep < MAXLEVEL)
319 m = static_cast<sal_uInt16>(m + nStep);
321 if (aCollArr[m] != NULL)
322 nCount--;
325 if (nCount == 0)
326 aMoveArr[n] = m;
327 else
328 aMoveArr[n] = -1;
331 else
332 aMoveArr[n] = -1;
335 /* If moving of the outline levels is applicable, i.e. for all
336 outline levels occuring in the document there has to be a valid
337 target outline level implied by aMoveArr. */
338 bool bMoveApplicable = true;
339 for (n = nSttPos; n < nEndPos; n++)
341 SwTxtNode* pTxtNd = rOutlNds[ n ]->GetTxtNode();
342 SwTxtFmtColl* pColl = pTxtNd->GetTxtColl();
344 if( pColl->IsAssignedToListLevelOfOutlineStyle() )
346 const int nLevel = pColl->GetAssignedOutlineStyleLevel();
347 if (aMoveArr[nLevel] == -1)
348 bMoveApplicable = false;
349 }//<-end,zhaojianwei
351 // Check on outline level attribute of text node, if text node is
352 // not an outline via a to outline style assigned paragraph style.
353 else
355 const int nNewOutlineLevel = pTxtNd->GetAttrOutlineLevel() + nOffset;
356 if ( nNewOutlineLevel < 1 || nNewOutlineLevel > MAXLEVEL )
358 bMoveApplicable = false;
363 if (! bMoveApplicable )
364 return false;
366 if (GetIDocumentUndoRedo().DoesUndo())
368 GetIDocumentUndoRedo().StartUndo(UNDO_OUTLINE_LR, NULL);
369 SwUndo *const pUndoOLR( new SwUndoOutlineLeftRight( rPam, nOffset ) );
370 GetIDocumentUndoRedo().AppendUndo(pUndoOLR);
373 // 2. Apply the new style to all Nodes
375 n = nSttPos;
376 while( n < nEndPos)
378 SwTxtNode* pTxtNd = rOutlNds[ n ]->GetTxtNode();
379 SwTxtFmtColl* pColl = pTxtNd->GetTxtColl();
381 if( pColl->IsAssignedToListLevelOfOutlineStyle() )
383 const int nLevel = pColl->GetAssignedOutlineStyleLevel();//#outline level,add by zhaojianwei
385 OSL_ENSURE(aMoveArr[nLevel] >= 0,
386 "move table: current TxtColl not found when building table!");
389 if (nLevel < MAXLEVEL && aMoveArr[nLevel] >= 0)
391 pColl = aCollArr[ aMoveArr[nLevel] ];
393 if (pColl != NULL)
394 pColl = (SwTxtFmtColl*)pTxtNd->ChgFmtColl( pColl );
398 else if( pTxtNd->GetAttrOutlineLevel() > 0) //#outline level,add by zhaojianwei
400 int nLevel = pTxtNd->GetAttrOutlineLevel() + nOffset;
401 if( 0 <= nLevel && nLevel <= MAXLEVEL)
402 pTxtNd->SetAttrOutlineLevel( nLevel );
404 }//<-end,zhaojianwei
406 n++;
407 // Undo ???
409 if (GetIDocumentUndoRedo().DoesUndo())
411 GetIDocumentUndoRedo().EndUndo(UNDO_OUTLINE_LR, NULL);
414 ChkCondColls();
415 SetModified();
417 return true;
420 // Move up/down
421 bool SwDoc::MoveOutlinePara( const SwPaM& rPam, short nOffset )
423 // Do not move to special sections in the nodes array
424 const SwPosition& rStt = *rPam.Start(),
425 & rEnd = &rStt == rPam.GetPoint() ? *rPam.GetMark()
426 : *rPam.GetPoint();
427 if( GetNodes().GetOutLineNds().empty() || !nOffset ||
428 (rStt.nNode.GetIndex() < GetNodes().GetEndOfExtras().GetIndex()) ||
429 (rEnd.nNode.GetIndex() < GetNodes().GetEndOfExtras().GetIndex()))
431 return false;
434 sal_uInt16 nAktPos = 0;
435 SwNodeIndex aSttRg( rStt.nNode ), aEndRg( rEnd.nNode );
437 int nOutLineLevel = MAXLEVEL; //<-end,zhaojianwei
438 SwNode* pSrch = &aSttRg.GetNode();
440 if( pSrch->IsTxtNode())
441 nOutLineLevel = static_cast<sal_uInt8>(((SwTxtNode*)pSrch)->GetAttrOutlineLevel()-1);//<-end,zhaojianwei
442 SwNode* pEndSrch = &aEndRg.GetNode();
443 if( !GetNodes().GetOutLineNds().Seek_Entry( pSrch, &nAktPos ) )
445 if( !nAktPos )
446 return false; // Promoting or demoting before the first outline => no.
447 if( --nAktPos )
448 aSttRg = *GetNodes().GetOutLineNds()[ nAktPos ];
449 else if( 0 > nOffset )
450 return false; // Promoting at the top of document?!
451 else
452 aSttRg = *GetNodes().GetEndOfContent().StartOfSectionNode();
454 sal_uInt16 nTmpPos = 0;
455 // If the given range ends at an outlined text node we have to decide if it has to be a part of
456 // the moving range or not. Normally it will be a sub outline of our chapter
457 // and has to be moved, too. But if the chapter ends with a table(or a section end),
458 // the next text node will be choosen and this could be the next outline of the same level.
459 // The criteria has to be the outline level: sub level => incorporate, same/higher level => no.
460 if( GetNodes().GetOutLineNds().Seek_Entry( pEndSrch, &nTmpPos ) )
462 if( !pEndSrch->IsTxtNode() || pEndSrch == pSrch ||
463 nOutLineLevel < ((SwTxtNode*)pEndSrch)->GetAttrOutlineLevel()-1 )//<-end,zhaojianwei
464 ++nTmpPos; // For sub outlines only!
467 aEndRg = nTmpPos < GetNodes().GetOutLineNds().size()
468 ? *GetNodes().GetOutLineNds()[ nTmpPos ]
469 : GetNodes().GetEndOfContent();
470 if( nOffset >= 0 )
471 nAktPos = nTmpPos;
472 if( aEndRg == aSttRg )
474 OSL_FAIL( "Moving outlines: Surprising selection" );
475 ++aEndRg;
478 const SwNode* pNd;
479 // The following code corrects the range to handle sections (start/end nodes)
480 // The range will be extended if the least node before the range is a start node
481 // which ends inside the range => The complete section will be moved.
482 // The range will be shrinked if the last position is a start node.
483 // The range will be shrinked if the last node is an end node which starts before the range.
484 aSttRg--;
485 while( aSttRg.GetNode().IsStartNode() )
487 pNd = aSttRg.GetNode().EndOfSectionNode();
488 if( pNd->GetIndex() >= aEndRg.GetIndex() )
489 break;
490 aSttRg--;
492 ++aSttRg;
494 aEndRg--;
495 while( aEndRg.GetNode().IsStartNode() )
496 aEndRg--;
497 while( aEndRg.GetNode().IsEndNode() )
499 pNd = aEndRg.GetNode().StartOfSectionNode();
500 if( pNd->GetIndex() >= aSttRg.GetIndex() )
501 break;
502 aEndRg--;
504 ++aEndRg;
506 // calculation of the new position
507 if( nOffset < 0 && nAktPos < sal_uInt16(-nOffset) )
508 pNd = GetNodes().GetEndOfContent().StartOfSectionNode();
509 else if( nAktPos + nOffset >= (sal_uInt16)GetNodes().GetOutLineNds().size() )
510 pNd = &GetNodes().GetEndOfContent();
511 else
512 pNd = GetNodes().GetOutLineNds()[ nAktPos + nOffset ];
514 sal_uLong nNewPos = pNd->GetIndex();
516 // And now a correction of the insert position if necessary...
517 SwNodeIndex aInsertPos( *pNd, -1 );
518 while( aInsertPos.GetNode().IsStartNode() )
520 // Just before the insert position starts a section:
521 // when I'm moving forward I do not want to enter the section,
522 // when I'm moving backward I want to stay in the section if I'm already a part of,
523 // I want to stay outside if I was outside before.
524 if( nOffset < 0 )
526 pNd = aInsertPos.GetNode().EndOfSectionNode();
527 if( pNd->GetIndex() >= aEndRg.GetIndex() )
528 break;
530 aInsertPos--;
531 --nNewPos;
533 if( nOffset >= 0 )
535 // When just before the insert position a section ends, it is okay when I'm moving backward
536 // because I want to stay outside the section.
537 // When moving forward I've to check if I started inside or outside the section
538 // because I don't want to enter of leave such a section
539 while( aInsertPos.GetNode().IsEndNode() )
541 pNd = aInsertPos.GetNode().StartOfSectionNode();
542 if( pNd->GetIndex() >= aSttRg.GetIndex() )
543 break;
544 aInsertPos--;
545 --nNewPos;
548 // We do not want to move into tables (at the moment)
549 ++aInsertPos;
550 pNd = &aInsertPos.GetNode();
551 if( pNd->IsTableNode() )
552 pNd = pNd->StartOfSectionNode();
553 if( pNd->FindTableNode() )
554 return false;
556 OSL_ENSURE( aSttRg.GetIndex() > nNewPos || nNewPos >= aEndRg.GetIndex(),
557 "Position lies within Move range" );
559 // If a Position inside the special nodes array sections was calculated,
560 // set it to document start instead.
561 // Sections or Tables at the document start will be pushed backwards.
562 nNewPos = std::max( nNewPos, GetNodes().GetEndOfExtras().GetIndex() + 2 );
564 long nOffs = nNewPos - ( 0 < nOffset ? aEndRg.GetIndex() : aSttRg.GetIndex());
565 SwPaM aPam( aSttRg, aEndRg, 0, -1 );
566 return MoveParagraph( aPam, nOffs, true );
569 static sal_uInt16 lcl_FindOutlineName( const SwNodes& rNds, const String& rName,
570 bool bExact )
572 sal_uInt16 nSavePos = USHRT_MAX;
573 const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
574 for( sal_uInt16 n = 0; n < rOutlNds.size(); ++n )
576 SwTxtNode* pTxtNd = rOutlNds[ n ]->GetTxtNode();
577 String sTxt( pTxtNd->GetExpandTxt() );
578 if( sTxt.Equals( rName ) )
580 // Found "exact", set Pos to the Node
581 nSavePos = n;
582 break;
584 else if( !bExact && USHRT_MAX == nSavePos &&
585 COMPARE_EQUAL == sTxt.CompareTo( rName, rName.Len()) )
587 // maybe we just found the text's first part
588 nSavePos = n;
592 return nSavePos;
595 static sal_uInt16 lcl_FindOutlineNum( const SwNodes& rNds, String& rName )
597 // Valid numbers are (always just offsets!):
598 // ([Number]+\.)+ (as a regular expression!)
599 // (Number follwed by a period, with 5 repetitions)
600 // i.e.: "1.1.", "1.", "1.1.1."
601 sal_Int32 nPos = 0;
602 String sNum = rName.GetToken( 0, '.', nPos );
603 if( -1 == nPos )
604 return USHRT_MAX; // invalid number!
606 sal_uInt16 nLevelVal[ MAXLEVEL ]; // numbers of all levels
607 memset( nLevelVal, 0, MAXLEVEL * sizeof( nLevelVal[0] ));
608 sal_uInt8 nLevel = 0;
609 String sName( rName );
611 while( -1 != nPos )
613 sal_uInt16 nVal = 0;
614 sal_Unicode c;
615 for( sal_uInt16 n = 0; n < sNum.Len(); ++n )
616 if( '0' <= ( c = sNum.GetChar( n )) && c <= '9' )
618 nVal *= 10; nVal += c - '0';
620 else if( nLevel )
621 break; // "almost" valid number
622 else
623 return USHRT_MAX; // invalid number!
625 if( MAXLEVEL > nLevel )
626 nLevelVal[ nLevel++ ] = nVal;
628 sName.Erase( 0, nPos );
629 nPos = 0;
630 sNum = sName.GetToken( 0, '.', nPos );
631 // #i4533# without this check all parts delimited by a dot are treated as outline numbers
632 if(!comphelper::string::isdigitAsciiString(sNum))
633 nPos = -1;
635 rName = sName; // that's the follow-up text
637 // read all levels, so search the document for this outline
638 const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
639 // Without OutlineNodes searching doesn't pay off
640 // and we save a crash
641 if( rOutlNds.empty() )
642 return USHRT_MAX;
643 SwTxtNode* pNd;
644 nPos = 0;
645 // search in the existing outline nodes for the required outline num array
646 for( ; nPos < (sal_Int32) rOutlNds.size(); ++nPos )
648 pNd = rOutlNds[ nPos ]->GetTxtNode();
649 const int nLvl = pNd->GetAttrOutlineLevel()-1; //<-end,zhaojianwei
650 if( nLvl == nLevel - 1)
652 // #i51089#, #i68289#
653 // Assure, that text node has the correct numbering level. Otherwise,
654 // its number vector will not fit to the searched level.
655 if ( pNd->GetNum() &&
656 pNd->GetActualListLevel() == ( nLevel - 1 ) )
658 const SwNodeNum & rNdNum = *(pNd->GetNum());
659 SwNumberTree::tNumberVector aLevelVal = rNdNum.GetNumberVector();
660 // now compare with the one searched for
661 bool bEqual = true;
662 for( sal_uInt8 n = 0; (n < nLevel) && bEqual; ++n )
664 bEqual = aLevelVal[n] == nLevelVal[n];
666 if(bEqual)
668 break;
671 else
673 // A text node, which has an outline paragraph style applied and
674 // has as hard attribute 'no numbering' set, has an outline level,
675 // but no numbering tree node. Thus, consider this situation in
676 // the assertion condition.
677 OSL_ENSURE( !pNd->GetNumRule(),
678 "<lcl_FindOutlineNum(..)> - text node with outline level and numbering rule, but without numbering tree node. This is a serious defect -> inform OD" );
682 if( nPos >= (sal_Int32) rOutlNds.size() )
683 nPos = USHRT_MAX;
684 return nPos;
687 // Add this bullet point:
689 // A Name can contain a Number and/or the Text.
691 // First, we try to find the correct Entry via the Number.
692 // If it exists, we compare the Text, to see if it's the right one.
693 // If that's not the case, we search again via the Text. If it is
694 // found, we got the right entry. Or else we use the one found by
695 // searching for the Number.
696 // If we don't have a Number, we search via the Text only.
697 bool SwDoc::GotoOutline( SwPosition& rPos, const String& rName ) const
699 if( rName.Len() )
701 const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
703 // 1. step: via the Number:
704 String sName( rName );
705 sal_uInt16 nFndPos = ::lcl_FindOutlineNum( GetNodes(), sName );
706 if( USHRT_MAX != nFndPos )
708 SwTxtNode* pNd = rOutlNds[ nFndPos ]->GetTxtNode();
709 String sExpandedText = pNd->GetExpandTxt();
710 //#i4533# leading numbers followed by a dot have been remove while
711 //searching for the outline position
712 //to compensate this they must be removed from the paragraphs text content, too
713 sal_Int32 nPos = 0;
714 String sTempNum;
715 while(sExpandedText.Len() && (sTempNum = sExpandedText.GetToken(0, '.', nPos)).Len() &&
716 -1 != nPos &&
717 comphelper::string::isdigitAsciiString(sTempNum))
719 sExpandedText.Erase(0, nPos);
720 nPos = 0;
723 if( !sExpandedText.Equals( sName ) )
725 sal_uInt16 nTmp = ::lcl_FindOutlineName( GetNodes(), sName, true );
726 if( USHRT_MAX != nTmp ) // found via the Name
728 nFndPos = nTmp;
729 pNd = rOutlNds[ nFndPos ]->GetTxtNode();
732 rPos.nNode = *pNd;
733 rPos.nContent.Assign( pNd, 0 );
734 return true;
737 nFndPos = ::lcl_FindOutlineName( GetNodes(), rName, false );
738 if( USHRT_MAX != nFndPos )
740 SwTxtNode* pNd = rOutlNds[ nFndPos ]->GetTxtNode();
741 rPos.nNode = *pNd;
742 rPos.nContent.Assign( pNd, 0 );
743 return true;
746 // #i68289# additional search on hyperlink URL without its outline numbering part
747 if ( !sName.Equals( rName ) )
749 nFndPos = ::lcl_FindOutlineName( GetNodes(), sName, false );
750 if( USHRT_MAX != nFndPos )
752 SwTxtNode* pNd = rOutlNds[ nFndPos ]->GetTxtNode();
753 rPos.nNode = *pNd;
754 rPos.nContent.Assign( pNd, 0 );
755 return true;
759 return false;
762 static void lcl_ChgNumRule( SwDoc& rDoc, const SwNumRule& rRule )
764 SwNumRule* pOld = rDoc.FindNumRulePtr( rRule.GetName() );
765 OSL_ENSURE( pOld, "we cannot proceed without the old NumRule" );
767 sal_uInt16 nChgFmtLevel = 0, nMask = 1;
768 sal_uInt8 n;
770 for( n = 0; n < MAXLEVEL; ++n, nMask <<= 1 )
772 const SwNumFmt& rOldFmt = pOld->Get( n ),
773 & rNewFmt = rRule.Get( n );
775 if( rOldFmt != rNewFmt )
777 nChgFmtLevel |= nMask;
779 else if( SVX_NUM_NUMBER_NONE > rNewFmt.GetNumberingType() && 1 < rNewFmt.GetIncludeUpperLevels() &&
780 0 != (nChgFmtLevel & GetUpperLvlChg( n, rNewFmt.GetIncludeUpperLevels(),nMask )) )
781 nChgFmtLevel |= nMask;
784 if( !nChgFmtLevel ) // Nothing has been changed?
786 const bool bInvalidateNumRule( pOld->IsContinusNum() != rRule.IsContinusNum() );
787 pOld->CheckCharFmts( &rDoc );
788 pOld->SetContinusNum( rRule.IsContinusNum() );
790 if ( bInvalidateNumRule )
792 pOld->SetInvalidRule(sal_True);
795 return ;
798 SwNumRule::tTxtNodeList aTxtNodeList;
799 pOld->GetTxtNodeList( aTxtNodeList );
800 sal_uInt8 nLvl( 0 );
801 for ( SwNumRule::tTxtNodeList::iterator aIter = aTxtNodeList.begin();
802 aIter != aTxtNodeList.end(); ++aIter )
804 SwTxtNode* pTxtNd = *aIter;
805 nLvl = static_cast<sal_uInt8>(pTxtNd->GetActualListLevel());
807 if( nLvl < MAXLEVEL )
809 if( nChgFmtLevel & ( 1 << nLvl ))
811 pTxtNd->NumRuleChgd();
816 for( n = 0; n < MAXLEVEL; ++n )
817 if( nChgFmtLevel & ( 1 << n ))
818 pOld->Set( n, rRule.GetNumFmt( n ));
820 pOld->CheckCharFmts( &rDoc );
821 pOld->SetInvalidRule(sal_True);
822 pOld->SetContinusNum( rRule.IsContinusNum() );
824 rDoc.UpdateNumRule();
827 void SwDoc::SetNumRule( const SwPaM& rPam,
828 const SwNumRule& rRule,
829 const bool bCreateNewList,
830 const String sContinuedListId,
831 bool bSetItem,
832 const bool bResetIndentAttrs )
834 SwUndoInsNum * pUndo = NULL;
835 if (GetIDocumentUndoRedo().DoesUndo())
837 // Start/End for attributes!
838 GetIDocumentUndoRedo().StartUndo( UNDO_INSNUM, NULL );
839 pUndo = new SwUndoInsNum( rPam, rRule );
840 GetIDocumentUndoRedo().AppendUndo(pUndo);
843 SwNumRule * pNew = FindNumRulePtr( rRule.GetName() );
844 bool bUpdateRule = false;
846 if( !pNew )
848 pNew = (*mpNumRuleTbl)[ MakeNumRule( rRule.GetName(), &rRule ) ];
850 else if (rRule != *pNew)
852 bUpdateRule = true;
855 if (bUpdateRule)
857 if( pUndo )
859 pUndo->SaveOldNumRule( *pNew );
860 ::lcl_ChgNumRule( *this, rRule );
861 pUndo->SetLRSpaceEndPos();
863 else
865 ::lcl_ChgNumRule( *this, rRule );
869 if ( bSetItem )
871 if ( bCreateNewList )
873 String sListId;
874 if ( !bUpdateRule )
876 // apply list id of list, which has been created for the new list style
877 sListId = pNew->GetDefaultListId();
879 else
881 // create new list and apply its list id
882 SwList* pNewList = createList( String(), pNew->GetName() );
883 OSL_ENSURE( pNewList,
884 "<SwDoc::SetNumRule(..)> - could not create new list. Serious defect -> please inform OD." );
885 sListId = pNewList->GetListId();
887 InsertPoolItem( rPam,
888 SfxStringItem( RES_PARATR_LIST_ID, sListId ), 0 );
890 else if ( sContinuedListId.Len() > 0 )
892 // apply given list id
893 InsertPoolItem( rPam,
894 SfxStringItem( RES_PARATR_LIST_ID, sContinuedListId ), 0 );
898 if ( ! rPam.HasMark())
900 SwTxtNode * pTxtNd = rPam.GetPoint()->nNode.GetNode().GetTxtNode();
901 // consider case that the PaM doesn't denote a text node - e.g. it denotes a graphic node
902 if ( pTxtNd )
904 SwNumRule * pRule = pTxtNd->GetNumRule();
906 if (pRule && pRule->GetName() == pNew->GetName())
908 bSetItem = false;
910 if ( !pTxtNd->IsInList() )
912 pTxtNd->AddToList();
915 // Only clear numbering attribute at text node, if at paragraph
916 // style the new numbering rule is found.
917 else if ( !pRule )
919 SwTxtFmtColl* pColl = pTxtNd->GetTxtColl();
920 if ( pColl )
922 SwNumRule* pCollRule = FindNumRulePtr(pColl->GetNumRule().GetValue());
923 if ( pCollRule && pCollRule->GetName() == pNew->GetName() )
925 pTxtNd->ResetAttr( RES_PARATR_NUMRULE );
926 bSetItem = false;
933 if ( bSetItem )
935 InsertPoolItem( rPam, SwNumRuleItem( pNew->GetName() ), 0 );
938 if ( bResetIndentAttrs &&
939 pNew && pNew->Get( 0 ).GetPositionAndSpaceMode() == SvxNumberFormat::LABEL_ALIGNMENT )
941 ::lcl_ResetIndentAttrs(this, rPam, RES_LR_SPACE);
944 if (GetIDocumentUndoRedo().DoesUndo())
946 GetIDocumentUndoRedo().EndUndo( UNDO_INSNUM, NULL );
949 SetModified();
952 void SwDoc::SetCounted(const SwPaM & rPam, bool bCounted)
954 if ( bCounted )
956 ::lcl_ResetIndentAttrs(this, rPam, RES_PARATR_LIST_ISCOUNTED);
958 else
960 InsertPoolItem( rPam,
961 SfxBoolItem( RES_PARATR_LIST_ISCOUNTED, sal_False ), 0 );
965 void SwDoc::SetNumRuleStart( const SwPosition& rPos, sal_Bool bFlag )
967 SwTxtNode* pTxtNd = rPos.nNode.GetNode().GetTxtNode();
969 if (pTxtNd)
971 const SwNumRule* pRule = pTxtNd->GetNumRule();
972 if( pRule && !bFlag != !pTxtNd->IsListRestart())
974 if (GetIDocumentUndoRedo().DoesUndo())
976 SwUndo *const pUndo( new SwUndoNumRuleStart(rPos, bFlag) );
977 GetIDocumentUndoRedo().AppendUndo(pUndo);
980 pTxtNd->SetListRestart(bFlag ? true : false);
982 SetModified();
987 void SwDoc::SetNodeNumStart( const SwPosition& rPos, sal_uInt16 nStt )
989 SwTxtNode* pTxtNd = rPos.nNode.GetNode().GetTxtNode();
991 if (pTxtNd)
993 if ( !pTxtNd->HasAttrListRestartValue() ||
994 pTxtNd->GetAttrListRestartValue() != nStt )
996 if (GetIDocumentUndoRedo().DoesUndo())
998 SwUndo *const pUndo( new SwUndoNumRuleStart(rPos, nStt) );
999 GetIDocumentUndoRedo().AppendUndo(pUndo);
1001 pTxtNd->SetAttrListRestartValue( nStt );
1003 SetModified();
1008 // We can only delete if the Rule is unused!
1009 bool SwDoc::DelNumRule( const String& rName, bool bBroadcast )
1011 sal_uInt16 nPos = FindNumRule( rName );
1013 if ( (*mpNumRuleTbl)[ nPos ] == GetOutlineNumRule() )
1015 OSL_FAIL( "<SwDoc::DelNumRule(..)> - No deletion of outline list style. This is serious defect - please inform OD" );
1016 return false;
1019 if( USHRT_MAX != nPos && !IsUsed( *(*mpNumRuleTbl)[ nPos ] ))
1021 if (GetIDocumentUndoRedo().DoesUndo())
1023 SwUndo * pUndo =
1024 new SwUndoNumruleDelete(*(*mpNumRuleTbl)[nPos], this);
1025 GetIDocumentUndoRedo().AppendUndo(pUndo);
1028 if (bBroadcast)
1029 BroadcastStyleOperation(rName, SFX_STYLE_FAMILY_PSEUDO,
1030 SFX_STYLESHEET_ERASED);
1032 deleteListForListStyle( rName );
1034 // delete further list, which have the deleted list style as default list style
1035 std::vector< SwList* > aListsForDeletion;
1036 tHashMapForLists::iterator aListIter = maLists.begin();
1037 while ( aListIter != maLists.end() )
1039 SwList* pList = (*aListIter).second;
1040 if ( pList->GetDefaultListStyleName() == rName )
1042 aListsForDeletion.push_back( pList );
1045 ++aListIter;
1047 while ( !aListsForDeletion.empty() )
1049 SwList* pList = aListsForDeletion.back();
1050 aListsForDeletion.pop_back();
1051 deleteList( pList->GetListId() );
1054 // #i34097# DeleteAndDestroy deletes rName if
1055 // rName is directly taken from the numrule.
1056 const String aTmpName( rName );
1057 delete (*mpNumRuleTbl)[ nPos ];
1058 mpNumRuleTbl->erase( mpNumRuleTbl->begin() + nPos );
1059 maNumRuleMap.erase(aTmpName);
1061 SetModified();
1062 return true;
1064 return false;
1067 void SwDoc::ChgNumRuleFmts( const SwNumRule& rRule, const String * pName )
1069 SwNumRule* pRule = FindNumRulePtr( pName ? *pName : rRule.GetName() );
1070 if( pRule )
1072 SwUndoInsNum* pUndo = 0;
1073 if (GetIDocumentUndoRedo().DoesUndo())
1075 pUndo = new SwUndoInsNum( *pRule, rRule );
1076 pUndo->GetHistory();
1077 GetIDocumentUndoRedo().AppendUndo( pUndo );
1079 ::lcl_ChgNumRule( *this, rRule );
1081 if( pUndo )
1082 pUndo->SetLRSpaceEndPos();
1084 SetModified();
1088 bool SwDoc::RenameNumRule(const String & rOldName, const String & rNewName,
1089 bool bBroadcast)
1091 bool bResult = false;
1092 SwNumRule * pNumRule = FindNumRulePtr(rOldName);
1094 if (pNumRule)
1096 if (GetIDocumentUndoRedo().DoesUndo())
1098 SwUndo * pUndo = new SwUndoNumruleRename(rOldName, rNewName, this);
1099 GetIDocumentUndoRedo().AppendUndo(pUndo);
1102 SwNumRule::tTxtNodeList aTxtNodeList;
1103 pNumRule->GetTxtNodeList( aTxtNodeList );
1105 pNumRule->SetName( rNewName, *this );
1107 SwNumRuleItem aItem(rNewName);
1109 for ( SwNumRule::tTxtNodeList::iterator aIter = aTxtNodeList.begin();
1110 aIter != aTxtNodeList.end(); ++aIter )
1112 SwTxtNode * pTxtNd = *aIter;
1113 pTxtNd->SetAttr(aItem);
1116 bResult = true;
1118 if (bBroadcast)
1119 BroadcastStyleOperation(rOldName, SFX_STYLE_FAMILY_PSEUDO,
1120 SFX_STYLESHEET_MODIFIED);
1123 return bResult;
1126 void SwDoc::StopNumRuleAnimations( OutputDevice* pOut )
1128 for( sal_uInt16 n = GetNumRuleTbl().size(); n; )
1130 SwNumRule::tTxtNodeList aTxtNodeList;
1131 GetNumRuleTbl()[ --n ]->GetTxtNodeList( aTxtNodeList );
1132 for ( SwNumRule::tTxtNodeList::iterator aTxtNodeIter = aTxtNodeList.begin();
1133 aTxtNodeIter != aTxtNodeList.end(); ++aTxtNodeIter )
1135 SwTxtNode* pTNd = *aTxtNodeIter;
1136 SwIterator<SwTxtFrm,SwTxtNode> aIter(*pTNd);
1137 for(SwTxtFrm* pFrm = aIter.First(); pFrm; pFrm = aIter.Next() )
1138 if( pFrm->HasAnimation() )
1139 pFrm->StopAnimation( pOut );
1144 bool SwDoc::ReplaceNumRule( const SwPosition& rPos,
1145 const String& rOldRule, const String& rNewRule )
1147 bool bRet = false;
1148 SwNumRule *pOldRule = FindNumRulePtr( rOldRule ),
1149 *pNewRule = FindNumRulePtr( rNewRule );
1150 if( pOldRule && pNewRule && pOldRule != pNewRule )
1152 SwUndoInsNum* pUndo = 0;
1153 if (GetIDocumentUndoRedo().DoesUndo())
1155 // Start/End for attributes!
1156 GetIDocumentUndoRedo().StartUndo( UNDO_START, NULL );
1157 pUndo = new SwUndoInsNum( rPos, *pNewRule, rOldRule );
1158 GetIDocumentUndoRedo().AppendUndo(pUndo);
1161 SwNumRule::tTxtNodeList aTxtNodeList;
1162 pOldRule->GetTxtNodeList( aTxtNodeList );
1163 if ( aTxtNodeList.size() > 0 )
1166 SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : 0 );
1167 sal_uInt16 nChgFmtLevel = 0;
1168 for( sal_uInt8 n = 0; n < MAXLEVEL; ++n )
1170 const SwNumFmt& rOldFmt = pOldRule->Get( n ),
1171 & rNewFmt = pNewRule->Get( n );
1173 if( rOldFmt.GetAbsLSpace() != rNewFmt.GetAbsLSpace() ||
1174 rOldFmt.GetFirstLineOffset() != rNewFmt.GetFirstLineOffset() )
1175 nChgFmtLevel |= ( 1 << n );
1178 const SwTxtNode* pGivenTxtNode = rPos.nNode.GetNode().GetTxtNode();
1179 SwNumRuleItem aRule( rNewRule );
1181 for ( SwNumRule::tTxtNodeList::iterator aIter = aTxtNodeList.begin();
1182 aIter != aTxtNodeList.end(); ++aIter )
1184 SwTxtNode* pTxtNd = *aIter;
1186 if ( pGivenTxtNode &&
1187 pGivenTxtNode->GetListId() == pTxtNd->GetListId() )
1189 aRegH.RegisterInModify( pTxtNd, *pTxtNd );
1191 pTxtNd->SetAttr( aRule );
1192 pTxtNd->NumRuleChgd();
1195 GetIDocumentUndoRedo().EndUndo( UNDO_END, NULL );
1196 SetModified();
1198 bRet = true;
1202 return bRet;
1205 namespace
1207 struct ListStyleData
1209 SwNumRule* pReplaceNumRule;
1210 bool bCreateNewList;
1211 String sListId;
1213 ListStyleData()
1214 : pReplaceNumRule( 0 ),
1215 bCreateNewList( false ),
1216 sListId()
1221 void SwDoc::MakeUniqueNumRules(const SwPaM & rPaM)
1223 OSL_ENSURE( rPaM.GetDoc() == this, "need same doc" );
1225 ::std::map<SwNumRule *, ListStyleData> aMyNumRuleMap;
1227 sal_uLong nStt = rPaM.Start()->nNode.GetIndex();
1228 sal_uLong nEnd = rPaM.End()->nNode.GetIndex();
1230 bool bFirst = true;
1232 for (sal_uLong n = nStt; n <= nEnd; n++)
1234 SwTxtNode * pCNd = GetNodes()[n]->GetTxtNode();
1236 if (pCNd)
1238 SwNumRule * pRule = pCNd->GetNumRule();
1240 if (pRule && pRule->IsAutoRule() && ! pRule->IsOutlineRule())
1242 ListStyleData aListStyleData = aMyNumRuleMap[pRule];
1244 if ( aListStyleData.pReplaceNumRule == 0 )
1246 if (bFirst)
1248 SwPosition aPos(*pCNd);
1249 aListStyleData.pReplaceNumRule =
1250 const_cast<SwNumRule *>
1251 (SearchNumRule( aPos, false, pCNd->HasNumber(),
1252 false, 0,
1253 aListStyleData.sListId, true ));
1256 if ( aListStyleData.pReplaceNumRule == 0 )
1258 aListStyleData.pReplaceNumRule = new SwNumRule(*pRule);
1260 aListStyleData.pReplaceNumRule->SetName(
1261 GetUniqueNumRuleName(), *this );
1263 aListStyleData.bCreateNewList = true;
1266 aMyNumRuleMap[pRule] = aListStyleData;
1269 SwPaM aPam(*pCNd);
1271 SetNumRule( aPam, *aListStyleData.pReplaceNumRule,
1272 aListStyleData.bCreateNewList,
1273 aListStyleData.sListId );
1274 if ( aListStyleData.bCreateNewList )
1276 aListStyleData.bCreateNewList = false;
1277 aListStyleData.sListId = pCNd->GetListId();
1278 aMyNumRuleMap[pRule] = aListStyleData;
1281 bFirst = false;
1287 bool SwDoc::NoNum( const SwPaM& rPam )
1290 bool bRet = SplitNode( *rPam.GetPoint(), false );
1291 // Do we actually use Numbering at all?
1292 if( bRet )
1294 // Set NoNum and Upate
1295 const SwNodeIndex& rIdx = rPam.GetPoint()->nNode;
1296 SwTxtNode* pNd = rIdx.GetNode().GetTxtNode();
1297 const SwNumRule* pRule = pNd->GetNumRule();
1298 if( pRule )
1300 pNd->SetCountedInList(false);
1302 SetModified();
1304 else
1305 bRet = false; // no Numbering or just always sal_True?
1307 return bRet;
1310 void SwDoc::DelNumRules( const SwPaM& rPam )
1312 sal_uLong nStt = rPam.GetPoint()->nNode.GetIndex(),
1313 nEnd = rPam.GetMark()->nNode.GetIndex();
1314 if( nStt > nEnd )
1316 sal_uLong nTmp = nStt; nStt = nEnd; nEnd = nTmp;
1319 SwUndoDelNum* pUndo;
1320 if (GetIDocumentUndoRedo().DoesUndo())
1322 pUndo = new SwUndoDelNum( rPam );
1323 GetIDocumentUndoRedo().AppendUndo(pUndo);
1325 else
1326 pUndo = 0;
1328 SwRegHistory aRegH( pUndo ? pUndo->GetHistory() : 0 );
1330 SwNumRuleItem aEmptyRule( aEmptyStr );
1331 const SwNode* pOutlNd = 0;
1332 for( ; nStt <= nEnd; ++nStt )
1334 SwTxtNode* pTNd = GetNodes()[ nStt ]->GetTxtNode();
1335 SwNumRule* pNumRuleOfTxtNode = pTNd ? pTNd->GetNumRule() : 0;
1336 if ( pTNd && pNumRuleOfTxtNode )
1338 // recognize changes of attribute for undo
1339 aRegH.RegisterInModify( pTNd, *pTNd );
1341 if( pUndo )
1342 pUndo->AddNode( *pTNd, sal_False );
1344 // directly set list style attribute is reset, otherwise empty
1345 // list style is applied
1346 const SfxItemSet* pAttrSet = pTNd->GetpSwAttrSet();
1347 if ( pAttrSet &&
1348 pAttrSet->GetItemState( RES_PARATR_NUMRULE, sal_False ) == SFX_ITEM_SET )
1349 pTNd->ResetAttr( RES_PARATR_NUMRULE );
1350 else
1351 pTNd->SetAttr( aEmptyRule );
1353 pTNd->ResetAttr( RES_PARATR_LIST_ID );
1354 pTNd->ResetAttr( RES_PARATR_LIST_LEVEL );
1355 pTNd->ResetAttr( RES_PARATR_LIST_ISRESTART );
1356 pTNd->ResetAttr( RES_PARATR_LIST_RESTARTVALUE );
1357 pTNd->ResetAttr( RES_PARATR_LIST_ISCOUNTED );
1359 if( RES_CONDTXTFMTCOLL == pTNd->GetFmtColl()->Which() )
1361 pTNd->ChkCondColl();
1363 else if( !pOutlNd &&
1364 static_cast<SwTxtFmtColl*>(pTNd->GetFmtColl())->IsAssignedToListLevelOfOutlineStyle() )//<-end,zhaojianwei
1366 pOutlNd = pTNd;
1371 // Finally, update all
1372 UpdateNumRule();
1374 if( pOutlNd )
1375 GetNodes().UpdtOutlineIdx( *pOutlNd );
1378 void SwDoc::InvalidateNumRules()
1380 for (sal_uInt16 n = 0; n < mpNumRuleTbl->size(); ++n)
1381 (*mpNumRuleTbl)[n]->SetInvalidRule(sal_True);
1384 // To the next/preceding Bullet at the same Level
1385 static bool lcl_IsNumOk( sal_uInt8 nSrchNum, sal_uInt8& rLower, sal_uInt8& rUpper,
1386 bool bOverUpper, sal_uInt8 nNumber )
1388 OSL_ENSURE( nNumber < MAXLEVEL,
1389 "<lcl_IsNumOk(..)> - misusage of method" );
1391 bool bRet = false;
1393 if( bOverUpper ? nSrchNum == nNumber : nSrchNum >= nNumber )
1394 bRet = true;
1395 else if( nNumber > rLower )
1396 rLower = nNumber;
1397 else if( nNumber < rUpper )
1398 rUpper = nNumber;
1400 return bRet;
1403 static bool lcl_IsValidPrevNextNumNode( const SwNodeIndex& rIdx )
1405 bool bRet = false;
1406 const SwNode& rNd = rIdx.GetNode();
1407 switch( rNd.GetNodeType() )
1409 case ND_ENDNODE:
1410 bRet = SwTableBoxStartNode == rNd.StartOfSectionNode()->GetStartNodeType() ||
1411 rNd.StartOfSectionNode()->IsSectionNode();
1412 break;
1414 case ND_STARTNODE:
1415 bRet = SwTableBoxStartNode == ((SwStartNode&)rNd).GetStartNodeType();
1416 break;
1418 case ND_SECTIONNODE: // that one's valid, so proceed
1419 bRet = true;
1420 break;
1422 return bRet;
1425 static bool lcl_GotoNextPrevNum( SwPosition& rPos, bool bNext,
1426 bool bOverUpper, sal_uInt8* pUpper, sal_uInt8* pLower )
1428 const SwTxtNode* pNd = rPos.nNode.GetNode().GetTxtNode();
1429 const SwNumRule* pRule;
1430 if( !pNd || 0 == ( pRule = pNd->GetNumRule()))
1431 return false;
1433 sal_uInt8 nSrchNum = static_cast<sal_uInt8>(pNd->GetActualListLevel());
1435 SwNodeIndex aIdx( rPos.nNode );
1436 if( ! pNd->IsCountedInList() )
1438 // If NO_NUMLEVEL is switched on, we search the preceding Node with Numbering
1439 bool bError = false;
1440 do {
1441 aIdx--;
1442 if( aIdx.GetNode().IsTxtNode() )
1444 pNd = aIdx.GetNode().GetTxtNode();
1445 pRule = pNd->GetNumRule();
1447 sal_uInt8 nTmpNum;
1449 if( pRule )
1451 nTmpNum = static_cast<sal_uInt8>(pNd->GetActualListLevel());
1452 if( !( ! pNd->IsCountedInList() &&
1453 (nTmpNum >= nSrchNum )) )
1454 break; // found it!
1456 else
1457 bError = true;
1459 else
1460 bError = !lcl_IsValidPrevNextNumNode( aIdx );
1462 } while( !bError );
1463 if( bError )
1464 return false;
1467 sal_uInt8 nLower = nSrchNum, nUpper = nSrchNum;
1468 bool bRet = false;
1470 const SwTxtNode* pLast;
1471 if( bNext )
1472 aIdx++, pLast = pNd;
1473 else
1474 aIdx--, pLast = 0;
1476 while( bNext ? ( aIdx.GetIndex() < aIdx.GetNodes().Count() - 1 )
1477 : aIdx.GetIndex() )
1479 if( aIdx.GetNode().IsTxtNode() )
1481 pNd = aIdx.GetNode().GetTxtNode();
1482 pRule = pNd->GetNumRule();
1483 if( pRule )
1485 if( ::lcl_IsNumOk( nSrchNum, nLower, nUpper, bOverUpper,
1486 static_cast<sal_uInt8>(pNd->GetActualListLevel()) ))
1488 rPos.nNode = aIdx;
1489 rPos.nContent.Assign( (SwTxtNode*)pNd, 0 );
1490 bRet = true;
1491 break;
1493 else
1494 pLast = pNd;
1496 else
1497 break;
1499 else if( !lcl_IsValidPrevNextNumNode( aIdx ))
1500 break;
1502 if( bNext )
1503 ++aIdx;
1504 else
1505 aIdx--;
1508 if( !bRet && !bOverUpper && pLast ) // do not iterate over higher numbers, but still to the end
1510 if( bNext )
1512 rPos.nNode = aIdx;
1513 if( aIdx.GetNode().IsCntntNode() )
1514 rPos.nContent.Assign( aIdx.GetNode().GetCntntNode(), 0 );
1516 else
1518 rPos.nNode.Assign( *pLast );
1519 rPos.nContent.Assign( (SwTxtNode*)pLast, 0 );
1521 bRet = true;
1524 if( bRet )
1526 if( pUpper )
1527 *pUpper = nUpper;
1528 if( pLower )
1529 *pLower = nLower;
1531 return bRet;
1534 bool SwDoc::GotoNextNum( SwPosition& rPos, bool bOverUpper,
1535 sal_uInt8* pUpper, sal_uInt8* pLower )
1537 return ::lcl_GotoNextPrevNum( rPos, true, bOverUpper, pUpper, pLower );
1540 const SwNumRule * SwDoc::SearchNumRule(const SwPosition & rPos,
1541 const bool bForward,
1542 const bool bNum,
1543 const bool bOutline,
1544 int nNonEmptyAllowed,
1545 String& sListId,
1546 const bool bInvestigateStartNode)
1548 const SwNumRule * pResult = NULL;
1549 SwTxtNode * pTxtNd = rPos.nNode.GetNode().GetTxtNode();
1550 SwNode * pStartFromNode = pTxtNd;
1552 if (pTxtNd)
1554 SwNodeIndex aIdx(rPos.nNode);
1556 // - the start node has also been investigated, if requested.
1557 const SwNode * pNode = NULL;
1560 if ( !bInvestigateStartNode )
1562 if (bForward)
1563 ++aIdx;
1564 else
1565 aIdx--;
1568 if (aIdx.GetNode().IsTxtNode())
1570 pTxtNd = aIdx.GetNode().GetTxtNode();
1572 const SwNumRule * pNumRule = pTxtNd->GetNumRule();
1573 if (pNumRule)
1575 if ( ( pNumRule->IsOutlineRule() == ( bOutline ? sal_True : sal_False ) ) &&
1576 ( ( bNum && pNumRule->Get(0).IsEnumeration()) ||
1577 ( !bNum && pNumRule->Get(0).IsItemize() ) ) ) // #i22362#, #i29560#
1579 pResult = pTxtNd->GetNumRule();
1580 // provide also the list id, to which the text node belongs.
1581 sListId = pTxtNd->GetListId();
1584 break;
1586 else if (pTxtNd->Len() > 0 || NULL != pTxtNd->GetNumRule())
1588 if (nNonEmptyAllowed == 0)
1589 break;
1591 nNonEmptyAllowed--;
1593 if (nNonEmptyAllowed < 0)
1594 nNonEmptyAllowed = -1;
1598 if ( bInvestigateStartNode )
1600 if (bForward)
1601 ++aIdx;
1602 else
1603 aIdx--;
1606 pNode = &aIdx.GetNode();
1608 while (!(pNode == GetNodes().DocumentSectionStartNode(pStartFromNode) ||
1609 pNode == GetNodes().DocumentSectionEndNode(pStartFromNode)));
1612 return pResult;
1616 bool SwDoc::GotoPrevNum( SwPosition& rPos, bool bOverUpper,
1617 sal_uInt8* pUpper, sal_uInt8* pLower )
1619 return ::lcl_GotoNextPrevNum( rPos, false, bOverUpper, pUpper, pLower );
1622 bool SwDoc::NumUpDown( const SwPaM& rPam, bool bDown )
1624 sal_uLong nStt = rPam.GetPoint()->nNode.GetIndex(),
1625 nEnd = rPam.GetMark()->nNode.GetIndex();
1626 if( nStt > nEnd )
1628 sal_uLong nTmp = nStt; nStt = nEnd; nEnd = nTmp;
1631 // -> outline nodes are promoted or demoted differently
1632 bool bOnlyOutline = true;
1633 bool bOnlyNonOutline = true;
1634 for (sal_uLong n = nStt; n <= nEnd; n++)
1636 SwTxtNode * pTxtNd = GetNodes()[n]->GetTxtNode();
1638 if (pTxtNd)
1640 SwNumRule * pRule = pTxtNd->GetNumRule();
1642 if (pRule)
1644 if (pRule->IsOutlineRule())
1645 bOnlyNonOutline = false;
1646 else
1647 bOnlyOutline = false;
1652 bool bRet = true;
1653 sal_Int8 nDiff = bDown ? 1 : -1;
1655 if (bOnlyOutline)
1656 bRet = OutlineUpDown(rPam, nDiff);
1657 else if (bOnlyNonOutline)
1659 /* #i24560#
1661 Only promote or demote if all selected paragraphs are
1662 promotable resp. demotable.
1665 for (sal_uLong nTmp = nStt; nTmp <= nEnd; ++nTmp)
1667 SwTxtNode* pTNd = GetNodes()[ nTmp ]->GetTxtNode();
1669 // Make code robust: consider case that the node doesn't denote a
1670 // text node.
1671 if ( pTNd )
1673 SwNumRule * pRule = pTNd->GetNumRule();
1675 if (pRule)
1677 sal_uInt8 nLevel = static_cast<sal_uInt8>(pTNd->GetActualListLevel());
1678 if( (-1 == nDiff && 0 >= nLevel) ||
1679 (1 == nDiff && MAXLEVEL - 1 <= nLevel))
1680 bRet = false;
1685 if( bRet )
1687 if (GetIDocumentUndoRedo().DoesUndo())
1689 SwUndo *const pUndo( new SwUndoNumUpDown(rPam, nDiff) );
1690 GetIDocumentUndoRedo().AppendUndo(pUndo);
1693 for(sal_uLong nTmp = nStt; nTmp <= nEnd; ++nTmp )
1695 SwTxtNode* pTNd = GetNodes()[ nTmp ]->GetTxtNode();
1697 if( pTNd)
1699 SwNumRule * pRule = pTNd->GetNumRule();
1701 if (pRule)
1703 sal_uInt8 nLevel = static_cast<sal_uInt8>(pTNd->GetActualListLevel());
1704 nLevel = nLevel + nDiff;
1706 pTNd->SetAttrListLevel(nLevel);
1711 ChkCondColls();
1712 SetModified();
1716 return bRet;
1719 bool SwDoc::MoveParagraph( const SwPaM& rPam, long nOffset, bool bIsOutlMv )
1721 const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End();
1723 sal_uLong nStIdx = pStt->nNode.GetIndex();
1724 sal_uLong nEndIdx = pEnd->nNode.GetIndex();
1726 // Here are some sophisticated checks whether the wished PaM will be moved or not.
1727 // For moving outlines (bIsOutlMv) I've already done some checks, so here are two different
1728 // checks...
1729 SwNode *pTmp1;
1730 SwNode *pTmp2;
1731 if( bIsOutlMv )
1733 // For moving chapters (outline) the following reason will deny the move:
1734 // if a start node is inside the moved range and its end node outside or vice versa.
1735 // If a start node is the first moved paragraph, its end node has to be within the moved
1736 // range, too (e.g. as last node).
1737 // If an end node is the last node of the moved range, its start node has to be a part of
1738 // the moved section, too.
1739 pTmp1 = GetNodes()[ nStIdx ];
1740 if( pTmp1->IsStartNode() )
1741 { // First is a start node
1742 pTmp2 = pTmp1->EndOfSectionNode();
1743 if( pTmp2->GetIndex() > nEndIdx )
1744 return false; // Its end node is behind the moved range
1746 pTmp1 = pTmp1->StartOfSectionNode()->EndOfSectionNode();
1747 if( pTmp1->GetIndex() <= nEndIdx )
1748 return false; // End node inside but start node before moved range => no.
1749 pTmp1 = GetNodes()[ nEndIdx ];
1750 if( pTmp1->IsEndNode() )
1751 { // The last one is an end node
1752 pTmp1 = pTmp1->StartOfSectionNode();
1753 if( pTmp1->GetIndex() < nStIdx )
1754 return false; // Its start node is before the moved range.
1756 pTmp1 = pTmp1->StartOfSectionNode();
1757 if( pTmp1->GetIndex() >= nStIdx )
1758 return false; // A start node which ends behind the moved range => no.
1761 sal_uLong nInStIdx, nInEndIdx;
1762 long nOffs = nOffset;
1763 if( nOffset > 0 )
1765 nInEndIdx = nEndIdx;
1766 nEndIdx += nOffset;
1767 ++nOffs;
1769 else
1771 // Impossible to move to negative index
1772 if( sal_uLong(abs( nOffset )) > nStIdx)
1773 return false;
1775 nInEndIdx = nStIdx - 1;
1776 nStIdx += nOffset;
1778 nInStIdx = nInEndIdx + 1;
1779 // The following paragraphs shall be swapped:
1780 // Swap [ nStIdx, nInEndIdx ] with [ nInStIdx, nEndIdx ]
1782 if( nEndIdx >= GetNodes().GetEndOfContent().GetIndex() )
1783 return false;
1785 if( !bIsOutlMv )
1786 { // And here the restrictions for moving paragraphs other than chapters (outlines)
1787 // The plan is to exchange [nStIdx,nInEndIdx] and [nStartIdx,nEndIdx]
1788 // It will checked if the both "start" nodes as well as the both "end" notes belongs to
1789 // the same start-end-section. This is more restrictive than the conditions checked above.
1790 // E.g. a paragraph will not escape from a section or be inserted to another section.
1791 pTmp1 = GetNodes()[ nStIdx ]->StartOfSectionNode();
1792 pTmp2 = GetNodes()[ nInStIdx ]->StartOfSectionNode();
1793 if( pTmp1 != pTmp2 )
1794 return false; // "start" nodes in different sections
1795 pTmp1 = GetNodes()[ nEndIdx ];
1796 bool bIsEndNode = pTmp1->IsEndNode();
1797 if( !pTmp1->IsStartNode() )
1799 pTmp1 = pTmp1->StartOfSectionNode();
1800 if( bIsEndNode ) // For end nodes the first start node is of course inside the range,
1801 pTmp1 = pTmp1->StartOfSectionNode(); // I've to check the start node of the start node.
1803 pTmp1 = pTmp1->EndOfSectionNode();
1804 pTmp2 = GetNodes()[ nInEndIdx ];
1805 if( !pTmp2->IsStartNode() )
1807 bIsEndNode = pTmp2->IsEndNode();
1808 pTmp2 = pTmp2->StartOfSectionNode();
1809 if( bIsEndNode )
1810 pTmp2 = pTmp2->StartOfSectionNode();
1812 pTmp2 = pTmp2->EndOfSectionNode();
1813 if( pTmp1 != pTmp2 )
1814 return false; // The "end" notes are in different sections
1817 // Test for Redlining - Can the Selection be moved at all, actually?
1818 if( !IsIgnoreRedline() )
1820 sal_uInt16 nRedlPos = GetRedlinePos( pStt->nNode.GetNode(), nsRedlineType_t::REDLINE_DELETE );
1821 if( USHRT_MAX != nRedlPos )
1823 SwPosition aStPos( *pStt ), aEndPos( *pEnd );
1824 aStPos.nContent = 0;
1825 SwCntntNode* pCNd = pEnd->nNode.GetNode().GetCntntNode();
1826 aEndPos.nContent = pCNd ? pCNd->Len() : 1;
1827 bool bCheckDel = true;
1829 // There is a some Redline Delete Object for the range
1830 for( ; nRedlPos < GetRedlineTbl().size(); ++nRedlPos )
1832 const SwRedline* pTmp = GetRedlineTbl()[ nRedlPos ];
1833 if( !bCheckDel || nsRedlineType_t::REDLINE_DELETE == pTmp->GetType() )
1835 const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
1836 switch( ComparePosition( *pRStt, *pREnd, aStPos, aEndPos ))
1838 case POS_COLLIDE_START:
1839 case POS_BEHIND: // Pos1 comes after Pos2
1840 nRedlPos = GetRedlineTbl().size();
1841 break;
1843 case POS_COLLIDE_END:
1844 case POS_BEFORE: // Pos1 comes before Pos2
1845 break;
1846 case POS_INSIDE: // Pos1 is completely inside Pos2
1847 // that's valid, but check all following for overlapping
1848 bCheckDel = false;
1849 break;
1851 case POS_OUTSIDE: // Pos2 is completely inside Pos1
1852 case POS_EQUAL: // Pos1 is equal to Pos2
1853 case POS_OVERLAP_BEFORE: // Pos1 overlaps Pos2 in the beginning
1854 case POS_OVERLAP_BEHIND: // Pos1 overlaps Pos2 at the end
1855 return false;
1863 // Send DataChanged before moving. We then can detect
1864 // which objects are still in the range.
1865 // After the move they could come before/after the
1866 // Position.
1867 SwDataChanged aTmp( rPam );
1870 SwNodeIndex aIdx( nOffset > 0 ? pEnd->nNode : pStt->nNode, nOffs );
1871 SwNodeRange aMvRg( pStt->nNode, 0, pEnd->nNode, +1 );
1873 SwRedline* pOwnRedl = 0;
1874 if( IsRedlineOn() )
1876 // If the range is completely in the own Redline, we can move it!
1877 sal_uInt16 nRedlPos = GetRedlinePos( pStt->nNode.GetNode(), nsRedlineType_t::REDLINE_INSERT );
1878 if( USHRT_MAX != nRedlPos )
1880 SwRedline* pTmp = GetRedlineTbl()[ nRedlPos ];
1881 const SwPosition *pRStt = pTmp->Start(), *pREnd = pTmp->End();
1882 SwRedline aTmpRedl( nsRedlineType_t::REDLINE_INSERT, rPam );
1883 const SwCntntNode* pCEndNd = pEnd->nNode.GetNode().GetCntntNode();
1884 // Is completely in the range and is the own Redline too?
1885 if( aTmpRedl.IsOwnRedline( *pTmp ) &&
1886 (pRStt->nNode < pStt->nNode ||
1887 (pRStt->nNode == pStt->nNode && !pRStt->nContent.GetIndex()) ) &&
1888 (pEnd->nNode < pREnd->nNode ||
1889 (pEnd->nNode == pREnd->nNode &&
1890 pCEndNd ? pREnd->nContent.GetIndex() == pCEndNd->Len()
1891 : !pREnd->nContent.GetIndex() )) )
1893 pOwnRedl = pTmp;
1894 if( nRedlPos + 1 < (sal_uInt16)GetRedlineTbl().size() )
1896 pTmp = GetRedlineTbl()[ nRedlPos+1 ];
1897 if( *pTmp->Start() == *pREnd )
1898 // then don't!
1899 pOwnRedl = 0;
1902 if( pOwnRedl &&
1903 !( pRStt->nNode <= aIdx && aIdx <= pREnd->nNode ))
1905 // it's not in itself, so don't move it
1906 pOwnRedl = 0;
1911 if( !pOwnRedl )
1913 GetIDocumentUndoRedo().StartUndo( UNDO_START, NULL );
1915 // First the Insert, then the Delete
1916 SwPosition aInsPos( aIdx );
1917 aInsPos.nContent.Assign( aIdx.GetNode().GetCntntNode(), 0 );
1919 SwPaM aPam( pStt->nNode, aMvRg.aEnd );
1921 SwPaM& rOrigPam = (SwPaM&)rPam;
1922 rOrigPam.DeleteMark();
1923 rOrigPam.GetPoint()->nNode = aIdx.GetIndex() - 1;
1925 bool bDelLastPara = !aInsPos.nNode.GetNode().IsCntntNode();
1927 /* When copying to a non-content node Copy will
1928 insert a paragraph before that node and insert before
1929 that inserted node. Copy creates an SwUndoInserts that
1930 does not cover the extra paragraph. Thus we insert the
1931 extra paragraph ourselves, _with_ correct undo
1932 information. */
1933 if (bDelLastPara)
1935 /* aInsPos points to the non-content node. Move it to
1936 the previous content node. */
1937 SwPaM aInsPam(aInsPos);
1938 sal_Bool bMoved = aInsPam.Move(fnMoveBackward);
1939 OSL_ENSURE(bMoved, "No content node found!");
1941 if (bMoved)
1943 /* Append the new node after the content node
1944 found. The new position to insert the moved
1945 paragraph at is before the inserted
1946 paragraph. */
1947 AppendTxtNode(*aInsPam.GetPoint());
1948 aInsPos = *aInsPam.GetPoint();
1952 CopyRange( aPam, aInsPos, false );
1953 if( bDelLastPara )
1955 // We need to remove the last empty Node again
1956 aIdx = aInsPos.nNode;
1957 SwCntntNode* pCNd = GetNodes().GoPrevious( &aInsPos.nNode );
1958 xub_StrLen nCLen = 0; if( pCNd ) nCLen = pCNd->Len();
1959 aInsPos.nContent.Assign( pCNd, nCLen );
1961 // All, that are in the to-be-deleted Node, need to be
1962 // moved to the next Node
1963 SwPosition* pPos;
1964 for( sal_uInt16 n = 0; n < GetRedlineTbl().size(); ++n )
1966 SwRedline* pTmp = GetRedlineTbl()[ n ];
1967 if( ( pPos = &pTmp->GetBound(sal_True))->nNode == aIdx )
1969 pPos->nNode++;
1970 pPos->nContent.Assign( pPos->nNode.GetNode().GetCntntNode(),0);
1972 if( ( pPos = &pTmp->GetBound(sal_False))->nNode == aIdx )
1974 pPos->nNode++;
1975 pPos->nContent.Assign( pPos->nNode.GetNode().GetCntntNode(),0);
1978 CorrRel( aIdx, aInsPos, 0, sal_False );
1980 pCNd->JoinNext();
1983 rOrigPam.GetPoint()->nNode++;
1984 rOrigPam.GetPoint()->nContent.Assign( rOrigPam.GetCntntNode(), 0 );
1986 RedlineMode_t eOld = GetRedlineMode();
1987 checkRedlining(eOld);
1988 if (GetIDocumentUndoRedo().DoesUndo())
1990 // Still NEEDS to be optimized (even after 14 years)
1991 SetRedlineMode(
1992 (RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT | nsRedlineMode_t::REDLINE_SHOW_DELETE));
1993 SwUndo *const pUndo(new SwUndoRedlineDelete(aPam, UNDO_DELETE));
1994 GetIDocumentUndoRedo().AppendUndo(pUndo);
1997 SwRedline* pNewRedline = new SwRedline( nsRedlineType_t::REDLINE_DELETE, aPam );
1999 // prevent assertion from aPam's target being deleted
2000 // (Alternatively, one could just let aPam go out of scope, but
2001 // that requires touching a lot of code.)
2002 aPam.GetBound(sal_True).nContent.Assign( NULL, 0 );
2003 aPam.GetBound(sal_False).nContent.Assign( NULL, 0 );
2005 AppendRedline( pNewRedline, true );
2007 // Still NEEDS to be optimized!
2008 SetRedlineMode( eOld );
2009 GetIDocumentUndoRedo().EndUndo( UNDO_END, NULL );
2010 SetModified();
2012 return true;
2016 if( !pOwnRedl && !IsIgnoreRedline() && !GetRedlineTbl().empty() )
2018 SwPaM aTemp(aIdx);
2019 SplitRedline(aTemp);
2022 sal_uLong nRedlSttNd(0), nRedlEndNd(0);
2023 if( pOwnRedl )
2025 const SwPosition *pRStt = pOwnRedl->Start(), *pREnd = pOwnRedl->End();
2026 nRedlSttNd = pRStt->nNode.GetIndex();
2027 nRedlEndNd = pREnd->nNode.GetIndex();
2030 SwUndoMoveNum* pUndo = 0;
2031 sal_uLong nMoved = 0;
2032 if (GetIDocumentUndoRedo().DoesUndo())
2034 pUndo = new SwUndoMoveNum( rPam, nOffset, bIsOutlMv );
2035 nMoved = rPam.End()->nNode.GetIndex() - rPam.Start()->nNode.GetIndex() + 1;
2039 MoveNodeRange( aMvRg, aIdx, DOC_MOVEREDLINES );
2041 if( pUndo )
2043 // i57907: Under circumstances (sections at the end of a chapter)
2044 // the rPam.Start() is not moved to the new position.
2045 // But aIdx should be at the new end position and as long as the
2046 // number of moved paragraphs is nMoved, I know, where the new
2047 // position is.
2048 pUndo->SetStartNode( aIdx.GetIndex() - nMoved );
2049 GetIDocumentUndoRedo().AppendUndo(pUndo);
2052 if( pOwnRedl )
2054 SwPosition *pRStt = pOwnRedl->Start(), *pREnd = pOwnRedl->End();
2055 if( pRStt->nNode.GetIndex() != nRedlSttNd )
2057 pRStt->nNode = nRedlSttNd;
2058 pRStt->nContent.Assign( pRStt->nNode.GetNode().GetCntntNode(),0);
2060 if( pREnd->nNode.GetIndex() != nRedlEndNd )
2062 pREnd->nNode = nRedlEndNd;
2063 SwCntntNode* pCNd = pREnd->nNode.GetNode().GetCntntNode();
2064 xub_StrLen nL = 0; if( pCNd ) nL = pCNd->Len();
2065 pREnd->nContent.Assign( pCNd, nL );
2069 SetModified();
2070 return true;
2073 bool SwDoc::NumOrNoNum( const SwNodeIndex& rIdx, sal_Bool bDel )
2075 bool bResult = false;
2076 SwTxtNode * pTxtNd = rIdx.GetNode().GetTxtNode();
2078 if (pTxtNd && pTxtNd->GetNumRule() != NULL &&
2079 (pTxtNd->HasNumber() || pTxtNd->HasBullet()))
2081 if ( !pTxtNd->IsCountedInList() == !bDel)
2083 sal_Bool bOldNum = bDel; // == pTxtNd->IsCounted();
2084 sal_Bool bNewNum = bDel ? sal_False : sal_True;
2085 pTxtNd->SetCountedInList(bNewNum ? true : false);
2087 SetModified();
2089 bResult = true;
2091 if (GetIDocumentUndoRedo().DoesUndo())
2093 SwUndoNumOrNoNum * pUndo =
2094 new SwUndoNumOrNoNum(rIdx, bOldNum, bNewNum);
2096 GetIDocumentUndoRedo().AppendUndo(pUndo);
2099 else if (bDel && pTxtNd->GetNumRule(sal_False) &&
2100 pTxtNd->GetActualListLevel() >= 0 &&
2101 pTxtNd->GetActualListLevel() < MAXLEVEL)
2103 SwPaM aPam(*pTxtNd);
2105 DelNumRules(aPam);
2107 bResult = true;
2111 return bResult;
2114 SwNumRule* SwDoc::GetCurrNumRule( const SwPosition& rPos ) const
2116 SwNumRule* pRet = 0;
2117 SwTxtNode* pTNd = rPos.nNode.GetNode().GetTxtNode();
2119 if( pTNd )
2121 pRet = pTNd->GetNumRule();
2124 return pRet;
2127 sal_uInt16 SwDoc::FindNumRule( const String& rName ) const
2129 for( sal_uInt16 n = mpNumRuleTbl->size(); n; )
2130 if( (*mpNumRuleTbl)[ --n ]->GetName() == rName )
2131 return n;
2133 return USHRT_MAX;
2136 SwNumRule* SwDoc::FindNumRulePtr( const String& rName ) const
2138 SwNumRule * pResult = 0;
2140 pResult = maNumRuleMap[rName];
2142 if ( !pResult )
2144 for (sal_uInt16 n = 0; n < mpNumRuleTbl->size(); ++n)
2146 if ((*mpNumRuleTbl)[n]->GetName() == rName)
2148 pResult = (*mpNumRuleTbl)[n];
2150 break;
2155 return pResult;
2158 void SwDoc::AddNumRule(SwNumRule * pRule)
2160 if ((SAL_MAX_UINT16 - 1) <= mpNumRuleTbl->size())
2162 OSL_ENSURE(false, "SwDoc::AddNumRule: table full.");
2163 abort(); // this should never happen on real documents
2165 mpNumRuleTbl->push_back(pRule);
2166 maNumRuleMap[pRule->GetName()] = pRule;
2167 pRule->SetNumRuleMap(&maNumRuleMap);
2169 createListForListStyle( pRule->GetName() );
2172 sal_uInt16 SwDoc::MakeNumRule( const String &rName,
2173 const SwNumRule* pCpy,
2174 bool bBroadcast,
2175 const SvxNumberFormat::SvxNumPositionAndSpaceMode eDefaultNumberFormatPositionAndSpaceMode )
2177 SwNumRule* pNew;
2178 if( pCpy )
2180 pNew = new SwNumRule( *pCpy );
2182 pNew->SetName( GetUniqueNumRuleName( &rName ), *this );
2184 if( pNew->GetName() != rName )
2186 pNew->SetPoolFmtId( USHRT_MAX );
2187 pNew->SetPoolHelpId( USHRT_MAX );
2188 pNew->SetPoolHlpFileId( UCHAR_MAX );
2189 pNew->SetDefaultListId( String() );
2191 pNew->CheckCharFmts( this );
2193 else
2195 pNew = new SwNumRule( GetUniqueNumRuleName( &rName ),
2196 eDefaultNumberFormatPositionAndSpaceMode );
2199 sal_uInt16 nRet = mpNumRuleTbl->size();
2201 AddNumRule(pNew);
2203 if (GetIDocumentUndoRedo().DoesUndo())
2205 SwUndo * pUndo = new SwUndoNumruleCreate(pNew, this);
2206 GetIDocumentUndoRedo().AppendUndo(pUndo);
2209 if (bBroadcast)
2210 BroadcastStyleOperation(pNew->GetName(), SFX_STYLE_FAMILY_PSEUDO,
2211 SFX_STYLESHEET_CREATED);
2213 return nRet;
2216 String SwDoc::GetUniqueNumRuleName( const String* pChkStr, bool bAutoNum ) const
2218 String aName;
2219 if( bAutoNum )
2221 static rtlRandomPool s_RandomPool( rtl_random_createPool() );
2222 sal_Int64 n;
2223 rtl_random_getBytes( s_RandomPool, &n, sizeof(n) );
2224 aName = OUString::valueOf( (n < 0 ? -n : n) );
2225 if( pChkStr && !pChkStr->Len() )
2226 pChkStr = 0;
2228 else if( pChkStr && pChkStr->Len() )
2229 aName = *pChkStr;
2230 else
2232 pChkStr = 0;
2233 aName = SW_RESSTR( STR_NUMRULE_DEFNAME );
2236 sal_uInt16 nNum(0), nTmp, nFlagSize = ( mpNumRuleTbl->size() / 8 ) +2;
2237 sal_uInt8* pSetFlags = new sal_uInt8[ nFlagSize ];
2238 memset( pSetFlags, 0, nFlagSize );
2240 xub_StrLen nNmLen = aName.Len();
2241 if( !bAutoNum && pChkStr )
2243 while( nNmLen-- && '0' <= aName.GetChar( nNmLen ) &&
2244 '9' >= aName.GetChar( nNmLen ) )
2245 ; //nop
2247 if( ++nNmLen < aName.Len() )
2249 aName.Erase( nNmLen );
2250 pChkStr = 0;
2254 const SwNumRule* pNumRule;
2255 sal_uInt16 n;
2257 for( n = 0; n < mpNumRuleTbl->size(); ++n )
2258 if( 0 != ( pNumRule = (*mpNumRuleTbl)[ n ] ) )
2260 const String& rNm = pNumRule->GetName();
2261 if( rNm.Match( aName ) == nNmLen )
2263 // Determine Number and set the Flag
2264 nNum = (sal_uInt16)rNm.Copy( nNmLen ).ToInt32();
2265 if( nNum-- && nNum < mpNumRuleTbl->size() )
2266 pSetFlags[ nNum / 8 ] |= (0x01 << ( nNum & 0x07 ));
2268 if( pChkStr && pChkStr->Equals( rNm ) )
2269 pChkStr = 0;
2272 if( !pChkStr )
2274 // All Numbers have been flagged accordingly, so identify the right Number
2275 nNum = mpNumRuleTbl->size();
2276 for( n = 0; n < nFlagSize; ++n )
2277 if( 0xff != ( nTmp = pSetFlags[ n ] ))
2279 // identify the Number
2280 nNum = n * 8;
2281 while( nTmp & 1 )
2282 ++nNum, nTmp >>= 1;
2283 break;
2287 delete [] pSetFlags;
2288 if( pChkStr && pChkStr->Len() )
2289 return *pChkStr;
2290 return aName += OUString::number( ++nNum );
2293 void SwDoc::UpdateNumRule()
2295 const SwNumRuleTbl& rNmTbl = GetNumRuleTbl();
2296 for( sal_uInt16 n = 0; n < rNmTbl.size(); ++n )
2297 if( rNmTbl[ n ]->IsInvalidRule() )
2298 rNmTbl[ n ]->Validate();
2301 void SwDoc::MarkListLevel( const String& sListId,
2302 const int nListLevel,
2303 const bool bValue )
2305 SwList* pList = getListByName( sListId );
2307 if ( pList )
2309 MarkListLevel( *pList, nListLevel, bValue );
2313 void SwDoc::MarkListLevel( SwList& rList,
2314 const int nListLevel,
2315 const bool bValue )
2317 // Set new marked list level and notify all affected nodes of the changed mark.
2318 rList.MarkListLevel( nListLevel, bValue );
2321 bool SwDoc::IsFirstOfNumRule(SwPosition & rPos)
2323 bool bResult = false;
2324 SwTxtNode * pTxtNode = rPos.nNode.GetNode().GetTxtNode();
2326 if (pTxtNode)
2328 SwNumRule * pNumRule = pTxtNode->GetNumRule();
2330 if (pNumRule)
2331 bResult = pTxtNode->IsFirstOfNumRule();
2334 return bResult;
2337 // implementation for interface <IDocumentListItems>
2338 bool SwDoc::lessThanNodeNum::operator()( const SwNodeNum* pNodeNumOne,
2339 const SwNodeNum* pNodeNumTwo ) const
2341 return pNodeNumOne->LessThan( *pNodeNumTwo );
2344 void SwDoc::addListItem( const SwNodeNum& rNodeNum )
2346 if ( mpListItemsList == 0 )
2348 return;
2351 const bool bAlreadyInserted(
2352 mpListItemsList->find( &rNodeNum ) != mpListItemsList->end() );
2353 OSL_ENSURE( !bAlreadyInserted,
2354 "<SwDoc::InsertListItem(..)> - <SwNodeNum> instance already registered as numbered item!" );
2355 if ( !bAlreadyInserted )
2357 mpListItemsList->insert( &rNodeNum );
2361 void SwDoc::removeListItem( const SwNodeNum& rNodeNum )
2363 if ( mpListItemsList == 0 )
2365 return;
2368 const tImplSortedNodeNumList::size_type nDeleted = mpListItemsList->erase( &rNodeNum );
2369 if ( nDeleted > 1 )
2371 OSL_FAIL( "<SwDoc::RemoveListItem(..)> - <SwNodeNum> was registered more than once as numbered item!" );
2375 String SwDoc::getListItemText( const SwNodeNum& rNodeNum,
2376 const bool bWithNumber,
2377 const bool bWithSpacesForLevel ) const
2379 return rNodeNum.GetTxtNode()
2380 ? rNodeNum.GetTxtNode()->GetExpandTxt( 0, STRING_LEN, bWithNumber,
2381 bWithNumber, bWithSpacesForLevel )
2382 : String();
2385 void SwDoc::getListItems( tSortedNodeNumList& orNodeNumList ) const
2387 orNodeNumList.clear();
2388 orNodeNumList.reserve( mpListItemsList->size() );
2390 tImplSortedNodeNumList::iterator aIter;
2391 tImplSortedNodeNumList::iterator aEndIter = mpListItemsList->end();
2392 for ( aIter = mpListItemsList->begin(); aIter != aEndIter; ++aIter )
2394 orNodeNumList.push_back( (*aIter) );
2398 void SwDoc::getNumItems( tSortedNodeNumList& orNodeNumList ) const
2400 orNodeNumList.clear();
2401 orNodeNumList.reserve( mpListItemsList->size() );
2403 tImplSortedNodeNumList::iterator aIter;
2404 tImplSortedNodeNumList::iterator aEndIter = mpListItemsList->end();
2405 for ( aIter = mpListItemsList->begin(); aIter != aEndIter; ++aIter )
2407 const SwNodeNum* pNodeNum = (*aIter);
2408 if ( pNodeNum->IsCounted() &&
2409 pNodeNum->GetTxtNode() && pNodeNum->GetTxtNode()->HasNumber() )
2411 orNodeNumList.push_back( pNodeNum );
2416 // implementation for interface <IDocumentOutlineNodes>
2417 sal_Int32 SwDoc::getOutlineNodesCount() const
2419 return GetNodes().GetOutLineNds().size();
2422 int SwDoc::getOutlineLevel( const sal_Int32 nIdx ) const
2424 return GetNodes().GetOutLineNds()[ static_cast<sal_uInt16>(nIdx) ]->
2425 // GetTxtNode()->GetOutlineLevel(); //#outline level,zhaojianwei
2426 GetTxtNode()->GetAttrOutlineLevel()-1; //<-end,zhaojianwei
2429 String SwDoc::getOutlineText( const sal_Int32 nIdx,
2430 const bool bWithNumber,
2431 const bool bWithSpacesForLevel ) const
2433 return GetNodes().GetOutLineNds()[ static_cast<sal_uInt16>(nIdx) ]->
2434 GetTxtNode()->GetExpandTxt( 0, STRING_LEN, bWithNumber,
2435 bWithNumber, bWithSpacesForLevel );
2438 SwTxtNode* SwDoc::getOutlineNode( const sal_Int32 nIdx ) const
2440 return GetNodes().GetOutLineNds()[ static_cast<sal_uInt16>(nIdx) ]->GetTxtNode();
2443 void SwDoc::getOutlineNodes( IDocumentOutlineNodes::tSortedOutlineNodeList& orOutlineNodeList ) const
2445 orOutlineNodeList.clear();
2446 orOutlineNodeList.reserve( getOutlineNodesCount() );
2448 const sal_uInt16 nOutlCount( static_cast<sal_uInt16>(getOutlineNodesCount()) );
2449 for ( sal_uInt16 i = 0; i < nOutlCount; ++i )
2451 orOutlineNodeList.push_back(
2452 GetNodes().GetOutLineNds()[i]->GetTxtNode() );
2456 // implementation of interface IDocumentListsAccess
2457 SwList* SwDoc::createList( String sListId,
2458 const String sDefaultListStyleName )
2460 if ( sListId.Len() == 0 )
2462 sListId = listfunc::CreateUniqueListId( *this );
2465 if ( getListByName( sListId ) )
2467 OSL_FAIL( "<SwDoc::createList(..)> - provided list id already used. Serious defect -> please inform OD." );
2468 return 0;
2471 SwNumRule* pDefaultNumRuleForNewList = FindNumRulePtr( sDefaultListStyleName );
2472 if ( !pDefaultNumRuleForNewList )
2474 OSL_FAIL( "<SwDoc::createList(..)> - for provided default list style name no list style is found. Serious defect -> please inform OD." );
2475 return 0;
2478 SwList* pNewList = new SwList( sListId, *pDefaultNumRuleForNewList, GetNodes() );
2479 maLists[sListId] = pNewList;
2481 return pNewList;
2484 void SwDoc::deleteList( const String sListId )
2486 SwList* pList = getListByName( sListId );
2487 if ( pList )
2489 maLists.erase( sListId );
2490 delete pList;
2494 SwList* SwDoc::getListByName( const String sListId ) const
2496 SwList* pList = 0;
2498 boost::unordered_map< String, SwList*, StringHash >::const_iterator
2499 aListIter = maLists.find( sListId );
2500 if ( aListIter != maLists.end() )
2502 pList = (*aListIter).second;
2505 return pList;
2508 SwList* SwDoc::createListForListStyle( const String sListStyleName )
2510 if ( sListStyleName.Len() == 0 )
2512 OSL_FAIL( "<SwDoc::createListForListStyle(..)> - no list style name provided. Serious defect -> please inform OD." );
2513 return 0;
2516 if ( getListForListStyle( sListStyleName ) )
2518 OSL_FAIL( "<SwDoc::createListForListStyle(..)> - a list for the provided list style name already exists. Serious defect -> please inform OD." );
2519 return 0;
2522 SwNumRule* pNumRule = FindNumRulePtr( sListStyleName );
2523 if ( !pNumRule )
2525 OSL_FAIL( "<SwDoc::createListForListStyle(..)> - for provided list style name no list style is found. Serious defect -> please inform OD." );
2526 return 0;
2529 String sListId( pNumRule->GetDefaultListId() ); // can be empty String
2530 if ( getListByName( sListId ) )
2532 sListId = String();
2534 SwList* pNewList = createList( sListId, sListStyleName );
2535 maListStyleLists[sListStyleName] = pNewList;
2536 pNumRule->SetDefaultListId( pNewList->GetListId() );
2538 return pNewList;
2541 SwList* SwDoc::getListForListStyle( const String sListStyleName ) const
2543 SwList* pList = 0;
2545 boost::unordered_map< String, SwList*, StringHash >::const_iterator
2546 aListIter = maListStyleLists.find( sListStyleName );
2547 if ( aListIter != maListStyleLists.end() )
2549 pList = (*aListIter).second;
2552 return pList;
2555 void SwDoc::deleteListForListStyle( const String sListStyleName )
2557 String sListId;
2559 SwList* pList = getListForListStyle( sListStyleName );
2560 OSL_ENSURE( pList,
2561 "<SwDoc::deleteListForListStyle(..)> - misusage of method: no list found for given list style name" );
2562 if ( pList )
2564 sListId = pList->GetListId();
2567 if ( sListId.Len() > 0 )
2569 maListStyleLists.erase( sListStyleName );
2570 deleteList( sListId );
2574 void SwDoc::trackChangeOfListStyleName( const String sListStyleName,
2575 const String sNewListStyleName )
2577 SwList* pList = getListForListStyle( sListStyleName );
2578 OSL_ENSURE( pList,
2579 "<SwDoc::changeOfListStyleName(..)> - misusage of method: no list found for given list style name" );
2581 if ( pList != 0 )
2583 maListStyleLists.erase( sListStyleName );
2584 maListStyleLists[sNewListStyleName] = pList;
2588 namespace listfunc
2590 const String MakeListIdUnique( const SwDoc& rDoc,
2591 const String aSuggestedUniqueListId )
2593 long nHitCount = 0;
2594 String aTmpStr = aSuggestedUniqueListId;
2595 while ( rDoc.getListByName( aTmpStr ) )
2597 ++nHitCount;
2598 aTmpStr = aSuggestedUniqueListId;
2599 aTmpStr += OUString::number( nHitCount );
2602 return aTmpStr;
2604 const String CreateUniqueListId( const SwDoc& rDoc )
2606 // #i92478#
2607 OUString aNewListId( "list" );
2608 // #o12311627#
2609 static rtlRandomPool s_RandomPool( rtl_random_createPool() );
2610 sal_Int64 n;
2611 rtl_random_getBytes( s_RandomPool, &n, sizeof(n) );
2612 aNewListId += OUString::valueOf( (n < 0 ? -n : n) );
2614 return MakeListIdUnique( rDoc, aNewListId );
2618 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */