f097720636
Change-Id: I08ee0ba87c5c54403a329100b69e96aac3aded28 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164857 Tested-by: Jenkins Reviewed-by: Mike Kaganski <mike.kaganski@collabora.com>
1333 lines
43 KiB
C++
1333 lines
43 KiB
C++
/* -*- 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 "menufloatingwindow.hxx"
|
|
#include "menuitemlist.hxx"
|
|
#include "menubarwindow.hxx"
|
|
#include "bufferdevice.hxx"
|
|
|
|
#include <sal/log.hxx>
|
|
#include <salframe.hxx>
|
|
#include <svdata.hxx>
|
|
#include <vcl/decoview.hxx>
|
|
#include <vcl/settings.hxx>
|
|
#include <window.h>
|
|
|
|
MenuFloatingWindow::MenuFloatingWindow( Menu* pMen, vcl::Window* pParent, WinBits nStyle ) :
|
|
FloatingWindow( pParent, nStyle ),
|
|
pMenu(pMen),
|
|
aHighlightChangedTimer("vcl::MenuFloatingWindow aHighlightChangedTimer"),
|
|
aSubmenuCloseTimer( "vcl::MenuFloatingWindow aSubmenuCloseTimer" ),
|
|
aScrollTimer( "vcl::MenuFloatingWindow aScrollTimer" ),
|
|
nHighlightedItem(ITEMPOS_INVALID),
|
|
nMBDownPos(ITEMPOS_INVALID),
|
|
nScrollerHeight(0),
|
|
nFirstEntry(0),
|
|
nPosInParent(ITEMPOS_INVALID),
|
|
bInExecute(false),
|
|
bScrollMenu(false),
|
|
bScrollUp(false),
|
|
bScrollDown(false),
|
|
bIgnoreFirstMove(true),
|
|
bKeyInput(false)
|
|
{
|
|
mpWindowImpl->mbMenuFloatingWindow= true;
|
|
|
|
ApplySettings(*GetOutDev());
|
|
|
|
SetPopupModeEndHdl( LINK( this, MenuFloatingWindow, PopupEnd ) );
|
|
|
|
aHighlightChangedTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, HighlightChanged ) );
|
|
aHighlightChangedTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
|
|
|
|
aSubmenuCloseTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
|
|
aSubmenuCloseTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, SubmenuClose ) );
|
|
|
|
aScrollTimer.SetInvokeHandler( LINK( this, MenuFloatingWindow, AutoScroll ) );
|
|
|
|
AddEventListener( LINK( this, MenuFloatingWindow, ShowHideListener ) );
|
|
}
|
|
|
|
void MenuFloatingWindow::doShutdown()
|
|
{
|
|
if( !pMenu )
|
|
return;
|
|
|
|
// #105373# notify toolkit that highlight was removed
|
|
// otherwise the entry will not be read when the menu is opened again
|
|
if( nHighlightedItem != ITEMPOS_INVALID )
|
|
pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, nHighlightedItem );
|
|
if (!bKeyInput && pMenu && pMenu->pStartedFrom && !pMenu->pStartedFrom->IsMenuBar())
|
|
{
|
|
// #102461# remove highlight in parent
|
|
size_t i, nCount = pMenu->pStartedFrom->pItemList->size();
|
|
for(i = 0; i < nCount; i++)
|
|
{
|
|
MenuItemData* pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
|
|
if( pData && ( pData->pSubMenu == pMenu ) )
|
|
break;
|
|
}
|
|
if( i < nCount )
|
|
{
|
|
MenuFloatingWindow* pPWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->ImplGetWindow());
|
|
if (pPWin)
|
|
pPWin->InvalidateItem(i);
|
|
}
|
|
}
|
|
|
|
// free the reference to the accessible component
|
|
SetAccessible( css::uno::Reference< css::accessibility::XAccessible >() );
|
|
|
|
aHighlightChangedTimer.Stop();
|
|
|
|
// #95056# invalidate screen area covered by system window
|
|
// so this can be taken into account if the commandhandler performs a scroll operation
|
|
if( GetParent() )
|
|
{
|
|
tools::Rectangle aInvRect( GetWindowExtentsRelative( *GetParent() ) );
|
|
GetParent()->Invalidate( aInvRect );
|
|
}
|
|
pMenu = nullptr;
|
|
RemoveEventListener( LINK( this, MenuFloatingWindow, ShowHideListener ) );
|
|
|
|
aScrollTimer.Stop();
|
|
aSubmenuCloseTimer.Stop();
|
|
aSubmenuCloseTimer.Stop();
|
|
aHighlightChangedTimer.Stop();
|
|
aHighlightChangedTimer.Stop();
|
|
|
|
}
|
|
|
|
MenuFloatingWindow::~MenuFloatingWindow()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void MenuFloatingWindow::dispose()
|
|
{
|
|
doShutdown();
|
|
pMenu.clear();
|
|
pActivePopup.clear();
|
|
xSaveFocusId.clear();
|
|
FloatingWindow::dispose();
|
|
}
|
|
|
|
void MenuFloatingWindow::Resize()
|
|
{
|
|
InitMenuClipRegion(*GetOutDev()); // FIXME
|
|
}
|
|
|
|
void MenuFloatingWindow::ApplySettings(vcl::RenderContext& rRenderContext)
|
|
{
|
|
FloatingWindow::ApplySettings(rRenderContext);
|
|
|
|
if (IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItem) &&
|
|
IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
|
|
{
|
|
AllSettings aSettings(GetSettings());
|
|
ImplGetFrame()->UpdateSettings(aSettings); // Update theme colors.
|
|
StyleSettings aStyle(aSettings.GetStyleSettings());
|
|
Color aHighlightTextColor = ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor;
|
|
if (aHighlightTextColor != COL_TRANSPARENT)
|
|
{
|
|
aStyle.SetMenuHighlightTextColor(aHighlightTextColor);
|
|
}
|
|
aSettings.SetStyleSettings(aStyle);
|
|
GetOutDev()->SetSettings(aSettings);
|
|
}
|
|
|
|
const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
SetPointFont(rRenderContext, rStyleSettings.GetMenuFont());
|
|
|
|
if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
|
|
{
|
|
rRenderContext.SetBackground(); // background will be drawn by NWF
|
|
}
|
|
else
|
|
rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetMenuColor()));
|
|
|
|
rRenderContext.SetTextColor(rStyleSettings.GetMenuTextColor());
|
|
rRenderContext.SetTextFillColor();
|
|
rRenderContext.SetLineColor();
|
|
}
|
|
|
|
/// Get a negative pixel offset for an offset menu
|
|
tools::Long MenuFloatingWindow::ImplGetStartY() const
|
|
{
|
|
tools::Long nY = 0;
|
|
if( pMenu )
|
|
{
|
|
// avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
|
|
if ( nFirstEntry > 0 && !pMenu->GetItemList()->GetDataFromPos(nFirstEntry - 1) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
for ( sal_uInt16 n = 0; n < nFirstEntry; n++ )
|
|
nY += pMenu->GetItemList()->GetDataFromPos( n )->aSz.Height();
|
|
nY -= pMenu->GetTitleHeight();
|
|
}
|
|
return -nY;
|
|
}
|
|
|
|
vcl::Region MenuFloatingWindow::ImplCalcClipRegion() const
|
|
{
|
|
Size aOutSz = GetOutputSizePixel();
|
|
tools::Rectangle aRect( Point(), aOutSz );
|
|
aRect.AdjustTop(nScrollerHeight );
|
|
aRect.AdjustBottom( -nScrollerHeight );
|
|
|
|
vcl::Region aRegion(aRect);
|
|
|
|
return aRegion;
|
|
}
|
|
|
|
void MenuFloatingWindow::InitMenuClipRegion(vcl::RenderContext& rRenderContext)
|
|
{
|
|
if (IsScrollMenu())
|
|
{
|
|
rRenderContext.SetClipRegion(ImplCalcClipRegion());
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetClipRegion();
|
|
}
|
|
}
|
|
|
|
void MenuFloatingWindow::ImplHighlightItem( const MouseEvent& rMEvt, bool bMBDown )
|
|
{
|
|
if( ! pMenu )
|
|
return;
|
|
|
|
tools::Long nY = GetInitialItemY();
|
|
tools::Long nMouseY = rMEvt.GetPosPixel().Y();
|
|
Size aOutSz = GetOutputSizePixel();
|
|
if ( ( nMouseY >= nY ) && ( nMouseY < aOutSz.Height() ) )
|
|
{
|
|
bool bHighlighted = false;
|
|
size_t nCount = pMenu->pItemList->size();
|
|
for ( size_t n = 0; !bHighlighted && ( n < nCount ); n++ )
|
|
{
|
|
if ( pMenu->ImplIsVisible( n ) )
|
|
{
|
|
MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( n );
|
|
tools::Long nOldY = nY;
|
|
nY += pItemData->aSz.Height();
|
|
if ( ( nOldY <= nMouseY ) && ( nY > nMouseY ) && pMenu->ImplIsSelectable( n ) )
|
|
{
|
|
bool bPopupArea = true;
|
|
if ( pItemData->nBits & MenuItemBits::POPUPSELECT )
|
|
{
|
|
// only when clicked over the arrow...
|
|
Size aSz = GetOutputSizePixel();
|
|
tools::Long nFontHeight = GetTextHeight();
|
|
bPopupArea = ( rMEvt.GetPosPixel().X() >= ( aSz.Width() - nFontHeight - nFontHeight/4 ) );
|
|
}
|
|
|
|
if ( bMBDown )
|
|
{
|
|
if ( n != nHighlightedItem )
|
|
{
|
|
ChangeHighlightItem( static_cast<sal_uInt16>(n), false );
|
|
}
|
|
|
|
bool bAllowNewPopup = true;
|
|
if ( pActivePopup )
|
|
{
|
|
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
|
|
bAllowNewPopup = pData && ( pData->pSubMenu != pActivePopup );
|
|
if ( bAllowNewPopup )
|
|
KillActivePopup();
|
|
}
|
|
|
|
if ( bPopupArea && bAllowNewPopup )
|
|
{
|
|
HighlightChanged( nullptr );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( n != nHighlightedItem )
|
|
{
|
|
ChangeHighlightItem( static_cast<sal_uInt16>(n), true );
|
|
}
|
|
else if ( pItemData->nBits & MenuItemBits::POPUPSELECT )
|
|
{
|
|
if ( bPopupArea && ( pActivePopup != pItemData->pSubMenu ) )
|
|
HighlightChanged( nullptr );
|
|
}
|
|
}
|
|
bHighlighted = true;
|
|
}
|
|
}
|
|
}
|
|
if ( !bHighlighted )
|
|
ChangeHighlightItem( ITEMPOS_INVALID, true );
|
|
}
|
|
else
|
|
{
|
|
ImplScroll( rMEvt.GetPosPixel() );
|
|
ChangeHighlightItem( ITEMPOS_INVALID, true );
|
|
}
|
|
}
|
|
|
|
IMPL_LINK_NOARG(MenuFloatingWindow, PopupEnd, FloatingWindow*, void)
|
|
{
|
|
// "this" will be deleted before the end of this method!
|
|
Menu* pM = pMenu;
|
|
if ( bInExecute )
|
|
{
|
|
End();
|
|
if ( pActivePopup )
|
|
{
|
|
KillActivePopup(); // should be ok to just remove it
|
|
//pActivePopup->bCanceled = true;
|
|
}
|
|
pMenu->bInCallback = true;
|
|
pMenu->Deactivate();
|
|
pMenu->bInCallback = false;
|
|
}
|
|
else
|
|
{
|
|
if (pMenu && pMenu->pStartedFrom)
|
|
pMenu->pStartedFrom->ClosePopup(pMenu);
|
|
}
|
|
|
|
if ( pM )
|
|
pM->pStartedFrom = nullptr;
|
|
}
|
|
|
|
IMPL_LINK_NOARG(MenuFloatingWindow, AutoScroll, Timer *, void)
|
|
{
|
|
ImplScroll( GetPointerPosPixel() );
|
|
}
|
|
|
|
IMPL_LINK( MenuFloatingWindow, HighlightChanged, Timer*, pTimer, void )
|
|
{
|
|
if( ! pMenu )
|
|
return;
|
|
|
|
MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( nHighlightedItem );
|
|
if ( !pItemData )
|
|
return;
|
|
|
|
if ( pActivePopup && ( pActivePopup != pItemData->pSubMenu ) )
|
|
{
|
|
FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
|
|
SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose );
|
|
KillActivePopup();
|
|
SetPopupModeFlags( nOldFlags );
|
|
}
|
|
if ( !(pItemData->bEnabled && pItemData->pSubMenu && pItemData->pSubMenu->GetItemCount() && ( pItemData->pSubMenu != pActivePopup )) )
|
|
return;
|
|
|
|
pActivePopup = pItemData->pSubMenu.get();
|
|
tools::Long nY = nScrollerHeight+ImplGetStartY();
|
|
MenuItemData* pData = nullptr;
|
|
for ( sal_uLong n = 0; n < nHighlightedItem; n++ )
|
|
{
|
|
pData = pMenu->pItemList->GetDataFromPos( n );
|
|
nY += pData->aSz.Height();
|
|
}
|
|
pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem );
|
|
Size MySize = GetOutputSizePixel();
|
|
Point aItemTopLeft( 0, nY );
|
|
Point aItemBottomRight( aItemTopLeft );
|
|
aItemBottomRight.AdjustX(MySize.Width() );
|
|
aItemBottomRight.AdjustY(pData->aSz.Height() );
|
|
|
|
// shift the popups a little
|
|
aItemTopLeft.AdjustX(2 );
|
|
aItemBottomRight.AdjustX( -2 );
|
|
if ( nHighlightedItem )
|
|
aItemTopLeft.AdjustY( -2 );
|
|
else
|
|
{
|
|
sal_Int32 nL, nT, nR, nB;
|
|
GetBorder( nL, nT, nR, nB );
|
|
aItemTopLeft.AdjustY( -nT );
|
|
}
|
|
|
|
// pTest: crash due to Reschedule() in call of Activate()
|
|
// Also it is prevented that submenus are displayed which
|
|
// were for long in Activate Rescheduled and which should not be
|
|
// displayed now.
|
|
Menu* pTest = pActivePopup;
|
|
FloatWinPopupFlags nOldFlags = GetPopupModeFlags();
|
|
SetPopupModeFlags( GetPopupModeFlags() | FloatWinPopupFlags::NoAppFocusClose );
|
|
sal_uInt16 nRet = pActivePopup->ImplExecute( this, tools::Rectangle( aItemTopLeft, aItemBottomRight ), FloatWinPopupFlags::Right, pMenu, pTimer == nullptr );
|
|
SetPopupModeFlags( nOldFlags );
|
|
|
|
// nRet != 0, if it was stopped during Activate()...
|
|
if ( !nRet && ( pActivePopup == pTest ) && pActivePopup->ImplGetWindow() )
|
|
pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this );
|
|
}
|
|
|
|
IMPL_LINK_NOARG(MenuFloatingWindow, SubmenuClose, Timer *, void)
|
|
{
|
|
if( pMenu && pMenu->pStartedFrom )
|
|
{
|
|
MenuFloatingWindow* pWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->GetWindow());
|
|
if( pWin )
|
|
pWin->KillActivePopup();
|
|
}
|
|
}
|
|
|
|
IMPL_LINK( MenuFloatingWindow, ShowHideListener, VclWindowEvent&, rEvent, void )
|
|
{
|
|
if( ! pMenu )
|
|
return;
|
|
|
|
if( rEvent.GetId() == VclEventId::WindowShow )
|
|
pMenu->ImplCallEventListeners( VclEventId::MenuShow, ITEMPOS_INVALID );
|
|
else if( rEvent.GetId() == VclEventId::WindowHide )
|
|
pMenu->ImplCallEventListeners( VclEventId::MenuHide, ITEMPOS_INVALID );
|
|
}
|
|
|
|
void MenuFloatingWindow::EnableScrollMenu( bool b )
|
|
{
|
|
bScrollMenu = b;
|
|
nScrollerHeight = b ? static_cast<sal_uInt16>(GetSettings().GetStyleSettings().GetScrollBarSize()) /2 : 0;
|
|
bScrollDown = true;
|
|
InitMenuClipRegion(*GetOutDev());
|
|
}
|
|
|
|
void MenuFloatingWindow::Start()
|
|
{
|
|
if (bInExecute)
|
|
return;
|
|
bInExecute = true;
|
|
if (GetParent())
|
|
GetParent()->IncModalCount();
|
|
}
|
|
|
|
bool MenuFloatingWindow::MenuInHierarchyHasFocus() const
|
|
{
|
|
if (HasChildPathFocus())
|
|
return true;
|
|
PopupMenu* pSub = GetActivePopup();
|
|
if (!pSub)
|
|
return false;
|
|
return pSub->ImplGetFloatingWindow()->HasChildPathFocus();
|
|
}
|
|
|
|
void MenuFloatingWindow::End()
|
|
{
|
|
if (!bInExecute)
|
|
return;
|
|
|
|
if (GetParent() && !GetParent()->isDisposed())
|
|
GetParent()->DecModalCount();
|
|
|
|
// restore focus to previous window if we still have the focus
|
|
VclPtr<vcl::Window> xFocusId(xSaveFocusId);
|
|
xSaveFocusId = nullptr;
|
|
if (xFocusId != nullptr && MenuInHierarchyHasFocus())
|
|
{
|
|
ImplGetSVData()->mpWinData->mbNoDeactivate = false;
|
|
Window::EndSaveFocus(xFocusId);
|
|
}
|
|
|
|
bInExecute = false;
|
|
}
|
|
|
|
void MenuFloatingWindow::Execute()
|
|
{
|
|
ImplSVData* pSVData = ImplGetSVData();
|
|
|
|
pSVData->maAppData.mpActivePopupMenu = static_cast<PopupMenu*>(pMenu.get());
|
|
|
|
Start();
|
|
|
|
while (bInExecute && !Application::IsQuit())
|
|
Application::Yield();
|
|
|
|
pSVData->maAppData.mpActivePopupMenu = nullptr;
|
|
}
|
|
|
|
void MenuFloatingWindow::StopExecute()
|
|
{
|
|
End();
|
|
|
|
ImplEndPopupMode(FloatWinPopupEndFlags::NONE, xSaveFocusId);
|
|
|
|
aHighlightChangedTimer.Stop();
|
|
if (pActivePopup)
|
|
{
|
|
KillActivePopup();
|
|
}
|
|
// notify parent, needed for accessibility
|
|
if( pMenu && pMenu->pStartedFrom )
|
|
pMenu->pStartedFrom->ImplCallEventListeners( VclEventId::MenuSubmenuDeactivate, nPosInParent );
|
|
}
|
|
|
|
void MenuFloatingWindow::KillActivePopup( PopupMenu* pThisOnly )
|
|
{
|
|
if ( !pActivePopup || ( pThisOnly && ( pThisOnly != pActivePopup ) ) )
|
|
return;
|
|
|
|
if( pActivePopup->pWindow )
|
|
if( static_cast<FloatingWindow *>(pActivePopup->pWindow.get())->IsInCleanUp() )
|
|
return; // kill it later
|
|
if ( pActivePopup->bInCallback )
|
|
pActivePopup->bCanceled = true;
|
|
|
|
// For all actions pActivePopup = 0, if e.g.
|
|
// PopupModeEndHdl the popups to destroy were called synchronous
|
|
PopupMenu* pPopup = pActivePopup;
|
|
pActivePopup = nullptr;
|
|
pPopup->bInCallback = true;
|
|
pPopup->Deactivate();
|
|
pPopup->bInCallback = false;
|
|
if ( pPopup->ImplGetWindow() )
|
|
{
|
|
pPopup->ImplGetFloatingWindow()->StopExecute();
|
|
pPopup->ImplGetFloatingWindow()->doShutdown();
|
|
pPopup->pWindow.disposeAndClear();
|
|
|
|
PaintImmediately();
|
|
}
|
|
}
|
|
|
|
void MenuFloatingWindow::EndExecute()
|
|
{
|
|
Menu* pStart = pMenu ? pMenu->ImplGetStartMenu() : nullptr;
|
|
|
|
// if started elsewhere, cleanup there as well
|
|
MenuFloatingWindow* pCleanUpFrom = this;
|
|
MenuFloatingWindow* pWin = this;
|
|
while (pWin && !pWin->bInExecute &&
|
|
pWin->pMenu->pStartedFrom && !pWin->pMenu->pStartedFrom->IsMenuBar())
|
|
{
|
|
pWin = static_cast<PopupMenu*>(pWin->pMenu->pStartedFrom.get())->ImplGetFloatingWindow();
|
|
}
|
|
if ( pWin )
|
|
pCleanUpFrom = pWin;
|
|
|
|
// this window will be destroyed => store date locally...
|
|
Menu* pM = pMenu;
|
|
sal_uInt16 nItem = nHighlightedItem;
|
|
|
|
pCleanUpFrom->StopExecute();
|
|
|
|
if ( !(nItem != ITEMPOS_INVALID && pM) )
|
|
return;
|
|
|
|
MenuItemData* pItemData = pM->GetItemList()->GetDataFromPos( nItem );
|
|
if ( pItemData && !pItemData->bIsTemporary )
|
|
{
|
|
pM->nSelectedId = pItemData->nId;
|
|
pM->sSelectedIdent = pItemData->sIdent;
|
|
if (pStart)
|
|
{
|
|
pStart->nSelectedId = pItemData->nId;
|
|
pStart->sSelectedIdent = pItemData->sIdent;
|
|
}
|
|
|
|
pM->ImplSelect();
|
|
}
|
|
}
|
|
|
|
void MenuFloatingWindow::EndExecute( sal_uInt16 nId )
|
|
{
|
|
size_t nPos;
|
|
if ( pMenu && pMenu->GetItemList()->GetData( nId, nPos ) )
|
|
nHighlightedItem = nPos;
|
|
else
|
|
nHighlightedItem = ITEMPOS_INVALID;
|
|
|
|
EndExecute();
|
|
}
|
|
|
|
void MenuFloatingWindow::MouseButtonDown( const MouseEvent& rMEvt )
|
|
{
|
|
// TH creates a ToTop on this window, but the active popup
|
|
// should stay on top...
|
|
// due to focus change this would close all menus -> don't do it (#94123)
|
|
//if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup )
|
|
// pActivePopup->ImplGetFloatingWindow()->ToTop( ToTopFlags::NoGrabFocus );
|
|
|
|
ImplHighlightItem( rMEvt, true );
|
|
|
|
nMBDownPos = nHighlightedItem;
|
|
}
|
|
|
|
void MenuFloatingWindow::MouseButtonUp( const MouseEvent& rMEvt )
|
|
{
|
|
MenuItemData* pData = pMenu ? pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ) : nullptr;
|
|
// nMBDownPos store in local variable and reset immediately,
|
|
// as it will be too late after EndExecute
|
|
sal_uInt16 _nMBDownPos = nMBDownPos;
|
|
nMBDownPos = ITEMPOS_INVALID;
|
|
if ( !(pData && pData->bEnabled && ( pData->eType != MenuItemType::SEPARATOR )) )
|
|
return;
|
|
|
|
if ( !pData->pSubMenu )
|
|
{
|
|
EndExecute();
|
|
}
|
|
else if ( ( pData->nBits & MenuItemBits::POPUPSELECT ) && ( nHighlightedItem == _nMBDownPos ) && ( rMEvt.GetClicks() == 2 ) )
|
|
{
|
|
// not when clicked over the arrow...
|
|
Size aSz = GetOutputSizePixel();
|
|
tools::Long nFontHeight = GetTextHeight();
|
|
if ( rMEvt.GetPosPixel().X() < ( aSz.Width() - nFontHeight - nFontHeight/4 ) )
|
|
EndExecute();
|
|
}
|
|
|
|
}
|
|
|
|
void MenuFloatingWindow::MouseMove( const MouseEvent& rMEvt )
|
|
{
|
|
if ( !IsVisible() || rMEvt.IsSynthetic() || rMEvt.IsEnterWindow() )
|
|
return;
|
|
|
|
if ( rMEvt.IsLeaveWindow() )
|
|
{
|
|
// #102461# do not remove highlight if a popup menu is open at this position
|
|
MenuItemData* pData = pMenu ? pMenu->pItemList->GetDataFromPos( nHighlightedItem ) : nullptr;
|
|
// close popup with some delayed if we leave somewhere else
|
|
if( pActivePopup && pData && pData->pSubMenu != pActivePopup )
|
|
pActivePopup->ImplGetFloatingWindow()->aSubmenuCloseTimer.Start();
|
|
|
|
if( !pActivePopup || (pData && pData->pSubMenu != pActivePopup ) )
|
|
ChangeHighlightItem( ITEMPOS_INVALID, false );
|
|
|
|
if ( IsScrollMenu() )
|
|
ImplScroll( rMEvt.GetPosPixel() );
|
|
}
|
|
else
|
|
{
|
|
aSubmenuCloseTimer.Stop();
|
|
if( bIgnoreFirstMove )
|
|
bIgnoreFirstMove = false;
|
|
else
|
|
ImplHighlightItem( rMEvt, false );
|
|
}
|
|
}
|
|
|
|
void MenuFloatingWindow::ImplScroll( bool bUp )
|
|
{
|
|
KillActivePopup();
|
|
PaintImmediately();
|
|
|
|
if (!pMenu)
|
|
return;
|
|
|
|
Invalidate();
|
|
|
|
pMenu->ImplKillLayoutData();
|
|
|
|
if ( bScrollUp && bUp )
|
|
{
|
|
nFirstEntry = pMenu->ImplGetPrevVisible( nFirstEntry );
|
|
SAL_WARN_IF( nFirstEntry == ITEMPOS_INVALID, "vcl", "Scroll?!" );
|
|
|
|
// avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
|
|
const auto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry );
|
|
if ( pItemData )
|
|
{
|
|
tools::Long nScrollEntryHeight = pItemData->aSz.Height();
|
|
|
|
if ( !bScrollDown )
|
|
{
|
|
bScrollDown = true;
|
|
Invalidate();
|
|
}
|
|
|
|
if ( pMenu->ImplGetPrevVisible( nFirstEntry ) == ITEMPOS_INVALID )
|
|
{
|
|
bScrollUp = false;
|
|
Invalidate();
|
|
}
|
|
|
|
Scroll( 0, nScrollEntryHeight, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip );
|
|
}
|
|
}
|
|
else if ( bScrollDown && !bUp )
|
|
{
|
|
// avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686)
|
|
const auto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry );
|
|
if ( pItemData )
|
|
{
|
|
tools::Long nScrollEntryHeight = pItemData->aSz.Height();
|
|
|
|
nFirstEntry = pMenu->ImplGetNextVisible( nFirstEntry );
|
|
SAL_WARN_IF( nFirstEntry == ITEMPOS_INVALID, "vcl", "Scroll?!" );
|
|
|
|
if ( !bScrollUp )
|
|
{
|
|
bScrollUp = true;
|
|
Invalidate();
|
|
}
|
|
|
|
tools::Long nHeight = GetOutputSizePixel().Height();
|
|
sal_uInt16 nLastVisible;
|
|
static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( nHeight, nFirstEntry, &nLastVisible );
|
|
if ( pMenu->ImplGetNextVisible( nLastVisible ) == ITEMPOS_INVALID )
|
|
{
|
|
bScrollDown = false;
|
|
Invalidate();
|
|
}
|
|
|
|
Scroll( 0, -nScrollEntryHeight, ImplCalcClipRegion().GetBoundRect(), ScrollFlags::Clip );
|
|
}
|
|
}
|
|
|
|
Invalidate();
|
|
}
|
|
|
|
void MenuFloatingWindow::ImplScroll( const Point& rMousePos )
|
|
{
|
|
Size aOutSz = GetOutputSizePixel();
|
|
|
|
tools::Long nY = nScrollerHeight;
|
|
tools::Long nMouseY = rMousePos.Y();
|
|
tools::Long nDelta = 0;
|
|
|
|
if ( bScrollUp && ( nMouseY < nY ) )
|
|
{
|
|
ImplScroll( true );
|
|
nDelta = nY - nMouseY;
|
|
}
|
|
else if ( bScrollDown && ( nMouseY > ( aOutSz.Height() - nY ) ) )
|
|
{
|
|
ImplScroll( false );
|
|
nDelta = nMouseY - ( aOutSz.Height() - nY );
|
|
}
|
|
|
|
if ( !nDelta )
|
|
return;
|
|
|
|
aScrollTimer.Stop(); // if scrolled through MouseMove.
|
|
tools::Long nTimeout;
|
|
if ( nDelta < 3 )
|
|
nTimeout = 200;
|
|
else if ( nDelta < 5 )
|
|
nTimeout = 100;
|
|
else if ( nDelta < 8 )
|
|
nTimeout = 70;
|
|
else if ( nDelta < 12 )
|
|
nTimeout = 40;
|
|
else
|
|
nTimeout = 20;
|
|
aScrollTimer.SetTimeout( nTimeout );
|
|
aScrollTimer.Start();
|
|
}
|
|
void MenuFloatingWindow::ChangeHighlightItem( sal_uInt16 n, bool bStartPopupTimer )
|
|
{
|
|
// #57934# if necessary, immediately close the active, as TH's backgroundstorage works.
|
|
// #65750# we prefer to refrain from the background storage of small lines.
|
|
// otherwise the menus are difficult to operate.
|
|
// MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n );
|
|
// if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) )
|
|
// KillActivePopup();
|
|
|
|
aSubmenuCloseTimer.Stop();
|
|
if( ! pMenu )
|
|
return;
|
|
|
|
if ( nHighlightedItem != ITEMPOS_INVALID )
|
|
{
|
|
InvalidateItem(nHighlightedItem);
|
|
pMenu->ImplCallEventListeners( VclEventId::MenuDehighlight, nHighlightedItem );
|
|
}
|
|
|
|
nHighlightedItem = n;
|
|
SAL_WARN_IF( !pMenu->ImplIsVisible( nHighlightedItem ) && nHighlightedItem != ITEMPOS_INVALID, "vcl", "ChangeHighlightItem: Not visible!" );
|
|
if( nHighlightedItem != ITEMPOS_INVALID )
|
|
{
|
|
if (pMenu->pStartedFrom && !pMenu->pStartedFrom->IsMenuBar())
|
|
{
|
|
// #102461# make sure parent entry is highlighted as well
|
|
size_t i, nCount = pMenu->pStartedFrom->pItemList->size();
|
|
for(i = 0; i < nCount; i++)
|
|
{
|
|
MenuItemData* pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
|
|
if( pData && ( pData->pSubMenu == pMenu ) )
|
|
break;
|
|
}
|
|
if( i < nCount )
|
|
{
|
|
MenuFloatingWindow* pPWin = static_cast<MenuFloatingWindow*>(pMenu->pStartedFrom->ImplGetWindow());
|
|
if( pPWin && pPWin->nHighlightedItem != i )
|
|
{
|
|
pPWin->InvalidateItem(i);
|
|
pPWin->nHighlightedItem = i;
|
|
}
|
|
}
|
|
}
|
|
InvalidateItem(nHighlightedItem);
|
|
pMenu->ImplCallHighlight( nHighlightedItem );
|
|
}
|
|
else
|
|
{
|
|
pMenu->nSelectedId = 0;
|
|
pMenu->sSelectedIdent.clear();
|
|
}
|
|
|
|
if ( bStartPopupTimer )
|
|
{
|
|
// #102438# Menu items are not selectable
|
|
// If a menu item is selected by an AT-tool via the XAccessibleAction, XAccessibleValue
|
|
// or XAccessibleSelection interface, and the parent popup menus are not executed yet,
|
|
// the parent popup menus must be executed SYNCHRONOUSLY, before the menu item is selected.
|
|
if ( GetSettings().GetMouseSettings().GetMenuDelay() )
|
|
aHighlightChangedTimer.Start();
|
|
else
|
|
HighlightChanged( &aHighlightChangedTimer );
|
|
}
|
|
}
|
|
|
|
/// Calculate the initial vertical pixel offset of the first item.
|
|
/// May be negative for scrolled windows.
|
|
tools::Long MenuFloatingWindow::GetInitialItemY(tools::Long *pStartY) const
|
|
{
|
|
tools::Long nStartY = ImplGetStartY();
|
|
if (pStartY)
|
|
*pStartY = nStartY;
|
|
return nScrollerHeight + nStartY +
|
|
ImplGetSVData()->maNWFData.mnMenuFormatBorderY;
|
|
}
|
|
|
|
/// Emit an Invalidate just for this item's area
|
|
void MenuFloatingWindow::InvalidateItem(sal_uInt16 nPos)
|
|
{
|
|
if (!pMenu)
|
|
return;
|
|
|
|
tools::Long nY = GetInitialItemY();
|
|
size_t nCount = pMenu->pItemList->size();
|
|
for (size_t n = 0; n < nCount; n++)
|
|
{
|
|
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
|
|
tools::Long nHeight = pData->aSz.Height();
|
|
if (n == nPos)
|
|
{
|
|
Size aWidth( GetSizePixel() );
|
|
tools::Rectangle aRect(Point(0, nY), Size(aWidth.Width(), nHeight));
|
|
Invalidate( aRect );
|
|
}
|
|
nY += nHeight;
|
|
}
|
|
}
|
|
|
|
void MenuFloatingWindow::RenderHighlightItem(vcl::RenderContext& rRenderContext, sal_uInt16 nPos)
|
|
{
|
|
if (!pMenu)
|
|
return;
|
|
|
|
Size aSz(GetOutputSizePixel());
|
|
|
|
tools::Long nX = 0;
|
|
tools::Long nStartY;
|
|
tools::Long nY = GetInitialItemY(&nStartY);
|
|
|
|
int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX;
|
|
|
|
size_t nCount = pMenu->pItemList->size();
|
|
for (size_t n = 0; n < nCount; n++)
|
|
{
|
|
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
|
|
if (n == nPos)
|
|
{
|
|
SAL_WARN_IF(!pMenu->ImplIsVisible(n), "vcl", "Highlight: Item not visible!");
|
|
if (pData->eType != MenuItemType::SEPARATOR)
|
|
{
|
|
bool bRestoreLineColor = false;
|
|
Color oldLineColor;
|
|
bool bDrawItemRect = true;
|
|
|
|
tools::Rectangle aItemRect(Point(nX + nOuterSpaceX, nY), Size(aSz.Width() - 2 * nOuterSpaceX, pData->aSz.Height()));
|
|
if (pData->nBits & MenuItemBits::POPUPSELECT)
|
|
{
|
|
tools::Long nFontHeight = GetTextHeight();
|
|
aItemRect.AdjustRight( -(nFontHeight + nFontHeight / 4) );
|
|
}
|
|
|
|
if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
|
|
{
|
|
Size aPxSize(GetOutputSizePixel());
|
|
rRenderContext.Push(vcl::PushFlags::CLIPREGION);
|
|
rRenderContext.IntersectClipRegion(tools::Rectangle(Point(nX, nY), Size(aSz.Width(), pData->aSz.Height())));
|
|
tools::Rectangle aCtrlRect(Point(nX, 0), Size(aPxSize.Width()-nX, aPxSize.Height()));
|
|
MenupopupValue aVal(pMenu->nTextPos-GUTTERBORDER, aItemRect);
|
|
rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire,
|
|
aCtrlRect, ControlState::ENABLED, aVal, OUString());
|
|
if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::MenuItem))
|
|
{
|
|
bDrawItemRect = false;
|
|
if (!rRenderContext.DrawNativeControl(ControlType::MenuPopup, ControlPart::MenuItem, aItemRect,
|
|
ControlState::SELECTED | (pData->bEnabled
|
|
? ControlState::ENABLED
|
|
: ControlState::NONE),
|
|
aVal, OUString()))
|
|
{
|
|
bDrawItemRect = true;
|
|
}
|
|
}
|
|
else
|
|
bDrawItemRect = true;
|
|
rRenderContext.Pop();
|
|
}
|
|
if (bDrawItemRect)
|
|
{
|
|
if (pData->bEnabled)
|
|
rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
|
|
else
|
|
{
|
|
rRenderContext.SetFillColor();
|
|
oldLineColor = rRenderContext.GetLineColor();
|
|
rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
|
|
bRestoreLineColor = true;
|
|
}
|
|
|
|
rRenderContext.DrawRect(aItemRect);
|
|
}
|
|
pMenu->ImplPaint(rRenderContext, GetOutputSizePixel(), nScrollerHeight, nStartY, pData, true/*bHighlight*/);
|
|
if (bRestoreLineColor)
|
|
rRenderContext.SetLineColor(oldLineColor);
|
|
}
|
|
return;
|
|
}
|
|
|
|
nY += pData->aSz.Height();
|
|
}
|
|
}
|
|
|
|
tools::Rectangle MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos ) const
|
|
{
|
|
if( ! pMenu )
|
|
return tools::Rectangle();
|
|
|
|
tools::Rectangle aRect;
|
|
Size aSz = GetOutputSizePixel();
|
|
tools::Long nStartY = ImplGetStartY();
|
|
tools::Long nY = nScrollerHeight+nStartY;
|
|
|
|
size_t nCount = pMenu->pItemList->size();
|
|
for ( size_t n = 0; n < nCount; n++ )
|
|
{
|
|
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
|
|
if ( n == nPos )
|
|
{
|
|
SAL_WARN_IF( !pMenu->ImplIsVisible( n ), "vcl", "ImplGetItemRect: Item not visible!" );
|
|
if ( pData->eType != MenuItemType::SEPARATOR )
|
|
{
|
|
aRect = tools::Rectangle( Point( 0, nY ), Size( aSz.Width(), pData->aSz.Height() ) );
|
|
if ( pData->nBits & MenuItemBits::POPUPSELECT )
|
|
{
|
|
tools::Long nFontHeight = GetTextHeight();
|
|
aRect.AdjustRight( -(nFontHeight + nFontHeight/4) );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
nY += pData->aSz.Height();
|
|
}
|
|
return aRect;
|
|
}
|
|
|
|
void MenuFloatingWindow::ImplCursorUpDown( bool bUp, bool bHomeEnd )
|
|
{
|
|
if( ! pMenu )
|
|
return;
|
|
|
|
const StyleSettings& rSettings = GetSettings().GetStyleSettings();
|
|
|
|
sal_uInt16 n = nHighlightedItem;
|
|
if ( n == ITEMPOS_INVALID )
|
|
{
|
|
if ( bUp )
|
|
n = 0;
|
|
else
|
|
n = pMenu->GetItemCount()-1;
|
|
}
|
|
|
|
sal_uInt16 nLoop = n;
|
|
|
|
if( bHomeEnd )
|
|
{
|
|
// absolute positioning
|
|
if( bUp )
|
|
{
|
|
n = pMenu->GetItemCount();
|
|
nLoop = n-1;
|
|
}
|
|
else
|
|
{
|
|
n = sal_uInt16(-1);
|
|
nLoop = n+1;
|
|
}
|
|
}
|
|
|
|
do
|
|
{
|
|
if ( bUp )
|
|
{
|
|
if ( n )
|
|
n--;
|
|
else
|
|
if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) )
|
|
n = pMenu->GetItemCount()-1;
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
n++;
|
|
if ( n >= pMenu->GetItemCount() )
|
|
{
|
|
if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) )
|
|
n = 0;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( n );
|
|
if ( ( pData->bEnabled || !rSettings.GetSkipDisabledInMenus() )
|
|
&& ( pData->eType != MenuItemType::SEPARATOR ) && pMenu->ImplIsVisible( n ) && pMenu->ImplIsSelectable( n ) )
|
|
{
|
|
// Is selection in visible area?
|
|
if ( IsScrollMenu() )
|
|
{
|
|
ChangeHighlightItem( ITEMPOS_INVALID, false );
|
|
|
|
while ( n < nFirstEntry )
|
|
ImplScroll( true );
|
|
|
|
Size aOutSz = GetOutputSizePixel();
|
|
sal_uInt16 nLastVisible;
|
|
static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
|
|
while ( n > nLastVisible )
|
|
{
|
|
ImplScroll( false );
|
|
static_cast<PopupMenu*>(pMenu.get())->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
|
|
}
|
|
}
|
|
ChangeHighlightItem( n, false );
|
|
break;
|
|
}
|
|
} while ( n != nLoop );
|
|
}
|
|
|
|
void MenuFloatingWindow::KeyInput( const KeyEvent& rKEvent )
|
|
{
|
|
VclPtr<vcl::Window> xWindow = this;
|
|
|
|
bool autoacc = ImplGetSVData()->maNWFData.mbAutoAccel;
|
|
sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode();
|
|
bKeyInput = true;
|
|
switch ( nCode )
|
|
{
|
|
case KEY_UP:
|
|
case KEY_DOWN:
|
|
{
|
|
ImplCursorUpDown( nCode == KEY_UP );
|
|
}
|
|
break;
|
|
case KEY_END:
|
|
case KEY_HOME:
|
|
{
|
|
ImplCursorUpDown( nCode == KEY_END, true );
|
|
}
|
|
break;
|
|
case KEY_F6:
|
|
case KEY_ESCAPE:
|
|
{
|
|
// Ctrl-F6 acts like ESC here, the menu bar however will then put the focus in the document
|
|
if( nCode == KEY_F6 && !rKEvent.GetKeyCode().IsMod1() )
|
|
break;
|
|
if( pMenu )
|
|
{
|
|
if ( !pMenu->pStartedFrom )
|
|
{
|
|
StopExecute();
|
|
KillActivePopup();
|
|
}
|
|
else if (pMenu->pStartedFrom->IsMenuBar())
|
|
{
|
|
pMenu->pStartedFrom->MenuBarKeyInput(rKEvent);
|
|
}
|
|
else
|
|
{
|
|
StopExecute();
|
|
PopupMenu* pPopupMenu = static_cast<PopupMenu*>(pMenu->pStartedFrom.get());
|
|
MenuFloatingWindow* pFloat = pPopupMenu->ImplGetFloatingWindow();
|
|
pFloat->GrabFocus();
|
|
pFloat->KillActivePopup();
|
|
pPopupMenu->ImplCallHighlight(pFloat->nHighlightedItem);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_LEFT:
|
|
{
|
|
if ( pMenu && pMenu->pStartedFrom )
|
|
{
|
|
StopExecute();
|
|
if (pMenu->pStartedFrom->IsMenuBar())
|
|
{
|
|
pMenu->pStartedFrom->MenuBarKeyInput(rKEvent);
|
|
}
|
|
else
|
|
{
|
|
MenuFloatingWindow* pFloat = static_cast<PopupMenu*>(pMenu->pStartedFrom.get())->ImplGetFloatingWindow();
|
|
pFloat->GrabFocus();
|
|
pFloat->KillActivePopup();
|
|
sal_uInt16 highlightItem = pFloat->GetHighlightedItem();
|
|
pFloat->ChangeHighlightItem(highlightItem, false);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_RIGHT:
|
|
{
|
|
if( pMenu )
|
|
{
|
|
bool bDone = false;
|
|
if ( nHighlightedItem != ITEMPOS_INVALID )
|
|
{
|
|
MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
|
|
if ( pData && pData->pSubMenu )
|
|
{
|
|
HighlightChanged( nullptr );
|
|
bDone = true;
|
|
}
|
|
}
|
|
if ( !bDone )
|
|
{
|
|
Menu* pStart = pMenu->ImplGetStartMenu();
|
|
if (pStart && pStart->IsMenuBar())
|
|
{
|
|
// Forward...
|
|
pStart->ImplGetWindow()->KeyInput( rKEvent );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_RETURN:
|
|
{
|
|
if( pMenu )
|
|
{
|
|
MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
|
|
if ( pData && pData->bEnabled )
|
|
{
|
|
if ( pData->pSubMenu )
|
|
HighlightChanged( nullptr );
|
|
else
|
|
EndExecute();
|
|
}
|
|
else
|
|
StopExecute();
|
|
}
|
|
}
|
|
break;
|
|
case KEY_MENU:
|
|
{
|
|
if( pMenu )
|
|
{
|
|
Menu* pStart = pMenu->ImplGetStartMenu();
|
|
if (pStart && pStart->IsMenuBar())
|
|
{
|
|
// Forward...
|
|
pStart->ImplGetWindow()->KeyInput( rKEvent );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
sal_Unicode nCharCode = rKEvent.GetCharCode();
|
|
size_t nPos = 0;
|
|
size_t nDuplicates = 0;
|
|
MenuItemData* pData = (nCharCode && pMenu) ?
|
|
pMenu->GetItemList()->SearchItem(nCharCode, rKEvent.GetKeyCode(), nPos, nDuplicates, nHighlightedItem) : nullptr;
|
|
if (pData)
|
|
{
|
|
if ( pData->pSubMenu || nDuplicates > 1 )
|
|
{
|
|
ChangeHighlightItem( nPos, false );
|
|
HighlightChanged( nullptr );
|
|
}
|
|
else
|
|
{
|
|
nHighlightedItem = nPos;
|
|
EndExecute();
|
|
}
|
|
}
|
|
else
|
|
FloatingWindow::KeyInput( rKEvent );
|
|
}
|
|
}
|
|
|
|
if (pMenu && pMenu->pStartedFrom && pMenu->pStartedFrom->IsMenuBar())
|
|
{
|
|
MenuBar *pMenuBar = static_cast<MenuBar*>(pMenu->pStartedFrom.get());
|
|
const bool bShowAccels = !autoacc || nCode != KEY_ESCAPE;
|
|
if (pMenuBar->getMenuBarWindow()->GetMBWMenuKey() != bShowAccels)
|
|
{
|
|
pMenuBar->getMenuBarWindow()->SetMBWMenuKey(bShowAccels);
|
|
pMenuBar->getMenuBarWindow()->SetMBWHideAccel(!bShowAccels);
|
|
if (autoacc)
|
|
Invalidate(InvalidateFlags::Update);
|
|
}
|
|
}
|
|
|
|
// #105474# check if menu window was not destroyed
|
|
if ( !xWindow->isDisposed() )
|
|
{
|
|
bKeyInput = false;
|
|
}
|
|
}
|
|
|
|
void MenuFloatingWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rPaintRect)
|
|
{
|
|
if (!pMenu)
|
|
return;
|
|
|
|
// Set the clip before the buffering starts: rPaintRect may be larger than the current clip,
|
|
// this way the buffer -> render context copy happens with this clip.
|
|
rRenderContext.Push(vcl::PushFlags::CLIPREGION);
|
|
rRenderContext.SetClipRegion(vcl::Region(rPaintRect));
|
|
|
|
// Make sure that all actual rendering happens in one go to avoid flicker.
|
|
vcl::BufferDevice pBuffer(this, rRenderContext);
|
|
|
|
if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
|
|
{
|
|
pBuffer->SetClipRegion();
|
|
tools::Long nX = 0;
|
|
Size aPxSize(GetOutputSizePixel());
|
|
aPxSize.AdjustWidth( -nX );
|
|
ImplControlValue aVal(pMenu->nTextPos - GUTTERBORDER);
|
|
pBuffer->DrawNativeControl(ControlType::MenuPopup, ControlPart::Entire,
|
|
tools::Rectangle(Point(nX, 0), aPxSize), ControlState::ENABLED,
|
|
aVal, OUString());
|
|
InitMenuClipRegion(*pBuffer);
|
|
}
|
|
if (IsScrollMenu())
|
|
{
|
|
ImplDrawScroller(*pBuffer, true);
|
|
ImplDrawScroller(*pBuffer, false);
|
|
}
|
|
pBuffer->SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuColor());
|
|
pMenu->ImplPaint(*pBuffer, GetOutputSizePixel(), nScrollerHeight, ImplGetStartY());
|
|
if (nHighlightedItem != ITEMPOS_INVALID)
|
|
RenderHighlightItem(*pBuffer, nHighlightedItem);
|
|
|
|
pBuffer.Dispose();
|
|
rRenderContext.Pop();
|
|
}
|
|
|
|
void MenuFloatingWindow::ImplDrawScroller(vcl::RenderContext& rRenderContext, bool bUp)
|
|
{
|
|
if (!pMenu)
|
|
return;
|
|
|
|
rRenderContext.SetClipRegion();
|
|
|
|
Size aOutSz(GetOutputSizePixel());
|
|
tools::Long nY = bUp ? 0 : (aOutSz.Height() - nScrollerHeight);
|
|
tools::Long nX = 0;
|
|
tools::Rectangle aRect(Point(nX, nY), Size(aOutSz.Width() - nX, nScrollerHeight));
|
|
|
|
DecorationView aDecoView(&rRenderContext);
|
|
SymbolType eSymbol = bUp ? SymbolType::SPIN_UP : SymbolType::SPIN_DOWN;
|
|
|
|
DrawSymbolFlags nStyle = DrawSymbolFlags::NONE;
|
|
if ((bUp && !bScrollUp) || (!bUp && !bScrollDown))
|
|
nStyle |= DrawSymbolFlags::Disable;
|
|
|
|
aDecoView.DrawSymbol(aRect, eSymbol, rRenderContext.GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle);
|
|
|
|
InitMenuClipRegion(rRenderContext);
|
|
}
|
|
|
|
void MenuFloatingWindow::RequestHelp( const HelpEvent& rHEvt )
|
|
{
|
|
sal_uInt16 nId = nHighlightedItem;
|
|
Menu* pM = pMenu;
|
|
vcl::Window* pW = this;
|
|
|
|
// #102618# Get item rect before destroying the window in EndExecute() call
|
|
tools::Rectangle aHighlightRect( ImplGetItemRect( nHighlightedItem ) );
|
|
|
|
if ( rHEvt.GetMode() & HelpEventMode::CONTEXT )
|
|
{
|
|
nHighlightedItem = ITEMPOS_INVALID;
|
|
EndExecute();
|
|
pW = nullptr;
|
|
}
|
|
|
|
if( !ImplHandleHelpEvent( pW, pM, nId, rHEvt, aHighlightRect ) )
|
|
Window::RequestHelp( rHEvt );
|
|
}
|
|
|
|
void MenuFloatingWindow::StateChanged( StateChangedType nType )
|
|
{
|
|
FloatingWindow::StateChanged( nType );
|
|
|
|
if ( ( nType == StateChangedType::ControlForeground ) || ( nType == StateChangedType::ControlBackground ) )
|
|
{
|
|
ApplySettings(*GetOutDev());
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void MenuFloatingWindow::DataChanged( const DataChangedEvent& rDCEvt )
|
|
{
|
|
FloatingWindow::DataChanged( rDCEvt );
|
|
|
|
if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
|
|
(rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
|
|
((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
|
|
(rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
|
|
{
|
|
ApplySettings(*GetOutDev());
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
void MenuFloatingWindow::Command( const CommandEvent& rCEvt )
|
|
{
|
|
if ( rCEvt.GetCommand() == CommandEventId::Wheel )
|
|
{
|
|
const CommandWheelData* pData = rCEvt.GetWheelData();
|
|
if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
|
|
{
|
|
ImplScroll( pData->GetDelta() > 0 );
|
|
MouseMove( MouseEvent( GetPointerPosPixel(), 0 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
css::uno::Reference<css::accessibility::XAccessible> MenuFloatingWindow::CreateAccessible()
|
|
{
|
|
css::uno::Reference<css::accessibility::XAccessible> xAcc;
|
|
|
|
if (pMenu && !pMenu->pStartedFrom)
|
|
xAcc = pMenu->GetAccessible();
|
|
|
|
return xAcc;
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|