Bump for 3.6-28
[LibreOffice.git] / editeng / source / misc / hangulhanja.cxx
blob86bf60a38ffdd2c49d4d1e36f06cd09f98688188
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include <editeng/hangulhanja.hxx>
30 #include <vcl/msgbox.hxx>
31 #include <vcl/button.hxx>
32 #include <unotools/lingucfg.hxx>
33 #include <unotools/linguprops.hxx>
35 #include <set>
36 #include <map>
37 #include <com/sun/star/uno/Sequence.hxx>
38 #include <com/sun/star/i18n/XBreakIterator.hpp>
39 #include <com/sun/star/i18n/ScriptType.hpp>
40 #include <com/sun/star/i18n/UnicodeScript.hpp>
41 #include <com/sun/star/i18n/XTextConversion.hpp>
42 #include <com/sun/star/i18n/XExtendedTextConversion.hpp>
43 #include <com/sun/star/i18n/TextConversionType.hpp>
44 #include <com/sun/star/i18n/TextConversionOption.hpp>
45 #include <com/sun/star/i18n/WordType.hpp>
46 #include <vcl/stdtext.hxx>
47 #include <unotools/charclass.hxx>
49 #include <editeng/edtdlg.hxx>
50 #include <editeng/editrids.hrc>
51 #include <editeng/unolingu.hxx>
53 #define HHC HangulHanjaConversion
55 //.............................................................................
56 namespace editeng
58 //.............................................................................
60 using namespace ::com::sun::star::uno;
61 using namespace ::com::sun::star::i18n;
62 using namespace ::com::sun::star::i18n::TextConversionOption;
63 using namespace ::com::sun::star::i18n::TextConversionType;
64 using namespace ::com::sun::star::lang;
66 class HangulHanjaConversion_Impl
68 private:
69 typedef ::std::set< ::rtl::OUString, ::std::less< ::rtl::OUString > > StringBag;
70 typedef ::std::map< ::rtl::OUString, ::rtl::OUString, ::std::less< ::rtl::OUString > > StringMap;
72 private:
73 StringBag m_sIgnoreList;
74 StringMap m_aChangeList;
75 static StringMap m_aRecentlyUsedList;
77 // general
78 AbstractHangulHanjaConversionDialog*
79 m_pConversionDialog; // the dialog to display for user interaction
80 Window* m_pUIParent; // the parent window for any UI we raise
81 Reference< XMultiServiceFactory >
82 m_xORB; // the service factory to use
83 Reference< XTextConversion >
84 m_xConverter; // the text conversion service
85 Locale m_aSourceLocale; // the locale we're working with
87 // additions for Chinese simplified / traditional conversion
88 HHC::ConversionType m_eConvType; // conversion type (Hangul/Hanja, simplified/traditional Chinese,...)
89 LanguageType m_nSourceLang; // just a 'copy' of m_aSourceLocale in order in order to
90 // save the applications from always converting to this
91 // type in their implementations
92 LanguageType m_nTargetLang; // target language of new replacement text
93 const Font* m_pTargetFont; // target font of new replacement text
94 sal_Int32 m_nConvOptions; // text conversion options (as used by 'getConversions')
95 sal_Bool m_bIsInteractive; // specifies if the conversion requires user interaction
96 // (and likeley a specialised dialog) or if it is to run
97 // automatically without any user interaction.
98 // True for Hangul / Hanja conversion
99 // False for Chinese simlified / traditional conversion
101 HangulHanjaConversion* m_pAntiImpl; // our "anti-impl" instance
103 // options
104 sal_Bool m_bByCharacter; // are we in "by character" mode currently?
105 HHC::ConversionFormat m_eConversionFormat; // the current format for the conversion
106 HHC::ConversionDirection m_ePrimaryConversionDirection; // the primary conversion direction
107 HHC::ConversionDirection m_eCurrentConversionDirection; // the primary conversion direction
109 //options from Hangul/Hanja Options dialog (also saved to configuration)
110 bool m_bIgnorePostPositionalWord;
111 bool m_bShowRecentlyUsedFirst;
112 bool m_bAutoReplaceUnique;
114 // state
115 ::rtl::OUString m_sCurrentPortion; // the text which we are currently working on
116 LanguageType m_nCurrentPortionLang; // language of m_sCurrentPortion found
117 sal_Int32 m_nCurrentStartIndex; // the start index within m_sCurrentPortion of the current convertible portion
118 sal_Int32 m_nCurrentEndIndex; // the end index (excluding) within m_sCurrentPortion of the current convertible portion
119 sal_Int32 m_nReplacementBaseIndex;// index which ReplaceUnit-calls need to be relative to
120 sal_Int32 m_nCurrentConversionOption;
121 sal_Int16 m_nCurrentConversionType;
122 Sequence< ::rtl::OUString >
123 m_aCurrentSuggestions; // the suggestions for the current unit
124 // (means for the text [m_nCurrentStartIndex, m_nCurrentEndIndex) in m_sCurrentPortion)
125 sal_Bool m_bTryBothDirections; // specifies if other conversion directions should be tried when looking for convertible characters
128 public:
129 HangulHanjaConversion_Impl(
130 Window* _pUIParent,
131 const Reference< XMultiServiceFactory >& _rxORB,
132 const Locale& _rSourceLocale,
133 const Locale& _rTargetLocale,
134 const Font* _pTargetFont,
135 sal_Int32 _nConvOptions,
136 sal_Bool _bIsInteractive,
137 HangulHanjaConversion* _pAntiImpl );
139 public:
141 static void SetUseSavedConversionDirectionState( sal_Bool bVal );
143 void DoDocumentConversion( );
145 inline sal_Bool IsByCharacter( ) const { return m_bByCharacter; }
147 inline sal_Bool IsValid() const { return m_xConverter.is(); }
149 inline LanguageType GetSourceLang() const { return m_nSourceLang; }
150 inline LanguageType GetTargetLang() const { return m_nTargetLang; }
151 inline const Font * GetTargetFont() const { return m_pTargetFont; }
152 inline sal_Int32 GetConvOptions() const { return m_nConvOptions; }
153 inline sal_Bool IsInteractive() const { return m_bIsInteractive; }
155 protected:
156 void createDialog();
158 /** continue with the conversion, return <TRUE/> if and only if the complete conversion is done
159 @param _bRepeatCurrentUnit
160 if <TRUE/>, an implNextConvertible will be called initially to advance to the next convertible.
161 if <FALSE/>, the method will initially work with the current convertible unit
163 sal_Bool ContinueConversion( bool _bRepeatCurrentUnit );
165 private:
166 DECL_LINK( OnOptionsChanged, void* );
167 DECL_LINK( OnIgnore, void* );
168 DECL_LINK( OnIgnoreAll, void* );
169 DECL_LINK( OnChange, void* );
170 DECL_LINK( OnChangeAll, void* );
171 DECL_LINK( OnByCharClicked, CheckBox* );
172 DECL_LINK( OnConversionTypeChanged, void* );
173 DECL_LINK( OnFind, void* );
175 /** proceed, after the current convertible has been handled
177 <p><b>Attention:</b>
178 When returning from this method, the dialog may have been deleted!</p>
180 @param _bRepeatCurrentUnit
181 will be passed to the <member>ContinueConversion</member> call
183 void implProceed( bool _bRepeatCurrentUnit );
185 // change the current convertible, and do _not_ proceed
186 void implChange( const ::rtl::OUString& _rChangeInto );
188 /** find the next convertible piece of text, with possibly advancing to the next portion
190 @see HangulHanjaConversion::GetNextPortion
192 sal_Bool implNextConvertible( bool _bRepeatUnit );
194 /** find the next convertible unit within the current portion
195 @param _bRepeatUnit
196 if <TRUE/>, the search will start at the beginning of the current unit,
197 if <FALSE/>, it will start at the end of the current unit
199 bool implNextConvertibleUnit( const sal_Int32 _nStartAt );
201 /** retrieves the next portion, with setting the index members properly
202 @return
203 <TRUE/> if and only if there is a next portion
205 bool implRetrieveNextPortion( );
207 /** determine the ConversionDirection for m_sCurrentPortion
208 @return
209 <FALSE/> if and only if something went wrong
211 bool implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection );
213 /** member m_aCurrentSuggestions and m_nCurrentEndIndex are updated according to the other settings and current dictionaries
215 if _bAllowSearchNextConvertibleText is true _nStartAt is used as starting point to search the next
216 convertible text portion. This may result in changing of the member m_nCurrentStartIndex additionally.
218 @return
219 <TRUE/> if Suggestions were found
221 bool implUpdateSuggestions( const bool _bAllowSearchNextConvertibleText=false, const sal_Int32 _nStartAt=-1 );
223 /** reads the options from Hangul/Hanja Options dialog that are saved to configuration
225 void implReadOptionsFromConfiguration();
227 /** get the string currently considered to be replaced or ignored
229 ::rtl::OUString GetCurrentUnit() const;
231 /** read options from configuration, update suggestion list and dialog content
233 void implUpdateData();
235 /** get the conversion direction dependent from m_eConvType and m_eCurrentConversionDirection
236 in case of switching the direction is allowed this can be triggered with parameter bSwitchDirection
238 sal_Int16 implGetConversionType( bool bSwitchDirection=false ) const;
241 HangulHanjaConversion_Impl::StringMap HangulHanjaConversion_Impl::m_aRecentlyUsedList = HangulHanjaConversion_Impl::StringMap();
243 HangulHanjaConversion_Impl::HangulHanjaConversion_Impl( Window* _pUIParent,
244 const Reference< XMultiServiceFactory >& _rxORB,
245 const Locale& _rSourceLocale,
246 const Locale& _rTargetLocale,
247 const Font* _pTargetFont,
248 sal_Int32 _nOptions,
249 sal_Bool _bIsInteractive,
250 HangulHanjaConversion* _pAntiImpl )
251 : m_pConversionDialog( NULL )
252 , m_pUIParent( _pUIParent )
253 , m_xORB( _rxORB )
254 , m_aSourceLocale( _rSourceLocale )
255 , m_nSourceLang( SvxLocaleToLanguage( _rSourceLocale ) )
256 , m_nTargetLang( SvxLocaleToLanguage( _rTargetLocale ) )
257 , m_pTargetFont( _pTargetFont )
258 , m_bIsInteractive( _bIsInteractive )
259 , m_pAntiImpl( _pAntiImpl )
260 , m_nCurrentPortionLang( LANGUAGE_NONE )
261 , m_nCurrentStartIndex( 0 )
262 , m_nCurrentEndIndex( 0 )
263 , m_nReplacementBaseIndex( 0 )
264 , m_nCurrentConversionOption( TextConversionOption::NONE )
265 , m_nCurrentConversionType( -1 ) // not yet known
266 , m_bTryBothDirections( sal_True )
268 implReadOptionsFromConfiguration();
270 DBG_ASSERT( m_xORB.is(), "HangulHanjaConversion_Impl::HangulHanjaConversion_Impl: no ORB!" );
272 // determine conversion type
273 if (m_nSourceLang == LANGUAGE_KOREAN && m_nTargetLang == LANGUAGE_KOREAN)
274 m_eConvType = HHC::eConvHangulHanja;
275 else if ( (m_nSourceLang == LANGUAGE_CHINESE_TRADITIONAL && m_nTargetLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
276 (m_nSourceLang == LANGUAGE_CHINESE_SIMPLIFIED && m_nTargetLang == LANGUAGE_CHINESE_TRADITIONAL) )
277 m_eConvType = HHC::eConvSimplifiedTraditional;
278 else
280 OSL_FAIL( "failed to determine conversion type from languages" );
283 // set remaining conversion parameters to their default values
284 m_nConvOptions = _nOptions;
285 m_bByCharacter = 0 != (_nOptions & CHARACTER_BY_CHARACTER);
286 m_eConversionFormat = HHC::eSimpleConversion;
287 m_ePrimaryConversionDirection = HHC::eHangulToHanja; // used for eConvHangulHanja
288 m_eCurrentConversionDirection = HHC::eHangulToHanja; // used for eConvHangulHanja
290 if ( m_xORB.is() )
292 ::rtl::OUString sTextConversionService( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.TextConversion" ) );
293 m_xConverter = m_xConverter.query( m_xORB->createInstance( sTextConversionService ) );
294 if ( !m_xConverter.is() )
295 ShowServiceNotAvailableError( m_pUIParent, sTextConversionService, sal_True );
300 void HangulHanjaConversion_Impl::createDialog()
302 DBG_ASSERT( m_bIsInteractive, "createDialog when the conversion should not be interactive?" );
303 if ( m_bIsInteractive && !m_pConversionDialog )
305 EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
306 if(pFact)
308 m_pConversionDialog = pFact->CreateHangulHanjaConversionDialog(m_pUIParent, m_ePrimaryConversionDirection );
309 DBG_ASSERT(m_pConversionDialog, "Dialogdiet fail!");
311 m_pConversionDialog->EnableRubySupport( m_pAntiImpl->HasRubySupport() );
313 m_pConversionDialog->SetByCharacter( m_bByCharacter );
314 m_pConversionDialog->SetConversionFormat( m_eConversionFormat );
315 m_pConversionDialog->SetConversionDirectionState( m_bTryBothDirections, m_ePrimaryConversionDirection );
317 // the handlers
318 m_pConversionDialog->SetOptionsChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnOptionsChanged ) );
319 m_pConversionDialog->SetIgnoreHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnore ) );
320 m_pConversionDialog->SetIgnoreAllHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnoreAll ) );
321 m_pConversionDialog->SetChangeHdl( LINK( this, HangulHanjaConversion_Impl, OnChange ) );
322 m_pConversionDialog->SetChangeAllHdl( LINK( this, HangulHanjaConversion_Impl, OnChangeAll ) );
323 m_pConversionDialog->SetClickByCharacterHdl( LINK( this, HangulHanjaConversion_Impl, OnByCharClicked ) );
324 m_pConversionDialog->SetConversionFormatChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnConversionTypeChanged ) );
325 m_pConversionDialog->SetFindHdl( LINK( this, HangulHanjaConversion_Impl, OnFind ) );
330 sal_Int16 HangulHanjaConversion_Impl::implGetConversionType( bool bSwitchDirection ) const
332 sal_Int16 nConversionType = -1;
333 if (m_eConvType == HHC::eConvHangulHanja)
334 nConversionType = HHC::eHangulToHanja == ( m_eCurrentConversionDirection && !bSwitchDirection ) ? TO_HANJA : TO_HANGUL;
335 else if (m_eConvType == HHC::eConvSimplifiedTraditional)
336 nConversionType = LANGUAGE_CHINESE_SIMPLIFIED == m_nTargetLang ? TO_SCHINESE : TO_TCHINESE;
337 DBG_ASSERT( nConversionType != -1, "unexpected conversion type" );
338 return nConversionType;
341 bool HangulHanjaConversion_Impl::implUpdateSuggestions( bool _bAllowSearchNextConvertibleText, const sal_Int32 _nStartAt )
343 // parameters for the converter
344 sal_Int32 nStartSearch = m_nCurrentStartIndex;
345 if( _bAllowSearchNextConvertibleText )
346 nStartSearch = _nStartAt;
348 sal_Int32 nLength = m_sCurrentPortion.getLength() - nStartSearch;
349 m_nCurrentConversionType = implGetConversionType();
350 m_nCurrentConversionOption = IsByCharacter() ? CHARACTER_BY_CHARACTER : NONE;
351 if( m_bIgnorePostPositionalWord )
352 m_nCurrentConversionOption = m_nCurrentConversionOption | IGNORE_POST_POSITIONAL_WORD;
354 // no need to check both directions for chinese conversion (saves time)
355 if (m_eConvType == HHC::eConvSimplifiedTraditional)
356 m_bTryBothDirections = sal_False;
358 sal_Bool bFoundAny = sal_True;
361 TextConversionResult aResult = m_xConverter->getConversions(
362 m_sCurrentPortion,
363 nStartSearch,
364 nLength,
365 m_aSourceLocale,
366 m_nCurrentConversionType,
367 m_nCurrentConversionOption
369 sal_Bool bFoundPrimary = aResult.Boundary.startPos < aResult.Boundary.endPos;
370 bFoundAny = bFoundPrimary;
372 if ( m_bTryBothDirections )
373 { // see if we find another convertible when assuming the other direction
374 TextConversionResult aSecondResult = m_xConverter->getConversions(
375 m_sCurrentPortion,
376 nStartSearch,
377 nLength,
378 m_aSourceLocale,
379 implGetConversionType( true ), // switched!
380 m_nCurrentConversionOption
382 if ( aSecondResult.Boundary.startPos < aSecondResult.Boundary.endPos )
383 { // we indeed found such a convertible
385 // in case the first attempt (with the original conversion direction)
386 // didn't find anything
387 if ( !bFoundPrimary
388 // or if the second location is _before_ the first one
389 || ( aSecondResult.Boundary.startPos < aResult.Boundary.startPos )
392 // then use the second finding
393 aResult = aSecondResult;
395 // our current conversion direction changed now
396 m_eCurrentConversionDirection = ( HHC::eHangulToHanja == m_eCurrentConversionDirection )
397 ? HHC::eHanjaToHangul : HHC::eHangulToHanja;
398 bFoundAny = sal_True;
403 if( _bAllowSearchNextConvertibleText )
405 //this might change the current position
406 m_aCurrentSuggestions = aResult.Candidates;
407 m_nCurrentStartIndex = aResult.Boundary.startPos;
408 m_nCurrentEndIndex = aResult.Boundary.endPos;
410 else
412 //the change of starting position is not allowed
413 if( m_nCurrentStartIndex == aResult.Boundary.startPos
414 && aResult.Boundary.endPos != aResult.Boundary.startPos )
416 m_aCurrentSuggestions = aResult.Candidates;
417 m_nCurrentEndIndex = aResult.Boundary.endPos;
419 else
421 m_aCurrentSuggestions.realloc( 0 );
422 if( m_sCurrentPortion.getLength() >= m_nCurrentStartIndex+1 )
423 m_nCurrentEndIndex = m_nCurrentStartIndex+1;
427 //put recently used string to front:
428 if( m_bShowRecentlyUsedFirst && m_aCurrentSuggestions.getLength()>1 )
430 ::rtl::OUString sCurrentUnit( GetCurrentUnit() );
431 StringMap::const_iterator aRecentlyUsed = m_aRecentlyUsedList.find( sCurrentUnit );
432 bool bUsedBefore = aRecentlyUsed != m_aRecentlyUsedList.end();
433 if( bUsedBefore && m_aCurrentSuggestions[0] != aRecentlyUsed->second )
435 sal_Int32 nCount = m_aCurrentSuggestions.getLength();
436 Sequence< ::rtl::OUString > aTmp(nCount);
437 aTmp[0]=aRecentlyUsed->second;
438 sal_Int32 nDiff = 1;
439 for( sal_Int32 n=1; n<nCount; n++)//we had 0 already
441 if( nDiff && m_aCurrentSuggestions[n-nDiff]==aRecentlyUsed->second )
442 nDiff=0;
443 aTmp[n]=m_aCurrentSuggestions[n-nDiff];
445 m_aCurrentSuggestions = aTmp;
449 catch( const Exception& )
451 OSL_FAIL( "HangulHanjaConversion_Impl::implNextConvertibleUnit: caught an exception!" );
453 //!!! at least we want to move on in the text in order
454 //!!! to avoid an endless loop...
455 return false;
457 return bFoundAny;
460 bool HangulHanjaConversion_Impl::implNextConvertibleUnit( const sal_Int32 _nStartAt )
462 m_aCurrentSuggestions.realloc( 0 );
464 // ask the TextConversion service for the next convertible piece of text
466 // get current values from dialog
467 if( m_eConvType == HHC::eConvHangulHanja && m_pConversionDialog )
469 m_bTryBothDirections = m_pConversionDialog->GetUseBothDirections();
470 HHC::ConversionDirection eDialogDirection = HHC::eHangulToHanja;
471 eDialogDirection = m_pConversionDialog->GetDirection( eDialogDirection );
473 if( !m_bTryBothDirections && eDialogDirection != m_eCurrentConversionDirection )
475 m_eCurrentConversionDirection = eDialogDirection;
478 // save curently used value for possible later use
479 m_pAntiImpl->m_bTryBothDirectionsSave = m_bTryBothDirections;
480 m_pAntiImpl->m_ePrimaryConversionDirectionSave = m_eCurrentConversionDirection;
483 bool bFoundAny = implUpdateSuggestions( true, _nStartAt );
485 return bFoundAny &&
486 (m_nCurrentStartIndex < m_sCurrentPortion.getLength());
489 bool HangulHanjaConversion_Impl::implRetrieveNextPortion( )
491 sal_Bool bAllowImplicitChanges = m_eConvType == HHC::eConvSimplifiedTraditional;
493 m_sCurrentPortion = ::rtl::OUString();
494 m_nCurrentPortionLang = LANGUAGE_NONE;
495 m_pAntiImpl->GetNextPortion( m_sCurrentPortion, m_nCurrentPortionLang, bAllowImplicitChanges );
496 m_nReplacementBaseIndex = 0;
497 m_nCurrentStartIndex = m_nCurrentEndIndex = 0;
499 bool bRet = !m_sCurrentPortion.isEmpty();
501 if (m_eConvType == HHC::eConvHangulHanja && m_bTryBothDirections)
502 implGetConversionDirectionForCurrentPortion( m_eCurrentConversionDirection );
504 return bRet;
507 sal_Bool HangulHanjaConversion_Impl::implNextConvertible( bool _bRepeatUnit )
509 if ( _bRepeatUnit || ( m_nCurrentEndIndex < m_sCurrentPortion.getLength() ) )
511 if ( implNextConvertibleUnit(
512 _bRepeatUnit
513 ? ( IsByCharacter() ? m_nCurrentStartIndex : m_nCurrentStartIndex )
514 : m_nCurrentEndIndex
516 return sal_True;
519 // no convertible text in the current portion anymore
520 // -> advance to the next portion
523 // next portion
524 if ( implRetrieveNextPortion( ) )
525 { // there is a next portion
526 // -> find the next convertible unit in the current portion
527 if ( implNextConvertibleUnit( 0 ) )
528 return sal_True;
531 while ( !m_sCurrentPortion.isEmpty() );
533 // no more portions
534 return sal_False;
537 ::rtl::OUString HangulHanjaConversion_Impl::GetCurrentUnit() const
539 DBG_ASSERT( m_nCurrentStartIndex < m_sCurrentPortion.getLength(),
540 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
541 DBG_ASSERT( m_nCurrentEndIndex <= m_sCurrentPortion.getLength(),
542 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
543 DBG_ASSERT( m_nCurrentStartIndex <= m_nCurrentEndIndex,
544 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid interval!" );
546 ::rtl::OUString sCurrentUnit = m_sCurrentPortion.copy( m_nCurrentStartIndex, m_nCurrentEndIndex - m_nCurrentStartIndex );
547 return sCurrentUnit;
550 sal_Bool HangulHanjaConversion_Impl::ContinueConversion( bool _bRepeatCurrentUnit )
552 sal_Bool bNeedUserInteraction = sal_False; // when we leave here, do we need user interaction?
553 sal_Bool bDocumentDone = sal_False; // did we already check the whole document?
555 while ( !bDocumentDone && !bNeedUserInteraction && implNextConvertible( _bRepeatCurrentUnit ) )
557 ::rtl::OUString sCurrentUnit( GetCurrentUnit() );
559 // do we need to ignore it?
560 sal_Bool bAlwaysIgnoreThis = m_sIgnoreList.end() != m_sIgnoreList.find( sCurrentUnit );
562 // do we need to change it?
563 StringMap::const_iterator aChangeListPos = m_aChangeList.find( sCurrentUnit );
564 sal_Bool bAlwaysChangeThis = m_aChangeList.end() != aChangeListPos;
566 // do we automatically change this?
567 sal_Bool bAutoChange = m_bAutoReplaceUnique && m_aCurrentSuggestions.getLength() == 1;
569 if (!m_bIsInteractive)
571 // silent conversion (e.g. for simplified/traditional Chinese)...
572 if(m_aCurrentSuggestions.getLength()>0)
573 implChange( m_aCurrentSuggestions.getConstArray()[0] );
575 else if (bAutoChange)
577 implChange( m_aCurrentSuggestions.getConstArray()[0] );
579 else if ( bAlwaysChangeThis )
581 implChange( aChangeListPos->second );
583 else if ( !bAlwaysIgnoreThis )
585 // here we need to ask the user for what to do with the text
586 // for this, allow derivees to highlight the current text unit in a possible document view
587 m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
589 DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
590 if( m_pConversionDialog )
591 m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
593 // do not look for the next convertible: We have to wait for the user to interactivly
594 // decide what happens with the current convertible
595 bNeedUserInteraction = sal_True;
599 return bDocumentDone || !bNeedUserInteraction;
602 bool HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection )
604 // - For eConvHangulHanja the direction is determined by
605 // the first encountered Korean character.
606 // - For eConvSimplifiedTraditional the conversion direction
607 // is already specified by the source language.
609 bool bSuccess = true;
611 if (m_eConvType == HHC::eConvHangulHanja)
613 bSuccess = false;
616 // get the break iterator service
617 ::rtl::OUString sBreakIteratorService( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.BreakIterator" ) );
618 Reference< XInterface > xBI( m_xORB->createInstance( ::rtl::OUString( sBreakIteratorService ) ) );
619 Reference< XBreakIterator > xBreakIter( xBI, UNO_QUERY );
620 if ( !xBreakIter.is() )
622 ShowServiceNotAvailableError( m_pUIParent, sBreakIteratorService, sal_True );
624 else
626 sal_Int32 nNextAsianScript = xBreakIter->beginOfScript( m_sCurrentPortion, m_nCurrentStartIndex, com::sun::star::i18n::ScriptType::ASIAN );
627 if ( -1 == nNextAsianScript )
628 nNextAsianScript = xBreakIter->nextScript( m_sCurrentPortion, m_nCurrentStartIndex, com::sun::star::i18n::ScriptType::ASIAN );
629 if ( ( nNextAsianScript >= m_nCurrentStartIndex ) && ( nNextAsianScript < m_sCurrentPortion.getLength() ) )
630 { // found asian text
632 // determine if it's Hangul
633 CharClass aCharClassificaton( m_xORB, m_aSourceLocale );
634 sal_Int16 nScript = aCharClassificaton.getScript( m_sCurrentPortion, sal::static_int_cast< sal_uInt16 >(nNextAsianScript) );
635 if ( ( UnicodeScript_kHangulJamo == nScript )
636 || ( UnicodeScript_kHangulCompatibilityJamo == nScript )
637 || ( UnicodeScript_kHangulSyllable == nScript )
640 rDirection = HHC::eHangulToHanja;
642 else
644 rDirection = HHC::eHanjaToHangul;
647 bSuccess = true;
651 catch( const Exception& )
653 OSL_FAIL( "HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion: caught an exception!" );
657 return bSuccess;
660 void HangulHanjaConversion_Impl::DoDocumentConversion( )
662 // clear the change-all list - it's to be re-initialized for every single document
664 StringMap aEmpty;
665 m_aChangeList.swap( aEmpty );
668 // first of all, we need to guess the direction of our conversion - it is determined by the first
669 // hangul or hanja character in the first text
670 if ( !implRetrieveNextPortion() )
672 DBG_WARNING( "HangulHanjaConversion_Impl::DoDocumentConversion: why did you call me if you do have nothing to convert?" );
673 // nothing to do
674 return;
676 if( m_eConvType == HHC::eConvHangulHanja )
678 //init conversion direction from saved value
679 HHC::ConversionDirection eDirection = HHC::eHangulToHanja;
680 if(!implGetConversionDirectionForCurrentPortion( eDirection ))
681 // something went wrong, has already been asserted
682 return;
684 if (m_pAntiImpl->IsUseSavedConversionDirectionState())
686 m_ePrimaryConversionDirection = m_pAntiImpl->m_ePrimaryConversionDirectionSave;
687 m_bTryBothDirections = m_pAntiImpl->m_bTryBothDirectionsSave;
688 if( m_bTryBothDirections )
689 m_eCurrentConversionDirection = eDirection;
690 else
691 m_eCurrentConversionDirection = m_ePrimaryConversionDirection;
693 else
695 m_ePrimaryConversionDirection = eDirection;
696 m_eCurrentConversionDirection = eDirection;
700 if (m_bIsInteractive && m_eConvType == HHC::eConvHangulHanja)
702 //always open dialog if at least having a hangul or hanja text portion
703 createDialog();
704 if(m_pAntiImpl->IsUseSavedConversionDirectionState())
705 ContinueConversion( sal_False );
706 else
707 implUpdateData();
708 m_pConversionDialog->Execute();
709 DELETEZ( m_pConversionDialog );
711 else
713 #ifdef DBG_UTIL
714 sal_Bool bCompletelyDone =
715 #endif
716 ContinueConversion( sal_False );
717 DBG_ASSERT( bCompletelyDone, "HangulHanjaConversion_Impl::DoDocumentConversion: ContinueConversion should have returned true here!" );
721 void HangulHanjaConversion_Impl::implProceed( bool _bRepeatCurrentUnit )
723 if ( ContinueConversion( _bRepeatCurrentUnit ) )
724 { // we're done with the whole document
725 DBG_ASSERT( !m_bIsInteractive || m_pConversionDialog, "HangulHanjaConversion_Impl::implProceed: we should not reach this here without dialog!" );
726 if ( m_pConversionDialog )
727 m_pConversionDialog->EndDialog( RET_OK );
731 void HangulHanjaConversion_Impl::implChange( const ::rtl::OUString& _rChangeInto )
733 if( _rChangeInto.isEmpty() )
734 return;
736 // translate the conversion format into a replacement action
737 // this translation depends on whether we have a Hangul original, or a Hanja original
739 HHC::ReplacementAction eAction( HHC::eExchange );
741 if (m_eConvType == HHC::eConvHangulHanja)
743 // is the original we're about to change in Hangul?
744 sal_Bool bOriginalIsHangul = HHC::eHangulToHanja == m_eCurrentConversionDirection;
746 switch ( m_eConversionFormat )
748 case HHC::eSimpleConversion: eAction = HHC::eExchange; break;
749 case HHC::eHangulBracketed: eAction = bOriginalIsHangul ? HHC::eOriginalBracketed : HHC::eReplacementBracketed; break;
750 case HHC::eHanjaBracketed: eAction = bOriginalIsHangul ? HHC::eReplacementBracketed : HHC::eOriginalBracketed; break;
751 case HHC::eRubyHanjaAbove: eAction = bOriginalIsHangul ? HHC::eReplacementAbove : HHC::eOriginalAbove; break;
752 case HHC::eRubyHanjaBelow: eAction = bOriginalIsHangul ? HHC::eReplacementBelow : HHC::eOriginalBelow; break;
753 case HHC::eRubyHangulAbove: eAction = bOriginalIsHangul ? HHC::eOriginalAbove : HHC::eReplacementAbove; break;
754 case HHC::eRubyHangulBelow: eAction = bOriginalIsHangul ? HHC::eOriginalBelow : HHC::eReplacementBelow; break;
755 default:
756 OSL_FAIL( "HangulHanjaConversion_Impl::implChange: invalid/unexpected conversion format!" );
760 // the proper indicies (the wrapper implementation needs indicies relative to the
761 // previous replacement)
762 DBG_ASSERT( ( m_nReplacementBaseIndex <= m_nCurrentStartIndex ) && ( m_nReplacementBaseIndex <= m_nCurrentEndIndex ),
763 "HangulHanjaConversion_Impl::implChange: invalid replacement base!" );
765 sal_Int32 nStartIndex = m_nCurrentStartIndex - m_nReplacementBaseIndex;
766 sal_Int32 nEndIndex = m_nCurrentEndIndex - m_nReplacementBaseIndex;
768 //remind this decision
769 m_aRecentlyUsedList[ GetCurrentUnit() ] = _rChangeInto;
771 LanguageType *pNewUnitLang = 0;
772 LanguageType nNewUnitLang = LANGUAGE_NONE;
773 if (m_eConvType == HHC::eConvSimplifiedTraditional)
775 // check if language needs to be changed
776 if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL &&
777 !m_pAntiImpl->IsTraditional( m_nCurrentPortionLang ))
778 nNewUnitLang = LANGUAGE_CHINESE_TRADITIONAL;
779 else if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED &&
780 !m_pAntiImpl->IsSimplified( m_nCurrentPortionLang ))
781 nNewUnitLang = LANGUAGE_CHINESE_SIMPLIFIED;
782 if (nNewUnitLang != LANGUAGE_NONE)
783 pNewUnitLang = &nNewUnitLang;
786 // according to FT we should not (yet) bother about Hangul/Hanja conversion here
788 // aOffsets is needed in ReplaceUnit below in order to to find out
789 // exactly which characters are really changed in order to keep as much
790 // from attributation for the text as possible.
791 Sequence< sal_Int32 > aOffsets;
792 Reference< XExtendedTextConversion > xExtConverter( m_xConverter, UNO_QUERY );
793 if (m_eConvType == HHC::eConvSimplifiedTraditional && xExtConverter.is())
797 ::rtl::OUString aConvText = xExtConverter->getConversionWithOffset(
798 m_sCurrentPortion,
799 m_nCurrentStartIndex,
800 m_nCurrentEndIndex - m_nCurrentStartIndex,
801 m_aSourceLocale,
802 m_nCurrentConversionType,
803 m_nCurrentConversionOption,
804 aOffsets
807 catch( const Exception& )
809 OSL_FAIL( "HangulHanjaConversion_Impl::implChange: caught unexpected exception!" );
810 aOffsets.realloc(0);
814 // do the replacement
815 m_pAntiImpl->ReplaceUnit( nStartIndex, nEndIndex, m_sCurrentPortion,
816 _rChangeInto, aOffsets, eAction, pNewUnitLang );
819 // adjust the replacement base
820 m_nReplacementBaseIndex = m_nCurrentEndIndex;
823 void HangulHanjaConversion_Impl::implReadOptionsFromConfiguration()
825 SvtLinguConfig aLngCfg;
826 aLngCfg.GetProperty( UPH_IS_IGNORE_POST_POSITIONAL_WORD ) >>= m_bIgnorePostPositionalWord;
827 aLngCfg.GetProperty( UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST ) >>= m_bShowRecentlyUsedFirst;
828 aLngCfg.GetProperty( UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES ) >>= m_bAutoReplaceUnique;
831 void HangulHanjaConversion_Impl::implUpdateData()
833 implReadOptionsFromConfiguration();
834 implUpdateSuggestions();
836 if(m_pConversionDialog)
838 ::rtl::OUString sCurrentUnit( GetCurrentUnit() );
840 m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
841 m_pConversionDialog->FocusSuggestion();
844 m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
847 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnOptionsChanged)
849 //options and dictionaries might have been changed
850 //-> update our internal settings and the dialog
851 implUpdateData();
853 return 0L;
856 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnIgnore)
858 // simply ignore, and proceed
859 implProceed( sal_False );
860 return 0L;
863 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnIgnoreAll)
865 DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnIgnoreAll: no dialog! How this?" );
867 if ( m_pConversionDialog )
869 String sCurrentUnit = m_pConversionDialog->GetCurrentString();
870 DBG_ASSERT( m_sIgnoreList.end() == m_sIgnoreList.find( sCurrentUnit ),
871 "HangulHanjaConversion_Impl, OnIgnoreAll: shouldn't this have been ignored before" );
873 // put into the "ignore all" list
874 m_sIgnoreList.insert( sCurrentUnit );
876 // and proceed
877 implProceed( sal_False );
880 return 0L;
883 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChange)
885 // change
886 DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
887 if( m_pConversionDialog )
888 implChange( m_pConversionDialog->GetCurrentSuggestion( ) );
889 // and proceed
890 implProceed( sal_False );
892 return 0L;
895 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChangeAll)
897 DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnChangeAll: no dialog! How this?" );
898 if ( m_pConversionDialog )
900 ::rtl::OUString sCurrentUnit( m_pConversionDialog->GetCurrentString() );
901 ::rtl::OUString sChangeInto( m_pConversionDialog->GetCurrentSuggestion( ) );
903 if( !sChangeInto.isEmpty() )
905 // change the current occurrence
906 implChange( sChangeInto );
908 // put into the "change all" list
909 m_aChangeList.insert( StringMap::value_type( sCurrentUnit, sChangeInto ) );
912 // and proceed
913 implProceed( sal_False );
916 return 0L;
919 IMPL_LINK( HangulHanjaConversion_Impl, OnByCharClicked, CheckBox*, _pBox )
921 m_bByCharacter = _pBox->IsChecked();
923 // continue conversion, without advancing to the next unit, but instead continuing with the current unit
924 implProceed( sal_True );
925 return 0L;
928 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnConversionTypeChanged)
930 DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
931 if( m_pConversionDialog )
932 m_eConversionFormat = m_pConversionDialog->GetConversionFormat( );
933 return 0L;
936 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnFind)
938 DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnFind: where did this come from?" );
939 if ( m_pConversionDialog )
943 ::rtl::OUString sNewOriginal( m_pConversionDialog->GetCurrentSuggestion( ) );
944 Sequence< ::rtl::OUString > aSuggestions;
946 DBG_ASSERT( m_xConverter.is(), "HangulHanjaConversion_Impl::OnFind: no converter!" );
947 TextConversionResult aToHanja = m_xConverter->getConversions(
948 sNewOriginal,
949 0, sNewOriginal.getLength(),
950 m_aSourceLocale,
951 TextConversionType::TO_HANJA,
952 TextConversionOption::NONE
954 TextConversionResult aToHangul = m_xConverter->getConversions(
955 sNewOriginal,
956 0, sNewOriginal.getLength(),
957 m_aSourceLocale,
958 TextConversionType::TO_HANGUL,
959 TextConversionOption::NONE
962 bool bHaveToHanja = ( aToHanja.Boundary.startPos < aToHanja.Boundary.endPos );
963 bool bHaveToHangul = ( aToHangul.Boundary.startPos < aToHangul.Boundary.endPos );
965 TextConversionResult* pResult = NULL;
966 if ( bHaveToHanja && bHaveToHangul )
967 { // it found convertibles in both directions -> use the first
968 if ( aToHangul.Boundary.startPos < aToHanja.Boundary.startPos )
969 pResult = &aToHangul;
970 else
971 pResult = &aToHanja;
973 else if ( bHaveToHanja )
974 { // only found toHanja
975 pResult = &aToHanja;
977 else
978 { // only found toHangul
979 pResult = &aToHangul;
981 if ( pResult )
982 aSuggestions = pResult->Candidates;
984 m_pConversionDialog->SetCurrentString( sNewOriginal, aSuggestions, false );
985 m_pConversionDialog->FocusSuggestion();
987 catch( const Exception& )
989 OSL_FAIL( "HangulHanjaConversion_Impl::OnFind: caught an exception!" );
992 return 0L;
995 sal_Bool HangulHanjaConversion::m_bUseSavedValues = sal_False;
996 sal_Bool HangulHanjaConversion::m_bTryBothDirectionsSave = sal_False;
997 HHC::ConversionDirection HangulHanjaConversion::m_ePrimaryConversionDirectionSave = HHC::eHangulToHanja;
999 HangulHanjaConversion::HangulHanjaConversion( Window* _pUIParent,
1000 const Reference< XMultiServiceFactory >& _rxORB,
1001 const Locale& _rSourceLocale, const Locale& _rTargetLocale,
1002 const Font* _pTargetFont,
1003 sal_Int32 _nOptions, sal_Bool _bIsInteractive)
1004 :m_pImpl( new HangulHanjaConversion_Impl( _pUIParent, _rxORB, _rSourceLocale, _rTargetLocale, _pTargetFont, _nOptions, _bIsInteractive, this ) )
1008 HangulHanjaConversion::~HangulHanjaConversion( )
1012 void HangulHanjaConversion::SetUseSavedConversionDirectionState( sal_Bool bVal )
1014 m_bUseSavedValues = bVal;
1017 sal_Bool HangulHanjaConversion::IsUseSavedConversionDirectionState()
1019 return m_bUseSavedValues;
1022 LanguageType HangulHanjaConversion::GetSourceLanguage( ) const
1024 return m_pImpl->GetSourceLang();
1027 LanguageType HangulHanjaConversion::GetTargetLanguage( ) const
1029 return m_pImpl->GetTargetLang();
1032 const Font * HangulHanjaConversion::GetTargetFont( ) const
1034 return m_pImpl->GetTargetFont();
1037 sal_Int32 HangulHanjaConversion::GetConversionOptions( ) const
1039 return m_pImpl->GetConvOptions();
1042 sal_Bool HangulHanjaConversion::IsInteractive( ) const
1044 return m_pImpl->IsInteractive();
1047 void HangulHanjaConversion::HandleNewUnit( const sal_Int32, const sal_Int32 )
1049 // nothing to do, only derived classes need this.
1052 void HangulHanjaConversion::GetNextPortion( ::rtl::OUString&, LanguageType&, sal_Bool )
1054 OSL_FAIL( "HangulHanjaConversion::GetNextPortion: to be overridden!" );
1057 void HangulHanjaConversion::ReplaceUnit(
1058 const sal_Int32, const sal_Int32,
1059 const ::rtl::OUString&,
1060 const ::rtl::OUString&,
1061 const ::com::sun::star::uno::Sequence< sal_Int32 > &,
1062 ReplacementAction,
1063 LanguageType * )
1065 OSL_FAIL( "HangulHanjaConversion::ReplaceUnit: to be overridden!" );
1068 sal_Bool HangulHanjaConversion::HasRubySupport() const
1070 OSL_FAIL( "HangulHanjaConversion::HasRubySupport: to be overridden!" );
1071 return sal_False;
1074 void HangulHanjaConversion::ConvertDocument()
1076 if ( m_pImpl->IsValid() )
1077 m_pImpl->DoDocumentConversion( );
1080 } // namespace svx
1082 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */