]> git.mxchange.org Git - flightgear.git/blob - src/GUI/FGCocoaMenuBar.mm
Reset work, fix time-slew on OSG event handling.
[flightgear.git] / src / GUI / FGCocoaMenuBar.mm
1 #include "FGCocoaMenuBar.hxx"
2
3 #include <Cocoa/Cocoa.h>
4
5 #include <boost/foreach.hpp>
6
7 #include <simgear/props/props.hxx>
8 #include <simgear/props/props_io.hxx>
9 #include <simgear/debug/logstream.hxx>
10 #include <simgear/structure/SGBinding.hxx>
11 #include <simgear/misc/strutils.hxx>
12
13 #include <Main/fg_props.hxx>
14 #include <GUI/CocoaHelpers_private.h>
15
16 #include <iostream>
17
18 using std::string;
19 using std::map;
20 using std::cout;
21 using namespace simgear;
22
23 typedef std::map<NSMenuItem*, SGBindingList> MenuItemBindings;
24
25 @class CocoaMenuDelegate;
26
27 class FGCocoaMenuBar::CocoaMenuBarPrivate
28 {
29 public:
30   CocoaMenuBarPrivate();
31   ~CocoaMenuBarPrivate();
32   
33   void menuFromProps(NSMenu* menu, SGPropertyNode* menuNode);
34   
35   void fireBindingsForItem(NSMenuItem* item);
36   
37 public:
38   CocoaMenuDelegate* delegate;
39   
40   MenuItemBindings itemBindings;
41     std::vector<SGPropertyChangeListener*> listeners;
42 };
43
44 // prior to the 10.6 SDK, NSMenuDelegate was an informal protocol
45 #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1060
46 @protocol NSMenuDelegate <NSObject>
47 @end
48 #endif
49
50 @interface CocoaMenuDelegate : NSObject <NSMenuDelegate> {
51 @private
52   FGCocoaMenuBar::CocoaMenuBarPrivate* peer;
53 }
54
55 @property (nonatomic, assign) FGCocoaMenuBar::CocoaMenuBarPrivate* peer;
56 @end
57
58 @implementation CocoaMenuDelegate
59
60 @synthesize peer;
61
62 - (void) itemAction:(id) sender
63 {
64   peer->fireBindingsForItem((NSMenuItem*) sender);
65 }
66
67 @end
68
69 static void setFunctionKeyShortcut(const std::string& shortcut, NSMenuItem* item)
70 {
71     unichar shortcutChar = NSF1FunctionKey;
72     if (shortcut == "F1") {
73         shortcutChar = NSF1FunctionKey;
74     } else if (shortcut == "F2") {
75         shortcutChar = NSF2FunctionKey;
76     } else if (shortcut == "F3") {
77         shortcutChar = NSF3FunctionKey;
78     } else if (shortcut == "F10") {
79         shortcutChar = NSF10FunctionKey;
80     } else if (shortcut == "F11") {
81         shortcutChar = NSF11FunctionKey;
82     } else if (shortcut == "F12") {
83         shortcutChar = NSF12FunctionKey;
84     } else {
85         SG_LOG(SG_GENERAL, SG_WARN, "CocoaMenu:setFunctionKeyShortcut: unsupported:" << shortcut);
86     }
87     
88   unichar ch[1];
89   ch[0] = shortcutChar;
90   [item setKeyEquivalentModifierMask:NSFunctionKeyMask];
91   [item setKeyEquivalent:[NSString stringWithCharacters:ch length:1]];
92   
93 }
94
95
96
97 static void setItemShortcutFromString(NSMenuItem* item, const string& s)
98 {
99     std::string shortcut;
100   
101   bool hasCtrl = strutils::starts_with(s, "Ctrl-"); 
102   bool hasShift = strutils::starts_with(s, "Shift-");
103   bool hasAlt = strutils::starts_with(s, "Alt-");
104   
105   int offset = 0; // character offset from start of string
106   if (hasShift) offset += 6;
107   if (hasCtrl) offset += 5;
108   if (hasAlt) offset += 4;
109   
110   shortcut = s.substr(offset);
111   if (shortcut == "Esc")
112     shortcut = "\e";    
113   
114     if ((shortcut.length() >= 2) && (shortcut[0] == 'F') && isdigit(shortcut[1])) {
115         setFunctionKeyShortcut(shortcut, item);
116         return;
117     }
118
119     simgear::strutils::lowercase(shortcut);
120   [item setKeyEquivalent:[NSString stringWithCString:shortcut.c_str() encoding:NSUTF8StringEncoding]];
121   NSUInteger modifiers = 0;
122   if (hasCtrl) modifiers |= NSControlKeyMask;
123   if (hasShift) modifiers |= NSShiftKeyMask;
124   if (hasAlt) modifiers |= NSAlternateKeyMask;
125   
126   [item setKeyEquivalentModifierMask:modifiers];
127 }
128
129 namespace {
130
131   class CocoaEnabledListener : public SGPropertyChangeListener
132   {
133   public:
134     CocoaEnabledListener(NSMenuItem* i) :
135       item(i)
136     {
137     
138     }
139       
140       ~CocoaEnabledListener()
141       {
142           
143       }
144     
145     
146     virtual void valueChanged(SGPropertyNode *node) 
147     {
148       CocoaAutoreleasePool pool;
149       BOOL b = node->getBoolValue();
150       [item setEnabled:b];
151     }
152     
153   private:
154     NSMenuItem* item;
155   };
156 } // of anonymous namespace
157
158 FGCocoaMenuBar::CocoaMenuBarPrivate::CocoaMenuBarPrivate()
159 {
160   delegate = [[CocoaMenuDelegate alloc] init];
161   delegate.peer = this;
162 }
163   
164 FGCocoaMenuBar::CocoaMenuBarPrivate::~CocoaMenuBarPrivate()
165 {
166   CocoaAutoreleasePool pool;
167   [delegate release];
168 }
169   
170 static bool labelIsSeparator(NSString* s)
171 {
172   return [s hasPrefix:@"---"];
173 }
174   
175 void FGCocoaMenuBar::CocoaMenuBarPrivate::menuFromProps(NSMenu* menu, SGPropertyNode* menuNode)
176 {
177   int index = 0;
178   BOOST_FOREACH(SGPropertyNode_ptr n, menuNode->getChildren("item")) {
179     if (!n->hasValue("enabled")) {
180       n->setBoolValue("enabled", true);
181     }
182     
183     string l = getLocalizedLabel(n);
184     NSString* label = stdStringToCocoa(strutils::simplify(l));
185     string shortcut = n->getStringValue("key");
186     
187     NSMenuItem* item;
188     if (index >= [menu numberOfItems]) {
189       if (labelIsSeparator(label)) {
190         item = [NSMenuItem separatorItem];
191         [menu addItem:item];
192       } else {        
193         item = [menu addItemWithTitle:label action:nil keyEquivalent:@""];
194         if (!shortcut.empty()) {
195           setItemShortcutFromString(item, shortcut);
196         }
197         
198         SGPropertyChangeListener* enableListener = new CocoaEnabledListener(item);
199         listeners.push_back(enableListener);
200         n->getNode("enabled")->addChangeListener(enableListener);
201         [item setTarget:delegate];
202         [item setAction:@selector(itemAction:)];
203       }
204     } else {
205       item = [menu itemAtIndex:index];
206       [item setTitle:label]; 
207     }
208     
209     BOOL enabled = n->getBoolValue("enabled");
210     [item setEnabled:enabled];
211     
212     SGBindingList bl = readBindingList(n->getChildren("binding"), globals->get_props());
213       
214     itemBindings[item] = bl;    
215     ++index;
216   } // of item iteration
217 }
218
219 void FGCocoaMenuBar::CocoaMenuBarPrivate::fireBindingsForItem(NSMenuItem *item)
220 {
221   MenuItemBindings::iterator it = itemBindings.find(item);
222   if (it == itemBindings.end()) {
223     return;
224   }
225     
226   fireBindingList(it->second);
227 }
228
229 FGCocoaMenuBar::FGCocoaMenuBar() :
230   p(new CocoaMenuBarPrivate)
231 {
232   
233 }
234
235 FGCocoaMenuBar::~FGCocoaMenuBar()
236 {
237     CocoaAutoreleasePool ap;
238     NSMenu* mainBar = [[NSApplication sharedApplication] mainMenu];
239
240     int num = [mainBar numberOfItems];
241     for (int index=1; index < num; ++index) {
242         NSMenuItem* topLevelItem = [mainBar itemAtIndex:index];
243         [topLevelItem.submenu removeAllItems];
244     }
245     
246     std::vector<SGPropertyChangeListener*>::iterator it;
247     for (it = p->listeners.begin(); it != p->listeners.end(); ++it) {
248         delete *it;
249     }    
250 }
251
252 void FGCocoaMenuBar::init()
253 {
254   CocoaAutoreleasePool pool;
255   
256   NSMenu* mainBar = [[NSApplication sharedApplication] mainMenu];
257   SGPropertyNode_ptr props = fgGetNode("/sim/menubar/default",true);
258   
259   int index = 0;
260   NSMenuItem* previousMenu = [mainBar itemAtIndex:0];
261   if (![[previousMenu title] isEqualToString:@"FlightGear"]) {
262     [previousMenu setTitle:@"FlightGear"];
263   }
264   
265   BOOST_FOREACH(SGPropertyNode_ptr n, props->getChildren("menu")) {
266     NSString* label = stdStringToCocoa(getLocalizedLabel(n));
267     NSMenuItem* item = [mainBar itemWithTitle:label];
268     NSMenu* menu;
269     
270     if (!item) {
271       NSInteger insertIndex = [mainBar indexOfItem:previousMenu] + 1; 
272       item = [mainBar insertItemWithTitle:label action:nil keyEquivalent:@"" atIndex:insertIndex];
273       item.tag = index + 400;
274       
275       menu = [[NSMenu alloc] init];
276       menu.title = label;
277       [menu setAutoenablesItems:NO];
278       [mainBar setSubmenu:menu forItem:item];
279       [menu autorelease];
280     } else {
281       menu = item.submenu;
282     }
283     
284   // synchronise menu with properties
285     p->menuFromProps(menu, n);
286     ++index;
287     previousMenu = item;
288     
289   // track menu enable/disable state
290     if (!n->hasValue("enabled")) {
291       n->setBoolValue("enabled", true);
292     }
293     
294     SGPropertyChangeListener* l = new CocoaEnabledListener(item);
295     p->listeners.push_back(l);
296     n->getNode("enabled")->addChangeListener(l);
297   }
298 }
299
300 bool FGCocoaMenuBar::isVisible() const
301 {
302   return true;
303 }
304
305 void FGCocoaMenuBar::show()
306 {
307   // no-op
308 }
309
310 void FGCocoaMenuBar::hide()
311 {
312   // no-op
313 }
314