blob: 9d73cf6ef8298466b56577ddb7107fa99665e7cc [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_editeng.hxx"
#include <editeng/hangulhanja.hxx>
#include <vcl/msgbox.hxx>
#include <vcl/button.hxx>
#include <unotools/lingucfg.hxx>
#include <unotools/linguprops.hxx>
#include <set>
#include <map>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/i18n/XBreakIterator.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/UnicodeScript.hpp>
#include <com/sun/star/i18n/XTextConversion.hpp>
#include <com/sun/star/i18n/XExtendedTextConversion.hpp>
#include <com/sun/star/i18n/TextConversionType.hpp>
#include <com/sun/star/i18n/TextConversionOption.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <vcl/stdtext.hxx>
#include <unotools/charclass.hxx>
#include <editeng/edtdlg.hxx>
#include <editeng/editrids.hrc>
#include <editeng/unolingu.hxx>
#define HHC HangulHanjaConversion
//.............................................................................
namespace editeng
{
//.............................................................................
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::i18n;
using namespace ::com::sun::star::i18n::TextConversionOption;
using namespace ::com::sun::star::i18n::TextConversionType;
using namespace ::com::sun::star::lang;
/*
using HangulHanjaConversion::ReplacementAction;
using HangulHanjaConversion::eExchange;
using HangulHanjaConversion::eReplacementBracketed;
using HangulHanjaConversion::eOriginalBracketed;
using HangulHanjaConversion::eReplacementAbove;
using HangulHanjaConversion::eOriginalAbove;
using HangulHanjaConversion::eReplacementBelow;
using HangulHanjaConversion::eOriginalBelow;
using HangulHanjaConversion::eHangulToHanja;
using HangulHanjaConversion::eHanjaToHangul;
using HangulHanjaConversion::eSimpleConversion;
using HangulHanjaConversion::eHangulBracketed;
using HangulHanjaConversion::eHanjaBracketed;
using HangulHanjaConversion::eRubyHanjaAbove;
using HangulHanjaConversion::eRubyHanjaBelow;
using HangulHanjaConversion::eRubyHangulAbove;
using HangulHanjaConversion::eRubyHangulBelow;
using ::com::sun::star::i18n::TextConversionType::TO_HANJA;
using ::com::sun::star::i18n::TextConversionType::TO_HANGUL;
using ::com::sun::star::i18n::TextConversionOption::CHARACTER_BY_CHARACTER;
using ::com::sun::star::i18n::TextConversionOption::NONE;
*/
//=========================================================================
//= HangulHanjaConversion_Impl
//=========================================================================
//using HangulHanjaConversion::ConversionFormat;
class HangulHanjaConversion_Impl
{
private:
typedef ::std::set< ::rtl::OUString, ::std::less< ::rtl::OUString > > StringBag;
typedef ::std::map< ::rtl::OUString, ::rtl::OUString, ::std::less< ::rtl::OUString > > StringMap;
private:
StringBag m_sIgnoreList;
StringMap m_aChangeList;
static StringMap m_aRecentlyUsedList;
// general
AbstractHangulHanjaConversionDialog* //CHINA001 HangulHanjaConversionDialog*
m_pConversionDialog; // the dialog to display for user interaction
Window* m_pUIParent; // the parent window for any UI we raise
Reference< XMultiServiceFactory >
m_xORB; // the service factory to use
Reference< XTextConversion >
m_xConverter; // the text conversion service
Locale m_aSourceLocale; // the locale we're working with
// additions for Chinese simplified / traditional conversion
HHC::ConversionType m_eConvType; // conversion type (Hangul/Hanja, simplified/traditional Chinese,...)
LanguageType m_nSourceLang; // just a 'copy' of m_aSourceLocale in order in order to
// save the applications from always converting to this
// type in their implementations
LanguageType m_nTargetLang; // target language of new replacement text
const Font* m_pTargetFont; // target font of new replacement text
sal_Int32 m_nConvOptions; // text conversion options (as used by 'getConversions')
sal_Bool m_bIsInteractive; // specifies if the conversion requires user interaction
// (and likeley a specialised dialog) or if it is to run
// automatically without any user interaction.
// True for Hangul / Hanja conversion
// False for Chinese simlified / traditional conversion
HangulHanjaConversion* m_pAntiImpl; // our "anti-impl" instance
// options
sal_Bool m_bByCharacter; // are we in "by character" mode currently?
HHC::ConversionFormat m_eConversionFormat; // the current format for the conversion
HHC::ConversionDirection m_ePrimaryConversionDirection; // the primary conversion direction
HHC::ConversionDirection m_eCurrentConversionDirection; // the primary conversion direction
//options from Hangul/Hanja Options dialog (also saved to configuration)
bool m_bIgnorePostPositionalWord;
bool m_bShowRecentlyUsedFirst;
bool m_bAutoReplaceUnique;
// state
::rtl::OUString m_sCurrentPortion; // the text which we are currently working on
LanguageType m_nCurrentPortionLang; // language of m_sCurrentPortion found
sal_Int32 m_nCurrentStartIndex; // the start index within m_sCurrentPortion of the current convertible portion
sal_Int32 m_nCurrentEndIndex; // the end index (excluding) within m_sCurrentPortion of the current convertible portion
sal_Int32 m_nReplacementBaseIndex;// index which ReplaceUnit-calls need to be relative to
sal_Int32 m_nCurrentConversionOption;
sal_Int16 m_nCurrentConversionType;
Sequence< ::rtl::OUString >
m_aCurrentSuggestions; // the suggestions for the current unit
// (means for the text [m_nCurrentStartIndex, m_nCurrentEndIndex) in m_sCurrentPortion)
sal_Bool m_bTryBothDirections; // specifies if other conversion directions should be tried when looking for convertible characters
public:
HangulHanjaConversion_Impl(
Window* _pUIParent,
const Reference< XMultiServiceFactory >& _rxORB,
const Locale& _rSourceLocale,
const Locale& _rTargetLocale,
const Font* _pTargetFont,
sal_Int32 _nConvOptions,
sal_Bool _bIsInteractive,
HangulHanjaConversion* _pAntiImpl );
public:
static void SetUseSavedConversionDirectionState( sal_Bool bVal );
void DoDocumentConversion( );
inline sal_Bool IsByCharacter( ) const { return m_bByCharacter; }
inline sal_Bool IsValid() const { return m_xConverter.is(); }
inline LanguageType GetSourceLang() const { return m_nSourceLang; }
inline LanguageType GetTargetLang() const { return m_nTargetLang; }
inline const Font * GetTargetFont() const { return m_pTargetFont; }
inline sal_Int32 GetConvOptions() const { return m_nConvOptions; }
inline sal_Bool IsInteractive() const { return m_bIsInteractive; }
protected:
void createDialog();
/** continue with the conversion, return <TRUE/> if and only if the complete conversion is done
@param _bRepeatCurrentUnit
if <TRUE/>, an implNextConvertible will be called initially to advance to the next convertible.
if <FALSE/>, the method will initially work with the current convertible unit
*/
sal_Bool ContinueConversion( bool _bRepeatCurrentUnit );
private:
DECL_LINK( OnOptionsChanged, void* );
DECL_LINK( OnIgnore, void* );
DECL_LINK( OnIgnoreAll, void* );
DECL_LINK( OnChange, void* );
DECL_LINK( OnChangeAll, void* );
DECL_LINK( OnByCharClicked, CheckBox* );
DECL_LINK( OnConversionTypeChanged, void* );
DECL_LINK( OnFind, void* );
/** proceed, after the current convertible has been handled
<p><b>Attention:</b>
When returning from this method, the dialog may have been deleted!</p>
@param _bRepeatCurrentUnit
will be passed to the <member>ContinueConversion</member> call
*/
void implProceed( bool _bRepeatCurrentUnit );
// change the current convertible, and do _not_ proceed
void implChange( const ::rtl::OUString& _rChangeInto );
/** find the next convertible piece of text, with possibly advancing to the next portion
@see HangulHanjaConversion::GetNextPortion
*/
sal_Bool implNextConvertible( bool _bRepeatUnit );
/** find the next convertible unit within the current portion
@param _bRepeatUnit
if <TRUE/>, the search will start at the beginning of the current unit,
if <FALSE/>, it will start at the end of the current unit
*/
bool implNextConvertibleUnit( const sal_Int32 _nStartAt );
/** retrieves the next portion, with setting the index members properly
@return
<TRUE/> if and only if there is a next portion
*/
bool implRetrieveNextPortion( );
/** determine the ConversionDirection for m_sCurrentPortion
@return
<FALSE/> if and only if something went wrong
*/
bool implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection );
/** member m_aCurrentSuggestions and m_nCurrentEndIndex are updated according to the other settings and current dictionaries
if _bAllowSearchNextConvertibleText is true _nStartAt is used as starting point to search the next
convertible text portion. This may result in changing of the member m_nCurrentStartIndex additionally.
@return
<TRUE/> if Suggestions were found
*/
bool implUpdateSuggestions( const bool _bAllowSearchNextConvertibleText=false, const sal_Int32 _nStartAt=-1 );
/** reads the options from Hangul/Hanja Options dialog that are saved to configuration
*/
void implReadOptionsFromConfiguration();
/** get the string currently considered to be replaced or ignored
*/
::rtl::OUString GetCurrentUnit() const;
/** read options from configuration, update suggestion list and dialog content
*/
void implUpdateData();
/** get the conversion direction dependent from m_eConvType and m_eCurrentConversionDirection
in case of switching the direction is allowed this can be triggered with parameter bSwitchDirection
*/
sal_Int16 implGetConversionType( bool bSwitchDirection=false ) const;
};
//=========================================================================
//= HangulHanjaConversion_Impl
//=========================================================================
//-------------------------------------------------------------------------
// static member initialization
HangulHanjaConversion_Impl::StringMap HangulHanjaConversion_Impl::m_aRecentlyUsedList = HangulHanjaConversion_Impl::StringMap();
//-------------------------------------------------------------------------
HangulHanjaConversion_Impl::HangulHanjaConversion_Impl( Window* _pUIParent,
const Reference< XMultiServiceFactory >& _rxORB,
const Locale& _rSourceLocale,
const Locale& _rTargetLocale,
const Font* _pTargetFont,
sal_Int32 _nOptions,
sal_Bool _bIsInteractive,
HangulHanjaConversion* _pAntiImpl )
: m_pConversionDialog( NULL )
, m_pUIParent( _pUIParent )
, m_xORB( _rxORB )
, m_aSourceLocale( _rSourceLocale )
, m_nSourceLang( SvxLocaleToLanguage( _rSourceLocale ) )
, m_nTargetLang( SvxLocaleToLanguage( _rTargetLocale ) )
, m_pTargetFont( _pTargetFont )
, m_bIsInteractive( _bIsInteractive )
, m_pAntiImpl( _pAntiImpl )
, m_nCurrentPortionLang( LANGUAGE_NONE )
, m_nCurrentStartIndex( 0 )
, m_nCurrentEndIndex( 0 )
, m_nReplacementBaseIndex( 0 )
, m_nCurrentConversionOption( TextConversionOption::NONE )
, m_nCurrentConversionType( -1 ) // not yet known
, m_bTryBothDirections( sal_True )
{
implReadOptionsFromConfiguration();
DBG_ASSERT( m_xORB.is(), "HangulHanjaConversion_Impl::HangulHanjaConversion_Impl: no ORB!" );
// determine conversion type
if (m_nSourceLang == LANGUAGE_KOREAN && m_nTargetLang == LANGUAGE_KOREAN)
m_eConvType = HHC::eConvHangulHanja;
else if ( (m_nSourceLang == LANGUAGE_CHINESE_TRADITIONAL && m_nTargetLang == LANGUAGE_CHINESE_SIMPLIFIED) ||
(m_nSourceLang == LANGUAGE_CHINESE_SIMPLIFIED && m_nTargetLang == LANGUAGE_CHINESE_TRADITIONAL) )
m_eConvType = HHC::eConvSimplifiedTraditional;
else
{
DBG_ERROR( "failed to determine conversion type from languages" );
}
// set remaining conversion parameters to their default values
m_nConvOptions = _nOptions;
m_bByCharacter = 0 != (_nOptions & CHARACTER_BY_CHARACTER);
m_eConversionFormat = HHC::eSimpleConversion;
m_ePrimaryConversionDirection = HHC::eHangulToHanja; // used for eConvHangulHanja
m_eCurrentConversionDirection = HHC::eHangulToHanja; // used for eConvHangulHanja
if ( m_xORB.is() )
{
::rtl::OUString sTextConversionService( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.TextConversion" ) );
m_xConverter = m_xConverter.query( m_xORB->createInstance( sTextConversionService ) );
if ( !m_xConverter.is() )
ShowServiceNotAvailableError( m_pUIParent, sTextConversionService, sal_True );
}
}
//-------------------------------------------------------------------------
void HangulHanjaConversion_Impl::createDialog()
{
DBG_ASSERT( m_bIsInteractive, "createDialog when the conversion should not be interactive?" );
if ( m_bIsInteractive && !m_pConversionDialog )
{
EditAbstractDialogFactory* pFact = EditAbstractDialogFactory::Create();
if(pFact)
{
m_pConversionDialog = pFact->CreateHangulHanjaConversionDialog(m_pUIParent, m_ePrimaryConversionDirection );
DBG_ASSERT(m_pConversionDialog, "Dialogdiet fail!");//CHINA001
m_pConversionDialog->EnableRubySupport( m_pAntiImpl->HasRubySupport() );
m_pConversionDialog->SetByCharacter( m_bByCharacter );
m_pConversionDialog->SetConversionFormat( m_eConversionFormat );
m_pConversionDialog->SetConversionDirectionState( m_bTryBothDirections, m_ePrimaryConversionDirection );
// the handlers
m_pConversionDialog->SetOptionsChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnOptionsChanged ) );
m_pConversionDialog->SetIgnoreHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnore ) );
m_pConversionDialog->SetIgnoreAllHdl( LINK( this, HangulHanjaConversion_Impl, OnIgnoreAll ) );
m_pConversionDialog->SetChangeHdl( LINK( this, HangulHanjaConversion_Impl, OnChange ) );
m_pConversionDialog->SetChangeAllHdl( LINK( this, HangulHanjaConversion_Impl, OnChangeAll ) );
m_pConversionDialog->SetClickByCharacterHdl( LINK( this, HangulHanjaConversion_Impl, OnByCharClicked ) );
m_pConversionDialog->SetConversionFormatChangedHdl( LINK( this, HangulHanjaConversion_Impl, OnConversionTypeChanged ) );
m_pConversionDialog->SetFindHdl( LINK( this, HangulHanjaConversion_Impl, OnFind ) );
}
}
}
//-------------------------------------------------------------------------
sal_Int16 HangulHanjaConversion_Impl::implGetConversionType( bool bSwitchDirection ) const
{
sal_Int16 nConversionType = -1;
if (m_eConvType == HHC::eConvHangulHanja)
nConversionType = HHC::eHangulToHanja == ( m_eCurrentConversionDirection && !bSwitchDirection ) ? TO_HANJA : TO_HANGUL;
else if (m_eConvType == HHC::eConvSimplifiedTraditional)
nConversionType = LANGUAGE_CHINESE_SIMPLIFIED == m_nTargetLang ? TO_SCHINESE : TO_TCHINESE;
DBG_ASSERT( nConversionType != -1, "unexpected conversion type" );
return nConversionType;
}
//-------------------------------------------------------------------------
bool HangulHanjaConversion_Impl::implUpdateSuggestions( bool _bAllowSearchNextConvertibleText, const sal_Int32 _nStartAt )
{
// parameters for the converter
sal_Int32 nStartSearch = m_nCurrentStartIndex;
if( _bAllowSearchNextConvertibleText )
nStartSearch = _nStartAt;
sal_Int32 nLength = m_sCurrentPortion.getLength() - nStartSearch;
m_nCurrentConversionType = implGetConversionType();
m_nCurrentConversionOption = IsByCharacter() ? CHARACTER_BY_CHARACTER : NONE;
if( m_bIgnorePostPositionalWord )
m_nCurrentConversionOption = m_nCurrentConversionOption | IGNORE_POST_POSITIONAL_WORD;
// no need to check both directions for chinese conversion (saves time)
if (m_eConvType == HHC::eConvSimplifiedTraditional)
m_bTryBothDirections = sal_False;
sal_Bool bFoundAny = sal_True;
try
{
TextConversionResult aResult = m_xConverter->getConversions(
m_sCurrentPortion,
nStartSearch,
nLength,
m_aSourceLocale,
m_nCurrentConversionType,
m_nCurrentConversionOption
);
sal_Bool bFoundPrimary = aResult.Boundary.startPos < aResult.Boundary.endPos;
bFoundAny = bFoundPrimary;
if ( m_bTryBothDirections )
{ // see if we find another convertible when assuming the other direction
TextConversionResult aSecondResult = m_xConverter->getConversions(
m_sCurrentPortion,
nStartSearch,
nLength,
m_aSourceLocale,
implGetConversionType( true ), // switched!
m_nCurrentConversionOption
);
if ( aSecondResult.Boundary.startPos < aSecondResult.Boundary.endPos )
{ // we indeed found such a convertible
// in case the first attempt (with the original conversion direction)
// didn't find anything
if ( !bFoundPrimary
// or if the second location is _before_ the first one
|| ( aSecondResult.Boundary.startPos < aResult.Boundary.startPos )
)
{
// then use the second finding
aResult = aSecondResult;
// our current conversion direction changed now
m_eCurrentConversionDirection = ( HHC::eHangulToHanja == m_eCurrentConversionDirection )
? HHC::eHanjaToHangul : HHC::eHangulToHanja;
bFoundAny = sal_True;
}
}
}
if( _bAllowSearchNextConvertibleText )
{
//this might change the current position
m_aCurrentSuggestions = aResult.Candidates;
m_nCurrentStartIndex = aResult.Boundary.startPos;
m_nCurrentEndIndex = aResult.Boundary.endPos;
}
else
{
//the change of starting position is not allowed
if( m_nCurrentStartIndex == aResult.Boundary.startPos
&& aResult.Boundary.endPos != aResult.Boundary.startPos )
{
m_aCurrentSuggestions = aResult.Candidates;
m_nCurrentEndIndex = aResult.Boundary.endPos;
}
else
{
m_aCurrentSuggestions.realloc( 0 );
if( m_sCurrentPortion.getLength() >= m_nCurrentStartIndex+1 )
m_nCurrentEndIndex = m_nCurrentStartIndex+1;
}
}
//put recently used string to front:
if( m_bShowRecentlyUsedFirst && m_aCurrentSuggestions.getLength()>1 )
{
::rtl::OUString sCurrentUnit( GetCurrentUnit() );
StringMap::const_iterator aRecentlyUsed = m_aRecentlyUsedList.find( sCurrentUnit );
bool bUsedBefore = aRecentlyUsed != m_aRecentlyUsedList.end();
if( bUsedBefore && m_aCurrentSuggestions[0] != aRecentlyUsed->second )
{
sal_Int32 nCount = m_aCurrentSuggestions.getLength();
Sequence< ::rtl::OUString > aTmp(nCount);
aTmp[0]=aRecentlyUsed->second;
sal_Int32 nDiff = 1;
for( sal_Int32 n=1; n<nCount; n++)//we had 0 already
{
if( nDiff && m_aCurrentSuggestions[n-nDiff]==aRecentlyUsed->second )
nDiff=0;
aTmp[n]=m_aCurrentSuggestions[n-nDiff];
}
m_aCurrentSuggestions = aTmp;
}
}
}
catch( const Exception& )
{
DBG_ERROR( "HangulHanjaConversion_Impl::implNextConvertibleUnit: caught an exception!" );
//!!! at least we want to move on in the text in order
//!!! to avoid an endless loop...
return false;
}
return bFoundAny;
}
//-------------------------------------------------------------------------
bool HangulHanjaConversion_Impl::implNextConvertibleUnit( const sal_Int32 _nStartAt )
{
m_aCurrentSuggestions.realloc( 0 );
// ask the TextConversion service for the next convertible piece of text
// get current values from dialog
if( m_eConvType == HHC::eConvHangulHanja && m_pConversionDialog )
{
m_bTryBothDirections = m_pConversionDialog->GetUseBothDirections();
HHC::ConversionDirection eDialogDirection = HHC::eHangulToHanja;
eDialogDirection = m_pConversionDialog->GetDirection( eDialogDirection );
if( !m_bTryBothDirections && eDialogDirection != m_eCurrentConversionDirection )
{
m_eCurrentConversionDirection = eDialogDirection;
}
// save curently used value for possible later use
m_pAntiImpl->m_bTryBothDirectionsSave = m_bTryBothDirections;
m_pAntiImpl->m_ePrimaryConversionDirectionSave = m_eCurrentConversionDirection;
}
bool bFoundAny = implUpdateSuggestions( true, _nStartAt );
return bFoundAny &&
(m_nCurrentStartIndex < m_sCurrentPortion.getLength());
}
//-------------------------------------------------------------------------
bool HangulHanjaConversion_Impl::implRetrieveNextPortion( )
{
sal_Bool bAllowImplicitChanges = m_eConvType == HHC::eConvSimplifiedTraditional;
m_sCurrentPortion = ::rtl::OUString();
m_nCurrentPortionLang = LANGUAGE_NONE;
m_pAntiImpl->GetNextPortion( m_sCurrentPortion, m_nCurrentPortionLang, bAllowImplicitChanges );
m_nReplacementBaseIndex = 0;
m_nCurrentStartIndex = m_nCurrentEndIndex = 0;
bool bRet = 0 != m_sCurrentPortion.getLength();
if (m_eConvType == HHC::eConvHangulHanja && m_bTryBothDirections)
implGetConversionDirectionForCurrentPortion( m_eCurrentConversionDirection );
return bRet;
}
//-------------------------------------------------------------------------
sal_Bool HangulHanjaConversion_Impl::implNextConvertible( bool _bRepeatUnit )
{
if ( _bRepeatUnit || ( m_nCurrentEndIndex < m_sCurrentPortion.getLength() ) )
{
if ( implNextConvertibleUnit(
_bRepeatUnit
? ( IsByCharacter() ? m_nCurrentStartIndex : m_nCurrentStartIndex )
: m_nCurrentEndIndex
) )
return sal_True;
}
// no convertible text in the current portion anymore
// -> advance to the next portion
do
{
// next portion
if ( implRetrieveNextPortion( ) )
{ // there is a next portion
// -> find the next convertible unit in the current portion
if ( implNextConvertibleUnit( 0 ) )
return sal_True;
}
}
while ( m_sCurrentPortion.getLength() );
// no more portions
return sal_False;
}
//-------------------------------------------------------------------------
::rtl::OUString HangulHanjaConversion_Impl::GetCurrentUnit() const
{
DBG_ASSERT( m_nCurrentStartIndex < m_sCurrentPortion.getLength(),
"HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
DBG_ASSERT( m_nCurrentEndIndex <= m_sCurrentPortion.getLength(),
"HangulHanjaConversion_Impl::GetCurrentUnit: invalid index into current portion!" );
DBG_ASSERT( m_nCurrentStartIndex <= m_nCurrentEndIndex,
"HangulHanjaConversion_Impl::GetCurrentUnit: invalid interval!" );
::rtl::OUString sCurrentUnit = m_sCurrentPortion.copy( m_nCurrentStartIndex, m_nCurrentEndIndex - m_nCurrentStartIndex );
return sCurrentUnit;
}
//-------------------------------------------------------------------------
sal_Bool HangulHanjaConversion_Impl::ContinueConversion( bool _bRepeatCurrentUnit )
{
sal_Bool bNeedUserInteraction = sal_False; // when we leave here, do we need user interaction?
sal_Bool bDocumentDone = sal_False; // did we already check the whole document?
while ( !bDocumentDone && !bNeedUserInteraction && implNextConvertible( _bRepeatCurrentUnit ) )
{
::rtl::OUString sCurrentUnit( GetCurrentUnit() );
// do we need to ignore it?
sal_Bool bAlwaysIgnoreThis = m_sIgnoreList.end() != m_sIgnoreList.find( sCurrentUnit );
// do we need to change it?
StringMap::const_iterator aChangeListPos = m_aChangeList.find( sCurrentUnit );
sal_Bool bAlwaysChangeThis = m_aChangeList.end() != aChangeListPos;
// do we automatically change this?
sal_Bool bAutoChange = m_bAutoReplaceUnique && m_aCurrentSuggestions.getLength() == 1;
if (!m_bIsInteractive)
{
// silent conversion (e.g. for simplified/traditional Chinese)...
if(m_aCurrentSuggestions.getLength()>0)
implChange( m_aCurrentSuggestions.getConstArray()[0] );
}
else if (bAutoChange)
{
implChange( m_aCurrentSuggestions.getConstArray()[0] );
}
else if ( bAlwaysChangeThis )
{
implChange( aChangeListPos->second );
}
else if ( !bAlwaysIgnoreThis )
{
// here we need to ask the user for what to do with the text
// for this, allow derivees to highlight the current text unit in a possible document view
m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
if( m_pConversionDialog )
m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
// do not look for the next convertible: We have to wait for the user to interactivly
// decide what happens with the current convertible
bNeedUserInteraction = sal_True;
}
}
/*
if ( bDocumentDone )
return sal_True; // we explicitly know that the complete document is done
else if ( bNeedUserInteraction )
return sal_False; // the doc is not done, we found a convertible, but need the user to decide
else
return sal_True; // we did not find a next convertible, so the document is implicitly done
*/
return bDocumentDone || !bNeedUserInteraction;
}
//-------------------------------------------------------------------------
bool HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion( HHC::ConversionDirection& rDirection )
{
// - For eConvHangulHanja the direction is determined by
// the first encountered Korean character.
// - For eConvSimplifiedTraditional the conversion direction
// is already specified by the source language.
bool bSuccess = true;
if (m_eConvType == HHC::eConvHangulHanja)
{
bSuccess = false;
try
{
// get the break iterator service
::rtl::OUString sBreakIteratorService( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.BreakIterator" ) );
Reference< XInterface > xBI( m_xORB->createInstance( ::rtl::OUString( sBreakIteratorService ) ) );
Reference< XBreakIterator > xBreakIter( xBI, UNO_QUERY );
if ( !xBreakIter.is() )
{
ShowServiceNotAvailableError( m_pUIParent, sBreakIteratorService, sal_True );
}
else
{
sal_Int32 nNextAsianScript = xBreakIter->beginOfScript( m_sCurrentPortion, m_nCurrentStartIndex, com::sun::star::i18n::ScriptType::ASIAN );
if ( -1 == nNextAsianScript )
nNextAsianScript = xBreakIter->nextScript( m_sCurrentPortion, m_nCurrentStartIndex, com::sun::star::i18n::ScriptType::ASIAN );
if ( ( nNextAsianScript >= m_nCurrentStartIndex ) && ( nNextAsianScript < m_sCurrentPortion.getLength() ) )
{ // found asian text
// determine if it's Hangul
CharClass aCharClassificaton( m_xORB, m_aSourceLocale );
sal_Int16 nScript = aCharClassificaton.getScript( m_sCurrentPortion, sal::static_int_cast< sal_uInt16 >(nNextAsianScript) );
if ( ( UnicodeScript_kHangulJamo == nScript )
|| ( UnicodeScript_kHangulCompatibilityJamo == nScript )
|| ( UnicodeScript_kHangulSyllable == nScript )
)
{
rDirection = HHC::eHangulToHanja;
}
else
{
rDirection = HHC::eHanjaToHangul;
}
bSuccess = true;
}
}
}
catch( const Exception& )
{
DBG_ERROR( "HangulHanjaConversion_Impl::implGetConversionDirectionForCurrentPortion: caught an exception!" );
}
}
return bSuccess;
}
//-------------------------------------------------------------------------
void HangulHanjaConversion_Impl::DoDocumentConversion( )
{
// clear the change-all list - it's to be re-initialized for every single document
{
StringMap aEmpty;
m_aChangeList.swap( aEmpty );
}
// first of all, we need to guess the direction of our conversion - it is determined by the first
// hangul or hanja character in the first text
if ( !implRetrieveNextPortion() )
{
DBG_WARNING( "HangulHanjaConversion_Impl::DoDocumentConversion: why did you call me if you do have nothing to convert?" );
// nothing to do
return;
}
if( m_eConvType == HHC::eConvHangulHanja )
{
//init conversion direction from saved value
HHC::ConversionDirection eDirection = HHC::eHangulToHanja;
if(!implGetConversionDirectionForCurrentPortion( eDirection ))
// something went wrong, has already been asserted
return;
if (m_pAntiImpl->IsUseSavedConversionDirectionState())
{
m_ePrimaryConversionDirection = m_pAntiImpl->m_ePrimaryConversionDirectionSave;
m_bTryBothDirections = m_pAntiImpl->m_bTryBothDirectionsSave;
if( m_bTryBothDirections )
m_eCurrentConversionDirection = eDirection;
else
m_eCurrentConversionDirection = m_ePrimaryConversionDirection;
}
else
{
m_ePrimaryConversionDirection = eDirection;
m_eCurrentConversionDirection = eDirection;
}
}
if (m_bIsInteractive && m_eConvType == HHC::eConvHangulHanja)
{
//always open dialog if at least having a hangul or hanja text portion
createDialog();
if(m_pAntiImpl->IsUseSavedConversionDirectionState())
ContinueConversion( sal_False );
else
implUpdateData();
m_pConversionDialog->Execute();
DELETEZ( m_pConversionDialog );
}
else
{
#ifdef DBG_UTIL
sal_Bool bCompletelyDone =
#endif
ContinueConversion( sal_False );
DBG_ASSERT( bCompletelyDone, "HangulHanjaConversion_Impl::DoDocumentConversion: ContinueConversion should have returned true here!" );
}
}
//-------------------------------------------------------------------------
void HangulHanjaConversion_Impl::implProceed( bool _bRepeatCurrentUnit )
{
if ( ContinueConversion( _bRepeatCurrentUnit ) )
{ // we're done with the whole document
DBG_ASSERT( !m_bIsInteractive || m_pConversionDialog, "HangulHanjaConversion_Impl::implProceed: we should not reach this here without dialog!" );
if ( m_pConversionDialog )
m_pConversionDialog->EndDialog( RET_OK );
}
}
//-------------------------------------------------------------------------
void HangulHanjaConversion_Impl::implChange( const ::rtl::OUString& _rChangeInto )
{
if( !_rChangeInto.getLength() )
return;
// translate the conversion format into a replacement action
// this translation depends on whether we have a Hangul original, or a Hanja original
HHC::ReplacementAction eAction( HHC::eExchange );
if (m_eConvType == HHC::eConvHangulHanja)
{
// is the original we're about to change in Hangul?
sal_Bool bOriginalIsHangul = HHC::eHangulToHanja == m_eCurrentConversionDirection;
switch ( m_eConversionFormat )
{
case HHC::eSimpleConversion: eAction = HHC::eExchange; break;
case HHC::eHangulBracketed: eAction = bOriginalIsHangul ? HHC::eOriginalBracketed : HHC::eReplacementBracketed; break;
case HHC::eHanjaBracketed: eAction = bOriginalIsHangul ? HHC::eReplacementBracketed : HHC::eOriginalBracketed; break;
case HHC::eRubyHanjaAbove: eAction = bOriginalIsHangul ? HHC::eReplacementAbove : HHC::eOriginalAbove; break;
case HHC::eRubyHanjaBelow: eAction = bOriginalIsHangul ? HHC::eReplacementBelow : HHC::eOriginalBelow; break;
case HHC::eRubyHangulAbove: eAction = bOriginalIsHangul ? HHC::eOriginalAbove : HHC::eReplacementAbove; break;
case HHC::eRubyHangulBelow: eAction = bOriginalIsHangul ? HHC::eOriginalBelow : HHC::eReplacementBelow; break;
default:
DBG_ERROR( "HangulHanjaConversion_Impl::implChange: invalid/unexpected conversion format!" );
}
}
// the proper indicies (the wrapper implementation needs indicies relative to the
// previous replacement)
DBG_ASSERT( ( m_nReplacementBaseIndex <= m_nCurrentStartIndex ) && ( m_nReplacementBaseIndex <= m_nCurrentEndIndex ),
"HangulHanjaConversion_Impl::implChange: invalid replacement base!" );
sal_Int32 nStartIndex = m_nCurrentStartIndex - m_nReplacementBaseIndex;
sal_Int32 nEndIndex = m_nCurrentEndIndex - m_nReplacementBaseIndex;
//remind this decision
m_aRecentlyUsedList[ GetCurrentUnit() ] = _rChangeInto;
LanguageType *pNewUnitLang = 0;
LanguageType nNewUnitLang = LANGUAGE_NONE;
if (m_eConvType == HHC::eConvSimplifiedTraditional)
{
// check if language needs to be changed
if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_TRADITIONAL &&
!m_pAntiImpl->IsTraditional( m_nCurrentPortionLang ))
nNewUnitLang = LANGUAGE_CHINESE_TRADITIONAL;
else if ( m_pAntiImpl->GetTargetLanguage() == LANGUAGE_CHINESE_SIMPLIFIED &&
!m_pAntiImpl->IsSimplified( m_nCurrentPortionLang ))
nNewUnitLang = LANGUAGE_CHINESE_SIMPLIFIED;
if (nNewUnitLang != LANGUAGE_NONE)
pNewUnitLang = &nNewUnitLang;
}
// according to FT we should not (yet) bother about Hangul/Hanja conversion here
//
// aOffsets is needed in ReplaceUnit below in order to to find out
// exactly which characters are really changed in order to keep as much
// from attributation for the text as possible.
Sequence< sal_Int32 > aOffsets;
Reference< XExtendedTextConversion > xExtConverter( m_xConverter, UNO_QUERY );
if (m_eConvType == HHC::eConvSimplifiedTraditional && xExtConverter.is())
{
try
{
::rtl::OUString aConvText = xExtConverter->getConversionWithOffset(
m_sCurrentPortion,
m_nCurrentStartIndex,
m_nCurrentEndIndex - m_nCurrentStartIndex,
m_aSourceLocale,
m_nCurrentConversionType,
m_nCurrentConversionOption,
aOffsets
);
}
catch( const Exception& )
{
DBG_ERROR( "HangulHanjaConversion_Impl::implChange: caught unexpected exception!" );
aOffsets.realloc(0);
}
}
// do the replacement
m_pAntiImpl->ReplaceUnit( nStartIndex, nEndIndex, m_sCurrentPortion,
_rChangeInto, aOffsets, eAction, pNewUnitLang );
// adjust the replacement base
m_nReplacementBaseIndex = m_nCurrentEndIndex;
}
//-------------------------------------------------------------------------
void HangulHanjaConversion_Impl::implReadOptionsFromConfiguration()
{
SvtLinguConfig aLngCfg;
aLngCfg.GetProperty( UPH_IS_IGNORE_POST_POSITIONAL_WORD ) >>= m_bIgnorePostPositionalWord;
aLngCfg.GetProperty( UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST ) >>= m_bShowRecentlyUsedFirst;
aLngCfg.GetProperty( UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES ) >>= m_bAutoReplaceUnique;
}
//-------------------------------------------------------------------------
void HangulHanjaConversion_Impl::implUpdateData()
{
implReadOptionsFromConfiguration();
implUpdateSuggestions();
if(m_pConversionDialog)
{
::rtl::OUString sCurrentUnit( GetCurrentUnit() );
m_pConversionDialog->SetCurrentString( sCurrentUnit, m_aCurrentSuggestions );
m_pConversionDialog->FocusSuggestion();
}
m_pAntiImpl->HandleNewUnit( m_nCurrentStartIndex - m_nReplacementBaseIndex, m_nCurrentEndIndex - m_nReplacementBaseIndex );
}
//-------------------------------------------------------------------------
IMPL_LINK( HangulHanjaConversion_Impl, OnOptionsChanged, void*, EMPTYARG )
{
//options and dictionaries might have been changed
//-> update our internal settings and the dialog
implUpdateData();
return 0L;
}
//-------------------------------------------------------------------------
IMPL_LINK( HangulHanjaConversion_Impl, OnIgnore, void*, EMPTYARG )
{
// simply ignore, and proceed
implProceed( sal_False );
return 0L;
}
//-------------------------------------------------------------------------
IMPL_LINK( HangulHanjaConversion_Impl, OnIgnoreAll, void*, EMPTYARG )
{
DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnIgnoreAll: no dialog! How this?" );
if ( m_pConversionDialog )
{
String sCurrentUnit = m_pConversionDialog->GetCurrentString();
DBG_ASSERT( m_sIgnoreList.end() == m_sIgnoreList.find( sCurrentUnit ),
"HangulHanjaConversion_Impl, OnIgnoreAll: shouldn't this have been ignored before" );
// put into the "ignore all" list
m_sIgnoreList.insert( sCurrentUnit );
// and proceed
implProceed( sal_False );
}
return 0L;
}
//-------------------------------------------------------------------------
IMPL_LINK( HangulHanjaConversion_Impl, OnChange, void*, EMPTYARG )
{
// change
DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
if( m_pConversionDialog )
implChange( m_pConversionDialog->GetCurrentSuggestion( ) );
// and proceed
implProceed( sal_False );
return 0L;
}
//-------------------------------------------------------------------------
IMPL_LINK( HangulHanjaConversion_Impl, OnChangeAll, void*, EMPTYARG )
{
DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnChangeAll: no dialog! How this?" );
if ( m_pConversionDialog )
{
::rtl::OUString sCurrentUnit( m_pConversionDialog->GetCurrentString() );
::rtl::OUString sChangeInto( m_pConversionDialog->GetCurrentSuggestion( ) );
if( sChangeInto.getLength() )
{
// change the current occurence
implChange( sChangeInto );
// put into the "change all" list
m_aChangeList.insert( StringMap::value_type( sCurrentUnit, sChangeInto ) );
}
// and proceed
implProceed( sal_False );
}
return 0L;
}
//-------------------------------------------------------------------------
IMPL_LINK( HangulHanjaConversion_Impl, OnByCharClicked, CheckBox*, _pBox )
{
m_bByCharacter = _pBox->IsChecked();
// continue conversion, without advancing to the next unit, but instead continuing with the current unit
implProceed( sal_True );
return 0L;
}
//-------------------------------------------------------------------------
IMPL_LINK( HangulHanjaConversion_Impl, OnConversionTypeChanged, void*, EMPTYARG )
{
DBG_ASSERT( m_pConversionDialog, "we should always have a dialog here!" );
if( m_pConversionDialog )
m_eConversionFormat = m_pConversionDialog->GetConversionFormat( );
return 0L;
}
//-------------------------------------------------------------------------
IMPL_LINK( HangulHanjaConversion_Impl, OnFind, void*, EMPTYARG )
{
DBG_ASSERT( m_pConversionDialog, "HangulHanjaConversion_Impl::OnFind: where did this come from?" );
if ( m_pConversionDialog )
{
try
{
::rtl::OUString sNewOriginal( m_pConversionDialog->GetCurrentSuggestion( ) );
Sequence< ::rtl::OUString > aSuggestions;
DBG_ASSERT( m_xConverter.is(), "HangulHanjaConversion_Impl::OnFind: no converter!" );
TextConversionResult aToHanja = m_xConverter->getConversions(
sNewOriginal,
0, sNewOriginal.getLength(),
m_aSourceLocale,
TextConversionType::TO_HANJA,
TextConversionOption::NONE
);
TextConversionResult aToHangul = m_xConverter->getConversions(
sNewOriginal,
0, sNewOriginal.getLength(),
m_aSourceLocale,
TextConversionType::TO_HANGUL,
TextConversionOption::NONE
);
bool bHaveToHanja = ( aToHanja.Boundary.startPos < aToHanja.Boundary.endPos );
bool bHaveToHangul = ( aToHangul.Boundary.startPos < aToHangul.Boundary.endPos );
TextConversionResult* pResult = NULL;
if ( bHaveToHanja && bHaveToHangul )
{ // it found convertibles in both directions -> use the first
if ( aToHangul.Boundary.startPos < aToHanja.Boundary.startPos )
pResult = &aToHangul;
else
pResult = &aToHanja;
}
else if ( bHaveToHanja )
{ // only found toHanja
pResult = &aToHanja;
}
else
{ // only found toHangul
pResult = &aToHangul;
}
if ( pResult )
aSuggestions = pResult->Candidates;
m_pConversionDialog->SetCurrentString( sNewOriginal, aSuggestions, false );
m_pConversionDialog->FocusSuggestion();
}
catch( const Exception& )
{
DBG_ERROR( "HangulHanjaConversion_Impl::OnFind: caught an exception!" );
}
}
return 0L;
}
//=========================================================================
//= HangulHanjaConversion
//=========================================================================
//-------------------------------------------------------------------------
// static member initialization
sal_Bool HangulHanjaConversion::m_bUseSavedValues = sal_False;
sal_Bool HangulHanjaConversion::m_bTryBothDirectionsSave = sal_False;
HHC::ConversionDirection HangulHanjaConversion::m_ePrimaryConversionDirectionSave = HHC::eHangulToHanja;
//-------------------------------------------------------------------------
HangulHanjaConversion::HangulHanjaConversion( Window* _pUIParent,
const Reference< XMultiServiceFactory >& _rxORB,
const Locale& _rSourceLocale, const Locale& _rTargetLocale,
const Font* _pTargetFont,
sal_Int32 _nOptions, sal_Bool _bIsInteractive)
:m_pImpl( new HangulHanjaConversion_Impl( _pUIParent, _rxORB, _rSourceLocale, _rTargetLocale, _pTargetFont, _nOptions, _bIsInteractive, this ) )
{
}
//-------------------------------------------------------------------------
HangulHanjaConversion::~HangulHanjaConversion( )
{
}
//-------------------------------------------------------------------------
void HangulHanjaConversion::SetUseSavedConversionDirectionState( sal_Bool bVal )
{
m_bUseSavedValues = bVal;
}
//-------------------------------------------------------------------------
sal_Bool HangulHanjaConversion::IsUseSavedConversionDirectionState()
{
return m_bUseSavedValues;
}
//-------------------------------------------------------------------------
LanguageType HangulHanjaConversion::GetSourceLanguage( ) const
{
return m_pImpl->GetSourceLang();
}
//-------------------------------------------------------------------------
LanguageType HangulHanjaConversion::GetTargetLanguage( ) const
{
return m_pImpl->GetTargetLang();
}
//-------------------------------------------------------------------------
const Font * HangulHanjaConversion::GetTargetFont( ) const
{
return m_pImpl->GetTargetFont();
}
//-------------------------------------------------------------------------
sal_Int32 HangulHanjaConversion::GetConversionOptions( ) const
{
return m_pImpl->GetConvOptions();
}
//-------------------------------------------------------------------------
sal_Bool HangulHanjaConversion::IsInteractive( ) const
{
return m_pImpl->IsInteractive();
}
//-------------------------------------------------------------------------
void HangulHanjaConversion::HandleNewUnit( const sal_Int32, const sal_Int32 )
{
// nothing to do, only derived classes need this.
}
//-------------------------------------------------------------------------
void HangulHanjaConversion::GetNextPortion( ::rtl::OUString&, LanguageType&, sal_Bool )
{
DBG_ERROR( "HangulHanjaConversion::GetNextPortion: to be overridden!" );
}
//-------------------------------------------------------------------------
void HangulHanjaConversion::ReplaceUnit(
const sal_Int32, const sal_Int32,
const ::rtl::OUString&,
const ::rtl::OUString&,
const ::com::sun::star::uno::Sequence< sal_Int32 > &,
ReplacementAction,
LanguageType * )
{
DBG_ERROR( "HangulHanjaConversion::ReplaceUnit: to be overridden!" );
}
//-------------------------------------------------------------------------
sal_Bool HangulHanjaConversion::HasRubySupport() const
{
DBG_ERROR( "HangulHanjaConversion::HasRubySupport: to be overridden!" );
return sal_False;
}
//-------------------------------------------------------------------------
void HangulHanjaConversion::ConvertDocument()
{
if ( m_pImpl->IsValid() )
m_pImpl->DoDocumentConversion( );
}
//.............................................................................
} // namespace svx
//.............................................................................