/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define IMPL_MIN_NEEDSYSWIN 49 bool ImplCallPreNotify( NotifyEvent& rEvt ) { return rEvt.GetWindow()->CompatPreNotify( rEvt ); } static bool ImplHandleMouseFloatMode( vcl::Window* pChild, const Point& rMousePos, sal_uInt16 nCode, MouseNotifyEvent nSVEvent, bool bMouseLeave ) { ImplSVData* pSVData = ImplGetSVData(); if (pSVData->mpWinData->mpFirstFloat && !pSVData->mpWinData->mpCaptureWin && !pSVData->mpWinData->mpFirstFloat->ImplIsFloatPopupModeWindow(pChild)) { /* * #93895# since floats are system windows, coordinates have * to be converted to float relative for the hittest */ bool bHitTestInsideRect = false; FloatingWindow* pFloat = pSVData->mpWinData->mpFirstFloat->ImplFloatHitTest( pChild, rMousePos, bHitTestInsideRect ); if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) { if ( bMouseLeave ) return true; if ( !pFloat || bHitTestInsideRect ) { if ( ImplGetSVHelpData().mpHelpWin && !ImplGetSVHelpData().mbKeyboardHelp ) ImplDestroyHelpWindow( true ); pChild->ImplGetFrame()->SetPointer( PointerStyle::Arrow ); return true; } } else { if ( nCode & MOUSE_LEFT ) { if ( nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN ) { if ( !pFloat ) { FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat(); pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); return true; } else if ( bHitTestInsideRect ) { pFloat->ImplSetMouseDown(); return true; } } else { if ( pFloat ) { if ( bHitTestInsideRect ) { if ( pFloat->ImplIsMouseDown() ) pFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel ); return true; } } else { FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat(); FloatWinPopupFlags nPopupFlags = pLastLevelFloat->GetPopupModeFlags(); if ( !(nPopupFlags & FloatWinPopupFlags::NoMouseUpClose) ) { pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); return true; } } } } else { if ( !pFloat ) { FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat(); FloatWinPopupFlags nPopupFlags = pLastLevelFloat->GetPopupModeFlags(); if ( nPopupFlags & FloatWinPopupFlags::AllMouseButtonClose ) { if ( (nPopupFlags & FloatWinPopupFlags::NoMouseUpClose) && (nSVEvent == MouseNotifyEvent::MOUSEBUTTONUP) ) return true; pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); return true; } else return true; } } } } return false; } static void ImplHandleMouseHelpRequest( vcl::Window* pChild, const Point& rMousePos ) { ImplSVHelpData& aHelpData = ImplGetSVHelpData(); if ( aHelpData.mpHelpWin && ( aHelpData.mpHelpWin->IsWindowOrChild( pChild ) || pChild->IsWindowOrChild( aHelpData.mpHelpWin ) )) return; HelpEventMode nHelpMode = HelpEventMode::NONE; if ( aHelpData.mbQuickHelp ) nHelpMode = HelpEventMode::QUICK; if ( aHelpData.mbBalloonHelp ) nHelpMode |= HelpEventMode::BALLOON; if ( !(bool(nHelpMode)) ) return; if ( pChild->IsInputEnabled() && !pChild->IsInModalMode() ) { HelpEvent aHelpEvent( rMousePos, nHelpMode ); aHelpData.mbRequestingHelp = true; pChild->RequestHelp( aHelpEvent ); aHelpData.mbRequestingHelp = false; } // #104172# do not kill keyboard activated tooltips else if ( aHelpData.mpHelpWin && !aHelpData.mbKeyboardHelp) { ImplDestroyHelpWindow( true ); } } static void ImplSetMousePointer( vcl::Window const * pChild ) { if ( ImplGetSVHelpData().mbExtHelpMode ) pChild->ImplGetFrame()->SetPointer( PointerStyle::Help ); else pChild->ImplGetFrame()->SetPointer( pChild->ImplGetMousePointer() ); } static bool ImplCallCommand( const VclPtr& pChild, CommandEventId nEvt, void const * pData = nullptr, bool bMouse = false, Point const * pPos = nullptr ) { Point aPos; if ( pPos ) aPos = *pPos; else { if( bMouse ) aPos = pChild->GetPointerPosPixel(); else { // simulate mouseposition at center of window Size aSize( pChild->GetOutputSizePixel() ); aPos = Point( aSize.getWidth()/2, aSize.getHeight()/2 ); } } CommandEvent aCEvt( aPos, nEvt, bMouse, pData ); NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pChild, &aCEvt ); bool bPreNotify = ImplCallPreNotify( aNCmdEvt ); if ( pChild->IsDisposed() ) return false; if ( !bPreNotify ) { pChild->ImplGetWindowImpl()->mbCommand = false; pChild->Command( aCEvt ); if( pChild->IsDisposed() ) return false; pChild->ImplNotifyKeyMouseCommandEventListeners( aNCmdEvt ); if ( pChild->IsDisposed() ) return false; if ( pChild->ImplGetWindowImpl()->mbCommand ) return true; } return false; } /* #i34277# delayed context menu activation; * necessary if there already was a popup menu running. */ namespace { struct ContextMenuEvent { VclPtr pWindow; Point aChildPos; }; } static void ContextMenuEventLink( void* pCEvent, void* ) { ContextMenuEvent* pEv = static_cast(pCEvent); if( ! pEv->pWindow->IsDisposed() ) { ImplCallCommand( pEv->pWindow, CommandEventId::ContextMenu, nullptr, true, &pEv->aChildPos ); } delete pEv; } bool ImplHandleMouseEvent( const VclPtr& xWindow, MouseNotifyEvent nSVEvent, bool bMouseLeave, tools::Long nX, tools::Long nY, sal_uInt64 nMsgTime, sal_uInt16 nCode, MouseEventModifiers nMode ) { ImplSVHelpData& aHelpData = ImplGetSVHelpData(); ImplSVData* pSVData = ImplGetSVData(); Point aMousePos( nX, nY ); VclPtr pChild; bool bRet(false); sal_uInt16 nClicks(0); ImplFrameData* pWinFrameData = xWindow->ImplGetFrameData(); sal_uInt16 nOldCode = pWinFrameData->mnMouseCode; // we need a mousemove event, before we get a mousebuttondown or a // mousebuttonup event if ( (nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN) || (nSVEvent == MouseNotifyEvent::MOUSEBUTTONUP) ) { if ( (nSVEvent == MouseNotifyEvent::MOUSEBUTTONUP) && aHelpData.mbExtHelpMode ) Help::EndExtHelp(); if ( aHelpData.mpHelpWin ) { if( xWindow->ImplGetWindow() == aHelpData.mpHelpWin ) { ImplDestroyHelpWindow( false ); return true; // xWindow is dead now - avoid crash! } else ImplDestroyHelpWindow( true ); } if ( (pWinFrameData->mnLastMouseX != nX) || (pWinFrameData->mnLastMouseY != nY) ) { sal_uInt16 nMoveCode = nCode & ~(MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE); ImplHandleMouseEvent(xWindow, MouseNotifyEvent::MOUSEMOVE, false, nX, nY, nMsgTime, nMoveCode, nMode); } } // update frame data pWinFrameData->mnBeforeLastMouseX = pWinFrameData->mnLastMouseX; pWinFrameData->mnBeforeLastMouseY = pWinFrameData->mnLastMouseY; pWinFrameData->mnLastMouseX = nX; pWinFrameData->mnLastMouseY = nY; pWinFrameData->mnMouseCode = nCode; MouseEventModifiers const nTmpMask = MouseEventModifiers::SYNTHETIC | MouseEventModifiers::MODIFIERCHANGED; pWinFrameData->mnMouseMode = nMode & ~nTmpMask; if ( bMouseLeave ) { pWinFrameData->mbMouseIn = false; if ( ImplGetSVHelpData().mpHelpWin && !ImplGetSVHelpData().mbKeyboardHelp ) { ImplDestroyHelpWindow( true ); if ( xWindow->IsDisposed() ) return true; // xWindow is dead now - avoid crash! (#122045#) } } else pWinFrameData->mbMouseIn = true; DBG_ASSERT(!pSVData->mpWinData->mpTrackWin || (pSVData->mpWinData->mpTrackWin == pSVData->mpWinData->mpCaptureWin), "ImplHandleMouseEvent: TrackWin != CaptureWin"); // AutoScrollMode if (pSVData->mpWinData->mpAutoScrollWin && (nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN)) { pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll(); return true; } // find mouse window if (pSVData->mpWinData->mpCaptureWin) { pChild = pSVData->mpWinData->mpCaptureWin; SAL_WARN_IF( xWindow != pChild->ImplGetFrameWindow(), "vcl", "ImplHandleMouseEvent: mouse event is not sent to capture window" ); // java client cannot capture mouse correctly if ( xWindow != pChild->ImplGetFrameWindow() ) return false; if ( bMouseLeave ) return false; } else { if ( bMouseLeave ) pChild = nullptr; else pChild = xWindow->ImplFindWindow( aMousePos ); } // test this because mouse events are buffered in the remote version // and size may not be in sync if ( !pChild && !bMouseLeave ) return false; // execute a few tests and catch the message or implement the status if ( pChild ) { if( pChild->ImplIsAntiparallel() ) { // re-mirror frame pos at pChild const OutputDevice *pChildWinOutDev = pChild->GetOutDev(); pChildWinOutDev->ReMirror( aMousePos ); } // no mouse messages to disabled windows // #106845# if the window was disabled during capturing we have to pass the mouse events to release capturing if (pSVData->mpWinData->mpCaptureWin.get() != pChild && (!pChild->IsEnabled() || !pChild->IsInputEnabled() || pChild->IsInModalMode())) { ImplHandleMouseFloatMode( pChild, aMousePos, nCode, nSVEvent, bMouseLeave ); if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) { ImplHandleMouseHelpRequest( pChild, aMousePos ); if( pWinFrameData->mpMouseMoveWin.get() != pChild ) nMode |= MouseEventModifiers::ENTERWINDOW; } // Call the hook also, if Window is disabled Point aChildPos = pChild->ImplFrameToOutput( aMousePos ); MouseEvent aMEvt( aChildPos, pWinFrameData->mnClickCount, nMode, nCode, nCode ); NotifyEvent aNEvt( nSVEvent, pChild, &aMEvt ); if ( nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN ) return true; else { // Set normal MousePointer for disabled windows if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) ImplSetMousePointer( pChild ); return false; } } // End ExtTextInput-Mode, if the user click in the same TopLevel Window if (pSVData->mpWinData->mpExtTextInputWin && ((nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN) || (nSVEvent == MouseNotifyEvent::MOUSEBUTTONUP))) pSVData->mpWinData->mpExtTextInputWin->EndExtTextInput(); } // determine mouse event data if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) { // check if MouseMove belongs to same window and if the // status did not change if ( pChild ) { Point aChildMousePos = pChild->ImplFrameToOutput( aMousePos ); if ( !bMouseLeave && (pChild == pWinFrameData->mpMouseMoveWin) && (aChildMousePos.X() == pWinFrameData->mnLastMouseWinX) && (aChildMousePos.Y() == pWinFrameData->mnLastMouseWinY) && (nOldCode == pWinFrameData->mnMouseCode) ) { // set mouse pointer anew, as it could have changed // due to the mode switch ImplSetMousePointer( pChild ); return false; } pWinFrameData->mnLastMouseWinX = aChildMousePos.X(); pWinFrameData->mnLastMouseWinY = aChildMousePos.Y(); } // mouse click nClicks = pWinFrameData->mnClickCount; // call Start-Drag handler if required // Warning: should be called before Move, as otherwise during // fast mouse movements the applications move to the selection state vcl::Window* pMouseDownWin = pWinFrameData->mpMouseDownWin; if ( pMouseDownWin ) { // check for matching StartDrag mode. We only compare // the status of the mouse buttons, such that e. g. Mod1 can // change immediately to the copy mode const MouseSettings& rMSettings = pMouseDownWin->GetSettings().GetMouseSettings(); if ( (nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) == (MouseSettings::GetStartDragCode() & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) ) { if ( !pMouseDownWin->ImplGetFrameData()->mbStartDragCalled ) { tools::Long nDragW = rMSettings.GetStartDragWidth(); tools::Long nDragH = rMSettings.GetStartDragHeight(); //long nMouseX = nX; //long nMouseY = nY; tools::Long nMouseX = aMousePos.X(); // #106074# use the possibly re-mirrored coordinates (RTL) ! nX,nY are unmodified ! tools::Long nMouseY = aMousePos.Y(); if ( (((nMouseX-nDragW) > pMouseDownWin->ImplGetFrameData()->mnFirstMouseX) || ((nMouseX+nDragW) < pMouseDownWin->ImplGetFrameData()->mnFirstMouseX)) || (((nMouseY-nDragH) > pMouseDownWin->ImplGetFrameData()->mnFirstMouseY) || ((nMouseY+nDragH) < pMouseDownWin->ImplGetFrameData()->mnFirstMouseY)) ) { pMouseDownWin->ImplGetFrameData()->mbStartDragCalled = true; // Check if drag source provides its own recognizer if( pMouseDownWin->ImplGetFrameData()->mbInternalDragGestureRecognizer ) { // query DropTarget from child window css::uno::Reference< css::datatransfer::dnd::XDragGestureRecognizer > xDragGestureRecognizer( pMouseDownWin->ImplGetWindowImpl()->mxDNDListenerContainer, css::uno::UNO_QUERY ); if( xDragGestureRecognizer.is() ) { // retrieve mouse position relative to mouse down window Point relLoc = pMouseDownWin->ImplFrameToOutput( Point( pMouseDownWin->ImplGetFrameData()->mnFirstMouseX, pMouseDownWin->ImplGetFrameData()->mnFirstMouseY ) ); // create a UNO mouse event out of the available data css::awt::MouseEvent aMouseEvent( static_cast < css::uno::XInterface * > ( nullptr ), #ifdef MACOSX nCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3), #else nCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2), #endif nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE), nMouseX, nMouseY, nClicks, false ); SolarMutexReleaser aReleaser; // FIXME: where do I get Action from ? css::uno::Reference< css::datatransfer::dnd::XDragSource > xDragSource = pMouseDownWin->GetDragSource(); if( xDragSource.is() ) { static_cast < DNDListenerContainer * > ( xDragGestureRecognizer.get() )->fireDragGestureEvent( 0, relLoc.X(), relLoc.Y(), xDragSource, css::uno::makeAny( aMouseEvent ) ); } } } } } } else pMouseDownWin->ImplGetFrameData()->mbStartDragCalled = true; } // test for mouseleave and mouseenter VclPtr pMouseMoveWin = pWinFrameData->mpMouseMoveWin; if ( pChild != pMouseMoveWin ) { if ( pMouseMoveWin ) { Point aLeaveMousePos = pMouseMoveWin->ImplFrameToOutput( aMousePos ); MouseEvent aMLeaveEvt( aLeaveMousePos, nClicks, nMode | MouseEventModifiers::LEAVEWINDOW, nCode, nCode ); NotifyEvent aNLeaveEvt( MouseNotifyEvent::MOUSEMOVE, pMouseMoveWin, &aMLeaveEvt ); pWinFrameData->mbInMouseMove = true; pMouseMoveWin->ImplGetWinData()->mbMouseOver = false; // A MouseLeave can destroy this window if ( !ImplCallPreNotify( aNLeaveEvt ) ) { pMouseMoveWin->MouseMove( aMLeaveEvt ); if( !pMouseMoveWin->IsDisposed() ) aNLeaveEvt.GetWindow()->ImplNotifyKeyMouseCommandEventListeners( aNLeaveEvt ); } pWinFrameData->mpMouseMoveWin = nullptr; pWinFrameData->mbInMouseMove = false; if ( pChild && pChild->IsDisposed() ) pChild = nullptr; if ( pMouseMoveWin->IsDisposed() ) return true; } nMode |= MouseEventModifiers::ENTERWINDOW; } pWinFrameData->mpMouseMoveWin = pChild; if( pChild ) pChild->ImplGetWinData()->mbMouseOver = true; // MouseLeave if ( !pChild ) return false; } else { if (pChild) { // mouse click if ( nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN ) { const MouseSettings& rMSettings = pChild->GetSettings().GetMouseSettings(); sal_uInt64 nDblClkTime = rMSettings.GetDoubleClickTime(); tools::Long nDblClkW = rMSettings.GetDoubleClickWidth(); tools::Long nDblClkH = rMSettings.GetDoubleClickHeight(); //long nMouseX = nX; //long nMouseY = nY; tools::Long nMouseX = aMousePos.X(); // #106074# use the possibly re-mirrored coordinates (RTL) ! nX,nY are unmodified ! tools::Long nMouseY = aMousePos.Y(); if ( (pChild == pChild->ImplGetFrameData()->mpMouseDownWin) && (nCode == pChild->ImplGetFrameData()->mnFirstMouseCode) && ((nMsgTime-pChild->ImplGetFrameData()->mnMouseDownTime) < nDblClkTime) && ((nMouseX-nDblClkW) <= pChild->ImplGetFrameData()->mnFirstMouseX) && ((nMouseX+nDblClkW) >= pChild->ImplGetFrameData()->mnFirstMouseX) && ((nMouseY-nDblClkH) <= pChild->ImplGetFrameData()->mnFirstMouseY) && ((nMouseY+nDblClkH) >= pChild->ImplGetFrameData()->mnFirstMouseY) ) { pChild->ImplGetFrameData()->mnClickCount++; pChild->ImplGetFrameData()->mbStartDragCalled = true; } else { pChild->ImplGetFrameData()->mpMouseDownWin = pChild; pChild->ImplGetFrameData()->mnClickCount = 1; pChild->ImplGetFrameData()->mnFirstMouseX = nMouseX; pChild->ImplGetFrameData()->mnFirstMouseY = nMouseY; pChild->ImplGetFrameData()->mnFirstMouseCode = nCode; pChild->ImplGetFrameData()->mbStartDragCalled = (nCode & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)) != (MouseSettings::GetStartDragCode() & (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE)); } pChild->ImplGetFrameData()->mnMouseDownTime = nMsgTime; } nClicks = pChild->ImplGetFrameData()->mnClickCount; } pSVData->maAppData.mnLastInputTime = tools::Time::GetSystemTicks(); } SAL_WARN_IF( !pChild, "vcl", "ImplHandleMouseEvent: pChild == NULL" ); if (!pChild) return false; // create mouse event Point aChildPos = pChild->ImplFrameToOutput( aMousePos ); MouseEvent aMEvt( aChildPos, nClicks, nMode, nCode, nCode ); // tracking window gets the mouse events if (pSVData->mpWinData->mpTrackWin) pChild = pSVData->mpWinData->mpTrackWin; // handle FloatingMode if (!pSVData->mpWinData->mpTrackWin && pSVData->mpWinData->mpFirstFloat) { if ( ImplHandleMouseFloatMode( pChild, aMousePos, nCode, nSVEvent, bMouseLeave ) ) { if ( !pChild->IsDisposed() ) { pChild->ImplGetFrameData()->mbStartDragCalled = true; } return true; } } // call handler bool bCallHelpRequest = true; SAL_WARN_IF( !pChild, "vcl", "ImplHandleMouseEvent: pChild is NULL" ); if (!pChild) return false; NotifyEvent aNEvt( nSVEvent, pChild, &aMEvt ); if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) pChild->ImplGetFrameData()->mbInMouseMove = true; // bring window into foreground on mouseclick if ( nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN ) { if (!pSVData->mpWinData->mpFirstFloat && // totop for floating windows in popup would change the focus and would close them immediately !(pChild->ImplGetFrameWindow()->GetStyle() & WB_OWNERDRAWDECORATION)) // ownerdrawdecorated windows must never grab focus pChild->ToTop(); if ( pChild->IsDisposed() ) return true; } if ( ImplCallPreNotify( aNEvt ) || pChild->IsDisposed() ) bRet = true; else { bRet = false; if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) { if (pSVData->mpWinData->mpTrackWin) { TrackingEvent aTEvt( aMEvt ); pChild->Tracking( aTEvt ); if ( !pChild->IsDisposed() ) { // When ScrollRepeat, we restart the timer if (pSVData->mpWinData->mpTrackTimer && (pSVData->mpWinData->mnTrackFlags & StartTrackingFlags::ScrollRepeat)) pSVData->mpWinData->mpTrackTimer->Start(); } bCallHelpRequest = false; bRet = true; } else { if( pChild->IsDisposed() ) bCallHelpRequest = false; else { // if the MouseMove handler changes the help window's visibility // the HelpRequest handler should not be called anymore vcl::Window* pOldHelpTextWin = ImplGetSVHelpData().mpHelpWin; pChild->MouseMove( aMEvt ); if ( pOldHelpTextWin != ImplGetSVHelpData().mpHelpWin ) bCallHelpRequest = false; } } } else if ( nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN ) { if ( pSVData->mpWinData->mpTrackWin ) bRet = true; else { pChild->ImplGetWindowImpl()->mbMouseButtonDown = false; pChild->MouseButtonDown( aMEvt ); } } else { if (pSVData->mpWinData->mpTrackWin) { pChild->EndTracking(); bRet = true; } else { pChild->ImplGetWindowImpl()->mbMouseButtonUp = false; pChild->MouseButtonUp( aMEvt ); } } assert(aNEvt.GetWindow() == pChild); if (!pChild->IsDisposed()) pChild->ImplNotifyKeyMouseCommandEventListeners( aNEvt ); } if (pChild->IsDisposed()) return true; if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) pChild->ImplGetWindowImpl()->mpFrameData->mbInMouseMove = false; if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) { if ( bCallHelpRequest && !ImplGetSVHelpData().mbKeyboardHelp ) ImplHandleMouseHelpRequest( pChild, pChild->OutputToScreenPixel( aMEvt.GetPosPixel() ) ); bRet = true; } else if ( !bRet ) { if ( nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN ) { if ( !pChild->ImplGetWindowImpl()->mbMouseButtonDown ) bRet = true; } else { if ( !pChild->ImplGetWindowImpl()->mbMouseButtonUp ) bRet = true; } } if ( nSVEvent == MouseNotifyEvent::MOUSEMOVE ) { // set new mouse pointer if ( !bMouseLeave ) ImplSetMousePointer( pChild ); } else if ( (nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN) || (nSVEvent == MouseNotifyEvent::MOUSEBUTTONUP) ) { // Command-Events if ( /*!bRet &&*/ (nClicks == 1) && (nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN) && (nCode == MOUSE_MIDDLE) ) { MouseMiddleButtonAction nMiddleAction = pChild->GetSettings().GetMouseSettings().GetMiddleButtonAction(); if ( nMiddleAction == MouseMiddleButtonAction::AutoScroll ) bRet = !ImplCallCommand( pChild, CommandEventId::StartAutoScroll, nullptr, true, &aChildPos ); else if ( nMiddleAction == MouseMiddleButtonAction::PasteSelection ) bRet = !ImplCallCommand( pChild, CommandEventId::PasteSelection, nullptr, true, &aChildPos ); } else { // ContextMenu if ( (nCode == MouseSettings::GetContextMenuCode()) && (nClicks == MouseSettings::GetContextMenuClicks()) ) { bool bContextMenu = (nSVEvent == MouseNotifyEvent::MOUSEBUTTONDOWN); if ( bContextMenu ) { if( pSVData->maAppData.mpActivePopupMenu ) { /* #i34277# there already is a context menu open * that was probably just closed with EndPopupMode. * We need to give the eventual corresponding * PopupMenu::Execute a chance to end properly. * Therefore delay context menu command and * issue only after popping one frame of the * Yield stack. */ ContextMenuEvent* pEv = new ContextMenuEvent; pEv->pWindow = pChild; pEv->aChildPos = aChildPos; Application::PostUserEvent( Link( pEv, ContextMenuEventLink ) ); } else bRet = ! ImplCallCommand( pChild, CommandEventId::ContextMenu, nullptr, true, &aChildPos ); } } } } return bRet; } static vcl::Window* ImplGetKeyInputWindow( vcl::Window* pWindow ) { ImplSVData* pSVData = ImplGetSVData(); // determine last input time pSVData->maAppData.mnLastInputTime = tools::Time::GetSystemTicks(); // #127104# workaround for destroyed windows if( pWindow->ImplGetWindowImpl() == nullptr ) return nullptr; // find window - is every time the window which has currently the // focus or the last time the focus. // the first floating window always has the focus, try it, or any parent floating windows, first vcl::Window* pChild = pSVData->mpWinData->mpFirstFloat; while (pChild) { if (pChild->ImplGetWindowImpl()->mbFloatWin) { if (static_cast(pChild)->GrabsFocus()) break; } else if (pChild->ImplGetWindowImpl()->mbDockWin) { vcl::Window* pParent = pChild->GetWindow(GetWindowType::RealParent); if (pParent && pParent->ImplGetWindowImpl()->mbFloatWin && static_cast(pParent)->GrabsFocus()) break; } pChild = pChild->GetParent(); } if (!pChild) pChild = pWindow; pChild = pChild->ImplGetWindowImpl()->mpFrameData->mpFocusWin; // no child - then no input if ( !pChild ) return nullptr; // We call also KeyInput if we haven't the focus, because on Unix // system this is often the case when a Lookup Choice Window has // the focus - because this windows send the KeyInput directly to // the window without resetting the focus // no keyinput to disabled windows if ( !pChild->IsEnabled() || !pChild->IsInputEnabled() || pChild->IsInModalMode() ) return nullptr; return pChild; } static bool ImplHandleKey( vcl::Window* pWindow, MouseNotifyEvent nSVEvent, sal_uInt16 nKeyCode, sal_uInt16 nCharCode, sal_uInt16 nRepeat, bool bForward ) { ImplSVData* pSVData = ImplGetSVData(); vcl::KeyCode aKeyCode( nKeyCode, nKeyCode ); sal_uInt16 nEvCode = aKeyCode.GetCode(); // allow application key listeners to remove the key event // but make sure we're not forwarding external KeyEvents, (ie where bForward is false) // because those are coming back from the listener itself and MUST be processed if( bForward ) { VclEventId nVCLEvent; switch( nSVEvent ) { case MouseNotifyEvent::KEYINPUT: nVCLEvent = VclEventId::WindowKeyInput; break; case MouseNotifyEvent::KEYUP: nVCLEvent = VclEventId::WindowKeyUp; break; default: nVCLEvent = VclEventId::NONE; break; } KeyEvent aKeyEvent(static_cast(nCharCode), aKeyCode, nRepeat); if (nVCLEvent != VclEventId::NONE && Application::HandleKey(nVCLEvent, pWindow, &aKeyEvent)) return true; } bool bCtrlF6 = (aKeyCode.GetCode() == KEY_F6) && aKeyCode.IsMod1(); // determine last input time pSVData->maAppData.mnLastInputTime = tools::Time::GetSystemTicks(); // handle tracking window if ( nSVEvent == MouseNotifyEvent::KEYINPUT ) { if ( ImplGetSVHelpData().mbExtHelpMode ) { Help::EndExtHelp(); if ( nEvCode == KEY_ESCAPE ) return true; } if ( ImplGetSVHelpData().mpHelpWin ) ImplDestroyHelpWindow( false ); // AutoScrollMode if (pSVData->mpWinData->mpAutoScrollWin) { pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll(); if ( nEvCode == KEY_ESCAPE ) return true; } if (pSVData->mpWinData->mpTrackWin) { sal_uInt16 nOrigCode = aKeyCode.GetCode(); if ( nOrigCode == KEY_ESCAPE ) { pSVData->mpWinData->mpTrackWin->EndTracking( TrackingEventFlags::Cancel | TrackingEventFlags::Key ); if (pSVData->mpWinData->mpFirstFloat) { FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat(); if ( !(pLastLevelFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoKeyClose) ) { sal_uInt16 nEscCode = aKeyCode.GetCode(); if ( nEscCode == KEY_ESCAPE ) pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); } } return true; } else if ( nOrigCode == KEY_RETURN ) { pSVData->mpWinData->mpTrackWin->EndTracking( TrackingEventFlags::Key ); return true; } else return true; } // handle FloatingMode if (pSVData->mpWinData->mpFirstFloat) { FloatingWindow* pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat(); if ( !(pLastLevelFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoKeyClose) ) { sal_uInt16 nCode = aKeyCode.GetCode(); if ( (nCode == KEY_ESCAPE) || bCtrlF6) { pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); if( !bCtrlF6 ) return true; } } } // test for accel if ( pSVData->maAppData.mpAccelMgr ) { if ( pSVData->maAppData.mpAccelMgr->IsAccelKey( aKeyCode ) ) return true; } } // find window VclPtr pChild = ImplGetKeyInputWindow( pWindow ); if ( !pChild ) return false; // #i1820# use locale specific decimal separator if (nEvCode == KEY_DECIMAL) { // tdf#138932: don't modify the meaning of the key for password box bool bPass = false; if (auto pEdit = dynamic_cast(pChild.get())) bPass = pEdit->IsPassword(); if (!bPass && Application::GetSettings().GetMiscSettings().GetEnableLocalizedDecimalSep()) { OUString aSep(pWindow->GetSettings().GetLocaleDataWrapper().getNumDecimalSep()); nCharCode = static_cast(aSep[0]); } } // RTL: mirror cursor keys const OutputDevice *pChildOutDev = pChild->GetOutDev(); if( (aKeyCode.GetCode() == KEY_LEFT || aKeyCode.GetCode() == KEY_RIGHT) && pChildOutDev->HasMirroredGraphics() && pChild->IsRTLEnabled() ) aKeyCode = vcl::KeyCode( aKeyCode.GetCode() == KEY_LEFT ? KEY_RIGHT : KEY_LEFT, aKeyCode.GetModifier() ); KeyEvent aKeyEvt( static_cast(nCharCode), aKeyCode, nRepeat ); NotifyEvent aNotifyEvt( nSVEvent, pChild, &aKeyEvt ); bool bKeyPreNotify = ImplCallPreNotify( aNotifyEvt ); bool bRet = true; if ( !bKeyPreNotify && !pChild->IsDisposed() ) { if ( nSVEvent == MouseNotifyEvent::KEYINPUT ) { UITestLogger::getInstance().logKeyInput(pChild, aKeyEvt); pChild->ImplGetWindowImpl()->mbKeyInput = false; pChild->KeyInput( aKeyEvt ); } else { pChild->ImplGetWindowImpl()->mbKeyUp = false; pChild->KeyUp( aKeyEvt ); } if( !pChild->IsDisposed() ) aNotifyEvt.GetWindow()->ImplNotifyKeyMouseCommandEventListeners( aNotifyEvt ); } if ( pChild->IsDisposed() ) return true; if ( nSVEvent == MouseNotifyEvent::KEYINPUT ) { if ( !bKeyPreNotify && pChild->ImplGetWindowImpl()->mbKeyInput ) { sal_uInt16 nCode = aKeyCode.GetCode(); // #101999# is focus in or below toolbox bool bToolboxFocus=false; if( (nCode == KEY_F1) && aKeyCode.IsShift() ) { vcl::Window *pWin = pWindow->ImplGetWindowImpl()->mpFrameData->mpFocusWin; while( pWin ) { if( pWin->ImplGetWindowImpl()->mbToolBox ) { bToolboxFocus = true; break; } else pWin = pWin->GetParent(); } } // ContextMenu if ( (nCode == KEY_CONTEXTMENU) || ((nCode == KEY_F10) && aKeyCode.IsShift() && !aKeyCode.IsMod1() && !aKeyCode.IsMod2() ) ) bRet = !ImplCallCommand( pChild, CommandEventId::ContextMenu ); else if ( ( (nCode == KEY_F2) && aKeyCode.IsShift() ) || ( (nCode == KEY_F1) && aKeyCode.IsMod1() ) || // #101999# no active help when focus in toolbox, simulate BalloonHelp instead ( (nCode == KEY_F1) && aKeyCode.IsShift() && bToolboxFocus ) ) { // TipHelp via Keyboard (Shift-F2 or Ctrl-F1) // simulate mouseposition at center of window Size aSize = pChild->GetOutputSize(); Point aPos( aSize.getWidth()/2, aSize.getHeight()/2 ); aPos = pChild->OutputToScreenPixel( aPos ); HelpEvent aHelpEvent( aPos, HelpEventMode::BALLOON ); aHelpEvent.SetKeyboardActivated( true ); ImplGetSVHelpData().mbSetKeyboardHelp = true; pChild->RequestHelp( aHelpEvent ); ImplGetSVHelpData().mbSetKeyboardHelp = false; } else if ( (nCode == KEY_F1) || (nCode == KEY_HELP) ) { if ( !aKeyCode.GetModifier() ) { if ( ImplGetSVHelpData().mbContextHelp ) { Point aMousePos = pChild->OutputToScreenPixel( pChild->GetPointerPosPixel() ); HelpEvent aHelpEvent( aMousePos, HelpEventMode::CONTEXT ); pChild->RequestHelp( aHelpEvent ); } else bRet = false; } else if ( aKeyCode.IsShift() ) { if ( ImplGetSVHelpData().mbExtHelp ) Help::StartExtHelp(); else bRet = false; } } else bRet = false; } } else { if ( !bKeyPreNotify && pChild->ImplGetWindowImpl()->mbKeyUp ) bRet = false; } // #105591# send keyinput to parent if we are a floating window and the key was not processed yet if( !bRet && pWindow->ImplGetWindowImpl() && pWindow->ImplGetWindowImpl()->mbFloatWin && pWindow->GetParent() && (pWindow->ImplGetWindowImpl()->mpFrame != pWindow->GetParent()->ImplGetWindowImpl()->mpFrame) ) { pChild = pWindow->GetParent(); // call handler NotifyEvent aNEvt( nSVEvent, pChild, &aKeyEvt ); bool bPreNotify = ImplCallPreNotify( aNEvt ); if ( pChild->IsDisposed() ) return true; if ( !bPreNotify ) { if ( nSVEvent == MouseNotifyEvent::KEYINPUT ) { pChild->ImplGetWindowImpl()->mbKeyInput = false; pChild->KeyInput( aKeyEvt ); } else { pChild->ImplGetWindowImpl()->mbKeyUp = false; pChild->KeyUp( aKeyEvt ); } if( !pChild->IsDisposed() ) aNEvt.GetWindow()->ImplNotifyKeyMouseCommandEventListeners( aNEvt ); if ( pChild->IsDisposed() ) return true; } if( bPreNotify || !pChild->ImplGetWindowImpl()->mbKeyInput ) bRet = true; } return bRet; } static bool ImplHandleExtTextInput( vcl::Window* pWindow, const OUString& rText, const ExtTextInputAttr* pTextAttr, sal_Int32 nCursorPos, sal_uInt16 nCursorFlags ) { ImplSVData* pSVData = ImplGetSVData(); vcl::Window* pChild = nullptr; int nTries = 200; while( nTries-- ) { pChild = pSVData->mpWinData->mpExtTextInputWin; if ( !pChild ) { pChild = ImplGetKeyInputWindow( pWindow ); if ( !pChild ) return false; } if( !pChild->ImplGetWindowImpl()->mpFrameData->mnFocusId ) break; if (comphelper::LibreOfficeKit::isActive()) { SAL_WARN("vcl", "Failed to get ext text input context"); break; } Application::Yield(); } // If it is the first ExtTextInput call, we inform the information // and allocate the data, which we must store in this mode ImplWinData* pWinData = pChild->ImplGetWinData(); if ( !pChild->ImplGetWindowImpl()->mbExtTextInput ) { pChild->ImplGetWindowImpl()->mbExtTextInput = true; pWinData->mpExtOldText = OUString(); pWinData->mpExtOldAttrAry.reset(); pSVData->mpWinData->mpExtTextInputWin = pChild; ImplCallCommand( pChild, CommandEventId::StartExtTextInput ); } // be aware of being recursively called in StartExtTextInput if ( !pChild->ImplGetWindowImpl()->mbExtTextInput ) return false; // Test for changes bool bOnlyCursor = false; sal_Int32 nMinLen = std::min( pWinData->mpExtOldText->getLength(), rText.getLength() ); sal_Int32 nDeltaStart = 0; while ( nDeltaStart < nMinLen ) { if ( (*pWinData->mpExtOldText)[nDeltaStart] != rText[nDeltaStart] ) break; nDeltaStart++; } if ( pWinData->mpExtOldAttrAry || pTextAttr ) { if ( !pWinData->mpExtOldAttrAry || !pTextAttr ) nDeltaStart = 0; else { sal_Int32 i = 0; while ( i < nDeltaStart ) { if ( pWinData->mpExtOldAttrAry[i] != pTextAttr[i] ) { nDeltaStart = i; break; } i++; } } } if ( (nDeltaStart >= nMinLen) && (pWinData->mpExtOldText->getLength() == rText.getLength()) ) bOnlyCursor = true; // Call Event and store the information CommandExtTextInputData aData( rText, pTextAttr, nCursorPos, nCursorFlags, bOnlyCursor ); *pWinData->mpExtOldText = rText; pWinData->mpExtOldAttrAry.reset(); if ( pTextAttr ) { pWinData->mpExtOldAttrAry.reset( new ExtTextInputAttr[rText.getLength()] ); memcpy( pWinData->mpExtOldAttrAry.get(), pTextAttr, rText.getLength()*sizeof( ExtTextInputAttr ) ); } return !ImplCallCommand( pChild, CommandEventId::ExtTextInput, &aData ); } static bool ImplHandleEndExtTextInput() { ImplSVData* pSVData = ImplGetSVData(); vcl::Window* pChild = pSVData->mpWinData->mpExtTextInputWin; bool bRet = false; if ( pChild ) { pChild->ImplGetWindowImpl()->mbExtTextInput = false; pSVData->mpWinData->mpExtTextInputWin = nullptr; ImplWinData* pWinData = pChild->ImplGetWinData(); pWinData->mpExtOldText.reset(); pWinData->mpExtOldAttrAry.reset(); bRet = !ImplCallCommand( pChild, CommandEventId::EndExtTextInput ); } return bRet; } static void ImplHandleExtTextInputPos( vcl::Window* pWindow, tools::Rectangle& rRect, tools::Long& rInputWidth, bool * pVertical ) { ImplSVData* pSVData = ImplGetSVData(); vcl::Window* pChild = pSVData->mpWinData->mpExtTextInputWin; if ( !pChild ) pChild = ImplGetKeyInputWindow( pWindow ); else { // Test, if the Window is related to the frame if ( !pWindow->ImplIsWindowOrChild( pChild ) ) pChild = ImplGetKeyInputWindow( pWindow ); } if ( pChild ) { const OutputDevice *pChildOutDev = pChild->GetOutDev(); ImplCallCommand( pChild, CommandEventId::CursorPos ); const tools::Rectangle* pRect = pChild->GetCursorRect(); if ( pRect ) rRect = pChildOutDev->ImplLogicToDevicePixel( *pRect ); else { vcl::Cursor* pCursor = pChild->GetCursor(); if ( pCursor ) { Point aPos = pChildOutDev->ImplLogicToDevicePixel( pCursor->GetPos() ); Size aSize = pChild->LogicToPixel( pCursor->GetSize() ); if ( !aSize.Width() ) aSize.setWidth( pChild->GetSettings().GetStyleSettings().GetCursorSize() ); rRect = tools::Rectangle( aPos, aSize ); } else rRect = tools::Rectangle( Point( pChild->GetOutOffXPixel(), pChild->GetOutOffYPixel() ), Size() ); } rInputWidth = pChild->ImplLogicWidthToDevicePixel( pChild->GetCursorExtTextInputWidth() ); if ( !rInputWidth ) rInputWidth = rRect.GetWidth(); } if (pVertical != nullptr) *pVertical = pChild != nullptr && pChild->GetInputContext().GetFont().IsVertical(); } static bool ImplHandleInputContextChange( vcl::Window* pWindow ) { vcl::Window* pChild = ImplGetKeyInputWindow( pWindow ); CommandInputContextData aData; return !ImplCallCommand( pChild, CommandEventId::InputContextChange, &aData ); } static bool ImplCallWheelCommand( const VclPtr& pWindow, const Point& rPos, const CommandWheelData* pWheelData ) { Point aCmdMousePos = pWindow->ImplFrameToOutput( rPos ); CommandEvent aCEvt( aCmdMousePos, CommandEventId::Wheel, true, pWheelData ); NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pWindow, &aCEvt ); bool bPreNotify = ImplCallPreNotify( aNCmdEvt ); if ( pWindow->IsDisposed() ) return false; if ( !bPreNotify ) { pWindow->ImplGetWindowImpl()->mbCommand = false; pWindow->Command( aCEvt ); if ( pWindow->IsDisposed() ) return false; if ( pWindow->ImplGetWindowImpl()->mbCommand ) return true; } return false; } static bool acceptableWheelScrollTarget(const vcl::Window *pMouseWindow) { return (pMouseWindow && !pMouseWindow->isDisposed() && pMouseWindow->IsInputEnabled() && !pMouseWindow->IsInModalMode()); } //If the last event at the same absolute screen position was handled by a //different window then reuse that window if the event occurs within 1/2 a //second, i.e. so scrolling down something like the calc sidebar that contains //widgets that respond to wheel events will continue to send the event to the //scrolling widget in favour of the widget that happens to end up under the //mouse. static bool shouldReusePreviousMouseWindow(const SalWheelMouseEvent& rPrevEvt, const SalWheelMouseEvent& rEvt) { return (rEvt.mnX == rPrevEvt.mnX && rEvt.mnY == rPrevEvt.mnY && rEvt.mnTime-rPrevEvt.mnTime < 500/*ms*/); } namespace { class HandleGestureEventBase { protected: ImplSVData* m_pSVData; VclPtr m_pWindow; Point m_aMousePos; public: HandleGestureEventBase(vcl::Window *pWindow, const Point &rMousePos) : m_pSVData(ImplGetSVData()) , m_pWindow(pWindow) , m_aMousePos(rMousePos) { } bool Setup(); vcl::Window* FindTarget(); vcl::Window* Dispatch(vcl::Window* pTarget); virtual bool CallCommand(vcl::Window *pWindow, const Point &rMousePos) = 0; virtual ~HandleGestureEventBase() {} }; } bool HandleGestureEventBase::Setup() { if (m_pSVData->mpWinData->mpAutoScrollWin) m_pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll(); if (ImplGetSVHelpData().mpHelpWin) ImplDestroyHelpWindow( true ); return !m_pWindow->IsDisposed(); } vcl::Window* HandleGestureEventBase::FindTarget() { // first check any floating window ( eg. drop down listboxes) vcl::Window *pMouseWindow = nullptr; if (m_pSVData->mpWinData->mpFirstFloat && !m_pSVData->mpWinData->mpCaptureWin && !m_pSVData->mpWinData->mpFirstFloat->ImplIsFloatPopupModeWindow( m_pWindow ) ) { bool bHitTestInsideRect = false; pMouseWindow = m_pSVData->mpWinData->mpFirstFloat->ImplFloatHitTest( m_pWindow, m_aMousePos, bHitTestInsideRect ); if (!pMouseWindow) pMouseWindow = m_pSVData->mpWinData->mpFirstFloat; } // then try the window directly beneath the mouse if( !pMouseWindow ) { pMouseWindow = m_pWindow->ImplFindWindow( m_aMousePos ); } else { // transform coordinates to float window frame coordinates pMouseWindow = pMouseWindow->ImplFindWindow( pMouseWindow->OutputToScreenPixel( pMouseWindow->AbsoluteScreenToOutputPixel( m_pWindow->OutputToAbsoluteScreenPixel( m_pWindow->ScreenToOutputPixel( m_aMousePos ) ) ) ) ); } while (acceptableWheelScrollTarget(pMouseWindow)) { if (pMouseWindow->IsEnabled()) break; //try the parent if this one is disabled pMouseWindow = pMouseWindow->GetParent(); } return pMouseWindow; } vcl::Window *HandleGestureEventBase::Dispatch(vcl::Window* pMouseWindow) { vcl::Window *pDispatchedTo = nullptr; if (acceptableWheelScrollTarget(pMouseWindow) && pMouseWindow->IsEnabled()) { // transform coordinates to float window frame coordinates Point aRelMousePos( pMouseWindow->OutputToScreenPixel( pMouseWindow->AbsoluteScreenToOutputPixel( m_pWindow->OutputToAbsoluteScreenPixel( m_pWindow->ScreenToOutputPixel( m_aMousePos ) ) ) ) ); bool bPropogate = CallCommand(pMouseWindow, aRelMousePos); if (!bPropogate) pDispatchedTo = pMouseWindow; } // if the command was not handled try the focus window if (!pDispatchedTo) { vcl::Window* pFocusWindow = m_pWindow->ImplGetWindowImpl()->mpFrameData->mpFocusWin; if ( pFocusWindow && (pFocusWindow != pMouseWindow) && (pFocusWindow == m_pSVData->mpWinData->mpFocusWin) ) { // no wheel-messages to disabled windows if ( pFocusWindow->IsEnabled() && pFocusWindow->IsInputEnabled() && ! pFocusWindow->IsInModalMode() ) { // transform coordinates to focus window frame coordinates Point aRelMousePos( pFocusWindow->OutputToScreenPixel( pFocusWindow->AbsoluteScreenToOutputPixel( m_pWindow->OutputToAbsoluteScreenPixel( m_pWindow->ScreenToOutputPixel( m_aMousePos ) ) ) ) ); bool bPropogate = CallCommand(pFocusWindow, aRelMousePos); if (!bPropogate) pDispatchedTo = pMouseWindow; } } } return pDispatchedTo; } namespace { class HandleWheelEvent : public HandleGestureEventBase { private: CommandWheelData m_aWheelData; public: HandleWheelEvent(vcl::Window *pWindow, const SalWheelMouseEvent& rEvt) : HandleGestureEventBase(pWindow, Point(rEvt.mnX, rEvt.mnY)) { CommandWheelMode nMode; sal_uInt16 nCode = rEvt.mnCode; bool bHorz = rEvt.mbHorz; bool bPixel = rEvt.mbDeltaIsPixel; if ( nCode & KEY_MOD1 ) nMode = CommandWheelMode::ZOOM; else if ( nCode & KEY_MOD2 ) nMode = CommandWheelMode::DATAZOOM; else { nMode = CommandWheelMode::SCROLL; // #i85450# interpret shift-wheel as horizontal wheel action if( (nCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3)) == KEY_SHIFT ) bHorz = true; } m_aWheelData = CommandWheelData(rEvt.mnDelta, rEvt.mnNotchDelta, rEvt.mnScrollLines, nMode, nCode, bHorz, bPixel); } virtual bool CallCommand(vcl::Window *pWindow, const Point &rMousePos) override { return ImplCallWheelCommand(pWindow, rMousePos, &m_aWheelData); } bool HandleEvent(const SalWheelMouseEvent& rEvt); }; } bool HandleWheelEvent::HandleEvent(const SalWheelMouseEvent& rEvt) { if (!Setup()) return false; VclPtr xMouseWindow = FindTarget(); ImplSVData* pSVData = ImplGetSVData(); // avoid the problem that scrolling via wheel to this point brings a widget // under the mouse that also accepts wheel commands, so stick with the old // widget if the time gap is very small if (shouldReusePreviousMouseWindow(pSVData->mpWinData->maLastWheelEvent, rEvt) && acceptableWheelScrollTarget(pSVData->mpWinData->mpLastWheelWindow)) { xMouseWindow = pSVData->mpWinData->mpLastWheelWindow; } pSVData->mpWinData->maLastWheelEvent = rEvt; pSVData->mpWinData->mpLastWheelWindow = Dispatch(xMouseWindow); return pSVData->mpWinData->mpLastWheelWindow; } namespace { class HandleGestureEvent : public HandleGestureEventBase { public: HandleGestureEvent(vcl::Window *pWindow, const Point &rMousePos) : HandleGestureEventBase(pWindow, rMousePos) { } bool HandleEvent(); }; } bool HandleGestureEvent::HandleEvent() { if (!Setup()) return false; vcl::Window *pTarget = FindTarget(); bool bHandled = Dispatch(pTarget) != nullptr; return bHandled; } static bool ImplHandleWheelEvent(vcl::Window* pWindow, const SalWheelMouseEvent& rEvt) { HandleWheelEvent aHandler(pWindow, rEvt); return aHandler.HandleEvent(rEvt); } namespace { class HandleSwipeEvent : public HandleGestureEvent { private: CommandSwipeData m_aSwipeData; public: HandleSwipeEvent(vcl::Window *pWindow, const SalSwipeEvent& rEvt) : HandleGestureEvent(pWindow, Point(rEvt.mnX, rEvt.mnY)), m_aSwipeData(rEvt.mnVelocityX) { } virtual bool CallCommand(vcl::Window *pWindow, const Point &/*rMousePos*/) override { return ImplCallCommand(pWindow, CommandEventId::Swipe, &m_aSwipeData); } }; } static bool ImplHandleSwipe(vcl::Window *pWindow, const SalSwipeEvent& rEvt) { HandleSwipeEvent aHandler(pWindow, rEvt); return aHandler.HandleEvent(); } namespace { class HandleLongPressEvent : public HandleGestureEvent { private: CommandLongPressData m_aLongPressData; public: HandleLongPressEvent(vcl::Window *pWindow, const SalLongPressEvent& rEvt) : HandleGestureEvent(pWindow, Point(rEvt.mnX, rEvt.mnY)), m_aLongPressData(rEvt.mnX, rEvt.mnY) { } virtual bool CallCommand(vcl::Window *pWindow, const Point &/*rMousePos*/) override { return ImplCallCommand(pWindow, CommandEventId::LongPress, &m_aLongPressData); } }; } static bool ImplHandleLongPress(vcl::Window *pWindow, const SalLongPressEvent& rEvt) { HandleLongPressEvent aHandler(pWindow, rEvt); return aHandler.HandleEvent(); } namespace { class HandleGeneralGestureEvent : public HandleGestureEvent { private: CommandGestureData m_aGestureData; public: HandleGeneralGestureEvent(vcl::Window* pWindow, const SalGestureEvent& rEvent) : HandleGestureEvent(pWindow, Point(rEvent.mnX, rEvent.mnY)) , m_aGestureData(rEvent.mnX, rEvent.mnY, rEvent.meEventType, rEvent.mfOffset, rEvent.meOrientation) { } virtual bool CallCommand(vcl::Window* pWindow, const Point& /*rMousePos*/) override { return ImplCallCommand(pWindow, CommandEventId::Gesture, &m_aGestureData); } }; } static bool ImplHandleGestureEvent(vcl::Window* pWindow, const SalGestureEvent& rEvent) { HandleGeneralGestureEvent aHandler(pWindow, rEvent); return aHandler.HandleEvent(); } static void ImplHandlePaint( vcl::Window* pWindow, const tools::Rectangle& rBoundRect, bool bImmediateUpdate ) { // system paint events must be checked for re-mirroring pWindow->ImplGetWindowImpl()->mnPaintFlags |= ImplPaintFlags::CheckRtl; // trigger paint for all windows that live in the new paint region vcl::Region aRegion( rBoundRect ); pWindow->ImplInvalidateOverlapFrameRegion( aRegion ); if( bImmediateUpdate ) { // #i87663# trigger possible pending resize notifications // (GetSizePixel does that for us) pWindow->GetSizePixel(); // force drawing immediately pWindow->PaintImmediately(); } } static void KillOwnPopups( vcl::Window const * pWindow ) { ImplSVData* pSVData = ImplGetSVData(); vcl::Window *pParent = pWindow->ImplGetWindowImpl()->mpFrameWindow; vcl::Window *pChild = pSVData->mpWinData->mpFirstFloat; if ( pChild && pParent->ImplIsWindowOrChild( pChild, true ) ) { if (!(pSVData->mpWinData->mpFirstFloat->GetPopupModeFlags() & FloatWinPopupFlags::NoAppFocusClose)) pSVData->mpWinData->mpFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll); } } void ImplHandleResize( vcl::Window* pWindow, tools::Long nNewWidth, tools::Long nNewHeight ) { const bool bChanged = (nNewWidth != pWindow->GetOutputWidthPixel()) || (nNewHeight != pWindow->GetOutputHeightPixel()); if (bChanged && pWindow->GetStyle() & (WB_MOVEABLE|WB_SIZEABLE)) { KillOwnPopups( pWindow ); if( pWindow->ImplGetWindow() != ImplGetSVHelpData().mpHelpWin ) ImplDestroyHelpWindow( true ); } if ( (nNewWidth > 0 && nNewHeight > 0) || pWindow->ImplGetWindow()->ImplGetWindowImpl()->mbAllResize ) { if (bChanged) { pWindow->mnOutWidth = nNewWidth; pWindow->mnOutHeight = nNewHeight; pWindow->ImplGetWindowImpl()->mbWaitSystemResize = false; if ( pWindow->IsReallyVisible() ) pWindow->ImplSetClipFlag(); if ( pWindow->IsVisible() || pWindow->ImplGetWindow()->ImplGetWindowImpl()->mbAllResize || ( pWindow->ImplGetWindowImpl()->mbFrame && pWindow->ImplGetWindowImpl()->mpClientWindow ) ) // propagate resize for system border windows { bool bStartTimer = true; // use resize buffering for user resizes // ownerdraw decorated windows and floating windows can be resized immediately (i.e. synchronously) if( pWindow->ImplGetWindowImpl()->mbFrame && (pWindow->GetStyle() & WB_SIZEABLE) && !(pWindow->GetStyle() & WB_OWNERDRAWDECORATION) // synchronous resize for ownerdraw decorated windows (toolbars) && !pWindow->ImplGetWindowImpl()->mbFloatWin ) // synchronous resize for floating windows, #i43799# { if( pWindow->ImplGetWindowImpl()->mpClientWindow ) { // #i42750# presentation wants to be informed about resize // as early as possible WorkWindow* pWorkWindow = dynamic_cast(pWindow->ImplGetWindowImpl()->mpClientWindow.get()); if( ! pWorkWindow || pWorkWindow->IsPresentationMode() ) bStartTimer = false; } else { WorkWindow* pWorkWindow = dynamic_cast(pWindow); if( ! pWorkWindow || pWorkWindow->IsPresentationMode() ) bStartTimer = false; } } else bStartTimer = false; if( bStartTimer ) pWindow->ImplGetWindowImpl()->mpFrameData->maResizeIdle.Start(); else pWindow->ImplCallResize(); // otherwise menus cannot be positioned } else pWindow->ImplGetWindowImpl()->mbCallResize = true; if (pWindow->SupportsDoubleBuffering() && pWindow->ImplGetWindowImpl()->mbFrame) { // Propagate resize for the frame's buffer. pWindow->ImplGetWindowImpl()->mpFrameData->mpBuffer->SetOutputSizePixel(pWindow->GetOutputSizePixel()); } } } pWindow->ImplGetWindowImpl()->mpFrameData->mbNeedSysWindow = (nNewWidth < IMPL_MIN_NEEDSYSWIN) || (nNewHeight < IMPL_MIN_NEEDSYSWIN); bool bMinimized = (nNewWidth <= 0) || (nNewHeight <= 0); if( bMinimized != pWindow->ImplGetWindowImpl()->mpFrameData->mbMinimized ) pWindow->ImplGetWindowImpl()->mpFrameWindow->ImplNotifyIconifiedState( bMinimized ); pWindow->ImplGetWindowImpl()->mpFrameData->mbMinimized = bMinimized; } static void ImplHandleMove( vcl::Window* pWindow ) { if( pWindow->ImplGetWindowImpl()->mbFrame && pWindow->ImplIsFloatingWindow() && pWindow->IsReallyVisible() ) { static_cast(pWindow)->EndPopupMode( FloatWinPopupEndFlags::TearOff ); pWindow->ImplCallMove(); } if( pWindow->GetStyle() & (WB_MOVEABLE|WB_SIZEABLE) ) { KillOwnPopups( pWindow ); if( pWindow->ImplGetWindow() != ImplGetSVHelpData().mpHelpWin ) ImplDestroyHelpWindow( true ); } if ( pWindow->IsVisible() ) pWindow->ImplCallMove(); else pWindow->ImplGetWindowImpl()->mbCallMove = true; // make sure the framepos will be updated on the next Show() if ( pWindow->ImplGetWindowImpl()->mbFrame && pWindow->ImplGetWindowImpl()->mpClientWindow ) pWindow->ImplGetWindowImpl()->mpClientWindow->ImplCallMove(); // notify client to update geometry } static void ImplHandleMoveResize( vcl::Window* pWindow, tools::Long nNewWidth, tools::Long nNewHeight ) { ImplHandleMove( pWindow ); ImplHandleResize( pWindow, nNewWidth, nNewHeight ); } static void ImplActivateFloatingWindows( vcl::Window const * pWindow, bool bActive ) { // First check all overlapping windows vcl::Window* pTempWindow = pWindow->ImplGetWindowImpl()->mpFirstOverlap; while ( pTempWindow ) { if ( pTempWindow->GetActivateMode() == ActivateModeFlags::NONE ) { if ( (pTempWindow->GetType() == WindowType::BORDERWINDOW) && (pTempWindow->ImplGetWindow()->GetType() == WindowType::FLOATINGWINDOW) ) static_cast(pTempWindow)->SetDisplayActive( bActive ); } ImplActivateFloatingWindows( pTempWindow, bActive ); pTempWindow = pTempWindow->ImplGetWindowImpl()->mpNext; } } IMPL_LINK_NOARG(vcl::Window, ImplAsyncFocusHdl, void*, void) { ImplGetWindowImpl()->mpFrameData->mnFocusId = nullptr; // If the status has been preserved, because we got back the focus // in the meantime, we do nothing bool bHasFocus = ImplGetWindowImpl()->mpFrameData->mbHasFocus || ImplGetWindowImpl()->mpFrameData->mbSysObjFocus; // next execute the delayed functions if ( bHasFocus ) { // redraw all floating windows inactive if ( ImplGetWindowImpl()->mpFrameData->mbStartFocusState != bHasFocus ) ImplActivateFloatingWindows( this, bHasFocus ); if ( ImplGetWindowImpl()->mpFrameData->mpFocusWin ) { bool bHandled = false; if ( ImplGetWindowImpl()->mpFrameData->mpFocusWin->IsInputEnabled() && ! ImplGetWindowImpl()->mpFrameData->mpFocusWin->IsInModalMode() ) { if ( ImplGetWindowImpl()->mpFrameData->mpFocusWin->IsEnabled() ) { ImplGetWindowImpl()->mpFrameData->mpFocusWin->GrabFocus(); bHandled = true; } else if( ImplGetWindowImpl()->mpFrameData->mpFocusWin->ImplHasDlgCtrl() ) { // #109094# if the focus is restored to a disabled dialog control (was disabled meanwhile) // try to move it to the next control ImplGetWindowImpl()->mpFrameData->mpFocusWin->ImplDlgCtrlNextWindow(); bHandled = true; } } if ( !bHandled ) { ImplSVData* pSVData = ImplGetSVData(); vcl::Window* pTopLevelWindow = ImplGetWindowImpl()->mpFrameData->mpFocusWin->ImplGetFirstOverlapWindow(); if ((!pTopLevelWindow->IsInputEnabled() || pTopLevelWindow->IsInModalMode()) && !pSVData->mpWinData->mpExecuteDialogs.empty()) pSVData->mpWinData->mpExecuteDialogs.back()->ToTop(ToTopFlags::RestoreWhenMin | ToTopFlags::GrabFocusOnly); else pTopLevelWindow->GrabFocus(); } } else GrabFocus(); } else { vcl::Window* pFocusWin = ImplGetWindowImpl()->mpFrameData->mpFocusWin; if ( pFocusWin ) { ImplSVData* pSVData = ImplGetSVData(); if (pSVData->mpWinData->mpFocusWin == pFocusWin) { // transfer the FocusWindow vcl::Window* pOverlapWindow = pFocusWin->ImplGetFirstOverlapWindow(); pOverlapWindow->ImplGetWindowImpl()->mpLastFocusWindow = pFocusWin; pSVData->mpWinData->mpFocusWin = nullptr; if ( pFocusWin->ImplGetWindowImpl()->mpCursor ) pFocusWin->ImplGetWindowImpl()->mpCursor->ImplHide(); // call the Deactivate vcl::Window* pOldOverlapWindow = pFocusWin->ImplGetFirstOverlapWindow(); vcl::Window* pOldRealWindow = pOldOverlapWindow->ImplGetWindow(); pOldOverlapWindow->ImplGetWindowImpl()->mbActive = false; pOldOverlapWindow->Deactivate(); if ( pOldRealWindow != pOldOverlapWindow ) { pOldRealWindow->ImplGetWindowImpl()->mbActive = false; pOldRealWindow->Deactivate(); } // TrackingMode is ended in ImplHandleLoseFocus #ifdef _WIN32 // To avoid problems with the Unix IME pFocusWin->EndExtTextInput(); #endif NotifyEvent aNEvt(MouseNotifyEvent::LOSEFOCUS, pFocusWin); if (!ImplCallPreNotify(aNEvt)) pFocusWin->CompatLoseFocus(); pFocusWin->ImplCallDeactivateListeners(nullptr); } } // Redraw all floating window inactive if ( ImplGetWindowImpl()->mpFrameData->mbStartFocusState != bHasFocus ) ImplActivateFloatingWindows( this, bHasFocus ); } } static void ImplHandleGetFocus( vcl::Window* pWindow ) { pWindow->ImplGetWindowImpl()->mpFrameData->mbHasFocus = true; // execute Focus-Events after a delay, such that SystemChildWindows // do not blink when they receive focus if ( !pWindow->ImplGetWindowImpl()->mpFrameData->mnFocusId ) { pWindow->ImplGetWindowImpl()->mpFrameData->mbStartFocusState = !pWindow->ImplGetWindowImpl()->mpFrameData->mbHasFocus; pWindow->ImplGetWindowImpl()->mpFrameData->mnFocusId = Application::PostUserEvent( LINK( pWindow, vcl::Window, ImplAsyncFocusHdl ), nullptr, true); vcl::Window* pFocusWin = pWindow->ImplGetWindowImpl()->mpFrameData->mpFocusWin; if ( pFocusWin && pFocusWin->ImplGetWindowImpl()->mpCursor ) pFocusWin->ImplGetWindowImpl()->mpCursor->ImplShow(); } } static void ImplHandleLoseFocus( vcl::Window* pWindow ) { ImplSVData* pSVData = ImplGetSVData(); // Abort the autoscroll if the frame loses focus if (pSVData->mpWinData->mpAutoScrollWin) pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll(); // Abort tracking if the frame loses focus if (pSVData->mpWinData->mpTrackWin) { if (pSVData->mpWinData->mpTrackWin->ImplGetWindowImpl()->mpFrameWindow == pWindow) pSVData->mpWinData->mpTrackWin->EndTracking(TrackingEventFlags::Cancel); } pWindow->ImplGetWindowImpl()->mpFrameData->mbHasFocus = false; // execute Focus-Events after a delay, such that SystemChildWindows // do not flicker when they receive focus if ( !pWindow->ImplGetWindowImpl()->mpFrameData->mnFocusId ) { pWindow->ImplGetWindowImpl()->mpFrameData->mbStartFocusState = !pWindow->ImplGetWindowImpl()->mpFrameData->mbHasFocus; pWindow->ImplGetWindowImpl()->mpFrameData->mnFocusId = Application::PostUserEvent( LINK( pWindow, vcl::Window, ImplAsyncFocusHdl ), nullptr, true ); } vcl::Window* pFocusWin = pWindow->ImplGetWindowImpl()->mpFrameData->mpFocusWin; if ( pFocusWin && pFocusWin->ImplGetWindowImpl()->mpCursor ) pFocusWin->ImplGetWindowImpl()->mpCursor->ImplHide(); // Make sure that no menu is visible when a toplevel window loses focus. VclPtr pFirstFloat = pSVData->mpWinData->mpFirstFloat; if (pFirstFloat && !pWindow->GetParent()) pFirstFloat->EndPopupMode(FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll); } namespace { struct DelayedCloseEvent { VclPtr pWindow; }; } static void DelayedCloseEventLink( void* pCEvent, void* ) { DelayedCloseEvent* pEv = static_cast(pCEvent); if( ! pEv->pWindow->IsDisposed() ) { // dispatch to correct window type if( pEv->pWindow->IsSystemWindow() ) static_cast(pEv->pWindow.get())->Close(); else if( pEv->pWindow->IsDockingWindow() ) static_cast(pEv->pWindow.get())->Close(); } delete pEv; } static void ImplHandleClose( const vcl::Window* pWindow ) { ImplSVData* pSVData = ImplGetSVData(); bool bWasPopup = false; if( pWindow->ImplIsFloatingWindow() && static_cast(pWindow)->ImplIsInPrivatePopupMode() ) { bWasPopup = true; } // on Close stop all floating modes and end popups if (pSVData->mpWinData->mpFirstFloat) { FloatingWindow* pLastLevelFloat; pLastLevelFloat = pSVData->mpWinData->mpFirstFloat->ImplFindLastLevelFloat(); pLastLevelFloat->EndPopupMode( FloatWinPopupEndFlags::Cancel | FloatWinPopupEndFlags::CloseAll ); } if ( ImplGetSVHelpData().mbExtHelpMode ) Help::EndExtHelp(); if ( ImplGetSVHelpData().mpHelpWin ) ImplDestroyHelpWindow( false ); // AutoScrollMode if (pSVData->mpWinData->mpAutoScrollWin) pSVData->mpWinData->mpAutoScrollWin->EndAutoScroll(); if (pSVData->mpWinData->mpTrackWin) pSVData->mpWinData->mpTrackWin->EndTracking( TrackingEventFlags::Cancel | TrackingEventFlags::Key ); if (bWasPopup) return; vcl::Window *pWin = pWindow->ImplGetWindow(); SystemWindow* pSysWin = dynamic_cast(pWin); if (pSysWin) { // See if the custom close handler is set. const Link& rLink = pSysWin->GetCloseHdl(); if (rLink.IsSet()) { rLink.Call(*pSysWin); return; } } // check whether close is allowed if ( pWin->IsEnabled() && pWin->IsInputEnabled() && !pWin->IsInModalMode() ) { DelayedCloseEvent* pEv = new DelayedCloseEvent; pEv->pWindow = pWin; Application::PostUserEvent( Link( pEv, DelayedCloseEventLink ) ); } } static void ImplHandleUserEvent( ImplSVEvent* pSVEvent ) { if ( pSVEvent ) { if ( pSVEvent->mbCall ) { pSVEvent->maLink.Call( pSVEvent->mpData ); } delete pSVEvent; } } static MouseEventModifiers ImplGetMouseMoveMode( SalMouseEvent const * pEvent ) { MouseEventModifiers nMode = MouseEventModifiers::NONE; if ( !pEvent->mnCode ) nMode |= MouseEventModifiers::SIMPLEMOVE; if ( (pEvent->mnCode & MOUSE_LEFT) && !(pEvent->mnCode & KEY_MOD1) ) nMode |= MouseEventModifiers::DRAGMOVE; if ( (pEvent->mnCode & MOUSE_LEFT) && (pEvent->mnCode & KEY_MOD1) ) nMode |= MouseEventModifiers::DRAGCOPY; return nMode; } static MouseEventModifiers ImplGetMouseButtonMode( SalMouseEvent const * pEvent ) { MouseEventModifiers nMode = MouseEventModifiers::NONE; if ( pEvent->mnButton == MOUSE_LEFT ) nMode |= MouseEventModifiers::SIMPLECLICK; if ( (pEvent->mnButton == MOUSE_LEFT) && !(pEvent->mnCode & (MOUSE_MIDDLE | MOUSE_RIGHT)) ) nMode |= MouseEventModifiers::SELECT; if ( (pEvent->mnButton == MOUSE_LEFT) && (pEvent->mnCode & KEY_MOD1) && !(pEvent->mnCode & (MOUSE_MIDDLE | MOUSE_RIGHT | KEY_SHIFT)) ) nMode |= MouseEventModifiers::MULTISELECT; if ( (pEvent->mnButton == MOUSE_LEFT) && (pEvent->mnCode & KEY_SHIFT) && !(pEvent->mnCode & (MOUSE_MIDDLE | MOUSE_RIGHT | KEY_MOD1)) ) nMode |= MouseEventModifiers::RANGESELECT; return nMode; } static bool ImplHandleSalMouseLeave( vcl::Window* pWindow, SalMouseEvent const * pEvent ) { return ImplHandleMouseEvent( pWindow, MouseNotifyEvent::MOUSEMOVE, true, pEvent->mnX, pEvent->mnY, pEvent->mnTime, pEvent->mnCode, ImplGetMouseMoveMode( pEvent ) ); } static bool ImplHandleSalMouseMove( vcl::Window* pWindow, SalMouseEvent const * pEvent ) { return ImplHandleMouseEvent( pWindow, MouseNotifyEvent::MOUSEMOVE, false, pEvent->mnX, pEvent->mnY, pEvent->mnTime, pEvent->mnCode, ImplGetMouseMoveMode( pEvent ) ); } static bool ImplHandleSalMouseButtonDown( vcl::Window* pWindow, SalMouseEvent const * pEvent ) { return ImplHandleMouseEvent( pWindow, MouseNotifyEvent::MOUSEBUTTONDOWN, false, pEvent->mnX, pEvent->mnY, pEvent->mnTime, #ifdef MACOSX pEvent->mnButton | (pEvent->mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3)), #else pEvent->mnButton | (pEvent->mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2)), #endif ImplGetMouseButtonMode( pEvent ) ); } static bool ImplHandleSalMouseButtonUp( vcl::Window* pWindow, SalMouseEvent const * pEvent ) { return ImplHandleMouseEvent( pWindow, MouseNotifyEvent::MOUSEBUTTONUP, false, pEvent->mnX, pEvent->mnY, pEvent->mnTime, #ifdef MACOSX pEvent->mnButton | (pEvent->mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3)), #else pEvent->mnButton | (pEvent->mnCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2)), #endif ImplGetMouseButtonMode( pEvent ) ); } static bool ImplHandleMenuEvent( vcl::Window const * pWindow, SalMenuEvent* pEvent, SalEvent nEvent ) { // Find SystemWindow and its Menubar and let it dispatch the command bool bRet = false; vcl::Window *pWin = pWindow->ImplGetWindowImpl()->mpFirstChild; while ( pWin ) { if ( pWin->ImplGetWindowImpl()->mbSysWin ) break; pWin = pWin->ImplGetWindowImpl()->mpNext; } if( pWin ) { MenuBar *pMenuBar = static_cast(pWin)->GetMenuBar(); if( pMenuBar ) { switch( nEvent ) { case SalEvent::MenuActivate: pMenuBar->HandleMenuActivateEvent( static_cast(pEvent->mpMenu) ); bRet = true; break; case SalEvent::MenuDeactivate: pMenuBar->HandleMenuDeActivateEvent( static_cast(pEvent->mpMenu) ); bRet = true; break; case SalEvent::MenuHighlight: bRet = pMenuBar->HandleMenuHighlightEvent( static_cast(pEvent->mpMenu), pEvent->mnId ); break; case SalEvent::MenuButtonCommand: bRet = pMenuBar->HandleMenuButtonEvent( pEvent->mnId ); break; case SalEvent::MenuCommand: bRet = pMenuBar->HandleMenuCommandEvent( static_cast(pEvent->mpMenu), pEvent->mnId ); break; default: break; } } } return bRet; } static void ImplHandleSalKeyMod( vcl::Window* pWindow, SalKeyModEvent const * pEvent ) { ImplSVData* pSVData = ImplGetSVData(); vcl::Window* pTrackWin = pSVData->mpWinData->mpTrackWin; if ( pTrackWin ) pWindow = pTrackWin; #ifdef MACOSX sal_uInt16 nOldCode = pWindow->ImplGetWindowImpl()->mpFrameData->mnMouseCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3); #else sal_uInt16 nOldCode = pWindow->ImplGetWindowImpl()->mpFrameData->mnMouseCode & (KEY_SHIFT | KEY_MOD1 | KEY_MOD2); #endif sal_uInt16 nNewCode = pEvent->mnCode; if ( nOldCode != nNewCode ) { #ifdef MACOSX nNewCode |= pWindow->ImplGetWindowImpl()->mpFrameData->mnMouseCode & ~(KEY_SHIFT | KEY_MOD1 | KEY_MOD2 | KEY_MOD3); #else nNewCode |= pWindow->ImplGetWindowImpl()->mpFrameData->mnMouseCode & ~(KEY_SHIFT | KEY_MOD1 | KEY_MOD2); #endif pWindow->ImplGetWindowImpl()->mpFrameWindow->ImplCallMouseMove( nNewCode, true ); } // #105224# send commandevent to allow special treatment of Ctrl-LeftShift/Ctrl-RightShift etc. // + auto-accelerator feature, tdf#92630 // try to find a key input window... vcl::Window* pChild = ImplGetKeyInputWindow( pWindow ); //...otherwise fail safe... if (!pChild) pChild = pWindow; CommandModKeyData data( pEvent->mnModKeyCode, pEvent->mbDown ); ImplCallCommand( pChild, CommandEventId::ModKeyChange, &data ); } static void ImplHandleInputLanguageChange( vcl::Window* pWindow ) { // find window vcl::Window* pChild = ImplGetKeyInputWindow( pWindow ); if ( !pChild ) return; ImplCallCommand( pChild, CommandEventId::InputLanguageChange ); } static void ImplHandleSalSettings( SalEvent nEvent ) { Application* pApp = GetpApp(); if ( !pApp ) return; if ( nEvent == SalEvent::SettingsChanged ) { AllSettings aSettings = Application::GetSettings(); Application::MergeSystemSettings( aSettings ); pApp->OverrideSystemSettings( aSettings ); Application::SetSettings( aSettings ); } else { DataChangedEventType nType; switch ( nEvent ) { case SalEvent::PrinterChanged: ImplDeletePrnQueueList(); nType = DataChangedEventType::PRINTER; break; case SalEvent::DisplayChanged: nType = DataChangedEventType::DISPLAY; break; case SalEvent::FontChanged: OutputDevice::ImplUpdateAllFontData( true ); nType = DataChangedEventType::FONTS; break; default: nType = DataChangedEventType::NONE; break; } if ( nType != DataChangedEventType::NONE ) { DataChangedEvent aDCEvt( nType ); Application::ImplCallEventListenersApplicationDataChanged(&aDCEvt); Application::NotifyAllWindows( aDCEvt ); } } } static void ImplHandleSalExtTextInputPos( vcl::Window* pWindow, SalExtTextInputPosEvent* pEvt ) { tools::Rectangle aCursorRect; ImplHandleExtTextInputPos( pWindow, aCursorRect, pEvt->mnExtWidth, &pEvt->mbVertical ); if ( aCursorRect.IsEmpty() ) { pEvt->mnX = -1; pEvt->mnY = -1; pEvt->mnWidth = -1; pEvt->mnHeight = -1; } else { pEvt->mnX = aCursorRect.Left(); pEvt->mnY = aCursorRect.Top(); pEvt->mnWidth = aCursorRect.GetWidth(); pEvt->mnHeight = aCursorRect.GetHeight(); } } static bool ImplHandleShowDialog( vcl::Window* pWindow, ShowDialogId nDialogId ) { if( ! pWindow ) return false; if( pWindow->GetType() == WindowType::BORDERWINDOW ) { vcl::Window* pWrkWin = pWindow->GetWindow( GetWindowType::Client ); if( pWrkWin ) pWindow = pWrkWin; } CommandDialogData aCmdData( nDialogId ); return ImplCallCommand( pWindow, CommandEventId::ShowDialog, &aCmdData ); } static void ImplHandleSurroundingTextRequest( vcl::Window *pWindow, OUString& rText, Selection &rSelRange ) { vcl::Window* pChild = ImplGetKeyInputWindow( pWindow ); if ( !pChild ) { rText.clear(); rSelRange.setMin( 0 ); rSelRange.setMax( 0 ); } else { rText = pChild->GetSurroundingText(); Selection aSel = pChild->GetSurroundingTextSelection(); rSelRange.setMin( aSel.Min() ); rSelRange.setMax( aSel.Max() ); } } static void ImplHandleSalSurroundingTextRequest( vcl::Window *pWindow, SalSurroundingTextRequestEvent *pEvt ) { Selection aSelRange; ImplHandleSurroundingTextRequest( pWindow, pEvt->maText, aSelRange ); aSelRange.Justify(); if( aSelRange.Min() < 0 ) pEvt->mnStart = 0; else if( aSelRange.Min() > pEvt->maText.getLength() ) pEvt->mnStart = pEvt->maText.getLength(); else pEvt->mnStart = aSelRange.Min(); if( aSelRange.Max() < 0 ) pEvt->mnStart = 0; else if( aSelRange.Max() > pEvt->maText.getLength() ) pEvt->mnEnd = pEvt->maText.getLength(); else pEvt->mnEnd = aSelRange.Max(); } static void ImplHandleSalDeleteSurroundingTextRequest( vcl::Window *pWindow, SalSurroundingTextSelectionChangeEvent *pEvt ) { vcl::Window* pChild = ImplGetKeyInputWindow( pWindow ); Selection aSelection(pEvt->mnStart, pEvt->mnEnd); if (pChild && pChild->DeleteSurroundingText(aSelection)) { pEvt->mnStart = aSelection.Min(); pEvt->mnEnd = aSelection.Max(); } else { pEvt->mnStart = pEvt->mnEnd = SAL_MAX_UINT32; } } static void ImplHandleSurroundingTextSelectionChange( vcl::Window *pWindow, sal_uLong nStart, sal_uLong nEnd ) { vcl::Window* pChild = ImplGetKeyInputWindow( pWindow ); if( pChild ) { CommandSelectionChangeData data( nStart, nEnd ); ImplCallCommand( pChild, CommandEventId::SelectionChange, &data ); } } static void ImplHandleStartReconversion( vcl::Window *pWindow ) { vcl::Window* pChild = ImplGetKeyInputWindow( pWindow ); if( pChild ) ImplCallCommand( pChild, CommandEventId::PrepareReconversion ); } static void ImplHandleSalQueryCharPosition( vcl::Window *pWindow, SalQueryCharPositionEvent *pEvt ) { pEvt->mbValid = false; pEvt->mbVertical = false; pEvt->mnCursorBoundX = 0; pEvt->mnCursorBoundY = 0; pEvt->mnCursorBoundWidth = 0; pEvt->mnCursorBoundHeight = 0; ImplSVData* pSVData = ImplGetSVData(); vcl::Window* pChild = pSVData->mpWinData->mpExtTextInputWin; if ( !pChild ) pChild = ImplGetKeyInputWindow( pWindow ); else { // Test, if the Window is related to the frame if ( !pWindow->ImplIsWindowOrChild( pChild ) ) pChild = ImplGetKeyInputWindow( pWindow ); } if( !pChild ) return; ImplCallCommand( pChild, CommandEventId::QueryCharPosition ); ImplWinData* pWinData = pChild->ImplGetWinData(); if ( !(pWinData->mpCompositionCharRects && pEvt->mnCharPos < o3tl::make_unsigned( pWinData->mnCompositionCharRects )) ) return; const OutputDevice *pChildOutDev = pChild->GetOutDev(); const tools::Rectangle& aRect = pWinData->mpCompositionCharRects[ pEvt->mnCharPos ]; tools::Rectangle aDeviceRect = pChildOutDev->ImplLogicToDevicePixel( aRect ); Point aAbsScreenPos = pChild->OutputToAbsoluteScreenPixel( pChild->ScreenToOutputPixel(aDeviceRect.TopLeft()) ); pEvt->mnCursorBoundX = aAbsScreenPos.X(); pEvt->mnCursorBoundY = aAbsScreenPos.Y(); pEvt->mnCursorBoundWidth = aDeviceRect.GetWidth(); pEvt->mnCursorBoundHeight = aDeviceRect.GetHeight(); pEvt->mbVertical = pWinData->mbVertical; pEvt->mbValid = true; } bool ImplWindowFrameProc( vcl::Window* _pWindow, SalEvent nEvent, const void* pEvent ) { DBG_TESTSOLARMUTEX(); // Ensure the window survives during this method. VclPtr pWindow( _pWindow ); bool bRet = false; // #119709# for some unknown reason it is possible to receive events (in this case key events) // although the corresponding VCL window must have been destroyed already // at least ImplGetWindowImpl() was NULL in these cases, so check this here if( pWindow->ImplGetWindowImpl() == nullptr ) return false; switch ( nEvent ) { case SalEvent::MouseMove: bRet = ImplHandleSalMouseMove( pWindow, static_cast(pEvent) ); break; case SalEvent::ExternalMouseMove: { MouseEvent const * pMouseEvt = static_cast(pEvent); SalMouseEvent aSalMouseEvent; aSalMouseEvent.mnTime = tools::Time::GetSystemTicks(); aSalMouseEvent.mnX = pMouseEvt->GetPosPixel().X(); aSalMouseEvent.mnY = pMouseEvt->GetPosPixel().Y(); aSalMouseEvent.mnButton = 0; aSalMouseEvent.mnCode = pMouseEvt->GetButtons() | pMouseEvt->GetModifier(); bRet = ImplHandleSalMouseMove( pWindow, &aSalMouseEvent ); } break; case SalEvent::MouseLeave: bRet = ImplHandleSalMouseLeave( pWindow, static_cast(pEvent) ); break; case SalEvent::MouseButtonDown: bRet = ImplHandleSalMouseButtonDown( pWindow, static_cast(pEvent) ); break; case SalEvent::ExternalMouseButtonDown: { MouseEvent const * pMouseEvt = static_cast(pEvent); SalMouseEvent aSalMouseEvent; aSalMouseEvent.mnTime = tools::Time::GetSystemTicks(); aSalMouseEvent.mnX = pMouseEvt->GetPosPixel().X(); aSalMouseEvent.mnY = pMouseEvt->GetPosPixel().Y(); aSalMouseEvent.mnButton = pMouseEvt->GetButtons(); aSalMouseEvent.mnCode = pMouseEvt->GetButtons() | pMouseEvt->GetModifier(); bRet = ImplHandleSalMouseButtonDown( pWindow, &aSalMouseEvent ); } break; case SalEvent::MouseButtonUp: bRet = ImplHandleSalMouseButtonUp( pWindow, static_cast(pEvent) ); break; case SalEvent::ExternalMouseButtonUp: { MouseEvent const * pMouseEvt = static_cast(pEvent); SalMouseEvent aSalMouseEvent; aSalMouseEvent.mnTime = tools::Time::GetSystemTicks(); aSalMouseEvent.mnX = pMouseEvt->GetPosPixel().X(); aSalMouseEvent.mnY = pMouseEvt->GetPosPixel().Y(); aSalMouseEvent.mnButton = pMouseEvt->GetButtons(); aSalMouseEvent.mnCode = pMouseEvt->GetButtons() | pMouseEvt->GetModifier(); bRet = ImplHandleSalMouseButtonUp( pWindow, &aSalMouseEvent ); } break; case SalEvent::MouseActivate: bRet = false; break; case SalEvent::KeyInput: { SalKeyEvent const * pKeyEvt = static_cast(pEvent); bRet = ImplHandleKey( pWindow, MouseNotifyEvent::KEYINPUT, pKeyEvt->mnCode, pKeyEvt->mnCharCode, pKeyEvt->mnRepeat, true ); } break; case SalEvent::ExternalKeyInput: { KeyEvent const * pKeyEvt = static_cast(pEvent); bRet = ImplHandleKey( pWindow, MouseNotifyEvent::KEYINPUT, pKeyEvt->GetKeyCode().GetFullCode(), pKeyEvt->GetCharCode(), pKeyEvt->GetRepeat(), false ); } break; case SalEvent::KeyUp: { SalKeyEvent const * pKeyEvt = static_cast(pEvent); bRet = ImplHandleKey( pWindow, MouseNotifyEvent::KEYUP, pKeyEvt->mnCode, pKeyEvt->mnCharCode, pKeyEvt->mnRepeat, true ); } break; case SalEvent::ExternalKeyUp: { KeyEvent const * pKeyEvt = static_cast(pEvent); bRet = ImplHandleKey( pWindow, MouseNotifyEvent::KEYUP, pKeyEvt->GetKeyCode().GetFullCode(), pKeyEvt->GetCharCode(), pKeyEvt->GetRepeat(), false ); } break; case SalEvent::KeyModChange: ImplHandleSalKeyMod( pWindow, static_cast(pEvent) ); break; case SalEvent::InputLanguageChange: ImplHandleInputLanguageChange( pWindow ); break; case SalEvent::MenuActivate: case SalEvent::MenuDeactivate: case SalEvent::MenuHighlight: case SalEvent::MenuCommand: case SalEvent::MenuButtonCommand: bRet = ImplHandleMenuEvent( pWindow, const_cast(static_cast(pEvent)), nEvent ); break; case SalEvent::WheelMouse: bRet = ImplHandleWheelEvent( pWindow, *static_cast(pEvent)); break; case SalEvent::Paint: { SalPaintEvent const * pPaintEvt = static_cast(pEvent); if( AllSettings::GetLayoutRTL() ) { SalFrame* pSalFrame = pWindow->ImplGetWindowImpl()->mpFrame; const_cast(pPaintEvt)->mnBoundX = pSalFrame->maGeometry.nWidth-pPaintEvt->mnBoundWidth-pPaintEvt->mnBoundX; } tools::Rectangle aBoundRect( Point( pPaintEvt->mnBoundX, pPaintEvt->mnBoundY ), Size( pPaintEvt->mnBoundWidth, pPaintEvt->mnBoundHeight ) ); ImplHandlePaint( pWindow, aBoundRect, pPaintEvt->mbImmediateUpdate ); } break; case SalEvent::Move: ImplHandleMove( pWindow ); break; case SalEvent::Resize: { tools::Long nNewWidth; tools::Long nNewHeight; pWindow->ImplGetWindowImpl()->mpFrame->GetClientSize( nNewWidth, nNewHeight ); ImplHandleResize( pWindow, nNewWidth, nNewHeight ); } break; case SalEvent::MoveResize: { SalFrameGeometry g = pWindow->ImplGetWindowImpl()->mpFrame->GetGeometry(); ImplHandleMoveResize( pWindow, g.nWidth, g.nHeight ); } break; case SalEvent::ClosePopups: { KillOwnPopups( pWindow ); } break; case SalEvent::GetFocus: ImplHandleGetFocus( pWindow ); break; case SalEvent::LoseFocus: ImplHandleLoseFocus( pWindow ); break; case SalEvent::Close: ImplHandleClose( pWindow ); break; case SalEvent::Shutdown: { static bool bInQueryExit = false; if( !bInQueryExit ) { bInQueryExit = true; if ( GetpApp()->QueryExit() ) { // end the message loop Application::Quit(); return false; } else { bInQueryExit = false; return true; } } return false; } case SalEvent::SettingsChanged: case SalEvent::PrinterChanged: case SalEvent::DisplayChanged: case SalEvent::FontChanged: ImplHandleSalSettings( nEvent ); break; case SalEvent::UserEvent: ImplHandleUserEvent( const_cast(static_cast(pEvent)) ); break; case SalEvent::ExtTextInput: { SalExtTextInputEvent const * pEvt = static_cast(pEvent); bRet = ImplHandleExtTextInput( pWindow, pEvt->maText, pEvt->mpTextAttr, pEvt->mnCursorPos, pEvt->mnCursorFlags ); } break; case SalEvent::EndExtTextInput: bRet = ImplHandleEndExtTextInput(); break; case SalEvent::ExtTextInputPos: ImplHandleSalExtTextInputPos( pWindow, const_cast(static_cast(pEvent)) ); break; case SalEvent::InputContextChange: bRet = ImplHandleInputContextChange( pWindow ); break; case SalEvent::ShowDialog: { ShowDialogId nLOKWindowId = static_cast(reinterpret_cast(pEvent)); bRet = ImplHandleShowDialog( pWindow, nLOKWindowId ); } break; case SalEvent::SurroundingTextRequest: ImplHandleSalSurroundingTextRequest( pWindow, const_cast(static_cast(pEvent)) ); break; case SalEvent::DeleteSurroundingTextRequest: ImplHandleSalDeleteSurroundingTextRequest( pWindow, const_cast(static_cast(pEvent)) ); break; case SalEvent::SurroundingTextSelectionChange: { SalSurroundingTextSelectionChangeEvent const * pEvt = static_cast(pEvent); ImplHandleSurroundingTextSelectionChange( pWindow, pEvt->mnStart, pEvt->mnEnd ); [[fallthrough]]; // TODO: Fallthrough really intended? } case SalEvent::StartReconversion: ImplHandleStartReconversion( pWindow ); break; case SalEvent::QueryCharPosition: ImplHandleSalQueryCharPosition( pWindow, const_cast(static_cast(pEvent)) ); break; case SalEvent::Swipe: bRet = ImplHandleSwipe(pWindow, *static_cast(pEvent)); break; case SalEvent::LongPress: bRet = ImplHandleLongPress(pWindow, *static_cast(pEvent)); break; case SalEvent::ExternalGesture: { auto const * pGestureEvent = static_cast(pEvent); SalGestureEvent aSalGestureEvent; aSalGestureEvent.mfOffset = pGestureEvent->mnOffset; aSalGestureEvent.mnX = pGestureEvent->mnX; aSalGestureEvent.mnY = pGestureEvent->mnY; aSalGestureEvent.meEventType = pGestureEvent->meEventType; aSalGestureEvent.meOrientation = pGestureEvent->meOrientation; bRet = ImplHandleGestureEvent(pWindow, aSalGestureEvent); } break; case SalEvent::Gesture: { auto const * aSalGestureEvent = static_cast(pEvent); bRet = ImplHandleGestureEvent(pWindow, *aSalGestureEvent); } break; default: SAL_WARN( "vcl.layout", "ImplWindowFrameProc(): unknown event (" << static_cast(nEvent) << ")" ); break; } return bRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */