blob: 3d9c5d0b16e9295e0b629184f31ab42162c0733b [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sfx2.hxx"
#include "unotools/moduleoptions.hxx"
#include "unotools/dynamicmenuoptions.hxx"
#include "unotools/historyoptions.hxx"
#include "tools/urlobj.hxx"
#include "osl/file.h"
#include "comphelper/sequenceashashmap.hxx"
#include "vos/mutex.hxx"
#include "sfx2/app.hxx"
#include "app.hrc"
#define USE_APP_SHORTCUTS
#include "shutdownicon.hxx"
#include "com/sun/star/util/XStringWidth.hpp"
#include "cppuhelper/implbase1.hxx"
#include <set>
#include <vector>
#include "premac.h"
#include <Cocoa/Cocoa.h>
#include "postmac.h"
using namespace ::rtl;
using namespace ::osl;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::task;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::util;
#define MI_OPEN 1
#define MI_WRITER 2
#define MI_CALC 3
#define MI_IMPRESS 4
#define MI_DRAW 5
#define MI_BASE 6
#define MI_MATH 7
#define MI_TEMPLATE 8
#define MI_STARTMODULE 9
@interface QSMenuExecute : NSObject
{
}
-(void)executeMenuItem: (NSMenuItem*)pItem;
-(void)dockIconClicked: (NSObject*)pSender;
@end
@implementation QSMenuExecute
-(void)executeMenuItem: (NSMenuItem*)pItem
{
switch( [pItem tag] )
{
case MI_OPEN:
ShutdownIcon::FileOpen();
break;
case MI_WRITER:
ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( WRITER_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
break;
case MI_CALC:
ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( CALC_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
break;
case MI_IMPRESS:
ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( IMPRESS_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
break;
case MI_DRAW:
ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( DRAW_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
break;
case MI_BASE:
ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( BASE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
break;
case MI_MATH:
ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( MATH_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
break;
case MI_TEMPLATE:
ShutdownIcon::FromTemplate();
break;
case MI_STARTMODULE:
ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
break;
default:
break;
}
}
-(void)dockIconClicked: (NSObject*)pSender
{
(void)pSender;
// start start module
ShutdownIcon::OpenURL( OUString( RTL_CONSTASCII_USTRINGPARAM( STARTMODULE_URL ) ), OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ) );
}
@end
bool ShutdownIcon::IsQuickstarterInstalled()
{
return true;
}
static NSMenuItem* pDefMenu = nil, *pDockSubMenu = nil;
static QSMenuExecute* pExecute = nil;
static std::set< OUString > aShortcuts;
static NSString* getAutoreleasedString( const rtl::OUString& rStr )
{
return [[[NSString alloc] initWithCharacters: rStr.getStr() length: rStr.getLength()] autorelease];
}
struct RecentMenuEntry
{
rtl::OUString aURL;
rtl::OUString aFilter;
rtl::OUString aTitle;
rtl::OUString aPassword;
};
class RecentFilesStringLength : public ::cppu::WeakImplHelper1< ::com::sun::star::util::XStringWidth >
{
public:
RecentFilesStringLength() {}
virtual ~RecentFilesStringLength() {}
// XStringWidth
sal_Int32 SAL_CALL queryStringWidth( const ::rtl::OUString& aString )
throw (::com::sun::star::uno::RuntimeException)
{
return aString.getLength();
}
};
@interface RecentMenuDelegate : NSObject
{
std::vector< RecentMenuEntry >* m_pRecentFilesItems;
}
-(id)init;
-(void)dealloc;
-(void)menuNeedsUpdate:(NSMenu *)menu;
-(void)executeRecentEntry: (NSMenuItem*)item;
@end
@implementation RecentMenuDelegate
-(id)init
{
if( (self = [super init]) )
{
m_pRecentFilesItems = new std::vector< RecentMenuEntry >();
}
return self;
}
-(void)dealloc
{
delete m_pRecentFilesItems;
[super dealloc];
}
-(void)menuNeedsUpdate:(NSMenu *)menu
{
// clear menu
int nItems = [menu numberOfItems];
while( nItems -- )
[menu removeItemAtIndex: 0];
// update recent item list
Sequence< Sequence< PropertyValue > > aHistoryList( SvtHistoryOptions().GetList( ePICKLIST ) );
int nPickListMenuItems = ( aHistoryList.getLength() > 99 ) ? 99 : aHistoryList.getLength();
m_pRecentFilesItems->clear();
if( ( nPickListMenuItems > 0 ) )
{
for ( int i = 0; i < nPickListMenuItems; i++ )
{
Sequence< PropertyValue >& rPickListEntry = aHistoryList[i];
RecentMenuEntry aRecentFile;
for ( int j = 0; j < rPickListEntry.getLength(); j++ )
{
Any a = rPickListEntry[j].Value;
if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_URL )
a >>= aRecentFile.aURL;
else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_FILTER )
a >>= aRecentFile.aFilter;
else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_TITLE )
a >>= aRecentFile.aTitle;
else if ( rPickListEntry[j].Name == HISTORY_PROPERTYNAME_PASSWORD )
a >>= aRecentFile.aPassword;
}
m_pRecentFilesItems->push_back( aRecentFile );
}
}
// insert new recent items
for ( sal_uInt32 i = 0; i < m_pRecentFilesItems->size(); i++ )
{
rtl::OUString aMenuTitle;
INetURLObject aURL( (*m_pRecentFilesItems)[i].aURL );
if ( aURL.GetProtocol() == INET_PROT_FILE )
{
// Do handle file URL differently => convert it to a system
// path and abbreviate it with a special function:
String aFileSystemPath( aURL.getFSysPath( INetURLObject::FSYS_DETECT ) );
::rtl::OUString aSystemPath( aFileSystemPath );
::rtl::OUString aCompactedSystemPath;
oslFileError nError = osl_abbreviateSystemPath( aSystemPath.pData, &aCompactedSystemPath.pData, 46, NULL );
if ( !nError )
aMenuTitle = String( aCompactedSystemPath );
else
aMenuTitle = aSystemPath;
}
else
{
// Use INetURLObject to abbreviate all other URLs
Reference< XStringWidth > xStringLength( new RecentFilesStringLength() );
aMenuTitle = aURL.getAbbreviated( xStringLength, 46, INetURLObject::DECODE_UNAMBIGUOUS );
}
NSMenuItem* pNewItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( aMenuTitle )
action: @selector(executeRecentEntry:)
keyEquivalent: @""];
[pNewItem setTag: i];
[pNewItem setTarget: self];
[pNewItem setEnabled: YES];
[menu addItem: pNewItem];
[pNewItem autorelease];
}
}
-(void)executeRecentEntry: (NSMenuItem*)item
{
sal_Int32 nIndex = [item tag];
if( ( nIndex >= 0 ) && ( nIndex < static_cast<sal_Int32>( m_pRecentFilesItems->size() ) ) )
{
const RecentMenuEntry& rRecentFile = (*m_pRecentFilesItems)[ nIndex ];
int NUM_OF_PICKLIST_ARGS = 3;
Sequence< PropertyValue > aArgsList( NUM_OF_PICKLIST_ARGS );
aArgsList[0].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Referer" ));
aArgsList[0].Value = makeAny( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "private:user" ) ) );
// documents in the picklist will never be opened as templates
aArgsList[1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AsTemplate" ));
aArgsList[1].Value = makeAny( (sal_Bool) sal_False );
::rtl::OUString aFilter( rRecentFile.aFilter );
sal_Int32 nPos = aFilter.indexOf( '|' );
if ( nPos >= 0 )
{
rtl::OUString aFilterOptions;
if ( nPos < ( aFilter.getLength() - 1 ) )
aFilterOptions = aFilter.copy( nPos+1 );
aArgsList[2].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterOptions" ));
aArgsList[2].Value = makeAny( aFilterOptions );
aFilter = aFilter.copy( 0, nPos-1 );
aArgsList.realloc( ++NUM_OF_PICKLIST_ARGS );
}
aArgsList[NUM_OF_PICKLIST_ARGS-1].Name = rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FilterName" ));
aArgsList[NUM_OF_PICKLIST_ARGS-1].Value = makeAny( aFilter );
ShutdownIcon::OpenURL( rRecentFile.aURL, OUString( RTL_CONSTASCII_USTRINGPARAM( "_default" ) ), aArgsList );
}
}
@end
static RecentMenuDelegate* pRecentDelegate = nil;
static rtl::OUString getShortCut( const rtl::OUString i_rTitle )
{
// create shortcut
rtl::OUString aKeyEquiv;
for( sal_Int32 nIndex = 0; nIndex < i_rTitle.getLength(); nIndex++ )
{
rtl::OUString aShortcut( i_rTitle.copy( nIndex, 1 ).toAsciiLowerCase() );
if( aShortcuts.find( aShortcut ) == aShortcuts.end() )
{
aShortcuts.insert( aShortcut );
aKeyEquiv = aShortcut;
break;
}
}
return aKeyEquiv;
}
static void appendMenuItem( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const rtl::OUString& i_rTitle, int i_nTag, const rtl::OUString& i_rKeyEquiv )
{
if( ! i_rTitle.getLength() )
return;
NSMenuItem* pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
action: @selector(executeMenuItem:)
keyEquivalent: (i_rKeyEquiv.getLength() ? getAutoreleasedString( i_rKeyEquiv ) : @"")
];
[pItem setTag: i_nTag];
[pItem setTarget: pExecute];
[pItem setEnabled: YES];
[i_pMenu addItem: pItem];
if( i_pDockMenu )
{
// create a similar entry in the dock menu
pItem = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( i_rTitle )
action: @selector(executeMenuItem:)
keyEquivalent: @""
];
[pItem setTag: i_nTag];
[pItem setTarget: pExecute];
[pItem setEnabled: YES];
[i_pDockMenu addItem: pItem];
}
}
static void appendRecentMenu( NSMenu* i_pMenu, NSMenu* i_pDockMenu, const String& i_rTitle )
{
if( ! pRecentDelegate )
pRecentDelegate = [[RecentMenuDelegate alloc] init];
NSMenuItem* pItem = [i_pMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
action: @selector(executeMenuItem:)
keyEquivalent: @""
];
[pItem setEnabled: YES];
NSMenu* pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
[pRecentMenu setDelegate: pRecentDelegate];
[pRecentMenu setAutoenablesItems: NO];
[pItem setSubmenu: pRecentMenu];
if( i_pDockMenu )
{
// create a similar entry in the dock menu
pItem = [i_pDockMenu addItemWithTitle: getAutoreleasedString( i_rTitle )
action: @selector(executeMenuItem:)
keyEquivalent: @""
];
[pItem setEnabled: YES];
pRecentMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( i_rTitle ) ];
[pRecentMenu setDelegate: pRecentDelegate];
[pRecentMenu setAutoenablesItems: NO];
[pItem setSubmenu: pRecentMenu];
}
}
extern "C"
{
void aqua_init_systray()
{
::vos::OGuard aGuard( Application::GetSolarMutex() );
ShutdownIcon *pShutdownIcon = ShutdownIcon::getInstance();
if( ! pShutdownIcon )
return;
// disable shutdown
pShutdownIcon->SetVeto( true );
pShutdownIcon->addTerminateListener();
if( ! pDefMenu )
{
if( [NSApp respondsToSelector: @selector(addFallbackMenuItem:)] )
{
aShortcuts.clear();
pExecute = [[QSMenuExecute alloc] init];
pDefMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""];
pDockSubMenu = [[NSMenuItem alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) ) action: NULL keyEquivalent: @""];
NSMenu* pMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )];
[pMenu setAutoenablesItems: NO];
NSMenu* pDockMenu = [[NSMenu alloc] initWithTitle: getAutoreleasedString( pShutdownIcon->GetResString( STR_QUICKSTART_FILE ) )];
[pDockMenu setAutoenablesItems: NO];
// collect the URLs of the entries in the File/New menu
SvtModuleOptions aModuleOptions;
std::set< rtl::OUString > aFileNewAppsAvailable;
SvtDynamicMenuOptions aOpt;
Sequence < Sequence < PropertyValue > > aNewMenu = aOpt.GetMenu( E_NEWMENU );
const rtl::OUString sURLKey( RTL_CONSTASCII_USTRINGPARAM( "URL" ) );
const Sequence< PropertyValue >* pNewMenu = aNewMenu.getConstArray();
const Sequence< PropertyValue >* pNewMenuEnd = aNewMenu.getConstArray() + aNewMenu.getLength();
for ( ; pNewMenu != pNewMenuEnd; ++pNewMenu )
{
comphelper::SequenceAsHashMap aEntryItems( *pNewMenu );
rtl::OUString sURL( aEntryItems.getUnpackedValueOrDefault( sURLKey, rtl::OUString() ) );
if ( sURL.getLength() )
aFileNewAppsAvailable.insert( sURL );
}
// describe the menu entries for launching the applications
struct MenuEntryDescriptor
{
SvtModuleOptions::EModule eModuleIdentifier;
int nMenuTag;
const char* pAsciiURLDescription;
} aMenuItems[] =
{
{ SvtModuleOptions::E_SWRITER, MI_WRITER, WRITER_URL },
{ SvtModuleOptions::E_SCALC, MI_CALC, CALC_URL },
{ SvtModuleOptions::E_SIMPRESS, MI_IMPRESS, IMPRESS_WIZARD_URL },
{ SvtModuleOptions::E_SDRAW, MI_DRAW, DRAW_URL },
{ SvtModuleOptions::E_SDATABASE, MI_BASE, BASE_URL },
{ SvtModuleOptions::E_SMATH, MI_MATH, MATH_URL }
};
// insert entry for startcenter
if( aModuleOptions.IsModuleInstalled( SvtModuleOptions::E_SSTARTMODULE ) )
{
appendMenuItem( pMenu, nil, pShutdownIcon->GetResString( STR_QUICKSTART_STARTCENTER ), MI_STARTMODULE, rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "n" ) ) );
if( [NSApp respondsToSelector: @selector(setDockIconClickHandler:)] )
[NSApp performSelector:@selector(setDockIconClickHandler:) withObject: pExecute];
else
DBG_ERROR( "setDockIconClickHandler selector failed on NSApp\n" );
}
// insert the menu entries for launching the applications
for ( size_t i = 0; i < sizeof( aMenuItems ) / sizeof( aMenuItems[0] ); ++i )
{
if ( !aModuleOptions.IsModuleInstalled( aMenuItems[i].eModuleIdentifier ) )
// the complete application is not even installed
continue;
rtl::OUString sURL( ::rtl::OUString::createFromAscii( aMenuItems[i].pAsciiURLDescription ) );
if ( aFileNewAppsAvailable.find( sURL ) == aFileNewAppsAvailable.end() )
// the application is installed, but the entry has been configured to *not* appear in the File/New
// menu => also let not appear it in the quickstarter
continue;
rtl::OUString aKeyEquiv( getShortCut( pShutdownIcon->GetUrlDescription( sURL ) ) );
appendMenuItem( pMenu, pDockMenu, pShutdownIcon->GetUrlDescription( sURL ), aMenuItems[i].nMenuTag, aKeyEquiv );
}
// insert the remaining menu entries
// add recent menu
appendRecentMenu( pMenu, pDockMenu, pShutdownIcon->GetResString( STR_QUICKSTART_RECENTDOC ) );
rtl::OUString aTitle( pShutdownIcon->GetResString( STR_QUICKSTART_FROMTEMPLATE ) );
rtl::OUString aKeyEquiv( getShortCut( aTitle ) );
appendMenuItem( pMenu, pDockMenu, aTitle, MI_TEMPLATE, aKeyEquiv );
aTitle = pShutdownIcon->GetResString( STR_QUICKSTART_FILEOPEN );
aKeyEquiv = getShortCut( aTitle );
appendMenuItem( pMenu, pDockMenu, aTitle, MI_OPEN, aKeyEquiv );
[pDefMenu setSubmenu: pMenu];
[NSApp performSelector:@selector(addFallbackMenuItem:) withObject: pDefMenu];
if( [NSApp respondsToSelector: @selector(addDockMenuItem:)] )
{
[pDockSubMenu setSubmenu: pDockMenu];
// insert a separator to the dock menu
[NSApp performSelector:@selector(addDockMenuItem:) withObject: [NSMenuItem separatorItem]];
// and now add the submenu
[NSApp performSelector:@selector(addDockMenuItem:) withObject: pDockSubMenu];
}
else
DBG_ERROR( "addDockMenuItem selector failed on NSApp\n" );
}
else
DBG_ERROR( "addFallbackMenuItem selector failed on NSApp\n" );
}
}
void SAL_DLLPUBLIC_EXPORT aqua_shutdown_systray()
{
}
}