bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / source / window / dlgctrl.cxx
blob8db4402d9ef8ffe61802eaef6126b787c8c87e8f
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 .
21 #include <svdata.hxx>
22 #include <window.h>
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;
59 if ( pResult )
61 vcl::Window* pNonLayoutParent = getNonLayoutParent(pResult);
62 while ( pNonLayoutParent && ( pResult->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) )
64 pResult = pNonLayoutParent;
65 pNonLayoutParent = getNonLayoutParent(pResult);
69 return 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());
80 if (pTempTabPage)
82 vcl::Window* pTempWindow = pTabControl->GetWindow(GetWindowType::FirstChild);
83 while (pTempWindow)
85 if (pTempWindow->ImplGetWindow() == pTempTabPage)
87 return const_cast<TabPage*>(pTempTabPage);
89 pTempWindow = nextLogicalChildOfParent(pTabControl, pTempWindow);
93 return nullptr;
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);
113 while (pWindow)
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)
123 if (n == nIndex)
124 return pWindow;
125 ++nIndex;
127 if (pWindow->GetStyle() & (WB_DIALOGCONTROL | WB_CHILDDLGCTRL))
128 pFoundWindow = ImplGetSubChildWindow(pWindow, n, nIndex);
129 else
130 pFoundWindow = pWindow;
132 if (n == nIndex)
133 return pFoundWindow;
134 ++nIndex;
137 pWindow = nextLogicalChildOfParent(pParent, pNextWindow);
138 pNextWindow = pWindow;
141 --nIndex;
142 assert(!pFoundWindow || (pFoundWindow == pFoundWindow->ImplGetWindow()));
143 return pFoundWindow;
146 vcl::Window* ImplGetChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
148 pParent = ImplGetTopParentOfTabHierarchy( pParent );
150 nIndex = 0;
151 vcl::Window* pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
152 if ( bTestEnable )
154 sal_uInt16 n2 = nIndex;
155 while ( pWindow && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) )
157 n2 = nIndex+1;
158 nIndex = 0;
159 pWindow = ImplGetSubChildWindow( pParent, n2, nIndex );
160 if ( nIndex < n2 )
161 break;
164 if ( (nIndex < n2) && n )
168 n--;
169 nIndex = 0;
170 pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
172 while ( pWindow && n && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) );
175 return pWindow;
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 );
181 if ( n == nIndex )
183 n = 0;
184 pWindow = ImplGetChildWindow( pParent, n, nIndex, bTestEnable );
186 return pWindow;
189 namespace vcl {
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 ) )
199 return true;
202 return false;
205 vcl::Window* Window::ImplGetDlgWindow( sal_uInt16 nIndex, GetDlgWindowType nType,
206 sal_uInt16 nFormStart, sal_uInt16 nFormEnd,
207 sal_uInt16* pIndex )
209 SAL_WARN_IF( (nIndex < nFormStart) || (nIndex > nFormEnd), "vcl",
210 "Window::ImplGetDlgWindow() - nIndex not in Form" );
212 vcl::Window* pWindow = nullptr;
213 sal_uInt16 i;
214 sal_uInt16 nTemp;
215 sal_uInt16 nStartIndex;
217 if ( nType == GetDlgWindowType::Prev )
219 i = nIndex;
222 if ( i > nFormStart )
223 i--;
224 else
225 i = nFormEnd;
226 pWindow = ImplGetChildWindow( this, i, nTemp, true );
227 if ( !pWindow )
228 break;
229 if ( (i == nTemp) && (pWindow->GetStyle() & WB_TABSTOP) )
231 if ( WindowType::TOOLBOX == pWindow->GetType() )
233 if ( lcl_ToolBoxTabStop( pWindow ) )
234 break;
236 else
237 break;
240 while ( i != nIndex );
242 else
244 i = nIndex;
245 pWindow = ImplGetChildWindow( this, i, i, (nType == GetDlgWindowType::First) );
246 if ( pWindow )
248 nStartIndex = i;
250 if ( nType == GetDlgWindowType::Next )
252 if ( i < nFormEnd )
254 pWindow = ImplGetNextWindow( this, i, i, true );
255 if ( (i > nFormEnd) || (i < nFormStart) )
256 pWindow = ImplGetChildWindow( this, nFormStart, i, true );
258 else
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 ) )
275 break;
277 else
278 break;
280 if( i == nOldIndex ) // only disabled controls ?
282 i = nStartIndex2;
283 break;
285 nOldIndex = i;
286 if ( (i > nFormEnd) || (i < nFormStart) )
287 pWindow = ImplGetChildWindow( this, nFormStart, i, true );
288 else
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)) )
295 i = nStartIndex;
299 if ( nType == GetDlgWindowType::First )
301 if ( pWindow )
303 if ( pWindow->GetType() == WindowType::TABCONTROL )
305 vcl::Window* pNextWindow = ImplGetDlgWindow( i, GetDlgWindowType::Next );
306 if ( pNextWindow )
308 if ( pWindow->IsChild( pNextWindow ) )
309 pWindow = pNextWindow;
313 if ( !(pWindow->GetStyle() & WB_TABSTOP) )
314 pWindow = nullptr;
319 if ( pIndex )
320 *pIndex = i;
322 return pWindow;
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;
333 sal_uInt16 i;
334 sal_uInt16 nSecond_i = 0;
335 sal_uInt16 nFormStart = 0;
336 sal_uInt16 nSecondFormStart = 0;
337 sal_uInt16 nFormEnd;
339 // find focus window in the child list
340 vcl::Window* pFirstChildWindow = pSWindow = ImplGetChildWindow( pParent, 0, i, false );
342 if( pWindow == nullptr )
343 pWindow = pSWindow;
345 while ( pSWindow )
347 // the DialogControlStart mark is only accepted for the direct children
348 if ( !ImplHasIndirectTabParent( pSWindow )
349 && pSWindow->ImplGetWindow()->IsDialogControlStart() )
350 nFormStart = i;
352 // SecondWindow for composite controls like ComboBoxes and arrays
353 if ( pSWindow->ImplIsWindowOrChild( pWindow ) )
355 pSecondWindow = pSWindow;
356 nSecond_i = i;
357 nSecondFormStart = nFormStart;
358 if ( pSWindow == pWindow )
359 break;
362 pSWindow = ImplGetNextWindow( pParent, i, i, false );
363 if ( !i )
364 pSWindow = nullptr;
367 if ( !pSWindow )
369 // Window not found; we cannot handle it
370 if ( !pSecondWindow )
371 return nullptr;
372 else
374 pSWindow = pSecondWindow;
375 i = nSecond_i;
376 nFormStart = nSecondFormStart;
380 // initialize
381 rIndex = i;
382 rFormStart = nFormStart;
384 // find end of template
385 sal_Int32 nIteration = 0;
388 nFormEnd = i;
389 pTempWindow = ImplGetNextWindow( pParent, i, i, false );
391 // the DialogControlStart mark is only accepted for the direct children
392 if ( !i
393 || ( pTempWindow && !ImplHasIndirectTabParent( pTempWindow )
394 && pTempWindow->ImplGetWindow()->IsDialogControlStart() ) )
395 break;
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...
403 nIteration++;
404 if ( nIteration >= 2 )
406 // this is an unexpected scenario
407 SAL_WARN( "vcl", "It seems to be an endless loop!" );
408 rFormStart = 0;
409 break;
413 while ( pTempWindow );
414 rFormEnd = nFormEnd;
416 return pSWindow;
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];
435 if ( i < nFormEnd )
436 pWindow = ImplGetNextWindow( pParent, i, i, true );
437 else
438 pWindow = ImplGetChildWindow( pParent, nFormStart, i, true );
439 while( pWindow )
441 const OUString aStr = pWindow->GetText();
442 sal_Int32 nPos = aStr.indexOf( '~' );
443 while (nPos != -1)
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?");
455 if (pMnemonicWidget)
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 );
464 rIndex = i;
465 return pWindow;
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,
485 bCheckEnable );
486 if( pAccelWin )
487 return pAccelWin;
490 if ( i == nStart )
491 break;
493 if ( i < nFormEnd )
495 pWindow = ImplGetNextWindow( pParent, i, i, bCheckEnable );
496 if( ! pWindow )
497 pWindow = ImplGetChildWindow( pParent, nFormStart, i, bCheckEnable );
499 else
500 pWindow = ImplGetChildWindow( pParent, nFormStart, i, bCheckEnable );
503 return nullptr;
506 namespace vcl {
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))
521 return;
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 );
529 else
530 ImplGrabFocus( nFlags );
532 else
534 ImplGrabFocus( nFlags );
535 if (bUniqueMnemonic)
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();
548 else
550 if ( GetType() == WindowType::RADIOBUTTON )
552 if ( !static_cast<RadioButton*>(this)->IsChecked() )
553 static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
554 else
555 ImplGrabFocus( nFlags );
557 else
558 ImplGrabFocus( nFlags );
562 } /* namespace vcl */
564 namespace
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
571 //buttons.
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())
582 ++aI;
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 );
589 return true;
591 aI = std::find_if(rGroup.begin(), aStart, isSuitableDestination);
592 if (aI != aStart)
594 vcl::Window *pWindow = *aI;
595 pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
596 return true;
598 return false;
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
606 return false;
608 if (bBackward)
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);
619 namespace vcl {
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;
628 sal_uInt16 i;
629 sal_uInt16 iButton;
630 sal_uInt16 iButtonStart;
631 sal_uInt16 iTemp;
632 sal_uInt16 nIndex;
633 sal_uInt16 nFormStart;
634 sal_uInt16 nFormEnd;
635 DialogControlFlags nDlgCtrlFlags;
637 // we cannot take over control without Focus-window
638 vcl::Window* pFocusWindow = Application::GetFocusWindow();
639 if ( !pFocusWindow || !ImplIsWindowOrChild( pFocusWindow ) )
640 return false;
642 // find Focus-Window in the child list
643 pSWindow = ::ImplFindDlgCtrlWindow( this, pFocusWindow,
644 nIndex, nFormStart, nFormEnd );
645 if ( !pSWindow )
646 return false;
647 i = nIndex;
649 nDlgCtrlFlags = DialogControlFlags::NONE;
650 pTempWindow = pSWindow;
653 nDlgCtrlFlags |= pTempWindow->GetDialogControlFlags();
654 if ( pTempWindow == this )
655 break;
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 )
671 break;
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;
683 sal_uInt16 iStart;
684 if ( aKeyCode.IsShift() )
686 nType = GetDlgWindowType::Prev;
687 nGetFocusFlags |= GetFocusFlags::Backward;
689 else
691 nType = GetDlgWindowType::Next;
692 nGetFocusFlags |= GetFocusFlags::Forward;
694 iStart = i;
695 pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
696 while ( pTempWindow && (pTempWindow != pSWindow) )
698 if ( !pTempWindow->mpWindowImpl->mbPushButton )
700 // get Around-Flag
701 if ( nType == GetDlgWindowType::Prev )
703 if ( nNewIndex > iStart )
704 nGetFocusFlags |= GetFocusFlags::Around;
706 else
708 if ( nNewIndex < iStart )
709 nGetFocusFlags |= GetFocusFlags::Around;
711 pTempWindow->ImplControlFocus( nGetFocusFlags );
712 return true;
714 else
716 i = nNewIndex;
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;
734 return true;
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 )
746 break;
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;
759 return true;
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;
771 bool bForm = false;
773 // for Ctrl-Tab check if we want to jump to next template
774 if ( aKeyCode.IsMod1() )
776 // search group
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() )
786 if ( iTemp != 0 )
787 bForm = true;
788 if ( aKeyCode.IsShift() )
790 if ( iTemp <= nIndex )
791 pFormFirstWindow = pPrevFirstFormFirstWindow;
792 pPrevFirstFormFirstWindow = pTempWindow;
794 else
796 if ( (iTemp > nIndex) && !pFormFirstWindow )
797 pFormFirstWindow = pTempWindow;
799 pLastFormFirstWindow = pTempWindow;
802 pTempWindow = ImplGetNextWindow( this, iTemp, iTemp, false );
803 if ( !iTemp )
804 pTempWindow = nullptr;
807 if ( bForm )
809 if ( !pFormFirstWindow )
811 if ( aKeyCode.IsShift() )
812 pFormFirstWindow = pLastFormFirstWindow;
813 else
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();
828 return true;
834 if ( !bForm )
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;
847 else
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;
865 return true;
867 else if ( pWindow )
869 // get Around-Flag
870 if ( nType == GetDlgWindowType::Prev )
872 if ( nNewIndex > i )
873 nGetFocusFlags |= GetFocusFlags::Around;
875 else
877 if ( nNewIndex < i )
878 nGetFocusFlags |= GetFocusFlags::Around;
880 pWindow->ImplControlFocus( nGetFocusFlags );
881 return true;
887 else if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_UP) )
889 if (pSWindow->GetType() == WindowType::RADIOBUTTON)
890 return nextInGroup(static_cast<RadioButton*>(pSWindow), true);
891 else
893 WinBits nStyle = pSWindow->GetStyle();
894 if ( !(nStyle & WB_GROUP) )
896 vcl::Window* pWindow = prevLogicalChildOfParent(this, pSWindow);
897 while ( pWindow )
899 pWindow = pWindow->ImplGetWindow();
901 nStyle = pWindow->GetStyle();
903 if (isSuitableDestination(pWindow))
905 if ( pWindow != pSWindow )
906 pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
907 return true;
910 if ( nStyle & WB_GROUP )
911 break;
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);
922 else
924 vcl::Window* pWindow = nextLogicalChildOfParent(this, pSWindow);
925 while ( pWindow )
927 pWindow = pWindow->ImplGetWindow();
929 WinBits nStyle = pWindow->GetStyle();
931 if ( nStyle & WB_GROUP )
932 break;
934 if (isSuitableDestination(pWindow))
936 pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
937 return true;
940 pWindow = nextLogicalChildOfParent(this, pWindow);
944 else if (aKeyCode.IsMod2()) // tdf#151385
946 sal_Unicode c = rKEvt.GetCharCode();
947 if ( c )
949 pSWindow = ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd );
950 if ( pSWindow )
952 GetFocusFlags nGetFocusFlags = GetFocusFlags::Mnemonic;
953 if ( pSWindow == ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd ) )
954 nGetFocusFlags |= GetFocusFlags::UniqueMnemonic;
955 pSWindow->ImplControlFocus( nGetFocusFlags );
956 return true;
962 if (isSuitableDestination(pButtonWindow))
964 if ( bKeyInput )
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();
982 return true;
985 return false;
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;
1008 sal_uInt16 nIndex;
1009 sal_uInt16 nFormStart;
1010 sal_uInt16 nFormEnd;
1012 // lookup window for dialog control
1013 pDlgCtrl = this;
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) )
1021 return;
1023 // lookup window in child list
1024 pSWindow = ::ImplFindDlgCtrlWindow( pDlgCtrlParent, pDlgCtrl,
1025 nIndex, nFormStart, nFormEnd );
1026 if ( !pSWindow )
1027 return;
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,
1035 bool bGetFocus )
1037 PushButton* pOldDefButton = nullptr;
1038 PushButton* pNewDefButton = nullptr;
1039 vcl::Window* pSWindow;
1040 sal_uInt16 i;
1041 sal_uInt16 nFormStart;
1042 sal_uInt16 nFormEnd;
1044 // find template
1045 pSWindow = ::ImplFindDlgCtrlWindow( pParent, pFocusWindow, i, nFormStart, nFormEnd );
1046 if ( !pSWindow )
1048 nFormStart = 0;
1049 nFormEnd = 0xFFFF;
1052 pSWindow = ImplGetChildWindow( pParent, nFormStart, i, false );
1053 while ( pSWindow )
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) )
1068 pSWindow = nullptr;
1071 if ( !bGetFocus )
1073 sal_uInt16 nDummy;
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 )
1104 sal_uInt16 nIndex;
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
1114 KeyEvent aKeyEvent;
1116 sal_Unicode nAccel = getAccel( GetText() );
1117 if( ! nAccel )
1119 vcl::Window* pWindow = GetAccessibleRelationLabeledBy();
1120 if( pWindow )
1121 nAccel = getAccel( pWindow->GetText() );
1123 if( nAccel )
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 == '.' )
1133 nCode = KEY_POINT;
1134 else if( nAccel == '-' )
1135 nCode = KEY_SUBTRACT;
1136 vcl::KeyCode aKeyCode( nCode, false, false, true, false );
1137 aKeyEvent = KeyEvent( nAccel, aKeyCode );
1139 return aKeyEvent;
1142 } /* namespace vcl */
1144 sal_Unicode getAccel( std::u16string_view rStr )
1146 sal_Unicode nChar = 0;
1147 size_t nPos = 0;
1150 nPos = rStr.find( '~', nPos );
1151 if( nPos != std::u16string_view::npos && nPos < rStr.size() )
1152 nChar = rStr[ ++nPos ];
1153 else
1154 nChar = 0;
1155 } while( nChar == '~' );
1156 return nChar;
1159 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */