In the paints, we must use the size of the Window for the computations, not of the RenderContext - the RenderContext can be much bigger than the Window in the double-buffering case. Fixes for example the list boxes, and many others. Change-Id: I4c7607569f88b2d097587140858d0862e54b5ea6
1628 lines
48 KiB
C++
1628 lines
48 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 <com/sun/star/accessibility/AccessibleEventId.hpp>
|
|
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
|
|
#include <comphelper/processfactory.hxx>
|
|
|
|
#include <vcl/dockwin.hxx>
|
|
#include <vcl/decoview.hxx>
|
|
#include <vcl/image.hxx>
|
|
#include <vcl/taskpanelist.hxx>
|
|
#include <vcl/toolbox.hxx>
|
|
#include <vcl/settings.hxx>
|
|
|
|
#include <svtools/valueset.hxx>
|
|
#include <svtools/toolbarmenu.hxx>
|
|
#include "toolbarmenuimp.hxx"
|
|
|
|
using namespace ::com::sun::star::uno;
|
|
using namespace ::com::sun::star::lang;
|
|
using namespace ::com::sun::star::frame;
|
|
using namespace ::com::sun::star::accessibility;
|
|
|
|
namespace svtools {
|
|
|
|
static vcl::Window* GetTopMostParentSystemWindow( vcl::Window* pWindow )
|
|
{
|
|
OSL_ASSERT( pWindow );
|
|
if ( pWindow )
|
|
{
|
|
// ->manually search topmost system window
|
|
// required because their might be another system window between this and the top window
|
|
pWindow = pWindow->GetParent();
|
|
SystemWindow* pTopMostSysWin = NULL;
|
|
while ( pWindow )
|
|
{
|
|
if ( pWindow->IsSystemWindow() )
|
|
pTopMostSysWin = static_cast<SystemWindow*>(pWindow);
|
|
pWindow = pWindow->GetParent();
|
|
}
|
|
pWindow = pTopMostSysWin;
|
|
OSL_ASSERT( pWindow );
|
|
return pWindow;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenuEntry::init( int nEntryId, MenuItemBits nBits )
|
|
{
|
|
mnEntryId = nEntryId;
|
|
mnBits = nBits;
|
|
|
|
mbHasText = false;
|
|
mbHasImage = false;
|
|
mbChecked = false;
|
|
mbEnabled = true;
|
|
|
|
mpControl = NULL;
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenuEntry::ToolbarMenuEntry( ToolbarMenu& rMenu, int nEntryId, const OUString& rText, MenuItemBits nBits )
|
|
: mrMenu( rMenu )
|
|
{
|
|
init( nEntryId, nBits );
|
|
|
|
maText = rText;
|
|
mbHasText = true;
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenuEntry::ToolbarMenuEntry( ToolbarMenu& rMenu, int nEntryId, const Image& rImage, const OUString& rText, MenuItemBits nBits )
|
|
: mrMenu( rMenu )
|
|
{
|
|
init( nEntryId, nBits );
|
|
|
|
maText = rText;
|
|
mbHasText = true;
|
|
|
|
maImage = rImage;
|
|
mbHasImage = true;
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenuEntry::ToolbarMenuEntry( ToolbarMenu& rMenu, int nEntryId, Control* pControl, MenuItemBits nBits )
|
|
: mrMenu( rMenu )
|
|
{
|
|
init( nEntryId, nBits );
|
|
|
|
if( pControl )
|
|
{
|
|
mpControl = pControl;
|
|
mpControl->Show();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenuEntry::~ToolbarMenuEntry()
|
|
{
|
|
if( mxAccContext.is() )
|
|
{
|
|
Reference< XComponent > xComponent( mxAccContext, UNO_QUERY );
|
|
if( xComponent.is() )
|
|
xComponent->dispose();
|
|
mxAccContext.clear();
|
|
}
|
|
mpControl.disposeAndClear();
|
|
}
|
|
|
|
|
|
|
|
const Reference< XAccessibleContext >& ToolbarMenuEntry::GetAccessible( bool bCreate /* = false */ )
|
|
{
|
|
if( !mxAccContext.is() && bCreate )
|
|
{
|
|
if( mpControl )
|
|
{
|
|
mxAccContext = Reference< XAccessibleContext >( mpControl->GetAccessible( true ), UNO_QUERY );
|
|
}
|
|
else
|
|
{
|
|
mxAccContext = Reference< XAccessibleContext >( new ToolbarMenuEntryAcc( this ) );
|
|
}
|
|
}
|
|
|
|
return mxAccContext;
|
|
}
|
|
|
|
|
|
|
|
sal_Int32 ToolbarMenuEntry::getAccessibleChildCount() throw (RuntimeException)
|
|
{
|
|
if( mpControl )
|
|
{
|
|
const Reference< XAccessibleContext >& xContext = GetAccessible( true );
|
|
if( xContext.is() )
|
|
{
|
|
return xContext->getAccessibleChildCount();
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
Reference< XAccessible > ToolbarMenuEntry::getAccessibleChild( sal_Int32 index ) throw (IndexOutOfBoundsException, RuntimeException)
|
|
{
|
|
const Reference< XAccessibleContext >& xContext = GetAccessible( true );
|
|
if( mpControl )
|
|
{
|
|
if( xContext.is() )
|
|
{
|
|
return xContext->getAccessibleChild(index);
|
|
}
|
|
}
|
|
else if( index == 0 )
|
|
{
|
|
Reference< XAccessible > xRet( xContext, UNO_QUERY );
|
|
if( xRet.is() )
|
|
return xRet;
|
|
}
|
|
|
|
throw IndexOutOfBoundsException();
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenu_Impl::ToolbarMenu_Impl( ToolbarMenu& rMenu, const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame )
|
|
: mrMenu( rMenu )
|
|
, mxFrame( xFrame )
|
|
, mnCheckPos(0)
|
|
, mnImagePos(0)
|
|
, mnTextPos(0)
|
|
, mnHighlightedEntry(-1)
|
|
, mnSelectedEntry(-1)
|
|
, mnLastColumn(0)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenu_Impl::~ToolbarMenu_Impl()
|
|
{
|
|
setAccessible( 0 );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu_Impl::setAccessible( ToolbarMenuAcc* pAccessible )
|
|
{
|
|
if( mxAccessible.get() != pAccessible )
|
|
{
|
|
if( mxAccessible.is() )
|
|
mxAccessible->dispose();
|
|
|
|
mxAccessible.set( pAccessible );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu_Impl::fireAccessibleEvent( short nEventId, const ::com::sun::star::uno::Any& rOldValue, const ::com::sun::star::uno::Any& rNewValue )
|
|
{
|
|
if( mxAccessible.is() )
|
|
mxAccessible->FireAccessibleEvent( nEventId, rOldValue, rNewValue );
|
|
}
|
|
|
|
|
|
|
|
bool ToolbarMenu_Impl::hasAccessibleListeners()
|
|
{
|
|
return( mxAccessible.is() && mxAccessible->HasAccessibleListeners() );
|
|
}
|
|
|
|
|
|
|
|
sal_Int32 ToolbarMenu_Impl::getAccessibleChildCount() throw (RuntimeException)
|
|
{
|
|
sal_Int32 nCount = 0;
|
|
const int nEntryCount = maEntryVector.size();
|
|
for( int nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = maEntryVector[nEntry];
|
|
if( pEntry )
|
|
{
|
|
if( pEntry->mpControl )
|
|
{
|
|
nCount += pEntry->getAccessibleChildCount();
|
|
}
|
|
else
|
|
{
|
|
nCount += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nCount;
|
|
}
|
|
|
|
|
|
|
|
Reference< XAccessible > ToolbarMenu_Impl::getAccessibleChild( sal_Int32 index ) throw (IndexOutOfBoundsException, RuntimeException)
|
|
{
|
|
const int nEntryCount = maEntryVector.size();
|
|
for( int nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = maEntryVector[nEntry];
|
|
if( pEntry )
|
|
{
|
|
const sal_Int32 nCount = pEntry->getAccessibleChildCount();
|
|
if( index < nCount )
|
|
{
|
|
return pEntry->getAccessibleChild( index );
|
|
}
|
|
index -= nCount;
|
|
}
|
|
}
|
|
|
|
throw IndexOutOfBoundsException();
|
|
}
|
|
|
|
|
|
|
|
Reference< XAccessible > ToolbarMenu_Impl::getAccessibleChild( Control* pControl, sal_Int32 childIndex ) throw (IndexOutOfBoundsException, RuntimeException)
|
|
{
|
|
const int nEntryCount = maEntryVector.size();
|
|
for( int nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = maEntryVector[nEntry];
|
|
if( pEntry && (pEntry->mpControl.get() == pControl) )
|
|
{
|
|
return pEntry->getAccessibleChild( childIndex );
|
|
}
|
|
}
|
|
|
|
throw IndexOutOfBoundsException();
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu_Impl::selectAccessibleChild( sal_Int32 nChildIndex ) throw (IndexOutOfBoundsException, RuntimeException)
|
|
{
|
|
const int nEntryCount = maEntryVector.size();
|
|
for( int nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = maEntryVector[nEntry];
|
|
if( pEntry )
|
|
{
|
|
const sal_Int32 nCount = pEntry->getAccessibleChildCount();
|
|
if( nChildIndex < nCount )
|
|
{
|
|
if( pEntry->mpControl )
|
|
{
|
|
Reference< XAccessibleSelection > xSel( pEntry->GetAccessible(true), UNO_QUERY_THROW );
|
|
xSel->selectAccessibleChild(nChildIndex);
|
|
}
|
|
else if( pEntry->mnEntryId != TITLE_ID )
|
|
{
|
|
mrMenu.implSelectEntry( nEntry );
|
|
}
|
|
return;
|
|
}
|
|
nChildIndex -= nCount;
|
|
}
|
|
}
|
|
|
|
throw IndexOutOfBoundsException();
|
|
}
|
|
|
|
|
|
|
|
bool ToolbarMenu_Impl::isAccessibleChildSelected( sal_Int32 nChildIndex ) throw (IndexOutOfBoundsException, RuntimeException)
|
|
{
|
|
const int nEntryCount = maEntryVector.size();
|
|
for( int nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = maEntryVector[nEntry];
|
|
if( pEntry )
|
|
{
|
|
const sal_Int32 nCount = pEntry->getAccessibleChildCount();
|
|
if( nChildIndex < nCount )
|
|
{
|
|
if( mnHighlightedEntry == nEntry )
|
|
{
|
|
if( pEntry->mpControl )
|
|
{
|
|
Reference< XAccessibleSelection > xSel( pEntry->GetAccessible(true), UNO_QUERY_THROW );
|
|
xSel->isAccessibleChildSelected(nChildIndex);
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
nChildIndex -= nCount;
|
|
}
|
|
}
|
|
|
|
throw IndexOutOfBoundsException();
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu_Impl::clearAccessibleSelection()
|
|
{
|
|
if( mnHighlightedEntry != -1 )
|
|
{
|
|
mrMenu.Invalidate();
|
|
mnHighlightedEntry = -1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void ToolbarMenu_Impl::notifyHighlightedEntry()
|
|
{
|
|
if( hasAccessibleListeners() )
|
|
{
|
|
ToolbarMenuEntry* pEntry = implGetEntry( mnHighlightedEntry );
|
|
if( pEntry && pEntry->mbEnabled && (pEntry->mnEntryId != TITLE_ID) )
|
|
{
|
|
Any aNew;
|
|
Any aOld( mxOldSelection );
|
|
if( pEntry->mpControl )
|
|
{
|
|
sal_Int32 nChildIndex = 0;
|
|
// todo: if other controls than ValueSet are allowed, adapt this code
|
|
ValueSet* pValueSet = dynamic_cast< ValueSet* >( pEntry->mpControl.get() );
|
|
if( pValueSet )
|
|
nChildIndex = static_cast< sal_Int32 >( pValueSet->GetItemPos( pValueSet->GetSelectItemId() ) );
|
|
|
|
if( (nChildIndex >= pEntry->getAccessibleChildCount()) || (nChildIndex < 0) )
|
|
return;
|
|
|
|
aNew <<= getAccessibleChild( pEntry->mpControl, nChildIndex );
|
|
}
|
|
else
|
|
{
|
|
aNew <<= pEntry->GetAccessible(true);
|
|
}
|
|
|
|
fireAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, aOld, aNew );
|
|
fireAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, aOld, aNew );
|
|
fireAccessibleEvent( AccessibleEventId::STATE_CHANGED, Any(), Any( AccessibleStateType::FOCUSED ) );
|
|
aNew >>= mxOldSelection;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenuEntry* ToolbarMenu_Impl::implGetEntry( int nEntry ) const
|
|
{
|
|
if( (nEntry < 0) || (nEntry >= (int)maEntryVector.size() ) )
|
|
return NULL;
|
|
|
|
return maEntryVector[nEntry];
|
|
}
|
|
|
|
|
|
|
|
|
|
IMPL_LINK( ToolbarMenu, HighlightHdl, Control *, pControl )
|
|
{
|
|
(void)pControl;
|
|
mpImpl->notifyHighlightedEntry();
|
|
return 0;
|
|
}
|
|
|
|
ToolbarMenu::ToolbarMenu( const Reference< XFrame >& rFrame, vcl::Window* pParentWindow, WinBits nBits )
|
|
: DockingWindow(pParentWindow, nBits)
|
|
{
|
|
implInit(rFrame);
|
|
}
|
|
|
|
void ToolbarMenu::implInit(const Reference< XFrame >& rFrame)
|
|
{
|
|
mpImpl = new ToolbarMenu_Impl( *this, rFrame );
|
|
|
|
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
|
|
SetControlBackground( rStyleSettings.GetMenuColor() );
|
|
|
|
initWindow();
|
|
|
|
vcl::Window* pWindow = GetTopMostParentSystemWindow( this );
|
|
if ( pWindow )
|
|
static_cast<SystemWindow*>(pWindow)->GetTaskPaneList()->AddWindow( this );
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenu::~ToolbarMenu()
|
|
{
|
|
disposeOnce();
|
|
}
|
|
|
|
void ToolbarMenu::dispose()
|
|
{
|
|
vcl::Window* pWindow = GetTopMostParentSystemWindow( this );
|
|
if ( pWindow )
|
|
static_cast<SystemWindow*>(pWindow)->GetTaskPaneList()->RemoveWindow( this );
|
|
|
|
if ( mpImpl->mxStatusListener.is() )
|
|
{
|
|
mpImpl->mxStatusListener->dispose();
|
|
mpImpl->mxStatusListener.clear();
|
|
}
|
|
|
|
// delete all menu entries
|
|
const int nEntryCount = mpImpl->maEntryVector.size();
|
|
int nEntry;
|
|
for( nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
delete mpImpl->maEntryVector[nEntry];
|
|
}
|
|
|
|
delete mpImpl;
|
|
mpImpl = NULL;
|
|
|
|
DockingWindow::dispose();
|
|
}
|
|
|
|
|
|
|
|
int ToolbarMenu::getSelectedEntryId() const
|
|
{
|
|
ToolbarMenuEntry* pEntry = implGetEntry( mpImpl->mnSelectedEntry );
|
|
return pEntry ? pEntry->mnEntryId : -1;
|
|
}
|
|
|
|
|
|
|
|
int ToolbarMenu::getHighlightedEntryId() const
|
|
{
|
|
ToolbarMenuEntry* pEntry = implGetEntry( mpImpl->mnHighlightedEntry );
|
|
return pEntry ? pEntry->mnEntryId : -1;
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::checkEntry( int nEntryId, bool bChecked )
|
|
{
|
|
ToolbarMenuEntry* pEntry = implSearchEntry( nEntryId );
|
|
if( pEntry && pEntry->mbChecked != bChecked )
|
|
{
|
|
pEntry->mbChecked = bChecked;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::enableEntry( int nEntryId, bool bEnable )
|
|
{
|
|
ToolbarMenuEntry* pEntry = implSearchEntry( nEntryId );
|
|
if( pEntry && pEntry->mbEnabled != bEnable )
|
|
{
|
|
pEntry->mbEnabled = bEnable;
|
|
if( pEntry->mpControl )
|
|
{
|
|
pEntry->mpControl->Enable( bEnable );
|
|
|
|
// hack for the valueset to make it paint itself anew
|
|
pEntry->mpControl->Resize();
|
|
}
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::setEntryText( int nEntryId, const OUString& rStr )
|
|
{
|
|
ToolbarMenuEntry* pEntry = implSearchEntry( nEntryId );
|
|
if( pEntry && pEntry->maText != rStr )
|
|
{
|
|
pEntry->maText = rStr;
|
|
mpImpl->maSize = implCalcSize();
|
|
if( IsVisible() )
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::setEntryImage( int nEntryId, const Image& rImage )
|
|
{
|
|
ToolbarMenuEntry* pEntry = implSearchEntry( nEntryId );
|
|
if( pEntry && pEntry->maImage != rImage )
|
|
{
|
|
pEntry->maImage = rImage;
|
|
mpImpl->maSize = implCalcSize();
|
|
if( IsVisible() )
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::initWindow()
|
|
{
|
|
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
|
|
|
|
// FIXME RenderContext
|
|
SetPointFont(*this, rStyleSettings.GetMenuFont());
|
|
SetBackground(Wallpaper(GetControlBackground()));
|
|
SetTextColor(rStyleSettings.GetMenuTextColor());
|
|
SetTextFillColor();
|
|
SetLineColor();
|
|
|
|
mpImpl->maSize = implCalcSize();
|
|
}
|
|
|
|
|
|
|
|
static long ImplGetNativeCheckAndRadioSize(vcl::RenderContext& rRenderContext, long& rCheckHeight, long& rRadioHeight, long &rMaxWidth )
|
|
{
|
|
rMaxWidth = rCheckHeight = rRadioHeight = 0;
|
|
|
|
ImplControlValue aVal;
|
|
Rectangle aNativeBounds;
|
|
Rectangle aNativeContent;
|
|
Point tmp( 0, 0 );
|
|
Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_ITEM_CHECK_MARK))
|
|
{
|
|
if (rRenderContext.GetNativeControlRegion(ControlType(CTRL_MENU_POPUP), ControlPart(PART_MENU_ITEM_CHECK_MARK),
|
|
aCtrlRegion, ControlState(ControlState::ENABLED), aVal, OUString(),
|
|
aNativeBounds, aNativeContent)
|
|
)
|
|
{
|
|
rCheckHeight = aNativeBounds.GetHeight();
|
|
rMaxWidth = aNativeContent.GetWidth();
|
|
}
|
|
}
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_ITEM_RADIO_MARK))
|
|
{
|
|
if (rRenderContext.GetNativeControlRegion(ControlType(CTRL_MENU_POPUP), ControlPart(PART_MENU_ITEM_RADIO_MARK),
|
|
aCtrlRegion, ControlState(ControlState::ENABLED), aVal, OUString(),
|
|
aNativeBounds, aNativeContent)
|
|
)
|
|
{
|
|
rRadioHeight = aNativeBounds.GetHeight();
|
|
rMaxWidth = std::max (rMaxWidth, aNativeContent.GetWidth());
|
|
}
|
|
}
|
|
return (rCheckHeight > rRadioHeight) ? rCheckHeight : rRadioHeight;
|
|
}
|
|
|
|
#define gfxExtra 7
|
|
|
|
Size ToolbarMenu::implCalcSize()
|
|
{
|
|
const long nFontHeight = GetTextHeight();
|
|
long nExtra = nFontHeight/4;
|
|
|
|
Size aSz;
|
|
Size aMaxImgSz;
|
|
long nMaxTextWidth = 0;
|
|
long nMinMenuItemHeight = nFontHeight+2;
|
|
|
|
const int nEntryCount = mpImpl->maEntryVector.size();
|
|
int nEntry;
|
|
|
|
const StyleSettings& rSettings = GetSettings().GetStyleSettings();
|
|
const bool bUseImages = rSettings.GetUseImagesInMenus();
|
|
|
|
// get maximum image size
|
|
if( bUseImages )
|
|
{
|
|
for( nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = mpImpl->maEntryVector[nEntry];
|
|
if( pEntry && pEntry->mbHasImage )
|
|
{
|
|
Size aImgSz( pEntry->maImage.GetSizePixel() );
|
|
nMinMenuItemHeight = std::max( nMinMenuItemHeight, aImgSz.Height() + 6 );
|
|
aMaxImgSz.Width() = std::max( aMaxImgSz.Width(), aImgSz.Width() );
|
|
}
|
|
}
|
|
}
|
|
|
|
mpImpl->mnCheckPos = nExtra;
|
|
mpImpl->mnImagePos = nExtra;
|
|
mpImpl->mnTextPos = mpImpl->mnImagePos + aMaxImgSz.Width();
|
|
|
|
if ( aMaxImgSz.Width() )
|
|
mpImpl->mnTextPos += std::max( nExtra, 7L );
|
|
|
|
// set heights, calc maximum width
|
|
for( nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = mpImpl->maEntryVector[nEntry];
|
|
|
|
if( pEntry )
|
|
{
|
|
// Text:
|
|
if( pEntry->mbHasText || pEntry->mbHasImage )
|
|
{
|
|
pEntry->maSize.Height() = nMinMenuItemHeight;
|
|
|
|
if( pEntry->mbHasText )
|
|
{
|
|
long nTextWidth = GetCtrlTextWidth( pEntry->maText ) + mpImpl->mnTextPos + nExtra;
|
|
nMaxTextWidth = std::max( nTextWidth, nMaxTextWidth );
|
|
}
|
|
}
|
|
// Control:
|
|
else if( pEntry->mpControl )
|
|
{
|
|
Size aControlSize( pEntry->mpControl->GetOutputSizePixel() );
|
|
|
|
nMaxTextWidth = std::max( aControlSize.Width(), nMaxTextWidth );
|
|
pEntry->maSize.Height() = aControlSize.Height() + 1;
|
|
}
|
|
|
|
if( pEntry->HasCheck() && !pEntry->mbHasImage )
|
|
{
|
|
if (IsNativeControlSupported(CTRL_MENU_POPUP, (pEntry->mnBits & MenuItemBits::RADIOCHECK)
|
|
? PART_MENU_ITEM_CHECK_MARK
|
|
: PART_MENU_ITEM_RADIO_MARK ) )
|
|
{
|
|
long nCheckHeight = 0, nRadioHeight = 0, nMaxCheckWidth = 0;
|
|
ImplGetNativeCheckAndRadioSize(*this, nCheckHeight, nRadioHeight, nMaxCheckWidth);
|
|
|
|
long nCtrlHeight = (pEntry->mnBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
|
|
nMaxTextWidth += nCtrlHeight + gfxExtra;
|
|
}
|
|
else if( pEntry->mbChecked )
|
|
{
|
|
long nSymbolWidth = (nFontHeight*25)/40;
|
|
if ( pEntry->mnBits & MenuItemBits::RADIOCHECK )
|
|
nSymbolWidth = nFontHeight/2;
|
|
|
|
nMaxTextWidth += nSymbolWidth;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
aSz.Width() = nMaxTextWidth + (BORDER_X<<1);
|
|
|
|
// positionate controls
|
|
int nY = BORDER_Y;
|
|
for( nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = mpImpl->maEntryVector[nEntry];
|
|
|
|
if (pEntry)
|
|
{
|
|
pEntry->maSize.Width() = nMaxTextWidth;
|
|
|
|
if( pEntry->mpControl )
|
|
{
|
|
Size aControlSize( pEntry->mpControl->GetOutputSizePixel() );
|
|
Point aControlPos( (aSz.Width() - aControlSize.Width())>>1, nY);
|
|
|
|
pEntry->mpControl->SetPosPixel( aControlPos );
|
|
|
|
pEntry->maRect = Rectangle( aControlPos, aControlSize );
|
|
}
|
|
else
|
|
{
|
|
pEntry->maRect = Rectangle( Point( 0, nY ), pEntry->maSize );
|
|
}
|
|
|
|
nY += pEntry->maSize.Height();
|
|
}
|
|
else
|
|
{
|
|
nY += SEPARATOR_HEIGHT;
|
|
}
|
|
}
|
|
|
|
aSz.Height() += nY + BORDER_Y;
|
|
|
|
return aSz;
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::highlightFirstEntry()
|
|
{
|
|
implChangeHighlightEntry( 0 );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::GetFocus()
|
|
{
|
|
if( mpImpl->mnHighlightedEntry == -1 )
|
|
implChangeHighlightEntry( 0 );
|
|
|
|
DockingWindow::GetFocus();
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::LoseFocus()
|
|
{
|
|
if( mpImpl && mpImpl->mnHighlightedEntry != -1 )
|
|
implChangeHighlightEntry( -1 );
|
|
|
|
DockingWindow::LoseFocus();
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::appendEntry( int nEntryId, const OUString& rStr, MenuItemBits nItemBits )
|
|
{
|
|
appendEntry( new ToolbarMenuEntry( *this, nEntryId, rStr, nItemBits ) );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::appendEntry( int nEntryId, const OUString& rStr, const Image& rImage, MenuItemBits nItemBits )
|
|
{
|
|
appendEntry( new ToolbarMenuEntry( *this, nEntryId, rImage, rStr, nItemBits ) );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::appendEntry( int nEntryId, Control* pControl, MenuItemBits nItemBits )
|
|
{
|
|
appendEntry( new ToolbarMenuEntry( *this, nEntryId, pControl, nItemBits ) );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::appendEntry( ToolbarMenuEntry* pEntry )
|
|
{
|
|
mpImpl->maEntryVector.push_back(pEntry);
|
|
mpImpl->maSize = implCalcSize();
|
|
if (IsVisible())
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::appendSeparator()
|
|
{
|
|
appendEntry( 0 );
|
|
}
|
|
|
|
|
|
|
|
/** creates an empty ValueSet that is initialized and can be inserted with appendEntry. */
|
|
VclPtr<ValueSet> ToolbarMenu::createEmptyValueSetControl()
|
|
{
|
|
VclPtr<ValueSet> pSet = VclPtr<ValueSet>::Create( this, WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT );
|
|
pSet->EnableFullItemMode( false );
|
|
pSet->SetColor( GetControlBackground() );
|
|
pSet->SetHighlightHdl( LINK( this, ToolbarMenu, HighlightHdl ) );
|
|
return pSet;
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenuEntry* ToolbarMenu::implGetEntry( int nEntry ) const
|
|
{
|
|
return mpImpl->implGetEntry( nEntry );
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenuEntry* ToolbarMenu::implSearchEntry( int nEntryId ) const
|
|
{
|
|
const int nEntryCount = mpImpl->maEntryVector.size();
|
|
int nEntry;
|
|
for( nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* p = mpImpl->maEntryVector[nEntry];
|
|
if( p && p->mnEntryId == nEntryId )
|
|
{
|
|
return p;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::implHighlightEntry(vcl::RenderContext& rRenderContext, int nHighlightEntry, bool bHighlight)
|
|
{
|
|
Size aSz(GetOutputSizePixel());
|
|
long nX = 0;
|
|
long nY = 0;
|
|
|
|
const int nEntryCount = mpImpl->maEntryVector.size();
|
|
|
|
for (int nEntry = 0; nEntry < nEntryCount; nEntry++)
|
|
{
|
|
ToolbarMenuEntry* pEntry = mpImpl->maEntryVector[nEntry];
|
|
if (pEntry && (nEntry == nHighlightEntry))
|
|
{
|
|
// no highlights for controls only items
|
|
if (pEntry->mpControl)
|
|
{
|
|
if (!bHighlight)
|
|
{
|
|
ValueSet* pValueSet = dynamic_cast<ValueSet*>(pEntry->mpControl.get());
|
|
if (pValueSet)
|
|
{
|
|
pValueSet->SetNoSelection();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
bool bRestoreLineColor = false;
|
|
Color oldLineColor;
|
|
bool bDrawItemRect = true;
|
|
|
|
Rectangle aItemRect(Point(nX, nY), Size(aSz.Width(), pEntry->maSize.Height()));
|
|
if (pEntry->mnBits & MenuItemBits::POPUPSELECT)
|
|
{
|
|
long nFontHeight = GetTextHeight();
|
|
aItemRect.Right() -= nFontHeight + nFontHeight / 4;
|
|
}
|
|
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL))
|
|
{
|
|
Size aPxSize(GetOutputSizePixel());
|
|
rRenderContext.Push(PushFlags::CLIPREGION);
|
|
rRenderContext.IntersectClipRegion(Rectangle(Point(nX, nY), Size(aSz.Width(), pEntry->maSize.Height())));
|
|
Rectangle aCtrlRect(Point(nX, 0), Size(aPxSize.Width() - nX, aPxSize.Height()));
|
|
rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, aCtrlRect,
|
|
ControlState::ENABLED, ImplControlValue(), OUString());
|
|
if (bHighlight && rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP, PART_MENU_ITEM))
|
|
{
|
|
bDrawItemRect = false;
|
|
ControlState eState = ControlState::SELECTED | (pEntry->mbEnabled ? ControlState::ENABLED : ControlState::NONE);
|
|
if (!rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, PART_MENU_ITEM, aItemRect,
|
|
eState, ImplControlValue(), OUString()))
|
|
{
|
|
bDrawItemRect = bHighlight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bDrawItemRect = bHighlight;
|
|
}
|
|
rRenderContext.Pop();
|
|
}
|
|
if (bDrawItemRect)
|
|
{
|
|
if (bHighlight)
|
|
{
|
|
if (pEntry->mbEnabled)
|
|
{
|
|
rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetFillColor();
|
|
oldLineColor = rRenderContext.GetLineColor();
|
|
rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuHighlightColor());
|
|
bRestoreLineColor = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuColor());
|
|
}
|
|
rRenderContext.DrawRect(aItemRect);
|
|
}
|
|
implPaint(rRenderContext, pEntry, bHighlight);
|
|
if (bRestoreLineColor)
|
|
rRenderContext.SetLineColor(oldLineColor);
|
|
break;
|
|
}
|
|
|
|
nY += pEntry ? pEntry->maSize.Height() : SEPARATOR_HEIGHT;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::implSelectEntry( int nSelectedEntry )
|
|
{
|
|
mpImpl->mnSelectedEntry = nSelectedEntry;
|
|
|
|
ToolbarMenuEntry* pEntry = NULL;
|
|
if( nSelectedEntry != -1 )
|
|
pEntry = mpImpl->maEntryVector[ nSelectedEntry ];
|
|
|
|
if( pEntry )
|
|
mpImpl->maSelectHdl.Call( this );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::MouseButtonDown( const MouseEvent& rMEvt )
|
|
{
|
|
implHighlightAtPosition(rMEvt, true);
|
|
implSelectEntry(mpImpl->mnHighlightedEntry);
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::MouseButtonUp( const MouseEvent& )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::MouseMove( const MouseEvent& rMEvt )
|
|
{
|
|
if (!IsVisible())
|
|
return;
|
|
|
|
implHighlightAtPosition(rMEvt, false);
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::implHighlightAtPosition(const MouseEvent& rMEvt, bool /*bMBDown*/)
|
|
{
|
|
long nMouseY = rMEvt.GetPosPixel().Y();
|
|
Size aOutSz = GetOutputSizePixel();
|
|
if ( ( nMouseY >= 0 ) && ( nMouseY < aOutSz.Height() ) )
|
|
{
|
|
long nY = 0;
|
|
bool bHighlighted = false;
|
|
|
|
const int nEntryCount = mpImpl->maEntryVector.size();
|
|
int nEntry;
|
|
for( nEntry = 0; nEntry < nEntryCount; nEntry++ )
|
|
{
|
|
ToolbarMenuEntry* pEntry = mpImpl->maEntryVector[nEntry];
|
|
if( pEntry )
|
|
{
|
|
long nOldY = nY;
|
|
nY += pEntry->maSize.Height();
|
|
|
|
if( pEntry->mnEntryId != TITLE_ID )
|
|
{
|
|
if ( ( nOldY <= nMouseY ) && ( nY > nMouseY ) )
|
|
{
|
|
if( nEntry != mpImpl->mnHighlightedEntry )
|
|
{
|
|
implChangeHighlightEntry( nEntry );
|
|
}
|
|
bHighlighted = true;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nY += SEPARATOR_HEIGHT;
|
|
}
|
|
}
|
|
if ( !bHighlighted )
|
|
implChangeHighlightEntry( -1 );
|
|
}
|
|
else
|
|
{
|
|
implChangeHighlightEntry( -1 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::implChangeHighlightEntry(int nEntry)
|
|
{
|
|
mpImpl->mnHighlightedEntry = nEntry;
|
|
Invalidate();
|
|
|
|
mpImpl->notifyHighlightedEntry();
|
|
}
|
|
|
|
|
|
|
|
static bool implCheckSubControlCursorMove( Control* pControl, bool bUp, int& nLastColumn )
|
|
{
|
|
ValueSet* pValueSet = dynamic_cast< ValueSet* >( pControl );
|
|
if( pValueSet )
|
|
{
|
|
size_t nItemPos = pValueSet->GetItemPos( pValueSet->GetSelectItemId() );
|
|
if( nItemPos != VALUESET_ITEM_NOTFOUND )
|
|
{
|
|
const sal_uInt16 nColCount = pValueSet->GetColCount();
|
|
const size_t nLine = nItemPos / nColCount;
|
|
|
|
nLastColumn = nItemPos - (nLine * nColCount);
|
|
|
|
if( bUp )
|
|
{
|
|
return nLine > 0;
|
|
}
|
|
else
|
|
{
|
|
const size_t nLineCount = (pValueSet->GetItemCount() + nColCount - 1) / nColCount;
|
|
return (nLine+1) < nLineCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
ToolbarMenuEntry* ToolbarMenu::implCursorUpDown( bool bUp, bool bHomeEnd )
|
|
{
|
|
int n = 0, nLoop = 0;
|
|
if( !bHomeEnd )
|
|
{
|
|
n = mpImpl->mnHighlightedEntry;
|
|
if( n == -1 )
|
|
{
|
|
if( bUp )
|
|
n = 0;
|
|
else
|
|
n = mpImpl->maEntryVector.size()-1;
|
|
}
|
|
else
|
|
{
|
|
// if we have a currently selected entry and
|
|
// cursor keys are used than check if this entry
|
|
// has a control that can use those cursor keys
|
|
ToolbarMenuEntry* pData = mpImpl->maEntryVector[n];
|
|
if( pData && pData->mpControl && !pData->mbHasText )
|
|
{
|
|
if( implCheckSubControlCursorMove( pData->mpControl, bUp, mpImpl->mnLastColumn ) )
|
|
return pData;
|
|
}
|
|
}
|
|
nLoop = n;
|
|
}
|
|
else
|
|
{
|
|
// absolute positioning
|
|
if( bUp )
|
|
{
|
|
n = mpImpl->maEntryVector.size();
|
|
nLoop = n-1;
|
|
}
|
|
else
|
|
{
|
|
n = -1;
|
|
nLoop = mpImpl->maEntryVector.size()-1;
|
|
}
|
|
}
|
|
|
|
do
|
|
{
|
|
if( bUp )
|
|
{
|
|
if ( n )
|
|
n--;
|
|
else
|
|
if( mpImpl->mnHighlightedEntry == -1 )
|
|
n = mpImpl->maEntryVector.size()-1;
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if( n < ((int)mpImpl->maEntryVector.size()-1) )
|
|
n++;
|
|
else
|
|
if( mpImpl->mnHighlightedEntry == -1 )
|
|
n = 0;
|
|
else
|
|
break;
|
|
}
|
|
|
|
ToolbarMenuEntry* pData = mpImpl->maEntryVector[n];
|
|
if( pData && (pData->mnEntryId != TITLE_ID) )
|
|
{
|
|
implChangeHighlightEntry( n );
|
|
return pData;
|
|
}
|
|
} while ( n != nLoop );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu_Impl::implHighlightControl( sal_uInt16 nCode, Control* pControl )
|
|
{
|
|
ValueSet* pValueSet = dynamic_cast< ValueSet* >( pControl );
|
|
if( pValueSet )
|
|
{
|
|
const size_t nItemCount = pValueSet->GetItemCount();
|
|
size_t nItemPos = VALUESET_ITEM_NOTFOUND;
|
|
switch( nCode )
|
|
{
|
|
case KEY_UP:
|
|
{
|
|
const sal_uInt16 nColCount = pValueSet->GetColCount();
|
|
const sal_uInt16 nLastLine = nItemCount / nColCount;
|
|
nItemPos = std::min( static_cast<size_t>(((nLastLine-1) * nColCount) + mnLastColumn), nItemCount-1 );
|
|
break;
|
|
}
|
|
case KEY_DOWN:
|
|
nItemPos = std::min( static_cast<size_t>(mnLastColumn), nItemCount-1 );
|
|
break;
|
|
case KEY_END:
|
|
nItemPos = nItemCount -1;
|
|
break;
|
|
case KEY_HOME:
|
|
nItemPos = 0;
|
|
break;
|
|
}
|
|
pValueSet->SelectItem( pValueSet->GetItemId( nItemPos ) );
|
|
notifyHighlightedEntry();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::KeyInput( const KeyEvent& rKEvent )
|
|
{
|
|
Control* pForwardControl = 0;
|
|
sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode();
|
|
switch ( nCode )
|
|
{
|
|
case KEY_UP:
|
|
case KEY_DOWN:
|
|
{
|
|
int nOldEntry = mpImpl->mnHighlightedEntry;
|
|
ToolbarMenuEntry*p = implCursorUpDown( nCode == KEY_UP, false );
|
|
if( p && p->mpControl )
|
|
{
|
|
if( nOldEntry != mpImpl->mnHighlightedEntry )
|
|
{
|
|
mpImpl->implHighlightControl( nCode, p->mpControl );
|
|
}
|
|
else
|
|
{
|
|
// in case we are in a system floating window, GrabFocus does not work :-/
|
|
pForwardControl = p->mpControl;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case KEY_END:
|
|
case KEY_HOME:
|
|
{
|
|
ToolbarMenuEntry* p = implCursorUpDown( nCode == KEY_END, true );
|
|
if( p && p->mpControl )
|
|
{
|
|
mpImpl->implHighlightControl( nCode, p->mpControl );
|
|
}
|
|
}
|
|
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;
|
|
|
|
implSelectEntry( -1 );
|
|
}
|
|
break;
|
|
|
|
case KEY_RETURN:
|
|
{
|
|
ToolbarMenuEntry* pEntry = implGetEntry( mpImpl->mnHighlightedEntry );
|
|
if ( pEntry && pEntry->mbEnabled && (pEntry->mnEntryId != TITLE_ID) )
|
|
{
|
|
if( pEntry->mpControl )
|
|
{
|
|
pForwardControl = pEntry->mpControl;
|
|
}
|
|
else
|
|
{
|
|
implSelectEntry( mpImpl->mnHighlightedEntry );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
ToolbarMenuEntry* pEntry = implGetEntry( mpImpl->mnHighlightedEntry );
|
|
if ( pEntry && pEntry->mbEnabled && pEntry->mpControl && !pEntry->mbHasText )
|
|
{
|
|
pForwardControl = pEntry->mpControl;
|
|
}
|
|
}
|
|
|
|
}
|
|
if( pForwardControl )
|
|
pForwardControl->KeyInput( rKEvent );
|
|
|
|
}
|
|
|
|
|
|
static void ImplPaintCheckBackground(vcl::RenderContext& rRenderContext, vcl::Window& rWindow, const Rectangle& i_rRect, bool i_bHighlight )
|
|
{
|
|
bool bNativeOk = false;
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_TOOLBAR, PART_BUTTON))
|
|
{
|
|
ImplControlValue aControlValue;
|
|
ControlState nState = ControlState::PRESSED | ControlState::ENABLED;
|
|
|
|
aControlValue.setTristateVal(BUTTONVALUE_ON);
|
|
|
|
bNativeOk = rRenderContext.DrawNativeControl(CTRL_TOOLBAR, PART_BUTTON,
|
|
i_rRect, nState, aControlValue, OUString());
|
|
}
|
|
|
|
if (!bNativeOk)
|
|
{
|
|
const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
Color aColor(i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor());
|
|
vcl::RenderTools::DrawSelectionBackground(rRenderContext, rWindow, i_rRect, 0, i_bHighlight, true, false, NULL, 2, &aColor);
|
|
}
|
|
}
|
|
|
|
void ToolbarMenu::implPaint(vcl::RenderContext& rRenderContext, ToolbarMenuEntry* pThisOnly, bool bHighlighted)
|
|
{
|
|
sal_uInt16 nBorder = 0; long nStartY = 0; // from Menu implementations, needed when we support native menu background & scrollable menu
|
|
|
|
long nFontHeight = GetTextHeight();
|
|
|
|
long nCheckHeight = 0, nRadioHeight = 0, nMaxCheckWidth = 0;
|
|
ImplGetNativeCheckAndRadioSize(rRenderContext, nCheckHeight, nRadioHeight, nMaxCheckWidth);
|
|
|
|
DecorationView aDecoView(&rRenderContext);
|
|
const StyleSettings& rSettings = rRenderContext.GetSettings().GetStyleSettings();
|
|
const bool bUseImages = rSettings.GetUseImagesInMenus();
|
|
|
|
int nOuterSpace = 0; // ImplGetSVData()->maNWFData.mnMenuFormatExtraBorder;
|
|
Point aTopLeft(nOuterSpace, nOuterSpace), aTmpPos;
|
|
|
|
Size aOutSz(GetOutputSizePixel());
|
|
const int nEntryCount = mpImpl->maEntryVector.size();
|
|
int nEntry;
|
|
for (nEntry = 0; nEntry < nEntryCount; nEntry++)
|
|
{
|
|
ToolbarMenuEntry* pEntry = mpImpl->maEntryVector[nEntry];
|
|
|
|
Point aPos(aTopLeft);
|
|
aPos.Y() += nBorder;
|
|
aPos.Y() += nStartY;
|
|
|
|
if ((pEntry == 0) && !pThisOnly)
|
|
{
|
|
// Separator
|
|
aTmpPos.Y() = aPos.Y() + ((SEPARATOR_HEIGHT - 2) / 2);
|
|
aTmpPos.X() = aPos.X() + 2 + nOuterSpace;
|
|
rRenderContext.SetLineColor(rSettings.GetShadowColor());
|
|
rRenderContext.DrawLine(aTmpPos, Point(aOutSz.Width() - 3 - 2 * nOuterSpace, aTmpPos.Y()));
|
|
aTmpPos.Y()++;
|
|
rRenderContext.SetLineColor(rSettings.GetLightColor());
|
|
rRenderContext.DrawLine(aTmpPos, Point( aOutSz.Width() - 3 - 2 * nOuterSpace, aTmpPos.Y()));
|
|
rRenderContext.SetLineColor();
|
|
}
|
|
else if (!pThisOnly || (pEntry == pThisOnly))
|
|
{
|
|
const bool bTitle = pEntry->mnEntryId == TITLE_ID;
|
|
|
|
if (pThisOnly && bHighlighted)
|
|
rRenderContext.SetTextColor(rSettings.GetMenuHighlightTextColor());
|
|
|
|
if( aPos.Y() >= 0 )
|
|
{
|
|
long nTextOffsetY = ((pEntry->maSize.Height() - nFontHeight) / 2);
|
|
|
|
DrawTextFlags nTextStyle = DrawTextFlags::NONE;
|
|
DrawSymbolFlags nSymbolStyle = DrawSymbolFlags::NONE;
|
|
DrawImageFlags nImageStyle = DrawImageFlags::NONE;
|
|
|
|
if (!pEntry->mbEnabled)
|
|
{
|
|
nTextStyle |= DrawTextFlags::Disable;
|
|
nSymbolStyle |= DrawSymbolFlags::Disable;
|
|
nImageStyle |= DrawImageFlags::Disable;
|
|
}
|
|
|
|
Rectangle aOuterCheckRect(Point(aPos.X() + mpImpl->mnCheckPos, aPos.Y()),
|
|
Size(pEntry->maSize.Height(), pEntry->maSize.Height()));
|
|
aOuterCheckRect.Left() += 1;
|
|
aOuterCheckRect.Right() -= 1;
|
|
aOuterCheckRect.Top() += 1;
|
|
aOuterCheckRect.Bottom() -= 1;
|
|
|
|
if (bTitle)
|
|
{
|
|
// fill the background
|
|
Rectangle aRect(aTopLeft, Size(aOutSz.Width(), pEntry->maSize.Height()));
|
|
rRenderContext.SetFillColor(rSettings.GetDialogColor());
|
|
rRenderContext.SetLineColor();
|
|
rRenderContext.DrawRect(aRect);
|
|
rRenderContext.SetLineColor(rSettings.GetLightColor());
|
|
rRenderContext.DrawLine(aRect.TopLeft(), aRect.TopRight());
|
|
rRenderContext.SetLineColor(rSettings.GetShadowColor());
|
|
rRenderContext.DrawLine(aRect.BottomLeft(), aRect.BottomRight());
|
|
}
|
|
|
|
// CheckMark
|
|
if (pEntry->HasCheck())
|
|
{
|
|
// draw selection transparent marker if checked
|
|
// onto that either a checkmark or the item image
|
|
// will be painted
|
|
// however do not do this if native checks will be painted since
|
|
// the selection color too often does not fit the theme's check and/or radio
|
|
|
|
if (!pEntry->mbHasImage)
|
|
{
|
|
if (rRenderContext.IsNativeControlSupported(CTRL_MENU_POPUP,
|
|
(pEntry->mnBits & MenuItemBits::RADIOCHECK)
|
|
? PART_MENU_ITEM_CHECK_MARK
|
|
: PART_MENU_ITEM_RADIO_MARK))
|
|
{
|
|
ControlPart nPart = ((pEntry->mnBits & MenuItemBits::RADIOCHECK)
|
|
? PART_MENU_ITEM_RADIO_MARK
|
|
: PART_MENU_ITEM_CHECK_MARK);
|
|
|
|
ControlState nState = ControlState::NONE;
|
|
|
|
if (pEntry->mbChecked)
|
|
nState |= ControlState::PRESSED;
|
|
|
|
if (pEntry->mbEnabled)
|
|
nState |= ControlState::ENABLED;
|
|
|
|
if ( bHighlighted )
|
|
nState |= ControlState::SELECTED;
|
|
|
|
long nCtrlHeight = (pEntry->mnBits & MenuItemBits::RADIOCHECK) ? nCheckHeight : nRadioHeight;
|
|
aTmpPos.X() = aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - nCtrlHeight) / 2;
|
|
aTmpPos.Y() = aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - nCtrlHeight) / 2;
|
|
|
|
Rectangle aCheckRect(aTmpPos, Size(nCtrlHeight, nCtrlHeight));
|
|
rRenderContext.DrawNativeControl(CTRL_MENU_POPUP, nPart, aCheckRect,
|
|
nState, ImplControlValue(), OUString());
|
|
aPos.setX(aPos.getX() + nCtrlHeight + gfxExtra);
|
|
}
|
|
else if (pEntry->mbChecked) // by default do nothing for unchecked items
|
|
{
|
|
ImplPaintCheckBackground(rRenderContext, *this, aOuterCheckRect, pThisOnly && bHighlighted);
|
|
|
|
SymbolType eSymbol;
|
|
Size aSymbolSize;
|
|
if (pEntry->mnBits & MenuItemBits::RADIOCHECK)
|
|
{
|
|
eSymbol = SymbolType::RADIOCHECKMARK;
|
|
aSymbolSize = Size(nFontHeight / 2, nFontHeight / 2);
|
|
}
|
|
else
|
|
{
|
|
eSymbol = SymbolType::CHECKMARK;
|
|
aSymbolSize = Size((nFontHeight * 25) / 40, nFontHeight / 2);
|
|
}
|
|
aTmpPos.X() = aOuterCheckRect.Left() + (aOuterCheckRect.GetWidth() - aSymbolSize.Width())/2;
|
|
aTmpPos.Y() = aOuterCheckRect.Top() + (aOuterCheckRect.GetHeight() - aSymbolSize.Height())/2;
|
|
Rectangle aRect( aTmpPos, aSymbolSize );
|
|
aDecoView.DrawSymbol(aRect, eSymbol, GetTextColor(), nSymbolStyle);
|
|
aPos.setX(aPos.getX() + aSymbolSize.getWidth( ) + gfxExtra);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Image:
|
|
if (pEntry->mbHasImage && bUseImages)
|
|
{
|
|
if (pEntry->mbChecked)
|
|
ImplPaintCheckBackground(rRenderContext, *this, aOuterCheckRect, pThisOnly && bHighlighted);
|
|
aTmpPos = aOuterCheckRect.TopLeft();
|
|
aTmpPos.X() += (aOuterCheckRect.GetWidth()-pEntry->maImage.GetSizePixel().Width())/2;
|
|
aTmpPos.Y() += (aOuterCheckRect.GetHeight()-pEntry->maImage.GetSizePixel().Height())/2;
|
|
rRenderContext.DrawImage( aTmpPos, pEntry->maImage, nImageStyle );
|
|
}
|
|
|
|
// Text:
|
|
if (pEntry->mbHasText)
|
|
{
|
|
aTmpPos.X() = aPos.X() + (bTitle ? 4 : mpImpl->mnTextPos);
|
|
aTmpPos.Y() = aPos.Y();
|
|
aTmpPos.Y() += nTextOffsetY;
|
|
DrawTextFlags nStyle = nTextStyle|DrawTextFlags::Mnemonic;
|
|
|
|
rRenderContext.DrawCtrlText(aTmpPos, pEntry->maText, 0, pEntry->maText.getLength(), nStyle, NULL, NULL);
|
|
}
|
|
|
|
if (pThisOnly && bHighlighted)
|
|
{
|
|
// This restores the normal menu or menu bar text
|
|
// color for when it is no longer highlighted.
|
|
rRenderContext.SetTextColor(rSettings.GetMenuTextColor());
|
|
}
|
|
}
|
|
}
|
|
|
|
aTopLeft.Y() += pEntry ? pEntry->maSize.Height() : SEPARATOR_HEIGHT;
|
|
}
|
|
}
|
|
|
|
void ToolbarMenu::Paint(vcl::RenderContext& rRenderContext, const Rectangle&)
|
|
{
|
|
rRenderContext.SetFillColor(rRenderContext.GetSettings().GetStyleSettings().GetMenuColor());
|
|
|
|
implPaint(rRenderContext);
|
|
|
|
if (mpImpl->mnHighlightedEntry != -1)
|
|
implHighlightEntry(rRenderContext, mpImpl->mnHighlightedEntry, true);
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::RequestHelp( const HelpEvent& rHEvt )
|
|
{
|
|
DockingWindow::RequestHelp( rHEvt );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::StateChanged( StateChangedType nType )
|
|
{
|
|
DockingWindow::StateChanged( nType );
|
|
|
|
if ( ( nType == StateChangedType::ControlForeground ) || ( nType == StateChangedType::ControlBackground ) )
|
|
{
|
|
initWindow();
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::DataChanged( const DataChangedEvent& rDCEvt )
|
|
{
|
|
DockingWindow::DataChanged( rDCEvt );
|
|
|
|
if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
|
|
(rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
|
|
((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
|
|
(rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
|
|
{
|
|
initWindow();
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::Command( const CommandEvent& rCEvt )
|
|
{
|
|
if ( rCEvt.GetCommand() == CommandEventId::Wheel )
|
|
{
|
|
const CommandWheelData* pData = rCEvt.GetWheelData();
|
|
if( !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) )
|
|
{
|
|
implCursorUpDown( pData->GetDelta() > 0L, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
Reference< ::com::sun::star::accessibility::XAccessible > ToolbarMenu::CreateAccessible()
|
|
{
|
|
mpImpl->setAccessible( new ToolbarMenuAcc( *mpImpl ) );
|
|
return Reference< XAccessible >( mpImpl->mxAccessible.get() );
|
|
}
|
|
|
|
|
|
|
|
// todo: move to new base class that will replace SfxPopupWindo
|
|
void ToolbarMenu::AddStatusListener( const OUString& rCommandURL )
|
|
{
|
|
initStatusListener();
|
|
mpImpl->mxStatusListener->addStatusListener( rCommandURL );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& /*Event*/ ) throw ( ::com::sun::star::uno::RuntimeException )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
class ToolbarMenuStatusListener : public svt::FrameStatusListener
|
|
{
|
|
public:
|
|
ToolbarMenuStatusListener( const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame,
|
|
ToolbarMenu& rToolbarMenu );
|
|
|
|
virtual void SAL_CALL dispose() throw (::com::sun::star::uno::RuntimeException, std::exception) SAL_OVERRIDE;
|
|
virtual void SAL_CALL statusChanged( const ::com::sun::star::frame::FeatureStateEvent& Event ) throw ( ::com::sun::star::uno::RuntimeException, std::exception ) SAL_OVERRIDE;
|
|
|
|
VclPtr<ToolbarMenu> mpMenu;
|
|
};
|
|
|
|
|
|
|
|
ToolbarMenuStatusListener::ToolbarMenuStatusListener(
|
|
const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame,
|
|
ToolbarMenu& rToolbarMenu )
|
|
: svt::FrameStatusListener( ::comphelper::getProcessComponentContext(), xFrame )
|
|
, mpMenu( &rToolbarMenu )
|
|
{
|
|
}
|
|
|
|
|
|
|
|
void SAL_CALL ToolbarMenuStatusListener::dispose() throw (::com::sun::star::uno::RuntimeException, std::exception)
|
|
{
|
|
mpMenu.clear();
|
|
svt::FrameStatusListener::dispose();
|
|
}
|
|
|
|
|
|
|
|
void SAL_CALL ToolbarMenuStatusListener::statusChanged( const ::com::sun::star::frame::FeatureStateEvent& Event ) throw ( ::com::sun::star::uno::RuntimeException, std::exception )
|
|
{
|
|
if( mpMenu )
|
|
mpMenu->statusChanged( Event );
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::initStatusListener()
|
|
{
|
|
if( !mpImpl->mxStatusListener.is() )
|
|
mpImpl->mxStatusListener.set( new ToolbarMenuStatusListener( mpImpl->mxFrame, *this ) );
|
|
}
|
|
|
|
|
|
|
|
bool ToolbarMenu::IsInPopupMode()
|
|
{
|
|
return GetDockingManager()->IsInPopupMode(this);
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::EndPopupMode()
|
|
{
|
|
GetDockingManager()->EndPopupMode(this);
|
|
}
|
|
|
|
|
|
|
|
const Size& ToolbarMenu::getMenuSize() const
|
|
{
|
|
return mpImpl->maSize;
|
|
}
|
|
|
|
|
|
|
|
void ToolbarMenu::SetSelectHdl( const Link<>& rLink )
|
|
{
|
|
mpImpl->maSelectHdl = rLink;
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|