bump product version to 5.0.4.1
[LibreOffice.git] / editeng / source / misc / hangulhanja.cxx
blobb77aeec9ae0becab5f6b4bb9e218c8874ff47377
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <editeng/hangulhanja.hxx>
21 #include <vcl/msgbox.hxx>
22 #include <vcl/button.hxx>
23 #include <unotools/lingucfg.hxx>
24 #include <unotools/linguprops.hxx>
26 #include <set>
27 #include <map>
28 #include <comphelper/processfactory.hxx>
29 #include <com/sun/star/uno/Sequence.hxx>
30 #include <com/sun/star/i18n/BreakIterator.hpp>
31 #include <com/sun/star/i18n/ScriptType.hpp>
32 #include <com/sun/star/i18n/UnicodeScript.hpp>
33 #include <com/sun/star/i18n/TextConversion.hpp>
34 #include <com/sun/star/i18n/XExtendedTextConversion.hpp>
35 #include <com/sun/star/i18n/TextConversionType.hpp>
36 #include <com/sun/star/i18n/TextConversionOption.hpp>
37 #include <com/sun/star/i18n/WordType.hpp>
38 #include <vcl/stdtext.hxx>
39 #include <unotools/charclass.hxx>
41 #include <editeng/edtdlg.hxx>
42 #include <editeng/editrids.hrc>
43 #include <editeng/unolingu.hxx>
45 #define HHC HangulHanjaConversion
48 namespace editeng
52 using namespace ::com::sun::star;
53 using namespace ::com::sun::star::uno;
54 using namespace ::com::sun::star::i18n;
55 using namespace ::com::sun::star::i18n::TextConversionOption;
56 using namespace ::com::sun::star::i18n::TextConversionType;
58 class HangulHanjaConversion_Impl
60 private:
61 typedef ::std::set< OUString, ::std::less< OUString > > StringBag;
62 typedef ::std::map< OUString, OUString, ::std::less< OUString > > StringMap;
64 private:
65 StringBag m_sIgnoreList;
66 StringMap m_aChangeList;
67 static StringMap m_aRecentlyUsedList;
69 // general
70 AbstractHangulHanjaConversionDialog*
71 m_pConversionDialog; // the dialog to display for user interaction
72 VclPtr<vcl::Window> m_pUIParent; // the parent window for any UI we raise
73 Reference< XComponentContext >
74 m_xContext; // the service factory to use
75 Reference< XExtendedTextConversion >
76 m_xConverter; // the text conversion service
77 lang::Locale m_aSourceLocale; // the locale we're working with
79 // additions for Chinese simplified / traditional conversion
80 HHC::ConversionType m_eConvType; // conversion type (Hangul/Hanja, simplified/traditional Chinese,...)
81 LanguageType m_nSourceLang; // just a 'copy' of m_aSourceLocale in order in order to
82 // save the applications from always converting to this
83 // type in their implementations
84 LanguageType m_nTargetLang; // target language of new replacement text
85 const vcl::Font* m_pTargetFont; // target font of new replacement text
86 sal_Int32 m_nConvOptions; // text conversion options (as used by 'getConversions')
87 bool m_bIsInteractive; // specifies if the conversion requires user interaction
88 // (and likeley a specialised dialog) or if it is to run
89 // automatically without any user interaction.
90 // True for Hangul / Hanja conversion
91 // False for Chinese simlified / traditional conversion
93 HangulHanjaConversion* m_pAntiImpl; // our "anti-impl" instance
95 // options
96 bool m_bByCharacter; // are we in "by character" mode currently?
97 HHC::ConversionFormat m_eConversionFormat; // the current format for the conversion
98 HHC::ConversionDirection m_ePrimaryConversionDirection; // the primary conversion direction
99 HHC::ConversionDirection m_eCurrentConversionDirection; // the primary conversion direction
101 //options from Hangul/Hanja Options dialog (also saved to configuration)
102 bool m_bIgnorePostPositionalWord;
103 bool m_bShowRecentlyUsedFirst;
104 bool m_bAutoReplaceUnique;
106 // state
107 OUString m_sCurrentPortion; // the text which we are currently working on
108 LanguageType m_nCurrentPortionLang; // language of m_sCurrentPortion found
109 sal_Int32 m_nCurrentStartIndex; // the start index within m_sCurrentPortion of the current convertible portion
110 sal_Int32 m_nCurrentEndIndex; // the end index (excluding) within m_sCurrentPortion of the current convertible portion
111 sal_Int32 m_nReplacementBaseIndex;// index which ReplaceUnit-calls need to be relative to
112 sal_Int32 m_nCurrentConversionOption;
113 sal_Int16 m_nCurrentConversionType;
114 Sequence< OUString >
115 m_aCurrentSuggestions; // the suggestions for the current unit
116 // (means for the text [m_nCurrentStartIndex, m_nCurrentEndIndex) in m_sCurrentPortion)
117 bool m_bTryBothDirections; // specifies if other conversion directions should be tried when looking for convertible characters
120 public:
121 HangulHanjaConversion_Impl(
122 vcl::Window* _pUIParent,
123 const Reference< XComponentContext >& rxContext,
124 const lang::Locale& _rSourceLocale,
125 const lang::Locale& _rTargetLocale,
126 const vcl::Font* _pTargetFont,
127 sal_Int32 _nConvOptions,
128 bool _bIsInteractive,
129 HangulHanjaConversion* _pAntiImpl );
131 public:
132 void DoDocumentConversion( );
134 inline bool IsByCharacter( ) const { return m_bByCharacter; }
136 inline bool IsValid() const { return m_xConverter.is(); }
138 inline LanguageType GetSourceLang() const { return m_nSourceLang; }
139 inline LanguageType GetTargetLang() const { return m_nTargetLang; }
140 inline const vcl::Font * GetTargetFont() const { return m_pTargetFont; }
141 inline sal_Int32 GetConvOptions() const { return m_nConvOptions; }
142 inline bool IsInteractive() const { return m_bIsInteractive; }
144 protected:
145 void createDialog();
147 /** continue with the conversion, return <TRUE/> if and only if the complete conversion is done
148 @param _bRepeatCurrentUnit
149 if <TRUE/>, an implNextConvertible will be called initially to advance to the next convertible.
150 if <FALSE/>, the method will initially work with the current convertible unit
152 bool ContinueConversion( bool _bRepeatCurrentUnit );
154 private:
155 DECL_LINK( OnOptionsChanged, void* );
156 DECL_LINK( OnIgnore, void* );
157 DECL_LINK( OnIgnoreAll, void* );
158 DECL_LINK( OnChange, void* );
159 DECL_LINK( OnChangeAll, void* );
160 DECL_LINK( OnByCharClicked, CheckBox* );
161 DECL_LINK( OnConversionTypeChanged, void* );
162 DECL_LINK( OnFind, void* );
164 /** proceed, after the current convertible has been handled
166 <p><b>Attention:</b>
167 When returning from this method, the dialog may have been deleted!</p>
169 @param _bRepeatCurrentUnit
170 will be passed to the <member>ContinueConversion</member> call
172 void implProceed( bool _bRepeatCurrentUnit );
174 // change the current convertible, and do _not_ proceed
175 void implChange( const OUString& _rChangeInto );
177 /** find the next convertible piece of text, with possibly advancing to the next portion
179 @see HangulHanjaConversion::GetNextPortion
181 bool implNextConvertible( bool _bRepeatUnit );
183 /** find the next convertible unit within the current portion
184 @param _bRepeatUnit
185 if <TRUE/>, the search will start at the beginning of the current unit,
186 if <FALSE/>, it will start at the end of the current unit
188 bool implNextConvertibleUnit( const sal_Int32 _nStartAt );
190 /** retrieves the next portion, with setting the index members properly
191 @return
192 <TRUE/> if and only if there is a next portion
194 bool implRetrieveNextPortion( );
196 /** determine the ConversionDirection for m_sCurrentPortion
197 @return
198 <FALSE/> if and only if something went wrong
200 bool implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection );
202 /** member m_aCurrentSuggestions and m_nCurrentEndIndex are updated according to the other settings and current dictionaries
204 if _bAllowSearchNextConvertibleText is true _nStartAt is used as starting point to search the next
205 convertible text portion. This may result in changing of the member m_nCurrentStartIndex additionally.
207 @return
208 <TRUE/> if Suggestions were found
210 bool implUpdateSuggestions( const bool _bAllowSearchNextConvertibleText=false, const sal_Int32 _nStartAt=-1 );
212 /** reads the options from Hangul/Hanja Options dialog that are saved to configuration
214 void implReadOptionsFromConfiguration();
216 /** get the string currently considered to be replaced or ignored
218 OUString GetCurrentUnit() const;
220 /** read options from configuration, update suggestion list and dialog content
222 void implUpdateData();
224 /** get the conversion direction dependent from m_eConvType and m_eCurrentConversionDirection
225 in case of switching the direction is allowed this can be triggered with parameter bSwitchDirection
227 sal_Int16 implGetConversionType( bool bSwitchDirection=false ) const;
230 HangulHanjaConversion_Impl::StringMap HangulHanjaConversion_Impl::m_aRecentlyUsedList = HangulHanjaConversion_Impl::StringMap();
232 HangulHanjaConversion_Impl::HangulHanjaConversion_Impl( vcl::Window* _pUIParent,
233 const Reference< XComponentContext >& rxContext,
234 const lang::Locale& _rSourceLocale,
235 const lang::Locale& _rTargetLocale,
236 const vcl::Font* _pTargetFont,
237 sal_Int32 _nOptions,
238 bool _bIsInteractive,
239 HangulHanjaConversion* _pAntiImpl )
240 : m_pConversionDialog( NULL )
241 , m_pUIParent( _pUIParent )
242 , m_xContext( rxContext )
243 , m_aSourceLocale( _rSourceLocale )
244 , m_nSourceLang( LanguageTag::convertToLanguageType( _rSourceLocale ) )
245 , m_nTargetLang( LanguageTag::convertToLanguageType( _rTargetLocale ) )
246 , m_pTargetFont( _pTargetFont )
247 , m_nConvOptions(_nOptions)
248 , m_bIsInteractive( _bIsInteractive )
249 , m_pAntiImpl( _pAntiImpl )
250 , m_bByCharacter((_nOptions & CHARACTER_BY_CHARACTER) != 0)
251 , m_eConversionFormat( HHC::eSimpleConversion)
252 , m_ePrimaryConversionDirection( HHC::eHangulToHanja) // used for eConvHangulHanja
253 , m_eCurrentConversionDirection( HHC::eHangulToHanja) // used for eConvHangulHanja
254 , m_nCurrentPortionLang( LANGUAGE_NONE )
255 , m_nCurrentStartIndex( 0 )
256 , m_nCurrentEndIndex( 0 )
257 , m_nReplacementBaseIndex( 0 )
258 , m_nCurrentConversionOption( TextConversionOption::NONE )
259 , m_nCurrentConversionType( -1 ) // not yet known
260 , m_bTryBothDirections( true )
262 implReadOptionsFromConfiguration();
264 DBG_ASSERT( m_xContext.is(), "HangulHanjaConversion_Impl::HangulHanjaConversion_Impl: no ORB!" );
266 // determine conversion type
267 if (m_nSourceLang == LANGUAGE_KOREAN && m_nTargetLang == LANGUAGE_KOREAN)
268 m_eConvType = HHC::eConvHangulHanja;
269 else if ( (m_nSourceLang == LANGUAGE_CHINESE_TRADITIONAL && m_nTargetLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
270 (m_nSourceLang == LANGUAGE_CHINESE_SIMPLIFIED && m_nTargetLang == LANGUAGE_CHINESE_TRADITIONAL) )
271 m_eConvType = HHC::eConvSimplifiedTraditional;
272 else
274 m_eConvType = HHC::eConvHangulHanja;
275 OSL_FAIL( "failed to determine conversion type from languages" );
278 m_xConverter = TextConversion::create( m_xContext );
281 void HangulHanjaConversion_Impl::createDialog()
283 DBG_ASSERT( m_bIsInteractive, "createDialog when the conversion should not be interactive?" );
284 if ( m_bIsInteractive && !m_pConversionDialog )
286 EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
287 if(pFact)
289 m_pConversionDialog = pFact->CreateHangulHanjaConversionDialog(m_pUIParent, m_ePrimaryConversionDirection );
290 DBG_ASSERT(m_pConversionDialog, "Dialog creation failed!");
292 m_pConversionDialog->EnableRubySupport( m_pAntiImpl->HasRubySupport() );
294 m_pConversionDialog->SetByCharacter( m_bByCharacter );
295 m_pConversionDialog->SetConversionFormat( m_eConversionFormat );
296 m_pConversionDialog->SetConversionDirectionState( m_bTryBothDirections, m_ePrimaryConversionDirection );
298 // the handlers
299 m_pConversionDialog->SetOptionsChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnOptionsChanged ) );
300 m_pConversionDialog->SetIgnoreHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnore ) );
301 m_pConversionDialog->SetIgnoreAllHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnoreAll ) );
302 m_pConversionDialog->SetChangeHdl( LINK( this, HangulHanjaConversion_Impl, OnChange ) );
303 m_pConversionDialog->SetChangeAllHdl( LINK( this, HangulHanjaConversion_Impl, OnChangeAll ) );
304 m_pConversionDialog->SetClickByCharacterHdl( LINK( this, HangulHanjaConversion_Impl, OnByCharClicked ) );
305 m_pConversionDialog->SetConversionFormatChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnConversionTypeChanged ) );
306 m_pConversionDialog->SetFindHdl( LINK( this, HangulHanjaConversion_Impl, OnFind ) );
311 sal_Int16 HangulHanjaConversion_Impl::implGetConversionType( bool bSwitchDirection ) const
313 sal_Int16 nConversionType = -1;
314 if (m_eConvType == HHC::eConvHangulHanja)
315 nConversionType = ( HHC::eHangulToHanja == m_eCurrentConversionDirection && !bSwitchDirection ) ? TO_HANJA : TO_HANGUL;
316 else if (m_eConvType == HHC::eConvSimplifiedTraditional)
317 nConversionType = LANGUAGE_CHINESE_SIMPLIFIED == m_nTargetLang ? TO_SCHINESE : TO_TCHINESE;
318 DBG_ASSERT( nConversionType != -1, "unexpected conversion type" );
319 return nConversionType;
322 bool HangulHanjaConversion_Impl::implUpdateSuggestions( bool _bAllowSearchNextConvertibleText, const sal_Int32 _nStartAt )
324 // parameters for the converter
325 sal_Int32 nStartSearch = m_nCurrentStartIndex;
326 if( _bAllowSearchNextConvertibleText )
327 nStartSearch = _nStartAt;
329 sal_Int32 nLength = m_sCurrentPortion.getLength() - nStartSearch;
330 m_nCurrentConversionType = implGetConversionType();
331 m_nCurrentConversionOption = IsByCharacter() ? CHARACTER_BY_CHARACTER : css::i18n::TextConversionOption::NONE;
332 if( m_bIgnorePostPositionalWord )
333 m_nCurrentConversionOption = m_nCurrentConversionOption | IGNORE_POST_POSITIONAL_WORD;
335 // no need to check both directions for chinese conversion (saves time)
336 if (m_eConvType == HHC::eConvSimplifiedTraditional)
337 m_bTryBothDirections = false;
339 bool bFoundAny = true;
342 TextConversionResult aResult = m_xConverter->getConversions(
343 m_sCurrentPortion,
344 nStartSearch,
345 nLength,
346 m_aSourceLocale,
347 m_nCurrentConversionType,
348 m_nCurrentConversionOption
350 const bool bFoundPrimary = aResult.Boundary.startPos < aResult.Boundary.endPos;
351 bFoundAny = bFoundPrimary;
353 if ( m_bTryBothDirections )
354 { // see if we find another convertible when assuming the other direction
355 TextConversionResult aSecondResult = m_xConverter->getConversions(
356 m_sCurrentPortion,
357 nStartSearch,
358 nLength,
359 m_aSourceLocale,
360 implGetConversionType( true ), // switched!
361 m_nCurrentConversionOption
363 if ( aSecondResult.Boundary.startPos < aSecondResult.Boundary.endPos )
364 { // we indeed found such a convertible
366 // in case the first attempt (with the original conversion direction)
367 // didn't find anything
368 if ( !bFoundPrimary
369 // or if the second location is _before_ the first one
370 || ( aSecondResult.Boundary.startPos < aResult.Boundary.startPos )
373 // then use the second finding
374 aResult = aSecondResult;
376 // our current conversion direction changed now
377 m_eCurrentConversionDirection = ( HHC::eHangulToHanja == m_eCurrentConversionDirection )
378 ? HHC::eHanjaToHangul : HHC::eHangulToHanja;
379 bFoundAny = true;
384 if( _bAllowSearchNextConvertibleText )
386 //this might change the current position
387 m_aCurrentSuggestions = aResult.Candidates;
388 m_nCurrentStartIndex = aResult.Boundary.startPos;
389 m_nCurrentEndIndex = aResult.Boundary.endPos;
391 else
393 //the change of starting position is not allowed
394 if( m_nCurrentStartIndex == aResult.Boundary.startPos
395 && aResult.Boundary.endPos != aResult.Boundary.startPos )
397 m_aCurrentSuggestions = aResult.Candidates;
398 m_nCurrentEndIndex = aResult.Boundary.endPos;
400 else
402 m_aCurrentSuggestions.realloc( 0 );
403 if( m_sCurrentPortion.getLength() >= m_nCurrentStartIndex+1 )
404 m_nCurrentEndIndex = m_nCurrentStartIndex+1;
408 //put recently used string to front:
409 if( m_bShowRecentlyUsedFirst && m_aCurrentSuggestions.getLength()>1 )
411 OUString sCurrentUnit( GetCurrentUnit() );
412 StringMap::const_iterator aRecentlyUsed = m_aRecentlyUsedList.find( sCurrentUnit );
413 bool bUsedBefore = aRecentlyUsed != m_aRecentlyUsedList.end();
414 if( bUsedBefore && m_aCurrentSuggestions[0] != aRecentlyUsed->second )
416 sal_Int32 nCount = m_aCurrentSuggestions.getLength();
417 Sequence< OUString > aTmp(nCount);
418 aTmp[0]=aRecentlyUsed->second;
419 sal_Int32 nDiff = 1;
420 for( sal_Int32 n=1; n<nCount; n++)//we had 0 already
422 if( nDiff && m_aCurrentSuggestions[n-nDiff]==aRecentlyUsed->second )
423 nDiff=0;
424 aTmp[n]=m_aCurrentSuggestions[n-nDiff];
426 m_aCurrentSuggestions = aTmp;
430 catch( const Exception& )
432 OSL_FAIL( "HangulHanjaConversion_Impl::implNextConvertibleUnit: caught an exception!" );
434 //!!! at least we want to move on in the text in order
435 //!!! to avoid an endless loop...
436 return false;
438 return bFoundAny;
441 bool HangulHanjaConversion_Impl::implNextConvertibleUnit( const sal_Int32 _nStartAt )
443 m_aCurrentSuggestions.realloc( 0 );
445 // ask the TextConversion service for the next convertible piece of text
447 // get current values from dialog
448 if( m_eConvType == HHC::eConvHangulHanja && m_pConversionDialog )
450 m_bTryBothDirections = m_pConversionDialog->GetUseBothDirections();
451 HHC::ConversionDirection eDialogDirection = HHC::eHangulToHanja;
452 eDialogDirection = m_pConversionDialog->GetDirection( eDialogDirection );
454 if( !m_bTryBothDirections && eDialogDirection != m_eCurrentConversionDirection )
456 m_eCurrentConversionDirection = eDialogDirection;
459 // save currently used value for possible later use
460 m_pAntiImpl->m_bTryBothDirectionsSave = m_bTryBothDirections;
461 m_pAntiImpl->m_ePrimaryConversionDirectionSave = m_eCurrentConversionDirection;
464 bool bFoundAny = implUpdateSuggestions( true, _nStartAt );
466 return bFoundAny &&
467 (m_nCurrentStartIndex < m_sCurrentPortion.getLength());
470 bool HangulHanjaConversion_Impl::implRetrieveNextPortion( )
472 const bool bAllowImplicitChanges = m_eConvType == HHC::eConvSimplifiedTraditional;
474 m_sCurrentPortion.clear();
475 m_nCurrentPortionLang = LANGUAGE_NONE;
476 m_pAntiImpl->GetNextPortion( m_sCurrentPortion, m_nCurrentPortionLang, bAllowImplicitChanges );
477 m_nReplacementBaseIndex = 0;
478 m_nCurrentStartIndex = m_nCurrentEndIndex = 0;
480 bool bRet = !m_sCurrentPortion.isEmpty();
482 if (m_eConvType == HHC::eConvHangulHanja && m_bTryBothDirections)
483 implGetConversionDirectionForCurrentPortion( m_eCurrentConversionDirection );
485 return bRet;
488 bool HangulHanjaConversion_Impl::implNextConvertible( bool _bRepeatUnit )
490 if ( _bRepeatUnit || ( m_nCurrentEndIndex < m_sCurrentPortion.getLength() ) )
492 if ( implNextConvertibleUnit(
493 _bRepeatUnit
494 ? m_nCurrentStartIndex
495 : m_nCurrentEndIndex
497 return true;
500 // no convertible text in the current portion anymore
501 // -> advance to the next portion
504 // next portion
505 if ( implRetrieveNextPortion( ) )
506 { // there is a next portion
507 // -> find the next convertible unit in the current portion
508 if ( implNextConvertibleUnit( 0 ) )
509 return true;
512 while ( !m_sCurrentPortion.isEmpty() );
514 // no more portions
515 return false;
518 OUString HangulHanjaConversion_Impl::GetCurrentUnit() const
520 DBG_ASSERT( m_nCurrentStartIndex < m_sCurrentPortion.getLength(),
521 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
522 DBG_ASSERT( m_nCurrentEndIndex <= m_sCurrentPortion.getLength(),
523 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
524 DBG_ASSERT( m_nCurrentStartIndex <= m_nCurrentEndIndex,
525 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid interval!" );
527 OUString sCurrentUnit = m_sCurrentPortion.copy( m_nCurrentStartIndex, m_nCurrentEndIndex - m_nCurrentStartIndex );
528 return sCurrentUnit;
531 bool HangulHanjaConversion_Impl::ContinueConversion( bool _bRepeatCurrentUnit )
533 while ( implNextConvertible( _bRepeatCurrentUnit ) )
535 OUString sCurrentUnit( GetCurrentUnit() );
537 // do we need to ignore it?
538 const bool bAlwaysIgnoreThis = m_sIgnoreList.end() != m_sIgnoreList.find( sCurrentUnit );
540 // do we need to change it?
541 StringMap::const_iterator aChangeListPos = m_aChangeList.find( sCurrentUnit );
542 const bool bAlwaysChangeThis = m_aChangeList.end() != aChangeListPos;
544 // do we automatically change this?
545 const bool bAutoChange = m_bAutoReplaceUnique && m_aCurrentSuggestions.getLength() == 1;
547 if (!m_bIsInteractive)
549 // silent conversion (e.g. for simplified/traditional Chinese)...
550 if(m_aCurrentSuggestions.getLength()>0)
551 implChange( m_aCurrentSuggestions.getConstArray()[0] );
553 else if (bAutoChange)
555 implChange( m_aCurrentSuggestions.getConstArray()[0] );
557 else if ( bAlwaysChangeThis )
559 implChange( aChangeListPos->second );
561 else if ( !bAlwaysIgnoreThis )
563 // here we need to ask the user for what to do with the text
564 // for this, allow derivees to highlight the current text unit in a possible document view
565 m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
567 DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
568 if( m_pConversionDialog )
569 m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
571 // do not look for the next convertible: We have to wait for the user to interactivly
572 // decide what happens with the current convertible
573 return false;
577 return true;
580 bool HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection )
582 // - For eConvHangulHanja the direction is determined by
583 // the first encountered Korean character.
584 // - For eConvSimplifiedTraditional the conversion direction
585 // is already specified by the source language.
587 bool bSuccess = true;
589 if (m_eConvType == HHC::eConvHangulHanja)
591 bSuccess = false;
594 // get the break iterator service
595 Reference< XBreakIterator > xBreakIter = i18n::BreakIterator::create( m_xContext );
596 sal_Int32 nNextAsianScript = xBreakIter->beginOfScript( m_sCurrentPortion, m_nCurrentStartIndex, com::sun::star::i18n::ScriptType::ASIAN );
597 if ( -1 == nNextAsianScript )
598 nNextAsianScript = xBreakIter->nextScript( m_sCurrentPortion, m_nCurrentStartIndex, com::sun::star::i18n::ScriptType::ASIAN );
599 if ( ( nNextAsianScript >= m_nCurrentStartIndex ) && ( nNextAsianScript < m_sCurrentPortion.getLength() ) )
600 { // found asian text
602 // determine if it's Hangul
603 CharClass aCharClassificaton( m_xContext, LanguageTag( m_aSourceLocale) );
604 sal_Int16 nScript = aCharClassificaton.getScript( m_sCurrentPortion, sal::static_int_cast< sal_uInt16 >(nNextAsianScript) );
605 if ( ( UnicodeScript_kHangulJamo == nScript )
606 || ( UnicodeScript_kHangulCompatibilityJamo == nScript )
607 || ( UnicodeScript_kHangulSyllable == nScript )
610 rDirection = HHC::eHangulToHanja;
612 else
614 rDirection = HHC::eHanjaToHangul;
617 bSuccess = true;
620 catch( const Exception& )
622 OSL_FAIL( "HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion: caught an exception!" );
626 return bSuccess;
629 void HangulHanjaConversion_Impl::DoDocumentConversion( )
631 // clear the change-all list - it's to be re-initialized for every single document
633 StringMap aEmpty;
634 m_aChangeList.swap( aEmpty );
637 // first of all, we need to guess the direction of our conversion - it is determined by the first
638 // hangul or hanja character in the first text
639 if ( !implRetrieveNextPortion() )
641 DBG_WARNING( "HangulHanjaConversion_Impl::DoDocumentConversion: why did you call me if you do have nothing to convert?" );
642 // nothing to do
643 return;
645 if( m_eConvType == HHC::eConvHangulHanja )
647 //init conversion direction from saved value
648 HHC::ConversionDirection eDirection = HHC::eHangulToHanja;
649 if(!implGetConversionDirectionForCurrentPortion( eDirection ))
650 // something went wrong, has already been asserted
651 return;
653 if (HangulHanjaConversion::IsUseSavedConversionDirectionState())
655 m_ePrimaryConversionDirection = m_pAntiImpl->m_ePrimaryConversionDirectionSave;
656 m_bTryBothDirections = m_pAntiImpl->m_bTryBothDirectionsSave;
657 if( m_bTryBothDirections )
658 m_eCurrentConversionDirection = eDirection;
659 else
660 m_eCurrentConversionDirection = m_ePrimaryConversionDirection;
662 else
664 m_ePrimaryConversionDirection = eDirection;
665 m_eCurrentConversionDirection = eDirection;
669 if (m_bIsInteractive && m_eConvType == HHC::eConvHangulHanja)
671 //always open dialog if at least having a hangul or hanja text portion
672 createDialog();
673 if(HangulHanjaConversion::IsUseSavedConversionDirectionState())
674 ContinueConversion( false );
675 else
676 implUpdateData();
677 m_pConversionDialog->Execute();
678 DELETEZ( m_pConversionDialog );
680 else
682 #ifdef DBG_UTIL
683 const bool bCompletelyDone =
684 #endif
685 ContinueConversion( false );
686 DBG_ASSERT( bCompletelyDone, "HangulHanjaConversion_Impl::DoDocumentConversion: ContinueConversion should have returned true here!" );
690 void HangulHanjaConversion_Impl::implProceed( bool _bRepeatCurrentUnit )
692 if ( ContinueConversion( _bRepeatCurrentUnit ) )
693 { // we're done with the whole document
694 DBG_ASSERT( !m_bIsInteractive || m_pConversionDialog, "HangulHanjaConversion_Impl::implProceed: we should not reach this here without dialog!" );
695 if ( m_pConversionDialog )
696 m_pConversionDialog->EndDialog( RET_OK );
700 void HangulHanjaConversion_Impl::implChange( const OUString& _rChangeInto )
702 if( _rChangeInto.isEmpty() )
703 return;
705 // translate the conversion format into a replacement action
706 // this translation depends on whether we have a Hangul original, or a Hanja original
708 HHC::ReplacementAction eAction( HHC::eExchange );
710 if (m_eConvType == HHC::eConvHangulHanja)
712 // is the original we're about to change in Hangul?
713 const bool bOriginalIsHangul = HHC::eHangulToHanja == m_eCurrentConversionDirection;
715 switch ( m_eConversionFormat )
717 case HHC::eSimpleConversion: eAction = HHC::eExchange; break;
718 case HHC::eHangulBracketed: eAction = bOriginalIsHangul ? HHC::eOriginalBracketed : HHC::eReplacementBracketed; break;
719 case HHC::eHanjaBracketed: eAction = bOriginalIsHangul ? HHC::eReplacementBracketed : HHC::eOriginalBracketed; break;
720 case HHC::eRubyHanjaAbove: eAction = bOriginalIsHangul ? HHC::eReplacementAbove : HHC::eOriginalAbove; break;
721 case HHC::eRubyHanjaBelow: eAction = bOriginalIsHangul ? HHC::eReplacementBelow : HHC::eOriginalBelow; break;
722 case HHC::eRubyHangulAbove: eAction = bOriginalIsHangul ? HHC::eOriginalAbove : HHC::eReplacementAbove; break;
723 case HHC::eRubyHangulBelow: eAction = bOriginalIsHangul ? HHC::eOriginalBelow : HHC::eReplacementBelow; break;
724 default:
725 OSL_FAIL( "HangulHanjaConversion_Impl::implChange: invalid/unexpected conversion format!" );
729 // the proper indices (the wrapper implementation needs indices relative to the
730 // previous replacement)
731 DBG_ASSERT( ( m_nReplacementBaseIndex <= m_nCurrentStartIndex ) && ( m_nReplacementBaseIndex <= m_nCurrentEndIndex ),
732 "HangulHanjaConversion_Impl::implChange: invalid replacement base!" );
734 sal_Int32 nStartIndex = m_nCurrentStartIndex - m_nReplacementBaseIndex;
735 sal_Int32 nEndIndex = m_nCurrentEndIndex - m_nReplacementBaseIndex;
737 //remind this decision
738 m_aRecentlyUsedList[ GetCurrentUnit() ] = _rChangeInto;
740 LanguageType *pNewUnitLang = 0;
741 LanguageType nNewUnitLang = LANGUAGE_NONE;
742 if (m_eConvType == HHC::eConvSimplifiedTraditional)
744 // check if language needs to be changed
745 if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL &&
746 !HangulHanjaConversion::IsTraditional( m_nCurrentPortionLang ))
747 nNewUnitLang = LANGUAGE_CHINESE_TRADITIONAL;
748 else if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED &&
749 !HangulHanjaConversion::IsSimplified( m_nCurrentPortionLang ))
750 nNewUnitLang = LANGUAGE_CHINESE_SIMPLIFIED;
751 if (nNewUnitLang != LANGUAGE_NONE)
752 pNewUnitLang = &nNewUnitLang;
755 // according to FT we should not (yet) bother about Hangul/Hanja conversion here
757 // aOffsets is needed in ReplaceUnit below in order to find out
758 // exactly which characters are really changed in order to keep as much
759 // from attributation for the text as possible.
760 Sequence< sal_Int32 > aOffsets;
761 Reference< XExtendedTextConversion > xExtConverter( m_xConverter, UNO_QUERY );
762 if (m_eConvType == HHC::eConvSimplifiedTraditional && xExtConverter.is())
766 xExtConverter->getConversionWithOffset(
767 m_sCurrentPortion,
768 m_nCurrentStartIndex,
769 m_nCurrentEndIndex - m_nCurrentStartIndex,
770 m_aSourceLocale,
771 m_nCurrentConversionType,
772 m_nCurrentConversionOption,
773 aOffsets
776 catch( const Exception& )
778 OSL_FAIL( "HangulHanjaConversion_Impl::implChange: caught unexpected exception!" );
779 aOffsets.realloc(0);
783 // do the replacement
784 m_pAntiImpl->ReplaceUnit( nStartIndex, nEndIndex, m_sCurrentPortion,
785 _rChangeInto, aOffsets, eAction, pNewUnitLang );
788 // adjust the replacement base
789 m_nReplacementBaseIndex = m_nCurrentEndIndex;
792 void HangulHanjaConversion_Impl::implReadOptionsFromConfiguration()
794 SvtLinguConfig aLngCfg;
795 aLngCfg.GetProperty( UPH_IS_IGNORE_POST_POSITIONAL_WORD ) >>= m_bIgnorePostPositionalWord;
796 aLngCfg.GetProperty( UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST ) >>= m_bShowRecentlyUsedFirst;
797 aLngCfg.GetProperty( UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES ) >>= m_bAutoReplaceUnique;
800 void HangulHanjaConversion_Impl::implUpdateData()
802 implReadOptionsFromConfiguration();
803 implUpdateSuggestions();
805 if(m_pConversionDialog)
807 OUString sCurrentUnit( GetCurrentUnit() );
809 m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
810 m_pConversionDialog->FocusSuggestion();
813 m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
816 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnOptionsChanged)
818 //options and dictionaries might have been changed
819 //-> update our internal settings and the dialog
820 implUpdateData();
822 return 0L;
825 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnIgnore)
827 // simply ignore, and proceed
828 implProceed( false );
829 return 0L;
832 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnIgnoreAll)
834 DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnIgnoreAll: no dialog! How this?" );
836 if ( m_pConversionDialog )
838 OUString sCurrentUnit = m_pConversionDialog->GetCurrentString();
839 DBG_ASSERT( m_sIgnoreList.end() == m_sIgnoreList.find( sCurrentUnit ),
840 "HangulHanjaConversion_Impl, OnIgnoreAll: shouldn't this have been ignored before" );
842 // put into the "ignore all" list
843 m_sIgnoreList.insert( sCurrentUnit );
845 // and proceed
846 implProceed( false );
849 return 0L;
852 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChange)
854 // change
855 DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
856 if( m_pConversionDialog )
857 implChange( m_pConversionDialog->GetCurrentSuggestion( ) );
858 // and proceed
859 implProceed( false );
861 return 0L;
864 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChangeAll)
866 DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnChangeAll: no dialog! How this?" );
867 if ( m_pConversionDialog )
869 OUString sCurrentUnit( m_pConversionDialog->GetCurrentString() );
870 OUString sChangeInto( m_pConversionDialog->GetCurrentSuggestion( ) );
872 if( !sChangeInto.isEmpty() )
874 // change the current occurrence
875 implChange( sChangeInto );
877 // put into the "change all" list
878 m_aChangeList.insert( StringMap::value_type( sCurrentUnit, sChangeInto ) );
881 // and proceed
882 implProceed( false );
885 return 0L;
888 IMPL_LINK( HangulHanjaConversion_Impl, OnByCharClicked, CheckBox*, _pBox )
890 m_bByCharacter = _pBox->IsChecked();
892 // continue conversion, without advancing to the next unit, but instead continuing with the current unit
893 implProceed( true );
894 return 0L;
897 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnConversionTypeChanged)
899 DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
900 if( m_pConversionDialog )
901 m_eConversionFormat = m_pConversionDialog->GetConversionFormat( );
902 return 0L;
905 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnFind)
907 DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnFind: where did this come from?" );
908 if ( m_pConversionDialog )
912 OUString sNewOriginal( m_pConversionDialog->GetCurrentSuggestion( ) );
913 Sequence< OUString > aSuggestions;
915 DBG_ASSERT( m_xConverter.is(), "HangulHanjaConversion_Impl::OnFind: no converter!" );
916 TextConversionResult aToHanja = m_xConverter->getConversions(
917 sNewOriginal,
918 0, sNewOriginal.getLength(),
919 m_aSourceLocale,
920 TextConversionType::TO_HANJA,
921 TextConversionOption::NONE
923 TextConversionResult aToHangul = m_xConverter->getConversions(
924 sNewOriginal,
925 0, sNewOriginal.getLength(),
926 m_aSourceLocale,
927 TextConversionType::TO_HANGUL,
928 TextConversionOption::NONE
931 bool bHaveToHanja = ( aToHanja.Boundary.startPos < aToHanja.Boundary.endPos );
932 bool bHaveToHangul = ( aToHangul.Boundary.startPos < aToHangul.Boundary.endPos );
934 TextConversionResult* pResult = NULL;
935 if ( bHaveToHanja && bHaveToHangul )
936 { // it found convertibles in both directions -> use the first
937 if ( aToHangul.Boundary.startPos < aToHanja.Boundary.startPos )
938 pResult = &aToHangul;
939 else
940 pResult = &aToHanja;
942 else if ( bHaveToHanja )
943 { // only found toHanja
944 pResult = &aToHanja;
946 else
947 { // only found toHangul
948 pResult = &aToHangul;
950 if ( pResult )
951 aSuggestions = pResult->Candidates;
953 m_pConversionDialog->SetCurrentString( sNewOriginal, aSuggestions, false );
954 m_pConversionDialog->FocusSuggestion();
956 catch( const Exception& )
958 OSL_FAIL( "HangulHanjaConversion_Impl::OnFind: caught an exception!" );
961 return 0L;
964 bool HangulHanjaConversion::m_bUseSavedValues = false;
965 bool HangulHanjaConversion::m_bTryBothDirectionsSave = false;
966 HHC::ConversionDirection HangulHanjaConversion::m_ePrimaryConversionDirectionSave = HHC::eHangulToHanja;
968 HangulHanjaConversion::HangulHanjaConversion( vcl::Window* _pUIParent,
969 const Reference< XComponentContext >& rxContext,
970 const lang::Locale& _rSourceLocale, const lang::Locale& _rTargetLocale,
971 const vcl::Font* _pTargetFont,
972 sal_Int32 _nOptions, bool _bIsInteractive)
973 :m_pImpl( new HangulHanjaConversion_Impl( _pUIParent, rxContext, _rSourceLocale, _rTargetLocale, _pTargetFont, _nOptions, _bIsInteractive, this ) )
977 HangulHanjaConversion::~HangulHanjaConversion( )
981 void HangulHanjaConversion::SetUseSavedConversionDirectionState( bool bVal )
983 m_bUseSavedValues = bVal;
986 bool HangulHanjaConversion::IsUseSavedConversionDirectionState()
988 return m_bUseSavedValues;
991 LanguageType HangulHanjaConversion::GetSourceLanguage( ) const
993 return m_pImpl->GetSourceLang();
996 LanguageType HangulHanjaConversion::GetTargetLanguage( ) const
998 return m_pImpl->GetTargetLang();
1001 const vcl::Font * HangulHanjaConversion::GetTargetFont( ) const
1003 return m_pImpl->GetTargetFont();
1006 sal_Int32 HangulHanjaConversion::GetConversionOptions( ) const
1008 return m_pImpl->GetConvOptions();
1011 bool HangulHanjaConversion::IsInteractive( ) const
1013 return m_pImpl->IsInteractive();
1016 void HangulHanjaConversion::ConvertDocument()
1018 if ( m_pImpl->IsValid() )
1019 m_pImpl->DoDocumentConversion( );
1024 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */