/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_vcl.hxx" #include "svsys.h" #include "vcl/salinst.hxx" #include "tools/list.hxx" #include "tools/debug.hxx" #include "vcl/svdata.hxx" #include "vcl/svapp.hxx" #include "vcl/mnemonic.hxx" #include "vcl/image.hxx" #include "vcl/event.hxx" #include "vcl/help.hxx" #include "vcl/svids.hrc" #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 "tools/rc.h" #include "vcl/menu.hxx" #include "vcl/button.hxx" #include "vcl/gradient.hxx" #include "vcl/i18nhelp.hxx" #include "vcl/taskpanelist.hxx" #include "vcl/window.h" #include "vcl/controllayout.hxx" #include "vcl/toolbox.hxx" #include "tools/stream.hxx" #include "vcl/salmenu.hxx" #include "vcl/salframe.hxx" #include "vcl/dockingarea.hxx" #include #include #include #include #include #include #include #include #include "vcl/lazydelete.hxx" #include namespace vcl { struct MenuLayoutData : public ControlLayoutData { std::vector< USHORT > m_aLineItemIds; std::vector< USHORT > m_aLineItemPositions; std::map< USHORT, Rectangle > m_aVisibleItemBoundRects; }; } using namespace ::com::sun::star; using namespace vcl; DBG_NAME( Menu ) #define ITEMPOS_INVALID 0xFFFF #define EXTRASPACEY 2 #define EXTRAITEMHEIGHT 4 // document closer #define IID_DOCUMENTCLOSE 1 #ifdef OS2 #include // return TRUE if hilite should be executed: left mouse button down // or xwp mouse hook enabled static BOOL ImplHilite( const MouseEvent& rMEvt ) { static BOOL init = FALSE; static HOOKCONFIG hc; // read XWP settings at program startup if (init == FALSE) { BOOL rc; ULONG cb = sizeof(HOOKCONFIG); memset(&hc, 0, sizeof(HOOKCONFIG)); rc = PrfQueryProfileData( HINI_USER, INIAPP_XWPHOOK, INIKEY_HOOK_CONFIG, &hc, &cb); init = TRUE; } // check mouse left button if (rMEvt.GetButtons() == MOUSE_LEFT) return TRUE; // return xwp flag return hc.fSlidingMenus; } #endif static 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) ? TRUE : FALSE; } struct MenuItemData { USHORT 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) ULONG nHelpId; // Help-Id ULONG nUserValue; // User value Image aImage; // Image KeyCode aAccelKey; // Accelerator-Key BOOL bChecked; // Checked BOOL bEnabled; // Enabled BOOL bVisible; // Visible (note: this flag will not override MENU_FLAG_HIDEDISABLEDENTRIES when true) BOOL bIsTemporary; // Temporary inserted ('No selection possible') 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( USHORT nId, MenuItemType eType, MenuItemBits nBits, const XubString& rStr, const Image& rImage, Menu* pMenu, USHORT nPos ); void InsertSeparator( USHORT nPos ); void Remove( USHORT nPos ); MenuItemData* GetData( USHORT nSVId, USHORT& rPos ) const; MenuItemData* GetData( USHORT nSVId ) const { USHORT nTemp; return GetData( nSVId, nTemp ); } MenuItemData* GetDataFromPos( ULONG nPos ) const { return (MenuItemData*)List::GetObject( nPos ); } MenuItemData* SearchItem( xub_Unicode cSelectChar, KeyCode aKeyCode, USHORT& rPos, USHORT& nDuplicates, USHORT nCurrentPos ) const; USHORT GetItemCount( xub_Unicode cSelectChar ) const; USHORT GetItemCount( KeyCode aKeyCode ) const; uno::Reference< i18n::XCharacterClassification > GetCharClass() const; }; MenuItemList::~MenuItemList() { for ( ULONG n = Count(); n; ) { MenuItemData* pData = GetDataFromPos( --n ); delete pData; } } MenuItemData* MenuItemList::Insert( USHORT nId, MenuItemType eType, MenuItemBits nBits, const XubString& rStr, const Image& rImage, Menu* pMenu, USHORT nPos ) { MenuItemData* pData = new MenuItemData( rStr, rImage ); pData->nId = nId; pData->eType = eType; pData->nBits = nBits; pData->pSubMenu = NULL; pData->pAutoSubMenu = NULL; pData->nHelpId = 0; pData->nUserValue = 0; pData->bChecked = FALSE; pData->bEnabled = TRUE; pData->bVisible = TRUE; pData->bIsTemporary = FALSE; pData->bMirrorMode = 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( USHORT nPos ) { MenuItemData* pData = new MenuItemData; pData->nId = 0; pData->eType = MENUITEM_SEPARATOR; pData->nBits = 0; pData->pSubMenu = NULL; pData->pAutoSubMenu = NULL; pData->nHelpId = 0; pData->nUserValue = 0; pData->bChecked = FALSE; pData->bEnabled = TRUE; pData->bVisible = TRUE; pData->bIsTemporary = FALSE; pData->bMirrorMode = 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( USHORT nPos ) { MenuItemData* pData = (MenuItemData*)List::Remove( (ULONG)nPos ); if ( pData ) delete pData; } MenuItemData* MenuItemList::GetData( USHORT nSVId, USHORT& 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, USHORT& rPos, USHORT& nDuplicates, USHORT nCurrentPos ) const { const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); USHORT nListCount = (USHORT)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('A' + (aKeyCode.GetCode() - KEY_A)); for ( rPos = 0; rPos < nListCount; rPos++) { MenuItemData* pData = GetDataFromPos( rPos ); if ( pData->bEnabled ) { USHORT 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; } USHORT MenuItemList::GetItemCount( xub_Unicode cSelectChar ) const { // returns number of entries with same mnemonic const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); USHORT nItems = 0, nPos; for ( nPos = (USHORT)Count(); nPos; ) { MenuItemData* pData = GetDataFromPos( --nPos ); if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) ) nItems++; } return nItems; } USHORT 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('A' + (aKeyCode.GetCode() - KEY_A)); USHORT nItems = 0, nPos; for ( nPos = (USHORT)Count(); nPos; ) { MenuItemData* pData = GetDataFromPos( --nPos ); if ( pData->bEnabled ) { USHORT 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; ULONG nSaveFocusId; // long nStartY; USHORT nHighlightedItem; // gehighlightetes/selektiertes Item USHORT nMBDownPos; USHORT nScrollerHeight; USHORT nFirstEntry; USHORT nBorder; USHORT nPosInParent; BOOL bInExecute; BOOL bScrollMenu; BOOL bScrollUp; BOOL bScrollDown; BOOL bIgnoreFirstMove; 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( BOOL bIncludeLogo = TRUE ) const; void ImplInitClipRegion(); void ImplDrawScroller( BOOL bUp ); using Window::ImplScroll; void ImplScroll( const Point& rMousePos ); void ImplScroll( BOOL bUp ); void ImplCursorUpDown( BOOL bUp, BOOL bHomeEnd = FALSE ); void ImplHighlightItem( const MouseEvent& rMEvt, BOOL bMBDown ); long ImplGetStartY() const; Rectangle ImplGetItemRect( USHORT 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( ULONG nId ) { nSaveFocusId = nId; } ULONG GetFocusId() const { return nSaveFocusId; } void EnableScrollMenu( BOOL b ); BOOL IsScrollMenu() const { return bScrollMenu; } USHORT GetScrollerHeight() const { return nScrollerHeight; } void Execute(); void StopExecute( ULONG nFocusId = 0 ); void EndExecute(); void EndExecute( USHORT nSelectId ); PopupMenu* GetActivePopup() const { return pActivePopup; } void KillActivePopup( PopupMenu* pThisOnly = NULL ); void HighlightItem( USHORT nPos, BOOL bHighlight ); void ChangeHighlightItem( USHORT n, BOOL bStartPopupTimer ); USHORT GetHighlightedItem() const { return nHighlightedItem; } void SetPosInParent( USHORT nPos ) { nPosInParent = nPos; } USHORT 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 { USHORT nItems = GetItemCount(); for( USHORT i = 0; i < nItems; i++ ) { USHORT 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 { USHORT m_nId; Link m_aSelectLink; Link m_aHighlightLink; AddButtonEntry() : m_nId( 0 ) {} }; Menu* pMenu; PopupMenu* pActivePopup; USHORT nHighlightedItem; ULONG nSaveFocusId; BOOL mbAutoPopup; BOOL bIgnoreFirstMove; BOOL bStayActive; DecoToolBox aCloser; PushButton aFloatBtn; PushButton aHideBtn; std::map< USHORT, AddButtonEntry > m_aAddButtons; void HighlightItem( USHORT nPos, BOOL bHighlight ); void ChangeHighlightItem( USHORT n, BOOL bSelectPopupEntry, BOOL bAllowRestoreFocus = TRUE, BOOL bDefaultToDocument = TRUE ); USHORT ImplFindEntry( const Point& rMousePos ) const; void ImplCreatePopup( BOOL bPreSelectFirst ); BOOL ImplHandleKeyEvent( const KeyEvent& rKEvent, BOOL bFromMenu = TRUE ); Rectangle ImplGetItemRect( USHORT 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( BOOL bClose, BOOL bFloat, 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( ULONG nId ) { nSaveFocusId = nId; } ULONG GetFocusId() const { return nSaveFocusId; } void SetMenu( MenuBar* pMenu ); void KillActivePopup(); PopupMenu* GetActivePopup() const { return pActivePopup; } void PopupClosed( Menu* pMenu ); USHORT GetHighlightedItem() const { return nHighlightedItem; } virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > CreateAccessible(); void SetAutoPopup( BOOL bAuto ) { mbAutoPopup = bAuto; } void ImplLayoutChanged(); Size MinCloseButtonSize(); // add an arbitrary button to the menubar (will appear next to closer) USHORT AddMenuBarButton( const Image&, const Link&, const String&, USHORT nPos ); void SetMenuBarButtonHighlightHdl( USHORT nId, const Link& ); Rectangle GetMenuBarButtonRectPixel( USHORT nId ); void RemoveMenuBarButton( USHORT nId ); bool HandleMenuButtonEvent( USHORT 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 ULONG ImplChangeTipTimeout( ULONG nTimeout, Window *pWindow ) { AllSettings aAllSettings( pWindow->GetSettings() ); HelpSettings aHelpSettings( aAllSettings.GetHelpSettings() ); ULONG nRet = aHelpSettings.GetTipTimeout(); aHelpSettings.SetTipTimeout( nTimeout ); aAllSettings.SetHelpSettings( aHelpSettings ); pWindow->SetSettings( aAllSettings ); return nRet; } static BOOL ImplHandleHelpEvent( Window* pMenuWindow, Menu* pMenu, USHORT nHighlightedItem, const HelpEvent& rHEvt, const Rectangle &rHighlightRect ) { if( ! pMenu ) return FALSE; BOOL bDone = FALSE; USHORT 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 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 = 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 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 = 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 ); ULONG nHelpId = pMenu->GetHelpId( nId ); if ( aCommand.Len() ) pHelp->Start( aCommand, NULL ); else if ( nHelpId ) pHelp->Start( nHelpId, NULL ); else pHelp->Start( OOO_HELP_INDEX, NULL ); } bDone = 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; BOOL bDone = 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 = 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 = FALSE; ImplInit(); } // this constructor makes sure we're creating the native menu // with the correct type (ie, MenuBar vs. PopupMenu) Menu::Menu( BOOL bMenubar ) { DBG_CTOR( Menu, NULL ); bIsMenuBar = bMenubar; ImplInit(); } Menu::~Menu() { DBG_DTOR( Menu, NULL ); vcl::LazyDeletor::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 ); bKilled = TRUE; delete pItemList; delete pLogo; delete mpLayoutData; // Native-support: destroy SalMenu ImplSetSalMenu( NULL ); } void Menu::doLazyDelete() { vcl::LazyDeletor::Delete( this ); } void Menu::ImplInit() { mnHighlightedItemPos = ITEMPOS_INVALID; mpSalMenu = NULL; nMenuFlags = MENU_FLAG_SHOWCHECKIMAGES; nDefaultItem = 0; //bIsMenuBar = 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 = FALSE; bInCallback = FALSE; bKilled = FALSE; mpLayoutData = NULL; // Native-support: returns NULL if not supported mpSalMenu = ImplGetSVData()->mpDefInst->CreateMenu( bIsMenuBar ); } 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 ); ULONG nObjMask = ReadLongRes(); if( nObjMask & RSC_MENU_ITEMS ) { ULONG nObjFollows = ReadLongRes(); // MenuItems einfuegen for( 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(ReadLongRes()) ); } void Menu::CreateAutoMnemonics() { MnemonicGenerator aMnemonicGenerator; 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 = TRUE; ImplCallEventListeners( VCLEVENT_MENU_ACTIVATE, ITEMPOS_INVALID ); if ( !aActivateHdl.Call( this ) ) { Menu* pStartMenu = ImplGetStartMenu(); if ( pStartMenu && ( pStartMenu != this ) ) { pStartMenu->bInCallback = TRUE; // MT 11/01: Call EventListener here? I don't know... pStartMenu->aActivateHdl.Call( this ); pStartMenu->bInCallback = FALSE; } } bInCallback = FALSE; } void Menu::Deactivate() { for ( USHORT n = (USHORT)pItemList->Count(); n; ) { MenuItemData* pData = pItemList->GetDataFromPos( --n ); if ( pData->bIsTemporary ) pItemList->Remove( n ); } bInCallback = TRUE; Menu* pStartMenu = ImplGetStartMenu(); ImplCallEventListeners( VCLEVENT_MENU_DEACTIVATE, ITEMPOS_INVALID ); if ( !aDeactivateHdl.Call( this ) ) { if ( pStartMenu && ( pStartMenu != this ) ) { pStartMenu->bInCallback = TRUE; pStartMenu->aDeactivateHdl.Call( this ); pStartMenu->bInCallback = FALSE; } } bInCallback = FALSE; if ( this == pStartMenu ) GetpApp()->HideHelpStatusText(); } void Menu::Highlight() { Menu* pStartMenu = ImplGetStartMenu(); if ( !aHighlightHdl.Call( this ) ) { if ( pStartMenu && ( pStartMenu != this ) ) pStartMenu->aHighlightHdl.Call( this ); } if ( GetCurItemId() ) GetpApp()->ShowHelpStatusText( GetHelpText( GetCurItemId() ) ); } void Menu::ImplSelect() { MenuItemData* pData = GetItemList()->GetData( nSelectedId ); if ( pData && (pData->nBits & MIB_AUTOCHECK) ) { BOOL bChecked = IsItemChecked( nSelectedId ); if ( pData->nBits & MIB_RADIOCHECK ) { if ( !bChecked ) CheckItem( nSelectedId, 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() { ImplCallEventListeners( VCLEVENT_MENU_SELECT, GetItemPos( GetCurItemId() ) ); if ( !aSelectHdl.Call( this ) ) { 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( ULONG nEvent, USHORT nPos ) { VclMenuEvent aEvent( this, nEvent, nPos ); // This is needed by atk accessibility bridge if ( nEvent == VCLEVENT_MENU_HIGHLIGHT ) { ImplGetSVData()->mpApp->ImplCallEventListeners( &aEvent ); } if ( !maEventListeners.empty() ) maEventListeners.Call( &aEvent ); Menu* pMenu = this; while ( pMenu ) { if ( !maChildEventListeners.empty() ) maChildEventListeners.Call( &aEvent ); 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( USHORT nItemId, const XubString& rStr, MenuItemBits nItemBits, USHORT 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 >= (USHORT)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( USHORT nItemId, const Image& rImage, MenuItemBits nItemBits, USHORT nPos ) { InsertItem( nItemId, ImplGetSVEmptyStr(), nItemBits, nPos ); SetItemImage( nItemId, rImage ); } void Menu::InsertItem( USHORT nItemId, const XubString& rStr, const Image& rImage, MenuItemBits nItemBits, USHORT nPos ) { InsertItem( nItemId, rStr, nItemBits, nPos ); SetItemImage( nItemId, rImage ); } void Menu::InsertItem( const ResId& rResId, USHORT nPos ) { ResMgr* pMgr = rResId.GetResMgr(); if( ! pMgr ) return; ULONG nObjMask; GetRes( rResId.SetRT( RSC_MENUITEM ) ); nObjMask = ReadLongRes(); BOOL bSep = FALSE; if ( nObjMask & RSC_MENUITEM_SEPARATOR ) bSep = (BOOL)ReadShortRes(); USHORT nItemId = 1; if ( nObjMask & RSC_MENUITEM_ID ) nItemId = sal::static_int_cast(ReadLongRes()); MenuItemBits nStatus = 0; if ( nObjMask & RSC_MENUITEM_STATUS ) nStatus = sal::static_int_cast(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 ); } ULONG nHelpId = 0; if ( nObjMask & RSC_MENUITEM_HELPID ) { nHelpId = ReadLongRes(); if ( !bSep ) SetHelpId( nItemId, nHelpId ); } if( !bSep /* && SvHelpSettings::HelpText( aHelpText, nHelpId ) */ ) 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, (BOOL)ReadShortRes() ); } if ( nObjMask & RSC_MENUITEM_DISABLE ) { if ( !bSep ) EnableItem( nItemId, !(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( USHORT nPos ) { // do nothing if its a menu bar if ( bIsMenuBar ) return; // if position > ItemCount, append if ( nPos >= (USHORT)pItemList->Count() ) nPos = MENU_APPEND; // put separator in item list pItemList->InsertSeparator( nPos ); // update native menu USHORT itemPos = nPos != MENU_APPEND ? nPos : (USHORT)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( USHORT nPos ) { BOOL bRemove = FALSE; if ( nPos < GetItemCount() ) { // update native menu if( ImplGetSalMenu() ) ImplGetSalMenu()->RemoveItem( nPos ); pItemList->Remove( nPos ); bRemove = 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, USHORT nPos, USHORT nNewPos, USHORT nMode = 0 ) { MenuItemType eType = rMenu.GetItemType( nPos ); if ( eType == MENUITEM_DONTKNOW ) return; if ( eType == MENUITEM_SEPARATOR ) pThis->InsertSeparator( nNewPos ); else { USHORT 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, TRUE ); if ( !rMenu.IsItemEnabled( nId ) ) pThis->EnableItem( nId, FALSE ); pThis->SetHelpId( nId, pData->nHelpId ); 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, USHORT nPos, USHORT nNewPos ) { ImplCopyItem( this, rMenu, nPos, nNewPos ); } void Menu::Clear() { for ( USHORT i = GetItemCount(); i; i-- ) RemoveItem( 0 ); } USHORT Menu::GetItemCount() const { return (USHORT)pItemList->Count(); } USHORT Menu::ImplGetVisibleItemCount() const { USHORT nItems = 0; for ( USHORT n = (USHORT)pItemList->Count(); n; ) { if ( ImplIsVisible( --n ) ) nItems++; } return nItems; } USHORT Menu::ImplGetFirstVisible() const { for ( USHORT n = 0; n < pItemList->Count(); n++ ) { if ( ImplIsVisible( n ) ) return n; } return ITEMPOS_INVALID; } USHORT Menu::ImplGetPrevVisible( USHORT nPos ) const { for ( USHORT n = nPos; n; ) { if ( n && ImplIsVisible( --n ) ) return n; } return ITEMPOS_INVALID; } USHORT Menu::ImplGetNextVisible( USHORT nPos ) const { for ( USHORT n = nPos+1; n < pItemList->Count(); n++ ) { if ( ImplIsVisible( n ) ) return n; } return ITEMPOS_INVALID; } USHORT Menu::GetItemId( USHORT nPos ) const { MenuItemData* pData = pItemList->GetDataFromPos( nPos ); if ( pData ) return pData->nId; else return 0; } USHORT Menu::GetItemPos( USHORT nItemId ) const { USHORT nPos; MenuItemData* pData = pItemList->GetData( nItemId, nPos ); if ( pData ) return nPos; else return MENU_ITEM_NOTFOUND; } MenuItemType Menu::GetItemType( USHORT nPos ) const { MenuItemData* pData = pItemList->GetDataFromPos( nPos ); if ( pData ) return pData->eType; else return MENUITEM_DONTKNOW; } USHORT Menu::GetCurItemId() const { return nSelectedId; } void Menu::SetItemBits( USHORT nItemId, MenuItemBits nBits ) { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) pData->nBits = nBits; } MenuItemBits Menu::GetItemBits( USHORT nItemId ) const { MenuItemBits nBits = 0; MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) nBits = pData->nBits; return nBits; } void Menu::SetUserValue( USHORT nItemId, ULONG nValue ) { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) pData->nUserValue = nValue; } ULONG Menu::GetUserValue( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); return pData ? pData->nUserValue : 0; } void Menu::SetPopupMenu( USHORT nItemId, PopupMenu* pMenu ) { USHORT 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( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return (PopupMenu*)(pData->pSubMenu); else return NULL; } void Menu::SetAccelKey( USHORT nItemId, const KeyCode& rKeyCode ) { USHORT 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( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->aAccelKey; else return KeyCode(); } KeyEvent Menu::GetActivationKey( USHORT nItemId ) const { KeyEvent aRet; MenuItemData* pData = pItemList->GetData( nItemId ); if( pData ) { USHORT nPos = pData->aText.Search( '~' ); if( nPos != STRING_NOTFOUND && nPos < pData->aText.Len()-1 ) { USHORT 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( USHORT nItemId, BOOL bCheck ) { USHORT 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; USHORT nGroupPos; USHORT nItemCount = GetItemCount(); BOOL bFound = FALSE; nGroupPos = nPos; while ( nGroupPos ) { pGroupData = pItemList->GetDataFromPos( nGroupPos-1 ); if ( pGroupData->nBits & MIB_RADIOCHECK ) { if ( IsItemChecked( pGroupData->nId ) ) { CheckItem( pGroupData->nId, FALSE ); bFound = 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, 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 ); } BOOL Menu::IsItemChecked( USHORT nItemId ) const { USHORT nPos; MenuItemData* pData = pItemList->GetData( nItemId, nPos ); if ( !pData ) return FALSE; return pData->bChecked; } void Menu::EnableItem( USHORT nItemId, BOOL bEnable ) { USHORT 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; ULONG nCount = pItemList->Count(); for ( 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 ); } } BOOL Menu::IsItemEnabled( USHORT nItemId ) const { USHORT nPos; MenuItemData* pData = pItemList->GetData( nItemId, nPos ); if ( !pData ) return FALSE; return pData->bEnabled; } void Menu::ShowItem( USHORT nItemId, BOOL bVisible ) { USHORT 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( USHORT nItemId, const XubString& rStr ) { USHORT 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( USHORT nItemId ) const { USHORT nPos; MenuItemData* pData = pItemList->GetData( nItemId, nPos ); if ( pData ) return pData->aText; else return ImplGetSVEmptyStr(); } void Menu::SetItemImage( USHORT nItemId, const Image& rImage ) { USHORT 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( USHORT nItemId, long nAngle10 ) { USHORT 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( USHORT nItemId, BOOL bMirror ) { USHORT 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( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->aImage; else return Image(); } long Menu::GetItemImageAngle( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->nItemImageAngle; else return 0; } BOOL Menu::GetItemImageMirrorMode( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->bMirrorMode; else return FALSE; } void Menu::SetItemCommand( USHORT nItemId, const String& rCommand ) { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) pData->aCommandStr = rCommand; } const XubString& Menu::GetItemCommand( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->aCommandStr; else return ImplGetSVEmptyStr(); } void Menu::SetHelpCommand( USHORT nItemId, const XubString& rStr ) { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) pData->aHelpCommandStr = rStr; } const XubString& Menu::GetHelpCommand( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->aHelpCommandStr; else return ImplGetSVEmptyStr(); } void Menu::SetHelpText( USHORT nItemId, const XubString& rStr ) { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) pData->aHelpText = rStr; } const XubString& Menu::ImplGetHelpText( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) { if ( !pData->aHelpText.Len() && (( pData->nHelpId ) || ( 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->nHelpId ) pData->aHelpText = pHelp->GetHelpText( pData->nHelpId, NULL ); } } return pData->aHelpText; } else return ImplGetSVEmptyStr(); } const XubString& Menu::GetHelpText( USHORT nItemId ) const { return ImplGetHelpText( nItemId ); } void Menu::SetTipHelpText( USHORT nItemId, const XubString& rStr ) { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) pData->aTipHelpText = rStr; } const XubString& Menu::GetTipHelpText( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->aTipHelpText; else return ImplGetSVEmptyStr(); } void Menu::SetHelpId( USHORT nItemId, ULONG nHelpId ) { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) pData->nHelpId = nHelpId; } ULONG Menu::GetHelpId( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->nHelpId; else return 0; } Menu& Menu::operator=( const Menu& rMenu ) { // Aufraeumen Clear(); // Items kopieren USHORT nCount = rMenu.GetItemCount(); for ( USHORT 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; } BOOL Menu::ImplIsVisible( USHORT nPos ) const { BOOL bVisible = TRUE; MenuItemData* pData = pItemList->GetDataFromPos( nPos ); // check general visibility first if( pData && !pData->bVisible ) bVisible = FALSE; if ( bVisible && pData && pData->eType == MENUITEM_SEPARATOR ) { if( nPos == 0 ) // no separator should be shown at the very beginning bVisible = FALSE; else { // always avoid adjacent separators USHORT nCount = (USHORT) pItemList->Count(); USHORT 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 = FALSE; // check for separator if( pNextData && pNextData->bVisible && pNextData->eType == MENUITEM_SEPARATOR ) bVisible = 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 = 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 = FALSE; else if ( pData->eType != MENUITEM_SEPARATOR ) // separators handled above { // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( TRUE ) ); bVisible = pData->bEnabled; // SubMenus nicht pruefen, weil sie ggf. erst im Activate() gefuellt werden. } } return bVisible; } BOOL Menu::IsItemVisible( USHORT nItemId ) const { return IsMenuVisible() && ImplIsVisible( GetItemPos( nItemId ) ); } BOOL Menu::IsItemPosVisible( USHORT nItemPos ) const { return IsMenuVisible() && ImplIsVisible( nItemPos ); } BOOL Menu::IsMenuVisible() const { return pWindow && pWindow->IsReallyVisible(); } BOOL Menu::ImplIsSelectable( USHORT nPos ) const { BOOL bSelectable = TRUE; MenuItemData* pData = pItemList->GetDataFromPos( nPos ); // check general visibility first if ( pData && ( pData->nBits & MIB_NOSELECT ) ) bSelectable = FALSE; return bSelectable; } void Menu::SelectItem( USHORT nItemId ) { if( bIsMenuBar ) static_cast(this)->SelectEntry( nItemId ); else static_cast(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; Region aNativeBounds; Region aNativeContent; Point tmp( 0, 0 ); Region 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.GetBoundRect().GetHeight(); rMaxWidth = aNativeContent.GetBoundRect().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.GetBoundRect().GetHeight(); rMaxWidth = Max (rMaxWidth, aNativeContent.GetBoundRect().GetWidth()); } } } return (rCheckHeight > rRadioHeight) ? rCheckHeight : rRadioHeight; } 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 ( USHORT i = (USHORT)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 ( USHORT n = (USHORT)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 ) { USHORT gfxExtra = (USHORT) Max( nExtra, 7L ); // #107710# increase space between checkmarks/images/text nCheckPos = (USHORT)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 = (USHORT)(nImagePos + nImgOrChkWidth); } else { nImagePos = nCheckPos; nTextPos = (USHORT)(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 = (USHORT)(2*nExtra); aSz.Height() = nFontHeight+6; // get menubar height from native methods if supported if( pWindow->IsNativeControlSupported( CTRL_MENUBAR, PART_ENTIRE_CONTROL ) ) { ImplControlValue aVal; Region aNativeBounds; Region aNativeContent; Point tmp( 0, 0 ); Region aCtrlRegion( Rectangle( 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.GetBoundRect().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 ) { BOOL bNativeOk = FALSE; if( i_pWindow->IsNativeControlSupported( CTRL_TOOLBAR, PART_BUTTON ) ) { ImplControlValue aControlValue; Region 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, TRUE, FALSE, 2, NULL, &aColor ); } } void Menu::ImplPaint( Window* pWin, USHORT nBorder, long nStartY, MenuItemData* pThisItemOnly, 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(); USHORT nCount = (USHORT)pItemList->Count(); if( bLayout ) mpLayoutData->m_aVisibleItemBoundRects.clear(); for ( USHORT 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; USHORT nTextStyle = 0; USHORT nSymbolStyle = 0; USHORT 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 ) ) { 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 ) ); pWin->DrawNativeControl( CTRL_MENU_POPUP, nPart, Region( aCheckRect ), nState, ImplControlValue(), 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; USHORT 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 ) ); } pWin->DrawCtrlText( aTmpPos, pData->aText, 0, pData->aText.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 ( pData->nBits & MIB_POPUPSELECT ) // { // aTmpPos.Y() += nFontHeight/2 ; // pWin->SetLineColor( rSettings.GetShadowColor() ); // pWin->DrawLine( aTmpPos, Point( aTmpPos.X() + nFontHeight/3, aTmpPos.Y() ) ); // pWin->SetLineColor( rSettings.GetLightColor() ); // aTmpPos.Y()++; // pWin->DrawLine( aTmpPos, Point( aTmpPos.X() + nFontHeight/3, aTmpPos.Y() ) ); // pWin->SetLineColor(); // } } 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( USHORT nHighlightedItem ) { nSelectedId = 0; MenuItemData* pData = pItemList->GetDataFromPos( nHighlightedItem ); if ( pData ) nSelectedId = pData->nId; ImplCallEventListeners( VCLEVENT_MENU_HIGHLIGHT, GetItemPos( GetCurItemId() ) ); Highlight(); nSelectedId = 0; } IMPL_LINK( Menu, ImplCallSelect, Menu*, EMPTYARG ) { nEventId = 0; Select(); return 0; } Menu* Menu::ImplFindSelectMenu() { Menu* pSelMenu = nEventId ? this : NULL; for ( ULONG n = GetItemList()->Count(); n && !pSelMenu; ) { MenuItemData* pData = GetItemList()->GetDataFromPos( --n ); if ( pData->pSubMenu ) pSelMenu = pData->pSubMenu->ImplFindSelectMenu(); } return pSelMenu; } Menu* Menu::ImplFindMenu( USHORT nItemId ) { Menu* pSelMenu = NULL; for ( 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( BOOL bCheckPopups, BOOL bRemoveEmptyPopups ) { for ( USHORT n = 0; n < GetItemCount(); n++ ) { BOOL bRemove = FALSE; MenuItemData* pItem = pItemList->GetDataFromPos( n ); if ( pItem->eType == MENUITEM_SEPARATOR ) { if ( !n || ( GetItemType( n-1 ) == MENUITEM_SEPARATOR ) ) bRemove = TRUE; } else bRemove = !pItem->bEnabled; if ( bCheckPopups && pItem->pSubMenu ) { pItem->pSubMenu->RemoveDisabledEntries( TRUE ); if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() ) bRemove = TRUE; } if ( bRemove ) RemoveItem( n-- ); } if ( GetItemCount() ) { USHORT nLast = GetItemCount() - 1; MenuItemData* pItem = pItemList->GetDataFromPos( nLast ); if ( pItem->eType == MENUITEM_SEPARATOR ) RemoveItem( nLast ); } delete mpLayoutData, mpLayoutData = NULL; } BOOL Menu::HasValidEntries( BOOL bCheckPopups ) { BOOL bValidEntries = FALSE; USHORT nCount = GetItemCount(); for ( USHORT 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( TRUE ); else bValidEntries = 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, FALSE, true ); } else { MenuFloatingWindow* pFloat = (MenuFloatingWindow*)pWindow; ImplPaint( pWindow, pFloat->nScrollerHeight, pFloat->ImplGetStartY(), 0, FALSE, true ); } } } String Menu::GetDisplayText() const { if( ! mpLayoutData ) ImplFillLayoutData(); return mpLayoutData ? mpLayoutData->m_aDisplayText : String(); } Rectangle Menu::GetCharacterBounds( USHORT 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, USHORT& 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( USHORT 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 ); } USHORT Menu::GetDisplayItemId( long nLine ) const { USHORT nItemId = 0; if( ! mpLayoutData ) ImplFillLayoutData(); if( mpLayoutData && ( nLine >= 0 ) && ( nLine < (long)mpLayoutData->m_aLineItemIds.size() ) ) nItemId = mpLayoutData->m_aLineItemIds[nLine]; return nItemId; } BOOL Menu::ConvertPoint( Point& rPoint, Window* pReferenceWindow ) const { BOOL bRet = FALSE; if( pWindow && pReferenceWindow ) { rPoint = pReferenceWindow->OutputToAbsoluteScreenPixel( rPoint ); rPoint = pWindow->AbsoluteScreenToOutputPixel( rPoint ); bRet = TRUE; } return bRet; } Rectangle Menu::GetBoundingRectangle( USHORT nPos ) const { Rectangle aRet; if( ! mpLayoutData ) ImplFillLayoutData(); if( mpLayoutData ) { std::map< USHORT, Rectangle >::const_iterator it = mpLayoutData->m_aVisibleItemBoundRects.find( nPos ); if( it != mpLayoutData->m_aVisibleItemBoundRects.end() ) aRet = it->second; } return aRet; } void Menu::SetAccessibleName( USHORT nItemId, const XubString& rStr ) { USHORT nPos; MenuItemData* pData = pItemList->GetData( nItemId, nPos ); if ( pData && !rStr.Equals( pData->aAccessibleName ) ) { pData->aAccessibleName = rStr; ImplCallEventListeners( VCLEVENT_MENU_ACCESSIBLENAMECHANGED, nPos ); } } XubString Menu::GetAccessibleName( USHORT nItemId ) const { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) return pData->aAccessibleName; else return ImplGetSVEmptyStr(); } void Menu::SetAccessibleDescription( USHORT nItemId, const XubString& rStr ) { MenuItemData* pData = pItemList->GetData( nItemId ); if ( pData ) pData->aAccessibleDescription = rStr; } XubString Menu::GetAccessibleDescription( USHORT 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; } BOOL Menu::GetSystemMenuData( SystemMenuData* pData ) const { Menu* pMenu = (Menu*)this; if( pData && pMenu->ImplGetSalMenu() ) { pMenu->ImplGetSalMenu()->GetSystemMenuData( pData ); return TRUE; } else return FALSE; } bool Menu::IsHighlighted( USHORT 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( USHORT nItemPos ) { if ( pWindow ) { if ( bIsMenuBar ) { MenuBarWindow* pMenuWin = static_cast< MenuBarWindow* >( pWindow ); pMenuWin->SetAutoPopup( FALSE ); pMenuWin->ChangeHighlightItem( nItemPos, FALSE ); } else { static_cast< MenuFloatingWindow* >( pWindow )->ChangeHighlightItem( nItemPos, FALSE ); } } } // ----------- // - MenuBar - // ----------- MenuBar::MenuBar() : Menu( TRUE ) { mbDisplayable = TRUE; mbCloserVisible = FALSE; mbFloatBtnVisible = FALSE; mbHideBtnVisible = FALSE; } MenuBar::MenuBar( const MenuBar& rMenu ) : Menu( TRUE ) { mbDisplayable = TRUE; mbCloserVisible = FALSE; mbFloatBtnVisible = FALSE; mbHideBtnVisible = FALSE; *this = rMenu; bIsMenuBar = TRUE; } MenuBar::MenuBar( const ResId& rResId ) : Menu ( TRUE ) { mbDisplayable = TRUE; mbCloserVisible = FALSE; mbFloatBtnVisible = FALSE; mbHideBtnVisible = FALSE; ImplLoadRes( rResId ); } MenuBar::~MenuBar() { ImplDestroy( this, TRUE ); } void MenuBar::ShowCloser( BOOL bShow ) { ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible ); } void MenuBar::ShowFloatButton( BOOL bShow ) { ShowButtons( mbCloserVisible, bShow, mbHideBtnVisible ); } void MenuBar::ShowHideButton( BOOL bShow ) { ShowButtons( mbCloserVisible, mbFloatBtnVisible, bShow ); } void MenuBar::ShowButtons( BOOL bClose, BOOL bFloat, 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( 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, BOOL bDelete ) { MenuBarWindow* pWindow = (MenuBarWindow*) pMenu->ImplGetWindow(); if ( pWindow && bDelete ) { pWindow->KillActivePopup(); delete pWindow; } pMenu->pWindow = NULL; } BOOL MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent, BOOL bFromMenu ) { BOOL bDone = 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( USHORT nId ) { MenuBarWindow* pMenuWin = (MenuBarWindow*) ImplGetWindow(); if( pMenuWin ) { pMenuWin->GrabFocus(); nId = GetItemPos( nId ); // #99705# popup the selected menu pMenuWin->SetAutoPopup( TRUE ); if( ITEMPOS_INVALID != pMenuWin->nHighlightedItem ) { pMenuWin->KillActivePopup(); pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); } if( nId != ITEMPOS_INVALID ) pMenuWin->ChangeHighlightItem( nId, FALSE ); } } // ----------------------------------------------------------------------- // handler for native menu selection and command events BOOL MenuBar::HandleMenuActivateEvent( Menu *pMenu ) const { if( pMenu ) { pMenu->pStartedFrom = (Menu*)this; pMenu->bInCallback = TRUE; pMenu->Activate(); pMenu->bInCallback = FALSE; } return TRUE; } BOOL MenuBar::HandleMenuDeActivateEvent( Menu *pMenu ) const { if( pMenu ) { pMenu->pStartedFrom = (Menu*)this; pMenu->bInCallback = TRUE; pMenu->Deactivate(); pMenu->bInCallback = FALSE; } return TRUE; } BOOL MenuBar::HandleMenuHighlightEvent( Menu *pMenu, USHORT nHighlightEventId ) const { if( !pMenu ) pMenu = ((Menu*) this)->ImplFindMenu( nHighlightEventId ); if( pMenu ) { if( mnHighlightedItemPos != ITEMPOS_INVALID ) pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, mnHighlightedItemPos ); pMenu->mnHighlightedItemPos = pMenu->GetItemPos( nHighlightEventId ); pMenu->nSelectedId = nHighlightEventId; pMenu->pStartedFrom = (Menu*)this; pMenu->ImplCallHighlight( pMenu->mnHighlightedItemPos ); return TRUE; } else return FALSE; } BOOL MenuBar::HandleMenuCommandEvent( Menu *pMenu, USHORT nCommandEventId ) const { if( !pMenu ) pMenu = ((Menu*) this)->ImplFindMenu( nCommandEventId ); if( pMenu ) { pMenu->nSelectedId = nCommandEventId; pMenu->pStartedFrom = (Menu*)this; pMenu->ImplSelect(); return TRUE; } else return FALSE; } USHORT MenuBar::AddMenuBarButton( const Image& i_rImage, const Link& i_rLink, USHORT i_nPos ) { return AddMenuBarButton( i_rImage, i_rLink, String(), i_nPos ); } USHORT MenuBar::AddMenuBarButton( const Image& i_rImage, const Link& i_rLink, const String& i_rToolTip, USHORT i_nPos ) { return pWindow ? static_cast(pWindow)->AddMenuBarButton( i_rImage, i_rLink, i_rToolTip, i_nPos ) : 0; } void MenuBar::SetMenuBarButtonHighlightHdl( USHORT nId, const Link& rLink ) { if( pWindow ) static_cast(pWindow)->SetMenuBarButtonHighlightHdl( nId, rLink ); } Rectangle MenuBar::GetMenuBarButtonRectPixel( USHORT nId ) { return pWindow ? static_cast(pWindow)->GetMenuBarButtonRectPixel( nId ) : Rectangle(); } void MenuBar::RemoveMenuBarButton( USHORT nId ) { if( pWindow ) static_cast(pWindow)->RemoveMenuBarButton( nId ); } BOOL MenuBar::HandleMenuButtonEvent( Menu *, USHORT i_nButtonId ) const { return static_cast(pWindow)->HandleMenuButtonEvent( i_nButtonId ); } // ----------------------------------------------------------------------- // BOOL PopupMenu::bAnyPopupInExecute = 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 } BOOL PopupMenu::IsInExecute() { return GetActivePopupMenu() ? TRUE : FALSE; } PopupMenu* PopupMenu::GetActivePopupMenu() { ImplSVData* pSVData = ImplGetSVData(); return pSVData->maAppData.mpActivePopupMenu; } void PopupMenu::EndExecute( USHORT nSelectId ) { if ( ImplGetWindow() ) ImplGetFloatingWindow()->EndExecute( nSelectId ); } void PopupMenu::SelectEntry( USHORT nId ) { if ( ImplGetWindow() ) { if( nId != ITEMPOS_INVALID ) { USHORT nPos; MenuItemData* pData = GetItemList()->GetData( nId, nPos ); if ( pData->pSubMenu ) ImplGetFloatingWindow()->ChangeHighlightItem( nPos, TRUE ); else ImplGetFloatingWindow()->EndExecute( nId ); } else { MenuFloatingWindow* pFloat = ImplGetFloatingWindow(); pFloat->GrabFocus(); USHORT nPos; for( nPos = 0; nPos < GetItemList()->Count(); nPos++ ) { MenuItemData* pData = (MenuItemData*)GetItemList()->GetObject( nPos ); if( pData->pSubMenu ) { pFloat->KillActivePopup(); } } pFloat->ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); } } } void PopupMenu::SetSelectedEntry( USHORT nId ) { nSelectedId = nId; } USHORT PopupMenu::Execute( Window* pExecWindow, const Point& rPopupPos ) { return Execute( pExecWindow, Rectangle( rPopupPos, rPopupPos ), POPUPMENU_EXECUTE_DOWN ); } USHORT PopupMenu::Execute( Window* pExecWindow, const Rectangle& rRect, USHORT nFlags ) { 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, FALSE ); } USHORT PopupMenu::ImplExecute( Window* pW, const Rectangle& rRect, ULONG nPopupModeFlags, Menu* pSFrom, BOOL bPreSelectFirst ) { if ( !pSFrom && ( PopupMenu::IsInExecute() || !GetItemCount() ) ) return 0; delete mpLayoutData, mpLayoutData = NULL; ImplSVData* pSVData = ImplGetSVData(); pStartedFrom = pSFrom; nSelectedId = 0; bCanceled = FALSE; ULONG nFocusId = 0; BOOL bRealExecute = FALSE; if ( !pStartedFrom ) { pSVData->maWinData.mbNoDeactivate = TRUE; nFocusId = Window::SaveFocus(); bRealExecute = 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 = TRUE; // hier schon setzen, falls Activate ueberladen Activate(); bInCallback = 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; USHORT 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 ); pData->bIsTemporary = TRUE; } } 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( TRUE ); USHORT nStart = ImplGetFirstVisible(); USHORT 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 && 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 ) { USHORT 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 ) { USHORT nCount = (USHORT)pItemList->Count(); for ( USHORT 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, 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 = 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; } USHORT PopupMenu::ImplCalcVisEntries( long nMaxHeight, USHORT nStartEntry, USHORT* pLastVisible ) const { nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight(); long nHeight = 0; USHORT nEntries = (USHORT) pItemList->Count(); USHORT nVisEntries = 0; if ( pLastVisible ) *pLastVisible = 0; for ( USHORT 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( USHORT nEntries ) const { long nHeight = 0; USHORT nFound = 0; for ( USHORT n = 0; ( nFound < nEntries ) && ( n < pItemList->Count() ); n++ ) { if ( ImplIsVisible( (USHORT) n ) ) { MenuItemData* pData = pItemList->GetDataFromPos( n ); nHeight += pData->aSz.Height(); nFound++; } } nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight(); return nHeight; } static void ImplInitMenuWindow( Window* pWin, BOOL bFont, 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( 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= TRUE; pMenu = pMen; pActivePopup = 0; nSaveFocusId = 0; bInExecute = FALSE; bScrollMenu = FALSE; nHighlightedItem = ITEMPOS_INVALID; nMBDownPos = ITEMPOS_INVALID; nPosInParent = ITEMPOS_INVALID; nScrollerHeight = 0; // nStartY = 0; nBorder = EXTRASPACEY; nFirstEntry = 0; bScrollUp = FALSE; bScrollDown = FALSE; bIgnoreFirstMove = TRUE; bKeyInput = FALSE; EnableSaveBackground(); ImplInitMenuWindow( this, TRUE, 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 ); if( !bKeyInput && pMenu && pMenu->pStartedFrom && !pMenu->pStartedFrom->bIsMenuBar ) { // #102461# remove highlight in parent MenuItemData* pData; USHORT i, nCount = (USHORT)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, 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 ( USHORT n = 0; n < nFirstEntry; n++ ) nY += pMenu->GetItemList()->GetDataFromPos( n )->aSz.Height(); } return -nY; } Region MenuFloatingWindow::ImplCalcClipRegion( 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, BOOL bMBDown ) { if( ! pMenu ) return; long nY = nScrollerHeight; long nMouseY = rMEvt.GetPosPixel().Y(); Size aOutSz = GetOutputSizePixel(); if ( ( nMouseY >= nY ) && ( nMouseY < ( aOutSz.Height() - nY ) ) ) { BOOL bHighlighted = FALSE; USHORT nCount = (USHORT)pMenu->pItemList->Count(); nY += ImplGetStartY(); // ggf. gescrollt. for ( USHORT 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 ) ) { BOOL bPopupArea = 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( (USHORT)n, FALSE ); } BOOL bAllowNewPopup = TRUE; if ( pActivePopup ) { MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); bAllowNewPopup = pData && ( pData->pSubMenu != pActivePopup ); if ( bAllowNewPopup ) KillActivePopup(); } if ( bPopupArea && bAllowNewPopup ) { HighlightChanged( NULL ); } } else { if ( n != nHighlightedItem ) { ChangeHighlightItem( (USHORT)n, TRUE ); } else if ( pItemData->nBits & MIB_POPUPSELECT ) { if ( bPopupArea && ( pActivePopup != pItemData->pSubMenu ) ) HighlightChanged( NULL ); } } bHighlighted = TRUE; } } } if ( !bHighlighted ) ChangeHighlightItem( ITEMPOS_INVALID, TRUE ); } else { ImplScroll( rMEvt.GetPosPixel() ); ChangeHighlightItem( ITEMPOS_INVALID, 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 = TRUE; } bInExecute = FALSE; pMenu->bInCallback = TRUE; pMenu->Deactivate(); pMenu->bInCallback = 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 ) ) { 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 ( 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; ULONG nOldFlags = GetPopupModeFlags(); SetPopupModeFlags( GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE ); USHORT nRet = pActivePopup->ImplExecute( this, Rectangle( aItemTopLeft, aItemBottomRight ), FLOATWIN_POPUPMODE_RIGHT, pMenu, pTimer ? FALSE : 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( BOOL b ) { bScrollMenu = b; nScrollerHeight = b ? (USHORT) GetSettings().GetStyleSettings().GetScrollBarSize() /2 : 0; bScrollDown = TRUE; ImplInitClipRegion(); } void MenuFloatingWindow::Execute() { ImplSVData* pSVData = ImplGetSVData(); pSVData->maAppData.mpActivePopupMenu = (PopupMenu*)pMenu; bInExecute = TRUE; // bCallingSelect = FALSE; while ( bInExecute ) Application::Yield(); pSVData->maAppData.mpActivePopupMenu = NULL; // while ( bCallingSelect ) // Application::Yield(); } void MenuFloatingWindow::StopExecute( ULONG nFocusId ) { // Focus wieder herstellen // (kann schon im Select wieder hergestellt wurden sein) if ( nSaveFocusId ) { Window::EndSaveFocus( nFocusId, FALSE ); nFocusId = nSaveFocusId; if ( nFocusId ) { nSaveFocusId = 0; ImplGetSVData()->maWinData.mbNoDeactivate = FALSE; } } ImplEndPopupMode( 0, nFocusId ); aHighlightChangedTimer.Stop(); bInExecute = 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 = 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 = TRUE; pPopup->Deactivate(); pPopup->bInCallback = 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; ULONG nFocusId = 0; if ( pStart && pStart->bIsMenuBar ) { nFocusId = ((MenuBarWindow*)((MenuBar*)pStart)->ImplGetWindow())->GetFocusId(); if ( nFocusId ) { ((MenuBarWindow*)((MenuBar*)pStart)->ImplGetWindow())->SetFocusId( 0 ); ImplGetSVData()->maWinData.mbNoDeactivate = 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; USHORT 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( USHORT nId ) { USHORT 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, 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 USHORT _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, FALSE ); #ifdef OS2 } #endif if ( IsScrollMenu() ) ImplScroll( rMEvt.GetPosPixel() ); } else #ifdef OS2 if ( ImplHilite(rMEvt) ) #endif { aSubmenuCloseTimer.Stop(); if( bIgnoreFirstMove ) bIgnoreFirstMove = FALSE; else ImplHighlightItem( rMEvt, FALSE ); } } void MenuFloatingWindow::ImplScroll( BOOL bUp ) { KillActivePopup(); Update(); if( ! pMenu ) return; HighlightItem( nHighlightedItem, 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 = TRUE; ImplDrawScroller( FALSE ); } if ( pMenu->ImplGetPrevVisible( nFirstEntry ) == ITEMPOS_INVALID ) { bScrollUp = FALSE; ImplDrawScroller( TRUE ); } Scroll( 0, nScrollEntryHeight, ImplCalcClipRegion( 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 = TRUE; ImplDrawScroller( TRUE ); } long nHeight = GetOutputSizePixel().Height(); USHORT nLastVisible; ((PopupMenu*)pMenu)->ImplCalcVisEntries( nHeight, nFirstEntry, &nLastVisible ); if ( pMenu->ImplGetNextVisible( nLastVisible ) == ITEMPOS_INVALID ) { bScrollDown = FALSE; ImplDrawScroller( FALSE ); } // nStartY -= nEntryHeight; Scroll( 0, -nScrollEntryHeight, ImplCalcClipRegion( FALSE ).GetBoundRect(), SCROLL_CLIP ); } HighlightItem( nHighlightedItem, 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( TRUE ); nDelta = nY - nMouseY; } else if ( bScrollDown && ( nMouseY > ( aOutSz.Height() - nY ) ) ) { ImplScroll( 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( USHORT n, 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, FALSE ); pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, nHighlightedItem ); } nHighlightedItem = (USHORT)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; USHORT i, nCount = (USHORT)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, TRUE ); pPWin->nHighlightedItem = i; } } } HighlightItem( nHighlightedItem, TRUE ); 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( USHORT nPos, 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; USHORT nCount = (USHORT)pMenu->pItemList->Count(); for ( USHORT 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 ) { BOOL bRestoreLineColor = 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() ) ); DrawNativeControl( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, Region( aCtrlRect ), CTRL_STATE_ENABLED, ImplControlValue(), OUString() ); if( bHighlight && IsNativeControlSupported( CTRL_MENU_POPUP, PART_MENU_ITEM ) ) { bDrawItemRect = false; if( FALSE == DrawNativeControl( CTRL_MENU_POPUP, PART_MENU_ITEM, Region( aItemRect ), CTRL_STATE_SELECTED | ( pData->bEnabled? CTRL_STATE_ENABLED: 0 ), ImplControlValue(), 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 = 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( USHORT 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(); USHORT nCount = (USHORT)pMenu->pItemList->Count(); for ( USHORT 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( BOOL bUp, BOOL bHomeEnd ) { if( ! pMenu ) return; const StyleSettings& rSettings = GetSettings().GetStyleSettings(); USHORT n = nHighlightedItem; if ( n == ITEMPOS_INVALID ) { if ( bUp ) n = 0; else n = pMenu->GetItemCount()-1; } USHORT nLoop = n; if( bHomeEnd ) { // absolute positioning if( bUp ) { n = pMenu->GetItemCount(); nLoop = n-1; } else { n = (USHORT)-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, FALSE ); while ( n < nFirstEntry ) ImplScroll( TRUE ); Size aOutSz = GetOutputSizePixel(); USHORT nLastVisible; ((PopupMenu*)pMenu)->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible ); while ( n > nLastVisible ) { ImplScroll( FALSE ); ((PopupMenu*)pMenu)->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry, &nLastVisible ); } } ChangeHighlightItem( n, FALSE ); break; } } while ( n != nLoop ); } void MenuFloatingWindow::KeyInput( const KeyEvent& rKEvent ) { ImplDelData aDelData; ImplAddDel( &aDelData ); USHORT nCode = rKEvent.GetKeyCode().GetCode(); bKeyInput = TRUE; switch ( nCode ) { case KEY_UP: case KEY_DOWN: { ImplCursorUpDown( nCode == KEY_UP ); } break; case KEY_END: case KEY_HOME: { ImplCursorUpDown( nCode == KEY_END, TRUE ); } break; case KEY_F6: case KEY_ESCAPE: { // Ctrl-F6 acts like ESC here, the menu bar however will then put the focus in the document if( nCode == KEY_F6 && !rKEvent.GetKeyCode().IsMod1() ) break; if( pMenu ) { if ( !pMenu->pStartedFrom ) { StopExecute(); KillActivePopup(); } else if ( pMenu->pStartedFrom->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(); } } } break; case KEY_RIGHT: { if( pMenu ) { BOOL bDone = FALSE; if ( nHighlightedItem != ITEMPOS_INVALID ) { MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); if ( pData && pData->pSubMenu ) { HighlightChanged( 0 ); bDone = 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(); USHORT nPos = 0; USHORT 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, 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 = 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; DrawNativeControl( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, Region( Rectangle( Point( nX, 0 ), aPxSize ) ), CTRL_STATE_ENABLED, ImplControlValue(), OUString() ); ImplInitClipRegion(); } if ( IsScrollMenu() ) { ImplDrawScroller( TRUE ); ImplDrawScroller( FALSE ); } SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() ); pMenu->ImplPaint( this, nScrollerHeight, ImplGetStartY() ); if ( nHighlightedItem != ITEMPOS_INVALID ) HighlightItem( nHighlightedItem, TRUE ); } void MenuFloatingWindow::ImplDrawScroller( 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; USHORT 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 ) { USHORT 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, FALSE, 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, FALSE, 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 = TRUE; nSaveFocusId = 0; bIgnoreFirstMove = TRUE; bStayActive = 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( 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, TRUE, 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( BOOL bClose, BOOL bFloat, 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::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(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(pMenu); if( pEvent->GetId() == VCLEVENT_TOOLBOX_HIGHLIGHT ) aArg.nId = aCloser.GetHighlightItemId(); else if( pEvent->GetId() == VCLEVENT_TOOLBOX_HIGHLIGHTOFF ) { USHORT nPos = static_cast< USHORT >(reinterpret_cast(pEvent->GetData())); aArg.nId = aCloser.GetItemId( nPos ); } std::map< USHORT, 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( BOOL bPreSelectFirst ) { MenuItemData* pItemData = pMenu ? pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ) : NULL; if ( pItemData ) { bIgnoreFirstMove = 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 ( 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 = TRUE; pActivePopup->bInCallback = TRUE; pActivePopup->Deactivate(); pActivePopup->bInCallback = 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, FALSE, ImplGetFrameWindow()->ImplGetFrameData()->mbHasFocus, FALSE ); } } void MenuBarWindow::MouseButtonDown( const MouseEvent& rMEvt ) { mbAutoPopup = TRUE; USHORT nEntry = ImplFindEntry( rMEvt.GetPosPixel() ); if ( ( nEntry != ITEMPOS_INVALID ) && ( nEntry != nHighlightedItem ) ) { ChangeHighlightItem( nEntry, FALSE ); } else { KillActivePopup(); ChangeHighlightItem( ITEMPOS_INVALID, 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 = FALSE; return; } USHORT nEntry = ImplFindEntry( rMEvt.GetPosPixel() ); if ( ( nEntry != ITEMPOS_INVALID ) #ifdef OS2 && ( ImplHilite(rMEvt) ) #endif && ( nEntry != nHighlightedItem ) ) ChangeHighlightItem( nEntry, FALSE ); } void MenuBarWindow::ChangeHighlightItem( USHORT n, BOOL bSelectEntry, BOOL bAllowRestoreFocus, 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... BOOL bJustActivated = FALSE; if ( ( nHighlightedItem == ITEMPOS_INVALID ) && ( n != ITEMPOS_INVALID ) ) { ImplGetSVData()->maWinData.mbNoDeactivate = TRUE; if( !bStayActive ) { // #105406# avoid saving the focus when we already have the focus BOOL bNoSaveFocus = (this == ImplGetSVData()->maWinData.mpFocusWin ); if( nSaveFocusId ) { if( !ImplGetSVData()->maWinData.mbNoSaveFocus ) { // we didn't clean up last time Window::EndSaveFocus( nSaveFocusId, 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 = FALSE; pMenu->bInCallback = TRUE; // hier schon setzen, falls Activate ueberladen pMenu->Activate(); pMenu->bInCallback = FALSE; bJustActivated = TRUE; } else if ( ( nHighlightedItem != ITEMPOS_INVALID ) && ( n == ITEMPOS_INVALID ) ) { pMenu->bInCallback = TRUE; pMenu->Deactivate(); pMenu->bInCallback = FALSE; ImplGetSVData()->maWinData.mbNoDeactivate = FALSE; if( !ImplGetSVData()->maWinData.mbNoSaveFocus ) { 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, FALSE ); pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, nHighlightedItem ); } nHighlightedItem = (USHORT)n; DBG_ASSERT( ( nHighlightedItem == ITEMPOS_INVALID ) || pMenu->ImplIsVisible( nHighlightedItem ), "ChangeHighlightItem: Not visible!" ); HighlightItem( nHighlightedItem, TRUE ); pMenu->ImplCallHighlight( nHighlightedItem ); if( mbAutoPopup ) ImplCreatePopup( bSelectEntry ); // #58935# #73659# Focus, wenn kein Popup drunter haengt... if ( bJustActivated && !pActivePopup ) GrabFocus(); } void MenuBarWindow::HighlightItem( USHORT nPos, BOOL bHighlight ) { if( ! pMenu ) return; long nX = 0; ULONG nCount = pMenu->pItemList->Count(); for ( 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) ImplControlValue aControlValue; MenubarValue aMenubarValue; aMenubarValue.maTopDockingAreaHeight = ImplGetTopDockingAreaHeight( this ); aControlValue.setOptionalVal( (void *)(&aMenubarValue) ); Point tmp(0,0); Region aBgRegion( Rectangle( tmp, GetOutputSizePixel() ) ); DrawNativeControl( CTRL_MENUBAR, PART_ENTIRE_CONTROL, aBgRegion, CTRL_STATE_ENABLED, aControlValue, OUString() ); ImplAddNWFSeparator( this, aMenubarValue ); // draw selected item DrawNativeControl( CTRL_MENUBAR, PART_MENU_ITEM, Region( 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) ) { ImplControlValue aControlValue; MenubarValue aMenubarValue; aMenubarValue.maTopDockingAreaHeight = ImplGetTopDockingAreaHeight( this ); aControlValue.setOptionalVal( (void *)(&aMenubarValue) ); // use full window size to get proper gradient // but clip accordingly Point aPt; Rectangle aCtrlRect( aPt, GetOutputSizePixel() ); Region aCtrlRegion( aCtrlRect ); DrawNativeControl( CTRL_MENUBAR, PART_ENTIRE_CONTROL, aCtrlRegion, CTRL_STATE_ENABLED, aControlValue, rtl::OUString() ); ImplAddNWFSeparator( this, aMenubarValue ); } else Erase( aRect ); } Pop(); pMenu->ImplPaint( this, 0, 0, pData, bHighlight ); } return; } nX += pData->aSz.Width(); } } Rectangle MenuBarWindow::ImplGetItemRect( USHORT nPos ) { Rectangle aRect; if( pMenu ) { long nX = 0; ULONG nCount = pMenu->pItemList->Count(); for ( 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 ); } BOOL MenuBarWindow::ImplHandleKeyEvent( const KeyEvent& rKEvent, BOOL bFromMenu ) { if( ! pMenu ) return FALSE; if ( pMenu->bInCallback ) return TRUE; // schlucken BOOL bDone = FALSE; USHORT 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 TRUE; } } if ( nCode == KEY_MENU && !rKEvent.GetKeyCode().IsShift() ) // only F10, not Shift-F10 { mbAutoPopup = ImplGetSVData()->maNWFData.mbOpenMenuOnF10; if ( nHighlightedItem == ITEMPOS_INVALID ) { ChangeHighlightItem( 0, FALSE ); GrabFocus(); } else { ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); nSaveFocusId = 0; } bDone = TRUE; } else if ( bFromMenu ) { if ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) || ( nCode == KEY_HOME ) || ( nCode == KEY_END ) ) { USHORT 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(pActivePopup->ImplGetWindow()); if( pSubWindow ) bWasHighlight = (pSubWindow->GetHighlightedItem() != ITEMPOS_INVALID); } USHORT nLoop = n; if( nCode == KEY_HOME ) { n = (USHORT)-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 ) ) { BOOL bDoSelect = TRUE; if( ImplGetSVData()->maNWFData.mbOpenMenuOnF10 ) bDoSelect = bWasHighlight; ChangeHighlightItem( n, bDoSelect ); break; } } while ( n != nLoop ); bDone = TRUE; } else if ( nCode == KEY_RETURN ) { if( pActivePopup ) KillActivePopup(); else if ( !mbAutoPopup ) { ImplCreatePopup( TRUE ); mbAutoPopup = TRUE; } bDone = TRUE; } else if ( ( nCode == KEY_UP ) || ( nCode == KEY_DOWN ) ) { if ( !mbAutoPopup ) { ImplCreatePopup( TRUE ); mbAutoPopup = TRUE; } bDone = TRUE; } else if ( nCode == KEY_ESCAPE || ( nCode == KEY_F6 && rKEvent.GetKeyCode().IsMod1() ) ) { if( pActivePopup ) { // bring focus to menu bar without any open popup mbAutoPopup = FALSE; USHORT n = nHighlightedItem; nHighlightedItem = ITEMPOS_INVALID; bStayActive = TRUE; ChangeHighlightItem( n, FALSE ); bStayActive = FALSE; KillActivePopup(); GrabFocus(); } else ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); if( nCode == KEY_F6 && rKEvent.GetKeyCode().IsMod1() ) { // put focus into document GrabFocusToDocument(); } bDone = TRUE; } } if ( !bDone && ( bFromMenu || rKEvent.GetKeyCode().IsMod2() ) ) { xub_Unicode nCharCode = rKEvent.GetCharCode(); if ( nCharCode ) { USHORT nEntry, nDuplicates; MenuItemData* pData = pMenu->GetItemList()->SearchItem( nCharCode, rKEvent.GetKeyCode(), nEntry, nDuplicates, nHighlightedItem ); if ( pData && (nEntry != ITEMPOS_INVALID) ) { mbAutoPopup = TRUE; ChangeHighlightItem( nEntry, TRUE ); bDone = TRUE; } else { // Wegen Systemmenu und anderen System-HotKeys, nur // eigenstaendige Character-Kombinationen auswerten USHORT 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; Region aCtrlRegion( Rectangle( aPt, GetOutputSizePixel() ) ); ImplControlValue aControlValue; MenubarValue aMenubarValue; aMenubarValue.maTopDockingAreaHeight = ImplGetTopDockingAreaHeight( this ); aControlValue.setOptionalVal( (void *)(&aMenubarValue) ); DrawNativeControl( CTRL_MENUBAR, PART_ENTIRE_CONTROL, aCtrlRegion, CTRL_STATE_ENABLED, aControlValue, rtl::OUString() ); ImplAddNWFSeparator( this, aMenubarValue ); } SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() ); pMenu->ImplPaint( this, 0 ); if ( nHighlightedItem != ITEMPOS_INVALID ) HighlightItem( nHighlightedItem, 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(); } USHORT MenuBarWindow::ImplFindEntry( const Point& rMousePos ) const { if( pMenu ) { long nX = 0; USHORT nCount = (USHORT)pMenu->pItemList->Count(); for ( USHORT n = 0; n < nCount; n++ ) { MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); if ( pMenu->ImplIsVisible( n ) ) { nX += pData->aSz.Width(); if ( nX > rMousePos.X() ) return (USHORT)n; } } } return ITEMPOS_INVALID; } void MenuBarWindow::RequestHelp( const HelpEvent& rHEvt ) { USHORT nId = nHighlightedItem; if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) ) ChangeHighlightItem( ITEMPOS_INVALID, 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, FALSE, TRUE ); Invalidate(); } else if( pMenu ) pMenu->ImplKillLayoutData(); } void MenuBarWindow::ImplLayoutChanged() { if( pMenu ) { ImplInitMenuWindow( this, TRUE, 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( TRUE ) ) ChangeHighlightItem( ITEMPOS_INVALID, FALSE, FALSE ); } void MenuBarWindow::GetFocus() { if ( nHighlightedItem == ITEMPOS_INVALID ) { mbAutoPopup = FALSE; // do not open menu when activated by focus handling like taskpane cycling ChangeHighlightItem( 0, 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; } USHORT MenuBarWindow::AddMenuBarButton( const Image& i_rImage, const Link& i_rLink, const String& i_rToolTip, USHORT i_nPos ) { // find first free button id USHORT nId = IID_DOCUMENTCLOSE; std::map< USHORT, AddButtonEntry >::const_iterator it; if( i_nPos > m_aAddButtons.size() ) i_nPos = static_cast(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( USHORT nId, const Link& rLink ) { std::map< USHORT, AddButtonEntry >::iterator it = m_aAddButtons.find( nId ); if( it != m_aAddButtons.end() ) it->second.m_aHighlightLink = rLink; } Rectangle MenuBarWindow::GetMenuBarButtonRectPixel( USHORT 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( USHORT nId ) { USHORT nPos = aCloser.GetItemPos( nId ); aCloser.RemoveItem( nPos ); m_aAddButtons.erase( nId ); aCloser.calcMinSize(); ImplLayoutChanged(); if( pMenu->mpSalMenu ) pMenu->mpSalMenu->RemoveMenuBarButton( nId ); } bool MenuBarWindow::HandleMenuButtonEvent( USHORT i_nButtonId ) { std::map< USHORT, 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(pMenu); return it->second.m_aSelectLink.Call( &aArg ); } return FALSE; }