1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: macspellimp.cxx,v $
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>
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));
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
));
95 ///////////////////////////////////////////////////////////////////////////
97 MacSpellChecker::MacSpellChecker() :
98 aEvtListeners ( GetLinguMutex() )
108 NSAutoreleasePool
* pool
= [[NSAutoreleasePool alloc
] init
];
109 macSpell
= [NSSpellChecker sharedSpellChecker
];
110 macTag
= [NSSpellChecker uniqueSpellDocumentTag
];
115 MacSpellChecker::~MacSpellChecker()
118 // for (int i = 0; i < numdict; i++) {
119 // if (aDicts[i]) delete aDicts[i];
126 if (aDEncs
) delete[] aDEncs
;
128 if (aDLocs
) delete[] aDLocs
;
130 if (aDNames
) delete[] aDNames
;
133 pPropHelper
->RemoveAsPropListener();
137 PropertyHelper_Spell
& MacSpellChecker::GetPropHelper_Impl()
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
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
;
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
;
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();
210 //first add the user dictionaries
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() );
222 //eliminate duplicates (is this needed for MacOS?)
223 for (j
= 0; j
< numlocs
; j
++) {
224 if (nLoc
== pLocale
[j
]) newloc
= 0;
227 pLocale
[numlocs
] = nLoc
;
231 //pointer to Hunspell dictionary - not needed for MAC
234 // Dictionary file names not valid for Mac Spell
235 //aDNames[k] = aPathOpt.GetLinguisticPath() + A2OU("/ooo/") + A2OU(postspdict[i]->filename);
239 aSuppLocales
.realloc(numlocs
);
242 /* no dictionary.lst found so register no dictionaries */
248 aSuppLocales
.realloc(0);
251 /* de-allocation of memory is handled inside the DictMgr */
262 sal_Bool SAL_CALL
MacSpellChecker::hasLocale(const Locale
& rLocale
)
263 throw(RuntimeException
)
265 MutexGuard
aGuard( GetLinguMutex() );
268 if (!aSuppLocales
.getLength())
271 INT32 nLen
= aSuppLocales
.getLength();
272 for (INT32 i
= 0; i
< nLen
; ++i
)
274 const Locale
*pLocale
= aSuppLocales
.getConstArray();
275 if (rLocale
== pLocale
[i
])
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)
295 // first handle smart quotes both single and double
296 OUStringBuffer
rBuf(rWord
);
297 sal_Int32 n
= rBuf
.getLength();
299 for (sal_Int32 ix
=0; ix
< n
; 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());
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
];
318 aLang
= [aLang stringByAppendingString
:aTaggedCountry
];
322 NSRange range
= [macSpell checkSpellingOfString
:aNSStr startingAt
:0 language
:aLang wrap
:FALSE inSpellDocumentWithTag
:macTag wordCount
:&aCount
];
335 nRes
= SpellFailure::SPELLING_ERROR
;
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())
355 if (!hasLocale( rLocale
))
356 #ifdef LINGU_EXCEPTIONS
357 throw( IllegalArgumentException() );
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
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
);
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
)
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
);
401 Sequence
< OUString
> aStr( 0 );
403 // first handle smart quotes (single and double)
404 OUStringBuffer
rBuf(rWord
);
405 sal_Int32 n
= rBuf
.getLength();
407 for (sal_Int32 ix
=0; ix
< n
; 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());
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
];
425 aLang
= [aLang stringByAppendingString
:aTaggedCountry
];
427 [macSpell setLanguage
:aLang
];
428 NSArray
*guesses
= [macSpell guessesForWord
:aNSStr
];
429 count
= [guesses 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
]);
445 // now return an empty alternative for no suggestions or the list of alternatives if some found
446 SpellAlternatives
*pAlt
= new SpellAlternatives
;
448 pAlt
->SetWordLanguage( aTmp
, nLang
);
449 pAlt
->SetFailureType( SpellFailure::SPELLING_ERROR
);
450 pAlt
->SetAlternatives( aStr
);
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())
469 if (!hasLocale( rLocale
))
470 #ifdef LINGU_EXCEPTIONS
471 throw( IllegalArgumentException() );
476 Reference
< XSpellAlternatives
> xAlt
;
477 if (!isValid( rWord
, rLocale
, rProperties
))
479 xAlt
= GetProposals( rWord
, rLocale
);
485 Reference
< XInterface
> SAL_CALL
MacSpellChecker_CreateInstance(
486 const Reference
< XMultiServiceFactory
> & /*rSMgr*/ )
490 Reference
< XInterface
> xService
= (cppu::OWeakObject
*) new MacSpellChecker
;
496 MacSpellChecker::addLinguServiceEventListener(
497 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
498 throw(RuntimeException
)
500 MutexGuard
aGuard( GetLinguMutex() );
503 if (!bDisposing
&& rxLstnr
.is())
505 bRes
= GetPropHelper().addLinguServiceEventListener( rxLstnr
);
512 MacSpellChecker::removeLinguServiceEventListener(
513 const Reference
< XLinguServiceEventListener
>& rxLstnr
)
514 throw(RuntimeException
)
516 MutexGuard
aGuard( GetLinguMutex() );
519 if (!bDisposing
&& rxLstnr
.is())
521 DBG_ASSERT( xPropHelper
.is(), "xPropHelper non existent" );
522 bRes
= GetPropHelper().removeLinguServiceEventListener( rxLstnr
);
529 MacSpellChecker::getServiceDisplayName( const Locale
& /*rLocale*/ )
530 throw(RuntimeException
)
532 MutexGuard
aGuard( GetLinguMutex() );
533 return A2OU( "Mac OS X Spell Checker" );
538 MacSpellChecker::initialize( const Sequence
< Any
>& rArguments
)
539 throw(Exception
, RuntimeException
)
541 MutexGuard
aGuard( GetLinguMutex() );
545 INT32 nLen
= rArguments
.getLength();
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
561 DBG_ERROR( "wrong number of arguments in sequence" );
568 MacSpellChecker::dispose()
569 throw(RuntimeException
)
571 MutexGuard
aGuard( GetLinguMutex() );
576 EventObject
aEvtObj( (XSpellChecker
*) this );
577 aEvtListeners
.disposeAndClear( aEvtObj
);
583 MacSpellChecker::addEventListener( const Reference
< XEventListener
>& rxListener
)
584 throw(RuntimeException
)
586 MutexGuard
aGuard( GetLinguMutex() );
588 if (!bDisposing
&& rxListener
.is())
589 aEvtListeners
.addInterface( rxListener
);
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
)
631 Sequence
< OUString
> SAL_CALL
MacSpellChecker::getSupportedServiceNames()
632 throw(RuntimeException
)
634 MutexGuard
aGuard( GetLinguMutex() );
636 return getSupportedServiceNames_Static();
640 Sequence
< OUString
> MacSpellChecker::getSupportedServiceNames_Static()
643 MutexGuard
aGuard( GetLinguMutex() );
645 Sequence
< OUString
> aSNS( 1 ); // auch mehr als 1 Service moeglich
646 aSNS
.getArray()[0] = A2OU( SN_SPELLCHECKER
);
651 sal_Bool SAL_CALL
MacSpellChecker_writeInfo(
652 void * /*pServiceManager*/, registry::XRegistryKey
* pRegistryKey
)
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
] );
676 void * SAL_CALL
MacSpellChecker_getFactory( const sal_Char
* pImplName
,
677 XMultiServiceFactory
* pServiceManager
, void * )
680 if ( !MacSpellChecker::getImplementationName_Static().compareToAscii( pImplName
) )
682 Reference
< XSingleServiceFactory
> xFactory
=
683 cppu::createOneInstanceFactory(
685 MacSpellChecker::getImplementationName_Static(),
686 MacSpellChecker_CreateInstance
,
687 MacSpellChecker::getSupportedServiceNames_Static());
688 // acquire, because we return an interface pointer instead of a reference
690 pRet
= xFactory
.get();
696 ///////////////////////////////////////////////////////////////////////////