Line data Source code
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 <comphelper/string.hxx>
21 : #include <tools/urlobj.hxx>
22 : #include <hintids.hxx>
23 : #include <hints.hxx>
24 : #include <unotools/transliterationwrapper.hxx>
25 : #include <acmplwrd.hxx>
26 : #include <doc.hxx>
27 : #include <ndindex.hxx>
28 : #include <docary.hxx>
29 : #include <ndtxt.hxx>
30 : #include <pam.hxx>
31 : #include <pagedesc.hxx>
32 : #include <poolfmt.hxx>
33 : #include <calbck.hxx>
34 : #include <IDocumentStylePoolAccess.hxx>
35 : #include <editeng/svxacorr.hxx>
36 :
37 : #include <editeng/acorrcfg.hxx>
38 : #include <sfx2/docfile.hxx>
39 : #include <docsh.hxx>
40 :
41 : #include <vector>
42 :
43 : class SwAutoCompleteClient : public SwClient
44 : {
45 : SwAutoCompleteWord* pAutoCompleteWord;
46 : SwDoc* pDoc;
47 : #if OSL_DEBUG_LEVEL > 0
48 : static sal_uLong nSwAutoCompleteClientCount;
49 : #endif
50 : public:
51 : SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc);
52 : SwAutoCompleteClient(const SwAutoCompleteClient& rClient);
53 : virtual ~SwAutoCompleteClient();
54 :
55 : SwAutoCompleteClient& operator=(const SwAutoCompleteClient& rClient);
56 :
57 6596 : const SwDoc& GetDoc(){return *pDoc;}
58 : #if OSL_DEBUG_LEVEL > 0
59 : static sal_uLong GetElementCount() {return nSwAutoCompleteClientCount;}
60 : #endif
61 : protected:
62 : virtual void Modify( const SfxPoolItem* pOld, const SfxPoolItem *pNew) SAL_OVERRIDE;
63 : };
64 :
65 : typedef std::vector<SwAutoCompleteClient> SwAutoCompleteClientVector;
66 :
67 90 : class SwAutoCompleteWord_Impl
68 : {
69 : SwAutoCompleteClientVector aClientVector;
70 : SwAutoCompleteWord& rAutoCompleteWord;
71 : public:
72 90 : SwAutoCompleteWord_Impl(SwAutoCompleteWord& rParent) :
73 90 : rAutoCompleteWord(rParent){}
74 : void AddDocument(SwDoc& rDoc);
75 : void RemoveDocument(const SwDoc& rDoc);
76 : };
77 :
78 : typedef const SwDoc* SwDocPtr;
79 : typedef std::vector<SwDocPtr> SwDocPtrVector;
80 : class SwAutoCompleteString
81 : : public editeng::IAutoCompleteString
82 : {
83 : #if OSL_DEBUG_LEVEL > 0
84 : static sal_uLong nSwAutoCompleteStringCount;
85 : #endif
86 : SwDocPtrVector aSourceDocs;
87 : public:
88 : SwAutoCompleteString(const OUString& rStr, sal_Int32 nPos, sal_Int32 nLen);
89 :
90 : virtual ~SwAutoCompleteString();
91 : void AddDocument(const SwDoc& rDoc);
92 : //returns true if last document reference has been removed
93 : bool RemoveDocument(const SwDoc& rDoc);
94 : #if OSL_DEBUG_LEVEL > 0
95 : static sal_uLong GetElementCount() {return nSwAutoCompleteStringCount;}
96 : #endif
97 : };
98 : #if OSL_DEBUG_LEVEL > 0
99 : sal_uLong SwAutoCompleteClient::nSwAutoCompleteClientCount = 0;
100 : sal_uLong SwAutoCompleteString::nSwAutoCompleteStringCount = 0;
101 : #endif
102 :
103 10 : SwAutoCompleteClient::SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc) :
104 : pAutoCompleteWord(&rToTell),
105 10 : pDoc(&rSwDoc)
106 : {
107 10 : pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->Add(this);
108 : #if OSL_DEBUG_LEVEL > 0
109 : ++nSwAutoCompleteClientCount;
110 : #endif
111 10 : }
112 :
113 12 : SwAutoCompleteClient::SwAutoCompleteClient(const SwAutoCompleteClient& rClient) :
114 : SwClient(),
115 : pAutoCompleteWord(rClient.pAutoCompleteWord),
116 12 : pDoc(rClient.pDoc)
117 : {
118 12 : pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->Add(this);
119 : #if OSL_DEBUG_LEVEL > 0
120 : ++nSwAutoCompleteClientCount;
121 : #endif
122 12 : }
123 :
124 22 : SwAutoCompleteClient::~SwAutoCompleteClient()
125 : {
126 : #if OSL_DEBUG_LEVEL > 0
127 : --nSwAutoCompleteClientCount;
128 : #endif
129 22 : }
130 :
131 2 : SwAutoCompleteClient& SwAutoCompleteClient::operator=(const SwAutoCompleteClient& rClient)
132 : {
133 2 : pAutoCompleteWord = rClient.pAutoCompleteWord;
134 2 : pDoc = rClient.pDoc;
135 2 : if(rClient.GetRegisteredIn())
136 2 : ((SwModify*)rClient.GetRegisteredIn())->Add(this);
137 0 : else if(GetRegisteredIn())
138 0 : GetRegisteredInNonConst()->Remove(this);
139 2 : return *this;
140 : }
141 :
142 10 : void SwAutoCompleteClient::Modify( const SfxPoolItem* pOld, const SfxPoolItem *)
143 : {
144 10 : switch( pOld ? pOld->Which() : 0 )
145 : {
146 : case RES_REMOVE_UNO_OBJECT:
147 : case RES_OBJECTDYING:
148 10 : if( (void*)GetRegisteredIn() == ((SwPtrMsgPoolItem *)pOld)->pObject )
149 10 : ((SwModify*)GetRegisteredIn())->Remove(this);
150 10 : pAutoCompleteWord->DocumentDying(*pDoc);
151 10 : break;
152 : }
153 10 : }
154 :
155 6152 : void SwAutoCompleteWord_Impl::AddDocument(SwDoc& rDoc)
156 : {
157 6152 : SwAutoCompleteClientVector::iterator aIt;
158 6596 : for(aIt = aClientVector.begin(); aIt != aClientVector.end(); ++aIt)
159 : {
160 6586 : if(&aIt->GetDoc() == &rDoc)
161 12294 : return;
162 : }
163 10 : aClientVector.push_back(SwAutoCompleteClient(rAutoCompleteWord, rDoc));
164 : }
165 :
166 10 : void SwAutoCompleteWord_Impl::RemoveDocument(const SwDoc& rDoc)
167 : {
168 10 : SwAutoCompleteClientVector::iterator aIt;
169 10 : for(aIt = aClientVector.begin(); aIt != aClientVector.end(); ++aIt)
170 : {
171 10 : if(&aIt->GetDoc() == &rDoc)
172 : {
173 10 : aClientVector.erase(aIt);
174 20 : return;
175 : }
176 : }
177 : }
178 :
179 6148 : SwAutoCompleteString::SwAutoCompleteString(
180 : const OUString& rStr, sal_Int32 const nPos, sal_Int32 const nLen)
181 6148 : : editeng::IAutoCompleteString(rStr.copy(nPos, nLen))
182 : {
183 : #if OSL_DEBUG_LEVEL > 0
184 : ++nSwAutoCompleteStringCount;
185 : #endif
186 6148 : }
187 :
188 12296 : SwAutoCompleteString::~SwAutoCompleteString()
189 : {
190 : #if OSL_DEBUG_LEVEL > 0
191 : --nSwAutoCompleteStringCount;
192 : #endif
193 12296 : }
194 :
195 8720 : void SwAutoCompleteString::AddDocument(const SwDoc& rDoc)
196 : {
197 9164 : for(SwDocPtrVector::iterator aIt = aSourceDocs.begin(); aIt != aSourceDocs.end(); ++aIt)
198 : {
199 2574 : if( *aIt == &rDoc )
200 10850 : return;
201 : }
202 6590 : aSourceDocs.push_back(&rDoc);
203 : }
204 :
205 4052 : bool SwAutoCompleteString::RemoveDocument(const SwDoc& rDoc)
206 : {
207 4052 : for(SwDocPtrVector::iterator aIt = aSourceDocs.begin(); aIt != aSourceDocs.end(); ++aIt)
208 : {
209 2494 : if( *aIt == &rDoc )
210 : {
211 2494 : aSourceDocs.erase(aIt);
212 2494 : return aSourceDocs.empty();
213 : }
214 : }
215 1558 : return false;
216 : }
217 :
218 90 : SwAutoCompleteWord::SwAutoCompleteWord( sal_uInt16 nWords, sal_uInt16 nMWrdLen ) :
219 90 : pImpl(new SwAutoCompleteWord_Impl(*this)),
220 : nMaxCount( nWords ),
221 : nMinWrdLen( nMWrdLen ),
222 180 : bLockWordLst( false )
223 : {
224 90 : }
225 :
226 180 : SwAutoCompleteWord::~SwAutoCompleteWord()
227 : {
228 90 : m_WordList.DeleteAndDestroyAll(); // so the assertion below works
229 90 : delete pImpl;
230 : #if OSL_DEBUG_LEVEL > 0
231 : sal_uLong nStrings = SwAutoCompleteString::GetElementCount();
232 : sal_uLong nClients = SwAutoCompleteClient::GetElementCount();
233 : OSL_ENSURE(!nStrings && !nClients, "AutoComplete: clients or string count mismatch");
234 : #endif
235 90 : }
236 :
237 6152 : bool SwAutoCompleteWord::InsertWord( const OUString& rWord, SwDoc& rDoc )
238 : {
239 6152 : SwDocShell* pDocShell = rDoc.GetDocShell();
240 6152 : SfxMedium* pMedium = pDocShell ? pDocShell->GetMedium() : 0;
241 : // strings from help module should not be added
242 6152 : if( pMedium )
243 : {
244 6152 : const INetURLObject& rURL = pMedium->GetURLObject();
245 6152 : if ( rURL.GetProtocol() == INET_PROT_VND_SUN_STAR_HELP )
246 0 : return false;
247 : }
248 :
249 6152 : OUString aNewWord(rWord);
250 6152 : aNewWord = comphelper::string::remove(aNewWord, CH_TXTATR_INWORD);
251 6152 : aNewWord = comphelper::string::remove(aNewWord, CH_TXTATR_BREAKWORD);
252 :
253 6152 : pImpl->AddDocument(rDoc);
254 6152 : bool bRet = false;
255 6152 : sal_Int32 nWrdLen = aNewWord.getLength();
256 12316 : while( nWrdLen && '.' == aNewWord[ nWrdLen-1 ])
257 12 : --nWrdLen;
258 :
259 6152 : if( !bLockWordLst && nWrdLen >= nMinWrdLen )
260 : {
261 6148 : SwAutoCompleteString* pNew = new SwAutoCompleteString( aNewWord, 0, nWrdLen );
262 6148 : pNew->AddDocument(rDoc);
263 : std::pair<editeng::SortedAutoCompleteStrings::const_iterator, bool>
264 6148 : aInsPair = m_WordList.insert(pNew);
265 :
266 6148 : m_LookupTree.insert( OUString(aNewWord).copy(0, nWrdLen) );
267 :
268 6148 : if (aInsPair.second)
269 : {
270 3576 : bRet = true;
271 3576 : if (aLRULst.size() >= nMaxCount)
272 : {
273 : // the last one needs to be removed
274 : // so that there is space for the first one
275 1524 : SwAutoCompleteString* pDel = aLRULst.back();
276 1524 : aLRULst.pop_back();
277 1524 : m_WordList.erase(pDel);
278 1524 : delete pDel;
279 : }
280 3576 : aLRULst.push_front(pNew);
281 : }
282 : else
283 : {
284 2572 : delete pNew;
285 : // then move "up"
286 2572 : pNew = (SwAutoCompleteString*)(*aInsPair.first);
287 :
288 : // add the document to the already inserted string
289 2572 : pNew->AddDocument(rDoc);
290 :
291 : // move pNew to the front of the LRU list
292 2572 : SwAutoCompleteStringPtrDeque::iterator it = std::find( aLRULst.begin(), aLRULst.end(), pNew );
293 : OSL_ENSURE( aLRULst.end() != it, "String not found" );
294 2572 : if ( aLRULst.begin() != it && aLRULst.end() != it )
295 : {
296 2572 : aLRULst.erase( it );
297 2572 : aLRULst.push_front( pNew );
298 : }
299 : }
300 : }
301 6152 : return bRet;
302 : }
303 :
304 0 : void SwAutoCompleteWord::SetMaxCount( sal_uInt16 nNewMax )
305 : {
306 0 : if( nNewMax < nMaxCount && aLRULst.size() > nNewMax )
307 : {
308 : // remove the trailing ones
309 0 : sal_uInt16 nLRUIndex = nNewMax-1;
310 0 : while (nNewMax < m_WordList.size() && nLRUIndex < aLRULst.size())
311 : {
312 : editeng::SortedAutoCompleteStrings::const_iterator it =
313 0 : m_WordList.find(aLRULst[ nLRUIndex++ ]);
314 : OSL_ENSURE( m_WordList.end() != it, "String not found" );
315 0 : editeng::IAutoCompleteString *const pDel = *it;
316 0 : m_WordList.erase(it - m_WordList.begin());
317 0 : delete pDel;
318 : }
319 0 : aLRULst.erase( aLRULst.begin() + nNewMax - 1, aLRULst.end() );
320 : }
321 0 : nMaxCount = nNewMax;
322 0 : }
323 :
324 0 : void SwAutoCompleteWord::SetMinWordLen( sal_uInt16 n )
325 : {
326 : // Do you really want to remove all words that are less than the minWrdLen?
327 0 : if( n < nMinWrdLen )
328 : {
329 0 : for (size_t nPos = 0; nPos < m_WordList.size(); ++nPos)
330 0 : if (m_WordList[ nPos ]->GetAutoCompleteString().getLength() < n)
331 : {
332 : SwAutoCompleteString *const pDel =
333 0 : dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos]);
334 0 : m_WordList.erase(nPos);
335 :
336 0 : SwAutoCompleteStringPtrDeque::iterator it = std::find( aLRULst.begin(), aLRULst.end(), pDel );
337 : OSL_ENSURE( aLRULst.end() != it, "String not found" );
338 0 : aLRULst.erase( it );
339 0 : --nPos;
340 0 : delete pDel;
341 : }
342 : }
343 :
344 0 : nMinWrdLen = n;
345 0 : }
346 :
347 : /** Return all words matching a given prefix
348 : *
349 : * @param aMatch the prefix to search for
350 : * @param aWords the words to search in
351 : */
352 0 : bool SwAutoCompleteWord::GetWordsMatching(const OUString& aMatch, std::vector<OUString>& aWords) const
353 : {
354 0 : OUString aStringRoot = aMatch;
355 :
356 0 : std::vector<OUString> suggestions;
357 0 : m_LookupTree.findSuggestions(aStringRoot, suggestions);
358 :
359 0 : if (suggestions.empty())
360 : {
361 0 : return false;
362 : }
363 :
364 0 : for (size_t i = 0; i < suggestions.size(); i++)
365 : {
366 0 : aWords.push_back( suggestions[i] );
367 : }
368 :
369 0 : return true;
370 : }
371 :
372 0 : void SwAutoCompleteWord::CheckChangedList(
373 : const editeng::SortedAutoCompleteStrings& rNewLst)
374 : {
375 0 : size_t nMyLen = m_WordList.size(), nNewLen = rNewLst.size();
376 0 : size_t nMyPos = 0, nNewPos = 0;
377 :
378 0 : for( ; nMyPos < nMyLen && nNewPos < nNewLen; ++nMyPos, ++nNewPos )
379 : {
380 0 : const editeng::IAutoCompleteString * pStr = rNewLst[ nNewPos ];
381 0 : while (m_WordList[nMyPos] != pStr)
382 : {
383 : SwAutoCompleteString *const pDel =
384 0 : dynamic_cast<SwAutoCompleteString*>(m_WordList[nMyPos]);
385 0 : m_WordList.erase(nMyPos);
386 0 : SwAutoCompleteStringPtrDeque::iterator it = std::find( aLRULst.begin(), aLRULst.end(), pDel );
387 : OSL_ENSURE( aLRULst.end() != it, "String not found" );
388 0 : aLRULst.erase( it );
389 0 : delete pDel;
390 0 : if( nMyPos >= --nMyLen )
391 0 : break;
392 : }
393 : }
394 : // remove the elements at the end of the array
395 0 : if( nMyPos < nMyLen )
396 : {
397 : // clear LRU array first then delete the string object
398 0 : for( ; nNewPos < nMyLen; ++nNewPos )
399 : {
400 : SwAutoCompleteString *const pDel =
401 0 : dynamic_cast<SwAutoCompleteString*>(m_WordList[nNewPos]);
402 0 : SwAutoCompleteStringPtrDeque::iterator it = std::find( aLRULst.begin(), aLRULst.end(), pDel );
403 : OSL_ENSURE( aLRULst.end() != it, "String not found" );
404 0 : aLRULst.erase( it );
405 0 : delete pDel;
406 : }
407 : // remove from array
408 0 : m_WordList.erase(m_WordList.begin() + nMyPos,
409 0 : m_WordList.begin() + nMyLen);
410 : }
411 0 : }
412 :
413 10 : void SwAutoCompleteWord::DocumentDying(const SwDoc& rDoc)
414 : {
415 10 : pImpl->RemoveDocument(rDoc);
416 :
417 10 : SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
418 10 : const bool bDelete = !pACorr->GetSwFlags().bAutoCmpltKeepList;
419 4062 : for (size_t nPos = m_WordList.size(); nPos; nPos--)
420 : {
421 4052 : SwAutoCompleteString *const pCurrent = dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos - 1]);
422 4052 : if(pCurrent && pCurrent->RemoveDocument(rDoc) && bDelete)
423 : {
424 0 : m_WordList.erase(nPos - 1);
425 0 : SwAutoCompleteStringPtrDeque::iterator it = std::find( aLRULst.begin(), aLRULst.end(), pCurrent );
426 : OSL_ENSURE( aLRULst.end() != it, "word not found in LRU list" );
427 0 : aLRULst.erase( it );
428 0 : delete pCurrent;
429 : }
430 : }
431 280 : }
432 :
433 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|