nss: upgrade to release 3.73
[LibreOffice.git] / sfx2 / source / appl / shutdowniconaqua.mm
blob028a9a6e64ec6c505902ae682d08b144a06fa3e3
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 <rtl/ustring.hxx>
25 #include <tools/urlobj.hxx>
26 #include <osl/file.h>
27 #include <comphelper/sequenceashashmap.hxx>
28 #include <sfx2/app.hxx>
29 #include <sal/macros.h>
30 #include <sfx2/sfxresid.hxx>
31 #include <sfx2/strings.hrc>
32 #include <vcl/svapp.hxx>
33 #include "shutdownicon.hxx"
35 #include <com/sun/star/util/XStringWidth.hpp>
37 #include <cppuhelper/implbase.hxx>
39 #include <set>
40 #include <vector>
42 #include <premac.h>
43 #include <objc/objc-runtime.h>
44 #include <Cocoa/Cocoa.h>
45 #include <postmac.h>
47 #define MI_OPEN                    1
48 #define MI_WRITER                  2
49 #define MI_CALC                    3
50 #define MI_IMPRESS                 4
51 #define MI_DRAW                    5
52 #define MI_BASE                    6
53 #define MI_MATH                    7
54 #define MI_TEMPLATE                8
55 #define MI_STARTMODULE             9
57 @interface QSMenuExecute : NSObject
60 -(void)executeMenuItem: (NSMenuItem*)pItem;
61 -(void)dockIconClicked: (NSObject*)pSender;
62 @end
64 @implementation QSMenuExecute
65 -(void)executeMenuItem: (NSMenuItem*)pItem
67     switch( [pItem tag] )
68     {
69     case MI_OPEN:
70         ShutdownIcon::FileOpen();
71         break;
72     case MI_WRITER:
73         ShutdownIcon::OpenURL( WRITER_URL, "_default" );
74         break;
75     case MI_CALC:
76         ShutdownIcon::OpenURL( CALC_URL, "_default" );
77         break;
78     case MI_IMPRESS:
79         ShutdownIcon::OpenURL( IMPRESS_URL, "_default" );
80         break;
81     case MI_DRAW:
82         ShutdownIcon::OpenURL( DRAW_URL, "_default" );
83         break;
84     case MI_BASE:
85         ShutdownIcon::OpenURL( BASE_URL, "_default" );
86         break;
87     case MI_MATH:
88         ShutdownIcon::OpenURL( MATH_URL, "_default" );
89         break;
90     case MI_TEMPLATE:
91         ShutdownIcon::FromTemplate();
92         break;
93     case MI_STARTMODULE:
94         ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
95         break;
96     default:
97         break;
98     }
101 -(void)dockIconClicked: (NSObject*)pSender
103     (void)pSender;
104     // start module
105     ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
108 @end
110 bool ShutdownIcon::IsQuickstarterInstalled()
112     return true;
115 static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
116 static QSMenuExecute* pExecute = nil;
118 static std::set< OUString > aShortcuts;
120 static NSString* getAutoreleasedString( const OUString& rStr )
122     return [[[NSString alloc] initWithCharacters: reinterpret_cast<unichar const *>(rStr.getStr()) length: rStr.getLength()] autorelease];
125 namespace {
127 struct RecentMenuEntry
129     OUString aURL;
130     OUString aFilter;
131     OUString aTitle;
132     OUString aPassword;
135 class RecentFilesStringLength : public ::cppu::WeakImplHelper< css::util::XStringWidth >
137     public:
138         RecentFilesStringLength() {}
140         // XStringWidth
141         sal_Int32 SAL_CALL queryStringWidth( const OUString& aString ) override
142         {
143             return aString.getLength();
144         }
149 @interface RecentMenuDelegate : NSObject <NSMenuDelegate>
151     std::vector< RecentMenuEntry >* m_pRecentFilesItems;
153 -(id)init;
154 -(void)dealloc;
155 -(void)menuNeedsUpdate:(NSMenu *)menu;
156 -(void)executeRecentEntry: (NSMenuItem*)item;
157 @end
159 @implementation RecentMenuDelegate
160 -(id)init
162     if( (self = [super init]) )
163     {
164         m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
165     }
166     return self;
169 -(void)dealloc
171     delete m_pRecentFilesItems;
172     [super dealloc];
175 -(void)menuNeedsUpdate:(NSMenu *)menu
177     // clear menu
178     int nItems = [menu numberOfItems];
179     while( nItems -- )
180         [menu removeItemAtIndex: 0];
182     // update recent item list
183     css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) );
185     int nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength();
187     m_pRecentFilesItems->clear();
188     if( nPickListMenuItems > 0 )
189     {
190         for ( int i = 0; i < nPickListMenuItems; i++ )
191         {
192             css::uno::Sequence< css::beans::PropertyValue > const & rPickListEntry = aHistoryList[i];
193             RecentMenuEntry aRecentFile;
195             for ( const css::beans::PropertyValue& rProp : rPickListEntry )
196             {
197                 const css::uno::Any& a = rProp.Value;
199                 if ( rProp.Name == HISTORY_PROPERTYNAME_URL )
200                     a >>= aRecentFile.aURL;
201                 else if ( rProp.Name == HISTORY_PROPERTYNAME_FILTER )
202                     a >>= aRecentFile.aFilter;
203                 else if ( rProp.Name == HISTORY_PROPERTYNAME_TITLE )
204                     a >>= aRecentFile.aTitle;
205                 else if ( rProp.Name == HISTORY_PROPERTYNAME_PASSWORD )
206                     a >>= aRecentFile.aPassword;
207             }
209             m_pRecentFilesItems->push_back( aRecentFile );
210         }
211     }
213     // insert new recent items
214     for ( std::vector<RecentMenuEntry>::size_type i = 0; i < m_pRecentFilesItems->size(); i++ )
215     {
216         OUString   aMenuTitle;
217         INetURLObject   aURL( (*m_pRecentFilesItems)[i].aURL );
219         if ( aURL.GetProtocol() == INetProtocol::File )
220         {
221             // Do handle file URL differently => convert it to a system
222             // path and abbreviate it with a special function:
223             OUString aSystemPath( aURL.getFSysPath( FSysStyle::Detect ) );
224             OUString aCompactedSystemPath;
226             oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, nullptr );
227             if ( !nError )
228                 aMenuTitle = aCompactedSystemPath;
229             else
230                 aMenuTitle = aSystemPath;
231         }
232         else
233         {
234             // Use INetURLObject to abbreviate all other URLs
235             css::uno::Reference< css::util::XStringWidth > xStringLength( new RecentFilesStringLength() );
236             aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DecodeMechanism::Unambiguous );
237         }
239         NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
240                                                    action: @selector(executeRecentEntry:)
241                                                    keyEquivalent: @""];
242         [pNewItem setTag: i];
243         [pNewItem setTarget: self];
244         [pNewItem setEnabled: YES];
245         [menu addItem: pNewItem];
246         [pNewItem autorelease];
247     }
250 -(void)executeRecentEntry: (NSMenuItem*)item
252     sal_Int32 nIndex = [item tag];
253     if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
254     {
255         const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
256         int NUM_OF_PICKLIST_ARGS = 3;
257         css::uno::Sequence< css::beans::PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
259         aArgsList[0].Name = "Referer";
260         aArgsList[0].Value <<= OUString( "private:user" );
262         // documents in the picklist will never be opened as templates
263         aArgsList[1].Name = "AsTemplate";
264         aArgsList[1].Value <<= false;
266         OUString  aFilter( rRecentFile.aFilter );
267         sal_Int32 nPos = aFilter.indexOf( '|' );
268         if ( nPos >= 0 )
269         {
270             OUString aFilterOptions;
272             if ( nPos < ( aFilter.getLength() - 1 ) )
273                 aFilterOptions = aFilter.copy( nPos+1 );
275             aArgsList[2].Name = "FilterOptions";
276             aArgsList[2].Value <<= aFilterOptions;
278             aFilter = aFilter.copy( 0, nPos-1 );
279             aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
280         }
282         aArgsList[NUM_OF_PICKLIST_ARGS-1].Name = "FilterName";
283         aArgsList[NUM_OF_PICKLIST_ARGS-1].Value <<= aFilter;
285         ShutdownIcon::OpenURL( rRecentFile.aURL, "_default", aArgsList );
286     }
288 @end
290 static RecentMenuDelegate* pRecentDelegate = nil;
292 static OUString getShortCut( const OUString& i_rTitle )
294     // create shortcut
295     OUString aKeyEquiv;
296     for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
297     {
298         OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
299         if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
300         {
301             aShortcuts.insert( aShortcut );
302             aKeyEquiv = aShortcut;
303             break;
304         }
305     }
307     return aKeyEquiv;   
310 static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle, int i_nTag, const OUString& i_rKeyEquiv )
312     if( ! i_rTitle.getLength() )
313         return;
315     NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
316                                             action: @selector(executeMenuItem:)
317                                             keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
318                         ];
319     [pItem setTag: i_nTag];
320     [pItem setTarget: pExecute];
321     [pItem setEnabled: YES];
322     [i_pMenu addItem: pItem];
324     if( i_pDockMenu )
325     {
326         // create a similar entry in the dock menu
327         pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
328                                     action: @selector(executeMenuItem:)
329                                     keyEquivalent: @""
330                             ];
331         [pItem setTag: i_nTag];
332         [pItem setTarget: pExecute];
333         [pItem setEnabled: YES];
334         [i_pDockMenu addItem: pItem];
335     }
338 static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle )
340     if( ! pRecentDelegate )
341         pRecentDelegate = [[RecentMenuDelegate alloc] init];
343     NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
344                                                    action: @selector(executeMenuItem:)
345                                                    keyEquivalent: @""
346                         ];
347     [pItem setEnabled: YES];
348     NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
350     [pRecentMenu setDelegate: pRecentDelegate];
352     [pRecentMenu setAutoenablesItems: NO];
353     [pItem setSubmenu: pRecentMenu];
355     if( i_pDockMenu )
356     {
357         // create a similar entry in the dock menu
358         pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
359                              action: @selector(executeMenuItem:)
360                              keyEquivalent: @""
361                         ];
362         [pItem setEnabled: YES];
363         pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
365         [pRecentMenu setDelegate: pRecentDelegate];
367         [pRecentMenu setAutoenablesItems: NO];
368         [pItem setSubmenu: pRecentMenu];
369     }
373 extern "C"
376 void aqua_init_systray()
378     SolarMutexGuard aGuard;
380     ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
381     if( ! pShutdownIcon )
382         return;
384     // disable shutdown
385     pShutdownIcon->SetVeto( true );
386     ShutdownIcon::addTerminateListener();
388     if( ! pDefMenu )
389     {
390         if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
391         {
392             aShortcuts.clear();
394             pExecute = [[QSMenuExecute alloc] init];
395             pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
396             pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
397             NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
398             [pMenu setAutoenablesItems: NO];
399             NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
400             [pDockMenu setAutoenablesItems: NO];
402             // collect the URLs of the entries in the File/New menu
403             SvtModuleOptions    aModuleOptions;
404             std::set< OUString > aFileNewAppsAvailable;
405             SvtDynamicMenuOptions aOpt;
406             css::uno::Sequence < css::uno::Sequence < css::beans::PropertyValue > > const aNewMenu = aOpt.GetMenu( EDynamicMenuType::NewMenu );
408             for ( auto const & newMenuProp : aNewMenu )
409             {
410                 comphelper::SequenceAsHashMap aEntryItems( newMenuProp );
411                 OUString sURL( aEntryItems.getUnpackedValueOrDefault( "URL", OUString() ) );
412                 if ( sURL.getLength() )
413                     aFileNewAppsAvailable.insert( sURL );
414             }
416             // describe the menu entries for launching the applications
417             struct MenuEntryDescriptor
418             {
419                 SvtModuleOptions::EModule   eModuleIdentifier;
420                 int                         nMenuTag;
421                 const char*                 pAsciiURLDescription;
422             }   aMenuItems[] =
423             {
424                 { SvtModuleOptions::EModule::WRITER,    MI_WRITER,  WRITER_URL },
425                 { SvtModuleOptions::EModule::CALC,      MI_CALC,    CALC_URL },
426                 { SvtModuleOptions::EModule::IMPRESS,   MI_IMPRESS, IMPRESS_WIZARD_URL },
427                 { SvtModuleOptions::EModule::DRAW,      MI_DRAW,    DRAW_URL },
428                 { SvtModuleOptions::EModule::DATABASE,  MI_BASE,    BASE_URL },
429                 { SvtModuleOptions::EModule::MATH,      MI_MATH,    MATH_URL }
430             };
432             // insert entry for startcenter
433             if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) )
434             {
435                 appendMenuItem( pMenu, nil, SfxResId(STR_QUICKSTART_STARTCENTER), MI_STARTMODULE, "n" );
436                 if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
437                     [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
438                 else
439                     OSL_FAIL( "setDockIconClickHandler selector failed on NSApp" );
441             }
443             // insert the menu entries for launching the applications
444             for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
445             {
446                 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
447                     // the complete application is not even installed
448                     continue;
450                 OUString sURL( OUString::createFromAscii( aMenuItems[i].pAsciiURLDescription ) );
452                 if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
453                     // the application is installed, but the entry has been configured to *not* appear in the File/New
454                     // menu => also let not appear it in the quickstarter
455                     continue;
457                 OUString aKeyEquiv( getShortCut( ShutdownIcon::GetUrlDescription( sURL ) ) );
459                 appendMenuItem( pMenu, pDockMenu, ShutdownIcon::GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
460             }
462             // insert the remaining menu entries
464             // add recent menu
465             appendRecentMenu( pMenu, pDockMenu, SfxResId(STR_QUICKSTART_RECENTDOC) );
467             OUString aTitle( SfxResId(STR_QUICKSTART_FROMTEMPLATE) );
468             OUString aKeyEquiv( getShortCut( aTitle ) );
469             appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
470             aTitle = SfxResId(STR_QUICKSTART_FILEOPEN);
471             aKeyEquiv = getShortCut( aTitle );
472             appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
474             [pDefMenu setSubmenu: pMenu];
475             [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
477             if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
478             {
479                 [pDockSubMenu setSubmenu: pDockMenu];
480                 // add the submenu
481                 [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
482             }
483             else
484                 OSL_FAIL( "addDockMenuItem selector failed on NSApp" );
485         }
486         else
487             OSL_FAIL( "addFallbackMenuItem selector failed on NSApp" );
488     }
491 void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
497 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */