]> git.mxchange.org Git - flightgear.git/blob - src/GUI/FGCocoaMenuBar.mm
Merge branch 'attenuation' into navaids-radio
[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
12 #include <Main/fg_props.hxx>
13
14 #include <iostream>
15
16 using std::string;
17 using std::map;
18 using std::cout;
19
20 typedef std::map<NSMenuItem*, SGBindingList> MenuItemBindings;
21
22 @class CocoaMenuDelegate;
23
24 class FGCocoaMenuBar::CocoaMenuBarPrivate
25 {
26 public:
27   CocoaMenuBarPrivate();
28   ~CocoaMenuBarPrivate();
29   
30   bool labelIsSeparator(const std::string& s) const;  
31   void menuFromProps(NSMenu* menu, SGPropertyNode* menuNode);
32   
33   void fireBindingsForItem(NSMenuItem* item);
34   
35 public:
36   CocoaMenuDelegate* delegate;
37   
38   MenuItemBindings itemBindings;
39 };
40
41
42 @interface CocoaMenuDelegate : NSObject <NSMenuDelegate> {
43 @private
44   FGCocoaMenuBar::CocoaMenuBarPrivate* peer;
45 }
46
47 @property (nonatomic, assign) FGCocoaMenuBar::CocoaMenuBarPrivate* peer;
48 @end
49
50 @implementation CocoaMenuDelegate
51
52 @synthesize peer;
53
54 - (void) itemAction:(id) sender
55 {
56   peer->fireBindingsForItem((NSMenuItem*) sender);
57 }
58
59 @end
60
61 static NSString* stdStringToCocoa(const string& s)
62 {
63   return [NSString stringWithUTF8String:s.c_str()];
64 }
65
66 class EnabledListener : public SGPropertyChangeListener
67 {
68 public:
69   EnabledListener(NSMenuItem* i) :
70     item(i)
71   {}
72   
73   
74   virtual void valueChanged(SGPropertyNode *node) 
75   {
76     BOOL b = node->getBoolValue();
77     [item setEnabled:b];
78   }
79   
80 private:
81   NSMenuItem* item;
82 };
83
84
85
86 FGCocoaMenuBar::CocoaMenuBarPrivate::CocoaMenuBarPrivate()
87 {
88   delegate = [[CocoaMenuDelegate alloc] init];
89   delegate.peer = this;
90 }
91   
92 FGCocoaMenuBar::CocoaMenuBarPrivate::~CocoaMenuBarPrivate()
93 {
94   [delegate release];
95 }
96   
97 bool FGCocoaMenuBar::CocoaMenuBarPrivate::labelIsSeparator(const std::string& s) const
98 {
99   for (unsigned int i=0; i<s.size(); ++i) {
100     if (s[i] != '-') {
101       return false;
102     }
103   }
104   
105   return true;
106 }
107   
108 void FGCocoaMenuBar::CocoaMenuBarPrivate::menuFromProps(NSMenu* menu, SGPropertyNode* menuNode)
109 {
110   int index = 0;
111   BOOST_FOREACH(SGPropertyNode_ptr n, menuNode->getChildren("item")) {
112     if (!n->hasValue("enabled")) {
113       n->setBoolValue("enabled", true);
114     }
115     
116     string l = n->getStringValue("label");
117     string::size_type pos = l.find("(");
118     if (pos != string::npos) {
119       l = l.substr(0, pos);
120     }
121     
122     NSString* label = stdStringToCocoa(l);
123     NSString* shortcut = @"";
124     NSMenuItem* item;
125     if (index >= [menu numberOfItems]) {
126       if (labelIsSeparator(l)) {
127         item = [NSMenuItem separatorItem];
128         [menu addItem:item];
129       } else {        
130         item = [menu addItemWithTitle:label action:nil keyEquivalent:shortcut];
131         n->getNode("enabled")->addChangeListener(new EnabledListener(item));
132         [item setTarget:delegate];
133         [item setAction:@selector(itemAction:)];
134       }
135     } else {
136       item = [menu itemAtIndex:index];
137       [item setTitle:label]; 
138     }
139     
140     BOOL enabled = n->getBoolValue("enabled");
141     [item setEnabled:enabled];
142     
143     SGBindingList bl;
144     BOOST_FOREACH(SGPropertyNode_ptr binding, n->getChildren("binding")) {
145     // have to clone the bindings, since SGBinding takes ownership of the
146     // passed in node. Seems like something is wrong here, but following the
147     // PUI code for the moment.
148       SGPropertyNode* cloned(new SGPropertyNode);
149       copyProperties(binding, cloned);
150       bl.push_back(new SGBinding(cloned, globals->get_props()));
151     }
152     
153     itemBindings[item] = bl;    
154     ++index;
155   } // of item iteration
156 }
157
158 void FGCocoaMenuBar::CocoaMenuBarPrivate::fireBindingsForItem(NSMenuItem *item)
159 {
160   MenuItemBindings::iterator it = itemBindings.find(item);
161   if (it == itemBindings.end()) {
162     return;
163   }
164
165   BOOST_FOREACH(SGSharedPtr<SGBinding> b, it->second) {
166     b->fire();
167   }
168 }
169
170 FGCocoaMenuBar::FGCocoaMenuBar() :
171   p(new CocoaMenuBarPrivate)
172 {
173   
174 }
175
176 FGCocoaMenuBar::~FGCocoaMenuBar()
177 {
178   
179 }
180
181 void FGCocoaMenuBar::init()
182 {
183   NSMenu* mainBar = [[NSApplication sharedApplication] mainMenu];
184   SGPropertyNode_ptr props = fgGetNode("/sim/menubar/default",true);
185   
186   int index = 0;
187   NSMenuItem* previousMenu = [mainBar itemAtIndex:0];
188   if (![[previousMenu title] isEqualToString:@"FlightGear"]) {
189     [previousMenu setTitle:@"FlightGear"];
190   }
191   
192   BOOST_FOREACH(SGPropertyNode_ptr n, props->getChildren("menu")) {
193     NSString* label = stdStringToCocoa(n->getStringValue("label"));
194     NSMenuItem* item = [mainBar itemWithTitle:label];
195     NSMenu* menu;
196     
197     if (!item) {
198       NSInteger insertIndex = [mainBar indexOfItem:previousMenu] + 1; 
199       item = [mainBar insertItemWithTitle:label action:nil keyEquivalent:@"" atIndex:insertIndex];
200       item.tag = index + 400;
201       
202       menu = [[NSMenu alloc] init];
203       menu.title = label;
204       [menu setAutoenablesItems:NO];
205       [mainBar setSubmenu:menu forItem:item];
206       [menu autorelease];
207     } else {
208       menu = item.submenu;
209     }
210     
211   // synchronise menu with properties
212     p->menuFromProps(menu, n);
213     ++index;
214     previousMenu = item;
215   }
216 }
217
218 bool FGCocoaMenuBar::isVisible() const
219 {
220   return true;
221 }
222
223 void FGCocoaMenuBar::show()
224 {
225   // no-op
226 }
227
228 void FGCocoaMenuBar::hide()
229 {
230   // no-op
231 }