1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include "unotools/moduleoptions.hxx"
22 #include "unotools/dynamicmenuoptions.hxx"
23 #include "unotools/historyoptions.hxx"
24 #include "tools/urlobj.hxx"
26 #include "comphelper/sequenceashashmap.hxx"
27 #include "osl/mutex.hxx"
28 #include "sfx2/app.hxx"
29 #include <sal/macros.h>
31 #define USE_APP_SHORTCUTS
32 #include "shutdownicon.hxx"
34 #include "com/sun/star/util/XStringWidth.hpp"
36 #include "cppuhelper/implbase1.hxx"
42 #include <objc/objc-runtime.h>
43 #include <Cocoa/Cocoa.h>
46 using namespace ::rtl;
47 using namespace ::osl;
48 using namespace ::com::sun::star::uno;
49 using namespace ::com::sun::star::task;
50 using namespace ::com::sun::star::lang;
51 using namespace ::com::sun::star::beans;
52 using namespace ::com::sun::star::util;
62 #define MI_STARTMODULE 9
64 @interface QSMenuExecute : NSObject
67 -(void)executeMenuItem: (NSMenuItem*)pItem;
68 -(void)dockIconClicked: (NSObject*)pSender;
71 @implementation QSMenuExecute
72 -(void)executeMenuItem: (NSMenuItem*)pItem
77 ShutdownIcon::FileOpen();
80 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( WRITER_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
83 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( CALC_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
86 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( IMPRESS_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
89 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( DRAW_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
92 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( BASE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
95 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( MATH_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
98 ShutdownIcon::FromTemplate();
101 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
108 -(void)dockIconClicked: (NSObject*)pSender
111 // start start module
112 ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
117 bool ShutdownIcon::IsQuickstarterInstalled()
122 static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
123 static QSMenuExecute* pExecute = nil;
125 static std::set< OUString > aShortcuts;
127 static NSString* getAutoreleasedString( const rtl::OUString& rStr )
129 return [[[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()] autorelease];
132 struct RecentMenuEntry
135 rtl::OUString aFilter;
136 rtl::OUString aTitle;
137 rtl::OUString aPassword;
140 class RecentFilesStringLength : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XStringWidth >
143 RecentFilesStringLength() {}
144 virtual ~RecentFilesStringLength() {}
147 sal_Int32 SAL_CALL queryStringWidth( const ::rtl::OUString& aString )
148 throw (::com::sun::star::uno::RuntimeException)
150 return aString.getLength();
154 @interface RecentMenuDelegate : NSObject
156 std::vector< RecentMenuEntry >* m_pRecentFilesItems;
160 -(void)menuNeedsUpdate:(NSMenu *)menu;
161 -(void)executeRecentEntry: (NSMenuItem*)item;
164 @implementation RecentMenuDelegate
167 if( (self = [super init]) )
169 m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
176 delete m_pRecentFilesItems;
180 -(void)menuNeedsUpdate:(NSMenu *)menu
183 int nItems = [menu numberOfItems];
185 [menu removeItemAtIndex: 0];
187 // update recent item list
188 Sequence< Sequence< PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) );
190 int nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength();
192 m_pRecentFilesItems->clear();
193 if( ( nPickListMenuItems > 0 ) )
195 for ( int i = 0; i < nPickListMenuItems; i++ )
197 Sequence< PropertyValue >& rPickListEntry = aHistoryList[i];
198 RecentMenuEntry aRecentFile;
200 for ( int j = 0; j < rPickListEntry.getLength(); j++ )
202 Any a = rPickListEntry[j].Value;
204 if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_URL )
205 a >>= aRecentFile.aURL;
206 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_FILTER )
207 a >>= aRecentFile.aFilter;
208 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_TITLE )
209 a >>= aRecentFile.aTitle;
210 else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_PASSWORD )
211 a >>= aRecentFile.aPassword;
214 m_pRecentFilesItems->push_back( aRecentFile );
218 // insert new recent items
219 for ( sal_uInt32 i = 0; i < m_pRecentFilesItems->size(); i++ )
221 rtl::OUString aMenuTitle;
222 INetURLObject aURL( (*m_pRecentFilesItems)[i].aURL );
224 if ( aURL.GetProtocol() == INET_PROT_FILE )
226 // Do handle file URL differently => convert it to a system
227 // path and abbreviate it with a special function:
228 String aFileSystemPath( aURL.getFSysPath( INetURLObject::FSYS_DETECT ) );
230 ::rtl::OUString aSystemPath( aFileSystemPath );
231 ::rtl::OUString aCompactedSystemPath;
233 oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL );
235 aMenuTitle = String( aCompactedSystemPath );
237 aMenuTitle = aSystemPath;
241 // Use INetURLObject to abbreviate all other URLs
242 Reference< XStringWidth > xStringLength( new RecentFilesStringLength() );
243 aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS );
246 NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
247 action: @selector(executeRecentEntry:)
249 [pNewItem setTag: i];
250 [pNewItem setTarget: self];
251 [pNewItem setEnabled: YES];
252 [menu addItem: pNewItem];
253 [pNewItem autorelease];
257 -(void)executeRecentEntry: (NSMenuItem*)item
259 sal_Int32 nIndex = [item tag];
260 if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
262 const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
263 int NUM_OF_PICKLIST_ARGS = 3;
264 Sequence< PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
266 aArgsList[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer" ));
267 aArgsList[0].Value = makeAny( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:user" ) ) );
269 // documents in the picklist will never be opened as templates
270 aArgsList[1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate" ));
271 aArgsList[1].Value = makeAny( (sal_Bool) sal_False );
273 ::rtl::OUString aFilter( rRecentFile.aFilter );
274 sal_Int32 nPos = aFilter.indexOf( '|' );
277 rtl::OUString aFilterOptions;
279 if ( nPos < ( aFilter.getLength() - 1 ) )
280 aFilterOptions = aFilter.copy( nPos+1 );
282 aArgsList[2].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterOptions" ));
283 aArgsList[2].Value = makeAny( aFilterOptions );
285 aFilter = aFilter.copy( 0, nPos-1 );
286 aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
289 aArgsList[NUM_OF_PICKLIST_ARGS-1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" ));
290 aArgsList[NUM_OF_PICKLIST_ARGS-1].Value = makeAny( aFilter );
292 ShutdownIcon::OpenURL( rRecentFile.aURL, OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ), aArgsList );
297 static RecentMenuDelegate* pRecentDelegate = nil;
299 static rtl::OUString getShortCut( const rtl::OUString i_rTitle )
302 rtl::OUString aKeyEquiv;
303 for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
305 rtl::OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
306 if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
308 aShortcuts.insert( aShortcut );
309 aKeyEquiv = aShortcut;
317 static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const rtl::OUString& i_rTitle, int i_nTag, const rtl::OUString& i_rKeyEquiv )
319 if( ! i_rTitle.getLength() )
322 NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
323 action: @selector(executeMenuItem:)
324 keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
326 [pItem setTag: i_nTag];
327 [pItem setTarget: pExecute];
328 [pItem setEnabled: YES];
329 [i_pMenu addItem: pItem];
333 // create a similar entry in the dock menu
334 pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
335 action: @selector(executeMenuItem:)
338 [pItem setTag: i_nTag];
339 [pItem setTarget: pExecute];
340 [pItem setEnabled: YES];
341 [i_pDockMenu addItem: pItem];
345 static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const String& i_rTitle )
347 if( ! pRecentDelegate )
348 pRecentDelegate = [[RecentMenuDelegate alloc] init];
350 NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
351 action: @selector(executeMenuItem:)
354 [pItem setEnabled: YES];
355 NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
357 // When compiling against 10.6 SDK, we get the warning:
358 // class 'RecentMenuDelegate' does not implement the 'NSMenuDelegate' protocol
360 // No idea if that is a bogus warning, or if the way this is
361 // implemented just is so weird that the compiler gets
362 // confused. Anyway, to avoid warnings, instead of this:
363 // [pRecentMenu setDelegate: pRecentDelegate];
365 objc_msgSend(pRecentMenu, @selector(setDelegate:), pRecentDelegate);
367 [pRecentMenu setAutoenablesItems: NO];
368 [pItem setSubmenu: pRecentMenu];
372 // create a similar entry in the dock menu
373 pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
374 action: @selector(executeMenuItem:)
377 [pItem setEnabled: YES];
378 pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
381 // [pRecentMenu setDelegate: pRecentDelegate];
382 objc_msgSend(pRecentMenu, @selector(setDelegate:), pRecentDelegate);
384 [pRecentMenu setAutoenablesItems: NO];
385 [pItem setSubmenu: pRecentMenu];
393 void aqua_init_systray()
395 SolarMutexGuard aGuard;
397 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
398 if( ! pShutdownIcon )
402 pShutdownIcon->SetVeto( true );
403 pShutdownIcon->addTerminateListener();
407 if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
411 pExecute = [[QSMenuExecute alloc] init];
412 pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""];
413 pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""];
414 NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )];
415 [pMenu setAutoenablesItems: NO];
416 NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )];
417 [pDockMenu setAutoenablesItems: NO];
419 // collect the URLs of the entries in the File/New menu
420 SvtModuleOptions aModuleOptions;
421 std::set< rtl::OUString > aFileNewAppsAvailable;
422 SvtDynamicMenuOptions aOpt;
423 Sequence < Sequence < PropertyValue > > aNewMenu = aOpt.GetMenu( E_NEWMENU );
424 const rtl::OUString sURLKey( "URL" );
426 const Sequence< PropertyValue >* pNewMenu = aNewMenu.getConstArray();
427 const Sequence< PropertyValue >* pNewMenuEnd = aNewMenu.getConstArray() + aNewMenu.getLength();
428 for ( ; pNewMenu != pNewMenuEnd; ++pNewMenu )
430 comphelper::SequenceAsHashMap aEntryItems( *pNewMenu );
431 rtl::OUString sURL( aEntryItems.getUnpackedValueOrDefault( sURLKey, rtl::OUString() ) );
432 if ( sURL.getLength() )
433 aFileNewAppsAvailable.insert( sURL );
436 // describe the menu entries for launching the applications
437 struct MenuEntryDescriptor
439 SvtModuleOptions::EModule eModuleIdentifier;
441 const char* pAsciiURLDescription;
444 { SvtModuleOptions::E_SWRITER, MI_WRITER, WRITER_URL },
445 { SvtModuleOptions::E_SCALC, MI_CALC, CALC_URL },
446 { SvtModuleOptions::E_SIMPRESS, MI_IMPRESS, IMPRESS_WIZARD_URL },
447 { SvtModuleOptions::E_SDRAW, MI_DRAW, DRAW_URL },
448 { SvtModuleOptions::E_SDATABASE, MI_BASE, BASE_URL },
449 { SvtModuleOptions::E_SMATH, MI_MATH, MATH_URL }
452 // insert entry for startcenter
453 if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::E_SSTARTMODULE ) )
455 appendMenuItem( pMenu, nil, pShutdownIcon->GetResString( STR_QUICKSTART_STARTCENTER ), MI_STARTMODULE, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "n" ) ) );
456 if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
457 [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
459 OSL_FAIL( "setDockIconClickHandler selector failed on NSApp\n" );
463 // insert the menu entries for launching the applications
464 for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
466 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
467 // the complete application is not even installed
470 rtl::OUString sURL( ::rtl::OUString::createFromAscii( aMenuItems[i].pAsciiURLDescription ) );
472 if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
473 // the application is installed, but the entry has been configured to *not* appear in the File/New
474 // menu => also let not appear it in the quickstarter
477 rtl::OUString aKeyEquiv( getShortCut( pShutdownIcon->GetUrlDescription( sURL ) ) );
479 appendMenuItem( pMenu, pDockMenu, pShutdownIcon->GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
482 // insert the remaining menu entries
485 appendRecentMenu( pMenu, pDockMenu, pShutdownIcon->GetResString( STR_QUICKSTART_RECENTDOC ) );
487 rtl::OUString aTitle( pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE ) );
488 rtl::OUString aKeyEquiv( getShortCut( aTitle ) );
489 appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
490 aTitle = pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN );
491 aKeyEquiv = getShortCut( aTitle );
492 appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
494 [pDefMenu setSubmenu: pMenu];
495 [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
497 if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
499 [pDockSubMenu setSubmenu: pDockMenu];
500 // insert a separator to the dock menu
501 [NSApp performSelector:@selector(addDockMenuItem:) withObject: [NSMenuItem separatorItem]];
502 // and now add the submenu
503 [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
506 OSL_FAIL( "addDockMenuItem selector failed on NSApp\n" );
509 OSL_FAIL( "addFallbackMenuItem selector failed on NSApp\n" );
513 void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
519 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */