xref: /core/sfx2/source/appl/shutdowniconaqua.mm (revision 81260259067e706dec84542efaf7c4fc807b9a36)
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 */
19
20
21#include <unotools/moduleoptions.hxx>
22#include <unotools/dynamicmenuoptions.hxx>
23#include <unotools/historyoptions.hxx>
24#include <officecfg/Office/Common.hxx>
25#include <rtl/ustring.hxx>
26#include <tools/urlobj.hxx>
27#include <osl/file.h>
28#include <osl/diagnose.h>
29#include <comphelper/sequenceashashmap.hxx>
30#include <sfx2/app.hxx>
31#include <sal/macros.h>
32#include <sfx2/sfxresid.hxx>
33#include <sfx2/strings.hrc>
34#include <vcl/svapp.hxx>
35#include <vcl/mnemonic.hxx>
36#include <vcl/image.hxx>
37#include <svtools/imagemgr.hxx>
38#include <shutdownicon.hxx>
39
40#include <com/sun/star/util/XStringWidth.hpp>
41
42#include <cppuhelper/implbase.hxx>
43
44#include <set>
45#include <vector>
46
47#include <premac.h>
48#include <objc/objc-runtime.h>
49#include <Cocoa/Cocoa.h>
50#include <postmac.h>
51
52#define MI_OPEN                    1
53#define MI_WRITER                  2
54#define MI_CALC                    3
55#define MI_IMPRESS                 4
56#define MI_DRAW                    5
57#define MI_BASE                    6
58#define MI_MATH                    7
59#define MI_TEMPLATE                8
60#define MI_STARTMODULE             9
61
62#define UNO_TOGGLECURRENTMODULE_COMMAND ".uno:ToggleCurrentModule"
63
64@interface QSMenuExecute : NSObject
65{
66}
67-(void)executeMenuItem: (NSMenuItem*)pItem;
68-(void)dockIconClicked: (NSObject*)pSender;
69@end
70
71@implementation QSMenuExecute
72-(void)executeMenuItem: (NSMenuItem*)pItem
73{
74    SolarMutexGuard aGuard;
75
76    switch( [pItem tag] )
77    {
78    case MI_OPEN:
79        ShutdownIcon::FileOpen();
80        break;
81    case MI_WRITER:
82        ShutdownIcon::OpenURL( WRITER_URL, "_default" );
83        break;
84    case MI_CALC:
85        ShutdownIcon::OpenURL( CALC_URL, "_default" );
86        break;
87    case MI_IMPRESS:
88        ShutdownIcon::OpenURL( IMPRESS_URL, "_default" );
89        break;
90    case MI_DRAW:
91        ShutdownIcon::OpenURL( DRAW_URL, "_default" );
92        break;
93    case MI_BASE:
94        ShutdownIcon::OpenURL( BASE_URL, "_default" );
95        break;
96    case MI_MATH:
97        ShutdownIcon::OpenURL( MATH_URL, "_default" );
98        break;
99    case MI_TEMPLATE:
100        ShutdownIcon::FromTemplate();
101        break;
102    case MI_STARTMODULE:
103        ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
104        break;
105    default:
106        break;
107    }
108}
109
110-(void)dockIconClicked: (NSObject*)pSender
111{
112    (void)pSender;
113
114    SolarMutexGuard aGuard;
115
116    // start module
117    ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
118}
119
120@end
121
122bool ShutdownIcon::IsQuickstarterInstalled()
123{
124    return true;
125}
126
127static NSArray<NSMenuItem*>* pPreferredMenus = nil;
128static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
129static QSMenuExecute* pExecute = nil;
130
131static std::set< OUString > aShortcuts;
132
133static NSString* getAutoreleasedString( const OUString& rStr )
134{
135    return [[[NSString alloc] initWithCharacters: reinterpret_cast<unichar const *>(rStr.getStr()) length: rStr.getLength()] autorelease];
136}
137
138namespace {
139
140struct RecentMenuEntry
141{
142    OUString aURL;
143    OUString aFilter;
144    OUString aTitle;
145    OUString aPassword;
146};
147
148}
149
150@interface QSCommandMenuItem : NSMenuItem
151{
152    OUString m_aCommand;
153}
154-(void)menuItemTriggered: (id)aSender;
155-(void)setCommand: (OUString)aCommand;
156@end
157
158@implementation QSCommandMenuItem
159
160-(void)menuItemTriggered: (id)aSender
161{
162    if ( m_aCommand.isEmpty() )
163        return;
164
165    SolarMutexGuard aGuard;
166
167    if ( m_aCommand == "vnd.org.libreoffice.recentdocs:ClearRecentFileList" )
168    {
169        // Clearing the recent file list requires an extra step
170        SvtHistoryOptions::Clear( EHistoryType::PickList, false );
171    }
172    else if ( m_aCommand == ".uno:Open" )
173    {
174        ShutdownIcon::FileOpen();
175        return;
176    }
177    else if ( m_aCommand == ".uno:ConfigureDialog" )
178    {
179        // Selecting some menu items will cause a crash if there are
180        // no visible windows
181        ShutdownIcon::OpenURL( STARTMODULE_URL, "_default" );
182    }
183    else if ( m_aCommand == UNO_TOGGLECURRENTMODULE_COMMAND )
184    {
185        bool bIsExclusive = officecfg::Office::Common::History::ShowCurrentModuleOnly::get();
186        std::shared_ptr<comphelper::ConfigurationChanges> batch(comphelper::ConfigurationChanges::create());
187        officecfg::Office::Common::History::ShowCurrentModuleOnly::set(!bIsExclusive, batch);
188        batch->commit();
189        [self setState: bIsExclusive ? NSControlStateValueOff : NSControlStateValueOn];
190        return;
191    }
192
193    // "private:" commands are used for menu items in the File > New menu
194    if ( m_aCommand.startsWith( "private:" ) || m_aCommand == STARTMODULE_URL )
195        ShutdownIcon::OpenURL( m_aCommand, "_default" );
196    else
197        ShutdownIcon::FromCommand( m_aCommand );
198}
199
200-(void)setCommand: (OUString)aCommand
201{
202    m_aCommand = aCommand;
203}
204
205@end
206
207@interface RecentMenuDelegate : NSObject <NSMenuDelegate>
208{
209    std::vector< RecentMenuEntry >* m_pRecentFilesItems;
210}
211-(id)init;
212-(void)dealloc;
213-(void)menuNeedsUpdate:(NSMenu *)menu;
214-(void)executeRecentEntry: (NSMenuItem*)item;
215@end
216
217@implementation RecentMenuDelegate
218-(id)init
219{
220    if( (self = [super init]) )
221    {
222        m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
223    }
224    return self;
225}
226
227-(void)dealloc
228{
229    delete m_pRecentFilesItems;
230    [super dealloc];
231}
232
233-(void)menuNeedsUpdate:(NSMenu *)menu
234{
235    SolarMutexGuard aGuard;
236
237    // clear menu
238    int nItems = [menu numberOfItems];
239    while( nItems -- )
240        [menu removeItemAtIndex: 0];
241
242    // update recent item list
243    std::vector< SvtHistoryOptions::HistoryItem > aHistoryList( SvtHistoryOptions::GetList( EHistoryType::PickList ) );
244
245    int nPickListMenuItems = ( aHistoryList.size() > 99 ) ? 99 : aHistoryList.size();
246
247    m_pRecentFilesItems->clear();
248    if( nPickListMenuItems > 0 )
249    {
250        for ( int i = 0; i < nPickListMenuItems; i++ )
251        {
252            const SvtHistoryOptions::HistoryItem & rPickListEntry = aHistoryList[i];
253            RecentMenuEntry aRecentFile;
254            aRecentFile.aURL = rPickListEntry.sURL;
255            aRecentFile.aFilter = rPickListEntry.sFilter;
256            aRecentFile.aTitle = rPickListEntry.sTitle;
257            m_pRecentFilesItems->push_back( aRecentFile );
258        }
259    }
260
261    // insert new recent items
262    for ( std::vector<RecentMenuEntry>::size_type i = 0; i < m_pRecentFilesItems->size(); i++ )
263    {
264        OUStringBuffer aMenuShortCut;
265        if ( i <= 9 )
266        {
267            if ( i == 9 )
268                aMenuShortCut.append( "1~0. " );
269            else
270            {
271                aMenuShortCut.append( "~N. " );
272                aMenuShortCut[ 1 ] = sal_Unicode( i + '1' );
273            }
274        }
275        else
276        {
277            aMenuShortCut.append( OUString::number(sal_Int32( i + 1 ) ) + ". " );
278        }
279
280        OUString   aMenuTitle;
281        INetURLObject   aURL( (*m_pRecentFilesItems)[i].aURL );
282        NSImage *pImage = nil;
283
284        if ( aURL.GetProtocol() == INetProtocol::File )
285        {
286            // Do handle file URL differently: don't show the protocol,
287            // just the file name
288            aMenuTitle = aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset);
289
290            if ( [NSApp respondsToSelector: @selector(createNSImage:)] )
291            {
292                Bitmap aThumbnail(SvFileInformationManager::GetFileImageId(aURL));
293                Size aBmpSize = aThumbnail.GetSizePixel();
294                if ( aBmpSize.Width() > 0 && aBmpSize.Height() > 0 )
295                {
296                    Image aImage( aThumbnail );
297                    NSValue *pImageValue = [NSValue valueWithPointer: &aImage];
298                    pImage = [NSApp performSelector: @selector(createNSImage:) withObject: pImageValue];
299                }
300            }
301        }
302        else
303        {
304            // In all other URLs show the protocol name before the file name
305            aMenuTitle   = INetURLObject::GetSchemeName(aURL.GetProtocol()) + ": " + aURL.getName();
306        }
307
308        aMenuShortCut.append( aMenuTitle );
309        aMenuTitle = MnemonicGenerator::EraseAllMnemonicChars( aMenuShortCut.makeStringAndClear() );
310        if ( aMenuTitle.isEmpty() )
311            continue;
312
313        if ( aMenuTitle.endsWith( "...", &aMenuTitle ) )
314            aMenuTitle += u"\u2026";
315
316        NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
317                                                   action: @selector(executeRecentEntry:)
318                                                   keyEquivalent: @""];
319        [pNewItem setTag: i];
320        [pNewItem setTarget: self];
321        [pNewItem setEnabled: YES];
322        if ( pImage )
323        {
324            [pNewItem setImage: pImage];
325            [pImage release];
326        }
327        [menu addItem: pNewItem];
328        [pNewItem autorelease];
329    }
330
331    if ( [menu numberOfItems] )
332    {
333        TranslateId aId( "STR_CLEAR_RECENT_FILES", "Clear List" );
334        OUString aClearList = Translate::get( aId, Translate::Create("fwk") );
335        if ( !aClearList.isEmpty() )
336        {
337            [menu addItem: [NSMenuItem separatorItem]];
338
339            QSCommandMenuItem* pNewItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( aClearList ) action: @selector(menuItemTriggered:) keyEquivalent: @""];
340            [pNewItem setCommand: "vnd.org.libreoffice.recentdocs:ClearRecentFileList"];
341            [pNewItem setTarget: pNewItem];
342            [pNewItem setEnabled: YES];
343            [menu addItem: pNewItem];
344            [pNewItem autorelease];
345
346            aId = TranslateId( "STR_TOGGLECURRENTMODULE", "Current Module Only" );
347            OUString aToggleCurrentMode = Translate::get( aId, Translate::Create("fwk") );
348            if ( !aToggleCurrentMode.isEmpty() )
349            {
350                pNewItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( aToggleCurrentMode ) action: @selector(menuItemTriggered:) keyEquivalent: @""];
351                [pNewItem setCommand: UNO_TOGGLECURRENTMODULE_COMMAND];
352                [pNewItem setTarget: pNewItem];
353                [pNewItem setState: officecfg::Office::Common::History::ShowCurrentModuleOnly::get() ? NSControlStateValueOn : NSControlStateValueOff];
354                [pNewItem setEnabled: YES];
355                [menu addItem: pNewItem];
356                [pNewItem autorelease];
357            }
358        }
359    }
360    else
361    {
362        TranslateId aId( "STR_NODOCUMENT", "No Documents" );
363        OUString aNoDocuments = Translate::get( aId, Translate::Create("fwk") );
364        if ( !aNoDocuments.isEmpty() )
365        {
366            NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aNoDocuments ) action: nil keyEquivalent: @""];
367            [pNewItem setEnabled: YES];
368            [menu addItem: pNewItem];
369            [pNewItem autorelease];
370        }
371    }
372}
373
374-(void)executeRecentEntry: (NSMenuItem*)item
375{
376    sal_Int32 nIndex = [item tag];
377    if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
378    {
379        const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
380        int NUM_OF_PICKLIST_ARGS = 3;
381        css::uno::Sequence< css::beans::PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
382        css::beans::PropertyValue* pArgsList = aArgsList.getArray();
383
384        pArgsList[0].Name = "Referer";
385        pArgsList[0].Value <<= OUString( "private:user" );
386
387        // documents in the picklist will never be opened as templates
388        pArgsList[1].Name = "AsTemplate";
389        pArgsList[1].Value <<= false;
390
391        OUString  aFilter( rRecentFile.aFilter );
392        sal_Int32 nPos = aFilter.indexOf( '|' );
393        if ( nPos >= 0 )
394        {
395            OUString aFilterOptions;
396
397            if ( nPos < ( aFilter.getLength() - 1 ) )
398                aFilterOptions = aFilter.copy( nPos+1 );
399
400            pArgsList[2].Name = "FilterOptions";
401            pArgsList[2].Value <<= aFilterOptions;
402
403            aFilter = aFilter.copy( 0, nPos-1 );
404            aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
405            pArgsList = aArgsList.getArray();
406        }
407
408        pArgsList[NUM_OF_PICKLIST_ARGS-1].Name = "FilterName";
409        pArgsList[NUM_OF_PICKLIST_ARGS-1].Value <<= aFilter;
410
411        ShutdownIcon::OpenURL( rRecentFile.aURL, "_default", aArgsList );
412    }
413}
414@end
415
416static RecentMenuDelegate* pRecentDelegate = nil;
417
418static OUString getShortCut( const OUString& i_rTitle )
419{
420    // create shortcut
421    OUString aKeyEquiv;
422    for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
423    {
424        OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
425        if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
426        {
427            aShortcuts.insert( aShortcut );
428            aKeyEquiv = aShortcut;
429            break;
430        }
431    }
432
433    return aKeyEquiv;
434}
435
436static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle, int i_nTag, const OUString& i_rKeyEquiv )
437{
438    if( ! i_rTitle.getLength() )
439        return;
440
441    NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
442                                            action: @selector(executeMenuItem:)
443                                            keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
444                        ];
445    [pItem setTag: i_nTag];
446    [pItem setTarget: pExecute];
447    [pItem setEnabled: YES];
448    [i_pMenu addItem: pItem];
449    [pItem autorelease];
450
451    if( i_pDockMenu )
452    {
453        // create a similar entry in the dock menu
454        pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
455                                    action: @selector(executeMenuItem:)
456                                    keyEquivalent: @""
457                            ];
458        [pItem setTag: i_nTag];
459        [pItem setTarget: pExecute];
460        [pItem setEnabled: YES];
461        [i_pDockMenu addItem: pItem];
462        [pItem autorelease];
463    }
464}
465
466static void appendRecentMenu( NSMenu* i_pMenu, const OUString& i_rTitle )
467{
468    if( ! pRecentDelegate )
469        pRecentDelegate = [[RecentMenuDelegate alloc] init];
470
471    NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
472                                                   action: @selector(executeMenuItem:)
473                                                   keyEquivalent: @""
474                        ];
475    [pItem setEnabled: YES];
476    NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
477
478    [pRecentMenu setDelegate: pRecentDelegate];
479
480    [pRecentMenu setAutoenablesItems: NO];
481    [pItem setSubmenu: pRecentMenu];
482}
483
484static void setKeyEquivalent( const vcl::KeyCode &rKeyCode, NSMenuItem *pNSMenuItem )
485{
486    if ( !pNSMenuItem )
487        return;
488
489    sal_uInt16 nKeyCode = rKeyCode.GetCode();
490    if ( !nKeyCode )
491        return;
492
493    sal_Unicode nCommandKey = 0;
494    if ((nKeyCode>=KEY_A) && (nKeyCode<=KEY_Z))           // letter A..Z
495        nCommandKey = nKeyCode - KEY_A + 'a';
496    else if ((nKeyCode>=KEY_0) && (nKeyCode<=KEY_9))      // numbers 0..9
497        nCommandKey = nKeyCode - KEY_0 + '0';
498    else if ((nKeyCode>=KEY_F1) && (nKeyCode<=KEY_F26))   // function keys F1..F26
499        nCommandKey = nKeyCode - KEY_F1 + NSF1FunctionKey;
500
501    if ( !nCommandKey )
502        return;
503
504    sal_uInt16 nModifier = rKeyCode.GetModifier();
505    int nItemModifier = 0;
506
507    if ( nModifier & KEY_SHIFT )
508    {
509        nItemModifier |= NSEventModifierFlagShift;   // actually useful only for function keys
510        if ( nKeyCode >= KEY_A && nKeyCode <= KEY_Z )
511            nCommandKey = nKeyCode - KEY_A + 'A';
512    }
513
514    if ( nModifier & KEY_MOD1 )
515        nItemModifier |= NSEventModifierFlagCommand;
516
517    if ( nModifier & KEY_MOD2 )
518        nItemModifier |= NSEventModifierFlagOption;
519
520    if ( nModifier & KEY_MOD3 )
521        nItemModifier |= NSEventModifierFlagControl;
522
523    OUString aCommandKey( &nCommandKey, 1 );
524    NSString *pCommandKey = [NSString stringWithCharacters: reinterpret_cast< unichar const* >(aCommandKey.getStr()) length: aCommandKey.getLength()];
525    [pNSMenuItem setKeyEquivalent: pCommandKey];
526    [pNSMenuItem setKeyEquivalentModifierMask: nItemModifier];
527}
528
529static NSMenu *getNSMenuForVCLMenu( Menu *pMenu )
530{
531    NSMenu *pRet = nil;
532
533    if ( !pMenu )
534        return pRet;
535
536    pMenu->Activate();
537
538    sal_uInt16 nItemCount = pMenu->GetItemCount();
539    if ( nItemCount )
540    {
541        pRet = [[[NSMenu alloc] initWithTitle: @""] autorelease];
542        [pRet setAutoenablesItems: NO];
543        for ( sal_uInt16 i = 0; i < nItemCount; i++ )
544        {
545            sal_uInt16 nId = pMenu->GetItemId( i );
546            if ( nId && pMenu->IsItemEnabled( nId ) )
547            {
548                OUString aText = MnemonicGenerator::EraseAllMnemonicChars( pMenu->GetItemText( nId ) );
549                if ( aText.isEmpty() )
550                    continue;
551
552                if ( aText.endsWith( "...", &aText ) )
553                    aText += u"\u2026";
554
555                // Use a custom menu in place of the Start Center's recent
556                // documents menu so that the list can be dynamically updated
557                OUString aCommand = pMenu->GetItemCommand( nId );
558                if ( aCommand == ".uno:RecentFileList" )
559                {
560                    appendRecentMenu( pRet, aText );
561                    continue;
562                }
563
564                NSString *pText = getAutoreleasedString( aText );
565                // TODO: use the QSMenuExecute class to connect the command
566                // string to one of the existing handler functions
567                QSCommandMenuItem *pNSMenuItem = [[QSCommandMenuItem alloc] initWithTitle: pText action: @selector(menuItemTriggered:) keyEquivalent: @""];
568                NSMenu *pNSSubmenu = getNSMenuForVCLMenu( pMenu->GetPopupMenu( nId ) );
569                if ( pNSSubmenu && [pNSSubmenu numberOfItems] )
570                {
571                    [pNSSubmenu setTitle: pText];
572                    [pNSMenuItem setSubmenu: pNSSubmenu];
573
574                    if ( aCommand == ".uno:AddDirect" )
575                    {
576                        SvtModuleOptions aModuleOptions;
577                        if ( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) )
578                        {
579                            QSCommandMenuItem *pStartModuleMenuItem = [[QSCommandMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId( STR_QUICKSTART_STARTCENTER ) ) action: @selector(menuItemTriggered:) keyEquivalent: @"n"];
580                            [pStartModuleMenuItem setTarget: pStartModuleMenuItem];
581                            [pStartModuleMenuItem setCommand: STARTMODULE_URL];
582                            [pNSSubmenu insertItem: pStartModuleMenuItem atIndex: 0];
583                            [pStartModuleMenuItem autorelease];
584                        }
585                    }
586                }
587                else if ( !aCommand.isEmpty() )
588                {
589                    [pNSMenuItem setTarget: pNSMenuItem];
590                    [pNSMenuItem setCommand: aCommand];
591
592                    // Use the default menu's special "open new file" shortcuts
593                    if ( aCommand == WRITER_URL )
594                        [pNSMenuItem setKeyEquivalent: @"t"];
595                    else if ( aCommand == CALC_URL )
596                        [pNSMenuItem setKeyEquivalent: @"s"];
597                    else if ( aCommand == IMPRESS_WIZARD_URL )
598                        [pNSMenuItem setKeyEquivalent: @"p"];
599                    else if ( aCommand == DRAW_URL )
600                        [pNSMenuItem setKeyEquivalent: @"d"];
601                    else if ( aCommand == MATH_URL )
602                        [pNSMenuItem setKeyEquivalent: @"f"];
603                    else if ( aCommand == BASE_URL )
604                        [pNSMenuItem setKeyEquivalent: @"a"];
605                    else
606                        setKeyEquivalent( pMenu->GetAccelKey( nId ), pNSMenuItem );
607                }
608
609                [pRet addItem: pNSMenuItem];
610                [pNSMenuItem autorelease];
611            }
612            else if ( pMenu->GetItemType( i ) == MenuItemType::SEPARATOR )
613            {
614                [pRet addItem: [NSMenuItem separatorItem]];
615            }
616        }
617    }
618
619    pMenu->Deactivate();
620
621    return pRet;
622}
623
624static void clearDefaultMenuBar()
625{
626    if( ![NSApp respondsToSelector: @selector(removeFallbackMenuItem:)] )
627        return;
628
629    // Remove previous default menu
630    if ( pDefMenu )
631        [NSApp performSelector:@selector(removeFallbackMenuItem:) withObject: pDefMenu];
632
633    // Remove previous preferred menu
634    if ( pPreferredMenus && [pPreferredMenus count] )
635    {
636        for ( NSMenuItem *pNSMenuItem in pPreferredMenus )
637            [NSApp performSelector:@selector(removeFallbackMenuItem:) withObject: pNSMenuItem];
638    }
639}
640
641static void resetMenuBar()
642{
643    if( ![NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
644        return;
645
646    clearDefaultMenuBar();
647
648    if ( pPreferredMenus && [pPreferredMenus count] )
649    {
650        for ( NSMenuItem *pNSMenuItem in pPreferredMenus )
651            [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pNSMenuItem];
652    }
653    else if ( pDefMenu )
654    {
655        [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
656    }
657}
658
659void ShutdownIcon::SetDefaultMenuBar( MenuBar *pMenuBar )
660{
661    if ( !pMenuBar )
662        return;
663
664    SolarMutexGuard aGuard;
665
666    clearDefaultMenuBar();
667    if ( pPreferredMenus )
668    {
669        [pPreferredMenus release];
670        pPreferredMenus = nil;
671    }
672
673    NSMenu *pNSMenu = getNSMenuForVCLMenu( pMenuBar );
674    if ( pNSMenu && [pNSMenu numberOfItems] )
675    {
676        pPreferredMenus = [NSMutableArray arrayWithArray: [pNSMenu itemArray]];
677        [pNSMenu removeAllItems];
678        [pPreferredMenus retain];
679    }
680
681    resetMenuBar();
682}
683
684
685extern "C"
686{
687
688void aqua_init_systray()
689{
690    SolarMutexGuard aGuard;
691
692    ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
693    if( ! pShutdownIcon )
694        return;
695
696    // disable shutdown
697    pShutdownIcon->SetVeto( true );
698    ShutdownIcon::addTerminateListener();
699
700    if( ! pDefMenu )
701    {
702        if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
703        {
704            aShortcuts.clear();
705
706            pExecute = [[QSMenuExecute alloc] init];
707            pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
708            pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
709            NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
710            [pMenu setAutoenablesItems: NO];
711            NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
712            [pDockMenu setAutoenablesItems: NO];
713
714            // collect the URLs of the entries in the File/New menu
715            SvtModuleOptions    aModuleOptions;
716            std::set< OUString > aFileNewAppsAvailable;
717            std::vector < SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu );
718
719            for ( SvtDynMenuEntry const & newMenuProp : aNewMenu )
720            {
721                if ( !newMenuProp.sURL.isEmpty() )
722                    aFileNewAppsAvailable.insert( newMenuProp.sURL );
723            }
724
725            // describe the menu entries for launching the applications
726            struct MenuEntryDescriptor
727            {
728                SvtModuleOptions::EModule   eModuleIdentifier;
729                int                         nMenuTag;
730                OUString                    sURLDescription;
731            } static constexpr aMenuItems[] =
732            {
733                { SvtModuleOptions::EModule::WRITER,    MI_WRITER,  WRITER_URL },
734                { SvtModuleOptions::EModule::CALC,      MI_CALC,    CALC_URL },
735                { SvtModuleOptions::EModule::IMPRESS,   MI_IMPRESS, IMPRESS_WIZARD_URL },
736                { SvtModuleOptions::EModule::DRAW,      MI_DRAW,    DRAW_URL },
737                { SvtModuleOptions::EModule::DATABASE,  MI_BASE,    BASE_URL },
738                { SvtModuleOptions::EModule::MATH,      MI_MATH,    MATH_URL }
739            };
740
741            // insert entry for startcenter
742            if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::EModule::STARTMODULE ) )
743            {
744                appendMenuItem( pMenu, nil, SfxResId(STR_QUICKSTART_STARTCENTER), MI_STARTMODULE, "n" );
745                if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
746                    [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
747                else
748                    OSL_FAIL( "setDockIconClickHandler selector failed on NSApp" );
749
750            }
751
752            // insert the menu entries for launching the applications
753            for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
754            {
755                if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
756                    // the complete application is not even installed
757                    continue;
758
759                const OUString& sURL( aMenuItems[i].sURLDescription );
760
761                if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
762                    // the application is installed, but the entry has been configured to *not* appear in the File/New
763                    // menu => also let not appear it in the quickstarter
764                    continue;
765
766                OUString aKeyEquiv( getShortCut( ShutdownIcon::GetUrlDescription( sURL ) ) );
767
768                appendMenuItem( pMenu, pDockMenu, ShutdownIcon::GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
769            }
770
771            // insert the remaining menu entries
772
773            // add recent menu
774            appendRecentMenu( pMenu, SfxResId(STR_QUICKSTART_RECENTDOC) );
775
776            OUString aTitle( SfxResId(STR_QUICKSTART_FROMTEMPLATE) );
777            OUString aKeyEquiv( getShortCut( aTitle ) );
778            appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
779            aTitle = SfxResId(STR_QUICKSTART_FILEOPEN);
780            aKeyEquiv = getShortCut( aTitle );
781            appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
782
783            [pDefMenu setSubmenu: pMenu];
784            resetMenuBar();
785
786            if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
787            {
788                [pDockSubMenu setSubmenu: pDockMenu];
789                // add the submenu
790                [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
791            }
792            else
793                OSL_FAIL( "addDockMenuItem selector failed on NSApp" );
794
795            [pMenu autorelease];
796            [pDockMenu autorelease];
797        }
798        else
799            OSL_FAIL( "addFallbackMenuItem selector failed on NSApp" );
800    }
801}
802
803void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
804{
805}
806
807}
808
809/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
810