diff options
Diffstat (limited to 'vcl/unx/generic/app/i18n_status.cxx')
-rw-r--r-- | vcl/unx/generic/app/i18n_status.cxx | 735 |
1 files changed, 735 insertions, 0 deletions
diff --git a/vcl/unx/generic/app/i18n_status.cxx b/vcl/unx/generic/app/i18n_status.cxx new file mode 100644 index 000000000000..8cae9d12eedd --- /dev/null +++ b/vcl/unx/generic/app/i18n_status.cxx @@ -0,0 +1,735 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_vcl.hxx" + +#if OSL_DEBUG_LEVEL > 1 +#include <stdio.h> +#endif +#include <sal/alloca.h> + +#include <tools/prex.h> +#include <X11/Xlib.h> +#include <unx/XIM.h> +#include <tools/postx.h> + +#include <unx/salunx.h> +#include <unx/i18n_status.hxx> +#include <unx/i18n_ic.hxx> +#include <unx/saldisp.hxx> +#include <unx/salframe.h> +#include <unx/saldata.hxx> + +#include <vcl/wrkwin.hxx> +#include <vcl/fixed.hxx> +#include <vcl/menubtn.hxx> +#include <vcl/menu.hxx> +#include <vcl/svapp.hxx> +#include <vcl/sysdata.hxx> + +#include <svdata.hxx> + +using namespace vcl; + +namespace vcl { + +class StatusWindow : public WorkWindow +{ +protected: + StatusWindow( WinBits nWinBits ); +public: + virtual ~StatusWindow(); + + virtual void setPosition( SalFrame* ); + virtual void setText( const String & ) = 0; + virtual String getText() const = 0; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ) = 0; + virtual void toggle( bool bOn ) = 0; +}; + +} + +StatusWindow::StatusWindow( WinBits nWinBits ) : + WorkWindow( NULL, nWinBits ) +{ +} + +StatusWindow::~StatusWindow() {} + +void StatusWindow::setPosition( SalFrame* ) +{ +} + +// -------------------------------------------------------------------------- + +namespace vcl { + +class XIMStatusWindow : public StatusWindow +{ + FixedText m_aStatusText; + SalFrame* m_pLastParent; + Size m_aWindowSize; + bool m_bAnchoredAtRight; + // true if the right edge (instead of the left edge) should stay at a + // fixed position when re-sizing the window + + // for delayed showing + bool m_bDelayedShow; + I18NStatus::ShowReason m_eDelayedReason; + sal_uLong m_nDelayedEvent; + // for toggling + bool m_bOn; + + Point updatePosition(); + void layout(); + bool checkLastParent() const; + + DECL_LINK( DelayedShowHdl, void* ); +public: + XIMStatusWindow( bool bOn ); + virtual ~XIMStatusWindow(); + + virtual void setPosition( SalFrame* ); + virtual void setText( const String & ); + virtual String getText() const; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ); + virtual void toggle( bool bOn ); + + // overload WorkWindow::DataChanged + virtual void DataChanged( const DataChangedEvent& rEvt ); +}; + +} + +XIMStatusWindow::XIMStatusWindow( bool bOn ) : + StatusWindow( WB_BORDER | WB_SYSTEMFLOATWIN | WB_TOOLTIPWIN ), + m_aStatusText( this, 0 ), + m_pLastParent( NULL ), + m_bAnchoredAtRight( false ), + m_bDelayedShow( false ), + m_eDelayedReason( I18NStatus::contextmap ), + m_nDelayedEvent( 0 ), + m_bOn( bOn ) +{ + layout(); +} + +XIMStatusWindow::~XIMStatusWindow() +{ + if( m_nDelayedEvent ) + Application::RemoveUserEvent( m_nDelayedEvent ); +} + +void XIMStatusWindow::toggle( bool bOn ) +{ + m_bOn = bOn; + show( bOn, I18NStatus::contextmap ); +} + +void XIMStatusWindow::layout() +{ + m_aWindowSize.Width() = m_aStatusText.GetTextWidth( m_aStatusText.GetText() )+8; + Font aFont( m_aStatusText.GetFont() ); + m_aWindowSize.Height() = aFont.GetHeight()+10; + m_aWindowSize = LogicToPixel( m_aWindowSize ); + + Size aControlSize( m_aWindowSize ); + aControlSize.Width() -= 4; + aControlSize.Height() -= 4; + + m_aStatusText.SetPosSizePixel( Point( 1, 1 ), aControlSize ); + m_aStatusText.SetFont( aFont ); + m_aStatusText.Show( sal_True ); + + if (m_bAnchoredAtRight && IsVisible()) + { + SalFrame* pFrame = (SalFrame*)GetSystemData()->pSalFrame; + long nDelta = pFrame->maGeometry.nWidth - m_aWindowSize.Width(); + pFrame->SetPosSize( pFrame->maGeometry.nX + nDelta, + pFrame->maGeometry.nY, + m_aWindowSize.Width(), + m_aWindowSize.Height(), + SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + else + SetOutputSizePixel( m_aWindowSize ); +} + +bool XIMStatusWindow::checkLastParent() const +{ + if( m_pLastParent ) + { + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + for( std::list< SalFrame* >::const_iterator it = rFrames.begin(); it != rFrames.end(); ++it ) + { + if( *it == m_pLastParent ) + return true; + } + } + return false; +} + +void XIMStatusWindow::DataChanged( const DataChangedEvent& ) +{ + m_aStatusText.SetSettings( GetSettings() ); + layout(); +} + +Point XIMStatusWindow::updatePosition() +{ + Point aRet; + if( checkLastParent() ) + { + const SystemEnvData* pParentEnvData = m_pLastParent->GetSystemData(); + + SalExtTextInputPosEvent aPosEvent; + m_pLastParent->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent ); + int x, y; + XLIB_Window aChild; + XTranslateCoordinates( (Display*)pParentEnvData->pDisplay, + (XLIB_Window)pParentEnvData->aShellWindow, + GetX11SalData()->GetDisplay()->GetRootWindow( GetX11SalData()->GetDisplay()->GetDefaultScreenNumber() ), + 0, 0, + &x, &y, + &aChild ); + + // TODO: Currently, place the status window to the (physical) left of + // the cursor iff in vertical mode (assuming that the columns in + // vertical mode are always written from right to left, this causes the + // status window to keep out of the text already written). This + // heuristic would break if there is ever a vertical mode in which the + // columns are written from left to right. Also, more elaborate + // positioning for (both horizontal and vertical) left-to-right and + // right-to-left text would be possible. + bool bLeft = aPosEvent.mbVertical; + // true if status window is to the left of the cursor + + int const nGap = 4; // between cursor and status window + if (aPosEvent.mbVertical) + { + aRet.X() = x + aPosEvent.mnX + (bLeft + ? -m_aWindowSize.Width() - nGap + : aPosEvent.mnHeight + nGap); + aRet.Y() = y + aPosEvent.mnY; + } + else + { + aRet.X() = x + aPosEvent.mnX + (bLeft ? -m_aWindowSize.Width() : 0); + aRet.Y() = y + aPosEvent.mnY+aPosEvent.mnHeight + nGap; + } + + m_bAnchoredAtRight = bLeft; + } + return aRet; +} + +void XIMStatusWindow::setPosition( SalFrame* pParent ) +{ + if( pParent ) + { + if( pParent != m_pLastParent ) + { + setText( String() ); + m_pLastParent = pParent; + Show( sal_False, SHOW_NOACTIVATE ); + } + if( IsVisible() ) + { + const SystemEnvData* pEnvData = GetSystemData(); + SalFrame* pStatusFrame = (SalFrame*)pEnvData->pSalFrame; + Point aPoint = updatePosition(); + pStatusFrame->SetPosSize( aPoint.X(), aPoint.Y(), m_aWindowSize.Width(), m_aWindowSize.Height(), SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + } +} + +IMPL_LINK( XIMStatusWindow, DelayedShowHdl, void*, EMPTYARG ) +{ + m_nDelayedEvent = 0; + const SystemEnvData* pData = GetSystemData(); + SalFrame* pStatusFrame = (SalFrame*)pData->pSalFrame; + if( m_bDelayedShow ) + { + Size aControlSize( m_aWindowSize.Width()-4, m_aWindowSize.Height()-4 ); + m_aStatusText.SetPosSizePixel( Point( 1, 1 ), aControlSize ); + Point aPoint = updatePosition(); + pStatusFrame->SetPosSize( aPoint.X(), aPoint.Y(), m_aWindowSize.Width(), m_aWindowSize.Height(), SAL_FRAME_POSSIZE_X | SAL_FRAME_POSSIZE_Y | SAL_FRAME_POSSIZE_WIDTH | SAL_FRAME_POSSIZE_HEIGHT ); + } + Show( m_bDelayedShow && m_bOn, SHOW_NOACTIVATE ); + if( m_bDelayedShow ) + { + XRaiseWindow( (Display*)pData->pDisplay, + (XLIB_Window)pData->aShellWindow ); + } + return 0; +} + +void XIMStatusWindow::show( bool bShow, I18NStatus::ShowReason eReason ) +{ + if( bShow && ! m_aStatusText.GetText().Len() ) + bShow = false; + + m_bDelayedShow = bShow; + m_eDelayedReason = eReason; + if( ! m_nDelayedEvent ) + m_nDelayedEvent = Application::PostUserEvent( LINK( this, XIMStatusWindow, DelayedShowHdl ) ); +} + +void XIMStatusWindow::setText( const String& rText ) +{ + m_aStatusText.SetText( rText ); + m_aWindowSize.Width() = m_aStatusText.GetTextWidth( rText )+8; +} + +String XIMStatusWindow::getText() const +{ + return m_aStatusText.GetText(); +} + +// -------------------------------------------------------------------------- + +namespace vcl { + +class IIIMPStatusWindow : public StatusWindow +{ + MenuButton m_aStatusBtn; + PopupMenu m_aMenu; + SalFrame* m_pResetFocus; + bool m_bShow; + bool m_bOn; + + DECL_LINK( SelectHdl, MenuButton* ); + + void show(); + +public: + IIIMPStatusWindow( SalFrame* pParent, bool bOn ); // for initial position + virtual ~IIIMPStatusWindow(); + + virtual void setText( const String & ); + virtual String getText() const; + virtual void show( bool bShow, I18NStatus::ShowReason eReason ); + virtual void toggle( bool bOn ); + void layout(); + + // overload Window focus handler + virtual void GetFocus(); + // overload WorkWindow::DataChanged + virtual void DataChanged( const DataChangedEvent& rEvt ); +}; + +} + +IIIMPStatusWindow::IIIMPStatusWindow( SalFrame* pParent, bool bOn ) : + StatusWindow( WB_MOVEABLE ), + m_aStatusBtn( this, WB_BORDER ), + m_pResetFocus( pParent ), + m_bShow( true ), + m_bOn( bOn ) +{ + SetText( String( RTL_CONSTASCII_USTRINGPARAM( "IME Status" ) ) ); + + layout(); + + m_aStatusBtn.SetSelectHdl( LINK( this, IIIMPStatusWindow, SelectHdl ) ); + m_aStatusBtn.SetPopupMenu( &m_aMenu ); + m_aStatusBtn.Show( sal_True ); + + const ::std::vector< I18NStatus::ChoiceData >& rChoices( I18NStatus::get().getChoices() ); + int i = 1; + for( ::std::vector< I18NStatus::ChoiceData >::const_iterator it = rChoices.begin(); it != rChoices.end(); ++it, i++ ) + m_aMenu.InsertItem( i, it->aString ); + + if( pParent ) + { + const SystemEnvData* pEnvData = GetSystemData(); + + const SalFrameGeometry& rGeom( pParent->GetUnmirroredGeometry() ); + int nDistance = rGeom.nTopDecoration; + if( nDistance < 20 ) + nDistance = 20; + XMoveWindow( (Display*)pEnvData->pDisplay, + (XLIB_Window)pEnvData->aShellWindow, + rGeom.nX, + rGeom.nY + rGeom.nHeight + nDistance + ); + } +#if OSL_DEBUG_LEVEL > 1 + else + fprintf( stderr, "Warning: could not reposition status window since no frame\n" ); +#endif + EnableAlwaysOnTop( sal_True ); +} + +IIIMPStatusWindow::~IIIMPStatusWindow() +{ +} + +void IIIMPStatusWindow::layout() +{ + Font aFont( m_aStatusBtn.GetFont() ); + Size aSize( 15*aFont.GetHeight(), aFont.GetHeight()+14 ); + aSize = m_aStatusBtn.LogicToPixel( aSize ); + + m_aStatusBtn.SetPosSizePixel( Point( 0, 0 ), aSize ); + SetOutputSizePixel( aSize ); + if( IsVisible() ) + Invalidate(); +} + +void IIIMPStatusWindow::DataChanged( const DataChangedEvent& ) +{ + m_aStatusBtn.SetSettings( GetSettings() ); + layout(); +} + +void IIIMPStatusWindow::setText( const String& rText ) +{ + m_aStatusBtn.SetText( rText ); +} + +String IIIMPStatusWindow::getText() const +{ + return m_aStatusBtn.GetText(); +} + +void IIIMPStatusWindow::show( bool bShow, I18NStatus::ShowReason eReason ) +{ + // hide IIIMPStatusWindow only in presentations + if( ! bShow + && eReason != I18NStatus::presentation + ) + return; + + m_bShow = bShow; + show(); +} + +void IIIMPStatusWindow::toggle( bool bOn ) +{ + if (bOn != m_bOn) + { + m_bOn = bOn; + show(); + } +} + +void IIIMPStatusWindow::show() +{ + if (m_bOn && m_bShow && !IsVisible()) + m_pResetFocus = I18NStatus::get().getParent(); + Show(m_bOn && m_bShow); +} + +void IIIMPStatusWindow::GetFocus() +{ + /* + * this is here just to put the focus back to the application + * window at startup on clickToFocus WMs + */ + WorkWindow::GetFocus(); + if( m_pResetFocus ) + { + /* + * look if reset focus still exists + * since reset focus really is an internal hack there should + * not be a method to be called in SalFrame destructor + */ + const std::list< SalFrame* >& rFrames = GetX11SalData()->GetDisplay()->getFrames(); + std::list< SalFrame* >::const_iterator it; + for( it = rFrames.begin(); it != rFrames.end() && *it != m_pResetFocus; ++it ) + ; + if( it != rFrames.end() ) + { + const SystemEnvData* pParentEnvData = m_pResetFocus->GetSystemData(); + SalXLib* pXLib = GetX11SalData()->GetDisplay()->GetXLib(); + pXLib->PushXErrorLevel( true ); + XSetInputFocus( (Display*)pParentEnvData->pDisplay, + (XLIB_Window)pParentEnvData->aShellWindow, + RevertToNone, + CurrentTime + ); + XSync( (Display*)pParentEnvData->pDisplay, False ); + pXLib->PopXErrorLevel(); + } + m_pResetFocus = NULL; + } +} + +// -------------------------------------------------------------------------- + +IMPL_LINK( IIIMPStatusWindow, SelectHdl, MenuButton*, pBtn ) +{ + if( pBtn == & m_aStatusBtn ) + { + const ::std::vector< I18NStatus::ChoiceData >& rChoices( I18NStatus::get().getChoices() ); + unsigned int nIndex = m_aStatusBtn.GetCurItemId()-1; + if( nIndex < rChoices.size() ) + { + XSetICValues( static_cast<X11SalFrame*>(I18NStatus::get().getParent())->getInputContext()->GetContext(), + XNUnicodeCharacterSubset, + rChoices[nIndex].pData, + NULL); + // FIXME: get rid of X11SalFrame + X11SalFrame* pParent = static_cast<X11SalFrame*>(I18NStatus::get().getParent()); + if( pParent && pParent->isMapped() ) + { + const SystemEnvData* pEnv = pParent->GetSystemData(); + SalXLib* pXLib = GetX11SalData()->GetDisplay()->GetXLib(); + pXLib->PushXErrorLevel( true ); + XSetInputFocus( (Display*)pEnv->pDisplay, + (XLIB_Window)pEnv->aShellWindow, + RevertToNone, + CurrentTime + ); + XSync( (Display*)pEnv->pDisplay, False ); + pXLib->PopXErrorLevel(); + } + } + } + return 0; +} + +/* + * I18NStatus + */ + +I18NStatus* I18NStatus::pInstance = NULL; + +I18NStatus& I18NStatus::get() +{ + if( ! pInstance ) + pInstance = new I18NStatus(); + return *pInstance; +} + +// -------------------------------------------------------------------------- + +bool I18NStatus::exists() +{ + return pInstance != NULL; +} + +// -------------------------------------------------------------------------- + +void I18NStatus::free() +{ + if( pInstance ) + delete pInstance, pInstance = NULL; +} + +// -------------------------------------------------------------------------- + +I18NStatus::I18NStatus() : + m_pParent( NULL ), + m_pStatusWindow( NULL ) +{ +} + +// -------------------------------------------------------------------------- + +I18NStatus::~I18NStatus() +{ + if( m_pStatusWindow ) + delete m_pStatusWindow, m_pStatusWindow = NULL; + if( pInstance == this ) + pInstance = NULL; +} + +// -------------------------------------------------------------------------- + +void I18NStatus::setParent( SalFrame* pParent ) +{ + m_pParent = pParent; + if( ! m_pStatusWindow ) + { + bool bIIIMPmode = m_aChoices.begin() != m_aChoices.end(); + if( bIIIMPmode ) + m_pStatusWindow = new IIIMPStatusWindow( pParent, + getStatusWindowMode() ); + else + m_pStatusWindow = new XIMStatusWindow( getStatusWindowMode() ); + setStatusText( m_aCurrentIM ); + } + m_pStatusWindow->setPosition( m_pParent ); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::show( bool bShow, ShowReason eReason ) +{ + if( m_pStatusWindow ) + { + m_pStatusWindow->setPosition( m_pParent ); + m_pStatusWindow->show( bShow, eReason ); + } +} + +// -------------------------------------------------------------------------- + +void I18NStatus::setStatusText( const String& rText ) +{ + if( m_pStatusWindow ) + { + /* + * #93614# convert fullwidth ASCII forms to ascii + */ + int nChars = rText.Len()+1; + sal_Unicode* pBuffer = (sal_Unicode*)alloca( nChars*sizeof( sal_Unicode ) ); + const sal_Unicode* pCopy = rText.GetBuffer(); + for( int i = 0; i < nChars; i++ ) + { + if( pCopy[i] >=0xff00 && pCopy[i] <= 0xff5f ) + pBuffer[i] = (pCopy[i] & 0xff) + 0x20; + else + pBuffer[i] = pCopy[i]; + } + String aText( pBuffer ); + m_pStatusWindow->setText( aText ); + m_pStatusWindow->setPosition( m_pParent ); + + bool bVisible = true; + if( m_pParent ) + { + long w, h; + m_pParent->GetClientSize( w, h ); + if( w == 0 || h == 0 ) + { + bVisible = false; + } + } + + m_pStatusWindow->show( bVisible, contextmap ); + } +} + +// -------------------------------------------------------------------------- + +void I18NStatus::changeIM( const String& rIM ) +{ + m_aCurrentIM = rIM; +} + +// -------------------------------------------------------------------------- + +String I18NStatus::getStatusText() const +{ + return m_pStatusWindow ? m_pStatusWindow->getText() : String(); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::clearChoices() +{ + m_aChoices.clear(); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::addChoice( const String& rChoice, void* pData ) +{ + ChoiceData aData; + aData.pData = pData; + aData.aString = rChoice; + m_aChoices.push_back( aData ); +} + +// -------------------------------------------------------------------------- + +void I18NStatus::toTop() const +{ + if( m_pStatusWindow ) + { + const SystemEnvData* pData = m_pStatusWindow->GetSystemData(); + XRaiseWindow( (Display*)pData->pDisplay, + (XLIB_Window)pData->aShellWindow ); + } +} + +// -------------------------------------------------------------------------- + +SalFrame* I18NStatus::getStatusFrame() const +{ + SalFrame* pRet = NULL; + if( m_pStatusWindow ) + { + const SystemEnvData* pData = m_pStatusWindow->GetSystemData(); + pRet = (SalFrame*)pData->pSalFrame; + } + return pRet; +} + +bool I18NStatus::canToggleStatusWindow() const +{ + return true; +} + +void I18NStatus::toggleStatusWindow() +{ + if (m_pStatusWindow != 0) + m_pStatusWindow->toggle(getStatusWindowMode()); +} + +bool I18NStatus::getStatusWindowMode() +{ + switch (ImplGetSVData()->maAppData.meShowImeStatusWindow) + { + default: // ImplSVAppData::ImeStatusWindowMode_UNKNOWN + return Application::GetShowImeStatusWindowDefault(); + case ImplSVAppData::ImeStatusWindowMode_HIDE: + return false; + case ImplSVAppData::ImeStatusWindowMode_SHOW: + return true; + } +} + +/* + * X11ImeStatus + */ +X11ImeStatus::~X11ImeStatus() +{ + vcl::I18NStatus::free(); +} + +bool X11ImeStatus::canToggle() +{ + return vcl::I18NStatus::get().canToggleStatusWindow(); +} + +void X11ImeStatus::toggle() +{ + vcl::I18NStatus::get().toggleStatusWindow(); +} + +SalI18NImeStatus* X11SalInstance::CreateI18NImeStatus() +{ + return new X11ImeStatus(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |