fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / lingucomponent / source / spellcheck / macosxspell / macspellimp.mm
blob609e315f7f932ec9455c1b489efd2a18599e9bc2
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  */
20 #include <com/sun/star/uno/Reference.h>
21 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
23 #include <com/sun/star/linguistic2/SpellFailure.hpp>
24 #include <cppuhelper/factory.hxx>
25 #include <cppuhelper/supportsservice.hxx>
26 #include <com/sun/star/registry/XRegistryKey.hpp>
27 #include <tools/debug.hxx>
28 #include <osl/mutex.hxx>
30 #include <macspellimp.hxx>
32 #include <linguistic/spelldta.hxx>
33 #include <unotools/pathoptions.hxx>
34 #include <unotools/useroptions.hxx>
35 #include <osl/file.hxx>
36 #include <rtl/ustrbuf.hxx>
38 using namespace utl;
39 using namespace osl;
40 using namespace com::sun::star;
41 using namespace com::sun::star::beans;
42 using namespace com::sun::star::lang;
43 using namespace com::sun::star::uno;
44 using namespace com::sun::star::linguistic2;
45 using namespace linguistic;
47 using ::rtl::OUString;
48 using ::rtl::OString;
49 using ::rtl::OUStringBuffer;
50 using ::rtl::OUStringToOString;
52 MacSpellChecker::MacSpellChecker() :
53     aEvtListeners( GetLinguMutex() )
55     aDEncs = NULL;
56     aDLocs = NULL;
57     aDNames = NULL;
58     bDisposing = false;
59     pPropHelper = NULL;
60     numdict = 0;
61     NSApplicationLoad();
62     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
63     macSpell = [NSSpellChecker sharedSpellChecker];
64     macTag = [NSSpellChecker uniqueSpellDocumentTag];
65     [pool release];
69 MacSpellChecker::~MacSpellChecker()
71   numdict = 0;
72   if (aDEncs) delete[] aDEncs;
73   aDEncs = NULL;
74   if (aDLocs) delete[] aDLocs;
75   aDLocs = NULL;
76   if (aDNames) delete[] aDNames;
77   aDNames = NULL;
78   if (pPropHelper)
79      pPropHelper->RemoveAsPropListener();
83 PropertyHelper_Spell & MacSpellChecker::GetPropHelper_Impl()
85     if (!pPropHelper)
86     {
87         Reference< XLinguProperties >   xPropSet( GetLinguProperties(), UNO_QUERY );
89         pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
90         xPropHelper = pPropHelper;
91         pPropHelper->AddAsPropListener();   //! after a reference is established
92     }
93     return *pPropHelper;
97 Sequence< Locale > SAL_CALL MacSpellChecker::getLocales()
98         throw(RuntimeException)
100     MutexGuard  aGuard( GetLinguMutex() );
102         // this routine should return the locales supported by the installed
103         // dictionaries.  So here we need to parse both the user edited
104         // dictionary list and the shared dictionary list
105         // to see what dictionaries the admin/user has installed
107         int numshr;          // number of shared dictionary entries
108         rtl_TextEncoding aEnc = RTL_TEXTENCODING_UTF8;
110         std::vector<NSString *> postspdict;
112     if (!numdict) {
114         // invoke a dictionary manager to get the user dictionary list
115         // TODO How on Mac OS X?
117         // invoke a second  dictionary manager to get the shared dictionary list
118         NSArray *aLocales = [NSLocale availableLocaleIdentifiers];
120         //Test for existence of the dictionaries
121         for (unsigned int i = 0; i < [aLocales count]; i++)
122         {
123             NSString* pLangStr = (NSString*)[aLocales objectAtIndex:i];
124             if( [macSpell setLanguage:pLangStr ] )
125             {
126                 postspdict.push_back( pLangStr );
127             }
128         }
130         numshr = postspdict.size();
132         // we really should merge these and remove duplicates but since
133         // users can name their dictionaries anything they want it would
134         // be impossible to know if a real duplication exists unless we
135         // add some unique key to each myspell dictionary
136         numdict = numshr;
138         if (numdict) {
139             aDLocs = new Locale [numdict];
140             aDEncs  = new rtl_TextEncoding [numdict];
141             aDNames = new OUString [numdict];
142             aSuppLocales.realloc(numdict);
143             Locale * pLocale = aSuppLocales.getArray();
144             int numlocs = 0;
145             int newloc;
146             int i,j;
147             int k = 0;
149             //first add the user dictionaries
150             //TODO for MAC?
152             // now add the shared dictionaries
153             for (i = 0; i < numshr; i++) {
154                 NSDictionary *aLocDict = [ NSLocale componentsFromLocaleIdentifier:postspdict[i] ];
155                 NSString* aLang = [ aLocDict objectForKey:NSLocaleLanguageCode ];
156                 NSString* aCountry = [ aLocDict objectForKey:NSLocaleCountryCode ];
157                 OUString lang([aLang cStringUsingEncoding: NSUTF8StringEncoding], [aLang length], aEnc);
158                 OUString country([ aCountry cStringUsingEncoding: NSUTF8StringEncoding], [aCountry length], aEnc);
159                 Locale nLoc( lang, country, OUString() );
160                 newloc = 1;
161                 //eliminate duplicates (is this needed for MacOS?)
162                 for (j = 0; j < numlocs; j++) {
163                     if (nLoc == pLocale[j]) newloc = 0;
164                 }
165                 if (newloc) {
166                     pLocale[numlocs] = nLoc;
167                     numlocs++;
168                 }
169                 aDLocs[k] = nLoc;
170                 aDEncs[k] = 0;
171                 k++;
172             }
174             aSuppLocales.realloc(numlocs);
176         } else {
177             /* no dictionary.lst found so register no dictionaries */
178             numdict = 0;
179                 aDEncs  = NULL;
180                 aDLocs = NULL;
181                 aDNames = NULL;
182                 aSuppLocales.realloc(0);
183             }
184         }
186     return aSuppLocales;
191 sal_Bool SAL_CALL MacSpellChecker::hasLocale(const Locale& rLocale)
192         throw(RuntimeException)
194     MutexGuard  aGuard( GetLinguMutex() );
196     bool bRes = false;
197     if (!aSuppLocales.getLength())
198         getLocales();
200     sal_Int32 nLen = aSuppLocales.getLength();
201     for (sal_Int32 i = 0;  i < nLen;  ++i)
202     {
203         const Locale *pLocale = aSuppLocales.getConstArray();
204         if (rLocale == pLocale[i])
205         {
206             bRes = true;
207             break;
208         }
209     }
210     return bRes;
214 sal_Int16 MacSpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale )
216     rtl_TextEncoding aEnc;
218     // initialize a myspell object for each dictionary once
219         // (note: mutex is held higher up in isValid)
222     sal_Int16 nRes = -1;
224         // first handle smart quotes both single and double
225     OUStringBuffer rBuf(rWord);
226         sal_Int32 n = rBuf.getLength();
227         sal_Unicode c;
228     for (sal_Int32 ix=0; ix < n; ix++) {
229         c = rBuf[ix];
230         if ((c == 0x201C) || (c == 0x201D)) rBuf[ix] = (sal_Unicode)0x0022;
231         if ((c == 0x2018) || (c == 0x2019)) rBuf[ix] = (sal_Unicode)0x0027;
232         }
233         OUString nWord(rBuf.makeStringAndClear());
235     if (n)
236     {
237         aEnc = 0;
238         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
239         NSString* aNSStr = [[NSString alloc] initWithCharacters: nWord.getStr() length: nWord.getLength()];
240         NSString* aLang = [[NSString alloc] initWithCharacters: rLocale.Language.getStr() length: rLocale.Language.getLength()];
241         if(rLocale.Country.getLength()>0)
242         {
243             NSString* aCountry = [[NSString alloc] initWithCharacters: rLocale.Country.getStr() length: rLocale.Country.getLength()];
244             NSString* aTag = @"_";
245             NSString* aTaggedCountry = [aTag stringByAppendingString:aCountry];
246             [aLang autorelease];
247             aLang = [aLang  stringByAppendingString:aTaggedCountry];
248         }
250         NSInteger aCount;
251         NSRange range = [macSpell checkSpellingOfString:aNSStr startingAt:0 language:aLang wrap:sal_False inSpellDocumentWithTag:macTag wordCount:&aCount];
252         int rVal = 0;
253         if(range.length>0)
254         {
255             rVal = -1;
256         }
257         else
258         {
259             rVal = 1;
260         }
261         [pool release];
262         if (rVal != 1)
263         {
264             nRes = SpellFailure::SPELLING_ERROR;
265         } else {
266             return -1;
267         }
268     }
269     return nRes;
274 sal_Bool SAL_CALL
275     MacSpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
276             const PropertyValues& rProperties )
277         throw(IllegalArgumentException, RuntimeException)
279     MutexGuard  aGuard( GetLinguMutex() );
281      if (rLocale == Locale()  ||  !rWord.getLength())
282         return sal_True;
284     if (!hasLocale( rLocale ))
285         return sal_True;
287     // Get property values to be used.
288     // These are be the default values set in the SN_LINGU_PROPERTIES
289     // PropertySet which are overridden by the supplied ones from the
290     // last argument.
291     // You'll probably like to use a simpler solution than the provided
292     // one using the PropertyHelper_Spell.
294     PropertyHelper_Spell &rHelper = GetPropHelper();
295     rHelper.SetTmpPropVals( rProperties );
297     sal_Int16 nFailure = GetSpellFailure( rWord, rLocale );
298     if (nFailure != -1)
299     {
300         sal_Int16 nLang = LinguLocaleToLanguage( rLocale );
301         // postprocess result for errors that should be ignored
302         if (   (!rHelper.IsSpellUpperCase()  && IsUpper( rWord, nLang ))
303             || (!rHelper.IsSpellWithDigits() && HasDigits( rWord ))
304             || (!rHelper.IsSpellCapitalization()
305                 &&  nFailure == SpellFailure::CAPTION_ERROR)
306         )
307             nFailure = -1;
308     }
310     return (nFailure == -1);
314 Reference< XSpellAlternatives >
315     MacSpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
317     // Retrieves the return values for the 'spell' function call in case
318     // of a misspelled word.
319     // Especially it may give a list of suggested (correct) words:
321     Reference< XSpellAlternatives > xRes;
322         // note: mutex is held by higher up by spell which covers both
324     sal_Int16 nLang = LinguLocaleToLanguage( rLocale );
325     int count;
326     Sequence< OUString > aStr( 0 );
328         // first handle smart quotes (single and double)
329     OUStringBuffer rBuf(rWord);
330         sal_Int32 n = rBuf.getLength();
331         sal_Unicode c;
332     for (sal_Int32 ix=0; ix < n; ix++) {
333          c = rBuf[ix];
334          if ((c == 0x201C) || (c == 0x201D)) rBuf[ix] = (sal_Unicode)0x0022;
335          if ((c == 0x2018) || (c == 0x2019)) rBuf[ix] = (sal_Unicode)0x0027;
336         }
337         OUString nWord(rBuf.makeStringAndClear());
339     if (n)
340     {
341         NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
342         NSString* aNSStr = [[NSString alloc] initWithCharacters: nWord.getStr() length: nWord.getLength()];
343         NSString* aLang = [[NSString alloc] initWithCharacters: rLocale.Language.getStr() length: rLocale.Language.getLength() ];
344         if(rLocale.Country.getLength()>0)
345         {
346             NSString* aCountry = [[NSString alloc] initWithCharacters: rLocale.Country.getStr() length: rLocale.Country.getLength() ];
347             NSString* aTag = @"_";
348             NSString* aTaggedCountry = [aTag stringByAppendingString:aCountry];
349             [aLang autorelease];
350             aLang = [aLang  stringByAppendingString:aTaggedCountry];
351         }
352         [macSpell setLanguage:aLang];
353         NSArray *guesses = [macSpell guessesForWordRange:NSMakeRange(0, [aNSStr length]) inString:aNSStr language:aLang inSpellDocumentWithTag:0];
354         count = [guesses count];
355         if (count)
356         {
357            aStr.realloc( count );
358            OUString *pStr = aStr.getArray();
359                for (int ii=0; ii < count; ii++)
360                {
361                   // if needed add: if (suglst[ii] == NULL) continue;
362                   NSString* guess = [guesses objectAtIndex:ii];
363                   OUString cvtwrd(reinterpret_cast<const sal_Unicode*>([guess cStringUsingEncoding:NSUnicodeStringEncoding]), (sal_Int32)[guess length]);
364                   pStr[ii] = cvtwrd;
365                }
366         }
367        [pool release];
368     }
370             // now return an empty alternative for no suggestions or the list of alternatives if some found
371         SpellAlternatives *pAlt = new SpellAlternatives;
372         pAlt->SetWordLanguage( rWord, nLang );
373         pAlt->SetFailureType( SpellFailure::SPELLING_ERROR );
374         pAlt->SetAlternatives( aStr );
375         xRes = pAlt;
376         return xRes;
383 Reference< XSpellAlternatives > SAL_CALL
384     MacSpellChecker::spell( const OUString& rWord, const Locale& rLocale,
385             const PropertyValues& rProperties )
386         throw(IllegalArgumentException, RuntimeException)
388     MutexGuard  aGuard( GetLinguMutex() );
390      if (rLocale == Locale()  ||  !rWord.getLength())
391         return NULL;
393     if (!hasLocale( rLocale ))
394         return NULL;
396     Reference< XSpellAlternatives > xAlt;
397     if (!isValid( rWord, rLocale, rProperties ))
398     {
399         xAlt =  GetProposals( rWord, rLocale );
400     }
401     return xAlt;
405 Reference< XInterface > SAL_CALL MacSpellChecker_CreateInstance(
406             const Reference< XMultiServiceFactory > & /*rSMgr*/ )
407         throw(Exception)
410     Reference< XInterface > xService = (cppu::OWeakObject*) new MacSpellChecker;
411     return xService;
415 sal_Bool SAL_CALL
416     MacSpellChecker::addLinguServiceEventListener(
417             const Reference< XLinguServiceEventListener >& rxLstnr )
418         throw(RuntimeException)
420     MutexGuard  aGuard( GetLinguMutex() );
422     bool bRes = false;
423     if (!bDisposing && rxLstnr.is())
424     {
425         bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
426     }
427     return bRes;
431 sal_Bool SAL_CALL
432     MacSpellChecker::removeLinguServiceEventListener(
433             const Reference< XLinguServiceEventListener >& rxLstnr )
434         throw(RuntimeException)
436     MutexGuard  aGuard( GetLinguMutex() );
438     bool bRes = false;
439     if (!bDisposing && rxLstnr.is())
440     {
441         DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
442         bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
443     }
444     return bRes;
448 OUString SAL_CALL
449     MacSpellChecker::getServiceDisplayName( const Locale& /*rLocale*/ )
450         throw(RuntimeException)
452     MutexGuard  aGuard( GetLinguMutex() );
453     return OUString( "Mac OS X Spell Checker" );
457 void SAL_CALL
458     MacSpellChecker::initialize( const Sequence< Any >& rArguments )
459         throw(Exception, RuntimeException)
461     MutexGuard  aGuard( GetLinguMutex() );
463     if (!pPropHelper)
464     {
465         sal_Int32 nLen = rArguments.getLength();
466         if (2 == nLen)
467         {
468             Reference< XLinguProperties >   xPropSet;
469             rArguments.getConstArray()[0] >>= xPropSet;
470             //rArguments.getConstArray()[1] >>= xDicList;
472             //! Pointer allows for access of the non-UNO functions.
473             //! And the reference to the UNO-functions while increasing
474             //! the ref-count and will implicitly free the memory
475             //! when the object is not longer used.
476             pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
477             xPropHelper = pPropHelper;
478             pPropHelper->AddAsPropListener();   //! after a reference is established
479         }
480         else
481             OSL_FAIL( "wrong number of arguments in sequence" );
483     }
487 void SAL_CALL
488     MacSpellChecker::dispose()
489         throw(RuntimeException)
491     MutexGuard  aGuard( GetLinguMutex() );
493     if (!bDisposing)
494     {
495         bDisposing = true;
496         EventObject aEvtObj( (XSpellChecker *) this );
497         aEvtListeners.disposeAndClear( aEvtObj );
498     }
502 void SAL_CALL
503     MacSpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
504         throw(RuntimeException)
506     MutexGuard  aGuard( GetLinguMutex() );
508     if (!bDisposing && rxListener.is())
509         aEvtListeners.addInterface( rxListener );
513 void SAL_CALL
514     MacSpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
515         throw(RuntimeException)
517     MutexGuard  aGuard( GetLinguMutex() );
519     if (!bDisposing && rxListener.is())
520         aEvtListeners.removeInterface( rxListener );
523 // Service specific part
524 OUString SAL_CALL MacSpellChecker::getImplementationName()
525         throw(RuntimeException)
527     MutexGuard  aGuard( GetLinguMutex() );
529     return getImplementationName_Static();
532 sal_Bool SAL_CALL MacSpellChecker::supportsService( const OUString& ServiceName )
533         throw(RuntimeException)
535     return cppu::supportsService(this, ServiceName);
538 Sequence< OUString > SAL_CALL MacSpellChecker::getSupportedServiceNames()
539         throw(RuntimeException)
541     MutexGuard  aGuard( GetLinguMutex() );
543     return getSupportedServiceNames_Static();
546 Sequence< OUString > MacSpellChecker::getSupportedServiceNames_Static()
547         throw()
549     MutexGuard  aGuard( GetLinguMutex() );
551     Sequence< OUString > aSNS( 1 ); // auch mehr als 1 Service moeglich
552     aSNS.getArray()[0] = SN_SPELLCHECKER;
553     return aSNS;
556 void * SAL_CALL MacSpellChecker_getFactory( const sal_Char * pImplName,
557             XMultiServiceFactory * pServiceManager, void *  )
559     void * pRet = 0;
560     if ( MacSpellChecker::getImplementationName_Static().equalsAscii( pImplName ) )
561     {
562         Reference< XSingleServiceFactory > xFactory =
563             cppu::createOneInstanceFactory(
564                 pServiceManager,
565                 MacSpellChecker::getImplementationName_Static(),
566                 MacSpellChecker_CreateInstance,
567                 MacSpellChecker::getSupportedServiceNames_Static());
568         // acquire, because we return an interface pointer instead of a reference
569         xFactory->acquire();
570         pRet = xFactory.get();
571     }
572     return pRet;
578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */