1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
21 #include <svdata.hxx>
22 #include <window.h>
23 
24 #include "dlgctrl.hxx"
25 #include <vcl/event.hxx>
26 #include <vcl/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/button.hxx>
32 #include <vcl/settings.hxx>
33 #include <sal/log.hxx>
34 #include <i18nlangtag/languagetag.hxx>
35 
36 #include <com/sun/star/i18n/XCharacterClassification.hpp>
37 
38 using namespace ::com::sun::star;
39 
ImplHasIndirectTabParent(vcl::Window * pWindow)40 static bool ImplHasIndirectTabParent( vcl::Window* pWindow )
41 {
42     // The window has indirect tab parent if it is included in tab hierarchy
43     // of the indirect parent window
44 
45     vcl::Window* pNonLayoutParent = getNonLayoutParent(pWindow);
46     return ( pNonLayoutParent
47           && ( pNonLayoutParent->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) );
48 }
49 
ImplGetTopParentOfTabHierarchy(vcl::Window * pParent)50 static vcl::Window* ImplGetTopParentOfTabHierarchy( vcl::Window* pParent )
51 {
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
55 
56     vcl::Window* pResult = pParent;
57 
58     if ( pResult )
59     {
60         vcl::Window* pNonLayoutParent = getNonLayoutParent(pResult);
61         while ( pNonLayoutParent && ( pResult->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) )
62         {
63             pResult = pNonLayoutParent;
64             pNonLayoutParent = getNonLayoutParent(pResult);
65         }
66     }
67 
68     return pResult;
69 }
70 
ImplGetCurTabWindow(const vcl::Window * pWindow)71 static vcl::Window* ImplGetCurTabWindow(const vcl::Window* pWindow)
72 {
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)
80     {
81         vcl::Window* pTempWindow = pTabControl->GetWindow(GetWindowType::FirstChild);
82         while (pTempWindow)
83         {
84             if (pTempWindow->ImplGetWindow() == pTempTabPage)
85             {
86                 return const_cast<TabPage*>(pTempTabPage);
87             }
88             pTempWindow = nextLogicalChildOfParent(pTabControl, pTempWindow);
89         }
90     }
91 
92     return nullptr;
93 }
94 
ImplGetSubChildWindow(vcl::Window * pParent,sal_uInt16 n,sal_uInt16 & nIndex)95 static vcl::Window* ImplGetSubChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex )
96 {
97     // ignore all windows with mpClientWindow set
98     for (vcl::Window *pNewParent = pParent->ImplGetWindow();
99          pParent != pNewParent; pParent = pNewParent);
100 
101     vcl::Window* pFoundWindow = nullptr;
102     vcl::Window* pWindow = firstLogicalChildOfParent(pParent);
103     vcl::Window* pNextWindow = pWindow;
104 
105     // process just the current page of a tab control
106     if (pWindow && pParent->GetType() == WindowType::TABCONTROL)
107     {
108         pWindow = ImplGetCurTabWindow(pParent);
109         pNextWindow = lastLogicalChildOfParent(pParent);
110     }
111 
112     while (pWindow)
113     {
114         pWindow = pWindow->ImplGetWindow();
115 
116         // skip invisible and disabled windows
117         if (isVisibleInLayout(pWindow))
118         {
119             // return the TabControl itself, before handling its page
120             if (pWindow->GetType() == WindowType::TABCONTROL)
121             {
122                 if (n == nIndex)
123                     return pWindow;
124                 ++nIndex;
125             }
126             if (pWindow->GetStyle() & (WB_DIALOGCONTROL | WB_CHILDDLGCTRL))
127                 pFoundWindow = ImplGetSubChildWindow(pWindow, n, nIndex);
128             else
129                 pFoundWindow = pWindow;
130 
131             if (n == nIndex)
132                 return pFoundWindow;
133             ++nIndex;
134         }
135 
136         pWindow = nextLogicalChildOfParent(pParent, pNextWindow);
137         pNextWindow = pWindow;
138     }
139 
140     --nIndex;
141     assert(!pFoundWindow || (pFoundWindow == pFoundWindow->ImplGetWindow()));
142     return pFoundWindow;
143 }
144 
ImplGetChildWindow(vcl::Window * pParent,sal_uInt16 n,sal_uInt16 & nIndex,bool bTestEnable)145 vcl::Window* ImplGetChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
146 {
147     pParent = ImplGetTopParentOfTabHierarchy( pParent );
148 
149     nIndex = 0;
150     vcl::Window* pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
151     if ( bTestEnable )
152     {
153         sal_uInt16 n2 = nIndex;
154         while ( pWindow && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) )
155         {
156             n2 = nIndex+1;
157             nIndex = 0;
158             pWindow = ImplGetSubChildWindow( pParent, n2, nIndex );
159             if ( nIndex < n2 )
160                 break;
161         }
162 
163         if ( (nIndex < n2) && n )
164         {
165             do
166             {
167                 n--;
168                 nIndex = 0;
169                 pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
170             }
171             while ( pWindow && n && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) );
172         }
173     }
174     return pWindow;
175 }
176 
ImplGetNextWindow(vcl::Window * pParent,sal_uInt16 n,sal_uInt16 & nIndex,bool bTestEnable)177 static vcl::Window* ImplGetNextWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
178 {
179     vcl::Window* pWindow = ImplGetChildWindow( pParent, n+1, nIndex, bTestEnable );
180     if ( n == nIndex )
181     {
182         n = 0;
183         pWindow = ImplGetChildWindow( pParent, n, nIndex, bTestEnable );
184     }
185     return pWindow;
186 }
187 
188 namespace vcl {
189 
lcl_ToolBoxTabStop(Window * pWindow)190 static bool lcl_ToolBoxTabStop( Window* pWindow )
191 {
192     ToolBox* pToolBoxWindow = static_cast<ToolBox*>( pWindow );
193 
194     sal_uInt16 nId;
195     for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < pToolBoxWindow->GetItemCount(); nPos++ )
196     {
197         nId = pToolBoxWindow->GetItemId( nPos );
198         if ( pToolBoxWindow->IsItemVisible( nId ) && pToolBoxWindow->IsItemEnabled( nId ) )
199             return true;
200     }
201 
202     return false;
203 }
204 
ImplGetDlgWindow(sal_uInt16 nIndex,GetDlgWindowType nType,sal_uInt16 nFormStart,sal_uInt16 nFormEnd,sal_uInt16 * pIndex)205 vcl::Window* Window::ImplGetDlgWindow( sal_uInt16 nIndex, GetDlgWindowType nType,
206                                   sal_uInt16 nFormStart, sal_uInt16 nFormEnd,
207                                   sal_uInt16* pIndex )
208 {
209     SAL_WARN_IF( (nIndex < nFormStart) || (nIndex > nFormEnd), "vcl",
210                 "Window::ImplGetDlgWindow() - nIndex not in Form" );
211 
212     vcl::Window* pWindow = nullptr;
213     sal_uInt16  i;
214     sal_uInt16  nTemp;
215     sal_uInt16  nStartIndex;
216 
217     if ( nType == GetDlgWindowType::Prev )
218     {
219         i = nIndex;
220         do
221         {
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) )
230             {
231                 if ( WindowType::TOOLBOX == pWindow->GetType() )
232                 {
233                     if ( lcl_ToolBoxTabStop( pWindow ) )
234                         break;
235                 }
236                 else
237                     break;
238             }
239         }
240         while ( i != nIndex );
241     }
242     else
243     {
244         i = nIndex;
245         pWindow = ImplGetChildWindow( this, i, i, (nType == GetDlgWindowType::First) );
246         if ( pWindow )
247         {
248             nStartIndex = i;
249 
250             if ( nType == GetDlgWindowType::Next )
251             {
252                 if ( i < nFormEnd )
253                 {
254                     pWindow = ImplGetNextWindow( this, i, i, true );
255                     if ( (i > nFormEnd) || (i < nFormStart) )
256                         pWindow = ImplGetChildWindow( this, nFormStart, i, true );
257                 }
258                 else
259                     pWindow = ImplGetChildWindow( this, nFormStart, i, true );
260             }
261 
262             if (i <= nFormEnd && pWindow)
263             {
264                 // carry the 2nd index, in case all controls are disabled
265                 sal_uInt16 nStartIndex2 = i;
266                 sal_uInt16 nOldIndex = i+1;
267 
268                 do
269                 {
270                     if ( pWindow->GetStyle() & WB_TABSTOP )
271                     {
272                         if ( WindowType::TOOLBOX == pWindow->GetType() )
273                         {
274                             if ( lcl_ToolBoxTabStop( pWindow ) )
275                                 break;
276                         }
277                         else
278                             break;
279                     }
280                     if( i == nOldIndex ) // only disabled controls ?
281                     {
282                         i = nStartIndex2;
283                         break;
284                     }
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 );
290                 }
291                 while (i != nStartIndex && i != nStartIndex2 && pWindow);
292 
293                 if ( (i == nStartIndex2) && pWindow &&
294                      (!(pWindow->GetStyle() & WB_TABSTOP) || !isEnabledInLayout(pWindow)) )
295                     i = nStartIndex;
296             }
297         }
298 
299         if ( nType == GetDlgWindowType::First )
300         {
301             if ( pWindow )
302             {
303                 if ( pWindow->GetType() == WindowType::TABCONTROL )
304                 {
305                     vcl::Window* pNextWindow = ImplGetDlgWindow( i, GetDlgWindowType::Next );
306                     if ( pNextWindow )
307                     {
308                         if ( pWindow->IsChild( pNextWindow ) )
309                             pWindow = pNextWindow;
310                     }
311                 }
312 
313                 if ( !(pWindow->GetStyle() & WB_TABSTOP) )
314                     pWindow = nullptr;
315             }
316         }
317     }
318 
319     if ( pIndex )
320         *pIndex = i;
321 
322     return pWindow;
323 }
324 
325 } /* namespace vcl */
326 
ImplFindDlgCtrlWindow(vcl::Window * pParent,vcl::Window * pWindow,sal_uInt16 & rIndex,sal_uInt16 & rFormStart,sal_uInt16 & rFormEnd)327 vcl::Window* ImplFindDlgCtrlWindow( vcl::Window* pParent, vcl::Window* pWindow, sal_uInt16& rIndex,
328                                sal_uInt16& rFormStart, sal_uInt16& rFormEnd )
329 {
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;
338 
339     // find focus window in the child list
340     vcl::Window* pFirstChildWindow = pSWindow = ImplGetChildWindow( pParent, 0, i, false );
341 
342     if( pWindow == nullptr )
343         pWindow = pSWindow;
344 
345     while ( pSWindow )
346     {
347         // the DialogControlStart mark is only accepted for the direct children
348         if ( !ImplHasIndirectTabParent( pSWindow )
349           && pSWindow->ImplGetWindow()->IsDialogControlStart() )
350             nFormStart = i;
351 
352         // SecondWindow for composite controls like ComboBoxes and arrays
353         if ( pSWindow->ImplIsWindowOrChild( pWindow ) )
354         {
355             pSecondWindow = pSWindow;
356             nSecond_i = i;
357             nSecondFormStart = nFormStart;
358             if ( pSWindow == pWindow )
359                 break;
360         }
361 
362         pSWindow = ImplGetNextWindow( pParent, i, i, false );
363         if ( !i )
364             pSWindow = nullptr;
365     }
366 
367     if ( !pSWindow )
368     {
369         // Window not found; we cannot handle it
370         if ( !pSecondWindow )
371             return nullptr;
372         else
373         {
374             pSWindow = pSecondWindow;
375             i = nSecond_i;
376             nFormStart = nSecondFormStart;
377         }
378     }
379 
380     // initialize
381     rIndex = i;
382     rFormStart = nFormStart;
383 
384     // find end of template
385     sal_Int32 nIteration = 0;
386     do
387     {
388         nFormEnd = i;
389         pTempWindow = ImplGetNextWindow( pParent, i, i, false );
390 
391         // the DialogControlStart mark is only accepted for the direct children
392         if ( !i
393           || ( pTempWindow && !ImplHasIndirectTabParent( pTempWindow )
394                && pTempWindow->ImplGetWindow()->IsDialogControlStart() ) )
395             break;
396 
397         if ( pTempWindow && pTempWindow == pFirstChildWindow )
398         {
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 )
405             {
406                 // this is an unexpected scenario
407                 SAL_WARN( "vcl", "It seems to be an endless loop!" );
408                 rFormStart = 0;
409                 break;
410             }
411         }
412     }
413     while ( pTempWindow );
414     rFormEnd = nFormEnd;
415 
416     return pSWindow;
417 }
418 
ImplFindAccelWindow(vcl::Window * pParent,sal_uInt16 & rIndex,sal_Unicode cCharCode,sal_uInt16 nFormStart,sal_uInt16 nFormEnd,bool bCheckEnable)419 vcl::Window* ImplFindAccelWindow( vcl::Window* pParent, sal_uInt16& rIndex, sal_Unicode cCharCode,
420                              sal_uInt16 nFormStart, sal_uInt16 nFormEnd, bool bCheckEnable )
421 {
422     SAL_WARN_IF( (rIndex < nFormStart) || (rIndex > nFormEnd), "vcl",
423                 "Window::ImplFindAccelWindow() - rIndex not in Form" );
424 
425     sal_Unicode cCompareChar;
426     sal_uInt16  nStart = rIndex;
427     sal_uInt16  i = rIndex;
428     vcl::Window* pWindow;
429 
430     uno::Reference<i18n::XCharacterClassification> const& xCharClass(ImplGetCharClass());
431 
432     const css::lang::Locale& rLocale = Application::GetSettings().GetUILanguageTag().getLocale();
433     cCharCode = xCharClass->toUpper( OUString(cCharCode), 0, 1, rLocale )[0];
434 
435     if ( i < nFormEnd )
436         pWindow = ImplGetNextWindow( pParent, i, i, true );
437     else
438         pWindow = ImplGetChildWindow( pParent, nFormStart, i, true );
439     while( pWindow )
440     {
441         const OUString aStr = pWindow->GetText();
442         sal_Int32 nPos = aStr.indexOf( '~' );
443         while (nPos != -1)
444         {
445             cCompareChar = aStr[nPos+1];
446             cCompareChar = xCharClass->toUpper( OUString(cCompareChar), 0, 1, rLocale )[0];
447             if ( cCompareChar == cCharCode )
448             {
449                 if (pWindow->GetType() == WindowType::FIXEDTEXT)
450                 {
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;
457                 }
458 
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;
466             }
467             nPos = aStr.indexOf( '~', nPos+1 );
468         }
469 
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 )
475         {
476             sal_uInt16  nChildIndex;
477             sal_uInt16  nChildFormStart;
478             sal_uInt16  nChildFormEnd;
479 
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;
488         }
489 
490         if ( i == nStart )
491             break;
492 
493         if ( i < nFormEnd )
494         {
495             pWindow = ImplGetNextWindow( pParent, i, i, bCheckEnable );
496             if( ! pWindow )
497                 pWindow = ImplGetChildWindow( pParent, nFormStart, i, bCheckEnable );
498         }
499         else
500             pWindow = ImplGetChildWindow( pParent, nFormStart, i, bCheckEnable );
501     }
502 
503     return nullptr;
504 }
505 
506 namespace vcl {
507 
SetMnemonicActivateHdl(const Link<vcl::Window &,bool> & rLink)508 void Window::SetMnemonicActivateHdl(const Link<vcl::Window&, bool>& rLink)
509 {
510     if (mpWindowImpl) // may be called after dispose
511     {
512         mpWindowImpl->maMnemonicActivateHdl = rLink;
513     }
514 }
515 
ImplControlFocus(GetFocusFlags nFlags)516 void Window::ImplControlFocus( GetFocusFlags nFlags )
517 {
518     if ( nFlags & GetFocusFlags::Mnemonic )
519     {
520         if (mpWindowImpl->maMnemonicActivateHdl.Call(*this))
521             return;
522 
523         if ( GetType() == WindowType::RADIOBUTTON )
524         {
525             if ( !static_cast<RadioButton*>(this)->IsChecked() )
526                 static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
527             else
528                 ImplGrabFocus( nFlags );
529         }
530         else
531         {
532             ImplGrabFocus( nFlags );
533             if ( nFlags & GetFocusFlags::UniqueMnemonic )
534             {
535                 if ( GetType() == WindowType::CHECKBOX )
536                     static_cast<CheckBox*>(this)->ImplCheck();
537                 else if ( mpWindowImpl->mbPushButton )
538                 {
539                     static_cast<PushButton*>(this)->SetPressed( true );
540                     static_cast<PushButton*>(this)->SetPressed( false );
541                     static_cast<PushButton*>(this)->Click();
542                 }
543             }
544         }
545     }
546     else
547     {
548         if ( GetType() == WindowType::RADIOBUTTON )
549         {
550             if ( !static_cast<RadioButton*>(this)->IsChecked() )
551                 static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
552             else
553                 ImplGrabFocus( nFlags );
554         }
555         else
556             ImplGrabFocus( nFlags );
557     }
558 }
559 
560 } /* namespace vcl */
561 
562 namespace
563 {
isSuitableDestination(vcl::Window const * pWindow)564     bool isSuitableDestination(vcl::Window const *pWindow)
565     {
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)
572                );
573     }
574 
focusNextInGroup(const std::vector<VclPtr<RadioButton>>::iterator & aStart,std::vector<VclPtr<RadioButton>> & rGroup)575     bool focusNextInGroup(const std::vector<VclPtr<RadioButton> >::iterator& aStart, std::vector<VclPtr<RadioButton> > &rGroup)
576     {
577         std::vector<VclPtr<RadioButton> >::iterator aI(aStart);
578 
579         if (aStart != rGroup.end())
580             ++aI;
581 
582         aI = std::find_if(aI, rGroup.end(), isSuitableDestination);
583         if (aI != rGroup.end())
584         {
585             vcl::Window *pWindow = *aI;
586             pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
587             return true;
588         }
589         aI = std::find_if(rGroup.begin(), aStart, isSuitableDestination);
590         if (aI != aStart)
591         {
592             vcl::Window *pWindow = *aI;
593             pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
594             return true;
595         }
596         return false;
597     }
598 
nextInGroup(RadioButton * pSourceWindow,bool bBackward)599     bool nextInGroup(RadioButton *pSourceWindow, bool bBackward)
600     {
601         std::vector<VclPtr<RadioButton> > aGroup(pSourceWindow->GetRadioButtonGroup());
602 
603         if (aGroup.size() == 1) //only one button in group
604             return false;
605 
606         if (bBackward)
607             std::reverse(aGroup.begin(), aGroup.end());
608 
609         auto aStart(std::find(aGroup.begin(), aGroup.end(), VclPtr<RadioButton>(pSourceWindow)));
610 
611         assert(aStart != aGroup.end());
612 
613         return focusNextInGroup(aStart, aGroup);
614     }
615 }
616 
617 namespace vcl {
618 
ImplDlgCtrl(const KeyEvent & rKEvt,bool bKeyInput)619 bool Window::ImplDlgCtrl( const KeyEvent& rKEvt, bool bKeyInput )
620 {
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;
634 
635     // we cannot take over control without Focus-window
636     vcl::Window* pFocusWindow = Application::GetFocusWindow();
637     if ( !pFocusWindow || !ImplIsWindowOrChild( pFocusWindow ) )
638         return false;
639 
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;
646 
647     nDlgCtrlFlags = DialogControlFlags::NONE;
648     pTempWindow = pSWindow;
649     do
650     {
651         nDlgCtrlFlags |= pTempWindow->GetDialogControlFlags();
652         if ( pTempWindow == this )
653             break;
654         pTempWindow = pTempWindow->ImplGetParent();
655     }
656     while ( pTempWindow );
657 
658     pButtonWindow = nullptr;
659 
660     if ( nKeyCode == KEY_RETURN )
661     {
662         // search first for a DefPushButton/CancelButton
663         pButtonWindow = ImplGetChildWindow( this, nFormStart, iButton, true );
664         iButtonStart = iButton;
665         while ( pButtonWindow )
666         {
667             if ( (pButtonWindow->GetStyle() & WB_DEFBUTTON) &&
668                  pButtonWindow->mpWindowImpl->mbPushButton )
669                 break;
670 
671             pButtonWindow = ImplGetNextWindow( this, iButton, iButton, true );
672             if ( (iButton <= iButtonStart) || (iButton > nFormEnd) )
673                 pButtonWindow = nullptr;
674         }
675 
676         if ( bKeyInput && !pButtonWindow && (nDlgCtrlFlags & DialogControlFlags::Return) )
677         {
678             GetDlgWindowType nType;
679             GetFocusFlags    nGetFocusFlags = GetFocusFlags::Tab;
680             sal_uInt16  nNewIndex;
681             sal_uInt16  iStart;
682             if ( aKeyCode.IsShift() )
683             {
684                 nType = GetDlgWindowType::Prev;
685                 nGetFocusFlags |= GetFocusFlags::Backward;
686             }
687             else
688             {
689                 nType = GetDlgWindowType::Next;
690                 nGetFocusFlags |= GetFocusFlags::Forward;
691             }
692             iStart = i;
693             pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
694             while ( pTempWindow && (pTempWindow != pSWindow) )
695             {
696                 if ( !pTempWindow->mpWindowImpl->mbPushButton )
697                 {
698                     // get Around-Flag
699                     if ( nType == GetDlgWindowType::Prev )
700                     {
701                         if ( nNewIndex > iStart )
702                             nGetFocusFlags |= GetFocusFlags::Around;
703                     }
704                     else
705                     {
706                         if ( nNewIndex < iStart )
707                             nGetFocusFlags |= GetFocusFlags::Around;
708                     }
709                     pTempWindow->ImplControlFocus( nGetFocusFlags );
710                     return true;
711                 }
712                 else
713                 {
714                     i = nNewIndex;
715                     pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
716                 }
717                 if ( (i <= iStart) || (i > nFormEnd) )
718                     pTempWindow = nullptr;
719             }
720             // if this is the same window, simulate a Get/LoseFocus,
721             // in case AROUND is being processed
722             if ( pTempWindow && (pTempWindow == pSWindow) )
723             {
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;
733             }
734         }
735     }
736     else if ( nKeyCode == KEY_ESCAPE )
737     {
738         // search first for a DefPushButton/CancelButton
739         pButtonWindow = ImplGetChildWindow( this, nFormStart, iButton, true );
740         iButtonStart = iButton;
741         while ( pButtonWindow )
742         {
743             if ( pButtonWindow->GetType() == WindowType::CANCELBUTTON )
744                 break;
745 
746             pButtonWindow = ImplGetNextWindow( this, iButton, iButton, true );
747             if ( (iButton <= iButtonStart) || (iButton > nFormEnd) )
748                 pButtonWindow = nullptr;
749         }
750 
751         if ( bKeyInput && mpWindowImpl->mpDlgCtrlDownWindow )
752         {
753             if ( mpWindowImpl->mpDlgCtrlDownWindow.get() != pButtonWindow )
754             {
755                 static_cast<PushButton*>(mpWindowImpl->mpDlgCtrlDownWindow.get())->SetPressed( false );
756                 mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
757                 return true;
758             }
759         }
760     }
761     else if ( bKeyInput )
762     {
763         if ( nKeyCode == KEY_TAB )
764         {
765             // do not skip Alt key, for MS Windows
766             if ( !aKeyCode.IsMod2() )
767             {
768                 GetDlgWindowType nType;
769                 GetFocusFlags    nGetFocusFlags = GetFocusFlags::Tab;
770                 sal_uInt16  nNewIndex;
771                 bool        bFormular = false;
772 
773                 // for Ctrl-Tab check if we want to jump to next template
774                 if ( aKeyCode.IsMod1() )
775                 {
776                     // search group
777                     vcl::Window* pFormularFirstWindow = nullptr;
778                     vcl::Window* pLastFormularFirstWindow = nullptr;
779                     pTempWindow = ImplGetChildWindow( this, 0, iTemp, false );
780                     vcl::Window* pPrevFirstFormularFirstWindow = nullptr;
781                     vcl::Window* pFirstFormularFirstWindow = pTempWindow;
782                     while ( pTempWindow )
783                     {
784                         if ( pTempWindow->ImplGetWindow()->IsDialogControlStart() )
785                         {
786                             if ( iTemp != 0 )
787                                 bFormular = true;
788                             if ( aKeyCode.IsShift() )
789                             {
790                                 if ( iTemp <= nIndex )
791                                     pFormularFirstWindow = pPrevFirstFormularFirstWindow;
792                                 pPrevFirstFormularFirstWindow = pTempWindow;
793                             }
794                             else
795                             {
796                                 if ( (iTemp > nIndex) && !pFormularFirstWindow )
797                                     pFormularFirstWindow = pTempWindow;
798                             }
799                             pLastFormularFirstWindow = pTempWindow;
800                         }
801 
802                         pTempWindow = ImplGetNextWindow( this, iTemp, iTemp, false );
803                         if ( !iTemp )
804                             pTempWindow = nullptr;
805                     }
806 
807                     if ( bFormular )
808                     {
809                         if ( !pFormularFirstWindow )
810                         {
811                             if ( aKeyCode.IsShift() )
812                                 pFormularFirstWindow = pLastFormularFirstWindow;
813                             else
814                                 pFormularFirstWindow = pFirstFormularFirstWindow;
815                         }
816 
817                         sal_uInt16 nFoundFormStart = 0;
818                         sal_uInt16 nFoundFormEnd = 0;
819                         sal_uInt16 nTempIndex = 0;
820                         if ( ::ImplFindDlgCtrlWindow( this, pFormularFirstWindow, nTempIndex,
821                                                       nFoundFormStart, nFoundFormEnd ) )
822                         {
823                             nTempIndex = nFoundFormStart;
824                             pFormularFirstWindow = ImplGetDlgWindow( nTempIndex, GetDlgWindowType::First, nFoundFormStart, nFoundFormEnd );
825                             if ( pFormularFirstWindow )
826                             {
827                                 pFormularFirstWindow->ImplControlFocus();
828                                 return true;
829                             }
830                         }
831                     }
832                 }
833 
834                 if ( !bFormular )
835                 {
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))
839                     {
840                         if ( aKeyCode.IsShift() )
841                         {
842                             nType = GetDlgWindowType::Prev;
843                             nGetFocusFlags |= GetFocusFlags::Backward;
844                         }
845                         else
846                         {
847                             nType = GetDlgWindowType::Next;
848                             nGetFocusFlags |= GetFocusFlags::Forward;
849                         }
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 )
854                         {
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;
864                         }
865                         else if ( pWindow )
866                         {
867                             // get Around-Flag
868                             if ( nType == GetDlgWindowType::Prev )
869                             {
870                                 if ( nNewIndex > i )
871                                     nGetFocusFlags |= GetFocusFlags::Around;
872                             }
873                             else
874                             {
875                                 if ( nNewIndex < i )
876                                     nGetFocusFlags |= GetFocusFlags::Around;
877                             }
878                             pWindow->ImplControlFocus( nGetFocusFlags );
879                             return true;
880                         }
881                     }
882                 }
883             }
884         }
885         else if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_UP) )
886         {
887             if (pSWindow->GetType() == WindowType::RADIOBUTTON)
888                 return nextInGroup(static_cast<RadioButton*>(pSWindow), true);
889             else
890             {
891                 WinBits nStyle = pSWindow->GetStyle();
892                 if ( !(nStyle & WB_GROUP) )
893                 {
894                     vcl::Window* pWindow = prevLogicalChildOfParent(this, pSWindow);
895                     while ( pWindow )
896                     {
897                         pWindow = pWindow->ImplGetWindow();
898 
899                         nStyle = pWindow->GetStyle();
900 
901                         if (isSuitableDestination(pWindow))
902                         {
903                             if ( pWindow != pSWindow )
904                                 pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
905                             return true;
906                         }
907 
908                         if ( nStyle & WB_GROUP )
909                             break;
910 
911                         pWindow = prevLogicalChildOfParent(this, pWindow);
912                     }
913                 }
914             }
915         }
916         else if ( (nKeyCode == KEY_RIGHT) || (nKeyCode == KEY_DOWN) )
917         {
918             if (pSWindow->GetType() == WindowType::RADIOBUTTON)
919                 return nextInGroup(static_cast<RadioButton*>(pSWindow), false);
920             else
921             {
922                 vcl::Window* pWindow = nextLogicalChildOfParent(this, pSWindow);
923                 while ( pWindow )
924                 {
925                     pWindow = pWindow->ImplGetWindow();
926 
927                     WinBits nStyle = pWindow->GetStyle();
928 
929                     if ( nStyle & WB_GROUP )
930                         break;
931 
932                     if (isSuitableDestination(pWindow))
933                     {
934                         pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
935                         return true;
936                     }
937 
938                     pWindow = nextLogicalChildOfParent(this, pWindow);
939                 }
940             }
941         }
942         else
943         {
944             sal_Unicode c = rKEvt.GetCharCode();
945             if ( c )
946             {
947                 pSWindow = ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd );
948                 if ( pSWindow )
949                 {
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;
955                 }
956             }
957         }
958     }
959 
960     if (isSuitableDestination(pButtonWindow))
961     {
962         if ( bKeyInput )
963         {
964             if ( mpWindowImpl->mpDlgCtrlDownWindow && (mpWindowImpl->mpDlgCtrlDownWindow.get() != pButtonWindow) )
965             {
966                 static_cast<PushButton*>(mpWindowImpl->mpDlgCtrlDownWindow.get())->SetPressed( false );
967                 mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
968             }
969 
970             static_cast<PushButton*>(pButtonWindow)->SetPressed( true );
971             mpWindowImpl->mpDlgCtrlDownWindow = pButtonWindow;
972         }
973         else if ( mpWindowImpl->mpDlgCtrlDownWindow.get() == pButtonWindow )
974         {
975             mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
976             static_cast<PushButton*>(pButtonWindow)->SetPressed( false );
977             static_cast<PushButton*>(pButtonWindow)->Click();
978         }
979 
980         return true;
981     }
982 
983     return false;
984 }
985 
986 // checks if this window has dialog control
ImplHasDlgCtrl() const987 bool Window::ImplHasDlgCtrl() const
988 {
989     vcl::Window* pDlgCtrlParent;
990 
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();
997 
998     return pDlgCtrlParent && ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL);
999 }
1000 
ImplDlgCtrlNextWindow()1001 void Window::ImplDlgCtrlNextWindow()
1002 {
1003     vcl::Window* pDlgCtrlParent;
1004     vcl::Window* pDlgCtrl;
1005     vcl::Window* pSWindow;
1006     sal_uInt16  nIndex;
1007     sal_uInt16  nFormStart;
1008     sal_uInt16  nFormEnd;
1009 
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();
1017 
1018     if ( !pDlgCtrlParent || (GetStyle() & WB_NODIALOGCONTROL) || ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
1019         return;
1020 
1021     // lookup window in child list
1022     pSWindow = ::ImplFindDlgCtrlWindow( pDlgCtrlParent, pDlgCtrl,
1023                                         nIndex, nFormStart, nFormEnd );
1024     if ( !pSWindow )
1025         return;
1026 
1027     vcl::Window* pWindow = pDlgCtrlParent->ImplGetDlgWindow( nIndex, GetDlgWindowType::Next, nFormStart, nFormEnd );
1028     if ( pWindow && (pWindow != pSWindow) )
1029         pWindow->ImplControlFocus();
1030 }
1031 
ImplDlgCtrlUpdateDefButton(vcl::Window * pParent,vcl::Window * pFocusWindow,bool bGetFocus)1032 static void ImplDlgCtrlUpdateDefButton( vcl::Window* pParent, vcl::Window* pFocusWindow,
1033                                         bool bGetFocus )
1034 {
1035     PushButton* pOldDefButton   = nullptr;
1036     PushButton* pNewDefButton   = nullptr;
1037     vcl::Window*     pSWindow;
1038     sal_uInt16      i;
1039     sal_uInt16      nFormStart;
1040     sal_uInt16      nFormEnd;
1041 
1042     // find template
1043     pSWindow = ::ImplFindDlgCtrlWindow( pParent, pFocusWindow, i, nFormStart, nFormEnd );
1044     if ( !pSWindow )
1045     {
1046         nFormStart = 0;
1047         nFormEnd = 0xFFFF;
1048     }
1049 
1050     pSWindow = ImplGetChildWindow( pParent, nFormStart, i, false );
1051     while ( pSWindow )
1052     {
1053         if ( pSWindow->ImplIsPushButton() )
1054         {
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;
1062         }
1063 
1064         pSWindow = ImplGetNextWindow( pParent, i, i, false );
1065         if ( !i || (i > nFormEnd) )
1066             pSWindow = nullptr;
1067     }
1068 
1069     if ( !bGetFocus )
1070     {
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;
1078     }
1079 
1080     if ( pOldDefButton != pNewDefButton )
1081     {
1082         if ( pOldDefButton )
1083             pOldDefButton->ImplSetDefButton( false );
1084         if ( pNewDefButton )
1085             pNewDefButton->ImplSetDefButton( true );
1086     }
1087 }
1088 
ImplDlgCtrlFocusChanged(vcl::Window * pWindow,bool bGetFocus)1089 void Window::ImplDlgCtrlFocusChanged( vcl::Window* pWindow, bool bGetFocus )
1090 {
1091     if ( mpWindowImpl->mpDlgCtrlDownWindow && !bGetFocus )
1092     {
1093         static_cast<PushButton*>(mpWindowImpl->mpDlgCtrlDownWindow.get())->SetPressed( false );
1094         mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
1095     }
1096 
1097     ImplDlgCtrlUpdateDefButton( this, pWindow, bGetFocus );
1098 }
1099 
ImplFindDlgCtrlWindow(vcl::Window * pWindow)1100 vcl::Window* Window::ImplFindDlgCtrlWindow( vcl::Window* pWindow )
1101 {
1102     sal_uInt16  nIndex;
1103     sal_uInt16  nFormStart;
1104     sal_uInt16  nFormEnd;
1105 
1106     // find Focus-Window in the Child-List and return
1107     return ::ImplFindDlgCtrlWindow( this, pWindow, nIndex, nFormStart, nFormEnd );
1108 }
1109 
GetActivationKey() const1110 KeyEvent Window::GetActivationKey() const
1111 {
1112     KeyEvent aKeyEvent;
1113 
1114     sal_Unicode nAccel = getAccel( GetText() );
1115     if( ! nAccel )
1116     {
1117         vcl::Window* pWindow = GetAccessibleRelationLabeledBy();
1118         if( pWindow )
1119             nAccel = getAccel( pWindow->GetText() );
1120     }
1121     if( nAccel )
1122     {
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 );
1136     }
1137     return aKeyEvent;
1138 }
1139 
1140 } /* namespace vcl */
1141 
getAccel(const OUString & rStr)1142 sal_Unicode getAccel( const OUString& rStr )
1143 {
1144     sal_Unicode nChar = 0;
1145     sal_Int32 nPos = 0;
1146     do
1147     {
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;
1155 }
1156 
1157 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1158