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 122699 : 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 59 : class SwAutoCompleteWord_Impl
68 : {
69 : SwAutoCompleteClientVector aClientVector;
70 : SwAutoCompleteWord& rAutoCompleteWord;
71 : public:
72 59 : explicit SwAutoCompleteWord_Impl(SwAutoCompleteWord& rParent) :
73 59 : 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 32 : SwAutoCompleteClient::SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc) :
104 : pAutoCompleteWord(&rToTell),
105 32 : pDoc(&rSwDoc)
106 : {
107 32 : pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->Add(this);
108 : #if OSL_DEBUG_LEVEL > 0
109 : ++nSwAutoCompleteClientCount;
110 : #endif
111 32 : }
112 :
113 33 : SwAutoCompleteClient::SwAutoCompleteClient(const SwAutoCompleteClient& rClient) :
114 : SwClient(),
115 : pAutoCompleteWord(rClient.pAutoCompleteWord),
116 33 : pDoc(rClient.pDoc)
117 : {
118 33 : pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->Add(this);
119 : #if OSL_DEBUG_LEVEL > 0
120 : ++nSwAutoCompleteClientCount;
121 : #endif
122 33 : }
123 :
124 65 : SwAutoCompleteClient::~SwAutoCompleteClient()
125 : {
126 : #if OSL_DEBUG_LEVEL > 0
127 : --nSwAutoCompleteClientCount;
128 : #endif
129 65 : }
130 :
131 1 : SwAutoCompleteClient& SwAutoCompleteClient::operator=(const SwAutoCompleteClient& rClient)
132 : {
133 1 : pAutoCompleteWord = rClient.pAutoCompleteWord;
134 1 : pDoc = rClient.pDoc;
135 1 : if(rClient.GetRegisteredIn())
136 1 : const_cast<SwModify*>(rClient.GetRegisteredIn())->Add(this);
137 0 : else if(GetRegisteredIn())
138 0 : GetRegisteredInNonConst()->Remove(this);
139 1 : return *this;
140 : }
141 :
142 32 : void SwAutoCompleteClient::Modify( const SfxPoolItem* pOld, const SfxPoolItem *)
143 : {
144 32 : switch( pOld ? pOld->Which() : 0 )
145 : {
146 : case RES_REMOVE_UNO_OBJECT:
147 : case RES_OBJECTDYING:
148 32 : if( static_cast<void*>(GetRegisteredIn()) == static_cast<const SwPtrMsgPoolItem *>(pOld)->pObject )
149 32 : static_cast<SwModify*>(GetRegisteredIn())->Remove(this);
150 32 : pAutoCompleteWord->DocumentDying(*pDoc);
151 32 : break;
152 : }
153 32 : }
154 :
155 121802 : void SwAutoCompleteWord_Impl::AddDocument(SwDoc& rDoc)
156 : {
157 121802 : SwAutoCompleteClientVector::iterator aIt;
158 122699 : for(aIt = aClientVector.begin(); aIt != aClientVector.end(); ++aIt)
159 : {
160 122667 : if(&aIt->GetDoc() == &rDoc)
161 243572 : return;
162 : }
163 32 : aClientVector.push_back(SwAutoCompleteClient(rAutoCompleteWord, rDoc));
164 : }
165 :
166 32 : void SwAutoCompleteWord_Impl::RemoveDocument(const SwDoc& rDoc)
167 : {
168 32 : SwAutoCompleteClientVector::iterator aIt;
169 32 : for(aIt = aClientVector.begin(); aIt != aClientVector.end(); ++aIt)
170 : {
171 32 : if(&aIt->GetDoc() == &rDoc)
172 : {
173 32 : aClientVector.erase(aIt);
174 64 : return;
175 : }
176 : }
177 : }
178 :
179 121800 : SwAutoCompleteString::SwAutoCompleteString(
180 : const OUString& rStr, sal_Int32 const nPos, sal_Int32 const nLen)
181 121800 : : editeng::IAutoCompleteString(rStr.copy(nPos, nLen))
182 : {
183 : #if OSL_DEBUG_LEVEL > 0
184 : ++nSwAutoCompleteStringCount;
185 : #endif
186 121800 : }
187 :
188 243600 : SwAutoCompleteString::~SwAutoCompleteString()
189 : {
190 : #if OSL_DEBUG_LEVEL > 0
191 : --nSwAutoCompleteStringCount;
192 : #endif
193 243600 : }
194 :
195 239654 : void SwAutoCompleteString::AddDocument(const SwDoc& rDoc)
196 : {
197 240361 : for(SwDocPtrVector::iterator aIt = aSourceDocs.begin(); aIt != aSourceDocs.end(); ++aIt)
198 : {
199 117828 : if( *aIt == &rDoc )
200 356775 : return;
201 : }
202 122533 : aSourceDocs.push_back(&rDoc);
203 : }
204 :
205 2133 : bool SwAutoCompleteString::RemoveDocument(const SwDoc& rDoc)
206 : {
207 2323 : for(SwDocPtrVector::iterator aIt = aSourceDocs.begin(); aIt != aSourceDocs.end(); ++aIt)
208 : {
209 1956 : if( *aIt == &rDoc )
210 : {
211 1766 : aSourceDocs.erase(aIt);
212 1766 : return aSourceDocs.empty();
213 : }
214 : }
215 367 : return false;
216 : }
217 :
218 59 : SwAutoCompleteWord::SwAutoCompleteWord( sal_uInt16 nWords, sal_uInt16 nMWrdLen ) :
219 59 : pImpl(new SwAutoCompleteWord_Impl(*this)),
220 : nMaxCount( nWords ),
221 : nMinWrdLen( nMWrdLen ),
222 118 : bLockWordLst( false )
223 : {
224 59 : }
225 :
226 118 : SwAutoCompleteWord::~SwAutoCompleteWord()
227 : {
228 59 : m_WordList.DeleteAndDestroyAll(); // so the assertion below works
229 59 : 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 59 : }
236 :
237 121802 : bool SwAutoCompleteWord::InsertWord( const OUString& rWord, SwDoc& rDoc )
238 : {
239 121802 : SwDocShell* pDocShell = rDoc.GetDocShell();
240 121802 : SfxMedium* pMedium = pDocShell ? pDocShell->GetMedium() : 0;
241 : // strings from help module should not be added
242 121802 : if( pMedium )
243 : {
244 121802 : const INetURLObject& rURL = pMedium->GetURLObject();
245 121802 : if ( rURL.GetProtocol() == INetProtocol::VndSunStarHelp )
246 0 : return false;
247 : }
248 :
249 121802 : OUString aNewWord(rWord);
250 121802 : aNewWord = comphelper::string::remove(aNewWord, CH_TXTATR_INWORD);
251 121802 : aNewWord = comphelper::string::remove(aNewWord, CH_TXTATR_BREAKWORD);
252 :
253 121802 : pImpl->AddDocument(rDoc);
254 121802 : bool bRet = false;
255 121802 : sal_Int32 nWrdLen = aNewWord.getLength();
256 243614 : while( nWrdLen && '.' == aNewWord[ nWrdLen-1 ])
257 10 : --nWrdLen;
258 :
259 121802 : if( !bLockWordLst && nWrdLen >= nMinWrdLen )
260 : {
261 121800 : SwAutoCompleteString* pNew = new SwAutoCompleteString( aNewWord, 0, nWrdLen );
262 121800 : pNew->AddDocument(rDoc);
263 : std::pair<editeng::SortedAutoCompleteStrings::const_iterator, bool>
264 121800 : aInsPair = m_WordList.insert(pNew);
265 :
266 121800 : m_LookupTree.insert( OUString(aNewWord).copy(0, nWrdLen) );
267 :
268 121800 : if (aInsPair.second)
269 : {
270 3946 : bRet = true;
271 3946 : if (aLRULst.size() >= nMaxCount)
272 : {
273 : // the last one needs to be removed
274 : // so that there is space for the first one
275 2913 : SwAutoCompleteString* pDel = aLRULst.back();
276 2913 : aLRULst.pop_back();
277 2913 : m_WordList.erase(pDel);
278 2913 : delete pDel;
279 : }
280 3946 : aLRULst.push_front(pNew);
281 : }
282 : else
283 : {
284 117854 : delete pNew;
285 : // then move "up"
286 117854 : pNew = static_cast<SwAutoCompleteString*>(*aInsPair.first);
287 :
288 : // add the document to the already inserted string
289 117854 : pNew->AddDocument(rDoc);
290 :
291 : // move pNew to the front of the LRU list
292 117854 : SwAutoCompleteStringPtrDeque::iterator it = std::find( aLRULst.begin(), aLRULst.end(), pNew );
293 : OSL_ENSURE( aLRULst.end() != it, "String not found" );
294 117854 : if ( aLRULst.begin() != it && aLRULst.end() != it )
295 : {
296 117691 : aLRULst.erase( it );
297 117691 : aLRULst.push_front( pNew );
298 : }
299 : }
300 : }
301 121802 : 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 32 : void SwAutoCompleteWord::DocumentDying(const SwDoc& rDoc)
414 : {
415 32 : pImpl->RemoveDocument(rDoc);
416 :
417 32 : SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
418 32 : const bool bDelete = !pACorr->GetSwFlags().bAutoCmpltKeepList;
419 2165 : for (size_t nPos = m_WordList.size(); nPos; nPos--)
420 : {
421 2133 : SwAutoCompleteString *const pCurrent = dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos - 1]);
422 2133 : 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 209 : }
432 :
433 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|