blob: 63cdd0f517a352df4416a9236eb0f304f8b656da [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include "tools/list.hxx"
#include "tools/debug.hxx"
#include "tools/diagnose_ex.h"
#include "tools/rc.h"
#include "tools/stream.hxx"
#include "vcl/svapp.hxx"
#include "vcl/mnemonic.hxx"
#include "vcl/image.hxx"
#include "vcl/event.hxx"
#include "vcl/help.hxx"
#include "vcl/floatwin.hxx"
#include "vcl/wrkwin.hxx"
#include "vcl/timer.hxx"
#include "vcl/sound.hxx"
#include "vcl/decoview.hxx"
#include "vcl/bitmap.hxx"
#include "vcl/menu.hxx"
#include "vcl/button.hxx"
#include "vcl/gradient.hxx"
#include "vcl/i18nhelp.hxx"
#include "vcl/taskpanelist.hxx"
#include "vcl/controllayout.hxx"
#include "vcl/toolbox.hxx"
#include "vcl/dockingarea.hxx"
#include "salinst.hxx"
#include "svdata.hxx"
#include "svids.hrc"
#include "window.h"
#include "salmenu.hxx"
#include "salframe.hxx"
#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/i18n/XCharacterClassification.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <com/sun/star/accessibility/AccessibleRole.hpp>
#include <vcl/unowrap.hxx>
#include <vcl/unohelp.hxx>
#include <vcl/configsettings.hxx>
#include "vcl/lazydelete.hxx"
#include <map>
namespace vcl
{
struct MenuLayoutData : public ControlLayoutData
{
std::vector< sal_uInt16 > m_aLineItemIds;
std::vector< sal_uInt16 > m_aLineItemPositions;
std::map< sal_uInt16, Rectangle > m_aVisibleItemBoundRects;
};
}
using namespace ::com::sun::star;
using namespace vcl;
DBG_NAME( Menu )
#define ITEMPOS_INVALID 0xFFFF
#define EXTRASPACEY 2
#define EXTRAITEMHEIGHT 4
#define GUTTERBORDER 8
// document closer
#define IID_DOCUMENTCLOSE 1
#ifdef OS2
#include "svsys.h"
#include "os2/salmenu.h"
// return sal_True if hilite should be executed: left mouse button down
// or xwp mouse hook enabled
static sal_Bool ImplHilite( const MouseEvent& rMEvt )
{
static sal_Bool init = sal_False;
static HOOKCONFIG hc;
// read XWP settings at program startup
if (init == sal_False) {
sal_Bool rc;
sal_uLong cb = sizeof(HOOKCONFIG);
memset(&hc, 0, sizeof(HOOKCONFIG));
rc = PrfQueryProfileData( HINI_USER, INIAPP_XWPHOOK, INIKEY_HOOK_CONFIG,
&hc, &cb);
init = sal_True;
}
// check mouse left button
if (rMEvt.GetButtons() == MOUSE_LEFT)
return sal_True;
// return xwp flag
return hc.fSlidingMenus;
}
#endif
static sal_Bool ImplAccelDisabled()
{
// display of accelerator strings may be suppressed via configuration
static int nAccelDisabled = -1;
if( nAccelDisabled == -1 )
{
rtl::OUString aStr =
vcl::SettingsConfigItem::get()->
getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Menu" ) ),
rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SuppressAccelerators" ) ) );
nAccelDisabled = aStr.equalsIgnoreAsciiCaseAscii( "true" ) ? 1 : 0;
}
return (nAccelDisabled == 1) ? sal_True : sal_False;
}
struct MenuItemData
{
sal_uInt16 nId; // SV Id
MenuItemType eType; // MenuItem-Type
MenuItemBits nBits; // MenuItem-Bits
Menu* pSubMenu; // Pointer auf das SubMenu
Menu* pAutoSubMenu; // Pointer auf SubMenu aus Resource
XubString aText; // Menu-Text
XubString aHelpText; // Help-String
XubString aTipHelpText; // TipHelp-String (eg, expanded filenames)
XubString aCommandStr; // CommandString
XubString aHelpCommandStr; // Help command string (to reference external help)
rtl::OString aHelpId; // Help-Id
sal_uLong nUserValue; // User value
Image aImage; // Image
KeyCode aAccelKey; // Accelerator-Key
sal_Bool bChecked; // Checked
sal_Bool bEnabled; // Enabled
sal_Bool bVisible; // Visible (note: this flag will not override MENU_FLAG_HIDEDISABLEDENTRIES when true)
sal_Bool bIsTemporary; // Temporary inserted ('No selection possible')
sal_Bool bMirrorMode;
long nItemImageAngle;
Size aSz; // nur temporaer gueltig
XubString aAccessibleName; // accessible name
XubString aAccessibleDescription; // accessible description
SalMenuItem* pSalMenuItem; // access to native menu
MenuItemData() :
pSalMenuItem ( NULL )
{}
MenuItemData( const XubString& rStr, const Image& rImage ) :
aText( rStr ),
aImage( rImage ),
pSalMenuItem ( NULL )
{}
~MenuItemData();
bool HasCheck()
{
return bChecked || ( nBits & ( MIB_RADIOCHECK | MIB_CHECKABLE | MIB_AUTOCHECK ) );
}
};
MenuItemData::~MenuItemData()
{
if( pAutoSubMenu )
{
((PopupMenu*)pAutoSubMenu)->pRefAutoSubMenu = NULL;
delete pAutoSubMenu;
pAutoSubMenu = NULL;
}
if( pSalMenuItem )
ImplGetSVData()->mpDefInst->DestroyMenuItem( pSalMenuItem );
}
class MenuItemList : public List
{
private:
uno::Reference< i18n::XCharacterClassification > xCharClass;
public:
MenuItemList() : List( 16, 4 ) {}
~MenuItemList();
MenuItemData* Insert( sal_uInt16 nId, MenuItemType eType, MenuItemBits nBits,
const XubString& rStr, const Image& rImage,
Menu* pMenu, sal_uInt16 nPos );
void InsertSeparator( sal_uInt16 nPos );
void Remove( sal_uInt16 nPos );
MenuItemData* GetData( sal_uInt16 nSVId, sal_uInt16& rPos ) const;
MenuItemData* GetData( sal_uInt16 nSVId ) const
{ sal_uInt16 nTemp; return GetData( nSVId, nTemp ); }
MenuItemData* GetDataFromPos( sal_uLong nPos ) const
{ return (MenuItemData*)List::GetObject( nPos ); }
MenuItemData* SearchItem( xub_Unicode cSelectChar, KeyCode aKeyCode, sal_uInt16& rPos, sal_uInt16& nDuplicates, sal_uInt16 nCurrentPos ) const;
sal_uInt16 GetItemCount( xub_Unicode cSelectChar ) const;
sal_uInt16 GetItemCount( KeyCode aKeyCode ) const;
uno::Reference< i18n::XCharacterClassification > GetCharClass() const;
};
MenuItemList::~MenuItemList()
{
for ( sal_uLong n = Count(); n; )
{
MenuItemData* pData = GetDataFromPos( --n );
delete pData;
}
}
MenuItemData* MenuItemList::Insert( sal_uInt16 nId, MenuItemType eType,
MenuItemBits nBits,
const XubString& rStr, const Image& rImage,
Menu* pMenu, sal_uInt16 nPos )
{
MenuItemData* pData = new MenuItemData( rStr, rImage );
pData->nId = nId;
pData->eType = eType;
pData->nBits = nBits;
pData->pSubMenu = NULL;
pData->pAutoSubMenu = NULL;
pData->nUserValue = 0;
pData->bChecked = sal_False;
pData->bEnabled = sal_True;
pData->bVisible = sal_True;
pData->bIsTemporary = sal_False;
pData->bMirrorMode = sal_False;
pData->nItemImageAngle = 0;
SalItemParams aSalMIData;
aSalMIData.nId = nId;
aSalMIData.eType = eType;
aSalMIData.nBits = nBits;
aSalMIData.pMenu = pMenu;
aSalMIData.aText = rStr;
aSalMIData.aImage = rImage;
// Native-support: returns NULL if not supported
pData->pSalMenuItem = ImplGetSVData()->mpDefInst->CreateMenuItem( &aSalMIData );
List::Insert( (void*)pData, nPos );
return pData;
}
void MenuItemList::InsertSeparator( sal_uInt16 nPos )
{
MenuItemData* pData = new MenuItemData;
pData->nId = 0;
pData->eType = MENUITEM_SEPARATOR;
pData->nBits = 0;
pData->pSubMenu = NULL;
pData->pAutoSubMenu = NULL;
pData->nUserValue = 0;
pData->bChecked = sal_False;
pData->bEnabled = sal_True;
pData->bVisible = sal_True;
pData->bIsTemporary = sal_False;
pData->bMirrorMode = sal_False;
pData->nItemImageAngle = 0;
SalItemParams aSalMIData;
aSalMIData.nId = 0;
aSalMIData.eType = MENUITEM_SEPARATOR;
aSalMIData.nBits = 0;
aSalMIData.pMenu = NULL;
aSalMIData.aText = XubString();
aSalMIData.aImage = Image();
// Native-support: returns NULL if not supported
pData->pSalMenuItem = ImplGetSVData()->mpDefInst->CreateMenuItem( &aSalMIData );
List::Insert( (void*)pData, nPos );
}
void MenuItemList::Remove( sal_uInt16 nPos )
{
MenuItemData* pData = (MenuItemData*)List::Remove( (sal_uLong)nPos );
if ( pData )
delete pData;
}
MenuItemData* MenuItemList::GetData( sal_uInt16 nSVId, sal_uInt16& rPos ) const
{
rPos = 0;
MenuItemData* pData = (MenuItemData*)GetObject( rPos );
while ( pData )
{
if ( pData->nId == nSVId )
return pData;
rPos++;
pData = (MenuItemData*)GetObject( rPos );
}
return NULL;
}
MenuItemData* MenuItemList::SearchItem( xub_Unicode cSelectChar, KeyCode aKeyCode, sal_uInt16& rPos, sal_uInt16& nDuplicates, sal_uInt16 nCurrentPos ) const
{
const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
sal_uInt16 nListCount = (sal_uInt16)Count();
// try character code first
nDuplicates = GetItemCount( cSelectChar ); // return number of duplicates
if( nDuplicates )
{
for ( rPos = 0; rPos < nListCount; rPos++)
{
MenuItemData* pData = GetDataFromPos( rPos );
if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) )
{
if( nDuplicates > 1 && rPos == nCurrentPos )
continue; // select next entry with the same mnemonic
else
return pData;
}
}
}
// nothing found, try keycode instead
nDuplicates = GetItemCount( aKeyCode ); // return number of duplicates
if( nDuplicates )
{
char ascii = 0;
if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z )
ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A));
for ( rPos = 0; rPos < nListCount; rPos++)
{
MenuItemData* pData = GetDataFromPos( rPos );
if ( pData->bEnabled )
{
sal_uInt16 n = pData->aText.Search( '~' );
if ( n != STRING_NOTFOUND )
{
KeyCode mnKeyCode;
xub_Unicode mnUnicode = pData->aText.GetChar(n+1);
Window* pDefWindow = ImplGetDefaultWindow();
if( (pDefWindow && pDefWindow->ImplGetFrame()->MapUnicodeToKeyCode( mnUnicode, Application::GetSettings().GetUILanguage(), mnKeyCode )
&& aKeyCode.GetCode() == mnKeyCode.GetCode())
|| (ascii && rI18nHelper.MatchMnemonic( pData->aText, ascii ) ) )
{
if( nDuplicates > 1 && rPos == nCurrentPos )
continue; // select next entry with the same mnemonic
else
return pData;
}
}
}
}
}
return NULL;
}
sal_uInt16 MenuItemList::GetItemCount( xub_Unicode cSelectChar ) const
{
// returns number of entries with same mnemonic
const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
sal_uInt16 nItems = 0, nPos;
for ( nPos = (sal_uInt16)Count(); nPos; )
{
MenuItemData* pData = GetDataFromPos( --nPos );
if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) )
nItems++;
}
return nItems;
}
sal_uInt16 MenuItemList::GetItemCount( KeyCode aKeyCode ) const
{
// returns number of entries with same mnemonic
// uses key codes instead of character codes
const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper();
char ascii = 0;
if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z )
ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A));
sal_uInt16 nItems = 0, nPos;
for ( nPos = (sal_uInt16)Count(); nPos; )
{
MenuItemData* pData = GetDataFromPos( --nPos );
if ( pData->bEnabled )
{
sal_uInt16 n = pData->aText.Search( '~' );
if ( n != STRING_NOTFOUND )
{
KeyCode mnKeyCode;
// if MapUnicodeToKeyCode fails or is unsupported we try the pure ascii mapping of the keycodes
// so we have working shortcuts when ascii mnemonics are used
Window* pDefWindow = ImplGetDefaultWindow();
if( (pDefWindow && pDefWindow->ImplGetFrame()->MapUnicodeToKeyCode( pData->aText.GetChar(n+1), Application::GetSettings().GetUILanguage(), mnKeyCode )
&& aKeyCode.GetCode() == mnKeyCode.GetCode())
|| ( ascii && rI18nHelper.MatchMnemonic( pData->aText, ascii ) ) )
nItems++;
}
}
}
return nItems;
}
uno::Reference< i18n::XCharacterClassification > MenuItemList::GetCharClass() const
{
if ( !xCharClass.is() )
((MenuItemList*)this)->xCharClass = vcl::unohelper::CreateCharacterClassification();
return xCharClass;
}
// ----------------------
// - MenuFloatingWindow -
// ----------------------
class MenuFloatingWindow : public FloatingWindow
{
friend void Menu::ImplFillLayoutData() const;
friend Menu::~Menu();
private:
Menu* pMenu;
PopupMenu* pActivePopup;
Timer aHighlightChangedTimer;
Timer aSubmenuCloseTimer;
Timer aScrollTimer;
sal_uLong nSaveFocusId;
// long nStartY;
sal_uInt16 nHighlightedItem; // gehighlightetes/selektiertes Item
sal_uInt16 nMBDownPos;
sal_uInt16 nScrollerHeight;
sal_uInt16 nFirstEntry;
sal_uInt16 nBorder;
sal_uInt16 nPosInParent;
sal_Bool bInExecute;
sal_Bool bScrollMenu;
sal_Bool bScrollUp;
sal_Bool bScrollDown;
sal_Bool bIgnoreFirstMove;
sal_Bool bKeyInput;
DECL_LINK( PopupEnd, FloatingWindow* );
DECL_LINK( HighlightChanged, Timer* );
DECL_LINK( SubmenuClose, Timer* );
DECL_LINK( AutoScroll, Timer* );
DECL_LINK( ShowHideListener, VclWindowEvent* );
void StateChanged( StateChangedType nType );
void DataChanged( const DataChangedEvent& rDCEvt );
protected:
Region ImplCalcClipRegion( sal_Bool bIncludeLogo = sal_True ) const;
void ImplInitClipRegion();
void ImplDrawScroller( sal_Bool bUp );
using Window::ImplScroll;
void ImplScroll( const Point& rMousePos );
void ImplScroll( sal_Bool bUp );
void ImplCursorUpDown( sal_Bool bUp, sal_Bool bHomeEnd = sal_False );
void ImplHighlightItem( const MouseEvent& rMEvt, sal_Bool bMBDown );
long ImplGetStartY() const;
Rectangle ImplGetItemRect( sal_uInt16 nPos );
public:
MenuFloatingWindow( Menu* pMenu, Window* pParent, WinBits nStyle );
~MenuFloatingWindow();
void doShutdown();
virtual void MouseMove( const MouseEvent& rMEvt );
virtual void MouseButtonDown( const MouseEvent& rMEvt );
virtual void MouseButtonUp( const MouseEvent& rMEvt );
virtual void KeyInput( const KeyEvent& rKEvent );
virtual void Command( const CommandEvent& rCEvt );
virtual void Paint( const Rectangle& rRect );
virtual void RequestHelp( const HelpEvent& rHEvt );
virtual void Resize();
void SetFocusId( sal_uLong nId ) { nSaveFocusId = nId; }
sal_uLong GetFocusId() const { return nSaveFocusId; }
void EnableScrollMenu( sal_Bool b );
sal_Bool IsScrollMenu() const { return bScrollMenu; }
sal_uInt16 GetScrollerHeight() const { return nScrollerHeight; }
void Execute();
void StopExecute( sal_uLong nFocusId = 0 );
void EndExecute();
void EndExecute( sal_uInt16 nSelectId );
PopupMenu* GetActivePopup() const { return pActivePopup; }
void KillActivePopup( PopupMenu* pThisOnly = NULL );
void HighlightItem( sal_uInt16 nPos, sal_Bool bHighlight );
void ChangeHighlightItem( sal_uInt16 n, sal_Bool bStartPopupTimer );
sal_uInt16 GetHighlightedItem() const { return nHighlightedItem; }
void SetPosInParent( sal_uInt16 nPos ) { nPosInParent = nPos; }
sal_uInt16 GetPosInParent() const { return nPosInParent; }
virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > CreateAccessible();
};
// To get the transparent mouse-over look, the closer is actually a toolbox
// overload DataChange to handle style changes correctly
class DecoToolBox : public ToolBox
{
long lastSize;
Size maMinSize;
using Window::ImplInit;
public:
DecoToolBox( Window* pParent, WinBits nStyle = 0 );
DecoToolBox( Window* pParent, const ResId& rResId );
void ImplInit();
void DataChanged( const DataChangedEvent& rDCEvt );
void SetImages( long nMaxHeight = 0, bool bForce = false );
void calcMinSize();
Size getMinSize();
Image maImage;
Image maImageHC;
};
DecoToolBox::DecoToolBox( Window* pParent, WinBits nStyle ) :
ToolBox( pParent, nStyle )
{
ImplInit();
}
DecoToolBox::DecoToolBox( Window* pParent, const ResId& rResId ) :
ToolBox( pParent, rResId )
{
ImplInit();
}
void DecoToolBox::ImplInit()
{
lastSize = -1;
calcMinSize();
}
void DecoToolBox::DataChanged( const DataChangedEvent& rDCEvt )
{
Window::DataChanged( rDCEvt );
if ( rDCEvt.GetFlags() & SETTINGS_STYLE )
{
calcMinSize();
SetBackground();
SetImages( 0, true);
}
}
void DecoToolBox::calcMinSize()
{
ToolBox aTbx( GetParent() );
if( GetItemCount() == 0 )
{
ResMgr* pResMgr = ImplGetResMgr();
Bitmap aBitmap;
if( pResMgr )
aBitmap = Bitmap( ResId( SV_RESID_BITMAP_CLOSEDOC, *pResMgr ) );
aTbx.InsertItem( IID_DOCUMENTCLOSE, Image( aBitmap ) );
}
else
{
sal_uInt16 nItems = GetItemCount();
for( sal_uInt16 i = 0; i < nItems; i++ )
{
sal_uInt16 nId = GetItemId( i );
aTbx.InsertItem( nId, GetItemImage( nId ) );
}
}
aTbx.SetOutStyle( TOOLBOX_STYLE_FLAT );
maMinSize = aTbx.CalcWindowSizePixel();
}
Size DecoToolBox::getMinSize()
{
return maMinSize;
}
void DecoToolBox::SetImages( long nMaxHeight, bool bForce )
{
long border = getMinSize().Height() - maImage.GetSizePixel().Height();
if( !nMaxHeight && lastSize != -1 )
nMaxHeight = lastSize + border; // don't change anything if called with 0
if( nMaxHeight < getMinSize().Height() )
nMaxHeight = getMinSize().Height();
if( (lastSize != nMaxHeight - border) || bForce )
{
lastSize = nMaxHeight - border;
Color aEraseColor( 255, 255, 255, 255 );
BitmapEx aBmpExDst( maImage.GetBitmapEx() );
BitmapEx aBmpExSrc( GetSettings().GetStyleSettings().GetHighContrastMode() ?
maImageHC.GetBitmapEx() : aBmpExDst );
aEraseColor.SetTransparency( 255 );
aBmpExDst.Erase( aEraseColor );
aBmpExDst.SetSizePixel( Size( lastSize, lastSize ) );
Rectangle aSrcRect( Point(0,0), maImage.GetSizePixel() );
Rectangle aDestRect( Point((lastSize - maImage.GetSizePixel().Width())/2,
(lastSize - maImage.GetSizePixel().Height())/2 ),
maImage.GetSizePixel() );
aBmpExDst.CopyPixel( aDestRect, aSrcRect, &aBmpExSrc );
SetItemImage( IID_DOCUMENTCLOSE, Image( aBmpExDst ) );
}
}
// Eine Basicklasse fuer beide (wegen pActivePopup, Timer, ...) waere nett,
// aber dann musste eine 'Container'-Klasse gemacht werden, da von
// unterschiedlichen Windows abgeleitet...
// In den meisten Funktionen muessen dann sowieso Sonderbehandlungen fuer
// MenuBar, PopupMenu gemacht werden, also doch zwei verschiedene Klassen.
class MenuBarWindow : public Window
{
friend class MenuBar;
friend class Menu;
private:
struct AddButtonEntry
{
sal_uInt16 m_nId;
Link m_aSelectLink;
Link m_aHighlightLink;
AddButtonEntry() : m_nId( 0 ) {}
};
Menu* pMenu;
PopupMenu* pActivePopup;
sal_uInt16 nHighlightedItem;
sal_uLong nSaveFocusId;
sal_Bool mbAutoPopup;
sal_Bool bIgnoreFirstMove;
sal_Bool bStayActive;
DecoToolBox aCloser;
PushButton aFloatBtn;
PushButton aHideBtn;
std::map< sal_uInt16, AddButtonEntry > m_aAddButtons;
void HighlightItem( sal_uInt16 nPos, sal_Bool bHighlight );
void ChangeHighlightItem( sal_uInt16 n, sal_Bool bSelectPopupEntry, sal_Bool bAllowRestoreFocus = sal_True, sal_Bool bDefaultToDocument = sal_True );
sal_uInt16 ImplFindEntry( const Point& rMousePos ) const;
void ImplCreatePopup( sal_Bool bPreSelectFirst );
sal_Bool ImplHandleKeyEvent( const KeyEvent& rKEvent, sal_Bool bFromMenu = sal_True );
Rectangle ImplGetItemRect( sal_uInt16 nPos );
void ImplInitStyleSettings();
DECL_LINK( CloserHdl, PushButton* );
DECL_LINK( FloatHdl, PushButton* );
DECL_LINK( HideHdl, PushButton* );
DECL_LINK( ToolboxEventHdl, VclWindowEvent* );
DECL_LINK( ShowHideListener, VclWindowEvent* );
void StateChanged( StateChangedType nType );
void DataChanged( const DataChangedEvent& rDCEvt );
void LoseFocus();
void GetFocus();
public:
MenuBarWindow( Window* pParent );
~MenuBarWindow();
void ShowButtons( sal_Bool bClose, sal_Bool bFloat, sal_Bool bHide );
virtual void MouseMove( const MouseEvent& rMEvt );
virtual void MouseButtonDown( const MouseEvent& rMEvt );
virtual void MouseButtonUp( const MouseEvent& rMEvt );
virtual void KeyInput( const KeyEvent& rKEvent );
virtual void Paint( const Rectangle& rRect );
virtual void Resize();
virtual void RequestHelp( const HelpEvent& rHEvt );
void SetFocusId( sal_uLong nId ) { nSaveFocusId = nId; }
sal_uLong GetFocusId() const { return nSaveFocusId; }
void SetMenu( MenuBar* pMenu );
void KillActivePopup();
PopupMenu* GetActivePopup() const { return pActivePopup; }
void PopupClosed( Menu* pMenu );
sal_uInt16 GetHighlightedItem() const { return nHighlightedItem; }
virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > CreateAccessible();
void SetAutoPopup( sal_Bool bAuto ) { mbAutoPopup = bAuto; }
void ImplLayoutChanged();
Size MinCloseButtonSize();
// add an arbitrary button to the menubar (will appear next to closer)
sal_uInt16 AddMenuBarButton( const Image&, const Link&, const String&, sal_uInt16 nPos );
void SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link& );
Rectangle GetMenuBarButtonRectPixel( sal_uInt16 nId );
void RemoveMenuBarButton( sal_uInt16 nId );
bool HandleMenuButtonEvent( sal_uInt16 i_nButtonId );
};
static void ImplAddNWFSeparator( Window *pThis, const MenubarValue& rMenubarValue )
{
// add a separator if
// - we have an adjacent docking area
// - and if toolbars would draw them as well (mbDockingAreaSeparateTB must not be set, see dockingarea.cxx)
if( rMenubarValue.maTopDockingAreaHeight && !ImplGetSVData()->maNWFData.mbDockingAreaSeparateTB )
{
// note: the menubar only provides the upper (dark) half of it, the rest (bright part) is drawn by the docking area
pThis->SetLineColor( pThis->GetSettings().GetStyleSettings().GetSeparatorColor() );
Point aPt;
Rectangle aRect( aPt, pThis->GetOutputSizePixel() );
pThis->DrawLine( aRect.BottomLeft(), aRect.BottomRight() );
}
}
static void ImplSetMenuItemData( MenuItemData* pData )
{
// Daten umsetzen
if ( !pData->aImage )
pData->eType = MENUITEM_STRING;
else if ( !pData->aText.Len() )
pData->eType = MENUITEM_IMAGE;
else
pData->eType = MENUITEM_STRINGIMAGE;
}
static sal_uLong ImplChangeTipTimeout( sal_uLong nTimeout, Window *pWindow )
{
AllSettings aAllSettings( pWindow->GetSettings() );
HelpSettings aHelpSettings( aAllSettings.GetHelpSettings() );
sal_uLong nRet = aHelpSettings.GetTipTimeout();
aHelpSettings.SetTipTimeout( nTimeout );
aAllSettings.SetHelpSettings( aHelpSettings );
pWindow->SetSettings( aAllSettings );
return nRet;
}
static sal_Bool ImplHandleHelpEvent( Window* pMenuWindow, Menu* pMenu, sal_uInt16 nHighlightedItem, const HelpEvent& rHEvt, const Rectangle &rHighlightRect )
{
if( ! pMenu )
return sal_False;
sal_Bool bDone = sal_False;
sal_uInt16 nId = 0;
if ( nHighlightedItem != ITEMPOS_INVALID )
{
MenuItemData* pItemData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
if ( pItemData )
nId = pItemData->nId;
}
if ( ( rHEvt.GetMode() & HELPMODE_BALLOON ) && pMenuWindow )
{
Point aPos;
if( rHEvt.KeyboardActivated() )
aPos = rHighlightRect.Center();
else
aPos = rHEvt.GetMousePosPixel();
Rectangle aRect( aPos, Size() );
if( pMenu->GetHelpText( nId ).Len() )
Help::ShowBalloon( pMenuWindow, aPos, pMenu->GetHelpText( nId ) );
else
{
// give user a chance to read the full filename
sal_uLong oldTimeout=ImplChangeTipTimeout( 60000, pMenuWindow );
// call always, even when strlen==0 to correctly remove tip
Help::ShowQuickHelp( pMenuWindow, aRect, pMenu->GetTipHelpText( nId ) );
ImplChangeTipTimeout( oldTimeout, pMenuWindow );
}
bDone = sal_True;
}
else if ( ( rHEvt.GetMode() & HELPMODE_QUICK ) && pMenuWindow )
{
Point aPos = rHEvt.GetMousePosPixel();
Rectangle aRect( aPos, Size() );
// give user a chance to read the full filename
sal_uLong oldTimeout=ImplChangeTipTimeout( 60000, pMenuWindow );
// call always, even when strlen==0 to correctly remove tip
Help::ShowQuickHelp( pMenuWindow, aRect, pMenu->GetTipHelpText( nId ) );
ImplChangeTipTimeout( oldTimeout, pMenuWindow );
bDone = sal_True;
}
else if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) )
{
// Ist eine Hilfe in die Applikation selektiert
Help* pHelp = Application::GetHelp();
if ( pHelp )
{
// Ist eine ID vorhanden, dann Hilfe mit der ID aufrufen, sonst
// den Hilfe-Index
String aCommand = pMenu->GetItemCommand( nId );
rtl::OString aHelpId( pMenu->GetHelpId( nId ) );
if( ! aHelpId.getLength() )
aHelpId = OOO_HELP_INDEX;
if ( aCommand.Len() )
pHelp->Start( aCommand, NULL );
else
pHelp->Start( rtl::OStringToOUString( aHelpId, RTL_TEXTENCODING_UTF8 ), NULL );
}
bDone = sal_True;
}
return bDone;
}
static int ImplGetTopDockingAreaHeight( Window *pWindow )
{
// find docking area that is top aligned and return its height
// note: dockingareas are direct children of the SystemWindow
int height=0;
sal_Bool bDone = sal_False;
if( pWindow->ImplGetFrameWindow() )
{
Window *pWin = pWindow->ImplGetFrameWindow()->GetWindow( WINDOW_FIRSTCHILD); //mpWindowImpl->mpFirstChild;
while( pWin && !bDone )
{
if( pWin->IsSystemWindow() )
{
pWin = pWin->GetWindow( WINDOW_FIRSTCHILD); //mpWindowImpl->mpFirstChild;
while( pWin && !bDone )
{
DockingAreaWindow *pDockingArea = dynamic_cast< DockingAreaWindow* >( pWin );
if( pDockingArea && pDockingArea->GetAlign() == WINDOWALIGN_TOP )
{
bDone = sal_True;
if( pDockingArea->IsVisible() )
height = pDockingArea->GetOutputSizePixel().Height();
}
else
pWin = pWin->GetWindow( WINDOW_NEXT ); //mpWindowImpl->mpNext;
}
}
else
pWin = pWin->GetWindow( WINDOW_NEXT ); //mpWindowImpl->mpNext;
}
}
return height;
}
Menu::Menu()
{
DBG_CTOR( Menu, NULL );
bIsMenuBar = sal_False;
ImplInit();
}
// this constructor makes sure we're creating the native menu
// with the correct type (ie, MenuBar vs. PopupMenu)
Menu::Menu( sal_Bool bMenubar )
{
DBG_CTOR( Menu, NULL );
bIsMenuBar = bMenubar;
ImplInit();
}
Menu::~Menu()
{
DBG_DTOR( Menu, NULL );
vcl::LazyDeletor<Menu>::Undelete( this );
ImplCallEventListeners( VCLEVENT_OBJECT_DYING, ITEMPOS_INVALID );
// at the window free the reference to the accessible component
// and make sure the MenuFloatingWindow knows about our destruction
if ( pWindow )
{
MenuFloatingWindow* pFloat = (MenuFloatingWindow*)pWindow;
if( pFloat->pMenu == this )
pFloat->pMenu = NULL;
pWindow->SetAccessible( ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >() );
}
// dispose accessible components
if ( mxAccessible.is() )
{
::com::sun::star::uno::Reference< ::com::sun::star::lang::XComponent> xComponent( mxAccessible, ::com::sun::star::uno::UNO_QUERY );
if ( xComponent.is() )
xComponent->dispose();
}
if ( nEventId )
Application::RemoveUserEvent( nEventId );
// Notify deletion of this menu
ImplMenuDelData* pDelData = mpFirstDel;
while ( pDelData )
{
pDelData->mpMenu = NULL;
pDelData = pDelData->mpNext;
}
bKilled = sal_True;
delete pItemList;
delete pLogo;
delete mpLayoutData;
// Native-support: destroy SalMenu
ImplSetSalMenu( NULL );
}
void Menu::doLazyDelete()
{
vcl::LazyDeletor<Menu>::Delete( this );
}
void Menu::ImplInit()
{
mnHighlightedItemPos = ITEMPOS_INVALID;
mpSalMenu = NULL;
nMenuFlags = MENU_FLAG_SHOWCHECKIMAGES;
nDefaultItem = 0;
//bIsMenuBar = sal_False; // this is now set in the ctor, must not be changed here!!!
nSelectedId = 0;
pItemList = new MenuItemList;
pLogo = NULL;
pStartedFrom = NULL;
pWindow = NULL;
nEventId = 0;
bCanceled = sal_False;
bInCallback = sal_False;
bKilled = sal_False;
mpLayoutData = NULL;
mpFirstDel = NULL; // Dtor notification list
// Native-support: returns NULL if not supported
mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu( bIsMenuBar, this );
}
Menu* Menu::ImplGetStartedFrom() const
{
return pStartedFrom;
}
void Menu::ImplLoadRes( const ResId& rResId )
{
ResMgr* pMgr = rResId.GetResMgr();
if( ! pMgr )
return;
rResId.SetRT( RSC_MENU );
GetRes( rResId );
sal_uLong nObjMask = ReadLongRes();
if( nObjMask & RSC_MENU_ITEMS )
{
sal_uLong nObjFollows = ReadLongRes();
// MenuItems einfuegen
for( sal_uLong i = 0; i < nObjFollows; i++ )
{
InsertItem( ResId( (RSHEADER_TYPE*)GetClassRes(), *pMgr ) );
IncrementRes( GetObjSizeRes( (RSHEADER_TYPE*)GetClassRes() ) );
}
}
if( nObjMask & RSC_MENU_TEXT )
{
if( bIsMenuBar ) // Kein Titel im Menubar
ReadStringRes();
else
aTitleText = ReadStringRes();
}
if( nObjMask & RSC_MENU_DEFAULTITEMID )
SetDefaultItem( sal::static_int_cast<sal_uInt16>(ReadLongRes()) );
}
void Menu::CreateAutoMnemonics()
{
MnemonicGenerator aMnemonicGenerator;
sal_uLong n;
for ( n = 0; n < pItemList->Count(); n++ )
{
MenuItemData* pData = pItemList->GetDataFromPos(n);
if ( ! (pData->nBits & MIB_NOSELECT ) )
aMnemonicGenerator.RegisterMnemonic( pData->aText );
}
for ( n = 0; n < pItemList->Count(); n++ )
{
MenuItemData* pData = pItemList->GetDataFromPos(n);
if ( ! (pData->nBits & MIB_NOSELECT ) )
aMnemonicGenerator.CreateMnemonic( pData->aText );
}
}
void Menu::Activate()
{
bInCallback = sal_True;
ImplMenuDelData aDelData( this );
ImplCallEventListeners( VCLEVENT_MENU_ACTIVATE, ITEMPOS_INVALID );
if( !aDelData.isDeleted() )
{
if ( !aActivateHdl.Call( this ) )
{
if( !aDelData.isDeleted() )
{
Menu* pStartMenu = ImplGetStartMenu();
if ( pStartMenu && ( pStartMenu != this ) )
{
pStartMenu->bInCallback = sal_True;
// MT 11/01: Call EventListener here? I don't know...
pStartMenu->aActivateHdl.Call( this );
pStartMenu->bInCallback = sal_False;
}
}
}
bInCallback = sal_False;
}
}
void Menu::Deactivate()
{
for ( sal_uInt16 n = (sal_uInt16)pItemList->Count(); n; )
{
MenuItemData* pData = pItemList->GetDataFromPos( --n );
if ( pData->bIsTemporary )
pItemList->Remove( n );
}
bInCallback = sal_True;
ImplMenuDelData aDelData( this );
Menu* pStartMenu = ImplGetStartMenu();
ImplCallEventListeners( VCLEVENT_MENU_DEACTIVATE, ITEMPOS_INVALID );
if( !aDelData.isDeleted() )
{
if ( !aDeactivateHdl.Call( this ) )
{
if( !aDelData.isDeleted() )
{
if ( pStartMenu && ( pStartMenu != this ) )
{
pStartMenu->bInCallback = sal_True;
pStartMenu->aDeactivateHdl.Call( this );
pStartMenu->bInCallback = sal_False;
}
}
}
}
if( !aDelData.isDeleted() )
{
bInCallback = sal_False;
if ( this == pStartMenu )
GetpApp()->HideHelpStatusText();
}
}
void Menu::Highlight()
{
ImplMenuDelData aDelData( this );
Menu* pStartMenu = ImplGetStartMenu();
if ( !aHighlightHdl.Call( this ) && !aDelData.isDeleted() )
{
if ( pStartMenu && ( pStartMenu != this ) )
pStartMenu->aHighlightHdl.Call( this );
}
if ( !aDelData.isDeleted() && GetCurItemId() )
GetpApp()->ShowHelpStatusText( GetHelpText( GetCurItemId() ) );
}
void Menu::ImplSelect()
{
MenuItemData* pData = GetItemList()->GetData( nSelectedId );
if ( pData && (pData->nBits & MIB_AUTOCHECK) )
{
sal_Bool bChecked = IsItemChecked( nSelectedId );
if ( pData->nBits & MIB_RADIOCHECK )
{
if ( !bChecked )
CheckItem( nSelectedId, sal_True );
}
else
CheckItem( nSelectedId, !bChecked );
}
// Select rufen
ImplSVData* pSVData = ImplGetSVData();
pSVData->maAppData.mpActivePopupMenu = NULL; // Falls neues Execute im Select()
Application::PostUserEvent( nEventId, LINK( this, Menu, ImplCallSelect ) );
}
void Menu::Select()
{
ImplMenuDelData aDelData( this );
ImplCallEventListeners( VCLEVENT_MENU_SELECT, GetItemPos( GetCurItemId() ) );
if ( !aDelData.isDeleted() && !aSelectHdl.Call( this ) )
{
if( !aDelData.isDeleted() )
{
Menu* pStartMenu = ImplGetStartMenu();
if ( pStartMenu && ( pStartMenu != this ) )
{
pStartMenu->nSelectedId = nSelectedId;
pStartMenu->aSelectHdl.Call( this );
}
}
}
}
void Menu::ImplSelectWithStart( Menu* pSMenu )
{
Menu* pOldStartedFrom = pStartedFrom;
pStartedFrom = pSMenu;
Menu* pOldStartedStarted = pOldStartedFrom ? pOldStartedFrom->pStartedFrom : NULL;
Select();
if( pOldStartedFrom )
pOldStartedFrom->pStartedFrom = pOldStartedStarted;
pStartedFrom = pOldStartedFrom;
}
void Menu::RequestHelp( const HelpEvent& )
{
}
void Menu::ImplCallEventListeners( sal_uLong nEvent, sal_uInt16 nPos )
{
ImplMenuDelData aDelData( this );
VclMenuEvent aEvent( this, nEvent, nPos );
// This is needed by atk accessibility bridge
if ( nEvent == VCLEVENT_MENU_HIGHLIGHT )
{
ImplGetSVData()->mpApp->ImplCallEventListeners( &aEvent );
}
if ( !aDelData.isDeleted() && !maEventListeners.empty() )
maEventListeners.Call( &aEvent );
if( !aDelData.isDeleted() )
{
Menu* pMenu = this;
while ( pMenu )
{
if ( !maChildEventListeners.empty() )
maChildEventListeners.Call( &aEvent );
if( aDelData.isDeleted() )
break;
pMenu = ( pMenu->pStartedFrom != pMenu ) ? pMenu->pStartedFrom : NULL;
}
}
}
void Menu::AddEventListener( const Link& rEventListener )
{
maEventListeners.push_back( rEventListener );
}
void Menu::RemoveEventListener( const Link& rEventListener )
{
maEventListeners.remove( rEventListener );
}
// -----------------------------------------------------------------------
//void Menu::AddChildEventListener( const Link& rEventListener )
//{
// mpDummy4_WindowChildEventListeners->push_back( rEventListener );
//}
// -----------------------------------------------------------------------
//void Menu::RemoveChildEventListener( const Link& rEventListener )
//{
// mpDummy4_WindowChildEventListeners->remove( rEventListener );
//}
void Menu::InsertItem( sal_uInt16 nItemId, const XubString& rStr, MenuItemBits nItemBits, sal_uInt16 nPos )
{
DBG_ASSERT( nItemId, "Menu::InsertItem(): ItemId == 0" );
DBG_ASSERT( GetItemPos( nItemId ) == MENU_ITEM_NOTFOUND,
"Menu::InsertItem(): ItemId already exists" );
// if Position > ItemCount, append
if ( nPos >= (sal_uInt16)pItemList->Count() )
nPos = MENU_APPEND;
// put Item in MenuItemList
MenuItemData* pData = pItemList->Insert( nItemId, MENUITEM_STRING,
nItemBits, rStr, Image(), this, nPos );
// update native menu
if( ImplGetSalMenu() && pData->pSalMenuItem )
ImplGetSalMenu()->InsertItem( pData->pSalMenuItem, nPos );
Window* pWin = ImplGetWindow();
delete mpLayoutData, mpLayoutData = NULL;
if ( pWin )
{
ImplCalcSize( pWin );
if ( pWin->IsVisible() )
pWin->Invalidate();
}
ImplCallEventListeners( VCLEVENT_MENU_INSERTITEM, nPos );
}
void Menu::InsertItem( sal_uInt16 nItemId, const Image& rImage,
MenuItemBits nItemBits, sal_uInt16 nPos )
{
InsertItem( nItemId, ImplGetSVEmptyStr(), nItemBits, nPos );
SetItemImage( nItemId, rImage );
}
void Menu::InsertItem( sal_uInt16 nItemId,
const XubString& rStr, const Image& rImage,
MenuItemBits nItemBits, sal_uInt16 nPos )
{
InsertItem( nItemId, rStr, nItemBits, nPos );
SetItemImage( nItemId, rImage );
}
void Menu::InsertItem( const ResId& rResId, sal_uInt16 nPos )
{
ResMgr* pMgr = rResId.GetResMgr();
if( ! pMgr )
return;
sal_uLong nObjMask;
GetRes( rResId.SetRT( RSC_MENUITEM ) );
nObjMask = ReadLongRes();
sal_Bool bSep = sal_False;
if ( nObjMask & RSC_MENUITEM_SEPARATOR )
bSep = (sal_Bool)ReadShortRes();
sal_uInt16 nItemId = 1;
if ( nObjMask & RSC_MENUITEM_ID )
nItemId = sal::static_int_cast<sal_uInt16>(ReadLongRes());
MenuItemBits nStatus = 0;
if ( nObjMask & RSC_MENUITEM_STATUS )
nStatus = sal::static_int_cast<MenuItemBits>(ReadLongRes());
String aText;
if ( nObjMask & RSC_MENUITEM_TEXT )
aText = ReadStringRes();
// Item erzeugen
if ( nObjMask & RSC_MENUITEM_BITMAP )
{
if ( !bSep )
{
Bitmap aBmp( ResId( (RSHEADER_TYPE*)GetClassRes(), *pMgr ) );
if ( aText.Len() )
InsertItem( nItemId, aText, aBmp, nStatus, nPos );
else
InsertItem( nItemId, aBmp, nStatus, nPos );
}
IncrementRes( GetObjSizeRes( (RSHEADER_TYPE*)GetClassRes() ) );
}
else if ( !bSep )
InsertItem( nItemId, aText, nStatus, nPos );
if ( bSep )
InsertSeparator( nPos );
String aHelpText;
if ( nObjMask & RSC_MENUITEM_HELPTEXT )
{
aHelpText = ReadStringRes();
if( !bSep )
SetHelpText( nItemId, aHelpText );
}
if ( nObjMask & RSC_MENUITEM_HELPID )
{
rtl::OString aHelpId( ReadByteStringRes() );
if ( !bSep )
SetHelpId( nItemId, aHelpId );
}
if( !bSep )
SetHelpText( nItemId, aHelpText );
if ( nObjMask & RSC_MENUITEM_KEYCODE )
{
if ( !bSep )
SetAccelKey( nItemId, KeyCode( ResId( (RSHEADER_TYPE*)GetClassRes(), *pMgr ) ) );
IncrementRes( GetObjSizeRes( (RSHEADER_TYPE*)GetClassRes() ) );
}
if( nObjMask & RSC_MENUITEM_CHECKED )
{
if ( !bSep )
CheckItem( nItemId, (sal_Bool)ReadShortRes() );
}
if ( nObjMask & RSC_MENUITEM_DISABLE )
{
if ( !bSep )
EnableItem( nItemId, !(sal_Bool)ReadShortRes() );
}
if ( nObjMask & RSC_MENUITEM_COMMAND )
{
String aCommandStr = ReadStringRes();
if ( !bSep )
SetItemCommand( nItemId, aCommandStr );
}
if ( nObjMask & RSC_MENUITEM_MENU )
{
if ( !bSep )
{
MenuItemData* pData = GetItemList()->GetData( nItemId );
if ( pData )
{
PopupMenu* pSubMenu = new PopupMenu( ResId( (RSHEADER_TYPE*)GetClassRes(), *pMgr ) );
pData->pAutoSubMenu = pSubMenu;
// #111060# keep track of this pointer, may be it will be deleted from outside
pSubMenu->pRefAutoSubMenu = &pData->pAutoSubMenu;
SetPopupMenu( nItemId, pSubMenu );
}
}
IncrementRes( GetObjSizeRes( (RSHEADER_TYPE*)GetClassRes() ) );
}
delete mpLayoutData, mpLayoutData = NULL;
}
void Menu::InsertSeparator( sal_uInt16 nPos )
{
// do nothing if its a menu bar
if ( bIsMenuBar )
return;
// if position > ItemCount, append
if ( nPos >= (sal_uInt16)pItemList->Count() )
nPos = MENU_APPEND;
// put separator in item list
pItemList->InsertSeparator( nPos );
// update native menu
sal_uInt16 itemPos = nPos != MENU_APPEND ? nPos : (sal_uInt16)pItemList->Count() - 1;
MenuItemData *pData = pItemList->GetDataFromPos( itemPos );
if( ImplGetSalMenu() && pData && pData->pSalMenuItem )
ImplGetSalMenu()->InsertItem( pData->pSalMenuItem, nPos );
delete mpLayoutData, mpLayoutData = NULL;
ImplCallEventListeners( VCLEVENT_MENU_INSERTITEM, nPos );
}
void Menu::RemoveItem( sal_uInt16 nPos )
{
sal_Bool bRemove = sal_False;
if ( nPos < GetItemCount() )
{
// update native menu
if( ImplGetSalMenu() )
ImplGetSalMenu()->RemoveItem( nPos );
pItemList->Remove( nPos );
bRemove = sal_True;
}
Window* pWin = ImplGetWindow();
if ( pWin )
{
ImplCalcSize( pWin );
if ( pWin->IsVisible() )
pWin->Invalidate();
}
delete mpLayoutData, mpLayoutData = NULL;
if ( bRemove )
ImplCallEventListeners( VCLEVENT_MENU_REMOVEITEM, nPos );
}
void ImplCopyItem( Menu* pThis, const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos,
sal_uInt16 nMode = 0 )
{
MenuItemType eType = rMenu.GetItemType( nPos );
if ( eType == MENUITEM_DONTKNOW )
return;
if ( eType == MENUITEM_SEPARATOR )
pThis->InsertSeparator( nNewPos );
else
{
sal_uInt16 nId = rMenu.GetItemId( nPos );
DBG_ASSERT( pThis->GetItemPos( nId ) == MENU_ITEM_NOTFOUND,
"Menu::CopyItem(): ItemId already exists" );
MenuItemData* pData = rMenu.GetItemList()->GetData( nId );
if ( eType == MENUITEM_STRINGIMAGE )
pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, nNewPos );
else if ( eType == MENUITEM_STRING )
pThis->InsertItem( nId, pData->aText, pData->nBits, nNewPos );
else
pThis->InsertItem( nId, pData->aImage, pData->nBits, nNewPos );
if ( rMenu.IsItemChecked( nId ) )
pThis->CheckItem( nId, sal_True );
if ( !rMenu.IsItemEnabled( nId ) )
pThis->EnableItem( nId, sal_False );
pThis->SetHelpId( nId, pData->aHelpId );
pThis->SetHelpText( nId, pData->aHelpText );
pThis->SetAccelKey( nId, pData->aAccelKey );
pThis->SetItemCommand( nId, pData->aCommandStr );
pThis->SetHelpCommand( nId, pData->aHelpCommandStr );
PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId );
if ( pSubMenu )
{
// AutoKopie anlegen
if ( nMode == 1 )
{
PopupMenu* pNewMenu = new PopupMenu( *pSubMenu );
pThis->SetPopupMenu( nId, pNewMenu );
// SetAutoMenu( pThis, nId, pNewMenu );
}
else
pThis->SetPopupMenu( nId, pSubMenu );
}
}
}
void Menu::CopyItem( const Menu& rMenu, sal_uInt16 nPos, sal_uInt16 nNewPos )
{
ImplCopyItem( this, rMenu, nPos, nNewPos );
}
void Menu::Clear()
{
for ( sal_uInt16 i = GetItemCount(); i; i-- )
RemoveItem( 0 );
}
sal_uInt16 Menu::GetItemCount() const
{
return (sal_uInt16)pItemList->Count();
}
sal_uInt16 Menu::ImplGetVisibleItemCount() const
{
sal_uInt16 nItems = 0;
for ( sal_uInt16 n = (sal_uInt16)pItemList->Count(); n; )
{
if ( ImplIsVisible( --n ) )
nItems++;
}
return nItems;
}
sal_uInt16 Menu::ImplGetFirstVisible() const
{
for ( sal_uInt16 n = 0; n < pItemList->Count(); n++ )
{
if ( ImplIsVisible( n ) )
return n;
}
return ITEMPOS_INVALID;
}
sal_uInt16 Menu::ImplGetPrevVisible( sal_uInt16 nPos ) const
{
for ( sal_uInt16 n = nPos; n; )
{
if ( n && ImplIsVisible( --n ) )
return n;
}
return ITEMPOS_INVALID;
}
sal_uInt16 Menu::ImplGetNextVisible( sal_uInt16 nPos ) const
{
for ( sal_uInt16 n = nPos+1; n < pItemList->Count(); n++ )
{
if ( ImplIsVisible( n ) )
return n;
}
return ITEMPOS_INVALID;
}
sal_uInt16 Menu::GetItemId( sal_uInt16 nPos ) const
{
MenuItemData* pData = pItemList->GetDataFromPos( nPos );
if ( pData )
return pData->nId;
else
return 0;
}
sal_uInt16 Menu::GetItemPos( sal_uInt16 nItemId ) const
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( pData )
return nPos;
else
return MENU_ITEM_NOTFOUND;
}
MenuItemType Menu::GetItemType( sal_uInt16 nPos ) const
{
MenuItemData* pData = pItemList->GetDataFromPos( nPos );
if ( pData )
return pData->eType;
else
return MENUITEM_DONTKNOW;
}
sal_uInt16 Menu::GetCurItemId() const
{
return nSelectedId;
}
void Menu::SetHightlightItem( sal_uInt16 nHighlightedItem )
{
this->nHighlightedItem = nHighlightedItem;
}
sal_uInt16 Menu::GetHighlightItem() const
{
return nHighlightedItem;
}
XubString Menu::GetItemAccKeyStrFromPos(sal_uInt16 nPos) const
{
MenuItemData* pData = pItemList->GetDataFromPos( nPos );
if (pData)
{
return pData->aAccelKey.GetName();
}
return XubString();
}
sal_Bool Menu::IsTemporaryItemFromPos(sal_uInt16 nPos ) const
{
MenuItemData* pData = pItemList->GetDataFromPos( nPos );
if (pData)
{
return pData->bIsTemporary;
}
return sal_False;
}
void Menu::SetItemBits( sal_uInt16 nItemId, MenuItemBits nBits )
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
pData->nBits = nBits;
}
MenuItemBits Menu::GetItemBits( sal_uInt16 nItemId ) const
{
MenuItemBits nBits = 0;
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
nBits = pData->nBits;
return nBits;
}
void Menu::SetUserValue( sal_uInt16 nItemId, sal_uLong nValue )
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
pData->nUserValue = nValue;
}
sal_uLong Menu::GetUserValue( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
return pData ? pData->nUserValue : 0;
}
void Menu::SetPopupMenu( sal_uInt16 nItemId, PopupMenu* pMenu )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
// Item does not exist -> return NULL
if ( !pData )
return;
// same menu, nothing to do
if ( (PopupMenu*)pData->pSubMenu == pMenu )
return;
// data exchange
pData->pSubMenu = pMenu;
// #112023# Make sure pStartedFrom does not point to invalid (old) data
if ( pData->pSubMenu )
pData->pSubMenu->pStartedFrom = 0;
// set native submenu
if( ImplGetSalMenu() && pData->pSalMenuItem )
{
if( pMenu )
ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem, pMenu->ImplGetSalMenu(), nPos );
else
ImplGetSalMenu()->SetSubMenu( pData->pSalMenuItem, NULL, nPos );
}
ImplCallEventListeners( VCLEVENT_MENU_SUBMENUCHANGED, nPos );
}
PopupMenu* Menu::GetPopupMenu( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return (PopupMenu*)(pData->pSubMenu);
else
return NULL;
}
void Menu::SetAccelKey( sal_uInt16 nItemId, const KeyCode& rKeyCode )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( !pData )
return;
if ( pData->aAccelKey == rKeyCode )
return;
pData->aAccelKey = rKeyCode;
// update native menu
if( ImplGetSalMenu() && pData->pSalMenuItem )
ImplGetSalMenu()->SetAccelerator( nPos, pData->pSalMenuItem, rKeyCode, rKeyCode.GetName() );
}
KeyCode Menu::GetAccelKey( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->aAccelKey;
else
return KeyCode();
}
KeyEvent Menu::GetActivationKey( sal_uInt16 nItemId ) const
{
KeyEvent aRet;
MenuItemData* pData = pItemList->GetData( nItemId );
if( pData )
{
sal_uInt16 nPos = pData->aText.Search( '~' );
if( nPos != STRING_NOTFOUND && nPos < pData->aText.Len()-1 )
{
sal_uInt16 nCode = 0;
sal_Unicode cAccel = pData->aText.GetChar( nPos+1 );
if( cAccel >= 'a' && cAccel <= 'z' )
nCode = KEY_A + (cAccel-'a');
else if( cAccel >= 'A' && cAccel <= 'Z' )
nCode = KEY_A + (cAccel-'A');
else if( cAccel >= '0' && cAccel <= '9' )
nCode = KEY_0 + (cAccel-'0');
if(nCode )
aRet = KeyEvent( cAccel, KeyCode( nCode, KEY_MOD2 ) );
}
}
return aRet;
}
void Menu::CheckItem( sal_uInt16 nItemId, sal_Bool bCheck )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( !pData || pData->bChecked == bCheck )
return;
// Wenn RadioCheck, dann vorherigen unchecken
if ( bCheck && (pData->nBits & MIB_AUTOCHECK) &&
(pData->nBits & MIB_RADIOCHECK) )
{
MenuItemData* pGroupData;
sal_uInt16 nGroupPos;
sal_uInt16 nItemCount = GetItemCount();
sal_Bool bFound = sal_False;
nGroupPos = nPos;
while ( nGroupPos )
{
pGroupData = pItemList->GetDataFromPos( nGroupPos-1 );
if ( pGroupData->nBits & MIB_RADIOCHECK )
{
if ( IsItemChecked( pGroupData->nId ) )
{
CheckItem( pGroupData->nId, sal_False );
bFound = sal_True;
break;
}
}
else
break;
nGroupPos--;
}
if ( !bFound )
{
nGroupPos = nPos+1;
while ( nGroupPos < nItemCount )
{
pGroupData = pItemList->GetDataFromPos( nGroupPos );
if ( pGroupData->nBits & MIB_RADIOCHECK )
{
if ( IsItemChecked( pGroupData->nId ) )
{
CheckItem( pGroupData->nId, sal_False );
break;
}
}
else
break;
nGroupPos++;
}
}
}
pData->bChecked = bCheck;
// update native menu
if( ImplGetSalMenu() )
ImplGetSalMenu()->CheckItem( nPos, bCheck );
ImplCallEventListeners( bCheck ? VCLEVENT_MENU_ITEMCHECKED : VCLEVENT_MENU_ITEMUNCHECKED, nPos );
}
sal_Bool Menu::IsItemChecked( sal_uInt16 nItemId ) const
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( !pData )
return sal_False;
return pData->bChecked;
}
void Menu::EnableItem( sal_uInt16 nItemId, sal_Bool bEnable )
{
sal_uInt16 nPos;
MenuItemData* pItemData = pItemList->GetData( nItemId, nPos );
if ( pItemData && ( pItemData->bEnabled != bEnable ) )
{
pItemData->bEnabled = bEnable;
Window* pWin = ImplGetWindow();
if ( pWin && pWin->IsVisible() )
{
DBG_ASSERT( bIsMenuBar, "Menu::EnableItem - Popup visible!" );
long nX = 0;
sal_uLong nCount = pItemList->Count();
for ( sal_uLong n = 0; n < nCount; n++ )
{
MenuItemData* pData = pItemList->GetDataFromPos( n );
if ( n == nPos )
{
pWin->Invalidate( Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) );
break;
}
nX += pData->aSz.Width();
}
}
// update native menu
if( ImplGetSalMenu() )
ImplGetSalMenu()->EnableItem( nPos, bEnable );
ImplCallEventListeners( bEnable ? VCLEVENT_MENU_ENABLE : VCLEVENT_MENU_DISABLE, nPos );
}
}
sal_Bool Menu::IsItemEnabled( sal_uInt16 nItemId ) const
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( !pData )
return sal_False;
return pData->bEnabled;
}
void Menu::ShowItem( sal_uInt16 nItemId, sal_Bool bVisible )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
DBG_ASSERT( !bIsMenuBar, "Menu::ShowItem - ignored for menu bar entries!" );
if ( !bIsMenuBar && pData && ( pData->bVisible != bVisible ) )
{
Window* pWin = ImplGetWindow();
if ( pWin && pWin->IsVisible() )
{
DBG_ASSERT( 0, "Menu::ShowItem - ignored for visible popups!" );
return;
}
pData->bVisible = bVisible;
// update native menu
// as long as there is no support to hide native menu entries, we just disable them
// TODO: add support to show/hide native menu entries
if( ImplGetSalMenu() )
ImplGetSalMenu()->EnableItem( nPos, bVisible );
}
}
void Menu::SetItemText( sal_uInt16 nItemId, const XubString& rStr )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( !pData )
return;
if ( !rStr.Equals( pData->aText ) )
{
pData->aText = rStr;
ImplSetMenuItemData( pData );
// update native menu
if( ImplGetSalMenu() && pData->pSalMenuItem )
ImplGetSalMenu()->SetItemText( nPos, pData->pSalMenuItem, rStr );
Window* pWin = ImplGetWindow();
delete mpLayoutData, mpLayoutData = NULL;
if ( pWin && IsMenuBar() )
{
ImplCalcSize( pWin );
if ( pWin->IsVisible() )
pWin->Invalidate();
}
ImplCallEventListeners( VCLEVENT_MENU_ITEMTEXTCHANGED, nPos );
}
}
XubString Menu::GetItemText( sal_uInt16 nItemId ) const
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( pData )
return pData->aText;
else
return ImplGetSVEmptyStr();
}
void Menu::SetItemImage( sal_uInt16 nItemId, const Image& rImage )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( !pData )
return;
pData->aImage = rImage;
ImplSetMenuItemData( pData );
// update native menu
if( ImplGetSalMenu() && pData->pSalMenuItem )
ImplGetSalMenu()->SetItemImage( nPos, pData->pSalMenuItem, rImage );
}
static inline Image ImplRotImage( const Image& rImage, long nAngle10 )
{
Image aRet;
BitmapEx aBmpEx( rImage.GetBitmapEx() );
aBmpEx.Rotate( nAngle10, COL_WHITE );
return Image( aBmpEx );
}
void Menu::SetItemImageAngle( sal_uInt16 nItemId, long nAngle10 )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( pData )
{
long nDeltaAngle = (nAngle10 - pData->nItemImageAngle) % 3600;
while( nDeltaAngle < 0 )
nDeltaAngle += 3600;
pData->nItemImageAngle = nAngle10;
if( nDeltaAngle && !!pData->aImage )
pData->aImage = ImplRotImage( pData->aImage, nDeltaAngle );
}
}
static inline Image ImplMirrorImage( const Image& rImage )
{
Image aRet;
BitmapEx aBmpEx( rImage.GetBitmapEx() );
aBmpEx.Mirror( BMP_MIRROR_HORZ );
return Image( aBmpEx );
}
void Menu::SetItemImageMirrorMode( sal_uInt16 nItemId, sal_Bool bMirror )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( pData )
{
if( ( pData->bMirrorMode && ! bMirror ) ||
( ! pData->bMirrorMode && bMirror )
)
{
pData->bMirrorMode = bMirror ? true : false;
if( !!pData->aImage )
pData->aImage = ImplMirrorImage( pData->aImage );
}
}
}
Image Menu::GetItemImage( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->aImage;
else
return Image();
}
long Menu::GetItemImageAngle( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->nItemImageAngle;
else
return 0;
}
sal_Bool Menu::GetItemImageMirrorMode( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->bMirrorMode;
else
return sal_False;
}
void Menu::SetItemCommand( sal_uInt16 nItemId, const String& rCommand )
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
pData->aCommandStr = rCommand;
}
const XubString& Menu::GetItemCommand( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->aCommandStr;
else
return ImplGetSVEmptyStr();
}
void Menu::SetHelpCommand( sal_uInt16 nItemId, const XubString& rStr )
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
pData->aHelpCommandStr = rStr;
}
const XubString& Menu::GetHelpCommand( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->aHelpCommandStr;
else
return ImplGetSVEmptyStr();
}
void Menu::SetHelpText( sal_uInt16 nItemId, const XubString& rStr )
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
pData->aHelpText = rStr;
}
const XubString& Menu::ImplGetHelpText( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
{
if ( !pData->aHelpText.Len() &&
(( pData->aHelpId.getLength() ) || ( pData->aCommandStr.Len() )))
{
Help* pHelp = Application::GetHelp();
if ( pHelp )
{
if ( pData->aCommandStr.Len() )
pData->aHelpText = pHelp->GetHelpText( pData->aCommandStr, NULL );
if( !pData->aHelpText.Len() && pData->aHelpId.getLength() )
pData->aHelpText = pHelp->GetHelpText( rtl::OStringToOUString( pData->aHelpId, RTL_TEXTENCODING_UTF8 ), NULL );
}
}
return pData->aHelpText;
}
else
return ImplGetSVEmptyStr();
}
const XubString& Menu::GetHelpText( sal_uInt16 nItemId ) const
{
return ImplGetHelpText( nItemId );
}
void Menu::SetTipHelpText( sal_uInt16 nItemId, const XubString& rStr )
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
pData->aTipHelpText = rStr;
}
const XubString& Menu::GetTipHelpText( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->aTipHelpText;
else
return ImplGetSVEmptyStr();
}
void Menu::SetHelpId( sal_uInt16 nItemId, const rtl::OString& rHelpId )
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
pData->aHelpId = rHelpId;
}
rtl::OString Menu::GetHelpId( sal_uInt16 nItemId ) const
{
rtl::OString aRet;
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
{
if ( pData->aHelpId.getLength() )
aRet = pData->aHelpId;
else
aRet = ::rtl::OUStringToOString( pData->aCommandStr, RTL_TEXTENCODING_UTF8 );
}
return aRet;
}
Menu& Menu::operator=( const Menu& rMenu )
{
// Aufraeumen
Clear();
// Items kopieren
sal_uInt16 nCount = rMenu.GetItemCount();
for ( sal_uInt16 i = 0; i < nCount; i++ )
ImplCopyItem( this, rMenu, i, MENU_APPEND, 1 );
nDefaultItem = rMenu.nDefaultItem;
aActivateHdl = rMenu.aActivateHdl;
aDeactivateHdl = rMenu.aDeactivateHdl;
aHighlightHdl = rMenu.aHighlightHdl;
aSelectHdl = rMenu.aSelectHdl;
aTitleText = rMenu.aTitleText;
bIsMenuBar = rMenu.bIsMenuBar;
return *this;
}
sal_Bool Menu::ImplIsVisible( sal_uInt16 nPos ) const
{
sal_Bool bVisible = sal_True;
MenuItemData* pData = pItemList->GetDataFromPos( nPos );
// check general visibility first
if( pData && !pData->bVisible )
bVisible = sal_False;
if ( bVisible && pData && pData->eType == MENUITEM_SEPARATOR )
{
if( nPos == 0 ) // no separator should be shown at the very beginning
bVisible = sal_False;
else
{
// always avoid adjacent separators
sal_uInt16 nCount = (sal_uInt16) pItemList->Count();
sal_uInt16 n;
MenuItemData* pNextData = NULL;
// search next visible item
for( n = nPos + 1; n < nCount; n++ )
{
pNextData = pItemList->GetDataFromPos( n );
if( pNextData && pNextData->bVisible )
{
if( pNextData->eType == MENUITEM_SEPARATOR || ImplIsVisible(n) )
break;
}
}
if( n == nCount ) // no next visible item
bVisible = sal_False;
// check for separator
if( pNextData && pNextData->bVisible && pNextData->eType == MENUITEM_SEPARATOR )
bVisible = sal_False;
if( bVisible )
{
for( n = nPos; n > 0; n-- )
{
pNextData = pItemList->GetDataFromPos( n-1 );
if( pNextData && pNextData->bVisible )
{
if( pNextData->eType != MENUITEM_SEPARATOR && ImplIsVisible(n-1) )
break;
}
}
if( n == 0 ) // no previous visible item
bVisible = sal_False;
}
}
}
// Fuer den Menubar nicht erlaubt, weil ich nicht mitbekomme
// ob dadurch ein Eintrag verschwindet oder wieder da ist.
if ( bVisible && !bIsMenuBar && ( nMenuFlags & MENU_FLAG_HIDEDISABLEDENTRIES ) &&
!( nMenuFlags & MENU_FLAG_ALWAYSSHOWDISABLEDENTRIES ) )
{
if( !pData ) // e.g. nPos == ITEMPOS_INVALID
bVisible = sal_False;
else if ( pData->eType != MENUITEM_SEPARATOR ) // separators handled above
{
// bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( sal_True ) );
bVisible = pData->bEnabled; // SubMenus nicht pruefen, weil sie ggf. erst im Activate() gefuellt werden.
}
}
return bVisible;
}
sal_Bool Menu::IsItemVisible( sal_uInt16 nItemId ) const
{
return IsMenuVisible() && ImplIsVisible( GetItemPos( nItemId ) );
}
sal_Bool Menu::IsItemPosVisible( sal_uInt16 nItemPos ) const
{
return IsMenuVisible() && ImplIsVisible( nItemPos );
}
sal_Bool Menu::IsMenuVisible() const
{
return pWindow && pWindow->IsReallyVisible();
}
sal_Bool Menu::ImplIsSelectable( sal_uInt16 nPos ) const
{
sal_Bool bSelectable = sal_True;
MenuItemData* pData = pItemList->GetDataFromPos( nPos );
// check general visibility first
if ( pData && ( pData->nBits & MIB_NOSELECT ) )
bSelectable = sal_False;
return bSelectable;
}
void Menu::SelectItem( sal_uInt16 nItemId )
{
if( bIsMenuBar )
static_cast<MenuBar*>(this)->SelectEntry( nItemId );
else
static_cast<PopupMenu*>(this)->SelectEntry( nItemId );
}
::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > Menu::GetAccessible()
{
// Since PopupMenu are sometimes shared by different instances of MenuBar, the mxAccessible member gets
// overwritten and may contain a disposed object when the initial menubar gets set again. So use the
// mxAccessible member only for sub menus.
if ( pStartedFrom )
{
for ( sal_uInt16 i = 0, nCount = pStartedFrom->GetItemCount(); i < nCount; ++i )
{
sal_uInt16 nItemId = pStartedFrom->GetItemId( i );
if ( static_cast< Menu* >( pStartedFrom->GetPopupMenu( nItemId ) ) == this )
{
::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > xParent = pStartedFrom->GetAccessible();
if ( xParent.is() )
{
::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessibleContext > xParentContext( xParent->getAccessibleContext() );
if ( xParentContext.is() )
return xParentContext->getAccessibleChild( i );
}
}
}
}
else if ( !mxAccessible.is() )
{
UnoWrapperBase* pWrapper = Application::GetUnoWrapper();
if ( pWrapper )
mxAccessible = pWrapper->CreateAccessible( this, bIsMenuBar );
}
return mxAccessible;
}
void Menu::SetAccessible( const ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible >& rxAccessible )
{
mxAccessible = rxAccessible;
}
long Menu::ImplGetNativeCheckAndRadioSize( Window* pWin, long& rCheckHeight, long& rRadioHeight, long &rMaxWidth ) const
{
rMaxWidth = rCheckHeight = rRadioHeight = 0;
if( ! bIsMenuBar )
{
ImplControlValue aVal;
Rectangle aNativeBounds;
Rectangle aNativeContent;
Point tmp( 0, 0 );
Rectangle aCtrlRegion( Rectangle( tmp, Size( 100, 15 ) ) );
if( pWin->IsNativeControlSupported( CTRL_MENU_POPUP, PART_MENU_ITEM_CHECK_MARK ) )
{
if( pWin->GetNativeControlRegion( ControlType(CTRL_MENU_POPUP),
ControlPart(PART_MENU_ITEM_CHECK_MARK),
aCtrlRegion,
ControlState(CTRL_STATE_ENABLED),
aVal,
OUString(),
aNativeBounds,
aNativeContent )
)
{
rCheckHeight = aNativeBounds.GetHeight();
rMaxWidth = aNativeContent.GetWidth();
}
}
if( pWin->IsNativeControlSupported( CTRL_MENU_POPUP, PART_MENU_ITEM_RADIO_MARK ) )
{
if( pWin->GetNativeControlRegion( ControlType(CTRL_MENU_POPUP),
ControlPart(PART_MENU_ITEM_RADIO_MARK),
aCtrlRegion,
ControlState(CTRL_STATE_ENABLED),
aVal,
OUString(),
aNativeBounds,
aNativeContent )
)
{
rRadioHeight = aNativeBounds.GetHeight();
rMaxWidth = Max (rMaxWidth, aNativeContent.GetWidth());
}
}
}
return (rCheckHeight > rRadioHeight) ? rCheckHeight : rRadioHeight;
}
// -----------------------------------------------------------------------
void Menu::ImplAddDel( ImplMenuDelData& rDel )
{
DBG_ASSERT( !rDel.mpMenu, "Menu::ImplAddDel(): cannot add ImplMenuDelData twice !" );
if( !rDel.mpMenu )
{
rDel.mpMenu = this;
rDel.mpNext = mpFirstDel;
mpFirstDel = &rDel;
}
}
// -----------------------------------------------------------------------
void Menu::ImplRemoveDel( ImplMenuDelData& rDel )
{
rDel.mpMenu = NULL;
if ( mpFirstDel == &rDel )
{
mpFirstDel = rDel.mpNext;
}
else
{
ImplMenuDelData* pData = mpFirstDel;
while ( pData && (pData->mpNext != &rDel) )
pData = pData->mpNext;
DBG_ASSERT( pData, "Menu::ImplRemoveDel(): ImplMenuDelData not registered !" );
if( pData )
pData->mpNext = rDel.mpNext;
}
}
// -----------------------------------------------------------------------
Size Menu::ImplCalcSize( Window* pWin )
{
// | Checked| Image| Text| Accel/Popup|
// Fuer Symbole: nFontHeight x nFontHeight
long nFontHeight = pWin->GetTextHeight();
long nExtra = nFontHeight/4;
Size aSz;
Size aMaxImgSz;
long nMaxWidth = 0;
long nMinMenuItemHeight = nFontHeight;
long nCheckHeight = 0, nRadioHeight = 0;
long nCheckWidth = 0, nMaxCheckWidth = 0;
long nMax = ImplGetNativeCheckAndRadioSize( pWin, nCheckHeight, nRadioHeight, nMaxCheckWidth );
if( nMax > nMinMenuItemHeight )
nMinMenuItemHeight = nMax;
const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings();
if ( rSettings.GetUseImagesInMenus() )
{
nMinMenuItemHeight = 16;
for ( sal_uInt16 i = (sal_uInt16)pItemList->Count(); i; )
{
MenuItemData* pData = pItemList->GetDataFromPos( --i );
if ( ImplIsVisible( i ) && (( pData->eType == MENUITEM_IMAGE ) || ( pData->eType == MENUITEM_STRINGIMAGE )))
{
Size aImgSz = pData->aImage.GetSizePixel();
if ( aImgSz.Height() > aMaxImgSz.Height() )
aMaxImgSz.Height() = aImgSz.Height();
if ( aImgSz.Height() > nMinMenuItemHeight )
nMinMenuItemHeight = aImgSz.Height();
break;
}
}
}
for ( sal_uInt16 n = (sal_uInt16)pItemList->Count(); n; )
{
MenuItemData* pData = pItemList->GetDataFromPos( --n );
pData->aSz.Height() = 0;
pData->aSz.Width() = 0;
if ( ImplIsVisible( n ) )
{
long nWidth = 0;
// Separator
if ( !bIsMenuBar && ( pData->eType == MENUITEM_SEPARATOR ) )
{
DBG_ASSERT( !bIsMenuBar, "Separator in MenuBar ?! " );
pData->aSz.Height() = 4;
}
// Image:
if ( !bIsMenuBar && ( ( pData->eType == MENUITEM_IMAGE ) || ( pData->eType == MENUITEM_STRINGIMAGE ) ) )
{
Size aImgSz = pData->aImage.GetSizePixel();
aImgSz.Height() += 4; // add a border for native marks
aImgSz.Width() += 4; // add a border for native marks
if ( aImgSz.Width() > aMaxImgSz.Width() )
aMaxImgSz.Width() = aImgSz.Width();
if ( aImgSz.Height() > aMaxImgSz.Height() )
aMaxImgSz.Height() = aImgSz.Height();
if ( aImgSz.Height() > pData->aSz.Height() )
pData->aSz.Height() = aImgSz.Height();
}
// Check Buttons:
if ( !bIsMenuBar && pData->HasCheck() )
{
nCheckWidth = nMaxCheckWidth;
if (nMenuFlags & MENU_FLAG_SHOWCHECKIMAGES)
{
// checks / images take the same place
if( ! ( ( pData->eType == MENUITEM_IMAGE ) || ( pData->eType == MENUITEM_STRINGIMAGE ) ) )
nWidth += nCheckWidth + nExtra * 2;
}
}
// Text:
if ( (pData->eType == MENUITEM_STRING) || (pData->eType == MENUITEM_STRINGIMAGE) )
{
long nTextWidth = pWin->GetCtrlTextWidth( pData->aText );
long nTextHeight = pWin->GetTextHeight();
// if ( nTextHeight > pData->aSz.Height() )
// pData->aSz.Height() = nTextHeight;
if ( bIsMenuBar )
{
if ( nTextHeight > pData->aSz.Height() )
pData->aSz.Height() = nTextHeight;
pData->aSz.Width() = nTextWidth + 4*nExtra;
aSz.Width() += pData->aSz.Width();
}
else
pData->aSz.Height() = Max( Max( nTextHeight, pData->aSz.Height() ), nMinMenuItemHeight );
nWidth += nTextWidth;
}
// Accel
if ( !bIsMenuBar && pData->aAccelKey.GetCode() && !ImplAccelDisabled() )
{
String aName = pData->aAccelKey.GetName();
long nAccWidth = pWin->GetTextWidth( aName );
nAccWidth += nExtra;
nWidth += nAccWidth;
}
// SubMenu?
if ( !bIsMenuBar && pData->pSubMenu )
{
if ( nFontHeight > nWidth )
nWidth += nFontHeight;
pData->aSz.Height() = Max( Max( nFontHeight, pData->aSz.Height() ), nMinMenuItemHeight );
}
pData->aSz.Height() += EXTRAITEMHEIGHT; // Etwas mehr Abstand:
if ( !bIsMenuBar )
aSz.Height() += (long)pData->aSz.Height();
if ( nWidth > nMaxWidth )
nMaxWidth = nWidth;
}
}
if ( !bIsMenuBar )
{
// popup menus should not be wider than half the screen
// except on rather small screens
// TODO: move GetScreenNumber from SystemWindow to Window ?
// currently we rely on internal privileges
unsigned int nScreenNumber = pWin->ImplGetWindowImpl()->mpFrame->maGeometry.nScreenNumber;
Rectangle aDispRect( Application::GetScreenPosSizePixel( nScreenNumber ) );
long nScreenWidth = aDispRect.GetWidth() >= 800 ? aDispRect.GetWidth() : 800;
if( nMaxWidth > nScreenWidth/2 )
nMaxWidth = nScreenWidth/2;
sal_uInt16 gfxExtra = (sal_uInt16) Max( nExtra, 7L ); // #107710# increase space between checkmarks/images/text
nCheckPos = (sal_uInt16)nExtra;
if (nMenuFlags & MENU_FLAG_SHOWCHECKIMAGES)
{
long nImgOrChkWidth = 0;
nImagePos = nCheckPos;
if( nMax > 0 ) // NWF case
nImgOrChkWidth = nMax + nExtra;
else // non NWF case
nImgOrChkWidth = nFontHeight/2 + gfxExtra;
nImgOrChkWidth = Max( nImgOrChkWidth, aMaxImgSz.Width() + gfxExtra );
nTextPos = (sal_uInt16)(nImagePos + nImgOrChkWidth);
}
else
{
nImagePos = nCheckPos;
nTextPos = (sal_uInt16)(nImagePos + Max( aMaxImgSz.Width(), nCheckWidth ));
}
nTextPos = nTextPos + gfxExtra;
aSz.Width() = nTextPos + nMaxWidth + nExtra;
aSz.Width() += 4*nExtra; // a _little_ more ...
int nOuterSpace = ImplGetSVData()->maNWFData.mnMenuFormatExtraBorder;
aSz.Width() += 2*nOuterSpace;
aSz.Height() += 2*nOuterSpace;
}
else
{
nTextPos = (sal_uInt16)(2*nExtra);
aSz.Height() = nFontHeight+6;
// get menubar height from native methods if supported
if( pWindow->IsNativeControlSupported( CTRL_MENUBAR, PART_ENTIRE_CONTROL ) )
{
ImplControlValue aVal;
Rectangle aNativeBounds;
Rectangle aNativeContent;
Point tmp( 0, 0 );
Rectangle aCtrlRegion( tmp, Size( 100, 15 ) );
if( pWindow->GetNativeControlRegion( ControlType(CTRL_MENUBAR),
ControlPart(PART_ENTIRE_CONTROL),
aCtrlRegion,
ControlState(CTRL_STATE_ENABLED),
aVal,
OUString(),
aNativeBounds,
aNativeContent )
)
{
int nNativeHeight = aNativeBounds.GetHeight();
if( nNativeHeight > aSz.Height() )
aSz.Height() = nNativeHeight;
}
}
// account for the size of the close button, which actually is a toolbox
// due to NWF this is variable
long nCloserHeight = ((MenuBarWindow*) pWindow)->MinCloseButtonSize().Height();
if( aSz.Height() < nCloserHeight )
aSz.Height() = nCloserHeight;
}
if ( pLogo )
aSz.Width() += pLogo->aBitmap.GetSizePixel().Width();
return aSz;
}
static void ImplPaintCheckBackground( Window* i_pWindow, const Rectangle& i_rRect, bool i_bHighlight )
{
sal_Bool bNativeOk = sal_False;
if( i_pWindow->IsNativeControlSupported( CTRL_TOOLBAR, PART_BUTTON ) )
{
ImplControlValue aControlValue;
Rectangle aCtrlRegion( i_rRect );
ControlState nState = CTRL_STATE_PRESSED | CTRL_STATE_ENABLED;
aControlValue.setTristateVal( BUTTONVALUE_ON );
bNativeOk = i_pWindow->DrawNativeControl( CTRL_TOOLBAR, PART_BUTTON,
aCtrlRegion, nState, aControlValue,
rtl::OUString() );
}
if( ! bNativeOk )
{
const StyleSettings& rSettings = i_pWindow->GetSettings().GetStyleSettings();
Color aColor( i_bHighlight ? rSettings.GetMenuHighlightTextColor() : rSettings.GetHighlightColor() );
i_pWindow->DrawSelectionBackground( i_rRect, 0, i_bHighlight, sal_True, sal_False, 2, NULL, &aColor );
}
}
static String getShortenedString( const String& i_rLong, Window* i_pWin, long i_nMaxWidth )
{
xub_StrLen nPos = STRING_NOTFOUND;
String aNonMnem( OutputDevice::GetNonMnemonicString( i_rLong, nPos ) );
aNonMnem = i_pWin->GetEllipsisString( aNonMnem, i_nMaxWidth, TEXT_DRAW_CENTERELLIPSIS );
// re-insert mnemonic
if( nPos != STRING_NOTFOUND )
{
if( nPos < aNonMnem.Len() && i_rLong.GetChar(nPos+1) == aNonMnem.GetChar(nPos) )
{
rtl::OUStringBuffer aBuf( i_rLong.Len() );
aBuf.append( aNonMnem.GetBuffer(), nPos );
aBuf.append( sal_Unicode('~') );
aBuf.append( aNonMnem.GetBuffer()+nPos );
aNonMnem = aBuf.makeStringAndClear();
}
}
return aNonMnem;
}
void Menu::ImplPaint( Window* pWin, sal_uInt16 nBorder, long nStartY, MenuItemData* pThisItemOnly, sal_Bool bHighlighted, bool bLayout ) const
{
// Fuer Symbole: nFontHeight x nFontHeight
long nFontHeight = pWin->GetTextHeight();
long nExtra = nFontHeight/4;
long nCheckHeight = 0, nRadioHeight = 0, nMaxCheckWidth = 0;
ImplGetNativeCheckAndRadioSize( pWin, nCheckHeight, nRadioHeight, nMaxCheckWidth );
DecorationView aDecoView( pWin );
const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings();
Point aTopLeft, aTmpPos;
if ( pLogo )
aTopLeft.X() = pLogo->aBitmap.GetSizePixel().Width();
int nOuterSpace = 0;
if( !bIsMenuBar )
{
nOuterSpace = ImplGetSVData()->maNWFData.mnMenuFormatExtraBorder;
aTopLeft.X() += nOuterSpace;
aTopLeft.Y() += nOuterSpace;
}
Size aOutSz = pWin->GetOutputSizePixel();
sal_uInt16 nCount = (sal_uInt16)pItemList->Count();
if( bLayout )
mpLayoutData->m_aVisibleItemBoundRects.clear();
for ( sal_uInt16 n = 0; n < nCount; n++ )
{
MenuItemData* pData = pItemList->GetDataFromPos( n );
if ( ImplIsVisible( n ) && ( !pThisItemOnly || ( pData == pThisItemOnly ) ) )
{
if ( pThisItemOnly && bHighlighted )
pWin->SetTextColor( rSettings.GetMenuHighlightTextColor() );
Point aPos( aTopLeft );
aPos.Y() += nBorder;
aPos.Y() += nStartY;
if ( aPos.Y() >= 0 )
{
long nTextOffsetY = ((pData->aSz.Height()-nFontHeight)/2);
if( bIsMenuBar )
nTextOffsetY += (aOutSz.Height()-pData->aSz.Height()) / 2;
sal_uInt16 nTextStyle = 0;
sal_uInt16 nSymbolStyle = 0;
sal_uInt16 nImageStyle = 0;
// SubMenus ohne Items werden nicht mehr disablte dargestellt,
// wenn keine Items enthalten sind, da die Anwendung selber
// darauf achten muss. Ansonsten gibt es Faelle, wo beim
// asyncronen laden die Eintraege disablte dargestellt werden.
if ( !pData->bEnabled )
{
nTextStyle |= TEXT_DRAW_DISABLE;
nSymbolStyle |= SYMBOL_DRAW_DISABLE;
nImageStyle |= IMAGE_DRAW_DISABLE;
}
// Separator
if ( !bLayout && !bIsMenuBar && ( pData->eType == MENUITEM_SEPARATOR ) )
{
bool bNativeOk = false;
if( pWin->IsNativeControlSupported( CTRL_MENU_POPUP,
PART_MENU_SEPARATOR ) )
{
ControlState nState = 0;
if ( pData->bEnabled )
nState |= CTRL_STATE_ENABLED;
if ( bHighlighted )
nState |= CTRL_STATE_SELECTED;
Size aSz( pData->aSz );
aSz.Width() = aOutSz.Width() - 2*nOuterSpace;
Rectangle aItemRect( aPos, aSz );
MenupopupValue aVal( nTextPos-GUTTERBORDER, aItemRect );
bNativeOk = pWin->DrawNativeControl( CTRL_MENU_POPUP, PART_MENU_SEPARATOR,
aItemRect,
nState,
aVal,
OUString() );
}
if( ! bNativeOk )
{
aTmpPos.Y() = aPos.Y() + ((pData->aSz.Height()-2)/2);
aTmpPos.X() = aPos.X() + 2 + nOuterSpace;
pWin->SetLineColor( rSettings.GetShadowColor() );
pWin->DrawLine( aTmpPos, Point( aOutSz.Width() - 3 - 2*nOuterSpace, aTmpPos.Y() ) );
aTmpPos.Y()++;
pWin->SetLineColor( rSettings.GetLightColor() );
pWin->DrawLine( aTmpPos, Point( aOutSz.Width() - 3 - 2*nOuterSpace, aTmpPos.Y() ) );
pWin->SetLineColor();
}
}
Rectangle aOuterCheckRect( Point( aPos.X()+nCheckPos, aPos.Y() ), Size( pData->aSz.Height(), pData->aSz.Height() ) );
aOuterCheckRect.Left() += 1;
aOuterCheckRect.Right() -= 1;
aOuterCheckRect.Top() += 1;
aOuterCheckRect.Bottom() -= 1;
// CheckMark
if ( !bLayout && !bIsMenuBar && pData->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( ! ( ( pData->eType == MENUITEM_IMAGE ) || ( pData->eType == MENUITEM_STRINGIMAGE ) ) )
{
if ( pWin->IsNativeControlSupported( CTRL_MENU_POPUP,
(pData->nBits & MIB_RADIOCHECK)
? PART_MENU_ITEM_CHECK_MARK
: PART_MENU_ITEM_RADIO_MARK ) )
{
ControlPart nPart = ((pData->nBits & MIB_RADIOCHECK)
? PART_MENU_ITEM_RADIO_MARK
: PART_MENU_ITEM_CHECK_MARK);
ControlState nState = 0;
if ( pData->bChecked )
nState |= CTRL_STATE_PRESSED;
if ( pData->bEnabled )
nState |= CTRL_STATE_ENABLED;
if ( bHighlighted )
nState |= CTRL_STATE_SELECTED;
long nCtrlHeight = (pData->nBits & MIB_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 ) );
MenupopupValue aVal( nTextPos-GUTTERBORDER, Rectangle( aPos, pData->aSz ) );
pWin->DrawNativeControl( CTRL_MENU_POPUP, nPart,
aCheckRect,
nState,
aVal,
OUString() );
}
else if ( pData->bChecked ) // by default do nothing for unchecked items
{
ImplPaintCheckBackground( pWin, aOuterCheckRect, pThisItemOnly && bHighlighted );
SymbolType eSymbol;
Size aSymbolSize;
if ( pData->nBits & MIB_RADIOCHECK )
{
eSymbol = SYMBOL_RADIOCHECKMARK;
aSymbolSize = Size( nFontHeight/2, nFontHeight/2 );
}
else
{
eSymbol = SYMBOL_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, pWin->GetTextColor(), nSymbolStyle );
}
}
}
// Image:
if ( !bLayout && !bIsMenuBar && ( ( pData->eType == MENUITEM_IMAGE ) || ( pData->eType == MENUITEM_STRINGIMAGE ) ) )
{
// Don't render an image for a check thing
if ((nMenuFlags & MENU_FLAG_SHOWCHECKIMAGES) || !pData->HasCheck() )
{
if( pData->bChecked )
ImplPaintCheckBackground( pWin, aOuterCheckRect, pThisItemOnly && bHighlighted );
aTmpPos = aOuterCheckRect.TopLeft();
aTmpPos.X() += (aOuterCheckRect.GetWidth()-pData->aImage.GetSizePixel().Width())/2;
aTmpPos.Y() += (aOuterCheckRect.GetHeight()-pData->aImage.GetSizePixel().Height())/2;
pWin->DrawImage( aTmpPos, pData->aImage, nImageStyle );
}
}
// Text:
if ( ( pData->eType == MENUITEM_STRING ) || ( pData->eType == MENUITEM_STRINGIMAGE ) )
{
aTmpPos.X() = aPos.X() + nTextPos;
aTmpPos.Y() = aPos.Y();
aTmpPos.Y() += nTextOffsetY;
sal_uInt16 nStyle = nTextStyle|TEXT_DRAW_MNEMONIC;
if ( pData->bIsTemporary )
nStyle |= TEXT_DRAW_DISABLE;
MetricVector* pVector = bLayout ? &mpLayoutData->m_aUnicodeBoundRects : NULL;
String* pDisplayText = bLayout ? &mpLayoutData->m_aDisplayText : NULL;
if( bLayout )
{
mpLayoutData->m_aLineIndices.push_back( mpLayoutData->m_aDisplayText.Len() );
mpLayoutData->m_aLineItemIds.push_back( pData->nId );
mpLayoutData->m_aLineItemPositions.push_back( n );
}
// #i47946# with NWF painted menus the background is transparent
// since DrawCtrlText can depend on the background (e.g. for
// TEXT_DRAW_DISABLE), temporarily set a background which
// hopefully matches the NWF background since it is read
// from the system style settings
bool bSetTmpBackground = !pWin->IsBackground() && pWin->IsNativeControlSupported( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL );
if( bSetTmpBackground )
{
Color aBg = bIsMenuBar ?
pWin->GetSettings().GetStyleSettings().GetMenuBarColor() :
pWin->GetSettings().GetStyleSettings().GetMenuColor();
pWin->SetBackground( Wallpaper( aBg ) );
}
// how much space is there for the text ?
long nMaxItemTextWidth = aOutSz.Width() - aTmpPos.X() - nExtra - nOuterSpace;
if( !bIsMenuBar && pData->aAccelKey.GetCode() && !ImplAccelDisabled() )
{
XubString aAccText = pData->aAccelKey.GetName();
nMaxItemTextWidth -= pWin->GetTextWidth( aAccText ) + 3*nExtra;
}
if( !bIsMenuBar && pData->pSubMenu )
{
nMaxItemTextWidth -= nFontHeight - nExtra;
}
String aItemText( getShortenedString( pData->aText, pWin, nMaxItemTextWidth ) );
pWin->DrawCtrlText( aTmpPos, aItemText, 0, aItemText.Len(), nStyle, pVector, pDisplayText );
if( bSetTmpBackground )
pWin->SetBackground();
}
// Accel
if ( !bLayout && !bIsMenuBar && pData->aAccelKey.GetCode() && !ImplAccelDisabled() )
{
XubString aAccText = pData->aAccelKey.GetName();
aTmpPos.X() = aOutSz.Width() - pWin->GetTextWidth( aAccText );
aTmpPos.X() -= 4*nExtra;
aTmpPos.X() -= nOuterSpace;
aTmpPos.Y() = aPos.Y();
aTmpPos.Y() += nTextOffsetY;
pWin->DrawCtrlText( aTmpPos, aAccText, 0, aAccText.Len(), nTextStyle );
}
// SubMenu?
if ( !bLayout && !bIsMenuBar && pData->pSubMenu )
{
aTmpPos.X() = aOutSz.Width() - nFontHeight + nExtra - nOuterSpace;
aTmpPos.Y() = aPos.Y();
aTmpPos.Y() += nExtra/2;
aTmpPos.Y() += ( pData->aSz.Height() / 2 ) - ( nFontHeight/4 );
if ( pData->nBits & MIB_POPUPSELECT )
{
pWin->SetTextColor( rSettings.GetMenuTextColor() );
Point aTmpPos2( aPos );
aTmpPos2.X() = aOutSz.Width() - nFontHeight - nFontHeight/4;
aDecoView.DrawFrame(
Rectangle( aTmpPos2, Size( nFontHeight+nFontHeight/4, pData->aSz.Height() ) ), FRAME_DRAW_GROUP );
}
aDecoView.DrawSymbol(
Rectangle( aTmpPos, Size( nFontHeight/2, nFontHeight/2 ) ),
SYMBOL_SPIN_RIGHT, pWin->GetTextColor(), nSymbolStyle );
}
if ( pThisItemOnly && bHighlighted )
{
// This restores the normal menu or menu bar text
// color for when it is no longer highlighted.
if ( bIsMenuBar )
pWin->SetTextColor( rSettings.GetMenuBarTextColor() );
else
pWin->SetTextColor( rSettings.GetMenuTextColor() );
}
}
if( bLayout )
{
if ( !bIsMenuBar )
mpLayoutData->m_aVisibleItemBoundRects[ n ] = Rectangle( aTopLeft, Size( aOutSz.Width(), pData->aSz.Height() ) );
else
mpLayoutData->m_aVisibleItemBoundRects[ n ] = Rectangle( aTopLeft, pData->aSz );
}
}
if ( !bIsMenuBar )
{
aTopLeft.Y() += pData->aSz.Height();
}
else
{
aTopLeft.X() += pData->aSz.Width();
}
}
if ( !bLayout && !pThisItemOnly && pLogo )
{
Size aLogoSz = pLogo->aBitmap.GetSizePixel();
Rectangle aRect( Point( 0, 0 ), Point( aLogoSz.Width()-1, aOutSz.Height() ) );
if ( pWin->GetColorCount() >= 256 )
{
Gradient aGrad( GRADIENT_LINEAR, pLogo->aStartColor, pLogo->aEndColor );
aGrad.SetAngle( 1800 );
aGrad.SetBorder( 15 );
pWin->DrawGradient( aRect, aGrad );
}
else
{
pWin->SetFillColor( pLogo->aStartColor );
pWin->DrawRect( aRect );
}
Point aLogoPos( 0, aOutSz.Height() - aLogoSz.Height() );
pLogo->aBitmap.Draw( pWin, aLogoPos );
}
}
Menu* Menu::ImplGetStartMenu()
{
Menu* pStart = this;
while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) )
pStart = pStart->pStartedFrom;
return pStart;
}
void Menu::ImplCallHighlight( sal_uInt16 nHighlightedItem )
{
ImplMenuDelData aDelData( this );
nSelectedId = 0;
MenuItemData* pData = pItemList->GetDataFromPos( nHighlightedItem );
if ( pData )
nSelectedId = pData->nId;
ImplCallEventListeners( VCLEVENT_MENU_HIGHLIGHT, GetItemPos( GetCurItemId() ) );
if( !aDelData.isDeleted() )
{
Highlight();
nSelectedId = 0;
}
}
IMPL_LINK( Menu, ImplCallSelect, Menu*, EMPTYARG )
{
nEventId = 0;
Select();
return 0;
}
Menu* Menu::ImplFindSelectMenu()
{
Menu* pSelMenu = nEventId ? this : NULL;
for ( sal_uLong n = GetItemList()->Count(); n && !pSelMenu; )
{
MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
if ( pData->pSubMenu )
pSelMenu = pData->pSubMenu->ImplFindSelectMenu();
}
return pSelMenu;
}
Menu* Menu::ImplFindMenu( sal_uInt16 nItemId )
{
Menu* pSelMenu = NULL;
for ( sal_uLong n = GetItemList()->Count(); n && !pSelMenu; )
{
MenuItemData* pData = GetItemList()->GetDataFromPos( --n );
if( pData->nId == nItemId )
pSelMenu = this;
else if ( pData->pSubMenu )
pSelMenu = pData->pSubMenu->ImplFindMenu( nItemId );
}
return pSelMenu;
}
void Menu::RemoveDisabledEntries( sal_Bool bCheckPopups, sal_Bool bRemoveEmptyPopups )
{
for ( sal_uInt16 n = 0; n < GetItemCount(); n++ )
{
sal_Bool bRemove = sal_False;
MenuItemData* pItem = pItemList->GetDataFromPos( n );
if ( pItem->eType == MENUITEM_SEPARATOR )
{
if ( !n || ( GetItemType( n-1 ) == MENUITEM_SEPARATOR ) )
bRemove = sal_True;
}
else
bRemove = !pItem->bEnabled;
if ( bCheckPopups && pItem->pSubMenu )
{
pItem->pSubMenu->RemoveDisabledEntries( sal_True );
if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() )
bRemove = sal_True;
}
if ( bRemove )
RemoveItem( n-- );
}
if ( GetItemCount() )
{
sal_uInt16 nLast = GetItemCount() - 1;
MenuItemData* pItem = pItemList->GetDataFromPos( nLast );
if ( pItem->eType == MENUITEM_SEPARATOR )
RemoveItem( nLast );
}
delete mpLayoutData, mpLayoutData = NULL;
}
sal_Bool Menu::HasValidEntries( sal_Bool bCheckPopups )
{
sal_Bool bValidEntries = sal_False;
sal_uInt16 nCount = GetItemCount();
for ( sal_uInt16 n = 0; !bValidEntries && ( n < nCount ); n++ )
{
MenuItemData* pItem = pItemList->GetDataFromPos( n );
if ( pItem->bEnabled && ( pItem->eType != MENUITEM_SEPARATOR ) )
{
if ( bCheckPopups && pItem->pSubMenu )
bValidEntries = pItem->pSubMenu->HasValidEntries( sal_True );
else
bValidEntries = sal_True;
}
}
return bValidEntries;
}
void Menu::SetLogo( const MenuLogo& rLogo )
{
delete pLogo;
pLogo = new MenuLogo( rLogo );
}
void Menu::SetLogo()
{
delete pLogo;
pLogo = NULL;
}
MenuLogo Menu::GetLogo() const
{
MenuLogo aLogo;
if ( pLogo )
aLogo = *pLogo;
return aLogo;
}
void Menu::ImplKillLayoutData() const
{
delete mpLayoutData, mpLayoutData = NULL;
}
void Menu::ImplFillLayoutData() const
{
if( pWindow && pWindow->IsReallyVisible() )
{
mpLayoutData = new MenuLayoutData();
if( bIsMenuBar )
{
ImplPaint( pWindow, 0, 0, 0, sal_False, true );
}
else
{
MenuFloatingWindow* pFloat = (MenuFloatingWindow*)pWindow;
ImplPaint( pWindow, pFloat->nScrollerHeight, pFloat->ImplGetStartY(), 0, sal_False, true );
}
}
}
String Menu::GetDisplayText() const
{
if( ! mpLayoutData )
ImplFillLayoutData();
return mpLayoutData ? mpLayoutData->m_aDisplayText : String();
}
Rectangle Menu::GetCharacterBounds( sal_uInt16 nItemID, long nIndex ) const
{
long nItemIndex = -1;
if( ! mpLayoutData )
ImplFillLayoutData();
if( mpLayoutData )
{
for( size_t i = 0; i < mpLayoutData->m_aLineItemIds.size(); i++ )
{
if( mpLayoutData->m_aLineItemIds[i] == nItemID )
{
nItemIndex = mpLayoutData->m_aLineIndices[i];
break;
}
}
}
return (mpLayoutData && nItemIndex != -1) ? mpLayoutData->GetCharacterBounds( nItemIndex+nIndex ) : Rectangle();
}
long Menu::GetIndexForPoint( const Point& rPoint, sal_uInt16& rItemID ) const
{
long nIndex = -1;
rItemID = 0;
if( ! mpLayoutData )
ImplFillLayoutData();
if( mpLayoutData )
{
nIndex = mpLayoutData->GetIndexForPoint( rPoint );
for( size_t i = 0; i < mpLayoutData->m_aLineIndices.size(); i++ )
{
if( mpLayoutData->m_aLineIndices[i] <= nIndex &&
(i == mpLayoutData->m_aLineIndices.size()-1 || mpLayoutData->m_aLineIndices[i+1] > nIndex) )
{
// make index relative to item
nIndex -= mpLayoutData->m_aLineIndices[i];
rItemID = mpLayoutData->m_aLineItemIds[i];
break;
}
}
}
return nIndex;
}
long Menu::GetLineCount() const
{
if( ! mpLayoutData )
ImplFillLayoutData();
return mpLayoutData ? mpLayoutData->GetLineCount() : 0;
}
Pair Menu::GetLineStartEnd( long nLine ) const
{
if( ! mpLayoutData )
ImplFillLayoutData();
return mpLayoutData ? mpLayoutData->GetLineStartEnd( nLine ) : Pair( -1, -1 );
}
Pair Menu::GetItemStartEnd( sal_uInt16 nItem ) const
{
if( ! mpLayoutData )
ImplFillLayoutData();
for( size_t i = 0; i < mpLayoutData->m_aLineItemIds.size(); i++ )
if( mpLayoutData->m_aLineItemIds[i] == nItem )
return GetLineStartEnd( i );
return Pair( -1, -1 );
}
sal_uInt16 Menu::GetDisplayItemId( long nLine ) const
{
sal_uInt16 nItemId = 0;
if( ! mpLayoutData )
ImplFillLayoutData();
if( mpLayoutData && ( nLine >= 0 ) && ( nLine < (long)mpLayoutData->m_aLineItemIds.size() ) )
nItemId = mpLayoutData->m_aLineItemIds[nLine];
return nItemId;
}
sal_Bool Menu::ConvertPoint( Point& rPoint, Window* pReferenceWindow ) const
{
sal_Bool bRet = sal_False;
if( pWindow && pReferenceWindow )
{
rPoint = pReferenceWindow->OutputToAbsoluteScreenPixel( rPoint );
rPoint = pWindow->AbsoluteScreenToOutputPixel( rPoint );
bRet = sal_True;
}
return bRet;
}
Rectangle Menu::GetBoundingRectangle( sal_uInt16 nPos ) const
{
Rectangle aRet;
if( ! mpLayoutData )
ImplFillLayoutData();
if( mpLayoutData )
{
std::map< sal_uInt16, Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos );
if( it != mpLayoutData->m_aVisibleItemBoundRects.end() )
aRet = it->second;
}
return aRet;
}
void Menu::SetAccessibleName( sal_uInt16 nItemId, const XubString& rStr )
{
sal_uInt16 nPos;
MenuItemData* pData = pItemList->GetData( nItemId, nPos );
if ( pData && !rStr.Equals( pData->aAccessibleName ) )
{
pData->aAccessibleName = rStr;
ImplCallEventListeners( VCLEVENT_MENU_ACCESSIBLENAMECHANGED, nPos );
}
}
XubString Menu::GetAccessibleName( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->aAccessibleName;
else
return ImplGetSVEmptyStr();
}
void Menu::SetAccessibleDescription( sal_uInt16 nItemId, const XubString& rStr )
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
pData->aAccessibleDescription = rStr;
}
XubString Menu::GetAccessibleDescription( sal_uInt16 nItemId ) const
{
MenuItemData* pData = pItemList->GetData( nItemId );
if ( pData )
return pData->aAccessibleDescription;
else
return ImplGetSVEmptyStr();
}
void Menu::ImplSetSalMenu( SalMenu *pSalMenu )
{
if( mpSalMenu )
ImplGetSVData()->mpDefInst->DestroyMenu( mpSalMenu );
mpSalMenu = pSalMenu;
}
sal_Bool Menu::GetSystemMenuData( SystemMenuData* pData ) const
{
Menu* pMenu = (Menu*)this;
if( pData && pMenu->ImplGetSalMenu() )
{
pMenu->ImplGetSalMenu()->GetSystemMenuData( pData );
return sal_True;
}
else
return sal_False;
}
bool Menu::IsHighlighted( sal_uInt16 nItemPos ) const
{
bool bRet = false;
if( pWindow )
{
if( bIsMenuBar )
bRet = ( nItemPos == static_cast< MenuBarWindow * > (pWindow)->GetHighlightedItem() );
else
bRet = ( nItemPos == static_cast< MenuFloatingWindow * > (pWindow)->GetHighlightedItem() );
}
return bRet;
}
void Menu::HighlightItem( sal_uInt16 nItemPos )
{
if ( pWindow )
{
if ( bIsMenuBar )
{
MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( pWindow );
pMenuWin->SetAutoPopup( sal_False );
pMenuWin->ChangeHighlightItem( nItemPos, sal_False );
}
else
{
static_cast< MenuFloatingWindow* >( pWindow )->ChangeHighlightItem( nItemPos, sal_False );
}
}
}
// -----------
// - MenuBar -
// -----------
MenuBar::MenuBar() : Menu( sal_True )
{
mbDisplayable = sal_True;
mbCloserVisible = sal_False;
mbFloatBtnVisible = sal_False;
mbHideBtnVisible = sal_False;
}
MenuBar::MenuBar( const MenuBar& rMenu ) : Menu( sal_True )
{
mbDisplayable = sal_True;
mbCloserVisible = sal_False;
mbFloatBtnVisible = sal_False;
mbHideBtnVisible = sal_False;
*this = rMenu;
bIsMenuBar = sal_True;
}
MenuBar::MenuBar( const ResId& rResId ) : Menu ( sal_True )
{
mbDisplayable = sal_True;
mbCloserVisible = sal_False;
mbFloatBtnVisible = sal_False;
mbHideBtnVisible = sal_False;
ImplLoadRes( rResId );
}
MenuBar::~MenuBar()
{
ImplDestroy( this, sal_True );
}
void MenuBar::ShowCloser( sal_Bool bShow )
{
ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible );
}
void MenuBar::ShowFloatButton( sal_Bool bShow )
{
ShowButtons( mbCloserVisible, bShow, mbHideBtnVisible );
}
void MenuBar::ShowHideButton( sal_Bool bShow )
{
ShowButtons( mbCloserVisible, mbFloatBtnVisible, bShow );
}
void MenuBar::ShowButtons( sal_Bool bClose, sal_Bool bFloat, sal_Bool bHide )
{
if ( (bClose != mbCloserVisible) ||
(bFloat != mbFloatBtnVisible) ||
(bHide != mbHideBtnVisible) )
{
mbCloserVisible = bClose;
mbFloatBtnVisible = bFloat;
mbHideBtnVisible = bHide;
if ( ImplGetWindow() )
((MenuBarWindow*)ImplGetWindow())->ShowButtons( bClose, bFloat, bHide );
}
}
void MenuBar::SetDisplayable( sal_Bool bDisplayable )
{
if( bDisplayable != mbDisplayable )
{
mbDisplayable = bDisplayable;
MenuBarWindow* pMenuWin = (MenuBarWindow*) ImplGetWindow();
if( pMenuWin )
pMenuWin->ImplLayoutChanged();
}
}
Window* MenuBar::ImplCreate( Window* pParent, Window* pWindow, MenuBar* pMenu )
{
if ( !pWindow )
pWindow = new MenuBarWindow( pParent );
pMenu->pStartedFrom = 0;
pMenu->pWindow = pWindow;
((MenuBarWindow*)pWindow)->SetMenu( pMenu );
long nHeight = pMenu->ImplCalcSize( pWindow ).Height();
// depending on the native implementation or the displayable flag
// the menubar windows is supressed (ie, height=0)
if( !((MenuBar*) pMenu)->IsDisplayable() ||
( pMenu->ImplGetSalMenu() && pMenu->ImplGetSalMenu()->VisibleMenuBar() ) )
nHeight = 0;
pWindow->SetPosSizePixel( 0, 0, 0, nHeight, WINDOW_POSSIZE_HEIGHT );
return pWindow;
}
void MenuBar::ImplDestroy( MenuBar* pMenu, sal_Bool bDelete )
{
MenuBarWindow* pWindow = (MenuBarWindow*) pMenu->ImplGetWindow();
if ( pWindow && bDelete )
{
pWindow->KillActivePopup();
delete pWindow;
}
pMenu->pWindow = NULL;
}
sal_Bool MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent, sal_Bool bFromMenu )
{
sal_Bool bDone = sal_False;
// No keyboard processing when system handles the menu or our menubar is invisible
if( !IsDisplayable() ||
( ImplGetSalMenu() && ImplGetSalMenu()->VisibleMenuBar() ) )
return bDone;
// Enabled-Abfragen, falls diese Methode von einem anderen Fenster gerufen wurde...
Window* pWin = ImplGetWindow();
if ( pWin && pWin->IsEnabled() && pWin->IsInputEnabled() && ! pWin->IsInModalMode() )
bDone = ((MenuBarWindow*)pWin)->ImplHandleKeyEvent( rKEvent, bFromMenu );
return bDone;
}
// -----------------------------------------------------------------------
void MenuBar::SelectEntry( sal_uInt16 nId )
{
MenuBarWindow* pMenuWin = (MenuBarWindow*) ImplGetWindow();
if( pMenuWin )
{
pMenuWin->GrabFocus();
nId = GetItemPos( nId );
// #99705# popup the selected menu
pMenuWin->SetAutoPopup( sal_True );
if( ITEMPOS_INVALID != pMenuWin->nHighlightedItem )
{
pMenuWin->KillActivePopup();
pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, sal_False );
}
if( nId != ITEMPOS_INVALID )
pMenuWin->ChangeHighlightItem( nId, sal_False );
}
}
// -----------------------------------------------------------------------
// handler for native menu selection and command events
sal_Bool MenuBar::HandleMenuActivateEvent( Menu *pMenu ) const
{
if( pMenu )
{
ImplMenuDelData aDelData( this );
pMenu->pStartedFrom = (Menu*)this;
pMenu->bInCallback = sal_True;
pMenu->Activate();
if( !aDelData.isDeleted() )
pMenu->bInCallback = sal_False;
}
return sal_True;
}
sal_Bool MenuBar::HandleMenuDeActivateEvent( Menu *pMenu ) const
{
if( pMenu )
{
ImplMenuDelData aDelData( this );
pMenu->pStartedFrom = (Menu*)this;
pMenu->bInCallback = sal_True;
pMenu->Deactivate();
if( !aDelData.isDeleted() )
pMenu->bInCallback = sal_False;
}
return sal_True;
}
sal_Bool MenuBar::HandleMenuHighlightEvent( Menu *pMenu, sal_uInt16 nHighlightEventId ) const
{
if( !pMenu )
pMenu = ((Menu*) this)->ImplFindMenu( nHighlightEventId );
if( pMenu )
{
ImplMenuDelData aDelData( pMenu );
if( mnHighlightedItemPos != ITEMPOS_INVALID )
pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, mnHighlightedItemPos );
if( !aDelData.isDeleted() )
{
pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId );
pMenu->nSelectedId = nHighlightEventId;
pMenu->pStartedFrom = (Menu*)this;
pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos );
}
return sal_True;
}
else
return sal_False;
}
sal_Bool MenuBar::HandleMenuCommandEvent( Menu *pMenu, sal_uInt16 nCommandEventId ) const
{
if( !pMenu )
pMenu = ((Menu*) this)->ImplFindMenu( nCommandEventId );
if( pMenu )
{
pMenu->nSelectedId = nCommandEventId;
pMenu->pStartedFrom = (Menu*)this;
pMenu->ImplSelect();
return sal_True;
}
else
return sal_False;
}
sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link& i_rLink, sal_uInt16 i_nPos )
{
return AddMenuBarButton( i_rImage, i_rLink, String(), i_nPos );
}
sal_uInt16 MenuBar::AddMenuBarButton( const Image& i_rImage, const Link& i_rLink, const String& i_rToolTip, sal_uInt16 i_nPos )
{
return pWindow ? static_cast<MenuBarWindow*>(pWindow)->AddMenuBarButton( i_rImage, i_rLink, i_rToolTip, i_nPos ) : 0;
}
void MenuBar::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link& rLink )
{
if( pWindow )
static_cast<MenuBarWindow*>(pWindow)->SetMenuBarButtonHighlightHdl( nId, rLink );
}
Rectangle MenuBar::GetMenuBarButtonRectPixel( sal_uInt16 nId )
{
return pWindow ? static_cast<MenuBarWindow*>(pWindow)->GetMenuBarButtonRectPixel( nId ) : Rectangle();
}
void MenuBar::RemoveMenuBarButton( sal_uInt16 nId )
{
if( pWindow )
static_cast<MenuBarWindow*>(pWindow)->RemoveMenuBarButton( nId );
}
sal_Bool MenuBar::HandleMenuButtonEvent( Menu *, sal_uInt16 i_nButtonId ) const
{
return static_cast<MenuBarWindow*>(pWindow)->HandleMenuButtonEvent( i_nButtonId );
}
// -----------------------------------------------------------------------
// sal_Bool PopupMenu::bAnyPopupInExecute = sal_False;
PopupMenu::PopupMenu()
{
pRefAutoSubMenu = NULL;
}
PopupMenu::PopupMenu( const ResId& rResId )
{
pRefAutoSubMenu = NULL;
ImplLoadRes( rResId );
}
PopupMenu::PopupMenu( const PopupMenu& rMenu ) : Menu()
{
pRefAutoSubMenu = NULL;
*this = rMenu;
}
PopupMenu::~PopupMenu()
{
if( pRefAutoSubMenu && *pRefAutoSubMenu == this )
*pRefAutoSubMenu = NULL; // #111060# avoid second delete in ~MenuItemData
}
sal_Bool PopupMenu::IsInExecute()
{
return GetActivePopupMenu() ? sal_True : sal_False;
}
PopupMenu* PopupMenu::GetActivePopupMenu()
{
ImplSVData* pSVData = ImplGetSVData();
return pSVData->maAppData.mpActivePopupMenu;
}
void PopupMenu::EndExecute( sal_uInt16 nSelectId )
{
if ( ImplGetWindow() )
ImplGetFloatingWindow()->EndExecute( nSelectId );
}
void PopupMenu::SelectEntry( sal_uInt16 nId )
{
if ( ImplGetWindow() )
{
if( nId != ITEMPOS_INVALID )
{
sal_uInt16 nPos;
MenuItemData* pData = GetItemList()->GetData( nId, nPos );
if ( pData->pSubMenu )
ImplGetFloatingWindow()->ChangeHighlightItem( nPos, sal_True );
else
ImplGetFloatingWindow()->EndExecute( nId );
}
else
{
MenuFloatingWindow* pFloat = ImplGetFloatingWindow();
pFloat->GrabFocus();
sal_uInt16 nPos;
for( nPos = 0; nPos < GetItemList()->Count(); nPos++ )
{
MenuItemData* pData = (MenuItemData*)GetItemList()->GetObject( nPos );
if( pData->pSubMenu )
{
pFloat->KillActivePopup();
}
}
pFloat->ChangeHighlightItem( ITEMPOS_INVALID, sal_False );
}
}
}
void PopupMenu::SetSelectedEntry( sal_uInt16 nId )
{
nSelectedId = nId;
}
sal_uInt16 PopupMenu::Execute( Window* pExecWindow, const Point& rPopupPos )
{
return Execute( pExecWindow, Rectangle( rPopupPos, rPopupPos ), POPUPMENU_EXECUTE_DOWN );
}
sal_uInt16 PopupMenu::Execute( Window* pExecWindow, const Rectangle& rRect, sal_uInt16 nFlags )
{
ENSURE_OR_RETURN( pExecWindow, "PopupMenu::Execute: need a non-NULL window!", 0 );
sal_uLong nPopupModeFlags = 0;
if ( nFlags & POPUPMENU_EXECUTE_DOWN )
nPopupModeFlags = FLOATWIN_POPUPMODE_DOWN;
else if ( nFlags & POPUPMENU_EXECUTE_UP )
nPopupModeFlags = FLOATWIN_POPUPMODE_UP;
else if ( nFlags & POPUPMENU_EXECUTE_LEFT )
nPopupModeFlags = FLOATWIN_POPUPMODE_LEFT;
else if ( nFlags & POPUPMENU_EXECUTE_RIGHT )
nPopupModeFlags = FLOATWIN_POPUPMODE_RIGHT;
else
nPopupModeFlags = FLOATWIN_POPUPMODE_DOWN;
if (nFlags & POPUPMENU_NOMOUSEUPCLOSE ) // allow popup menus to stay open on mouse button up
nPopupModeFlags |= FLOATWIN_POPUPMODE_NOMOUSEUPCLOSE; // useful if the menu was opened on mousebutton down (eg toolbox configuration)
return ImplExecute( pExecWindow, rRect, nPopupModeFlags, 0, sal_False );
}
sal_uInt16 PopupMenu::ImplExecute( Window* pW, const Rectangle& rRect, sal_uLong nPopupModeFlags, Menu* pSFrom, sal_Bool bPreSelectFirst )
{
if ( !pSFrom && ( PopupMenu::IsInExecute() || !GetItemCount() ) )
return 0;
delete mpLayoutData, mpLayoutData = NULL;
ImplSVData* pSVData = ImplGetSVData();
pStartedFrom = pSFrom;
nSelectedId = 0;
bCanceled = sal_False;
sal_uLong nFocusId = 0;
sal_Bool bRealExecute = sal_False;
if ( !pStartedFrom )
{
pSVData->maWinData.mbNoDeactivate = sal_True;
nFocusId = Window::SaveFocus();
bRealExecute = sal_True;
}
else
{
// assure that only one menu is open at a time
if( pStartedFrom->bIsMenuBar && pSVData->maWinData.mpFirstFloat )
pSVData->maWinData.mpFirstFloat->EndPopupMode( FLOATWIN_POPUPMODEEND_CANCEL | FLOATWIN_POPUPMODEEND_CLOSEALL );
}
DBG_ASSERT( !ImplGetWindow(), "Win?!" );
Rectangle aRect( rRect );
aRect.SetPos( pW->OutputToScreenPixel( aRect.TopLeft() ) );
WinBits nStyle = WB_BORDER;
if ( bRealExecute )
nPopupModeFlags |= FLOATWIN_POPUPMODE_NEWLEVEL;
if ( !pStartedFrom || !pStartedFrom->bIsMenuBar )
nPopupModeFlags |= FLOATWIN_POPUPMODE_PATHMOUSECANCELCLICK | FLOATWIN_POPUPMODE_ALLMOUSEBUTTONCLOSE;
nPopupModeFlags |= FLOATWIN_POPUPMODE_NOKEYCLOSE;
// Kann beim Debuggen hilfreich sein.
// nPopupModeFlags |= FLOATWIN_POPUPMODE_NOFOCUSCLOSE;
ImplDelData aDelData;
pW->ImplAddDel( &aDelData );
bInCallback = sal_True; // hier schon setzen, falls Activate ueberladen
Activate();
bInCallback = sal_False;
if ( aDelData.IsDelete() )
return 0; // Error
pW->ImplRemoveDel( &aDelData );
if ( bCanceled || bKilled )
return 0;
if ( !GetItemCount() )
return 0;
// Das Flag MENU_FLAG_HIDEDISABLEDENTRIES wird vererbt.
if ( pSFrom )
{
if ( pSFrom->nMenuFlags & MENU_FLAG_HIDEDISABLEDENTRIES )
nMenuFlags |= MENU_FLAG_HIDEDISABLEDENTRIES;
else
nMenuFlags &= ~MENU_FLAG_HIDEDISABLEDENTRIES;
}
else
// #102790# context menues shall never show disabled entries
nMenuFlags |= MENU_FLAG_HIDEDISABLEDENTRIES;
sal_uInt16 nVisibleEntries = ImplGetVisibleItemCount();
if ( !nVisibleEntries )
{
ResMgr* pResMgr = ImplGetResMgr();
if( pResMgr )
{
String aTmpEntryText( ResId( SV_RESID_STRING_NOSELECTIONPOSSIBLE, *pResMgr ) );
MenuItemData* pData = pItemList->Insert(
0xFFFF, MENUITEM_STRING, 0, aTmpEntryText, Image(), NULL, 0xFFFF );
sal_uInt16 nmPos;
pData = pItemList->GetData( pData->nId, nmPos );
pData->bIsTemporary = sal_True;
ImplCallEventListeners(VCLEVENT_MENU_SUBMENUCHANGED,nmPos);
}
}
else if ( Application::GetSettings().GetStyleSettings().GetAutoMnemonic() && !( nMenuFlags & MENU_FLAG_NOAUTOMNEMONICS ) )
{
CreateAutoMnemonics();
}
MenuFloatingWindow* pWin = new MenuFloatingWindow( this, pW, nStyle | WB_SYSTEMWINDOW );
if( pSVData->maNWFData.mbFlatMenu )
pWin->SetBorderStyle( WINDOW_BORDER_NOBORDER );
else
pWin->SetBorderStyle( pWin->GetBorderStyle() | WINDOW_BORDER_MENU );
pWindow = pWin;
Size aSz = ImplCalcSize( pWin );
long nMaxHeight = pWin->GetDesktopRectPixel().GetHeight();
if( Application::GetScreenCount() > 1 && ! Application::IsMultiDisplay() )
{
Window* pDeskW = pWindow->GetWindow( WINDOW_REALPARENT );
if( ! pDeskW )
pDeskW = pWindow;
Point aDesktopTL( pDeskW->OutputToAbsoluteScreenPixel( aRect.TopLeft() ) );
nMaxHeight = Application::GetWorkAreaPosSizePixel(
Application::GetBestScreen( Rectangle( aDesktopTL, aRect.GetSize() ) )
).GetHeight();
}
if ( pStartedFrom && pStartedFrom->bIsMenuBar )
nMaxHeight -= pW->GetSizePixel().Height();
sal_Int32 nLeft, nTop, nRight, nBottom;
pWindow->GetBorder( nLeft, nTop, nRight, nBottom );
nMaxHeight -= nTop+nBottom;
if ( aSz.Height() > nMaxHeight )
{
pWin->EnableScrollMenu( sal_True );
sal_uInt16 nStart = ImplGetFirstVisible();
sal_uInt16 nEntries = ImplCalcVisEntries( nMaxHeight, nStart );
aSz.Height() = ImplCalcHeight( nEntries );
}
pWin->SetFocusId( nFocusId );
pWin->SetOutputSizePixel( aSz );
// #102158# menus must never grab the focus, otherwise
// they will be closed immediately
// from now on focus grabbing is only prohibited automatically if
// FLOATWIN_POPUPMODE_GRABFOCUS was set (which is done below), because some
// floaters (like floating toolboxes) may grab the focus
// pWin->GrabFocus();
if ( GetItemCount() )
{
SalMenu* pMenu = ImplGetSalMenu();
if( pMenu && bRealExecute && pMenu->ShowNativePopupMenu( pWin, aRect, nPopupModeFlags | FLOATWIN_POPUPMODE_GRABFOCUS ) )
{
pWin->StopExecute(0);
pWin->doShutdown();
pWindow->doLazyDelete();
pWindow = NULL;
return nSelectedId;
}
else
{
pWin->StartPopupMode( aRect, nPopupModeFlags | FLOATWIN_POPUPMODE_GRABFOCUS );
}
if( pSFrom )
{
sal_uInt16 aPos;
if( pSFrom->bIsMenuBar )
aPos = ((MenuBarWindow *) pSFrom->pWindow)->GetHighlightedItem();
else
aPos = ((MenuFloatingWindow *) pSFrom->pWindow)->GetHighlightedItem();
pWin->SetPosInParent( aPos ); // store position to be sent in SUBMENUDEACTIVATE
pSFrom->ImplCallEventListeners( VCLEVENT_MENU_SUBMENUACTIVATE, aPos );
}
}
if ( bPreSelectFirst )
{
sal_uInt16 nCount = (sal_uInt16)pItemList->Count();
for ( sal_uInt16 n = 0; n < nCount; n++ )
{
MenuItemData* pData = pItemList->GetDataFromPos( n );
if ( ( pData->bEnabled || !Application::GetSettings().GetStyleSettings().GetSkipDisabledInMenus() )
&& ( pData->eType != MENUITEM_SEPARATOR ) && ImplIsVisible( n ) && ImplIsSelectable( n ) )
{
pWin->ChangeHighlightItem( n, sal_False );
break;
}
}
}
if ( bRealExecute )
{
pWin->ImplAddDel( &aDelData );
ImplDelData aModalWinDel;
pW->ImplAddDel( &aModalWinDel );
pW->ImplIncModalCount();
pWin->Execute();
DBG_ASSERT( ! aModalWinDel.IsDead(), "window for popup died, modal count incorrect !" );
if( ! aModalWinDel.IsDead() )
pW->ImplDecModalCount();
if ( !aDelData.IsDelete() )
pWin->ImplRemoveDel( &aDelData );
else
return 0;
// Focus wieder herstellen (kann schon im Select wieder
// hergestellt wurden sein
nFocusId = pWin->GetFocusId();
if ( nFocusId )
{
pWin->SetFocusId( 0 );
pSVData->maWinData.mbNoDeactivate = sal_False;
}
pWin->ImplEndPopupMode( 0, nFocusId );
if ( nSelectedId ) // Dann abraeumen... ( sonst macht TH das )
{
PopupMenu* pSub = pWin->GetActivePopup();
while ( pSub )
{
pSub->ImplGetFloatingWindow()->EndPopupMode();
pSub = pSub->ImplGetFloatingWindow()->GetActivePopup();
}
}
pWin->doShutdown();
pWindow->doLazyDelete();
pWindow = NULL;
// Steht noch ein Select aus?
Menu* pSelect = ImplFindSelectMenu();
if ( pSelect )
{
// Beim Popup-Menu muss das Select vor dem Verlassen von Execute gerufen werden!
Application::RemoveUserEvent( pSelect->nEventId );
pSelect->nEventId = 0;
pSelect->Select();
}
}
return bRealExecute ? nSelectedId : 0;
}
sal_uInt16 PopupMenu::ImplCalcVisEntries( long nMaxHeight, sal_uInt16 nStartEntry, sal_uInt16* pLastVisible ) const
{
nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight();
long nHeight = 0;
sal_uInt16 nEntries = (sal_uInt16) pItemList->Count();
sal_uInt16 nVisEntries = 0;
if ( pLastVisible )
*pLastVisible = 0;
for ( sal_uInt16 n = nStartEntry; n < nEntries; n++ )
{
if ( ImplIsVisible( n ) )
{
MenuItemData* pData = pItemList->GetDataFromPos( n );
nHeight += pData->aSz.Height();
if ( nHeight > nMaxHeight )
break;
if ( pLastVisible )
*pLastVisible = n;
nVisEntries++;
}
}
return nVisEntries;
}
long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const
{
long nHeight = 0;
sal_uInt16 nFound = 0;
for ( sal_uInt16 n = 0; ( nFound < nEntries ) && ( n < pItemList->Count() ); n++ )
{
if ( ImplIsVisible( (sal_uInt16) n ) )
{
MenuItemData* pData = pItemList->GetDataFromPos( n );
nHeight += pData->aSz.Height();
nFound++;
}
}
nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight();
return nHeight;
}
static void ImplInitMenuWindow( Window* pWin, sal_Bool bFont, sal_Bool bMenuBar )
{
const StyleSettings& rStyleSettings = pWin->GetSettings().GetStyleSettings();
if ( bFont )
pWin->SetPointFont( rStyleSettings.GetMenuFont() );
if( bMenuBar )
{
if( pWin->IsNativeControlSupported( CTRL_MENUBAR, PART_ENTIRE_CONTROL ) )
{
pWin->SetBackground(); // background will be drawn by NWF
}
else
{
Wallpaper aWallpaper;
aWallpaper.SetStyle( WALLPAPER_APPLICATIONGRADIENT );
pWin->SetBackground( aWallpaper );
pWin->SetPaintTransparent( sal_False );
pWin->SetParentClipMode( 0 );
}
}
else
{
if( pWin->IsNativeControlSupported( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL ) )
{
pWin->SetBackground(); // background will be drawn by NWF
}
else
pWin->SetBackground( Wallpaper( rStyleSettings.GetMenuColor() ) );
}
if ( bMenuBar )
pWin->SetTextColor( rStyleSettings.GetMenuBarTextColor() );
else
pWin->SetTextColor( rStyleSettings.GetMenuTextColor() );
pWin->SetTextFillColor();
pWin->SetLineColor();
}
MenuFloatingWindow::MenuFloatingWindow( Menu* pMen, Window* pParent, WinBits nStyle ) :
FloatingWindow( pParent, nStyle )
{
mpWindowImpl->mbMenuFloatingWindow= sal_True;
pMenu = pMen;
pActivePopup = 0;
nSaveFocusId = 0;
bInExecute = sal_False;
bScrollMenu = sal_False;
nHighlightedItem = ITEMPOS_INVALID;
nMBDownPos = ITEMPOS_INVALID;
nPosInParent = ITEMPOS_INVALID;
nScrollerHeight = 0;
// nStartY = 0;
nBorder = EXTRASPACEY;
nFirstEntry = 0;
bScrollUp = sal_False;
bScrollDown = sal_False;
bIgnoreFirstMove = sal_True;
bKeyInput = sal_False;
EnableSaveBackground();
ImplInitMenuWindow( this, sal_True, sal_False );
SetPopupModeEndHdl( LINK( this, MenuFloatingWindow, PopupEnd ) );
aHighlightChangedTimer.SetTimeoutHdl( LINK( this, MenuFloatingWindow, HighlightChanged ) );
aHighlightChangedTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
aSubmenuCloseTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() );
aSubmenuCloseTimer.SetTimeoutHdl( LINK( this, MenuFloatingWindow, SubmenuClose ) );
aScrollTimer.SetTimeoutHdl( LINK( this, MenuFloatingWindow, AutoScroll ) );
AddEventListener( LINK( this, MenuFloatingWindow, ShowHideListener ) );
}
void MenuFloatingWindow::doShutdown()
{
if( pMenu )
{
// #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( VCLEVENT_MENU_DEHIGHLIGHT, nHighlightedItem );
pMenu->SetHightlightItem(ITEMPOS_INVALID);
if( !bKeyInput && pMenu && pMenu->pStartedFrom && !pMenu->pStartedFrom->bIsMenuBar )
{
// #102461# remove highlight in parent
MenuItemData* pData;
sal_uInt16 i, nCount = (sal_uInt16)pMenu->pStartedFrom->pItemList->Count();
for(i = 0; i < nCount; i++)
{
pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
if( pData && ( pData->pSubMenu == pMenu ) )
break;
}
if( i < nCount )
{
MenuFloatingWindow* pPWin = (MenuFloatingWindow*)pMenu->pStartedFrom->ImplGetWindow();
if( pPWin )
pPWin->HighlightItem( i, sal_False );
}
}
// free the reference to the accessible component
SetAccessible( ::com::sun::star::uno::Reference< ::com::sun::star::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() )
{
Rectangle aInvRect( GetWindowExtentsRelative( GetParent() ) );
GetParent()->Invalidate( aInvRect );
}
pMenu = NULL;
RemoveEventListener( LINK( this, MenuFloatingWindow, ShowHideListener ) );
}
}
MenuFloatingWindow::~MenuFloatingWindow()
{
doShutdown();
}
void MenuFloatingWindow::Resize()
{
ImplInitClipRegion();
}
long MenuFloatingWindow::ImplGetStartY() const
{
long nY = 0;
if( pMenu )
{
for ( sal_uInt16 n = 0; n < nFirstEntry; n++ )
nY += pMenu->GetItemList()->GetDataFromPos( n )->aSz.Height();
}
return -nY;
}
Region MenuFloatingWindow::ImplCalcClipRegion( sal_Bool bIncludeLogo ) const
{
Size aOutSz = GetOutputSizePixel();
Point aPos;
Rectangle aRect( aPos, aOutSz );
aRect.Top() += nScrollerHeight;
aRect.Bottom() -= nScrollerHeight;
if ( pMenu && pMenu->pLogo && !bIncludeLogo )
aRect.Left() += pMenu->pLogo->aBitmap.GetSizePixel().Width();
Region aRegion = aRect;
if ( pMenu && pMenu->pLogo && bIncludeLogo && nScrollerHeight )
aRegion.Union( Rectangle( Point(), Size( pMenu->pLogo->aBitmap.GetSizePixel().Width(), aOutSz.Height() ) ) );
return aRegion;
}
void MenuFloatingWindow::ImplInitClipRegion()
{
if ( IsScrollMenu() )
{
SetClipRegion( ImplCalcClipRegion() );
}
else
{
SetClipRegion();
}
}
void MenuFloatingWindow::ImplHighlightItem( const MouseEvent& rMEvt, sal_Bool bMBDown )
{
if( ! pMenu )
return;
long nY = nScrollerHeight;
long nMouseY = rMEvt.GetPosPixel().Y();
Size aOutSz = GetOutputSizePixel();
if ( ( nMouseY >= nY ) && ( nMouseY < ( aOutSz.Height() - nY ) ) )
{
sal_Bool bHighlighted = sal_False;
sal_uInt16 nCount = (sal_uInt16)pMenu->pItemList->Count();
nY += ImplGetStartY(); // ggf. gescrollt.
for ( sal_uInt16 n = 0; !bHighlighted && ( n < nCount ); n++ )
{
if ( pMenu->ImplIsVisible( n ) )
{
MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( n );
long nOldY = nY;
nY += pItemData->aSz.Height();
if ( ( nOldY <= nMouseY ) && ( nY > nMouseY ) && pMenu->ImplIsSelectable( n ) )
{
sal_Bool bPopupArea = sal_True;
if ( pItemData->nBits & MIB_POPUPSELECT )
{
// Nur wenn ueber dem Pfeil geklickt wurde...
Size aSz = GetOutputSizePixel();
long nFontHeight = GetTextHeight();
bPopupArea = ( rMEvt.GetPosPixel().X() >= ( aSz.Width() - nFontHeight - nFontHeight/4 ) );
}
if ( bMBDown )
{
if ( n != nHighlightedItem )
{
ChangeHighlightItem( (sal_uInt16)n, sal_False );
}
sal_Bool bAllowNewPopup = sal_True;
if ( pActivePopup )
{
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
bAllowNewPopup = pData && ( pData->pSubMenu != pActivePopup );
if ( bAllowNewPopup )
KillActivePopup();
}
if ( bPopupArea && bAllowNewPopup )
{
HighlightChanged( NULL );
}
}
else
{
if ( n != nHighlightedItem )
{
ChangeHighlightItem( (sal_uInt16)n, sal_True );
}
else if ( pItemData->nBits & MIB_POPUPSELECT )
{
if ( bPopupArea && ( pActivePopup != pItemData->pSubMenu ) )
HighlightChanged( NULL );
}
}
bHighlighted = sal_True;
}
}
}
if ( !bHighlighted )
ChangeHighlightItem( ITEMPOS_INVALID, sal_True );
}
else
{
ImplScroll( rMEvt.GetPosPixel() );
ChangeHighlightItem( ITEMPOS_INVALID, sal_True );
}
}
IMPL_LINK( MenuFloatingWindow, PopupEnd, FloatingWindow*, EMPTYARG )
{
// "this" will be deleted before the end of this method!
Menu* pM = pMenu;
if ( bInExecute )
{
if ( pActivePopup )
{
//DBG_ASSERT( !pActivePopup->ImplGetWindow(), "PopupEnd, obwohl pActivePopup MIT Window!" );
KillActivePopup(); // should be ok to just remove it
//pActivePopup->bCanceled = sal_True;
}
bInExecute = sal_False;
pMenu->bInCallback = sal_True;
pMenu->Deactivate();
pMenu->bInCallback = sal_False;
}
else
{
if( pMenu )
{
// Wenn dies Fenster von TH geschlossen wurde, hat noch ein anderes
// Menu dieses Fenster als pActivePopup.
if ( pMenu->pStartedFrom )
{
// Das pWin am 'Parent' kann aber schon 0 sein, falls die Kette von
// vorne abgeraeumt wurde und jetzt die EndPopup-Events eintrudeln
if ( pMenu->pStartedFrom->bIsMenuBar )
{
MenuBarWindow* p = (MenuBarWindow*) pMenu->pStartedFrom->ImplGetWindow();
if ( p )
p->PopupClosed( pMenu );
}
else
{
MenuFloatingWindow* p = (MenuFloatingWindow*) pMenu->pStartedFrom->ImplGetWindow();
if ( p )
p->KillActivePopup( (PopupMenu*)pMenu );
}
}
}
}
if ( pM )
pM->pStartedFrom = 0;
return 0;
}
IMPL_LINK( MenuFloatingWindow, AutoScroll, Timer*, EMPTYARG )
{
ImplScroll( GetPointerPosPixel() );
return 1;
}
IMPL_LINK( MenuFloatingWindow, HighlightChanged, Timer*, pTimer )
{
if( ! pMenu )
return 0;
MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( nHighlightedItem );
if ( pItemData )
{
if ( pActivePopup && ( pActivePopup != pItemData->pSubMenu ) )
{
sal_uLong nOldFlags = GetPopupModeFlags();
SetPopupModeFlags( GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE );
KillActivePopup();
SetPopupModeFlags( nOldFlags );
}
if ( pItemData->bEnabled && pItemData->pSubMenu && pItemData->pSubMenu->GetItemCount() && ( pItemData->pSubMenu != pActivePopup ) )
{
pActivePopup = (PopupMenu*)pItemData->pSubMenu;
long nY = nScrollerHeight+ImplGetStartY();
MenuItemData* pData = 0;
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 MyPos = GetPosPixel();
// Point aItemTopLeft( MyPos.X(), MyPos.Y()+nY );
Point aItemTopLeft( 0, nY );
Point aItemBottomRight( aItemTopLeft );
aItemBottomRight.X() += MySize.Width();
aItemBottomRight.Y() += pData->aSz.Height();
// Popups leicht versetzen:
aItemTopLeft.X() += 2;
aItemBottomRight.X() -= 2;
if ( nHighlightedItem )
aItemTopLeft.Y() -= 2;
else
{
sal_Int32 nL, nT, nR, nB;
GetBorder( nL, nT, nR, nB );
aItemTopLeft.Y() -= nT;
}
// pTest: Wegen Abstuerzen durch Reschedule() im Aufruf von Activate()
// Ausserdem wird damit auch verhindert, dass SubMenus angezeigt werden,
// die lange im Activate Rescheduled haben und jetzt schon nicht mehr
// angezeigt werden sollen.
Menu* pTest = pActivePopup;
sal_uLong nOldFlags = GetPopupModeFlags();
SetPopupModeFlags( GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE );
sal_uInt16 nRet = pActivePopup->ImplExecute( this, Rectangle( aItemTopLeft, aItemBottomRight ), FLOATWIN_POPUPMODE_RIGHT, pMenu, pTimer ? sal_False : sal_True );
SetPopupModeFlags( nOldFlags );
// nRet != 0, wenn es waerend Activate() abgeschossen wurde...
if ( !nRet && ( pActivePopup == pTest ) && pActivePopup->ImplGetWindow() )
pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this );
}
}
return 0;
}
IMPL_LINK( MenuFloatingWindow, SubmenuClose, Timer*, EMPTYARG )
{
if( pMenu && pMenu->pStartedFrom )
{
MenuFloatingWindow* pWin = (MenuFloatingWindow*) pMenu->pStartedFrom->GetWindow();
if( pWin )
pWin->KillActivePopup();
}
return 0;
}
IMPL_LINK( MenuFloatingWindow, ShowHideListener, VclWindowEvent*, pEvent )
{
if( ! pMenu )
return 0;
if( pEvent->GetId() == VCLEVENT_WINDOW_SHOW )
pMenu->ImplCallEventListeners( VCLEVENT_MENU_SHOW, ITEMPOS_INVALID );
else if( pEvent->GetId() == VCLEVENT_WINDOW_HIDE )
pMenu->ImplCallEventListeners( VCLEVENT_MENU_HIDE, ITEMPOS_INVALID );
return 0;
}
void MenuFloatingWindow::EnableScrollMenu( sal_Bool b )
{
bScrollMenu = b;
nScrollerHeight = b ? (sal_uInt16) GetSettings().GetStyleSettings().GetScrollBarSize() /2 : 0;
bScrollDown = sal_True;
ImplInitClipRegion();
}
void MenuFloatingWindow::Execute()
{
ImplSVData* pSVData = ImplGetSVData();
pSVData->maAppData.mpActivePopupMenu = (PopupMenu*)pMenu;
bInExecute = sal_True;
// bCallingSelect = sal_False;
while ( bInExecute )
Application::Yield();
pSVData->maAppData.mpActivePopupMenu = NULL;
// while ( bCallingSelect )
// Application::Yield();
}
void MenuFloatingWindow::StopExecute( sal_uLong nFocusId )
{
// Focus wieder herstellen
// (kann schon im Select wieder hergestellt wurden sein)
if ( nSaveFocusId )
{
Window::EndSaveFocus( nFocusId, sal_False );
nFocusId = nSaveFocusId;
if ( nFocusId )
{
nSaveFocusId = 0;
ImplGetSVData()->maWinData.mbNoDeactivate = sal_False;
}
}
ImplEndPopupMode( 0, nFocusId );
aHighlightChangedTimer.Stop();
bInExecute = sal_False;
if ( pActivePopup )
{
KillActivePopup();
}
// notify parent, needed for accessibility
if( pMenu && pMenu->pStartedFrom )
pMenu->pStartedFrom->ImplCallEventListeners( VCLEVENT_MENU_SUBMENUDEACTIVATE, nPosInParent );
}
void MenuFloatingWindow::KillActivePopup( PopupMenu* pThisOnly )
{
if ( pActivePopup && ( !pThisOnly || ( pThisOnly == pActivePopup ) ) )
{
if( pActivePopup->pWindow != NULL )
if( ((FloatingWindow *) pActivePopup->pWindow)->IsInCleanUp() )
return; // kill it later
if ( pActivePopup->bInCallback )
pActivePopup->bCanceled = sal_True;
// Vor allen Aktionen schon pActivePopup = 0, falls z.B.
// PopupModeEndHdl des zu zerstoerenden Popups mal synchron gerufen wird.
PopupMenu* pPopup = pActivePopup;
pActivePopup = NULL;
pPopup->bInCallback = sal_True;
pPopup->Deactivate();
pPopup->bInCallback = sal_False;
if ( pPopup->ImplGetWindow() )
{
pPopup->ImplGetFloatingWindow()->StopExecute();
pPopup->ImplGetFloatingWindow()->doShutdown();
pPopup->pWindow->doLazyDelete();
pPopup->pWindow = NULL;
Update();
}
}
}
void MenuFloatingWindow::EndExecute()
{
Menu* pStart = pMenu ? pMenu->ImplGetStartMenu() : NULL;
sal_uLong nFocusId = 0;
if ( pStart && pStart->bIsMenuBar )
{
nFocusId = ((MenuBarWindow*)((MenuBar*)pStart)->ImplGetWindow())->GetFocusId();
if ( nFocusId )
{
((MenuBarWindow*)((MenuBar*)pStart)->ImplGetWindow())->SetFocusId( 0 );
ImplGetSVData()->maWinData.mbNoDeactivate = sal_False;
}
}
// Wenn von woanders gestartet, dann ab dort aufraumen:
MenuFloatingWindow* pCleanUpFrom = this;
MenuFloatingWindow* pWin = this;
while ( pWin && !pWin->bInExecute &&
pWin->pMenu->pStartedFrom && !pWin->pMenu->pStartedFrom->bIsMenuBar )
{
pWin = ((PopupMenu*)pWin->pMenu->pStartedFrom)->ImplGetFloatingWindow();
}
if ( pWin )
pCleanUpFrom = pWin;
// Dies Fenster wird gleich zerstoert => Daten lokal merken...
Menu* pM = pMenu;
sal_uInt16 nItem = nHighlightedItem;
pCleanUpFrom->StopExecute( nFocusId );
if ( nItem != ITEMPOS_INVALID && pM )
{
MenuItemData* pItemData = pM->GetItemList()->GetDataFromPos( nItem );
if ( pItemData && !pItemData->bIsTemporary )
{
pM->nSelectedId = pItemData->nId;
if ( pStart )
pStart->nSelectedId = pItemData->nId;
pM->ImplSelect();
}
}
}
void MenuFloatingWindow::EndExecute( sal_uInt16 nId )
{
sal_uInt16 nPos;
if ( pMenu && pMenu->GetItemList()->GetData( nId, nPos ) )
nHighlightedItem = nPos;
else
nHighlightedItem = ITEMPOS_INVALID;
EndExecute();
}
void MenuFloatingWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
// TH macht ein ToTop auf dieses Fenster, aber das aktive Popup
// soll oben bleiben...
// due to focus chage this would close all menues -> don't do it (#94123)
//if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup )
// pActivePopup->ImplGetFloatingWindow()->ToTop( TOTOP_NOGRABFOCUS );
ImplHighlightItem( rMEvt, sal_True );
nMBDownPos = nHighlightedItem;
}
void MenuFloatingWindow::MouseButtonUp( const MouseEvent& rMEvt )
{
MenuItemData* pData = pMenu ? pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ) : NULL;
// nMBDownPos in lokaler Variable merken und gleich zuruecksetzen,
// weil nach EndExecute zu spaet
sal_uInt16 _nMBDownPos = nMBDownPos;
nMBDownPos = ITEMPOS_INVALID;
if ( pData && pData->bEnabled && ( pData->eType != MENUITEM_SEPARATOR ) )
{
if ( !pData->pSubMenu )
{
EndExecute();
}
else if ( ( pData->nBits & MIB_POPUPSELECT ) && ( nHighlightedItem == _nMBDownPos ) && ( rMEvt.GetClicks() == 2 ) )
{
// Nicht wenn ueber dem Pfeil geklickt wurde...
Size aSz = GetOutputSizePixel();
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() )
{
#ifdef OS2
if ( ImplHilite(rMEvt) )
{
#endif
// #102461# do not remove highlight if a popup menu is open at this position
MenuItemData* pData = pMenu ? pMenu->pItemList->GetDataFromPos( nHighlightedItem ) : NULL;
// 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, sal_False );
#ifdef OS2
}
#endif
if ( IsScrollMenu() )
ImplScroll( rMEvt.GetPosPixel() );
}
else
#ifdef OS2
if ( ImplHilite(rMEvt) )
#endif
{
aSubmenuCloseTimer.Stop();
if( bIgnoreFirstMove )
bIgnoreFirstMove = sal_False;
else
ImplHighlightItem( rMEvt, sal_False );
}
}
void MenuFloatingWindow::ImplScroll( sal_Bool bUp )
{
KillActivePopup();
Update();
if( ! pMenu )
return;
HighlightItem( nHighlightedItem, sal_False );
pMenu->ImplKillLayoutData();
if ( bScrollUp && bUp )
{
nFirstEntry = pMenu->ImplGetPrevVisible( nFirstEntry );
DBG_ASSERT( nFirstEntry != ITEMPOS_INVALID, "Scroll?!" );
long nScrollEntryHeight = pMenu->GetItemList()->GetDataFromPos( nFirstEntry )->aSz.Height();
// nStartY += nEntryHeight;
if ( !bScrollDown )
{
bScrollDown = sal_True;
ImplDrawScroller( sal_False );
}
if ( pMenu->ImplGetPrevVisible( nFirstEntry ) == ITEMPOS_INVALID )
{
bScrollUp = sal_False;
ImplDrawScroller( sal_True );
}
Scroll( 0, nScrollEntryHeight, ImplCalcClipRegion( sal_False ).GetBoundRect(), SCROLL_CLIP );
}
else if ( bScrollDown && !bUp )
{
long nScrollEntryHeight = pMenu->GetItemList()->GetDataFromPos( nFirstEntry )->aSz.Height();
nFirstEntry = pMenu->ImplGetNextVisible( nFirstEntry );
DBG_ASSERT( nFirstEntry != ITEMPOS_INVALID, "Scroll?!" );
if ( !bScrollUp )
{
bScrollUp = sal_True;
ImplDrawScroller( sal_True );
}
long nHeight = GetOutputSizePixel().Height();
sal_uInt16 nLastVisible;
((PopupMenu*)pMenu)->ImplCalcVisEntries( nHeight, nFirstEntry, &nLastVisible );
if ( pMenu->ImplGetNextVisible( nLastVisible ) == ITEMPOS_INVALID )
{
bScrollDown = sal_False;
ImplDrawScroller( sal_False );
}
// nStartY -= nEntryHeight;
Scroll( 0, -nScrollEntryHeight, ImplCalcClipRegion( sal_False ).GetBoundRect(), SCROLL_CLIP );
}
HighlightItem( nHighlightedItem, sal_True );
}
void MenuFloatingWindow::ImplScroll( const Point& rMousePos )
{
Size aOutSz = GetOutputSizePixel();
long nY = nScrollerHeight;
long nMouseY = rMousePos.Y();
long nDelta = 0;
if ( bScrollUp && ( nMouseY < nY ) )
{
ImplScroll( sal_True );
nDelta = nY - nMouseY;
}
else if ( bScrollDown && ( nMouseY > ( aOutSz.Height() - nY ) ) )
{
ImplScroll( sal_False );
nDelta = nMouseY - ( aOutSz.Height() - nY );
}
if ( nDelta )
{
aScrollTimer.Stop(); // Falls durch MouseMove gescrollt.
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, sal_Bool bStartPopupTimer )
{
// #57934# ggf. das aktive Popup sofort schliessen, damit TH's Hintergrundsicherung funktioniert.
// #65750# Dann verzichten wir lieber auf den schmalen Streifen Hintergrundsicherung.
// Sonst lassen sich die Menus schlecht bedienen.
// MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n );
// if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) )
// KillActivePopup();
aSubmenuCloseTimer.Stop();
if( ! pMenu )
return;
if ( nHighlightedItem != ITEMPOS_INVALID )
{
HighlightItem( nHighlightedItem, sal_False );
pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, nHighlightedItem );
}
nHighlightedItem = (sal_uInt16)n;
DBG_ASSERT( pMenu->ImplIsVisible( nHighlightedItem ) || nHighlightedItem == ITEMPOS_INVALID, "ChangeHighlightItem: Not visible!" );
if( nHighlightedItem != ITEMPOS_INVALID )
{
if( pMenu->pStartedFrom && !pMenu->pStartedFrom->bIsMenuBar )
{
// #102461# make sure parent entry is highlighted as well
MenuItemData* pData;
sal_uInt16 i, nCount = (sal_uInt16)pMenu->pStartedFrom->pItemList->Count();
for(i = 0; i < nCount; i++)
{
pData = pMenu->pStartedFrom->pItemList->GetDataFromPos( i );
if( pData && ( pData->pSubMenu == pMenu ) )
break;
}
if( i < nCount )
{
MenuFloatingWindow* pPWin = (MenuFloatingWindow*)pMenu->pStartedFrom->ImplGetWindow();
if( pPWin && pPWin->nHighlightedItem != i )
{
pPWin->HighlightItem( i, sal_True );
pPWin->nHighlightedItem = i;
}
}
}
HighlightItem( nHighlightedItem, sal_True );
pMenu->SetHightlightItem(nHighlightedItem);
pMenu->ImplCallHighlight( nHighlightedItem );
}
else
pMenu->nSelectedId = 0;
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 );
}
}
void MenuFloatingWindow::HighlightItem( sal_uInt16 nPos, sal_Bool bHighlight )
{
if( ! pMenu )
return;
Size aSz = GetOutputSizePixel();
long nStartY = ImplGetStartY();
long nY = nScrollerHeight+nStartY;
long nX = 0;
if ( pMenu->pLogo )
nX = pMenu->pLogo->aBitmap.GetSizePixel().Width();
int nOuterSpace = ImplGetSVData()->maNWFData.mnMenuFormatExtraBorder;
nY += nOuterSpace;
sal_uInt16 nCount = (sal_uInt16)pMenu->pItemList->Count();
for ( sal_uInt16 n = 0; n < nCount; n++ )
{
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
if ( n == nPos )
{
DBG_ASSERT( pMenu->ImplIsVisible( n ), "Highlight: Item not visible!" );
if ( pData->eType != MENUITEM_SEPARATOR )
{
sal_Bool bRestoreLineColor = sal_False;
Color oldLineColor;
bool bDrawItemRect = true;
Rectangle aItemRect( Point( nX+nOuterSpace, nY ), Size( aSz.Width()-2*nOuterSpace, pData->aSz.Height() ) );
if ( pData->nBits & MIB_POPUPSELECT )
{
long nFontHeight = GetTextHeight();
aItemRect.Right() -= nFontHeight + nFontHeight/4;
}
if( IsNativeControlSupported( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL ) )
{
Size aPxSize( GetOutputSizePixel() );
Push( PUSH_CLIPREGION );
IntersectClipRegion( Rectangle( Point( nX, nY ), Size( aSz.Width(), pData->aSz.Height() ) ) );
Rectangle aCtrlRect( Point( nX, 0 ), Size( aPxSize.Width()-nX, aPxSize.Height() ) );
MenupopupValue aVal( pMenu->nTextPos-GUTTERBORDER, aItemRect );
DrawNativeControl( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL,
aCtrlRect,
CTRL_STATE_ENABLED,
aVal,
OUString() );
if( bHighlight &&
IsNativeControlSupported( CTRL_MENU_POPUP, PART_MENU_ITEM ) )
{
bDrawItemRect = false;
if( sal_False == DrawNativeControl( CTRL_MENU_POPUP, PART_MENU_ITEM,
aItemRect,
CTRL_STATE_SELECTED | ( pData->bEnabled? CTRL_STATE_ENABLED: 0 ),
aVal,
OUString() ) )
{
bDrawItemRect = bHighlight;
}
}
else
bDrawItemRect = bHighlight;
Pop();
}
if( bDrawItemRect )
{
if ( bHighlight )
{
if( pData->bEnabled )
SetFillColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() );
else
{
SetFillColor();
oldLineColor = GetLineColor();
SetLineColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() );
bRestoreLineColor = sal_True;
}
}
else
SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() );
DrawRect( aItemRect );
}
pMenu->ImplPaint( this, nScrollerHeight, nStartY, pData, bHighlight );
if( bRestoreLineColor )
SetLineColor( oldLineColor );
}
return;
}
nY += pData->aSz.Height();
}
}
Rectangle MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos )
{
if( ! pMenu )
return Rectangle();
Rectangle aRect;
Size aSz = GetOutputSizePixel();
long nStartY = ImplGetStartY();
long nY = nScrollerHeight+nStartY;
long nX = 0;
if ( pMenu->pLogo )
nX = pMenu->pLogo->aBitmap.GetSizePixel().Width();
sal_uInt16 nCount = (sal_uInt16)pMenu->pItemList->Count();
for ( sal_uInt16 n = 0; n < nCount; n++ )
{
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
if ( n == nPos )
{
DBG_ASSERT( pMenu->ImplIsVisible( n ), "ImplGetItemRect: Item not visible!" );
if ( pData->eType != MENUITEM_SEPARATOR )
{
aRect = Rectangle( Point( nX, nY ), Size( aSz.Width(), pData->aSz.Height() ) );
if ( pData->nBits & MIB_POPUPSELECT )
{
long nFontHeight = GetTextHeight();
aRect.Right() -= nFontHeight + nFontHeight/4;
}
}
break;
}
nY += pData->aSz.Height();
}
return aRect;
}
void MenuFloatingWindow::ImplCursorUpDown( sal_Bool bUp, sal_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 = (MenuItemData*)pMenu->GetItemList()->GetDataFromPos( n );
if ( ( pData->bEnabled || !rSettings.GetSkipDisabledInMenus() )
&& ( pData->eType != MENUITEM_SEPARATOR ) && pMenu->ImplIsVisible( n ) && pMenu->ImplIsSelectable( n ) )
{
// Selektion noch im sichtbaren Bereich?
if ( IsScrollMenu() )
{
ChangeHighlightItem( ITEMPOS_INVALID, sal_False );
while ( n < nFirstEntry )
ImplScroll( sal_True );
Size aOutSz = GetOutputSizePixel();
sal_uInt16 nLastVisible;
((PopupMenu*)pMenu)->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
while ( n > nLastVisible )
{
ImplScroll( sal_False );
((PopupMenu*)pMenu)->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible );
}
}
ChangeHighlightItem( n, sal_False );
break;
}
} while ( n != nLoop );
}
void MenuFloatingWindow::KeyInput( const KeyEvent& rKEvent )
{
ImplDelData aDelData;
ImplAddDel( &aDelData );
sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode();
bKeyInput = sal_True;
switch ( nCode )
{
case KEY_UP:
case KEY_DOWN:
{
ImplCursorUpDown( nCode == KEY_UP );
}
break;
case KEY_END:
case KEY_HOME:
{
ImplCursorUpDown( nCode == KEY_END, sal_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->bIsMenuBar )
{
// Forward...
((MenuBarWindow*)((MenuBar*)pMenu->pStartedFrom)->ImplGetWindow())->KeyInput( rKEvent );
}
else
{
StopExecute();
PopupMenu* pPopupMenu = (PopupMenu*)pMenu->pStartedFrom;
MenuFloatingWindow* pFloat = pPopupMenu->ImplGetFloatingWindow();
pFloat->GrabFocus();
pFloat->KillActivePopup();
pPopupMenu->ImplCallHighlight(pFloat->nHighlightedItem);
}
}
}
break;
case KEY_LEFT:
{
if ( pMenu && pMenu->pStartedFrom )
{
StopExecute();
if ( pMenu->pStartedFrom->bIsMenuBar )
{
// Forward...
((MenuBarWindow*)((MenuBar*)pMenu->pStartedFrom)->ImplGetWindow())->KeyInput( rKEvent );
}
else
{
MenuFloatingWindow* pFloat = ((PopupMenu*)pMenu->pStartedFrom)->ImplGetFloatingWindow();
pFloat->GrabFocus();
pFloat->KillActivePopup();
sal_uInt16 highlightItem = pFloat->GetHighlightedItem();
pFloat->ChangeHighlightItem(highlightItem, sal_False);
}
}
}
break;
case KEY_RIGHT:
{
if( pMenu )
{
sal_Bool bDone = sal_False;
if ( nHighlightedItem != ITEMPOS_INVALID )
{
MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem );
if ( pData && pData->pSubMenu )
{
HighlightChanged( 0 );
bDone = sal_True;
}
}
if ( !bDone )
{
Menu* pStart = pMenu->ImplGetStartMenu();
if ( pStart && pStart->bIsMenuBar )
{
// 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( 0 );
else
EndExecute();
}
else
StopExecute();
}
}
break;
case KEY_MENU:
{
if( pMenu )
{
Menu* pStart = pMenu->ImplGetStartMenu();
if ( pStart && pStart->bIsMenuBar )
{
// Forward...
pStart->ImplGetWindow()->KeyInput( rKEvent );
}
}
}
break;
default:
{
xub_Unicode nCharCode = rKEvent.GetCharCode();
sal_uInt16 nPos = 0;
sal_uInt16 nDuplicates = 0;
MenuItemData* pData = (nCharCode && pMenu) ? pMenu->GetItemList()->SearchItem( nCharCode, rKEvent.GetKeyCode(), nPos, nDuplicates, nHighlightedItem ) : NULL;
if ( pData )
{
if ( pData->pSubMenu || nDuplicates > 1 )
{
ChangeHighlightItem( nPos, sal_False );
HighlightChanged( 0 );
}
else
{
nHighlightedItem = nPos;
EndExecute();
}
}
else
{
// Bei ungueltigen Tasten Beepen, aber nicht bei HELP und F-Tasten
if ( !rKEvent.GetKeyCode().IsMod2() && ( nCode != KEY_HELP ) && ( rKEvent.GetKeyCode().GetGroup() != KEYGROUP_FKEYS ) )
Sound::Beep();
FloatingWindow::KeyInput( rKEvent );
}
}
}
// #105474# check if menu window was not destroyed
if ( !aDelData.IsDelete() )
{
ImplRemoveDel( &aDelData );
bKeyInput = sal_False;
}
}
void MenuFloatingWindow::Paint( const Rectangle& )
{
if( ! pMenu )
return;
if( IsNativeControlSupported( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL ) )
{
SetClipRegion();
long nX = pMenu->pLogo ? pMenu->pLogo->aBitmap.GetSizePixel().Width() : 0;
Size aPxSize( GetOutputSizePixel() );
aPxSize.Width() -= nX;
ImplControlValue aVal( pMenu->nTextPos-GUTTERBORDER );
DrawNativeControl( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL,
Rectangle( Point( nX, 0 ), aPxSize ),
CTRL_STATE_ENABLED,
aVal,
OUString() );
ImplInitClipRegion();
}
if ( IsScrollMenu() )
{
ImplDrawScroller( sal_True );
ImplDrawScroller( sal_False );
}
SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() );
pMenu->ImplPaint( this, nScrollerHeight, ImplGetStartY() );
if ( nHighlightedItem != ITEMPOS_INVALID )
HighlightItem( nHighlightedItem, sal_True );
}
void MenuFloatingWindow::ImplDrawScroller( sal_Bool bUp )
{
if( ! pMenu )
return;
SetClipRegion();
Size aOutSz = GetOutputSizePixel();
long nY = bUp ? 0 : ( aOutSz.Height() - nScrollerHeight );
long nX = pMenu->pLogo ? pMenu->pLogo->aBitmap.GetSizePixel().Width() : 0;
Rectangle aRect( Point( nX, nY ), Size( aOutSz.Width()-nX, nScrollerHeight ) );
DecorationView aDecoView( this );
SymbolType eSymbol = bUp ? SYMBOL_SPIN_UP : SYMBOL_SPIN_DOWN;
sal_uInt16 nStyle = 0;
if ( ( bUp && !bScrollUp ) || ( !bUp && !bScrollDown ) )
nStyle |= SYMBOL_DRAW_DISABLE;
aDecoView.DrawSymbol( aRect, eSymbol, GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle );
ImplInitClipRegion();
}
void MenuFloatingWindow::RequestHelp( const HelpEvent& rHEvt )
{
sal_uInt16 nId = nHighlightedItem;
Menu* pM = pMenu;
Window* pW = this;
// #102618# Get item rect before destroying the window in EndExecute() call
Rectangle aHighlightRect( ImplGetItemRect( nHighlightedItem ) );
if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) )
{
nHighlightedItem = ITEMPOS_INVALID;
EndExecute();
pW = NULL;
}
if( !ImplHandleHelpEvent( pW, pM, nId, rHEvt, aHighlightRect ) )
Window::RequestHelp( rHEvt );
}
void MenuFloatingWindow::StateChanged( StateChangedType nType )
{
FloatingWindow::StateChanged( nType );
if ( ( nType == STATE_CHANGE_CONTROLFOREGROUND ) || ( nType == STATE_CHANGE_CONTROLBACKGROUND ) )
{
ImplInitMenuWindow( this, sal_False, sal_False );
Invalidate();
}
}
void MenuFloatingWindow::DataChanged( const DataChangedEvent& rDCEvt )
{
FloatingWindow::DataChanged( rDCEvt );
if ( (rDCEvt.GetType() == DATACHANGED_FONTS) ||
(rDCEvt.GetType() == DATACHANGED_FONTSUBSTITUTION) ||
((rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
(rDCEvt.GetFlags() & SETTINGS_STYLE)) )
{
ImplInitMenuWindow( this, sal_False, sal_False );
Invalidate();
}
}
void MenuFloatingWindow::Command( const CommandEvent& rCEvt )
{
if ( rCEvt.GetCommand() == COMMAND_WHEEL )
{
const CommandWheelData* pData = rCEvt.GetWheelData();
if( !pData->GetModifier() && ( pData->GetMode() == COMMAND_WHEEL_SCROLL ) )
{
// ImplCursorUpDown( pData->GetDelta() > 0L );
ImplScroll( pData->GetDelta() > 0L );
MouseMove( MouseEvent( GetPointerPosPixel(), 0 ) );
}
}
}
::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > MenuFloatingWindow::CreateAccessible()
{
::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > xAcc;
if ( pMenu && !pMenu->pStartedFrom )
xAcc = pMenu->GetAccessible();
return xAcc;
}
MenuBarWindow::MenuBarWindow( Window* pParent ) :
Window( pParent, 0 ),
aCloser( this ),
aFloatBtn( this, WB_NOPOINTERFOCUS | WB_SMALLSTYLE | WB_RECTSTYLE ),
aHideBtn( this, WB_NOPOINTERFOCUS | WB_SMALLSTYLE | WB_RECTSTYLE )
{
SetType( WINDOW_MENUBARWINDOW );
pMenu = NULL;
pActivePopup = NULL;
nSaveFocusId = 0;
nHighlightedItem = ITEMPOS_INVALID;
mbAutoPopup = sal_True;
nSaveFocusId = 0;
bIgnoreFirstMove = sal_True;
bStayActive = sal_False;
ResMgr* pResMgr = ImplGetResMgr();
if( pResMgr )
{
BitmapEx aBitmap( ResId( SV_RESID_BITMAP_CLOSEDOC, *pResMgr ) );
BitmapEx aBitmapHC( ResId( SV_RESID_BITMAP_CLOSEDOCHC, *pResMgr ) );
aCloser.maImage = Image( aBitmap );
aCloser.maImageHC = Image( aBitmapHC );
aCloser.SetOutStyle( TOOLBOX_STYLE_FLAT );
aCloser.SetBackground();
aCloser.SetPaintTransparent( sal_True );
aCloser.SetParentClipMode( PARENTCLIPMODE_NOCLIP );
aCloser.InsertItem( IID_DOCUMENTCLOSE,
GetSettings().GetStyleSettings().GetHighContrastMode() ? aCloser.maImageHC : aCloser.maImage, 0 );
aCloser.SetSelectHdl( LINK( this, MenuBarWindow, CloserHdl ) );
aCloser.AddEventListener( LINK( this, MenuBarWindow, ToolboxEventHdl ) );
aCloser.SetQuickHelpText( IID_DOCUMENTCLOSE, XubString( ResId( SV_HELPTEXT_CLOSEDOCUMENT, *pResMgr ) ) );
aFloatBtn.SetClickHdl( LINK( this, MenuBarWindow, FloatHdl ) );
aFloatBtn.SetSymbol( SYMBOL_FLOAT );
aFloatBtn.SetQuickHelpText( XubString( ResId( SV_HELPTEXT_RESTORE, *pResMgr ) ) );
aHideBtn.SetClickHdl( LINK( this, MenuBarWindow, HideHdl ) );
aHideBtn.SetSymbol( SYMBOL_HIDE );
aHideBtn.SetQuickHelpText( XubString( ResId( SV_HELPTEXT_MINIMIZE, *pResMgr ) ) );
}
ImplInitStyleSettings();
AddEventListener( LINK( this, MenuBarWindow, ShowHideListener ) );
}
MenuBarWindow::~MenuBarWindow()
{
aCloser.RemoveEventListener( LINK( this, MenuBarWindow, ToolboxEventHdl ) );
RemoveEventListener( LINK( this, MenuBarWindow, ShowHideListener ) );
}
void MenuBarWindow::SetMenu( MenuBar* pMen )
{
pMenu = pMen;
KillActivePopup();
nHighlightedItem = ITEMPOS_INVALID;
ImplInitMenuWindow( this, sal_True, sal_True );
if ( pMen )
{
aCloser.ShowItem( IID_DOCUMENTCLOSE, pMen->HasCloser() );
aCloser.Show( pMen->HasCloser() || !m_aAddButtons.empty() );
aFloatBtn.Show( pMen->HasFloatButton() );
aHideBtn.Show( pMen->HasHideButton() );
}
Invalidate();
// show and connect native menubar
if( pMenu && pMenu->ImplGetSalMenu() )
{
if( pMenu->ImplGetSalMenu()->VisibleMenuBar() )
ImplGetFrame()->SetMenu( pMenu->ImplGetSalMenu() );
pMenu->ImplGetSalMenu()->SetFrame( ImplGetFrame() );
}
}
void MenuBarWindow::ShowButtons( sal_Bool bClose, sal_Bool bFloat, sal_Bool bHide )
{
aCloser.ShowItem( IID_DOCUMENTCLOSE, bClose );
aCloser.Show( bClose || ! m_aAddButtons.empty() );
aFloatBtn.Show( bFloat );
aHideBtn.Show( bHide );
Resize();
}
Size MenuBarWindow::MinCloseButtonSize()
{
return aCloser.getMinSize();
}
IMPL_LINK( MenuBarWindow, CloserHdl, PushButton*, EMPTYARG )
{
if( ! pMenu )
return 0;
if( aCloser.GetCurItemId() == IID_DOCUMENTCLOSE )
{
// #i106052# call close hdl asynchronously to ease handler implementation
// this avoids still being in the handler while the DecoToolBox already
// gets destroyed
Application::PostUserEvent( ((MenuBar*)pMenu)->GetCloserHdl(), pMenu );
}
else
{
std::map<sal_uInt16,AddButtonEntry>::iterator it = m_aAddButtons.find( aCloser.GetCurItemId() );
if( it != m_aAddButtons.end() )
{
MenuBar::MenuBarButtonCallbackArg aArg;
aArg.nId = it->first;
aArg.bHighlight = (aCloser.GetHighlightItemId() == it->first);
aArg.pMenuBar = dynamic_cast<MenuBar*>(pMenu);
return it->second.m_aSelectLink.Call( &aArg );
}
}
return 0;
}
IMPL_LINK( MenuBarWindow, ToolboxEventHdl, VclWindowEvent*, pEvent )
{
if( ! pMenu )
return 0;
MenuBar::MenuBarButtonCallbackArg aArg;
aArg.nId = 0xffff;
aArg.bHighlight = (pEvent->GetId() == VCLEVENT_TOOLBOX_HIGHLIGHT);
aArg.pMenuBar = dynamic_cast<MenuBar*>(pMenu);
if( pEvent->GetId() == VCLEVENT_TOOLBOX_HIGHLIGHT )
aArg.nId = aCloser.GetHighlightItemId();
else if( pEvent->GetId() == VCLEVENT_TOOLBOX_HIGHLIGHTOFF )
{
sal_uInt16 nPos = static_cast< sal_uInt16 >(reinterpret_cast<sal_IntPtr>(pEvent->GetData()));
aArg.nId = aCloser.GetItemId( nPos );
}
std::map< sal_uInt16, AddButtonEntry >::iterator it = m_aAddButtons.find( aArg.nId );
if( it != m_aAddButtons.end() )
{
it->second.m_aHighlightLink.Call( &aArg );
}
return 0;
}
IMPL_LINK( MenuBarWindow, ShowHideListener, VclWindowEvent*, pEvent )
{
if( ! pMenu )
return 0;
if( pEvent->GetId() == VCLEVENT_WINDOW_SHOW )
pMenu->ImplCallEventListeners( VCLEVENT_MENU_SHOW, ITEMPOS_INVALID );
else if( pEvent->GetId() == VCLEVENT_WINDOW_HIDE )
pMenu->ImplCallEventListeners( VCLEVENT_MENU_HIDE, ITEMPOS_INVALID );
return 0;
}
IMPL_LINK( MenuBarWindow, FloatHdl, PushButton*, EMPTYARG )
{
return pMenu ? ((MenuBar*)pMenu)->GetFloatButtonClickHdl().Call( pMenu ) : 0;
}
IMPL_LINK( MenuBarWindow, HideHdl, PushButton*, EMPTYARG )
{
return pMenu ? ((MenuBar*)pMenu)->GetHideButtonClickHdl().Call( pMenu ) : 0;
}
void MenuBarWindow::ImplCreatePopup( sal_Bool bPreSelectFirst )
{
MenuItemData* pItemData = pMenu ? pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ) : NULL;
if ( pItemData )
{
bIgnoreFirstMove = sal_True;
if ( pActivePopup && ( pActivePopup != pItemData->pSubMenu ) )
{
KillActivePopup();
}
if ( pItemData->bEnabled && pItemData->pSubMenu && ( nHighlightedItem != ITEMPOS_INVALID ) && ( pItemData->pSubMenu != pActivePopup ) )
{
pActivePopup = (PopupMenu*)pItemData->pSubMenu;
long nX = 0;
MenuItemData* pData = 0;
for ( sal_uLong n = 0; n < nHighlightedItem; n++ )
{
pData = pMenu->GetItemList()->GetDataFromPos( n );
nX += pData->aSz.Width();
}
pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem );
// Point MyPos = GetPosPixel();
// Point aItemTopLeft( MyPos.X()+nX, MyPos.Y() );
Point aItemTopLeft( nX, 0 );
Point aItemBottomRight( aItemTopLeft );
aItemBottomRight.X() += pData->aSz.Width();
// Im Vollbild-Modus hat die MenuBar ggf. die Hoehe 0:
// Nicht immer einfach die Window-Hoehe nehmen, weil ItemHeight < WindowHeight.
if ( GetSizePixel().Height() )
{
// #107747# give menuitems the height of the menubar
aItemBottomRight.Y() += GetOutputSizePixel().Height()-1;
}
// ImplExecute ist doch nicht modal...
// #99071# do not grab the focus, otherwise it will be restored to the menubar
// when the frame is reactivated later
//GrabFocus();
pActivePopup->ImplExecute( this, Rectangle( aItemTopLeft, aItemBottomRight ), FLOATWIN_POPUPMODE_DOWN, pMenu, bPreSelectFirst );
if ( pActivePopup )
{
// Hat kein Window, wenn vorher abgebrochen oder keine Eintraege
if ( pActivePopup->ImplGetFloatingWindow() )
pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this );
else
pActivePopup = NULL;
}
}
}
}
void MenuBarWindow::KillActivePopup()
{
if ( pActivePopup )
{
if( pActivePopup->pWindow != NULL )
if( ((FloatingWindow *) pActivePopup->pWindow)->IsInCleanUp() )
return; // kill it later
if ( pActivePopup->bInCallback )
pActivePopup->bCanceled = sal_True;
pActivePopup->bInCallback = sal_True;
pActivePopup->Deactivate();
pActivePopup->bInCallback = sal_False;
// Abfrage auf pActivePopup, falls im Deactivate abgeschossen...
if ( pActivePopup && pActivePopup->ImplGetWindow() )
{
pActivePopup->ImplGetFloatingWindow()->StopExecute();
pActivePopup->ImplGetFloatingWindow()->doShutdown();
pActivePopup->pWindow->doLazyDelete();
pActivePopup->pWindow = NULL;
}
pActivePopup = 0;
}
}
void MenuBarWindow::PopupClosed( Menu* pPopup )
{
if ( pPopup == pActivePopup )
{
KillActivePopup();
ChangeHighlightItem( ITEMPOS_INVALID, sal_False, ImplGetFrameWindow()->ImplGetFrameData()->mbHasFocus, sal_False );
}
}
void MenuBarWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
mbAutoPopup = sal_True;
sal_uInt16 nEntry = ImplFindEntry( rMEvt.GetPosPixel() );
if ( ( nEntry != ITEMPOS_INVALID ) && ( nEntry != nHighlightedItem ) )
{
ChangeHighlightItem( nEntry, sal_False );
}
else
{
KillActivePopup();
ChangeHighlightItem( ITEMPOS_INVALID, sal_False );
}
}
void MenuBarWindow::MouseButtonUp( const MouseEvent& )
{
}
void MenuBarWindow::MouseMove( const MouseEvent& rMEvt )
{
// Im Move nur Highlighten, wenn schon eins gehighlightet.
if ( rMEvt.IsSynthetic() || rMEvt.IsLeaveWindow() || rMEvt.IsEnterWindow() || ( nHighlightedItem == ITEMPOS_INVALID ) )
return;
if( bIgnoreFirstMove )
{
bIgnoreFirstMove = sal_False;
return;
}
sal_uInt16 nEntry = ImplFindEntry( rMEvt.GetPosPixel() );
if ( ( nEntry != ITEMPOS_INVALID )
#ifdef OS2
&& ( ImplHilite(rMEvt) )
#endif
&& ( nEntry != nHighlightedItem ) )
ChangeHighlightItem( nEntry, sal_False );
}
void MenuBarWindow::ChangeHighlightItem( sal_uInt16 n, sal_Bool bSelectEntry, sal_Bool bAllowRestoreFocus, sal_Bool bDefaultToDocument)
{
if( ! pMenu )
return;
// #57934# ggf. das aktive Popup sofort schliessen, damit TH's Hintergrundsicherung funktioniert.
MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n );
if ( pActivePopup && pActivePopup->ImplGetWindow() && ( !pNextData || ( pActivePopup != pNextData->pSubMenu ) ) )
KillActivePopup(); // pActivePopup ggf. ohne pWin, wenn in Activate() Rescheduled wurde
// Activate am MenuBar immer nur einmal pro Vorgang...
sal_Bool bJustActivated = sal_False;
if ( ( nHighlightedItem == ITEMPOS_INVALID ) && ( n != ITEMPOS_INVALID ) )
{
ImplGetSVData()->maWinData.mbNoDeactivate = sal_True;
if( !bStayActive )
{
// #105406# avoid saving the focus when we already have the focus
sal_Bool bNoSaveFocus = (this == ImplGetSVData()->maWinData.mpFocusWin );
if( nSaveFocusId )
{
if( !ImplGetSVData()->maWinData.mbNoSaveFocus )
{
// we didn't clean up last time
Window::EndSaveFocus( nSaveFocusId, sal_False ); // clean up
nSaveFocusId = 0;
if( !bNoSaveFocus )
nSaveFocusId = Window::SaveFocus(); // only save focus when initially activated
}
else {
; // do nothing: we 're activated again from taskpanelist, focus was already saved
}
}
else
{
if( !bNoSaveFocus )
nSaveFocusId = Window::SaveFocus(); // only save focus when initially activated
}
}
else
bStayActive = sal_False;
pMenu->bInCallback = sal_True; // hier schon setzen, falls Activate ueberladen
pMenu->Activate();
pMenu->bInCallback = sal_False;
bJustActivated = sal_True;
}
else if ( ( nHighlightedItem != ITEMPOS_INVALID ) && ( n == ITEMPOS_INVALID ) )
{
pMenu->bInCallback = sal_True;
pMenu->Deactivate();
pMenu->bInCallback = sal_False;
ImplGetSVData()->maWinData.mbNoDeactivate = sal_False;
if( !ImplGetSVData()->maWinData.mbNoSaveFocus )
{
sal_uLong nTempFocusId = nSaveFocusId;
nSaveFocusId = 0;
Window::EndSaveFocus( nTempFocusId, bAllowRestoreFocus );
// #105406# restore focus to document if we could not save focus before
if( bDefaultToDocument && !nTempFocusId && bAllowRestoreFocus )
GrabFocusToDocument();
}
}
if ( nHighlightedItem != ITEMPOS_INVALID )
{
HighlightItem( nHighlightedItem, sal_False );
pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, nHighlightedItem );
}
nHighlightedItem = (sal_uInt16)n;
DBG_ASSERT( ( nHighlightedItem == ITEMPOS_INVALID ) || pMenu->ImplIsVisible( nHighlightedItem ), "ChangeHighlightItem: Not visible!" );
HighlightItem( nHighlightedItem, sal_True );
pMenu->SetHightlightItem(nHighlightedItem);
pMenu->ImplCallHighlight( nHighlightedItem );
if( mbAutoPopup )
ImplCreatePopup( bSelectEntry );
// #58935# #73659# Focus, wenn kein Popup drunter haengt...
if ( bJustActivated && !pActivePopup )
GrabFocus();
}
void MenuBarWindow::HighlightItem( sal_uInt16 nPos, sal_Bool bHighlight )
{
if( ! pMenu )
return;
long nX = 0;
sal_uLong nCount = pMenu->pItemList->Count();
for ( sal_uLong n = 0; n < nCount; n++ )
{
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
if ( n == nPos )
{
if ( pData->eType != MENUITEM_SEPARATOR )
{
// #107747# give menuitems the height of the menubar
Rectangle aRect = Rectangle( Point( nX, 1 ), Size( pData->aSz.Width(), GetOutputSizePixel().Height()-2 ) );
Push( PUSH_CLIPREGION );
IntersectClipRegion( aRect );
if ( bHighlight )
{
if( IsNativeControlSupported( CTRL_MENUBAR, PART_MENU_ITEM ) &&
IsNativeControlSupported( CTRL_MENUBAR, PART_ENTIRE_CONTROL ) )
{
// draw background (transparency)
MenubarValue aControlValue;
aControlValue.maTopDockingAreaHeight = ImplGetTopDockingAreaHeight( this );
Point tmp(0,0);
Rectangle aBgRegion( tmp, GetOutputSizePixel() );
DrawNativeControl( CTRL_MENUBAR, PART_ENTIRE_CONTROL,
aBgRegion,
CTRL_STATE_ENABLED,
aControlValue,
OUString() );
ImplAddNWFSeparator( this, aControlValue );
// draw selected item
DrawNativeControl( CTRL_MENUBAR, PART_MENU_ITEM,
aRect,
CTRL_STATE_ENABLED | CTRL_STATE_SELECTED,
aControlValue,
OUString() );
}
else
{
SetFillColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() );
SetLineColor();
DrawRect( aRect );
}
}
else
{
if( IsNativeControlSupported( CTRL_MENUBAR, PART_ENTIRE_CONTROL) )
{
MenubarValue aMenubarValue;
aMenubarValue.maTopDockingAreaHeight = ImplGetTopDockingAreaHeight( this );
// use full window size to get proper gradient
// but clip accordingly
Point aPt;
Rectangle aCtrlRect( aPt, GetOutputSizePixel() );
DrawNativeControl( CTRL_MENUBAR, PART_ENTIRE_CONTROL, aCtrlRect, CTRL_STATE_ENABLED, aMenubarValue, rtl::OUString() );
ImplAddNWFSeparator( this, aMenubarValue );
}
else
Erase( aRect );
}
Pop();
pMenu->ImplPaint( this, 0, 0, pData, bHighlight );
}
return;
}
nX += pData->aSz.Width();
}
}
Rectangle MenuBarWindow::ImplGetItemRect( sal_uInt16 nPos )
{
Rectangle aRect;
if( pMenu )
{
long nX = 0;
sal_uLong nCount = pMenu->pItemList->Count();
for ( sal_uLong n = 0; n < nCount; n++ )
{
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
if ( n == nPos )
{
if ( pData->eType != MENUITEM_SEPARATOR )
// #107747# give menuitems the height of the menubar
aRect = Rectangle( Point( nX, 1 ), Size( pData->aSz.Width(), GetOutputSizePixel().Height()-2 ) );
break;
}
nX += pData->aSz.Width();
}
}
return aRect;
}
void MenuBarWindow::KeyInput( const KeyEvent& rKEvent )
{
if ( !ImplHandleKeyEvent( rKEvent ) )
Window::KeyInput( rKEvent );
}
sal_Bool MenuBarWindow::ImplHandleKeyEvent( const KeyEvent& rKEvent, sal_Bool bFromMenu )
{
if( ! pMenu )
return sal_False;
if ( pMenu->bInCallback )
return sal_True; // schlucken
sal_Bool bDone = sal_False;
sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode();
if( GetParent() )
{
if( GetParent()->GetWindow( WINDOW_CLIENT )->IsSystemWindow() )
{
SystemWindow *pSysWin = (SystemWindow*)GetParent()->GetWindow( WINDOW_CLIENT );
if( pSysWin->GetTaskPaneList() )
if( pSysWin->GetTaskPaneList()->HandleKeyEvent( rKEvent ) )
return sal_True;
}
}
if ( nCode == KEY_MENU && !rKEvent.GetKeyCode().IsShift() ) // only F10, not Shift-F10
{
mbAutoPopup = ImplGetSVData()->maNWFData.mbOpenMenuOnF10;
if ( nHighlightedItem == ITEMPOS_INVALID )
{
ChangeHighlightItem( 0, sal_False );
GrabFocus();
}
else
{
ChangeHighlightItem( ITEMPOS_INVALID, sal_False );
nSaveFocusId = 0;
}
bDone = sal_True;
}
else if ( bFromMenu )
{
if ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ||
( nCode == KEY_HOME ) || ( nCode == KEY_END ) )
{
sal_uInt16 n = nHighlightedItem;
if ( n == ITEMPOS_INVALID )
{
if ( nCode == KEY_LEFT)
n = 0;
else
n = pMenu->GetItemCount()-1;
}
// handling gtk like (aka mbOpenMenuOnF10)
// do not highlight an item when opening a sub menu
// unless there already was a higlighted sub menu item
bool bWasHighlight = false;
if( pActivePopup )
{
MenuFloatingWindow* pSubWindow = dynamic_cast<MenuFloatingWindow*>(pActivePopup->ImplGetWindow());
if( pSubWindow )
bWasHighlight = (pSubWindow->GetHighlightedItem() != ITEMPOS_INVALID);
}
sal_uInt16 nLoop = n;
if( nCode == KEY_HOME )
{ n = (sal_uInt16)-1; nLoop = n+1; }
if( nCode == KEY_END )
{ n = pMenu->GetItemCount(); nLoop = n-1; }
do
{
if ( nCode == KEY_LEFT || nCode == KEY_END )
{
if ( n )
n--;
else
n = pMenu->GetItemCount()-1;
}
if ( nCode == KEY_RIGHT || nCode == KEY_HOME )
{
n++;
if ( n >= pMenu->GetItemCount() )
n = 0;
}
MenuItemData* pData = (MenuItemData*)pMenu->GetItemList()->GetDataFromPos( n );
if ( ( pData->eType != MENUITEM_SEPARATOR ) && pMenu->ImplIsVisible( n ) )
{
sal_Bool bDoSelect = sal_True;
if( ImplGetSVData()->maNWFData.mbOpenMenuOnF10 )
bDoSelect = bWasHighlight;
ChangeHighlightItem( n, bDoSelect );
break;
}
} while ( n != nLoop );
bDone = sal_True;
}
else if ( nCode == KEY_RETURN )
{
if( pActivePopup ) KillActivePopup();
else
if ( !mbAutoPopup )
{
ImplCreatePopup( sal_True );
mbAutoPopup = sal_True;
}
bDone = sal_True;
}
else if ( ( nCode == KEY_UP ) || ( nCode == KEY_DOWN ) )
{
if ( !mbAutoPopup )
{
ImplCreatePopup( sal_True );
mbAutoPopup = sal_True;
}
bDone = sal_True;
}
else if ( nCode == KEY_ESCAPE || ( nCode == KEY_F6 && rKEvent.GetKeyCode().IsMod1() ) )
{
if( pActivePopup )
{
// bring focus to menu bar without any open popup
mbAutoPopup = sal_False;
sal_uInt16 n = nHighlightedItem;
nHighlightedItem = ITEMPOS_INVALID;
bStayActive = sal_True;
ChangeHighlightItem( n, sal_False );
bStayActive = sal_False;
KillActivePopup();
GrabFocus();
}
else
ChangeHighlightItem( ITEMPOS_INVALID, sal_False );
if( nCode == KEY_F6 && rKEvent.GetKeyCode().IsMod1() )
{
// put focus into document
GrabFocusToDocument();
}
bDone = sal_True;
}
}
if ( !bDone && ( bFromMenu || rKEvent.GetKeyCode().IsMod2() ) )
{
xub_Unicode nCharCode = rKEvent.GetCharCode();
if ( nCharCode )
{
sal_uInt16 nEntry, nDuplicates;
MenuItemData* pData = pMenu->GetItemList()->SearchItem( nCharCode, rKEvent.GetKeyCode(), nEntry, nDuplicates, nHighlightedItem );
if ( pData && (nEntry != ITEMPOS_INVALID) )
{
mbAutoPopup = sal_True;
ChangeHighlightItem( nEntry, sal_True );
bDone = sal_True;
}
else
{
// Wegen Systemmenu und anderen System-HotKeys, nur
// eigenstaendige Character-Kombinationen auswerten
sal_uInt16 nKeyCode = rKEvent.GetKeyCode().GetCode();
if ( ((nKeyCode >= KEY_A) && (nKeyCode <= KEY_Z)) )
Sound::Beep();
}
}
}
return bDone;
}
void MenuBarWindow::Paint( const Rectangle& )
{
if( ! pMenu )
return;
// no VCL paint if native menus
if( pMenu->ImplGetSalMenu() && pMenu->ImplGetSalMenu()->VisibleMenuBar() )
{
ImplGetFrame()->DrawMenuBar();
return;
}
if( IsNativeControlSupported( CTRL_MENUBAR, PART_ENTIRE_CONTROL) )
{
Point aPt;
Rectangle aCtrlRegion( aPt, GetOutputSizePixel() );
MenubarValue aMenubarValue;
aMenubarValue.maTopDockingAreaHeight = ImplGetTopDockingAreaHeight( this );
DrawNativeControl( CTRL_MENUBAR, PART_ENTIRE_CONTROL, aCtrlRegion, CTRL_STATE_ENABLED, aMenubarValue, rtl::OUString() );
ImplAddNWFSeparator( this, aMenubarValue );
}
SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() );
pMenu->ImplPaint( this, 0 );
if ( nHighlightedItem != ITEMPOS_INVALID )
HighlightItem( nHighlightedItem, sal_True );
// in high contrast mode draw a separating line on the lower edge
if( ! IsNativeControlSupported( CTRL_MENUBAR, PART_ENTIRE_CONTROL) &&
GetSettings().GetStyleSettings().GetHighContrastMode() )
{
Push( PUSH_LINECOLOR | PUSH_MAPMODE );
SetLineColor( Color( COL_WHITE ) );
SetMapMode( MapMode( MAP_PIXEL ) );
Size aSize = GetSizePixel();
DrawLine( Point( 0, aSize.Height()-1 ), Point( aSize.Width()-1, aSize.Height()-1 ) );
Pop();
}
}
void MenuBarWindow::Resize()
{
Size aOutSz = GetOutputSizePixel();
long n = aOutSz.Height()-4;
long nX = aOutSz.Width()-3;
long nY = 2;
if ( aCloser.IsVisible() )
{
aCloser.Hide();
aCloser.SetImages( n );
Size aTbxSize( aCloser.CalcWindowSizePixel() );
nX -= aTbxSize.Width();
long nTbxY = (aOutSz.Height() - aTbxSize.Height())/2;
aCloser.SetPosSizePixel( nX, nTbxY, aTbxSize.Width(), aTbxSize.Height() );
nX -= 3;
aCloser.Show();
}
if ( aFloatBtn.IsVisible() )
{
nX -= n;
aFloatBtn.SetPosSizePixel( nX, nY, n, n );
}
if ( aHideBtn.IsVisible() )
{
nX -= n;
aHideBtn.SetPosSizePixel( nX, nY, n, n );
}
aFloatBtn.SetSymbol( SYMBOL_FLOAT );
aHideBtn.SetSymbol( SYMBOL_HIDE );
//aCloser.SetSymbol( SYMBOL_CLOSE ); //is a toolbox now
Invalidate();
}
sal_uInt16 MenuBarWindow::ImplFindEntry( const Point& rMousePos ) const
{
if( pMenu )
{
long nX = 0;
sal_uInt16 nCount = (sal_uInt16)pMenu->pItemList->Count();
for ( sal_uInt16 n = 0; n < nCount; n++ )
{
MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n );
if ( pMenu->ImplIsVisible( n ) )
{
nX += pData->aSz.Width();
if ( nX > rMousePos.X() )
return (sal_uInt16)n;
}
}
}
return ITEMPOS_INVALID;
}
void MenuBarWindow::RequestHelp( const HelpEvent& rHEvt )
{
sal_uInt16 nId = nHighlightedItem;
if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) )
ChangeHighlightItem( ITEMPOS_INVALID, sal_True );
Rectangle aHighlightRect( ImplGetItemRect( nHighlightedItem ) );
if( !ImplHandleHelpEvent( this, pMenu, nId, rHEvt, aHighlightRect ) )
Window::RequestHelp( rHEvt );
}
void MenuBarWindow::StateChanged( StateChangedType nType )
{
Window::StateChanged( nType );
if ( ( nType == STATE_CHANGE_CONTROLFOREGROUND ) ||
( nType == STATE_CHANGE_CONTROLBACKGROUND ) )
{
ImplInitMenuWindow( this, sal_False, sal_True );
Invalidate();
}
else if( pMenu )
pMenu->ImplKillLayoutData();
}
void MenuBarWindow::ImplLayoutChanged()
{
if( pMenu )
{
ImplInitMenuWindow( this, sal_True, sal_True );
// Falls sich der Font geaendert hat.
long nHeight = pMenu->ImplCalcSize( this ).Height();
// depending on the native implementation or the displayable flag
// the menubar windows is supressed (ie, height=0)
if( !((MenuBar*) pMenu)->IsDisplayable() ||
( pMenu->ImplGetSalMenu() && pMenu->ImplGetSalMenu()->VisibleMenuBar() ) )
nHeight = 0;
SetPosSizePixel( 0, 0, 0, nHeight, WINDOW_POSSIZE_HEIGHT );
GetParent()->Resize();
Invalidate();
Resize();
if( pMenu )
pMenu->ImplKillLayoutData();
}
}
void MenuBarWindow::ImplInitStyleSettings()
{
if( IsNativeControlSupported( CTRL_MENUBAR, PART_MENU_ITEM ) &&
IsNativeControlSupported( CTRL_MENUBAR, PART_ENTIRE_CONTROL ) )
{
Color aHighlightTextColor = ImplGetSVData()->maNWFData.maMenuBarHighlightTextColor;
if( aHighlightTextColor != Color( COL_TRANSPARENT ) )
{
AllSettings aSettings( GetSettings() );
StyleSettings aStyle( aSettings.GetStyleSettings() );
aStyle.SetMenuHighlightTextColor( aHighlightTextColor );
aSettings.SetStyleSettings( aStyle );
OutputDevice::SetSettings( aSettings );
}
}
}
void MenuBarWindow::DataChanged( const DataChangedEvent& rDCEvt )
{
Window::DataChanged( rDCEvt );
if ( (rDCEvt.GetType() == DATACHANGED_FONTS) ||
(rDCEvt.GetType() == DATACHANGED_FONTSUBSTITUTION) ||
((rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
(rDCEvt.GetFlags() & SETTINGS_STYLE)) )
{
ImplLayoutChanged();
ImplInitStyleSettings();
}
}
void MenuBarWindow::LoseFocus()
{
if ( !HasChildPathFocus( sal_True ) )
ChangeHighlightItem( ITEMPOS_INVALID, sal_False, sal_False );
}
void MenuBarWindow::GetFocus()
{
if ( nHighlightedItem == ITEMPOS_INVALID )
{
mbAutoPopup = sal_False; // do not open menu when activated by focus handling like taskpane cycling
ChangeHighlightItem( 0, sal_False );
}
}
::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > MenuBarWindow::CreateAccessible()
{
::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > xAcc;
if ( pMenu )
xAcc = pMenu->GetAccessible();
return xAcc;
}
sal_uInt16 MenuBarWindow::AddMenuBarButton( const Image& i_rImage, const Link& i_rLink, const String& i_rToolTip, sal_uInt16 i_nPos )
{
// find first free button id
sal_uInt16 nId = IID_DOCUMENTCLOSE;
std::map< sal_uInt16, AddButtonEntry >::const_iterator it;
if( i_nPos > m_aAddButtons.size() )
i_nPos = static_cast<sal_uInt16>(m_aAddButtons.size());
do
{
nId++;
it = m_aAddButtons.find( nId );
} while( it != m_aAddButtons.end() && nId < 128 );
DBG_ASSERT( nId < 128, "too many addbuttons in menubar" );
AddButtonEntry& rNewEntry = m_aAddButtons[nId];
rNewEntry.m_nId = nId;
rNewEntry.m_aSelectLink = i_rLink;
aCloser.InsertItem( nId, i_rImage, 0, 0 );
aCloser.calcMinSize();
ShowButtons( aCloser.IsItemVisible( IID_DOCUMENTCLOSE ),
aFloatBtn.IsVisible(),
aHideBtn.IsVisible() );
ImplLayoutChanged();
if( pMenu->mpSalMenu )
pMenu->mpSalMenu->AddMenuBarButton( SalMenuButtonItem( nId, i_rImage, i_rToolTip ) );
return nId;
}
void MenuBarWindow::SetMenuBarButtonHighlightHdl( sal_uInt16 nId, const Link& rLink )
{
std::map< sal_uInt16, AddButtonEntry >::iterator it = m_aAddButtons.find( nId );
if( it != m_aAddButtons.end() )
it->second.m_aHighlightLink = rLink;
}
Rectangle MenuBarWindow::GetMenuBarButtonRectPixel( sal_uInt16 nId )
{
Rectangle aRect;
if( m_aAddButtons.find( nId ) != m_aAddButtons.end() )
{
if( pMenu->mpSalMenu )
{
aRect = pMenu->mpSalMenu->GetMenuBarButtonRectPixel( nId, ImplGetWindowImpl()->mpFrame );
if( aRect == Rectangle( Point( -1, -1 ), Size( 1, 1 ) ) )
{
// system menu button is somehwere but location cannot be determined
return Rectangle();
}
}
if( aRect.IsEmpty() )
{
aRect = aCloser.GetItemRect( nId );
Point aOffset = aCloser.OutputToScreenPixel( Point() );
aRect.Move( aOffset.X(), aOffset.Y() );
}
}
return aRect;
}
void MenuBarWindow::RemoveMenuBarButton( sal_uInt16 nId )
{
sal_uInt16 nPos = aCloser.GetItemPos( nId );
aCloser.RemoveItem( nPos );
m_aAddButtons.erase( nId );
aCloser.calcMinSize();
ImplLayoutChanged();
if( pMenu->mpSalMenu )
pMenu->mpSalMenu->RemoveMenuBarButton( nId );
}
bool MenuBarWindow::HandleMenuButtonEvent( sal_uInt16 i_nButtonId )
{
std::map< sal_uInt16, AddButtonEntry >::iterator it = m_aAddButtons.find( i_nButtonId );
if( it != m_aAddButtons.end() )
{
MenuBar::MenuBarButtonCallbackArg aArg;
aArg.nId = it->first;
aArg.bHighlight = true;
aArg.pMenuBar = dynamic_cast<MenuBar*>(pMenu);
return it->second.m_aSelectLink.Call( &aArg );
}
return sal_False;
}
ImplMenuDelData::ImplMenuDelData( const Menu* pMenu )
: mpNext( 0 )
, mpMenu( 0 )
{
if( pMenu )
const_cast< Menu* >( pMenu )->ImplAddDel( *this );
}
ImplMenuDelData::~ImplMenuDelData()
{
if( mpMenu )
const_cast< Menu* >( mpMenu )->ImplRemoveDel( *this );
}