summaryrefslogtreecommitdiffstats
path: root/vcl/source/window/dlgctrl.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/window/dlgctrl.cxx')
-rw-r--r--vcl/source/window/dlgctrl.cxx1168
1 files changed, 1168 insertions, 0 deletions
diff --git a/vcl/source/window/dlgctrl.cxx b/vcl/source/window/dlgctrl.cxx
new file mode 100644
index 0000000000..ac75333d90
--- /dev/null
+++ b/vcl/source/window/dlgctrl.cxx
@@ -0,0 +1,1168 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <svdata.hxx>
+#include <window.h>
+
+#include "dlgctrl.hxx"
+#include <vcl/event.hxx>
+#include <vcl/toolkit/fixed.hxx>
+#include <vcl/layout.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/tabpage.hxx>
+#include <vcl/tabctrl.hxx>
+#include <vcl/toolkit/button.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <com/sun/star/i18n/XCharacterClassification.hpp>
+
+using namespace ::com::sun::star;
+
+static bool ImplHasIndirectTabParent( vcl::Window* pWindow )
+{
+ // The window has indirect tab parent if it is included in tab hierarchy
+ // of the indirect parent window
+
+ vcl::Window* pNonLayoutParent = getNonLayoutParent(pWindow);
+ return ( pNonLayoutParent
+ && ( pNonLayoutParent->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) );
+}
+
+static vcl::Window* ImplGetTopParentOfTabHierarchy( vcl::Window* pParent )
+{
+ // The method allows to find the most close parent containing all the
+ // window from the current tab-hierarchy
+ // The direct parent should be provided as a parameter here
+
+ vcl::Window* pResult = pParent;
+
+ if ( pResult )
+ {
+ vcl::Window* pNonLayoutParent = getNonLayoutParent(pResult);
+ while ( pNonLayoutParent && ( pResult->ImplGetWindow()->GetStyle() & WB_CHILDDLGCTRL ) )
+ {
+ pResult = pNonLayoutParent;
+ pNonLayoutParent = getNonLayoutParent(pResult);
+ }
+ }
+
+ return pResult;
+}
+
+static vcl::Window* ImplGetCurTabWindow(const vcl::Window* pWindow)
+{
+ assert(pWindow->GetType() == WindowType::TABCONTROL);
+ const TabControl* pTabControl = static_cast<const TabControl*>(pWindow);
+ // Check if the TabPage is a Child of the TabControl and still exists (by
+ // walking all child windows); because it could be that the TabPage has been
+ // destroyed already by a Dialog-Dtor, event that the TabControl still exists.
+ const TabPage* pTempTabPage = pTabControl->GetTabPage(pTabControl->GetCurPageId());
+ if (pTempTabPage)
+ {
+ vcl::Window* pTempWindow = pTabControl->GetWindow(GetWindowType::FirstChild);
+ while (pTempWindow)
+ {
+ if (pTempWindow->ImplGetWindow() == pTempTabPage)
+ {
+ return const_cast<TabPage*>(pTempTabPage);
+ }
+ pTempWindow = nextLogicalChildOfParent(pTabControl, pTempWindow);
+ }
+ }
+
+ return nullptr;
+}
+
+static vcl::Window* ImplGetSubChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex )
+{
+ // ignore all windows with mpClientWindow set
+ for (vcl::Window *pNewParent = pParent->ImplGetWindow();
+ pParent != pNewParent; pParent = pNewParent);
+
+ vcl::Window* pFoundWindow = nullptr;
+ vcl::Window* pWindow = firstLogicalChildOfParent(pParent);
+ vcl::Window* pNextWindow = pWindow;
+
+ // process just the current page of a tab control
+ if (pWindow && pParent->GetType() == WindowType::TABCONTROL)
+ {
+ pWindow = ImplGetCurTabWindow(pParent);
+ pNextWindow = lastLogicalChildOfParent(pParent);
+ }
+
+ while (pWindow)
+ {
+ pWindow = pWindow->ImplGetWindow();
+
+ // skip invisible and disabled windows
+ if (isVisibleInLayout(pWindow))
+ {
+ // return the TabControl itself, before handling its page
+ if (pWindow->GetType() == WindowType::TABCONTROL)
+ {
+ if (n == nIndex)
+ return pWindow;
+ ++nIndex;
+ }
+ if (pWindow->GetStyle() & (WB_DIALOGCONTROL | WB_CHILDDLGCTRL))
+ pFoundWindow = ImplGetSubChildWindow(pWindow, n, nIndex);
+ else
+ pFoundWindow = pWindow;
+
+ if (n == nIndex)
+ return pFoundWindow;
+ ++nIndex;
+ }
+
+ pWindow = nextLogicalChildOfParent(pParent, pNextWindow);
+ pNextWindow = pWindow;
+ }
+
+ --nIndex;
+ assert(!pFoundWindow || (pFoundWindow == pFoundWindow->ImplGetWindow()));
+ return pFoundWindow;
+}
+
+vcl::Window* ImplGetChildWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
+{
+ pParent = ImplGetTopParentOfTabHierarchy( pParent );
+
+ nIndex = 0;
+ vcl::Window* pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
+ if ( bTestEnable )
+ {
+ sal_uInt16 n2 = nIndex;
+ while ( pWindow && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) )
+ {
+ n2 = nIndex+1;
+ nIndex = 0;
+ pWindow = ImplGetSubChildWindow( pParent, n2, nIndex );
+ if ( nIndex < n2 )
+ break;
+ }
+
+ if ( (nIndex < n2) && n )
+ {
+ do
+ {
+ n--;
+ nIndex = 0;
+ pWindow = ImplGetSubChildWindow( pParent, n, nIndex );
+ }
+ while ( pWindow && n && (!isEnabledInLayout(pWindow) || !pWindow->IsInputEnabled()) );
+ }
+ }
+ return pWindow;
+}
+
+static vcl::Window* ImplGetNextWindow( vcl::Window* pParent, sal_uInt16 n, sal_uInt16& nIndex, bool bTestEnable )
+{
+ vcl::Window* pWindow = ImplGetChildWindow( pParent, n+1, nIndex, bTestEnable );
+ if ( n == nIndex )
+ {
+ n = 0;
+ pWindow = ImplGetChildWindow( pParent, n, nIndex, bTestEnable );
+ }
+ return pWindow;
+}
+
+namespace vcl {
+
+static bool lcl_ToolBoxTabStop( Window* pWindow )
+{
+ ToolBox* pToolBoxWindow = static_cast<ToolBox*>( pWindow );
+
+ for ( ToolBox::ImplToolItems::size_type nPos = 0; nPos < pToolBoxWindow->GetItemCount(); nPos++ )
+ {
+ ToolBoxItemId nId = pToolBoxWindow->GetItemId( nPos );
+ if ( pToolBoxWindow->IsItemVisible( nId ) && pToolBoxWindow->IsItemEnabled( nId ) )
+ return true;
+ }
+
+ return false;
+}
+
+vcl::Window* Window::ImplGetDlgWindow( sal_uInt16 nIndex, GetDlgWindowType nType,
+ sal_uInt16 nFormStart, sal_uInt16 nFormEnd,
+ sal_uInt16* pIndex )
+{
+ SAL_WARN_IF( (nIndex < nFormStart) || (nIndex > nFormEnd), "vcl",
+ "Window::ImplGetDlgWindow() - nIndex not in Form" );
+
+ vcl::Window* pWindow = nullptr;
+ sal_uInt16 i;
+ sal_uInt16 nTemp;
+ sal_uInt16 nStartIndex;
+
+ if ( nType == GetDlgWindowType::Prev )
+ {
+ i = nIndex;
+ do
+ {
+ if ( i > nFormStart )
+ i--;
+ else
+ i = nFormEnd;
+ pWindow = ImplGetChildWindow( this, i, nTemp, true );
+ if ( !pWindow )
+ break;
+ if ( (i == nTemp) && (pWindow->GetStyle() & WB_TABSTOP) )
+ {
+ if ( WindowType::TOOLBOX == pWindow->GetType() )
+ {
+ if ( lcl_ToolBoxTabStop( pWindow ) )
+ break;
+ }
+ else
+ break;
+ }
+ }
+ while ( i != nIndex );
+ }
+ else
+ {
+ i = nIndex;
+ pWindow = ImplGetChildWindow( this, i, i, (nType == GetDlgWindowType::First) );
+ if ( pWindow )
+ {
+ nStartIndex = i;
+
+ if ( nType == GetDlgWindowType::Next )
+ {
+ if ( i < nFormEnd )
+ {
+ pWindow = ImplGetNextWindow( this, i, i, true );
+ if ( (i > nFormEnd) || (i < nFormStart) )
+ pWindow = ImplGetChildWindow( this, nFormStart, i, true );
+ }
+ else
+ pWindow = ImplGetChildWindow( this, nFormStart, i, true );
+ }
+
+ if (i <= nFormEnd && pWindow)
+ {
+ // carry the 2nd index, in case all controls are disabled
+ sal_uInt16 nStartIndex2 = i;
+ sal_uInt16 nOldIndex = i+1;
+
+ do
+ {
+ if ( pWindow->GetStyle() & WB_TABSTOP )
+ {
+ if ( WindowType::TOOLBOX == pWindow->GetType() )
+ {
+ if ( lcl_ToolBoxTabStop( pWindow ) )
+ break;
+ }
+ else
+ break;
+ }
+ if( i == nOldIndex ) // only disabled controls ?
+ {
+ i = nStartIndex2;
+ break;
+ }
+ nOldIndex = i;
+ if ( (i > nFormEnd) || (i < nFormStart) )
+ pWindow = ImplGetChildWindow( this, nFormStart, i, true );
+ else
+ pWindow = ImplGetNextWindow( this, i, i, true );
+ }
+ while (i != nStartIndex && i != nStartIndex2 && pWindow);
+
+ if ( (i == nStartIndex2) && pWindow &&
+ (!(pWindow->GetStyle() & WB_TABSTOP) || !isEnabledInLayout(pWindow)) )
+ i = nStartIndex;
+ }
+ }
+
+ if ( nType == GetDlgWindowType::First )
+ {
+ if ( pWindow )
+ {
+ if ( pWindow->GetType() == WindowType::TABCONTROL )
+ {
+ vcl::Window* pNextWindow = ImplGetDlgWindow( i, GetDlgWindowType::Next );
+ if ( pNextWindow )
+ {
+ if ( pWindow->IsChild( pNextWindow ) )
+ pWindow = pNextWindow;
+ }
+ }
+
+ if ( !(pWindow->GetStyle() & WB_TABSTOP) )
+ pWindow = nullptr;
+ }
+ }
+ }
+
+ if ( pIndex )
+ *pIndex = i;
+
+ return pWindow;
+}
+
+} /* namespace vcl */
+
+vcl::Window* ImplFindDlgCtrlWindow( vcl::Window* pParent, vcl::Window* pWindow, sal_uInt16& rIndex,
+ sal_uInt16& rFormStart, sal_uInt16& rFormEnd )
+{
+ vcl::Window* pSWindow;
+ vcl::Window* pSecondWindow = nullptr;
+ vcl::Window* pTempWindow = nullptr;
+ sal_uInt16 i;
+ sal_uInt16 nSecond_i = 0;
+ sal_uInt16 nFormStart = 0;
+ sal_uInt16 nSecondFormStart = 0;
+ sal_uInt16 nFormEnd;
+
+ // find focus window in the child list
+ vcl::Window* pFirstChildWindow = pSWindow = ImplGetChildWindow( pParent, 0, i, false );
+
+ if( pWindow == nullptr )
+ pWindow = pSWindow;
+
+ while ( pSWindow )
+ {
+ // the DialogControlStart mark is only accepted for the direct children
+ if ( !ImplHasIndirectTabParent( pSWindow )
+ && pSWindow->ImplGetWindow()->IsDialogControlStart() )
+ nFormStart = i;
+
+ // SecondWindow for composite controls like ComboBoxes and arrays
+ if ( pSWindow->ImplIsWindowOrChild( pWindow ) )
+ {
+ pSecondWindow = pSWindow;
+ nSecond_i = i;
+ nSecondFormStart = nFormStart;
+ if ( pSWindow == pWindow )
+ break;
+ }
+
+ pSWindow = ImplGetNextWindow( pParent, i, i, false );
+ if ( !i )
+ pSWindow = nullptr;
+ }
+
+ if ( !pSWindow )
+ {
+ // Window not found; we cannot handle it
+ if ( !pSecondWindow )
+ return nullptr;
+ else
+ {
+ pSWindow = pSecondWindow;
+ i = nSecond_i;
+ nFormStart = nSecondFormStart;
+ }
+ }
+
+ // initialize
+ rIndex = i;
+ rFormStart = nFormStart;
+
+ // find end of template
+ sal_Int32 nIteration = 0;
+ do
+ {
+ nFormEnd = i;
+ pTempWindow = ImplGetNextWindow( pParent, i, i, false );
+
+ // the DialogControlStart mark is only accepted for the direct children
+ if ( !i
+ || ( pTempWindow && !ImplHasIndirectTabParent( pTempWindow )
+ && pTempWindow->ImplGetWindow()->IsDialogControlStart() ) )
+ break;
+
+ if ( pTempWindow && pTempWindow == pFirstChildWindow )
+ {
+ // It is possible to go through the begin of hierarchy once
+ // while looking for DialogControlStart mark.
+ // If it happens second time, it looks like an endless loop,
+ // that should be impossible, but just for the case...
+ nIteration++;
+ if ( nIteration >= 2 )
+ {
+ // this is an unexpected scenario
+ SAL_WARN( "vcl", "It seems to be an endless loop!" );
+ rFormStart = 0;
+ break;
+ }
+ }
+ }
+ while ( pTempWindow );
+ rFormEnd = nFormEnd;
+
+ return pSWindow;
+}
+
+vcl::Window* ImplFindAccelWindow( vcl::Window* pParent, sal_uInt16& rIndex, sal_Unicode cCharCode,
+ sal_uInt16 nFormStart, sal_uInt16 nFormEnd, bool bCheckEnable )
+{
+ SAL_WARN_IF( (rIndex < nFormStart) || (rIndex > nFormEnd), "vcl",
+ "Window::ImplFindAccelWindow() - rIndex not in Form" );
+
+ sal_Unicode cCompareChar;
+ sal_uInt16 nStart = rIndex;
+ sal_uInt16 i = rIndex;
+ vcl::Window* pWindow;
+
+ uno::Reference<i18n::XCharacterClassification> const& xCharClass(ImplGetCharClass());
+
+ const css::lang::Locale& rLocale = Application::GetSettings().GetUILanguageTag().getLocale();
+ cCharCode = xCharClass->toUpper( OUString(cCharCode), 0, 1, rLocale )[0];
+
+ if ( i < nFormEnd )
+ pWindow = ImplGetNextWindow( pParent, i, i, true );
+ else
+ pWindow = ImplGetChildWindow( pParent, nFormStart, i, true );
+ while( pWindow )
+ {
+ const OUString aStr = pWindow->GetText();
+ sal_Int32 nPos = aStr.indexOf( '~' );
+ while (nPos != -1)
+ {
+ cCompareChar = aStr[nPos+1];
+ cCompareChar = xCharClass->toUpper( OUString(cCompareChar), 0, 1, rLocale )[0];
+ if ( cCompareChar == cCharCode )
+ {
+ if (pWindow->GetType() == WindowType::FIXEDTEXT)
+ {
+ FixedText *pFixedText = static_cast<FixedText*>(pWindow);
+ vcl::Window *pMnemonicWidget = pFixedText->get_mnemonic_widget();
+ SAL_WARN_IF(isContainerWindow(pFixedText->GetParent()) && !pMnemonicWidget,
+ "vcl.a11y", "label missing mnemonic_widget?");
+ if (pMnemonicWidget)
+ return pMnemonicWidget;
+ }
+
+ // skip Static-Controls
+ if ( (pWindow->GetType() == WindowType::FIXEDTEXT) ||
+ (pWindow->GetType() == WindowType::FIXEDLINE) ||
+ (pWindow->GetType() == WindowType::GROUPBOX) )
+ pWindow = pParent->ImplGetDlgWindow( i, GetDlgWindowType::Next );
+ rIndex = i;
+ return pWindow;
+ }
+ nPos = aStr.indexOf( '~', nPos+1 );
+ }
+
+ // #i93011# it would have made sense to have this really recursive
+ // right from the start. However this would cause unpredictable side effects now
+ // so instead we have a style bit for some child windows, that want their
+ // children checked for accelerators
+ if( (pWindow->GetStyle() & WB_CHILDDLGCTRL) != 0 )
+ {
+ sal_uInt16 nChildIndex;
+ sal_uInt16 nChildFormStart;
+ sal_uInt16 nChildFormEnd;
+
+ // get form start and end
+ ::ImplFindDlgCtrlWindow( pWindow, nullptr,
+ nChildIndex, nChildFormStart, nChildFormEnd );
+ vcl::Window* pAccelWin = ImplFindAccelWindow( pWindow, nChildIndex, cCharCode,
+ nChildFormStart, nChildFormEnd,
+ bCheckEnable );
+ if( pAccelWin )
+ return pAccelWin;
+ }
+
+ if ( i == nStart )
+ break;
+
+ if ( i < nFormEnd )
+ {
+ pWindow = ImplGetNextWindow( pParent, i, i, bCheckEnable );
+ if( ! pWindow )
+ pWindow = ImplGetChildWindow( pParent, nFormStart, i, bCheckEnable );
+ }
+ else
+ pWindow = ImplGetChildWindow( pParent, nFormStart, i, bCheckEnable );
+ }
+
+ return nullptr;
+}
+
+namespace vcl {
+
+void Window::SetMnemonicActivateHdl(const Link<vcl::Window&, bool>& rLink)
+{
+ if (mpWindowImpl) // may be called after dispose
+ {
+ mpWindowImpl->maMnemonicActivateHdl = rLink;
+ }
+}
+
+void Window::ImplControlFocus( GetFocusFlags nFlags )
+{
+ if ( nFlags & GetFocusFlags::Mnemonic )
+ {
+ if (mpWindowImpl->maMnemonicActivateHdl.Call(*this))
+ return;
+
+ const bool bUniqueMnemonic(nFlags & GetFocusFlags::UniqueMnemonic);
+
+ if ( GetType() == WindowType::RADIOBUTTON )
+ {
+ if (bUniqueMnemonic && !static_cast<RadioButton*>(this)->IsChecked())
+ static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
+ else
+ ImplGrabFocus( nFlags );
+ }
+ else
+ {
+ ImplGrabFocus( nFlags );
+ if (bUniqueMnemonic)
+ {
+ if ( GetType() == WindowType::CHECKBOX )
+ static_cast<CheckBox*>(this)->ImplCheck();
+ else if ( mpWindowImpl->mbPushButton )
+ {
+ static_cast<PushButton*>(this)->SetPressed( true );
+ static_cast<PushButton*>(this)->SetPressed( false );
+ static_cast<PushButton*>(this)->Click();
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( GetType() == WindowType::RADIOBUTTON )
+ {
+ if ( !static_cast<RadioButton*>(this)->IsChecked() )
+ static_cast<RadioButton*>(this)->ImplCallClick( true, nFlags );
+ else
+ ImplGrabFocus( nFlags );
+ }
+ else
+ ImplGrabFocus( nFlags );
+ }
+}
+
+} /* namespace vcl */
+
+namespace
+{
+ bool isSuitableDestination(vcl::Window const *pWindow)
+ {
+ return (pWindow && isVisibleInLayout(pWindow) &&
+ isEnabledInLayout(pWindow) && pWindow->IsInputEnabled() &&
+ //Pure window shouldn't get window after controls such as
+ //buttons.
+ (pWindow->GetType() != WindowType::WINDOW &&
+ pWindow->GetType() != WindowType::WORKWINDOW && pWindow->GetType() != WindowType::CONTROL)
+ );
+ }
+
+ bool focusNextInGroup(const std::vector<VclPtr<RadioButton> >::iterator& aStart, std::vector<VclPtr<RadioButton> > &rGroup)
+ {
+ std::vector<VclPtr<RadioButton> >::iterator aI(aStart);
+
+ if (aStart != rGroup.end())
+ ++aI;
+
+ aI = std::find_if(aI, rGroup.end(), isSuitableDestination);
+ if (aI != rGroup.end())
+ {
+ vcl::Window *pWindow = *aI;
+ pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
+ return true;
+ }
+ aI = std::find_if(rGroup.begin(), aStart, isSuitableDestination);
+ if (aI != aStart)
+ {
+ vcl::Window *pWindow = *aI;
+ pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Forward );
+ return true;
+ }
+ return false;
+ }
+
+ bool nextInGroup(RadioButton *pSourceWindow, bool bBackward)
+ {
+ std::vector<VclPtr<RadioButton> > aGroup(pSourceWindow->GetRadioButtonGroup());
+
+ if (aGroup.size() < 2) // have to have at last 2 buttons to be a useful group
+ return false;
+
+ if (bBackward)
+ std::reverse(aGroup.begin(), aGroup.end());
+
+ auto aStart(std::find(aGroup.begin(), aGroup.end(), VclPtr<RadioButton>(pSourceWindow)));
+
+ assert(aStart != aGroup.end());
+
+ return focusNextInGroup(aStart, aGroup);
+ }
+}
+
+namespace vcl {
+
+bool Window::ImplDlgCtrl( const KeyEvent& rKEvt, bool bKeyInput )
+{
+ vcl::KeyCode aKeyCode = rKEvt.GetKeyCode();
+ sal_uInt16 nKeyCode = aKeyCode.GetCode();
+ vcl::Window* pSWindow;
+ vcl::Window* pTempWindow;
+ vcl::Window* pButtonWindow;
+ sal_uInt16 i;
+ sal_uInt16 iButton;
+ sal_uInt16 iButtonStart;
+ sal_uInt16 iTemp;
+ sal_uInt16 nIndex;
+ sal_uInt16 nFormStart;
+ sal_uInt16 nFormEnd;
+ DialogControlFlags nDlgCtrlFlags;
+
+ // we cannot take over control without Focus-window
+ vcl::Window* pFocusWindow = Application::GetFocusWindow();
+ if ( !pFocusWindow || !ImplIsWindowOrChild( pFocusWindow ) )
+ return false;
+
+ // find Focus-Window in the child list
+ pSWindow = ::ImplFindDlgCtrlWindow( this, pFocusWindow,
+ nIndex, nFormStart, nFormEnd );
+ if ( !pSWindow )
+ return false;
+ i = nIndex;
+
+ nDlgCtrlFlags = DialogControlFlags::NONE;
+ pTempWindow = pSWindow;
+ do
+ {
+ nDlgCtrlFlags |= pTempWindow->GetDialogControlFlags();
+ if ( pTempWindow == this )
+ break;
+ pTempWindow = pTempWindow->ImplGetParent();
+ }
+ while ( pTempWindow );
+
+ pButtonWindow = nullptr;
+
+ if ( nKeyCode == KEY_RETURN )
+ {
+ // search first for a DefPushButton/CancelButton
+ pButtonWindow = ImplGetChildWindow( this, nFormStart, iButton, true );
+ iButtonStart = iButton;
+ while ( pButtonWindow )
+ {
+ if ( (pButtonWindow->GetStyle() & WB_DEFBUTTON) &&
+ pButtonWindow->mpWindowImpl->mbPushButton )
+ break;
+
+ pButtonWindow = ImplGetNextWindow( this, iButton, iButton, true );
+ if ( (iButton <= iButtonStart) || (iButton > nFormEnd) )
+ pButtonWindow = nullptr;
+ }
+
+ if ( bKeyInput && !pButtonWindow && (nDlgCtrlFlags & DialogControlFlags::Return) )
+ {
+ GetDlgWindowType nType;
+ GetFocusFlags nGetFocusFlags = GetFocusFlags::Tab;
+ sal_uInt16 nNewIndex;
+ sal_uInt16 iStart;
+ if ( aKeyCode.IsShift() )
+ {
+ nType = GetDlgWindowType::Prev;
+ nGetFocusFlags |= GetFocusFlags::Backward;
+ }
+ else
+ {
+ nType = GetDlgWindowType::Next;
+ nGetFocusFlags |= GetFocusFlags::Forward;
+ }
+ iStart = i;
+ pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
+ while ( pTempWindow && (pTempWindow != pSWindow) )
+ {
+ if ( !pTempWindow->mpWindowImpl->mbPushButton )
+ {
+ // get Around-Flag
+ if ( nType == GetDlgWindowType::Prev )
+ {
+ if ( nNewIndex > iStart )
+ nGetFocusFlags |= GetFocusFlags::Around;
+ }
+ else
+ {
+ if ( nNewIndex < iStart )
+ nGetFocusFlags |= GetFocusFlags::Around;
+ }
+ pTempWindow->ImplControlFocus( nGetFocusFlags );
+ return true;
+ }
+ else
+ {
+ i = nNewIndex;
+ pTempWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
+ }
+ if ( (i <= iStart) || (i > nFormEnd) )
+ pTempWindow = nullptr;
+ }
+ // if this is the same window, simulate a Get/LoseFocus,
+ // in case AROUND is being processed
+ if ( pTempWindow && (pTempWindow == pSWindow) )
+ {
+ NotifyEvent aNEvt1( NotifyEventType::LOSEFOCUS, pSWindow );
+ if ( !ImplCallPreNotify( aNEvt1 ) )
+ pSWindow->CompatLoseFocus();
+ pSWindow->mpWindowImpl->mnGetFocusFlags = nGetFocusFlags | GetFocusFlags::Around;
+ NotifyEvent aNEvt2( NotifyEventType::GETFOCUS, pSWindow );
+ if ( !ImplCallPreNotify( aNEvt2 ) )
+ pSWindow->CompatGetFocus();
+ pSWindow->mpWindowImpl->mnGetFocusFlags = GetFocusFlags::NONE;
+ return true;
+ }
+ }
+ }
+ else if ( nKeyCode == KEY_ESCAPE )
+ {
+ // search first for a DefPushButton/CancelButton
+ pButtonWindow = ImplGetChildWindow( this, nFormStart, iButton, true );
+ iButtonStart = iButton;
+ while ( pButtonWindow )
+ {
+ if ( pButtonWindow->GetType() == WindowType::CANCELBUTTON )
+ break;
+
+ pButtonWindow = ImplGetNextWindow( this, iButton, iButton, true );
+ if ( (iButton <= iButtonStart) || (iButton > nFormEnd) )
+ pButtonWindow = nullptr;
+ }
+
+ if ( bKeyInput && mpWindowImpl->mpDlgCtrlDownWindow )
+ {
+ if ( mpWindowImpl->mpDlgCtrlDownWindow.get() != pButtonWindow )
+ {
+ mpWindowImpl->mpDlgCtrlDownWindow->SetPressed( false );
+ mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
+ return true;
+ }
+ }
+ }
+ else if ( bKeyInput )
+ {
+ if ( nKeyCode == KEY_TAB )
+ {
+ // do not skip Alt key, for MS Windows
+ if ( !aKeyCode.IsMod2() )
+ {
+ sal_uInt16 nNewIndex;
+ bool bForm = false;
+
+ // for Ctrl-Tab check if we want to jump to next template
+ if ( aKeyCode.IsMod1() )
+ {
+ // search group
+ vcl::Window* pFormFirstWindow = nullptr;
+ vcl::Window* pLastFormFirstWindow = nullptr;
+ pTempWindow = ImplGetChildWindow( this, 0, iTemp, false );
+ vcl::Window* pPrevFirstFormFirstWindow = nullptr;
+ vcl::Window* pFirstFormFirstWindow = pTempWindow;
+ while ( pTempWindow )
+ {
+ if ( pTempWindow->ImplGetWindow()->IsDialogControlStart() )
+ {
+ if ( iTemp != 0 )
+ bForm = true;
+ if ( aKeyCode.IsShift() )
+ {
+ if ( iTemp <= nIndex )
+ pFormFirstWindow = pPrevFirstFormFirstWindow;
+ pPrevFirstFormFirstWindow = pTempWindow;
+ }
+ else
+ {
+ if ( (iTemp > nIndex) && !pFormFirstWindow )
+ pFormFirstWindow = pTempWindow;
+ }
+ pLastFormFirstWindow = pTempWindow;
+ }
+
+ pTempWindow = ImplGetNextWindow( this, iTemp, iTemp, false );
+ if ( !iTemp )
+ pTempWindow = nullptr;
+ }
+
+ if ( bForm )
+ {
+ if ( !pFormFirstWindow )
+ {
+ if ( aKeyCode.IsShift() )
+ pFormFirstWindow = pLastFormFirstWindow;
+ else
+ pFormFirstWindow = pFirstFormFirstWindow;
+ }
+
+ sal_uInt16 nFoundFormStart = 0;
+ sal_uInt16 nFoundFormEnd = 0;
+ sal_uInt16 nTempIndex = 0;
+ if ( ::ImplFindDlgCtrlWindow( this, pFormFirstWindow, nTempIndex,
+ nFoundFormStart, nFoundFormEnd ) )
+ {
+ nTempIndex = nFoundFormStart;
+ pFormFirstWindow = ImplGetDlgWindow( nTempIndex, GetDlgWindowType::First, nFoundFormStart, nFoundFormEnd );
+ if ( pFormFirstWindow )
+ {
+ pFormFirstWindow->ImplControlFocus();
+ return true;
+ }
+ }
+ }
+ }
+
+ if ( !bForm )
+ {
+ // Only use Ctrl-TAB if it was allowed for the whole
+ // dialog or for the current control (#103667#)
+ if (!aKeyCode.IsMod1() || (pSWindow->GetStyle() & WB_NODIALOGCONTROL))
+ {
+ GetDlgWindowType nType;
+ GetFocusFlags nGetFocusFlags = GetFocusFlags::Tab;
+ if ( aKeyCode.IsShift() )
+ {
+ nType = GetDlgWindowType::Prev;
+ nGetFocusFlags |= GetFocusFlags::Backward;
+ }
+ else
+ {
+ nType = GetDlgWindowType::Next;
+ nGetFocusFlags |= GetFocusFlags::Forward;
+ }
+ vcl::Window* pWindow = ImplGetDlgWindow( i, nType, nFormStart, nFormEnd, &nNewIndex );
+ // if this is the same window, simulate a Get/LoseFocus,
+ // in case AROUND is being processed
+ if ( pWindow == pSWindow )
+ {
+ NotifyEvent aNEvt1( NotifyEventType::LOSEFOCUS, pSWindow );
+ if ( !ImplCallPreNotify( aNEvt1 ) )
+ pSWindow->CompatLoseFocus();
+ pSWindow->mpWindowImpl->mnGetFocusFlags = nGetFocusFlags | GetFocusFlags::Around;
+ NotifyEvent aNEvt2( NotifyEventType::GETFOCUS, pSWindow );
+ if ( !ImplCallPreNotify( aNEvt2 ) )
+ pSWindow->CompatGetFocus();
+ pSWindow->mpWindowImpl->mnGetFocusFlags = GetFocusFlags::NONE;
+ return true;
+ }
+ else if ( pWindow )
+ {
+ // get Around-Flag
+ if ( nType == GetDlgWindowType::Prev )
+ {
+ if ( nNewIndex > i )
+ nGetFocusFlags |= GetFocusFlags::Around;
+ }
+ else
+ {
+ if ( nNewIndex < i )
+ nGetFocusFlags |= GetFocusFlags::Around;
+ }
+ pWindow->ImplControlFocus( nGetFocusFlags );
+ return true;
+ }
+ }
+ }
+ }
+ }
+ else if ( (nKeyCode == KEY_LEFT) || (nKeyCode == KEY_UP) )
+ {
+ if (pSWindow->GetType() == WindowType::RADIOBUTTON)
+ return nextInGroup(static_cast<RadioButton*>(pSWindow), true);
+ else
+ {
+ WinBits nStyle = pSWindow->GetStyle();
+ if ( !(nStyle & WB_GROUP) )
+ {
+ vcl::Window* pWindow = prevLogicalChildOfParent(this, pSWindow);
+ while ( pWindow )
+ {
+ pWindow = pWindow->ImplGetWindow();
+
+ nStyle = pWindow->GetStyle();
+
+ if (isSuitableDestination(pWindow))
+ {
+ if ( pWindow != pSWindow )
+ pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
+ return true;
+ }
+
+ if ( nStyle & WB_GROUP )
+ break;
+
+ pWindow = prevLogicalChildOfParent(this, pWindow);
+ }
+ }
+ }
+ }
+ else if ( (nKeyCode == KEY_RIGHT) || (nKeyCode == KEY_DOWN) )
+ {
+ if (pSWindow->GetType() == WindowType::RADIOBUTTON)
+ return nextInGroup(static_cast<RadioButton*>(pSWindow), false);
+ else
+ {
+ vcl::Window* pWindow = nextLogicalChildOfParent(this, pSWindow);
+ while ( pWindow )
+ {
+ pWindow = pWindow->ImplGetWindow();
+
+ WinBits nStyle = pWindow->GetStyle();
+
+ if ( nStyle & WB_GROUP )
+ break;
+
+ if (isSuitableDestination(pWindow))
+ {
+ pWindow->ImplControlFocus( GetFocusFlags::CURSOR | GetFocusFlags::Backward );
+ return true;
+ }
+
+ pWindow = nextLogicalChildOfParent(this, pWindow);
+ }
+ }
+ }
+ else
+ {
+ sal_Unicode c = rKEvt.GetCharCode();
+ if ( c )
+ {
+ pSWindow = ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd );
+ if ( pSWindow )
+ {
+ GetFocusFlags nGetFocusFlags = GetFocusFlags::Mnemonic;
+ if ( pSWindow == ::ImplFindAccelWindow( this, i, c, nFormStart, nFormEnd ) )
+ nGetFocusFlags |= GetFocusFlags::UniqueMnemonic;
+#ifdef _WIN32
+ // tdf#157649 Allow omitting the Alt key when focus is in the dialog action area:
+ bool bIsButtonBox = dynamic_cast<VclButtonBox*>(pSWindow->GetParent()) != nullptr;
+ if ((bIsButtonBox && pSWindow->GetParent()->HasChildPathFocus(true)) || aKeyCode.IsMod2())
+#else
+ if (aKeyCode.IsMod2())
+#endif
+ {
+ pSWindow->ImplControlFocus( nGetFocusFlags );
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ if (isSuitableDestination(pButtonWindow))
+ {
+ if ( bKeyInput )
+ {
+ if ( mpWindowImpl->mpDlgCtrlDownWindow && (mpWindowImpl->mpDlgCtrlDownWindow.get() != pButtonWindow) )
+ {
+ mpWindowImpl->mpDlgCtrlDownWindow->SetPressed( false );
+ mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
+ }
+
+ static_cast<PushButton*>(pButtonWindow)->SetPressed( true );
+ mpWindowImpl->mpDlgCtrlDownWindow = static_cast<PushButton*>(pButtonWindow);
+ }
+ else if ( mpWindowImpl->mpDlgCtrlDownWindow.get() == pButtonWindow )
+ {
+ mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
+ static_cast<PushButton*>(pButtonWindow)->SetPressed( false );
+ static_cast<PushButton*>(pButtonWindow)->Click();
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+// checks if this window has dialog control
+bool Window::ImplHasDlgCtrl() const
+{
+ vcl::Window* pDlgCtrlParent;
+
+ // lookup window for dialog control
+ pDlgCtrlParent = ImplGetParent();
+ while ( pDlgCtrlParent &&
+ !pDlgCtrlParent->ImplIsOverlapWindow() &&
+ ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
+ pDlgCtrlParent = pDlgCtrlParent->ImplGetParent();
+
+ return pDlgCtrlParent && ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) == WB_DIALOGCONTROL);
+}
+
+void Window::ImplDlgCtrlNextWindow()
+{
+ vcl::Window* pDlgCtrlParent;
+ vcl::Window* pDlgCtrl;
+ vcl::Window* pSWindow;
+ sal_uInt16 nIndex;
+ sal_uInt16 nFormStart;
+ sal_uInt16 nFormEnd;
+
+ // lookup window for dialog control
+ pDlgCtrl = this;
+ pDlgCtrlParent = ImplGetParent();
+ while ( pDlgCtrlParent &&
+ !pDlgCtrlParent->ImplIsOverlapWindow() &&
+ ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
+ pDlgCtrlParent = pDlgCtrlParent->ImplGetParent();
+
+ if ( !pDlgCtrlParent || (GetStyle() & WB_NODIALOGCONTROL) || ((pDlgCtrlParent->GetStyle() & (WB_DIALOGCONTROL | WB_NODIALOGCONTROL)) != WB_DIALOGCONTROL) )
+ return;
+
+ // lookup window in child list
+ pSWindow = ::ImplFindDlgCtrlWindow( pDlgCtrlParent, pDlgCtrl,
+ nIndex, nFormStart, nFormEnd );
+ if ( !pSWindow )
+ return;
+
+ vcl::Window* pWindow = pDlgCtrlParent->ImplGetDlgWindow( nIndex, GetDlgWindowType::Next, nFormStart, nFormEnd );
+ if ( pWindow && (pWindow != pSWindow) )
+ pWindow->ImplControlFocus();
+}
+
+static void ImplDlgCtrlUpdateDefButton( vcl::Window* pParent, vcl::Window* pFocusWindow,
+ bool bGetFocus )
+{
+ PushButton* pOldDefButton = nullptr;
+ PushButton* pNewDefButton = nullptr;
+ vcl::Window* pSWindow;
+ sal_uInt16 i;
+ sal_uInt16 nFormStart;
+ sal_uInt16 nFormEnd;
+
+ // find template
+ pSWindow = ::ImplFindDlgCtrlWindow( pParent, pFocusWindow, i, nFormStart, nFormEnd );
+ if ( !pSWindow )
+ {
+ nFormStart = 0;
+ nFormEnd = 0xFFFF;
+ }
+
+ pSWindow = ImplGetChildWindow( pParent, nFormStart, i, false );
+ while ( pSWindow )
+ {
+ if ( pSWindow->ImplIsPushButton() )
+ {
+ PushButton* pPushButton = static_cast<PushButton*>(pSWindow);
+ if ( pPushButton->ImplIsDefButton() )
+ pOldDefButton = pPushButton;
+ if ( pPushButton->HasChildPathFocus() )
+ pNewDefButton = pPushButton;
+ else if ( !pNewDefButton && (pPushButton->GetStyle() & WB_DEFBUTTON) )
+ pNewDefButton = pPushButton;
+ }
+
+ pSWindow = ImplGetNextWindow( pParent, i, i, false );
+ if ( !i || (i > nFormEnd) )
+ pSWindow = nullptr;
+ }
+
+ if ( !bGetFocus )
+ {
+ sal_uInt16 nDummy;
+ vcl::Window* pNewFocusWindow = Application::GetFocusWindow();
+ if ( !pNewFocusWindow || !pParent->ImplIsWindowOrChild( pNewFocusWindow ) )
+ pNewDefButton = nullptr;
+ else if ( !::ImplFindDlgCtrlWindow( pParent, pNewFocusWindow, i, nDummy, nDummy ) ||
+ (i < nFormStart) || (i > nFormEnd) )
+ pNewDefButton = nullptr;
+ }
+
+ if ( pOldDefButton != pNewDefButton )
+ {
+ if ( pOldDefButton )
+ pOldDefButton->ImplSetDefButton( false );
+ if ( pNewDefButton )
+ pNewDefButton->ImplSetDefButton( true );
+ }
+}
+
+void Window::ImplDlgCtrlFocusChanged( vcl::Window* pWindow, bool bGetFocus )
+{
+ if ( mpWindowImpl->mpDlgCtrlDownWindow && !bGetFocus )
+ {
+ mpWindowImpl->mpDlgCtrlDownWindow->SetPressed( false );
+ mpWindowImpl->mpDlgCtrlDownWindow = nullptr;
+ }
+
+ ImplDlgCtrlUpdateDefButton( this, pWindow, bGetFocus );
+}
+
+vcl::Window* Window::ImplFindDlgCtrlWindow( vcl::Window* pWindow )
+{
+ sal_uInt16 nIndex;
+ sal_uInt16 nFormStart;
+ sal_uInt16 nFormEnd;
+
+ // find Focus-Window in the Child-List and return
+ return ::ImplFindDlgCtrlWindow( this, pWindow, nIndex, nFormStart, nFormEnd );
+}
+
+KeyEvent Window::GetActivationKey() const
+{
+ KeyEvent aKeyEvent;
+
+ sal_Unicode nAccel = getAccel( GetText() );
+ if( ! nAccel )
+ {
+ vcl::Window* pWindow = GetAccessibleRelationLabeledBy();
+ if( pWindow )
+ nAccel = getAccel( pWindow->GetText() );
+ }
+ if( nAccel )
+ {
+ sal_uInt16 nCode = 0;
+ if( nAccel >= 'a' && nAccel <= 'z' )
+ nCode = KEY_A + (nAccel-'a');
+ else if( nAccel >= 'A' && nAccel <= 'Z' )
+ nCode = KEY_A + (nAccel-'A');
+ else if( nAccel >= '0' && nAccel <= '9' )
+ nCode = KEY_0 + (nAccel-'0');
+ else if( nAccel == '.' )
+ nCode = KEY_POINT;
+ else if( nAccel == '-' )
+ nCode = KEY_SUBTRACT;
+ vcl::KeyCode aKeyCode( nCode, false, false, true, false );
+ aKeyEvent = KeyEvent( nAccel, aKeyCode );
+ }
+ return aKeyEvent;
+}
+
+} /* namespace vcl */
+
+sal_Unicode getAccel( std::u16string_view rStr )
+{
+ sal_Unicode nChar = 0;
+ size_t nPos = 0;
+ do
+ {
+ nPos = rStr.find( '~', nPos );
+ if( nPos != std::u16string_view::npos && nPos < rStr.size() )
+ nChar = rStr[ ++nPos ];
+ else
+ nChar = 0;
+ } while( nChar == '~' );
+ return nChar;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */