diff options
author | Jan Holesovsky <kendy@collabora.com> | 2014-09-07 12:14:30 +0200 |
---|---|---|
committer | Jan Holesovsky <kendy@collabora.com> | 2014-09-09 06:58:27 +0200 |
commit | 355267ba5885ad0a26ae1caa24460b89fff45b12 (patch) | |
tree | 8f4f105247f0c78460dd02e065592c5d7fdabd49 /vcl | |
parent | 047d2f953ada34ada593768766d3ea8a725f9442 (diff) |
vcl: Move MenuFloatingWindow to an own file + adapt code.
The needed changes included:
* Move MenuItemList and MenuItemData to an own file
* Introduce MenuWindow class for pieces shared between MenuFloatingWindow and
MenuBarWindow.
Change-Id: I129b6ddba6efb8f0cb60018a2d8abafed42552c8
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/Library_vcl.mk | 3 | ||||
-rw-r--r-- | vcl/source/window/menu.cxx | 1902 | ||||
-rw-r--r-- | vcl/source/window/menufloatingwindow.cxx | 1209 | ||||
-rw-r--r-- | vcl/source/window/menufloatingwindow.hxx | 121 | ||||
-rw-r--r-- | vcl/source/window/menuitemlist.cxx | 280 | ||||
-rw-r--r-- | vcl/source/window/menuitemlist.hxx | 152 | ||||
-rw-r--r-- | vcl/source/window/menuwindow.cxx | 161 | ||||
-rw-r--r-- | vcl/source/window/menuwindow.hxx | 49 |
8 files changed, 2049 insertions, 1828 deletions
diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 227372d9c7f6..ec50fddb2c57 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -138,6 +138,9 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/window/keyevent \ vcl/source/window/layout \ vcl/source/window/menu \ + vcl/source/window/menufloatingwindow \ + vcl/source/window/menuitemlist \ + vcl/source/window/menuwindow \ vcl/source/window/mnemonic \ vcl/source/window/mnemonicengine \ vcl/source/window/mouse \ diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx index a36a37b37ba8..096738034b60 100644 --- a/vcl/source/window/menu.cxx +++ b/vcl/source/window/menu.cxx @@ -17,30 +17,30 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#include "tools/debug.hxx" -#include "tools/diagnose_ex.h" -#include "tools/rc.h" -#include "tools/stream.hxx" - -#include "vcl/svapp.hxx" -#include "vcl/mnemonic.hxx" -#include "vcl/image.hxx" -#include "vcl/event.hxx" -#include "vcl/help.hxx" -#include "vcl/floatwin.hxx" -#include "vcl/wrkwin.hxx" -#include "vcl/timer.hxx" -#include "vcl/decoview.hxx" -#include "vcl/bitmap.hxx" -#include "vcl/menu.hxx" -#include "vcl/button.hxx" -#include "vcl/gradient.hxx" -#include "vcl/i18nhelp.hxx" -#include "vcl/taskpanelist.hxx" -#include "vcl/controllayout.hxx" -#include "vcl/toolbox.hxx" -#include "vcl/dockingarea.hxx" -#include "vcl/settings.hxx" +#include <tools/debug.hxx> +#include <tools/diagnose_ex.h> +#include <tools/rc.h> +#include <tools/stream.hxx> + +#include <vcl/svapp.hxx> +#include <vcl/mnemonic.hxx> +#include <vcl/image.hxx> +#include <vcl/event.hxx> +#include <vcl/help.hxx> +#include <vcl/floatwin.hxx> +#include <vcl/wrkwin.hxx> +#include <vcl/timer.hxx> +#include <vcl/decoview.hxx> +#include <vcl/bitmap.hxx> +#include <vcl/menu.hxx> +#include <vcl/button.hxx> +#include <vcl/gradient.hxx> +#include <vcl/i18nhelp.hxx> +#include <vcl/taskpanelist.hxx> +#include <vcl/controllayout.hxx> +#include <vcl/toolbox.hxx> +#include <vcl/dockingarea.hxx> +#include <vcl/settings.hxx> #include "salinst.hxx" #include "svdata.hxx" @@ -48,9 +48,10 @@ #include "window.h" #include "salmenu.hxx" #include "salframe.hxx" +#include "menufloatingwindow.hxx" +#include "menuitemlist.hxx" #include <com/sun/star/uno/Reference.h> -#include <com/sun/star/i18n/XCharacterClassification.hpp> #include <com/sun/star/lang/XComponent.hpp> #include <com/sun/star/accessibility/XAccessible.hpp> #include <com/sun/star/accessibility/AccessibleRole.hpp> @@ -79,11 +80,7 @@ struct MenuLayoutData : public ControlLayoutData using namespace ::com::sun::star; using namespace vcl; -#define ITEMPOS_INVALID 0xFFFF - -#define EXTRASPACEY 2 #define EXTRAITEMHEIGHT 4 -#define GUTTERBORDER 8 // document closer #define IID_DOCUMENTCLOSE 1 @@ -103,463 +100,6 @@ static bool ImplAccelDisabled() return nAccelDisabled == 1; } -struct MenuItemData -{ - sal_uInt16 nId; // SV Id - MenuItemType eType; // MenuItem-Type - MenuItemBits nBits; // MenuItem-Bits - Menu* pSubMenu; // Pointer to SubMenu - Menu* pAutoSubMenu; // Pointer to SubMenu from Resource - OUString aText; // Menu-Text - OUString aHelpText; // Help-String - OUString aTipHelpText; // TipHelp-String (eg, expanded filenames) - OUString aCommandStr; // CommandString - OUString aHelpCommandStr; // Help command string (to reference external help) - OString sIdent; - OString aHelpId; // Help-Id - sal_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; // only temporarily valid - OUString aAccessibleName; // accessible name - OUString aAccessibleDescription; // accessible description - - SalMenuItem* pSalMenuItem; // access to native menu - - MenuItemData() - : nId(0) - , eType(MENUITEM_DONTKNOW) - , nBits(0) - , pSubMenu(NULL) - , pAutoSubMenu(NULL) - , nUserValue(0) - , bChecked(false) - , bEnabled(false) - , bVisible(false) - , bIsTemporary(false) - , bMirrorMode(false) - , nItemImageAngle(0) - , pSalMenuItem(NULL) - { - } - MenuItemData( const OUString& rStr, const Image& rImage ) - : nId(0) - , eType(MENUITEM_DONTKNOW) - , nBits(0) - , pSubMenu(NULL) - , pAutoSubMenu(NULL) - , aText(rStr) - , nUserValue(0) - , aImage(rImage) - , bChecked(false) - , bEnabled(false) - , bVisible(false) - , bIsTemporary(false) - , bMirrorMode(false) - , nItemImageAngle(0) - , pSalMenuItem(NULL) - { - } - ~MenuItemData(); - bool HasCheck() const - { - 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 -{ -private: - typedef ::std::vector< MenuItemData* > MenuItemDataList_impl; - MenuItemDataList_impl maItemList; - - uno::Reference< i18n::XCharacterClassification > xCharClass; - -public: - MenuItemList() {} - ~MenuItemList(); - - MenuItemData* Insert( - sal_uInt16 nId, - MenuItemType eType, - MenuItemBits nBits, - const OUString& rStr, - const Image& rImage, - Menu* pMenu, - size_t nPos, - const OString &rIdent - ); - void InsertSeparator(const OString &rIdent, size_t nPos); - void Remove( size_t nPos ); - - MenuItemData* GetData( sal_uInt16 nSVId, size_t& rPos ) const; - MenuItemData* GetData( sal_uInt16 nSVId ) const - { - size_t nTemp; - return GetData( nSVId, nTemp ); - } - MenuItemData* GetDataFromPos( size_t nPos ) const - { - return ( nPos < maItemList.size() ) ? maItemList[ nPos ] : NULL; - } - - MenuItemData* SearchItem( - sal_Unicode cSelectChar, - KeyCode aKeyCode, - sal_uInt16& rPos, - sal_uInt16& nDuplicates, - sal_uInt16 nCurrentPos - ) const; - size_t GetItemCount( sal_Unicode cSelectChar ) const; - size_t GetItemCount( KeyCode aKeyCode ) const; - size_t size() - { - return maItemList.size(); - } -}; - -MenuItemList::~MenuItemList() -{ - for( size_t i = 0, n = maItemList.size(); i < n; ++i ) - delete maItemList[ i ]; -} - -MenuItemData* MenuItemList::Insert( - sal_uInt16 nId, - MenuItemType eType, - MenuItemBits nBits, - const OUString& rStr, - const Image& rImage, - Menu* pMenu, - size_t nPos, - const OString &rIdent -) -{ - MenuItemData* pData = new MenuItemData( rStr, rImage ); - pData->nId = nId; - pData->sIdent = rIdent; - pData->eType = eType; - pData->nBits = nBits; - pData->pSubMenu = NULL; - pData->pAutoSubMenu = NULL; - 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 ); - - if( nPos < maItemList.size() ) { - maItemList.insert( maItemList.begin() + nPos, pData ); - } else { - maItemList.push_back( pData ); - } - return pData; -} - -void MenuItemList::InsertSeparator(const OString &rIdent, size_t nPos) -{ - MenuItemData* pData = new MenuItemData; - pData->nId = 0; - pData->sIdent = rIdent; - pData->eType = MENUITEM_SEPARATOR; - pData->nBits = 0; - pData->pSubMenu = NULL; - pData->pAutoSubMenu = NULL; - 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 = OUString(); - aSalMIData.aImage = Image(); - - // Native-support: returns NULL if not supported - pData->pSalMenuItem = ImplGetSVData()->mpDefInst->CreateMenuItem( &aSalMIData ); - - if( nPos < maItemList.size() ) { - maItemList.insert( maItemList.begin() + nPos, pData ); - } else { - maItemList.push_back( pData ); - } -} - -void MenuItemList::Remove( size_t nPos ) -{ - if( nPos < maItemList.size() ) - { - delete maItemList[ nPos ]; - maItemList.erase( maItemList.begin() + nPos ); - } -} - -MenuItemData* MenuItemList::GetData( sal_uInt16 nSVId, size_t& rPos ) const -{ - for( size_t i = 0, n = maItemList.size(); i < n; ++i ) - { - if ( maItemList[ i ]->nId == nSVId ) - { - rPos = i; - return maItemList[ i ]; - } - } - return NULL; -} - -MenuItemData* MenuItemList::SearchItem( - sal_Unicode cSelectChar, - KeyCode aKeyCode, - sal_uInt16& rPos, - sal_uInt16& nDuplicates, - sal_uInt16 nCurrentPos -) const -{ - const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); - - size_t nListCount = maItemList.size(); - - // try character code first - nDuplicates = GetItemCount( cSelectChar ); // return number of duplicates - if( nDuplicates ) - { - for ( rPos = 0; rPos < nListCount; rPos++) - { - MenuItemData* pData = maItemList[ rPos ]; - if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) ) - { - if( nDuplicates > 1 && rPos == nCurrentPos ) - continue; // select next entry with the same mnemonic - else - return pData; - } - } - } - - // nothing found, try keycode instead - nDuplicates = GetItemCount( aKeyCode ); // return number of duplicates - - if( nDuplicates ) - { - char ascii = 0; - if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z ) - ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A)); - - for ( rPos = 0; rPos < nListCount; rPos++) - { - MenuItemData* pData = maItemList[ rPos ]; - if ( pData->bEnabled ) - { - sal_Int32 n = pData->aText.indexOf('~'); - if ( n != -1 ) - { - KeyCode mnKeyCode; - sal_Unicode mnUnicode = pData->aText[n+1]; - Window* pDefWindow = ImplGetDefaultWindow(); - if( ( pDefWindow - && pDefWindow->ImplGetFrame()->MapUnicodeToKeyCode( mnUnicode, - Application::GetSettings().GetUILanguageTag().getLanguageType(), 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; -} - -size_t MenuItemList::GetItemCount( sal_Unicode cSelectChar ) const -{ - // returns number of entries with same mnemonic - const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); - - size_t nItems = 0; - for ( size_t nPos = maItemList.size(); nPos; ) - { - MenuItemData* pData = maItemList[ --nPos ]; - if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) ) - nItems++; - } - - return nItems; -} - -size_t MenuItemList::GetItemCount( KeyCode aKeyCode ) const -{ - // returns number of entries with same mnemonic - // uses key codes instead of character codes - const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); - char ascii = 0; - if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z ) - ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A)); - - size_t nItems = 0; - for ( size_t nPos = maItemList.size(); nPos; ) - { - MenuItemData* pData = maItemList[ --nPos ]; - if ( pData->bEnabled ) - { - sal_Int32 n = pData->aText.indexOf('~'); - if (n != -1) - { - 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[n+1], - Application::GetSettings().GetUILanguageTag().getLanguageType(), mnKeyCode ) - && aKeyCode.GetCode() == mnKeyCode.GetCode() - ) - || ( ascii - && rI18nHelper.MatchMnemonic( pData->aText, ascii ) - ) - ) - nItems++; - } - } - } - - return nItems; -} - -// - MenuFloatingWindow - - -class MenuFloatingWindow : public FloatingWindow -{ - friend void Menu::ImplFillLayoutData() const; - friend Menu::~Menu(); - -private: - Menu* pMenu; - PopupMenu* pActivePopup; - Timer aHighlightChangedTimer; - Timer aSubmenuCloseTimer; - Timer aScrollTimer; - sal_uLong nSaveFocusId; - sal_uInt16 nHighlightedItem; // highlighted/selected Item - sal_uInt16 nMBDownPos; - sal_uInt16 nScrollerHeight; - sal_uInt16 nFirstEntry; - sal_uInt16 nBorder; - sal_uInt16 nPosInParent; - bool bInExecute; - - bool bScrollMenu; - bool bScrollUp; - bool bScrollDown; - bool bIgnoreFirstMove; - bool bKeyInput; - - DECL_LINK(PopupEnd, void *); - DECL_LINK( HighlightChanged, Timer* ); - DECL_LINK(SubmenuClose, void *); - DECL_LINK(AutoScroll, void *); - DECL_LINK( ShowHideListener, VclWindowEvent* ); - - virtual void StateChanged( StateChangedType nType ) SAL_OVERRIDE; - virtual void DataChanged( const DataChangedEvent& rDCEvt ) SAL_OVERRIDE; - - void InitMenuClipRegion(); - -protected: - Region ImplCalcClipRegion( bool bIncludeLogo = true ) const; - 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( sal_uInt16 nPos ); - -public: - MenuFloatingWindow( Menu* pMenu, Window* pParent, WinBits nStyle ); - virtual ~MenuFloatingWindow(); - - void doShutdown(); - - virtual void MouseMove( const MouseEvent& rMEvt ) SAL_OVERRIDE; - virtual void MouseButtonDown( const MouseEvent& rMEvt ) SAL_OVERRIDE; - virtual void MouseButtonUp( const MouseEvent& rMEvt ) SAL_OVERRIDE; - virtual void KeyInput( const KeyEvent& rKEvent ) SAL_OVERRIDE; - virtual void Command( const CommandEvent& rCEvt ) SAL_OVERRIDE; - virtual void Paint( const Rectangle& rRect ) SAL_OVERRIDE; - virtual void RequestHelp( const HelpEvent& rHEvt ) SAL_OVERRIDE; - virtual void Resize() SAL_OVERRIDE; - - void SetFocusId( sal_uLong nId ) { nSaveFocusId = nId; } - sal_uLong GetFocusId() const { return nSaveFocusId; } - - void EnableScrollMenu( bool b ); - bool IsScrollMenu() const { return bScrollMenu; } - sal_uInt16 GetScrollerHeight() const { return nScrollerHeight; } - - void Execute(); - void StopExecute( sal_uLong nFocusId = 0 ); - void EndExecute(); - void EndExecute( sal_uInt16 nSelectId ); - - PopupMenu* GetActivePopup() const { return pActivePopup; } - void KillActivePopup( PopupMenu* pThisOnly = NULL ); - - void HighlightItem( sal_uInt16 nPos, bool bHighlight ); - void ChangeHighlightItem( sal_uInt16 n, bool bStartPopupTimer ); - sal_uInt16 GetHighlightedItem() const { return nHighlightedItem; } - - void SetPosInParent( sal_uInt16 nPos ) { nPosInParent = nPos; } - - virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > CreateAccessible() SAL_OVERRIDE; -}; - // To get the transparent mouse-over look, the closer is actually a toolbox // overload DataChange to handle style changes correctly class DecoToolBox : public ToolBox @@ -670,7 +210,7 @@ void DecoToolBox::SetImages( long nMaxHeight, bool bForce ) // In most functions we would have to create exceptions for // menubar, popupmenu, hence we made two classes -class MenuBarWindow : public Window +class MenuBarWindow : public MenuWindow, public Window { friend class MenuBar; friend class Menu; @@ -783,87 +323,6 @@ static void ImplSetMenuItemData( MenuItemData* pData ) pData->eType = MENUITEM_STRINGIMAGE; } -static sal_uLong ImplChangeTipTimeout( sal_uLong nTimeout, Window *pWindow ) -{ - AllSettings aAllSettings( pWindow->GetSettings() ); - HelpSettings aHelpSettings( aAllSettings.GetHelpSettings() ); - sal_uLong nRet = aHelpSettings.GetTipTimeout(); - aHelpSettings.SetTipTimeout( nTimeout ); - aAllSettings.SetHelpSettings( aHelpSettings ); - pWindow->SetSettings( aAllSettings ); - return nRet; -} - -static bool ImplHandleHelpEvent( Window* pMenuWindow, Menu* pMenu, sal_uInt16 nHighlightedItem, const HelpEvent& rHEvt, const Rectangle &rHighlightRect ) -{ - if( ! pMenu ) - return false; - - bool bDone = false; - sal_uInt16 nId = 0; - - if ( nHighlightedItem != ITEMPOS_INVALID ) - { - MenuItemData* pItemData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); - if ( pItemData ) - nId = pItemData->nId; - } - - if ( ( rHEvt.GetMode() & HELPMODE_BALLOON ) && pMenuWindow ) - { - Point aPos; - if( rHEvt.KeyboardActivated() ) - aPos = rHighlightRect.Center(); - else - aPos = rHEvt.GetMousePosPixel(); - - Rectangle aRect( aPos, Size() ); - if (!pMenu->GetHelpText(nId).isEmpty()) - Help::ShowBalloon( pMenuWindow, aPos, pMenu->GetHelpText( nId ) ); - else - { - // give user a chance to read the full filename - sal_uLong oldTimeout=ImplChangeTipTimeout( 60000, pMenuWindow ); - // call always, even when strlen==0 to correctly remove tip - Help::ShowQuickHelp( pMenuWindow, aRect, pMenu->GetTipHelpText( nId ) ); - ImplChangeTipTimeout( oldTimeout, pMenuWindow ); - } - bDone = true; - } - else if ( ( rHEvt.GetMode() & HELPMODE_QUICK ) && pMenuWindow ) - { - Point aPos = rHEvt.GetMousePosPixel(); - Rectangle aRect( aPos, Size() ); - // give user a chance to read the full filename - sal_uLong oldTimeout=ImplChangeTipTimeout( 60000, pMenuWindow ); - // call always, even when strlen==0 to correctly remove tip - Help::ShowQuickHelp( pMenuWindow, aRect, pMenu->GetTipHelpText( nId ) ); - ImplChangeTipTimeout( oldTimeout, pMenuWindow ); - bDone = true; - } - else if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) ) - { - // is help in the application selected - Help* pHelp = Application::GetHelp(); - if ( pHelp ) - { - // is an id available, then call help with the id, otherwise - // use help-index - OUString aCommand = pMenu->GetItemCommand( nId ); - OString aHelpId( pMenu->GetHelpId( nId ) ); - if( aHelpId.isEmpty() ) - aHelpId = OOO_HELP_INDEX; - - if ( !aCommand.isEmpty() ) - pHelp->Start( aCommand, NULL ); - else - pHelp->Start( OStringToOUString( aHelpId, RTL_TEXTENCODING_UTF8 ), NULL ); - } - bDone = true; - } - return bDone; -} - static int ImplGetTopDockingAreaHeight( Window *pWindow ) { // find docking area that is top aligned and return its height @@ -3070,6 +2529,53 @@ bool Menu::HasValidEntries( bool bCheckPopups ) return bValidEntries; } +void Menu::CloseStartedFrom() +{ + // if the window was closed by TH, there is another menu + // which has this window as pActivePopup + if (!pStartedFrom) + return; + + // pWin from parent could be 0, if the list is + // cleaned from the start, now clean up the endpopup-events + if (pStartedFrom->bIsMenuBar) + { + MenuBarWindow* p = (MenuBarWindow*) pStartedFrom->ImplGetWindow(); + if (p) + p->PopupClosed(this); + } + else + { + MenuFloatingWindow* p = (MenuFloatingWindow*) pStartedFrom->ImplGetWindow(); + PopupMenu *pMenu = dynamic_cast<PopupMenu*>(this); + if (p && pMenu) + p->KillActivePopup(pMenu); + } +} + +sal_uLong Menu::DeactivateMenuBar(sal_uLong nFocusId) +{ + if (!bIsMenuBar) + return nFocusId; + + nFocusId = ((MenuBarWindow*)(dynamic_cast<MenuBar*>(this))->ImplGetWindow())->GetFocusId(); + if (nFocusId) + { + ((MenuBarWindow*)(dynamic_cast<MenuBar*>(this))->ImplGetWindow())->SetFocusId(0); + ImplGetSVData()->maWinData.mbNoDeactivate = false; + } + + return nFocusId; +} + +void Menu::MenuBarKeyInput(const KeyEvent& rEvent) +{ + if (!bIsMenuBar) + return; + + ((MenuBarWindow*)(dynamic_cast<MenuBar*>(this))->ImplGetWindow())->KeyInput(rEvent); +} + void Menu::ImplKillLayoutData() const { delete mpLayoutData, mpLayoutData = NULL; @@ -3861,1266 +3367,6 @@ long PopupMenu::ImplCalcHeight( sal_uInt16 nEntries ) const 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 ) - { - const BitmapEx& rPersonaBitmap = Application::GetSettings().GetStyleSettings().GetPersonaHeader(); - if ( !rPersonaBitmap.IsEmpty() ) - { - Wallpaper aWallpaper( rPersonaBitmap ); - aWallpaper.SetStyle( WALLPAPER_TOPRIGHT ); - aWallpaper.SetColor( Application::GetSettings().GetStyleSettings().GetWorkspaceColor() ); - - pWin->SetBackground( aWallpaper ); - pWin->SetPaintTransparent( false ); - pWin->SetParentClipMode( 0 ); - } - else 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; - 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 ); - pMenu->SetHighlightItem(ITEMPOS_INVALID); - if( !bKeyInput && pMenu && pMenu->pStartedFrom && !pMenu->pStartedFrom->bIsMenuBar ) - { - // #102461# remove highlight in parent - MenuItemData* pData; - size_t i, nCount = pMenu->pStartedFrom->pItemList->size(); - 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() -{ - InitMenuClipRegion(); -} - -long MenuFloatingWindow::ImplGetStartY() const -{ - long nY = 0; - if( pMenu ) - { - for ( sal_uInt16 n = 0; n < nFirstEntry; n++ ) - nY += pMenu->GetItemList()->GetDataFromPos( n )->aSz.Height(); - } - return -nY; -} - -Region MenuFloatingWindow::ImplCalcClipRegion( 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::InitMenuClipRegion() -{ - if ( IsScrollMenu() ) - { - SetClipRegion( ImplCalcClipRegion() ); - } - else - { - SetClipRegion(); - } -} - -void MenuFloatingWindow::ImplHighlightItem( const MouseEvent& rMEvt, bool bMBDown ) -{ - if( ! pMenu ) - return; - - long nY = nScrollerHeight + ImplGetSVData()->maNWFData.mnMenuFormatBorderY; - long nMouseY = rMEvt.GetPosPixel().Y(); - Size aOutSz = GetOutputSizePixel(); - if ( ( nMouseY >= nY ) && ( nMouseY < ( aOutSz.Height() - nY ) ) ) - { - bool bHighlighted = false; - size_t nCount = pMenu->pItemList->size(); - nY += ImplGetStartY(); // ggf. gescrollt. - for ( size_t 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 ) - { - // only when clicked over the arrow... - Size aSz = GetOutputSizePixel(); - long nFontHeight = GetTextHeight(); - bPopupArea = ( rMEvt.GetPosPixel().X() >= ( aSz.Width() - nFontHeight - nFontHeight/4 ) ); - } - - if ( bMBDown ) - { - if ( n != nHighlightedItem ) - { - ChangeHighlightItem( (sal_uInt16)n, false ); - } - - bool bAllowNewPopup = true; - if ( pActivePopup ) - { - MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); - bAllowNewPopup = pData && ( pData->pSubMenu != pActivePopup ); - if ( bAllowNewPopup ) - KillActivePopup(); - } - - if ( bPopupArea && bAllowNewPopup ) - { - HighlightChanged( NULL ); - } - } - else - { - if ( n != nHighlightedItem ) - { - ChangeHighlightItem( (sal_uInt16)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_NOARG(MenuFloatingWindow, PopupEnd) -{ - // "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 ) - { - // if the window was closed by TH, there is another menu - // which has this window as pActivePopup - if ( pMenu->pStartedFrom ) - { - // pWin from parent could be 0, if the list is - // cleaned from the start, now clean up the endpopup-events - 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_NOARG(MenuFloatingWindow, AutoScroll) -{ - ImplScroll( GetPointerPosPixel() ); - return 1; -} - -IMPL_LINK( MenuFloatingWindow, HighlightChanged, Timer*, pTimer ) -{ - if( ! pMenu ) - return 0; - - MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( nHighlightedItem ); - if ( pItemData ) - { - if ( pActivePopup && ( pActivePopup != pItemData->pSubMenu ) ) - { - sal_uLong nOldFlags = GetPopupModeFlags(); - SetPopupModeFlags( GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE ); - KillActivePopup(); - SetPopupModeFlags( nOldFlags ); - } - if ( pItemData->bEnabled && pItemData->pSubMenu && pItemData->pSubMenu->GetItemCount() && ( pItemData->pSubMenu != pActivePopup ) ) - { - pActivePopup = (PopupMenu*)pItemData->pSubMenu; - long nY = nScrollerHeight+ImplGetStartY(); - MenuItemData* pData = 0; - for ( sal_uLong n = 0; n < nHighlightedItem; n++ ) - { - pData = pMenu->pItemList->GetDataFromPos( n ); - nY += pData->aSz.Height(); - } - pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem ); - Size MySize = GetOutputSizePixel(); - Point aItemTopLeft( 0, nY ); - Point aItemBottomRight( aItemTopLeft ); - aItemBottomRight.X() += MySize.Width(); - aItemBottomRight.Y() += pData->aSz.Height(); - - // shift the popups a little - 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: crash due to Reschedule() in call of Activate() - // Also it is prevented that submenus are displayed which - // were for long in Activate Rescheduled and which should not be - // displayed now. - Menu* pTest = pActivePopup; - sal_uLong nOldFlags = GetPopupModeFlags(); - SetPopupModeFlags( GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE ); - sal_uInt16 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_NOARG(MenuFloatingWindow, SubmenuClose) -{ - 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 ? (sal_uInt16) GetSettings().GetStyleSettings().GetScrollBarSize() /2 : 0; - bScrollDown = true; - InitMenuClipRegion(); -} - -void MenuFloatingWindow::Execute() -{ - ImplSVData* pSVData = ImplGetSVData(); - - pSVData->maAppData.mpActivePopupMenu = (PopupMenu*)pMenu; - - bInExecute = true; -// bCallingSelect = false; - - while ( bInExecute ) - Application::Yield(); - - pSVData->maAppData.mpActivePopupMenu = NULL; -} - -void MenuFloatingWindow::StopExecute( sal_uLong nFocusId ) -{ - // restore focus - // (could have been restored in Select) - 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; - - // For all actions pActivePopup = 0, if e.g. - // PopupModeEndHdl the popups to destroy were called synchronous - 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; - sal_uLong nFocusId = 0; - if ( pStart && pStart->bIsMenuBar ) - { - nFocusId = ((MenuBarWindow*)((MenuBar*)pStart)->ImplGetWindow())->GetFocusId(); - if ( nFocusId ) - { - ((MenuBarWindow*)((MenuBar*)pStart)->ImplGetWindow())->SetFocusId( 0 ); - ImplGetSVData()->maWinData.mbNoDeactivate = false; - } - } - - // if started else where, cleanup there as well - 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; - - // this window will be destroyed => store date locally... - Menu* pM = pMenu; - sal_uInt16 nItem = nHighlightedItem; - - pCleanUpFrom->StopExecute( nFocusId ); - - if ( nItem != ITEMPOS_INVALID && pM ) - { - MenuItemData* pItemData = pM->GetItemList()->GetDataFromPos( nItem ); - if ( pItemData && !pItemData->bIsTemporary ) - { - pM->nSelectedId = pItemData->nId; - if ( pStart ) - pStart->nSelectedId = pItemData->nId; - - pM->ImplSelect(); - } - } -} - -void MenuFloatingWindow::EndExecute( sal_uInt16 nId ) -{ - size_t nPos; - if ( pMenu && pMenu->GetItemList()->GetData( nId, nPos ) ) - nHighlightedItem = nPos; - else - nHighlightedItem = ITEMPOS_INVALID; - - EndExecute(); -} - -void MenuFloatingWindow::MouseButtonDown( const MouseEvent& rMEvt ) -{ - // TH creates a ToTop on this window, but the active popup - // should stay on top... - // due to focus change this would close all menus -> don't do it (#94123) - //if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup ) - // pActivePopup->ImplGetFloatingWindow()->ToTop( TOTOP_NOGRABFOCUS ); - - ImplHighlightItem( rMEvt, true ); - - nMBDownPos = nHighlightedItem; -} - -void MenuFloatingWindow::MouseButtonUp( const MouseEvent& rMEvt ) -{ - MenuItemData* pData = pMenu ? pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ) : NULL; - // nMBDownPos store in local variable and reset immediately, - // as it will be too late after EndExecute - sal_uInt16 _nMBDownPos = nMBDownPos; - nMBDownPos = ITEMPOS_INVALID; - if ( pData && pData->bEnabled && ( pData->eType != MENUITEM_SEPARATOR ) ) - { - if ( !pData->pSubMenu ) - { - EndExecute(); - } - else if ( ( pData->nBits & MIB_POPUPSELECT ) && ( nHighlightedItem == _nMBDownPos ) && ( rMEvt.GetClicks() == 2 ) ) - { - // not when clicked over the arrow... - 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() ) - { - // #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 ); - - if ( IsScrollMenu() ) - ImplScroll( rMEvt.GetPosPixel() ); - } - else - { - 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(); - - 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(); - sal_uInt16 nLastVisible; - ((PopupMenu*)pMenu)->ImplCalcVisEntries( nHeight, nFirstEntry, &nLastVisible ); - if ( pMenu->ImplGetNextVisible( nLastVisible ) == ITEMPOS_INVALID ) - { - bScrollDown = false; - ImplDrawScroller( false ); - } - - 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(); // if scrolled through MouseMove. - long nTimeout; - if ( nDelta < 3 ) - nTimeout = 200; - else if ( nDelta < 5 ) - nTimeout = 100; - else if ( nDelta < 8 ) - nTimeout = 70; - else if ( nDelta < 12 ) - nTimeout = 40; - else - nTimeout = 20; - aScrollTimer.SetTimeout( nTimeout ); - aScrollTimer.Start(); - } -} -void MenuFloatingWindow::ChangeHighlightItem( sal_uInt16 n, bool bStartPopupTimer ) -{ - // #57934# ggf. immediately close the active, as TH's backgroundstorage works. - // #65750# we prefer to refrain from the background storage of small lines. - // otherwise the menus are difficult to operate. - // MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n ); - // if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) ) - // KillActivePopup(); - - aSubmenuCloseTimer.Stop(); - if( ! pMenu ) - return; - - if ( nHighlightedItem != ITEMPOS_INVALID ) - { - HighlightItem( nHighlightedItem, false ); - pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, nHighlightedItem ); - } - - nHighlightedItem = (sal_uInt16)n; - DBG_ASSERT( pMenu->ImplIsVisible( nHighlightedItem ) || nHighlightedItem == ITEMPOS_INVALID, "ChangeHighlightItem: Not visible!" ); - if( nHighlightedItem != ITEMPOS_INVALID ) - { - if( pMenu->pStartedFrom && !pMenu->pStartedFrom->bIsMenuBar ) - { - // #102461# make sure parent entry is highlighted as well - MenuItemData* pData; - size_t i, nCount = pMenu->pStartedFrom->pItemList->size(); - 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->SetHighlightItem(nHighlightedItem); - pMenu->ImplCallHighlight( nHighlightedItem ); - } - else - pMenu->nSelectedId = 0; - - if ( bStartPopupTimer ) - { - // #102438# Menu items are not selectable - // If a menu item is selected by an AT-tool via the XAccessibleAction, XAccessibleValue - // or XAccessibleSelection interface, and the parent popup menus are not executed yet, - // the parent popup menus must be executed SYNCHRONOUSLY, before the menu item is selected. - if ( GetSettings().GetMouseSettings().GetMenuDelay() ) - aHighlightChangedTimer.Start(); - else - HighlightChanged( &aHighlightChangedTimer ); - } -} - -void MenuFloatingWindow::HighlightItem( sal_uInt16 nPos, bool bHighlight ) -{ - if( ! pMenu ) - return; - - Size aSz = GetOutputSizePixel(); - long nStartY = ImplGetStartY(); - long nY = nScrollerHeight + nStartY + ImplGetSVData()->maNWFData.mnMenuFormatBorderY; - long nX = 0; - - if ( pMenu->pLogo ) - nX = pMenu->pLogo->aBitmap.GetSizePixel().Width(); - - int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX; - - size_t nCount = pMenu->pItemList->size(); - for ( size_t n = 0; n < nCount; n++ ) - { - MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); - if ( n == nPos ) - { - 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+nOuterSpaceX, nY ), Size( aSz.Width()-2*nOuterSpaceX, pData->aSz.Height() ) ); - if ( pData->nBits & MIB_POPUPSELECT ) - { - long nFontHeight = GetTextHeight(); - aItemRect.Right() -= nFontHeight + nFontHeight/4; - } - - if( IsNativeControlSupported( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL ) ) - { - Size aPxSize( GetOutputSizePixel() ); - Push( PUSH_CLIPREGION ); - IntersectClipRegion( Rectangle( Point( nX, nY ), Size( aSz.Width(), pData->aSz.Height() ) ) ); - Rectangle aCtrlRect( Point( nX, 0 ), Size( aPxSize.Width()-nX, aPxSize.Height() ) ); - MenupopupValue aVal( pMenu->nTextPos-GUTTERBORDER, aItemRect ); - DrawNativeControl( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, - aCtrlRect, - CTRL_STATE_ENABLED, - aVal, - OUString() ); - if( bHighlight && - IsNativeControlSupported( CTRL_MENU_POPUP, PART_MENU_ITEM ) ) - { - bDrawItemRect = false; - if( !DrawNativeControl( CTRL_MENU_POPUP, PART_MENU_ITEM, - aItemRect, - CTRL_STATE_SELECTED | ( pData->bEnabled? CTRL_STATE_ENABLED: 0 ), - aVal, - OUString() ) ) - { - bDrawItemRect = bHighlight; - } - } - else - bDrawItemRect = bHighlight; - Pop(); - } - if( bDrawItemRect ) - { - if ( bHighlight ) - { - if( pData->bEnabled ) - SetFillColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() ); - else - { - SetFillColor(); - oldLineColor = GetLineColor(); - SetLineColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() ); - bRestoreLineColor = true; - } - } - else - SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() ); - - DrawRect( aItemRect ); - } - pMenu->ImplPaint( this, nScrollerHeight, nStartY, pData, bHighlight ); - if( bRestoreLineColor ) - SetLineColor( oldLineColor ); - } - return; - } - - nY += pData->aSz.Height(); - } -} - -Rectangle MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos ) -{ - if( ! pMenu ) - return Rectangle(); - - Rectangle aRect; - Size aSz = GetOutputSizePixel(); - long nStartY = ImplGetStartY(); - long nY = nScrollerHeight+nStartY; - long nX = 0; - - if ( pMenu->pLogo ) - nX = pMenu->pLogo->aBitmap.GetSizePixel().Width(); - - size_t nCount = pMenu->pItemList->size(); - for ( size_t 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(); - - sal_uInt16 n = nHighlightedItem; - if ( n == ITEMPOS_INVALID ) - { - if ( bUp ) - n = 0; - else - n = pMenu->GetItemCount()-1; - } - - sal_uInt16 nLoop = n; - - if( bHomeEnd ) - { - // absolute positioning - if( bUp ) - { - n = pMenu->GetItemCount(); - nLoop = n-1; - } - else - { - n = (sal_uInt16)-1; - nLoop = n+1; - } - } - - do - { - if ( bUp ) - { - if ( n ) - n--; - else - if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) ) - n = pMenu->GetItemCount()-1; - else - break; - } - else - { - n++; - if ( n >= pMenu->GetItemCount() ) - { - if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) ) - n = 0; - else - break; - } - } - - MenuItemData* pData = (MenuItemData*)pMenu->GetItemList()->GetDataFromPos( n ); - if ( ( pData->bEnabled || !rSettings.GetSkipDisabledInMenus() ) - && ( pData->eType != MENUITEM_SEPARATOR ) && pMenu->ImplIsVisible( n ) && pMenu->ImplIsSelectable( n ) ) - { - // Is selection in visible area? - if ( IsScrollMenu() ) - { - ChangeHighlightItem( ITEMPOS_INVALID, false ); - - while ( n < nFirstEntry ) - ImplScroll( true ); - - Size aOutSz = GetOutputSizePixel(); - sal_uInt16 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 ); - - sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode(); - bKeyInput = true; - switch ( nCode ) - { - case KEY_UP: - case KEY_DOWN: - { - ImplCursorUpDown( nCode == KEY_UP ); - } - break; - case KEY_END: - case KEY_HOME: - { - ImplCursorUpDown( nCode == KEY_END, true ); - } - break; - case KEY_F6: - case KEY_ESCAPE: - { - // Ctrl-F6 acts like ESC here, the menu bar however will then put the focus in the document - if( nCode == KEY_F6 && !rKEvent.GetKeyCode().IsMod1() ) - break; - if( pMenu ) - { - if ( !pMenu->pStartedFrom ) - { - StopExecute(); - KillActivePopup(); - } - else if ( pMenu->pStartedFrom->bIsMenuBar ) - { - // Forward... - ((MenuBarWindow*)((MenuBar*)pMenu->pStartedFrom)->ImplGetWindow())->KeyInput( rKEvent ); - } - else - { - StopExecute(); - PopupMenu* pPopupMenu = (PopupMenu*)pMenu->pStartedFrom; - MenuFloatingWindow* pFloat = pPopupMenu->ImplGetFloatingWindow(); - pFloat->GrabFocus(); - pFloat->KillActivePopup(); - pPopupMenu->ImplCallHighlight(pFloat->nHighlightedItem); - } - } - } - break; - case KEY_LEFT: - { - if ( pMenu && pMenu->pStartedFrom ) - { - StopExecute(); - if ( pMenu->pStartedFrom->bIsMenuBar ) - { - // Forward... - ((MenuBarWindow*)((MenuBar*)pMenu->pStartedFrom)->ImplGetWindow())->KeyInput( rKEvent ); - } - else - { - MenuFloatingWindow* pFloat = ((PopupMenu*)pMenu->pStartedFrom)->ImplGetFloatingWindow(); - pFloat->GrabFocus(); - pFloat->KillActivePopup(); - sal_uInt16 highlightItem = pFloat->GetHighlightedItem(); - pFloat->ChangeHighlightItem(highlightItem, false); - } - } - } - break; - case KEY_RIGHT: - { - if( pMenu ) - { - bool bDone = false; - if ( nHighlightedItem != ITEMPOS_INVALID ) - { - MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); - if ( pData && pData->pSubMenu ) - { - HighlightChanged( 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: - { - sal_Unicode nCharCode = rKEvent.GetCharCode(); - sal_uInt16 nPos = 0; - sal_uInt16 nDuplicates = 0; - MenuItemData* pData = (nCharCode && pMenu) ? pMenu->GetItemList()->SearchItem( nCharCode, rKEvent.GetKeyCode(), nPos, nDuplicates, nHighlightedItem ) : NULL; - if ( pData ) - { - if ( pData->pSubMenu || nDuplicates > 1 ) - { - ChangeHighlightItem( nPos, false ); - HighlightChanged( 0 ); - } - else - { - nHighlightedItem = nPos; - EndExecute(); - } - } - else - FloatingWindow::KeyInput( rKEvent ); - } - } - // #105474# check if menu window was not destroyed - if ( !aDelData.IsDead() ) - { - 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; - ImplControlValue aVal( pMenu->nTextPos-GUTTERBORDER ); - DrawNativeControl( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, - Rectangle( Point( nX, 0 ), aPxSize ), - CTRL_STATE_ENABLED, - aVal, - OUString() ); - InitMenuClipRegion(); - } - 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; - - sal_uInt16 nStyle = 0; - if ( ( bUp && !bScrollUp ) || ( !bUp && !bScrollDown ) ) - nStyle |= SYMBOL_DRAW_DISABLE; - - aDecoView.DrawSymbol( aRect, eSymbol, GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle ); - - InitMenuClipRegion(); -} - -void MenuFloatingWindow::RequestHelp( const HelpEvent& rHEvt ) -{ - sal_uInt16 nId = nHighlightedItem; - Menu* pM = pMenu; - Window* pW = this; - - // #102618# Get item rect before destroying the window in EndExecute() call - Rectangle aHighlightRect( ImplGetItemRect( nHighlightedItem ) ); - - if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) ) - { - nHighlightedItem = ITEMPOS_INVALID; - EndExecute(); - pW = NULL; - } - - if( !ImplHandleHelpEvent( pW, pM, nId, rHEvt, aHighlightRect ) ) - Window::RequestHelp( rHEvt ); -} - -void MenuFloatingWindow::StateChanged( StateChangedType nType ) -{ - FloatingWindow::StateChanged( nType ); - - if ( ( nType == STATE_CHANGE_CONTROLFOREGROUND ) || ( nType == STATE_CHANGE_CONTROLBACKGROUND ) ) - { - ImplInitMenuWindow( this, 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 ), diff --git a/vcl/source/window/menufloatingwindow.cxx b/vcl/source/window/menufloatingwindow.cxx new file mode 100644 index 000000000000..6359606f39a2 --- /dev/null +++ b/vcl/source/window/menufloatingwindow.cxx @@ -0,0 +1,1209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "menufloatingwindow.hxx" +#include "menuitemlist.hxx" + +#include <svdata.hxx> +#include <vcl/decoview.hxx> +#include <vcl/settings.hxx> +#include <window.h> + +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; + 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 ); + pMenu->SetHighlightItem(ITEMPOS_INVALID); + if( !bKeyInput && pMenu && pMenu->pStartedFrom && !pMenu->pStartedFrom->bIsMenuBar ) + { + // #102461# remove highlight in parent + MenuItemData* pData; + size_t i, nCount = pMenu->pStartedFrom->pItemList->size(); + 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() +{ + InitMenuClipRegion(); +} + +long MenuFloatingWindow::ImplGetStartY() const +{ + long nY = 0; + if( pMenu ) + { + for ( sal_uInt16 n = 0; n < nFirstEntry; n++ ) + nY += pMenu->GetItemList()->GetDataFromPos( n )->aSz.Height(); + } + return -nY; +} + +Region MenuFloatingWindow::ImplCalcClipRegion( 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::InitMenuClipRegion() +{ + if ( IsScrollMenu() ) + { + SetClipRegion( ImplCalcClipRegion() ); + } + else + { + SetClipRegion(); + } +} + +void MenuFloatingWindow::ImplHighlightItem( const MouseEvent& rMEvt, bool bMBDown ) +{ + if( ! pMenu ) + return; + + long nY = nScrollerHeight + ImplGetSVData()->maNWFData.mnMenuFormatBorderY; + long nMouseY = rMEvt.GetPosPixel().Y(); + Size aOutSz = GetOutputSizePixel(); + if ( ( nMouseY >= nY ) && ( nMouseY < ( aOutSz.Height() - nY ) ) ) + { + bool bHighlighted = false; + size_t nCount = pMenu->pItemList->size(); + nY += ImplGetStartY(); // ggf. gescrollt. + for ( size_t 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 ) + { + // only when clicked over the arrow... + Size aSz = GetOutputSizePixel(); + long nFontHeight = GetTextHeight(); + bPopupArea = ( rMEvt.GetPosPixel().X() >= ( aSz.Width() - nFontHeight - nFontHeight/4 ) ); + } + + if ( bMBDown ) + { + if ( n != nHighlightedItem ) + { + ChangeHighlightItem( (sal_uInt16)n, false ); + } + + bool bAllowNewPopup = true; + if ( pActivePopup ) + { + MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); + bAllowNewPopup = pData && ( pData->pSubMenu != pActivePopup ); + if ( bAllowNewPopup ) + KillActivePopup(); + } + + if ( bPopupArea && bAllowNewPopup ) + { + HighlightChanged( NULL ); + } + } + else + { + if ( n != nHighlightedItem ) + { + ChangeHighlightItem( (sal_uInt16)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_NOARG(MenuFloatingWindow, PopupEnd) +{ + // "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) + pMenu->CloseStartedFrom(); + } + + if ( pM ) + pM->pStartedFrom = 0; + + return 0; +} + +IMPL_LINK_NOARG(MenuFloatingWindow, AutoScroll) +{ + ImplScroll( GetPointerPosPixel() ); + return 1; +} + +IMPL_LINK( MenuFloatingWindow, HighlightChanged, Timer*, pTimer ) +{ + if( ! pMenu ) + return 0; + + MenuItemData* pItemData = pMenu->pItemList->GetDataFromPos( nHighlightedItem ); + if ( pItemData ) + { + if ( pActivePopup && ( pActivePopup != pItemData->pSubMenu ) ) + { + sal_uLong nOldFlags = GetPopupModeFlags(); + SetPopupModeFlags( GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE ); + KillActivePopup(); + SetPopupModeFlags( nOldFlags ); + } + if ( pItemData->bEnabled && pItemData->pSubMenu && pItemData->pSubMenu->GetItemCount() && ( pItemData->pSubMenu != pActivePopup ) ) + { + pActivePopup = (PopupMenu*)pItemData->pSubMenu; + long nY = nScrollerHeight+ImplGetStartY(); + MenuItemData* pData = 0; + for ( sal_uLong n = 0; n < nHighlightedItem; n++ ) + { + pData = pMenu->pItemList->GetDataFromPos( n ); + nY += pData->aSz.Height(); + } + pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem ); + Size MySize = GetOutputSizePixel(); + Point aItemTopLeft( 0, nY ); + Point aItemBottomRight( aItemTopLeft ); + aItemBottomRight.X() += MySize.Width(); + aItemBottomRight.Y() += pData->aSz.Height(); + + // shift the popups a little + 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: crash due to Reschedule() in call of Activate() + // Also it is prevented that submenus are displayed which + // were for long in Activate Rescheduled and which should not be + // displayed now. + Menu* pTest = pActivePopup; + sal_uLong nOldFlags = GetPopupModeFlags(); + SetPopupModeFlags( GetPopupModeFlags() | FLOATWIN_POPUPMODE_NOAPPFOCUSCLOSE ); + sal_uInt16 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_NOARG(MenuFloatingWindow, SubmenuClose) +{ + 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 ? (sal_uInt16) GetSettings().GetStyleSettings().GetScrollBarSize() /2 : 0; + bScrollDown = true; + InitMenuClipRegion(); +} + +void MenuFloatingWindow::Execute() +{ + ImplSVData* pSVData = ImplGetSVData(); + + pSVData->maAppData.mpActivePopupMenu = (PopupMenu*)pMenu; + + bInExecute = true; +// bCallingSelect = false; + + while ( bInExecute ) + Application::Yield(); + + pSVData->maAppData.mpActivePopupMenu = NULL; +} + +void MenuFloatingWindow::StopExecute( sal_uLong nFocusId ) +{ + // restore focus + // (could have been restored in Select) + 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; + + // For all actions pActivePopup = 0, if e.g. + // PopupModeEndHdl the popups to destroy were called synchronous + 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; + sal_uLong nFocusId = 0; + if (pStart) + nFocusId = pStart->DeactivateMenuBar(nFocusId); + + // if started elsewhere, cleanup there as well + 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; + + // this window will be destroyed => store date locally... + Menu* pM = pMenu; + sal_uInt16 nItem = nHighlightedItem; + + pCleanUpFrom->StopExecute( nFocusId ); + + if ( nItem != ITEMPOS_INVALID && pM ) + { + MenuItemData* pItemData = pM->GetItemList()->GetDataFromPos( nItem ); + if ( pItemData && !pItemData->bIsTemporary ) + { + pM->nSelectedId = pItemData->nId; + if ( pStart ) + pStart->nSelectedId = pItemData->nId; + + pM->ImplSelect(); + } + } +} + +void MenuFloatingWindow::EndExecute( sal_uInt16 nId ) +{ + size_t nPos; + if ( pMenu && pMenu->GetItemList()->GetData( nId, nPos ) ) + nHighlightedItem = nPos; + else + nHighlightedItem = ITEMPOS_INVALID; + + EndExecute(); +} + +void MenuFloatingWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + // TH creates a ToTop on this window, but the active popup + // should stay on top... + // due to focus change this would close all menus -> don't do it (#94123) + //if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup ) + // pActivePopup->ImplGetFloatingWindow()->ToTop( TOTOP_NOGRABFOCUS ); + + ImplHighlightItem( rMEvt, true ); + + nMBDownPos = nHighlightedItem; +} + +void MenuFloatingWindow::MouseButtonUp( const MouseEvent& rMEvt ) +{ + MenuItemData* pData = pMenu ? pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ) : NULL; + // nMBDownPos store in local variable and reset immediately, + // as it will be too late after EndExecute + sal_uInt16 _nMBDownPos = nMBDownPos; + nMBDownPos = ITEMPOS_INVALID; + if ( pData && pData->bEnabled && ( pData->eType != MENUITEM_SEPARATOR ) ) + { + if ( !pData->pSubMenu ) + { + EndExecute(); + } + else if ( ( pData->nBits & MIB_POPUPSELECT ) && ( nHighlightedItem == _nMBDownPos ) && ( rMEvt.GetClicks() == 2 ) ) + { + // not when clicked over the arrow... + 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() ) + { + // #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 ); + + if ( IsScrollMenu() ) + ImplScroll( rMEvt.GetPosPixel() ); + } + else + { + 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(); + + 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(); + sal_uInt16 nLastVisible; + ((PopupMenu*)pMenu)->ImplCalcVisEntries( nHeight, nFirstEntry, &nLastVisible ); + if ( pMenu->ImplGetNextVisible( nLastVisible ) == ITEMPOS_INVALID ) + { + bScrollDown = false; + ImplDrawScroller( false ); + } + + 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(); // if scrolled through MouseMove. + long nTimeout; + if ( nDelta < 3 ) + nTimeout = 200; + else if ( nDelta < 5 ) + nTimeout = 100; + else if ( nDelta < 8 ) + nTimeout = 70; + else if ( nDelta < 12 ) + nTimeout = 40; + else + nTimeout = 20; + aScrollTimer.SetTimeout( nTimeout ); + aScrollTimer.Start(); + } +} +void MenuFloatingWindow::ChangeHighlightItem( sal_uInt16 n, bool bStartPopupTimer ) +{ + // #57934# ggf. immediately close the active, as TH's backgroundstorage works. + // #65750# we prefer to refrain from the background storage of small lines. + // otherwise the menus are difficult to operate. + // MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n ); + // if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) ) + // KillActivePopup(); + + aSubmenuCloseTimer.Stop(); + if( ! pMenu ) + return; + + if ( nHighlightedItem != ITEMPOS_INVALID ) + { + HighlightItem( nHighlightedItem, false ); + pMenu->ImplCallEventListeners( VCLEVENT_MENU_DEHIGHLIGHT, nHighlightedItem ); + } + + nHighlightedItem = (sal_uInt16)n; + DBG_ASSERT( pMenu->ImplIsVisible( nHighlightedItem ) || nHighlightedItem == ITEMPOS_INVALID, "ChangeHighlightItem: Not visible!" ); + if( nHighlightedItem != ITEMPOS_INVALID ) + { + if( pMenu->pStartedFrom && !pMenu->pStartedFrom->bIsMenuBar ) + { + // #102461# make sure parent entry is highlighted as well + MenuItemData* pData; + size_t i, nCount = pMenu->pStartedFrom->pItemList->size(); + 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->SetHighlightItem(nHighlightedItem); + pMenu->ImplCallHighlight( nHighlightedItem ); + } + else + pMenu->nSelectedId = 0; + + if ( bStartPopupTimer ) + { + // #102438# Menu items are not selectable + // If a menu item is selected by an AT-tool via the XAccessibleAction, XAccessibleValue + // or XAccessibleSelection interface, and the parent popup menus are not executed yet, + // the parent popup menus must be executed SYNCHRONOUSLY, before the menu item is selected. + if ( GetSettings().GetMouseSettings().GetMenuDelay() ) + aHighlightChangedTimer.Start(); + else + HighlightChanged( &aHighlightChangedTimer ); + } +} + +void MenuFloatingWindow::HighlightItem( sal_uInt16 nPos, bool bHighlight ) +{ + if( ! pMenu ) + return; + + Size aSz = GetOutputSizePixel(); + long nStartY = ImplGetStartY(); + long nY = nScrollerHeight + nStartY + ImplGetSVData()->maNWFData.mnMenuFormatBorderY; + long nX = 0; + + if ( pMenu->pLogo ) + nX = pMenu->pLogo->aBitmap.GetSizePixel().Width(); + + int nOuterSpaceX = ImplGetSVData()->maNWFData.mnMenuFormatBorderX; + + size_t nCount = pMenu->pItemList->size(); + for ( size_t n = 0; n < nCount; n++ ) + { + MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); + if ( n == nPos ) + { + 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+nOuterSpaceX, nY ), Size( aSz.Width()-2*nOuterSpaceX, pData->aSz.Height() ) ); + if ( pData->nBits & MIB_POPUPSELECT ) + { + long nFontHeight = GetTextHeight(); + aItemRect.Right() -= nFontHeight + nFontHeight/4; + } + + if( IsNativeControlSupported( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL ) ) + { + Size aPxSize( GetOutputSizePixel() ); + Push( PUSH_CLIPREGION ); + IntersectClipRegion( Rectangle( Point( nX, nY ), Size( aSz.Width(), pData->aSz.Height() ) ) ); + Rectangle aCtrlRect( Point( nX, 0 ), Size( aPxSize.Width()-nX, aPxSize.Height() ) ); + MenupopupValue aVal( pMenu->nTextPos-GUTTERBORDER, aItemRect ); + DrawNativeControl( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, + aCtrlRect, + CTRL_STATE_ENABLED, + aVal, + OUString() ); + if( bHighlight && + IsNativeControlSupported( CTRL_MENU_POPUP, PART_MENU_ITEM ) ) + { + bDrawItemRect = false; + if( !DrawNativeControl( CTRL_MENU_POPUP, PART_MENU_ITEM, + aItemRect, + CTRL_STATE_SELECTED | ( pData->bEnabled? CTRL_STATE_ENABLED: 0 ), + aVal, + OUString() ) ) + { + bDrawItemRect = bHighlight; + } + } + else + bDrawItemRect = bHighlight; + Pop(); + } + if( bDrawItemRect ) + { + if ( bHighlight ) + { + if( pData->bEnabled ) + SetFillColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() ); + else + { + SetFillColor(); + oldLineColor = GetLineColor(); + SetLineColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() ); + bRestoreLineColor = true; + } + } + else + SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() ); + + DrawRect( aItemRect ); + } + pMenu->ImplPaint( this, nScrollerHeight, nStartY, pData, bHighlight ); + if( bRestoreLineColor ) + SetLineColor( oldLineColor ); + } + return; + } + + nY += pData->aSz.Height(); + } +} + +Rectangle MenuFloatingWindow::ImplGetItemRect( sal_uInt16 nPos ) +{ + if( ! pMenu ) + return Rectangle(); + + Rectangle aRect; + Size aSz = GetOutputSizePixel(); + long nStartY = ImplGetStartY(); + long nY = nScrollerHeight+nStartY; + long nX = 0; + + if ( pMenu->pLogo ) + nX = pMenu->pLogo->aBitmap.GetSizePixel().Width(); + + size_t nCount = pMenu->pItemList->size(); + for ( size_t 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(); + + sal_uInt16 n = nHighlightedItem; + if ( n == ITEMPOS_INVALID ) + { + if ( bUp ) + n = 0; + else + n = pMenu->GetItemCount()-1; + } + + sal_uInt16 nLoop = n; + + if( bHomeEnd ) + { + // absolute positioning + if( bUp ) + { + n = pMenu->GetItemCount(); + nLoop = n-1; + } + else + { + n = (sal_uInt16)-1; + nLoop = n+1; + } + } + + do + { + if ( bUp ) + { + if ( n ) + n--; + else + if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) ) + n = pMenu->GetItemCount()-1; + else + break; + } + else + { + n++; + if ( n >= pMenu->GetItemCount() ) + { + if ( !IsScrollMenu() || ( nHighlightedItem == ITEMPOS_INVALID ) ) + n = 0; + else + break; + } + } + + MenuItemData* pData = (MenuItemData*)pMenu->GetItemList()->GetDataFromPos( n ); + if ( ( pData->bEnabled || !rSettings.GetSkipDisabledInMenus() ) + && ( pData->eType != MENUITEM_SEPARATOR ) && pMenu->ImplIsVisible( n ) && pMenu->ImplIsSelectable( n ) ) + { + // Is selection in visible area? + if ( IsScrollMenu() ) + { + ChangeHighlightItem( ITEMPOS_INVALID, false ); + + while ( n < nFirstEntry ) + ImplScroll( true ); + + Size aOutSz = GetOutputSizePixel(); + sal_uInt16 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 ); + + sal_uInt16 nCode = rKEvent.GetKeyCode().GetCode(); + bKeyInput = true; + switch ( nCode ) + { + case KEY_UP: + case KEY_DOWN: + { + ImplCursorUpDown( nCode == KEY_UP ); + } + break; + case KEY_END: + case KEY_HOME: + { + ImplCursorUpDown( nCode == KEY_END, true ); + } + break; + case KEY_F6: + case KEY_ESCAPE: + { + // Ctrl-F6 acts like ESC here, the menu bar however will then put the focus in the document + if( nCode == KEY_F6 && !rKEvent.GetKeyCode().IsMod1() ) + break; + if( pMenu ) + { + if ( !pMenu->pStartedFrom ) + { + StopExecute(); + KillActivePopup(); + } + else if ( pMenu->pStartedFrom->bIsMenuBar ) + { + pMenu->pStartedFrom->MenuBarKeyInput(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 ) + { + pMenu->pStartedFrom->MenuBarKeyInput(rKEvent); + } + else + { + MenuFloatingWindow* pFloat = ((PopupMenu*)pMenu->pStartedFrom)->ImplGetFloatingWindow(); + pFloat->GrabFocus(); + pFloat->KillActivePopup(); + sal_uInt16 highlightItem = pFloat->GetHighlightedItem(); + pFloat->ChangeHighlightItem(highlightItem, false); + } + } + } + break; + case KEY_RIGHT: + { + if( pMenu ) + { + bool bDone = false; + if ( nHighlightedItem != ITEMPOS_INVALID ) + { + MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); + if ( pData && pData->pSubMenu ) + { + HighlightChanged( 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: + { + sal_Unicode nCharCode = rKEvent.GetCharCode(); + sal_uInt16 nPos = 0; + sal_uInt16 nDuplicates = 0; + MenuItemData* pData = (nCharCode && pMenu) ? pMenu->GetItemList()->SearchItem( nCharCode, rKEvent.GetKeyCode(), nPos, nDuplicates, nHighlightedItem ) : NULL; + if ( pData ) + { + if ( pData->pSubMenu || nDuplicates > 1 ) + { + ChangeHighlightItem( nPos, false ); + HighlightChanged( 0 ); + } + else + { + nHighlightedItem = nPos; + EndExecute(); + } + } + else + FloatingWindow::KeyInput( rKEvent ); + } + } + // #105474# check if menu window was not destroyed + if ( !aDelData.IsDead() ) + { + 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; + ImplControlValue aVal( pMenu->nTextPos-GUTTERBORDER ); + DrawNativeControl( CTRL_MENU_POPUP, PART_ENTIRE_CONTROL, + Rectangle( Point( nX, 0 ), aPxSize ), + CTRL_STATE_ENABLED, + aVal, + OUString() ); + InitMenuClipRegion(); + } + 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; + + sal_uInt16 nStyle = 0; + if ( ( bUp && !bScrollUp ) || ( !bUp && !bScrollDown ) ) + nStyle |= SYMBOL_DRAW_DISABLE; + + aDecoView.DrawSymbol( aRect, eSymbol, GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle ); + + InitMenuClipRegion(); +} + +void MenuFloatingWindow::RequestHelp( const HelpEvent& rHEvt ) +{ + sal_uInt16 nId = nHighlightedItem; + Menu* pM = pMenu; + Window* pW = this; + + // #102618# Get item rect before destroying the window in EndExecute() call + Rectangle aHighlightRect( ImplGetItemRect( nHighlightedItem ) ); + + if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) ) + { + nHighlightedItem = ITEMPOS_INVALID; + EndExecute(); + pW = NULL; + } + + if( !ImplHandleHelpEvent( pW, pM, nId, rHEvt, aHighlightRect ) ) + Window::RequestHelp( rHEvt ); +} + +void MenuFloatingWindow::StateChanged( StateChangedType nType ) +{ + FloatingWindow::StateChanged( nType ); + + if ( ( nType == STATE_CHANGE_CONTROLFOREGROUND ) || ( nType == STATE_CHANGE_CONTROLBACKGROUND ) ) + { + ImplInitMenuWindow( this, 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; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/window/menufloatingwindow.hxx b/vcl/source/window/menufloatingwindow.hxx new file mode 100644 index 000000000000..c2f84c7eb2ab --- /dev/null +++ b/vcl/source/window/menufloatingwindow.hxx @@ -0,0 +1,121 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_SOURCE_WINDOW_MENUFLOATINGWINDOW_HXX +#define INCLUDED_VCL_SOURCE_WINDOW_MENUFLOATINGWINDOW_HXX + +#include "menuwindow.hxx" + +#include <vcl/floatwin.hxx> +#include <vcl/menu.hxx> + +#define EXTRASPACEY 2 +#define GUTTERBORDER 8 + +/** Class that implements the actual window of the floating menu. +*/ +class MenuFloatingWindow : public MenuWindow, public FloatingWindow +{ + friend void Menu::ImplFillLayoutData() const; + friend Menu::~Menu(); + +private: + Menu* pMenu; + PopupMenu* pActivePopup; + Timer aHighlightChangedTimer; + Timer aSubmenuCloseTimer; + Timer aScrollTimer; + sal_uLong nSaveFocusId; + sal_uInt16 nHighlightedItem; // highlighted/selected Item + sal_uInt16 nMBDownPos; + sal_uInt16 nScrollerHeight; + sal_uInt16 nFirstEntry; + sal_uInt16 nBorder; + sal_uInt16 nPosInParent; + bool bInExecute; + + bool bScrollMenu; + bool bScrollUp; + bool bScrollDown; + bool bIgnoreFirstMove; + bool bKeyInput; + + DECL_LINK(PopupEnd, void *); + DECL_LINK( HighlightChanged, Timer* ); + DECL_LINK(SubmenuClose, void *); + DECL_LINK(AutoScroll, void *); + DECL_LINK( ShowHideListener, VclWindowEvent* ); + + virtual void StateChanged( StateChangedType nType ) SAL_OVERRIDE; + virtual void DataChanged( const DataChangedEvent& rDCEvt ) SAL_OVERRIDE; + + void InitMenuClipRegion(); + +protected: + Region ImplCalcClipRegion( bool bIncludeLogo = true ) const; + 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( sal_uInt16 nPos ); + +public: + MenuFloatingWindow( Menu* pMenu, Window* pParent, WinBits nStyle ); + virtual ~MenuFloatingWindow(); + + void doShutdown(); + + virtual void MouseMove( const MouseEvent& rMEvt ) SAL_OVERRIDE; + virtual void MouseButtonDown( const MouseEvent& rMEvt ) SAL_OVERRIDE; + virtual void MouseButtonUp( const MouseEvent& rMEvt ) SAL_OVERRIDE; + virtual void KeyInput( const KeyEvent& rKEvent ) SAL_OVERRIDE; + virtual void Command( const CommandEvent& rCEvt ) SAL_OVERRIDE; + virtual void Paint( const Rectangle& rRect ) SAL_OVERRIDE; + virtual void RequestHelp( const HelpEvent& rHEvt ) SAL_OVERRIDE; + virtual void Resize() SAL_OVERRIDE; + + void SetFocusId( sal_uLong nId ) { nSaveFocusId = nId; } + sal_uLong GetFocusId() const { return nSaveFocusId; } + + void EnableScrollMenu( bool b ); + bool IsScrollMenu() const { return bScrollMenu; } + sal_uInt16 GetScrollerHeight() const { return nScrollerHeight; } + + void Execute(); + void StopExecute( sal_uLong nFocusId = 0 ); + void EndExecute(); + void EndExecute( sal_uInt16 nSelectId ); + + PopupMenu* GetActivePopup() const { return pActivePopup; } + void KillActivePopup( PopupMenu* pThisOnly = NULL ); + + void HighlightItem( sal_uInt16 nPos, bool bHighlight ); + void ChangeHighlightItem( sal_uInt16 n, bool bStartPopupTimer ); + sal_uInt16 GetHighlightedItem() const { return nHighlightedItem; } + + void SetPosInParent( sal_uInt16 nPos ) { nPosInParent = nPos; } + + virtual ::com::sun::star::uno::Reference< ::com::sun::star::accessibility::XAccessible > CreateAccessible() SAL_OVERRIDE; +}; + +#endif // INCLUDED_VCL_SOURCE_WINDOW_MENUFLOATINGWINDOW_HXX +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/window/menuitemlist.cxx b/vcl/source/window/menuitemlist.cxx new file mode 100644 index 000000000000..4fe0a0a23922 --- /dev/null +++ b/vcl/source/window/menuitemlist.cxx @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "menuitemlist.hxx" + +#include <salframe.hxx> +#include <salinst.hxx> +#include <salmenu.hxx> +#include <svdata.hxx> +#include <vcl/i18nhelp.hxx> +#include <vcl/settings.hxx> +#include <vcl/window.hxx> + +using namespace css; +using namespace vcl; + +MenuItemData::~MenuItemData() +{ + if( pAutoSubMenu ) + { + ((PopupMenu*)pAutoSubMenu)->pRefAutoSubMenu = NULL; + delete pAutoSubMenu; + pAutoSubMenu = NULL; + } + if( pSalMenuItem ) + ImplGetSVData()->mpDefInst->DestroyMenuItem( pSalMenuItem ); +} + +MenuItemList::~MenuItemList() +{ + for( size_t i = 0, n = maItemList.size(); i < n; ++i ) + delete maItemList[ i ]; +} + +MenuItemData* MenuItemList::Insert( + sal_uInt16 nId, + MenuItemType eType, + MenuItemBits nBits, + const OUString& rStr, + const Image& rImage, + Menu* pMenu, + size_t nPos, + const OString &rIdent +) +{ + MenuItemData* pData = new MenuItemData( rStr, rImage ); + pData->nId = nId; + pData->sIdent = rIdent; + pData->eType = eType; + pData->nBits = nBits; + pData->pSubMenu = NULL; + pData->pAutoSubMenu = NULL; + 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 ); + + if( nPos < maItemList.size() ) { + maItemList.insert( maItemList.begin() + nPos, pData ); + } else { + maItemList.push_back( pData ); + } + return pData; +} + +void MenuItemList::InsertSeparator(const OString &rIdent, size_t nPos) +{ + MenuItemData* pData = new MenuItemData; + pData->nId = 0; + pData->sIdent = rIdent; + pData->eType = MENUITEM_SEPARATOR; + pData->nBits = 0; + pData->pSubMenu = NULL; + pData->pAutoSubMenu = NULL; + 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 = OUString(); + aSalMIData.aImage = Image(); + + // Native-support: returns NULL if not supported + pData->pSalMenuItem = ImplGetSVData()->mpDefInst->CreateMenuItem( &aSalMIData ); + + if( nPos < maItemList.size() ) { + maItemList.insert( maItemList.begin() + nPos, pData ); + } else { + maItemList.push_back( pData ); + } +} + +void MenuItemList::Remove( size_t nPos ) +{ + if( nPos < maItemList.size() ) + { + delete maItemList[ nPos ]; + maItemList.erase( maItemList.begin() + nPos ); + } +} + +MenuItemData* MenuItemList::GetData( sal_uInt16 nSVId, size_t& rPos ) const +{ + for( size_t i = 0, n = maItemList.size(); i < n; ++i ) + { + if ( maItemList[ i ]->nId == nSVId ) + { + rPos = i; + return maItemList[ i ]; + } + } + return NULL; +} + +MenuItemData* MenuItemList::SearchItem( + sal_Unicode cSelectChar, + KeyCode aKeyCode, + sal_uInt16& rPos, + sal_uInt16& nDuplicates, + sal_uInt16 nCurrentPos +) const +{ + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + + size_t nListCount = maItemList.size(); + + // try character code first + nDuplicates = GetItemCount( cSelectChar ); // return number of duplicates + if( nDuplicates ) + { + for ( rPos = 0; rPos < nListCount; rPos++) + { + MenuItemData* pData = maItemList[ rPos ]; + if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) ) + { + if( nDuplicates > 1 && rPos == nCurrentPos ) + continue; // select next entry with the same mnemonic + else + return pData; + } + } + } + + // nothing found, try keycode instead + nDuplicates = GetItemCount( aKeyCode ); // return number of duplicates + + if( nDuplicates ) + { + char ascii = 0; + if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z ) + ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A)); + + for ( rPos = 0; rPos < nListCount; rPos++) + { + MenuItemData* pData = maItemList[ rPos ]; + if ( pData->bEnabled ) + { + sal_Int32 n = pData->aText.indexOf('~'); + if ( n != -1 ) + { + KeyCode mnKeyCode; + sal_Unicode mnUnicode = pData->aText[n+1]; + Window* pDefWindow = ImplGetDefaultWindow(); + if( ( pDefWindow + && pDefWindow->ImplGetFrame()->MapUnicodeToKeyCode( mnUnicode, + Application::GetSettings().GetUILanguageTag().getLanguageType(), 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; +} + +size_t MenuItemList::GetItemCount( sal_Unicode cSelectChar ) const +{ + // returns number of entries with same mnemonic + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + + size_t nItems = 0; + for ( size_t nPos = maItemList.size(); nPos; ) + { + MenuItemData* pData = maItemList[ --nPos ]; + if ( pData->bEnabled && rI18nHelper.MatchMnemonic( pData->aText, cSelectChar ) ) + nItems++; + } + + return nItems; +} + +size_t MenuItemList::GetItemCount( KeyCode aKeyCode ) const +{ + // returns number of entries with same mnemonic + // uses key codes instead of character codes + const vcl::I18nHelper& rI18nHelper = Application::GetSettings().GetUILocaleI18nHelper(); + char ascii = 0; + if( aKeyCode.GetCode() >= KEY_A && aKeyCode.GetCode() <= KEY_Z ) + ascii = sal::static_int_cast<char>('A' + (aKeyCode.GetCode() - KEY_A)); + + size_t nItems = 0; + for ( size_t nPos = maItemList.size(); nPos; ) + { + MenuItemData* pData = maItemList[ --nPos ]; + if ( pData->bEnabled ) + { + sal_Int32 n = pData->aText.indexOf('~'); + if (n != -1) + { + 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[n+1], + Application::GetSettings().GetUILanguageTag().getLanguageType(), mnKeyCode ) + && aKeyCode.GetCode() == mnKeyCode.GetCode() + ) + || ( ascii + && rI18nHelper.MatchMnemonic( pData->aText, ascii ) + ) + ) + nItems++; + } + } + } + + return nItems; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/window/menuitemlist.hxx b/vcl/source/window/menuitemlist.hxx new file mode 100644 index 000000000000..27f138efb4d1 --- /dev/null +++ b/vcl/source/window/menuitemlist.hxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <rsc/rsc-vcl-shared-types.hxx> +#include <vcl/image.hxx> +#include <vcl/keycod.hxx> +#include <vcl/menu.hxx> + +#include <com/sun/star/i18n/XCharacterClassification.hpp> + +#include <vector> + +class SalMenuItem; + +struct MenuItemData +{ + sal_uInt16 nId; // SV Id + MenuItemType eType; // MenuItem-Type + MenuItemBits nBits; // MenuItem-Bits + Menu* pSubMenu; // Pointer to SubMenu + Menu* pAutoSubMenu; // Pointer to SubMenu from Resource + OUString aText; // Menu-Text + OUString aHelpText; // Help-String + OUString aTipHelpText; // TipHelp-String (eg, expanded filenames) + OUString aCommandStr; // CommandString + OUString aHelpCommandStr; // Help command string (to reference external help) + OString sIdent; + OString aHelpId; // Help-Id + sal_uLong nUserValue; // User value + Image aImage; // Image + vcl::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; // only temporarily valid + OUString aAccessibleName; // accessible name + OUString aAccessibleDescription; // accessible description + + SalMenuItem* pSalMenuItem; // access to native menu + + MenuItemData() + : nId(0) + , eType(MENUITEM_DONTKNOW) + , nBits(0) + , pSubMenu(NULL) + , pAutoSubMenu(NULL) + , nUserValue(0) + , bChecked(false) + , bEnabled(false) + , bVisible(false) + , bIsTemporary(false) + , bMirrorMode(false) + , nItemImageAngle(0) + , pSalMenuItem(NULL) + { + } + MenuItemData( const OUString& rStr, const Image& rImage ) + : nId(0) + , eType(MENUITEM_DONTKNOW) + , nBits(0) + , pSubMenu(NULL) + , pAutoSubMenu(NULL) + , aText(rStr) + , nUserValue(0) + , aImage(rImage) + , bChecked(false) + , bEnabled(false) + , bVisible(false) + , bIsTemporary(false) + , bMirrorMode(false) + , nItemImageAngle(0) + , pSalMenuItem(NULL) + { + } + ~MenuItemData(); + bool HasCheck() const + { + return bChecked || ( nBits & ( MIB_RADIOCHECK | MIB_CHECKABLE | MIB_AUTOCHECK ) ); + } +}; + +class MenuItemList +{ +private: + typedef ::std::vector< MenuItemData* > MenuItemDataList_impl; + MenuItemDataList_impl maItemList; + + css::uno::Reference< css::i18n::XCharacterClassification > xCharClass; + +public: + MenuItemList() {} + ~MenuItemList(); + + MenuItemData* Insert( + sal_uInt16 nId, + MenuItemType eType, + MenuItemBits nBits, + const OUString& rStr, + const Image& rImage, + Menu* pMenu, + size_t nPos, + const OString &rIdent + ); + void InsertSeparator(const OString &rIdent, size_t nPos); + void Remove( size_t nPos ); + + MenuItemData* GetData( sal_uInt16 nSVId, size_t& rPos ) const; + MenuItemData* GetData( sal_uInt16 nSVId ) const + { + size_t nTemp; + return GetData( nSVId, nTemp ); + } + MenuItemData* GetDataFromPos( size_t nPos ) const + { + return ( nPos < maItemList.size() ) ? maItemList[ nPos ] : NULL; + } + + MenuItemData* SearchItem( + sal_Unicode cSelectChar, + vcl::KeyCode aKeyCode, + sal_uInt16& rPos, + sal_uInt16& nDuplicates, + sal_uInt16 nCurrentPos + ) const; + size_t GetItemCount( sal_Unicode cSelectChar ) const; + size_t GetItemCount( vcl::KeyCode aKeyCode ) const; + size_t size() + { + return maItemList.size(); + } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/window/menuwindow.cxx b/vcl/source/window/menuwindow.cxx new file mode 100644 index 000000000000..0f0a8dd52f6a --- /dev/null +++ b/vcl/source/window/menuwindow.cxx @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "menuwindow.hxx" +#include "menuitemlist.hxx" + +#include <vcl/help.hxx> +#include <vcl/menu.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> + +void MenuWindow::ImplInitMenuWindow(Window* pWin, bool bFont, bool bMenuBar) +{ + const StyleSettings& rStyleSettings = pWin->GetSettings().GetStyleSettings(); + + if ( bFont ) + pWin->SetPointFont( rStyleSettings.GetMenuFont() ); + if( bMenuBar ) + { + const BitmapEx& rPersonaBitmap = Application::GetSettings().GetStyleSettings().GetPersonaHeader(); + if ( !rPersonaBitmap.IsEmpty() ) + { + Wallpaper aWallpaper( rPersonaBitmap ); + aWallpaper.SetStyle( WALLPAPER_TOPRIGHT ); + aWallpaper.SetColor( Application::GetSettings().GetStyleSettings().GetWorkspaceColor() ); + + pWin->SetBackground( aWallpaper ); + pWin->SetPaintTransparent( false ); + pWin->SetParentClipMode( 0 ); + } + else 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(); +} + +static sal_uLong ImplChangeTipTimeout( sal_uLong nTimeout, Window *pWindow ) +{ + AllSettings aAllSettings( pWindow->GetSettings() ); + HelpSettings aHelpSettings( aAllSettings.GetHelpSettings() ); + sal_uLong nRet = aHelpSettings.GetTipTimeout(); + aHelpSettings.SetTipTimeout( nTimeout ); + aAllSettings.SetHelpSettings( aHelpSettings ); + pWindow->SetSettings( aAllSettings ); + return nRet; +} + +bool MenuWindow::ImplHandleHelpEvent(Window* pMenuWindow, Menu* pMenu, sal_uInt16 nHighlightedItem, + const HelpEvent& rHEvt, const Rectangle &rHighlightRect) +{ + if( ! pMenu ) + return false; + + bool bDone = false; + sal_uInt16 nId = 0; + + if ( nHighlightedItem != ITEMPOS_INVALID ) + { + MenuItemData* pItemData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); + if ( pItemData ) + nId = pItemData->nId; + } + + if ( ( rHEvt.GetMode() & HELPMODE_BALLOON ) && pMenuWindow ) + { + Point aPos; + if( rHEvt.KeyboardActivated() ) + aPos = rHighlightRect.Center(); + else + aPos = rHEvt.GetMousePosPixel(); + + Rectangle aRect( aPos, Size() ); + if (!pMenu->GetHelpText(nId).isEmpty()) + Help::ShowBalloon( pMenuWindow, aPos, pMenu->GetHelpText( nId ) ); + else + { + // give user a chance to read the full filename + sal_uLong oldTimeout=ImplChangeTipTimeout( 60000, pMenuWindow ); + // call always, even when strlen==0 to correctly remove tip + Help::ShowQuickHelp( pMenuWindow, aRect, pMenu->GetTipHelpText( nId ) ); + ImplChangeTipTimeout( oldTimeout, pMenuWindow ); + } + bDone = true; + } + else if ( ( rHEvt.GetMode() & HELPMODE_QUICK ) && pMenuWindow ) + { + Point aPos = rHEvt.GetMousePosPixel(); + Rectangle aRect( aPos, Size() ); + // give user a chance to read the full filename + sal_uLong oldTimeout=ImplChangeTipTimeout( 60000, pMenuWindow ); + // call always, even when strlen==0 to correctly remove tip + Help::ShowQuickHelp( pMenuWindow, aRect, pMenu->GetTipHelpText( nId ) ); + ImplChangeTipTimeout( oldTimeout, pMenuWindow ); + bDone = true; + } + else if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) ) + { + // is help in the application selected + Help* pHelp = Application::GetHelp(); + if ( pHelp ) + { + // is an id available, then call help with the id, otherwise + // use help-index + OUString aCommand = pMenu->GetItemCommand( nId ); + OString aHelpId( pMenu->GetHelpId( nId ) ); + if( aHelpId.isEmpty() ) + aHelpId = OOO_HELP_INDEX; + + if ( !aCommand.isEmpty() ) + pHelp->Start( aCommand, NULL ); + else + pHelp->Start( OStringToOUString( aHelpId, RTL_TEXTENCODING_UTF8 ), NULL ); + } + bDone = true; + } + return bDone; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/window/menuwindow.hxx b/vcl/source/window/menuwindow.hxx new file mode 100644 index 000000000000..9b2f099a5d57 --- /dev/null +++ b/vcl/source/window/menuwindow.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_VCL_SOURCE_WINDOW_MENUWINDOW_HXX +#define INCLUDED_VCL_SOURCE_WINDOW_MENUWINDOW_HXX + +#include <sal/types.h> + +class HelpEvent; +class Menu; +class Rectangle; +class Window; + +/** Common ancestor for MenuFloatingWindow and MenuBarWindow. + +The menu can be a floating window, or a menu bar. Even though this has +'Window' in the name, it is not derived from the VCL's Window class, as the +MenuFloatingWindow's or MenuBarWindow's already are VCL Windows. +*/ +class MenuWindow +{ +public: + + /// Sets up some visual properties of the underlying window. + void ImplInitMenuWindow(Window* pWin, bool bFont, bool bMenuBar); + + /// Show the appropriate help tooltip. + bool ImplHandleHelpEvent(Window* pMenuWindow, Menu* pMenu, sal_uInt16 nHighlightedItem, + const HelpEvent& rHEvt, const Rectangle &rHighlightRect); +}; + +#endif // INCLUDED_VCL_SOURCE_WINDOW_MENUWINDOW_HXX +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |