LibreOffice Module sfx2 (master) 1
shutdowniconaqua.mm
Go to the documentation of this file.
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
24#include <rtl/ustring.hxx>
25#include <tools/urlobj.hxx>
26#include <osl/file.h>
27#include <osl/diagnose.h>
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"
35
36#include <com/sun/star/util/XStringWidth.hpp>
37
39
40#include <set>
41#include <vector>
42
43#include <premac.h>
44#include <objc/objc-runtime.h>
45#include <Cocoa/Cocoa.h>
46#include <postmac.h>
47
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
57
58@interface QSMenuExecute : NSObject
59{
60}
61-(void)executeMenuItem: (NSMenuItem*)pItem;
62-(void)dockIconClicked: (NSObject*)pSender;
63@end
64
65@implementation QSMenuExecute
66-(void)executeMenuItem: (NSMenuItem*)pItem
67{
68 switch( [pItem tag] )
69 {
70 case MI_OPEN:
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:
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:
93 break;
94 case MI_STARTMODULE:
96 break;
97 default:
98 break;
99 }
100}
101
102-(void)dockIconClicked: (NSObject*)pSender
103{
104 (void)pSender;
105 // start module
107}
108
109@end
110
112{
113 return true;
114}
115
116static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
118
119static std::set< OUString > aShortcuts;
120
121static NSString* getAutoreleasedString( const OUString& rStr )
122{
123 return [[[NSString alloc] initWithCharacters: reinterpret_cast<unichar const *>(rStr.getStr()) length: rStr.getLength()] autorelease];
124}
125
126namespace {
127
128struct RecentMenuEntry
129{
130 OUString aURL;
131 OUString aFilter;
132 OUString aTitle;
133 OUString aPassword;
134};
135
136class RecentFilesStringLength : public ::cppu::WeakImplHelper< css::util::XStringWidth >
137{
138 public:
139 RecentFilesStringLength() {}
140
141 // XStringWidth
142 sal_Int32 SAL_CALL queryStringWidth( const OUString& aString ) override
143 {
144 return aString.getLength();
145 }
146};
147
148}
149
150@interface RecentMenuDelegate : NSObject <NSMenuDelegate>
151{
152 std::vector< RecentMenuEntry >* m_pRecentFilesItems;
154-(id)init;
155-(void)dealloc;
156-(void)menuNeedsUpdate:(NSMenu *)menu;
157-(void)executeRecentEntry: (NSMenuItem*)item;
158@end
159
160@implementation RecentMenuDelegate
161-(id)init
162{
163 if( (self = [super init]) )
164 {
165 m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
166 }
167 return self;
168}
169
170-(void)dealloc
171{
172 delete m_pRecentFilesItems;
173 [super dealloc];
174}
175
176-(void)menuNeedsUpdate:(NSMenu *)menu
177{
178 // clear menu
179 int nItems = [menu numberOfItems];
180 while( nItems -- )
181 [menu removeItemAtIndex: 0];
182
183 // update recent item list
184 std::vector< SvtHistoryOptions::HistoryItem > aHistoryList( SvtHistoryOptions::GetList( EHistoryType::PickList ) );
185
186 int nPickListMenuItems = ( aHistoryList.size() > 99 ) ? 99 : aHistoryList.size();
187
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 aRecentFile.aPassword = rPickListEntry.sPassword;
199 m_pRecentFilesItems->push_back( aRecentFile );
200 }
201 }
202
203 // insert new recent items
204 for ( std::vector<RecentMenuEntry>::size_type i = 0; i < m_pRecentFilesItems->size(); i++ )
205 {
206 OUString aMenuTitle;
208
209 if ( aURL.GetProtocol() == INetProtocol::File )
210 {
211 // Do handle file URL differently => convert it to a system
212 // path and abbreviate it with a special function:
213 OUString aSystemPath( aURL.getFSysPath( FSysStyle::Detect ) );
214 OUString aCompactedSystemPath;
215
216 oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, nullptr );
217 if ( !nError )
218 aMenuTitle = aCompactedSystemPath;
219 else
220 aMenuTitle = aSystemPath;
221 }
222 else
223 {
224 // Use INetURLObject to abbreviate all other URLs
225 css::uno::Reference< css::util::XStringWidth > xStringLength( new RecentFilesStringLength() );
226 aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DecodeMechanism::Unambiguous );
227 }
228
229 NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
230 action: @selector(executeRecentEntry:)
231 keyEquivalent: @""];
232 [pNewItem setTag: i];
233 [pNewItem setTarget: self];
234 [pNewItem setEnabled: YES];
235 [menu addItem: pNewItem];
236 [pNewItem autorelease];
237 }
238}
239
240-(void)executeRecentEntry: (NSMenuItem*)item
241{
242 sal_Int32 nIndex = [item tag];
243 if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
244 {
245 const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
246 int NUM_OF_PICKLIST_ARGS = 3;
247 css::uno::Sequence< css::beans::PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
248 css::beans::PropertyValue* pArgsList = aArgsList.getArray();
249
250 pArgsList[0].Name = "Referer";
251 pArgsList[0].Value <<= OUString( "private:user" );
252
253 // documents in the picklist will never be opened as templates
254 pArgsList[1].Name = "AsTemplate";
255 pArgsList[1].Value <<= false;
256
257 OUString aFilter( rRecentFile.aFilter );
258 sal_Int32 nPos = aFilter.indexOf( '|' );
259 if ( nPos >= 0 )
260 {
261 OUString aFilterOptions;
262
263 if ( nPos < ( aFilter.getLength() - 1 ) )
264 aFilterOptions = aFilter.copy( nPos+1 );
265
266 pArgsList[2].Name = "FilterOptions";
267 pArgsList[2].Value <<= aFilterOptions;
268
269 aFilter = aFilter.copy( 0, nPos-1 );
270 aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
271 pArgsList = aArgsList.getArray();
272 }
273
274 pArgsList[NUM_OF_PICKLIST_ARGS-1].Name = "FilterName";
275 pArgsList[NUM_OF_PICKLIST_ARGS-1].Value <<= aFilter;
276
277 ShutdownIcon::OpenURL( rRecentFile.aURL, "_default", aArgsList );
278 }
279}
280@end
281
284static OUString getShortCut( const OUString& i_rTitle )
286 // create shortcut
287 OUString aKeyEquiv;
288 for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
289 {
290 OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
291 if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
292 {
293 aShortcuts.insert( aShortcut );
294 aKeyEquiv = aShortcut;
295 break;
296 }
297 }
298
299 return aKeyEquiv;
300}
301
302static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const OUString& i_rTitle, int i_nTag, const OUString& i_rKeyEquiv )
304 if( ! i_rTitle.getLength() )
305 return;
306
307 NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
308 action: @selector(executeMenuItem:)
309 keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
310 ];
311 [pItem setTag: i_nTag];
312 [pItem setTarget: pExecute];
313 [pItem setEnabled: YES];
314 [i_pMenu addItem: pItem];
315
316 if( i_pDockMenu )
317 {
318 // create a similar entry in the dock menu
319 pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
320 action: @selector(executeMenuItem:)
321 keyEquivalent: @""
322 ];
323 [pItem setTag: i_nTag];
324 [pItem setTarget: pExecute];
325 [pItem setEnabled: YES];
326 [i_pDockMenu addItem: pItem];
327 }
328}
329
330static void appendRecentMenu( NSMenu* i_pMenu, const OUString& i_rTitle )
332 if( ! pRecentDelegate )
333 pRecentDelegate = [[RecentMenuDelegate alloc] init];
334
335 NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
336 action: @selector(executeMenuItem:)
337 keyEquivalent: @""
338 ];
339 [pItem setEnabled: YES];
340 NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
341
342 [pRecentMenu setDelegate: pRecentDelegate];
343
344 [pRecentMenu setAutoenablesItems: NO];
345 [pItem setSubmenu: pRecentMenu];
346}
347
348
349extern "C"
350{
351
354 SolarMutexGuard aGuard;
355
356 ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
357 if( ! pShutdownIcon )
358 return;
359
360 // disable shutdown
361 pShutdownIcon->SetVeto( true );
363
364 if( ! pDefMenu )
365 {
366 if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
367 {
368 aShortcuts.clear();
369
370 pExecute = [[QSMenuExecute alloc] init];
371 pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
372 pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) ) action: nullptr keyEquivalent: @""];
373 NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
374 [pMenu setAutoenablesItems: NO];
375 NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( SfxResId(STR_QUICKSTART_FILE) )];
376 [pDockMenu setAutoenablesItems: NO];
377
378 // collect the URLs of the entries in the File/New menu
379 SvtModuleOptions aModuleOptions;
380 std::set< OUString > aFileNewAppsAvailable;
381 std::vector < SvtDynMenuEntry > const aNewMenu = SvtDynamicMenuOptions::GetMenu( EDynamicMenuType::NewMenu );
382
383 for ( SvtDynMenuEntry const & newMenuProp : aNewMenu )
384 {
385 if ( !newMenuProp.sURL.isEmpty() )
386 aFileNewAppsAvailable.insert( newMenuProp.sURL );
387 }
388
389 // describe the menu entries for launching the applications
390 struct MenuEntryDescriptor
391 {
392 SvtModuleOptions::EModule eModuleIdentifier;
393 int nMenuTag;
394 rtl::OUStringConstExpr sURLDescription;
395 } static const aMenuItems[] =
396 {
403 };
404
405 // insert entry for startcenter
407 {
408 appendMenuItem( pMenu, nil, SfxResId(STR_QUICKSTART_STARTCENTER), MI_STARTMODULE, "n" );
409 if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
410 [NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
411 else
412 OSL_FAIL( "setDockIconClickHandler selector failed on NSApp" );
413
414 }
415
416 // insert the menu entries for launching the applications
417 for ( size_t i = 0; i < SAL_N_ELEMENTS( aMenuItems ); ++i )
418 {
419 if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
420 // the complete application is not even installed
421 continue;
422
423 const OUString& sURL( aMenuItems[i].sURLDescription );
424
425 if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
426 // the application is installed, but the entry has been configured to *not* appear in the File/New
427 // menu => also let not appear it in the quickstarter
428 continue;
429
430 OUString aKeyEquiv( getShortCut( ShutdownIcon::GetUrlDescription( sURL ) ) );
431
432 appendMenuItem( pMenu, pDockMenu, ShutdownIcon::GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
433 }
434
435 // insert the remaining menu entries
436
437 // add recent menu
438 appendRecentMenu( pMenu, SfxResId(STR_QUICKSTART_RECENTDOC) );
439
440 OUString aTitle( SfxResId(STR_QUICKSTART_FROMTEMPLATE) );
441 OUString aKeyEquiv( getShortCut( aTitle ) );
442 appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
443 aTitle = SfxResId(STR_QUICKSTART_FILEOPEN);
444 aKeyEquiv = getShortCut( aTitle );
445 appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
446
447 [pDefMenu setSubmenu: pMenu];
448 [NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
449
450 if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
451 {
452 [pDockSubMenu setSubmenu: pDockMenu];
453 // add the submenu
454 [NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
455 }
456 else
457 OSL_FAIL( "addDockMenuItem selector failed on NSApp" );
458 }
459 else
460 OSL_FAIL( "addFallbackMenuItem selector failed on NSApp" );
461 }
462}
463
464void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
466}
467
468}
469
470/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static bool IsQuickstarterInstalled()
static ShutdownIcon * getInstance()
static void FromTemplate()
static void addTerminateListener()
static void FileOpen()
static OUString GetUrlDescription(std::u16string_view aUrl)
static void OpenURL(const OUString &aURL, const OUString &rTarget, const css::uno::Sequence< css::beans::PropertyValue > &=css::uno::Sequence< css::beans::PropertyValue >(0))
void SetVeto(bool bVeto)
bool IsModuleInstalled(EModule eModule) const
URL aURL
std::vector< RecentMenuEntry > * m_pRecentFilesItems
sal_Int32 nIndex
sal_uInt16 nPos
Definition: linksrc.cxx:118
#define SAL_N_ELEMENTS(arr)
std::vector< SvtDynMenuEntry > GetMenu(EDynamicMenuType eMenu)
std::vector< HistoryItem > GetList(EHistoryType eHistory)
int i
static void init(struct DocumentMetadataAccess_Impl &i_rImpl)
init Impl struct
OUString SfxResId(TranslateId aId)
Definition: sfxresid.cxx:22
constexpr OUStringLiteral DRAW_URL
constexpr OUStringLiteral IMPRESS_URL
constexpr OUStringLiteral MATH_URL
constexpr OUStringLiteral STARTMODULE_URL
constexpr OUStringLiteral CALC_URL
constexpr OUStringLiteral BASE_URL
constexpr OUStringLiteral WRITER_URL
constexpr OUStringLiteral IMPRESS_WIZARD_URL
static NSString * getAutoreleasedString(const OUString &rStr)
#define MI_TEMPLATE
#define MI_IMPRESS
#define MI_DRAW
#define MI_OPEN
#define MI_STARTMODULE
static NSMenuItem * pDockSubMenu
static void appendRecentMenu(NSMenu *i_pMenu, const OUString &i_rTitle)
static RecentMenuDelegate * pRecentDelegate
static OUString getShortCut(const OUString &i_rTitle)
#define MI_BASE
#define MI_CALC
void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
#define MI_MATH
static std::set< OUString > aShortcuts
static void appendMenuItem(NSMenu *i_pMenu, NSMenu *i_pDockMenu, const OUString &i_rTitle, int i_nTag, const OUString &i_rKeyEquiv)
#define MI_WRITER
static NSMenuItem * pDefMenu
static QSMenuExecute * pExecute
void aqua_init_systray()