diff options
Diffstat (limited to 'sfx2/source/appl/shutdowniconaqua.mm')
-rw-r--r-- | sfx2/source/appl/shutdowniconaqua.mm | 382 |
1 files changed, 367 insertions, 15 deletions
diff --git a/sfx2/source/appl/shutdowniconaqua.mm b/sfx2/source/appl/shutdowniconaqua.mm index 6b519fcefbe9..9e037fe5726e 100644 --- a/sfx2/source/appl/shutdowniconaqua.mm +++ b/sfx2/source/appl/shutdowniconaqua.mm @@ -21,6 +21,7 @@ #include <unotools/moduleoptions.hxx> #include <unotools/dynamicmenuoptions.hxx> #include <unotools/historyoptions.hxx> +#include <officecfg/Office/Common.hxx> #include <rtl/ustring.hxx> #include <tools/urlobj.hxx> #include <osl/file.h> @@ -31,7 +32,10 @@ #include <sfx2/sfxresid.hxx> #include <sfx2/strings.hrc> #include <vcl/svapp.hxx> -#include "shutdownicon.hxx" +#include <vcl/mnemonic.hxx> +#include <vcl/image.hxx> +#include <svtools/imagemgr.hxx> +#include <shutdownicon.hxx> #include <com/sun/star/util/XStringWidth.hpp> @@ -55,6 +59,8 @@ #define MI_TEMPLATE 8 #define MI_STARTMODULE 9 +#define UNO_TOGGLECURRENTMODULE_COMMAND ".uno:ToggleCurrentModule" + @interface QSMenuExecute : NSObject { } @@ -65,6 +71,8 @@ @implementation QSMenuExecute -(void)executeMenuItem: (NSMenuItem*)pItem { + SolarMutexGuard aGuard; + switch( [pItem tag] ) { case MI_OPEN: @@ -102,6 +110,9 @@ -(void)dockIconClicked: (NSObject*)pSender { (void)pSender; + + SolarMutexGuard aGuard; + // start module ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" ); } @@ -113,6 +124,7 @@ bool ShutdownIcon::IsQuickstarterInstalled() return true; } +static NSArray<NSMenuItem*>* pPreferredMenus = nil; static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil; static QSMenuExecute* pExecute = nil; @@ -147,6 +159,63 @@ class RecentFilesStringLength : public ::cppu::WeakImplHelper< css::util::XStrin } +@interface QSCommandMenuItem : NSMenuItem +{ + OUString m_aCommand; +} +-(void)menuItemTriggered: (id)aSender; +-(void)setCommand: (OUString)aCommand; +@end + +@implementation QSCommandMenuItem + +-(void)menuItemTriggered: (id)aSender +{ + if ( m_aCommand.isEmpty() ) + return; + + SolarMutexGuard aGuard; + + if ( m_aCommand == "vnd.org.libreoffice.recentdocs:ClearRecentFileList" ) + { + // Clearing the recent file list requires an extra step + SvtHistoryOptions::Clear( EHistoryType::PickList, false ); + } + else if ( m_aCommand == ".uno:Open" ) + { + ShutdownIcon::FileOpen(); + return; + } + else if ( m_aCommand == ".uno:ConfigureDialog" ) + { + // Selecting some menu items will cause a crash if there are + // no visibile windows + ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" ); + } + else if ( m_aCommand == UNO_TOGGLECURRENTMODULE_COMMAND ) + { + bool bIsExclusive = officecfg::Office::Common::History::ShowCurrentModuleOnly::get(); + std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); + officecfg::Office::Common::History::ShowCurrentModuleOnly::set(!bIsExclusive, batch); + batch->commit(); + [self setState: bIsExclusive ? NSControlStateValueOff : NSControlStateValueOn]; + return; + } + + // "private:" commands are used for menu items in the File > New menu + if ( m_aCommand.startsWith( "private:" ) || m_aCommand == STARTMODULE_URL ) + ShutdownIcon::OpenURL( m_aCommand, "_default" ); + else + ShutdownIcon::FromCommand( m_aCommand ); +} + +-(void)setCommand: (OUString)aCommand +{ + m_aCommand = aCommand; +} + +@end + @interface RecentMenuDelegate : NSObject <NSMenuDelegate> { std::vector< RecentMenuEntry >* m_pRecentFilesItems; @@ -175,6 +244,8 @@ class RecentFilesStringLength : public ::cppu::WeakImplHelper< css::util::XStrin -(void)menuNeedsUpdate:(NSMenu *)menu { + SolarMutexGuard aGuard; + // clear menu int nItems = [menu numberOfItems]; while( nItems -- ) @@ -202,38 +273,114 @@ class RecentFilesStringLength : public ::cppu::WeakImplHelper< css::util::XStrin // insert new recent items for ( std::vector<RecentMenuEntry>::size_type i = 0; i < m_pRecentFilesItems->size(); i++ ) { + OUStringBuffer aMenuShortCut; + if ( i <= 9 ) + { + if ( i == 9 ) + aMenuShortCut.append( "1~0. " ); + else + { + aMenuShortCut.append( "~N. " ); + aMenuShortCut[ 1 ] = sal_Unicode( i + '1' ); + } + } + else + { + aMenuShortCut.append( OUString::number(sal_Int32( i + 1 ) ) + ". " ); + } + OUString aMenuTitle; INetURLObject aURL( (*m_pRecentFilesItems)[i].aURL ); + NSImage *pImage = nil; if ( aURL.GetProtocol() == INetProtocol::File ) { - // Do handle file URL differently => convert it to a system - // path and abbreviate it with a special function: - OUString aSystemPath( aURL.getFSysPath( FSysStyle::Detect ) ); - OUString aCompactedSystemPath; - - oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, nullptr ); - if ( !nError ) - aMenuTitle = aCompactedSystemPath; - else - aMenuTitle = aSystemPath; + // Do handle file URL differently: don't show the protocol, + // just the file name + aMenuTitle = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset); + + if ( [NSApp respondsToSelector: @selector(createNSImage:)] ) + { + BitmapEx aThumbnail(SvFileInformationManager::GetFileImageId(aURL)); + Size aBmpSize = aThumbnail.GetSizePixel(); + if ( aBmpSize.Width() > 0 && aBmpSize.Height() > 0 ) + { + Image aImage( aThumbnail ); + NSValue *pImageValue = [NSValue valueWithPointer: &aImage]; + pImage = [NSApp performSelector: @selector(createNSImage:) withObject: pImageValue]; + } + } } else { - // Use INetURLObject to abbreviate all other URLs - css::uno::Reference< css::util::XStringWidth > xStringLength( new RecentFilesStringLength() ); - aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DecodeMechanism::Unambiguous ); + // In all other URLs show the protocol name before the file name + aMenuTitle = INetURLObject::GetSchemeName(aURL.GetProtocol()) + ": " + aURL.getName(); } + aMenuShortCut.append( aMenuTitle ); + aMenuTitle = MnemonicGenerator::EraseAllMnemonicChars( aMenuShortCut.makeStringAndClear() ); + if ( aMenuTitle.isEmpty() ) + continue; + + if ( aMenuTitle.endsWith( "...", &aMenuTitle ) ) + aMenuTitle += u"\u2026"; + NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle ) action: @selector(executeRecentEntry:) keyEquivalent: @""]; [pNewItem setTag: i]; [pNewItem setTarget: self]; [pNewItem setEnabled: YES]; + if ( pImage ) + { + [pNewItem setImage: pImage]; + [pImage release]; + } [menu addItem: pNewItem]; [pNewItem autorelease]; } + + if ( [menu numberOfItems] ) + { + TranslateId aId( "STR_CLEAR_RECENT_FILES", "Clear List" ); + OUString aClearList = Translate::get( aId, Translate::Create("fwk") ); + if ( !aClearList.isEmpty() ) + { + [menu addItem: [NSMenuItem separatorItem]]; + + QSCommandMenuItem* pNewItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( aClearList ) action: @selector(menuItemTriggered:) keyEquivalent: @""]; + [pNewItem setCommand: "vnd.org.libreoffice.recentdocs:ClearRecentFileList"]; + [pNewItem setTarget: pNewItem]; + [pNewItem setEnabled: YES]; + [menu addItem: pNewItem]; + [pNewItem autorelease]; + + aId = TranslateId( "STR_TOGGLECURRENTMODULE", "Current Module Only" ); + OUString aToggleCurrentMode = Translate::get( aId, Translate::Create("fwk") ); + if ( !aToggleCurrentMode.isEmpty() ) + { + pNewItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( aToggleCurrentMode ) action: @selector(menuItemTriggered:) keyEquivalent: @""]; + [pNewItem setCommand: UNO_TOGGLECURRENTMODULE_COMMAND]; + [pNewItem setTarget: pNewItem]; + [pNewItem setState: officecfg::Office::Common::History::ShowCurrentModuleOnly::get() ? NSControlStateValueOn : NSControlStateValueOff]; + [pNewItem setEnabled: YES]; + [menu addItem: pNewItem]; + [pNewItem autorelease]; + } + } + } + else + { + TranslateId aId( "STR_NODOCUMENT", "No Documents" ); + OUString aNoDocuments = Translate::get( aId, Translate::Create("fwk") ); + if ( !aNoDocuments.isEmpty() ) + { + NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aNoDocuments ) action: nil keyEquivalent: @""]; + [pNewItem setEnabled: YES]; + [menu addItem: pNewItem]; + [pNewItem autorelease]; + } + } } -(void)executeRecentEntry: (NSMenuItem*)item @@ -311,6 +458,7 @@ static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString [pItem setTarget: pExecute]; [pItem setEnabled: YES]; [i_pMenu addItem: pItem]; + [pItem autorelease]; if( i_pDockMenu ) { @@ -323,6 +471,7 @@ static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString [pItem setTarget: pExecute]; [pItem setEnabled: YES]; [i_pDockMenu addItem: pItem]; + [pItem autorelease]; } } @@ -344,6 +493,206 @@ static void appendRecentMenu( NSMenu* i_pMenu, const OUString& i_rTitle ) [pItem setSubmenu: pRecentMenu]; } +void setKeyEquivalent( const vcl::KeyCode &rKeyCode, NSMenuItem *pNSMenuItem ) +{ + if ( !pNSMenuItem ) + return; + + sal_uInt16 nKeyCode = rKeyCode.GetCode(); + if ( !nKeyCode ) + return; + + sal_Unicode nCommandKey = 0; + 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; + + if ( !nCommandKey ) + return; + + sal_uInt16 nModifier = rKeyCode.GetModifier(); + int nItemModifier = 0; + + if ( nModifier & KEY_SHIFT ) + { + nItemModifier |= NSEventModifierFlagShift; // actually useful only for function keys + if ( nKeyCode >= KEY_A && nKeyCode <= KEY_Z ) + nCommandKey = nKeyCode - KEY_A + 'A'; + } + + if ( nModifier & KEY_MOD1 ) + nItemModifier |= NSEventModifierFlagCommand; + + if ( nModifier & KEY_MOD2 ) + nItemModifier |= NSEventModifierFlagOption; + + if ( nModifier & KEY_MOD3 ) + nItemModifier |= NSEventModifierFlagControl; + + OUString aCommandKey( &nCommandKey, 1 ); + NSString *pCommandKey = [NSString stringWithCharacters: reinterpret_cast< unichar const* >(aCommandKey.getStr()) length: aCommandKey.getLength()]; + [pNSMenuItem setKeyEquivalent: pCommandKey]; + [pNSMenuItem setKeyEquivalentModifierMask: nItemModifier]; +} + +static NSMenu *getNSMenuForVCLMenu( Menu *pMenu ) +{ + NSMenu *pRet = nil; + + if ( !pMenu ) + return pRet; + + pMenu->Activate(); + + sal_uInt16 nItemCount = pMenu->GetItemCount(); + if ( nItemCount ) + { + pRet = [[[NSMenu alloc] initWithTitle: @""] autorelease]; + [pRet setAutoenablesItems: NO]; + for ( sal_uInt16 i = 0; i < nItemCount; i++ ) + { + sal_uInt16 nId = pMenu->GetItemId( i ); + if ( nId && pMenu->IsItemEnabled( nId ) ) + { + OUString aText = MnemonicGenerator::EraseAllMnemonicChars( pMenu->GetItemText( nId ) ); + if ( aText.isEmpty() ) + continue; + + if ( aText.endsWith( "...", &aText ) ) + aText += u"\u2026"; + + // Use a custom menu in place of the Start Center's recent + // documents menu so that the list can be dynamically updated + OUString aCommand = pMenu->GetItemCommand( nId ); + if ( aCommand == ".uno:RecentFileList" ) + { + appendRecentMenu( pRet, aText ); + continue; + } + + NSString *pText = getAutoreleasedString( aText ); + // TODO: use the QSMenuExecute class to connect the command + // string to one of the existing handler functions + QSCommandMenuItem *pNSMenuItem = [[QSCommandMenuItem alloc] initWithTitle: pText action: @selector(menuItemTriggered:) keyEquivalent: @""]; + NSMenu *pNSSubmenu = getNSMenuForVCLMenu( pMenu->GetPopupMenu( nId ) ); + if ( pNSSubmenu && [pNSSubmenu numberOfItems] ) + { + [pNSSubmenu setTitle: pText]; + [pNSMenuItem setSubmenu: pNSSubmenu]; + + if ( aCommand == ".uno:AddDirect" ) + { + SvtModuleOptions aModuleOptions; + if ( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) ) + { + QSCommandMenuItem *pStartModuleMenuItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId( STR_QUICKSTART_STARTCENTER ) ) action: @selector(menuItemTriggered:) keyEquivalent: @"n"]; + [pStartModuleMenuItem setTarget: pStartModuleMenuItem]; + [pStartModuleMenuItem setCommand: STARTMODULE_URL]; + [pNSSubmenu insertItem: pStartModuleMenuItem atIndex: 0]; + [pStartModuleMenuItem autorelease]; + } + } + } + else if ( !aCommand.isEmpty() ) + { + [pNSMenuItem setTarget: pNSMenuItem]; + [pNSMenuItem setCommand: aCommand]; + + // Use the default menu's special "open new file" shortcuts + if ( aCommand == WRITER_URL ) + [pNSMenuItem setKeyEquivalent: @"t"]; + else if ( aCommand == CALC_URL ) + [pNSMenuItem setKeyEquivalent: @"s"]; + else if ( aCommand == IMPRESS_WIZARD_URL ) + [pNSMenuItem setKeyEquivalent: @"p"]; + else if ( aCommand == DRAW_URL ) + [pNSMenuItem setKeyEquivalent: @"d"]; + else if ( aCommand == MATH_URL ) + [pNSMenuItem setKeyEquivalent: @"f"]; + else if ( aCommand == BASE_URL ) + [pNSMenuItem setKeyEquivalent: @"a"]; + else + setKeyEquivalent( pMenu->GetAccelKey( nId ), pNSMenuItem ); + } + + [pRet addItem: pNSMenuItem]; + [pNSMenuItem autorelease]; + } + else if ( pMenu->GetItemType( i ) == MenuItemType::SEPARATOR ) + { + [pRet addItem: [NSMenuItem separatorItem]]; + } + } + } + + pMenu->Deactivate(); + + return pRet; +} + +static void clearDefaultMenuBar() +{ + if( ![NSApp respondsToSelector: @selector(removeFallbackMenuItem:)] ) + return; + + // Remove previous default menu + if ( pDefMenu ) + [NSApp performSelector:@selector(removeFallbackMenuItem:) withObject: pDefMenu]; + + // Remove previous preferred menu + if ( pPreferredMenus && [pPreferredMenus count] ) + { + for ( NSMenuItem *pNSMenuItem in pPreferredMenus ) + [NSApp performSelector:@selector(removeFallbackMenuItem:) withObject: pNSMenuItem]; + } +} + +static void resetMenuBar() +{ + if( ![NSApp respondsToSelector: @selector(addFallbackMenuItem:)] ) + return; + + clearDefaultMenuBar(); + + if ( pPreferredMenus && [pPreferredMenus count] ) + { + for ( NSMenuItem *pNSMenuItem in pPreferredMenus ) + [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pNSMenuItem]; + } + else if ( pDefMenu ) + { + [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu]; + } +} + +void ShutdownIcon::SetDefaultMenuBar( MenuBar *pMenuBar ) +{ + if ( !pMenuBar ) + return; + + SolarMutexGuard aGuard; + + clearDefaultMenuBar(); + if ( pPreferredMenus ) + { + [pPreferredMenus release]; + pPreferredMenus = nil; + } + + NSMenu *pNSMenu = getNSMenuForVCLMenu( pMenuBar ); + if ( pNSMenu && [pNSMenu numberOfItems] ) + { + pPreferredMenus = [NSMutableArray arrayWithArray: [pNSMenu itemArray]]; + [pNSMenu removeAllItems]; + [pPreferredMenus retain]; + } + + resetMenuBar(); +} + extern "C" { @@ -444,7 +793,7 @@ void aqua_init_systray() appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv ); [pDefMenu setSubmenu: pMenu]; - [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu]; + resetMenuBar(); if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] ) { @@ -454,6 +803,9 @@ void aqua_init_systray() } else OSL_FAIL( "addDockMenuItem selector failed on NSApp" ); + + [pMenu autorelease]; + [pDockMenu autorelease]; } else OSL_FAIL( "addFallbackMenuItem selector failed on NSApp" ); |