nss: upgrade to release 3.73
[LibreOffice.git] / vcl / source / window / dlgctrl.cxx
blob42a4ce82a8a278f2376f90eae6deb652fcef2bb6
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/settings.hxx>
33 #include <sal/log.hxx>
34 #include <i18nlangtag/languagetag.hxx>
36 #include <com/sun/star/i18n/XCharacterClassification.hpp>
38 using namespace ::com::sun::star;
40 static bool ImplHasIndirectTabParent( vcl::Window* pWindow )
42 // The window has indirect tab parent if it is included in tab hierarchy
43 // of the indirect parent window
45 vcl::Window* pNonLayoutParent = getNonLayoutParent(pWindow);
46 return ( pNonLayoutParent
47 && ( pNonLayoutParent->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) );
50 static vcl::Window* ImplGetTopParentOfTabHierarchy( vcl::Window* pParent )
52 // The method allows to find the most close parent containing all the
53 // window from the current tab-hierarchy
54 // The direct parent should be provided as a parameter here
56 vcl::Window* pResult = pParent;
58 if ( pResult )
60 vcl::Window* pNonLayoutParent = getNonLayoutParent(pResult);
61 while ( pNonLayoutParent && ( pResult->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) )
63 pResult = pNonLayoutParent;
64 pNonLayoutParent = getNonLayoutParent(pResult);
68 return pResult;
71 static vcl::Window* ImplGetCurTabWindow(const vcl::Window* pWindow)
73 assert(pWindow->GetType() == WindowType::TABCONTROL);
74 const TabControl* pTabControl = static_cast<const TabControl*>(pWindow);
75 // Check if the TabPage is a Child of the TabControl and still exists (by
76 // walking all child windows); because it could be that the TabPage has been
77 // destroyed already by a Dialog-Dtor, event that the TabControl still exists.
78 const TabPage* pTempTabPage = pTabControl->GetTabPage(pTabControl->GetCurPageId());
79 if (pTempTabPage)
81 vcl::Window* pTempWindow = pTabControl->GetWindow(GetWindowType::FirstChild);
82 while (pTempWindow)
84 if (pTempWindow->ImplGetWindow() == pTempTabPage)
86 return const_cast<TabPage*>(pTempTabPage);
88 pTempWindow = nextLogicalChildOfParent(pTabControl, pTempWindow);
92 return nullptr;
95 static vcl::Window* ImplGetSubChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex )
97 // ignore all windows with mpClientWindow set
98 for (vcl::Window *pNewParent = pParent->ImplGetWindow();
99 pParent != pNewParent; pParent = pNewParent);
101 vcl::Window* pFoundWindow = nullptr;
102 vcl::Window* pWindow = firstLogicalChildOfParent(pParent);
103 vcl::Window* pNextWindow = pWindow;
105 // process just the current page of a tab control
106 if (pWindow && pParent->GetType() == WindowType::TABCONTROL)
108 pWindow = ImplGetCurTabWindow(pParent);
109 pNextWindow = lastLogicalChildOfParent(pParent);
112 while (pWindow)
114 pWindow = pWindow->ImplGetWindow();
116 // skip invisible and disabled windows
117 if (isVisibleInLayout(pWindow))
119 // return the TabControl itself, before handling its page
120 if (pWindow->GetType() == WindowType::TABCONTROL)
122 if (n == nIndex)
123 return pWindow;
124 ++nIndex;
126 if (pWindow->GetStyle() & (WB_DIALOGCONTROL | WB_CHILDDLGCTRL))
127 pFoundWindow = ImplGetSubChildWindow(pWindow, n, nIndex);
128 else
129 pFoundWindow = pWindow;
131 if (n == nIndex)
132 return pFoundWindow;
133 ++nIndex;
136 pWindow = nextLogicalChildOfParent(pParent, pNextWindow);
137 pNextWindow = pWindow;
140 --nIndex;
141 assert(!pFoundWindow || (pFoundWindow == pFoundWindow->ImplGetWindow()));
142 return pFoundWindow;
145 vcl::Window* ImplGetChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
147 pParent = ImplGetTopParentOfTabHierarchy( pParent );
149 nIndex = 0;
150 vcl::Window* pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
151 if ( bTestEnable )
153 sal_uInt16 n2 = nIndex;
154 while ( pWindow && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) )
156 n2 = nIndex+1;
157 nIndex = 0;
158 pWindow = ImplGetSubChildWindow( pParent, n2, nIndex );
159 if ( nIndex < n2 )
160 break;
163 if ( (nIndex < n2) && n )
167 n--;
168 nIndex = 0;
169 pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
171 while ( pWindow && n && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) );
174 return pWindow;
177 static vcl::Window* ImplGetNextWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
179 vcl::Window* pWindow = ImplGetChildWindow( pParent, n+1, nIndex, bTestEnable );
180 if ( n == nIndex )
182 n = 0;
183 pWindow = ImplGetChildWindow( pParent, n, nIndex, bTestEnable );
185 return pWindow;
188 namespace vcl {
190 static bool lcl_ToolBoxTabStop( Window* pWindow )
192 ToolBox* pToolBoxWindow = static_cast<ToolBox*>( pWindow );
194 sal_uInt16 nId;
195 for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < pToolBoxWindow->GetItemCount(); nPos++ )
197 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 if ( GetType() == WindowType::RADIOBUTTON )
525 if ( !static_cast<RadioButton*>(this)->IsChecked() )
526 static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
527 else
528 ImplGrabFocus( nFlags );
530 else
532 ImplGrabFocus( nFlags );
533 if ( nFlags & GetFocusFlags::UniqueMnemonic )
535 if ( GetType() == WindowType::CHECKBOX )
536 static_cast<CheckBox*>(this)->ImplCheck();
537 else if ( mpWindowImpl->mbPushButton )
539 static_cast<PushButton*>(this)->SetPressed( true );
540 static_cast<PushButton*>(this)->SetPressed( false );
541 static_cast<PushButton*>(this)->Click();
546 else
548 if ( GetType() == WindowType::RADIOBUTTON )
550 if ( !static_cast<RadioButton*>(this)->IsChecked() )
551 static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
552 else
553 ImplGrabFocus( nFlags );
555 else
556 ImplGrabFocus( nFlags );
560 } /* namespace vcl */
562 namespace
564 bool isSuitableDestination(vcl::Window const *pWindow)
566 return (pWindow && isVisibleInLayout(pWindow) &&
567 isEnabledInLayout(pWindow) && pWindow->IsInputEnabled() &&
568 //Pure window shouldn't get window after controls such as
569 //buttons.
570 (pWindow->GetType() != WindowType::WINDOW &&
571 pWindow->GetType() != WindowType::WORKWINDOW && pWindow->GetType() != WindowType::CONTROL)
575 bool focusNextInGroup(const std::vector<VclPtr<RadioButton> >::iterator& aStart, std::vector<VclPtr<RadioButton> > &rGroup)
577 std::vector<VclPtr<RadioButton> >::iterator aI(aStart);
579 if (aStart != rGroup.end())
580 ++aI;
582 aI = std::find_if(aI, rGroup.end(), isSuitableDestination);
583 if (aI != rGroup.end())
585 vcl::Window *pWindow = *aI;
586 pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
587 return true;
589 aI = std::find_if(rGroup.begin(), aStart, isSuitableDestination);
590 if (aI != aStart)
592 vcl::Window *pWindow = *aI;
593 pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
594 return true;
596 return false;
599 bool nextInGroup(RadioButton *pSourceWindow, bool bBackward)
601 std::vector<VclPtr<RadioButton> > aGroup(pSourceWindow->GetRadioButtonGroup());
603 if (aGroup.size() < 2) // have to have at last 2 buttons to be a useful group
604 return false;
606 if (bBackward)
607 std::reverse(aGroup.begin(), aGroup.end());
609 auto aStart(std::find(aGroup.begin(), aGroup.end(), VclPtr<RadioButton>(pSourceWindow)));
611 assert(aStart != aGroup.end());
613 return focusNextInGroup(aStart, aGroup);
617 namespace vcl {
619 bool Window::ImplDlgCtrl( const KeyEvent& rKEvt, bool bKeyInput )
621 vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
622 sal_uInt16 nKeyCode = aKeyCode.GetCode();
623 vcl::Window* pSWindow;
624 vcl::Window* pTempWindow;
625 vcl::Window* pButtonWindow;
626 sal_uInt16 i;
627 sal_uInt16 iButton;
628 sal_uInt16 iButtonStart;
629 sal_uInt16 iTemp;
630 sal_uInt16 nIndex;
631 sal_uInt16 nFormStart;
632 sal_uInt16 nFormEnd;
633 DialogControlFlags nDlgCtrlFlags;
635 // we cannot take over control without Focus-window
636 vcl::Window* pFocusWindow = Application::GetFocusWindow();
637 if ( !pFocusWindow || !ImplIsWindowOrChild( pFocusWindow ) )
638 return false;
640 // find Focus-Window in the child list
641 pSWindow = ::ImplFindDlgCtrlWindow( this, pFocusWindow,
642 nIndex, nFormStart, nFormEnd );
643 if ( !pSWindow )
644 return false;
645 i = nIndex;
647 nDlgCtrlFlags = DialogControlFlags::NONE;
648 pTempWindow = pSWindow;
651 nDlgCtrlFlags |= pTempWindow->GetDialogControlFlags();
652 if ( pTempWindow == this )
653 break;
654 pTempWindow = pTempWindow->ImplGetParent();
656 while ( pTempWindow );
658 pButtonWindow = nullptr;
660 if ( nKeyCode == KEY_RETURN )
662 // search first for a DefPushButton/CancelButton
663 pButtonWindow = ImplGetChildWindow( this, nFormStart, iButton, true );
664 iButtonStart = iButton;
665 while ( pButtonWindow )
667 if ( (pButtonWindow->GetStyle() & WB_DEFBUTTON) &&
668 pButtonWindow->mpWindowImpl->mbPushButton )
669 break;
671 pButtonWindow = ImplGetNextWindow( this, iButton, iButton, true );
672 if ( (iButton <= iButtonStart) || (iButton > nFormEnd) )
673 pButtonWindow = nullptr;
676 if ( bKeyInput && !pButtonWindow && (nDlgCtrlFlags & DialogControlFlags::Return) )
678 GetDlgWindowType nType;
679 GetFocusFlags nGetFocusFlags = GetFocusFlags::Tab;
680 sal_uInt16 nNewIndex;
681 sal_uInt16 iStart;
682 if ( aKeyCode.IsShift() )
684 nType = GetDlgWindowType::Prev;
685 nGetFocusFlags |= GetFocusFlags::Backward;
687 else
689 nType = GetDlgWindowType::Next;
690 nGetFocusFlags |= GetFocusFlags::Forward;
692 iStart = i;
693 pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
694 while ( pTempWindow && (pTempWindow != pSWindow) )
696 if ( !pTempWindow->mpWindowImpl->mbPushButton )
698 // get Around-Flag
699 if ( nType == GetDlgWindowType::Prev )
701 if ( nNewIndex > iStart )
702 nGetFocusFlags |= GetFocusFlags::Around;
704 else
706 if ( nNewIndex < iStart )
707 nGetFocusFlags |= GetFocusFlags::Around;
709 pTempWindow->ImplControlFocus( nGetFocusFlags );
710 return true;
712 else
714 i = nNewIndex;
715 pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
717 if ( (i <= iStart) || (i > nFormEnd) )
718 pTempWindow = nullptr;
720 // if this is the same window, simulate a Get/LoseFocus,
721 // in case AROUND is being processed
722 if ( pTempWindow && (pTempWindow == pSWindow) )
724 NotifyEvent aNEvt1( MouseNotifyEvent::LOSEFOCUS, pSWindow );
725 if ( !ImplCallPreNotify( aNEvt1 ) )
726 pSWindow->CompatLoseFocus();
727 pSWindow->mpWindowImpl->mnGetFocusFlags = nGetFocusFlags | GetFocusFlags::Around;
728 NotifyEvent aNEvt2( MouseNotifyEvent::GETFOCUS, pSWindow );
729 if ( !ImplCallPreNotify( aNEvt2 ) )
730 pSWindow->CompatGetFocus();
731 pSWindow->mpWindowImpl->mnGetFocusFlags = GetFocusFlags::NONE;
732 return true;
736 else if ( nKeyCode == KEY_ESCAPE )
738 // search first for a DefPushButton/CancelButton
739 pButtonWindow = ImplGetChildWindow( this, nFormStart, iButton, true );
740 iButtonStart = iButton;
741 while ( pButtonWindow )
743 if ( pButtonWindow->GetType() == WindowType::CANCELBUTTON )
744 break;
746 pButtonWindow = ImplGetNextWindow( this, iButton, iButton, true );
747 if ( (iButton <= iButtonStart) || (iButton > nFormEnd) )
748 pButtonWindow = nullptr;
751 if ( bKeyInput && mpWindowImpl->mpDlgCtrlDownWindow )
753 if ( mpWindowImpl->mpDlgCtrlDownWindow.get() != pButtonWindow )
755 static_cast<PushButton*>(mpWindowImpl->mpDlgCtrlDownWindow.get())->SetPressed( false );
756 mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
757 return true;
761 else if ( bKeyInput )
763 if ( nKeyCode == KEY_TAB )
765 // do not skip Alt key, for MS Windows
766 if ( !aKeyCode.IsMod2() )
768 sal_uInt16 nNewIndex;
769 bool bForm = false;
771 // for Ctrl-Tab check if we want to jump to next template
772 if ( aKeyCode.IsMod1() )
774 // search group
775 vcl::Window* pFormFirstWindow = nullptr;
776 vcl::Window* pLastFormFirstWindow = nullptr;
777 pTempWindow = ImplGetChildWindow( this, 0, iTemp, false );
778 vcl::Window* pPrevFirstFormFirstWindow = nullptr;
779 vcl::Window* pFirstFormFirstWindow = pTempWindow;
780 while ( pTempWindow )
782 if ( pTempWindow->ImplGetWindow()->IsDialogControlStart() )
784 if ( iTemp != 0 )
785 bForm = true;
786 if ( aKeyCode.IsShift() )
788 if ( iTemp <= nIndex )
789 pFormFirstWindow = pPrevFirstFormFirstWindow;
790 pPrevFirstFormFirstWindow = pTempWindow;
792 else
794 if ( (iTemp > nIndex) && !pFormFirstWindow )
795 pFormFirstWindow = pTempWindow;
797 pLastFormFirstWindow = pTempWindow;
800 pTempWindow = ImplGetNextWindow( this, iTemp, iTemp, false );
801 if ( !iTemp )
802 pTempWindow = nullptr;
805 if ( bForm )
807 if ( !pFormFirstWindow )
809 if ( aKeyCode.IsShift() )
810 pFormFirstWindow = pLastFormFirstWindow;
811 else
812 pFormFirstWindow = pFirstFormFirstWindow;
815 sal_uInt16 nFoundFormStart = 0;
816 sal_uInt16 nFoundFormEnd = 0;
817 sal_uInt16 nTempIndex = 0;
818 if ( ::ImplFindDlgCtrlWindow( this, pFormFirstWindow, nTempIndex,
819 nFoundFormStart, nFoundFormEnd ) )
821 nTempIndex = nFoundFormStart;
822 pFormFirstWindow = ImplGetDlgWindow( nTempIndex, GetDlgWindowType::First, nFoundFormStart, nFoundFormEnd );
823 if ( pFormFirstWindow )
825 pFormFirstWindow->ImplControlFocus();
826 return true;
832 if ( !bForm )
834 // Only use Ctrl-TAB if it was allowed for the whole
835 // dialog or for the current control (#103667#)
836 if (!aKeyCode.IsMod1() || (pSWindow->GetStyle() & WB_NODIALOGCONTROL))
838 GetDlgWindowType nType;
839 GetFocusFlags nGetFocusFlags = GetFocusFlags::Tab;
840 if ( aKeyCode.IsShift() )
842 nType = GetDlgWindowType::Prev;
843 nGetFocusFlags |= GetFocusFlags::Backward;
845 else
847 nType = GetDlgWindowType::Next;
848 nGetFocusFlags |= GetFocusFlags::Forward;
850 vcl::Window* pWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
851 // if this is the same window, simulate a Get/LoseFocus,
852 // in case AROUND is being processed
853 if ( pWindow == pSWindow )
855 NotifyEvent aNEvt1( MouseNotifyEvent::LOSEFOCUS, pSWindow );
856 if ( !ImplCallPreNotify( aNEvt1 ) )
857 pSWindow->CompatLoseFocus();
858 pSWindow->mpWindowImpl->mnGetFocusFlags = nGetFocusFlags | GetFocusFlags::Around;
859 NotifyEvent aNEvt2( MouseNotifyEvent::GETFOCUS, pSWindow );
860 if ( !ImplCallPreNotify( aNEvt2 ) )
861 pSWindow->CompatGetFocus();
862 pSWindow->mpWindowImpl->mnGetFocusFlags = GetFocusFlags::NONE;
863 return true;
865 else if ( pWindow )
867 // get Around-Flag
868 if ( nType == GetDlgWindowType::Prev )
870 if ( nNewIndex > i )
871 nGetFocusFlags |= GetFocusFlags::Around;
873 else
875 if ( nNewIndex < i )
876 nGetFocusFlags |= GetFocusFlags::Around;
878 pWindow->ImplControlFocus( nGetFocusFlags );
879 return true;
885 else if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_UP) )
887 if (pSWindow->GetType() == WindowType::RADIOBUTTON)
888 return nextInGroup(static_cast<RadioButton*>(pSWindow), true);
889 else
891 WinBits nStyle = pSWindow->GetStyle();
892 if ( !(nStyle & WB_GROUP) )
894 vcl::Window* pWindow = prevLogicalChildOfParent(this, pSWindow);
895 while ( pWindow )
897 pWindow = pWindow->ImplGetWindow();
899 nStyle = pWindow->GetStyle();
901 if (isSuitableDestination(pWindow))
903 if ( pWindow != pSWindow )
904 pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
905 return true;
908 if ( nStyle & WB_GROUP )
909 break;
911 pWindow = prevLogicalChildOfParent(this, pWindow);
916 else if ( (nKeyCode == KEY_RIGHT) || (nKeyCode == KEY_DOWN) )
918 if (pSWindow->GetType() == WindowType::RADIOBUTTON)
919 return nextInGroup(static_cast<RadioButton*>(pSWindow), false);
920 else
922 vcl::Window* pWindow = nextLogicalChildOfParent(this, pSWindow);
923 while ( pWindow )
925 pWindow = pWindow->ImplGetWindow();
927 WinBits nStyle = pWindow->GetStyle();
929 if ( nStyle & WB_GROUP )
930 break;
932 if (isSuitableDestination(pWindow))
934 pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
935 return true;
938 pWindow = nextLogicalChildOfParent(this, pWindow);
942 else
944 sal_Unicode c = rKEvt.GetCharCode();
945 if ( c )
947 pSWindow = ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd );
948 if ( pSWindow )
950 GetFocusFlags nGetFocusFlags = GetFocusFlags::Mnemonic;
951 if ( pSWindow == ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd ) )
952 nGetFocusFlags |= GetFocusFlags::UniqueMnemonic;
953 pSWindow->ImplControlFocus( nGetFocusFlags );
954 return true;
960 if (isSuitableDestination(pButtonWindow))
962 if ( bKeyInput )
964 if ( mpWindowImpl->mpDlgCtrlDownWindow && (mpWindowImpl->mpDlgCtrlDownWindow.get() != pButtonWindow) )
966 static_cast<PushButton*>(mpWindowImpl->mpDlgCtrlDownWindow.get())->SetPressed( false );
967 mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
970 static_cast<PushButton*>(pButtonWindow)->SetPressed( true );
971 mpWindowImpl->mpDlgCtrlDownWindow = pButtonWindow;
973 else if ( mpWindowImpl->mpDlgCtrlDownWindow.get() == pButtonWindow )
975 mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
976 static_cast<PushButton*>(pButtonWindow)->SetPressed( false );
977 static_cast<PushButton*>(pButtonWindow)->Click();
980 return true;
983 return false;
986 // checks if this window has dialog control
987 bool Window::ImplHasDlgCtrl() const
989 vcl::Window* pDlgCtrlParent;
991 // lookup window for dialog control
992 pDlgCtrlParent = ImplGetParent();
993 while ( pDlgCtrlParent &&
994 !pDlgCtrlParent->ImplIsOverlapWindow() &&
995 ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
996 pDlgCtrlParent = pDlgCtrlParent->ImplGetParent();
998 return pDlgCtrlParent && ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL);
1001 void Window::ImplDlgCtrlNextWindow()
1003 vcl::Window* pDlgCtrlParent;
1004 vcl::Window* pDlgCtrl;
1005 vcl::Window* pSWindow;
1006 sal_uInt16 nIndex;
1007 sal_uInt16 nFormStart;
1008 sal_uInt16 nFormEnd;
1010 // lookup window for dialog control
1011 pDlgCtrl = this;
1012 pDlgCtrlParent = ImplGetParent();
1013 while ( pDlgCtrlParent &&
1014 !pDlgCtrlParent->ImplIsOverlapWindow() &&
1015 ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
1016 pDlgCtrlParent = pDlgCtrlParent->ImplGetParent();
1018 if ( !pDlgCtrlParent || (GetStyle() & WB_NODIALOGCONTROL) || ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
1019 return;
1021 // lookup window in child list
1022 pSWindow = ::ImplFindDlgCtrlWindow( pDlgCtrlParent, pDlgCtrl,
1023 nIndex, nFormStart, nFormEnd );
1024 if ( !pSWindow )
1025 return;
1027 vcl::Window* pWindow = pDlgCtrlParent->ImplGetDlgWindow( nIndex, GetDlgWindowType::Next, nFormStart, nFormEnd );
1028 if ( pWindow && (pWindow != pSWindow) )
1029 pWindow->ImplControlFocus();
1032 static void ImplDlgCtrlUpdateDefButton( vcl::Window* pParent, vcl::Window* pFocusWindow,
1033 bool bGetFocus )
1035 PushButton* pOldDefButton = nullptr;
1036 PushButton* pNewDefButton = nullptr;
1037 vcl::Window* pSWindow;
1038 sal_uInt16 i;
1039 sal_uInt16 nFormStart;
1040 sal_uInt16 nFormEnd;
1042 // find template
1043 pSWindow = ::ImplFindDlgCtrlWindow( pParent, pFocusWindow, i, nFormStart, nFormEnd );
1044 if ( !pSWindow )
1046 nFormStart = 0;
1047 nFormEnd = 0xFFFF;
1050 pSWindow = ImplGetChildWindow( pParent, nFormStart, i, false );
1051 while ( pSWindow )
1053 if ( pSWindow->ImplIsPushButton() )
1055 PushButton* pPushButton = static_cast<PushButton*>(pSWindow);
1056 if ( pPushButton->ImplIsDefButton() )
1057 pOldDefButton = pPushButton;
1058 if ( pPushButton->HasChildPathFocus() )
1059 pNewDefButton = pPushButton;
1060 else if ( !pNewDefButton && (pPushButton->GetStyle() & WB_DEFBUTTON) )
1061 pNewDefButton = pPushButton;
1064 pSWindow = ImplGetNextWindow( pParent, i, i, false );
1065 if ( !i || (i > nFormEnd) )
1066 pSWindow = nullptr;
1069 if ( !bGetFocus )
1071 sal_uInt16 nDummy;
1072 vcl::Window* pNewFocusWindow = Application::GetFocusWindow();
1073 if ( !pNewFocusWindow || !pParent->ImplIsWindowOrChild( pNewFocusWindow ) )
1074 pNewDefButton = nullptr;
1075 else if ( !::ImplFindDlgCtrlWindow( pParent, pNewFocusWindow, i, nDummy, nDummy ) ||
1076 (i < nFormStart) || (i > nFormEnd) )
1077 pNewDefButton = nullptr;
1080 if ( pOldDefButton != pNewDefButton )
1082 if ( pOldDefButton )
1083 pOldDefButton->ImplSetDefButton( false );
1084 if ( pNewDefButton )
1085 pNewDefButton->ImplSetDefButton( true );
1089 void Window::ImplDlgCtrlFocusChanged( vcl::Window* pWindow, bool bGetFocus )
1091 if ( mpWindowImpl->mpDlgCtrlDownWindow && !bGetFocus )
1093 static_cast<PushButton*>(mpWindowImpl->mpDlgCtrlDownWindow.get())->SetPressed( false );
1094 mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
1097 ImplDlgCtrlUpdateDefButton( this, pWindow, bGetFocus );
1100 vcl::Window* Window::ImplFindDlgCtrlWindow( vcl::Window* pWindow )
1102 sal_uInt16 nIndex;
1103 sal_uInt16 nFormStart;
1104 sal_uInt16 nFormEnd;
1106 // find Focus-Window in the Child-List and return
1107 return ::ImplFindDlgCtrlWindow( this, pWindow, nIndex, nFormStart, nFormEnd );
1110 KeyEvent Window::GetActivationKey() const
1112 KeyEvent aKeyEvent;
1114 sal_Unicode nAccel = getAccel( GetText() );
1115 if( ! nAccel )
1117 vcl::Window* pWindow = GetAccessibleRelationLabeledBy();
1118 if( pWindow )
1119 nAccel = getAccel( pWindow->GetText() );
1121 if( nAccel )
1123 sal_uInt16 nCode = 0;
1124 if( nAccel >= 'a' && nAccel <= 'z' )
1125 nCode = KEY_A + (nAccel-'a');
1126 else if( nAccel >= 'A' && nAccel <= 'Z' )
1127 nCode = KEY_A + (nAccel-'A');
1128 else if( nAccel >= '0' && nAccel <= '9' )
1129 nCode = KEY_0 + (nAccel-'0');
1130 else if( nAccel == '.' )
1131 nCode = KEY_POINT;
1132 else if( nAccel == '-' )
1133 nCode = KEY_SUBTRACT;
1134 vcl::KeyCode aKeyCode( nCode, false, false, true, false );
1135 aKeyEvent = KeyEvent( nAccel, aKeyCode );
1137 return aKeyEvent;
1140 } /* namespace vcl */
1142 sal_Unicode getAccel( const OUString& rStr )
1144 sal_Unicode nChar = 0;
1145 sal_Int32 nPos = 0;
1148 nPos = rStr.indexOf( '~', nPos );
1149 if( nPos != -1 && nPos < rStr.getLength() )
1150 nChar = rStr[ ++nPos ];
1151 else
1152 nChar = 0;
1153 } while( nChar == '~' );
1154 return nChar;
1157 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */