cid#1636693 COPY_INSTEAD_OF_MOVE
[LibreOffice.git] / sfx2 / source / appl / shutdowniconaqua.mm
blob6b519fcefbe97a0d983b70850b7da04cc6747cee
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 <osl/diagnose.h>
28 #include <comphelper/sequenceashashmap.hxx>
29 #include <sfx2/app.hxx>
30 #include <sal/macros.h>
31 #include <sfx2/sfxresid.hxx>
32 #include <sfx2/strings.hrc>
33 #include <vcl/svapp.hxx>
34 #include "shutdownicon.hxx"
36 #include <com/sun/star/util/XStringWidth.hpp>
38 #include <cppuhelper/implbase.hxx>
40 #include <set>
41 #include <vector>
43 #include <premac.h>
44 #include <objc/objc-runtime.h>
45 #include <Cocoa/Cocoa.h>
46 #include <postmac.h>
48 #define MI_OPEN                    1
49 #define MI_WRITER                  2
50 #define MI_CALC                    3
51 #define MI_IMPRESS                 4
52 #define MI_DRAW                    5
53 #define MI_BASE                    6
54 #define MI_MATH                    7
55 #define MI_TEMPLATE                8
56 #define MI_STARTMODULE             9
58 @interface QSMenuExecute : NSObject
61 -(void)executeMenuItem: (NSMenuItem*)pItem;
62 -(void)dockIconClicked: (NSObject*)pSender;
63 @end
65 @implementation QSMenuExecute
66 -(void)executeMenuItem: (NSMenuItem*)pItem
68     switch( [pItem tag] )
69     {
70     case MI_OPEN:
71         ShutdownIcon::FileOpen();
72         break;
73     case MI_WRITER:
74         ShutdownIcon::OpenURL( WRITER_URL, "_default" );
75         break;
76     case MI_CALC:
77         ShutdownIcon::OpenURL( CALC_URL, "_default" );
78         break;
79     case MI_IMPRESS:
80         ShutdownIcon::OpenURL( IMPRESS_URL, "_default" );
81         break;
82     case MI_DRAW:
83         ShutdownIcon::OpenURL( DRAW_URL, "_default" );
84         break;
85     case MI_BASE:
86         ShutdownIcon::OpenURL( BASE_URL, "_default" );
87         break;
88     case MI_MATH:
89         ShutdownIcon::OpenURL( MATH_URL, "_default" );
90         break;
91     case MI_TEMPLATE:
92         ShutdownIcon::FromTemplate();
93         break;
94     case MI_STARTMODULE:
95         ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
96         break;
97     default:
98         break;
99     }
102 -(void)dockIconClicked: (NSObject*)pSender
104     (void)pSender;
105     // start module
106     ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
109 @end
111 bool ShutdownIcon::IsQuickstarterInstalled()
113     return true;
116 static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
117 static QSMenuExecute* pExecute = nil;
119 static std::set< OUString > aShortcuts;
121 static NSString* getAutoreleasedString( const OUString& rStr )
123     return [[[NSString alloc] initWithCharacters: reinterpret_cast<unichar const *>(rStr.getStr()) length: rStr.getLength()] autorelease];
126 namespace {
128 struct RecentMenuEntry
130     OUString aURL;
131     OUString aFilter;
132     OUString aTitle;
133     OUString aPassword;
136 class RecentFilesStringLength : public ::cppu::WeakImplHelper< css::util::XStringWidth >
138     public:
139         RecentFilesStringLength() {}
141         // XStringWidth
142         sal_Int32 SAL_CALL queryStringWidth( const OUString& aString ) override
143         {
144             return aString.getLength();
145         }
150 @interface RecentMenuDelegate : NSObject <NSMenuDelegate>
152     std::vector< RecentMenuEntry >* m_pRecentFilesItems;
154 -(id)init;
155 -(void)dealloc;
156 -(void)menuNeedsUpdate:(NSMenu *)menu;
157 -(void)executeRecentEntry: (NSMenuItem*)item;
158 @end
160 @implementation RecentMenuDelegate
161 -(id)init
163     if( (self = [super init]) )
164     {
165         m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
166     }
167     return self;
170 -(void)dealloc
172     delete m_pRecentFilesItems;
173     [super dealloc];
176 -(void)menuNeedsUpdate:(NSMenu *)menu
178     // clear menu
179     int nItems = [menu numberOfItems];
180     while( nItems -- )
181         [menu removeItemAtIndex: 0];
183     // update recent item list
184     std::vector< SvtHistoryOptions::HistoryItem > aHistoryList( SvtHistoryOptions::GetList( EHistoryType::PickList ) );
186     int nPickListMenuItems = ( aHistoryList.size() > 99 ) ? 99 : aHistoryList.size();
188     m_pRecentFilesItems->clear();
189     if( nPickListMenuItems > 0 )
190     {
191         for ( int i = 0; i < nPickListMenuItems; i++ )
192         {
193             const SvtHistoryOptions::HistoryItem & rPickListEntry = aHistoryList[i];
194             RecentMenuEntry aRecentFile;
195             aRecentFile.aURL = rPickListEntry.sURL;
196             aRecentFile.aFilter = rPickListEntry.sFilter;
197             aRecentFile.aTitle = rPickListEntry.sTitle;
198             m_pRecentFilesItems->push_back( aRecentFile );
199         }
200     }
202     // insert new recent items
203     for ( std::vector<RecentMenuEntry>::size_type i = 0; i < m_pRecentFilesItems->size(); i++ )
204     {
205         OUString   aMenuTitle;
206         INetURLObject   aURL( (*m_pRecentFilesItems)[i].aURL );
208         if ( aURL.GetProtocol() == INetProtocol::File )
209         {
210             // Do handle file URL differently => convert it to a system
211             // path and abbreviate it with a special function:
212             OUString aSystemPath( aURL.getFSysPath( FSysStyle::Detect ) );
213             OUString aCompactedSystemPath;
215             oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, nullptr );
216             if ( !nError )
217                 aMenuTitle = aCompactedSystemPath;
218             else
219                 aMenuTitle = aSystemPath;
220         }
221         else
222         {
223             // Use INetURLObject to abbreviate all other URLs
224             css::uno::Reference< css::util::XStringWidth > xStringLength( new RecentFilesStringLength() );
225             aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DecodeMechanism::Unambiguous );
226         }
228         NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
229                                                    action: @selector(executeRecentEntry:)
230                                                    keyEquivalent: @""];
231         [pNewItem setTag: i];
232         [pNewItem setTarget: self];
233         [pNewItem setEnabled: YES];
234         [menu addItem: pNewItem];
235         [pNewItem autorelease];
236     }
239 -(void)executeRecentEntry: (NSMenuItem*)item
241     sal_Int32 nIndex = [item tag];
242     if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
243     {
244         const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
245         int NUM_OF_PICKLIST_ARGS = 3;
246         css::uno::Sequence< css::beans::PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
247         css::beans::PropertyValue* pArgsList = aArgsList.getArray();
249         pArgsList[0].Name = "Referer";
250         pArgsList[0].Value <<= OUString( "private:user" );
252         // documents in the picklist will never be opened as templates
253         pArgsList[1].Name = "AsTemplate";
254         pArgsList[1].Value <<= false;
256         OUString  aFilter( rRecentFile.aFilter );
257         sal_Int32 nPos = aFilter.indexOf( '|' );
258         if ( nPos >= 0 )
259         {
260             OUString aFilterOptions;
262             if ( nPos < ( aFilter.getLength() - 1 ) )
263                 aFilterOptions = aFilter.copy( nPos+1 );
265             pArgsList[2].Name = "FilterOptions";
266             pArgsList[2].Value <<= aFilterOptions;
268             aFilter = aFilter.copy( 0, nPos-1 );
269             aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
270             pArgsList = aArgsList.getArray();
271         }
273         pArgsList[NUM_OF_PICKLIST_ARGS-1].Name = "FilterName";
274         pArgsList[NUM_OF_PICKLIST_ARGS-1].Value <<= aFilter;
276         ShutdownIcon::OpenURL( rRecentFile.aURL, "_default", aArgsList );
277     }
279 @end
281 static RecentMenuDelegate* pRecentDelegate = nil;
283 static OUString getShortCut( const OUString& i_rTitle )
285     // create shortcut
286     OUString aKeyEquiv;
287     for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
288     {
289         OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
290         if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
291         {
292             aShortcuts.insert( aShortcut );
293             aKeyEquiv = aShortcut;
294             break;
295         }
296     }
298     return aKeyEquiv;   
301 static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle, int i_nTag, const OUString& i_rKeyEquiv )
303     if( ! i_rTitle.getLength() )
304         return;
306     NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
307                                             action: @selector(executeMenuItem:)
308                                             keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
309                         ];
310     [pItem setTag: i_nTag];
311     [pItem setTarget: pExecute];
312     [pItem setEnabled: YES];
313     [i_pMenu addItem: pItem];
315     if( i_pDockMenu )
316     {
317         // create a similar entry in the dock menu
318         pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
319                                     action: @selector(executeMenuItem:)
320                                     keyEquivalent: @""
321                             ];
322         [pItem setTag: i_nTag];
323         [pItem setTarget: pExecute];
324         [pItem setEnabled: YES];
325         [i_pDockMenu addItem: pItem];
326     }
329 static void appendRecentMenu( NSMenu* i_pMenu, const OUString& i_rTitle )
331     if( ! pRecentDelegate )
332         pRecentDelegate = [[RecentMenuDelegate alloc] init];
334     NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
335                                                    action: @selector(executeMenuItem:)
336                                                    keyEquivalent: @""
337                         ];
338     [pItem setEnabled: YES];
339     NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
341     [pRecentMenu setDelegate: pRecentDelegate];
343     [pRecentMenu setAutoenablesItems: NO];
344     [pItem setSubmenu: pRecentMenu];
348 extern "C"
351 void aqua_init_systray()
353     SolarMutexGuard aGuard;
355     ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
356     if( ! pShutdownIcon )
357         return;
359     // disable shutdown
360     pShutdownIcon->SetVeto( true );
361     ShutdownIcon::addTerminateListener();
363     if( ! pDefMenu )
364     {
365         if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
366         {
367             aShortcuts.clear();
369             pExecute = [[QSMenuExecute alloc] init];
370             pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
371             pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
372             NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
373             [pMenu setAutoenablesItems: NO];
374             NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
375             [pDockMenu setAutoenablesItems: NO];
377             // collect the URLs of the entries in the File/New menu
378             SvtModuleOptions    aModuleOptions;
379             std::set< OUString > aFileNewAppsAvailable;
380             std::vector < SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu );
382             for ( SvtDynMenuEntry const & newMenuProp : aNewMenu )
383             {
384                 if ( !newMenuProp.sURL.isEmpty() )
385                     aFileNewAppsAvailable.insert( newMenuProp.sURL );
386             }
388             // describe the menu entries for launching the applications
389             struct MenuEntryDescriptor
390             {
391                 SvtModuleOptions::EModule   eModuleIdentifier;
392                 int                         nMenuTag;
393                 OUString                    sURLDescription;
394             } static constexpr aMenuItems[] =
395             {
396                 { SvtModuleOptions::EModule::WRITER,    MI_WRITER,  WRITER_URL },
397                 { SvtModuleOptions::EModule::CALC,      MI_CALC,    CALC_URL },
398                 { SvtModuleOptions::EModule::IMPRESS,   MI_IMPRESS, IMPRESS_WIZARD_URL },
399                 { SvtModuleOptions::EModule::DRAW,      MI_DRAW,    DRAW_URL },
400                 { SvtModuleOptions::EModule::DATABASE,  MI_BASE,    BASE_URL },
401                 { SvtModuleOptions::EModule::MATH,      MI_MATH,    MATH_URL }
402             };
404             // insert entry for startcenter
405             if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) )
406             {
407                 appendMenuItem( pMenu, nil, SfxResId(STR_QUICKSTART_STARTCENTER), MI_STARTMODULE, "n" );
408                 if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
409                     [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
410                 else
411                     OSL_FAIL( "setDockIconClickHandler selector failed on NSApp" );
413             }
415             // insert the menu entries for launching the applications
416             for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
417             {
418                 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
419                     // the complete application is not even installed
420                     continue;
422                 const OUString& sURL( aMenuItems[i].sURLDescription );
424                 if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
425                     // the application is installed, but the entry has been configured to *not* appear in the File/New
426                     // menu => also let not appear it in the quickstarter
427                     continue;
429                 OUString aKeyEquiv( getShortCut( ShutdownIcon::GetUrlDescription( sURL ) ) );
431                 appendMenuItem( pMenu, pDockMenu, ShutdownIcon::GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
432             }
434             // insert the remaining menu entries
436             // add recent menu
437             appendRecentMenu( pMenu, SfxResId(STR_QUICKSTART_RECENTDOC) );
439             OUString aTitle( SfxResId(STR_QUICKSTART_FROMTEMPLATE) );
440             OUString aKeyEquiv( getShortCut( aTitle ) );
441             appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
442             aTitle = SfxResId(STR_QUICKSTART_FILEOPEN);
443             aKeyEquiv = getShortCut( aTitle );
444             appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
446             [pDefMenu setSubmenu: pMenu];
447             [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
449             if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
450             {
451                 [pDockSubMenu setSubmenu: pDockMenu];
452                 // add the submenu
453                 [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
454             }
455             else
456                 OSL_FAIL( "addDockMenuItem selector failed on NSApp" );
457         }
458         else
459             OSL_FAIL( "addFallbackMenuItem selector failed on NSApp" );
460     }
463 void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
469 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */