bump product version to 4.1.6.2
[LibreOffice.git] / sfx2 / source / appl / shutdowniconaqua.mm
blobe4367dabd1f90da0d78a8d932ded567263c4192d
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  */
21 #include "unotools/moduleoptions.hxx"
22 #include "unotools/dynamicmenuoptions.hxx"
23 #include "unotools/historyoptions.hxx"
24 #include "tools/urlobj.hxx"
25 #include "osl/file.h"
26 #include "comphelper/sequenceashashmap.hxx"
27 #include "osl/mutex.hxx"
28 #include "sfx2/app.hxx"
29 #include <sal/macros.h>
30 #include "app.hrc"
31 #define USE_APP_SHORTCUTS
32 #include "shutdownicon.hxx"
34 #include "com/sun/star/util/XStringWidth.hpp"
36 #include "cppuhelper/implbase1.hxx"
38 #include <set>
39 #include <vector>
41 #include "premac.h"
42 #include <objc/objc-runtime.h>
43 #include <Cocoa/Cocoa.h>
44 #include "postmac.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;
54 #define MI_OPEN                    1
55 #define MI_WRITER                  2
56 #define MI_CALC                    3
57 #define MI_IMPRESS                 4
58 #define MI_DRAW                    5
59 #define MI_BASE                    6
60 #define MI_MATH                    7
61 #define MI_TEMPLATE                8
62 #define MI_STARTMODULE             9
64 @interface QSMenuExecute : NSObject
67 -(void)executeMenuItem: (NSMenuItem*)pItem;
68 -(void)dockIconClicked: (NSObject*)pSender;
69 @end
71 @implementation QSMenuExecute
72 -(void)executeMenuItem: (NSMenuItem*)pItem
74     switch( [pItem tag] )
75     {
76     case MI_OPEN:
77         ShutdownIcon::FileOpen();
78         break;
79     case MI_WRITER:
80         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( WRITER_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
81         break;
82     case MI_CALC:
83         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( CALC_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
84         break;
85     case MI_IMPRESS:
86         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( IMPRESS_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
87         break;
88     case MI_DRAW:
89         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( DRAW_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
90         break;
91     case MI_BASE:
92         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( BASE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
93         break;
94     case MI_MATH:
95         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( MATH_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
96         break;
97     case MI_TEMPLATE:
98         ShutdownIcon::FromTemplate();
99         break;
100     case MI_STARTMODULE:
101         ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
102         break;
103     default:
104         break;
105     }
108 -(void)dockIconClicked: (NSObject*)pSender
110     (void)pSender;
111     // start start module
112     ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
115 @end
117 bool ShutdownIcon::IsQuickstarterInstalled()
119     return true;
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
134     rtl::OUString aURL;
135     rtl::OUString aFilter;
136     rtl::OUString aTitle;
137     rtl::OUString aPassword;
140 class RecentFilesStringLength : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XStringWidth >
142     public:
143         RecentFilesStringLength() {}
144         virtual ~RecentFilesStringLength() {}
146         // XStringWidth
147         sal_Int32 SAL_CALL queryStringWidth( const ::rtl::OUString& aString )
148             throw (::com::sun::star::uno::RuntimeException)
149         {
150             return aString.getLength();
151         }
154 @interface RecentMenuDelegate : NSObject
156     std::vector< RecentMenuEntry >* m_pRecentFilesItems;
158 -(id)init;
159 -(void)dealloc;
160 -(void)menuNeedsUpdate:(NSMenu *)menu;
161 -(void)executeRecentEntry: (NSMenuItem*)item;
162 @end
164 @implementation RecentMenuDelegate
165 -(id)init
167     if( (self = [super init]) )
168     {
169         m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
170     }
171     return self;
174 -(void)dealloc
176     delete m_pRecentFilesItems;
177     [super dealloc];
180 -(void)menuNeedsUpdate:(NSMenu *)menu
182     // clear menu
183     int nItems = [menu numberOfItems];
184     while( nItems -- )
185         [menu removeItemAtIndex: 0];
186     
187     // update recent item list
188     Sequence< Sequence< PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) );
190     int nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength();
191         
192     m_pRecentFilesItems->clear();
193     if( ( nPickListMenuItems > 0 ) )
194     {
195         for ( int i = 0; i < nPickListMenuItems; i++ )
196         {
197             Sequence< PropertyValue >& rPickListEntry = aHistoryList[i];
198             RecentMenuEntry aRecentFile;
199             
200             for ( int j = 0; j < rPickListEntry.getLength(); j++ )
201             {
202                 Any a = rPickListEntry[j].Value;
203                 
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;
212             }
213             
214             m_pRecentFilesItems->push_back( aRecentFile );
215         }
216     }
218     // insert new recent items
219     for ( sal_uInt32 i = 0; i < m_pRecentFilesItems->size(); i++ )
220     {
221         rtl::OUString   aMenuTitle;
222         INetURLObject   aURL( (*m_pRecentFilesItems)[i].aURL );
223         
224         if ( aURL.GetProtocol() == INET_PROT_FILE )
225         {
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 ) );
229             
230             ::rtl::OUString aSystemPath( aFileSystemPath );
231             ::rtl::OUString aCompactedSystemPath;
232             
233             oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL );
234             if ( !nError )
235                 aMenuTitle = String( aCompactedSystemPath );
236             else
237                 aMenuTitle = aSystemPath;
238         }
239         else
240         {
241             // Use INetURLObject to abbreviate all other URLs
242             Reference< XStringWidth > xStringLength( new RecentFilesStringLength() );
243             aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS );
244         }
245         
246         NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
247                                                    action: @selector(executeRecentEntry:)
248                                                    keyEquivalent: @""];
249         [pNewItem setTag: i];
250         [pNewItem setTarget: self];
251         [pNewItem setEnabled: YES];
252         [menu addItem: pNewItem];
253         [pNewItem autorelease];
254     }
257 -(void)executeRecentEntry: (NSMenuItem*)item
259     sal_Int32 nIndex = [item tag];
260     if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
261     {
262         const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
263         int NUM_OF_PICKLIST_ARGS = 3;
264         Sequence< PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
265         
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( '|' );
275         if ( nPos >= 0 )
276         {
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 );
287         }
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 );
293     }
295 @end
297 static RecentMenuDelegate* pRecentDelegate = nil;
299 static rtl::OUString getShortCut( const rtl::OUString i_rTitle )
301     // create shortcut
302     rtl::OUString aKeyEquiv;
303     for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
304     {
305         rtl::OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
306         if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
307         {
308             aShortcuts.insert( aShortcut );
309             aKeyEquiv = aShortcut;
310             break;
311         }
312     }
314     return aKeyEquiv;   
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() )
320         return;
321     
322     NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
323                                             action: @selector(executeMenuItem:)
324                                             keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
325                         ];
326     [pItem setTag: i_nTag];
327     [pItem setTarget: pExecute];
328     [pItem setEnabled: YES];
329     [i_pMenu addItem: pItem];
331     if( i_pDockMenu )
332     {
333         // create a similar entry in the dock menu
334         pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
335                                     action: @selector(executeMenuItem:)
336                                     keyEquivalent: @""
337                             ];
338         [pItem setTag: i_nTag];
339         [pItem setTarget: pExecute];
340         [pItem setEnabled: YES];
341         [i_pDockMenu addItem: pItem];
342     }
345 static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const String& i_rTitle )
347     if( ! pRecentDelegate )
348         pRecentDelegate = [[RecentMenuDelegate alloc] init];
349     
350     NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
351                                                    action: @selector(executeMenuItem:)
352                                                    keyEquivalent: @""
353                         ];
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];
364     // do this:
365     objc_msgSend(pRecentMenu, @selector(setDelegate:), pRecentDelegate);
367     [pRecentMenu setAutoenablesItems: NO];
368     [pItem setSubmenu: pRecentMenu];
370     if( i_pDockMenu )
371     {
372         // create a similar entry in the dock menu
373         pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
374                              action: @selector(executeMenuItem:)
375                              keyEquivalent: @""
376                         ];
377         [pItem setEnabled: YES];
378         pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
380         // See above
381         // [pRecentMenu setDelegate: pRecentDelegate];
382         objc_msgSend(pRecentMenu, @selector(setDelegate:), pRecentDelegate);
384         [pRecentMenu setAutoenablesItems: NO];
385         [pItem setSubmenu: pRecentMenu];
386     }
390 extern "C"
393 void aqua_init_systray()
395     SolarMutexGuard aGuard;
397     ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
398     if( ! pShutdownIcon )
399         return;
401     // disable shutdown
402     pShutdownIcon->SetVeto( true );
403     pShutdownIcon->addTerminateListener();
404     
405     if( ! pDefMenu )
406     {
407         if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
408         {
409             aShortcuts.clear();
410             
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];
418             
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" );
425         
426             const Sequence< PropertyValue >* pNewMenu = aNewMenu.getConstArray();
427             const Sequence< PropertyValue >* pNewMenuEnd = aNewMenu.getConstArray() + aNewMenu.getLength();
428             for ( ; pNewMenu != pNewMenuEnd; ++pNewMenu )
429             {
430                 comphelper::SequenceAsHashMap aEntryItems( *pNewMenu );
431                 rtl::OUString sURL( aEntryItems.getUnpackedValueOrDefault( sURLKey, rtl::OUString() ) );
432                 if ( sURL.getLength() )
433                     aFileNewAppsAvailable.insert( sURL );
434             }
435             
436             // describe the menu entries for launching the applications
437             struct MenuEntryDescriptor
438             {
439                 SvtModuleOptions::EModule   eModuleIdentifier;
440                 int                         nMenuTag;
441                 const char*                 pAsciiURLDescription;
442             }   aMenuItems[] =
443             {
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 }
450             };
452             // insert entry for startcenter
453             if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::E_SSTARTMODULE ) )
454             {
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];
458                 else
459                     OSL_FAIL( "setDockIconClickHandler selector failed on NSApp\n" );
461             }
462             
463             // insert the menu entries for launching the applications
464             for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
465             {
466                 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
467                     // the complete application is not even installed
468                     continue;
469         
470                 rtl::OUString sURL( ::rtl::OUString::createFromAscii( aMenuItems[i].pAsciiURLDescription ) );
471         
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
475                     continue;
476                 
477                 rtl::OUString aKeyEquiv( getShortCut( pShutdownIcon->GetUrlDescription( sURL ) ) );
478         
479                 appendMenuItem( pMenu, pDockMenu, pShutdownIcon->GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
480             }
482             // insert the remaining menu entries
484             // add recent menu
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 );
493             
494             [pDefMenu setSubmenu: pMenu];
495             [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
497             if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
498             {
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];
504             }
505             else
506                 OSL_FAIL( "addDockMenuItem selector failed on NSApp\n" );
507         }
508         else
509             OSL_FAIL( "addFallbackMenuItem selector failed on NSApp\n" );
510     }
513 void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
519 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */