Mac OS only: added the Fl_Mac_App_Menu::custom_application_menu_items() method

that allows customization of the application menu on the Mac platform.

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10932 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Manolo Gouy 2015-11-26 16:34:58 +00:00
parent 311bd8971e
commit ff65dbb5ae
4 changed files with 102 additions and 68 deletions

View File

@ -301,6 +301,7 @@ public:
static const char *show; static const char *show;
/** Localizable text for the "Quit xxx" application menu item */ /** Localizable text for the "Quit xxx" application menu item */
static const char *quit; static const char *quit;
static void custom_application_menu_items(const Fl_Menu_Item *m);
}; };
/** @} */ /** @} */

11
FL/x.H
View File

@ -21,17 +21,6 @@
// need to call Xlib directly. These symbols may not exist on non-X // need to call Xlib directly. These symbols may not exist on non-X
// systems. // systems.
/** \class Fl_Mac_App_Menu
Mac OS-specific class allowing to localize the application menu.
These character strings are used to build the application menu. They can be localized
at run time to any UTF-8 text by placing instructions such as this \e very
early in the program:
\verbatim
Fl_Mac_App_Menu::print = "Imprimer la fenêtre";
\endverbatim
*/
#if !defined(Fl_X_H) && !defined(FL_DOXYGEN) #if !defined(Fl_X_H) && !defined(FL_DOXYGEN)
# define Fl_X_H # define Fl_X_H

View File

