/* -*- 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 .
*/
// #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->GetWindow()); if (pPWin)
pPWin->InvalidateItem(i);
}
}
// free the reference to the accessible component
SetAccessible({});
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 ) );
if (rRenderContext.IsNativeControlSupported(ControlType::MenuPopup, ControlPart::Entire))
{
rRenderContext.SetBackground(); // background will be drawn by NWF
} else
rRenderContext.SetBackground(Wallpaper(rStyleSettings.GetMenuColor()));
/// 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;
}
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);
}
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->GetWindow() )
pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this );
}
void MenuFloatingWindow::Start()
{ if (bInExecute) return;
bInExecute = true; if (GetParent())
GetParent()->IncModalCount();
}
bool MenuFloatingWindow::MenuInHierarchyHasFocus() const
{ if (HasChildPathFocus()) returntrue;
PopupMenu* pSub = GetActivePopup(); if (!pSub) returnfalse; 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);
}
void MenuFloatingWindow::KillActivePopup()
{ if (!pActivePopup) return;
if (MenuFloatingWindow* pFloatWin = pActivePopup->ImplGetFloatingWindow())
{ if (pFloatWin->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->GetWindow())
{
pPopup->ImplGetFloatingWindow()->StopExecute();
pPopup->ImplGetFloatingWindow()->doShutdown();
pPopup->m_pWindow.disposeAndClear();
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();
} elseif ( ( 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();
}
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();
// avoid crash if somehow menu got disposed, and MenuItemList is empty (workaround for tdf#104686) constauto pItemData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry ); if ( pItemData )
{
tools::Long nScrollEntryHeight = pItemData->aSz.Height();
if ( !bScrollDown )
{
bScrollDown = true;
Invalidate();
}
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->GetWindow()); 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;
// #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);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.