1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 <unotools/lingucfg.hxx>
22 #include <unotools/linguprops.hxx>
26 #include <com/sun/star/uno/Sequence.hxx>
27 #include <com/sun/star/i18n/BreakIterator.hpp>
28 #include <com/sun/star/i18n/ScriptType.hpp>
29 #include <com/sun/star/i18n/UnicodeScript.hpp>
30 #include <com/sun/star/i18n/TextConversion.hpp>
31 #include <com/sun/star/i18n/XExtendedTextConversion.hpp>
32 #include <com/sun/star/i18n/TextConversionType.hpp>
33 #include <com/sun/star/i18n/TextConversionOption.hpp>
34 #include <vcl/weld.hxx>
35 #include <unotools/charclass.hxx>
36 #include <sal/log.hxx>
37 #include <osl/diagnose.h>
38 #include <tools/debug.hxx>
39 #include <comphelper/diagnose_ex.hxx>
41 #include <editeng/edtdlg.hxx>
43 #define HHC HangulHanjaConversion
50 using namespace ::com::sun::star
;
51 using namespace ::com::sun::star::uno
;
52 using namespace ::com::sun::star::i18n
;
53 using namespace ::com::sun::star::i18n::TextConversionOption
;
54 using namespace ::com::sun::star::i18n::TextConversionType
;
56 class HangulHanjaConversion_Impl
59 typedef std::set
<OUString
> StringBag
;
60 typedef std::map
<OUString
, OUString
> StringMap
;
63 StringBag m_sIgnoreList
;
64 StringMap m_aChangeList
;
65 static StringMap m_aRecentlyUsedList
;
68 VclPtr
<AbstractHangulHanjaConversionDialog
>
69 m_pConversionDialog
; // the dialog to display for user interaction
70 weld::Widget
* m_pUIParent
; // the parent window for any UI we raise
71 Reference
< XComponentContext
>
72 m_xContext
; // the service factory to use
73 Reference
< XExtendedTextConversion
>
74 m_xConverter
; // the text conversion service
75 lang::Locale m_aSourceLocale
; // the locale we're working with
77 // additions for Chinese simplified / traditional conversion
78 HHC::ConversionType m_eConvType
; // conversion type (Hangul/Hanja, simplified/traditional Chinese,...)
79 LanguageType m_nSourceLang
; // just a 'copy' of m_aSourceLocale in order to
80 // save the applications from always converting to this
81 // type in their implementations
82 LanguageType m_nTargetLang
; // target language of new replacement text
83 const vcl::Font
* m_pTargetFont
; // target font of new replacement text
84 sal_Int32 m_nConvOptions
; // text conversion options (as used by 'getConversions')
85 bool m_bIsInteractive
; // specifies if the conversion requires user interaction
86 // (and likely a specialised dialog) or if it is to run
87 // automatically without any user interaction.
88 // True for Hangul / Hanja conversion
89 // False for Chinese simplified / traditional conversion
91 HangulHanjaConversion
* m_pAntiImpl
; // our "anti-impl" instance
94 bool m_bByCharacter
; // are we in "by character" mode currently?
95 HHC::ConversionFormat m_eConversionFormat
; // the current format for the conversion
96 HHC::ConversionDirection m_ePrimaryConversionDirection
; // the primary conversion direction
97 HHC::ConversionDirection m_eCurrentConversionDirection
; // the primary conversion direction
99 //options from Hangul/Hanja Options dialog (also saved to configuration)
100 bool m_bIgnorePostPositionalWord
;
101 bool m_bShowRecentlyUsedFirst
;
102 bool m_bAutoReplaceUnique
;
105 OUString m_sCurrentPortion
; // the text which we are currently working on
106 LanguageType m_nCurrentPortionLang
; // language of m_sCurrentPortion found
107 sal_Int32 m_nCurrentStartIndex
; // the start index within m_sCurrentPortion of the current convertible portion
108 sal_Int32 m_nCurrentEndIndex
; // the end index (excluding) within m_sCurrentPortion of the current convertible portion
109 sal_Int32 m_nReplacementBaseIndex
;// index which ReplaceUnit-calls need to be relative to
110 sal_Int32 m_nCurrentConversionOption
;
111 sal_Int16 m_nCurrentConversionType
;
113 m_aCurrentSuggestions
; // the suggestions for the current unit
114 // (means for the text [m_nCurrentStartIndex, m_nCurrentEndIndex) in m_sCurrentPortion)
115 bool m_bTryBothDirections
; // specifies if other conversion directions should be tried when looking for convertible characters
119 HangulHanjaConversion_Impl(
120 weld::Widget
* pUIParent
,
121 const Reference
< XComponentContext
>& rxContext
,
122 const lang::Locale
& _rSourceLocale
,
123 const lang::Locale
& _rTargetLocale
,
124 const vcl::Font
* _pTargetFont
,
125 sal_Int32 _nConvOptions
,
126 bool _bIsInteractive
,
127 HangulHanjaConversion
* _pAntiImpl
);
130 void DoDocumentConversion( );
132 bool IsValid() const { return m_xConverter
.is(); }
134 weld::Widget
* GetUIParent() const { return m_pUIParent
; }
135 LanguageType
GetSourceLang() const { return m_nSourceLang
; }
136 LanguageType
GetTargetLang() const { return m_nTargetLang
; }
137 const vcl::Font
* GetTargetFont() const { return m_pTargetFont
; }
138 sal_Int32
GetConvOptions() const { return m_nConvOptions
; }
139 bool IsInteractive() const { return m_bIsInteractive
; }
144 /** continue with the conversion, return <TRUE/> if and only if the complete conversion is done
145 @param _bRepeatCurrentUnit
146 if <TRUE/>, an implNextConvertible will be called initially to advance to the next convertible.
147 if <FALSE/>, the method will initially work with the current convertible unit
149 bool ContinueConversion( bool _bRepeatCurrentUnit
);
152 DECL_LINK( OnOptionsChanged
, LinkParamNone
*, void );
153 DECL_LINK( OnIgnore
, weld::Button
&, void );
154 DECL_LINK( OnIgnoreAll
, weld::Button
&, void );
155 DECL_LINK( OnChange
, weld::Button
&, void );
156 DECL_LINK( OnChangeAll
, weld::Button
&, void );
157 DECL_LINK( OnByCharClicked
, weld::Toggleable
&, void );
158 DECL_LINK( OnConversionTypeChanged
, weld::Toggleable
&, void );
159 DECL_LINK( OnFind
, weld::Button
&, void );
161 /** proceed, after the current convertible has been handled
164 When returning from this method, the dialog may have been deleted!</p>
166 @param _bRepeatCurrentUnit
167 will be passed to the <member>ContinueConversion</member> call
169 void implProceed( bool _bRepeatCurrentUnit
);
171 // change the current convertible, and do _not_ proceed
172 void implChange( const OUString
& _rChangeInto
);
174 /** find the next convertible piece of text, with possibly advancing to the next portion
176 @see HangulHanjaConversion::GetNextPortion
178 bool implNextConvertible( bool _bRepeatUnit
);
180 /** find the next convertible unit within the current portion
182 if <TRUE/>, the search will start at the beginning of the current unit,
183 if <FALSE/>, it will start at the end of the current unit
185 bool implNextConvertibleUnit( const sal_Int32 _nStartAt
);
187 /** retrieves the next portion, with setting the index members properly
189 <TRUE/> if and only if there is a next portion
191 bool implRetrieveNextPortion( );
193 /** determine the ConversionDirection for m_sCurrentPortion
195 <FALSE/> if and only if something went wrong
197 bool implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection
& rDirection
);
199 /** member m_aCurrentSuggestions and m_nCurrentEndIndex are updated according to the other settings and current dictionaries
201 if _bAllowSearchNextConvertibleText is true _nStartAt is used as starting point to search the next
202 convertible text portion. This may result in changing of the member m_nCurrentStartIndex additionally.
205 <TRUE/> if Suggestions were found
207 bool implUpdateSuggestions( const bool _bAllowSearchNextConvertibleText
=false, const sal_Int32 _nStartAt
=-1 );
209 /** reads the options from Hangul/Hanja Options dialog that are saved to configuration
211 void implReadOptionsFromConfiguration();
213 /** get the string currently considered to be replaced or ignored
215 OUString
GetCurrentUnit() const;
217 /** read options from configuration, update suggestion list and dialog content
219 void implUpdateData();
221 /** get the conversion direction dependent from m_eConvType and m_eCurrentConversionDirection
222 in case of switching the direction is allowed this can be triggered with parameter bSwitchDirection
224 sal_Int16
implGetConversionType( bool bSwitchDirection
=false ) const;
227 HangulHanjaConversion_Impl::StringMap
HangulHanjaConversion_Impl::m_aRecentlyUsedList
= HangulHanjaConversion_Impl::StringMap();
229 HangulHanjaConversion_Impl::HangulHanjaConversion_Impl( weld::Widget
* pUIParent
,
230 const Reference
< XComponentContext
>& rxContext
,
231 const lang::Locale
& _rSourceLocale
,
232 const lang::Locale
& _rTargetLocale
,
233 const vcl::Font
* _pTargetFont
,
235 bool _bIsInteractive
,
236 HangulHanjaConversion
* _pAntiImpl
)
237 : m_pUIParent( pUIParent
)
238 , m_xContext( rxContext
)
239 , m_aSourceLocale( _rSourceLocale
)
240 , m_nSourceLang( LanguageTag::convertToLanguageType( _rSourceLocale
) )
241 , m_nTargetLang( LanguageTag::convertToLanguageType( _rTargetLocale
) )
242 , m_pTargetFont( _pTargetFont
)
243 , m_nConvOptions(_nOptions
)
244 , m_bIsInteractive( _bIsInteractive
)
245 , m_pAntiImpl( _pAntiImpl
)
246 , m_bByCharacter((_nOptions
& CHARACTER_BY_CHARACTER
) != 0)
247 , m_eConversionFormat( HHC::eSimpleConversion
)
248 , m_ePrimaryConversionDirection( HHC::eHangulToHanja
) // used for eConvHangulHanja
249 , m_eCurrentConversionDirection( HHC::eHangulToHanja
) // used for eConvHangulHanja
250 , m_nCurrentPortionLang( LANGUAGE_NONE
)
251 , m_nCurrentStartIndex( 0 )
252 , m_nCurrentEndIndex( 0 )
253 , m_nReplacementBaseIndex( 0 )
254 , m_nCurrentConversionOption( TextConversionOption::NONE
)
255 , m_nCurrentConversionType( -1 ) // not yet known
256 , m_bTryBothDirections( true )
258 implReadOptionsFromConfiguration();
260 DBG_ASSERT( m_xContext
.is(), "HangulHanjaConversion_Impl::HangulHanjaConversion_Impl: no ORB!" );
262 // determine conversion type
263 if (m_nSourceLang
== LANGUAGE_KOREAN
&& m_nTargetLang
== LANGUAGE_KOREAN
)
264 m_eConvType
= HHC::eConvHangulHanja
;
265 else if ( (m_nSourceLang
== LANGUAGE_CHINESE_TRADITIONAL
&& m_nTargetLang
== LANGUAGE_CHINESE_SIMPLIFIED
) ||
266 (m_nSourceLang
== LANGUAGE_CHINESE_SIMPLIFIED
&& m_nTargetLang
== LANGUAGE_CHINESE_TRADITIONAL
) )
267 m_eConvType
= HHC::eConvSimplifiedTraditional
;
270 m_eConvType
= HHC::eConvHangulHanja
;
271 OSL_FAIL( "failed to determine conversion type from languages" );
274 m_xConverter
= TextConversion::create( m_xContext
);
277 void HangulHanjaConversion_Impl::createDialog()
279 DBG_ASSERT( m_bIsInteractive
, "createDialog when the conversion should not be interactive?" );
280 if ( !m_bIsInteractive
|| m_pConversionDialog
)
283 EditAbstractDialogFactory
* pFact
= EditAbstractDialogFactory::Create();
284 m_pConversionDialog
= pFact
->CreateHangulHanjaConversionDialog(m_pUIParent
);
286 m_pConversionDialog
->EnableRubySupport( m_pAntiImpl
->HasRubySupport() );
288 m_pConversionDialog
->SetByCharacter( m_bByCharacter
);
289 m_pConversionDialog
->SetConversionFormat( m_eConversionFormat
);
290 m_pConversionDialog
->SetConversionDirectionState( m_bTryBothDirections
, m_ePrimaryConversionDirection
);
293 m_pConversionDialog
->SetOptionsChangedHdl( LINK( this, HangulHanjaConversion_Impl
, OnOptionsChanged
) );
294 m_pConversionDialog
->SetIgnoreHdl( LINK( this, HangulHanjaConversion_Impl
, OnIgnore
) );
295 m_pConversionDialog
->SetIgnoreAllHdl( LINK( this, HangulHanjaConversion_Impl
, OnIgnoreAll
) );
296 m_pConversionDialog
->SetChangeHdl( LINK( this, HangulHanjaConversion_Impl
, OnChange
) );
297 m_pConversionDialog
->SetChangeAllHdl( LINK( this, HangulHanjaConversion_Impl
, OnChangeAll
) );
298 m_pConversionDialog
->SetClickByCharacterHdl( LINK( this, HangulHanjaConversion_Impl
, OnByCharClicked
) );
299 m_pConversionDialog
->SetConversionFormatChangedHdl( LINK( this, HangulHanjaConversion_Impl
, OnConversionTypeChanged
) );
300 m_pConversionDialog
->SetFindHdl( LINK( this, HangulHanjaConversion_Impl
, OnFind
) );
303 sal_Int16
HangulHanjaConversion_Impl::implGetConversionType( bool bSwitchDirection
) const
305 sal_Int16 nConversionType
= -1;
306 if (m_eConvType
== HHC::eConvHangulHanja
)
307 nConversionType
= ( HHC::eHangulToHanja
== m_eCurrentConversionDirection
&& !bSwitchDirection
) ? TO_HANJA
: TO_HANGUL
;
308 else if (m_eConvType
== HHC::eConvSimplifiedTraditional
)
309 nConversionType
= LANGUAGE_CHINESE_SIMPLIFIED
== m_nTargetLang
? TO_SCHINESE
: TO_TCHINESE
;
310 DBG_ASSERT( nConversionType
!= -1, "unexpected conversion type" );
311 return nConversionType
;
314 bool HangulHanjaConversion_Impl::implUpdateSuggestions( bool _bAllowSearchNextConvertibleText
, const sal_Int32 _nStartAt
)
316 // parameters for the converter
317 sal_Int32 nStartSearch
= m_nCurrentStartIndex
;
318 if( _bAllowSearchNextConvertibleText
)
319 nStartSearch
= _nStartAt
;
321 sal_Int32 nLength
= m_sCurrentPortion
.getLength() - nStartSearch
;
322 m_nCurrentConversionType
= implGetConversionType();
323 m_nCurrentConversionOption
= m_bByCharacter
? CHARACTER_BY_CHARACTER
: css::i18n::TextConversionOption::NONE
;
324 if( m_bIgnorePostPositionalWord
)
325 m_nCurrentConversionOption
= m_nCurrentConversionOption
| IGNORE_POST_POSITIONAL_WORD
;
327 // no need to check both directions for chinese conversion (saves time)
328 if (m_eConvType
== HHC::eConvSimplifiedTraditional
)
329 m_bTryBothDirections
= false;
331 bool bFoundAny
= true;
334 TextConversionResult aResult
= m_xConverter
->getConversions(
339 m_nCurrentConversionType
,
340 m_nCurrentConversionOption
342 const bool bFoundPrimary
= aResult
.Boundary
.startPos
< aResult
.Boundary
.endPos
;
343 bFoundAny
= bFoundPrimary
;
345 if ( m_bTryBothDirections
)
346 { // see if we find another convertible when assuming the other direction
347 TextConversionResult aSecondResult
= m_xConverter
->getConversions(
352 implGetConversionType( true ), // switched!
353 m_nCurrentConversionOption
355 if ( aSecondResult
.Boundary
.startPos
< aSecondResult
.Boundary
.endPos
)
356 { // we indeed found such a convertible
358 // in case the first attempt (with the original conversion direction)
359 // didn't find anything
361 // or if the second location is _before_ the first one
362 || ( aSecondResult
.Boundary
.startPos
< aResult
.Boundary
.startPos
)
365 // then use the second finding
366 aResult
= std::move(aSecondResult
);
368 // our current conversion direction changed now
369 m_eCurrentConversionDirection
= ( HHC::eHangulToHanja
== m_eCurrentConversionDirection
)
370 ? HHC::eHanjaToHangul
: HHC::eHangulToHanja
;
376 if( _bAllowSearchNextConvertibleText
)
378 //this might change the current position
379 m_aCurrentSuggestions
= aResult
.Candidates
;
380 m_nCurrentStartIndex
= aResult
.Boundary
.startPos
;
381 m_nCurrentEndIndex
= aResult
.Boundary
.endPos
;
385 //the change of starting position is not allowed
386 if( m_nCurrentStartIndex
== aResult
.Boundary
.startPos
387 && aResult
.Boundary
.endPos
!= aResult
.Boundary
.startPos
)
389 m_aCurrentSuggestions
= aResult
.Candidates
;
390 m_nCurrentEndIndex
= aResult
.Boundary
.endPos
;
394 m_aCurrentSuggestions
.realloc( 0 );
395 if( m_sCurrentPortion
.getLength() >= m_nCurrentStartIndex
+1 )
396 m_nCurrentEndIndex
= m_nCurrentStartIndex
+1;
400 //put recently used string to front:
401 if( m_bShowRecentlyUsedFirst
&& m_aCurrentSuggestions
.getLength()>1 )
403 OUString
sCurrentUnit( GetCurrentUnit() );
404 StringMap::const_iterator aRecentlyUsed
= m_aRecentlyUsedList
.find( sCurrentUnit
);
405 bool bUsedBefore
= aRecentlyUsed
!= m_aRecentlyUsedList
.end();
406 if( bUsedBefore
&& m_aCurrentSuggestions
[0] != aRecentlyUsed
->second
)
408 sal_Int32 nCount
= m_aCurrentSuggestions
.getLength();
409 Sequence
< OUString
> aTmp(nCount
);
410 auto pTmp
= aTmp
.getArray();
411 pTmp
[0]=aRecentlyUsed
->second
;
413 for( sal_Int32 n
=1; n
<nCount
; n
++)//we had 0 already
415 if( nDiff
&& m_aCurrentSuggestions
[n
-nDiff
]==aRecentlyUsed
->second
)
417 pTmp
[n
]=m_aCurrentSuggestions
[n
-nDiff
];
419 m_aCurrentSuggestions
= std::move(aTmp
);
423 catch( const Exception
& )
425 TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::implNextConvertibleUnit" );
427 //!!! at least we want to move on in the text in order
428 //!!! to avoid an endless loop...
434 bool HangulHanjaConversion_Impl::implNextConvertibleUnit( const sal_Int32 _nStartAt
)
436 m_aCurrentSuggestions
.realloc( 0 );
438 // ask the TextConversion service for the next convertible piece of text
440 // get current values from dialog
441 if( m_eConvType
== HHC::eConvHangulHanja
&& m_pConversionDialog
)
443 m_bTryBothDirections
= m_pConversionDialog
->GetUseBothDirections();
444 HHC::ConversionDirection eDialogDirection
= m_pConversionDialog
->GetDirection( HHC::eHangulToHanja
);
446 if( !m_bTryBothDirections
&& eDialogDirection
!= m_eCurrentConversionDirection
)
448 m_eCurrentConversionDirection
= eDialogDirection
;
451 // save currently used value for possible later use
452 HangulHanjaConversion::m_bTryBothDirectionsSave
= m_bTryBothDirections
;
453 HangulHanjaConversion::m_ePrimaryConversionDirectionSave
= m_eCurrentConversionDirection
;
456 bool bFoundAny
= implUpdateSuggestions( true, _nStartAt
);
459 (m_nCurrentStartIndex
< m_sCurrentPortion
.getLength());
462 bool HangulHanjaConversion_Impl::implRetrieveNextPortion( )
464 const bool bAllowImplicitChanges
= m_eConvType
== HHC::eConvSimplifiedTraditional
;
466 m_sCurrentPortion
.clear();
467 m_nCurrentPortionLang
= LANGUAGE_NONE
;
468 m_pAntiImpl
->GetNextPortion( m_sCurrentPortion
, m_nCurrentPortionLang
, bAllowImplicitChanges
);
469 m_nReplacementBaseIndex
= 0;
470 m_nCurrentStartIndex
= m_nCurrentEndIndex
= 0;
472 bool bRet
= !m_sCurrentPortion
.isEmpty();
474 if (m_eConvType
== HHC::eConvHangulHanja
&& m_bTryBothDirections
)
475 implGetConversionDirectionForCurrentPortion( m_eCurrentConversionDirection
);
480 bool HangulHanjaConversion_Impl::implNextConvertible( bool _bRepeatUnit
)
482 if ( _bRepeatUnit
|| ( m_nCurrentEndIndex
< m_sCurrentPortion
.getLength() ) )
484 if ( implNextConvertibleUnit(
486 ? m_nCurrentStartIndex
492 // no convertible text in the current portion anymore
493 // -> advance to the next portion
497 if ( implRetrieveNextPortion( ) )
498 { // there is a next portion
499 // -> find the next convertible unit in the current portion
500 if ( implNextConvertibleUnit( 0 ) )
504 while ( !m_sCurrentPortion
.isEmpty() );
510 OUString
HangulHanjaConversion_Impl::GetCurrentUnit() const
512 DBG_ASSERT( m_nCurrentStartIndex
< m_sCurrentPortion
.getLength(),
513 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
514 DBG_ASSERT( m_nCurrentEndIndex
<= m_sCurrentPortion
.getLength(),
515 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
516 DBG_ASSERT( m_nCurrentStartIndex
<= m_nCurrentEndIndex
,
517 "HangulHanjaConversion_Impl::GetCurrentUnit: invalid interval!" );
519 OUString sCurrentUnit
= m_sCurrentPortion
.copy( m_nCurrentStartIndex
, m_nCurrentEndIndex
- m_nCurrentStartIndex
);
523 bool HangulHanjaConversion_Impl::ContinueConversion( bool _bRepeatCurrentUnit
)
525 while ( implNextConvertible( _bRepeatCurrentUnit
) )
527 OUString
sCurrentUnit( GetCurrentUnit() );
529 // do we need to ignore it?
530 const bool bAlwaysIgnoreThis
= m_sIgnoreList
.end() != m_sIgnoreList
.find( sCurrentUnit
);
532 // do we need to change it?
533 StringMap::const_iterator aChangeListPos
= m_aChangeList
.find( sCurrentUnit
);
534 const bool bAlwaysChangeThis
= m_aChangeList
.end() != aChangeListPos
;
536 // do we automatically change this?
537 const bool bAutoChange
= m_bAutoReplaceUnique
&& m_aCurrentSuggestions
.getLength() == 1;
539 if (!m_bIsInteractive
)
541 // silent conversion (e.g. for simplified/traditional Chinese)...
542 if(m_aCurrentSuggestions
.hasElements())
543 implChange( m_aCurrentSuggestions
.getConstArray()[0] );
545 else if (bAutoChange
)
547 implChange( m_aCurrentSuggestions
.getConstArray()[0] );
549 else if ( bAlwaysChangeThis
)
551 implChange( aChangeListPos
->second
);
553 else if ( !bAlwaysIgnoreThis
)
555 // here we need to ask the user for what to do with the text
556 // for this, allow derivees to highlight the current text unit in a possible document view
557 m_pAntiImpl
->HandleNewUnit( m_nCurrentStartIndex
- m_nReplacementBaseIndex
, m_nCurrentEndIndex
- m_nReplacementBaseIndex
);
559 DBG_ASSERT( m_pConversionDialog
, "we should always have a dialog here!" );
560 if( m_pConversionDialog
)
561 m_pConversionDialog
->SetCurrentString( sCurrentUnit
, m_aCurrentSuggestions
);
563 // do not look for the next convertible: We have to wait for the user to interactively
564 // decide what happens with the current convertible
572 bool HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection
& rDirection
)
574 // - For eConvHangulHanja the direction is determined by
575 // the first encountered Korean character.
576 // - For eConvSimplifiedTraditional the conversion direction
577 // is already specified by the source language.
579 bool bSuccess
= true;
581 if (m_eConvType
== HHC::eConvHangulHanja
)
586 // get the break iterator service
587 Reference
< XBreakIterator
> xBreakIter
= i18n::BreakIterator::create( m_xContext
);
588 sal_Int32 nNextAsianScript
= xBreakIter
->beginOfScript( m_sCurrentPortion
, m_nCurrentStartIndex
, css::i18n::ScriptType::ASIAN
);
589 if ( -1 == nNextAsianScript
)
590 nNextAsianScript
= xBreakIter
->nextScript( m_sCurrentPortion
, m_nCurrentStartIndex
, css::i18n::ScriptType::ASIAN
);
591 if ( ( nNextAsianScript
>= m_nCurrentStartIndex
) && ( nNextAsianScript
< m_sCurrentPortion
.getLength() ) )
592 { // found asian text
594 // determine if it's Hangul
595 CharClass
aCharClassificaton( m_xContext
, LanguageTag( m_aSourceLocale
) );
596 css::i18n::UnicodeScript nScript
= aCharClassificaton
.getScript( m_sCurrentPortion
, sal::static_int_cast
< sal_uInt16
>(nNextAsianScript
) );
597 if ( ( UnicodeScript_kHangulJamo
== nScript
)
598 || ( UnicodeScript_kHangulCompatibilityJamo
== nScript
)
599 || ( UnicodeScript_kHangulSyllable
== nScript
)
602 rDirection
= HHC::eHangulToHanja
;
606 rDirection
= HHC::eHanjaToHangul
;
612 catch( const Exception
& )
614 TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion" );
621 void HangulHanjaConversion_Impl::DoDocumentConversion( )
623 // clear the change-all list - it's to be re-initialized for every single document
624 StringMap().swap(m_aChangeList
);
626 // first of all, we need to guess the direction of our conversion - it is determined by the first
627 // hangul or hanja character in the first text
628 if ( !implRetrieveNextPortion() )
630 SAL_INFO( "editeng", "HangulHanjaConversion_Impl::DoDocumentConversion: why did you call me if you do have nothing to convert?" );
634 if( m_eConvType
== HHC::eConvHangulHanja
)
636 //init conversion direction from saved value
637 HHC::ConversionDirection eDirection
= HHC::eHangulToHanja
;
638 if(!implGetConversionDirectionForCurrentPortion( eDirection
))
639 // something went wrong, has already been asserted
642 if (HangulHanjaConversion::IsUseSavedConversionDirectionState())
644 m_ePrimaryConversionDirection
= HangulHanjaConversion::m_ePrimaryConversionDirectionSave
;
645 m_bTryBothDirections
= HangulHanjaConversion::m_bTryBothDirectionsSave
;
646 if( m_bTryBothDirections
)
647 m_eCurrentConversionDirection
= eDirection
;
649 m_eCurrentConversionDirection
= m_ePrimaryConversionDirection
;
653 m_ePrimaryConversionDirection
= eDirection
;
654 m_eCurrentConversionDirection
= eDirection
;
658 if (m_bIsInteractive
&& m_eConvType
== HHC::eConvHangulHanja
)
660 //always open dialog if at least having a hangul or hanja text portion
662 if(HangulHanjaConversion::IsUseSavedConversionDirectionState())
663 ContinueConversion( false );
666 m_pConversionDialog
->Execute();
667 m_pConversionDialog
.disposeAndClear();
671 const bool bCompletelyDone
= ContinueConversion( false );
672 DBG_ASSERT( bCompletelyDone
, "HangulHanjaConversion_Impl::DoDocumentConversion: ContinueConversion should have returned true here!" );
676 void HangulHanjaConversion_Impl::implProceed( bool _bRepeatCurrentUnit
)
678 if ( ContinueConversion( _bRepeatCurrentUnit
) )
679 { // we're done with the whole document
680 DBG_ASSERT( !m_bIsInteractive
|| m_pConversionDialog
, "HangulHanjaConversion_Impl::implProceed: we should not reach this here without dialog!" );
681 if ( m_pConversionDialog
)
682 m_pConversionDialog
->EndDialog( RET_OK
);
686 void HangulHanjaConversion_Impl::implChange( const OUString
& _rChangeInto
)
688 if( _rChangeInto
.isEmpty() )
691 // translate the conversion format into a replacement action
692 // this translation depends on whether we have a Hangul original, or a Hanja original
694 HHC::ReplacementAction
eAction( HHC::eExchange
);
696 if (m_eConvType
== HHC::eConvHangulHanja
)
698 // is the original we're about to change in Hangul?
699 const bool bOriginalIsHangul
= HHC::eHangulToHanja
== m_eCurrentConversionDirection
;
701 switch ( m_eConversionFormat
)
703 case HHC::eSimpleConversion
: eAction
= HHC::eExchange
; break;
704 case HHC::eHangulBracketed
: eAction
= bOriginalIsHangul
? HHC::eOriginalBracketed
: HHC::eReplacementBracketed
; break;
705 case HHC::eHanjaBracketed
: eAction
= bOriginalIsHangul
? HHC::eReplacementBracketed
: HHC::eOriginalBracketed
; break;
706 case HHC::eRubyHanjaAbove
: eAction
= bOriginalIsHangul
? HHC::eReplacementAbove
: HHC::eOriginalAbove
; break;
707 case HHC::eRubyHanjaBelow
: eAction
= bOriginalIsHangul
? HHC::eReplacementBelow
: HHC::eOriginalBelow
; break;
708 case HHC::eRubyHangulAbove
: eAction
= bOriginalIsHangul
? HHC::eOriginalAbove
: HHC::eReplacementAbove
; break;
709 case HHC::eRubyHangulBelow
: eAction
= bOriginalIsHangul
? HHC::eOriginalBelow
: HHC::eReplacementBelow
; break;
711 OSL_FAIL( "HangulHanjaConversion_Impl::implChange: invalid/unexpected conversion format!" );
715 // the proper indices (the wrapper implementation needs indices relative to the
716 // previous replacement)
717 DBG_ASSERT( ( m_nReplacementBaseIndex
<= m_nCurrentStartIndex
) && ( m_nReplacementBaseIndex
<= m_nCurrentEndIndex
),
718 "HangulHanjaConversion_Impl::implChange: invalid replacement base!" );
720 sal_Int32 nStartIndex
= m_nCurrentStartIndex
- m_nReplacementBaseIndex
;
721 sal_Int32 nEndIndex
= m_nCurrentEndIndex
- m_nReplacementBaseIndex
;
723 //remind this decision
724 m_aRecentlyUsedList
[ GetCurrentUnit() ] = _rChangeInto
;
726 LanguageType
*pNewUnitLang
= nullptr;
727 LanguageType nNewUnitLang
= LANGUAGE_NONE
;
728 if (m_eConvType
== HHC::eConvSimplifiedTraditional
)
730 // check if language needs to be changed
731 if ( m_pAntiImpl
->GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL
&&
732 !HangulHanjaConversion::IsTraditional( m_nCurrentPortionLang
))
733 nNewUnitLang
= LANGUAGE_CHINESE_TRADITIONAL
;
734 else if ( m_pAntiImpl
->GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED
&&
735 !HangulHanjaConversion::IsSimplified( m_nCurrentPortionLang
))
736 nNewUnitLang
= LANGUAGE_CHINESE_SIMPLIFIED
;
737 if (nNewUnitLang
!= LANGUAGE_NONE
)
738 pNewUnitLang
= &nNewUnitLang
;
741 // according to FT we should not (yet) bother about Hangul/Hanja conversion here
743 // aOffsets is needed in ReplaceUnit below in order to find out
744 // exactly which characters are really changed in order to keep as much
745 // from attributation for the text as possible.
746 Sequence
< sal_Int32
> aOffsets
;
747 if (m_eConvType
== HHC::eConvSimplifiedTraditional
&& m_xConverter
.is())
751 m_xConverter
->getConversionWithOffset(
753 m_nCurrentStartIndex
,
754 m_nCurrentEndIndex
- m_nCurrentStartIndex
,
756 m_nCurrentConversionType
,
757 m_nCurrentConversionOption
,
761 catch( const Exception
& )
763 TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::implChange: caught unexpected exception!" );
768 // do the replacement
769 m_pAntiImpl
->ReplaceUnit( nStartIndex
, nEndIndex
, m_sCurrentPortion
,
770 _rChangeInto
, aOffsets
, eAction
, pNewUnitLang
);
773 // adjust the replacement base
774 m_nReplacementBaseIndex
= m_nCurrentEndIndex
;
777 void HangulHanjaConversion_Impl::implReadOptionsFromConfiguration()
779 SvtLinguConfig aLngCfg
;
780 aLngCfg
.GetProperty( UPH_IS_IGNORE_POST_POSITIONAL_WORD
) >>= m_bIgnorePostPositionalWord
;
781 aLngCfg
.GetProperty( UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST
) >>= m_bShowRecentlyUsedFirst
;
782 aLngCfg
.GetProperty( UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES
) >>= m_bAutoReplaceUnique
;
785 void HangulHanjaConversion_Impl::implUpdateData()
787 implReadOptionsFromConfiguration();
788 implUpdateSuggestions();
790 if(m_pConversionDialog
)
792 OUString
sCurrentUnit( GetCurrentUnit() );
794 m_pConversionDialog
->SetCurrentString( sCurrentUnit
, m_aCurrentSuggestions
);
795 m_pConversionDialog
->FocusSuggestion();
798 m_pAntiImpl
->HandleNewUnit( m_nCurrentStartIndex
- m_nReplacementBaseIndex
, m_nCurrentEndIndex
- m_nReplacementBaseIndex
);
801 IMPL_LINK_NOARG(HangulHanjaConversion_Impl
, OnOptionsChanged
, LinkParamNone
*, void)
803 //options and dictionaries might have been changed
804 //-> update our internal settings and the dialog
808 IMPL_LINK_NOARG(HangulHanjaConversion_Impl
, OnIgnore
, weld::Button
&, void)
810 // simply ignore, and proceed
811 implProceed( false );
814 IMPL_LINK_NOARG(HangulHanjaConversion_Impl
, OnIgnoreAll
, weld::Button
&, void)
816 DBG_ASSERT( m_pConversionDialog
, "HangulHanjaConversion_Impl::OnIgnoreAll: no dialog! How this?" );
818 if ( m_pConversionDialog
)
820 OUString sCurrentUnit
= m_pConversionDialog
->GetCurrentString();
821 DBG_ASSERT( m_sIgnoreList
.end() == m_sIgnoreList
.find( sCurrentUnit
),
822 "HangulHanjaConversion_Impl, OnIgnoreAll: shouldn't this have been ignored before" );
824 // put into the "ignore all" list
825 m_sIgnoreList
.insert( sCurrentUnit
);
828 implProceed( false );
832 IMPL_LINK_NOARG(HangulHanjaConversion_Impl
, OnChange
, weld::Button
&, void)
835 DBG_ASSERT( m_pConversionDialog
, "we should always have a dialog here!" );
836 if( m_pConversionDialog
)
837 implChange( m_pConversionDialog
->GetCurrentSuggestion( ) );
839 implProceed( false );
842 IMPL_LINK_NOARG(HangulHanjaConversion_Impl
, OnChangeAll
, weld::Button
&, void)
844 DBG_ASSERT( m_pConversionDialog
, "HangulHanjaConversion_Impl::OnChangeAll: no dialog! How this?" );
845 if ( !m_pConversionDialog
)
848 OUString
sCurrentUnit( m_pConversionDialog
->GetCurrentString() );
849 OUString
sChangeInto( m_pConversionDialog
->GetCurrentSuggestion( ) );
851 if( !sChangeInto
.isEmpty() )
853 // change the current occurrence
854 implChange( sChangeInto
);
856 // put into the "change all" list
857 m_aChangeList
.emplace( sCurrentUnit
, sChangeInto
);
861 implProceed( false );
864 IMPL_LINK(HangulHanjaConversion_Impl
, OnByCharClicked
, weld::Toggleable
&, rBox
, void)
866 m_bByCharacter
= rBox
.get_active();
868 // continue conversion, without advancing to the next unit, but instead continuing with the current unit
872 IMPL_LINK_NOARG(HangulHanjaConversion_Impl
, OnConversionTypeChanged
, weld::Toggleable
&, void)
874 DBG_ASSERT( m_pConversionDialog
, "we should always have a dialog here!" );
875 if( m_pConversionDialog
)
876 m_eConversionFormat
= m_pConversionDialog
->GetConversionFormat( );
879 IMPL_LINK_NOARG(HangulHanjaConversion_Impl
, OnFind
, weld::Button
&, void)
881 DBG_ASSERT( m_pConversionDialog
, "HangulHanjaConversion_Impl::OnFind: where did this come from?" );
882 if ( !m_pConversionDialog
)
887 OUString
sNewOriginal( m_pConversionDialog
->GetCurrentSuggestion( ) );
888 Sequence
< OUString
> aSuggestions
;
890 DBG_ASSERT( m_xConverter
.is(), "HangulHanjaConversion_Impl::OnFind: no converter!" );
891 TextConversionResult aToHanja
= m_xConverter
->getConversions(
893 0, sNewOriginal
.getLength(),
895 TextConversionType::TO_HANJA
,
896 TextConversionOption::NONE
898 TextConversionResult aToHangul
= m_xConverter
->getConversions(
900 0, sNewOriginal
.getLength(),
902 TextConversionType::TO_HANGUL
,
903 TextConversionOption::NONE
906 bool bHaveToHanja
= ( aToHanja
.Boundary
.startPos
< aToHanja
.Boundary
.endPos
);
907 bool bHaveToHangul
= ( aToHangul
.Boundary
.startPos
< aToHangul
.Boundary
.endPos
);
909 TextConversionResult
* pResult
= nullptr;
910 if ( bHaveToHanja
&& bHaveToHangul
)
911 { // it found convertibles in both directions -> use the first
912 if ( aToHangul
.Boundary
.startPos
< aToHanja
.Boundary
.startPos
)
913 pResult
= &aToHangul
;
917 else if ( bHaveToHanja
)
918 { // only found toHanja
922 { // only found toHangul
923 pResult
= &aToHangul
;
926 aSuggestions
= pResult
->Candidates
;
928 m_pConversionDialog
->SetCurrentString( sNewOriginal
, aSuggestions
, false );
929 m_pConversionDialog
->FocusSuggestion();
931 catch( const Exception
& )
933 TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::OnFind" );
937 bool HangulHanjaConversion::m_bUseSavedValues
= false;
938 bool HangulHanjaConversion::m_bTryBothDirectionsSave
= false;
939 HHC::ConversionDirection
HangulHanjaConversion::m_ePrimaryConversionDirectionSave
= HHC::eHangulToHanja
;
941 HangulHanjaConversion::HangulHanjaConversion( weld::Widget
* pUIParent
,
942 const Reference
< XComponentContext
>& rxContext
,
943 const lang::Locale
& _rSourceLocale
, const lang::Locale
& _rTargetLocale
,
944 const vcl::Font
* _pTargetFont
,
945 sal_Int32 _nOptions
, bool _bIsInteractive
)
946 :m_pImpl( new HangulHanjaConversion_Impl( pUIParent
, rxContext
, _rSourceLocale
, _rTargetLocale
, _pTargetFont
, _nOptions
, _bIsInteractive
, this ) )
950 HangulHanjaConversion::~HangulHanjaConversion() COVERITY_NOEXCEPT_FALSE
954 void HangulHanjaConversion::SetUseSavedConversionDirectionState( bool bVal
)
956 m_bUseSavedValues
= bVal
;
959 bool HangulHanjaConversion::IsUseSavedConversionDirectionState()
961 return m_bUseSavedValues
;
964 weld::Widget
* HangulHanjaConversion::GetUIParent() const
966 return m_pImpl
->GetUIParent();
969 LanguageType
HangulHanjaConversion::GetSourceLanguage( ) const
971 return m_pImpl
->GetSourceLang();
974 LanguageType
HangulHanjaConversion::GetTargetLanguage( ) const
976 return m_pImpl
->GetTargetLang();
979 const vcl::Font
* HangulHanjaConversion::GetTargetFont( ) const
981 return m_pImpl
->GetTargetFont();
984 sal_Int32
HangulHanjaConversion::GetConversionOptions( ) const
986 return m_pImpl
->GetConvOptions();
989 bool HangulHanjaConversion::IsInteractive( ) const
991 return m_pImpl
->IsInteractive();
994 void HangulHanjaConversion::ConvertDocument()
996 if ( m_pImpl
->IsValid() )
997 m_pImpl
->DoDocumentConversion( );
1002 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */