]> git.mxchange.org Git - flightgear.git/blob - src/GUI/FGCocoaMenuBar.mm
Clean-up: move autosave.xml loading code to proper method
[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
131
132 FGCocoaMenuBar::CocoaMenuBarPrivate::CocoaMenuBarPrivate()
133 {
134   delegate = [[CocoaMenuDelegate alloc] init];
135   delegate.peer = this;
136 }
137   
138 FGCocoaMenuBar::CocoaMenuBarPrivate::~CocoaMenuBarPrivate()
139 {
140   [delegate release];
141 }
142   
143 static bool labelIsSeparator(NSString* s)
144 {
145   return [s hasPrefix:@"---"];
146 }
147   
148 void FGCocoaMenuBar::CocoaMenuBarPrivate::menuFromProps(NSMenu* menu, SGPropertyNode* menuNode)
149 {
150   int index = 0;
151   BOOST_FOREACH(SGPropertyNode_ptr n, menuNode->getChildren("item")) {
152     if (!n->hasValue("enabled")) {
153       n->setBoolValue("enabled", true);
154     }
155     
156     string shortcut;
157     string l = n->getStringValue("label");
158     string::size_type pos = l.find("(");
159     if (pos != string::npos) {
160       string full(l);
161       l = full.substr(0, pos);
162       shortcut = full.substr(pos + 1, full.size() - (pos + 2));
163     }
164     
165     NSString* label = stdStringToCocoa(strutils::simplify(l));
166     
167     NSMenuItem* item;
168     if (index >= [menu numberOfItems]) {
169       if (labelIsSeparator(label)) {
170         item = [NSMenuItem separatorItem];
171         [menu addItem:item];
172       } else {        
173         item = [menu addItemWithTitle:label action:nil keyEquivalent:@""];
174         if (!shortcut.empty()) {
175           setItemShortcutFromString(item, shortcut);
176         }
177         
178         n->getNode("enabled")->addChangeListener(new EnabledListener(item));
179         [item setTarget:delegate];
180         [item setAction:@selector(itemAction:)];
181       }
182     } else {
183       item = [menu itemAtIndex:index];
184       [item setTitle:label]; 
185     }
186     
187     BOOL enabled = n->getBoolValue("enabled");
188     [item setEnabled:enabled];
189     
190     SGBindingList bl;
191     BOOST_FOREACH(SGPropertyNode_ptr binding, n->getChildren("binding")) {
192     // have to clone the bindings, since SGBinding takes ownership of the
193     // passed in node. Seems like something is wrong here, but following the
194     // PUI code for the moment.
195       SGPropertyNode* cloned(new SGPropertyNode);
196       copyProperties(binding, cloned);
197       bl.push_back(new SGBinding(cloned, globals->get_props()));
198     }
199     
200     itemBindings[item] = bl;    
201     ++index;
202   } // of item iteration
203 }
204
205 void FGCocoaMenuBar::CocoaMenuBarPrivate::fireBindingsForItem(NSMenuItem *item)
206 {
207   MenuItemBindings::iterator it = itemBindings.find(item);
208   if (it == itemBindings.end()) {
209     return;
210   }
211
212   BOOST_FOREACH(SGSharedPtr<SGBinding> b, it->second) {
213     b->fire();
214   }
215 }
216
217 FGCocoaMenuBar::FGCocoaMenuBar() :
218   p(new CocoaMenuBarPrivate)
219 {
220   
221 }
222
223 FGCocoaMenuBar::~FGCocoaMenuBar()
224 {
225   
226 }
227
228 void FGCocoaMenuBar::init()
229 {
230   NSMenu* mainBar = [[NSApplication sharedApplication] mainMenu];
231   SGPropertyNode_ptr props = fgGetNode("/sim/menubar/default",true);
232   
233   int index = 0;
234   NSMenuItem* previousMenu = [mainBar itemAtIndex:0];
235   if (![[previousMenu title] isEqualToString:@"FlightGear"]) {
236     [previousMenu setTitle:@"FlightGear"];
237   }
238   
239   BOOST_FOREACH(SGPropertyNode_ptr n, props->getChildren("menu")) {
240     NSString* label = stdStringToCocoa(n->getStringValue("label"));
241     NSMenuItem* item = [mainBar itemWithTitle:label];
242     NSMenu* menu;
243     
244     if (!item) {
245       NSInteger insertIndex = [mainBar indexOfItem:previousMenu] + 1; 
246       item = [mainBar insertItemWithTitle:label action:nil keyEquivalent:@"" atIndex:insertIndex];
247       item.tag = index + 400;
248       
249       menu = [[NSMenu alloc] init];
250       menu.title = label;
251       [menu setAutoenablesItems:NO];
252       [mainBar setSubmenu:menu forItem:item];
253       [menu autorelease];
254     } else {
255       menu = item.submenu;
256     }
257     
258   // synchronise menu with properties
259     p->menuFromProps(menu, n);
260     ++index;
261     previousMenu = item;
262   }
263 }
264
265 bool FGCocoaMenuBar::isVisible() const
266 {
267   return true;
268 }
269
270 void FGCocoaMenuBar::show()
271 {
272   // no-op
273 }
274
275 void FGCocoaMenuBar::hide()
276 {
277   // no-op
278 }