tdf#94251: enable Emoji & Symbols, Dictation, and AutoFill menu items
These are added automatically by macOS whenever the main menu is set with [NSApp setMainMenu:] and an Edit menu exists. Previously LO would set the main menu before adding all the actual menus, which prevented macOS from doing the automatic additions. Instead, re-set the main menu whenever LO changes the menu layout. However, some Edit menu translations in LO do not match macOS standard Edit menu names (eg Español, where LO uses Editar but macOS uses Edición) and in these cases macOS won't add the standard items. To handle this case cache the macOS standard menu name inside the AquaSalMenuItem, use the macOS standard name when calling [NSApp setMainMenu:] and flip back to the LO name immediately after. Change-Id: I38d33299f05f99bb084175f6004a4f50068acc74 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/201933 Reviewed-by: Dan Williams <dcbw@libreoffice.org> Reviewed-by: Patrick Luby <guibomacdev@gmail.com> Tested-by: Jenkins Reviewed-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
This commit is contained in:
@@ -6,20 +6,24 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#
|
||||
nib_WRKDIR := $(gb_CustomTarget_workdir)/vcl/osx/res
|
||||
res_WRKDIR := $(gb_CustomTarget_workdir)/vcl/osx/res
|
||||
|
||||
$(nib_WRKDIR)/.dir:
|
||||
$(res_WRKDIR)/.dir:
|
||||
mkdir -p $(@D) && touch $@
|
||||
|
||||
$(nib_WRKDIR)/MainMenu.nib: $(SRCDIR)/vcl/osx/res/MainMenu.xib | $(nib_WRKDIR)/.dir
|
||||
$(res_WRKDIR)/MainMenu.nib: $(SRCDIR)/vcl/osx/res/MainMenu.xib | $(res_WRKDIR)/.dir
|
||||
$(call gb_Output_announce,$(@F),$(true),NIB,2)
|
||||
ibtool --compile "$@" "$<"
|
||||
|
||||
$(eval $(call gb_Package_Package,vcl_osxres,$(nib_WRKDIR)))
|
||||
$(res_WRKDIR)/MenuTranslations.plist: $(SRCDIR)/vcl/osx/res/MenuTranslations.plist | $(res_WRKDIR)/.dir
|
||||
cp "$<" "$@"
|
||||
|
||||
$(eval $(call gb_Package_Package,vcl_osxres,$(res_WRKDIR)))
|
||||
$(call gb_Package_get_clean_target,vcl_osxres): $(call gb_CustomTarget_get_clean_target,vcl/osx/res)
|
||||
|
||||
$(eval $(call gb_Package_add_files_with_dir,vcl_osxres,Resources,\
|
||||
MainMenu.nib \
|
||||
MenuTranslations.plist \
|
||||
))
|
||||
|
||||
# vim:set noet sw=4 ts=4:
|
||||
|
||||
@@ -165,6 +165,9 @@ public:
|
||||
static const short DispatchTimerEvent = 30;
|
||||
|
||||
static NSMenu* GetDynamicDockMenu();
|
||||
|
||||
private:
|
||||
NSDictionary *mpMenuTranslations;
|
||||
};
|
||||
|
||||
CGImageRef CreateCGImage( const Image& );
|
||||
|
||||
+14
-2
@@ -47,11 +47,15 @@ public: // for OOStatusView
|
||||
};
|
||||
private:
|
||||
std::vector< MenuBarButtonEntry > maButtons;
|
||||
NSString * mpAltTitle;
|
||||
NSDictionary * mpMenuTranslations;
|
||||
|
||||
MenuBarButtonEntry* findButtonItem( sal_uInt16 i_nItemId );
|
||||
static void statusLayout();
|
||||
void useAltTitle(bool bAltTitle);
|
||||
|
||||
public:
|
||||
AquaSalMenu( bool bMenuBar );
|
||||
AquaSalMenu( bool bMenuBar, NSDictionary *pTranslations );
|
||||
virtual ~AquaSalMenu() override;
|
||||
|
||||
virtual bool VisibleMenuBar() override;
|
||||
@@ -95,14 +99,22 @@ public:
|
||||
class AquaSalMenuItem : public SalMenuItem
|
||||
{
|
||||
public:
|
||||
AquaSalMenuItem( const SalItemParams* );
|
||||
AquaSalMenuItem( const SalItemParams*, NSDictionary* );
|
||||
virtual ~AquaSalMenuItem() override;
|
||||
OUString getItemCommand();
|
||||
void setTitle(const OUString& i_rText);
|
||||
void useAltTitle(bool bUseAlt);
|
||||
|
||||
sal_uInt16 mnId; // Item ID
|
||||
VclPtr<Menu> mpVCLMenu; // VCL Menu into which this MenuItem is inserted
|
||||
AquaSalMenu* mpParentMenu; // The menu in which this menu item is inserted
|
||||
AquaSalMenu* mpSubMenu; // Sub menu of this item (if defined)
|
||||
NSMenuItem* mpMenuItem; // The NSMenuItem
|
||||
|
||||
private:
|
||||
NSString * mpOrigTitle;
|
||||
NSString * mpAltTitle;
|
||||
NSDictionary * mpMenuTranslations;
|
||||
};
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>.uno:EditMenu</key>
|
||||
<dict>
|
||||
<key>ar</key>
|
||||
<string>تحرير</string>
|
||||
<key>ca</key>
|
||||
<string>Editar</string>
|
||||
<key>cs</key>
|
||||
<string>Úpravy</string>
|
||||
<key>da</key>
|
||||
<string>Rediger</string>
|
||||
<key>de</key>
|
||||
<string>Bearbeiten</string>
|
||||
<key>el</key>
|
||||
<string>Επεξεργασία</string>
|
||||
<key>es-419</key>
|
||||
<string>Edición</string>
|
||||
<key>es</key>
|
||||
<string>Edición</string>
|
||||
<key>fi</key>
|
||||
<string>Muokkaa</string>
|
||||
<key>fr</key>
|
||||
<string>Édition</string>
|
||||
<key>he</key>
|
||||
<string>עריכה</string>
|
||||
<key>hr</key>
|
||||
<string>Uredi</string>
|
||||
<key>hu</key>
|
||||
<string>Szerkesztés</string>
|
||||
<key>id</key>
|
||||
<string>Edit</string>
|
||||
<key>it</key>
|
||||
<string>Modifica</string>
|
||||
<key>ja</key>
|
||||
<string>編集</string>
|
||||
<key>ko</key>
|
||||
<string>편집</string>
|
||||
<key>ms</key>
|
||||
<string>Edit</string>
|
||||
<key>nb</key>
|
||||
<string>Rediger</string>
|
||||
<key>nl</key>
|
||||
<string>Wijzig</string>
|
||||
<key>pl</key>
|
||||
<string>Edycja</string>
|
||||
<key>pt-BR</key>
|
||||
<string>Editar</string>
|
||||
<key>pt-PT</key>
|
||||
<string>Edição</string>
|
||||
<key>ro</key>
|
||||
<string>Editare</string>
|
||||
<key>ru</key>
|
||||
<string>Правка</string>
|
||||
<key>sk</key>
|
||||
<string>Upraviť</string>
|
||||
<key>sv</key>
|
||||
<string>Redigera</string>
|
||||
<key>th</key>
|
||||
<string>แก้ไข</string>
|
||||
<key>tr</key>
|
||||
<string>Düzen</string>
|
||||
<key>uk</key>
|
||||
<string>Редагування</string>
|
||||
<key>vi</key>
|
||||
<string>Sửa</string>
|
||||
<key>zh-Hans</key>
|
||||
<string>编辑</string>
|
||||
<key>zh-Hant</key>
|
||||
<string>編輯</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -352,6 +352,9 @@ AquaSalInstance::AquaSalInstance()
|
||||
mpStepperCell = [[NSStepperCell alloc] init];
|
||||
mpListNodeCell = [[NSButtonCell alloc] init];
|
||||
|
||||
NSString *path = [[[NSBundle mainBundle] pathForResource:@"MenuTranslations" ofType:@"plist"] autorelease];
|
||||
mpMenuTranslations = [NSDictionary dictionaryWithContentsOfFile:path];
|
||||
|
||||
#if HAVE_FEATURE_SKIA
|
||||
AquaSkiaSalGraphicsImpl::prepareSkia();
|
||||
#endif
|
||||
@@ -375,6 +378,8 @@ AquaSalInstance::~AquaSalInstance()
|
||||
[mpCheckCell release];
|
||||
[mpButtonCell release];
|
||||
|
||||
[mpMenuTranslations release];
|
||||
|
||||
#if HAVE_FEATURE_SKIA
|
||||
SkiaHelper::cleanup();
|
||||
#endif
|
||||
|
||||
+109
-34
@@ -23,7 +23,9 @@
|
||||
|
||||
#include <objc/objc-runtime.h>
|
||||
|
||||
#include <osl/process.h>
|
||||
#include <rtl/ustrbuf.hxx>
|
||||
#include <rtl/ustring.hxx>
|
||||
#include <tools/debug.hxx>
|
||||
#include <tools/long.hxx>
|
||||
#include <vcl/commandevent.hxx>
|
||||
@@ -207,7 +209,7 @@ std::unique_ptr<SalMenu> AquaSalInstance::CreateMenu( bool bMenuBar, Menu* pVCLM
|
||||
{
|
||||
initAppMenu();
|
||||
|
||||
AquaSalMenu *pAquaSalMenu = new AquaSalMenu( bMenuBar );
|
||||
AquaSalMenu *pAquaSalMenu = new AquaSalMenu( bMenuBar, mpMenuTranslations );
|
||||
pAquaSalMenu->mpVCLMenu = pVCLMenu;
|
||||
|
||||
return std::unique_ptr<SalMenu>(pAquaSalMenu);
|
||||
@@ -215,7 +217,7 @@ std::unique_ptr<SalMenu> AquaSalInstance::CreateMenu( bool bMenuBar, Menu* pVCLM
|
||||
|
||||
std::unique_ptr<SalMenuItem> AquaSalInstance::CreateMenuItem( const SalItemParams & rItemData )
|
||||
{
|
||||
AquaSalMenuItem *pSalMenuItem = new AquaSalMenuItem( &rItemData );
|
||||
AquaSalMenuItem *pSalMenuItem = new AquaSalMenuItem( &rItemData, mpMenuTranslations );
|
||||
|
||||
return std::unique_ptr<SalMenuItem>(pSalMenuItem);
|
||||
}
|
||||
@@ -224,7 +226,8 @@ std::unique_ptr<SalMenuItem> AquaSalInstance::CreateMenuItem( const SalItemParam
|
||||
* AquaSalMenu
|
||||
*/
|
||||
|
||||
AquaSalMenu::AquaSalMenu( bool bMenuBar ) :
|
||||
AquaSalMenu::AquaSalMenu( bool bMenuBar, NSDictionary *pMenuTranslations ) :
|
||||
mpMenuTranslations( [pMenuTranslations retain] ),
|
||||
mbMenuBar( bMenuBar ),
|
||||
mpMenu( nil ),
|
||||
mpFrame( nullptr ),
|
||||
@@ -291,6 +294,10 @@ AquaSalMenu::~AquaSalMenu()
|
||||
[mpMenu autorelease];
|
||||
}
|
||||
}
|
||||
|
||||
[mpMenuTranslations autorelease];
|
||||
if( mpAltTitle )
|
||||
[mpAltTitle autorelease];
|
||||
}
|
||||
|
||||
bool AquaSalMenu::ShowNativePopupMenu(FloatingWindow * pWin, const tools::Rectangle& rRect, FloatWinPopupFlags nFlags)
|
||||
@@ -395,12 +402,19 @@ void AquaSalMenu::unsetMainMenu()
|
||||
{
|
||||
pCurrentMenuBar = nullptr;
|
||||
|
||||
// remove items from main menu
|
||||
// remove all menus except the app menu
|
||||
NSMenu* pMenu = [NSApp mainMenu];
|
||||
[NSApp setMainMenu:nil];
|
||||
for( int nItems = [pMenu numberOfItems]; nItems > 1; nItems-- )
|
||||
[pMenu removeItemAtIndex: 1];
|
||||
}
|
||||
|
||||
void AquaSalMenu::useAltTitle(bool bAltTitle)
|
||||
{
|
||||
for( std::vector<AquaSalMenuItem *>::size_type i = 0; i < maItems.size(); i++ )
|
||||
maItems[i]->useAltTitle(bAltTitle);
|
||||
}
|
||||
|
||||
void AquaSalMenu::setMainMenu()
|
||||
{
|
||||
SAL_WARN_IF( !mbMenuBar, "vcl", "setMainMenu on non menubar" );
|
||||
@@ -418,7 +432,7 @@ void AquaSalMenu::setMainMenu()
|
||||
// tdf#165448 Allow macOS to add menu items in LibreOffice windows menu
|
||||
// macOS will automatically insert menu items in NSApp's
|
||||
// windows menu so set that menu to LibreOffice's windows menu.
|
||||
if( maItems[i]->mpVCLMenu && maItems[i]->mpVCLMenu->GetItemCommand( maItems[i]->mnId ) == u".uno:WindowList"_ustr )
|
||||
if ( maItems[i]->getItemCommand() == u".uno:WindowList"_ustr )
|
||||
{
|
||||
// Avoid macOS inserting duplicate menu items in the
|
||||
// windows menu
|
||||
@@ -429,6 +443,15 @@ void AquaSalMenu::setMainMenu()
|
||||
}
|
||||
pCurrentMenuBar = this;
|
||||
|
||||
// Flip menu/item names to alt ones so macOS will automatically add standard
|
||||
// menu items like Dictation and Emojis & Symbols
|
||||
useAltTitle(true);
|
||||
|
||||
[NSApp setMainMenu:mpMenu];
|
||||
|
||||
// Flip alt menu/item names back to LO ones
|
||||
useAltTitle(false);
|
||||
|
||||
// change status item
|
||||
statusLayout();
|
||||
}
|
||||
@@ -453,6 +476,7 @@ void AquaSalMenu::setDefaultMenu()
|
||||
if( [pItem menu] == nil )
|
||||
[pMenu insertItem: pItem atIndex: i+1];
|
||||
}
|
||||
[NSApp setMainMenu:pMenu];
|
||||
|
||||
// Related: tdf#128186 force key window to a native full screen window
|
||||
// AquaSalMenu::setDefaultMenu() is generally called when the key
|
||||
@@ -672,25 +696,9 @@ void AquaSalMenu::SetItemImage( unsigned /*nPos*/, SalMenuItem* pSMI, const Imag
|
||||
|
||||
void AquaSalMenu::SetItemText( unsigned /*i_nPos*/, SalMenuItem* i_pSalMenuItem, const OUString& i_rText )
|
||||
{
|
||||
if (!i_pSalMenuItem)
|
||||
return;
|
||||
|
||||
AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem *>(i_pSalMenuItem);
|
||||
|
||||
// Delete all mnemonics of mbMenuBar and CJK-style mnemonic
|
||||
OUString aText = MnemonicGenerator::EraseAllMnemonicChars(i_rText);
|
||||
|
||||
if (aText.endsWith("...", &aText))
|
||||
aText += u"\u2026";
|
||||
|
||||
NSString* pString = CreateNSString( aText );
|
||||
if (pString)
|
||||
{
|
||||
[pAquaSalMenuItem->mpMenuItem setTitle: pString];
|
||||
// if the menu item has a submenu, change its title as well
|
||||
if (pAquaSalMenuItem->mpSubMenu)
|
||||
[pAquaSalMenuItem->mpSubMenu->mpMenu setTitle: pString];
|
||||
[pString release];
|
||||
if (i_pSalMenuItem) {
|
||||
AquaSalMenuItem *pAquaSalMenuItem = static_cast<AquaSalMenuItem *>(i_pSalMenuItem);
|
||||
pAquaSalMenuItem->setTitle( i_rText );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -927,16 +935,46 @@ SAL_WNODEPRECATED_DECLARATIONS_POP
|
||||
);
|
||||
}
|
||||
|
||||
static NSString *getMenuTranslation( NSDictionary *pDict, OUString rUnoCommand )
|
||||
{
|
||||
NSDictionary *pLangs = pDict[ [CreateNSString(rUnoCommand) autorelease] ];
|
||||
|
||||
if ( !pLangs )
|
||||
return NULL;
|
||||
|
||||
rtl_Locale *pLocale = NULL;
|
||||
osl_getProcessLocale(&pLocale);
|
||||
|
||||
NSString *pLang = [CreateNSString(pLocale->Language) autorelease];
|
||||
NSString *pRegion = [CreateNSString(pLocale->Country) autorelease];
|
||||
NSString *pLangAndRegion = NULL;
|
||||
|
||||
// HACK: Español (Latinoamérica) is reported as es-419 but osl_getProcessLocale()
|
||||
// can't deal with 3-character regions; convert back for translation lookup
|
||||
if ([pLang isEqualToString:@"es"] && [pRegion isEqualToString:@"41"])
|
||||
pLangAndRegion = @"es_419";
|
||||
|
||||
if (!pLangAndRegion)
|
||||
pLangAndRegion = [[[NSString alloc] initWithFormat:@"%@_%@", pLang, pRegion] autorelease];
|
||||
|
||||
NSString *pAltTitle = pLangs[pLangAndRegion];
|
||||
if ( !pAltTitle )
|
||||
pAltTitle = pLangs[pLang];
|
||||
|
||||
return pAltTitle;
|
||||
}
|
||||
|
||||
/*
|
||||
* SalMenuItem
|
||||
*/
|
||||
|
||||
AquaSalMenuItem::AquaSalMenuItem( const SalItemParams* pItemData ) :
|
||||
AquaSalMenuItem::AquaSalMenuItem( const SalItemParams* pItemData, NSDictionary *pMenuTranslations ) :
|
||||
mnId( pItemData->nId ),
|
||||
mpVCLMenu( pItemData->pMenu ),
|
||||
mpParentMenu( nullptr ),
|
||||
mpSubMenu( nullptr ),
|
||||
mpMenuItem( nil )
|
||||
mpMenuItem( nil ),
|
||||
mpMenuTranslations( [pMenuTranslations retain] )
|
||||
{
|
||||
if (pItemData->eType == MenuItemType::SEPARATOR)
|
||||
{
|
||||
@@ -950,14 +988,8 @@ AquaSalMenuItem::AquaSalMenuItem( const SalItemParams* pItemData ) :
|
||||
mpMenuItem = [[SalNSMenuItem alloc] initWithMenuItem: this];
|
||||
[static_cast<SalNSMenuItem*>(mpMenuItem) setReallyEnabled: YES];
|
||||
|
||||
// peel mnemonics because on mac there are no such things for menu items
|
||||
// Delete CJK-style mnemonics for the dropdown menu of the 'New button' and lower menu of 'File > New'
|
||||
NSString* pString = CreateNSString(MnemonicGenerator::EraseAllMnemonicChars(pItemData->aText));
|
||||
if (pString)
|
||||
{
|
||||
[mpMenuItem setTitle: pString];
|
||||
[pString release];
|
||||
}
|
||||
setTitle(pItemData->aText);
|
||||
|
||||
// anything but a separator should set a menu to dispatch to
|
||||
SAL_WARN_IF( !mpVCLMenu, "vcl", "no menu" );
|
||||
}
|
||||
@@ -973,6 +1005,49 @@ AquaSalMenuItem::~AquaSalMenuItem()
|
||||
*/
|
||||
if( mpMenuItem )
|
||||
[mpMenuItem autorelease];
|
||||
if( mpAltTitle )
|
||||
[mpAltTitle autorelease];
|
||||
[mpOrigTitle autorelease];
|
||||
[mpMenuTranslations autorelease];
|
||||
}
|
||||
|
||||
// getItemCommand() returns the UNO command ID of the menu item,
|
||||
// eg like ".uno:WindowList" or ".uno:EditMenu"
|
||||
OUString AquaSalMenuItem::getItemCommand()
|
||||
{
|
||||
return mpVCLMenu ? mpVCLMenu->GetItemCommand( mnId ) : OUString();
|
||||
}
|
||||
|
||||
void AquaSalMenuItem::useAltTitle(bool bUseAlt)
|
||||
{
|
||||
NSString *pTitle = mpOrigTitle;
|
||||
|
||||
if (bUseAlt && mpAltTitle)
|
||||
pTitle = mpAltTitle;
|
||||
|
||||
[mpMenuItem setTitle: pTitle];
|
||||
|
||||
// if the menu item has a submenu, change its title too
|
||||
if (mpSubMenu)
|
||||
[mpSubMenu->mpMenu setTitle: pTitle];
|
||||
}
|
||||
|
||||
void AquaSalMenuItem::setTitle(const OUString& i_rText)
|
||||
{
|
||||
// Delete all normal and CJK-style mnemonic
|
||||
OUString aText = MnemonicGenerator::EraseAllMnemonicChars(i_rText);
|
||||
|
||||
if (aText.endsWith("...", &aText))
|
||||
aText += u"\u2026";
|
||||
|
||||
mpOrigTitle = CreateNSString( aText );
|
||||
if ( !mpOrigTitle )
|
||||
return;
|
||||
|
||||
[mpAltTitle autorelease];
|
||||
mpAltTitle = getMenuTranslation( mpMenuTranslations, getItemCommand() );
|
||||
|
||||
useAltTitle(false);
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
||||
Reference in New Issue
Block a user