1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
30 #include "unotools/moduleoptions.hxx"
31 #include "unotools/dynamicmenuoptions.hxx"
32 #include "unotools/historyoptions.hxx"
33 #include "tools/urlobj.hxx"
35 #include "comphelper/sequenceashashmap.hxx"
36 #include "osl/mutex.hxx"
37 #include "sfx2/app.hxx"
38 #include <sal/macros.h>
40 #define USE_APP_SHORTCUTS
41 #include "shutdownicon.hxx"
43 #include "com/sun/star/util/XStringWidth.hpp"
45 #include "cppuhelper/implbase1.hxx"
51 #include <objc/objc-runtime.h>
52 #include <Cocoa/Cocoa.h>
55 using namespace ::rtl;
56 using namespace ::osl;
57 using namespace ::com::sun::star::uno;
58 using namespace ::com::sun::star::task;
59 using namespace ::com::sun::star::lang;
60 using namespace ::com::sun::star::beans;
61 using namespace ::com::sun::star::util;
71 #define MI_STARTMODULE 9
73 @interface QSMenuExecute : NSObject
76 -(void)executeMenuItem: (NSMenuItem*)pItem;
77 -(void)dockIconClicked: (NSObject*)pSender;
80 @implementation QSMenuExecute
81 -(void)executeMenuItem: (NSMenuItem*)pItem
86 ShutdownIcon::FileOpen();
89 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( WRITER_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
92 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( CALC_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
95 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( IMPRESS_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
98 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( DRAW_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
101 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( BASE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
104 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( MATH_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
107 ShutdownIcon::FromTemplate();
110 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
117 -(void)dockIconClicked: (NSObject*)pSender
120 // start start module
121 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
126 bool ShutdownIcon::IsQuickstarterInstalled()
131 static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
132 static QSMenuExecute* pExecute = nil;
134 static std::set< OUString > aShortcuts;
136 static NSString* getAutoreleasedString( const rtl::OUString& rStr )
138 return [[[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()] autorelease];
141 struct RecentMenuEntry
144 rtl::OUString aFilter;
145 rtl::OUString aTitle;
146 rtl::OUString aPassword;
149 class RecentFilesStringLength : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XStringWidth >
152 RecentFilesStringLength() {}
153 virtual ~RecentFilesStringLength() {}
156 sal_Int32 SAL_CALL queryStringWidth( const ::rtl::OUString& aString )
157 throw (::com::sun::star::uno::RuntimeException)
159 return aString.getLength();
163 @interface RecentMenuDelegate : NSObject
165 std::vector< RecentMenuEntry >* m_pRecentFilesItems;
169 -(void)menuNeedsUpdate:(NSMenu *)menu;
170 -(void)executeRecentEntry: (NSMenuItem*)item;
173 @implementation RecentMenuDelegate
176 if( (self = [super init]) )
178 m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
185 delete m_pRecentFilesItems;
189 -(void)menuNeedsUpdate:(NSMenu *)menu
192 int nItems = [menu numberOfItems];
194 [menu removeItemAtIndex: 0];
196 // update recent item list
197 Sequence< Sequence< PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) );
199 int nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength();
201 m_pRecentFilesItems->clear();
202 if( ( nPickListMenuItems > 0 ) )
204 for ( int i = 0; i < nPickListMenuItems; i++ )
206 Sequence< PropertyValue >& rPickListEntry = aHistoryList[i];
207 RecentMenuEntry aRecentFile;
209 for ( int j = 0; j < rPickListEntry.getLength(); j++ )
211 Any a = rPickListEntry[j].Value;
213 if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_URL )
214 a >>= aRecentFile.aURL;
215 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_FILTER )
216 a >>= aRecentFile.aFilter;
217 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_TITLE )
218 a >>= aRecentFile.aTitle;
219 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_PASSWORD )
220 a >>= aRecentFile.aPassword;
223 m_pRecentFilesItems->push_back( aRecentFile );
227 // insert new recent items
228 for ( sal_uInt32 i = 0; i < m_pRecentFilesItems->size(); i++ )
230 rtl::OUString aMenuTitle;
231 INetURLObject aURL( (*m_pRecentFilesItems)[i].aURL );
233 if ( aURL.GetProtocol() == INET_PROT_FILE )
235 // Do handle file URL differently => convert it to a system
236 // path and abbreviate it with a special function:
237 String aFileSystemPath( aURL.getFSysPath( INetURLObject::FSYS_DETECT ) );
239 ::rtl::OUString aSystemPath( aFileSystemPath );
240 ::rtl::OUString aCompactedSystemPath;
242 oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL );
244 aMenuTitle = String( aCompactedSystemPath );
246 aMenuTitle = aSystemPath;
250 // Use INetURLObject to abbreviate all other URLs
251 Reference< XStringWidth > xStringLength( new RecentFilesStringLength() );
252 aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS );
255 NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
256 action: @selector(executeRecentEntry:)
258 [pNewItem setTag: i];
259 [pNewItem setTarget: self];
260 [pNewItem setEnabled: YES];
261 [menu addItem: pNewItem];
262 [pNewItem autorelease];
266 -(void)executeRecentEntry: (NSMenuItem*)item
268 sal_Int32 nIndex = [item tag];
269 if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
271 const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
272 int NUM_OF_PICKLIST_ARGS = 3;
273 Sequence< PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
275 aArgsList[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer" ));
276 aArgsList[0].Value = makeAny( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:user" ) ) );
278 // documents in the picklist will never be opened as templates
279 aArgsList[1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate" ));
280 aArgsList[1].Value = makeAny( (sal_Bool) sal_False );
282 ::rtl::OUString aFilter( rRecentFile.aFilter );
283 sal_Int32 nPos = aFilter.indexOf( '|' );
286 rtl::OUString aFilterOptions;
288 if ( nPos < ( aFilter.getLength() - 1 ) )
289 aFilterOptions = aFilter.copy( nPos+1 );
291 aArgsList[2].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterOptions" ));
292 aArgsList[2].Value = makeAny( aFilterOptions );
294 aFilter = aFilter.copy( 0, nPos-1 );
295 aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
298 aArgsList[NUM_OF_PICKLIST_ARGS-1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" ));
299 aArgsList[NUM_OF_PICKLIST_ARGS-1].Value = makeAny( aFilter );
301 ShutdownIcon::OpenURL( rRecentFile.aURL, OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ), aArgsList );
306 static RecentMenuDelegate* pRecentDelegate = nil;
308 static rtl::OUString getShortCut( const rtl::OUString i_rTitle )
311 rtl::OUString aKeyEquiv;
312 for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
314 rtl::OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
315 if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
317 aShortcuts.insert( aShortcut );
318 aKeyEquiv = aShortcut;
326 static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const rtl::OUString& i_rTitle, int i_nTag, const rtl::OUString& i_rKeyEquiv )
328 if( ! i_rTitle.getLength() )
331 NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
332 action: @selector(executeMenuItem:)
333 keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
335 [pItem setTag: i_nTag];
336 [pItem setTarget: pExecute];
337 [pItem setEnabled: YES];
338 [i_pMenu addItem: pItem];
342 // create a similar entry in the dock menu
343 pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
344 action: @selector(executeMenuItem:)
347 [pItem setTag: i_nTag];
348 [pItem setTarget: pExecute];
349 [pItem setEnabled: YES];
350 [i_pDockMenu addItem: pItem];
354 static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const String& i_rTitle )
356 if( ! pRecentDelegate )
357 pRecentDelegate = [[RecentMenuDelegate alloc] init];
359 NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
360 action: @selector(executeMenuItem:)
363 [pItem setEnabled: YES];
364 NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
366 // When compiling against 10.6 SDK, we get the warning:
367 // class 'RecentMenuDelegate' does not implement the 'NSMenuDelegate' protocol
369 // No idea if that is a bogus warning, or if the way this is
370 // implemented just is so weird that the compiler gets
371 // confused. Anyway, to avoid warnings, instead of this:
372 // [pRecentMenu setDelegate: pRecentDelegate];
374 objc_msgSend(pRecentMenu, @selector(setDelegate:), pRecentDelegate);
376 [pRecentMenu setAutoenablesItems: NO];
377 [pItem setSubmenu: pRecentMenu];
381 // create a similar entry in the dock menu
382 pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
383 action: @selector(executeMenuItem:)
386 [pItem setEnabled: YES];
387 pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
390 // [pRecentMenu setDelegate: pRecentDelegate];
391 objc_msgSend(pRecentMenu, @selector(setDelegate:), pRecentDelegate);
393 [pRecentMenu setAutoenablesItems: NO];
394 [pItem setSubmenu: pRecentMenu];
402 void aqua_init_systray()
404 SolarMutexGuard aGuard;
406 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
407 if( ! pShutdownIcon )
411 pShutdownIcon->SetVeto( true );
412 pShutdownIcon->addTerminateListener();
416 if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
420 pExecute = [[QSMenuExecute alloc] init];
421 pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""];
422 pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""];
423 NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )];
424 [pMenu setAutoenablesItems: NO];
425 NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )];
426 [pDockMenu setAutoenablesItems: NO];
428 // collect the URLs of the entries in the File/New menu
429 SvtModuleOptions aModuleOptions;
430 std::set< rtl::OUString > aFileNewAppsAvailable;
431 SvtDynamicMenuOptions aOpt;
432 Sequence < Sequence < PropertyValue > > aNewMenu = aOpt.GetMenu( E_NEWMENU );
433 const rtl::OUString sURLKey( RTL_CONSTASCII_USTRINGPARAM( "URL" ) );
435 const Sequence< PropertyValue >* pNewMenu = aNewMenu.getConstArray();
436 const Sequence< PropertyValue >* pNewMenuEnd = aNewMenu.getConstArray() + aNewMenu.getLength();
437 for ( ; pNewMenu != pNewMenuEnd; ++pNewMenu )
439 comphelper::SequenceAsHashMap aEntryItems( *pNewMenu );
440 rtl::OUString sURL( aEntryItems.getUnpackedValueOrDefault( sURLKey, rtl::OUString() ) );
441 if ( sURL.getLength() )
442 aFileNewAppsAvailable.insert( sURL );
445 // describe the menu entries for launching the applications
446 struct MenuEntryDescriptor
448 SvtModuleOptions::EModule eModuleIdentifier;
450 const char* pAsciiURLDescription;
453 { SvtModuleOptions::E_SWRITER, MI_WRITER, WRITER_URL },
454 { SvtModuleOptions::E_SCALC, MI_CALC, CALC_URL },
455 { SvtModuleOptions::E_SIMPRESS, MI_IMPRESS, IMPRESS_WIZARD_URL },
456 { SvtModuleOptions::E_SDRAW, MI_DRAW, DRAW_URL },
457 { SvtModuleOptions::E_SDATABASE, MI_BASE, BASE_URL },
458 { SvtModuleOptions::E_SMATH, MI_MATH, MATH_URL }
461 // insert entry for startcenter
462 if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::E_SSTARTMODULE ) )
464 appendMenuItem( pMenu, nil, pShutdownIcon->GetResString( STR_QUICKSTART_STARTCENTER ), MI_STARTMODULE, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "n" ) ) );
465 if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
466 [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
468 OSL_FAIL( "setDockIconClickHandler selector failed on NSApp\n" );
472 // insert the menu entries for launching the applications
473 for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
475 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
476 // the complete application is not even installed
479 rtl::OUString sURL( ::rtl::OUString::createFromAscii( aMenuItems[i].pAsciiURLDescription ) );
481 if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
482 // the application is installed, but the entry has been configured to *not* appear in the File/New
483 // menu => also let not appear it in the quickstarter
486 rtl::OUString aKeyEquiv( getShortCut( pShutdownIcon->GetUrlDescription( sURL ) ) );
488 appendMenuItem( pMenu, pDockMenu, pShutdownIcon->GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
491 // insert the remaining menu entries
494 appendRecentMenu( pMenu, pDockMenu, pShutdownIcon->GetResString( STR_QUICKSTART_RECENTDOC ) );
496 rtl::OUString aTitle( pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE ) );
497 rtl::OUString aKeyEquiv( getShortCut( aTitle ) );
498 appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
499 aTitle = pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN );
500 aKeyEquiv = getShortCut( aTitle );
501 appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
503 [pDefMenu setSubmenu: pMenu];
504 [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
506 if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
508 [pDockSubMenu setSubmenu: pDockMenu];
509 // insert a separator to the dock menu
510 [NSApp performSelector:@selector(addDockMenuItem:) withObject: [NSMenuItem separatorItem]];
511 // and now add the submenu
512 [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
515 OSL_FAIL( "addDockMenuItem selector failed on NSApp\n" );
518 OSL_FAIL( "addFallbackMenuItem selector failed on NSApp\n" );
522 void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
528 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */