Update ooo320-m1
[ooovba.git] / lingucomponent / source / spellcheck / macosxspell / macspellimp.cxx
blob773af2397d35afa11a2414300b84205a29f24d01
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: macspellimp.cxx,v $
10 * $Revision: 1.5 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_lingucomponent.hxx"
33 #include <com/sun/star/uno/Reference.h>
34 #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
36 #include <com/sun/star/linguistic2/SpellFailure.hpp>
37 #include <cppuhelper/factory.hxx> // helper for factories
38 #include <com/sun/star/registry/XRegistryKey.hpp>
39 #include <tools/debug.hxx>
40 #include <unotools/processfactory.hxx>
41 #include <osl/mutex.hxx>
43 //#include <hunspell.hxx>
44 #include <dictmgr.hxx>
45 #include <macspellimp.hxx>
47 //#include <linguistic/lngprops.hxx>
48 #include <linguistic/spelldta.hxx>
49 #include <svtools/pathoptions.hxx>
50 #include <svtools/useroptions.hxx>
51 #include <osl/file.hxx>
52 #include <rtl/ustrbuf.hxx>
55 using namespace utl;
56 using namespace osl;
57 using namespace rtl;
58 using namespace com::sun::star;
59 using namespace com::sun::star::beans;
60 using namespace com::sun::star::lang;
61 using namespace com::sun::star::uno;
62 using namespace com::sun::star::linguistic2;
63 using namespace linguistic;
64 ///////////////////////////////////////////////////////////////////////////
65 // dbg_dump for development
66 #if OSL_DEBUG_LEVEL > 1
67 #include <rtl/strbuf.hxx>
68 #include <rtl/ustring.hxx>
70 const sal_Char *dbg_dump(const rtl::OString &rStr)
72 static rtl::OStringBuffer aStr;
74 aStr = rtl::OStringBuffer(rStr);
75 aStr.append(static_cast<char>(0));
76 return aStr.getStr();
79 const sal_Char *dbg_dump(const rtl::OUString &rStr)
81 return dbg_dump(rtl::OUStringToOString(rStr, RTL_TEXTENCODING_UTF8));
84 const sal_Char *dbg_dump(rtl_String *pStr)
86 return dbg_dump(rtl::OString(pStr));
89 const sal_Char *dbg_dump(rtl_uString *pStr)
91 return dbg_dump(rtl::OUString(pStr));
94 #endif
95 ///////////////////////////////////////////////////////////////////////////
97 MacSpellChecker::MacSpellChecker() :
98 aEvtListeners ( GetLinguMutex() )
100 // aDicts = NULL;
101 aDEncs = NULL;
102 aDLocs = NULL;
103 aDNames = NULL;
104 bDisposing = FALSE;
105 pPropHelper = NULL;
106 numdict = 0;
107 NSApplicationLoad();
108 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
109 macSpell = [NSSpellChecker sharedSpellChecker];
110 macTag = [NSSpellChecker uniqueSpellDocumentTag];
111 [pool release];
115 MacSpellChecker::~MacSpellChecker()
117 // if (aDicts) {
118 // for (int i = 0; i < numdict; i++) {
119 // if (aDicts[i]) delete aDicts[i];
120 // aDicts[i] = NULL;
121 // }
122 // delete[] aDicts;
123 // }
124 // aDicts = NULL;
125 numdict = 0;
126 if (aDEncs) delete[] aDEncs;
127 aDEncs = NULL;
128 if (aDLocs) delete[] aDLocs;
129 aDLocs = NULL;
130 if (aDNames) delete[] aDNames;
131 aDNames = NULL;
132 if (pPropHelper)
133 pPropHelper->RemoveAsPropListener();
137 PropertyHelper_Spell & MacSpellChecker::GetPropHelper_Impl()
139 if (!pPropHelper)
141 Reference< XPropertySet > xPropSet( GetLinguProperties(), UNO_QUERY );
143 pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
144 xPropHelper = pPropHelper;
145 pPropHelper->AddAsPropListener(); //! after a reference is established
147 return *pPropHelper;
151 Sequence< Locale > SAL_CALL MacSpellChecker::getLocales()
152 throw(RuntimeException)
154 MutexGuard aGuard( GetLinguMutex() );
156 // this routine should return the locales supported by the installed
157 // dictionaries. So here we need to parse both the user edited
158 // dictionary list and the shared dictionary list
159 // to see what dictionaries the admin/user has installed
161 int numusr; // number of user dictionary entries
162 int numshr; // number of shared dictionary entries
163 dictentry * spdict; // shared dict entry pointer
164 dictentry * updict; // user dict entry pointer
165 SvtPathOptions aPathOpt;
166 rtl_TextEncoding aEnc = RTL_TEXTENCODING_UTF8;
168 std::vector<objc_object *> postspdict;
169 //std::vector<dictentry *> postspdict;
170 std::vector<dictentry *> postupdict;
173 if (!numdict) {
175 // invoke a dictionary manager to get the user dictionary list
176 // TODO How on Mac OS X?
178 // invoke a second dictionary manager to get the shared dictionary list
179 NSArray *aLocales = [NSLocale availableLocaleIdentifiers];
181 //Test for existence of the dictionaries
182 for (unsigned int i = 0; i < [aLocales count]; i++)
184 if( [macSpell setLanguage:[aLocales objectAtIndex:i] ] )
186 postspdict.push_back( [ aLocales objectAtIndex:i ] );
190 numusr = postupdict.size();
191 numshr = postspdict.size();
193 // we really should merge these and remove duplicates but since
194 // users can name their dictionaries anything they want it would
195 // be impossible to know if a real duplication exists unless we
196 // add some unique key to each myspell dictionary
197 numdict = numshr + numusr;
199 if (numdict) {
200 aDLocs = new Locale [numdict];
201 aDEncs = new rtl_TextEncoding [numdict];
202 aDNames = new OUString [numdict];
203 aSuppLocales.realloc(numdict);
204 Locale * pLocale = aSuppLocales.getArray();
205 int numlocs = 0;
206 int newloc;
207 int i,j;
208 int k = 0;
210 //first add the user dictionaries
211 //TODO for MAC?
213 // now add the shared dictionaries
214 for (i = 0; i < numshr; i++) {
215 NSDictionary *aLocDict = [ NSLocale componentsFromLocaleIdentifier:postspdict[i] ];
216 NSString* aLang = [ aLocDict objectForKey:NSLocaleLanguageCode ];
217 NSString* aCountry = [ aLocDict objectForKey:NSLocaleCountryCode ];
218 OUString lang([aLang cStringUsingEncoding: NSUTF8StringEncoding], [aLang length], aEnc);
219 OUString country([ aCountry cStringUsingEncoding: NSUTF8StringEncoding], [aCountry length], aEnc);
220 Locale nLoc( lang, country, OUString() );
221 newloc = 1;
222 //eliminate duplicates (is this needed for MacOS?)
223 for (j = 0; j < numlocs; j++) {
224 if (nLoc == pLocale[j]) newloc = 0;
226 if (newloc) {
227 pLocale[numlocs] = nLoc;
228 numlocs++;
230 aDLocs[k] = nLoc;
231 //pointer to Hunspell dictionary - not needed for MAC
232 //aDicts[k] = NULL;
233 aDEncs[k] = 0;
234 // Dictionary file names not valid for Mac Spell
235 //aDNames[k] = aPathOpt.GetLinguisticPath() + A2OU("/ooo/") + A2OU(postspdict[i]->filename);
236 k++;
239 aSuppLocales.realloc(numlocs);
241 } else {
242 /* no dictionary.lst found so register no dictionaries */
243 numdict = 0;
244 //aDicts = NULL;
245 aDEncs = NULL;
246 aDLocs = NULL;
247 aDNames = NULL;
248 aSuppLocales.realloc(0);
251 /* de-allocation of memory is handled inside the DictMgr */
252 updict = NULL;
253 spdict = NULL;
257 return aSuppLocales;
262 sal_Bool SAL_CALL MacSpellChecker::hasLocale(const Locale& rLocale)
263 throw(RuntimeException)
265 MutexGuard aGuard( GetLinguMutex() );
267 BOOL bRes = FALSE;
268 if (!aSuppLocales.getLength())
269 getLocales();
271 INT32 nLen = aSuppLocales.getLength();
272 for (INT32 i = 0; i < nLen; ++i)
274 const Locale *pLocale = aSuppLocales.getConstArray();
275 if (rLocale == pLocale[i])
277 bRes = TRUE;
278 break;
281 return bRes;
285 INT16 MacSpellChecker::GetSpellFailure( const OUString &rWord, const Locale &rLocale )
287 rtl_TextEncoding aEnc;
289 // initialize a myspell object for each dictionary once
290 // (note: mutex is held higher up in isValid)
293 INT16 nRes = -1;
295 // first handle smart quotes both single and double
296 OUStringBuffer rBuf(rWord);
297 sal_Int32 n = rBuf.getLength();
298 sal_Unicode c;
299 for (sal_Int32 ix=0; ix < n; ix++) {
300 c = rBuf.charAt(ix);
301 if ((c == 0x201C) || (c == 0x201D)) rBuf.setCharAt(ix,(sal_Unicode)0x0022);
302 if ((c == 0x2018) || (c == 0x2019)) rBuf.setCharAt(ix,(sal_Unicode)0x0027);
304 OUString nWord(rBuf.makeStringAndClear());
306 if (n)
308 aEnc = 0;
309 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
310 NSString* aNSStr = [[NSString alloc] initWithCharacters: nWord.getStr() length: nWord.getLength()];
311 NSString* aLang = [[NSString alloc] initWithCharacters: rLocale.Language.getStr() length: rLocale.Language.getLength()];
312 if(rLocale.Country.getLength()>0)
314 NSString* aCountry = [[NSString alloc] initWithCharacters: rLocale.Country.getStr() length: rLocale.Country.getLength()];
315 NSString* aTag = @"_";
316 NSString* aTaggedCountry = [aTag stringByAppendingString:aCountry];
317 [aLang autorelease];
318 aLang = [aLang stringByAppendingString:aTaggedCountry];
321 int aCount;
322 NSRange range = [macSpell checkSpellingOfString:aNSStr startingAt:0 language:aLang wrap:FALSE inSpellDocumentWithTag:macTag wordCount:&aCount];
323 int rVal = 0;
324 if(range.length>0)
326 rVal = -1;
328 else
330 rVal = 1;
332 [pool release];
333 if (rVal != 1)
335 nRes = SpellFailure::SPELLING_ERROR;
336 } else {
337 return -1;
340 return nRes;
345 sal_Bool SAL_CALL
346 MacSpellChecker::isValid( const OUString& rWord, const Locale& rLocale,
347 const PropertyValues& rProperties )
348 throw(IllegalArgumentException, RuntimeException)
350 MutexGuard aGuard( GetLinguMutex() );
352 if (rLocale == Locale() || !rWord.getLength())
353 return TRUE;
355 if (!hasLocale( rLocale ))
356 #ifdef LINGU_EXCEPTIONS
357 throw( IllegalArgumentException() );
358 #else
359 return TRUE;
360 #endif
362 // Get property values to be used.
363 // These are be the default values set in the SN_LINGU_PROPERTIES
364 // PropertySet which are overridden by the supplied ones from the
365 // last argument.
366 // You'll probably like to use a simplier solution than the provided
367 // one using the PropertyHelper_Spell.
369 PropertyHelper_Spell &rHelper = GetPropHelper();
370 rHelper.SetTmpPropVals( rProperties );
372 INT16 nFailure = GetSpellFailure( rWord, rLocale );
373 if (nFailure != -1)
375 INT16 nLang = LocaleToLanguage( rLocale );
376 // postprocess result for errors that should be ignored
377 if ( (!rHelper.IsSpellUpperCase() && IsUpper( rWord, nLang ))
378 || (!rHelper.IsSpellWithDigits() && HasDigits( rWord ))
379 || (!rHelper.IsSpellCapitalization()
380 && nFailure == SpellFailure::CAPTION_ERROR)
382 nFailure = -1;
385 return (nFailure == -1);
389 Reference< XSpellAlternatives >
390 MacSpellChecker::GetProposals( const OUString &rWord, const Locale &rLocale )
392 // Retrieves the return values for the 'spell' function call in case
393 // of a misspelled word.
394 // Especially it may give a list of suggested (correct) words:
396 Reference< XSpellAlternatives > xRes;
397 // note: mutex is held by higher up by spell which covers both
399 INT16 nLang = LocaleToLanguage( rLocale );
400 int count;
401 Sequence< OUString > aStr( 0 );
403 // first handle smart quotes (single and double)
404 OUStringBuffer rBuf(rWord);
405 sal_Int32 n = rBuf.getLength();
406 sal_Unicode c;
407 for (sal_Int32 ix=0; ix < n; ix++) {
408 c = rBuf.charAt(ix);
409 if ((c == 0x201C) || (c == 0x201D)) rBuf.setCharAt(ix,(sal_Unicode)0x0022);
410 if ((c == 0x2018) || (c == 0x2019)) rBuf.setCharAt(ix,(sal_Unicode)0x0027);
412 OUString nWord(rBuf.makeStringAndClear());
414 if (n)
416 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
417 NSString* aNSStr = [[NSString alloc] initWithCharacters: nWord.getStr() length: nWord.getLength()];
418 NSString* aLang = [[NSString alloc] initWithCharacters: rLocale.Language.getStr() length: rLocale.Language.getLength() ];
419 if(rLocale.Country.getLength()>0)
421 NSString* aCountry = [[NSString alloc] initWithCharacters: rLocale.Country.getStr() length: rLocale.Country.getLength() ];
422 NSString* aTag = @"_";
423 NSString* aTaggedCountry = [aTag stringByAppendingString:aCountry];
424 [aLang autorelease];
425 aLang = [aLang stringByAppendingString:aTaggedCountry];
427 [macSpell setLanguage:aLang];
428 NSArray *guesses = [macSpell guessesForWord:aNSStr];
429 count = [guesses count];
430 if (count)
432 aStr.realloc( count );
433 OUString *pStr = aStr.getArray();
434 for (int ii=0; ii < count; ii++)
436 // if needed add: if (suglst[ii] == NULL) continue;
437 NSString* guess = [guesses objectAtIndex:ii];
438 OUString cvtwrd((const sal_Unicode*)[guess cStringUsingEncoding:NSUnicodeStringEncoding], (sal_Int32)[guess length]);
439 pStr[ii] = cvtwrd;
442 [pool release];
445 // now return an empty alternative for no suggestions or the list of alternatives if some found
446 SpellAlternatives *pAlt = new SpellAlternatives;
447 String aTmp(rWord);
448 pAlt->SetWordLanguage( aTmp, nLang );
449 pAlt->SetFailureType( SpellFailure::SPELLING_ERROR );
450 pAlt->SetAlternatives( aStr );
451 xRes = pAlt;
452 return xRes;
459 Reference< XSpellAlternatives > SAL_CALL
460 MacSpellChecker::spell( const OUString& rWord, const Locale& rLocale,
461 const PropertyValues& rProperties )
462 throw(IllegalArgumentException, RuntimeException)
464 MutexGuard aGuard( GetLinguMutex() );
466 if (rLocale == Locale() || !rWord.getLength())
467 return NULL;
469 if (!hasLocale( rLocale ))
470 #ifdef LINGU_EXCEPTIONS
471 throw( IllegalArgumentException() );
472 #else
473 return NULL;
474 #endif
476 Reference< XSpellAlternatives > xAlt;
477 if (!isValid( rWord, rLocale, rProperties ))
479 xAlt = GetProposals( rWord, rLocale );
481 return xAlt;
485 Reference< XInterface > SAL_CALL MacSpellChecker_CreateInstance(
486 const Reference< XMultiServiceFactory > & /*rSMgr*/ )
487 throw(Exception)
490 Reference< XInterface > xService = (cppu::OWeakObject*) new MacSpellChecker;
491 return xService;
495 sal_Bool SAL_CALL
496 MacSpellChecker::addLinguServiceEventListener(
497 const Reference< XLinguServiceEventListener >& rxLstnr )
498 throw(RuntimeException)
500 MutexGuard aGuard( GetLinguMutex() );
502 BOOL bRes = FALSE;
503 if (!bDisposing && rxLstnr.is())
505 bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
507 return bRes;
511 sal_Bool SAL_CALL
512 MacSpellChecker::removeLinguServiceEventListener(
513 const Reference< XLinguServiceEventListener >& rxLstnr )
514 throw(RuntimeException)
516 MutexGuard aGuard( GetLinguMutex() );
518 BOOL bRes = FALSE;
519 if (!bDisposing && rxLstnr.is())
521 DBG_ASSERT( xPropHelper.is(), "xPropHelper non existent" );
522 bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
524 return bRes;
528 OUString SAL_CALL
529 MacSpellChecker::getServiceDisplayName( const Locale& /*rLocale*/ )
530 throw(RuntimeException)
532 MutexGuard aGuard( GetLinguMutex() );
533 return A2OU( "Mac OS X Spell Checker" );
537 void SAL_CALL
538 MacSpellChecker::initialize( const Sequence< Any >& rArguments )
539 throw(Exception, RuntimeException)
541 MutexGuard aGuard( GetLinguMutex() );
543 if (!pPropHelper)
545 INT32 nLen = rArguments.getLength();
546 if (2 == nLen)
548 Reference< XPropertySet > xPropSet;
549 rArguments.getConstArray()[0] >>= xPropSet;
550 //rArguments.getConstArray()[1] >>= xDicList;
552 //! Pointer allows for access of the non-UNO functions.
553 //! And the reference to the UNO-functions while increasing
554 //! the ref-count and will implicitly free the memory
555 //! when the object is not longer used.
556 pPropHelper = new PropertyHelper_Spell( (XSpellChecker *) this, xPropSet );
557 xPropHelper = pPropHelper;
558 pPropHelper->AddAsPropListener(); //! after a reference is established
560 else
561 DBG_ERROR( "wrong number of arguments in sequence" );
567 void SAL_CALL
568 MacSpellChecker::dispose()
569 throw(RuntimeException)
571 MutexGuard aGuard( GetLinguMutex() );
573 if (!bDisposing)
575 bDisposing = TRUE;
576 EventObject aEvtObj( (XSpellChecker *) this );
577 aEvtListeners.disposeAndClear( aEvtObj );
582 void SAL_CALL
583 MacSpellChecker::addEventListener( const Reference< XEventListener >& rxListener )
584 throw(RuntimeException)
586 MutexGuard aGuard( GetLinguMutex() );
588 if (!bDisposing && rxListener.is())
589 aEvtListeners.addInterface( rxListener );
593 void SAL_CALL
594 MacSpellChecker::removeEventListener( const Reference< XEventListener >& rxListener )
595 throw(RuntimeException)
597 MutexGuard aGuard( GetLinguMutex() );
599 if (!bDisposing && rxListener.is())
600 aEvtListeners.removeInterface( rxListener );
604 ///////////////////////////////////////////////////////////////////////////
605 // Service specific part
608 OUString SAL_CALL MacSpellChecker::getImplementationName()
609 throw(RuntimeException)
611 MutexGuard aGuard( GetLinguMutex() );
613 return getImplementationName_Static();
617 sal_Bool SAL_CALL MacSpellChecker::supportsService( const OUString& ServiceName )
618 throw(RuntimeException)
620 MutexGuard aGuard( GetLinguMutex() );
622 Sequence< OUString > aSNL = getSupportedServiceNames();
623 const OUString * pArray = aSNL.getConstArray();
624 for( INT32 i = 0; i < aSNL.getLength(); i++ )
625 if( pArray[i] == ServiceName )
626 return TRUE;
627 return FALSE;
631 Sequence< OUString > SAL_CALL MacSpellChecker::getSupportedServiceNames()
632 throw(RuntimeException)
634 MutexGuard aGuard( GetLinguMutex() );
636 return getSupportedServiceNames_Static();
640 Sequence< OUString > MacSpellChecker::getSupportedServiceNames_Static()
641 throw()
643 MutexGuard aGuard( GetLinguMutex() );
645 Sequence< OUString > aSNS( 1 ); // auch mehr als 1 Service moeglich
646 aSNS.getArray()[0] = A2OU( SN_SPELLCHECKER );
647 return aSNS;
651 sal_Bool SAL_CALL MacSpellChecker_writeInfo(
652 void * /*pServiceManager*/, registry::XRegistryKey * pRegistryKey )
657 String aImpl( '/' );
658 aImpl += MacSpellChecker::getImplementationName_Static().getStr();
659 aImpl.AppendAscii( "/UNO/SERVICES" );
660 Reference< registry::XRegistryKey > xNewKey =
661 pRegistryKey->createKey( aImpl );
662 Sequence< OUString > aServices =
663 MacSpellChecker::getSupportedServiceNames_Static();
664 for( INT32 i = 0; i < aServices.getLength(); i++ )
665 xNewKey->createKey( aServices.getConstArray()[i] );
667 return sal_True;
669 catch(Exception &)
671 return sal_False;
676 void * SAL_CALL MacSpellChecker_getFactory( const sal_Char * pImplName,
677 XMultiServiceFactory * pServiceManager, void * )
679 void * pRet = 0;
680 if ( !MacSpellChecker::getImplementationName_Static().compareToAscii( pImplName ) )
682 Reference< XSingleServiceFactory > xFactory =
683 cppu::createOneInstanceFactory(
684 pServiceManager,
685 MacSpellChecker::getImplementationName_Static(),
686 MacSpellChecker_CreateInstance,
687 MacSpellChecker::getSupportedServiceNames_Static());
688 // acquire, because we return an interface pointer instead of a reference
689 xFactory->acquire();
690 pRet = xFactory.get();
692 return pRet;
696 ///////////////////////////////////////////////////////////////////////////