android: Update app-specific/MIME type icons
[LibreOffice.git] / sw / source / core / doc / acmplwrd.cxx
blob38011750bd29e8e9aafd992314aefe95f85e80ec
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <tools/urlobj.hxx>
21 #include <hintids.hxx>
22 #include <acmplwrd.hxx>
23 #include <doc.hxx>
24 #include <pagedesc.hxx>
25 #include <poolfmt.hxx>
26 #include <svl/listener.hxx>
27 #include <IDocumentStylePoolAccess.hxx>
28 #include <editeng/svxacorr.hxx>
29 #include <osl/diagnose.h>
31 #include <editeng/acorrcfg.hxx>
32 #include <sfx2/docfile.hxx>
33 #include <docsh.hxx>
35 #include <cassert>
36 #include <vector>
38 class SwAutoCompleteClient : public SvtListener
40 SwAutoCompleteWord* m_pAutoCompleteWord;
41 SwDoc* m_pDoc;
42 #if OSL_DEBUG_LEVEL > 0
43 static sal_uLong s_nSwAutoCompleteClientCount;
44 #endif
45 public:
46 SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc);
47 SwAutoCompleteClient(const SwAutoCompleteClient& rClient);
48 virtual ~SwAutoCompleteClient() override;
50 SwAutoCompleteClient& operator=(const SwAutoCompleteClient& rClient);
52 const SwDoc& GetDoc() const {return *m_pDoc;}
53 #if OSL_DEBUG_LEVEL > 0
54 static sal_uLong GetElementCount() {return s_nSwAutoCompleteClientCount;}
55 #endif
56 protected:
57 virtual void Notify(const SfxHint&) override;
58 private:
59 void DocumentDying();
62 class SwAutoCompleteWord_Impl
64 std::vector<SwAutoCompleteClient>
65 m_aClientVector;
66 SwAutoCompleteWord& m_rAutoCompleteWord;
67 public:
68 explicit SwAutoCompleteWord_Impl(SwAutoCompleteWord& rParent) :
69 m_rAutoCompleteWord(rParent){}
70 void AddDocument(SwDoc& rDoc);
71 void RemoveDocument(const SwDoc& rDoc);
74 class SwAutoCompleteString
75 : public editeng::IAutoCompleteString
77 #if OSL_DEBUG_LEVEL > 0
78 static sal_uLong s_nSwAutoCompleteStringCount;
79 #endif
80 std::vector<const SwDoc*> m_aSourceDocs;
81 public:
82 SwAutoCompleteString(const OUString& rStr, sal_Int32 nLen);
84 virtual ~SwAutoCompleteString() override;
85 void AddDocument(const SwDoc& rDoc);
86 //returns true if last document reference has been removed
87 bool RemoveDocument(const SwDoc& rDoc);
88 #if OSL_DEBUG_LEVEL > 0
89 static sal_uLong GetElementCount() {return s_nSwAutoCompleteStringCount;}
90 #endif
92 #if OSL_DEBUG_LEVEL > 0
93 sal_uLong SwAutoCompleteClient::s_nSwAutoCompleteClientCount = 0;
94 sal_uLong SwAutoCompleteString::s_nSwAutoCompleteStringCount = 0;
95 #endif
97 SwAutoCompleteClient::SwAutoCompleteClient(SwAutoCompleteWord& rToTell, SwDoc& rSwDoc) :
98 m_pAutoCompleteWord(&rToTell),
99 m_pDoc(&rSwDoc)
101 StartListening(m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
102 #if OSL_DEBUG_LEVEL > 0
103 ++s_nSwAutoCompleteClientCount;
104 #endif
107 SwAutoCompleteClient::SwAutoCompleteClient(const SwAutoCompleteClient& rClient) :
108 SvtListener(),
109 m_pAutoCompleteWord(rClient.m_pAutoCompleteWord),
110 m_pDoc(rClient.m_pDoc)
112 StartListening(m_pDoc->getIDocumentStylePoolAccess().GetPageDescFromPool(RES_POOLPAGE_STANDARD)->GetNotifier());
113 #if OSL_DEBUG_LEVEL > 0
114 ++s_nSwAutoCompleteClientCount;
115 #endif
118 SwAutoCompleteClient::~SwAutoCompleteClient()
120 #if OSL_DEBUG_LEVEL > 0
121 --s_nSwAutoCompleteClientCount;
122 #else
123 (void) this;
124 #endif
127 SwAutoCompleteClient& SwAutoCompleteClient::operator=(const SwAutoCompleteClient& rClient)
129 m_pAutoCompleteWord = rClient.m_pAutoCompleteWord;
130 m_pDoc = rClient.m_pDoc;
131 CopyAllBroadcasters(rClient);
132 return *this;
135 void SwAutoCompleteClient::DocumentDying()
137 EndListeningAll();
138 m_pAutoCompleteWord->DocumentDying(*m_pDoc);
141 void SwAutoCompleteClient::Notify(const SfxHint& rHint)
143 switch(rHint.GetId())
145 case SfxHintId::Dying:
146 DocumentDying();
147 return;
148 case SfxHintId::SwLegacyModify:
150 auto pLegacy = static_cast<const sw::LegacyModifyHint*>(&rHint);
151 switch(pLegacy->GetWhich())
153 case RES_REMOVE_UNO_OBJECT:
154 case RES_OBJECTDYING:
155 DocumentDying();
156 return;
157 default:
158 return;
161 default:
162 return;
166 void SwAutoCompleteWord_Impl::AddDocument(SwDoc& rDoc)
168 if (std::any_of(m_aClientVector.begin(), m_aClientVector.end(),
169 [&rDoc](SwAutoCompleteClient& rClient) { return &rClient.GetDoc() == &rDoc; }))
170 return;
171 m_aClientVector.emplace_back(m_rAutoCompleteWord, rDoc);
174 void SwAutoCompleteWord_Impl::RemoveDocument(const SwDoc& rDoc)
176 auto aIt = std::find_if(m_aClientVector.begin(), m_aClientVector.end(),
177 [&rDoc](SwAutoCompleteClient& rClient) { return &rClient.GetDoc() == &rDoc; });
178 if (aIt != m_aClientVector.end())
179 m_aClientVector.erase(aIt);
182 SwAutoCompleteString::SwAutoCompleteString(
183 const OUString& rStr, sal_Int32 const nLen)
184 : editeng::IAutoCompleteString(rStr.copy(0, nLen))
186 #if OSL_DEBUG_LEVEL > 0
187 ++s_nSwAutoCompleteStringCount;
188 #endif
191 SwAutoCompleteString::~SwAutoCompleteString()
193 #if OSL_DEBUG_LEVEL > 0
194 --s_nSwAutoCompleteStringCount;
195 #else
196 (void) this;
197 #endif
200 void SwAutoCompleteString::AddDocument(const SwDoc& rDoc)
202 auto aIt = std::find(m_aSourceDocs.begin(), m_aSourceDocs.end(), &rDoc);
203 if (aIt != m_aSourceDocs.end())
204 return;
205 m_aSourceDocs.push_back(&rDoc);
208 bool SwAutoCompleteString::RemoveDocument(const SwDoc& rDoc)
210 auto aIt = std::find(m_aSourceDocs.begin(), m_aSourceDocs.end(), &rDoc);
211 if (aIt != m_aSourceDocs.end())
213 m_aSourceDocs.erase(aIt);
214 return m_aSourceDocs.empty();
216 return false;
219 SwAutoCompleteWord::SwAutoCompleteWord(
220 editeng::SortedAutoCompleteStrings::size_type nWords, sal_uInt16 nMWrdLen ):
221 m_pImpl(new SwAutoCompleteWord_Impl(*this)),
222 m_nMaxCount( nWords ),
223 m_nMinWordLen( nMWrdLen ),
224 m_bLockWordList( false )
228 SwAutoCompleteWord::~SwAutoCompleteWord()
230 m_WordList.DeleteAndDestroyAll(); // so the assertion below works
231 #if OSL_DEBUG_LEVEL > 0
232 sal_uLong nStrings = SwAutoCompleteString::GetElementCount();
233 sal_uLong nClients = SwAutoCompleteClient::GetElementCount();
234 OSL_ENSURE(!nStrings && !nClients, "AutoComplete: clients or string count mismatch");
235 #endif
238 bool SwAutoCompleteWord::InsertWord( const OUString& rWord, SwDoc& rDoc )
240 SwDocShell* pDocShell = rDoc.GetDocShell();
241 SfxMedium* pMedium = pDocShell ? pDocShell->GetMedium() : nullptr;
242 // strings from help module should not be added
243 if( pMedium )
245 const INetURLObject& rURL = pMedium->GetURLObject();
246 if ( rURL.GetProtocol() == INetProtocol::VndSunStarHelp )
247 return false;
250 OUString aNewWord = rWord.replaceAll(OUStringChar(CH_TXTATR_INWORD), "")
251 .replaceAll(OUStringChar(CH_TXTATR_BREAKWORD), "");
253 m_pImpl->AddDocument(rDoc);
254 bool bRet = false;
255 sal_Int32 nWrdLen = aNewWord.getLength();
256 while( nWrdLen && '.' == aNewWord[ nWrdLen-1 ])
257 --nWrdLen;
259 if( !m_bLockWordList && nWrdLen >= m_nMinWordLen )
261 SwAutoCompleteString* pNew = new SwAutoCompleteString( aNewWord, nWrdLen );
262 pNew->AddDocument(rDoc);
263 std::pair<editeng::SortedAutoCompleteStrings::const_iterator, bool>
264 aInsPair = m_WordList.insert(pNew);
266 m_LookupTree.insert( aNewWord.subView(0, nWrdLen) );
268 if (aInsPair.second)
270 bRet = true;
271 if (m_aLRUList.size() >= m_nMaxCount)
273 // the last one needs to be removed
274 // so that there is space for the first one
275 SwAutoCompleteString* pDel = m_aLRUList.back();
276 m_aLRUList.pop_back();
277 m_WordList.erase(pDel);
278 delete pDel;
280 m_aLRUList.push_front(pNew);
282 else
284 delete pNew;
285 // then move "up"
286 pNew = static_cast<SwAutoCompleteString*>(*aInsPair.first);
288 // add the document to the already inserted string
289 pNew->AddDocument(rDoc);
291 // move pNew to the front of the LRU list
292 SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pNew );
293 OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
294 if ( m_aLRUList.begin() != it && m_aLRUList.end() != it )
296 m_aLRUList.erase( it );
297 m_aLRUList.push_front( pNew );
301 return bRet;
304 void SwAutoCompleteWord::SetMaxCount(
305 editeng::SortedAutoCompleteStrings::size_type nNewMax )
307 if( nNewMax < m_nMaxCount && m_aLRUList.size() > nNewMax )
309 // remove the trailing ones
310 SwAutoCompleteStringPtrDeque::size_type nLRUIndex = nNewMax-1;
311 while (nNewMax < m_WordList.size() && nLRUIndex < m_aLRUList.size())
313 auto it = m_WordList.find(m_aLRUList[nLRUIndex++]);
314 OSL_ENSURE( m_WordList.end() != it, "String not found" );
315 editeng::IAutoCompleteString *const pDel = *it;
316 m_WordList.erase(it);
317 delete pDel;
319 m_aLRUList.erase( m_aLRUList.begin() + nNewMax - 1, m_aLRUList.end() );
321 m_nMaxCount = nNewMax;
324 void SwAutoCompleteWord::SetMinWordLen( sal_uInt16 n )
326 // Do you really want to remove all words that are less than the minWrdLen?
327 if( n < m_nMinWordLen )
329 for (size_t nPos = 0; nPos < m_WordList.size(); ++nPos)
330 if (m_WordList[ nPos ]->GetAutoCompleteString().getLength() < n)
332 SwAutoCompleteString *const pDel =
333 dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos]);
334 m_WordList.erase_at(nPos);
336 SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
337 OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
338 m_aLRUList.erase( it );
339 --nPos;
340 delete pDel;
344 m_nMinWordLen = n;
347 /** Return all words matching a given prefix
349 * @param aMatch the prefix to search for
350 * @param rWords the words found matching
352 bool SwAutoCompleteWord::GetWordsMatching(std::u16string_view aMatch, std::vector<OUString>& rWords) const
354 assert(rWords.empty());
355 m_LookupTree.findSuggestions(aMatch, rWords);
356 return !rWords.empty();
359 void SwAutoCompleteWord::CheckChangedList(
360 const editeng::SortedAutoCompleteStrings& rNewLst)
362 size_t nMyLen = m_WordList.size(), nNewLen = rNewLst.size();
363 size_t nMyPos = 0, nNewPos = 0;
365 for( ; nMyPos < nMyLen && nNewPos < nNewLen; ++nMyPos, ++nNewPos )
367 const editeng::IAutoCompleteString * pStr = rNewLst[ nNewPos ];
368 while (m_WordList[nMyPos] != pStr)
370 SwAutoCompleteString *const pDel =
371 dynamic_cast<SwAutoCompleteString*>(m_WordList[nMyPos]);
372 m_WordList.erase_at(nMyPos);
373 SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
374 OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
375 m_aLRUList.erase( it );
376 delete pDel;
377 if( nMyPos >= --nMyLen )
378 break;
381 // remove the elements at the end of the array
382 if( nMyPos >= nMyLen )
383 return;
385 // clear LRU array first then delete the string object
386 for( ; nNewPos < nMyLen; ++nNewPos )
388 SwAutoCompleteString *const pDel =
389 dynamic_cast<SwAutoCompleteString*>(m_WordList[nNewPos]);
390 SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pDel );
391 OSL_ENSURE( m_aLRUList.end() != it, "String not found" );
392 m_aLRUList.erase( it );
393 delete pDel;
395 // remove from array
396 m_WordList.erase(m_WordList.begin() + nMyPos,
397 m_WordList.begin() + nMyLen);
400 void SwAutoCompleteWord::DocumentDying(const SwDoc& rDoc)
402 m_pImpl->RemoveDocument(rDoc);
404 SvxAutoCorrect* pACorr = SvxAutoCorrCfg::Get().GetAutoCorrect();
405 const bool bDelete = !pACorr->GetSwFlags().bAutoCmpltKeepList;
406 for (size_t nPos = m_WordList.size(); nPos; nPos--)
408 SwAutoCompleteString *const pCurrent = dynamic_cast<SwAutoCompleteString*>(m_WordList[nPos - 1]);
409 if(pCurrent && pCurrent->RemoveDocument(rDoc) && bDelete)
411 m_WordList.erase_at(nPos - 1);
412 SwAutoCompleteStringPtrDeque::iterator it = std::find( m_aLRUList.begin(), m_aLRUList.end(), pCurrent );
413 OSL_ENSURE( m_aLRUList.end() != it, "word not found in LRU list" );
414 m_aLRUList.erase( it );
415 delete pCurrent;
420 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */