Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
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>
25 :
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>
40 :
41 : #include <editeng/edtdlg.hxx>
42 : #include <editeng/editrids.hrc>
43 : #include <editeng/unolingu.hxx>
44 :
45 : #define HHC HangulHanjaConversion
46 :
47 :
48 : namespace editeng
49 : {
50 :
51 :
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;
57 :
58 4 : class HangulHanjaConversion_Impl
59 : {
60 : private:
61 : typedef ::std::set< OUString, ::std::less< OUString > > StringBag;
62 : typedef ::std::map< OUString, OUString, ::std::less< OUString > > StringMap;
63 :
64 : private:
65 : StringBag m_sIgnoreList;
66 : StringMap m_aChangeList;
67 : static StringMap m_aRecentlyUsedList;
68 :
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
78 :
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
92 :
93 : HangulHanjaConversion* m_pAntiImpl; // our "anti-impl" instance
94 :
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
100 :
101 : //options from Hangul/Hanja Options dialog (also saved to configuration)
102 : bool m_bIgnorePostPositionalWord;
103 : bool m_bShowRecentlyUsedFirst;
104 : bool m_bAutoReplaceUnique;
105 :
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
118 :
119 :
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 );
130 :
131 : public:
132 : void DoDocumentConversion( );
133 :
134 2 : inline bool IsByCharacter( ) const { return m_bByCharacter; }
135 :
136 4 : inline bool IsValid() const { return m_xConverter.is(); }
137 :
138 10 : inline LanguageType GetSourceLang() const { return m_nSourceLang; }
139 15 : inline LanguageType GetTargetLang() const { return m_nTargetLang; }
140 10 : inline const vcl::Font * GetTargetFont() const { return m_pTargetFont; }
141 0 : inline sal_Int32 GetConvOptions() const { return m_nConvOptions; }
142 0 : inline bool IsInteractive() const { return m_bIsInteractive; }
143 :
144 : protected:
145 : void createDialog();
146 :
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
151 : */
152 : bool ContinueConversion( bool _bRepeatCurrentUnit );
153 :
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* );
163 :
164 : /** proceed, after the current convertible has been handled
165 :
166 : <p><b>Attention:</b>
167 : When returning from this method, the dialog may have been deleted!</p>
168 :
169 : @param _bRepeatCurrentUnit
170 : will be passed to the <member>ContinueConversion</member> call
171 : */
172 : void implProceed( bool _bRepeatCurrentUnit );
173 :
174 : // change the current convertible, and do _not_ proceed
175 : void implChange( const OUString& _rChangeInto );
176 :
177 : /** find the next convertible piece of text, with possibly advancing to the next portion
178 :
179 : @see HangulHanjaConversion::GetNextPortion
180 : */
181 : bool implNextConvertible( bool _bRepeatUnit );
182 :
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
187 : */
188 : bool implNextConvertibleUnit( const sal_Int32 _nStartAt );
189 :
190 : /** retrieves the next portion, with setting the index members properly
191 : @return
192 : <TRUE/> if and only if there is a next portion
193 : */
194 : bool implRetrieveNextPortion( );
195 :
196 : /** determine the ConversionDirection for m_sCurrentPortion
197 : @return
198 : <FALSE/> if and only if something went wrong
199 : */
200 : bool implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection );
201 :
202 : /** member m_aCurrentSuggestions and m_nCurrentEndIndex are updated according to the other settings and current dictionaries
203 :
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.
206 :
207 : @return
208 : <TRUE/> if Suggestions were found
209 : */
210 : bool implUpdateSuggestions( const bool _bAllowSearchNextConvertibleText=false, const sal_Int32 _nStartAt=-1 );
211 :
212 : /** reads the options from Hangul/Hanja Options dialog that are saved to configuration
213 : */
214 : void implReadOptionsFromConfiguration();
215 :
216 : /** get the string currently considered to be replaced or ignored
217 : */
218 : OUString GetCurrentUnit() const;
219 :
220 : /** read options from configuration, update suggestion list and dialog content
221 : */
222 : void implUpdateData();
223 :
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
226 : */
227 : sal_Int16 implGetConversionType( bool bSwitchDirection=false ) const;
228 : };
229 :
230 148 : HangulHanjaConversion_Impl::StringMap HangulHanjaConversion_Impl::m_aRecentlyUsedList = HangulHanjaConversion_Impl::StringMap();
231 :
232 4 : 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 4 : , m_nSourceLang( LanguageTag::convertToLanguageType( _rSourceLocale ) )
245 4 : , m_nTargetLang( LanguageTag::convertToLanguageType( _rTargetLocale ) )
246 : , m_pTargetFont( _pTargetFont )
247 : , m_nConvOptions(_nOptions)
248 : , m_bIsInteractive( _bIsInteractive )
249 : , m_pAntiImpl( _pAntiImpl )
250 4 : , 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 16 : , m_bTryBothDirections( true )
261 : {
262 4 : implReadOptionsFromConfiguration();
263 :
264 : DBG_ASSERT( m_xContext.is(), "HangulHanjaConversion_Impl::HangulHanjaConversion_Impl: no ORB!" );
265 :
266 : // determine conversion type
267 4 : if (m_nSourceLang == LANGUAGE_KOREAN && m_nTargetLang == LANGUAGE_KOREAN)
268 0 : m_eConvType = HHC::eConvHangulHanja;
269 5 : else if ( (m_nSourceLang == LANGUAGE_CHINESE_TRADITIONAL && m_nTargetLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
270 2 : (m_nSourceLang == LANGUAGE_CHINESE_SIMPLIFIED && m_nTargetLang == LANGUAGE_CHINESE_TRADITIONAL) )
271 4 : m_eConvType = HHC::eConvSimplifiedTraditional;
272 : else
273 : {
274 0 : m_eConvType = HHC::eConvHangulHanja;
275 : OSL_FAIL( "failed to determine conversion type from languages" );
276 : }
277 :
278 4 : m_xConverter = TextConversion::create( m_xContext );
279 4 : }
280 :
281 0 : void HangulHanjaConversion_Impl::createDialog()
282 : {
283 : DBG_ASSERT( m_bIsInteractive, "createDialog when the conversion should not be interactive?" );
284 0 : if ( m_bIsInteractive && !m_pConversionDialog )
285 : {
286 0 : EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
287 0 : if(pFact)
288 : {
289 0 : m_pConversionDialog = pFact->CreateHangulHanjaConversionDialog(m_pUIParent, m_ePrimaryConversionDirection );
290 : DBG_ASSERT(m_pConversionDialog, "Dialog creation failed!");
291 :
292 0 : m_pConversionDialog->EnableRubySupport( m_pAntiImpl->HasRubySupport() );
293 :
294 0 : m_pConversionDialog->SetByCharacter( m_bByCharacter );
295 0 : m_pConversionDialog->SetConversionFormat( m_eConversionFormat );
296 0 : m_pConversionDialog->SetConversionDirectionState( m_bTryBothDirections, m_ePrimaryConversionDirection );
297 :
298 : // the handlers
299 0 : m_pConversionDialog->SetOptionsChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnOptionsChanged ) );
300 0 : m_pConversionDialog->SetIgnoreHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnore ) );
301 0 : m_pConversionDialog->SetIgnoreAllHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnoreAll ) );
302 0 : m_pConversionDialog->SetChangeHdl( LINK( this, HangulHanjaConversion_Impl, OnChange ) );
303 0 : m_pConversionDialog->SetChangeAllHdl( LINK( this, HangulHanjaConversion_Impl, OnChangeAll ) );
304 0 : m_pConversionDialog->SetClickByCharacterHdl( LINK( this, HangulHanjaConversion_Impl, OnByCharClicked ) );
305 0 : m_pConversionDialog->SetConversionFormatChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnConversionTypeChanged ) );
306 0 : m_pConversionDialog->SetFindHdl( LINK( this, HangulHanjaConversion_Impl, OnFind ) );
307 : }
308 : }
309 0 : }
310 :
311 2 : sal_Int16 HangulHanjaConversion_Impl::implGetConversionType( bool bSwitchDirection ) const
312 : {
313 2 : sal_Int16 nConversionType = -1;
314 2 : if (m_eConvType == HHC::eConvHangulHanja)
315 0 : nConversionType = ( HHC::eHangulToHanja == m_eCurrentConversionDirection && !bSwitchDirection ) ? TO_HANJA : TO_HANGUL;
316 2 : else if (m_eConvType == HHC::eConvSimplifiedTraditional)
317 2 : nConversionType = LANGUAGE_CHINESE_SIMPLIFIED == m_nTargetLang ? TO_SCHINESE : TO_TCHINESE;
318 : DBG_ASSERT( nConversionType != -1, "unexpected conversion type" );
319 2 : return nConversionType;
320 : }
321 :
322 2 : bool HangulHanjaConversion_Impl::implUpdateSuggestions( bool _bAllowSearchNextConvertibleText, const sal_Int32 _nStartAt )
323 : {
324 : // parameters for the converter
325 2 : sal_Int32 nStartSearch = m_nCurrentStartIndex;
326 2 : if( _bAllowSearchNextConvertibleText )
327 2 : nStartSearch = _nStartAt;
328 :
329 2 : sal_Int32 nLength = m_sCurrentPortion.getLength() - nStartSearch;
330 2 : m_nCurrentConversionType = implGetConversionType();
331 2 : m_nCurrentConversionOption = IsByCharacter() ? CHARACTER_BY_CHARACTER : css::i18n::TextConversionOption::NONE;
332 2 : if( m_bIgnorePostPositionalWord )
333 2 : m_nCurrentConversionOption = m_nCurrentConversionOption | IGNORE_POST_POSITIONAL_WORD;
334 :
335 : // no need to check both directions for chinese conversion (saves time)
336 2 : if (m_eConvType == HHC::eConvSimplifiedTraditional)
337 2 : m_bTryBothDirections = false;
338 :
339 2 : bool bFoundAny = true;
340 : try
341 : {
342 2 : TextConversionResult aResult = m_xConverter->getConversions(
343 : m_sCurrentPortion,
344 : nStartSearch,
345 : nLength,
346 : m_aSourceLocale,
347 : m_nCurrentConversionType,
348 : m_nCurrentConversionOption
349 2 : );
350 2 : const bool bFoundPrimary = aResult.Boundary.startPos < aResult.Boundary.endPos;
351 2 : bFoundAny = bFoundPrimary;
352 :
353 2 : if ( m_bTryBothDirections )
354 : { // see if we find another convertible when assuming the other direction
355 0 : TextConversionResult aSecondResult = m_xConverter->getConversions(
356 : m_sCurrentPortion,
357 : nStartSearch,
358 : nLength,
359 : m_aSourceLocale,
360 0 : implGetConversionType( true ), // switched!
361 : m_nCurrentConversionOption
362 0 : );
363 0 : if ( aSecondResult.Boundary.startPos < aSecondResult.Boundary.endPos )
364 : { // we indeed found such a convertible
365 :
366 : // in case the first attempt (with the original conversion direction)
367 : // didn't find anything
368 0 : if ( !bFoundPrimary
369 : // or if the second location is _before_ the first one
370 0 : || ( aSecondResult.Boundary.startPos < aResult.Boundary.startPos )
371 : )
372 : {
373 : // then use the second finding
374 0 : aResult = aSecondResult;
375 :
376 : // our current conversion direction changed now
377 0 : m_eCurrentConversionDirection = ( HHC::eHangulToHanja == m_eCurrentConversionDirection )
378 0 : ? HHC::eHanjaToHangul : HHC::eHangulToHanja;
379 0 : bFoundAny = true;
380 : }
381 0 : }
382 : }
383 :
384 2 : if( _bAllowSearchNextConvertibleText )
385 : {
386 : //this might change the current position
387 2 : m_aCurrentSuggestions = aResult.Candidates;
388 2 : m_nCurrentStartIndex = aResult.Boundary.startPos;
389 2 : m_nCurrentEndIndex = aResult.Boundary.endPos;
390 : }
391 : else
392 : {
393 : //the change of starting position is not allowed
394 0 : if( m_nCurrentStartIndex == aResult.Boundary.startPos
395 0 : && aResult.Boundary.endPos != aResult.Boundary.startPos )
396 : {
397 0 : m_aCurrentSuggestions = aResult.Candidates;
398 0 : m_nCurrentEndIndex = aResult.Boundary.endPos;
399 : }
400 : else
401 : {
402 0 : m_aCurrentSuggestions.realloc( 0 );
403 0 : if( m_sCurrentPortion.getLength() >= m_nCurrentStartIndex+1 )
404 0 : m_nCurrentEndIndex = m_nCurrentStartIndex+1;
405 : }
406 : }
407 :
408 : //put recently used string to front:
409 2 : if( m_bShowRecentlyUsedFirst && m_aCurrentSuggestions.getLength()>1 )
410 : {
411 0 : OUString sCurrentUnit( GetCurrentUnit() );
412 0 : StringMap::const_iterator aRecentlyUsed = m_aRecentlyUsedList.find( sCurrentUnit );
413 0 : bool bUsedBefore = aRecentlyUsed != m_aRecentlyUsedList.end();
414 0 : if( bUsedBefore && m_aCurrentSuggestions[0] != aRecentlyUsed->second )
415 : {
416 0 : sal_Int32 nCount = m_aCurrentSuggestions.getLength();
417 0 : Sequence< OUString > aTmp(nCount);
418 0 : aTmp[0]=aRecentlyUsed->second;
419 0 : sal_Int32 nDiff = 1;
420 0 : for( sal_Int32 n=1; n<nCount; n++)//we had 0 already
421 : {
422 0 : if( nDiff && m_aCurrentSuggestions[n-nDiff]==aRecentlyUsed->second )
423 0 : nDiff=0;
424 0 : aTmp[n]=m_aCurrentSuggestions[n-nDiff];
425 : }
426 0 : m_aCurrentSuggestions = aTmp;
427 0 : }
428 2 : }
429 : }
430 0 : catch( const Exception& )
431 : {
432 : OSL_FAIL( "HangulHanjaConversion_Impl::implNextConvertibleUnit: caught an exception!" );
433 :
434 : //!!! at least we want to move on in the text in order
435 : //!!! to avoid an endless loop...
436 0 : return false;
437 : }
438 2 : return bFoundAny;
439 : }
440 :
441 2 : bool HangulHanjaConversion_Impl::implNextConvertibleUnit( const sal_Int32 _nStartAt )
442 : {
443 2 : m_aCurrentSuggestions.realloc( 0 );
444 :
445 : // ask the TextConversion service for the next convertible piece of text
446 :
447 : // get current values from dialog
448 2 : if( m_eConvType == HHC::eConvHangulHanja && m_pConversionDialog )
449 : {
450 0 : m_bTryBothDirections = m_pConversionDialog->GetUseBothDirections();
451 0 : HHC::ConversionDirection eDialogDirection = HHC::eHangulToHanja;
452 0 : eDialogDirection = m_pConversionDialog->GetDirection( eDialogDirection );
453 :
454 0 : if( !m_bTryBothDirections && eDialogDirection != m_eCurrentConversionDirection )
455 : {
456 0 : m_eCurrentConversionDirection = eDialogDirection;
457 : }
458 :
459 : // save currently used value for possible later use
460 0 : m_pAntiImpl->m_bTryBothDirectionsSave = m_bTryBothDirections;
461 0 : m_pAntiImpl->m_ePrimaryConversionDirectionSave = m_eCurrentConversionDirection;
462 : }
463 :
464 2 : bool bFoundAny = implUpdateSuggestions( true, _nStartAt );
465 :
466 4 : return bFoundAny &&
467 4 : (m_nCurrentStartIndex < m_sCurrentPortion.getLength());
468 : }
469 :
470 6 : bool HangulHanjaConversion_Impl::implRetrieveNextPortion( )
471 : {
472 6 : const bool bAllowImplicitChanges = m_eConvType == HHC::eConvSimplifiedTraditional;
473 :
474 6 : m_sCurrentPortion.clear();
475 6 : m_nCurrentPortionLang = LANGUAGE_NONE;
476 6 : m_pAntiImpl->GetNextPortion( m_sCurrentPortion, m_nCurrentPortionLang, bAllowImplicitChanges );
477 6 : m_nReplacementBaseIndex = 0;
478 6 : m_nCurrentStartIndex = m_nCurrentEndIndex = 0;
479 :
480 6 : bool bRet = !m_sCurrentPortion.isEmpty();
481 :
482 6 : if (m_eConvType == HHC::eConvHangulHanja && m_bTryBothDirections)
483 0 : implGetConversionDirectionForCurrentPortion( m_eCurrentConversionDirection );
484 :
485 6 : return bRet;
486 : }
487 :
488 4 : bool HangulHanjaConversion_Impl::implNextConvertible( bool _bRepeatUnit )
489 : {
490 4 : if ( _bRepeatUnit || ( m_nCurrentEndIndex < m_sCurrentPortion.getLength() ) )
491 : {
492 2 : if ( implNextConvertibleUnit(
493 : _bRepeatUnit
494 : ? m_nCurrentStartIndex
495 : : m_nCurrentEndIndex
496 2 : ) )
497 2 : return true;
498 : }
499 :
500 : // no convertible text in the current portion anymore
501 : // -> advance to the next portion
502 2 : do
503 : {
504 : // next portion
505 2 : if ( implRetrieveNextPortion( ) )
506 : { // there is a next portion
507 : // -> find the next convertible unit in the current portion
508 0 : if ( implNextConvertibleUnit( 0 ) )
509 0 : return true;
510 : }
511 : }
512 2 : while ( !m_sCurrentPortion.isEmpty() );
513 :
514 : // no more portions
515 2 : return false;
516 : }
517 :
518 4 : OUString HangulHanjaConversion_Impl::GetCurrentUnit() const
519 : {
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!" );
526 :
527 4 : OUString sCurrentUnit = m_sCurrentPortion.copy( m_nCurrentStartIndex, m_nCurrentEndIndex - m_nCurrentStartIndex );
528 4 : return sCurrentUnit;
529 : }
530 :
531 2 : bool HangulHanjaConversion_Impl::ContinueConversion( bool _bRepeatCurrentUnit )
532 : {
533 6 : while ( implNextConvertible( _bRepeatCurrentUnit ) )
534 : {
535 2 : OUString sCurrentUnit( GetCurrentUnit() );
536 :
537 : // do we need to ignore it?
538 2 : const bool bAlwaysIgnoreThis = m_sIgnoreList.end() != m_sIgnoreList.find( sCurrentUnit );
539 :
540 : // do we need to change it?
541 2 : StringMap::const_iterator aChangeListPos = m_aChangeList.find( sCurrentUnit );
542 2 : const bool bAlwaysChangeThis = m_aChangeList.end() != aChangeListPos;
543 :
544 : // do we automatically change this?
545 2 : const bool bAutoChange = m_bAutoReplaceUnique && m_aCurrentSuggestions.getLength() == 1;
546 :
547 2 : if (!m_bIsInteractive)
548 : {
549 : // silent conversion (e.g. for simplified/traditional Chinese)...
550 2 : if(m_aCurrentSuggestions.getLength()>0)
551 2 : implChange( m_aCurrentSuggestions.getConstArray()[0] );
552 : }
553 0 : else if (bAutoChange)
554 : {
555 0 : implChange( m_aCurrentSuggestions.getConstArray()[0] );
556 : }
557 0 : else if ( bAlwaysChangeThis )
558 : {
559 0 : implChange( aChangeListPos->second );
560 : }
561 0 : else if ( !bAlwaysIgnoreThis )
562 : {
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 0 : m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
566 :
567 : DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
568 0 : if( m_pConversionDialog )
569 0 : m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
570 :
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 0 : return false;
574 : }
575 2 : }
576 :
577 2 : return true;
578 : }
579 :
580 0 : bool HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection )
581 : {
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.
586 :
587 0 : bool bSuccess = true;
588 :
589 0 : if (m_eConvType == HHC::eConvHangulHanja)
590 : {
591 0 : bSuccess = false;
592 : try
593 : {
594 : // get the break iterator service
595 0 : Reference< XBreakIterator > xBreakIter = i18n::BreakIterator::create( m_xContext );
596 0 : sal_Int32 nNextAsianScript = xBreakIter->beginOfScript( m_sCurrentPortion, m_nCurrentStartIndex, com::sun::star::i18n::ScriptType::ASIAN );
597 0 : if ( -1 == nNextAsianScript )
598 0 : nNextAsianScript = xBreakIter->nextScript( m_sCurrentPortion, m_nCurrentStartIndex, com::sun::star::i18n::ScriptType::ASIAN );
599 0 : if ( ( nNextAsianScript >= m_nCurrentStartIndex ) && ( nNextAsianScript < m_sCurrentPortion.getLength() ) )
600 : { // found asian text
601 :
602 : // determine if it's Hangul
603 0 : CharClass aCharClassificaton( m_xContext, LanguageTag( m_aSourceLocale) );
604 0 : sal_Int16 nScript = aCharClassificaton.getScript( m_sCurrentPortion, sal::static_int_cast< sal_uInt16 >(nNextAsianScript) );
605 0 : if ( ( UnicodeScript_kHangulJamo == nScript )
606 0 : || ( UnicodeScript_kHangulCompatibilityJamo == nScript )
607 0 : || ( UnicodeScript_kHangulSyllable == nScript )
608 : )
609 : {
610 0 : rDirection = HHC::eHangulToHanja;
611 : }
612 : else
613 : {
614 0 : rDirection = HHC::eHanjaToHangul;
615 : }
616 :
617 0 : bSuccess = true;
618 0 : }
619 : }
620 0 : catch( const Exception& )
621 : {
622 : OSL_FAIL( "HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion: caught an exception!" );
623 : }
624 : }
625 :
626 0 : return bSuccess;
627 : }
628 :
629 4 : void HangulHanjaConversion_Impl::DoDocumentConversion( )
630 : {
631 : // clear the change-all list - it's to be re-initialized for every single document
632 : {
633 4 : StringMap aEmpty;
634 4 : m_aChangeList.swap( aEmpty );
635 : }
636 :
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 4 : if ( !implRetrieveNextPortion() )
640 : {
641 : DBG_WARNING( "HangulHanjaConversion_Impl::DoDocumentConversion: why did you call me if you do have nothing to convert?" );
642 : // nothing to do
643 2 : return;
644 : }
645 2 : if( m_eConvType == HHC::eConvHangulHanja )
646 : {
647 : //init conversion direction from saved value
648 0 : HHC::ConversionDirection eDirection = HHC::eHangulToHanja;
649 0 : if(!implGetConversionDirectionForCurrentPortion( eDirection ))
650 : // something went wrong, has already been asserted
651 0 : return;
652 :
653 0 : if (HangulHanjaConversion::IsUseSavedConversionDirectionState())
654 : {
655 0 : m_ePrimaryConversionDirection = m_pAntiImpl->m_ePrimaryConversionDirectionSave;
656 0 : m_bTryBothDirections = m_pAntiImpl->m_bTryBothDirectionsSave;
657 0 : if( m_bTryBothDirections )
658 0 : m_eCurrentConversionDirection = eDirection;
659 : else
660 0 : m_eCurrentConversionDirection = m_ePrimaryConversionDirection;
661 : }
662 : else
663 : {
664 0 : m_ePrimaryConversionDirection = eDirection;
665 0 : m_eCurrentConversionDirection = eDirection;
666 : }
667 : }
668 :
669 2 : if (m_bIsInteractive && m_eConvType == HHC::eConvHangulHanja)
670 : {
671 : //always open dialog if at least having a hangul or hanja text portion
672 0 : createDialog();
673 0 : if(HangulHanjaConversion::IsUseSavedConversionDirectionState())
674 0 : ContinueConversion( false );
675 : else
676 0 : implUpdateData();
677 0 : m_pConversionDialog->Execute();
678 0 : DELETEZ( m_pConversionDialog );
679 : }
680 : else
681 : {
682 : #ifdef DBG_UTIL
683 : const bool bCompletelyDone =
684 : #endif
685 2 : ContinueConversion( false );
686 : DBG_ASSERT( bCompletelyDone, "HangulHanjaConversion_Impl::DoDocumentConversion: ContinueConversion should have returned true here!" );
687 : }
688 : }
689 :
690 0 : void HangulHanjaConversion_Impl::implProceed( bool _bRepeatCurrentUnit )
691 : {
692 0 : 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 0 : if ( m_pConversionDialog )
696 0 : m_pConversionDialog->EndDialog( RET_OK );
697 : }
698 0 : }
699 :
700 2 : void HangulHanjaConversion_Impl::implChange( const OUString& _rChangeInto )
701 : {
702 2 : if( _rChangeInto.isEmpty() )
703 2 : return;
704 :
705 : // translate the conversion format into a replacement action
706 : // this translation depends on whether we have a Hangul original, or a Hanja original
707 :
708 2 : HHC::ReplacementAction eAction( HHC::eExchange );
709 :
710 2 : if (m_eConvType == HHC::eConvHangulHanja)
711 : {
712 : // is the original we're about to change in Hangul?
713 0 : const bool bOriginalIsHangul = HHC::eHangulToHanja == m_eCurrentConversionDirection;
714 :
715 0 : switch ( m_eConversionFormat )
716 : {
717 0 : case HHC::eSimpleConversion: eAction = HHC::eExchange; break;
718 0 : case HHC::eHangulBracketed: eAction = bOriginalIsHangul ? HHC::eOriginalBracketed : HHC::eReplacementBracketed; break;
719 0 : case HHC::eHanjaBracketed: eAction = bOriginalIsHangul ? HHC::eReplacementBracketed : HHC::eOriginalBracketed; break;
720 0 : case HHC::eRubyHanjaAbove: eAction = bOriginalIsHangul ? HHC::eReplacementAbove : HHC::eOriginalAbove; break;
721 0 : case HHC::eRubyHanjaBelow: eAction = bOriginalIsHangul ? HHC::eReplacementBelow : HHC::eOriginalBelow; break;
722 0 : case HHC::eRubyHangulAbove: eAction = bOriginalIsHangul ? HHC::eOriginalAbove : HHC::eReplacementAbove; break;
723 0 : case HHC::eRubyHangulBelow: eAction = bOriginalIsHangul ? HHC::eOriginalBelow : HHC::eReplacementBelow; break;
724 : default:
725 : OSL_FAIL( "HangulHanjaConversion_Impl::implChange: invalid/unexpected conversion format!" );
726 : }
727 : }
728 :
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!" );
733 :
734 2 : sal_Int32 nStartIndex = m_nCurrentStartIndex - m_nReplacementBaseIndex;
735 2 : sal_Int32 nEndIndex = m_nCurrentEndIndex - m_nReplacementBaseIndex;
736 :
737 : //remind this decision
738 2 : m_aRecentlyUsedList[ GetCurrentUnit() ] = _rChangeInto;
739 :
740 2 : LanguageType *pNewUnitLang = 0;
741 2 : LanguageType nNewUnitLang = LANGUAGE_NONE;
742 2 : if (m_eConvType == HHC::eConvSimplifiedTraditional)
743 : {
744 : // check if language needs to be changed
745 3 : if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL &&
746 1 : !HangulHanjaConversion::IsTraditional( m_nCurrentPortionLang ))
747 1 : nNewUnitLang = LANGUAGE_CHINESE_TRADITIONAL;
748 2 : else if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED &&
749 1 : !HangulHanjaConversion::IsSimplified( m_nCurrentPortionLang ))
750 0 : nNewUnitLang = LANGUAGE_CHINESE_SIMPLIFIED;
751 2 : if (nNewUnitLang != LANGUAGE_NONE)
752 1 : pNewUnitLang = &nNewUnitLang;
753 : }
754 :
755 : // according to FT we should not (yet) bother about Hangul/Hanja conversion here
756 : //
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 2 : Sequence< sal_Int32 > aOffsets;
761 4 : Reference< XExtendedTextConversion > xExtConverter( m_xConverter, UNO_QUERY );
762 2 : if (m_eConvType == HHC::eConvSimplifiedTraditional && xExtConverter.is())
763 : {
764 : try
765 : {
766 2 : xExtConverter->getConversionWithOffset(
767 : m_sCurrentPortion,
768 : m_nCurrentStartIndex,
769 : m_nCurrentEndIndex - m_nCurrentStartIndex,
770 : m_aSourceLocale,
771 : m_nCurrentConversionType,
772 : m_nCurrentConversionOption,
773 : aOffsets
774 2 : );
775 : }
776 0 : catch( const Exception& )
777 : {
778 : OSL_FAIL( "HangulHanjaConversion_Impl::implChange: caught unexpected exception!" );
779 0 : aOffsets.realloc(0);
780 : }
781 : }
782 :
783 : // do the replacement
784 : m_pAntiImpl->ReplaceUnit( nStartIndex, nEndIndex, m_sCurrentPortion,
785 2 : _rChangeInto, aOffsets, eAction, pNewUnitLang );
786 :
787 :
788 : // adjust the replacement base
789 4 : m_nReplacementBaseIndex = m_nCurrentEndIndex;
790 : }
791 :
792 4 : void HangulHanjaConversion_Impl::implReadOptionsFromConfiguration()
793 : {
794 4 : SvtLinguConfig aLngCfg;
795 4 : aLngCfg.GetProperty( UPH_IS_IGNORE_POST_POSITIONAL_WORD ) >>= m_bIgnorePostPositionalWord;
796 4 : aLngCfg.GetProperty( UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST ) >>= m_bShowRecentlyUsedFirst;
797 4 : aLngCfg.GetProperty( UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES ) >>= m_bAutoReplaceUnique;
798 4 : }
799 :
800 0 : void HangulHanjaConversion_Impl::implUpdateData()
801 : {
802 0 : implReadOptionsFromConfiguration();
803 0 : implUpdateSuggestions();
804 :
805 0 : if(m_pConversionDialog)
806 : {
807 0 : OUString sCurrentUnit( GetCurrentUnit() );
808 :
809 0 : m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
810 0 : m_pConversionDialog->FocusSuggestion();
811 : }
812 :
813 0 : m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
814 0 : }
815 :
816 0 : IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnOptionsChanged)
817 : {
818 : //options and dictionaries might have been changed
819 : //-> update our internal settings and the dialog
820 0 : implUpdateData();
821 :
822 0 : return 0L;
823 : }
824 :
825 0 : IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnIgnore)
826 : {
827 : // simply ignore, and proceed
828 0 : implProceed( false );
829 0 : return 0L;
830 : }
831 :
832 0 : IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnIgnoreAll)
833 : {
834 : DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnIgnoreAll: no dialog! How this?" );
835 :
836 0 : if ( m_pConversionDialog )
837 : {
838 0 : 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" );
841 :
842 : // put into the "ignore all" list
843 0 : m_sIgnoreList.insert( sCurrentUnit );
844 :
845 : // and proceed
846 0 : implProceed( false );
847 : }
848 :
849 0 : return 0L;
850 : }
851 :
852 0 : IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChange)
853 : {
854 : // change
855 : DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
856 0 : if( m_pConversionDialog )
857 0 : implChange( m_pConversionDialog->GetCurrentSuggestion( ) );
858 : // and proceed
859 0 : implProceed( false );
860 :
861 0 : return 0L;
862 : }
863 :
864 0 : IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnChangeAll)
865 : {
866 : DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnChangeAll: no dialog! How this?" );
867 0 : if ( m_pConversionDialog )
868 : {
869 0 : OUString sCurrentUnit( m_pConversionDialog->GetCurrentString() );
870 0 : OUString sChangeInto( m_pConversionDialog->GetCurrentSuggestion( ) );
871 :
872 0 : if( !sChangeInto.isEmpty() )
873 : {
874 : // change the current occurrence
875 0 : implChange( sChangeInto );
876 :
877 : // put into the "change all" list
878 0 : m_aChangeList.insert( StringMap::value_type( sCurrentUnit, sChangeInto ) );
879 : }
880 :
881 : // and proceed
882 0 : implProceed( false );
883 : }
884 :
885 0 : return 0L;
886 : }
887 :
888 0 : IMPL_LINK( HangulHanjaConversion_Impl, OnByCharClicked, CheckBox*, _pBox )
889 : {
890 0 : m_bByCharacter = _pBox->IsChecked();
891 :
892 : // continue conversion, without advancing to the next unit, but instead continuing with the current unit
893 0 : implProceed( true );
894 0 : return 0L;
895 : }
896 :
897 0 : IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnConversionTypeChanged)
898 : {
899 : DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
900 0 : if( m_pConversionDialog )
901 0 : m_eConversionFormat = m_pConversionDialog->GetConversionFormat( );
902 0 : return 0L;
903 : }
904 :
905 0 : IMPL_LINK_NOARG(HangulHanjaConversion_Impl, OnFind)
906 : {
907 : DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnFind: where did this come from?" );
908 0 : if ( m_pConversionDialog )
909 : {
910 : try
911 : {
912 0 : OUString sNewOriginal( m_pConversionDialog->GetCurrentSuggestion( ) );
913 0 : Sequence< OUString > aSuggestions;
914 :
915 : DBG_ASSERT( m_xConverter.is(), "HangulHanjaConversion_Impl::OnFind: no converter!" );
916 0 : TextConversionResult aToHanja = m_xConverter->getConversions(
917 : sNewOriginal,
918 : 0, sNewOriginal.getLength(),
919 : m_aSourceLocale,
920 : TextConversionType::TO_HANJA,
921 : TextConversionOption::NONE
922 0 : );
923 0 : TextConversionResult aToHangul = m_xConverter->getConversions(
924 : sNewOriginal,
925 : 0, sNewOriginal.getLength(),
926 : m_aSourceLocale,
927 : TextConversionType::TO_HANGUL,
928 : TextConversionOption::NONE
929 0 : );
930 :
931 0 : bool bHaveToHanja = ( aToHanja.Boundary.startPos < aToHanja.Boundary.endPos );
932 0 : bool bHaveToHangul = ( aToHangul.Boundary.startPos < aToHangul.Boundary.endPos );
933 :
934 0 : TextConversionResult* pResult = NULL;
935 0 : if ( bHaveToHanja && bHaveToHangul )
936 : { // it found convertibles in both directions -> use the first
937 0 : if ( aToHangul.Boundary.startPos < aToHanja.Boundary.startPos )
938 0 : pResult = &aToHangul;
939 : else
940 0 : pResult = &aToHanja;
941 : }
942 0 : else if ( bHaveToHanja )
943 : { // only found toHanja
944 0 : pResult = &aToHanja;
945 : }
946 : else
947 : { // only found toHangul
948 0 : pResult = &aToHangul;
949 : }
950 0 : if ( pResult )
951 0 : aSuggestions = pResult->Candidates;
952 :
953 0 : m_pConversionDialog->SetCurrentString( sNewOriginal, aSuggestions, false );
954 0 : m_pConversionDialog->FocusSuggestion();
955 : }
956 0 : catch( const Exception& )
957 : {
958 : OSL_FAIL( "HangulHanjaConversion_Impl::OnFind: caught an exception!" );
959 : }
960 : }
961 0 : return 0L;
962 : }
963 :
964 : bool HangulHanjaConversion::m_bUseSavedValues = false;
965 : bool HangulHanjaConversion::m_bTryBothDirectionsSave = false;
966 : HHC::ConversionDirection HangulHanjaConversion::m_ePrimaryConversionDirectionSave = HHC::eHangulToHanja;
967 :
968 4 : 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 4 : :m_pImpl( new HangulHanjaConversion_Impl( _pUIParent, rxContext, _rSourceLocale, _rTargetLocale, _pTargetFont, _nOptions, _bIsInteractive, this ) )
974 : {
975 4 : }
976 :
977 4 : HangulHanjaConversion::~HangulHanjaConversion( )
978 : {
979 4 : }
980 :
981 0 : void HangulHanjaConversion::SetUseSavedConversionDirectionState( bool bVal )
982 : {
983 0 : m_bUseSavedValues = bVal;
984 0 : }
985 :
986 0 : bool HangulHanjaConversion::IsUseSavedConversionDirectionState()
987 : {
988 0 : return m_bUseSavedValues;
989 : }
990 :
991 10 : LanguageType HangulHanjaConversion::GetSourceLanguage( ) const
992 : {
993 10 : return m_pImpl->GetSourceLang();
994 : }
995 :
996 15 : LanguageType HangulHanjaConversion::GetTargetLanguage( ) const
997 : {
998 15 : return m_pImpl->GetTargetLang();
999 : }
1000 :
1001 10 : const vcl::Font * HangulHanjaConversion::GetTargetFont( ) const
1002 : {
1003 10 : return m_pImpl->GetTargetFont();
1004 : }
1005 :
1006 0 : sal_Int32 HangulHanjaConversion::GetConversionOptions( ) const
1007 : {
1008 0 : return m_pImpl->GetConvOptions();
1009 : }
1010 :
1011 0 : bool HangulHanjaConversion::IsInteractive( ) const
1012 : {
1013 0 : return m_pImpl->IsInteractive();
1014 : }
1015 :
1016 4 : void HangulHanjaConversion::ConvertDocument()
1017 : {
1018 4 : if ( m_pImpl->IsValid() )
1019 4 : m_pImpl->DoDocumentConversion( );
1020 4 : }
1021 :
1022 444 : }
1023 :
1024 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|