@ -48,6 +48,7 @@
typedef const Fl_Menu_Item *pFl_Menu_Item; typedef const Fl_Menu_Item *pFl_Menu_Item;
Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0; Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0;
static Fl_Menu_Bar *custom_menu;
static char *remove_ampersand(const char *s); static char *remove_ampersand(const char *s);
extern void (*fl_lock_function)(); extern void (*fl_lock_function)();
@ -75,12 +76,14 @@ typedef struct {
@interface FLMenuItem : NSMenuItem { @interface FLMenuItem : NSMenuItem {
} }
- (void) doCallback:(id)unused;
- (void) directCallback:(id)unused;
- (const Fl_Menu_Item*) getFlItem; - (const Fl_Menu_Item*) getFlItem;
- (void) itemCallback:(Fl_Menu_*)menu;
- (void) doCallback;
- (void) customCallback;
- (void) directCallback;
- (void) setKeyEquivalentModifierMask:(int)value; - (void) setKeyEquivalentModifierMask:(int)value;
- (void) setFltkShortcut:(int)key; - (void) setFltkShortcut:(int)key;
+ (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu; + (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu action:(SEL)selector;
@end @end
@implementation FLMenuItem @implementation FLMenuItem
@ -91,11 +94,10 @@ typedef struct {
if (smi->use_rank) return fl_sys_menu_bar->menu() + smi->rank; if (smi->use_rank) return fl_sys_menu_bar->menu() + smi->rank;
return smi->item; return smi->item;
} }
- (void) doCallback:(id)unused - (void) itemCallback:(Fl_Menu_*)menu
{ {
fl_lock_function();
const Fl_Menu_Item *item = [self getFlItem]; const Fl_Menu_Item *item = [self getFlItem];
fl_sys_menu_bar->picked(item); menu->picked(item);
if ( item->flags & FL_MENU_TOGGLE ) { // update the menu toggle symbol if ( item->flags & FL_MENU_TOGGLE ) { // update the menu toggle symbol
[self setState:(item->value() ? NSOnState : NSOffState)]; [self setState:(item->value() ? NSOnState : NSOffState)];
} }
@ -122,9 +124,20 @@ typedef struct {
[nsitem setState:(nsitem != self ? NSOffState : NSOnState)]; [nsitem setState:(nsitem != self ? NSOffState : NSOnState)];
} }
} }
}
- (void) doCallback
{
fl_lock_function();
[self itemCallback:fl_sys_menu_bar];
fl_unlock_function(); fl_unlock_function();
} }
- (void) directCallback:(id)unused - (void) customCallback
{
fl_lock_function();
[self itemCallback:custom_menu];
fl_unlock_function();
}
- (void) directCallback
{ {
fl_lock_function(); fl_lock_function();
Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes]; Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes];
@ -154,22 +167,22 @@ typedef struct {
[self setKeyEquivalent:[NSString stringWithCharacters:&mac_key length:1]]; [self setKeyEquivalent:[NSString stringWithCharacters:&mac_key length:1]];
[self setKeyEquivalentModifierMask:mod]; [self setKeyEquivalentModifierMask:mod];
} }
+ (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu + (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu action:(SEL)selector
{ {
char *name = remove_ampersand(mitem->label()); char *name = remove_ampersand(mitem->label());
CFStringRef cfname = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); NSString *title = NSLocalizedString([NSString stringWithUTF8String:name], nil);
free(name); free(name);
FLMenuItem *item = [[FLMenuItem alloc] initWithTitle:(NSString*)cfname FLMenuItem *item = [[FLMenuItem alloc] initWithTitle:title
action:@selector(doCallback:) action:selector
keyEquivalent:@""]; keyEquivalent:@""];
sys_menu_item smi; sys_menu_item smi;
smi.rank = fl_sys_menu_bar->find_index(mitem); // ≥ 0 if mitem is in the menu items of fl_sys_menu_bar, -1 if not // ≥ 0 if mitem is in the menu items of fl_sys_menu_bar, -1 if not
smi.rank = (fl_sys_menu_bar ? fl_sys_menu_bar->find_index(mitem) : -1);
smi.use_rank = (smi.rank >= 0); smi.use_rank = (smi.rank >= 0);
if (!smi.use_rank) smi.item = mitem; if (!smi.use_rank) smi.item = mitem;
NSData *pointer = [NSData dataWithBytes:&smi length:sizeof(smi)]; NSData *pointer = [NSData dataWithBytes:&smi length:sizeof(smi)];
[item setRepresentedObject:pointer]; [item setRepresentedObject:pointer];
[menu addItem:item]; [menu addItem:item];
CFRelease(cfname);
[item setTarget:item]; [item setTarget:item];
int retval = [menu indexOfItem:item]; int retval = [menu indexOfItem:item];
[item release]; [item release];
@ -190,7 +203,7 @@ void fl_mac_set_about( Fl_Callback *cb, void *user_data, int shortcut)
CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]); CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]);
[appleMenu removeItemAtIndex:0]; [appleMenu removeItemAtIndex:0];
FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname
action:@selector(directCallback:) action:@selector(directCallback)
keyEquivalent:@""] autorelease]; keyEquivalent:@""] autorelease];
if (aboutItem.shortcut()) if (aboutItem.shortcut())
[item setFltkShortcut:aboutItem.shortcut()]; [item setFltkShortcut:aboutItem.shortcut()];
@ -257,36 +270,37 @@ static char *remove_ampersand(const char *s)
/* /*
* create a sub menu for a specific menu handle * create a sub menu for a specific menu handle
*/ */
static void createSubMenu( NSMenu *mh, pFl_Menu_Item &mm, const Fl_Menu_Item *mitem) static void createSubMenu( NSMenu *mh, pFl_Menu_Item &mm, const Fl_Menu_Item *mitem, SEL selector)
{ {
NSMenu *submenu; NSMenu *submenu;
int miCnt, flags; int miCnt, flags;
NSMenuItem *menuItem; if (mitem) {
char *ts = remove_ampersand(mitem->text); NSMenuItem *menuItem;
CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8); char *ts = remove_ampersand(mitem->text);
free(ts); CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8);
submenu = [[NSMenu alloc] initWithTitle:(NSString*)title]; free(ts);
CFRelease(title); submenu = [[NSMenu alloc] initWithTitle:(NSString*)title];
[submenu setAutoenablesItems:NO]; CFRelease(title);
[submenu setAutoenablesItems:NO];
int cnt;
cnt = [mh numberOfItems];
cnt--;
menuItem = [mh itemAtIndex:cnt];
[menuItem setSubmenu:submenu];
[submenu release];
} else submenu = mh;
int cnt; while ( mm->text ) {
cnt = [mh numberOfItems];
cnt--;
menuItem = [mh itemAtIndex:cnt];
[menuItem setSubmenu:submenu];
[submenu release];
while ( mm->text )
{
if (!mm->visible() ) { // skip invisible items and submenus if (!mm->visible() ) { // skip invisible items and submenus
mm = mm->next(0); mm = mm->next(0);
continue; continue;
} }
miCnt = [FLMenuItem addNewItem:mm menu:submenu]; miCnt = [FLMenuItem addNewItem:mm menu:submenu action:selector];
setMenuFlags( submenu, miCnt, mm ); setMenuFlags( submenu, miCnt, mm );
setMenuShortcut( submenu, miCnt, mm ); setMenuShortcut( submenu, miCnt, mm );
if ( mm->flags & FL_MENU_INACTIVE || mitem->flags & FL_MENU_INACTIVE) { if ( mm->flags & FL_MENU_INACTIVE || (mitem && (mitem->flags & FL_MENU_INACTIVE))) {
NSMenuItem *item = [submenu itemAtIndex:miCnt]; NSMenuItem *item = [submenu itemAtIndex:miCnt];
[item setEnabled:NO]; [item setEnabled:NO];
} }
@ -294,12 +308,12 @@ static void createSubMenu( NSMenu *mh, pFl_Menu_Item &mm, const Fl_Menu_Item *m
if ( mm->flags & FL_SUBMENU ) if ( mm->flags & FL_SUBMENU )
{ {
mm++; mm++;
createSubMenu( submenu, mm, mm - 1 ); createSubMenu( submenu, mm, mm - 1, selector);
} }
else if ( mm->flags & FL_SUBMENU_POINTER ) else if ( mm->flags & FL_SUBMENU_POINTER )
{ {
const Fl_Menu_Item *smm = (Fl_Menu_Item*)mm->user_data_; const Fl_Menu_Item *smm = (Fl_Menu_Item*)mm->user_data_;
createSubMenu( submenu, smm, mm); createSubMenu( submenu, smm, mm, selector);
} }
if ( flags & FL_MENU_DIVIDER ) { if ( flags & FL_MENU_DIVIDER ) {
[submenu addItem:[NSMenuItem separatorItem]]; [submenu addItem:[NSMenuItem separatorItem]];
@ -316,33 +330,12 @@ static void createSubMenu( NSMenu *mh, pFl_Menu_Item &mm, const Fl_Menu_Item *m
static void convertToMenuBar(const Fl_Menu_Item *mm) static void convertToMenuBar(const Fl_Menu_Item *mm)
{ {
NSMenu *fl_system_menu = [NSApp mainMenu]; NSMenu *fl_system_menu = [NSApp mainMenu];
int rank;
int count;//first, delete all existing system menus int count;//first, delete all existing system menus
count = [fl_system_menu numberOfItems]; count = [fl_system_menu numberOfItems];
for(int i = count - 1; i > 0; i--) { for(int i = count - 1; i > 0; i--) {
[fl_system_menu removeItem:[fl_system_menu itemAtIndex:i]]; [fl_system_menu removeItem:[fl_system_menu itemAtIndex:i]];
} }
//now convert FLTK stuff into MacOS menus if (mm) createSubMenu(fl_system_menu, mm, NULL, @selector(doCallback));
for (;;)
{
if ( !mm || !mm->text )
break;
if (!mm->visible() ) { // skip invisible menus
mm = mm->next(0);
continue;
}
rank = [FLMenuItem addNewItem:mm menu:fl_system_menu];
if ( mm->flags & FL_SUBMENU ) {
mm++;
createSubMenu(fl_system_menu, mm, mm - 1);
}
else if ( mm->flags & FL_SUBMENU_POINTER ) {
const Fl_Menu_Item *smm = (Fl_Menu_Item*)mm->user_data_;
createSubMenu(fl_system_menu, smm, mm);
}
mm++;
}
} }
@ -479,6 +472,7 @@ Fl_Sys_Menu_Bar::Fl_Sys_Menu_Bar(int x,int y,int w,int h,const char *l)
: Fl_Menu_Bar(x,y,w,h,l) : Fl_Menu_Bar(x,y,w,h,l)
{ {
deactivate(); // don't let the old area take events deactivate(); // don't let the old area take events
if (fl_sys_menu_bar) delete fl_sys_menu_bar;
fl_sys_menu_bar = this; fl_sys_menu_bar = this;
Fl::add_handler(process_sys_menu_shortcuts); Fl::add_handler(process_sys_menu_shortcuts);
} }
@ -491,6 +485,46 @@ Fl_Sys_Menu_Bar::~Fl_Sys_Menu_Bar()
Fl::remove_handler(process_sys_menu_shortcuts); Fl::remove_handler(process_sys_menu_shortcuts);
} }
/** \class Fl_Mac_App_Menu
Mac OS-specific class allowing to customize and localize the application menu.
The public class attributes are used to build the application menu. They can be localized
at run time to any UTF-8 text by placing instructions such as this before fl_open_display()
gets called:
\verbatim
Fl_Mac_App_Menu::print = "Imprimer la fenêtre";
\endverbatim
\see \ref osissues_macos for another way to localization.
*/
/** Adds custom menu item(s) to the application menu of the system menu bar.
They are positioned after the "Print Front Window" item, or at its place
if it was removed with <tt>Fl_Mac_App_Menu::print = ""</tt>.
\param m zero-ending array of Fl_Menu_Item 's.
*/
void Fl_Mac_App_Menu::custom_application_menu_items(const Fl_Menu_Item *m)
{
fl_open_display(); // create the system menu, if needed
custom_menu = new Fl_Menu_Bar(0,0,0,0);
custom_menu->menu(m);
NSMenu *menu = [[[NSApp mainMenu] itemAtIndex:0] submenu]; // the application menu
NSInteger to_rank;
if ([[menu itemAtIndex:2] action] != @selector(printPanel)) { // the 'Print' item was removed
[menu insertItem:[NSMenuItem separatorItem] atIndex:1];
to_rank = 2;
} else to_rank = 3; // after the "Print Front Window" item
NSInteger count = [menu numberOfItems];
createSubMenu(menu, m, NULL, @selector(customCallback)); // add new items at end of application menu
NSInteger count2 = [menu numberOfItems];
for (NSInteger i = count; i < count2; i++) { // move new items to their desired position in application menu
NSMenuItem *item = [menu itemAtIndex:i];
[item retain];
[menu removeItemAtIndex:i];
[menu insertItem:item atIndex:to_rank++];
[item release];
}
}
#endif /* __APPLE__ */ #endif /* __APPLE__ */
// //

View File

@ -237,6 +237,16 @@ int main(int argc, char **argv) {
ch2.callback(menu_location_cb, &menubar); ch2.callback(menu_location_cb, &menubar);
#endif #endif
window.end(); window.end();
#ifdef __APPLE__
Fl_Menu_Item custom[] = {
{"Preferences", 0, test_cb, NULL, FL_MENU_DIVIDER},
{"Radio1", 0, test_cb, NULL, FL_MENU_RADIO|FL_MENU_VALUE},
{"Radio2", 0, test_cb, NULL, FL_MENU_RADIO},
{0}
};
Fl_Mac_App_Menu::custom_application_menu_items(custom);
#endif
window.show(argc, argv); window.show(argc, argv);
return Fl::run(); return Fl::run();
} }