/src/libreoffice/linguistic/source/dlistimp.cxx
Line | Count | Source |
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 | | |
21 | | #include <cppuhelper/factory.hxx> |
22 | | #include <osl/file.hxx> |
23 | | #include <tools/debug.hxx> |
24 | | #include <tools/stream.hxx> |
25 | | #include <tools/urlobj.hxx> |
26 | | #include <unotools/useroptions.hxx> |
27 | | #include <cppuhelper/supportsservice.hxx> |
28 | | #include <cppuhelper/weak.hxx> |
29 | | #include <unotools/localfilehelper.hxx> |
30 | | #include <comphelper/lok.hxx> |
31 | | #include <comphelper/processfactory.hxx> |
32 | | #include <comphelper/sequence.hxx> |
33 | | #include <unotools/ucbstreamhelper.hxx> |
34 | | #include <com/sun/star/frame/XStorable.hpp> |
35 | | #include <com/sun/star/uno/Reference.h> |
36 | | #include <com/sun/star/linguistic2/DictionaryEventFlags.hpp> |
37 | | #include <com/sun/star/linguistic2/DictionaryListEventFlags.hpp> |
38 | | #include <com/sun/star/ucb/SimpleFileAccess.hpp> |
39 | | #include <svtools/strings.hrc> |
40 | | #include <unotools/resmgr.hxx> |
41 | | #include <unotools/charclass.hxx> |
42 | | #include <sal/log.hxx> |
43 | | #include <utility> |
44 | | |
45 | | #include "dlistimp.hxx" |
46 | | #include "dicimp.hxx" |
47 | | #include "lngopt.hxx" |
48 | | |
49 | | using namespace osl; |
50 | | using namespace com::sun::star; |
51 | | using namespace com::sun::star::lang; |
52 | | using namespace com::sun::star::uno; |
53 | | using namespace com::sun::star::linguistic2; |
54 | | using namespace linguistic; |
55 | | |
56 | | |
57 | | static bool IsVers2OrNewer( const OUString& rFileURL, LanguageType& nLng, bool& bNeg, OUString& aDicName ); |
58 | | |
59 | | static void AddInternal( const uno::Reference< XDictionary > &rDic, |
60 | | const OUString& rNew ); |
61 | | static void AddUserData( const uno::Reference< XDictionary > &rDic ); |
62 | | |
63 | | |
64 | | class DicEvtListenerHelper : |
65 | | public cppu::WeakImplHelper |
66 | | < |
67 | | XDictionaryEventListener |
68 | | > |
69 | | { |
70 | | comphelper::OInterfaceContainerHelper3<XDictionaryListEventListener> aDicListEvtListeners; |
71 | | uno::Reference< XDictionaryList > xMyDicList; |
72 | | |
73 | | sal_Int16 nCondensedEvt; |
74 | | sal_Int16 nNumCollectEvtListeners; |
75 | | |
76 | | public: |
77 | | explicit DicEvtListenerHelper( uno::Reference< XDictionaryList > xDicList ); |
78 | | virtual ~DicEvtListenerHelper() override; |
79 | | |
80 | | // XEventListener |
81 | | virtual void SAL_CALL |
82 | | disposing( const EventObject& rSource ) override; |
83 | | |
84 | | // XDictionaryEventListener |
85 | | virtual void SAL_CALL |
86 | | processDictionaryEvent( const DictionaryEvent& rDicEvent ) override; |
87 | | |
88 | | // non-UNO functions |
89 | | void DisposeAndClear( const EventObject &rEvtObj ); |
90 | | |
91 | | bool AddDicListEvtListener( |
92 | | const uno::Reference< XDictionaryListEventListener >& rxListener ); |
93 | | bool RemoveDicListEvtListener( |
94 | | const uno::Reference< XDictionaryListEventListener >& rxListener ); |
95 | 0 | sal_Int16 BeginCollectEvents() { return ++nNumCollectEvtListeners;} |
96 | | sal_Int16 EndCollectEvents(); |
97 | | sal_Int16 FlushEvents(); |
98 | 0 | void ClearEvents() { nCondensedEvt = 0; } |
99 | | }; |
100 | | |
101 | | |
102 | | DicEvtListenerHelper::DicEvtListenerHelper( |
103 | | uno::Reference< XDictionaryList > xDicList ) : |
104 | 0 | aDicListEvtListeners ( GetLinguMutex() ), |
105 | 0 | xMyDicList (std::move( xDicList )), |
106 | 0 | nCondensedEvt(0), nNumCollectEvtListeners(0) |
107 | 0 | { |
108 | 0 | } |
109 | | |
110 | | |
111 | | DicEvtListenerHelper::~DicEvtListenerHelper() |
112 | 0 | { |
113 | 0 | DBG_ASSERT(aDicListEvtListeners.getLength() == 0, |
114 | 0 | "lng : event listeners are still existing"); |
115 | 0 | } |
116 | | |
117 | | |
118 | | void DicEvtListenerHelper::DisposeAndClear( const EventObject &rEvtObj ) |
119 | 0 | { |
120 | 0 | aDicListEvtListeners.disposeAndClear( rEvtObj ); |
121 | 0 | } |
122 | | |
123 | | |
124 | | void SAL_CALL DicEvtListenerHelper::disposing( const EventObject& rSource ) |
125 | 0 | { |
126 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
127 | |
|
128 | 0 | uno::Reference< XDictionaryListEventListener > xSrc( rSource.Source, UNO_QUERY ); |
129 | | |
130 | | // remove event object from EventListener list |
131 | 0 | if (xSrc.is()) |
132 | 0 | aDicListEvtListeners.removeInterface( xSrc ); |
133 | | |
134 | | // if object is a dictionary then remove it from the dictionary list |
135 | | // Note: this will probably happen only if someone makes a XDictionary |
136 | | // implementation of his own that is also a XComponent. |
137 | 0 | uno::Reference< XDictionary > xDic( rSource.Source, UNO_QUERY ); |
138 | 0 | if (xDic.is()) |
139 | 0 | { |
140 | 0 | xMyDicList->removeDictionary( xDic ); |
141 | 0 | } |
142 | 0 | } |
143 | | |
144 | | |
145 | | void SAL_CALL DicEvtListenerHelper::processDictionaryEvent( |
146 | | const DictionaryEvent& rDicEvent ) |
147 | 0 | { |
148 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
149 | |
|
150 | 0 | uno::Reference< XDictionary > xDic( rDicEvent.Source, UNO_QUERY ); |
151 | 0 | DBG_ASSERT(xDic.is(), "lng : missing event source"); |
152 | | |
153 | | // assert that there is a corresponding dictionary entry if one was |
154 | | // added or deleted |
155 | 0 | DBG_ASSERT( !(rDicEvent.nEvent & |
156 | 0 | (DictionaryEventFlags::ADD_ENTRY | DictionaryEventFlags::DEL_ENTRY)) |
157 | 0 | || rDicEvent.xDictionaryEntry.is(), |
158 | 0 | "lng : missing dictionary entry" ); |
159 | | |
160 | | // evaluate DictionaryEvents and update data for next DictionaryListEvent |
161 | 0 | DictionaryType eDicType = xDic->getDictionaryType(); |
162 | 0 | DBG_ASSERT(eDicType != DictionaryType_MIXED, |
163 | 0 | "lng : unexpected dictionary type"); |
164 | 0 | if ((rDicEvent.nEvent & DictionaryEventFlags::ADD_ENTRY) && xDic->isActive()) |
165 | 0 | nCondensedEvt |= rDicEvent.xDictionaryEntry->isNegative() ? |
166 | 0 | DictionaryListEventFlags::ADD_NEG_ENTRY : |
167 | 0 | DictionaryListEventFlags::ADD_POS_ENTRY; |
168 | 0 | if ((rDicEvent.nEvent & DictionaryEventFlags::DEL_ENTRY) && xDic->isActive()) |
169 | 0 | nCondensedEvt |= rDicEvent.xDictionaryEntry->isNegative() ? |
170 | 0 | DictionaryListEventFlags::DEL_NEG_ENTRY : |
171 | 0 | DictionaryListEventFlags::DEL_POS_ENTRY; |
172 | 0 | if ((rDicEvent.nEvent & DictionaryEventFlags::ENTRIES_CLEARED) && xDic->isActive()) |
173 | 0 | nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ? |
174 | 0 | DictionaryListEventFlags::DEL_NEG_ENTRY : |
175 | 0 | DictionaryListEventFlags::DEL_POS_ENTRY; |
176 | 0 | if ((rDicEvent.nEvent & DictionaryEventFlags::CHG_LANGUAGE) && xDic->isActive()) |
177 | 0 | nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ? |
178 | 0 | DictionaryListEventFlags::DEACTIVATE_NEG_DIC |
179 | 0 | | DictionaryListEventFlags::ACTIVATE_NEG_DIC : |
180 | 0 | DictionaryListEventFlags::DEACTIVATE_POS_DIC |
181 | 0 | | DictionaryListEventFlags::ACTIVATE_POS_DIC; |
182 | 0 | if (rDicEvent.nEvent & DictionaryEventFlags::ACTIVATE_DIC) |
183 | 0 | nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ? |
184 | 0 | DictionaryListEventFlags::ACTIVATE_NEG_DIC : |
185 | 0 | DictionaryListEventFlags::ACTIVATE_POS_DIC; |
186 | 0 | if (rDicEvent.nEvent & DictionaryEventFlags::DEACTIVATE_DIC) |
187 | 0 | nCondensedEvt |= eDicType == DictionaryType_NEGATIVE ? |
188 | 0 | DictionaryListEventFlags::DEACTIVATE_NEG_DIC : |
189 | 0 | DictionaryListEventFlags::DEACTIVATE_POS_DIC; |
190 | |
|
191 | 0 | if (nNumCollectEvtListeners == 0 && nCondensedEvt != 0) |
192 | 0 | FlushEvents(); |
193 | 0 | } |
194 | | |
195 | | |
196 | | bool DicEvtListenerHelper::AddDicListEvtListener( |
197 | | const uno::Reference< XDictionaryListEventListener >& xListener ) |
198 | 0 | { |
199 | 0 | DBG_ASSERT( xListener.is(), "empty reference" ); |
200 | 0 | sal_Int32 nCount = aDicListEvtListeners.getLength(); |
201 | 0 | return aDicListEvtListeners.addInterface( xListener ) != nCount; |
202 | 0 | } |
203 | | |
204 | | |
205 | | bool DicEvtListenerHelper::RemoveDicListEvtListener( |
206 | | const uno::Reference< XDictionaryListEventListener >& xListener ) |
207 | 0 | { |
208 | 0 | DBG_ASSERT( xListener.is(), "empty reference" ); |
209 | 0 | sal_Int32 nCount = aDicListEvtListeners.getLength(); |
210 | 0 | return aDicListEvtListeners.removeInterface( xListener ) != nCount; |
211 | 0 | } |
212 | | |
213 | | |
214 | | sal_Int16 DicEvtListenerHelper::EndCollectEvents() |
215 | 0 | { |
216 | 0 | DBG_ASSERT(nNumCollectEvtListeners > 0, "lng: mismatched function call"); |
217 | 0 | if (nNumCollectEvtListeners > 0) |
218 | 0 | { |
219 | 0 | FlushEvents(); |
220 | 0 | nNumCollectEvtListeners--; |
221 | 0 | } |
222 | |
|
223 | 0 | return nNumCollectEvtListeners; |
224 | 0 | } |
225 | | |
226 | | |
227 | | sal_Int16 DicEvtListenerHelper::FlushEvents() |
228 | 0 | { |
229 | 0 | if (0 != nCondensedEvt) |
230 | 0 | { |
231 | | // build DictionaryListEvent to pass on to listeners |
232 | 0 | uno::Sequence< DictionaryEvent > aDicEvents; |
233 | 0 | DictionaryListEvent aEvent( xMyDicList, nCondensedEvt, aDicEvents ); |
234 | | |
235 | | // pass on event |
236 | 0 | aDicListEvtListeners.notifyEach( &XDictionaryListEventListener::processDictionaryListEvent, aEvent ); |
237 | | |
238 | | // clear "list" of events |
239 | 0 | nCondensedEvt = 0; |
240 | 0 | } |
241 | |
|
242 | 0 | return nNumCollectEvtListeners; |
243 | 0 | } |
244 | | |
245 | | |
246 | | void DicList::MyAppExitListener::AtExit() |
247 | 0 | { |
248 | 0 | rMyDicList.SaveDics(); |
249 | 0 | } |
250 | | |
251 | | |
252 | | DicList::DicList() : |
253 | 0 | aEvtListeners ( GetLinguMutex() ) |
254 | 0 | { |
255 | 0 | mxDicEvtLstnrHelper = new DicEvtListenerHelper( this ); |
256 | 0 | bDisposing = false; |
257 | 0 | bInCreation = false; |
258 | |
|
259 | 0 | mxExitListener = new MyAppExitListener( *this ); |
260 | 0 | mxExitListener->Activate(); |
261 | 0 | } |
262 | | |
263 | | DicList::~DicList() |
264 | 0 | { |
265 | 0 | mxExitListener->Deactivate(); |
266 | 0 | } |
267 | | |
268 | | |
269 | | void DicList::SearchForDictionaries( |
270 | | DictionaryVec_t&rDicList, |
271 | | const OUString &rDicDirURL, |
272 | | bool bIsWriteablePath ) |
273 | 0 | { |
274 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
275 | |
|
276 | 0 | const uno::Sequence< OUString > aDirCnt( utl::LocalFileHelper:: |
277 | 0 | GetFolderContents( rDicDirURL, false ) ); |
278 | 0 | SvtSysLocale aSysLocale; |
279 | |
|
280 | 0 | for (const OUString& aURL : aDirCnt) |
281 | 0 | { |
282 | 0 | LanguageType nLang = LANGUAGE_NONE; |
283 | 0 | bool bNeg = false; |
284 | 0 | OUString aDicTitle = u""_ustr; |
285 | |
|
286 | 0 | if(!::IsVers2OrNewer( aURL, nLang, bNeg, aDicTitle )) |
287 | 0 | { |
288 | | // When not |
289 | 0 | sal_Int32 nPos = aURL.indexOf('.'); |
290 | 0 | OUString aExt( aURL.copy(nPos + 1).toAsciiLowerCase() ); |
291 | |
|
292 | 0 | if ("dcn" == aExt) // negative |
293 | 0 | bNeg = true; |
294 | 0 | else if ("dcp" == aExt) // positive |
295 | 0 | bNeg = false; |
296 | 0 | else |
297 | 0 | continue; // other files |
298 | 0 | } |
299 | | |
300 | | // Record in the list of Dictionaries |
301 | | // When it already exists don't record |
302 | 0 | OUString aTmp1 = aSysLocale.GetCharClass().lowercase( aURL); |
303 | 0 | sal_Int32 nPos = aTmp1.lastIndexOf( '/' ); |
304 | 0 | if (-1 != nPos) |
305 | 0 | aTmp1 = aTmp1.copy( nPos + 1 ); |
306 | 0 | OUString aTmp2; |
307 | 0 | size_t j; |
308 | 0 | size_t nCount = rDicList.size(); |
309 | 0 | for(j = 0; j < nCount; j++) |
310 | 0 | { |
311 | 0 | aTmp2 = rDicList[j]->getName(); |
312 | 0 | aTmp2 = aSysLocale.GetCharClass().lowercase( aTmp2); |
313 | 0 | if(aTmp1 == aTmp2) |
314 | 0 | break; |
315 | 0 | } |
316 | 0 | if(j >= nCount) // dictionary not yet in DicList |
317 | 0 | { |
318 | | // get decoded dictionary file name |
319 | 0 | INetURLObject aURLObj( aURL ); |
320 | 0 | OUString aDicName = aURLObj.getName( INetURLObject::LAST_SEGMENT, |
321 | 0 | true, INetURLObject::DecodeMechanism::WithCharset ); |
322 | |
|
323 | 0 | DictionaryType eType = bNeg ? DictionaryType_NEGATIVE : DictionaryType_POSITIVE; |
324 | 0 | uno::Reference< XDictionary > xDic = |
325 | 0 | new DictionaryNeo( aDicTitle.isEmpty() ? aDicName : aDicTitle, nLang, eType, aURL, bIsWriteablePath ); |
326 | | |
327 | | // when using libreofficekit we don't have "options" dialog to make user-dictionaries active |
328 | | // so when we add user-dictionary, we make them active as well |
329 | 0 | if (comphelper::LibreOfficeKit::isActive()) |
330 | 0 | xDic->setActive(true); |
331 | |
|
332 | 0 | addDictionary( xDic ); |
333 | 0 | nCount++; |
334 | 0 | } |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | | |
339 | | sal_Int32 DicList::GetDicPos(const uno::Reference< XDictionary > &xDic) |
340 | 0 | { |
341 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
342 | |
|
343 | 0 | DictionaryVec_t& rDicList = GetOrCreateDicList(); |
344 | 0 | size_t n = rDicList.size(); |
345 | 0 | for (size_t i = 0; i < n; i++) |
346 | 0 | { |
347 | 0 | if ( rDicList[i] == xDic ) |
348 | 0 | return i; |
349 | 0 | } |
350 | 0 | return -1; |
351 | 0 | } |
352 | | |
353 | | sal_Int16 SAL_CALL DicList::getCount() |
354 | 0 | { |
355 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
356 | 0 | return static_cast< sal_Int16 >(GetOrCreateDicList().size()); |
357 | 0 | } |
358 | | |
359 | | uno::Sequence< uno::Reference< XDictionary > > SAL_CALL |
360 | | DicList::getDictionaries() |
361 | 0 | { |
362 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
363 | |
|
364 | 0 | DictionaryVec_t& rDicList = GetOrCreateDicList(); |
365 | |
|
366 | 0 | return comphelper::containerToSequence(rDicList); |
367 | 0 | } |
368 | | |
369 | | uno::Reference< XDictionary > SAL_CALL |
370 | | DicList::getDictionaryByName( const OUString& aDictionaryName ) |
371 | 0 | { |
372 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
373 | |
|
374 | 0 | uno::Reference< XDictionary > xDic; |
375 | 0 | DictionaryVec_t& rDicList = GetOrCreateDicList(); |
376 | 0 | size_t nCount = rDicList.size(); |
377 | 0 | for (size_t i = 0; i < nCount; i++) |
378 | 0 | { |
379 | 0 | const uno::Reference< XDictionary > &rDic = rDicList[i]; |
380 | 0 | if (rDic.is() && rDic->getName() == aDictionaryName) |
381 | 0 | { |
382 | 0 | xDic = rDic; |
383 | 0 | break; |
384 | 0 | } |
385 | 0 | } |
386 | |
|
387 | 0 | return xDic; |
388 | 0 | } |
389 | | |
390 | | sal_Bool SAL_CALL DicList::addDictionary( |
391 | | const uno::Reference< XDictionary >& xDictionary ) |
392 | 0 | { |
393 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
394 | |
|
395 | 0 | if (bDisposing) |
396 | 0 | return false; |
397 | | |
398 | 0 | bool bRes = false; |
399 | 0 | if (xDictionary.is()) |
400 | 0 | { |
401 | 0 | DictionaryVec_t& rDicList = GetOrCreateDicList(); |
402 | 0 | rDicList.push_back( xDictionary ); |
403 | 0 | bRes = true; |
404 | | |
405 | | // add listener helper to the dictionaries listener lists |
406 | 0 | xDictionary->addDictionaryEventListener( mxDicEvtLstnrHelper ); |
407 | 0 | } |
408 | 0 | return bRes; |
409 | 0 | } |
410 | | |
411 | | sal_Bool SAL_CALL |
412 | | DicList::removeDictionary( const uno::Reference< XDictionary >& xDictionary ) |
413 | 0 | { |
414 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
415 | |
|
416 | 0 | if (bDisposing) |
417 | 0 | return false; |
418 | | |
419 | 0 | bool bRes = false; |
420 | 0 | sal_Int32 nPos = GetDicPos( xDictionary ); |
421 | 0 | if (nPos >= 0) |
422 | 0 | { |
423 | | // remove dictionary list from the dictionaries listener lists |
424 | 0 | DictionaryVec_t& rDicList = GetOrCreateDicList(); |
425 | 0 | uno::Reference< XDictionary > xDic( rDicList[ nPos ] ); |
426 | 0 | DBG_ASSERT(xDic.is(), "lng : empty reference"); |
427 | 0 | if (xDic.is()) |
428 | 0 | { |
429 | | // deactivate dictionary if not already done |
430 | 0 | xDic->setActive( false ); |
431 | |
|
432 | 0 | xDic->removeDictionaryEventListener( mxDicEvtLstnrHelper ); |
433 | 0 | } |
434 | | |
435 | | // remove element at nPos |
436 | 0 | rDicList.erase( rDicList.begin() + nPos ); |
437 | 0 | bRes = true; |
438 | 0 | } |
439 | 0 | return bRes; |
440 | 0 | } |
441 | | |
442 | | sal_Bool SAL_CALL DicList::addDictionaryListEventListener( |
443 | | const uno::Reference< XDictionaryListEventListener >& xListener, |
444 | | sal_Bool bReceiveVerbose ) |
445 | 0 | { |
446 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
447 | |
|
448 | 0 | if (bDisposing) |
449 | 0 | return false; |
450 | | |
451 | 0 | DBG_ASSERT(!bReceiveVerbose, "lng : not yet supported"); |
452 | |
|
453 | 0 | bool bRes = false; |
454 | 0 | if (xListener.is()) //! don't add empty references |
455 | 0 | { |
456 | 0 | bRes = mxDicEvtLstnrHelper->AddDicListEvtListener( xListener ); |
457 | 0 | } |
458 | 0 | return bRes; |
459 | 0 | } |
460 | | |
461 | | sal_Bool SAL_CALL DicList::removeDictionaryListEventListener( |
462 | | const uno::Reference< XDictionaryListEventListener >& xListener ) |
463 | 0 | { |
464 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
465 | |
|
466 | 0 | if (bDisposing) |
467 | 0 | return false; |
468 | | |
469 | 0 | bool bRes = false; |
470 | 0 | if(xListener.is()) |
471 | 0 | { |
472 | 0 | bRes = mxDicEvtLstnrHelper->RemoveDicListEvtListener( xListener ); |
473 | 0 | } |
474 | 0 | return bRes; |
475 | 0 | } |
476 | | |
477 | | sal_Int16 SAL_CALL DicList::beginCollectEvents() |
478 | 0 | { |
479 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
480 | 0 | return mxDicEvtLstnrHelper->BeginCollectEvents(); |
481 | 0 | } |
482 | | |
483 | | sal_Int16 SAL_CALL DicList::endCollectEvents() |
484 | 0 | { |
485 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
486 | 0 | return mxDicEvtLstnrHelper->EndCollectEvents(); |
487 | 0 | } |
488 | | |
489 | | sal_Int16 SAL_CALL DicList::flushEvents() |
490 | 0 | { |
491 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
492 | 0 | return mxDicEvtLstnrHelper->FlushEvents(); |
493 | 0 | } |
494 | | |
495 | | uno::Reference< XDictionary > SAL_CALL |
496 | | DicList::createDictionary( const OUString& rName, const Locale& rLocale, |
497 | | DictionaryType eDicType, const OUString& rURL ) |
498 | 0 | { |
499 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
500 | |
|
501 | 0 | LanguageType nLanguage = LinguLocaleToLanguage( rLocale ); |
502 | 0 | bool bIsWriteablePath = rURL.match( GetDictionaryWriteablePath() ); |
503 | 0 | return new DictionaryNeo( rName, nLanguage, eDicType, rURL, bIsWriteablePath ); |
504 | 0 | } |
505 | | |
506 | | |
507 | | uno::Reference< XDictionaryEntry > SAL_CALL |
508 | | DicList::queryDictionaryEntry( const OUString& rWord, const Locale& rLocale, |
509 | | sal_Bool bSearchPosDics, sal_Bool bSearchSpellEntry ) |
510 | 0 | { |
511 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
512 | 0 | return SearchDicList( this, rWord, LinguLocaleToLanguage( rLocale ), |
513 | 0 | bSearchPosDics, bSearchSpellEntry ); |
514 | 0 | } |
515 | | |
516 | | |
517 | | void SAL_CALL |
518 | | DicList::dispose() |
519 | 0 | { |
520 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
521 | |
|
522 | 0 | if (bDisposing) |
523 | 0 | return; |
524 | | |
525 | 0 | bDisposing = true; |
526 | 0 | EventObject aEvtObj( static_cast<XDictionaryList *>(this) ); |
527 | |
|
528 | 0 | aEvtListeners.disposeAndClear( aEvtObj ); |
529 | 0 | if (mxDicEvtLstnrHelper.is()) |
530 | 0 | mxDicEvtLstnrHelper->DisposeAndClear( aEvtObj ); |
531 | | |
532 | | //! avoid creation of dictionaries if not already done |
533 | 0 | if ( !aDicList.empty() ) |
534 | 0 | { |
535 | 0 | DictionaryVec_t& rDicList = GetOrCreateDicList(); |
536 | 0 | size_t nCount = rDicList.size(); |
537 | 0 | for (size_t i = 0; i < nCount; i++) |
538 | 0 | { |
539 | | // save (modified) dictionaries |
540 | 0 | uno::Reference< frame::XStorable > xStor( rDicList[i] , UNO_QUERY ); |
541 | 0 | if (xStor.is()) |
542 | 0 | { |
543 | 0 | try |
544 | 0 | { |
545 | 0 | if (!xStor->isReadonly() && xStor->hasLocation()) |
546 | 0 | xStor->store(); |
547 | 0 | } |
548 | 0 | catch(Exception &) |
549 | 0 | { |
550 | 0 | } |
551 | 0 | } |
552 | | |
553 | | // release references to (members of) this object hold by |
554 | | // dictionaries |
555 | 0 | if (rDicList[i].is()) |
556 | 0 | rDicList[i]->removeDictionaryEventListener( mxDicEvtLstnrHelper ); |
557 | 0 | } |
558 | 0 | } |
559 | 0 | mxDicEvtLstnrHelper.clear(); |
560 | 0 | } |
561 | | |
562 | | void SAL_CALL |
563 | | DicList::addEventListener( const uno::Reference< XEventListener >& rxListener ) |
564 | 0 | { |
565 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
566 | |
|
567 | 0 | if (!bDisposing && rxListener.is()) |
568 | 0 | aEvtListeners.addInterface( rxListener ); |
569 | 0 | } |
570 | | |
571 | | void SAL_CALL |
572 | | DicList::removeEventListener( const uno::Reference< XEventListener >& rxListener ) |
573 | 0 | { |
574 | 0 | osl::MutexGuard aGuard( GetLinguMutex() ); |
575 | |
|
576 | 0 | if (!bDisposing && rxListener.is()) |
577 | 0 | aEvtListeners.removeInterface( rxListener ); |
578 | 0 | } |
579 | | |
580 | | void SAL_CALL DicList::initialize(const css::uno::Sequence<css::uno::Any>& /*rArguments*/) |
581 | 0 | { |
582 | 0 | osl::MutexGuard aGuard(GetLinguMutex()); |
583 | |
|
584 | 0 | if (!bInCreation && !bDisposing) |
585 | 0 | { |
586 | 0 | CreateDicList(); |
587 | 0 | } |
588 | 0 | } |
589 | | |
590 | | void DicList::CreateDicList() |
591 | 0 | { |
592 | 0 | bInCreation = true; |
593 | | |
594 | | // look for dictionaries |
595 | 0 | const OUString aWriteablePath( GetDictionaryWriteablePath() ); |
596 | 0 | std::vector< OUString > aPaths( GetDictionaryPaths() ); |
597 | 0 | for (const OUString & aPath : aPaths) |
598 | 0 | { |
599 | 0 | const bool bIsWriteablePath = (aPath == aWriteablePath); |
600 | 0 | SearchForDictionaries( aDicList, aPath, bIsWriteablePath ); |
601 | 0 | } |
602 | | |
603 | | // create IgnoreAllList dictionary with empty URL (non persistent) |
604 | | // and add it to list |
605 | 0 | const LanguageTag tag = comphelper::LibreOfficeKit::isActive() |
606 | 0 | ? LanguageTag(u"en-US"_ustr) |
607 | 0 | : SvtSysLocale().GetUILanguageTag(); |
608 | 0 | std::locale loc(Translate::Create("svt", tag)); |
609 | 0 | uno::Reference< XDictionary > xIgnAll( |
610 | 0 | createDictionary( Translate::get(STR_DESCRIPTION_IGNOREALLLIST, loc), LinguLanguageToLocale( LANGUAGE_NONE ), |
611 | 0 | DictionaryType_POSITIVE, OUString() ) ); |
612 | 0 | if (xIgnAll.is()) |
613 | 0 | { |
614 | 0 | AddUserData( xIgnAll ); |
615 | 0 | xIgnAll->setActive( true ); |
616 | 0 | addDictionary( xIgnAll ); |
617 | 0 | } |
618 | | |
619 | | |
620 | | // evaluate list of dictionaries to be activated from configuration |
621 | | //! to suppress overwriting the list of active dictionaries in the |
622 | | //! configuration with incorrect arguments during the following |
623 | | //! activation of the dictionaries |
624 | 0 | mxDicEvtLstnrHelper->BeginCollectEvents(); |
625 | 0 | const uno::Sequence< OUString > aActiveDics( aOpt.GetActiveDics() ); |
626 | 0 | for (const OUString& rActiveDic : aActiveDics) |
627 | 0 | { |
628 | 0 | if (!rActiveDic.isEmpty()) |
629 | 0 | { |
630 | 0 | uno::Reference< XDictionary > xDic( getDictionaryByName( rActiveDic ) ); |
631 | 0 | if (xDic.is()) |
632 | 0 | xDic->setActive( true ); |
633 | 0 | } |
634 | 0 | } |
635 | | |
636 | | // suppress collected events during creation of the dictionary list. |
637 | | // there should be no events during creation. |
638 | 0 | mxDicEvtLstnrHelper->ClearEvents(); |
639 | |
|
640 | 0 | mxDicEvtLstnrHelper->EndCollectEvents(); |
641 | |
|
642 | 0 | bInCreation = false; |
643 | 0 | } |
644 | | |
645 | | |
646 | | void DicList::SaveDics() |
647 | 0 | { |
648 | | // save dics only if they have already been used/created. |
649 | | //! don't create them just for the purpose of saving them ! |
650 | 0 | if ( aDicList.empty() ) |
651 | 0 | return; |
652 | | |
653 | | // save (modified) dictionaries |
654 | 0 | DictionaryVec_t& rDicList = GetOrCreateDicList(); |
655 | 0 | size_t nCount = rDicList.size(); |
656 | 0 | for (size_t i = 0; i < nCount; i++) |
657 | 0 | { |
658 | | // save (modified) dictionaries |
659 | 0 | uno::Reference< frame::XStorable > xStor( rDicList[i], UNO_QUERY ); |
660 | 0 | if (xStor.is()) |
661 | 0 | { |
662 | 0 | try |
663 | 0 | { |
664 | 0 | if (!xStor->isReadonly() && xStor->hasLocation()) |
665 | 0 | xStor->store(); |
666 | 0 | } |
667 | 0 | catch(Exception &) |
668 | 0 | { |
669 | 0 | } |
670 | 0 | } |
671 | 0 | } |
672 | 0 | } |
673 | | |
674 | | |
675 | | // Service specific part |
676 | | |
677 | | OUString SAL_CALL DicList::getImplementationName( ) |
678 | 0 | { |
679 | 0 | return u"com.sun.star.lingu2.DicList"_ustr; |
680 | 0 | } |
681 | | |
682 | | |
683 | | sal_Bool SAL_CALL DicList::supportsService( const OUString& ServiceName ) |
684 | 0 | { |
685 | 0 | return cppu::supportsService(this, ServiceName); |
686 | 0 | } |
687 | | |
688 | | uno::Sequence< OUString > SAL_CALL DicList::getSupportedServiceNames( ) |
689 | 0 | { |
690 | 0 | return { u"com.sun.star.linguistic2.DictionaryList"_ustr }; |
691 | 0 | } |
692 | | |
693 | | |
694 | | |
695 | | static sal_Int32 lcl_GetToken( OUString &rToken, |
696 | | const OUString &rText, sal_Int32 nPos, std::u16string_view rDelim ) |
697 | 0 | { |
698 | 0 | sal_Int32 nRes = -1; |
699 | |
|
700 | 0 | if (rText.isEmpty() || nPos >= rText.getLength()) |
701 | 0 | rToken.clear(); |
702 | 0 | else if (rDelim.empty()) |
703 | 0 | { |
704 | 0 | rToken = rText; |
705 | 0 | if (!rToken.isEmpty()) |
706 | 0 | nRes = rText.getLength(); |
707 | 0 | } |
708 | 0 | else |
709 | 0 | { |
710 | 0 | sal_Int32 i; |
711 | 0 | for (i = nPos; i < rText.getLength(); ++i) |
712 | 0 | { |
713 | 0 | if (std::string_view::npos != rDelim.find( rText[i] )) |
714 | 0 | break; |
715 | 0 | } |
716 | |
|
717 | 0 | if (i >= rText.getLength()) // delimiter not found |
718 | 0 | rToken = rText.copy( nPos ); |
719 | 0 | else |
720 | 0 | rToken = rText.copy( nPos, i - nPos ); |
721 | 0 | nRes = i + 1; // continue after found delimiter |
722 | 0 | } |
723 | |
|
724 | 0 | return nRes; |
725 | 0 | } |
726 | | |
727 | | |
728 | | static void AddInternal( |
729 | | const uno::Reference<XDictionary> &rDic, |
730 | | const OUString& rNew ) |
731 | 0 | { |
732 | 0 | if (!rDic.is()) |
733 | 0 | return; |
734 | | |
735 | | //! TL TODO: word iterator should be used to break up the text |
736 | 0 | OUString aDelim(u"!\"#$%&'()*+,-/:;<=>?[]\\_^`{|}~\t \n"_ustr); |
737 | 0 | OSL_ENSURE(aDelim.indexOf(u'.') == -1, |
738 | 0 | "ensure no '.'"); |
739 | |
|
740 | 0 | OUString aToken; |
741 | 0 | sal_Int32 nPos = 0; |
742 | 0 | while (-1 != (nPos = lcl_GetToken( aToken, rNew, nPos, aDelim ))) |
743 | 0 | { |
744 | 0 | if( !aToken.isEmpty() && !IsNumeric( aToken ) ) |
745 | 0 | { |
746 | 0 | rDic->add( aToken, false, OUString() ); |
747 | 0 | } |
748 | 0 | } |
749 | 0 | } |
750 | | |
751 | | static void AddUserData( const uno::Reference< XDictionary > &rDic ) |
752 | 0 | { |
753 | 0 | if (rDic.is()) |
754 | 0 | { |
755 | 0 | SvtUserOptions aUserOpt; |
756 | 0 | AddInternal( rDic, aUserOpt.GetFullName() ); |
757 | 0 | AddInternal( rDic, aUserOpt.GetCompany() ); |
758 | 0 | AddInternal( rDic, aUserOpt.GetStreet() ); |
759 | 0 | AddInternal( rDic, aUserOpt.GetCity() ); |
760 | 0 | AddInternal( rDic, aUserOpt.GetTitle() ); |
761 | 0 | AddInternal( rDic, aUserOpt.GetPosition() ); |
762 | 0 | AddInternal( rDic, aUserOpt.GetEmail() ); |
763 | 0 | } |
764 | 0 | } |
765 | | |
766 | | static bool IsVers2OrNewer( const OUString& rFileURL, LanguageType& nLng, bool& bNeg, OUString& aDicName ) |
767 | 0 | { |
768 | 0 | if (rFileURL.isEmpty()) |
769 | 0 | return false; |
770 | 0 | OUString aExt; |
771 | 0 | sal_Int32 nPos = rFileURL.lastIndexOf( '.' ); |
772 | 0 | if (-1 != nPos) |
773 | 0 | aExt = rFileURL.copy( nPos + 1 ).toAsciiLowerCase(); |
774 | |
|
775 | 0 | if (aExt != "dic") |
776 | 0 | return false; |
777 | | |
778 | | // get stream to be used |
779 | 0 | const uno::Reference< uno::XComponentContext >& xContext( comphelper::getProcessComponentContext() ); |
780 | | |
781 | | // get XInputStream stream |
782 | 0 | uno::Reference< io::XInputStream > xStream; |
783 | 0 | try |
784 | 0 | { |
785 | 0 | uno::Reference< ucb::XSimpleFileAccess3 > xAccess( ucb::SimpleFileAccess::create(xContext) ); |
786 | 0 | xStream = xAccess->openFileRead( rFileURL ); |
787 | 0 | } |
788 | 0 | catch (const uno::Exception &) |
789 | 0 | { |
790 | 0 | SAL_WARN( "linguistic", "failed to get input stream" ); |
791 | 0 | } |
792 | 0 | DBG_ASSERT( xStream.is(), "failed to get stream for read" ); |
793 | 0 | if (!xStream.is()) |
794 | 0 | return false; |
795 | | |
796 | 0 | std::unique_ptr<SvStream> pStream( utl::UcbStreamHelper::CreateStream( xStream ) ); |
797 | |
|
798 | 0 | int nDicVersion = ReadDicVersion(*pStream, nLng, bNeg, aDicName); |
799 | 0 | return 2 == nDicVersion || nDicVersion >= 5; |
800 | 0 | } |
801 | | |
802 | | extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* |
803 | | linguistic_DicList_get_implementation( |
804 | | css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) |
805 | 0 | { |
806 | 0 | return cppu::acquire(new DicList()); |
807 | 0 | } |
808 | | |
809 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |