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