blob: 893496ffc23b17f37c4cfcbc4e75e2d5472967ab [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_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;
using namespace rtl;
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();
}