/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sw.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::util; SV_DECL_PTRARR_SORT( SwpFmts, SwFmt*, 0, 4 ) SV_IMPL_PTRARR_SORT( SwpFmts, SwFmt* ) // Sonderbehandlung fuer SvxFontItem, nur den Namen vergleichen: int CmpAttr( const SfxPoolItem& rItem1, const SfxPoolItem& rItem2 ) { switch( rItem1.Which() ) { case RES_CHRATR_FONT: return ((SvxFontItem&)rItem1).GetFamilyName() == ((SvxFontItem&)rItem2).GetFamilyName(); case RES_CHRATR_COLOR: return ((SvxColorItem&)rItem1).GetValue().IsRGBEqual( ((SvxColorItem&)rItem2).GetValue() ); case RES_PAGEDESC: return ((SwFmtPageDesc&)rItem1).GetNumOffset() == ((SwFmtPageDesc&)rItem2).GetNumOffset() && ((SwFmtPageDesc&)rItem1).GetPageDesc() == ((SwFmtPageDesc&)rItem2).GetPageDesc(); } return rItem1 == rItem2; } const SwTxtAttr* GetFrwrdTxtHint( const SwpHints& rHtsArr, sal_uInt16& rPos, xub_StrLen nCntntPos ) { while( rPos < rHtsArr.Count() ) { const SwTxtAttr *pTxtHt = rHtsArr.GetStart( rPos++ ); // der Start vom Attribut muss innerhalb des Bereiches liegen !! if( *pTxtHt->GetStart() >= nCntntPos ) return pTxtHt; // gueltiges TextAttribut } return 0; // kein gueltiges TextAttribut } const SwTxtAttr* GetBkwrdTxtHint( const SwpHints& rHtsArr, sal_uInt16& rPos, xub_StrLen nCntntPos ) { while( rPos > 0 ) { //Hack mit cast fuer das Update const SwTxtAttr *pTxtHt = rHtsArr.GetStart( --rPos ); // der Start vom Attribut muss innerhalb des Bereiches liegen !! if( *pTxtHt->GetStart() < nCntntPos ) return pTxtHt; // gueltiges TextAttribut } return 0; // kein gueltiges TextAttribut } void lcl_SetAttrPam( SwPaM & rPam, xub_StrLen nStart, const xub_StrLen* pEnde, const sal_Bool bSaveMark ) { xub_StrLen nCntntPos; if( bSaveMark ) nCntntPos = rPam.GetMark()->nContent.GetIndex(); else nCntntPos = rPam.GetPoint()->nContent.GetIndex(); sal_Bool bTstEnde = rPam.GetPoint()->nNode == rPam.GetMark()->nNode; SwCntntNode* pCNd = rPam.GetCntntNode(); rPam.GetPoint()->nContent.Assign( pCNd, nStart ); rPam.SetMark(); // Point == GetMark // Point zeigt auf das Ende vom SuchBereich oder Ende vom Attribut if( pEnde ) { if( bTstEnde && *pEnde > nCntntPos ) rPam.GetPoint()->nContent = nCntntPos; else rPam.GetPoint()->nContent = *pEnde; } } //------------------ Suche nach einem Text Attribut ----------------------- // diese Funktion sucht in einem TextNode nach dem vorgegebenen Attribut. // Wird es gefunden, dann hat der SwPaM den Bereich der das Attribut // umspannt, unter Beachtung des Suchbereiches sal_Bool lcl_Search( const SwTxtNode& rTxtNd, SwPaM& rPam, const SfxPoolItem& rCmpItem, SwMoveFn fnMove, sal_Bool bValue ) { if ( !rTxtNd.HasHints() ) return sal_False; const SwTxtAttr *pTxtHt = 0; sal_Bool bForward = fnMove == fnMoveForward; sal_uInt16 nPos = bForward ? 0 : rTxtNd.GetSwpHints().Count(); xub_StrLen nCntntPos = rPam.GetPoint()->nContent.GetIndex(); while( 0 != ( pTxtHt=(*fnMove->fnGetHint)(rTxtNd.GetSwpHints(),nPos,nCntntPos))) if( pTxtHt->Which() == rCmpItem.Which() && ( !bValue || CmpAttr( pTxtHt->GetAttr(), rCmpItem ))) { lcl_SetAttrPam( rPam, *pTxtHt->GetStart(), pTxtHt->GetEnd(), bForward ); return sal_True; } return sal_False; } //------------------ Suche nach mehren Text Attributen ------------------- struct _SwSrchChrAttr { sal_uInt16 nWhich; xub_StrLen nStt, nEnd; _SwSrchChrAttr( const SfxPoolItem& rItem, xub_StrLen nStart, xub_StrLen nAnyEnd ) : nWhich( rItem.Which() ), nStt( nStart ), nEnd( nAnyEnd ) {} }; class SwAttrCheckArr { _SwSrchChrAttr *pFndArr, *pStackArr; xub_StrLen nNdStt, nNdEnd; sal_uInt16 nArrStart, nArrLen; sal_uInt16 nFound, nStackCnt; SfxItemSet aCmpSet; sal_Bool bNoColls; sal_Bool bForward; public: SwAttrCheckArr( const SfxItemSet& rSet, int bForward, int bNoCollections ); ~SwAttrCheckArr(); void SetNewSet( const SwTxtNode& rTxtNd, const SwPaM& rPam ); // wieviele Attribute ueberhaupt ?? sal_uInt16 Count() const { return aCmpSet.Count(); } int Found() const { return nFound == aCmpSet.Count(); } int CheckStack(); xub_StrLen Start() const; xub_StrLen End() const; xub_StrLen GetNdStt() const { return nNdStt; } xub_StrLen GetNdEnd() const { return nNdEnd; } int SetAttrFwd( const SwTxtAttr& rAttr ); int SetAttrBwd( const SwTxtAttr& rAttr ); }; SwAttrCheckArr::SwAttrCheckArr( const SfxItemSet& rSet, int bFwd, int bNoCollections ) : aCmpSet( *rSet.GetPool(), RES_CHRATR_BEGIN, RES_TXTATR_END-1 ) { aCmpSet.Put( rSet, sal_False ); bNoColls = 0 != bNoCollections; bForward = 0 != bFwd; // Bestimmen den Bereich des Fnd/Stack-Arrays (Min/Max) SfxItemIter aIter( aCmpSet ); nArrStart = aCmpSet.GetWhichByPos( aIter.GetFirstPos() ); nArrLen = aCmpSet.GetWhichByPos( aIter.GetLastPos() ) - nArrStart+1; char* pFndChar = new char[ nArrLen * sizeof(_SwSrchChrAttr) ]; char* pStackChar = new char[ nArrLen * sizeof(_SwSrchChrAttr) ]; pFndArr = (_SwSrchChrAttr*)pFndChar; pStackArr = (_SwSrchChrAttr*)pStackChar; } SwAttrCheckArr::~SwAttrCheckArr() { delete[] (char*)pFndArr; delete[] (char*)pStackArr; } void SwAttrCheckArr::SetNewSet( const SwTxtNode& rTxtNd, const SwPaM& rPam ) { memset( pFndArr, 0, nArrLen * sizeof(_SwSrchChrAttr) ); memset( pStackArr, 0, nArrLen * sizeof(_SwSrchChrAttr) ); nFound = 0; nStackCnt = 0; if( bForward ) { nNdStt = rPam.GetPoint()->nContent.GetIndex(); nNdEnd = rPam.GetPoint()->nNode == rPam.GetMark()->nNode ? rPam.GetMark()->nContent.GetIndex() : rTxtNd.GetTxt().Len(); } else { nNdEnd = rPam.GetPoint()->nContent.GetIndex(); nNdStt = rPam.GetPoint()->nNode == rPam.GetMark()->nNode ? rPam.GetMark()->nContent.GetIndex() : 0; } if( bNoColls && !rTxtNd.HasSwAttrSet() ) return ; const SfxItemSet& rSet = rTxtNd.GetSwAttrSet(); // if( !rSet.Count() ) // return; SfxItemIter aIter( aCmpSet ); const SfxPoolItem* pItem = aIter.GetCurItem(); const SfxPoolItem* pFndItem; sal_uInt16 nWhich; while( sal_True ) { // nur testen, ob vorhanden ist ? if( IsInvalidItem( pItem ) ) { nWhich = aCmpSet.GetWhichByPos( aIter.GetCurPos() ); if( RES_TXTATR_END <= nWhich ) break; // Ende der TextAttribute if( SFX_ITEM_SET == rSet.GetItemState( nWhich, !bNoColls, &pFndItem ) && !CmpAttr( *pFndItem, rSet.GetPool()->GetDefaultItem( nWhich ) )) { pFndArr[ nWhich - nArrStart ] = _SwSrchChrAttr( *pFndItem, nNdStt, nNdEnd ); nFound++; } } else { if( RES_TXTATR_END <= (nWhich = pItem->Which() )) break; // Ende der TextAttribute //JP 27.02.95: wenn nach defaults gesucht wird, dann muss man bis zum Pool // runter // if( SFX_ITEM_SET == rSet.GetItemState( nWhich, !bNoColls, &pFndItem ) // && *pFndItem == *pItem ) if( CmpAttr( rSet.Get( nWhich, !bNoColls ), *pItem ) ) { pFndArr[ nWhich - nArrStart ] = _SwSrchChrAttr( *pItem, nNdStt, nNdEnd ); nFound++; } } if( aIter.IsAtEnd() ) break; pItem = aIter.NextItem(); } } static bool lcl_IsAttributeIgnorable(xub_StrLen const nNdStart, xub_StrLen const nNdEnd, _SwSrchChrAttr const& rTmp) { // #i115528#: if there is a paragraph attribute, it has been added by the // SwAttrCheckArr ctor, and nFound is 1. // if the paragraph is entirely covered by hints that override the paragraph // attribute, then this function must find an attribute to decrement nFound! // so check for an empty search range, let attributes that start/end there // cover it, and hope for the best... return ((nNdEnd == nNdStart) ? ((rTmp.nEnd < nNdStart) || (nNdEnd < rTmp.nStt)) : ((rTmp.nEnd <= nNdStart) || (nNdEnd <= rTmp.nStt))); } int SwAttrCheckArr::SetAttrFwd( const SwTxtAttr& rAttr ) { _SwSrchChrAttr aTmp( rAttr.GetAttr(), *rAttr.GetStart(), *rAttr.GetAnyEnd() ); // ignore all attributes not in search range if (lcl_IsAttributeIgnorable(nNdStt, nNdEnd, aTmp)) { return Found(); } const SfxPoolItem* pItem; // -------------------------------------------------------------- // Hier wird jetzt ausdruecklich auch in Zeichenvorlagen gesucht // -------------------------------------------------------------- sal_uInt16 nWhch = rAttr.Which(); SfxWhichIter* pIter = NULL; const SfxPoolItem* pTmpItem = NULL; const SfxItemSet* pSet = NULL; if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch ) { if( bNoColls && RES_TXTATR_CHARFMT == nWhch ) return Found(); pTmpItem = NULL; pSet = CharFmt::GetItemSet( rAttr.GetAttr() ); if ( pSet ) { pIter = new SfxWhichIter( *pSet ); nWhch = pIter->FirstWhich(); while( nWhch && SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) ) nWhch = pIter->NextWhich(); if( !nWhch ) pTmpItem = NULL; } } else pTmpItem = &rAttr.GetAttr(); while( pTmpItem ) { SfxItemState eState = aCmpSet.GetItemState( nWhch, sal_False, &pItem ); if( SFX_ITEM_DONTCARE == eState || SFX_ITEM_SET == eState ) { sal_uInt16 n; _SwSrchChrAttr* pCmp; // loesche erstmal alle, die bis zu der Start Position schon wieder // ungueltig sind: _SwSrchChrAttr* pArrPtr; if( nFound ) for( pArrPtr = pFndArr, n = 0; n < nArrLen; ++n, ++pArrPtr ) if( pArrPtr->nWhich && pArrPtr->nEnd <= aTmp.nStt ) { pArrPtr->nWhich = 0; // geloescht nFound--; } // loesche erstmal alle, die bis zu der Start Position schon wieder // ungueltig sind. Und verschiebe alle die "offen" sind, heisst ueber // die Start Position ragen, vom Stack in den FndSet if( nStackCnt ) for( pArrPtr = pStackArr, n=0; n < nArrLen; ++n, ++pArrPtr ) { if( !pArrPtr->nWhich ) continue; if( pArrPtr->nEnd <= aTmp.nStt ) { pArrPtr->nWhich = 0; // geloescht if( !--nStackCnt ) break; } else if( pArrPtr->nStt <= aTmp.nStt ) { if( ( pCmp = &pFndArr[ n ])->nWhich ) { if( pCmp->nEnd < pArrPtr->nEnd ) // erweitern pCmp->nEnd = pArrPtr->nEnd; } else { *pCmp = *pArrPtr; nFound++; } pArrPtr->nWhich = 0; if( !--nStackCnt ) break; } } sal_Bool bContinue = sal_False; if( SFX_ITEM_DONTCARE == eState ) { // wird Attribut gueltig ? if( !CmpAttr( aCmpSet.GetPool()->GetDefaultItem( nWhch ), *pTmpItem )) { // suche das Attribut und erweiter es gegebenenfalls if( !( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich ) { *pCmp = aTmp; // nicht gefunden, eintragen nFound++; } else if( pCmp->nEnd < aTmp.nEnd ) // erweitern ? pCmp->nEnd = aTmp.nEnd; bContinue = sal_True; } } // wird Attribut gueltig ? else if( CmpAttr( *pItem, *pTmpItem ) ) { pFndArr[ nWhch - nArrStart ] = aTmp; ++nFound; bContinue = sal_True; } // tja, dann muss es auf den Stack if( !bContinue && ( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich ) { // vorhanden, auf den Stack. Aber nur wenn es noch grosser ist if( pCmp->nEnd > aTmp.nEnd ) { ASSERT( !pStackArr[ nWhch - nArrStart ].nWhich, "Stack-Platz ist noch belegt" ); // --------- // JP 22.08.96: nur Ende manipulieren reicht nicht. Bug 30547 // pCmp->nStt = aTmp.nEnd; if( aTmp.nStt <= pCmp->nStt ) pCmp->nStt = aTmp.nEnd; else pCmp->nEnd = aTmp.nStt; // --------- pStackArr[ nWhch - nArrStart ] = *pCmp; nStackCnt++; } pCmp->nWhich = 0; nFound--; } } if( pIter ) { nWhch = pIter->NextWhich(); while( nWhch && SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) ) nWhch = pIter->NextWhich(); if( !nWhch ) break; } else break; } return Found(); } int SwAttrCheckArr::SetAttrBwd( const SwTxtAttr& rAttr ) { _SwSrchChrAttr aTmp( rAttr.GetAttr(), *rAttr.GetStart(), *rAttr.GetAnyEnd() ); // ignore all attributes not in search range if (lcl_IsAttributeIgnorable(nNdStt, nNdEnd, aTmp)) { return Found(); } const SfxPoolItem* pItem; // -------------------------------------------------------------- // Hier wird jetzt ausdruecklich auch in Zeichenvorlagen gesucht // -------------------------------------------------------------- sal_uInt16 nWhch = rAttr.Which(); SfxWhichIter* pIter = NULL; const SfxPoolItem* pTmpItem = NULL; const SfxItemSet* pSet = NULL; if( RES_TXTATR_CHARFMT == nWhch || RES_TXTATR_AUTOFMT == nWhch ) { if( bNoColls && RES_TXTATR_CHARFMT == nWhch ) return Found(); pSet = CharFmt::GetItemSet( rAttr.GetAttr() ); if ( pSet ) { pIter = new SfxWhichIter( *pSet ); nWhch = pIter->FirstWhich(); while( nWhch && SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) ) nWhch = pIter->NextWhich(); if( !nWhch ) pTmpItem = NULL; } } else pTmpItem = &rAttr.GetAttr(); while( pTmpItem ) { SfxItemState eState = aCmpSet.GetItemState( nWhch, sal_False, &pItem ); if( SFX_ITEM_DONTCARE == eState || SFX_ITEM_SET == eState ) { sal_uInt16 n; _SwSrchChrAttr* pCmp; // loesche erstmal alle, die bis zu der Start Position schon wieder // ungueltig sind: _SwSrchChrAttr* pArrPtr; if( nFound ) for( pArrPtr = pFndArr, n = 0; n < nArrLen; ++n, ++pArrPtr ) if( pArrPtr->nWhich && pArrPtr->nStt >= aTmp.nEnd ) { pArrPtr->nWhich = 0; // geloescht nFound--; } // loesche erstmal alle, die bis zu der Start Position schon wieder // ungueltig sind. Und verschiebe alle die "offen" sind, heisst ueber // die Start Position ragen, vom Stack in den FndSet if( nStackCnt ) for( pArrPtr = pStackArr, n = 0; n < nArrLen; ++n, ++pArrPtr ) { if( !pArrPtr->nWhich ) continue; if( pArrPtr->nStt >= aTmp.nEnd ) { pArrPtr->nWhich = 0; // geloescht if( !--nStackCnt ) break; } else if( pArrPtr->nEnd >= aTmp.nEnd ) { if( ( pCmp = &pFndArr[ n ])->nWhich ) { if( pCmp->nStt > pArrPtr->nStt ) // erweitern pCmp->nStt = pArrPtr->nStt; } else { *pCmp = *pArrPtr; nFound++; } pArrPtr->nWhich = 0; if( !--nStackCnt ) break; } } sal_Bool bContinue = sal_False; if( SFX_ITEM_DONTCARE == eState ) { // wird Attribut gueltig ? if( !CmpAttr( aCmpSet.GetPool()->GetDefaultItem( nWhch ), *pTmpItem ) ) { // suche das Attribut und erweiter es gegebenenfalls if( !( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich ) { *pCmp = aTmp; // nicht gefunden, eintragen nFound++; } else if( pCmp->nStt > aTmp.nStt ) // erweitern ? pCmp->nStt = aTmp.nStt; bContinue = sal_True; } } // wird Attribut gueltig ? else if( CmpAttr( *pItem, *pTmpItem )) { pFndArr[ nWhch - nArrStart ] = aTmp; ++nFound; bContinue = sal_True; } // tja, dann muss es auf den Stack if( !bContinue && ( pCmp = &pFndArr[ nWhch - nArrStart ])->nWhich ) { // vorhanden, auf den Stack. Aber nur wenn es noch grosser ist if( pCmp->nStt < aTmp.nStt ) { ASSERT( !pStackArr[ nWhch - nArrStart ].nWhich, "Stack-Platz ist noch belegt" ); // --------- // JP 22.08.96: nur Ende manipulieren reicht nicht. Bug 30547 // pCmp->nEnd = aTmp.nStt; if( aTmp.nEnd <= pCmp->nEnd ) pCmp->nEnd = aTmp.nStt; else pCmp->nStt = aTmp.nEnd; // --------- pStackArr[ nWhch - nArrStart ] = *pCmp; nStackCnt++; } pCmp->nWhich = 0; nFound--; } } if( pIter ) { nWhch = pIter->NextWhich(); while( nWhch && SFX_ITEM_SET != pSet->GetItemState( nWhch, sal_True, &pTmpItem ) ) nWhch = pIter->NextWhich(); if( !nWhch ) break; } else break; } return Found(); } xub_StrLen SwAttrCheckArr::Start() const { xub_StrLen nStart = nNdStt; _SwSrchChrAttr* pArrPtr = pFndArr; for( sal_uInt16 n = 0; n < nArrLen; ++n, ++pArrPtr ) if( pArrPtr->nWhich && pArrPtr->nStt > nStart ) nStart = pArrPtr->nStt; return nStart; } xub_StrLen SwAttrCheckArr::End() const { _SwSrchChrAttr* pArrPtr = pFndArr; xub_StrLen nEnd = nNdEnd; for( sal_uInt16 n = 0; n < nArrLen; ++n, ++pArrPtr ) if( pArrPtr->nWhich && pArrPtr->nEnd < nEnd ) nEnd = pArrPtr->nEnd; return nEnd; } int SwAttrCheckArr::CheckStack() { if( !nStackCnt ) return sal_False; sal_uInt16 n; xub_StrLen nSttPos = Start(), nEndPos = End(); _SwSrchChrAttr* pArrPtr; for( pArrPtr = pStackArr, n = 0; n < nArrLen; ++n, ++pArrPtr ) { if( !pArrPtr->nWhich ) continue; if( bForward ? pArrPtr->nEnd <= nSttPos : pArrPtr->nStt >= nEndPos ) { pArrPtr->nWhich = 0; // geloescht if( !--nStackCnt ) return nFound == aCmpSet.Count(); } else if( bForward ? pArrPtr->nStt < nEndPos : pArrPtr->nEnd > nSttPos ) { // alle die "offen" sind, heisst ueber die Start Position ragen, // im FndSet setzen ASSERT( !pFndArr[ n ].nWhich, "Array-Platz ist noch belegt" ); pFndArr[ n ] = *pArrPtr; pArrPtr->nWhich = 0; nFound++; if( !--nStackCnt ) return nFound == aCmpSet.Count(); } } return nFound == aCmpSet.Count(); } int lcl_SearchForward( const SwTxtNode& rTxtNd, SwAttrCheckArr& rCmpArr, SwPaM& rPam ) { xub_StrLen nEndPos, nSttPos; rCmpArr.SetNewSet( rTxtNd, rPam ); if( !rTxtNd.HasHints() ) { if( !rCmpArr.Found() ) return sal_False; nEndPos = rCmpArr.GetNdEnd(); lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_True ); return sal_True; } // dann gehe mal durch das nach "Start" sortierte Array const SwpHints& rHtArr = rTxtNd.GetSwpHints(); const SwTxtAttr* pAttr; sal_uInt16 nPos = 0; // sollte jetzt schon alles vorhanden sein, dann teste, mit welchem // das wieder beendet wird. if( rCmpArr.Found() ) { for( ; nPos < rHtArr.Count(); ++nPos ) if( !rCmpArr.SetAttrFwd( *( pAttr = rHtArr.GetStart( nPos )) ) ) { if( rCmpArr.GetNdStt() < *pAttr->GetStart() ) { // dann haben wir unser Ende: lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), pAttr->GetStart(), sal_True ); return sal_True; } // ansonsten muessen wir weiter suchen break; } if( nPos == rHtArr.Count() && rCmpArr.Found() ) { // dann haben wir unseren Bereich nEndPos = rCmpArr.GetNdEnd(); lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_True ); return sal_True; } } for( ; nPos < rHtArr.Count(); ++nPos ) if( rCmpArr.SetAttrFwd( *( pAttr = rHtArr.GetStart( nPos )) ) ) { // sollten noch mehr auf der gleichen Position anfangen ?? // auch die noch mit testen !! nSttPos = *pAttr->GetStart(); while( ++nPos < rHtArr.Count() && nSttPos == *( pAttr = rHtArr.GetStart( nPos ))->GetStart() && rCmpArr.SetAttrFwd( *pAttr ) ) ; if( !rCmpArr.Found() ) continue; // dann haben wir den Bereich zusammen if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) ) return sal_False; lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_True ); return sal_True; } if( !rCmpArr.CheckStack() || (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) ) return sal_False; lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_True ); return sal_True; } int lcl_SearchBackward( const SwTxtNode& rTxtNd, SwAttrCheckArr& rCmpArr, SwPaM& rPam ) { xub_StrLen nEndPos, nSttPos; rCmpArr.SetNewSet( rTxtNd, rPam ); if( !rTxtNd.HasHints() ) { if( !rCmpArr.Found() ) return sal_False; nEndPos = rCmpArr.GetNdEnd(); lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_False ); return sal_True; } // dann gehe mal durch das nach "Start" sortierte Array const SwpHints& rHtArr = rTxtNd.GetSwpHints(); const SwTxtAttr* pAttr; sal_uInt16 nPos = rHtArr.Count(); // sollte jetzt schon alles vorhanden sein, dann teste, mit welchem // das wieder beendet wird. if( rCmpArr.Found() ) { while( nPos ) if( !rCmpArr.SetAttrBwd( *( pAttr = rHtArr.GetEnd( --nPos )) ) ) { nSttPos = *pAttr->GetAnyEnd(); if( nSttPos < rCmpArr.GetNdEnd() ) { // dann haben wir unser Ende: nEndPos = rCmpArr.GetNdEnd(); lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False ); return sal_True; } // ansonsten muessen wir weiter suchen break; } if( !nPos && rCmpArr.Found() ) { // dann haben wir unseren Bereich nEndPos = rCmpArr.GetNdEnd(); lcl_SetAttrPam( rPam, rCmpArr.GetNdStt(), &nEndPos, sal_False ); return sal_True; } } while( nPos ) if( rCmpArr.SetAttrBwd( *( pAttr = rHtArr.GetEnd( --nPos )) ) ) { // sollten noch mehr auf der gleichen Position anfangen ?? // auch die noch mit testen !! if( nPos ) { nEndPos = *pAttr->GetAnyEnd(); while( --nPos && nEndPos == *( pAttr = rHtArr.GetEnd( nPos ))->GetAnyEnd() && rCmpArr.SetAttrBwd( *pAttr ) ) ; } if( !rCmpArr.Found() ) continue; // dann haben wir den Bereich zusammen if( (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) ) return sal_False; lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False ); return sal_True; } if( !rCmpArr.CheckStack() || (nSttPos = rCmpArr.Start()) > (nEndPos = rCmpArr.End()) ) return sal_False; lcl_SetAttrPam( rPam, nSttPos, &nEndPos, sal_False ); return sal_True; } int lcl_Search( const SwCntntNode& rCNd, const SfxItemSet& rCmpSet, sal_Bool bNoColls ) { // nur die harte Attributierung suchen ? if( bNoColls && !rCNd.HasSwAttrSet() ) return sal_False; const SfxItemSet& rNdSet = rCNd.GetSwAttrSet(); SfxItemIter aIter( rCmpSet ); const SfxPoolItem* pItem = aIter.GetCurItem(); const SfxPoolItem* pNdItem; sal_uInt16 nWhich; while( sal_True ) { // nur testen, ob vorhanden ist ? if( IsInvalidItem( pItem )) { nWhich = rCmpSet.GetWhichByPos( aIter.GetCurPos() ); if( SFX_ITEM_SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem ) || CmpAttr( *pNdItem, rNdSet.GetPool()->GetDefaultItem( nWhich ) )) return sal_False; } else { nWhich = pItem->Which(); //JP 27.02.95: wenn nach defaults gesucht wird, dann muss man bis zum Pool // runter // if( SFX_ITEM_SET != rNdSet.GetItemState( nWhich, !bNoColls, &pNdItem ) // || *pNdItem != *pItem ) if( !CmpAttr( rNdSet.Get( nWhich, !bNoColls ), *pItem )) return sal_False; } if( aIter.IsAtEnd() ) break; pItem = aIter.NextItem(); } return sal_True; // wurde gefunden } sal_Bool SwPaM::Find( const SfxPoolItem& rAttr, sal_Bool bValue, SwMoveFn fnMove, const SwPaM *pRegion, sal_Bool bInReadOnly ) { // stelle fest welches Attribut gesucht wird: sal_uInt16 nWhich = rAttr.Which(); int bCharAttr = isCHRATR(nWhich) || isTXTATR(nWhich); SwPaM* pPam = MakeRegion( fnMove, pRegion ); sal_Bool bFound = sal_False; sal_Bool bFirst = sal_True; sal_Bool bSrchForward = fnMove == fnMoveForward; SwCntntNode * pNode; const SfxPoolItem* pItem; SwpFmts aFmtArr; // Wenn am Anfang/Ende, aus dem Node moven if( bSrchForward ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetCntntNode()->Len() : !pPam->GetPoint()->nContent.GetIndex() ) { if( !(*fnMove->fnNds)( &pPam->GetPoint()->nNode, sal_False )) { delete pPam; return sal_False; } SwCntntNode *pNd = pPam->GetCntntNode(); xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len(); pPam->GetPoint()->nContent.Assign( pNd, nTmpPos ); } while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ) ) { if( bCharAttr ) { if( !pNode->IsTxtNode() ) // CharAttr sind nur in TextNodes continue; if( ((SwTxtNode*)pNode)->HasHints() && lcl_Search( *(SwTxtNode*)pNode, *pPam, rAttr, fnMove, bValue )) { // setze auf die Werte vom Attribut SetMark(); *GetPoint() = *pPam->GetPoint(); *GetMark() = *pPam->GetMark(); bFound = sal_True; break; } else if (isTXTATR(nWhich)) continue; // --> also weiter } // keine harte Attributierung, dann pruefe, ob die Vorlage schon // mal nach dem Attribut befragt wurde if( !pNode->HasSwAttrSet() ) { const SwFmt* pTmpFmt = pNode->GetFmtColl(); if( aFmtArr.Count() && aFmtArr.Seek_Entry( pTmpFmt )) continue; // die Collection wurde schon mal befragt aFmtArr.Insert( pTmpFmt ); } if( SFX_ITEM_SET == pNode->GetSwAttrSet().GetItemState( nWhich, sal_True, &pItem ) && ( !bValue || *pItem == rAttr ) ) { // FORWARD: Point an das Ende, GetMark zum Anfanf vom Node // BACKWARD: Point zum Anfang, GetMark an das Ende vom Node // und immer nach der Logik: inkl. Start, exkl. End !!! *GetPoint() = *pPam->GetPoint(); SetMark(); pNode->MakeEndIndex( &GetPoint()->nContent ); bFound = sal_True; break; } } // beim rueckwaerts Suchen noch Point und Mark vertauschen if( bFound && !bSrchForward ) Exchange(); delete pPam; return bFound; } typedef int (*FnSearchAttr)( const SwTxtNode&, SwAttrCheckArr&, SwPaM& ); sal_Bool SwPaM::Find( const SfxItemSet& rSet, sal_Bool bNoColls, SwMoveFn fnMove, const SwPaM *pRegion, sal_Bool bInReadOnly, sal_Bool bMoveFirst ) { SwPaM* pPam = MakeRegion( fnMove, pRegion ); sal_Bool bFound = sal_False; sal_Bool bFirst = sal_True; sal_Bool bSrchForward = fnMove == fnMoveForward; SwCntntNode * pNode; SwpFmts aFmtArr; // teste doch mal welche Text/Char-Attribute gesucht werden SwAttrCheckArr aCmpArr( rSet, bSrchForward, bNoColls ); SfxItemSet aOtherSet( GetDoc()->GetAttrPool(), RES_PARATR_BEGIN, RES_GRFATR_END-1 ); aOtherSet.Put( rSet, sal_False ); // alle Invalid-Items erhalten! FnSearchAttr fnSearch = bSrchForward ? (&::lcl_SearchForward) : (&::lcl_SearchBackward); // Wenn am Anfang/Ende, aus dem Node moven // Wenn am Anfang/Ende, aus dem Node moven if( bMoveFirst && ( bSrchForward ? pPam->GetPoint()->nContent.GetIndex() == pPam->GetCntntNode()->Len() : !pPam->GetPoint()->nContent.GetIndex() ) ) { if( !(*fnMove->fnNds)( &pPam->GetPoint()->nNode, sal_False )) { delete pPam; return sal_False; } SwCntntNode *pNd = pPam->GetCntntNode(); xub_StrLen nTmpPos = bSrchForward ? 0 : pNd->Len(); pPam->GetPoint()->nContent.Assign( pNd, nTmpPos ); } while( 0 != ( pNode = ::GetNode( *pPam, bFirst, fnMove, bInReadOnly ) ) ) { if( aCmpArr.Count() ) { if( !pNode->IsTxtNode() ) // CharAttr sind nur in TextNodes continue; if( (!aOtherSet.Count() || lcl_Search( *pNode, aOtherSet, bNoColls )) && (*fnSearch)( *(SwTxtNode*)pNode, aCmpArr, *pPam )) { // setze auf die Werte vom Attribut SetMark(); *GetPoint() = *pPam->GetPoint(); *GetMark() = *pPam->GetMark(); bFound = sal_True; break; } continue; // TextAttribute } if( !aOtherSet.Count() ) continue; // keine harte Attributierung, dann pruefe, ob die Vorlage schon // mal nach dem Attribut befragt wurde if( !pNode->HasSwAttrSet() ) { const SwFmt* pTmpFmt = pNode->GetFmtColl(); if( aFmtArr.Count() && aFmtArr.Seek_Entry( pTmpFmt )) continue; // die Collection wurde schon mal befragt aFmtArr.Insert( pTmpFmt ); } if( lcl_Search( *pNode, aOtherSet, bNoColls )) { // FORWARD: Point an das Ende, GetMark zum Anfanf vom Node // BACKWARD: Point zum Anfang, GetMark an das Ende vom Node // und immer nach der Logik: inkl. Start, exkl. End !!! *GetPoint() = *pPam->GetPoint(); SetMark(); pNode->MakeEndIndex( &GetPoint()->nContent ); bFound = sal_True; break; } } // beim rueckwaerts Suchen noch Point und Mark vertauschen if( bFound && !bSrchForward ) Exchange(); delete pPam; return bFound; } //------------------ Methoden vom SwCursor --------------------------- // Parameter fuer das Suchen vom Attributen struct SwFindParaAttr : public SwFindParas { sal_Bool bValue; const SfxItemSet *pSet, *pReplSet; const SearchOptions *pSearchOpt; SwCursor& rCursor; utl::TextSearch* pSTxt; SwFindParaAttr( const SfxItemSet& rSet, sal_Bool bNoCollection, const SearchOptions* pOpt, const SfxItemSet* pRSet, SwCursor& rCrsr ) : bValue( bNoCollection ), pSet( &rSet ), pReplSet( pRSet ), pSearchOpt( pOpt ), rCursor( rCrsr ),pSTxt( 0 ) {} virtual ~SwFindParaAttr() { delete pSTxt; } virtual int Find( SwPaM* , SwMoveFn , const SwPaM*, sal_Bool bInReadOnly ); virtual int IsReplaceMode() const; }; int SwFindParaAttr::Find( SwPaM* pCrsr, SwMoveFn fnMove, const SwPaM* pRegion, sal_Bool bInReadOnly ) { // String ersetzen ?? (nur wenn Text angegeben oder nicht attributiert // gesucht wird) sal_Bool bReplaceTxt = pSearchOpt && ( pSearchOpt->replaceString.getLength() || !pSet->Count() ); sal_Bool bReplaceAttr = pReplSet && pReplSet->Count(); sal_Bool bMoveFirst = !bReplaceAttr; if( bInReadOnly && (bReplaceAttr || bReplaceTxt )) bInReadOnly = sal_False; // wir suchen nach Attributen, soll zusaetzlich Text gesucht werden ? { SwPaM aRegion( *pRegion->GetMark(), *pRegion->GetPoint() ); SwPaM* pTextRegion = &aRegion; SwPaM aSrchPam( *pCrsr->GetPoint() ); while( sal_True ) { if( pSet->Count() ) // gibts ueberhaupt Attributierung? { // zuerst die Attributierung if( !aSrchPam.Find( *pSet, bValue, fnMove, &aRegion, bInReadOnly, bMoveFirst ) ) //JP 17.11.95: was ist mit Attributen in leeren Absaetzen !! // || *pCrsr->GetMark() == *pCrsr->GetPoint() ) // kein Bereich ?? return FIND_NOT_FOUND; bMoveFirst = sal_True; if( !pSearchOpt ) break; // ok, nur Attribute, also gefunden pTextRegion = &aSrchPam; } else if( !pSearchOpt ) return FIND_NOT_FOUND; // dann darin den Text if( !pSTxt ) { SearchOptions aTmp( *pSearchOpt ); // search in selection aTmp.searchFlag |= (SearchFlags::REG_NOT_BEGINOFLINE | SearchFlags::REG_NOT_ENDOFLINE); MsLangId::convertLanguageToLocale( LANGUAGE_SYSTEM, aTmp.Locale ); pSTxt = new utl::TextSearch( aTmp ); } // todo/mba: searching for attributes in Outliner text?! sal_Bool bSearchInNotes = sal_False; // Bug 24665: suche im richtigen Bereich weiter (pTextRegion!) if( aSrchPam.Find( *pSearchOpt, bSearchInNotes, *pSTxt, fnMove, pTextRegion, bInReadOnly ) && *aSrchPam.GetMark() != *aSrchPam.GetPoint() ) // gefunden ? break; // also raus else if( !pSet->Count() ) return FIND_NOT_FOUND; // nur Text und nicht gefunden /* // --> FME 2007-4-12 #i74765 # Why should we move the position? Moving the position results in bugs when there are two adjacent portions which both have the requested attributes set. I suspect this should be only be an optimization. Therefore I boldly remove it now! // JP: und wieder neu aufsetzen, aber eine Position weiter //JP 04.11.97: Bug 44897 - aber den Mark wieder aufheben, damit // weiterbewegt werden kann! { sal_Bool bCheckRegion = sal_True; SwPosition* pPos = aSrchPam.GetPoint(); if( !(*fnMove->fnNd)( &pPos->nNode.GetNode(), &pPos->nContent, CRSR_SKIP_CHARS )) { if( (*fnMove->fnNds)( &pPos->nNode, sal_False )) { SwCntntNode *pNd = pPos->nNode.GetNode().GetCntntNode(); xub_StrLen nCPos; if( fnMove == fnMoveForward ) nCPos = 0; else nCPos = pNd->Len(); pPos->nContent.Assign( pNd, nCPos ); } else bCheckRegion = sal_False; } if( !bCheckRegion || *aRegion.GetPoint() <= *pPos ) return FIND_NOT_FOUND; // nicht gefunden }*/ *aRegion.GetMark() = *aSrchPam.GetPoint(); } *pCrsr->GetPoint() = *aSrchPam.GetPoint(); pCrsr->SetMark(); *pCrsr->GetMark() = *aSrchPam.GetMark(); } if( bReplaceTxt ) { const bool bRegExp( SearchAlgorithms_REGEXP == pSearchOpt->algorithmType); SwIndex& rSttCntIdx = pCrsr->Start()->nContent; xub_StrLen nSttCnt = rSttCntIdx.GetIndex(); // damit die Region auch verschoben wird, in den Shell-Cursr-Ring // mit aufnehmen !! Ring *pPrevRing = 0; if( bRegExp ) { pPrevRing = pRegion->GetPrev(); ((Ring*)pRegion)->MoveRingTo( &rCursor ); } ::std::auto_ptr pRepl( (bRegExp) ? ReplaceBackReferences( *pSearchOpt, pCrsr ) : 0 ); rCursor.GetDoc()->ReplaceRange( *pCrsr, (pRepl.get()) ? *pRepl : String(pSearchOpt->replaceString), bRegExp ); rCursor.SaveTblBoxCntnt( pCrsr->GetPoint() ); if( bRegExp ) { // und die Region wieder herausnehmen: Ring *p, *pNext = (Ring*)pRegion; do { p = pNext; pNext = p->GetNext(); p->MoveTo( (Ring*)pRegion ); } while( p != pPrevRing ); } rSttCntIdx = nSttCnt; } if( bReplaceAttr ) { // --- Ist die Selection noch da ?????? // und noch die Attribute setzen #ifdef OLD pCrsr->GetDoc()->Insert( *pCrsr, *pReplSet, 0 ); #else //JP 13.07.95: alle gesuchten Attribute werden, wenn nicht im // ReplaceSet angegeben, auf Default zurueck gesetzt if( !pSet->Count() ) { pCrsr->GetDoc()->InsertItemSet( *pCrsr, *pReplSet, 0 ); } else { SfxItemPool* pPool = pReplSet->GetPool(); SfxItemSet aSet( *pPool, pReplSet->GetRanges() ); SfxItemIter aIter( *pSet ); const SfxPoolItem* pItem = aIter.GetCurItem(); while( sal_True ) { // alle die nicht gesetzt sind mit Pool-Defaults aufuellen if( !IsInvalidItem( pItem ) && SFX_ITEM_SET != pReplSet->GetItemState( pItem->Which(), sal_False )) aSet.Put( pPool->GetDefaultItem( pItem->Which() )); if( aIter.IsAtEnd() ) break; pItem = aIter.NextItem(); } aSet.Put( *pReplSet ); pCrsr->GetDoc()->InsertItemSet( *pCrsr, aSet, 0 ); } #endif return FIND_NO_RING; } else return FIND_FOUND; } int SwFindParaAttr::IsReplaceMode() const { return ( pSearchOpt && pSearchOpt->replaceString.getLength() ) || ( pReplSet && pReplSet->Count() ); } // Suchen nach Attributen sal_uLong SwCursor::Find( const SfxItemSet& rSet, sal_Bool bNoCollections, SwDocPositions nStart, SwDocPositions nEnde, sal_Bool& bCancel, FindRanges eFndRngs, const SearchOptions* pSearchOpt, const SfxItemSet* pReplSet ) { // OLE-Benachrichtigung abschalten !! SwDoc* pDoc = GetDoc(); Link aLnk( pDoc->GetOle2Link() ); pDoc->SetOle2Link( Link() ); sal_Bool bReplace = ( pSearchOpt && ( pSearchOpt->replaceString.getLength() || !rSet.Count() ) ) || (pReplSet && pReplSet->Count()); bool const bStartUndo = pDoc->GetIDocumentUndoRedo().DoesUndo() && bReplace; if (bStartUndo) { pDoc->GetIDocumentUndoRedo().StartUndo( UNDO_REPLACE, NULL ); } SwFindParaAttr aSwFindParaAttr( rSet, bNoCollections, pSearchOpt, pReplSet, *this ); sal_uLong nRet = FindAll(aSwFindParaAttr, nStart, nEnde, eFndRngs, bCancel ); pDoc->SetOle2Link( aLnk ); if( nRet && bReplace ) pDoc->SetModified(); if (bStartUndo) { pDoc->GetIDocumentUndoRedo().EndUndo( UNDO_REPLACE, NULL ); } return nRet; }