1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #if OSL_DEBUG_LEVEL > 1
23 #include <sal/alloca.h>
30 #include <unx/salunx.h>
31 #include <unx/i18n_status.hxx>
32 #include <unx/i18n_ic.hxx>
33 #include <unx/saldisp.hxx>
34 #include <unx/salframe.h>
35 #include <unx/saldata.hxx>
37 #include <vcl/wrkwin.hxx>
38 #include <vcl/fixed.hxx>
39 #include <vcl/menubtn.hxx>
40 #include <vcl/menu.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vcl/sysdata.hxx>
50 class StatusWindow
: public WorkWindow
53 StatusWindow( WinBits nWinBits
);
56 virtual void setPosition( SalFrame
* );
57 virtual void setText( const OUString
& ) = 0;
58 virtual void show( bool bShow
, I18NStatus::ShowReason eReason
) = 0;
59 virtual void toggle( bool bOn
) = 0;
64 StatusWindow::StatusWindow( WinBits nWinBits
) :
65 WorkWindow( NULL
, nWinBits
)
69 void StatusWindow::setPosition( SalFrame
* )
75 class XIMStatusWindow
: public StatusWindow
77 VclPtr
<FixedText
> m_aStatusText
;
78 SalFrame
* m_pLastParent
;
80 bool m_bAnchoredAtRight
;
81 // true if the right edge (instead of the left edge) should stay at a
82 // fixed position when re-sizing the window
84 // for delayed showing
86 I18NStatus::ShowReason m_eDelayedReason
;
87 ImplSVEvent
* m_nDelayedEvent
;
91 Point
updatePosition();
93 bool checkLastParent() const;
95 DECL_LINK( DelayedShowHdl
, void* );
97 XIMStatusWindow( bool bOn
);
98 virtual ~XIMStatusWindow();
100 virtual void setPosition( SalFrame
* ) SAL_OVERRIDE
;
101 virtual void setText( const OUString
& ) SAL_OVERRIDE
;
102 virtual void show( bool bShow
, I18NStatus::ShowReason eReason
) SAL_OVERRIDE
;
103 virtual void toggle( bool bOn
) SAL_OVERRIDE
;
104 virtual void dispose() SAL_OVERRIDE
;
106 // override WorkWindow::DataChanged
107 virtual void DataChanged( const DataChangedEvent
& rEvt
) SAL_OVERRIDE
;
112 XIMStatusWindow::XIMStatusWindow( bool bOn
) :
113 StatusWindow( WB_BORDER
| WB_SYSTEMFLOATWIN
| WB_TOOLTIPWIN
),
114 m_aStatusText(VclPtr
<FixedText
>::Create(this, 0)),
115 m_pLastParent( NULL
),
116 m_bAnchoredAtRight( false ),
117 m_bDelayedShow( false ),
118 m_eDelayedReason( I18NStatus::contextmap
),
119 m_nDelayedEvent( 0 ),
125 XIMStatusWindow::~XIMStatusWindow()
130 void XIMStatusWindow::dispose()
132 if( m_nDelayedEvent
)
133 Application::RemoveUserEvent( m_nDelayedEvent
);
134 m_aStatusText
.disposeAndClear();
135 StatusWindow::dispose();
138 void XIMStatusWindow::toggle( bool bOn
)
141 show( bOn
, I18NStatus::contextmap
);
144 void XIMStatusWindow::layout()
146 m_aWindowSize
.Width() = m_aStatusText
->GetTextWidth( m_aStatusText
->GetText() )+8;
147 Font
aFont( m_aStatusText
->GetFont() );
148 m_aWindowSize
.Height() = aFont
.GetHeight()+10;
149 m_aWindowSize
= LogicToPixel( m_aWindowSize
);
151 Size
aControlSize( m_aWindowSize
);
152 aControlSize
.Width() -= 4;
153 aControlSize
.Height() -= 4;
155 m_aStatusText
->SetPosSizePixel( Point( 1, 1 ), aControlSize
);
156 m_aStatusText
->SetFont( aFont
);
157 m_aStatusText
->Show( true );
159 if (m_bAnchoredAtRight
&& IsVisible())
161 SalFrame
* pFrame
= static_cast<SalFrame
*>(GetSystemData()->pSalFrame
);
162 long nDelta
= pFrame
->maGeometry
.nWidth
- m_aWindowSize
.Width();
163 pFrame
->SetPosSize( pFrame
->maGeometry
.nX
+ nDelta
,
164 pFrame
->maGeometry
.nY
,
165 m_aWindowSize
.Width(),
166 m_aWindowSize
.Height(),
167 SAL_FRAME_POSSIZE_X
| SAL_FRAME_POSSIZE_Y
| SAL_FRAME_POSSIZE_WIDTH
| SAL_FRAME_POSSIZE_HEIGHT
);
170 SetOutputSizePixel( m_aWindowSize
);
173 bool XIMStatusWindow::checkLastParent() const
177 const std::list
< SalFrame
* >& rFrames
= vcl_sal::getSalDisplay(GetGenericData())->getFrames();
178 for( std::list
< SalFrame
* >::const_iterator it
= rFrames
.begin(); it
!= rFrames
.end(); ++it
)
180 if( *it
== m_pLastParent
)
187 void XIMStatusWindow::DataChanged( const DataChangedEvent
& )
189 m_aStatusText
->SetSettings( GetSettings() );
193 Point
XIMStatusWindow::updatePosition()
196 if( checkLastParent() )
198 const SystemEnvData
* pParentEnvData
= m_pLastParent
->GetSystemData();
200 SalExtTextInputPosEvent aPosEvent
;
201 m_pLastParent
->CallCallback( SALEVENT_EXTTEXTINPUTPOS
, (void*)&aPosEvent
);
204 XTranslateCoordinates( static_cast<Display
*>(pParentEnvData
->pDisplay
),
205 (::Window
)pParentEnvData
->aShellWindow
,
206 vcl_sal::getSalDisplay(GetGenericData())->GetRootWindow( vcl_sal::getSalDisplay(GetGenericData())->GetDefaultXScreen() ),
211 // TODO: Currently, place the status window to the (physical) left of
212 // the cursor iff in vertical mode (assuming that the columns in
213 // vertical mode are always written from right to left, this causes the
214 // status window to keep out of the text already written). This
215 // heuristic would break if there is ever a vertical mode in which the
216 // columns are written from left to right. Also, more elaborate
217 // positioning for (both horizontal and vertical) left-to-right and
218 // right-to-left text would be possible.
219 bool bLeft
= aPosEvent
.mbVertical
;
220 // true if status window is to the left of the cursor
222 int const nGap
= 4; // between cursor and status window
223 if (aPosEvent
.mbVertical
)
225 aRet
.X() = x
+ aPosEvent
.mnX
+ (bLeft
226 ? -m_aWindowSize
.Width() - nGap
227 : aPosEvent
.mnHeight
+ nGap
);
228 aRet
.Y() = y
+ aPosEvent
.mnY
;
232 aRet
.X() = x
+ aPosEvent
.mnX
+ (bLeft
? -m_aWindowSize
.Width() : 0);
233 aRet
.Y() = y
+ aPosEvent
.mnY
+aPosEvent
.mnHeight
+ nGap
;
236 m_bAnchoredAtRight
= bLeft
;
241 void XIMStatusWindow::setPosition( SalFrame
* pParent
)
245 if( pParent
!= m_pLastParent
)
247 setText( OUString() );
248 m_pLastParent
= pParent
;
249 Show( false, SHOW_NOACTIVATE
);
253 const SystemEnvData
* pEnvData
= GetSystemData();
254 SalFrame
* pStatusFrame
= static_cast<SalFrame
*>(pEnvData
->pSalFrame
);
255 Point aPoint
= updatePosition();
256 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
);
261 IMPL_LINK_NOARG(XIMStatusWindow
, DelayedShowHdl
)
264 const SystemEnvData
* pData
= GetSystemData();
265 SalFrame
* pStatusFrame
= static_cast<SalFrame
*>(pData
->pSalFrame
);
268 Size
aControlSize( m_aWindowSize
.Width()-4, m_aWindowSize
.Height()-4 );
269 m_aStatusText
->SetPosSizePixel( Point( 1, 1 ), aControlSize
);
270 Point aPoint
= updatePosition();
271 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
);
273 Show( m_bDelayedShow
&& m_bOn
, SHOW_NOACTIVATE
);
276 XRaiseWindow( static_cast<Display
*>(pData
->pDisplay
),
277 (::Window
)pData
->aShellWindow
);
282 void XIMStatusWindow::show( bool bShow
, I18NStatus::ShowReason eReason
)
284 if( bShow
&& m_aStatusText
->GetText().isEmpty() )
287 m_bDelayedShow
= bShow
;
288 m_eDelayedReason
= eReason
;
289 if( ! m_nDelayedEvent
)
290 m_nDelayedEvent
= Application::PostUserEvent( LINK( this, XIMStatusWindow
, DelayedShowHdl
), NULL
, true );
293 void XIMStatusWindow::setText( const OUString
& rText
)
295 m_aStatusText
->SetText( rText
);
296 m_aWindowSize
.Width() = m_aStatusText
->GetTextWidth( rText
)+8;
301 class IIIMPStatusWindow
: public StatusWindow
303 VclPtr
<MenuButton
> m_aStatusBtn
;
305 SalFrame
* m_pResetFocus
;
309 DECL_LINK_TYPED( SelectHdl
, MenuButton
*, void );
314 IIIMPStatusWindow( SalFrame
* pParent
, bool bOn
); // for initial position
316 virtual void setText( const OUString
& ) SAL_OVERRIDE
;
317 virtual void show( bool bShow
, I18NStatus::ShowReason eReason
) SAL_OVERRIDE
;
318 virtual void toggle( bool bOn
) SAL_OVERRIDE
;
319 virtual ~IIIMPStatusWindow() { disposeOnce(); }
320 virtual void dispose() SAL_OVERRIDE
;
323 // override Window focus handler
324 virtual void GetFocus() SAL_OVERRIDE
;
325 // override WorkWindow::DataChanged
326 virtual void DataChanged( const DataChangedEvent
& rEvt
) SAL_OVERRIDE
;
331 IIIMPStatusWindow::IIIMPStatusWindow( SalFrame
* pParent
, bool bOn
) :
332 StatusWindow( WB_MOVEABLE
),
333 m_aStatusBtn(VclPtr
<MenuButton
>::Create(this, WB_BORDER
)),
334 m_pResetFocus( pParent
),
338 SetText( OUString( "IME Status" ) );
342 m_aStatusBtn
->SetSelectHdl( LINK( this, IIIMPStatusWindow
, SelectHdl
) );
343 m_aStatusBtn
->SetPopupMenu( &m_aMenu
);
344 m_aStatusBtn
->Show( true );
346 const ::std::vector
< I18NStatus::ChoiceData
>& rChoices( I18NStatus::get().getChoices() );
348 for( ::std::vector
< I18NStatus::ChoiceData
>::const_iterator it
= rChoices
.begin(); it
!= rChoices
.end(); ++it
, i
++ )
349 m_aMenu
.InsertItem( i
, it
->aString
);
353 const SystemEnvData
* pEnvData
= GetSystemData();
355 const SalFrameGeometry
& rGeom( pParent
->GetUnmirroredGeometry() );
356 int nDistance
= rGeom
.nTopDecoration
;
359 XMoveWindow( static_cast<Display
*>(pEnvData
->pDisplay
),
360 (::Window
)pEnvData
->aShellWindow
,
362 rGeom
.nY
+ rGeom
.nHeight
+ nDistance
365 #if OSL_DEBUG_LEVEL > 1
367 fprintf( stderr
, "Warning: could not reposition status window since no frame\n" );
369 EnableAlwaysOnTop( true );
372 void IIIMPStatusWindow::layout()
374 Font
aFont( m_aStatusBtn
->GetFont() );
375 Size
aSize( 15*aFont
.GetHeight(), aFont
.GetHeight()+14 );
376 aSize
= m_aStatusBtn
->LogicToPixel( aSize
);
378 m_aStatusBtn
->SetPosSizePixel( Point( 0, 0 ), aSize
);
379 SetOutputSizePixel( aSize
);
384 void IIIMPStatusWindow::DataChanged( const DataChangedEvent
& )
386 m_aStatusBtn
->SetSettings( GetSettings() );
390 void IIIMPStatusWindow::setText( const OUString
& rText
)
392 m_aStatusBtn
->SetText( rText
);
395 void IIIMPStatusWindow::show( bool bShow
, I18NStatus::ShowReason eReason
)
397 // hide IIIMPStatusWindow only in presentations
399 && eReason
!= I18NStatus::presentation
407 void IIIMPStatusWindow::toggle( bool bOn
)
416 void IIIMPStatusWindow::dispose()
418 m_aStatusBtn
.disposeAndClear();
419 StatusWindow::dispose();
422 void IIIMPStatusWindow::show()
424 if (m_bOn
&& m_bShow
&& !IsVisible())
425 m_pResetFocus
= I18NStatus::get().getParent();
426 Show(m_bOn
&& m_bShow
);
429 void IIIMPStatusWindow::GetFocus()
432 * this is here just to put the focus back to the application
433 * window at startup on clickToFocus WMs
435 WorkWindow::GetFocus();
439 * look if reset focus still exists
440 * since reset focus really is an internal hack there should
441 * not be a method to be called in SalFrame destructor
443 const std::list
< SalFrame
* >& rFrames
= vcl_sal::getSalDisplay(GetGenericData())->getFrames();
444 std::list
< SalFrame
* >::const_iterator it
;
445 for( it
= rFrames
.begin(); it
!= rFrames
.end() && *it
!= m_pResetFocus
; ++it
)
447 if( it
!= rFrames
.end() )
449 const SystemEnvData
* pParentEnvData
= m_pResetFocus
->GetSystemData();
450 GetGenericData()->ErrorTrapPush();
451 XSetInputFocus( static_cast<Display
*>(pParentEnvData
->pDisplay
),
452 (::Window
)pParentEnvData
->aShellWindow
,
456 XSync( static_cast<Display
*>(pParentEnvData
->pDisplay
), False
);
457 GetGenericData()->ErrorTrapPop();
459 m_pResetFocus
= NULL
;
463 IMPL_LINK_TYPED( IIIMPStatusWindow
, SelectHdl
, MenuButton
*, pBtn
, void )
465 if( pBtn
== m_aStatusBtn
)
467 const ::std::vector
< I18NStatus::ChoiceData
>& rChoices( I18NStatus::get().getChoices() );
468 unsigned int nIndex
= m_aStatusBtn
->GetCurItemId()-1;
469 if( nIndex
< rChoices
.size() )
471 XSetICValues( static_cast<X11SalFrame
*>(I18NStatus::get().getParent())->getInputContext()->GetContext(),
472 XNUnicodeCharacterSubset
,
473 rChoices
[nIndex
].pData
,
475 // FIXME: get rid of X11SalFrame
476 X11SalFrame
* pParent
= static_cast<X11SalFrame
*>(I18NStatus::get().getParent());
477 if( pParent
&& pParent
->isMapped() )
479 const SystemEnvData
* pEnv
= pParent
->GetSystemData();
480 GetGenericData()->ErrorTrapPush();
481 XSetInputFocus( static_cast<Display
*>(pEnv
->pDisplay
),
482 (::Window
)pEnv
->aShellWindow
,
486 XSync( static_cast<Display
*>(pEnv
->pDisplay
), False
);
487 GetGenericData()->ErrorTrapPop();
497 I18NStatus
* I18NStatus::pInstance
= NULL
;
499 I18NStatus
& I18NStatus::get()
502 pInstance
= new I18NStatus();
506 bool I18NStatus::exists()
508 return pInstance
!= NULL
;
511 void I18NStatus::free()
514 delete pInstance
, pInstance
= NULL
;
517 I18NStatus::I18NStatus() :
519 m_pStatusWindow( NULL
)
523 I18NStatus::~I18NStatus()
525 m_pStatusWindow
.disposeAndClear();
526 if( pInstance
== this )
530 void I18NStatus::setParent( SalFrame
* pParent
)
533 if( ! m_pStatusWindow
)
535 bool bIIIMPmode
= m_aChoices
.begin() != m_aChoices
.end();
537 m_pStatusWindow
= VclPtr
<IIIMPStatusWindow
>::Create( pParent
,
538 getStatusWindowMode() );
540 m_pStatusWindow
= VclPtr
<XIMStatusWindow
>::Create( getStatusWindowMode() );
541 setStatusText( m_aCurrentIM
);
543 m_pStatusWindow
->setPosition( m_pParent
);
546 void I18NStatus::show( bool bShow
, ShowReason eReason
)
548 if( m_pStatusWindow
)
550 m_pStatusWindow
->setPosition( m_pParent
);
551 m_pStatusWindow
->show( bShow
, eReason
);
555 void I18NStatus::setStatusText( const OUString
& rText
)
557 if( m_pStatusWindow
)
560 * #93614# convert fullwidth ASCII forms to ascii
562 int nChars
= rText
.getLength();
563 sal_Unicode
* pBuffer
= static_cast<sal_Unicode
*>(alloca( nChars
*sizeof( sal_Unicode
) ));
564 for( int i
= 0; i
< nChars
; i
++ )
566 if( rText
[i
] >=0xff00 && rText
[i
] <= 0xff5f )
567 pBuffer
[i
] = (rText
[i
] & 0xff) + 0x20;
569 pBuffer
[i
] = rText
[i
];
571 OUString
aText( pBuffer
, nChars
);
572 m_pStatusWindow
->setText( aText
);
573 m_pStatusWindow
->setPosition( m_pParent
);
575 bool bVisible
= true;
579 m_pParent
->GetClientSize( w
, h
);
580 if( w
== 0 || h
== 0 )
586 m_pStatusWindow
->show( bVisible
, contextmap
);
590 void I18NStatus::changeIM( const OUString
& rIM
)
595 SalFrame
* I18NStatus::getStatusFrame() const
597 SalFrame
* pRet
= NULL
;
598 if( m_pStatusWindow
)
600 const SystemEnvData
* pData
= m_pStatusWindow
->GetSystemData();
601 pRet
= static_cast<SalFrame
*>(pData
->pSalFrame
);
606 void I18NStatus::toggleStatusWindow()
608 if (m_pStatusWindow
!= nullptr)
609 m_pStatusWindow
->toggle(getStatusWindowMode());
612 bool I18NStatus::getStatusWindowMode()
614 switch (ImplGetSVData()->maAppData
.meShowImeStatusWindow
)
616 default: // ImplSVAppData::ImeStatusWindowMode_UNKNOWN
617 return Application::GetShowImeStatusWindowDefault();
618 case ImplSVAppData::ImeStatusWindowMode_HIDE
:
620 case ImplSVAppData::ImeStatusWindowMode_SHOW
:
628 X11ImeStatus::~X11ImeStatus()
630 vcl::I18NStatus::free();
633 bool X11ImeStatus::canToggle()
638 void X11ImeStatus::toggle()
640 vcl::I18NStatus::get().toggleStatusWindow();
643 SalI18NImeStatus
* X11SalInstance::CreateI18NImeStatus()
645 return new X11ImeStatus();
648 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */