xref: /trunk/main/sfx2/source/appl/shutdowniconaqua.mm (revision 91144cd0085a7583d2099b982122deb2184ab956)
1/**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements.  See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership.  The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License.  You may obtain a copy of the License at
10 *
11 *   http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied.  See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24// MARKER(update_precomp.py): autogen include statement, do not remove
25#include "precompiled_sfx2.hxx"
26
27#include "unotools/moduleoptions.hxx"
28#include "unotools/dynamicmenuoptions.hxx"
29#include "unotools/historyoptions.hxx"
30#include "tools/urlobj.hxx"
31#include "osl/file.h"
32#include "comphelper/sequenceashashmap.hxx"
33#include "vos/mutex.hxx"
34#include "sfx2/app.hxx"
35#include "app.hrc"
36#define USE_APP_SHORTCUTS
37#include "shutdownicon.hxx"
38
39#include "com/sun/star/util/XStringWidth.hpp"
40
41#include "cppuhelper/implbase1.hxx"
42
43#include <set>
44#include <vector>
45
46#include "premac.h"
47#include <Cocoa/Cocoa.h>
48#include "postmac.h"
49
50using namespace ::rtl;
51using namespace ::osl;
52using namespace ::com::sun::star::uno;
53using namespace ::com::sun::star::task;
54using namespace ::com::sun::star::lang;
55using namespace ::com::sun::star::beans;
56using namespace ::com::sun::star::util;
57
58#define MI_OPEN                    1
59#define MI_WRITER                  2
60#define MI_CALC                    3
61#define MI_IMPRESS                 4
62#define MI_DRAW                    5
63#define MI_BASE                    6
64#define MI_MATH                    7
65#define MI_TEMPLATE                8
66#define MI_STARTMODULE             9
67
68@interface QSMenuExecute : NSObject
69{
70}
71-(void)executeMenuItem: (NSMenuItem*)pItem;
72-(void)dockIconClicked: (NSObject*)pSender;
73@end
74
75@implementation QSMenuExecute
76-(void)executeMenuItem: (NSMenuItem*)pItem
77{
78    switch( [pItem tag] )
79    {
80    case MI_OPEN:
81        ShutdownIcon::FileOpen();
82        break;
83    case MI_WRITER:
84        ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( WRITER_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
85        break;
86    case MI_CALC:
87        ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( CALC_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
88        break;
89    case MI_IMPRESS:
90        ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( IMPRESS_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
91        break;
92    case MI_DRAW:
93        ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( DRAW_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
94        break;
95    case MI_BASE:
96        ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( BASE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
97        break;
98    case MI_MATH:
99        ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( MATH_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
100        break;
101    case MI_TEMPLATE:
102        ShutdownIcon::FromTemplate();
103        break;
104    case MI_STARTMODULE:
105        ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
106        break;
107    default:
108        break;
109    }
110}
111
112-(void)dockIconClicked: (NSObject*)pSender
113{
114    (void)pSender;
115    // start start module
116    ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
117}
118
119@end
120
121bool ShutdownIcon::IsQuickstarterInstalled()
122{
123    return true;
124}
125
126static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
127static QSMenuExecute* pExecute = nil;
128
129static std::set< OUString > aShortcuts;
130
131static NSString* getAutoreleasedString( const rtl::OUString& rStr )
132{
133    return [[[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()] autorelease];
134}
135
136struct RecentMenuEntry
137{
138    rtl::OUString aURL;
139    rtl::OUString aFilter;
140    rtl::OUString aTitle;
141    rtl::OUString aPassword;
142};
143
144class RecentFilesStringLength : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XStringWidth >
145{
146    public:
147        RecentFilesStringLength() {}
148        virtual ~RecentFilesStringLength() {}
149
150        // XStringWidth
151        sal_Int32 SAL_CALL queryStringWidth( const ::rtl::OUString& aString )
152        {
153            return aString.getLength();
154        }
155};
156
157@interface RecentMenuDelegate : NSObject
158{
159    std::vector< RecentMenuEntry >* m_pRecentFilesItems;
160}
161-(id)init;
162-(void)dealloc;
163-(void)menuNeedsUpdate:(NSMenu *)menu;
164-(void)executeRecentEntry: (NSMenuItem*)item;
165@end
166
167@implementation RecentMenuDelegate
168-(id)init
169{
170    if( (self = [super init]) )
171    {
172        m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
173    }
174    return self;
175}
176
177-(void)dealloc
178{
179    delete m_pRecentFilesItems;
180    [super dealloc];
181}
182
183-(void)menuNeedsUpdate:(NSMenu *)menu
184{
185    // clear menu
186    int nItems = [menu numberOfItems];
187    while( nItems -- )
188        [menu removeItemAtIndex: 0];
189
190    // update recent item list
191    Sequence< Sequence< PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) );
192
193    int nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength();
194
195    m_pRecentFilesItems->clear();
196    if( ( nPickListMenuItems > 0 ) )
197    {
198        for ( int i = 0; i < nPickListMenuItems; i++ )
199        {
200            Sequence< PropertyValue >& rPickListEntry = aHistoryList[i];
201            RecentMenuEntry aRecentFile;
202
203            for ( int j = 0; j < rPickListEntry.getLength(); j++ )
204            {
205                Any a = rPickListEntry[j].Value;
206
207                if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_URL )
208                    a >>= aRecentFile.aURL;
209                else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_FILTER )
210                    a >>= aRecentFile.aFilter;
211                else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_TITLE )
212                    a >>= aRecentFile.aTitle;
213                else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_PASSWORD )
214                    a >>= aRecentFile.aPassword;
215            }
216
217            m_pRecentFilesItems->push_back( aRecentFile );
218        }
219    }
220
221    // insert new recent items
222    for ( sal_uInt32 i = 0; i < m_pRecentFilesItems->size(); i++ )
223    {
224        rtl::OUString   aMenuTitle;
225        INetURLObject   aURL( (*m_pRecentFilesItems)[i].aURL );
226
227        if ( aURL.GetProtocol() == INET_PROT_FILE )
228        {
229            // Do handle file URL differently => convert it to a system
230            // path and abbreviate it with a special function:
231            String aFileSystemPath( aURL.getFSysPath( INetURLObject::FSYS_DETECT ) );
232
233            ::rtl::OUString aSystemPath( aFileSystemPath );
234            ::rtl::OUString aCompactedSystemPath;
235
236            oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL );
237            if ( !nError )
238                aMenuTitle = String( aCompactedSystemPath );
239            else
240                aMenuTitle = aSystemPath;
241        }
242        else
243        {
244            // Use INetURLObject to abbreviate all other URLs
245            Reference< XStringWidth > xStringLength( new RecentFilesStringLength() );
246            aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS );
247        }
248
249        NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
250                                                   action: @selector(executeRecentEntry:)
251                                                   keyEquivalent: @""];
252        [pNewItem setTag: i];
253        [pNewItem setTarget: self];
254        [pNewItem setEnabled: YES];
255        [menu addItem: pNewItem];
256        [pNewItem autorelease];
257    }
258}
259
260-(void)executeRecentEntry: (NSMenuItem*)item
261{
262    sal_Int32 nIndex = [item tag];
263    if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
264    {
265        const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
266        int NUM_OF_PICKLIST_ARGS = 3;
267        Sequence< PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
268
269        aArgsList[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer" ));
270        aArgsList[0].Value = makeAny( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:user" ) ) );
271
272        // documents in the picklist will never be opened as templates
273        aArgsList[1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate" ));
274        aArgsList[1].Value = makeAny( (sal_Bool) sal_False );
275
276        ::rtl::OUString  aFilter( rRecentFile.aFilter );
277        sal_Int32 nPos = aFilter.indexOf( '|' );
278        if ( nPos >= 0 )
279        {
280            rtl::OUString aFilterOptions;
281
282            if ( nPos < ( aFilter.getLength() - 1 ) )
283                aFilterOptions = aFilter.copy( nPos+1 );
284
285            aArgsList[2].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterOptions" ));
286            aArgsList[2].Value = makeAny( aFilterOptions );
287
288            aFilter = aFilter.copy( 0, nPos-1 );
289            aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
290        }
291
292        aArgsList[NUM_OF_PICKLIST_ARGS-1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" ));
293        aArgsList[NUM_OF_PICKLIST_ARGS-1].Value = makeAny( aFilter );
294
295        ShutdownIcon::OpenURL( rRecentFile.aURL, OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ), aArgsList );
296    }
297}
298@end
299
300static RecentMenuDelegate* pRecentDelegate = nil;
301
302static rtl::OUString getShortCut( const rtl::OUString i_rTitle )
303{
304    // create shortcut
305    rtl::OUString aKeyEquiv;
306    for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
307    {
308        rtl::OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
309        if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
310        {
311            aShortcuts.insert( aShortcut );
312            aKeyEquiv = aShortcut;
313            break;
314        }
315    }
316
317    return aKeyEquiv;
318}
319
320static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const rtl::OUString& i_rTitle, int i_nTag, const rtl::OUString& i_rKeyEquiv )
321{
322    if( ! i_rTitle.getLength() )
323        return;
324
325    NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
326                                            action: @selector(executeMenuItem:)
327                                            keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
328                        ];
329    [pItem setTag: i_nTag];
330    [pItem setTarget: pExecute];
331    [pItem setEnabled: YES];
332    [i_pMenu addItem: pItem];
333
334    if( i_pDockMenu )
335    {
336        // create a similar entry in the dock menu
337        pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
338                                    action: @selector(executeMenuItem:)
339                                    keyEquivalent: @""
340                            ];
341        [pItem setTag: i_nTag];
342        [pItem setTarget: pExecute];
343        [pItem setEnabled: YES];
344        [i_pDockMenu addItem: pItem];
345    }
346}
347
348static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const String& i_rTitle )
349{
350    if( ! pRecentDelegate )
351        pRecentDelegate = [[RecentMenuDelegate alloc] init];
352
353    NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
354                                                   action: @selector(executeMenuItem:)
355                                                   keyEquivalent: @""
356                        ];
357    [pItem setEnabled: YES];
358    NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
359    [pRecentMenu setDelegate: pRecentDelegate];
360    [pRecentMenu setAutoenablesItems: NO];
361    [pItem setSubmenu: pRecentMenu];
362
363    if( i_pDockMenu )
364    {
365        // create a similar entry in the dock menu
366        pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
367                             action: @selector(executeMenuItem:)
368                             keyEquivalent: @""
369                        ];
370        [pItem setEnabled: YES];
371        pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
372        [pRecentMenu setDelegate: pRecentDelegate];
373        [pRecentMenu setAutoenablesItems: NO];
374        [pItem setSubmenu: pRecentMenu];
375    }
376}
377
378
379extern "C"
380{
381
382void aqua_init_systray()
383{
384    ::vos::OGuard aGuard( Application::GetSolarMutex() );
385
386    ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
387    if( ! pShutdownIcon )
388        return;
389
390    // disable shutdown
391    pShutdownIcon->SetVeto( true );
392    pShutdownIcon->addTerminateListener();
393
394    if( ! pDefMenu )
395    {
396        if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
397        {
398            aShortcuts.clear();
399
400            pExecute = [[QSMenuExecute alloc] init];
401            pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""];
402            pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""];
403            NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )];
404            [pMenu setAutoenablesItems: NO];
405            NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )];
406            [pDockMenu setAutoenablesItems: NO];
407
408            // collect the URLs of the entries in the File/New menu
409            SvtModuleOptions    aModuleOptions;
410            std::set< rtl::OUString > aFileNewAppsAvailable;
411            SvtDynamicMenuOptions aOpt;
412            Sequence < Sequence < PropertyValue > > aNewMenu = aOpt.GetMenu( E_NEWMENU );
413            const rtl::OUString sURLKey( RTL_CONSTASCII_USTRINGPARAM( "URL" ) );
414
415            const Sequence< PropertyValue >* pNewMenu = aNewMenu.getConstArray();
416            const Sequence< PropertyValue >* pNewMenuEnd = aNewMenu.getConstArray() + aNewMenu.getLength();
417            for ( ; pNewMenu != pNewMenuEnd; ++pNewMenu )
418            {
419                comphelper::SequenceAsHashMap aEntryItems( *pNewMenu );
420                rtl::OUString sURL( aEntryItems.getUnpackedValueOrDefault( sURLKey, rtl::OUString() ) );
421                if ( sURL.getLength() )
422                    aFileNewAppsAvailable.insert( sURL );
423            }
424
425            // describe the menu entries for launching the applications
426            struct MenuEntryDescriptor
427            {
428                SvtModuleOptions::EModule   eModuleIdentifier;
429                int                         nMenuTag;
430                const char*                 pAsciiURLDescription;
431            }   aMenuItems[] =
432            {
433                { SvtModuleOptions::E_SWRITER,    MI_WRITER,  WRITER_URL },
434                { SvtModuleOptions::E_SCALC,      MI_CALC,    CALC_URL },
435                { SvtModuleOptions::E_SIMPRESS,   MI_IMPRESS, IMPRESS_WIZARD_URL },
436                { SvtModuleOptions::E_SDRAW,      MI_DRAW,    DRAW_URL },
437                { SvtModuleOptions::E_SDATABASE,  MI_BASE,    BASE_URL },
438                { SvtModuleOptions::E_SMATH,      MI_MATH,    MATH_URL }
439            };
440
441            // insert entry for startcenter
442            if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::E_SSTARTMODULE ) )
443            {
444                appendMenuItem( pMenu, nil, pShutdownIcon->GetResString( STR_QUICKSTART_STARTCENTER ), MI_STARTMODULE, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "n" ) ) );
445                if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
446                    [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
447                else
448                    DBG_ERROR( "setDockIconClickHandler selector failed on NSApp\n" );
449
450            }
451
452            // insert the menu entries for launching the applications
453            for ( size_t i = 0; i < sizeof( aMenuItems ) / sizeof( aMenuItems[0] ); ++i )
454            {
455                if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
456                    // the complete application is not even installed
457                    continue;
458
459                rtl::OUString sURL( ::rtl::OUString::createFromAscii( aMenuItems[i].pAsciiURLDescription ) );
460
461                if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
462                    // the application is installed, but the entry has been configured to *not* appear in the File/New
463                    // menu => also let not appear it in the quickstarter
464                    continue;
465
466                rtl::OUString aKeyEquiv( getShortCut( pShutdownIcon->GetUrlDescription( sURL ) ) );
467
468                appendMenuItem( pMenu, pDockMenu, pShutdownIcon->GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
469            }
470
471            // insert the remaining menu entries
472
473            // add recent menu
474            appendRecentMenu( pMenu, pDockMenu, pShutdownIcon->GetResString( STR_QUICKSTART_RECENTDOC ) );
475
476            rtl::OUString aTitle( pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE ) );
477            rtl::OUString aKeyEquiv( getShortCut( aTitle ) );
478            appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
479            aTitle = pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN );
480            aKeyEquiv = getShortCut( aTitle );
481            appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
482
483            [pDefMenu setSubmenu: pMenu];
484            [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
485
486            if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
487            {
488                [pDockSubMenu setSubmenu: pDockMenu];
489                // insert a separator to the dock menu
490                [NSApp performSelector:@selector(addDockMenuItem:) withObject: [NSMenuItem separatorItem]];
491                // and now add the submenu
492                [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
493            }
494            else
495                DBG_ERROR( "addDockMenuItem selector failed on NSApp\n" );
496        }
497        else
498            DBG_ERROR( "addFallbackMenuItem selector failed on NSApp\n" );
499    }
500}
501
502void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
503{
504}
505
506}
507