bump product version to 5.0.4.1
[LibreOffice.git] / vcl / unx / generic / app / i18n_status.cxx
blob896d345b45c3365b5a06fa096d2c2b8ca0732a79
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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
21 #include <stdio.h>
22 #endif
23 #include <sal/alloca.h>
25 #include <prex.h>
26 #include <X11/Xlib.h>
27 #include <unx/XIM.h>
28 #include <postx.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>
44 #include <svdata.hxx>
46 using namespace vcl;
48 namespace vcl {
50 class StatusWindow : public WorkWindow
52 protected:
53 StatusWindow( WinBits nWinBits );
54 public:
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* )
73 namespace vcl {
75 class XIMStatusWindow : public StatusWindow
77 VclPtr<FixedText> m_aStatusText;
78 SalFrame* m_pLastParent;
79 Size m_aWindowSize;
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
85 bool m_bDelayedShow;
86 I18NStatus::ShowReason m_eDelayedReason;
87 ImplSVEvent * m_nDelayedEvent;
88 // for toggling
89 bool m_bOn;
91 Point updatePosition();
92 void layout();
93 bool checkLastParent() const;
95 DECL_LINK( DelayedShowHdl, void* );
96 public:
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 ),
120 m_bOn( bOn )
122 layout();
125 XIMStatusWindow::~XIMStatusWindow()
127 disposeOnce();
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 )
140 m_bOn = 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 );
169 else
170 SetOutputSizePixel( m_aWindowSize );
173 bool XIMStatusWindow::checkLastParent() const
175 if( m_pLastParent )
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 )
181 return true;
184 return false;
187 void XIMStatusWindow::DataChanged( const DataChangedEvent& )
189 m_aStatusText->SetSettings( GetSettings() );
190 layout();
193 Point XIMStatusWindow::updatePosition()
195 Point aRet;
196 if( checkLastParent() )
198 const SystemEnvData* pParentEnvData = m_pLastParent->GetSystemData();
200 SalExtTextInputPosEvent aPosEvent;
201 m_pLastParent->CallCallback( SALEVENT_EXTTEXTINPUTPOS, (void*)&aPosEvent );
202 int x, y;
203 ::Window aChild;
204 XTranslateCoordinates( static_cast<Display*>(pParentEnvData->pDisplay),
205 (::Window)pParentEnvData->aShellWindow,
206 vcl_sal::getSalDisplay(GetGenericData())->GetRootWindow( vcl_sal::getSalDisplay(GetGenericData())->GetDefaultXScreen() ),
207 0, 0,
208 &x, &y,
209 &aChild );
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;
230 else
232 aRet.X() = x + aPosEvent.mnX + (bLeft ? -m_aWindowSize.Width() : 0);
233 aRet.Y() = y + aPosEvent.mnY+aPosEvent.mnHeight + nGap;
236 m_bAnchoredAtRight = bLeft;
238 return aRet;
241 void XIMStatusWindow::setPosition( SalFrame* pParent )
243 if( pParent )
245 if( pParent != m_pLastParent )
247 setText( OUString() );
248 m_pLastParent = pParent;
249 Show( false, SHOW_NOACTIVATE );
251 if( IsVisible() )
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)
263 m_nDelayedEvent = 0;
264 const SystemEnvData* pData = GetSystemData();
265 SalFrame* pStatusFrame = static_cast<SalFrame*>(pData->pSalFrame);
266 if( m_bDelayedShow )
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 );
274 if( m_bDelayedShow )
276 XRaiseWindow( static_cast<Display*>(pData->pDisplay),
277 (::Window)pData->aShellWindow );
279 return 0;
282 void XIMStatusWindow::show( bool bShow, I18NStatus::ShowReason eReason )
284 if( bShow && m_aStatusText->GetText().isEmpty() )
285 bShow = false;
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;
299 namespace vcl {
301 class IIIMPStatusWindow : public StatusWindow
303 VclPtr<MenuButton> m_aStatusBtn;
304 PopupMenu m_aMenu;
305 SalFrame* m_pResetFocus;
306 bool m_bShow;
307 bool m_bOn;
309 DECL_LINK_TYPED( SelectHdl, MenuButton*, void );
311 void show();
313 public:
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;
321 void layout();
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 ),
335 m_bShow( true ),
336 m_bOn( bOn )
338 SetText( OUString( "IME Status" ) );
340 layout();
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() );
347 int i = 1;
348 for( ::std::vector< I18NStatus::ChoiceData >::const_iterator it = rChoices.begin(); it != rChoices.end(); ++it, i++ )
349 m_aMenu.InsertItem( i, it->aString );
351 if( pParent )
353 const SystemEnvData* pEnvData = GetSystemData();
355 const SalFrameGeometry& rGeom( pParent->GetUnmirroredGeometry() );
356 int nDistance = rGeom.nTopDecoration;
357 if( nDistance < 20 )
358 nDistance = 20;
359 XMoveWindow( static_cast<Display*>(pEnvData->pDisplay),
360 (::Window)pEnvData->aShellWindow,
361 rGeom.nX,
362 rGeom.nY + rGeom.nHeight + nDistance
365 #if OSL_DEBUG_LEVEL > 1
366 else
367 fprintf( stderr, "Warning: could not reposition status window since no frame\n" );
368 #endif
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 );
380 if( IsVisible() )
381 Invalidate();
384 void IIIMPStatusWindow::DataChanged( const DataChangedEvent& )
386 m_aStatusBtn->SetSettings( GetSettings() );
387 layout();
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
398 if( ! bShow
399 && eReason != I18NStatus::presentation
401 return;
403 m_bShow = bShow;
404 show();
407 void IIIMPStatusWindow::toggle( bool bOn )
409 if (bOn != m_bOn)
411 m_bOn = bOn;
412 show();
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();
436 if( m_pResetFocus )
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,
453 RevertToNone,
454 CurrentTime
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,
474 NULL);
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,
483 RevertToNone,
484 CurrentTime
486 XSync( static_cast<Display*>(pEnv->pDisplay), False );
487 GetGenericData()->ErrorTrapPop();
494 * I18NStatus
497 I18NStatus* I18NStatus::pInstance = NULL;
499 I18NStatus& I18NStatus::get()
501 if( ! pInstance )
502 pInstance = new I18NStatus();
503 return *pInstance;
506 bool I18NStatus::exists()
508 return pInstance != NULL;
511 void I18NStatus::free()
513 if( pInstance )
514 delete pInstance, pInstance = NULL;
517 I18NStatus::I18NStatus() :
518 m_pParent( NULL ),
519 m_pStatusWindow( NULL )
523 I18NStatus::~I18NStatus()
525 m_pStatusWindow.disposeAndClear();
526 if( pInstance == this )
527 pInstance = NULL;
530 void I18NStatus::setParent( SalFrame* pParent )
532 m_pParent = pParent;
533 if( ! m_pStatusWindow )
535 bool bIIIMPmode = m_aChoices.begin() != m_aChoices.end();
536 if( bIIIMPmode )
537 m_pStatusWindow = VclPtr<IIIMPStatusWindow>::Create( pParent,
538 getStatusWindowMode() );
539 else
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;
568 else
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;
576 if( m_pParent )
578 long w, h;
579 m_pParent->GetClientSize( w, h );
580 if( w == 0 || h == 0 )
582 bVisible = false;
586 m_pStatusWindow->show( bVisible, contextmap );
590 void I18NStatus::changeIM( const OUString& rIM )
592 m_aCurrentIM = 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);
603 return pRet;
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:
619 return false;
620 case ImplSVAppData::ImeStatusWindowMode_SHOW:
621 return true;
626 * X11ImeStatus
628 X11ImeStatus::~X11ImeStatus()
630 vcl::I18NStatus::free();
633 bool X11ImeStatus::canToggle()
635 return true;
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: */