1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <algorithm>
21 
22 #include <com/sun/star/i18n/ScriptType.hpp>
23 
24 #include <editeng/langitem.hxx>
25 #include <osl/diagnose.h>
26 #include <svl/languageoptions.hxx>
27 #include <vcl/commandevent.hxx>
28 
29 #include <hintids.hxx>
30 #include <extinput.hxx>
31 #include <doc.hxx>
32 #include <IDocumentUndoRedo.hxx>
33 #include <index.hxx>
34 #include <ndtxt.hxx>
35 #include <swundo.hxx>
36 
37 using namespace ::com::sun::star;
38 
SwExtTextInput(const SwPaM & rPam,Ring * pRing)39 SwExtTextInput::SwExtTextInput( const SwPaM& rPam, Ring* pRing )
40     : SwPaM( *rPam.GetPoint(), static_cast<SwPaM*>(pRing) ),
41     m_eInputLanguage(LANGUAGE_DONTKNOW)
42 {
43     m_bIsOverwriteCursor = false;
44     m_bInsText = true;
45 }
46 
~SwExtTextInput()47 SwExtTextInput::~SwExtTextInput()
48 {
49     SwDoc& rDoc = GetDoc();
50     if (rDoc.IsInDtor()) { return; /* #i58606# */ }
51 
52     SwTextNode* pTNd = GetPoint()->nNode.GetNode().GetTextNode();
53     if( !pTNd )
54         return;
55 
56     SwIndex& rIdx = GetPoint()->nContent;
57     sal_Int32 nSttCnt = rIdx.GetIndex();
58     sal_Int32 nEndCnt = GetMark()->nContent.GetIndex();
59     if( nEndCnt == nSttCnt )
60         return;
61 
62     // Prevent IME edited text being grouped with non-IME edited text.
63     bool bKeepGroupUndo = rDoc.GetIDocumentUndoRedo().DoesGroupUndo();
64     rDoc.GetIDocumentUndoRedo().DoGroupUndo(false);
65     if( nEndCnt < nSttCnt )
66     {
67         std::swap(nSttCnt, nEndCnt);
68     }
69 
70     // In order to get Undo/Redlining etc. working correctly,
71     // we need to go through the Doc interface
72     rIdx = nSttCnt;
73     const OUString sText( pTNd->GetText().copy(nSttCnt, nEndCnt - nSttCnt));
74     if( m_bIsOverwriteCursor && !m_sOverwriteText.isEmpty() )
75     {
76         const sal_Int32 nLen = sText.getLength();
77         const sal_Int32 nOWLen = m_sOverwriteText.getLength();
78         if( nLen > nOWLen )
79         {
80             rIdx += nOWLen;
81             pTNd->EraseText( rIdx, nLen - nOWLen );
82             rIdx = nSttCnt;
83             pTNd->ReplaceText( rIdx, nOWLen, m_sOverwriteText );
84             if( m_bInsText )
85             {
86                 rIdx = nSttCnt;
87                 rDoc.GetIDocumentUndoRedo().StartUndo( SwUndoId::OVERWRITE, nullptr );
88                 rDoc.getIDocumentContentOperations().Overwrite( *this, sText.copy( 0, nOWLen ) );
89                 rDoc.getIDocumentContentOperations().InsertString( *this, sText.copy( nOWLen ) );
90                 rDoc.GetIDocumentUndoRedo().EndUndo( SwUndoId::OVERWRITE, nullptr );
91             }
92         }
93         else
94         {
95             pTNd->ReplaceText( rIdx, nLen, m_sOverwriteText.copy( 0, nLen ));
96             if( m_bInsText )
97             {
98                 rIdx = nSttCnt;
99                 rDoc.getIDocumentContentOperations().Overwrite( *this, sText );
100             }
101         }
102     }
103     else
104     {
105         pTNd->EraseText( rIdx, nEndCnt - nSttCnt );
106 
107         if( m_bInsText )
108         {
109             rDoc.getIDocumentContentOperations().InsertString( *this, sText );
110         }
111     }
112     rDoc.GetIDocumentUndoRedo().DoGroupUndo(bKeepGroupUndo);
113     if (m_eInputLanguage == LANGUAGE_DONTKNOW)
114         return;
115 
116     sal_uInt16 nWhich = RES_CHRATR_LANGUAGE;
117     sal_Int16 nScriptType = SvtLanguageOptions::GetI18NScriptTypeOfLanguage(m_eInputLanguage);
118     switch(nScriptType)
119     {
120         case  i18n::ScriptType::ASIAN:
121             nWhich = RES_CHRATR_CJK_LANGUAGE; break;
122         case  i18n::ScriptType::COMPLEX:
123             nWhich = RES_CHRATR_CTL_LANGUAGE; break;
124     }
125     // #i41974# Only set language attribute for CJK/CTL scripts.
126     if (RES_CHRATR_LANGUAGE != nWhich && pTNd->GetLang( nSttCnt, nEndCnt-nSttCnt, nScriptType) != m_eInputLanguage)
127     {
128         SvxLanguageItem aLangItem( m_eInputLanguage, nWhich );
129         rIdx = nSttCnt;
130         GetMark()->nContent = nEndCnt;
131         rDoc.getIDocumentContentOperations().InsertPoolItem(*this, aLangItem );
132     }
133 }
134 
SetInputData(const CommandExtTextInputData & rData)135 void SwExtTextInput::SetInputData( const CommandExtTextInputData& rData )
136 {
137     SwTextNode* pTNd = GetPoint()->nNode.GetNode().GetTextNode();
138     if( !pTNd )
139         return;
140 
141     sal_Int32 nSttCnt = GetPoint()->nContent.GetIndex();
142     sal_Int32 nEndCnt = GetMark()->nContent.GetIndex();
143     if( nEndCnt < nSttCnt )
144     {
145         std::swap(nSttCnt, nEndCnt);
146     }
147 
148     SwIndex aIdx( pTNd, nSttCnt );
149     const OUString& rNewStr = rData.GetText();
150 
151     if( m_bIsOverwriteCursor && !m_sOverwriteText.isEmpty() )
152     {
153         sal_Int32 nReplace = nEndCnt - nSttCnt;
154         const sal_Int32 nNewLen = rNewStr.getLength();
155         if( nNewLen < nReplace )
156         {
157             // We have to insert some characters from the saved original text
158             nReplace -= nNewLen;
159             aIdx += nNewLen;
160             pTNd->ReplaceText( aIdx, nReplace,
161                         m_sOverwriteText.copy( nNewLen, nReplace ));
162             aIdx = nSttCnt;
163             nReplace = nNewLen;
164         }
165         else
166         {
167             const sal_Int32 nOWLen = m_sOverwriteText.getLength();
168             if( nOWLen < nReplace )
169             {
170                 aIdx += nOWLen;
171                 pTNd->EraseText( aIdx, nReplace-nOWLen );
172                 aIdx = nSttCnt;
173                 nReplace = nOWLen;
174             }
175             else
176             {
177                 nReplace = std::min(nOWLen, nNewLen);
178             }
179         }
180 
181         pTNd->ReplaceText( aIdx, nReplace, rNewStr );
182         if( !HasMark() )
183             SetMark();
184         GetMark()->nContent = aIdx;
185     }
186     else
187     {
188         if( nSttCnt < nEndCnt )
189         {
190             pTNd->EraseText( aIdx, nEndCnt - nSttCnt );
191         }
192 
193         pTNd->InsertText( rNewStr, aIdx,
194                 SwInsertFlags::EMPTYEXPAND );
195         if( !HasMark() )
196             SetMark();
197     }
198 
199     GetPoint()->nContent = nSttCnt;
200 
201     m_aAttrs.clear();
202     if( rData.GetTextAttr() )
203     {
204         const ExtTextInputAttr *pAttrs = rData.GetTextAttr();
205         m_aAttrs.insert( m_aAttrs.begin(), pAttrs, pAttrs + rData.GetText().getLength() );
206     }
207 }
208 
SetOverwriteCursor(bool bFlag)209 void SwExtTextInput::SetOverwriteCursor( bool bFlag )
210 {
211     m_bIsOverwriteCursor = bFlag;
212     if (!m_bIsOverwriteCursor)
213         return;
214 
215     const SwTextNode *const pTNd = GetPoint()->nNode.GetNode().GetTextNode();
216     if (!pTNd)
217         return;
218 
219     const sal_Int32 nSttCnt = GetPoint()->nContent.GetIndex();
220     const sal_Int32 nEndCnt = GetMark()->nContent.GetIndex();
221     m_sOverwriteText = pTNd->GetText().copy( std::min(nSttCnt, nEndCnt) );
222     if( m_sOverwriteText.isEmpty() )
223         return;
224 
225     const sal_Int32 nInPos = m_sOverwriteText.indexOf( CH_TXTATR_INWORD );
226     const sal_Int32 nBrkPos = m_sOverwriteText.indexOf( CH_TXTATR_BREAKWORD );
227 
228     // Find the first attr found, if any.
229     sal_Int32 nPos = std::min(nInPos, nBrkPos);
230     if (nPos<0)
231     {
232         nPos = std::max(nInPos, nBrkPos);
233     }
234     if (nPos>=0)
235     {
236         m_sOverwriteText = m_sOverwriteText.copy( 0, nPos );
237     }
238 }
239 
240 // The Doc interfaces
241 
CreateExtTextInput(const SwPaM & rPam)242 SwExtTextInput* SwDoc::CreateExtTextInput( const SwPaM& rPam )
243 {
244     SwExtTextInput* pNew = new SwExtTextInput( rPam, mpExtInputRing );
245     if( !mpExtInputRing )
246         mpExtInputRing = pNew;
247     pNew->SetMark();
248     return pNew;
249 }
250 
DeleteExtTextInput(SwExtTextInput * pDel)251 void SwDoc::DeleteExtTextInput( SwExtTextInput* pDel )
252 {
253     if( pDel == mpExtInputRing )
254     {
255         if( pDel->GetNext() != mpExtInputRing )
256             mpExtInputRing = pDel->GetNext();
257         else
258             mpExtInputRing = nullptr;
259     }
260     delete pDel;
261 }
262 
GetExtTextInput(const SwNode & rNd,sal_Int32 nContentPos) const263 SwExtTextInput* SwDoc::GetExtTextInput( const SwNode& rNd,
264                                         sal_Int32 nContentPos ) const
265 {
266     SwExtTextInput* pRet = nullptr;
267     if( mpExtInputRing )
268     {
269         sal_uLong nNdIdx = rNd.GetIndex();
270         SwExtTextInput* pTmp = mpExtInputRing;
271         do {
272             sal_uLong nPt = pTmp->GetPoint()->nNode.GetIndex(),
273                   nMk = pTmp->GetMark()->nNode.GetIndex();
274             sal_Int32 nPtCnt = pTmp->GetPoint()->nContent.GetIndex();
275             sal_Int32 nMkCnt = pTmp->GetMark()->nContent.GetIndex();
276 
277             if( nPt < nMk || ( nPt == nMk && nPtCnt < nMkCnt ))
278             {
279                 sal_uLong nTmp = nMk; nMk = nPt; nPt = nTmp;
280                 sal_Int32 nTmp2 = nMkCnt; nMkCnt = nPtCnt; nPtCnt = nTmp2;
281             }
282 
283             if( nMk <= nNdIdx && nNdIdx <= nPt &&
284                 ( nContentPos<0 ||
285                     ( nMkCnt <= nContentPos && nContentPos <= nPtCnt )))
286             {
287                 pRet = pTmp;
288                 break;
289             }
290             pTmp = pTmp->GetNext();
291         } while ( pTmp!=mpExtInputRing );
292     }
293     return pRet;
294 }
295 
GetExtTextInput() const296 SwExtTextInput* SwDoc::GetExtTextInput() const
297 {
298     OSL_ENSURE( !mpExtInputRing || !mpExtInputRing->IsMultiSelection(),
299             "more than one InputEngine available" );
300     return mpExtInputRing;
301 }
302 
303 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
304