Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / sfx2 / source / appl / shutdowniconaqua.mm
blobcc646063c510120e46f8a7577ac78c337fd8cbdd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  * 
6  * Copyright 2000, 2010 Oracle and/or its affiliates.
7  *
8  * OpenOffice.org - a multi-platform office productivity suite
9  *
10  * This file is part of OpenOffice.org.
11  *
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.
15  *
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).
21  *
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.
26  *
27  ************************************************************************/
30 #include "unotools/moduleoptions.hxx"
31 #include "unotools/dynamicmenuoptions.hxx"
32 #include "unotools/historyoptions.hxx"
33 #include "tools/urlobj.hxx"
34 #include "osl/file.h"
35 #include "comphelper/sequenceashashmap.hxx"
36 #include "osl/mutex.hxx"
37 #include "sfx2/app.hxx"
38 #include <sal/macros.h>
39 #include "app.hrc"
40 #define USE_APP_SHORTCUTS
41 #include "shutdownicon.hxx"
43 #include "com/sun/star/util/XStringWidth.hpp"
45 #include "cppuhelper/implbase1.hxx"
47 #include <set>
48 #include <vector>
50 #include "premac.h"
51 #include <objc/objc-runtime.h>
52 #include <Cocoa/Cocoa.h>
53 #include "postmac.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;
63 #define MI_OPEN                    1
64 #define MI_WRITER                  2
65 #define MI_CALC                    3
66 #define MI_IMPRESS                 4
67 #define MI_DRAW                    5
68 #define MI_BASE                    6
69 #define MI_MATH                    7
70 #define MI_TEMPLATE                8
71 #define MI_STARTMODULE             9
73 @interface QSMenuExecute : NSObject
76 -(void)executeMenuItem: (NSMenuItem*)pItem;
77 -(void)dockIconClicked: (NSObject*)pSender;
78 @end
80 @implementation QSMenuExecute
81 -(void)executeMenuItem: (NSMenuItem*)pItem
83     switch( [pItem tag] )
84     {
85     case MI_OPEN:
86         ShutdownIcon::FileOpen();
87         break;
88     case MI_WRITER:
89         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( WRITER_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
90         break;
91     case MI_CALC:
92         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( CALC_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
93         break;
94     case MI_IMPRESS:
95         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( IMPRESS_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
96         break;
97     case MI_DRAW:
98         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( DRAW_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
99         break;
100     case MI_BASE:
101         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( BASE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
102         break;
103     case MI_MATH:
104         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( MATH_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
105         break;
106     case MI_TEMPLATE:
107         ShutdownIcon::FromTemplate();
108         break;
109     case MI_STARTMODULE:
110         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
111         break;
112     default:
113         break;
114     }
117 -(void)dockIconClicked: (NSObject*)pSender
119     (void)pSender;
120     // start start module
121     ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
124 @end
126 bool ShutdownIcon::IsQuickstarterInstalled()
128     return true;
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
143     rtl::OUString aURL;
144     rtl::OUString aFilter;
145     rtl::OUString aTitle;
146     rtl::OUString aPassword;
149 class RecentFilesStringLength : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XStringWidth >
151     public:
152         RecentFilesStringLength() {}
153         virtual ~RecentFilesStringLength() {}
155         // XStringWidth
156         sal_Int32 SAL_CALL queryStringWidth( const ::rtl::OUString& aString )
157             throw (::com::sun::star::uno::RuntimeException)
158         {
159             return aString.getLength();
160         }
163 @interface RecentMenuDelegate : NSObject
165     std::vector< RecentMenuEntry >* m_pRecentFilesItems;
167 -(id)init;
168 -(void)dealloc;
169 -(void)menuNeedsUpdate:(NSMenu *)menu;
170 -(void)executeRecentEntry: (NSMenuItem*)item;
171 @end
173 @implementation RecentMenuDelegate
174 -(id)init
176     if( (self = [super init]) )
177     {
178         m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
179     }
180     return self;
183 -(void)dealloc
185     delete m_pRecentFilesItems;
186     [super dealloc];
189 -(void)menuNeedsUpdate:(NSMenu *)menu
191     // clear menu
192     int nItems = [menu numberOfItems];
193     while( nItems -- )
194         [menu removeItemAtIndex: 0];
195     
196     // update recent item list
197     Sequence< Sequence< PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) );
199     int nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength();
200         
201     m_pRecentFilesItems->clear();
202     if( ( nPickListMenuItems > 0 ) )
203     {
204         for ( int i = 0; i < nPickListMenuItems; i++ )
205         {
206             Sequence< PropertyValue >& rPickListEntry = aHistoryList[i];
207             RecentMenuEntry aRecentFile;
208             
209             for ( int j = 0; j < rPickListEntry.getLength(); j++ )
210             {
211                 Any a = rPickListEntry[j].Value;
212                 
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;
221             }
222             
223             m_pRecentFilesItems->push_back( aRecentFile );
224         }
225     }
227     // insert new recent items
228     for ( sal_uInt32 i = 0; i < m_pRecentFilesItems->size(); i++ )
229     {
230         rtl::OUString   aMenuTitle;
231         INetURLObject   aURL( (*m_pRecentFilesItems)[i].aURL );
232         
233         if ( aURL.GetProtocol() == INET_PROT_FILE )
234         {
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 ) );
238             
239             ::rtl::OUString aSystemPath( aFileSystemPath );
240             ::rtl::OUString aCompactedSystemPath;
241             
242             oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL );
243             if ( !nError )
244                 aMenuTitle = String( aCompactedSystemPath );
245             else
246                 aMenuTitle = aSystemPath;
247         }
248         else
249         {
250             // Use INetURLObject to abbreviate all other URLs
251             Reference< XStringWidth > xStringLength( new RecentFilesStringLength() );
252             aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS );
253         }
254         
255         NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
256                                                    action: @selector(executeRecentEntry:)
257                                                    keyEquivalent: @""];
258         [pNewItem setTag: i];
259         [pNewItem setTarget: self];
260         [pNewItem setEnabled: YES];
261         [menu addItem: pNewItem];
262         [pNewItem autorelease];
263     }
266 -(void)executeRecentEntry: (NSMenuItem*)item
268     sal_Int32 nIndex = [item tag];
269     if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
270     {
271         const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
272         int NUM_OF_PICKLIST_ARGS = 3;
273         Sequence< PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
274         
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( '|' );
284         if ( nPos >= 0 )
285         {
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 );
296         }
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 );
302     }
304 @end
306 static RecentMenuDelegate* pRecentDelegate = nil;
308 static rtl::OUString getShortCut( const rtl::OUString i_rTitle )
310     // create shortcut
311     rtl::OUString aKeyEquiv;
312     for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
313     {
314         rtl::OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
315         if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
316         {
317             aShortcuts.insert( aShortcut );
318             aKeyEquiv = aShortcut;
319             break;
320         }
321     }
323     return aKeyEquiv;   
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() )
329         return;
330     
331     NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
332                                             action: @selector(executeMenuItem:)
333                                             keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
334                         ];
335     [pItem setTag: i_nTag];
336     [pItem setTarget: pExecute];
337     [pItem setEnabled: YES];
338     [i_pMenu addItem: pItem];
340     if( i_pDockMenu )
341     {
342         // create a similar entry in the dock menu
343         pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
344                                     action: @selector(executeMenuItem:)
345                                     keyEquivalent: @""
346                             ];
347         [pItem setTag: i_nTag];
348         [pItem setTarget: pExecute];
349         [pItem setEnabled: YES];
350         [i_pDockMenu addItem: pItem];
351     }
354 static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const String& i_rTitle )
356     if( ! pRecentDelegate )
357         pRecentDelegate = [[RecentMenuDelegate alloc] init];
358     
359     NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
360                                                    action: @selector(executeMenuItem:)
361                                                    keyEquivalent: @""
362                         ];
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];
373     // do this:
374     objc_msgSend(pRecentMenu, @selector(setDelegate:), pRecentDelegate);
376     [pRecentMenu setAutoenablesItems: NO];
377     [pItem setSubmenu: pRecentMenu];
379     if( i_pDockMenu )
380     {
381         // create a similar entry in the dock menu
382         pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
383                              action: @selector(executeMenuItem:)
384                              keyEquivalent: @""
385                         ];
386         [pItem setEnabled: YES];
387         pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
389         // See above
390         // [pRecentMenu setDelegate: pRecentDelegate];
391         objc_msgSend(pRecentMenu, @selector(setDelegate:), pRecentDelegate);
393         [pRecentMenu setAutoenablesItems: NO];
394         [pItem setSubmenu: pRecentMenu];
395     }
399 extern "C"
402 void aqua_init_systray()
404     SolarMutexGuard aGuard;
406     ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
407     if( ! pShutdownIcon )
408         return;
410     // disable shutdown
411     pShutdownIcon->SetVeto( true );
412     pShutdownIcon->addTerminateListener();
413     
414     if( ! pDefMenu )
415     {
416         if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
417         {
418             aShortcuts.clear();
419             
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];
427             
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" ) );
434         
435             const Sequence< PropertyValue >* pNewMenu = aNewMenu.getConstArray();
436             const Sequence< PropertyValue >* pNewMenuEnd = aNewMenu.getConstArray() + aNewMenu.getLength();
437             for ( ; pNewMenu != pNewMenuEnd; ++pNewMenu )
438             {
439                 comphelper::SequenceAsHashMap aEntryItems( *pNewMenu );
440                 rtl::OUString sURL( aEntryItems.getUnpackedValueOrDefault( sURLKey, rtl::OUString() ) );
441                 if ( sURL.getLength() )
442                     aFileNewAppsAvailable.insert( sURL );
443             }
444             
445             // describe the menu entries for launching the applications
446             struct MenuEntryDescriptor
447             {
448                 SvtModuleOptions::EModule   eModuleIdentifier;
449                 int                         nMenuTag;
450                 const char*                 pAsciiURLDescription;
451             }   aMenuItems[] =
452             {
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 }
459             };
461             // insert entry for startcenter
462             if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::E_SSTARTMODULE ) )
463             {
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];
467                 else
468                     OSL_FAIL( "setDockIconClickHandler selector failed on NSApp\n" );
470             }
471             
472             // insert the menu entries for launching the applications
473             for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
474             {
475                 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
476                     // the complete application is not even installed
477                     continue;
478         
479                 rtl::OUString sURL( ::rtl::OUString::createFromAscii( aMenuItems[i].pAsciiURLDescription ) );
480         
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
484                     continue;
485                 
486                 rtl::OUString aKeyEquiv( getShortCut( pShutdownIcon->GetUrlDescription( sURL ) ) );
487         
488                 appendMenuItem( pMenu, pDockMenu, pShutdownIcon->GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
489             }
491             // insert the remaining menu entries
493             // add recent menu
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 );
502             
503             [pDefMenu setSubmenu: pMenu];
504             [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
506             if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
507             {
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];
513             }
514             else
515                 OSL_FAIL( "addDockMenuItem selector failed on NSApp\n" );
516         }
517         else
518             OSL_FAIL( "addFallbackMenuItem selector failed on NSApp\n" );
519     }
522 void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
528 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */