| /************************************************************** |
| * |
| * 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 <UndoOverwrite.hxx> |
| |
| #include <tools/resid.hxx> |
| |
| #include <unotools/charclass.hxx> |
| #include <unotools/transliterationwrapper.hxx> |
| |
| #include <comphelper/processfactory.hxx> |
| |
| #include <doc.hxx> |
| #include <IDocumentUndoRedo.hxx> |
| #include <IShellCursorSupplier.hxx> |
| #include <swundo.hxx> // fuer die UndoIds |
| #include <pam.hxx> |
| #include <ndtxt.hxx> |
| #include <UndoCore.hxx> |
| #include <rolbck.hxx> |
| #include <acorrect.hxx> |
| #include <docary.hxx> |
| |
| #include <comcore.hrc> // #111827# |
| #include <undo.hrc> |
| |
| using namespace ::com::sun::star; |
| using namespace ::com::sun::star::i18n; |
| using namespace ::com::sun::star::uno; |
| |
| |
| //------------------------------------------------------------ |
| |
| // OVERWRITE |
| |
| |
| SwUndoOverwrite::SwUndoOverwrite( SwDoc* pDoc, SwPosition& rPos, |
| sal_Unicode cIns ) |
| : SwUndo(UNDO_OVERWRITE), |
| pRedlSaveData( 0 ), bGroup( sal_False ) |
| { |
| if( !pDoc->IsIgnoreRedline() && pDoc->GetRedlineTbl().Count() ) |
| { |
| SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(), |
| rPos.nNode, rPos.nContent.GetIndex()+1 ); |
| pRedlSaveData = new SwRedlineSaveDatas; |
| if( !FillSaveData( aPam, *pRedlSaveData, sal_False )) |
| delete pRedlSaveData, pRedlSaveData = 0; |
| } |
| |
| nSttNode = rPos.nNode.GetIndex(); |
| nSttCntnt = rPos.nContent.GetIndex(); |
| |
| SwTxtNode* pTxtNd = rPos.nNode.GetNode().GetTxtNode(); |
| ASSERT( pTxtNd, "Overwrite nicht im TextNode?" ); |
| |
| bInsChar = sal_True; |
| xub_StrLen nTxtNdLen = pTxtNd->GetTxt().Len(); |
| if( nSttCntnt < nTxtNdLen ) // kein reines Einfuegen ? |
| { |
| aDelStr.Insert( pTxtNd->GetTxt().GetChar( nSttCntnt ) ); |
| if( !pHistory ) |
| pHistory = new SwHistory; |
| SwRegHistory aRHst( *pTxtNd, pHistory ); |
| pHistory->CopyAttr( pTxtNd->GetpSwpHints(), nSttNode, 0, |
| nTxtNdLen, false ); |
| rPos.nContent++; |
| bInsChar = sal_False; |
| } |
| |
| sal_Bool bOldExpFlg = pTxtNd->IsIgnoreDontExpand(); |
| pTxtNd->SetIgnoreDontExpand( sal_True ); |
| |
| pTxtNd->InsertText( cIns, rPos.nContent, |
| IDocumentContentOperations::INS_EMPTYEXPAND ); |
| aInsStr.Insert( cIns ); |
| |
| if( !bInsChar ) |
| { |
| const SwIndex aTmpIndex( rPos.nContent, -2 ); |
| pTxtNd->EraseText( aTmpIndex, 1 ); |
| } |
| pTxtNd->SetIgnoreDontExpand( bOldExpFlg ); |
| |
| bCacheComment = false; |
| } |
| |
| SwUndoOverwrite::~SwUndoOverwrite() |
| { |
| delete pRedlSaveData; |
| } |
| |
| sal_Bool SwUndoOverwrite::CanGrouping( SwDoc* pDoc, SwPosition& rPos, |
| sal_Unicode cIns ) |
| { |
| /// ?? was ist mit nur eingefuegten Charaktern ??? |
| |
| // es kann nur das Loeschen von einzelnen char's zusammengefasst werden |
| if( rPos.nNode != nSttNode || !aInsStr.Len() || |
| ( !bGroup && aInsStr.Len() != 1 )) |
| return sal_False; |
| |
| // ist der Node ueberhaupt ein TextNode? |
| SwTxtNode * pDelTxtNd = rPos.nNode.GetNode().GetTxtNode(); |
| if( !pDelTxtNd || |
| ( pDelTxtNd->GetTxt().Len() != rPos.nContent.GetIndex() && |
| rPos.nContent.GetIndex() != ( nSttCntnt + aInsStr.Len() ))) |
| return sal_False; |
| |
| CharClass& rCC = GetAppCharClass(); |
| |
| // befrage das einzufuegende Charakter |
| if (( CH_TXTATR_BREAKWORD == cIns || CH_TXTATR_INWORD == cIns ) || |
| rCC.isLetterNumeric( String( cIns ), 0 ) != |
| rCC.isLetterNumeric( aInsStr, aInsStr.Len()-1 ) ) |
| return sal_False; |
| |
| { |
| SwRedlineSaveDatas* pTmpSav = new SwRedlineSaveDatas; |
| SwPaM aPam( rPos.nNode, rPos.nContent.GetIndex(), |
| rPos.nNode, rPos.nContent.GetIndex()+1 ); |
| |
| if( !FillSaveData( aPam, *pTmpSav, sal_False )) |
| delete pTmpSav, pTmpSav = 0; |
| |
| sal_Bool bOk = ( !pRedlSaveData && !pTmpSav ) || |
| ( pRedlSaveData && pTmpSav && |
| SwUndo::CanRedlineGroup( *pRedlSaveData, *pTmpSav, |
| nSttCntnt > rPos.nContent.GetIndex() )); |
| delete pTmpSav; |
| if( !bOk ) |
| return sal_False; |
| |
| pDoc->DeleteRedline( aPam, false, USHRT_MAX ); |
| } |
| |
| // Ok, die beiden 'Overwrites' koennen zusammen gefasst werden, also |
| // 'verschiebe' das enstprechende Zeichen |
| if( !bInsChar ) |
| { |
| if( rPos.nContent.GetIndex() < pDelTxtNd->GetTxt().Len() ) |
| { |
| aDelStr.Insert( pDelTxtNd->GetTxt().GetChar(rPos.nContent.GetIndex()) ); |
| rPos.nContent++; |
| } |
| else |
| bInsChar = sal_True; |
| } |
| |
| sal_Bool bOldExpFlg = pDelTxtNd->IsIgnoreDontExpand(); |
| pDelTxtNd->SetIgnoreDontExpand( sal_True ); |
| |
| pDelTxtNd->InsertText( cIns, rPos.nContent, |
| IDocumentContentOperations::INS_EMPTYEXPAND ); |
| aInsStr.Insert( cIns ); |
| |
| if( !bInsChar ) |
| { |
| const SwIndex aTmpIndex( rPos.nContent, -2 ); |
| pDelTxtNd->EraseText( aTmpIndex, 1 ); |
| } |
| pDelTxtNd->SetIgnoreDontExpand( bOldExpFlg ); |
| |
| bGroup = sal_True; |
| return sal_True; |
| } |
| |
| |
| |
| |
| |
| void SwUndoOverwrite::UndoImpl(::sw::UndoRedoContext & rContext) |
| { |
| SwDoc *const pDoc = & rContext.GetDoc(); |
| SwPaM *const pAktPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); |
| |
| pAktPam->DeleteMark(); |
| pAktPam->GetPoint()->nNode = nSttNode; |
| SwTxtNode* pTxtNd = pAktPam->GetNode()->GetTxtNode(); |
| ASSERT( pTxtNd, "Overwrite nicht im TextNode?" ); |
| SwIndex& rIdx = pAktPam->GetPoint()->nContent; |
| rIdx.Assign( pTxtNd, nSttCntnt ); |
| |
| SwAutoCorrExceptWord* pACEWord = pDoc->GetAutoCorrExceptWord(); |
| if( pACEWord ) |
| { |
| if( 1 == aInsStr.Len() && 1 == aDelStr.Len() ) |
| pACEWord->CheckChar( *pAktPam->GetPoint(), aDelStr.GetChar( 0 ) ); |
| pDoc->SetAutoCorrExceptWord( 0 ); |
| } |
| |
| // wurde nicht nur ueberschieben sondern auch geinsertet, so loesche |
| // den Ueberhang |
| if( aInsStr.Len() > aDelStr.Len() ) |
| { |
| rIdx += aDelStr.Len(); |
| pTxtNd->EraseText( rIdx, aInsStr.Len() - aDelStr.Len() ); |
| rIdx = nSttCntnt; |
| } |
| |
| if( aDelStr.Len() ) |
| { |
| String aTmpStr( '1' ); |
| sal_Unicode* pTmpStr = aTmpStr.GetBufferAccess(); |
| |
| sal_Bool bOldExpFlg = pTxtNd->IsIgnoreDontExpand(); |
| pTxtNd->SetIgnoreDontExpand( sal_True ); |
| |
| rIdx++; |
| for( xub_StrLen n = 0; n < aDelStr.Len(); n++ ) |
| { |
| // einzeln, damit die Attribute stehen bleiben !!! |
| *pTmpStr = aDelStr.GetChar( n ); |
| pTxtNd->InsertText( aTmpStr, rIdx /*???, SETATTR_NOTXTATRCHR*/ ); |
| rIdx -= 2; |
| pTxtNd->EraseText( rIdx, 1 ); |
| rIdx += 2; |
| } |
| pTxtNd->SetIgnoreDontExpand( bOldExpFlg ); |
| rIdx--; |
| } |
| if( pHistory ) |
| { |
| if( pTxtNd->GetpSwpHints() ) |
| pTxtNd->ClearSwpHintsArr( false ); |
| pHistory->TmpRollback( pDoc, 0, false ); |
| } |
| |
| if( pAktPam->GetMark()->nContent.GetIndex() != nSttCntnt ) |
| { |
| pAktPam->SetMark(); |
| pAktPam->GetMark()->nContent = nSttCntnt; |
| } |
| |
| if( pRedlSaveData ) |
| SetSaveData( *pDoc, *pRedlSaveData ); |
| } |
| |
| void SwUndoOverwrite::RepeatImpl(::sw::RepeatContext & rContext) |
| { |
| SwPaM *const pAktPam = & rContext.GetRepeatPaM(); |
| if( !aInsStr.Len() || pAktPam->HasMark() ) |
| return; |
| |
| SwDoc & rDoc = rContext.GetDoc(); |
| |
| { |
| ::sw::GroupUndoGuard const undoGuard(rDoc.GetIDocumentUndoRedo()); |
| rDoc.Overwrite(*pAktPam, aInsStr.GetChar(0)); |
| } |
| for( xub_StrLen n = 1; n < aInsStr.Len(); ++n ) |
| rDoc.Overwrite( *pAktPam, aInsStr.GetChar( n ) ); |
| } |
| |
| void SwUndoOverwrite::RedoImpl(::sw::UndoRedoContext & rContext) |
| { |
| SwDoc *const pDoc = & rContext.GetDoc(); |
| SwPaM *const pAktPam(& rContext.GetCursorSupplier().CreateNewShellCursor()); |
| |
| pAktPam->DeleteMark(); |
| pAktPam->GetPoint()->nNode = nSttNode; |
| SwTxtNode* pTxtNd = pAktPam->GetNode()->GetTxtNode(); |
| ASSERT( pTxtNd, "Overwrite nicht im TextNode?" ); |
| SwIndex& rIdx = pAktPam->GetPoint()->nContent; |
| |
| if( pRedlSaveData ) |
| { |
| rIdx.Assign( pTxtNd, nSttCntnt ); |
| pAktPam->SetMark(); |
| pAktPam->GetMark()->nContent += aInsStr.Len(); |
| pDoc->DeleteRedline( *pAktPam, false, USHRT_MAX ); |
| pAktPam->DeleteMark(); |
| } |
| rIdx.Assign( pTxtNd, aDelStr.Len() ? nSttCntnt+1 : nSttCntnt ); |
| |
| sal_Bool bOldExpFlg = pTxtNd->IsIgnoreDontExpand(); |
| pTxtNd->SetIgnoreDontExpand( sal_True ); |
| |
| for( xub_StrLen n = 0; n < aInsStr.Len(); n++ ) |
| { |
| // einzeln, damit die Attribute stehen bleiben !!! |
| pTxtNd->InsertText( aInsStr.GetChar( n ), rIdx, |
| IDocumentContentOperations::INS_EMPTYEXPAND ); |
| if( n < aDelStr.Len() ) |
| { |
| rIdx -= 2; |
| pTxtNd->EraseText( rIdx, 1 ); |
| rIdx += n+1 < aDelStr.Len() ? 2 : 1; |
| } |
| } |
| pTxtNd->SetIgnoreDontExpand( bOldExpFlg ); |
| |
| // alte Anfangs-Position vom UndoNodes-Array zurueckholen |
| if( pHistory ) |
| pHistory->SetTmpEnd( pHistory->Count() ); |
| if( pAktPam->GetMark()->nContent.GetIndex() != nSttCntnt ) |
| { |
| pAktPam->SetMark(); |
| pAktPam->GetMark()->nContent = nSttCntnt; |
| } |
| } |
| |
| SwRewriter SwUndoOverwrite::GetRewriter() const |
| { |
| SwRewriter aResult; |
| |
| String aString; |
| |
| aString += String(SW_RES(STR_START_QUOTE)); |
| aString += ShortenString(aInsStr, nUndoStringLength, |
| String(SW_RES(STR_LDOTS))); |
| aString += String(SW_RES(STR_END_QUOTE)); |
| |
| aResult.AddRule(UNDO_ARG1, aString); |
| |
| return aResult; |
| } |
| |
| //------------------------------------------------------------ |
| |
| struct _UndoTransliterate_Data |
| { |
| String sText; |
| SwHistory* pHistory; |
| Sequence< sal_Int32 >* pOffsets; |
| sal_uLong nNdIdx; |
| xub_StrLen nStart, nLen; |
| |
| _UndoTransliterate_Data( sal_uLong nNd, xub_StrLen nStt, xub_StrLen nStrLen, const String& rTxt ) |
| : sText( rTxt ), pHistory( 0 ), pOffsets( 0 ), |
| nNdIdx( nNd ), nStart( nStt ), nLen( nStrLen ) |
| {} |
| ~_UndoTransliterate_Data() { delete pOffsets; delete pHistory; } |
| |
| void SetChangeAtNode( SwDoc& rDoc ); |
| }; |
| |
| SwUndoTransliterate::SwUndoTransliterate( |
| const SwPaM& rPam, |
| const utl::TransliterationWrapper& rTrans ) |
| : SwUndo( UNDO_TRANSLITERATE ), SwUndRng( rPam ), nType( rTrans.getType() ) |
| { |
| } |
| |
| SwUndoTransliterate::~SwUndoTransliterate() |
| { |
| for (size_t i = 0; i < aChanges.size(); ++i) |
| delete aChanges[i]; |
| } |
| |
| void SwUndoTransliterate::UndoImpl(::sw::UndoRedoContext & rContext) |
| { |
| SwDoc & rDoc = rContext.GetDoc(); |
| |
| // since the changes were added to the vector from the end of the string/node towards |
| // the start, we need to revert them from the start towards the end now to keep the |
| // offset information of the undo data in sync with the changing text. |
| // Thus we need to iterate from the end of the vector to the start |
| for (sal_Int32 i = aChanges.size() - 1; i >= 0; --i) |
| aChanges[i]->SetChangeAtNode( rDoc ); |
| |
| AddUndoRedoPaM(rContext, true); |
| } |
| |
| void SwUndoTransliterate::RedoImpl(::sw::UndoRedoContext & rContext) |
| { |
| SwPaM & rPam( AddUndoRedoPaM(rContext) ); |
| DoTransliterate(rContext.GetDoc(), rPam); |
| } |
| |
| void SwUndoTransliterate::RepeatImpl(::sw::RepeatContext & rContext) |
| { |
| DoTransliterate(rContext.GetDoc(), rContext.GetRepeatPaM()); |
| } |
| |
| void SwUndoTransliterate::DoTransliterate(SwDoc & rDoc, SwPaM & rPam) |
| { |
| utl::TransliterationWrapper aTrans( ::comphelper::getProcessServiceFactory(), nType ); |
| rDoc.TransliterateText( rPam, aTrans ); |
| } |
| |
| void SwUndoTransliterate::AddChanges( SwTxtNode& rTNd, |
| xub_StrLen nStart, xub_StrLen nLen, |
| uno::Sequence <sal_Int32>& rOffsets ) |
| { |
| long nOffsLen = rOffsets.getLength(); |
| _UndoTransliterate_Data* pNew = new _UndoTransliterate_Data( |
| rTNd.GetIndex(), nStart, (xub_StrLen)nOffsLen, |
| rTNd.GetTxt().Copy( nStart, nLen )); |
| |
| aChanges.push_back( pNew ); |
| |
| const sal_Int32* pOffsets = rOffsets.getConstArray(); |
| // where did we need less memory ? |
| const sal_Int32* p = pOffsets; |
| for( long n = 0; n < nOffsLen; ++n, ++p ) |
| if( *p != ( nStart + n )) |
| { |
| // create the Offset array |
| pNew->pOffsets = new Sequence <sal_Int32> ( nLen ); |
| sal_Int32* pIdx = pNew->pOffsets->getArray(); |
| p = pOffsets; |
| long nMyOff, nNewVal = nStart; |
| for( n = 0, nMyOff = nStart; n < nOffsLen; ++p, ++n, ++nMyOff ) |
| { |
| if( *p < nMyOff ) |
| { |
| // something is deleted |
| nMyOff = *p; |
| *(pIdx-1) = nNewVal++; |
| } |
| else if( *p > nMyOff ) |
| { |
| for( ; *p > nMyOff; ++nMyOff ) |
| *pIdx++ = nNewVal; |
| --nMyOff; |
| --n; |
| --p; |
| } |
| else |
| *pIdx++ = nNewVal++; |
| } |
| |
| // and then we need to save the attributes/bookmarks |
| // but this data must moved every time to the last in the chain! |
| for (size_t i = 0; i + 1 < aChanges.size(); ++i) // check all changes but not the current one |
| { |
| _UndoTransliterate_Data* pD = aChanges[i]; |
| if( pD->nNdIdx == pNew->nNdIdx && pD->pHistory ) |
| { |
| // same node and have a history? |
| pNew->pHistory = pD->pHistory; |
| pD->pHistory = 0; |
| break; // more can't exist |
| } |
| } |
| |
| if( !pNew->pHistory ) |
| { |
| pNew->pHistory = new SwHistory; |
| SwRegHistory aRHst( rTNd, pNew->pHistory ); |
| pNew->pHistory->CopyAttr( rTNd.GetpSwpHints(), |
| pNew->nNdIdx, 0, rTNd.GetTxt().Len(), false ); |
| } |
| break; |
| } |
| } |
| |
| void _UndoTransliterate_Data::SetChangeAtNode( SwDoc& rDoc ) |
| { |
| SwTxtNode* pTNd = rDoc.GetNodes()[ nNdIdx ]->GetTxtNode(); |
| if( pTNd ) |
| { |
| Sequence <sal_Int32> aOffsets( pOffsets ? pOffsets->getLength() : nLen ); |
| if( pOffsets ) |
| aOffsets = *pOffsets; |
| else |
| { |
| sal_Int32* p = aOffsets.getArray(); |
| for( xub_StrLen n = 0; n < nLen; ++n, ++p ) |
| *p = n + nStart; |
| } |
| pTNd->ReplaceTextOnly( nStart, nLen, sText, aOffsets ); |
| |
| if( pHistory ) |
| { |
| if( pTNd->GetpSwpHints() ) |
| pTNd->ClearSwpHintsArr( false ); |
| pHistory->TmpRollback( &rDoc, 0, false ); |
| pHistory->SetTmpEnd( pHistory->Count() ); |
| } |
| } |
| } |
| |
| |