summaryrefslogtreecommitdiff
path: root/vcl/aqua/source/window/salmenu.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/aqua/source/window/salmenu.cxx')
-rw-r--r--vcl/aqua/source/window/salmenu.cxx960
1 files changed, 960 insertions, 0 deletions
diff --git a/vcl/aqua/source/window/salmenu.cxx b/vcl/aqua/source/window/salmenu.cxx
new file mode 100644
index 000000000000..df1de50d8709
--- /dev/null
+++ b/vcl/aqua/source/window/salmenu.cxx
@@ -0,0 +1,960 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#include "saldata.hxx"
+#include "salinst.h"
+#include "salmenu.h"
+#include "salnsmenu.h"
+#include "salframe.h"
+#include "salbmp.h"
+#include "vcl/svids.hrc"
+#include "vcl/cmdevt.hxx"
+#include "vcl/floatwin.hxx"
+#include "vcl/window.h"
+#include "vcl/window.hxx"
+#include "vcl/svapp.hxx"
+
+#include "rtl/ustrbuf.hxx"
+#include "aqua11ywrapper.h"
+
+const AquaSalMenu* AquaSalMenu::pCurrentMenuBar = NULL;
+
+@interface MainMenuSelector : NSObject
+{
+}
+-(void)showDialog: (int)nDialog;
+-(void)showPreferences: (id)sender;
+-(void)showAbout: (id)sender;
+@end
+
+@implementation MainMenuSelector
+-(void)showDialog: (int)nDialog
+{
+ if( AquaSalMenu::pCurrentMenuBar )
+ {
+ const AquaSalFrame* pFrame = AquaSalMenu::pCurrentMenuBar->mpFrame;
+ if( pFrame && AquaSalFrame::isAlive( pFrame ) )
+ {
+ pFrame->CallCallback( SALEVENT_SHOWDIALOG, reinterpret_cast<void*>(nDialog) );
+ }
+ }
+ else
+ {
+ String aDialog;
+ if( nDialog == SHOWDIALOG_ID_ABOUT )
+ aDialog = String( RTL_CONSTASCII_USTRINGPARAM( "ABOUT" ) );
+ else if( nDialog == SHOWDIALOG_ID_PREFERENCES )
+ aDialog = String( RTL_CONSTASCII_USTRINGPARAM( "PREFERENCES" ) );
+ const ApplicationEvent* pAppEvent = new ApplicationEvent( String(),
+ ApplicationAddress(),
+ ByteString( "SHOWDIALOG" ),
+ aDialog );
+ AquaSalInstance::aAppEventList.push_back( pAppEvent );
+ }
+}
+
+-(void)showPreferences: (id) sender
+{
+ [self showDialog: SHOWDIALOG_ID_PREFERENCES];
+}
+-(void)showAbout: (id) sender
+{
+ [self showDialog: SHOWDIALOG_ID_ABOUT];
+}
+@end
+
+
+// FIXME: currently this is leaked
+static MainMenuSelector* pMainMenuSelector = nil;
+
+static void initAppMenu()
+{
+ static bool bOnce = true;
+ if( bOnce )
+ {
+ bOnce = false;
+
+ ResMgr* pMgr = ImplGetResMgr();
+ if( pMgr )
+ {
+ // get the main menu
+ NSMenu* pMainMenu = [NSApp mainMenu];
+ if( pMainMenu != nil )
+ {
+ // create the action selector
+ pMainMenuSelector = [[MainMenuSelector alloc] init];
+
+ // get the proper submenu
+ NSMenu* pAppMenu = [[pMainMenu itemAtIndex: 0] submenu];
+ if( pAppMenu )
+ {
+ // insert about entry
+ String aAbout( ResId( SV_STDTEXT_ABOUT, *pMgr ) );
+ NSString* pString = CreateNSString( aAbout );
+ NSMenuItem* pNewItem = [pAppMenu insertItemWithTitle: pString
+ action: @selector(showAbout:)
+ keyEquivalent: @""
+ atIndex: 0];
+ if (pString)
+ [pString release];
+ if( pNewItem )
+ {
+ [pNewItem setTarget: pMainMenuSelector];
+ [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 1];
+ }
+
+ // insert preferences entry
+ String aPref( ResId( SV_STDTEXT_PREFERENCES, *pMgr ) );
+ pString = CreateNSString( aPref );
+ pNewItem = [pAppMenu insertItemWithTitle: pString
+ action: @selector(showPreferences:)
+ keyEquivalent: @","
+ atIndex: 2];
+ if (pString)
+ [pString release];
+ if( pNewItem )
+ {
+ [pNewItem setKeyEquivalentModifierMask: NSCommandKeyMask];
+ [pNewItem setTarget: pMainMenuSelector];
+ [pAppMenu insertItem: [NSMenuItem separatorItem] atIndex: 3];
+ }
+
+ // WARNING: ultra ugly code ahead
+
+ // rename standard entries
+ // rename "Services"
+ pNewItem = [pAppMenu itemAtIndex: 4];
+ if( pNewItem )
+ {
+ pString = CreateNSString( String( ResId( SV_MENU_MAC_SERVICES, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+
+ // rename "Hide NewApplication"
+ pNewItem = [pAppMenu itemAtIndex: 6];
+ if( pNewItem )
+ {
+ pString = CreateNSString( String( ResId( SV_MENU_MAC_HIDEAPP, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+
+ // rename "Hide Others"
+ pNewItem = [pAppMenu itemAtIndex: 7];
+ if( pNewItem )
+ {
+ pString = CreateNSString( String( ResId( SV_MENU_MAC_HIDEALL, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+
+ // rename "Show all"
+ pNewItem = [pAppMenu itemAtIndex: 8];
+ if( pNewItem )
+ {
+ pString = CreateNSString( String( ResId( SV_MENU_MAC_SHOWALL, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+
+ // rename "Quit NewApplication"
+ pNewItem = [pAppMenu itemAtIndex: 10];
+ if( pNewItem )
+ {
+ pString = CreateNSString( String( ResId( SV_MENU_MAC_QUITAPP, *pMgr ) ) );
+ [pNewItem setTitle: pString];
+ if( pString )
+ [pString release];
+ }
+ }
+ }
+ }
+ }
+}
+
+// =======================================================================
+
+SalMenu* AquaSalInstance::CreateMenu( BOOL bMenuBar )
+{
+ initAppMenu();
+
+ AquaSalMenu *pAquaSalMenu = new AquaSalMenu( bMenuBar );
+
+ return pAquaSalMenu;
+}
+
+void AquaSalInstance::DestroyMenu( SalMenu* pSalMenu )
+{
+ delete pSalMenu;
+}
+
+SalMenuItem* AquaSalInstance::CreateMenuItem( const SalItemParams* pItemData )
+{
+ if( !pItemData )
+ return NULL;
+
+ AquaSalMenuItem *pSalMenuItem = new AquaSalMenuItem( pItemData );
+
+ return pSalMenuItem;
+}
+
+void AquaSalInstance::DestroyMenuItem( SalMenuItem* pSalMenuItem )
+{
+ delete pSalMenuItem;
+}
+
+
+// =======================================================================
+
+
+/*
+ * AquaSalMenu
+ */
+
+AquaSalMenu::AquaSalMenu( bool bMenuBar ) :
+ mbMenuBar( bMenuBar ),
+ mpMenu( nil ),
+ mpVCLMenu( NULL ),
+ mpFrame( NULL ),
+ mpParentSalMenu( NULL )
+{
+ if( ! mbMenuBar )
+ {
+ mpMenu = [[SalNSMenu alloc] initWithMenu: this];
+ [mpMenu setDelegate: mpMenu];
+ }
+ else
+ {
+ mpMenu = [NSApp mainMenu];
+ }
+ [mpMenu setAutoenablesItems: NO];
+}
+
+AquaSalMenu::~AquaSalMenu()
+{
+ // actually someone should have done AquaSalFrame::SetMenu( NULL )
+ // on our frame, alas it is not so
+ if( mpFrame && AquaSalFrame::isAlive( mpFrame ) && mpFrame->mpMenu == this )
+ const_cast<AquaSalFrame*>(mpFrame)->mpMenu = NULL;
+
+ // this should normally be empty already, but be careful...
+ for( size_t i = 0; i < maButtons.size(); i++ )
+ releaseButtonEntry( maButtons[i] );
+ maButtons.clear();
+
+ // is this leaking in some cases ? the release often leads to a duplicate release
+ // it seems the parent item gets ownership of the menu
+ if( mpMenu )
+ {
+ if( mbMenuBar )
+ {
+ if( pCurrentMenuBar == this )
+ {
+ // if the current menubar gets destroyed, set the default menubar
+ setDefaultMenu();
+ }
+ }
+ else
+ // the system may still hold a reference on mpMenu
+ {
+ // so set the pointer to this AquaSalMenu to NULL
+ // to protect from calling a dead object
+
+ // in ! mbMenuBar case our mpMenu is actually a SalNSMenu*
+ // so we can safely cast here
+ [static_cast<SalNSMenu*>(mpMenu) setSalMenu: NULL];
+ /* #i89860# FIXME:
+ using [autorelease] here (and in AquaSalMenuItem::~AquaSalMenuItem)
+ instead of [release] fixes an occasional crash. That should
+ indicate that we release menus / menu items in the wrong order
+ somewhere, but I could not find that case.
+ */
+ [mpMenu autorelease];
+ }
+ }
+}
+
+sal_Int32 removeUnusedItemsRunner(NSMenu * pMenu)
+{
+ NSArray * elements = [pMenu itemArray];
+ NSEnumerator * it = [elements objectEnumerator];
+ id elem;
+ NSMenuItem * lastDisplayedMenuItem = nil;
+ sal_Int32 drawnItems = 0;
+ bool firstEnabledItemIsNoSeparator = false;
+ while((elem=[it nextObject]) != nil) {
+ NSMenuItem * item = static_cast<NSMenuItem *>(elem);
+ if( (![item isEnabled] && ![item isSeparatorItem]) || ([item isSeparatorItem] && (lastDisplayedMenuItem != nil && [lastDisplayedMenuItem isSeparatorItem])) ) {
+ [[item menu]removeItem:item];
+ } else {
+ if( ! firstEnabledItemIsNoSeparator && [item isSeparatorItem] ) {
+ [[item menu]removeItem:item];
+ } else {
+ firstEnabledItemIsNoSeparator = true;
+ lastDisplayedMenuItem = item;
+ drawnItems++;
+ if( [item hasSubmenu] ) {
+ removeUnusedItemsRunner( [item submenu] );
+ }
+ }
+ }
+ }
+ if( lastDisplayedMenuItem != nil && [lastDisplayedMenuItem isSeparatorItem]) {
+ [[lastDisplayedMenuItem menu]removeItem:lastDisplayedMenuItem];
+ }
+ return drawnItems;
+}
+
+bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow * pWin, const Rectangle& rRect, ULONG nFlags)
+{
+ // do not use native popup menu when AQUA_NATIVE_MENUS is set to FALSE
+ if( ! VisibleMenuBar() ) {
+ return false;
+ }
+
+ // set offsets for positioning
+ const float offset = 9.0;
+
+ // get the pointers
+ AquaSalFrame * pParentAquaSalFrame = (AquaSalFrame *) pWin->ImplGetWindowImpl()->mpRealParent->ImplGetFrame();
+ NSWindow * pParentNSWindow = pParentAquaSalFrame->mpWindow;
+ NSView * pParentNSView = [pParentNSWindow contentView];
+ NSView * pPopupNSView = ((AquaSalFrame *) pWin->ImplGetWindow()->ImplGetFrame())->mpView;
+ NSRect popupFrame = [pPopupNSView frame];
+
+ // since we manipulate the menu below (removing entries)
+ // let's rather make a copy here and work with that
+ NSMenu* pCopyMenu = [mpMenu copy];
+
+ // filter disabled elements
+ removeUnusedItemsRunner( pCopyMenu );
+
+ // create frame rect
+ NSRect displayPopupFrame = NSMakeRect( rRect.nLeft+(offset-1), rRect.nTop+(offset+1), popupFrame.size.width, 0 );
+ pParentAquaSalFrame->VCLToCocoa(displayPopupFrame, false);
+
+ // do the same strange semantics as vcl popup windows to arrive at a frame geometry
+ // in mirrored UI case; best done by actually executing the same code
+ USHORT nArrangeIndex;
+ pWin->SetPosPixel( pWin->ImplCalcPos( pWin, rRect, nFlags, nArrangeIndex ) );
+ displayPopupFrame.origin.x = pWin->ImplGetFrame()->maGeometry.nX - pParentAquaSalFrame->maGeometry.nX + offset;
+ displayPopupFrame.origin.y = pWin->ImplGetFrame()->maGeometry.nY - pParentAquaSalFrame->maGeometry.nY + offset;
+ pParentAquaSalFrame->VCLToCocoa(displayPopupFrame, false);
+
+ // #i111992# if this menu was opened due to a key event, prevent dispatching that yet again
+ if( [pParentNSView respondsToSelector: @selector(clearLastEvent)] )
+ [pParentNSView performSelector:@selector(clearLastEvent)];
+
+ // open popup menu
+ NSPopUpButtonCell * pPopUpButtonCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO];
+ [pPopUpButtonCell setMenu: pCopyMenu];
+ [pPopUpButtonCell selectItem:nil];
+ [AquaA11yWrapper setPopupMenuOpen: YES];
+ [pPopUpButtonCell performClickWithFrame:displayPopupFrame inView:pParentNSView];
+ [pPopUpButtonCell release];
+ [AquaA11yWrapper setPopupMenuOpen: NO];
+
+ // clean up the copy
+ [pCopyMenu release];
+ return true;
+}
+
+int AquaSalMenu::getItemIndexByPos( USHORT nPos ) const
+{
+ int nIndex = 0;
+ if( nPos == MENU_APPEND )
+ nIndex = [mpMenu numberOfItems];
+ else
+ nIndex = sal::static_int_cast<int>( mbMenuBar ? nPos+1 : nPos );
+ return nIndex;
+}
+
+const AquaSalFrame* AquaSalMenu::getFrame() const
+{
+ const AquaSalMenu* pMenu = this;
+ while( pMenu && ! pMenu->mpFrame )
+ pMenu = pMenu->mpParentSalMenu;
+ return pMenu ? pMenu->mpFrame : NULL;
+}
+
+void AquaSalMenu::unsetMainMenu()
+{
+ pCurrentMenuBar = NULL;
+
+ // remove items from main menu
+ NSMenu* pMenu = [NSApp mainMenu];
+ for( int nItems = [pMenu numberOfItems]; nItems > 1; nItems-- )
+ [pMenu removeItemAtIndex: 1];
+}
+
+void AquaSalMenu::setMainMenu()
+{
+ DBG_ASSERT( mbMenuBar, "setMainMenu on non menubar" );
+ if( mbMenuBar )
+ {
+ if( pCurrentMenuBar != this )
+ {
+ unsetMainMenu();
+ // insert our items
+ for( unsigned int i = 0; i < maItems.size(); i++ )
+ {
+ NSMenuItem* pItem = maItems[i]->mpMenuItem;
+ [mpMenu insertItem: pItem atIndex: i+1];
+ }
+ pCurrentMenuBar = this;
+
+ // change status item
+ statusLayout();
+ }
+ enableMainMenu( true );
+ }
+}
+
+void AquaSalMenu::setDefaultMenu()
+{
+ NSMenu* pMenu = [NSApp mainMenu];
+
+ unsetMainMenu();
+
+ // insert default items
+ std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
+ for( unsigned int i = 0, nAddItems = rFallbackMenu.size(); i < nAddItems; i++ )
+ {
+ NSMenuItem* pItem = rFallbackMenu[i];
+ if( [pItem menu] == nil )
+ [pMenu insertItem: pItem atIndex: i+1];
+ }
+}
+
+void AquaSalMenu::enableMainMenu( bool bEnable )
+{
+ NSMenu* pMainMenu = [NSApp mainMenu];
+ if( pMainMenu )
+ {
+ // enable/disable items from main menu
+ int nItems = [pMainMenu numberOfItems];
+ for( int n = 1; n < nItems; n++ )
+ {
+ NSMenuItem* pItem = [pMainMenu itemAtIndex: n];
+ [pItem setEnabled: bEnable ? YES : NO];
+ }
+ }
+}
+
+void AquaSalMenu::addFallbackMenuItem( NSMenuItem* pNewItem )
+{
+ initAppMenu();
+
+ std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
+
+ // prevent duplicate insertion
+ int nItems = rFallbackMenu.size();
+ for( int i = 0; i < nItems; i++ )
+ {
+ if( rFallbackMenu[i] == pNewItem )
+ return;
+ }
+
+ // push the item to the back and retain it
+ [pNewItem retain];
+ rFallbackMenu.push_back( pNewItem );
+
+ if( pCurrentMenuBar == NULL )
+ setDefaultMenu();
+}
+
+void AquaSalMenu::removeFallbackMenuItem( NSMenuItem* pOldItem )
+{
+ std::vector< NSMenuItem* >& rFallbackMenu( GetSalData()->maFallbackMenu );
+
+ // find item
+ unsigned int nItems = rFallbackMenu.size();
+ for( unsigned int i = 0; i < nItems; i++ )
+ {
+ if( rFallbackMenu[i] == pOldItem )
+ {
+ // remove item and release
+ rFallbackMenu.erase( rFallbackMenu.begin() + i );
+ [pOldItem release];
+
+ if( pCurrentMenuBar == NULL )
+ setDefaultMenu();
+
+ return;
+ }
+ }
+}
+
+BOOL AquaSalMenu::VisibleMenuBar()
+{
+ // Enable/disable experimental native menus code?
+ //
+ // To disable native menus, set the environment variable AQUA_NATIVE_MENUS to FALSE
+
+ static const char *pExperimental = getenv ("AQUA_NATIVE_MENUS");
+
+ if ( ImplGetSVData()->mbIsTestTool || (pExperimental && !strcasecmp(pExperimental, "FALSE")) )
+ return FALSE;
+
+ // End of experimental code enable/disable part
+
+ return TRUE;
+}
+
+void AquaSalMenu::SetFrame( const SalFrame *pFrame )
+{
+ mpFrame = static_cast<const AquaSalFrame*>(pFrame);
+}
+
+void AquaSalMenu::InsertItem( SalMenuItem* pSalMenuItem, unsigned nPos )
+{
+ AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem);
+
+ pAquaSalMenuItem->mpParentMenu = this;
+ DBG_ASSERT( pAquaSalMenuItem->mpVCLMenu == NULL ||
+ pAquaSalMenuItem->mpVCLMenu == mpVCLMenu ||
+ mpVCLMenu == NULL,
+ "resetting menu ?" );
+ if( pAquaSalMenuItem->mpVCLMenu )
+ mpVCLMenu = pAquaSalMenuItem->mpVCLMenu;
+
+ if( nPos == MENU_APPEND || nPos == maItems.size() )
+ maItems.push_back( pAquaSalMenuItem );
+ else if( nPos < maItems.size() )
+ maItems.insert( maItems.begin() + nPos, pAquaSalMenuItem );
+ else
+ {
+ DBG_ERROR( "invalid item index in insert" );
+ return;
+ }
+
+ if( ! mbMenuBar || pCurrentMenuBar == this )
+ [mpMenu insertItem: pAquaSalMenuItem->mpMenuItem atIndex: getItemIndexByPos(nPos)];
+}
+
+void AquaSalMenu::RemoveItem( unsigned nPos )
+{
+ AquaSalMenuItem* pRemoveItem = NULL;
+ if( nPos == MENU_APPEND || nPos == (maItems.size()-1) )
+ {
+ pRemoveItem = maItems.back();
+ maItems.pop_back();
+ }
+ else if( nPos < maItems.size() )
+ {
+ pRemoveItem = maItems[ nPos ];
+ maItems.erase( maItems.begin()+nPos );
+ }
+ else
+ {
+ DBG_ERROR( "invalid item index in remove" );
+ return;
+ }
+
+ pRemoveItem->mpParentMenu = NULL;
+
+ if( ! mbMenuBar || pCurrentMenuBar == this )
+ [mpMenu removeItemAtIndex: getItemIndexByPos(nPos)];
+}
+
+void AquaSalMenu::SetSubMenu( SalMenuItem* pSalMenuItem, SalMenu* pSubMenu, unsigned nPos )
+{
+ AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem*>(pSalMenuItem);
+ AquaSalMenu *subAquaSalMenu = static_cast<AquaSalMenu*>(pSubMenu);
+
+ if (subAquaSalMenu)
+ {
+ pAquaSalMenuItem->mpSubMenu = subAquaSalMenu;
+ if( subAquaSalMenu->mpParentSalMenu == NULL )
+ {
+ subAquaSalMenu->mpParentSalMenu = this;
+ [pAquaSalMenuItem->mpMenuItem setSubmenu: subAquaSalMenu->mpMenu];
+
+ // set title of submenu
+ [subAquaSalMenu->mpMenu setTitle: [pAquaSalMenuItem->mpMenuItem title]];
+ }
+ else if( subAquaSalMenu->mpParentSalMenu != this )
+ {
+ // cocoa doesn't allow menus to be submenus of multiple
+ // menu items, so place a copy in the menu item instead ?
+ // let's hope that NSMenu copy does the right thing
+ NSMenu* pCopy = [subAquaSalMenu->mpMenu copy];
+ [pAquaSalMenuItem->mpMenuItem setSubmenu: pCopy];
+
+ // set title of submenu
+ [pCopy setTitle: [pAquaSalMenuItem->mpMenuItem title]];
+ }
+ }
+ else
+ {
+ if( pAquaSalMenuItem->mpSubMenu )
+ {
+ if( pAquaSalMenuItem->mpSubMenu->mpParentSalMenu == this )
+ pAquaSalMenuItem->mpSubMenu->mpParentSalMenu = NULL;
+ }
+ pAquaSalMenuItem->mpSubMenu = NULL;
+ [pAquaSalMenuItem->mpMenuItem setSubmenu: nil];
+ }
+}
+
+void AquaSalMenu::CheckItem( unsigned nPos, BOOL bCheck )
+{
+ if( nPos < maItems.size() )
+ {
+ NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
+ [pItem setState: bCheck ? NSOnState : NSOffState];
+ }
+}
+
+void AquaSalMenu::EnableItem( unsigned nPos, BOOL bEnable )
+{
+ if( nPos < maItems.size() )
+ {
+ NSMenuItem* pItem = maItems[nPos]->mpMenuItem;
+ [pItem setEnabled: bEnable ? YES : NO];
+ }
+}
+
+void AquaSalMenu::SetItemImage( unsigned nPos, SalMenuItem* pSMI, const Image& rImage )
+{
+ AquaSalMenuItem* pSalMenuItem = static_cast<AquaSalMenuItem*>( pSMI );
+ if( ! pSalMenuItem || ! pSalMenuItem->mpMenuItem )
+ return;
+
+ NSImage* pImage = CreateNSImage( rImage );
+
+ [pSalMenuItem->mpMenuItem setImage: pImage];
+ if( pImage )
+ [pImage release];
+}
+
+void AquaSalMenu::SetItemText( unsigned i_nPos, SalMenuItem* i_pSalMenuItem, const XubString& i_rText )
+{
+ if (!i_pSalMenuItem)
+ return;
+
+ AquaSalMenuItem *pAquaSalMenuItem = (AquaSalMenuItem *) i_pSalMenuItem;
+
+ String aText( i_rText );
+
+ // Delete mnemonics
+ aText.EraseAllChars( '~' );
+
+ /* #i90015# until there is a correct solution
+ strip out any appended (.*) in menubar entries
+ */
+ if( mbMenuBar )
+ {
+ xub_StrLen nPos = aText.SearchBackward( sal_Unicode( '(' ) );
+ if( nPos != STRING_NOTFOUND )
+ {
+ xub_StrLen nPos2 = aText.Search( sal_Unicode( ')' ) );
+ if( nPos2 != STRING_NOTFOUND )
+ aText.Erase( nPos, nPos2-nPos+1 );
+ }
+ }
+
+ NSString* pString = CreateNSString( aText );
+ if (pString)
+ {
+ [pAquaSalMenuItem->mpMenuItem setTitle: pString];
+ // if the menu item has a submenu, change its title as well
+ if (pAquaSalMenuItem->mpSubMenu)
+ [pAquaSalMenuItem->mpSubMenu->mpMenu setTitle: pString];
+ [pString release];
+ }
+}
+
+void AquaSalMenu::SetAccelerator( unsigned nPos, SalMenuItem* pSalMenuItem, const KeyCode& rKeyCode, const XubString& rKeyName )
+{
+ USHORT nModifier;
+ sal_Unicode nCommandKey = 0;
+
+ USHORT nKeyCode=rKeyCode.GetCode();
+ if( nKeyCode )
+ {
+ if ((nKeyCode>=KEY_A) && (nKeyCode<=KEY_Z)) // letter A..Z
+ nCommandKey = nKeyCode-KEY_A + 'a';
+ else if ((nKeyCode>=KEY_0) && (nKeyCode<=KEY_9)) // numbers 0..9
+ nCommandKey = nKeyCode-KEY_0 + '0';
+ else if ((nKeyCode>=KEY_F1) && (nKeyCode<=KEY_F26)) // function keys F1..F26
+ nCommandKey = nKeyCode-KEY_F1 + NSF1FunctionKey;
+ else if( nKeyCode == KEY_REPEAT )
+ nCommandKey = NSRedoFunctionKey;
+ else if( nKeyCode == KEY_SPACE )
+ nCommandKey = ' ';
+ else
+ {
+ switch (nKeyCode)
+ {
+ case KEY_ADD:
+ nCommandKey='+';
+ break;
+ case KEY_SUBTRACT:
+ nCommandKey='-';
+ break;
+ case KEY_MULTIPLY:
+ nCommandKey='*';
+ break;
+ case KEY_DIVIDE:
+ nCommandKey='/';
+ break;
+ case KEY_POINT:
+ nCommandKey='.';
+ break;
+ case KEY_LESS:
+ nCommandKey='<';
+ break;
+ case KEY_GREATER:
+ nCommandKey='>';
+ break;
+ case KEY_EQUAL:
+ nCommandKey='=';
+ break;
+ }
+ }
+ }
+ else // not even a code ? nonsense -> ignore
+ return;
+
+ DBG_ASSERT( nCommandKey, "unmapped accelerator key" );
+
+ nModifier=rKeyCode.GetAllModifier();
+
+ // should always use the command key
+ int nItemModifier = 0;
+
+ if (nModifier & KEY_SHIFT)
+ {
+ nItemModifier |= NSShiftKeyMask; // actually useful only for function keys
+ if( nKeyCode >= KEY_A && nKeyCode <= KEY_Z )
+ nCommandKey = nKeyCode - KEY_A + 'A';
+ }
+
+ if (nModifier & KEY_MOD1)
+ nItemModifier |= NSCommandKeyMask;
+
+ if(nModifier & KEY_MOD2)
+ nItemModifier |= NSAlternateKeyMask;
+
+ if(nModifier & KEY_MOD3)
+ nItemModifier |= NSControlKeyMask;
+
+ AquaSalMenuItem *pAquaSalMenuItem = (AquaSalMenuItem *) pSalMenuItem;
+ NSString* pString = CreateNSString( rtl::OUString( &nCommandKey, 1 ) );
+ [pAquaSalMenuItem->mpMenuItem setKeyEquivalent: pString];
+ [pAquaSalMenuItem->mpMenuItem setKeyEquivalentModifierMask: nItemModifier];
+ if (pString)
+ [pString release];
+}
+
+void AquaSalMenu::GetSystemMenuData( SystemMenuData* pData )
+{
+}
+
+AquaSalMenu::MenuBarButtonEntry* AquaSalMenu::findButtonItem( USHORT i_nItemId )
+{
+ for( size_t i = 0; i < maButtons.size(); ++i )
+ {
+ if( maButtons[i].maButton.mnId == i_nItemId )
+ return &maButtons[i];
+ }
+ return NULL;
+}
+
+void AquaSalMenu::statusLayout()
+{
+ if( GetSalData()->mpStatusItem )
+ {
+ NSView* pView = [GetSalData()->mpStatusItem view];
+ if( [pView isMemberOfClass: [OOStatusItemView class]] ) // well of course it is
+ [(OOStatusItemView*)pView layout];
+ else
+ DBG_ERROR( "someone stole our status view" );
+ }
+}
+
+void AquaSalMenu::releaseButtonEntry( MenuBarButtonEntry& i_rEntry )
+{
+ if( i_rEntry.mpNSImage )
+ {
+ [i_rEntry.mpNSImage release];
+ i_rEntry.mpNSImage = nil;
+ }
+ if( i_rEntry.mpToolTipString )
+ {
+ [i_rEntry.mpToolTipString release];
+ i_rEntry.mpToolTipString = nil;
+ }
+}
+
+bool AquaSalMenu::AddMenuBarButton( const SalMenuButtonItem& i_rNewItem )
+{
+ if( ! mbMenuBar || ! VisibleMenuBar() )
+ return false;
+
+ MenuBarButtonEntry* pEntry = findButtonItem( i_rNewItem.mnId );
+ if( pEntry )
+ {
+ releaseButtonEntry( *pEntry );
+ pEntry->maButton = i_rNewItem;
+ pEntry->mpNSImage = CreateNSImage( i_rNewItem.maImage );
+ if( i_rNewItem.maToolTipText.getLength() )
+ pEntry->mpToolTipString = CreateNSString( i_rNewItem.maToolTipText );
+ }
+ else
+ {
+ maButtons.push_back( MenuBarButtonEntry( i_rNewItem ) );
+ maButtons.back().mpNSImage = CreateNSImage( i_rNewItem.maImage );
+ maButtons.back().mpToolTipString = CreateNSString( i_rNewItem.maToolTipText );
+ }
+
+ // lazy create status item
+ SalData::getStatusItem();
+
+ if( pCurrentMenuBar == this )
+ statusLayout();
+
+ return true;
+}
+
+void AquaSalMenu::RemoveMenuBarButton( USHORT i_nId )
+{
+ MenuBarButtonEntry* pEntry = findButtonItem( i_nId );
+ if( pEntry )
+ {
+ releaseButtonEntry( *pEntry );
+ // note: vector guarantees that its contents are in a plain array
+ maButtons.erase( maButtons.begin() + (pEntry - &maButtons[0]) );
+ }
+
+ if( pCurrentMenuBar == this )
+ statusLayout();
+}
+
+Rectangle AquaSalMenu::GetMenuBarButtonRectPixel( USHORT i_nItemId, SalFrame* i_pReferenceFrame )
+{
+ if( GetSalData()->mnSystemVersion < VER_LEOPARD )
+ return Rectangle( Point( -1, -1 ), Size( 1, 1 ) );
+
+ if( ! i_pReferenceFrame || ! AquaSalFrame::isAlive( static_cast<AquaSalFrame*>(i_pReferenceFrame) ) )
+ return Rectangle();
+
+ MenuBarButtonEntry* pEntry = findButtonItem( i_nItemId );
+
+ if( ! pEntry )
+ return Rectangle();
+
+ NSStatusItem* pItem = SalData::getStatusItem();
+ if( ! pItem )
+ return Rectangle();
+
+ NSView* pView = [pItem view];
+ if( ! pView )
+ return Rectangle();
+ NSWindow* pWin = [pView window];
+ if( ! pWin )
+ return Rectangle();
+
+ NSRect aRect = [pWin frame];
+ aRect.origin = [pWin convertBaseToScreen: NSMakePoint( 0, 0 )];
+
+ // make coordinates relative to reference frame
+ static_cast<AquaSalFrame*>(i_pReferenceFrame)->CocoaToVCL( aRect.origin );
+ aRect.origin.x -= i_pReferenceFrame->maGeometry.nX;
+ aRect.origin.y -= i_pReferenceFrame->maGeometry.nY + aRect.size.height;
+
+ return Rectangle( Point(static_cast<long int>(aRect.origin.x),
+ static_cast<long int>(aRect.origin.y)
+ ),
+ Size( static_cast<long int>(aRect.size.width),
+ static_cast<long int>(aRect.size.height)
+ )
+ );
+}
+
+// =======================================================================
+
+/*
+ * SalMenuItem
+ */
+
+AquaSalMenuItem::AquaSalMenuItem( const SalItemParams* pItemData ) :
+ mnId( pItemData->nId ),
+ mpVCLMenu( pItemData->pMenu ),
+ mpParentMenu( NULL ),
+ mpSubMenu( NULL ),
+ mpMenuItem( nil )
+{
+ String aText( pItemData->aText );
+
+ // Delete mnemonics
+ aText.EraseAllChars( '~' );
+
+ if (pItemData->eType == MENUITEM_SEPARATOR)
+ {
+ mpMenuItem = [NSMenuItem separatorItem];
+ // these can go occasionally go in and out of a menu, ensure their lifecycle
+ // also for the release in AquaSalMenuItem destructor
+ [mpMenuItem retain];
+ }
+ else
+ {
+ mpMenuItem = [[SalNSMenuItem alloc] initWithMenuItem: this];
+ [mpMenuItem setEnabled: YES];
+ NSString* pString = CreateNSString( aText );
+ if (pString)
+ {
+ [mpMenuItem setTitle: pString];
+ [pString release];
+ }
+ // anything but a separator should set a menu to dispatch to
+ DBG_ASSERT( mpVCLMenu, "no menu" );
+ }
+}
+
+AquaSalMenuItem::~AquaSalMenuItem()
+{
+ /* #i89860# FIXME:
+ using [autorelease] here (and in AquaSalMenu:::~AquaSalMenu) instead of
+ [release] fixes an occasional crash. That should indicate that we release
+ menus / menu items in the wrong order somewhere, but I
+ could not find that case.
+ */
+ if( mpMenuItem )
+ [mpMenuItem autorelease];
+}
+
+// -------------------------------------------------------------------
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */