tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / editeng / source / misc / hangulhanja.cxx
blob39e3411b65455c9e4d5d920c54374f41f7386f41
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 <unotools/lingucfg.hxx>
22 #include <unotools/linguprops.hxx>
24 #include <set>
25 #include <map>
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
46 namespace editeng
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
58 private:
59 typedef std::set<OUString> StringBag;
60 typedef std::map<OUString, OUString> StringMap;
62 private:
63 StringBag m_sIgnoreList;
64 StringMap m_aChangeList;
65 static StringMap m_aRecentlyUsedList;
67 // general
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
93 // options
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;
104 // state
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;
112 Sequence< OUString >
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
118 public:
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 );
129 public:
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; }
141 protected:
142 void createDialog();
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 );
151 private:
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
163 <p><b>Attention:</b>
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
181 @param _bRepeatUnit
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
188 @return
189 <TRUE/> if and only if there is a next portion
191 bool implRetrieveNextPortion( );
193 /** determine the ConversionDirection for m_sCurrentPortion
194 @return
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.
204 @return
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,
234 sal_Int32 _nOptions,
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;
268 else
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 )
281 return;
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 );
292 // the handlers
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(
335 m_sCurrentPortion,
336 nStartSearch,
337 nLength,
338 m_aSourceLocale,
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(
348 m_sCurrentPortion,
349 nStartSearch,
350 nLength,
351 m_aSourceLocale,
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
360 if ( !bFoundPrimary
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;
371 bFoundAny = true;
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;
383 else
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;
392 else
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;
412 sal_Int32 nDiff = 1;
413 for( sal_Int32 n=1; n<nCount; n++)//we had 0 already
415 if( nDiff && m_aCurrentSuggestions[n-nDiff]==aRecentlyUsed->second )
416 nDiff=0;
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...
429 return false;
431 return bFoundAny;
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 );
458 return bFoundAny &&
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 );
477 return bRet;
480 bool HangulHanjaConversion_Impl::implNextConvertible( bool _bRepeatUnit )
482 if ( _bRepeatUnit || ( m_nCurrentEndIndex < m_sCurrentPortion.getLength() ) )
484 if ( implNextConvertibleUnit(
485 _bRepeatUnit
486 ? m_nCurrentStartIndex
487 : m_nCurrentEndIndex
489 return true;
492 // no convertible text in the current portion anymore
493 // -> advance to the next portion
496 // 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 ) )
501 return true;
504 while ( !m_sCurrentPortion.isEmpty() );
506 // no more portions
507 return false;
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 );
520 return sCurrentUnit;
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
565 return false;
569 return true;
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)
583 bSuccess = false;
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;
604 else
606 rDirection = HHC::eHanjaToHangul;
609 bSuccess = true;
612 catch( const Exception& )
614 TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion" );
618 return bSuccess;
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?" );
631 // nothing to do
632 return;
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
640 return;
642 if (HangulHanjaConversion::IsUseSavedConversionDirectionState())
644 m_ePrimaryConversionDirection = HangulHanjaConversion::m_ePrimaryConversionDirectionSave;
645 m_bTryBothDirections = HangulHanjaConversion::m_bTryBothDirectionsSave;
646 if( m_bTryBothDirections )
647 m_eCurrentConversionDirection = eDirection;
648 else
649 m_eCurrentConversionDirection = m_ePrimaryConversionDirection;
651 else
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
661 createDialog();
662 if(HangulHanjaConversion::IsUseSavedConversionDirectionState())
663 ContinueConversion( false );
664 else
665 implUpdateData();
666 m_pConversionDialog->Execute();
667 m_pConversionDialog.disposeAndClear();
669 else
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() )
689 return;
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;
710 default:
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(
752 m_sCurrentPortion,
753 m_nCurrentStartIndex,
754 m_nCurrentEndIndex - m_nCurrentStartIndex,
755 m_aSourceLocale,
756 m_nCurrentConversionType,
757 m_nCurrentConversionOption,
758 aOffsets
761 catch( const Exception& )
763 TOOLS_WARN_EXCEPTION( "editeng", "HangulHanjaConversion_Impl::implChange: caught unexpected exception!" );
764 aOffsets.realloc(0);
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
805 implUpdateData();
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 );
827 // and proceed
828 implProceed( false );
832 IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChange, weld::Button&, void)
834 // change
835 DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
836 if( m_pConversionDialog )
837 implChange( m_pConversionDialog->GetCurrentSuggestion( ) );
838 // and proceed
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 )
846 return;
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 );
860 // and proceed
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
869 implProceed( true );
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 )
883 return;
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(
892 sNewOriginal,
893 0, sNewOriginal.getLength(),
894 m_aSourceLocale,
895 TextConversionType::TO_HANJA,
896 TextConversionOption::NONE
898 TextConversionResult aToHangul = m_xConverter->getConversions(
899 sNewOriginal,
900 0, sNewOriginal.getLength(),
901 m_aSourceLocale,
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;
914 else
915 pResult = &aToHanja;
917 else if ( bHaveToHanja )
918 { // only found toHanja
919 pResult = &aToHanja;
921 else
922 { // only found toHangul
923 pResult = &aToHangul;
925 if ( pResult )
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: */