diff options
| author | Manolo Gouy <Manolo> | 2013-04-16 14:02:42 +0000 |
|---|---|---|
| committer | Manolo Gouy <Manolo> | 2013-04-16 14:02:42 +0000 |
| commit | 6b69461ea9d84c405ffb7f41e1d8b4b306088407 (patch) | |
| tree | 8f427a2f4e1d9823cd26f7b15d7fe239780ff35f /src/Fl_Sys_Menu_Bar.mm | |
| parent | b176f4d3f61f668f52bb773aca18f68d5ed2a1ce (diff) | |
Renamed src/Fl_Sys_Menu_Bar.cxx to src/Fl_Sys_Menu_Bar.mm because objective-c code is used.
Some code is transferred from Fl_cocoa.mm thus reducing somewhat this very large source file.
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@9885 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src/Fl_Sys_Menu_Bar.mm')
| -rw-r--r-- | src/Fl_Sys_Menu_Bar.mm | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/src/Fl_Sys_Menu_Bar.mm b/src/Fl_Sys_Menu_Bar.mm new file mode 100644 index 000000000..a14a53215 --- /dev/null +++ b/src/Fl_Sys_Menu_Bar.mm @@ -0,0 +1,437 @@ +// +// "$Id$" +// +// MacOS system menu bar widget for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2013 by Bill Spitzak and others. +// +// This library is free software. Distribution and use rights are outlined in +// the file "COPYING" which should have been included with this file. If this +// file is missing or damaged, see the license at: +// +// http://www.fltk.org/COPYING.php +// +// Please report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +/** + * This code is a quick hack! It was written as a proof of concept. + * It has been tested on the "menubar" sample program and provides + * basic functionality. + * + * To use the System Menu Bar, simply replace the main Fl_Menu_Bar + * in an application with Fl_Sys_Menu_Bar. + * + * FLTK features not supported by the Mac System menu + * + * - no invisible menu items + * - no symbolic labels + * - embossed labels will be underlined instead + * - no font sizes + * - Shortcut Characters should be English alphanumeric only, no modifiers yet + * - no disable main menus + * - changes to menubar in run-time don't update! + * (disable, etc. - toggle and radio button do!) + * + * No care was taken to clean up the menu bar after destruction! + * ::menu(bar) should only be called once! + * Many other calls of the parent class don't work. + * Changing the menu items has no effect on the menu bar. + * Starting with OS X 10.5, FLTK applications must be created as + * a bundle for the System Menu Bar (and maybe other features) to work! + */ + +#if defined(__APPLE__) || defined(FL_DOXYGEN) +#include <FL/Fl_Menu_Item.H> +#include <FL/Fl_Sys_Menu_Bar.H> +#include <FL/x.H> +#include <FL/Fl.H> + +#import <Cocoa/Cocoa.h> + +#include "flstring.h" +#include <stdio.h> +#include <ctype.h> +#include <stdarg.h> + +typedef const Fl_Menu_Item *pFl_Menu_Item; + +Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0; + +static char *remove_ampersand(const char *s); +extern void (*fl_lock_function)(); +extern void (*fl_unlock_function)(); + +@interface FLMenuItem : NSMenuItem { +} +- (void) doCallback:(id)unused; +- (void) directCallback:(id)unused; +- (const Fl_Menu_Item*) getFlItem; +- (void) setKeyEquivalent:(char)value; +- (void) setKeyEquivalentModifierMask:(int)value; ++ (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu; +@end + +@implementation FLMenuItem +- (const Fl_Menu_Item*) getFlItem +{ + return *(const Fl_Menu_Item **)[(NSData*)[self representedObject] bytes]; +} +- (void) doCallback:(id)unused +{ + fl_lock_function(); + const Fl_Menu_Item *item = [self getFlItem]; + fl_sys_menu_bar->picked(item); + if ( item->flags & FL_MENU_TOGGLE ) { // update the menu toggle symbol + [self setState:(item->value() ? NSOnState : NSOffState)]; + } + else if ( item->flags & FL_MENU_RADIO ) { // update the menu radio symbols + NSMenu* menu = [self menu]; + NSInteger flRank = [menu indexOfItem:self]; + NSInteger last = [menu numberOfItems] - 1; + int from = flRank; + while(from > 0) { + if ([[menu itemAtIndex:from-1] isSeparatorItem]) break; + item = [(FLMenuItem*)[menu itemAtIndex:from-1] getFlItem]; + if ( !(item->flags & FL_MENU_RADIO) ) break; + from--; + } + int to = flRank; + while (to < last) { + if ([[menu itemAtIndex:to+1] isSeparatorItem]) break; + item = [(FLMenuItem*)[menu itemAtIndex:to+1] getFlItem]; + if (!(item->flags & FL_MENU_RADIO)) break; + to++; + } + for(int i = from; i <= to; i++) { + NSMenuItem *nsitem = [menu itemAtIndex:i]; + [nsitem setState:(nsitem != self ? NSOffState : NSOnState)]; + } + } + fl_unlock_function(); +} +- (void) directCallback:(id)unused +{ + fl_lock_function(); + Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes]; + if ( item && item->callback() ) item->do_callback(NULL); + fl_unlock_function(); +} +- (void) setKeyEquivalent:(char)key +{ + NSString *equiv = [[NSString alloc] initWithBytes:&key length:1 encoding:NSASCIIStringEncoding]; + [super setKeyEquivalent:equiv]; + [equiv release]; +} +- (void) setKeyEquivalentModifierMask:(int)value +{ + NSUInteger macMod = 0; + if ( value & FL_META ) macMod = NSCommandKeyMask; + if ( value & FL_SHIFT || isupper(value) ) macMod |= NSShiftKeyMask; + if ( value & FL_ALT ) macMod |= NSAlternateKeyMask; + if ( value & FL_CTRL ) macMod |= NSControlKeyMask; + [super setKeyEquivalentModifierMask:macMod]; +} ++ (int) addNewItem:(const Fl_Menu_Item*)mitem menu:(NSMenu*)menu +{ + char *name = remove_ampersand(mitem->label()); + CFStringRef cfname = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); + free(name); + FLMenuItem *item = [[FLMenuItem alloc] initWithTitle:(NSString*)cfname + action:@selector(doCallback:) + keyEquivalent:@""]; + NSData *pointer = [NSData dataWithBytes:&mitem length:sizeof(Fl_Menu_Item*)]; + [item setRepresentedObject:pointer]; + [menu addItem:item]; + CFRelease(cfname); + [item setTarget:item]; + int retval = [menu indexOfItem:item]; + [item release]; + return retval; +} +@end + + +void fl_mac_set_about( Fl_Callback *cb, void *user_data, int shortcut) +{ + fl_open_display(); + Fl_Menu_Item aboutItem; + memset(&aboutItem, 0, sizeof(Fl_Menu_Item)); + aboutItem.callback(cb); + aboutItem.user_data(user_data); + aboutItem.shortcut(shortcut); + NSMenu *appleMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu]; + CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]); + [appleMenu removeItemAtIndex:0]; + FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname + action:@selector(directCallback:) + keyEquivalent:@""] autorelease]; + if (aboutItem.shortcut()) { + [item setKeyEquivalent:(aboutItem.shortcut() & 0xff)]; + [item setKeyEquivalentModifierMask:aboutItem.shortcut()]; + } + NSData *pointer = [NSData dataWithBytes:&aboutItem length:sizeof(Fl_Menu_Item)]; + [item setRepresentedObject:pointer]; + [appleMenu insertItem:item atIndex:0]; + CFRelease(cfname); + [item setTarget:item]; +} + +/* + * Set a shortcut for an Apple menu item using the FLTK shortcut descriptor. + */ +static void setMenuShortcut( NSMenu* mh, int miCnt, const Fl_Menu_Item *m ) +{ + if ( !m->shortcut_ ) + return; + if ( m->flags & FL_SUBMENU ) + return; + if ( m->flags & FL_SUBMENU_POINTER ) + return; + char key = m->shortcut_ & 0xff; + if ( !isalnum( key ) ) + return; + + FLMenuItem* menuItem = (FLMenuItem*)[mh itemAtIndex:miCnt]; + [menuItem setKeyEquivalent:(m->shortcut_ & 0xff)]; + [menuItem setKeyEquivalentModifierMask:m->shortcut_]; +} + + +/* + * Set the Toggle and Radio flag based on FLTK flags + */ +static void setMenuFlags( NSMenu* mh, int miCnt, const Fl_Menu_Item *m ) +{ + if ( m->flags & FL_MENU_TOGGLE ) + { + NSMenuItem *menuItem = [mh itemAtIndex:miCnt]; + [menuItem setState:(m->flags & FL_MENU_VALUE ? NSOnState : NSOffState)]; + } + else if ( m->flags & FL_MENU_RADIO ) { + NSMenuItem *menuItem = [mh itemAtIndex:miCnt]; + [menuItem setState:(m->flags & FL_MENU_VALUE ? NSOnState : NSOffState)]; + } +} + +static char *remove_ampersand(const char *s) +{ + char *ret = strdup(s); + const char *p = s; + char *q = ret; + while(*p != 0) { + if (p[0]=='&') { + if (p[1]=='&') { + *q++ = '&'; p+=2; + } else { + p++; + } + } else { + *q++ = *p++; + } + } + *q = 0; + return ret; +} + + +/* + * create a sub menu for a specific menu handle + */ +static void createSubMenu( NSMenu *mh, pFl_Menu_Item &mm, const Fl_Menu_Item *mitem) +{ + NSMenu *submenu; + int miCnt, flags; + + NSMenuItem *menuItem; + char *ts = remove_ampersand(mitem->text); + CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8); + free(ts); + submenu = [[NSMenu alloc] initWithTitle:(NSString*)title]; + CFRelease(title); + [submenu setAutoenablesItems:NO]; + + int cnt; + cnt = [mh numberOfItems]; + cnt--; + menuItem = [mh itemAtIndex:cnt]; + [menuItem setSubmenu:submenu]; + [submenu release]; + + while ( mm->text ) + { + char visible = mm->visible() ? 1 : 0; + miCnt = [FLMenuItem addNewItem:mm menu:submenu]; + setMenuFlags( submenu, miCnt, mm ); + setMenuShortcut( submenu, miCnt, mm ); + if ( mm->flags & FL_MENU_INACTIVE || mitem->flags & FL_MENU_INACTIVE) { + NSMenuItem *item = [submenu itemAtIndex:miCnt]; + [item setEnabled:NO]; + } + flags = mm->flags; + if ( mm->flags & FL_SUBMENU ) + { + mm++; + createSubMenu( submenu, mm, mm - 1 ); + } + else if ( mm->flags & FL_SUBMENU_POINTER ) + { + const Fl_Menu_Item *smm = (Fl_Menu_Item*)mm->user_data_; + createSubMenu( submenu, smm, mm ); + } + if ( flags & FL_MENU_DIVIDER ) { + [submenu addItem:[NSMenuItem separatorItem]]; + } + if ( !visible ) { + [submenu removeItem:[submenu itemAtIndex:miCnt]]; + } + mm++; + } +} + + +/* + * convert a complete Fl_Menu_Item array into a series of menus in the top menu bar + * ALL PREVIOUS SYSTEM MENUS, EXCEPT THE APPLICATION MENU, ARE REPLACED BY THE NEW DATA + */ +static void convertToMenuBar(const Fl_Menu_Item *mm) +{ + NSMenu *fl_system_menu = [NSApp mainMenu]; + int rank; + int count;//first, delete all existing system menus + count = [fl_system_menu numberOfItems]; + for(int i = count - 1; i > 0; i--) { + [fl_system_menu removeItem:[fl_system_menu itemAtIndex:i]]; + } + //now convert FLTK stuff into MacOS menus + for (;;) + { + if ( !mm || !mm->text ) + break; + char visible = mm->visible() ? 1 : 0; + 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); + } + if ( !visible ) { + [fl_system_menu removeItem:[fl_system_menu itemAtIndex:rank]]; + } + mm++; + } +} + + +/** + * @brief create a system menu bar using the given list of menu structs + * + * \author Matthias Melcher + * + * @param m list of Fl_Menu_Item + */ +void Fl_Sys_Menu_Bar::menu(const Fl_Menu_Item *m) +{ + fl_open_display(); + Fl_Menu_Bar::menu( m ); + convertToMenuBar(m); +} + + +/** + * @brief add to the system menu bar a new menu item + * + * add to the system menu bar a new menu item, with a title string, shortcut int, + * callback, argument to the callback, and flags. + * + * @see Fl_Menu_::add(const char* label, int shortcut, Fl_Callback *cb, void *user_data, int flags) + */ +int Fl_Sys_Menu_Bar::add(const char* label, int shortcut, Fl_Callback *cb, void *user_data, int flags) +{ + fl_open_display(); + int rank = Fl_Menu_::add(label, shortcut, cb, user_data, flags); + convertToMenuBar(Fl_Menu_::menu()); + return rank; +} + +/** + * @brief insert in the system menu bar a new menu item + * + * insert in the system menu bar a new menu item, with a title string, shortcut int, + * callback, argument to the callback, and flags. + * + * @see Fl_Menu_::insert(int index, const char* label, int shortcut, Fl_Callback *cb, void *user_data, int flags) + */ +int Fl_Sys_Menu_Bar::insert(int index, const char* label, int shortcut, Fl_Callback *cb, void *user_data, int flags) +{ + fl_open_display(); + int rank = Fl_Menu_::insert(index, label, shortcut, cb, user_data, flags); + convertToMenuBar(Fl_Menu_::menu()); + return rank; +} + +void Fl_Sys_Menu_Bar::clear() +{ + Fl_Menu_::clear(); + convertToMenuBar(NULL); +} + +int Fl_Sys_Menu_Bar::clear_submenu(int index) +{ + int retval = Fl_Menu_::clear_submenu(index); + if (retval != -1) convertToMenuBar(Fl_Menu_::menu()); + return retval; +} + +/** + * @brief remove an item from the system menu bar + * + * @param rank the rank of the item to remove + */ +void Fl_Sys_Menu_Bar::remove(int rank) +{ + Fl_Menu_::remove(rank); + convertToMenuBar(Fl_Menu_::menu()); +} + + +/** + * @brief rename an item from the system menu bar + * + * @param rank the rank of the item to rename + * @param name the new item name as a UTF8 string + */ +void Fl_Sys_Menu_Bar::replace(int rank, const char *name) +{ + Fl_Menu_::replace(rank, name); + convertToMenuBar(Fl_Menu_::menu()); +} + + +/* + * Draw the menu bar. + * Nothing here because the OS does this for us. + */ +void Fl_Sys_Menu_Bar::draw() { +} + + +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) +{ + deactivate(); // don't let the old area take events + fl_sys_menu_bar = this; +} + + +#endif /* __APPLE__ */ + +// +// End of "$Id$". +// |
