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