summaryrefslogtreecommitdiff
path: root/src/Fl_Sys_Menu_Bar.mm
diff options
context:
space:
mode:
authorManolo Gouy <Manolo>2013-04-16 14:02:42 +0000
committerManolo Gouy <Manolo>2013-04-16 14:02:42 +0000
commit6b69461ea9d84c405ffb7f41e1d8b4b306088407 (patch)
tree8f427a2f4e1d9823cd26f7b15d7fe239780ff35f /src/Fl_Sys_Menu_Bar.mm
parentb176f4d3f61f668f52bb773aca18f68d5ed2a1ce (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.mm437
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$".
+//