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