1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2/* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 21#include <unotools/moduleoptions.hxx> 22#include <unotools/dynamicmenuoptions.hxx> 23#include <unotools/historyoptions.hxx> 24#include <officecfg/Office/Common.hxx> 25#include <rtl/ustring.hxx> 26#include <tools/urlobj.hxx> 27#include <osl/file.h> 28#include <osl/diagnose.h> 29#include <comphelper/sequenceashashmap.hxx> 30#include <sfx2/app.hxx> 31#include <sal/macros.h> 32#include <sfx2/sfxresid.hxx> 33#include <sfx2/strings.hrc> 34#include <vcl/svapp.hxx> 35#include <vcl/mnemonic.hxx> 36#include <vcl/image.hxx> 37#include <svtools/imagemgr.hxx> 38#include <shutdownicon.hxx> 39 40#include <com/sun/star/util/XStringWidth.hpp> 41 42#include <cppuhelper/implbase.hxx> 43 44#include <set> 45#include <vector> 46 47#include <premac.h> 48#include <objc/objc-runtime.h> 49#include <Cocoa/Cocoa.h> 50#include <postmac.h> 51 52#define MI_OPEN 1 53#define MI_WRITER 2 54#define MI_CALC 3 55#define MI_IMPRESS 4 56#define MI_DRAW 5 57#define MI_BASE 6 58#define MI_MATH 7 59#define MI_TEMPLATE 8 60#define MI_STARTMODULE 9 61 62#define UNO_TOGGLECURRENTMODULE_COMMAND ".uno:ToggleCurrentModule" 63 64@interface QSMenuExecute : NSObject 65{ 66} 67-(void)executeMenuItem: (NSMenuItem*)pItem; 68-(void)dockIconClicked: (NSObject*)pSender; 69@end 70 71@implementation QSMenuExecute 72-(void)executeMenuItem: (NSMenuItem*)pItem 73{ 74 SolarMutexGuard aGuard; 75 76 switch( [pItem tag] ) 77 { 78 case MI_OPEN: 79 ShutdownIcon::FileOpen(); 80 break; 81 case MI_WRITER: 82 ShutdownIcon::OpenURL( WRITER_URL, "_default" ); 83 break; 84 case MI_CALC: 85 ShutdownIcon::OpenURL( CALC_URL, "_default" ); 86 break; 87 case MI_IMPRESS: 88 ShutdownIcon::OpenURL( IMPRESS_URL, "_default" ); 89 break; 90 case MI_DRAW: 91 ShutdownIcon::OpenURL( DRAW_URL, "_default" ); 92 break; 93 case MI_BASE: 94 ShutdownIcon::OpenURL( BASE_URL, "_default" ); 95 break; 96 case MI_MATH: 97 ShutdownIcon::OpenURL( MATH_URL, "_default" ); 98 break; 99 case MI_TEMPLATE: 100 ShutdownIcon::FromTemplate(); 101 break; 102 case MI_STARTMODULE: 103 ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" ); 104 break; 105 default: 106 break; 107 } 108} 109 110-(void)dockIconClicked: (NSObject*)pSender 111{ 112 (void)pSender; 113 114 SolarMutexGuard aGuard; 115 116 // start module 117 ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" ); 118} 119 120@end 121 122bool ShutdownIcon::IsQuickstarterInstalled() 123{ 124 return true; 125} 126 127static NSArray<NSMenuItem*>* pPreferredMenus = nil; 128static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil; 129static QSMenuExecute* pExecute = nil; 130 131static std::set< OUString > aShortcuts; 132 133static NSString* getAutoreleasedString( const OUString& rStr ) 134{ 135 return [[[NSString alloc] initWithCharacters: reinterpret_cast<unichar const *>(rStr.getStr()) length: rStr.getLength()] autorelease]; 136} 137 138namespace { 139 140struct RecentMenuEntry 141{ 142 OUString aURL; 143 OUString aFilter; 144 OUString aTitle; 145 OUString aPassword; 146}; 147 148} 149 150@interface QSCommandMenuItem : NSMenuItem 151{ 152 OUString m_aCommand; 153} 154-(void)menuItemTriggered: (id)aSender; 155-(void)setCommand: (OUString)aCommand; 156@end 157 158@implementation QSCommandMenuItem 159 160-(void)menuItemTriggered: (id)aSender 161{ 162 if ( m_aCommand.isEmpty() ) 163 return; 164 165 SolarMutexGuard aGuard; 166 167 if ( m_aCommand == "vnd.org.libreoffice.recentdocs:ClearRecentFileList" ) 168 { 169 // Clearing the recent file list requires an extra step 170 SvtHistoryOptions::Clear( EHistoryType::PickList, false ); 171 } 172 else if ( m_aCommand == ".uno:Open" ) 173 { 174 ShutdownIcon::FileOpen(); 175 return; 176 } 177 else if ( m_aCommand == ".uno:ConfigureDialog" ) 178 { 179 // Selecting some menu items will cause a crash if there are 180 // no visible windows 181 ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" ); 182 } 183 else if ( m_aCommand == UNO_TOGGLECURRENTMODULE_COMMAND ) 184 { 185 bool bIsExclusive = officecfg::Office::Common::History::ShowCurrentModuleOnly::get(); 186 std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create()); 187 officecfg::Office::Common::History::ShowCurrentModuleOnly::set(!bIsExclusive, batch); 188 batch->commit(); 189 [self setState: bIsExclusive ? NSControlStateValueOff : NSControlStateValueOn]; 190 return; 191 } 192 193 // "private:" commands are used for menu items in the File > New menu 194 if ( m_aCommand.startsWith( "private:" ) || m_aCommand == STARTMODULE_URL ) 195 ShutdownIcon::OpenURL( m_aCommand, "_default" ); 196 else 197 ShutdownIcon::FromCommand( m_aCommand ); 198} 199 200-(void)setCommand: (OUString)aCommand 201{ 202 m_aCommand = aCommand; 203} 204 205@end 206 207@interface RecentMenuDelegate : NSObject <NSMenuDelegate> 208{ 209 std::vector< RecentMenuEntry >* m_pRecentFilesItems; 210} 211-(id)init; 212-(void)dealloc; 213-(void)menuNeedsUpdate:(NSMenu *)menu; 214-(void)executeRecentEntry: (NSMenuItem*)item; 215@end 216 217@implementation RecentMenuDelegate 218-(id)init 219{ 220 if( (self = [super init]) ) 221 { 222 m_pRecentFilesItems = new std::vector< RecentMenuEntry >(); 223 } 224 return self; 225} 226 227-(void)dealloc 228{ 229 delete m_pRecentFilesItems; 230 [super dealloc]; 231} 232 233-(void)menuNeedsUpdate:(NSMenu *)menu 234{ 235 SolarMutexGuard aGuard; 236 237 // clear menu 238 int nItems = [menu numberOfItems]; 239 while( nItems -- ) 240 [menu removeItemAtIndex: 0]; 241 242 // update recent item list 243 std::vector< SvtHistoryOptions::HistoryItem > aHistoryList( SvtHistoryOptions::GetList( EHistoryType::PickList ) ); 244 245 int nPickListMenuItems = ( aHistoryList.size() > 99 ) ? 99 : aHistoryList.size(); 246 247 m_pRecentFilesItems->clear(); 248 if( nPickListMenuItems > 0 ) 249 { 250 for ( int i = 0; i < nPickListMenuItems; i++ ) 251 { 252 const SvtHistoryOptions::HistoryItem & rPickListEntry = aHistoryList[i]; 253 RecentMenuEntry aRecentFile; 254 aRecentFile.aURL = rPickListEntry.sURL; 255 aRecentFile.aFilter = rPickListEntry.sFilter; 256 aRecentFile.aTitle = rPickListEntry.sTitle; 257 m_pRecentFilesItems->push_back( aRecentFile ); 258 } 259 } 260 261 // insert new recent items 262 for ( std::vector<RecentMenuEntry>::size_type i = 0; i < m_pRecentFilesItems->size(); i++ ) 263 { 264 OUStringBuffer aMenuShortCut; 265 if ( i <= 9 ) 266 { 267 if ( i == 9 ) 268 aMenuShortCut.append( "1~0. " ); 269 else 270 { 271 aMenuShortCut.append( "~N. " ); 272 aMenuShortCut[ 1 ] = sal_Unicode( i + '1' ); 273 } 274 } 275 else 276 { 277 aMenuShortCut.append( OUString::number(sal_Int32( i + 1 ) ) + ". " ); 278 } 279 280 OUString aMenuTitle; 281 INetURLObject aURL( (*m_pRecentFilesItems)[i].aURL ); 282 NSImage *pImage = nil; 283 284 if ( aURL.GetProtocol() == INetProtocol::File ) 285 { 286 // Do handle file URL differently: don't show the protocol, 287 // just the file name 288 aMenuTitle = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset); 289 290 if ( [NSApp respondsToSelector: @selector(createNSImage:)] ) 291 { 292 Bitmap aThumbnail(SvFileInformationManager::GetFileImageId(aURL)); 293 Size aBmpSize = aThumbnail.GetSizePixel(); 294 if ( aBmpSize.Width() > 0 && aBmpSize.Height() > 0 ) 295 { 296 Image aImage( aThumbnail ); 297 NSValue *pImageValue = [NSValue valueWithPointer: &aImage]; 298 pImage = [NSApp performSelector: @selector(createNSImage:) withObject: pImageValue]; 299 } 300 } 301 } 302 else 303 { 304 // In all other URLs show the protocol name before the file name 305 aMenuTitle = INetURLObject::GetSchemeName(aURL.GetProtocol()) + ": " + aURL.getName(); 306 } 307 308 aMenuShortCut.append( aMenuTitle ); 309 aMenuTitle = MnemonicGenerator::EraseAllMnemonicChars( aMenuShortCut.makeStringAndClear() ); 310 if ( aMenuTitle.isEmpty() ) 311 continue; 312 313 if ( aMenuTitle.endsWith( "...", &aMenuTitle ) ) 314 aMenuTitle += u"\u2026"; 315 316 NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle ) 317 action: @selector(executeRecentEntry:) 318 keyEquivalent: @""]; 319 [pNewItem setTag: i]; 320 [pNewItem setTarget: self]; 321 [pNewItem setEnabled: YES]; 322 if ( pImage ) 323 { 324 [pNewItem setImage: pImage]; 325 [pImage release]; 326 } 327 [menu addItem: pNewItem]; 328 [pNewItem autorelease]; 329 } 330 331 if ( [menu numberOfItems] ) 332 { 333 TranslateId aId( "STR_CLEAR_RECENT_FILES", "Clear List" ); 334 OUString aClearList = Translate::get( aId, Translate::Create("fwk") ); 335 if ( !aClearList.isEmpty() ) 336 { 337 [menu addItem: [NSMenuItem separatorItem]]; 338 339 QSCommandMenuItem* pNewItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( aClearList ) action: @selector(menuItemTriggered:) keyEquivalent: @""]; 340 [pNewItem setCommand: "vnd.org.libreoffice.recentdocs:ClearRecentFileList"]; 341 [pNewItem setTarget: pNewItem]; 342 [pNewItem setEnabled: YES]; 343 [menu addItem: pNewItem]; 344 [pNewItem autorelease]; 345 346 aId = TranslateId( "STR_TOGGLECURRENTMODULE", "Current Module Only" ); 347 OUString aToggleCurrentMode = Translate::get( aId, Translate::Create("fwk") ); 348 if ( !aToggleCurrentMode.isEmpty() ) 349 { 350 pNewItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( aToggleCurrentMode ) action: @selector(menuItemTriggered:) keyEquivalent: @""]; 351 [pNewItem setCommand: UNO_TOGGLECURRENTMODULE_COMMAND]; 352 [pNewItem setTarget: pNewItem]; 353 [pNewItem setState: officecfg::Office::Common::History::ShowCurrentModuleOnly::get() ? NSControlStateValueOn : NSControlStateValueOff]; 354 [pNewItem setEnabled: YES]; 355 [menu addItem: pNewItem]; 356 [pNewItem autorelease]; 357 } 358 } 359 } 360 else 361 { 362 TranslateId aId( "STR_NODOCUMENT", "No Documents" ); 363 OUString aNoDocuments = Translate::get( aId, Translate::Create("fwk") ); 364 if ( !aNoDocuments.isEmpty() ) 365 { 366 NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aNoDocuments ) action: nil keyEquivalent: @""]; 367 [pNewItem setEnabled: YES]; 368 [menu addItem: pNewItem]; 369 [pNewItem autorelease]; 370 } 371 } 372} 373 374-(void)executeRecentEntry: (NSMenuItem*)item 375{ 376 sal_Int32 nIndex = [item tag]; 377 if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) ) 378 { 379 const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ]; 380 int NUM_OF_PICKLIST_ARGS = 3; 381 css::uno::Sequence< css::beans::PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS ); 382 css::beans::PropertyValue* pArgsList = aArgsList.getArray(); 383 384 pArgsList[0].Name = "Referer"; 385 pArgsList[0].Value <<= OUString( "private:user" ); 386 387 // documents in the picklist will never be opened as templates 388 pArgsList[1].Name = "AsTemplate"; 389 pArgsList[1].Value <<= false; 390 391 OUString aFilter( rRecentFile.aFilter ); 392 sal_Int32 nPos = aFilter.indexOf( '|' ); 393 if ( nPos >= 0 ) 394 { 395 OUString aFilterOptions; 396 397 if ( nPos < ( aFilter.getLength() - 1 ) ) 398 aFilterOptions = aFilter.copy( nPos+1 ); 399 400 pArgsList[2].Name = "FilterOptions"; 401 pArgsList[2].Value <<= aFilterOptions; 402 403 aFilter = aFilter.copy( 0, nPos-1 ); 404 aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS ); 405 pArgsList = aArgsList.getArray(); 406 } 407 408 pArgsList[NUM_OF_PICKLIST_ARGS-1].Name = "FilterName"; 409 pArgsList[NUM_OF_PICKLIST_ARGS-1].Value <<= aFilter; 410 411 ShutdownIcon::OpenURL( rRecentFile.aURL, "_default", aArgsList ); 412 } 413} 414@end 415 416static RecentMenuDelegate* pRecentDelegate = nil; 417 418static OUString getShortCut( const OUString& i_rTitle ) 419{ 420 // create shortcut 421 OUString aKeyEquiv; 422 for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ ) 423 { 424 OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() ); 425 if( aShortcuts.find( aShortcut ) == aShortcuts.end() ) 426 { 427 aShortcuts.insert( aShortcut ); 428 aKeyEquiv = aShortcut; 429 break; 430 } 431 } 432 433 return aKeyEquiv; 434} 435 436static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle, int i_nTag, const OUString& i_rKeyEquiv ) 437{ 438 if( ! i_rTitle.getLength() ) 439 return; 440 441 NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle ) 442 action: @selector(executeMenuItem:) 443 keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"") 444 ]; 445 [pItem setTag: i_nTag]; 446 [pItem setTarget: pExecute]; 447 [pItem setEnabled: YES]; 448 [i_pMenu addItem: pItem]; 449 [pItem autorelease]; 450 451 if( i_pDockMenu ) 452 { 453 // create a similar entry in the dock menu 454 pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle ) 455 action: @selector(executeMenuItem:) 456 keyEquivalent: @"" 457 ]; 458 [pItem setTag: i_nTag]; 459 [pItem setTarget: pExecute]; 460 [pItem setEnabled: YES]; 461 [i_pDockMenu addItem: pItem]; 462 [pItem autorelease]; 463 } 464} 465 466static void appendRecentMenu( NSMenu* i_pMenu, const OUString& i_rTitle ) 467{ 468 if( ! pRecentDelegate ) 469 pRecentDelegate = [[RecentMenuDelegate alloc] init]; 470 471 NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle ) 472 action: @selector(executeMenuItem:) 473 keyEquivalent: @"" 474 ]; 475 [pItem setEnabled: YES]; 476 NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ]; 477 478 [pRecentMenu setDelegate: pRecentDelegate]; 479 480 [pRecentMenu setAutoenablesItems: NO]; 481 [pItem setSubmenu: pRecentMenu]; 482} 483 484static void setKeyEquivalent( const vcl::KeyCode &rKeyCode, NSMenuItem *pNSMenuItem ) 485{ 486 if ( !pNSMenuItem ) 487 return; 488 489 sal_uInt16 nKeyCode = rKeyCode.GetCode(); 490 if ( !nKeyCode ) 491 return; 492 493 sal_Unicode nCommandKey = 0; 494 if ((nKeyCode>=KEY_A) && (nKeyCode<=KEY_Z)) // letter A..Z 495 nCommandKey = nKeyCode - KEY_A + 'a'; 496 else if ((nKeyCode>=KEY_0) && (nKeyCode<=KEY_9)) // numbers 0..9 497 nCommandKey = nKeyCode - KEY_0 + '0'; 498 else if ((nKeyCode>=KEY_F1) && (nKeyCode<=KEY_F26)) // function keys F1..F26 499 nCommandKey = nKeyCode - KEY_F1 + NSF1FunctionKey; 500 501 if ( !nCommandKey ) 502 return; 503 504 sal_uInt16 nModifier = rKeyCode.GetModifier(); 505 int nItemModifier = 0; 506 507 if ( nModifier & KEY_SHIFT ) 508 { 509 nItemModifier |= NSEventModifierFlagShift; // actually useful only for function keys 510 if ( nKeyCode >= KEY_A && nKeyCode <= KEY_Z ) 511 nCommandKey = nKeyCode - KEY_A + 'A'; 512 } 513 514 if ( nModifier & KEY_MOD1 ) 515 nItemModifier |= NSEventModifierFlagCommand; 516 517 if ( nModifier & KEY_MOD2 ) 518 nItemModifier |= NSEventModifierFlagOption; 519 520 if ( nModifier & KEY_MOD3 ) 521 nItemModifier |= NSEventModifierFlagControl; 522 523 OUString aCommandKey( &nCommandKey, 1 ); 524 NSString *pCommandKey = [NSString stringWithCharacters: reinterpret_cast< unichar const* >(aCommandKey.getStr()) length: aCommandKey.getLength()]; 525 [pNSMenuItem setKeyEquivalent: pCommandKey]; 526 [pNSMenuItem setKeyEquivalentModifierMask: nItemModifier]; 527} 528 529static NSMenu *getNSMenuForVCLMenu( Menu *pMenu ) 530{ 531 NSMenu *pRet = nil; 532 533 if ( !pMenu ) 534 return pRet; 535 536 pMenu->Activate(); 537 538 sal_uInt16 nItemCount = pMenu->GetItemCount(); 539 if ( nItemCount ) 540 { 541 pRet = [[[NSMenu alloc] initWithTitle: @""] autorelease]; 542 [pRet setAutoenablesItems: NO]; 543 for ( sal_uInt16 i = 0; i < nItemCount; i++ ) 544 { 545 sal_uInt16 nId = pMenu->GetItemId( i ); 546 if ( nId && pMenu->IsItemEnabled( nId ) ) 547 { 548 OUString aText = MnemonicGenerator::EraseAllMnemonicChars( pMenu->GetItemText( nId ) ); 549 if ( aText.isEmpty() ) 550 continue; 551 552 if ( aText.endsWith( "...", &aText ) ) 553 aText += u"\u2026"; 554 555 // Use a custom menu in place of the Start Center's recent 556 // documents menu so that the list can be dynamically updated 557 OUString aCommand = pMenu->GetItemCommand( nId ); 558 if ( aCommand == ".uno:RecentFileList" ) 559 { 560 appendRecentMenu( pRet, aText ); 561 continue; 562 } 563 564 NSString *pText = getAutoreleasedString( aText ); 565 // TODO: use the QSMenuExecute class to connect the command 566 // string to one of the existing handler functions 567 QSCommandMenuItem *pNSMenuItem = [[QSCommandMenuItem alloc] initWithTitle: pText action: @selector(menuItemTriggered:) keyEquivalent: @""]; 568 NSMenu *pNSSubmenu = getNSMenuForVCLMenu( pMenu->GetPopupMenu( nId ) ); 569 if ( pNSSubmenu && [pNSSubmenu numberOfItems] ) 570 { 571 [pNSSubmenu setTitle: pText]; 572 [pNSMenuItem setSubmenu: pNSSubmenu]; 573 574 if ( aCommand == ".uno:AddDirect" ) 575 { 576 SvtModuleOptions aModuleOptions; 577 if ( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) ) 578 { 579 QSCommandMenuItem *pStartModuleMenuItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId( STR_QUICKSTART_STARTCENTER ) ) action: @selector(menuItemTriggered:) keyEquivalent: @"n"]; 580 [pStartModuleMenuItem setTarget: pStartModuleMenuItem]; 581 [pStartModuleMenuItem setCommand: STARTMODULE_URL]; 582 [pNSSubmenu insertItem: pStartModuleMenuItem atIndex: 0]; 583 [pStartModuleMenuItem autorelease]; 584 } 585 } 586 } 587 else if ( !aCommand.isEmpty() ) 588 { 589 [pNSMenuItem setTarget: pNSMenuItem]; 590 [pNSMenuItem setCommand: aCommand]; 591 592 // Use the default menu's special "open new file" shortcuts 593 if ( aCommand == WRITER_URL ) 594 [pNSMenuItem setKeyEquivalent: @"t"]; 595 else if ( aCommand == CALC_URL ) 596 [pNSMenuItem setKeyEquivalent: @"s"]; 597 else if ( aCommand == IMPRESS_WIZARD_URL ) 598 [pNSMenuItem setKeyEquivalent: @"p"]; 599 else if ( aCommand == DRAW_URL ) 600 [pNSMenuItem setKeyEquivalent: @"d"]; 601 else if ( aCommand == MATH_URL ) 602 [pNSMenuItem setKeyEquivalent: @"f"]; 603 else if ( aCommand == BASE_URL ) 604 [pNSMenuItem setKeyEquivalent: @"a"]; 605 else 606 setKeyEquivalent( pMenu->GetAccelKey( nId ), pNSMenuItem ); 607 } 608 609 [pRet addItem: pNSMenuItem]; 610 [pNSMenuItem autorelease]; 611 } 612 else if ( pMenu->GetItemType( i ) == MenuItemType::SEPARATOR ) 613 { 614 [pRet addItem: [NSMenuItem separatorItem]]; 615 } 616 } 617 } 618 619 pMenu->Deactivate(); 620 621 return pRet; 622} 623 624static void clearDefaultMenuBar() 625{ 626 if( ![NSApp respondsToSelector: @selector(removeFallbackMenuItem:)] ) 627 return; 628 629 // Remove previous default menu 630 if ( pDefMenu ) 631 [NSApp performSelector:@selector(removeFallbackMenuItem:) withObject: pDefMenu]; 632 633 // Remove previous preferred menu 634 if ( pPreferredMenus && [pPreferredMenus count] ) 635 { 636 for ( NSMenuItem *pNSMenuItem in pPreferredMenus ) 637 [NSApp performSelector:@selector(removeFallbackMenuItem:) withObject: pNSMenuItem]; 638 } 639} 640 641static void resetMenuBar() 642{ 643 if( ![NSApp respondsToSelector: @selector(addFallbackMenuItem:)] ) 644 return; 645 646 clearDefaultMenuBar(); 647 648 if ( pPreferredMenus && [pPreferredMenus count] ) 649 { 650 for ( NSMenuItem *pNSMenuItem in pPreferredMenus ) 651 [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pNSMenuItem]; 652 } 653 else if ( pDefMenu ) 654 { 655 [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu]; 656 } 657} 658 659void ShutdownIcon::SetDefaultMenuBar( MenuBar *pMenuBar ) 660{ 661 if ( !pMenuBar ) 662 return; 663 664 SolarMutexGuard aGuard; 665 666 clearDefaultMenuBar(); 667 if ( pPreferredMenus ) 668 { 669 [pPreferredMenus release]; 670 pPreferredMenus = nil; 671 } 672 673 NSMenu *pNSMenu = getNSMenuForVCLMenu( pMenuBar ); 674 if ( pNSMenu && [pNSMenu numberOfItems] ) 675 { 676 pPreferredMenus = [NSMutableArray arrayWithArray: [pNSMenu itemArray]]; 677 [pNSMenu removeAllItems]; 678 [pPreferredMenus retain]; 679 } 680 681 resetMenuBar(); 682} 683 684 685extern "C" 686{ 687 688void aqua_init_systray() 689{ 690 SolarMutexGuard aGuard; 691 692 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance(); 693 if( ! pShutdownIcon ) 694 return; 695 696 // disable shutdown 697 pShutdownIcon->SetVeto( true ); 698 ShutdownIcon::addTerminateListener(); 699 700 if( ! pDefMenu ) 701 { 702 if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] ) 703 { 704 aShortcuts.clear(); 705 706 pExecute = [[QSMenuExecute alloc] init]; 707 pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""]; 708 pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""]; 709 NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )]; 710 [pMenu setAutoenablesItems: NO]; 711 NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )]; 712 [pDockMenu setAutoenablesItems: NO]; 713 714 // collect the URLs of the entries in the File/New menu 715 SvtModuleOptions aModuleOptions; 716 std::set< OUString > aFileNewAppsAvailable; 717 std::vector < SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu ); 718 719 for ( SvtDynMenuEntry const & newMenuProp : aNewMenu ) 720 { 721 if ( !newMenuProp.sURL.isEmpty() ) 722 aFileNewAppsAvailable.insert( newMenuProp.sURL ); 723 } 724 725 // describe the menu entries for launching the applications 726 struct MenuEntryDescriptor 727 { 728 SvtModuleOptions::EModule eModuleIdentifier; 729 int nMenuTag; 730 OUString sURLDescription; 731 } static constexpr aMenuItems[] = 732 { 733 { SvtModuleOptions::EModule::WRITER, MI_WRITER, WRITER_URL }, 734 { SvtModuleOptions::EModule::CALC, MI_CALC, CALC_URL }, 735 { SvtModuleOptions::EModule::IMPRESS, MI_IMPRESS, IMPRESS_WIZARD_URL }, 736 { SvtModuleOptions::EModule::DRAW, MI_DRAW, DRAW_URL }, 737 { SvtModuleOptions::EModule::DATABASE, MI_BASE, BASE_URL }, 738 { SvtModuleOptions::EModule::MATH, MI_MATH, MATH_URL } 739 }; 740 741 // insert entry for startcenter 742 if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) ) 743 { 744 appendMenuItem( pMenu, nil, SfxResId(STR_QUICKSTART_STARTCENTER), MI_STARTMODULE, "n" ); 745 if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] ) 746 [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute]; 747 else 748 OSL_FAIL( "setDockIconClickHandler selector failed on NSApp" ); 749 750 } 751 752 // insert the menu entries for launching the applications 753 for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i ) 754 { 755 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) ) 756 // the complete application is not even installed 757 continue; 758 759 const OUString& sURL( aMenuItems[i].sURLDescription ); 760 761 if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() ) 762 // the application is installed, but the entry has been configured to *not* appear in the File/New 763 // menu => also let not appear it in the quickstarter 764 continue; 765 766 OUString aKeyEquiv( getShortCut( ShutdownIcon::GetUrlDescription( sURL ) ) ); 767 768 appendMenuItem( pMenu, pDockMenu, ShutdownIcon::GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv ); 769 } 770 771 // insert the remaining menu entries 772 773 // add recent menu 774 appendRecentMenu( pMenu, SfxResId(STR_QUICKSTART_RECENTDOC) ); 775 776 OUString aTitle( SfxResId(STR_QUICKSTART_FROMTEMPLATE) ); 777 OUString aKeyEquiv( getShortCut( aTitle ) ); 778 appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv ); 779 aTitle = SfxResId(STR_QUICKSTART_FILEOPEN); 780 aKeyEquiv = getShortCut( aTitle ); 781 appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv ); 782 783 [pDefMenu setSubmenu: pMenu]; 784 resetMenuBar(); 785 786 if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] ) 787 { 788 [pDockSubMenu setSubmenu: pDockMenu]; 789 // add the submenu 790 [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu]; 791 } 792 else 793 OSL_FAIL( "addDockMenuItem selector failed on NSApp" ); 794 795 [pMenu autorelease]; 796 [pDockMenu autorelease]; 797 } 798 else 799 OSL_FAIL( "addFallbackMenuItem selector failed on NSApp" ); 800 } 801} 802 803void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray() 804{ 805} 806 807} 808 809/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 810
