diff options
Diffstat (limited to 'vcl/source/window/menu.cxx')
-rw-r--r-- | vcl/source/window/menu.cxx | 3444 |
1 files changed, 3444 insertions, 0 deletions
diff --git a/vcl/source/window/menu.cxx b/vcl/source/window/menu.cxx new file mode 100644 index 000000000000..9df101095121 --- /dev/null +++ b/vcl/source/window/menu.cxx @@ -0,0 +1,3444 @@ +/************************************************************************* + * + * $RCSfile: menu.cxx,v $ + * + * $Revision: 1.1.1.1 $ + * + * last change: $Author: hr $ $Date: 2000-09-18 17:05:40 $ + * + * The Contents of this file are made available subject to the terms of + * either of the following licenses + * + * - GNU Lesser General Public License Version 2.1 + * - Sun Industry Standards Source License Version 1.1 + * + * Sun Microsystems Inc., October, 2000 + * + * GNU Lesser General Public License Version 2.1 + * ============================================= + * Copyright 2000 by Sun Microsystems, Inc. + * 901 San Antonio Road, Palo Alto, CA 94303, USA + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * + * Sun Industry Standards Source License Version 1.1 + * ================================================= + * The contents of this file are subject to the Sun Industry Standards + * Source License Version 1.1 (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.openoffice.org/license.html. + * + * Software provided under this License is provided on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, + * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. + * See the License for the specific provisions governing your rights and + * obligations concerning the Software. + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2000 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + * Contributor(s): _______________________________________ + * + * + ************************************************************************/ + +#define _SV_MENU_CXX + +#ifndef _LIST_HXX +#include <tools/list.hxx> +#endif +#ifndef _DEBUG_HXX +#include <tools/debug.hxx> +#endif + +#ifndef _SV_SVDATA_HXX +#include <svdata.hxx> +#endif +#ifndef _SV_SVAPP_HXX +#include <svapp.hxx> +#endif +#ifndef _SV_MNEMONIC_HXX +#include <mnemonic.hxx> +#endif +#ifndef _SV_IMAGE_HXX +#include <image.hxx> +#endif +#ifndef _SV_EVENT_HXX +#include <event.hxx> +#endif +#ifndef _SV_HELP_HXX +#include <help.hxx> +#endif +#ifndef _SV_SVIDS_HRC +#include <svids.hrc> +#endif +#ifndef _SV_FLOATWIN_HXX +#include <floatwin.hxx> +#endif +#ifndef _SV_WRKWIN_HXX +#include <wrkwin.hxx> +#endif +#ifndef _SV_TIMER_HXX +#include <timer.hxx> +#endif +#ifndef _SV_SOUND_HXX +#include <sound.hxx> +#endif +#ifndef _SV_DECOVIEW_HXX +#include <decoview.hxx> +#endif +#ifndef _SV_BITMAP_HXX +#include <bitmap.hxx> +#endif +#ifndef _SV_RC_H +#include <rc.h> +#endif +#ifndef _SV_MENU_HXX +#include <menu.hxx> +#endif +#ifndef _SV_BUTTON_HXX +#include <button.hxx> +#endif +#ifndef _SV_GRADIENT_HXX +#include <gradient.hxx> +#endif +#ifndef _SV_ACCESS_HXX +#include <access.hxx> +#endif + +#ifndef _ISOLANG_HXX +#include <tools/isolang.hxx> +#endif + +#pragma hdrstop + +#ifndef _COM_SUN_STAR_UNO_REFERENCE_H_ +#include <com/sun/star/uno/Reference.h> +#endif + +#ifndef _COM_SUN_STAR_LANG_XCHARACTERCLASSIFICATION_HPP_ +#include <com/sun/star/lang/XCharacterClassification.hpp> +#endif + +#include <unohelp.hxx> + +using namespace ::com::sun::star; + +DBG_NAME( Menu ); + +#define ITEMPOS_INVALID 0xFFFF + +#define EXTRASPACEY 2 +#define EXTRAITEMHEIGHT 4 + +DropEvent ImplTranslateDropEvent( const DropEvent& rE, Window* pSource, Window* pDest ) +{ + Point aPos = pSource->OutputToScreenPixel( rE.GetPosPixel() ); + aPos = pDest->ScreenToOutputPixel( aPos ); + return DropEvent( aPos, rE.GetData(), rE.GetAction(), rE.GetSourceOptions(), + rE.GetWindowType(), rE.IsDefaultAction() ); +} + +inline BOOL ImplIsMouseFollow() +{ + return ( Application::GetSettings().GetMouseSettings().GetFollow() & MOUSE_FOLLOW_MENU ) ? TRUE : FALSE; +} + + +struct MenuItemData +{ + USHORT nId; // SV Id + MenuItemType eType; // MenuItem-Type + MenuItemBits nBits; // MenuItem-Bits + Menu* pSubMenu; // Pointer auf das SubMenu + Menu* pAutoSubMenu; // Pointer auf SubMenu aus Resource + XubString aText; // Menu-Text + XubString aHelpText; // Help-String + XubString aCommandStr; // CommandString + ULONG nHelpId; // Help-Id + Image aImage; // Image + KeyCode aAccelKey; // Accelerator-Key + BOOL bChecked; // Checked + BOOL bEnabled; // Enabled + BOOL bIsTemporary; // Temporary inserted ('No selection possible') + + Size aSz; // nur temporaer gueltig + + MenuItemData() {} + MenuItemData( const XubString& rStr, const Image& rImage ) : + aText( rStr ), + aImage( rImage ) + {} + ~MenuItemData() { delete pAutoSubMenu; } +}; + + +class MenuItemList : public List +{ + uno::Reference< lang::XCharacterClassification > xCharClass; +public: + MenuItemList() : List( 16, 4 ) {} + ~MenuItemList(); + + MenuItemData* Insert( USHORT nId, MenuItemType eType, MenuItemBits nBits, + const XubString& rStr, const Image& rImage, + Menu* pMenu, USHORT nPos ); + void InsertSeparator( USHORT nPos ); + void Remove( USHORT nPos ); + + + MenuItemData* GetData( USHORT nSVId, USHORT& rPos ) const; + MenuItemData* GetData( USHORT nSVId ) const + { USHORT nTemp; return GetData( nSVId, nTemp ); } + MenuItemData* GetDataFromPos( ULONG nPos ) const + { return (MenuItemData*)List::GetObject( nPos ); } + + MenuItemData* SearchItem( xub_Unicode cSelectChar, USHORT& rPos ) const; + uno::Reference< lang::XCharacterClassification > GetCharClass() const; +}; + + + +MenuItemList::~MenuItemList() +{ + for ( ULONG n = Count(); n; ) + { + MenuItemData* pData = GetDataFromPos( --n ); + delete pData; + } +} + +MenuItemData* MenuItemList::Insert( USHORT nId, MenuItemType eType, + MenuItemBits nBits, + const XubString& rStr, const Image& rImage, + Menu* pMenu, USHORT nPos ) +{ + MenuItemData* pData = new MenuItemData( rStr, rImage ); + pData->nId = nId; + pData->eType = eType; + pData->nBits = nBits; + pData->pSubMenu = NULL; + pData->pAutoSubMenu = NULL; + pData->nHelpId = 0; + pData->bChecked = FALSE; + pData->bEnabled = TRUE; + pData->bIsTemporary = FALSE; + List::Insert( (void*)pData, nPos ); + return pData; +} + +void MenuItemList::InsertSeparator( USHORT nPos ) +{ + MenuItemData* pData = new MenuItemData; + pData->nId = 0; + pData->eType = MENUITEM_SEPARATOR; + pData->nBits = 0; + pData->pSubMenu = NULL; + pData->pAutoSubMenu = NULL; + pData->nHelpId = 0; + pData->bChecked = FALSE; + pData->bEnabled = TRUE; + pData->bIsTemporary = FALSE; + List::Insert( (void*)pData, nPos ); +} + +void MenuItemList::Remove( USHORT nPos ) +{ + MenuItemData* pData = (MenuItemData*)List::Remove( (ULONG)nPos ); + if ( pData ) + delete pData; +} + +MenuItemData* MenuItemList::GetData( USHORT nSVId, USHORT& rPos ) const +{ + rPos = 0; + MenuItemData* pData = (MenuItemData*)GetObject( rPos ); + while ( pData ) + { + if ( pData->nId == nSVId ) + return pData; + + rPos++; + pData = (MenuItemData*)GetObject( rPos ); + } + + return NULL; +} + +MenuItemData* MenuItemList::SearchItem( xub_Unicode cSelectChar, USHORT& rPos ) const +{ + const ::com::sun::star::lang::Locale& rLocale = Application::GetSettings().GetLocale(); + xub_Unicode cCharCode = GetCharClass()->toUpper( String(cSelectChar), 0, 1, rLocale )[0]; + for ( rPos = (USHORT)Count(); rPos; ) + { + MenuItemData* pData = GetDataFromPos( --rPos ); + if ( pData->bEnabled ) + { + USHORT n = pData->aText.Search( '~' ); + if ( n != STRING_NOTFOUND ) + { + xub_Unicode cCompareChar = pData->aText.GetChar( n+1 ); + cCompareChar = GetCharClass()->toUpper( String(cCompareChar), 0, 1, rLocale )[0]; + if ( cCompareChar == cCharCode ) + return pData; + } + } + } + + return 0; +} + +uno::Reference< lang::XCharacterClassification > MenuItemList::GetCharClass() const +{ + if ( !xCharClass.is() ) + ((MenuItemList*)this)->xCharClass = vcl::unohelper::CreateCharacterClassification(); + return xCharClass; +} + + + +class MenuFloatingWindow : public FloatingWindow +{ +private: + Menu* pMenu; + PopupMenu* pActivePopup; + Timer aHighlightChangedTimer; + Timer aScrollTimer; + ULONG nSaveFocusId; + long nStartY; + USHORT nHighlightedItem; // gehighlightetes/selektiertes Item + USHORT nMBDownPos; + USHORT nScrollerHeight; + USHORT nFirstEntry; + USHORT nBorder; + BOOL bInExecute; + + BOOL bScrollMenu; + BOOL bScrollUp; + BOOL bScrollDown; + + DECL_LINK( PopupEnd, FloatingWindow* ); + DECL_LINK( HighlightChanged, Timer* ); + DECL_LINK( AutoScroll, Timer* ); + + void StateChanged( StateChangedType nType ); + void DataChanged( const DataChangedEvent& rDCEvt ); + +protected: + Region ImplCalcClipRegion( BOOL bIncludeLogo = TRUE ) const; + void ImplInitClipRegion(); + void ImplDrawScroller( BOOL bUp ); + void ImplScroll( const Point& rMousePos ); + void ImplScroll( BOOL bUp ); + void ImplCursorUpDown( BOOL bUp ); + void ImplHighlightItem( const MouseEvent& rMEvt, BOOL bMBDown ); + +public: + MenuFloatingWindow( Menu* pMenu, Window* pParent, WinBits nStyle ); + ~MenuFloatingWindow(); + + virtual void MouseMove( const MouseEvent& rMEvt ); + virtual void MouseButtonDown( const MouseEvent& rMEvt ); + virtual void MouseButtonUp( const MouseEvent& rMEvt ); + virtual void KeyInput( const KeyEvent& rKEvent ); + virtual void Command( const CommandEvent& rCEvt ); + virtual void Paint( const Rectangle& rRect ); + virtual void RequestHelp( const HelpEvent& rHEvt ); + virtual void Resize(); + + void SetFocusId( ULONG nId ) { nSaveFocusId = nId; } + ULONG GetFocusId() const { return nSaveFocusId; } + + void EnableScrollMenu( BOOL b ); + BOOL IsScrollMenu() const { return bScrollMenu; } + USHORT GetScrollerHeight() const { return nScrollerHeight; } + + void Execute(); + void StopExecute( ULONG nFocusId = 0 ); + void EndExecute(); + void EndExecute( USHORT nSelectId ); + + PopupMenu* GetActivePopup() const { return pActivePopup; } + void KillActivePopup( PopupMenu* pThisOnly = NULL ); + + void HighlightItem( USHORT nPos, BOOL bHighlight ); + void ChangeHighlightItem( USHORT n, BOOL bStartPopupTimer ); +}; + + +// Eine Basicklasse fuer beide (wegen pActivePopup, Timer, ...) waere nett, +// aber dann musste eine 'Container'-Klasse gemacht werden, da von +// unterschiedlichen Windows abgeleitet... +// In den meisten Funktionen muessen dann sowieso Sonderbehandlungen fuer +// MenuBar, PopupMenu gemacht werden, also doch zwei verschiedene Klassen. + +class MenuBarWindow : public Window +{ + friend class MenuBar; + +private: + Menu* pMenu; + PopupMenu* pActivePopup; + USHORT nHighlightedItem; + ULONG nSaveFocusId; + BOOL mbAutoPopup; + + PushButton aCloser; + PushButton aFloatBtn; + PushButton aHideBtn; + + void HighlightItem( USHORT nPos, BOOL bHighlight ); + void ChangeHighlightItem( USHORT n, BOOL bSelectPopupEntry, BOOL bAllowRestoreFocus = TRUE ); + + USHORT ImplFindEntry( const Point& rMousePos ) const; + void ImplCreatePopup( BOOL bPreSelectFirst ); + BOOL ImplHandleKeyEvent( const KeyEvent& rKEvent, BOOL bFromMenu = TRUE ); + + DECL_LINK( CloserHdl, PushButton* ); + DECL_LINK( FloatHdl, PushButton* ); + DECL_LINK( HideHdl, PushButton* ); + + void StateChanged( StateChangedType nType ); + void DataChanged( const DataChangedEvent& rDCEvt ); + void LoseFocus(); + +public: + MenuBarWindow( Window* pParent ); + ~MenuBarWindow(); + + void ShowButtons( BOOL bClose, BOOL bFloat, BOOL bHide ); + + virtual void MouseMove( const MouseEvent& rMEvt ); + virtual void MouseButtonDown( const MouseEvent& rMEvt ); + virtual void MouseButtonUp( const MouseEvent& rMEvt ); + virtual void KeyInput( const KeyEvent& rKEvent ); + virtual void Paint( const Rectangle& rRect ); + virtual void Resize(); + virtual void RequestHelp( const HelpEvent& rHEvt ); + virtual BOOL QueryDrop( DropEvent& rDEvt ); + virtual BOOL Drop( const DropEvent& rDEvt ); + + void SetFocusId( ULONG nId ) { nSaveFocusId = nId; } + ULONG GetFocusId() const { return nSaveFocusId; } + + void SetMenu( MenuBar* pMenu ); + void KillActivePopup(); + PopupMenu* GetActivePopup() const { return pActivePopup; } + void PopupClosed( Menu* pMenu ); +}; + + + +static void ImplSetMenuItemData( MenuItemData* pData, USHORT nPos ) +{ + // Daten umsetzen + if ( !pData->aImage ) + pData->eType = MENUITEM_STRING; + else if ( !pData->aText.Len() ) + pData->eType = MENUITEM_IMAGE; + else + pData->eType = MENUITEM_STRINGIMAGE; +} + +static BOOL ImplHandleHelpEvent( Window* pMenuWindow, Menu* pMenu, USHORT nHighlightedItem, const HelpEvent& rHEvt ) +{ + BOOL bDone = FALSE; + USHORT nId = 0; + if ( nHighlightedItem != ITEMPOS_INVALID ) + { + MenuItemData* pItemData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); + if ( pItemData ) + nId = pItemData->nId; + } + + if ( ( rHEvt.GetMode() & HELPMODE_BALLOON ) && pMenuWindow ) + { + Point aPos = rHEvt.GetMousePosPixel(); + // Pos etwas nach unter-rechts korrigieren, wegen Pointer + aPos.X() += 15; +// aPos.Y() += 20; + Help::ShowBalloon( pMenuWindow, aPos, pMenu->GetHelpText( nId ) ); + bDone = TRUE; + } + else if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) ) + { + // Ist eine Hilfe in die Applikation selektiert + Help* pHelp = Application::GetHelp(); + if ( pHelp ) + { + // Ist eine ID vorhanden, dann Hilfe mit der ID aufrufen, sonst + // den Hilfe-Index + ULONG nHelpId = pMenu->GetHelpId( nId ); + if ( nHelpId ) + pHelp->Start( nHelpId ); + else + pHelp->Start( HELP_INDEX ); + } + bDone = TRUE; + } + return bDone; +} + + +Menu::Menu() +{ + DBG_CTOR( Menu, NULL ); + ImplInit(); +} + +Menu::~Menu() +{ + DBG_DTOR( Menu, NULL ); + + if ( nEventId ) + Application::RemoveUserEvent( nEventId ); + + bKilled = TRUE; + + delete pItemList; + delete pLogo; +} + +void Menu::ImplInit() +{ + nMenuFlags = 0; + nDefaultItem = 0; + bIsMenuBar = FALSE; + nSelectedId = 0; + pItemList = new MenuItemList; + pLogo = NULL; + pStartedFrom = NULL; + pWindow = NULL; + nEventId = 0; + bCanceled = FALSE; + bInCallback = FALSE; + bKilled = FALSE; +} + +void Menu::ImplLoadRes( const ResId& rResId ) +{ + rResId.SetRT( RSC_MENU ); + GetRes( rResId ); + + USHORT nObjMask = ReadShortRes(); + + if( nObjMask & RSC_MENU_ITEMS ) + { + USHORT nObjFollows = ReadShortRes(); + // MenuItems einfuegen + for( USHORT i = 0; i < nObjFollows; i++ ) + { + InsertItem( ResId( (RSHEADER_TYPE*)GetClassRes() ) ); + IncrementRes( GetObjSizeRes( (RSHEADER_TYPE*)GetClassRes() ) ); + } + } + + if( nObjMask & RSC_MENU_TEXT ) + { + if( bIsMenuBar ) // Kein Titel im Menubar + ReadStringRes(); + else + aTitleText = ReadStringRes(); + } + if( nObjMask & RSC_MENU_DEFAULTITEMID ) + SetDefaultItem( ReadShortRes() ); +} + +void Menu::Activate() +{ + bInCallback = TRUE; + if ( !aActivateHdl.Call( this ) ) + { + Menu* pStartMenu = ImplGetStartMenu(); + if ( pStartMenu && ( pStartMenu != this ) ) + { + pStartMenu->bInCallback = TRUE; + pStartMenu->aActivateHdl.Call( this ); + pStartMenu->bInCallback = FALSE; + } + } + bInCallback = FALSE; +} + +void Menu::Deactivate() +{ + for ( USHORT n = pItemList->Count(); n; ) + { + MenuItemData* pData = pItemList->GetDataFromPos( --n ); + if ( pData->bIsTemporary ) + pItemList->Remove( n ); + } + + bInCallback = TRUE; + Menu* pStartMenu = ImplGetStartMenu(); + if ( !aDeactivateHdl.Call( this ) ) + { + if ( pStartMenu && ( pStartMenu != this ) ) + { + pStartMenu->bInCallback = TRUE; + pStartMenu->aDeactivateHdl.Call( this ); + pStartMenu->bInCallback = FALSE; + } + } + bInCallback = FALSE; + + if ( this == pStartMenu ) + GetpApp()->HideHelpStatusText(); +} + +void Menu::Highlight() +{ + Menu* pStartMenu = ImplGetStartMenu(); + if ( !aHighlightHdl.Call( this ) ) + { + if ( pStartMenu && ( pStartMenu != this ) ) + pStartMenu->aHighlightHdl.Call( this ); + } + + if ( GetCurItemId() ) + GetpApp()->ShowHelpStatusText( GetHelpText( GetCurItemId() ) ); +} + +void Menu::ImplSelect() +{ + MenuItemData* pData = GetItemList()->GetData( nSelectedId ); + if ( pData && (pData->nBits & MIB_AUTOCHECK) ) + { + BOOL bChecked = IsItemChecked( nSelectedId ); + if ( pData->nBits & MIB_RADIOCHECK ) + { + if ( !bChecked ) + CheckItem( nSelectedId, TRUE ); + } + else + CheckItem( nSelectedId, !bChecked ); + } + + // Select rufen + ImplSVData* pSVData = ImplGetSVData(); + pSVData->maAppData.mpActivePopupMenu = NULL; // Falls neues Execute im Select() + Application::PostUserEvent( nEventId, LINK( this, Menu, ImplCallSelect ) ); +} + +void Menu::Select() +{ + if ( !aSelectHdl.Call( this ) ) + { + Menu* pStartMenu = ImplGetStartMenu(); + if ( pStartMenu && ( pStartMenu != this ) ) + { + pStartMenu->nSelectedId = nSelectedId; + pStartMenu->aSelectHdl.Call( this ); + } + } +} + +void Menu::RequestHelp( const HelpEvent& rHEvt ) +{ +} + +void Menu::InsertItem( USHORT nItemId, const XubString& rStr, MenuItemBits nItemBits, USHORT nPos ) +{ + DBG_ASSERT( nItemId, "Menu::InsertItem(): ItemId == 0" ); + DBG_ASSERT( GetItemPos( nItemId ) == MENU_ITEM_NOTFOUND, + "Menu::InsertItem(): ItemId already exists" ); + + // Falls Position > ItemCount, dann anheangen + if ( nPos >= (USHORT)pItemList->Count() ) + nPos = MENU_APPEND; + + // Item in die MenuItemListe aufnehmen + MenuItemData* pData = pItemList->Insert( nItemId, MENUITEM_STRING, + nItemBits, rStr, Image(), this, nPos ); + + Window* pWin = ImplGetWindow(); + if ( pWin ) + { + ImplCalcSize( pWin ); + if ( pWin->IsVisible() ) + pWin->Invalidate(); + } +} + +void Menu::InsertItem( USHORT nItemId, const Image& rImage, + MenuItemBits nItemBits, USHORT nPos ) +{ + InsertItem( nItemId, ImplGetSVEmptyStr(), nItemBits, nPos ); + SetItemImage( nItemId, rImage ); +} + +void Menu::InsertItem( USHORT nItemId, + const XubString& rStr, const Image& rImage, + MenuItemBits nItemBits, USHORT nPos ) +{ + InsertItem( nItemId, rStr, nItemBits, nPos ); + SetItemImage( nItemId, rImage ); +} + +void Menu::InsertItem( const ResId& rResId, USHORT nPos ) +{ + USHORT nObjMask; + + GetRes( rResId.SetRT( RSC_MENUITEM ) ); + nObjMask = ReadShortRes(); + + BOOL bSep = FALSE; + if ( nObjMask & RSC_MENUITEM_SEPARATOR ) + bSep = (BOOL)ReadShortRes(); + + USHORT nItemId = 1; + if ( nObjMask & RSC_MENUITEM_ID ) + nItemId = ReadShortRes(); + + USHORT nStatus = 0; + if ( nObjMask & RSC_MENUITEM_STATUS ) + nStatus = ReadShortRes(); + + String aText; + if ( nObjMask & RSC_MENUITEM_TEXT ) + aText = ReadStringRes(); + + // Item erzeugen + if ( nObjMask & RSC_MENUITEM_BITMAP ) + { + if ( !bSep ) + { + Bitmap aBmp( ResId( (RSHEADER_TYPE*)GetClassRes() ) ); + if ( aText.Len() ) + InsertItem( nItemId, aText, aBmp, nStatus, nPos ); + else + InsertItem( nItemId, aBmp, nStatus, nPos ); + } + IncrementRes( GetObjSizeRes( (RSHEADER_TYPE*)GetClassRes() ) ); + } + else if ( !bSep ) + InsertItem( nItemId, aText, nStatus, nPos ); + if ( bSep ) + InsertSeparator( nPos ); + + String aHelpText; + if ( nObjMask & RSC_MENUITEM_HELPTEXT ) + { + aHelpText = ReadStringRes(); + if( !bSep ) + SetHelpText( nItemId, aHelpText ); + } + + ULONG nHelpId = 0; + if ( nObjMask & RSC_MENUITEM_HELPID ) + { + nHelpId = ReadLongRes(); + if ( !bSep ) + SetHelpId( nItemId, nHelpId ); + } + + if( !bSep /* && SvHelpSettings::HelpText( aHelpText, nHelpId ) */ ) + SetHelpText( nItemId, aHelpText ); + + if ( nObjMask & RSC_MENUITEM_KEYCODE ) + { + if ( !bSep ) + SetAccelKey( nItemId, KeyCode( ResId( (RSHEADER_TYPE*)GetClassRes() ) ) ); + IncrementRes( GetObjSizeRes( (RSHEADER_TYPE*)GetClassRes() ) ); + } + if( nObjMask & RSC_MENUITEM_CHECKED ) + { + if ( !bSep ) + CheckItem( nItemId, (BOOL)ReadShortRes() ); + } + if ( nObjMask & RSC_MENUITEM_DISABLE ) + { + if ( !bSep ) + EnableItem( nItemId, !(BOOL)ReadShortRes() ); + } + if ( nObjMask & RSC_MENUITEM_COMMAND ) + { + String aCommandStr = ReadStringRes(); + if ( !bSep ) + SetItemCommand( nItemId, aCommandStr ); + } + if ( nObjMask & RSC_MENUITEM_MENU ) + { + if ( !bSep ) + { + MenuItemData* pData = GetItemList()->GetData( nItemId ); + if ( pData ) + { + PopupMenu* pSubMenu = new PopupMenu( ResId( (RSHEADER_TYPE*)GetClassRes() ) ); + pData->pAutoSubMenu = pSubMenu; + SetPopupMenu( nItemId, pSubMenu ); + } + } + IncrementRes( GetObjSizeRes( (RSHEADER_TYPE*)GetClassRes() ) ); + } +} + +void Menu::InsertSeparator( USHORT nPos ) +{ + // Handelt es sich um einen MenuBar, dann mache nichts + if ( bIsMenuBar ) + return; + + // Falls Position > ItemCount, dann anheangen + if ( nPos >= (USHORT)pItemList->Count() ) + nPos = MENU_APPEND; + + // Separator in die Item-Liste einfuegen + pItemList->InsertSeparator( nPos ); +} + +void Menu::RemoveItem( USHORT nPos ) +{ + if ( nPos < GetItemCount() ) + pItemList->Remove( nPos ); + + Window* pWin = ImplGetWindow(); + if ( pWin ) + { + ImplCalcSize( pWin ); + if ( pWin->IsVisible() ) + pWin->Invalidate(); + } +} + +void ImplCopyItem( Menu* pThis, const Menu& rMenu, USHORT nPos, USHORT nNewPos, + USHORT nMode = 0 ) +{ + MenuItemType eType = rMenu.GetItemType( nPos ); + + if ( eType == MENUITEM_DONTKNOW ) + return; + + if ( eType == MENUITEM_SEPARATOR ) + pThis->InsertSeparator( nNewPos ); + else + { + USHORT nId = rMenu.GetItemId( nPos ); + + DBG_ASSERT( pThis->GetItemPos( nId ) == MENU_ITEM_NOTFOUND, + "Menu::CopyItem(): ItemId already exists" ); + + MenuItemData* pData = rMenu.GetItemList()->GetData( nId ); + + if ( eType == MENUITEM_STRINGIMAGE ) + pThis->InsertItem( nId, pData->aText, pData->aImage, pData->nBits, nNewPos ); + else if ( eType == MENUITEM_STRING ) + pThis->InsertItem( nId, pData->aText, pData->nBits, nNewPos ); + else + pThis->InsertItem( nId, pData->aImage, pData->nBits, nNewPos ); + + if ( rMenu.IsItemChecked( nId ) ) + pThis->CheckItem( nId, TRUE ); + if ( !rMenu.IsItemEnabled( nId ) ) + pThis->EnableItem( nId, FALSE ); + pThis->SetHelpId( nId, pData->nHelpId ); + pThis->SetHelpText( nId, pData->aHelpText ); + pThis->SetAccelKey( nId, pData->aAccelKey ); + + PopupMenu* pSubMenu = rMenu.GetPopupMenu( nId ); + if ( pSubMenu ) + { + // AutoKopie anlegen + if ( nMode == 1 ) + { + PopupMenu* pNewMenu = new PopupMenu( *pSubMenu ); + pThis->SetPopupMenu( nId, pNewMenu ); +// SetAutoMenu( pThis, nId, pNewMenu ); + } + else + pThis->SetPopupMenu( nId, pSubMenu ); + } + } +} + +void Menu::CopyItem( const Menu& rMenu, USHORT nPos, USHORT nNewPos ) +{ + ImplCopyItem( this, rMenu, nPos, nNewPos ); +} + +void Menu::Clear() +{ + for ( USHORT i = GetItemCount(); i; i-- ) + RemoveItem( 0 ); +} + +USHORT Menu::GetItemCount() const +{ + return (USHORT)pItemList->Count(); +} + +USHORT Menu::ImplGetVisibleItemCount() const +{ + USHORT nItems = 0; + for ( USHORT n = (USHORT)pItemList->Count(); n; ) + { + if ( ImplIsVisible( --n ) ) + nItems++; + } + return nItems; +} + +USHORT Menu::GetItemId( USHORT nPos ) const +{ + MenuItemData* pData = pItemList->GetDataFromPos( nPos ); + + if ( pData ) + return pData->nId; + else + return 0; +} + +USHORT Menu::GetItemPos( USHORT nItemId ) const +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( pData ) + return nPos; + else + return MENU_ITEM_NOTFOUND; +} + +MenuItemType Menu::GetItemType( USHORT nPos ) const +{ + MenuItemData* pData = pItemList->GetDataFromPos( nPos ); + + if ( pData ) + return pData->eType; + else + return MENUITEM_DONTKNOW; +} + +USHORT Menu::GetCurItemId() const +{ + return nSelectedId; +} + +void Menu::SetItemBits( USHORT nItemId, MenuItemBits nBits ) +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + if ( pData ) + pData->nBits = nBits; +} + +MenuItemBits Menu::GetItemBits( USHORT nItemId ) const +{ + MenuItemBits nBits = 0; + MenuItemData* pData = pItemList->GetData( nItemId ); + if ( pData ) + nBits = pData->nBits; + return nBits; +} + +void Menu::SetPopupMenu( USHORT nItemId, PopupMenu* pMenu ) +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + // Item nicht vorhanden, dann NULL zurueckgeben + if ( !pData ) + return; + + // Gleiches Menu, danmn brauchen wir nichts machen + if ( (PopupMenu*)pData->pSubMenu == pMenu ) + return; + + // Daten austauschen + pData->pSubMenu = pMenu; +} + +PopupMenu* Menu::GetPopupMenu( USHORT nItemId ) const +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + return (PopupMenu*)(pData->pSubMenu); + else + return NULL; +} + +void Menu::SetAccelKey( USHORT nItemId, const KeyCode& rKeyCode ) +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( !pData ) + return; + + if ( pData->aAccelKey == rKeyCode ) + return; + + pData->aAccelKey = rKeyCode; +} + +KeyCode Menu::GetAccelKey( USHORT nItemId ) const +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + return pData->aAccelKey; + else + return KeyCode(); +} + +void Menu::CheckItem( USHORT nItemId, BOOL bCheck ) +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( !pData ) + return; + + // Wenn RadioCheck, dann vorherigen unchecken + if ( bCheck && (pData->nBits & MIB_AUTOCHECK) && + (pData->nBits & MIB_RADIOCHECK) ) + { + MenuItemData* pGroupData; + USHORT nGroupPos; + USHORT nItemCount = GetItemCount(); + BOOL bFound = FALSE; + + nGroupPos = nPos; + while ( nGroupPos ) + { + pGroupData = pItemList->GetDataFromPos( nGroupPos-1 ); + if ( pGroupData->nBits & MIB_RADIOCHECK ) + { + if ( IsItemChecked( pGroupData->nId ) ) + { + CheckItem( pGroupData->nId, FALSE ); + bFound = TRUE; + break; + } + } + else + break; + nGroupPos--; + } + + if ( !bFound ) + { + nGroupPos = nPos+1; + while ( nGroupPos < nItemCount ) + { + pGroupData = pItemList->GetDataFromPos( nGroupPos ); + if ( pGroupData->nBits & MIB_RADIOCHECK ) + { + if ( IsItemChecked( pGroupData->nId ) ) + { + CheckItem( pGroupData->nId, FALSE ); + break; + } + } + else + break; + nGroupPos++; + } + } + } + + pData->bChecked = bCheck; +} + +BOOL Menu::IsItemChecked( USHORT nItemId ) const +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( !pData ) + return FALSE; + + return pData->bChecked; +} + +void Menu::EnableItem( USHORT nItemId, BOOL bEnable ) +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( pData && ( pData->bEnabled != bEnable ) ) + { + pData->bEnabled = bEnable; + + Window* pWin = ImplGetWindow(); + if ( pWin && pWin->IsVisible() ) + { + DBG_ASSERT( bIsMenuBar, "Menu::EnableItem - Popup visible!" ); + long nX = 0; + ULONG nCount = pItemList->Count(); + for ( ULONG n = 0; n < nCount; n++ ) + { + MenuItemData* pData = pItemList->GetDataFromPos( n ); + if ( n == nPos ) + { + pWin->Invalidate( Rectangle( Point( nX, 0 ), Size( pData->aSz.Width(), pData->aSz.Height() ) ) ); + break; + } + nX += pData->aSz.Width(); + } + } + } +} + +BOOL Menu::IsItemEnabled( USHORT nItemId ) const +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( !pData ) + return FALSE; + + return pData->bEnabled; +} + +void Menu::SetItemText( USHORT nItemId, const XubString& rStr ) +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( !pData ) + return; + + pData->aText = rStr; + ImplSetMenuItemData( pData, nPos ); +} + +XubString Menu::GetItemText( USHORT nItemId ) const +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( pData ) + return pData->aText; + else + return ImplGetSVEmptyStr(); +} + +void Menu::SetItemImage( USHORT nItemId, const Image& rImage ) +{ + USHORT nPos; + MenuItemData* pData = pItemList->GetData( nItemId, nPos ); + + if ( !pData ) + return; + + pData->aImage = rImage; + ImplSetMenuItemData( pData, nPos ); +} + +Image Menu::GetItemImage( USHORT nItemId ) const +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + return pData->aImage; + else + return Image(); +} + +void Menu::SetItemCommand( USHORT nItemId, const String& rCommand ) +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + pData->aCommandStr = rCommand; +} + +const XubString& Menu::GetItemCommand( USHORT nItemId ) const +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + return pData->aCommandStr; + else + return ImplGetSVEmptyStr(); +} + +void Menu::SetHelpText( USHORT nItemId, const XubString& rStr ) +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + pData->aHelpText = rStr; +} + +const XubString& Menu::GetHelpText( USHORT nItemId ) const +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + { + if ( !pData->aHelpText.Len() && pData->nHelpId ) + { + Help* pHelp = Application::GetHelp(); + if ( pHelp ) + pData->aHelpText = pHelp->GetHelpText( pData->nHelpId ); + } + + return pData->aHelpText; + } + else + return ImplGetSVEmptyStr(); +} + +void Menu::SetHelpId( USHORT nItemId, ULONG nHelpId ) +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + pData->nHelpId = nHelpId; +} + +ULONG Menu::GetHelpId( USHORT nItemId ) const +{ + MenuItemData* pData = pItemList->GetData( nItemId ); + + if ( pData ) + return pData->nHelpId; + else + return 0; +} + +Menu& Menu::operator=( const Menu& rMenu ) +{ + // Aufraeumen + Clear(); + + // Items kopieren + USHORT nCount = rMenu.GetItemCount(); + for ( USHORT i = 0; i < nCount; i++ ) + ImplCopyItem( this, rMenu, i, MENU_APPEND, 1 ); + + nDefaultItem = rMenu.nDefaultItem; + aActivateHdl = rMenu.aActivateHdl; + aDeactivateHdl = rMenu.aDeactivateHdl; + aHighlightHdl = rMenu.aHighlightHdl; + aSelectHdl = rMenu.aSelectHdl; + aTitleText = rMenu.aTitleText; + bIsMenuBar = rMenu.bIsMenuBar; + + return *this; +} + +BOOL Menu::ImplIsVisible( USHORT nPos ) const +{ + BOOL bVisible = TRUE; + + // Fuer den Menubar nicht erlaubt, weil ich nicht mitbekomme + // ob dadurch ein Eintrag verschwindet oder wieder da ist. + if ( !bIsMenuBar && ( nMenuFlags & MENU_FLAG_HIDEDISABLEDENTRIES ) ) + { + MenuItemData* pData = pItemList->GetDataFromPos( nPos ); + if ( pData->eType != MENUITEM_SEPARATOR ) + { + // bVisible = pData->bEnabled && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( TRUE ) ); + bVisible = pData->bEnabled; // SubMenus nicht pruefen, weil sie ggf. erst im Activate() gefuellt werden. + } + else + { + // Ein Separator ist nur dann visible, wenn davor sichtbare Eintraege stehen. + USHORT nCount = (USHORT) pItemList->Count(); + USHORT n; + BOOL bPrevVisible = FALSE; + BOOL bNextVisible = FALSE; + for ( n = nPos; !bPrevVisible && n; ) + { + pData = pItemList->GetDataFromPos( --n ); + if ( pData->eType != MENUITEM_SEPARATOR ) + bPrevVisible = pData->bEnabled; // && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( TRUE ) ); + else + break; + } + if ( bPrevVisible ) + { + for ( n = nPos+1; !bNextVisible && ( n < nCount ); n++ ) + { + pData = pItemList->GetDataFromPos( n ); + if ( pData->eType != MENUITEM_SEPARATOR ) + bNextVisible = pData->bEnabled; // && ( !pData->pSubMenu || pData->pSubMenu->HasValidEntries( TRUE ) ); + // nicht beim naechsten Separator abbrechen... + } + } + bVisible = bPrevVisible && bNextVisible; + } + } + + return bVisible; +} + +Size Menu::ImplCalcSize( Window* pWin ) +{ + // | Checked| Image| Text| Accel/Popup| + + // Fuer Symbole: nFontHeight x nFontHeight + long nFontHeight = pWin->GetTextHeight(); + long nExtra = nFontHeight/4; + + Size aSz; + Size aMaxImgSz; + long nMaxTextWidth = 0; + long nMaxAccWidth = 0; + + for ( USHORT n = (USHORT)pItemList->Count(); n; ) + { + MenuItemData* pData = pItemList->GetDataFromPos( --n ); + + pData->aSz.Height() = 0; + pData->aSz.Width() = 0; + + if ( ImplIsVisible( n ) ) + { + + // Separator + if ( !bIsMenuBar && ( pData->eType == MENUITEM_SEPARATOR ) ) + { + DBG_ASSERT( !bIsMenuBar, "Separator in MenuBar ?! " ); + pData->aSz.Height() = 4; + } + + // Image: + if ( !bIsMenuBar && ( ( pData->eType == MENUITEM_IMAGE ) || ( pData->eType == MENUITEM_STRINGIMAGE ) ) ) + { + Size aImgSz = pData->aImage.GetSizePixel(); + if ( aImgSz.Width() > aMaxImgSz.Width() ) + aMaxImgSz.Width() = aImgSz.Width(); + if ( aImgSz.Height() > aMaxImgSz.Height() ) + aMaxImgSz.Height() = aImgSz.Height(); + if ( aImgSz.Height() > pData->aSz.Height() ) + pData->aSz.Height() = aImgSz.Height(); + } + + // Text: + if ( (pData->eType == MENUITEM_STRING) || (pData->eType == MENUITEM_STRINGIMAGE) ) + { + long nTextWidth = pWin->GetCtrlTextWidth( pData->aText ); + if ( nTextWidth > nMaxTextWidth ) + nMaxTextWidth = nTextWidth; + long nTextHeight = pWin->GetTextHeight(); + if ( nTextHeight > pData->aSz.Height() ) + pData->aSz.Height() = nTextHeight; + + if ( bIsMenuBar ) + { + pData->aSz.Width() = nTextWidth + 4*nExtra; + aSz.Width() += pData->aSz.Width(); + } + } + + // Accel + if ( !bIsMenuBar && pData->aAccelKey.GetCode() ) + { + String aName = pData->aAccelKey.GetName(); + long nAccWidth = pWin->GetTextWidth( aName ); + nAccWidth += nExtra; + if ( nAccWidth > nMaxAccWidth ) + nMaxAccWidth = nAccWidth; + } + + // SubMenu? + if ( !bIsMenuBar && pData->pSubMenu ) + { + if ( nFontHeight > nMaxAccWidth ) + nMaxAccWidth = nFontHeight; + if ( nFontHeight > pData->aSz.Height() ) + pData->aSz.Height() = nFontHeight; + } + + pData->aSz.Height() += EXTRAITEMHEIGHT; // Etwas mehr Abstand: + + if ( !bIsMenuBar ) + aSz.Height() += (long)pData->aSz.Height(); + } + } + + if ( !bIsMenuBar ) + { + nCheckPos = (USHORT)nExtra; + nImagePos = (USHORT)(nCheckPos + nFontHeight/2 + nExtra ); + nTextPos = (USHORT)(nImagePos+aMaxImgSz.Width()); + if ( aMaxImgSz.Width() ) + nTextPos += (USHORT)nExtra; + + aSz.Width() = nTextPos + nMaxTextWidth + nExtra + nMaxAccWidth; + aSz.Width() += 10*nExtra; // etwas mehr... + } + else + { + nTextPos = (USHORT)(2*nExtra); + aSz.Height() = nFontHeight+6; + } + + if ( pLogo ) + aSz.Width() += pLogo->aBitmap.GetSizePixel().Width(); + + return aSz; +} + +void Menu::ImplPaint( Window* pWin, USHORT nBorder, long nStartY, MenuItemData* pThisItemOnly, BOOL bHighlighted ) +{ + // Fuer Symbole: nFontHeight x nFontHeight + long nFontHeight = pWin->GetTextHeight(); + long nExtra = nFontHeight/4; + + DecorationView aDecoView( pWin ); + const StyleSettings& rSettings = pWin->GetSettings().GetStyleSettings(); + + Point aTopLeft, aTmpPos; + + if ( pLogo ) + aTopLeft.X() = pLogo->aBitmap.GetSizePixel().Width(); + + Size aOutSz = pWin->GetOutputSizePixel(); + long nMaxY = aOutSz.Height() - nBorder; + USHORT nCount = (USHORT)pItemList->Count(); + for ( USHORT n = 0; n < nCount; n++ ) + { + MenuItemData* pData = pItemList->GetDataFromPos( n ); + if ( ImplIsVisible( n ) && ( !pThisItemOnly || ( pData == pThisItemOnly ) ) ) + { + if ( pThisItemOnly && bHighlighted ) + pWin->SetTextColor( rSettings.GetMenuHighlightTextColor() ); + + Point aPos( aTopLeft ); + aPos.Y() += nBorder; + aPos.Y() += nStartY; + + if ( aPos.Y() >= 0 ) + { + long nTextOffsetY = ((pData->aSz.Height()-nFontHeight)/2); + USHORT nTextStyle = 0; + USHORT nSymbolStyle = 0; + USHORT nImageStyle = 0; + // SubMenus ohne Items werden nicht mehr disablte dargestellt, + // wenn keine Items enthalten sind, da die Anwendung selber + // darauf achten muss. Ansonsten gibt es Faelle, wo beim + // asyncronen laden die Eintraege disablte dargestellt werden. + if ( !pData->bEnabled ) + { + nTextStyle |= TEXT_DRAW_DISABLE; + nSymbolStyle |= SYMBOL_DRAW_DISABLE; + nImageStyle |= IMAGE_DRAW_DISABLE; + } + + // Separator + if ( !bIsMenuBar && ( pData->eType == MENUITEM_SEPARATOR ) ) + { + aTmpPos.Y() = aPos.Y() + ((pData->aSz.Height()-2)/2); + aTmpPos.X() = aPos.X() + 1; + pWin->SetLineColor( rSettings.GetShadowColor() ); + pWin->DrawLine( aTmpPos, Point( aOutSz.Width() - 1, aTmpPos.Y() ) ); + aTmpPos.Y()++; + pWin->SetLineColor( rSettings.GetLightColor() ); + pWin->DrawLine( aTmpPos, Point( aOutSz.Width() - 1, aTmpPos.Y() ) ); + pWin->SetLineColor(); + } + + // Image: + if ( !bIsMenuBar && ( ( pData->eType == MENUITEM_IMAGE ) || ( pData->eType == MENUITEM_STRINGIMAGE ) ) ) + { + aTmpPos.Y() = aPos.Y(); + aTmpPos.X() = aPos.X() + nImagePos; + aTmpPos.Y() += (pData->aSz.Height()-pData->aImage.GetSizePixel().Height())/2; + pWin->DrawImage( aTmpPos, pData->aImage, nImageStyle ); + } + + // Text: + if ( ( pData->eType == MENUITEM_STRING ) || ( pData->eType == MENUITEM_STRINGIMAGE ) ) + { + aTmpPos.X() = aPos.X() + nTextPos; + aTmpPos.Y() = aPos.Y(); + aTmpPos.Y() += nTextOffsetY; + USHORT nStyle = nTextStyle|TEXT_DRAW_MNEMONIC; + if ( pData->bIsTemporary ) + nStyle |= TEXT_DRAW_DISABLE; + pWin->DrawCtrlText( aTmpPos, pData->aText, 0, pData->aText.Len(), nStyle ); + } + + // Accel + if ( !bIsMenuBar && pData->aAccelKey.GetCode() ) + { + XubString aAccText = pData->aAccelKey.GetName(); + aTmpPos.X() = aOutSz.Width() - pWin->GetTextWidth( aAccText ); + aTmpPos.X() -= 3*nExtra; + aTmpPos.Y() = aPos.Y(); + aTmpPos.Y() += nTextOffsetY; + pWin->DrawCtrlText( aTmpPos, aAccText, 0, aAccText.Len(), nTextStyle ); + } + + // CheckMark + if ( !bIsMenuBar && pData->bChecked ) + { + Rectangle aRect; + SymbolType eSymbol; + aTmpPos.Y() = aPos.Y(); + aTmpPos.Y() += nExtra/2; + aTmpPos.Y() += pData->aSz.Height() / 2; + if ( pData->nBits & MIB_RADIOCHECK ) + { + aTmpPos.X() = aPos.X() + nCheckPos; + eSymbol = SYMBOL_RADIOCHECKMARK; + aTmpPos.Y() -= nFontHeight/4; + aRect = Rectangle( aTmpPos, Size( nFontHeight/2, nFontHeight/2 ) ); + } + else + { + aTmpPos.X() = aPos.X() + nCheckPos; + eSymbol = SYMBOL_CHECKMARK; + aTmpPos.Y() -= nFontHeight/4; + aRect = Rectangle( aTmpPos, Size( (nFontHeight*25)/40, nFontHeight/2 ) ); + } + aDecoView.DrawSymbol( aRect, eSymbol, pWin->GetTextColor(), nSymbolStyle ); + } + + // SubMenu? + if ( !bIsMenuBar && pData->pSubMenu ) + { + aTmpPos.X() = aOutSz.Width() - nFontHeight + nExtra; + aTmpPos.Y() = aPos.Y(); + aTmpPos.Y() += nExtra/2; + aTmpPos.Y() += ( pData->aSz.Height() / 2 ) - ( nFontHeight/4 ); + if ( pData->nBits & MIB_POPUPSELECT ) + { + pWin->SetTextColor( rSettings.GetMenuTextColor() ); + Point aTmpPos2( aPos ); + aTmpPos2.X() = aOutSz.Width() - nFontHeight - nFontHeight/4; + aDecoView.DrawFrame( + Rectangle( aTmpPos2, Size( nFontHeight+nFontHeight/4, pData->aSz.Height() ) ), FRAME_DRAW_GROUP ); + } + aDecoView.DrawSymbol( + Rectangle( aTmpPos, Size( nFontHeight/2, nFontHeight/2 ) ), + SYMBOL_SPIN_RIGHT, pWin->GetTextColor(), nSymbolStyle ); +// if ( pData->nBits & MIB_POPUPSELECT ) +// { +// aTmpPos.Y() += nFontHeight/2 ; +// pWin->SetLineColor( rSettings.GetShadowColor() ); +// pWin->DrawLine( aTmpPos, Point( aTmpPos.X() + nFontHeight/3, aTmpPos.Y() ) ); +// pWin->SetLineColor( rSettings.GetLightColor() ); +// aTmpPos.Y()++; +// pWin->DrawLine( aTmpPos, Point( aTmpPos.X() + nFontHeight/3, aTmpPos.Y() ) ); +// pWin->SetLineColor(); +// } + } + + if ( pThisItemOnly && bHighlighted ) + pWin->SetTextColor( rSettings.GetMenuTextColor() ); + } + } + + if ( !bIsMenuBar ) + { + aTopLeft.Y() += pData->aSz.Height(); + } + else + { + aTopLeft.X() += pData->aSz.Width(); + } + } + + if ( !pThisItemOnly && pLogo ) + { + Size aLogoSz = pLogo->aBitmap.GetSizePixel(); + + Rectangle aRect( Point( 0, 0 ), Point( aLogoSz.Width()-1, aOutSz.Height() ) ); + if ( pWin->GetColorCount() >= 256 ) + { + Gradient aGrad( GRADIENT_LINEAR, pLogo->aStartColor, pLogo->aEndColor ); + aGrad.SetAngle( 1800 ); + aGrad.SetBorder( 15 ); + pWin->DrawGradient( aRect, aGrad ); + } + else + { + pWin->SetFillColor( pLogo->aStartColor ); + pWin->DrawRect( aRect ); + } + + Point aLogoPos( 0, aOutSz.Height() - aLogoSz.Height() ); + pLogo->aBitmap.Draw( pWin, aLogoPos ); + } +} + +Menu* Menu::ImplGetStartMenu() +{ + Menu* pStart = this; + while ( pStart && pStart->pStartedFrom && ( pStart->pStartedFrom != pStart ) ) + pStart = pStart->pStartedFrom; + return pStart; +} + +void Menu::ImplCallHighlight( USHORT nHighlightedItem ) +{ + nSelectedId = 0; + MenuItemData* pData = pItemList->GetDataFromPos( nHighlightedItem ); + if ( pData ) + nSelectedId = pData->nId; + Highlight(); + nSelectedId = 0; +} + +IMPL_LINK( Menu, ImplCallSelect, Menu*, EMPTYARG ) +{ + nEventId = 0; + Select(); + return 0; +} + +Menu* Menu::ImplFindSelectMenu() +{ + Menu* pSelMenu = nEventId ? this : NULL; + + for ( ULONG n = GetItemList()->Count(); n && !pSelMenu; ) + { + MenuItemData* pData = GetItemList()->GetDataFromPos( --n ); + + if ( pData->pSubMenu ) + pSelMenu = pData->pSubMenu->ImplFindSelectMenu(); + } + + return pSelMenu; +} + +void Menu::RemoveDisabledEntries( BOOL bCheckPopups, BOOL bRemoveEmptyPopups ) +{ + for ( USHORT n = 0; n < GetItemCount(); n++ ) + { + BOOL bRemove = FALSE; + MenuItemData* pItem = pItemList->GetDataFromPos( n ); + if ( pItem->eType == MENUITEM_SEPARATOR ) + { + if ( !n || ( GetItemType( n-1 ) == MENUITEM_SEPARATOR ) ) + bRemove = TRUE; + } + else + bRemove = !pItem->bEnabled; + + if ( bCheckPopups && pItem->pSubMenu ) + { + pItem->pSubMenu->RemoveDisabledEntries( TRUE ); + if ( bRemoveEmptyPopups && !pItem->pSubMenu->GetItemCount() ) + bRemove = TRUE; + } + + if ( bRemove ) + RemoveItem( n-- ); + } + + if ( GetItemCount() ) + { + USHORT nLast = GetItemCount() - 1; + MenuItemData* pItem = pItemList->GetDataFromPos( nLast ); + if ( pItem->eType == MENUITEM_SEPARATOR ) + RemoveItem( nLast ); + } +} + +BOOL Menu::HasValidEntries( BOOL bCheckPopups ) +{ + BOOL bValidEntries = FALSE; + USHORT nCount = GetItemCount(); + for ( USHORT n = 0; !bValidEntries && ( n < nCount ); n++ ) + { + MenuItemData* pItem = pItemList->GetDataFromPos( n ); + if ( pItem->bEnabled && ( pItem->eType != MENUITEM_SEPARATOR ) ) + { + if ( bCheckPopups && pItem->pSubMenu ) + bValidEntries = pItem->pSubMenu->HasValidEntries( TRUE ); + else + bValidEntries = TRUE; + } + } + return bValidEntries; +} + +void Menu::SetLogo( const MenuLogo& rLogo ) +{ + delete pLogo; + pLogo = new MenuLogo( rLogo ); +} + +void Menu::SetLogo() +{ + delete pLogo; + pLogo = NULL; +} + +MenuLogo Menu::GetLogo() const +{ + MenuLogo aLogo; + if ( pLogo ) + aLogo = *pLogo; + return aLogo; +} + +void Menu::GetAccessObject( AccessObjectRef& rAcc ) const +{ + rAcc = new AccessObject( (void*) this, bIsMenuBar? ACCESS_TYPE_MENUBAR : ACCESS_TYPE_MENU ); +} + +// ----------- +// - MenuBar - +// ----------- + +MenuBar::MenuBar() +{ + bIsMenuBar = TRUE; + mbCloserVisible = FALSE; + mbFloatBtnVisible = FALSE; + mbHideBtnVisible = FALSE; +} + +MenuBar::MenuBar( const MenuBar& rMenu ) +{ + bIsMenuBar = TRUE; + mbCloserVisible = FALSE; + mbFloatBtnVisible = FALSE; + mbHideBtnVisible = FALSE; + *this = rMenu; + bIsMenuBar = TRUE; +} + +MenuBar::MenuBar( const ResId& rResId ) +{ + bIsMenuBar = TRUE; + mbCloserVisible = FALSE; + mbFloatBtnVisible = FALSE; + mbHideBtnVisible = FALSE; + ImplLoadRes( rResId ); +} + +MenuBar::~MenuBar() +{ + ImplDestroy( this, TRUE ); +} + +void MenuBar::ShowCloser( BOOL bShow ) +{ + ShowButtons( bShow, mbFloatBtnVisible, mbHideBtnVisible ); +} + +void MenuBar::ShowFloatButton( BOOL bShow ) +{ + ShowButtons( mbCloserVisible, bShow, mbHideBtnVisible ); +} + +void MenuBar::ShowHideButton( BOOL bShow ) +{ + ShowButtons( mbCloserVisible, mbFloatBtnVisible, bShow ); +} + +void MenuBar::ShowButtons( BOOL bClose, BOOL bFloat, BOOL bHide ) +{ + if ( (bClose != mbCloserVisible) || + (bFloat != mbFloatBtnVisible) || + (bHide != mbHideBtnVisible) ) + { + mbCloserVisible = bClose; + mbFloatBtnVisible = bFloat; + mbHideBtnVisible = bHide; + if ( ImplGetWindow() ) + ((MenuBarWindow*)ImplGetWindow())->ShowButtons( bClose, bFloat, bHide ); + } +} + +Window* MenuBar::ImplCreate( Window* pParent, Window* pWindow, MenuBar* pMenu ) +{ + if ( !pWindow ) + pWindow = new MenuBarWindow( pParent ); + + pMenu->pStartedFrom = 0; + pMenu->pWindow = pWindow; + ((MenuBarWindow*)pWindow)->SetMenu( pMenu ); + long nHeight = pMenu->ImplCalcSize( pWindow ).Height(); + pWindow->SetPosSizePixel( 0, 0, 0, nHeight, WINDOW_POSSIZE_HEIGHT ); + return pWindow; +} + +void MenuBar::ImplDestroy( MenuBar* pMenu, BOOL bDelete ) +{ + MenuBarWindow* pWindow = (MenuBarWindow*) pMenu->ImplGetWindow(); + if ( pWindow && bDelete ) + { + pWindow->KillActivePopup(); + delete pWindow; + } + pMenu->pWindow = NULL; +} + +BOOL MenuBar::ImplHandleKeyEvent( const KeyEvent& rKEvent, BOOL bFromMenu ) +{ + BOOL bDone = FALSE; + // Enabled-Abfragen, falls diese Methode von einem anderen Fenster gerufen wurde... + Window* pWin = ImplGetWindow(); + if ( pWin && pWin->IsEnabled() && pWin->IsInputEnabled() ) + bDone = ((MenuBarWindow*)pWin)->ImplHandleKeyEvent( rKEvent, bFromMenu ); + return bDone; +} + +// ----------------------------------------------------------------------- + +void MenuBar::SelectEntry( USHORT nId ) +{ + if( ImplGetWindow() ) + { + MenuBarWindow* pMenuWin = (MenuBarWindow*) ImplGetWindow(); + + pMenuWin->GrabFocus(); + nId = GetItemPos( nId ); + + if( ITEMPOS_INVALID == pMenuWin->nHighlightedItem ) + { + if( ( nId != ITEMPOS_INVALID ) && ( nId != pMenuWin->nHighlightedItem ) ) + pMenuWin->ChangeHighlightItem( nId, FALSE ); + } + else + { + pMenuWin->KillActivePopup(); + pMenuWin->ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); + } + } +} + +// BOOL PopupMenu::bAnyPopupInExecute = FALSE; + +PopupMenu::PopupMenu() +{ +} + +PopupMenu::PopupMenu( const ResId& rResId ) +{ + ImplLoadRes( rResId ); +} + +PopupMenu::PopupMenu( const PopupMenu& rMenu ) +{ + *this = rMenu; +} + +PopupMenu::~PopupMenu() +{ +} + +BOOL PopupMenu::IsInExecute() +{ + return GetActivePopupMenu() ? TRUE : FALSE; +} + +PopupMenu* PopupMenu::GetActivePopupMenu() +{ + ImplSVData* pSVData = ImplGetSVData(); + return pSVData->maAppData.mpActivePopupMenu; +} + +void PopupMenu::EndExecute( USHORT nSelectId ) +{ + if ( ImplGetWindow() ) + ImplGetFloatingWindow()->EndExecute( nSelectId ); +} + +void PopupMenu::SelectEntry( USHORT nId ) +{ + if ( ImplGetWindow() ) + { + USHORT nPos; + MenuItemData* pData = GetItemList()->GetData( nId, nPos ); + if ( pData->pSubMenu ) + ImplGetFloatingWindow()->ChangeHighlightItem( nPos, TRUE ); + else + ImplGetFloatingWindow()->EndExecute( nId ); + } +} + +USHORT PopupMenu::Execute( Window* pWindow, const Point& rPopupPos ) +{ + return Execute( pWindow, Rectangle( rPopupPos, rPopupPos ), POPUPMENU_EXECUTE_DOWN ); +} + +USHORT PopupMenu::Execute( Window* pWindow, const Rectangle& rRect, USHORT nFlags ) +{ + ULONG nPopupModeFlags = 0; + if ( nFlags & POPUPMENU_EXECUTE_DOWN ) + nPopupModeFlags = FLOATWIN_POPUPMODE_DOWN; + else if ( nFlags & POPUPMENU_EXECUTE_UP ) + nPopupModeFlags = FLOATWIN_POPUPMODE_UP; + else if ( nFlags & POPUPMENU_EXECUTE_LEFT ) + nPopupModeFlags = FLOATWIN_POPUPMODE_LEFT; + else if ( nFlags & POPUPMENU_EXECUTE_RIGHT ) + nPopupModeFlags = FLOATWIN_POPUPMODE_RIGHT; + else + nPopupModeFlags = FLOATWIN_POPUPMODE_DOWN; + return ImplExecute( pWindow, rRect, nPopupModeFlags, 0, FALSE ); +} + +USHORT PopupMenu::ImplExecute( Window* pW, const Rectangle& rRect, ULONG nPopupModeFlags, Menu* pSFrom, BOOL bPreSelectFirst ) +{ + // #59614# Mit TH abgesprochen dass die ASSERTION raus kommt, + // weil es evtl. legitim ist... +// DBG_ASSERT( !PopupMenu::IsInExecute() || pSFrom, "PopupMenu::Execute() called in PopupMenu::Execute()" ); + + if ( !pSFrom && ( PopupMenu::IsInExecute() || !GetItemCount() ) ) + return 0; + + ImplSVData* pSVData = ImplGetSVData(); + + pStartedFrom = pSFrom; + nSelectedId = 0; + bCanceled = FALSE; + + ULONG nFocusId = 0; + BOOL bRealExecute = FALSE; + if ( !pStartedFrom ) + { + pSVData->maWinData.mbNoDeactivate = TRUE; + nFocusId = Window::SaveFocus(); + bRealExecute = TRUE; + } + + DBG_ASSERT( !ImplGetWindow(), "Win?!" ); + Rectangle aRect( rRect ); + aRect.SetPos( pW->OutputToScreenPixel( aRect.TopLeft() ) ); + + WinBits nStyle = WB_BORDER; + if ( bRealExecute ) + nPopupModeFlags |= FLOATWIN_POPUPMODE_NEWLEVEL; + if ( !pStartedFrom || !pStartedFrom->bIsMenuBar ) + nPopupModeFlags |= FLOATWIN_POPUPMODE_PATHMOUSECANCELCLICK | FLOATWIN_POPUPMODE_ALLMOUSEBUTTONCLOSE; + + // Kann beim Debuggen hilfreich sein. + // nPopupModeFlags |= FLOATWIN_POPUPMODE_NOFOCUSCLOSE; + + ImplDelData aDelData; + pW->ImplAddDel( &aDelData ); + + bInCallback = TRUE; // hier schon setzen, falls Activate ueberladen + Activate(); + bInCallback = FALSE; + + if ( aDelData.IsDelete() ) + return 0; // Error + + pW->ImplRemoveDel( &aDelData ); + + if ( bCanceled || bKilled ) + return 0; + + if ( !GetItemCount() ) + return 0; + + // Das Flag MENU_FLAG_HIDEDISABLEDENTRIES wird vererbt. + if ( pSFrom ) + { + if ( pSFrom->nMenuFlags & MENU_FLAG_HIDEDISABLEDENTRIES ) + nMenuFlags |= MENU_FLAG_HIDEDISABLEDENTRIES; + else + nMenuFlags &= ~MENU_FLAG_HIDEDISABLEDENTRIES; + } + + USHORT nVisibleEntries = ImplGetVisibleItemCount(); + if ( !nVisibleEntries ) + { + String aTmpEntryText( ResId( SV_RESID_STRING_NOSELECTIONPOSSIBLE, ImplGetResMgr() ) ); + MenuItemData* pData = pItemList->Insert( + 0xFFFF, MENUITEM_STRING, 0, aTmpEntryText, Image(), NULL, 0xFFFF ); + pData->bIsTemporary = TRUE; + } + else if ( pSVData->maAppData.mbAutoMnemonics && !( nMenuFlags & MENU_FLAG_NOAUTOMNEMONICS ) ) + { + ImplMnemonicGenerator aMnemonicGenerator; + ULONG n; + for ( n = 0; n < pItemList->Count(); n++ ) + aMnemonicGenerator.RegisterMnemonic( pItemList->GetDataFromPos(n)->aText ); + for ( n = 0; n < pItemList->Count(); n++ ) + aMnemonicGenerator.CreateMnemonic( pItemList->GetDataFromPos(n)->aText ); + } + + MenuFloatingWindow* pWin = new MenuFloatingWindow( this, pW, nStyle ); + pWindow = pWin; + + Size aSz = ImplCalcSize( pWin ); + + long nMaxHeight = pWin->GetDesktopRectPixel().GetHeight(); + if ( pStartedFrom && pStartedFrom->bIsMenuBar ) + nMaxHeight -= pW->GetSizePixel().Height(); + long nLeft, nTop, nRight, nBottom; + pWindow->GetBorder( nLeft, nTop, nRight, nBottom ); + nMaxHeight -= nTop+nBottom; + if ( aSz.Height() > nMaxHeight ) + { + pWin->EnableScrollMenu( TRUE ); + USHORT nEntries = ImplCalcVisEntries( nMaxHeight ); + aSz.Height() = ImplCalcHeight( nEntries ); + } + + pWin->SetFocusId( nFocusId ); + pWin->SetOutputSizePixel( aSz ); + pWin->GrabFocus(); + if ( GetItemCount() ) + { + pWin->StartPopupMode( aRect, nPopupModeFlags ); + } + if ( bPreSelectFirst ) + { + USHORT nCount = (USHORT)pItemList->Count(); + for ( USHORT n = 0; n < nCount; n++ ) + { + MenuItemData* pData = pItemList->GetDataFromPos( n ); + if ( ( pData->eType != MENUITEM_SEPARATOR ) && ImplIsVisible( n ) ) + { + pWin->ChangeHighlightItem( n, FALSE ); + break; + } + } + } + if ( bRealExecute ) + { + pWin->Execute(); + + // Focus wieder herstellen (kann schon im Select wieder + // hergestellt wurden sein + nFocusId = pWin->GetFocusId(); + if ( nFocusId ) + { + pWin->SetFocusId( 0 ); + pSVData->maWinData.mbNoDeactivate = FALSE; + } + pWin->ImplEndPopupMode( 0, nFocusId ); + + if ( nSelectedId ) // Dann abraeumen... ( sonst macht TH das ) + { + PopupMenu* pSub = pWin->GetActivePopup(); + while ( pSub ) + { + pSub->ImplGetFloatingWindow()->EndPopupMode(); + pSub = pSub->ImplGetFloatingWindow()->GetActivePopup(); + } + } + delete pWindow; + pWindow = NULL; + + // Steht noch ein Select aus? + Menu* pSelect = ImplFindSelectMenu(); + if ( pSelect ) + { + // Beim Popup-Menu muss das Select vor dem Verlassen von Execute gerufen werden! + Application::RemoveUserEvent( pSelect->nEventId ); + pSelect->nEventId = 0; + pSelect->Select(); + } + } + + return bRealExecute ? nSelectedId : 0; +} + +USHORT PopupMenu::ImplCalcVisEntries( long nMaxHeight, USHORT nStartEntry ) const +{ + nMaxHeight -= 2 * ImplGetFloatingWindow()->GetScrollerHeight(); + + long nHeight = 0; + USHORT nEntries = (USHORT) pItemList->Count(); + USHORT nVisEntries = 0; + for ( USHORT n = nStartEntry; n < nEntries; n++ ) + { + if ( ImplIsVisible( n ) ) + { + MenuItemData* pData = pItemList->GetDataFromPos( n ); + nHeight += pData->aSz.Height(); + if ( nHeight > nMaxHeight ) + break; + nVisEntries++; + } + } + return nVisEntries; +} + +long PopupMenu::ImplCalcHeight( USHORT nEntries ) const +{ + long nHeight = 0; + + if ( nEntries > pItemList->Count() ) + nEntries = (USHORT) pItemList->Count(); + + for ( ULONG n = 0; n < nEntries; n++ ) + { + MenuItemData* pData = pItemList->GetDataFromPos( n ); + nHeight += pData->aSz.Height(); + } + + nHeight += 2*ImplGetFloatingWindow()->GetScrollerHeight(); + + return nHeight; +} + + +static void ImplInitMenuWindow( Window* pWin, BOOL bFont ) +{ + const StyleSettings& rStyleSettings = pWin->GetSettings().GetStyleSettings(); + + if ( bFont ) + pWin->SetPointFont( rStyleSettings.GetMenuFont() ); + pWin->SetBackground( Wallpaper( rStyleSettings.GetMenuColor() ) ); + pWin->SetTextColor( rStyleSettings.GetMenuTextColor() ); + pWin->SetTextFillColor(); + pWin->SetLineColor(); +} + +MenuFloatingWindow::MenuFloatingWindow( Menu* pMen, Window* pParent, WinBits nStyle ) : + FloatingWindow( pParent, nStyle ) +{ + pMenu = pMen; + pActivePopup = 0; + nSaveFocusId = 0; + bInExecute = FALSE; + bScrollMenu = FALSE; + nHighlightedItem = ITEMPOS_INVALID; + nMBDownPos = ITEMPOS_INVALID; + nScrollerHeight = 0; + nStartY = 0; + nBorder = EXTRASPACEY; + nFirstEntry = 0; + bScrollUp = FALSE; + bScrollDown = FALSE; + + EnableSaveBackground(); + ImplInitMenuWindow( this, TRUE ); + + SetPopupModeEndHdl( LINK( this, MenuFloatingWindow, PopupEnd ) ); + + aHighlightChangedTimer.SetTimeoutHdl( LINK( this, MenuFloatingWindow, HighlightChanged ) ); + aHighlightChangedTimer.SetTimeout( GetSettings().GetMouseSettings().GetMenuDelay() ); + aScrollTimer.SetTimeoutHdl( LINK( this, MenuFloatingWindow, AutoScroll ) ); + + if ( Application::GetAccessHdlCount() ) + Application::AccessNotify( AccessNotification( ACCESS_EVENT_POPUPMENU_START, pMenu ) ); +} + +MenuFloatingWindow::~MenuFloatingWindow() +{ + if( Application::GetAccessHdlCount() ) + Application::AccessNotify( AccessNotification( ACCESS_EVENT_POPUPMENU_END, pMenu ) ); + + aHighlightChangedTimer.Stop(); +} + +void MenuFloatingWindow::Resize() +{ + ImplInitClipRegion(); +} + +Region MenuFloatingWindow::ImplCalcClipRegion( BOOL bIncludeLogo ) const +{ + Size aOutSz = GetOutputSizePixel(); + Point aPos; + Rectangle aRect( aPos, aOutSz ); + long nBorder = nScrollerHeight; + aRect.Top() += nBorder; + aRect.Bottom() -= nBorder; + + if ( pMenu->pLogo && !bIncludeLogo ) + aRect.Left() += pMenu->pLogo->aBitmap.GetSizePixel().Width(); + + Region aRegion = aRect; + if ( pMenu->pLogo && bIncludeLogo && nScrollerHeight ) + aRegion.Union( Rectangle( Point(), Size( pMenu->pLogo->aBitmap.GetSizePixel().Width(), aOutSz.Height() ) ) ); + + return aRegion; +} + +void MenuFloatingWindow::ImplInitClipRegion() +{ + if ( IsScrollMenu() ) + { + SetClipRegion( ImplCalcClipRegion() ); + } + else + { + SetClipRegion(); + } +} + +void MenuFloatingWindow::ImplHighlightItem( const MouseEvent& rMEvt, BOOL bMBDown ) +{ + long nY = nScrollerHeight; + long nMouseY = rMEvt.GetPosPixel().Y(); + Size aOutSz = GetOutputSizePixel(); + if ( ( nMouseY >= nY ) && ( nMouseY < ( aOutSz.Height() - nY ) ) ) + { + BOOL bHighlighted = FALSE; + USHORT nCount = (USHORT)pMenu->pItemList->Count(); + nY += nStartY; // ggf. gescrollt. + for ( USHORT n = 0; !bHighlighted && ( n < nCount ); n++ ) + { + if ( pMenu->ImplIsVisible( n ) ) + { + MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); + long nOldY = nY; + nY += pData->aSz.Height(); + if ( ( nOldY <= nMouseY ) && ( nY > nMouseY ) ) + { + BOOL bPopupArea = TRUE; + if ( pData->nBits & MIB_POPUPSELECT ) + { + // Nur wenn ueber dem Pfeil geklickt wurde... + Size aSz = GetOutputSizePixel(); + long nFontHeight = GetTextHeight(); + bPopupArea = ( rMEvt.GetPosPixel().X() >= ( aSz.Width() - nFontHeight - nFontHeight/4 ) ); + } + + if ( bMBDown ) + { + if ( n != nHighlightedItem ) + ChangeHighlightItem( (USHORT)n, FALSE ); + + if ( pActivePopup ) + KillActivePopup(); + else if ( bPopupArea ) + HighlightChanged( NULL ); + } + else + { + if ( n != nHighlightedItem ) + { + ChangeHighlightItem( (USHORT)n, TRUE ); + } + else if ( pData->nBits & MIB_POPUPSELECT ) + { + if ( bPopupArea && ( pActivePopup != pData->pSubMenu ) ) + HighlightChanged( NULL ); + } + } + bHighlighted = TRUE; + } + } + } + if ( !bHighlighted ) + ChangeHighlightItem( ITEMPOS_INVALID, TRUE ); + } + else + { + ImplScroll( rMEvt.GetPosPixel() ); + ChangeHighlightItem( ITEMPOS_INVALID, TRUE ); + } +} + +IMPL_LINK( MenuFloatingWindow, PopupEnd, FloatingWindow*, pPopup ) +{ + if ( bInExecute ) + { + if ( pActivePopup ) + { + DBG_ASSERT( !pActivePopup->ImplGetWindow(), "PopupEnd, obwohl pActivePopup MIT Window!" ); + pActivePopup->bCanceled = TRUE; + } + bInExecute = FALSE; + pMenu->bInCallback = TRUE; + pMenu->Deactivate(); + pMenu->bInCallback = FALSE; + } + else + { + // Wenn dies Fenster von TH geschlossen wurde, hat noch ein anderes + // Menu dieses Fenster als pActivePopup. + if ( pMenu->pStartedFrom ) + { + // Das pWin am 'Parent' kann aber schon 0 sein, falls die Kette von + // vorne abgeraeumt wurde und jetzt die EndPopup-Events eintrudeln + if ( pMenu->pStartedFrom->bIsMenuBar ) + { + MenuBarWindow* p = (MenuBarWindow*) pMenu->pStartedFrom->ImplGetWindow(); + if ( p ) + p->PopupClosed( pMenu ); + } + else + { + MenuFloatingWindow* p = (MenuFloatingWindow*) pMenu->pStartedFrom->ImplGetWindow(); + if ( p ) + p->KillActivePopup( (PopupMenu*)pMenu ); + } + } + } + return 0; +} + +IMPL_LINK( MenuFloatingWindow, AutoScroll, Timer*, EMPTYARG ) +{ + ImplScroll( GetPointerPosPixel() ); + return 1; +} + +IMPL_LINK( MenuFloatingWindow, HighlightChanged, Timer*, pTimer ) +{ + MenuItemData* pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem ); + if ( pData ) + { + if ( pActivePopup && ( pActivePopup != pData->pSubMenu ) ) + { + KillActivePopup(); + } + if ( pData->bEnabled && pData->pSubMenu && pData->pSubMenu->GetItemCount() && ( pData->pSubMenu != pActivePopup ) ) + { + pActivePopup = (PopupMenu*)pData->pSubMenu; + long nY = nScrollerHeight+nStartY; + MenuItemData* pData = 0; + for ( ULONG n = 0; n < nHighlightedItem; n++ ) + { + pData = pMenu->pItemList->GetDataFromPos( n ); + nY += pData->aSz.Height(); + } + pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem ); + Size MySize = GetOutputSizePixel(); +// Point MyPos = GetPosPixel(); +// Point aItemTopLeft( MyPos.X(), MyPos.Y()+nY ); + Point aItemTopLeft( 0, nY ); + Point aItemBottomRight( aItemTopLeft ); + aItemBottomRight.X() += MySize.Width(); + aItemBottomRight.Y() += pData->aSz.Height(); + + // Popups leicht versetzen: + aItemTopLeft.X() += 2; + aItemBottomRight.X() -= 2; + if ( nHighlightedItem ) + aItemTopLeft.Y() -= 2; + else + { + long nL, nT, nR, nB; + GetBorder( nL, nT, nR, nB ); + aItemTopLeft.Y() -= nT; + } + + // pTest: Wegen Abstuerzen durch Reschedule() im Aufruf von Activate() + // Ausserdem wird damit auch verhindert, dass SubMenus angezeigt werden, + // die lange im Activate Rescheduled haben und jetzt schon nicht mehr + // angezeigt werden sollen. + Menu* pTest = pActivePopup; + USHORT nRet = pActivePopup->ImplExecute( this, Rectangle( aItemTopLeft, aItemBottomRight ), FLOATWIN_POPUPMODE_RIGHT, pMenu, pTimer ? FALSE : TRUE ); + + // nRet != 0, wenn es waerend Activate() abgeschossen wurde... + if ( !nRet && ( pActivePopup == pTest ) && pActivePopup->ImplGetWindow() ) + pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this ); + } + } + + return 0; +} + +void MenuFloatingWindow::EnableScrollMenu( BOOL b ) +{ + bScrollMenu = b; + nScrollerHeight = b ? (USHORT) GetSettings().GetStyleSettings().GetScrollBarSize() /2 : 0; + bScrollDown = TRUE; + ImplInitClipRegion(); +} + +void MenuFloatingWindow::Execute() +{ + ImplSVData* pSVData = ImplGetSVData(); + + pSVData->maAppData.mpActivePopupMenu = (PopupMenu*)pMenu; + + bInExecute = TRUE; +// bCallingSelect = FALSE; + + while ( bInExecute ) + Application::Yield(); + + pSVData->maAppData.mpActivePopupMenu = NULL; + +// while ( bCallingSelect ) +// Application::Yield(); +} + +void MenuFloatingWindow::StopExecute( ULONG nFocusId ) +{ + // Focus wieder herstellen + // (kann schon im Select wieder hergestellt wurden sein) + if ( nSaveFocusId ) + { + Window::EndSaveFocus( nFocusId, FALSE ); + nFocusId = nSaveFocusId; + if ( nFocusId ) + { + nSaveFocusId = 0; + ImplGetSVData()->maWinData.mbNoDeactivate = FALSE; + } + } + ImplEndPopupMode( 0, nFocusId ); + + aHighlightChangedTimer.Stop(); + bInExecute = FALSE; + if ( pActivePopup ) + { + KillActivePopup(); + } +} + +void MenuFloatingWindow::KillActivePopup( PopupMenu* pThisOnly ) +{ + if ( pActivePopup && ( !pThisOnly || ( pThisOnly == pActivePopup ) ) ) + { + if ( pActivePopup->bInCallback ) + pActivePopup->bCanceled = TRUE; + + // Vor allen Aktionen schon pActivePopup = 0, falls z.B. + // PopupModeEndHdl des zu zerstoerenden Popups mal synchron gerufen wird. + PopupMenu* pPopup = pActivePopup; + pActivePopup = NULL; + pPopup->bInCallback = TRUE; + pPopup->Deactivate(); + pPopup->bInCallback = FALSE; + if ( pPopup->ImplGetWindow() ) + { + pPopup->ImplGetFloatingWindow()->StopExecute(); + delete pPopup->pWindow; + pPopup->pWindow = NULL; + Update(); + } + } +} + +void MenuFloatingWindow::EndExecute() +{ + Menu* pStart = pMenu->ImplGetStartMenu(); + ULONG nFocusId = 0; + if ( pStart && pStart->bIsMenuBar ) + { + nFocusId = ((MenuBarWindow*)((MenuBar*)pStart)->ImplGetWindow())->GetFocusId(); + if ( nFocusId ) + { + ((MenuBarWindow*)((MenuBar*)pStart)->ImplGetWindow())->SetFocusId( 0 ); + ImplGetSVData()->maWinData.mbNoDeactivate = FALSE; + } + } + // Wenn von woanders gestartet, dann ab dort aufraumen: + MenuFloatingWindow* pCleanUpFrom = this; + MenuFloatingWindow* pWin = this; + while ( pWin && !pWin->bInExecute && + pWin->pMenu->pStartedFrom && !pWin->pMenu->pStartedFrom->bIsMenuBar ) + { + pWin = ((PopupMenu*)pWin->pMenu->pStartedFrom)->ImplGetFloatingWindow(); + } + if ( pWin ) + pCleanUpFrom = pWin; + + // Dies Fenster wird gleich zerstoert => Daten lokal merken... + Menu* pM = pMenu; + USHORT nItem = nHighlightedItem; + + pCleanUpFrom->StopExecute( nFocusId ); + + if ( nItem != ITEMPOS_INVALID ) + { + MenuItemData* pItemData = pM->GetItemList()->GetDataFromPos( nItem ); + if ( pItemData && !pItemData->bIsTemporary ) + { + pM->nSelectedId = pItemData->nId; + if ( pStart ) + pStart->nSelectedId = pItemData->nId; + + pM->ImplSelect(); + } + } +} + +void MenuFloatingWindow::EndExecute( USHORT nId ) +{ + USHORT nPos; + if ( pMenu->GetItemList()->GetData( nId, nPos ) ) + nHighlightedItem = nPos; + else + nHighlightedItem = ITEMPOS_INVALID; + + EndExecute(); +} + +void MenuFloatingWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + // TH macht ein ToTop auf dieses Fenster, aber das aktive Popup + // soll oben bleiben... + if ( pActivePopup && pActivePopup->ImplGetWindow() && !pActivePopup->ImplGetFloatingWindow()->pActivePopup ) + pActivePopup->ImplGetFloatingWindow()->ToTop(); + + if ( !ImplIsMouseFollow() ) + { + ImplHighlightItem( rMEvt, TRUE ); + } + + nMBDownPos = nHighlightedItem; +} + +void MenuFloatingWindow::MouseButtonUp( const MouseEvent& rMEvt ) +{ + MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); + // nMBDownPos in lokaler Variable merken und gleich zuruecksetzen, + // weil nach EndExecute zu spaet + USHORT _nMBDownPos = nMBDownPos; + nMBDownPos = ITEMPOS_INVALID; + if ( pData && pData->bEnabled && ( pData->eType != MENUITEM_SEPARATOR ) ) + { + if ( !pData->pSubMenu ) + { + EndExecute(); + } + else if ( ( pData->nBits & MIB_POPUPSELECT ) && ( nHighlightedItem == _nMBDownPos ) && ( rMEvt.GetClicks() == 2 ) ) + { + // Nicht wenn ueber dem Pfeil geklickt wurde... + Size aSz = GetOutputSizePixel(); + long nFontHeight = GetTextHeight(); + if ( rMEvt.GetPosPixel().X() < ( aSz.Width() - nFontHeight - nFontHeight/4 ) ) + EndExecute(); + } + } + +} + +void MenuFloatingWindow::MouseMove( const MouseEvent& rMEvt ) +{ + if ( !IsVisible() || rMEvt.IsSynthetic() ) + return; + + if ( rMEvt.IsLeaveWindow() ) + { + if ( ImplIsMouseFollow() || ( rMEvt.GetButtons() == MOUSE_LEFT ) ) + ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); + if ( IsScrollMenu() ) + ImplScroll( rMEvt.GetPosPixel() ); + } + else if ( ImplIsMouseFollow() || ( rMEvt.GetButtons() == MOUSE_LEFT ) ) + { + ImplHighlightItem( rMEvt, FALSE ); + } +} + +void MenuFloatingWindow::ImplScroll( BOOL bUp ) +{ + KillActivePopup(); + Update(); + + HighlightItem( nHighlightedItem, FALSE ); + + if ( bScrollUp && bUp ) + { + MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( --nFirstEntry ); + long nEntryHeight = pData->aSz.Height(); + + nStartY += nEntryHeight; + + if ( !bScrollDown ) + { + bScrollDown = TRUE; + ImplDrawScroller( FALSE ); + } + + if ( nFirstEntry == 0 ) + { + bScrollUp = FALSE; + ImplDrawScroller( TRUE ); + } + Scroll( 0, nEntryHeight, ImplCalcClipRegion( FALSE ).GetBoundRect(), SCROLL_CLIP ); + } + else if ( bScrollDown && !bUp ) + { + MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nFirstEntry++ ); + long nEntryHeight = pData->aSz.Height(); + + if ( !bScrollUp ) + { + bScrollUp = TRUE; + ImplDrawScroller( TRUE ); + } + + Size aOutSz = GetOutputSizePixel(); + USHORT nVisible = ((PopupMenu*)pMenu)->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry ); + if ( ( nFirstEntry + nVisible ) >= (USHORT) pMenu->GetItemList()->Count() ) + { + bScrollDown = FALSE; + ImplDrawScroller( FALSE ); + } + + nStartY -= nEntryHeight; + Scroll( 0, -nEntryHeight, ImplCalcClipRegion( FALSE ).GetBoundRect(), SCROLL_CLIP ); + } + + HighlightItem( nHighlightedItem, TRUE ); +} + +void MenuFloatingWindow::ImplScroll( const Point& rMousePos ) +{ + Size aOutSz = GetOutputSizePixel(); + + long nY = nScrollerHeight; + long nMouseY = rMousePos.Y(); + long nDelta = 0; + + if ( bScrollUp && ( nMouseY < nY ) ) + { + ImplScroll( TRUE ); + nDelta = nY - nMouseY; + } + else if ( bScrollDown && ( nMouseY > ( aOutSz.Height() - nY ) ) ) + { + ImplScroll( FALSE ); + nDelta = nMouseY - ( aOutSz.Height() - nY ); + } + + if ( nDelta ) + { + aScrollTimer.Stop(); // Falls durch MouseMove gescrollt. + long nTimeout; + if ( nDelta < 3 ) + nTimeout = 200; + else if ( nDelta < 5 ) + nTimeout = 100; + else if ( nDelta < 8 ) + nTimeout = 70; + else if ( nDelta < 12 ) + nTimeout = 40; + else + nTimeout = 20; + aScrollTimer.SetTimeout( nTimeout ); + aScrollTimer.Start(); + } +} +void MenuFloatingWindow::ChangeHighlightItem( USHORT n, BOOL bStartPopupTimer ) +{ + // #57934# ggf. das aktive Popup sofort schliessen, damit TH's Hintergrundsicherung funktioniert. + // #65750# Dann verzichten wir lieber auf den schmalen Streifen Hintergrundsicherung. + // Sonst lassen sich die Menus schlecht bedienen. +// MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n ); +// if ( pActivePopup && pNextData && ( pActivePopup != pNextData->pSubMenu ) ) +// KillActivePopup(); + + if ( nHighlightedItem != ITEMPOS_INVALID ) + HighlightItem( nHighlightedItem, FALSE ); + + nHighlightedItem = (USHORT)n; + DBG_ASSERT( ( nHighlightedItem == ITEMPOS_INVALID ) || pMenu->ImplIsVisible( nHighlightedItem ), "ChangeHighlightItem: Not visible!" ); + HighlightItem( nHighlightedItem, TRUE ); + pMenu->ImplCallHighlight( nHighlightedItem ); + + if ( bStartPopupTimer ) + aHighlightChangedTimer.Start(); +} + +void MenuFloatingWindow::HighlightItem( USHORT nPos, BOOL bHighlight ) +{ + Size aSz = GetOutputSizePixel(); + USHORT nBorder = nScrollerHeight; + long nY = nBorder+nStartY; + long nX = 0; + + if ( pMenu->pLogo ) + nX = pMenu->pLogo->aBitmap.GetSizePixel().Width(); + + USHORT nCount = (USHORT)pMenu->pItemList->Count(); + for ( USHORT n = 0; n < nCount; n++ ) + { + MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); + if ( n == nPos ) + { + DBG_ASSERT( pMenu->ImplIsVisible( n ), "Highlight: Item not visible!" ); + if ( pData->eType != MENUITEM_SEPARATOR ) + { + if ( bHighlight ) + SetFillColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() ); + else + SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() ); + + Rectangle aRect( Point( nX, nY ), Size( aSz.Width(), pData->aSz.Height() ) ); + if ( pData->nBits & MIB_POPUPSELECT ) + { + long nFontHeight = GetTextHeight(); + aRect.Right() -= nFontHeight + nFontHeight/4; + } + DrawRect( aRect ); + pMenu->ImplPaint( this, nBorder, nStartY, pData, bHighlight ); + } + return; + } + + nY += pData->aSz.Height(); + } +} + +void MenuFloatingWindow::ImplCursorUpDown( BOOL bUp ) +{ + USHORT n = nHighlightedItem; + if ( n == ITEMPOS_INVALID ) + { + if ( bUp ) + n = 0; + else + n = pMenu->GetItemCount()-1; + } + + USHORT nLoop = n; + 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->eType != MENUITEM_SEPARATOR ) && pMenu->ImplIsVisible( n ) ) + { + // Selektion noch im sichtbaren Bereich? + if ( IsScrollMenu() ) + { + ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); + + while ( n < nFirstEntry ) + ImplScroll( TRUE ); + + Size aOutSz = GetOutputSizePixel(); + while ( n >= ( nFirstEntry + ((PopupMenu*)pMenu)->ImplCalcVisEntries( aOutSz.Height(), nFirstEntry ) ) ) + ImplScroll( FALSE ); + } + ChangeHighlightItem( n, FALSE ); + break; + } + } while ( n != nLoop ); +} + +void MenuFloatingWindow::KeyInput( const KeyEvent& rKEvent ) +{ + USHORT nCode = rKEvent.GetKeyCode().GetCode(); + switch ( nCode ) + { + case KEY_UP: + case KEY_DOWN: + { + ImplCursorUpDown( nCode == KEY_UP ); + } + break; + case KEY_LEFT: + { + if ( pMenu->pStartedFrom ) + { + StopExecute(); + if ( pMenu->pStartedFrom->bIsMenuBar ) + { + // Weiterkeiten... + ((MenuBarWindow*)((MenuBar*)pMenu->pStartedFrom)->ImplGetWindow())->KeyInput( rKEvent ); + } + else + { + MenuFloatingWindow* pFloat = ((PopupMenu*)pMenu->pStartedFrom)->ImplGetFloatingWindow(); + pFloat->GrabFocus(); + pFloat->KillActivePopup(); + } + } + } + break; + case KEY_RIGHT: + { + 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 ) + { + // Weiterkeiten... + pStart->ImplGetWindow()->KeyInput( rKEvent ); + } + } + } + break; + case KEY_RETURN: + { + MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); + if ( pData && pData->bEnabled ) + { + if ( pData->pSubMenu ) + HighlightChanged( 0 ); + else + EndExecute(); + } + else + StopExecute(); + } + break; + case KEY_MENU: + { + Menu* pStart = pMenu->ImplGetStartMenu(); + if ( pStart && pStart->bIsMenuBar ) + { + // Weiterkeiten... + pStart->ImplGetWindow()->KeyInput( rKEvent ); + } + } + break; + default: + { + xub_Unicode nCharCode = rKEvent.GetCharCode(); + USHORT nPos; + MenuItemData* pData = pMenu->GetItemList()->SearchItem( nCharCode, nPos ); + if ( pData ) + { + if ( pData->pSubMenu ) + { + ChangeHighlightItem( nPos, FALSE ); + HighlightChanged( 0 ); + } + else + { + nHighlightedItem = nPos; + EndExecute(); + } + } + else + { + // Bei ungueltigen Tasten Beepen, aber nicht bei HELP und F-Tasten + if ( !rKEvent.GetKeyCode().IsControlMod() && ( nCode != KEY_HELP ) && ( rKEvent.GetKeyCode().GetGroup() != KEYGROUP_FKEYS ) ) + Sound::Beep(); + FloatingWindow::KeyInput( rKEvent ); + } + } + } +} + +void MenuFloatingWindow::Paint( const Rectangle& rRect ) +{ + if ( IsScrollMenu() ) + { + ImplDrawScroller( TRUE ); + ImplDrawScroller( FALSE ); + } + pMenu->ImplPaint( this, nScrollerHeight, nStartY ); + if ( nHighlightedItem != ITEMPOS_INVALID ) + HighlightItem( nHighlightedItem, TRUE ); +} + +void MenuFloatingWindow::ImplDrawScroller( BOOL bUp ) +{ + SetClipRegion(); + + Size aOutSz = GetOutputSizePixel(); + long nY = bUp ? 0 : ( aOutSz.Height() - nScrollerHeight ); + long nX = pMenu->pLogo ? pMenu->pLogo->aBitmap.GetSizePixel().Width() : 0; + Rectangle aRect( Point( nX, nY ), Size( aOutSz.Width()-nX, nScrollerHeight ) ); + + DecorationView aDecoView( this ); + SymbolType eSymbol = bUp ? SYMBOL_SPIN_UP : SYMBOL_SPIN_DOWN; + + USHORT nStyle = 0; + if ( ( bUp && !bScrollUp ) || ( !bUp && !bScrollDown ) ) + nStyle |= SYMBOL_DRAW_DISABLE; + + aDecoView.DrawSymbol( aRect, eSymbol, GetSettings().GetStyleSettings().GetButtonTextColor(), nStyle ); + + ImplInitClipRegion(); +} + +void MenuFloatingWindow::RequestHelp( const HelpEvent& rHEvt ) +{ + USHORT nId = nHighlightedItem; + Menu* pM = pMenu; + Window* pW = this; + if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) ) + { + nHighlightedItem = ITEMPOS_INVALID; + EndExecute(); + pW = NULL; + } + if( !ImplHandleHelpEvent( pW, pM, nId, rHEvt ) ) + Window::RequestHelp( rHEvt ); +} + +void MenuFloatingWindow::StateChanged( StateChangedType nType ) +{ + FloatingWindow::StateChanged( nType ); + + if ( ( nType == STATE_CHANGE_CONTROLFOREGROUND ) || ( nType == STATE_CHANGE_CONTROLBACKGROUND ) ) + { + ImplInitMenuWindow( this, 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 ); + 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 ) ); + } + } +} + + + +MenuBarWindow::MenuBarWindow( Window* pParent ) : + Window( pParent, 0 ), + aCloser( this, WB_NOPOINTERFOCUS | WB_SMALLSTYLE | WB_RECTSTYLE ), + aFloatBtn( this, WB_NOPOINTERFOCUS | WB_SMALLSTYLE | WB_RECTSTYLE ), + aHideBtn( this, WB_NOPOINTERFOCUS | WB_SMALLSTYLE | WB_RECTSTYLE ) +{ + pMenu = NULL; + pActivePopup = NULL; + nSaveFocusId = 0; + nHighlightedItem = ITEMPOS_INVALID; + mbAutoPopup = TRUE; + nSaveFocusId = 0; + + ResMgr* pResMgr = ImplGetResMgr(); + aCloser.SetClickHdl( LINK( this, MenuBarWindow, CloserHdl ) ); + aCloser.SetSymbol( SYMBOL_CLOSE ); + aCloser.SetQuickHelpText( XubString( ResId( SV_HELPTEXT_CLOSE, pResMgr ) ) ); + aFloatBtn.SetClickHdl( LINK( this, MenuBarWindow, FloatHdl ) ); + aFloatBtn.SetSymbol( SYMBOL_FLOAT ); + aFloatBtn.SetQuickHelpText( XubString( ResId( SV_HELPTEXT_RESTORE, pResMgr ) ) ); + aHideBtn.SetClickHdl( LINK( this, MenuBarWindow, HideHdl ) ); + aHideBtn.SetSymbol( SYMBOL_HIDE ); + aHideBtn.SetQuickHelpText( XubString( ResId( SV_HELPTEXT_MINIMIZE, pResMgr ) ) ); +} + +MenuBarWindow::~MenuBarWindow() +{ +} + +void MenuBarWindow::SetMenu( MenuBar* pMen ) +{ + pMenu = pMen; + KillActivePopup(); + nHighlightedItem = ITEMPOS_INVALID; + ImplInitMenuWindow( this, TRUE ); + if ( pMen ) + { + aCloser.Show( pMen->HasCloser() ); + aFloatBtn.Show( pMen->HasFloatButton() ); + aHideBtn.Show( pMen->HasHideButton() ); + } + Invalidate(); +} + +void MenuBarWindow::ShowButtons( BOOL bClose, BOOL bFloat, BOOL bHide ) +{ + aCloser.Show( bClose ); + aFloatBtn.Show( bFloat ); + aHideBtn.Show( bHide ); + Resize(); +} + +IMPL_LINK( MenuBarWindow, CloserHdl, PushButton*, EMPTYARG ) +{ + return ((MenuBar*)pMenu)->GetCloserHdl().Call( pMenu ); +} + +IMPL_LINK( MenuBarWindow, FloatHdl, PushButton*, EMPTYARG ) +{ + return ((MenuBar*)pMenu)->GetFloatButtonClickHdl().Call( pMenu ); +} + +IMPL_LINK( MenuBarWindow, HideHdl, PushButton*, EMPTYARG ) +{ + return ((MenuBar*)pMenu)->GetHideButtonClickHdl().Call( pMenu ); +} + +void MenuBarWindow::ImplCreatePopup( BOOL bPreSelectFirst ) +{ + MenuItemData* pData = pMenu->GetItemList()->GetDataFromPos( nHighlightedItem ); + if ( pData ) + { + if ( pActivePopup && ( pActivePopup != pData->pSubMenu ) ) + { + KillActivePopup(); + } + if ( pData->bEnabled && pData->pSubMenu && ( nHighlightedItem != ITEMPOS_INVALID ) && ( pData->pSubMenu != pActivePopup ) ) + { + pActivePopup = (PopupMenu*)pData->pSubMenu; + long nX = 0; + MenuItemData* pData = 0; + for ( ULONG n = 0; n < nHighlightedItem; n++ ) + { + pData = pMenu->GetItemList()->GetDataFromPos( n ); + nX += pData->aSz.Width(); + } + pData = pMenu->pItemList->GetDataFromPos( nHighlightedItem ); +// Point MyPos = GetPosPixel(); + Point aItemTopLeft( nX, 0 ); + Point aItemBottomRight( aItemTopLeft ); + aItemBottomRight.X() += pData->aSz.Width(); + + // Im Vollbild-Modus hat die MenuBar ggf. die Hoehe 0: + // Nicht immer einfach die Window-Hoehe nehmen, weil ItemHeight < WindowHeight. + if ( GetSizePixel().Height() ) + aItemBottomRight.Y() += pData->aSz.Height(); + + // ImplExecute ist doch nicht modal... + GrabFocus(); + pActivePopup->ImplExecute( this, Rectangle( aItemTopLeft, aItemBottomRight ), FLOATWIN_POPUPMODE_DOWN, pMenu, bPreSelectFirst ); + if ( pActivePopup ) + { + // Hat kein Window, wenn vorher abgebrochen oder keine Eintraege + if ( pActivePopup->ImplGetFloatingWindow() ) + pActivePopup->ImplGetFloatingWindow()->AddPopupModeWindow( this ); + else + pActivePopup = NULL; + } + } + } +} + + +void MenuBarWindow::KillActivePopup() +{ + if ( pActivePopup ) + { + if ( pActivePopup->bInCallback ) + pActivePopup->bCanceled = TRUE; + + pActivePopup->bInCallback = TRUE; + pActivePopup->Deactivate(); + pActivePopup->bInCallback = FALSE; + // Abfrage auf pActivePopup, falls im Deactivate abgeschossen... + if ( pActivePopup && pActivePopup->ImplGetWindow() ) + { + pActivePopup->ImplGetFloatingWindow()->StopExecute(); + delete pActivePopup->pWindow; + pActivePopup->pWindow = NULL; + } + pActivePopup = 0; + } +} + +void MenuBarWindow::PopupClosed( Menu* pPopup ) +{ + if ( pPopup == pActivePopup ) + { + KillActivePopup(); + ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); + } +} + +void MenuBarWindow::MouseButtonDown( const MouseEvent& rMEvt ) +{ + mbAutoPopup = TRUE; + USHORT nEntry = ImplFindEntry( rMEvt.GetPosPixel() ); + if ( ( nEntry != ITEMPOS_INVALID ) && ( nEntry != nHighlightedItem ) ) + { + ChangeHighlightItem( nEntry, ImplIsMouseFollow() ? FALSE : TRUE ); + } + else + { + KillActivePopup(); + ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); + } +} + +void MenuBarWindow::MouseButtonUp( const MouseEvent& rMEvt ) +{ +} + +void MenuBarWindow::MouseMove( const MouseEvent& rMEvt ) +{ + // Im Move nur Highlighten, wenn schon eins gehighlightet. + if ( rMEvt.IsSynthetic() || rMEvt.IsLeaveWindow() || ( nHighlightedItem == ITEMPOS_INVALID ) ) + return; + + USHORT nEntry = ImplFindEntry( rMEvt.GetPosPixel() ); + if ( ( nEntry != ITEMPOS_INVALID ) && ( nEntry != nHighlightedItem ) + && ( ImplIsMouseFollow() || ( rMEvt.GetButtons() == MOUSE_LEFT ) ) ) + ChangeHighlightItem( nEntry, FALSE ); +} + +void MenuBarWindow::ChangeHighlightItem( USHORT n, BOOL bSelectEntry, BOOL bAllowRestoreFocus ) +{ + // #57934# ggf. das aktive Popup sofort schliessen, damit TH's Hintergrundsicherung funktioniert. + MenuItemData* pNextData = pMenu->pItemList->GetDataFromPos( n ); + if ( pActivePopup && pActivePopup->ImplGetWindow() && ( !pNextData || ( pActivePopup != pNextData->pSubMenu ) ) ) + KillActivePopup(); // pActivePopup ggf. ohne pWin, wenn in Activate() Rescheduled wurde + + // Activate am MenuBar immer nur einmal pro Vorgang... + BOOL bJustActivated = FALSE; + if ( ( nHighlightedItem == ITEMPOS_INVALID ) && ( n != ITEMPOS_INVALID ) ) + { + ImplGetSVData()->maWinData.mbNoDeactivate = TRUE; + nSaveFocusId = Window::SaveFocus(); + pMenu->bInCallback = TRUE; // hier schon setzen, falls Activate ueberladen + pMenu->Activate(); + pMenu->bInCallback = FALSE; + bJustActivated = TRUE; + } + else if ( ( nHighlightedItem != ITEMPOS_INVALID ) && ( n == ITEMPOS_INVALID ) ) + { + pMenu->bInCallback = TRUE; + pMenu->Deactivate(); + pMenu->bInCallback = FALSE; + ULONG nTempFocusId = nSaveFocusId; + nSaveFocusId = 0; + ImplGetSVData()->maWinData.mbNoDeactivate = FALSE; + Window::EndSaveFocus( nTempFocusId, bAllowRestoreFocus ); + } + + if ( nHighlightedItem != ITEMPOS_INVALID ) + HighlightItem( nHighlightedItem, FALSE ); + + nHighlightedItem = (USHORT)n; + DBG_ASSERT( ( nHighlightedItem == ITEMPOS_INVALID ) || pMenu->ImplIsVisible( nHighlightedItem ), "ChangeHighlightItem: Not visible!" ); + HighlightItem( nHighlightedItem, TRUE ); + pMenu->ImplCallHighlight( nHighlightedItem ); + + if ( mbAutoPopup ) + ImplCreatePopup( bSelectEntry ); + + // #58935# #73659# Focus, wenn kein Popup drunter haengt... + if ( bJustActivated && !pActivePopup ) + GrabFocus(); +} + +void MenuBarWindow::HighlightItem( USHORT nPos, BOOL bHighlight ) +{ + long nX = 0; + ULONG nCount = pMenu->pItemList->Count(); + for ( ULONG n = 0; n < nCount; n++ ) + { + MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); + if ( n == nPos ) + { + if ( pData->eType != MENUITEM_SEPARATOR ) + { + if ( bHighlight ) + SetFillColor( GetSettings().GetStyleSettings().GetMenuHighlightColor() ); + else + SetFillColor( GetSettings().GetStyleSettings().GetMenuColor() ); + + DrawRect( Rectangle( Point( nX, 1 ), Size( pData->aSz.Width(), pData->aSz.Height()-2 ) ) ); + pMenu->ImplPaint( this, 0, 0, pData, bHighlight ); + } + return; + } + + nX += pData->aSz.Width(); + } +} + +void MenuBarWindow::KeyInput( const KeyEvent& rKEvent ) +{ + if ( !ImplHandleKeyEvent( rKEvent ) ) + Window::KeyInput( rKEvent ); +} + +BOOL MenuBarWindow::ImplHandleKeyEvent( const KeyEvent& rKEvent, BOOL bFromMenu ) +{ + if ( pMenu->bInCallback ) + return TRUE; // schlucken + + BOOL bDone = FALSE; + USHORT nCode = rKEvent.GetKeyCode().GetCode(); + if ( nCode == KEY_MENU ) + { + mbAutoPopup = FALSE; + if ( nHighlightedItem == ITEMPOS_INVALID ) + { + ChangeHighlightItem( 0, FALSE ); + GrabFocus(); + } + else + { + ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); + nSaveFocusId = 0; + } + bDone = TRUE; + } + else if ( bFromMenu ) + { + if ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) + { + USHORT n = nHighlightedItem; + if ( n == ITEMPOS_INVALID ) + { + if ( nCode == KEY_LEFT) + n = 0; + else + n = pMenu->GetItemCount()-1; + } + + USHORT nLoop = n; + do + { + if ( nCode == KEY_LEFT ) + { + if ( n ) + n--; + else + n = pMenu->GetItemCount()-1; + } + else + { + n++; + if ( n >= pMenu->GetItemCount() ) + n = 0; + } + + MenuItemData* pData = (MenuItemData*)pMenu->GetItemList()->GetDataFromPos( n ); + if ( ( pData->eType != MENUITEM_SEPARATOR ) && pMenu->ImplIsVisible( n ) ) + { + ChangeHighlightItem( n, TRUE ); + break; + } + } while ( n != nLoop ); + bDone = TRUE; + } + else if ( nCode == KEY_RETURN ) + { + KillActivePopup(); + bDone = TRUE; + } + else if ( ( nCode == KEY_UP ) || ( nCode == KEY_DOWN ) ) + { + if ( !mbAutoPopup ) + { + ImplCreatePopup( TRUE ); + mbAutoPopup = TRUE; + } + bDone = TRUE; + } + else if ( nCode == KEY_ESCAPE) + { + ChangeHighlightItem( ITEMPOS_INVALID, FALSE ); + bDone = TRUE; + } + } + + if ( !bDone && ( bFromMenu || rKEvent.GetKeyCode().IsControlMod() ) ) + { + xub_Unicode nCharCode = rKEvent.GetCharCode(); + if ( nCharCode ) + { + USHORT nEntry; + MenuItemData* pData = pMenu->GetItemList()->SearchItem( nCharCode, nEntry ); + if ( pData && (nEntry != ITEMPOS_INVALID) ) + { + mbAutoPopup = TRUE; + ChangeHighlightItem( nEntry, TRUE ); + bDone = TRUE; + } + else + { + // Wegen Systemmenu und anderen System-HotKeys, nur + // eigenstaendige Character-Kombinationen auswerten + USHORT nKeyCode = rKEvent.GetKeyCode().GetCode(); + if ( !nKeyCode || + ((nKeyCode >= KEY_A) && (nKeyCode <= KEY_Z)) ) + Sound::Beep(); + } + } + } + return bDone; +} + +void MenuBarWindow::Paint( const Rectangle& rRect ) +{ + pMenu->ImplPaint( this, 0 ); + if ( nHighlightedItem != ITEMPOS_INVALID ) + HighlightItem( nHighlightedItem, TRUE ); +} + +void MenuBarWindow::Resize() +{ + Size aOutSz = GetOutputSizePixel(); + long n = aOutSz.Height()-4; + long nX = aOutSz.Width()-3; + long nY = 2; + + ULONG nStyle = GetSettings().GetStyleSettings().GetOptions(); + if ( nStyle & (STYLE_OPTION_OS2STYLE | STYLE_OPTION_UNIXSTYLE | STYLE_OPTION_MACSTYLE) ) + { + if ( nStyle & STYLE_OPTION_OS2STYLE ) + { + nX += 3; + nY -= 2; + n += 4; + } + + if ( aFloatBtn.IsVisible() ) + { + nX -= n; + aFloatBtn.SetPosSizePixel( nX, nY, n, n ); + } + if ( aHideBtn.IsVisible() ) + { + nX -= n; + aHideBtn.SetPosSizePixel( nX, nY, n, n ); + } + if ( nStyle & (STYLE_OPTION_MACSTYLE | STYLE_OPTION_UNIXSTYLE) ) + { + if ( aFloatBtn.IsVisible() || aHideBtn.IsVisible() ) + nX -= 3; + } + if ( aCloser.IsVisible() ) + { + nX -= n; + aCloser.SetPosSizePixel( nX, nY, n, n ); + } + } + else + { + if ( aCloser.IsVisible() ) + { + nX -= n; + aCloser.SetPosSizePixel( nX, nY, n, n ); + nX -= 3; + } + if ( aFloatBtn.IsVisible() ) + { + nX -= n; + aFloatBtn.SetPosSizePixel( nX, nY, n, n ); + } + if ( aHideBtn.IsVisible() ) + { + nX -= n; + aHideBtn.SetPosSizePixel( nX, nY, n, n ); + } + } + + if ( nStyle & STYLE_OPTION_OS2STYLE ) + aFloatBtn.SetSymbol( SYMBOL_OS2FLOAT ); + else + aFloatBtn.SetSymbol( SYMBOL_FLOAT ); + if ( nStyle & STYLE_OPTION_OS2STYLE ) + aHideBtn.SetSymbol( SYMBOL_OS2HIDE ); + else + aHideBtn.SetSymbol( SYMBOL_HIDE ); + if ( nStyle & STYLE_OPTION_OS2STYLE ) + aCloser.SetSymbol( SYMBOL_OS2CLOSE ); + else + aCloser.SetSymbol( SYMBOL_CLOSE ); +} + +USHORT MenuBarWindow::ImplFindEntry( const Point& rMousePos ) const +{ + long nX = 0; + USHORT nCount = (USHORT)pMenu->pItemList->Count(); + for ( USHORT n = 0; n < nCount; n++ ) + { + MenuItemData* pData = pMenu->pItemList->GetDataFromPos( n ); + if ( pMenu->ImplIsVisible( n ) ) + { + nX += pData->aSz.Width(); + if ( nX > rMousePos.X() ) + return (USHORT)n; + } + } + return ITEMPOS_INVALID; +} + +void MenuBarWindow::RequestHelp( const HelpEvent& rHEvt ) +{ + USHORT nId = nHighlightedItem; + if ( rHEvt.GetMode() & (HELPMODE_CONTEXT | HELPMODE_EXTENDED) ) + ChangeHighlightItem( ITEMPOS_INVALID, TRUE ); + if( !ImplHandleHelpEvent( this, pMenu, nId, rHEvt ) ) + Window::RequestHelp( rHEvt ); +} + +BOOL MenuBarWindow::QueryDrop( DropEvent& rDEvt ) +{ + Window* pW = GetParent()->ImplGetWindow(); + DropEvent aEvent( ImplTranslateDropEvent( rDEvt, this, pW ) ); + BOOL bRet = pW->QueryDrop( aEvent ); + rDEvt = aEvent; + return bRet; +} + +BOOL MenuBarWindow::Drop( const DropEvent& rDEvt ) +{ + Window* pW = GetParent()->ImplGetWindow(); + return pW->Drop( ImplTranslateDropEvent( rDEvt, this, pW ) ); +} + + +void MenuBarWindow::StateChanged( StateChangedType nType ) +{ + Window::StateChanged( nType ); + + if ( ( nType == STATE_CHANGE_CONTROLFOREGROUND ) || + ( nType == STATE_CHANGE_CONTROLBACKGROUND ) ) + { + ImplInitMenuWindow( this, FALSE ); + Invalidate(); + } +} + +void MenuBarWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + Window::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DATACHANGED_FONTS) || + (rDCEvt.GetType() == DATACHANGED_FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DATACHANGED_SETTINGS) && + (rDCEvt.GetFlags() & SETTINGS_STYLE)) ) + { + ImplInitMenuWindow( this, TRUE ); + // Falls sich der Font geaendert hat. + long nHeight = pMenu->ImplCalcSize( this ).Height(); + SetPosSizePixel( 0, 0, 0, nHeight, WINDOW_POSSIZE_HEIGHT ); + GetParent()->Resize(); + Invalidate(); + Resize(); + } +} + +void MenuBarWindow::LoseFocus() +{ + if ( !HasChildPathFocus( TRUE ) ) + ChangeHighlightItem( ITEMPOS_INVALID, FALSE, FALSE ); +} |