1/************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28// MARKER(update_precomp.py): autogen include statement, do not remove 29#include "precompiled_sfx2.hxx" 30 31#include "unotools/moduleoptions.hxx" 32#include "unotools/dynamicmenuoptions.hxx" 33#include "unotools/historyoptions.hxx" 34#include "tools/urlobj.hxx" 35#include "osl/file.h" 36#include "comphelper/sequenceashashmap.hxx" 37#include "vos/mutex.hxx" 38#include "sfx2/app.hxx" 39#include "app.hrc" 40#define USE_APP_SHORTCUTS 41#include "shutdownicon.hxx" 42 43#include "com/sun/star/util/XStringWidth.hpp" 44 45#include "cppuhelper/implbase1.hxx" 46 47#include <set> 48#include <vector> 49 50#include "premac.h" 51#include <Cocoa/Cocoa.h> 52#include "postmac.h" 53 54using namespace ::rtl; 55using namespace ::osl; 56using namespace ::com::sun::star::uno; 57using namespace ::com::sun::star::task; 58using namespace ::com::sun::star::lang; 59using namespace ::com::sun::star::beans; 60using namespace ::com::sun::star::util; 61 62#define MI_OPEN 1 63#define MI_WRITER 2 64#define MI_CALC 3 65#define MI_IMPRESS 4 66#define MI_DRAW 5 67#define MI_BASE 6 68#define MI_MATH 7 69#define MI_TEMPLATE 8 70#define MI_STARTMODULE 9 71 72@interface QSMenuExecute : NSObject 73{ 74} 75-(void)executeMenuItem: (NSMenuItem*)pItem; 76-(void)dockIconClicked: (NSObject*)pSender; 77@end 78 79@implementation QSMenuExecute 80-(void)executeMenuItem: (NSMenuItem*)pItem 81{ 82 switch( [pItem tag] ) 83 { 84 case MI_OPEN: 85 ShutdownIcon::FileOpen(); 86 break; 87 case MI_WRITER: 88 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( WRITER_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); 89 break; 90 case MI_CALC: 91 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( CALC_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); 92 break; 93 case MI_IMPRESS: 94 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( IMPRESS_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); 95 break; 96 case MI_DRAW: 97 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( DRAW_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); 98 break; 99 case MI_BASE: 100 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( BASE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); 101 break; 102 case MI_MATH: 103 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( MATH_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); 104 break; 105 case MI_TEMPLATE: 106 ShutdownIcon::FromTemplate(); 107 break; 108 case MI_STARTMODULE: 109 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); 110 break; 111 default: 112 break; 113 } 114} 115 116-(void)dockIconClicked: (NSObject*)pSender 117{ 118 (void)pSender; 119 // start start module 120 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) ); 121} 122 123@end 124 125bool ShutdownIcon::IsQuickstarterInstalled() 126{ 127 return true; 128} 129 130static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil; 131static QSMenuExecute* pExecute = nil; 132 133static std::set< OUString > aShortcuts; 134 135static NSString* getAutoreleasedString( const rtl::OUString& rStr ) 136{ 137 return [[[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()] autorelease]; 138} 139 140struct RecentMenuEntry 141{ 142 rtl::OUString aURL; 143 rtl::OUString aFilter; 144 rtl::OUString aTitle; 145 rtl::OUString aPassword; 146}; 147 148class RecentFilesStringLength : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XStringWidth > 149{ 150 public: 151 RecentFilesStringLength() {} 152 virtual ~RecentFilesStringLength() {} 153 154 // XStringWidth 155 sal_Int32 SAL_CALL queryStringWidth( const ::rtl::OUString& aString ) 156 throw (::com::sun::star::uno::RuntimeException) 157 { 158 return aString.getLength(); 159 } 160}; 161 162@interface RecentMenuDelegate : NSObject 163{ 164 std::vector< RecentMenuEntry >* m_pRecentFilesItems; 165} 166-(id)init; 167-(void)dealloc; 168-(void)menuNeedsUpdate:(NSMenu *)menu; 169-(void)executeRecentEntry: (NSMenuItem*)item; 170@end 171 172@implementation RecentMenuDelegate 173-(id)init 174{ 175 if( (self = [super init]) ) 176 { 177 m_pRecentFilesItems = new std::vector< RecentMenuEntry >(); 178 } 179 return self; 180} 181 182-(void)dealloc 183{ 184 delete m_pRecentFilesItems; 185 [super dealloc]; 186} 187 188-(void)menuNeedsUpdate:(NSMenu *)menu 189{ 190 // clear menu 191 int nItems = [menu numberOfItems]; 192 while( nItems -- ) 193 [menu removeItemAtIndex: 0]; 194 195 // update recent item list 196 Sequence< Sequence< PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) ); 197 198 int nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength(); 199 200 m_pRecentFilesItems->clear(); 201 if( ( nPickListMenuItems > 0 ) ) 202 { 203 for ( int i = 0; i < nPickListMenuItems; i++ ) 204 { 205 Sequence< PropertyValue >& rPickListEntry = aHistoryList[i]; 206 RecentMenuEntry aRecentFile; 207 208 for ( int j = 0; j < rPickListEntry.getLength(); j++ ) 209 { 210 Any a = rPickListEntry[j].Value; 211 212 if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_URL ) 213 a >>= aRecentFile.aURL; 214 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_FILTER ) 215 a >>= aRecentFile.aFilter; 216 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_TITLE ) 217 a >>= aRecentFile.aTitle; 218 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_PASSWORD ) 219 a >>= aRecentFile.aPassword; 220 } 221 222 m_pRecentFilesItems->push_back( aRecentFile ); 223 } 224 } 225 226 // insert new recent items 227 for ( sal_uInt32 i = 0; i < m_pRecentFilesItems->size(); i++ ) 228 { 229 rtl::OUString aMenuTitle; 230 INetURLObject aURL( (*m_pRecentFilesItems)[i].aURL ); 231 232 if ( aURL.GetProtocol() == INET_PROT_FILE ) 233 { 234 // Do handle file URL differently => convert it to a system 235 // path and abbreviate it with a special function: 236 String aFileSystemPath( aURL.getFSysPath( INetURLObject::FSYS_DETECT ) ); 237 238 ::rtl::OUString aSystemPath( aFileSystemPath ); 239 ::rtl::OUString aCompactedSystemPath; 240 241 oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL ); 242 if ( !nError ) 243 aMenuTitle = String( aCompactedSystemPath ); 244 else 245 aMenuTitle = aSystemPath; 246 } 247 else 248 { 249 // Use INetURLObject to abbreviate all other URLs 250 Reference< XStringWidth > xStringLength( new RecentFilesStringLength() ); 251 aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS ); 252 } 253 254 NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle ) 255 action: @selector(executeRecentEntry:) 256 keyEquivalent: @""]; 257 [pNewItem setTag: i]; 258 [pNewItem setTarget: self]; 259 [pNewItem setEnabled: YES]; 260 [menu addItem: pNewItem]; 261 [pNewItem autorelease]; 262 } 263} 264 265-(void)executeRecentEntry: (NSMenuItem*)item 266{ 267 sal_Int32 nIndex = [item tag]; 268 if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) ) 269 { 270 const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ]; 271 int NUM_OF_PICKLIST_ARGS = 3; 272 Sequence< PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS ); 273 274 aArgsList[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer" )); 275 aArgsList[0].Value = makeAny( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:user" ) ) ); 276 277 // documents in the picklist will never be opened as templates 278 aArgsList[1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate" )); 279 aArgsList[1].Value = makeAny( (sal_Bool) sal_False ); 280 281 ::rtl::OUString aFilter( rRecentFile.aFilter ); 282 sal_Int32 nPos = aFilter.indexOf( '|' ); 283 if ( nPos >= 0 ) 284 { 285 rtl::OUString aFilterOptions; 286 287 if ( nPos < ( aFilter.getLength() - 1 ) ) 288 aFilterOptions = aFilter.copy( nPos+1 ); 289 290 aArgsList[2].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterOptions" )); 291 aArgsList[2].Value = makeAny( aFilterOptions ); 292 293 aFilter = aFilter.copy( 0, nPos-1 ); 294 aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS ); 295 } 296 297 aArgsList[NUM_OF_PICKLIST_ARGS-1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" )); 298 aArgsList[NUM_OF_PICKLIST_ARGS-1].Value = makeAny( aFilter ); 299 300 ShutdownIcon::OpenURL( rRecentFile.aURL, OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ), aArgsList ); 301 } 302} 303@end 304 305static RecentMenuDelegate* pRecentDelegate = nil; 306 307static rtl::OUString getShortCut( const rtl::OUString i_rTitle ) 308{ 309 // create shortcut 310 rtl::OUString aKeyEquiv; 311 for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ ) 312 { 313 rtl::OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() ); 314 if( aShortcuts.find( aShortcut ) == aShortcuts.end() ) 315 { 316 aShortcuts.insert( aShortcut ); 317 aKeyEquiv = aShortcut; 318 break; 319 } 320 } 321 322 return aKeyEquiv; 323} 324 325static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const rtl::OUString& i_rTitle, int i_nTag, const rtl::OUString& i_rKeyEquiv ) 326{ 327 if( ! i_rTitle.getLength() ) 328 return; 329 330 NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle ) 331 action: @selector(executeMenuItem:) 332 keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"") 333 ]; 334 [pItem setTag: i_nTag]; 335 [pItem setTarget: pExecute]; 336 [pItem setEnabled: YES]; 337 [i_pMenu addItem: pItem]; 338 339 if( i_pDockMenu ) 340 { 341 // create a similar entry in the dock menu 342 pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle ) 343 action: @selector(executeMenuItem:) 344 keyEquivalent: @"" 345 ]; 346 [pItem setTag: i_nTag]; 347 [pItem setTarget: pExecute]; 348 [pItem setEnabled: YES]; 349 [i_pDockMenu addItem: pItem]; 350 } 351} 352 353static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const String& i_rTitle ) 354{ 355 if( ! pRecentDelegate ) 356 pRecentDelegate = [[RecentMenuDelegate alloc] init]; 357 358 NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle ) 359 action: @selector(executeMenuItem:) 360 keyEquivalent: @"" 361 ]; 362 [pItem setEnabled: YES]; 363 NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ]; 364 [pRecentMenu setDelegate: pRecentDelegate]; 365 [pRecentMenu setAutoenablesItems: NO]; 366 [pItem setSubmenu: pRecentMenu]; 367 368 if( i_pDockMenu ) 369 { 370 // create a similar entry in the dock menu 371 pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle ) 372 action: @selector(executeMenuItem:) 373 keyEquivalent: @"" 374 ]; 375 [pItem setEnabled: YES]; 376 pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ]; 377 [pRecentMenu setDelegate: pRecentDelegate]; 378 [pRecentMenu setAutoenablesItems: NO]; 379 [pItem setSubmenu: pRecentMenu]; 380 } 381} 382 383 384extern "C" 385{ 386 387void aqua_init_systray() 388{ 389 ::vos::OGuard aGuard( Application::GetSolarMutex() ); 390 391 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance(); 392 if( ! pShutdownIcon ) 393 return; 394 395 // disable shutdown 396 pShutdownIcon->SetVeto( true ); 397 pShutdownIcon->addTerminateListener(); 398 399 if( ! pDefMenu ) 400 { 401 if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] ) 402 { 403 aShortcuts.clear(); 404 405 pExecute = [[QSMenuExecute alloc] init]; 406 pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""]; 407 pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""]; 408 NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )]; 409 [pMenu setAutoenablesItems: NO]; 410 NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )]; 411 [pDockMenu setAutoenablesItems: NO]; 412 413 // collect the URLs of the entries in the File/New menu 414 SvtModuleOptions aModuleOptions; 415 std::set< rtl::OUString > aFileNewAppsAvailable; 416 SvtDynamicMenuOptions aOpt; 417 Sequence < Sequence < PropertyValue > > aNewMenu = aOpt.GetMenu( E_NEWMENU ); 418 const rtl::OUString sURLKey( RTL_CONSTASCII_USTRINGPARAM( "URL" ) ); 419 420 const Sequence< PropertyValue >* pNewMenu = aNewMenu.getConstArray(); 421 const Sequence< PropertyValue >* pNewMenuEnd = aNewMenu.getConstArray() + aNewMenu.getLength(); 422 for ( ; pNewMenu != pNewMenuEnd; ++pNewMenu ) 423 { 424 comphelper::SequenceAsHashMap aEntryItems( *pNewMenu ); 425 rtl::OUString sURL( aEntryItems.getUnpackedValueOrDefault( sURLKey, rtl::OUString() ) ); 426 if ( sURL.getLength() ) 427 aFileNewAppsAvailable.insert( sURL ); 428 } 429 430 // describe the menu entries for launching the applications 431 struct MenuEntryDescriptor 432 { 433 SvtModuleOptions::EModule eModuleIdentifier; 434 int nMenuTag; 435 const char* pAsciiURLDescription; 436 } aMenuItems[] = 437 { 438 { SvtModuleOptions::E_SWRITER, MI_WRITER, WRITER_URL }, 439 { SvtModuleOptions::E_SCALC, MI_CALC, CALC_URL }, 440 { SvtModuleOptions::E_SIMPRESS, MI_IMPRESS, IMPRESS_WIZARD_URL }, 441 { SvtModuleOptions::E_SDRAW, MI_DRAW, DRAW_URL }, 442 { SvtModuleOptions::E_SDATABASE, MI_BASE, BASE_URL }, 443 { SvtModuleOptions::E_SMATH, MI_MATH, MATH_URL } 444 }; 445 446 // insert entry for startcenter 447 if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::E_SSTARTMODULE ) ) 448 { 449 appendMenuItem( pMenu, nil, pShutdownIcon->GetResString( STR_QUICKSTART_STARTCENTER ), MI_STARTMODULE, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "n" ) ) ); 450 if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] ) 451 [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute]; 452 else 453 DBG_ERROR( "setDockIconClickHandler selector failed on NSApp\n" ); 454 455 } 456 457 // insert the menu entries for launching the applications 458 for ( size_t i = 0; i < sizeof( aMenuItems ) / sizeof( aMenuItems[0] ); ++i ) 459 { 460 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) ) 461 // the complete application is not even installed 462 continue; 463 464 rtl::OUString sURL( ::rtl::OUString::createFromAscii( aMenuItems[i].pAsciiURLDescription ) ); 465 466 if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() ) 467 // the application is installed, but the entry has been configured to *not* appear in the File/New 468 // menu => also let not appear it in the quickstarter 469 continue; 470 471 rtl::OUString aKeyEquiv( getShortCut( pShutdownIcon->GetUrlDescription( sURL ) ) ); 472 473 appendMenuItem( pMenu, pDockMenu, pShutdownIcon->GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv ); 474 } 475 476 // insert the remaining menu entries 477 478 // add recent menu 479 appendRecentMenu( pMenu, pDockMenu, pShutdownIcon->GetResString( STR_QUICKSTART_RECENTDOC ) ); 480 481 rtl::OUString aTitle( pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE ) ); 482 rtl::OUString aKeyEquiv( getShortCut( aTitle ) ); 483 appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv ); 484 aTitle = pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN ); 485 aKeyEquiv = getShortCut( aTitle ); 486 appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv ); 487 488 [pDefMenu setSubmenu: pMenu]; 489 [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu]; 490 491 if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] ) 492 { 493 [pDockSubMenu setSubmenu: pDockMenu]; 494 // insert a separator to the dock menu 495 [NSApp performSelector:@selector(addDockMenuItem:) withObject: [NSMenuItem separatorItem]]; 496 // and now add the submenu 497 [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu]; 498 } 499 else 500 DBG_ERROR( "addDockMenuItem selector failed on NSApp\n" ); 501 } 502 else 503 DBG_ERROR( "addFallbackMenuItem selector failed on NSApp\n" ); 504 } 505} 506 507void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray() 508{ 509} 510 511} 512