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 .
24 #include "dlgctrl.hxx"
25 #include <vcl/event.hxx>
26 #include <vcl/toolkit/fixed.hxx>
27 #include <vcl/layout.hxx>
28 #include <vcl/svapp.hxx>
29 #include <vcl/tabpage.hxx>
30 #include <vcl/tabctrl.hxx>
31 #include <vcl/toolkit/button.hxx>
32 #include <vcl/toolbox.hxx>
33 #include <vcl/settings.hxx>
34 #include <sal/log.hxx>
35 #include <i18nlangtag/languagetag.hxx>
37 #include <com/sun/star/i18n/XCharacterClassification.hpp>
39 using namespace ::com::sun::star
;
41 static bool ImplHasIndirectTabParent( vcl::Window
* pWindow
)
43 // The window has indirect tab parent if it is included in tab hierarchy
44 // of the indirect parent window
46 vcl::Window
* pNonLayoutParent
= getNonLayoutParent(pWindow
);
47 return ( pNonLayoutParent
48 && ( pNonLayoutParent
->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL
) );
51 static vcl::Window
* ImplGetTopParentOfTabHierarchy( vcl::Window
* pParent
)
53 // The method allows to find the most close parent containing all the
54 // window from the current tab-hierarchy
55 // The direct parent should be provided as a parameter here
57 vcl::Window
* pResult
= pParent
;
61 vcl::Window
* pNonLayoutParent
= getNonLayoutParent(pResult
);
62 while ( pNonLayoutParent
&& ( pResult
->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL
) )
64 pResult
= pNonLayoutParent
;
65 pNonLayoutParent
= getNonLayoutParent(pResult
);
72 static vcl::Window
* ImplGetCurTabWindow(const vcl::Window
* pWindow
)
74 assert(pWindow
->GetType() == WindowType::TABCONTROL
);
75 const TabControl
* pTabControl
= static_cast<const TabControl
*>(pWindow
);
76 // Check if the TabPage is a Child of the TabControl and still exists (by
77 // walking all child windows); because it could be that the TabPage has been
78 // destroyed already by a Dialog-Dtor, event that the TabControl still exists.
79 const TabPage
* pTempTabPage
= pTabControl
->GetTabPage(pTabControl
->GetCurPageId());
82 vcl::Window
* pTempWindow
= pTabControl
->GetWindow(GetWindowType::FirstChild
);
85 if (pTempWindow
->ImplGetWindow() == pTempTabPage
)
87 return const_cast<TabPage
*>(pTempTabPage
);
89 pTempWindow
= nextLogicalChildOfParent(pTabControl
, pTempWindow
);
96 static vcl::Window
* ImplGetSubChildWindow( vcl::Window
* pParent
, sal_uInt16 n
, sal_uInt16
& nIndex
)
98 // ignore all windows with mpClientWindow set
99 for (vcl::Window
*pNewParent
= pParent
->ImplGetWindow();
100 pParent
!= pNewParent
; pParent
= pNewParent
);
102 vcl::Window
* pFoundWindow
= nullptr;
103 vcl::Window
* pWindow
= firstLogicalChildOfParent(pParent
);
104 vcl::Window
* pNextWindow
= pWindow
;
106 // process just the current page of a tab control
107 if (pWindow
&& pParent
->GetType() == WindowType::TABCONTROL
)
109 pWindow
= ImplGetCurTabWindow(pParent
);
110 pNextWindow
= lastLogicalChildOfParent(pParent
);
115 pWindow
= pWindow
->ImplGetWindow();
117 // skip invisible and disabled windows
118 if (isVisibleInLayout(pWindow
))
120 // return the TabControl itself, before handling its page
121 if (pWindow
->GetType() == WindowType::TABCONTROL
)
127 if (pWindow
->GetStyle() & (WB_DIALOGCONTROL
| WB_CHILDDLGCTRL
))
128 pFoundWindow
= ImplGetSubChildWindow(pWindow
, n
, nIndex
);
130 pFoundWindow
= pWindow
;
137 pWindow
= nextLogicalChildOfParent(pParent
, pNextWindow
);
138 pNextWindow
= pWindow
;
142 assert(!pFoundWindow
|| (pFoundWindow
== pFoundWindow
->ImplGetWindow()));
146 vcl::Window
* ImplGetChildWindow( vcl::Window
* pParent
, sal_uInt16 n
, sal_uInt16
& nIndex
, bool bTestEnable
)
148 pParent
= ImplGetTopParentOfTabHierarchy( pParent
);
151 vcl::Window
* pWindow
= ImplGetSubChildWindow( pParent
, n
, nIndex
);
154 sal_uInt16 n2
= nIndex
;
155 while ( pWindow
&& (!isEnabledInLayout(pWindow
) || !pWindow
->IsInputEnabled()) )
159 pWindow
= ImplGetSubChildWindow( pParent
, n2
, nIndex
);
164 if ( (nIndex
< n2
) && n
)
170 pWindow
= ImplGetSubChildWindow( pParent
, n
, nIndex
);
172 while ( pWindow
&& n
&& (!isEnabledInLayout(pWindow
) || !pWindow
->IsInputEnabled()) );
178 static vcl::Window
* ImplGetNextWindow( vcl::Window
* pParent
, sal_uInt16 n
, sal_uInt16
& nIndex
, bool bTestEnable
)
180 vcl::Window
* pWindow
= ImplGetChildWindow( pParent
, n
+1, nIndex
, bTestEnable
);
184 pWindow
= ImplGetChildWindow( pParent
, n
, nIndex
, bTestEnable
);
191 static bool lcl_ToolBoxTabStop( Window
* pWindow
)
193 ToolBox
* pToolBoxWindow
= static_cast<ToolBox
*>( pWindow
);
195 for ( ToolBox::ImplToolItems::size_type nPos
= 0; nPos
< pToolBoxWindow
->GetItemCount(); nPos
++ )
197 ToolBoxItemId nId
= pToolBoxWindow
->GetItemId( nPos
);
198 if ( pToolBoxWindow
->IsItemVisible( nId
) && pToolBoxWindow
->IsItemEnabled( nId
) )
205 vcl::Window
* Window::ImplGetDlgWindow( sal_uInt16 nIndex
, GetDlgWindowType nType
,
206 sal_uInt16 nFormStart
, sal_uInt16 nFormEnd
,
209 SAL_WARN_IF( (nIndex
< nFormStart
) || (nIndex
> nFormEnd
), "vcl",
210 "Window::ImplGetDlgWindow() - nIndex not in Form" );
212 vcl::Window
* pWindow
= nullptr;
215 sal_uInt16 nStartIndex
;
217 if ( nType
== GetDlgWindowType::Prev
)
222 if ( i
> nFormStart
)
226 pWindow
= ImplGetChildWindow( this, i
, nTemp
, true );
229 if ( (i
== nTemp
) && (pWindow
->GetStyle() & WB_TABSTOP
) )
231 if ( WindowType::TOOLBOX
== pWindow
->GetType() )
233 if ( lcl_ToolBoxTabStop( pWindow
) )
240 while ( i
!= nIndex
);
245 pWindow
= ImplGetChildWindow( this, i
, i
, (nType
== GetDlgWindowType::First
) );
250 if ( nType
== GetDlgWindowType::Next
)
254 pWindow
= ImplGetNextWindow( this, i
, i
, true );
255 if ( (i
> nFormEnd
) || (i
< nFormStart
) )
256 pWindow
= ImplGetChildWindow( this, nFormStart
, i
, true );
259 pWindow
= ImplGetChildWindow( this, nFormStart
, i
, true );
262 if (i
<= nFormEnd
&& pWindow
)
264 // carry the 2nd index, in case all controls are disabled
265 sal_uInt16 nStartIndex2
= i
;
266 sal_uInt16 nOldIndex
= i
+1;
270 if ( pWindow
->GetStyle() & WB_TABSTOP
)
272 if ( WindowType::TOOLBOX
== pWindow
->GetType() )
274 if ( lcl_ToolBoxTabStop( pWindow
) )
280 if( i
== nOldIndex
) // only disabled controls ?
286 if ( (i
> nFormEnd
) || (i
< nFormStart
) )
287 pWindow
= ImplGetChildWindow( this, nFormStart
, i
, true );
289 pWindow
= ImplGetNextWindow( this, i
, i
, true );
291 while (i
!= nStartIndex
&& i
!= nStartIndex2
&& pWindow
);
293 if ( (i
== nStartIndex2
) && pWindow
&&
294 (!(pWindow
->GetStyle() & WB_TABSTOP
) || !isEnabledInLayout(pWindow
)) )
299 if ( nType
== GetDlgWindowType::First
)
303 if ( pWindow
->GetType() == WindowType::TABCONTROL
)
305 vcl::Window
* pNextWindow
= ImplGetDlgWindow( i
, GetDlgWindowType::Next
);
308 if ( pWindow
->IsChild( pNextWindow
) )
309 pWindow
= pNextWindow
;
313 if ( !(pWindow
->GetStyle() & WB_TABSTOP
) )
325 } /* namespace vcl */
327 vcl::Window
* ImplFindDlgCtrlWindow( vcl::Window
* pParent
, vcl::Window
* pWindow
, sal_uInt16
& rIndex
,
328 sal_uInt16
& rFormStart
, sal_uInt16
& rFormEnd
)
330 vcl::Window
* pSWindow
;
331 vcl::Window
* pSecondWindow
= nullptr;
332 vcl::Window
* pTempWindow
= nullptr;
334 sal_uInt16 nSecond_i
= 0;
335 sal_uInt16 nFormStart
= 0;
336 sal_uInt16 nSecondFormStart
= 0;
339 // find focus window in the child list
340 vcl::Window
* pFirstChildWindow
= pSWindow
= ImplGetChildWindow( pParent
, 0, i
, false );
342 if( pWindow
== nullptr )
347 // the DialogControlStart mark is only accepted for the direct children
348 if ( !ImplHasIndirectTabParent( pSWindow
)
349 && pSWindow
->ImplGetWindow()->IsDialogControlStart() )
352 // SecondWindow for composite controls like ComboBoxes and arrays
353 if ( pSWindow
->ImplIsWindowOrChild( pWindow
) )
355 pSecondWindow
= pSWindow
;
357 nSecondFormStart
= nFormStart
;
358 if ( pSWindow
== pWindow
)
362 pSWindow
= ImplGetNextWindow( pParent
, i
, i
, false );
369 // Window not found; we cannot handle it
370 if ( !pSecondWindow
)
374 pSWindow
= pSecondWindow
;
376 nFormStart
= nSecondFormStart
;
382 rFormStart
= nFormStart
;
384 // find end of template
385 sal_Int32 nIteration
= 0;
389 pTempWindow
= ImplGetNextWindow( pParent
, i
, i
, false );
391 // the DialogControlStart mark is only accepted for the direct children
393 || ( pTempWindow
&& !ImplHasIndirectTabParent( pTempWindow
)
394 && pTempWindow
->ImplGetWindow()->IsDialogControlStart() ) )
397 if ( pTempWindow
&& pTempWindow
== pFirstChildWindow
)
399 // It is possible to go through the begin of hierarchy once
400 // while looking for DialogControlStart mark.
401 // If it happens second time, it looks like an endless loop,
402 // that should be impossible, but just for the case...
404 if ( nIteration
>= 2 )
406 // this is an unexpected scenario
407 SAL_WARN( "vcl", "It seems to be an endless loop!" );
413 while ( pTempWindow
);
419 vcl::Window
* ImplFindAccelWindow( vcl::Window
* pParent
, sal_uInt16
& rIndex
, sal_Unicode cCharCode
,
420 sal_uInt16 nFormStart
, sal_uInt16 nFormEnd
, bool bCheckEnable
)
422 SAL_WARN_IF( (rIndex
< nFormStart
) || (rIndex
> nFormEnd
), "vcl",
423 "Window::ImplFindAccelWindow() - rIndex not in Form" );
425 sal_Unicode cCompareChar
;
426 sal_uInt16 nStart
= rIndex
;
427 sal_uInt16 i
= rIndex
;
428 vcl::Window
* pWindow
;
430 uno::Reference
<i18n::XCharacterClassification
> const& xCharClass(ImplGetCharClass());
432 const css::lang::Locale
& rLocale
= Application::GetSettings().GetUILanguageTag().getLocale();
433 cCharCode
= xCharClass
->toUpper( OUString(cCharCode
), 0, 1, rLocale
)[0];
436 pWindow
= ImplGetNextWindow( pParent
, i
, i
, true );
438 pWindow
= ImplGetChildWindow( pParent
, nFormStart
, i
, true );
441 const OUString aStr
= pWindow
->GetText();
442 sal_Int32 nPos
= aStr
.indexOf( '~' );
445 cCompareChar
= aStr
[nPos
+1];
446 cCompareChar
= xCharClass
->toUpper( OUString(cCompareChar
), 0, 1, rLocale
)[0];
447 if ( cCompareChar
== cCharCode
)
449 if (pWindow
->GetType() == WindowType::FIXEDTEXT
)
451 FixedText
*pFixedText
= static_cast<FixedText
*>(pWindow
);
452 vcl::Window
*pMnemonicWidget
= pFixedText
->get_mnemonic_widget();
453 SAL_WARN_IF(isContainerWindow(pFixedText
->GetParent()) && !pMnemonicWidget
,
454 "vcl.a11y", "label missing mnemonic_widget?");
456 return pMnemonicWidget
;
459 // skip Static-Controls
460 if ( (pWindow
->GetType() == WindowType::FIXEDTEXT
) ||
461 (pWindow
->GetType() == WindowType::FIXEDLINE
) ||
462 (pWindow
->GetType() == WindowType::GROUPBOX
) )
463 pWindow
= pParent
->ImplGetDlgWindow( i
, GetDlgWindowType::Next
);
467 nPos
= aStr
.indexOf( '~', nPos
+1 );
470 // #i93011# it would have made sense to have this really recursive
471 // right from the start. However this would cause unpredictable side effects now
472 // so instead we have a style bit for some child windows, that want their
473 // children checked for accelerators
474 if( (pWindow
->GetStyle() & WB_CHILDDLGCTRL
) != 0 )
476 sal_uInt16 nChildIndex
;
477 sal_uInt16 nChildFormStart
;
478 sal_uInt16 nChildFormEnd
;
480 // get form start and end
481 ::ImplFindDlgCtrlWindow( pWindow
, nullptr,
482 nChildIndex
, nChildFormStart
, nChildFormEnd
);
483 vcl::Window
* pAccelWin
= ImplFindAccelWindow( pWindow
, nChildIndex
, cCharCode
,
484 nChildFormStart
, nChildFormEnd
,
495 pWindow
= ImplGetNextWindow( pParent
, i
, i
, bCheckEnable
);
497 pWindow
= ImplGetChildWindow( pParent
, nFormStart
, i
, bCheckEnable
);
500 pWindow
= ImplGetChildWindow( pParent
, nFormStart
, i
, bCheckEnable
);
508 void Window::SetMnemonicActivateHdl(const Link
<vcl::Window
&, bool>& rLink
)
510 if (mpWindowImpl
) // may be called after dispose
512 mpWindowImpl
->maMnemonicActivateHdl
= rLink
;
516 void Window::ImplControlFocus( GetFocusFlags nFlags
)
518 if ( nFlags
& GetFocusFlags::Mnemonic
)
520 if (mpWindowImpl
->maMnemonicActivateHdl
.Call(*this))
523 const bool bUniqueMnemonic(nFlags
& GetFocusFlags::UniqueMnemonic
);
525 if ( GetType() == WindowType::RADIOBUTTON
)
527 if (bUniqueMnemonic
&& !static_cast<RadioButton
*>(this)->IsChecked())
528 static_cast<RadioButton
*>(this)->ImplCallClick( true, nFlags
);
530 ImplGrabFocus( nFlags
);
534 ImplGrabFocus( nFlags
);
537 if ( GetType() == WindowType::CHECKBOX
)
538 static_cast<CheckBox
*>(this)->ImplCheck();
539 else if ( mpWindowImpl
->mbPushButton
)
541 static_cast<PushButton
*>(this)->SetPressed( true );
542 static_cast<PushButton
*>(this)->SetPressed( false );
543 static_cast<PushButton
*>(this)->Click();
550 if ( GetType() == WindowType::RADIOBUTTON
)
552 if ( !static_cast<RadioButton
*>(this)->IsChecked() )
553 static_cast<RadioButton
*>(this)->ImplCallClick( true, nFlags
);
555 ImplGrabFocus( nFlags
);
558 ImplGrabFocus( nFlags
);
562 } /* namespace vcl */
566 bool isSuitableDestination(vcl::Window
const *pWindow
)
568 return (pWindow
&& isVisibleInLayout(pWindow
) &&
569 isEnabledInLayout(pWindow
) && pWindow
->IsInputEnabled() &&
570 //Pure window shouldn't get window after controls such as
572 (pWindow
->GetType() != WindowType::WINDOW
&&
573 pWindow
->GetType() != WindowType::WORKWINDOW
&& pWindow
->GetType() != WindowType::CONTROL
)
577 bool focusNextInGroup(const std::vector
<VclPtr
<RadioButton
> >::iterator
& aStart
, std::vector
<VclPtr
<RadioButton
> > &rGroup
)
579 std::vector
<VclPtr
<RadioButton
> >::iterator
aI(aStart
);
581 if (aStart
!= rGroup
.end())
584 aI
= std::find_if(aI
, rGroup
.end(), isSuitableDestination
);
585 if (aI
!= rGroup
.end())
587 vcl::Window
*pWindow
= *aI
;
588 pWindow
->ImplControlFocus( GetFocusFlags::CURSOR
| GetFocusFlags::Forward
);
591 aI
= std::find_if(rGroup
.begin(), aStart
, isSuitableDestination
);
594 vcl::Window
*pWindow
= *aI
;
595 pWindow
->ImplControlFocus( GetFocusFlags::CURSOR
| GetFocusFlags::Forward
);
601 bool nextInGroup(RadioButton
*pSourceWindow
, bool bBackward
)
603 std::vector
<VclPtr
<RadioButton
> > aGroup(pSourceWindow
->GetRadioButtonGroup());
605 if (aGroup
.size() < 2) // have to have at last 2 buttons to be a useful group
609 std::reverse(aGroup
.begin(), aGroup
.end());
611 auto aStart(std::find(aGroup
.begin(), aGroup
.end(), VclPtr
<RadioButton
>(pSourceWindow
)));
613 assert(aStart
!= aGroup
.end());
615 return focusNextInGroup(aStart
, aGroup
);
621 bool Window::ImplDlgCtrl( const KeyEvent
& rKEvt
, bool bKeyInput
)
623 vcl::KeyCode aKeyCode
= rKEvt
.GetKeyCode();
624 sal_uInt16 nKeyCode
= aKeyCode
.GetCode();
625 vcl::Window
* pSWindow
;
626 vcl::Window
* pTempWindow
;
627 vcl::Window
* pButtonWindow
;
630 sal_uInt16 iButtonStart
;
633 sal_uInt16 nFormStart
;
635 DialogControlFlags nDlgCtrlFlags
;
637 // we cannot take over control without Focus-window
638 vcl::Window
* pFocusWindow
= Application::GetFocusWindow();
639 if ( !pFocusWindow
|| !ImplIsWindowOrChild( pFocusWindow
) )
642 // find Focus-Window in the child list
643 pSWindow
= ::ImplFindDlgCtrlWindow( this, pFocusWindow
,
644 nIndex
, nFormStart
, nFormEnd
);
649 nDlgCtrlFlags
= DialogControlFlags::NONE
;
650 pTempWindow
= pSWindow
;
653 nDlgCtrlFlags
|= pTempWindow
->GetDialogControlFlags();
654 if ( pTempWindow
== this )
656 pTempWindow
= pTempWindow
->ImplGetParent();
658 while ( pTempWindow
);
660 pButtonWindow
= nullptr;
662 if ( nKeyCode
== KEY_RETURN
)
664 // search first for a DefPushButton/CancelButton
665 pButtonWindow
= ImplGetChildWindow( this, nFormStart
, iButton
, true );
666 iButtonStart
= iButton
;
667 while ( pButtonWindow
)
669 if ( (pButtonWindow
->GetStyle() & WB_DEFBUTTON
) &&
670 pButtonWindow
->mpWindowImpl
->mbPushButton
)
673 pButtonWindow
= ImplGetNextWindow( this, iButton
, iButton
, true );
674 if ( (iButton
<= iButtonStart
) || (iButton
> nFormEnd
) )
675 pButtonWindow
= nullptr;
678 if ( bKeyInput
&& !pButtonWindow
&& (nDlgCtrlFlags
& DialogControlFlags::Return
) )
680 GetDlgWindowType nType
;
681 GetFocusFlags nGetFocusFlags
= GetFocusFlags::Tab
;
682 sal_uInt16 nNewIndex
;
684 if ( aKeyCode
.IsShift() )
686 nType
= GetDlgWindowType::Prev
;
687 nGetFocusFlags
|= GetFocusFlags::Backward
;
691 nType
= GetDlgWindowType::Next
;
692 nGetFocusFlags
|= GetFocusFlags::Forward
;
695 pTempWindow
= ImplGetDlgWindow( i
, nType
, nFormStart
, nFormEnd
, &nNewIndex
);
696 while ( pTempWindow
&& (pTempWindow
!= pSWindow
) )
698 if ( !pTempWindow
->mpWindowImpl
->mbPushButton
)
701 if ( nType
== GetDlgWindowType::Prev
)
703 if ( nNewIndex
> iStart
)
704 nGetFocusFlags
|= GetFocusFlags::Around
;
708 if ( nNewIndex
< iStart
)
709 nGetFocusFlags
|= GetFocusFlags::Around
;
711 pTempWindow
->ImplControlFocus( nGetFocusFlags
);
717 pTempWindow
= ImplGetDlgWindow( i
, nType
, nFormStart
, nFormEnd
, &nNewIndex
);
719 if ( (i
<= iStart
) || (i
> nFormEnd
) )
720 pTempWindow
= nullptr;
722 // if this is the same window, simulate a Get/LoseFocus,
723 // in case AROUND is being processed
724 if ( pTempWindow
&& (pTempWindow
== pSWindow
) )
726 NotifyEvent
aNEvt1( NotifyEventType::LOSEFOCUS
, pSWindow
);
727 if ( !ImplCallPreNotify( aNEvt1
) )
728 pSWindow
->CompatLoseFocus();
729 pSWindow
->mpWindowImpl
->mnGetFocusFlags
= nGetFocusFlags
| GetFocusFlags::Around
;
730 NotifyEvent
aNEvt2( NotifyEventType::GETFOCUS
, pSWindow
);
731 if ( !ImplCallPreNotify( aNEvt2
) )
732 pSWindow
->CompatGetFocus();
733 pSWindow
->mpWindowImpl
->mnGetFocusFlags
= GetFocusFlags::NONE
;
738 else if ( nKeyCode
== KEY_ESCAPE
)
740 // search first for a DefPushButton/CancelButton
741 pButtonWindow
= ImplGetChildWindow( this, nFormStart
, iButton
, true );
742 iButtonStart
= iButton
;
743 while ( pButtonWindow
)
745 if ( pButtonWindow
->GetType() == WindowType::CANCELBUTTON
)
748 pButtonWindow
= ImplGetNextWindow( this, iButton
, iButton
, true );
749 if ( (iButton
<= iButtonStart
) || (iButton
> nFormEnd
) )
750 pButtonWindow
= nullptr;
753 if ( bKeyInput
&& mpWindowImpl
->mpDlgCtrlDownWindow
)
755 if ( mpWindowImpl
->mpDlgCtrlDownWindow
.get() != pButtonWindow
)
757 static_cast<PushButton
*>(mpWindowImpl
->mpDlgCtrlDownWindow
.get())->SetPressed( false );
758 mpWindowImpl
->mpDlgCtrlDownWindow
= nullptr;
763 else if ( bKeyInput
)
765 if ( nKeyCode
== KEY_TAB
)
767 // do not skip Alt key, for MS Windows
768 if ( !aKeyCode
.IsMod2() )
770 sal_uInt16 nNewIndex
;
773 // for Ctrl-Tab check if we want to jump to next template
774 if ( aKeyCode
.IsMod1() )
777 vcl::Window
* pFormFirstWindow
= nullptr;
778 vcl::Window
* pLastFormFirstWindow
= nullptr;
779 pTempWindow
= ImplGetChildWindow( this, 0, iTemp
, false );
780 vcl::Window
* pPrevFirstFormFirstWindow
= nullptr;
781 vcl::Window
* pFirstFormFirstWindow
= pTempWindow
;
782 while ( pTempWindow
)
784 if ( pTempWindow
->ImplGetWindow()->IsDialogControlStart() )
788 if ( aKeyCode
.IsShift() )
790 if ( iTemp
<= nIndex
)
791 pFormFirstWindow
= pPrevFirstFormFirstWindow
;
792 pPrevFirstFormFirstWindow
= pTempWindow
;
796 if ( (iTemp
> nIndex
) && !pFormFirstWindow
)
797 pFormFirstWindow
= pTempWindow
;
799 pLastFormFirstWindow
= pTempWindow
;
802 pTempWindow
= ImplGetNextWindow( this, iTemp
, iTemp
, false );
804 pTempWindow
= nullptr;
809 if ( !pFormFirstWindow
)
811 if ( aKeyCode
.IsShift() )
812 pFormFirstWindow
= pLastFormFirstWindow
;
814 pFormFirstWindow
= pFirstFormFirstWindow
;
817 sal_uInt16 nFoundFormStart
= 0;
818 sal_uInt16 nFoundFormEnd
= 0;
819 sal_uInt16 nTempIndex
= 0;
820 if ( ::ImplFindDlgCtrlWindow( this, pFormFirstWindow
, nTempIndex
,
821 nFoundFormStart
, nFoundFormEnd
) )
823 nTempIndex
= nFoundFormStart
;
824 pFormFirstWindow
= ImplGetDlgWindow( nTempIndex
, GetDlgWindowType::First
, nFoundFormStart
, nFoundFormEnd
);
825 if ( pFormFirstWindow
)
827 pFormFirstWindow
->ImplControlFocus();
836 // Only use Ctrl-TAB if it was allowed for the whole
837 // dialog or for the current control (#103667#)
838 if (!aKeyCode
.IsMod1() || (pSWindow
->GetStyle() & WB_NODIALOGCONTROL
))
840 GetDlgWindowType nType
;
841 GetFocusFlags nGetFocusFlags
= GetFocusFlags::Tab
;
842 if ( aKeyCode
.IsShift() )
844 nType
= GetDlgWindowType::Prev
;
845 nGetFocusFlags
|= GetFocusFlags::Backward
;
849 nType
= GetDlgWindowType::Next
;
850 nGetFocusFlags
|= GetFocusFlags::Forward
;
852 vcl::Window
* pWindow
= ImplGetDlgWindow( i
, nType
, nFormStart
, nFormEnd
, &nNewIndex
);
853 // if this is the same window, simulate a Get/LoseFocus,
854 // in case AROUND is being processed
855 if ( pWindow
== pSWindow
)
857 NotifyEvent
aNEvt1( NotifyEventType::LOSEFOCUS
, pSWindow
);
858 if ( !ImplCallPreNotify( aNEvt1
) )
859 pSWindow
->CompatLoseFocus();
860 pSWindow
->mpWindowImpl
->mnGetFocusFlags
= nGetFocusFlags
| GetFocusFlags::Around
;
861 NotifyEvent
aNEvt2( NotifyEventType::GETFOCUS
, pSWindow
);
862 if ( !ImplCallPreNotify( aNEvt2
) )
863 pSWindow
->CompatGetFocus();
864 pSWindow
->mpWindowImpl
->mnGetFocusFlags
= GetFocusFlags::NONE
;
870 if ( nType
== GetDlgWindowType::Prev
)
873 nGetFocusFlags
|= GetFocusFlags::Around
;
878 nGetFocusFlags
|= GetFocusFlags::Around
;
880 pWindow
->ImplControlFocus( nGetFocusFlags
);
887 else if ( (nKeyCode
== KEY_LEFT
) || (nKeyCode
== KEY_UP
) )
889 if (pSWindow
->GetType() == WindowType::RADIOBUTTON
)
890 return nextInGroup(static_cast<RadioButton
*>(pSWindow
), true);
893 WinBits nStyle
= pSWindow
->GetStyle();
894 if ( !(nStyle
& WB_GROUP
) )
896 vcl::Window
* pWindow
= prevLogicalChildOfParent(this, pSWindow
);
899 pWindow
= pWindow
->ImplGetWindow();
901 nStyle
= pWindow
->GetStyle();
903 if (isSuitableDestination(pWindow
))
905 if ( pWindow
!= pSWindow
)
906 pWindow
->ImplControlFocus( GetFocusFlags::CURSOR
| GetFocusFlags::Backward
);
910 if ( nStyle
& WB_GROUP
)
913 pWindow
= prevLogicalChildOfParent(this, pWindow
);
918 else if ( (nKeyCode
== KEY_RIGHT
) || (nKeyCode
== KEY_DOWN
) )
920 if (pSWindow
->GetType() == WindowType::RADIOBUTTON
)
921 return nextInGroup(static_cast<RadioButton
*>(pSWindow
), false);
924 vcl::Window
* pWindow
= nextLogicalChildOfParent(this, pSWindow
);
927 pWindow
= pWindow
->ImplGetWindow();
929 WinBits nStyle
= pWindow
->GetStyle();
931 if ( nStyle
& WB_GROUP
)
934 if (isSuitableDestination(pWindow
))
936 pWindow
->ImplControlFocus( GetFocusFlags::CURSOR
| GetFocusFlags::Backward
);
940 pWindow
= nextLogicalChildOfParent(this, pWindow
);
944 else if (aKeyCode
.IsMod2()) // tdf#151385
946 sal_Unicode c
= rKEvt
.GetCharCode();
949 pSWindow
= ::ImplFindAccelWindow( this, i
, c
, nFormStart
, nFormEnd
);
952 GetFocusFlags nGetFocusFlags
= GetFocusFlags::Mnemonic
;
953 if ( pSWindow
== ::ImplFindAccelWindow( this, i
, c
, nFormStart
, nFormEnd
) )
954 nGetFocusFlags
|= GetFocusFlags::UniqueMnemonic
;
955 pSWindow
->ImplControlFocus( nGetFocusFlags
);
962 if (isSuitableDestination(pButtonWindow
))
966 if ( mpWindowImpl
->mpDlgCtrlDownWindow
&& (mpWindowImpl
->mpDlgCtrlDownWindow
.get() != pButtonWindow
) )
968 static_cast<PushButton
*>(mpWindowImpl
->mpDlgCtrlDownWindow
.get())->SetPressed( false );
969 mpWindowImpl
->mpDlgCtrlDownWindow
= nullptr;
972 static_cast<PushButton
*>(pButtonWindow
)->SetPressed( true );
973 mpWindowImpl
->mpDlgCtrlDownWindow
= pButtonWindow
;
975 else if ( mpWindowImpl
->mpDlgCtrlDownWindow
.get() == pButtonWindow
)
977 mpWindowImpl
->mpDlgCtrlDownWindow
= nullptr;
978 static_cast<PushButton
*>(pButtonWindow
)->SetPressed( false );
979 static_cast<PushButton
*>(pButtonWindow
)->Click();
988 // checks if this window has dialog control
989 bool Window::ImplHasDlgCtrl() const
991 vcl::Window
* pDlgCtrlParent
;
993 // lookup window for dialog control
994 pDlgCtrlParent
= ImplGetParent();
995 while ( pDlgCtrlParent
&&
996 !pDlgCtrlParent
->ImplIsOverlapWindow() &&
997 ((pDlgCtrlParent
->GetStyle() & (WB_DIALOGCONTROL
| WB_NODIALOGCONTROL
)) != WB_DIALOGCONTROL
) )
998 pDlgCtrlParent
= pDlgCtrlParent
->ImplGetParent();
1000 return pDlgCtrlParent
&& ((pDlgCtrlParent
->GetStyle() & (WB_DIALOGCONTROL
| WB_NODIALOGCONTROL
)) == WB_DIALOGCONTROL
);
1003 void Window::ImplDlgCtrlNextWindow()
1005 vcl::Window
* pDlgCtrlParent
;
1006 vcl::Window
* pDlgCtrl
;
1007 vcl::Window
* pSWindow
;
1009 sal_uInt16 nFormStart
;
1010 sal_uInt16 nFormEnd
;
1012 // lookup window for dialog control
1014 pDlgCtrlParent
= ImplGetParent();
1015 while ( pDlgCtrlParent
&&
1016 !pDlgCtrlParent
->ImplIsOverlapWindow() &&
1017 ((pDlgCtrlParent
->GetStyle() & (WB_DIALOGCONTROL
| WB_NODIALOGCONTROL
)) != WB_DIALOGCONTROL
) )
1018 pDlgCtrlParent
= pDlgCtrlParent
->ImplGetParent();
1020 if ( !pDlgCtrlParent
|| (GetStyle() & WB_NODIALOGCONTROL
) || ((pDlgCtrlParent
->GetStyle() & (WB_DIALOGCONTROL
| WB_NODIALOGCONTROL
)) != WB_DIALOGCONTROL
) )
1023 // lookup window in child list
1024 pSWindow
= ::ImplFindDlgCtrlWindow( pDlgCtrlParent
, pDlgCtrl
,
1025 nIndex
, nFormStart
, nFormEnd
);
1029 vcl::Window
* pWindow
= pDlgCtrlParent
->ImplGetDlgWindow( nIndex
, GetDlgWindowType::Next
, nFormStart
, nFormEnd
);
1030 if ( pWindow
&& (pWindow
!= pSWindow
) )
1031 pWindow
->ImplControlFocus();
1034 static void ImplDlgCtrlUpdateDefButton( vcl::Window
* pParent
, vcl::Window
* pFocusWindow
,
1037 PushButton
* pOldDefButton
= nullptr;
1038 PushButton
* pNewDefButton
= nullptr;
1039 vcl::Window
* pSWindow
;
1041 sal_uInt16 nFormStart
;
1042 sal_uInt16 nFormEnd
;
1045 pSWindow
= ::ImplFindDlgCtrlWindow( pParent
, pFocusWindow
, i
, nFormStart
, nFormEnd
);
1052 pSWindow
= ImplGetChildWindow( pParent
, nFormStart
, i
, false );
1055 if ( pSWindow
->ImplIsPushButton() )
1057 PushButton
* pPushButton
= static_cast<PushButton
*>(pSWindow
);
1058 if ( pPushButton
->ImplIsDefButton() )
1059 pOldDefButton
= pPushButton
;
1060 if ( pPushButton
->HasChildPathFocus() )
1061 pNewDefButton
= pPushButton
;
1062 else if ( !pNewDefButton
&& (pPushButton
->GetStyle() & WB_DEFBUTTON
) )
1063 pNewDefButton
= pPushButton
;
1066 pSWindow
= ImplGetNextWindow( pParent
, i
, i
, false );
1067 if ( !i
|| (i
> nFormEnd
) )
1074 vcl::Window
* pNewFocusWindow
= Application::GetFocusWindow();
1075 if ( !pNewFocusWindow
|| !pParent
->ImplIsWindowOrChild( pNewFocusWindow
) )
1076 pNewDefButton
= nullptr;
1077 else if ( !::ImplFindDlgCtrlWindow( pParent
, pNewFocusWindow
, i
, nDummy
, nDummy
) ||
1078 (i
< nFormStart
) || (i
> nFormEnd
) )
1079 pNewDefButton
= nullptr;
1082 if ( pOldDefButton
!= pNewDefButton
)
1084 if ( pOldDefButton
)
1085 pOldDefButton
->ImplSetDefButton( false );
1086 if ( pNewDefButton
)
1087 pNewDefButton
->ImplSetDefButton( true );
1091 void Window::ImplDlgCtrlFocusChanged( vcl::Window
* pWindow
, bool bGetFocus
)
1093 if ( mpWindowImpl
->mpDlgCtrlDownWindow
&& !bGetFocus
)
1095 static_cast<PushButton
*>(mpWindowImpl
->mpDlgCtrlDownWindow
.get())->SetPressed( false );
1096 mpWindowImpl
->mpDlgCtrlDownWindow
= nullptr;
1099 ImplDlgCtrlUpdateDefButton( this, pWindow
, bGetFocus
);
1102 vcl::Window
* Window::ImplFindDlgCtrlWindow( vcl::Window
* pWindow
)
1105 sal_uInt16 nFormStart
;
1106 sal_uInt16 nFormEnd
;
1108 // find Focus-Window in the Child-List and return
1109 return ::ImplFindDlgCtrlWindow( this, pWindow
, nIndex
, nFormStart
, nFormEnd
);
1112 KeyEvent
Window::GetActivationKey() const
1116 sal_Unicode nAccel
= getAccel( GetText() );
1119 vcl::Window
* pWindow
= GetAccessibleRelationLabeledBy();
1121 nAccel
= getAccel( pWindow
->GetText() );
1125 sal_uInt16 nCode
= 0;
1126 if( nAccel
>= 'a' && nAccel
<= 'z' )
1127 nCode
= KEY_A
+ (nAccel
-'a');
1128 else if( nAccel
>= 'A' && nAccel
<= 'Z' )
1129 nCode
= KEY_A
+ (nAccel
-'A');
1130 else if( nAccel
>= '0' && nAccel
<= '9' )
1131 nCode
= KEY_0
+ (nAccel
-'0');
1132 else if( nAccel
== '.' )
1134 else if( nAccel
== '-' )
1135 nCode
= KEY_SUBTRACT
;
1136 vcl::KeyCode
aKeyCode( nCode
, false, false, true, false );
1137 aKeyEvent
= KeyEvent( nAccel
, aKeyCode
);
1142 } /* namespace vcl */
1144 sal_Unicode
getAccel( std::u16string_view rStr
)
1146 sal_Unicode nChar
= 0;
1150 nPos
= rStr
.find( '~', nPos
);
1151 if( nPos
!= std::u16string_view::npos
&& nPos
< rStr
.size() )
1152 nChar
= rStr
[ ++nPos
];
1155 } while( nChar
== '~' );
1159 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */