Got 'Perspectives' menu working
[hooke.git] / hooke / ui / gui / menu.py
1 # Copyright
2
3 """Menu bar for Hooke.
4 """
5
6 import wx
7
8 from ...util.callback import callback, in_callback
9 from . import panel as panel
10
11
12 class Menu (wx.Menu):
13     """A `Bind`able version of :class:`wx.Menu`.
14
15     From the `wxPython Style Guide`_, you can't do
16     wx.Menu().Bind(...), so we hack around it by bubbling the Bind up
17     to the closest parent :class:`wx.Frame`.
18
19     .. _wxPython Style Guide:
20       http://wiki.wxpython.org/wxPython%20Style%20Guide#line-101
21     """
22     def __init__(self, parent=None, **kwargs):
23         self._parent = parent
24         self._bindings = []
25         super(Menu, self).__init__(**kwargs)
26
27     def Bind(self, **kwargs):
28         assert 'id' in kwargs, kwargs
29         obj = self
30         while not isinstance(obj, wx.Frame):
31             obj = obj._parent
32         obj.Bind(**kwargs)
33         self._bindings.append(kwargs)
34
35     def Unbind(self, **kwargs):
36         assert 'id' in kwargs, kwargs
37         try:
38             self._bindings.remove(kwargs)
39         except ValueError:
40             pass
41         kwargs.pop('handler', None)
42         obj = self
43         while not isinstance(obj, wx.Frame):
44             obj = obj._parent
45         obj.Unbind(**kwargs)
46
47     def _unbind_all_items(self):
48         for kwargs in self._bindings:
49             self.Unbind(**kwargs)
50         self._bindings = []
51
52
53 class MenuBar (wx.MenuBar):
54     """A `Bind`able version of :class:`wx.MenuBar`.
55
56     See :class:`Menu` for the motivation.
57     """
58     def __init__(self, parent=None, **kwargs):
59         self._parent = parent
60         super(MenuBar, self).__init__(**kwargs)
61
62     def Append(self, menu, title):
63         menu._parent = self
64         super(MenuBar, self).Append(menu, title)
65
66
67 class FileMenu (Menu):
68     def __init__(self, callbacks=None, **kwargs):
69         super(FileMenu, self).__init__(**kwargs)
70         if callbacks == None:
71             callbacks = {}
72         self._callbacks = callbacks
73         self._c = {'exit': self.Append(wx.ID_EXIT)}
74         self.Bind(event=wx.EVT_MENU, handler=self.close, id=wx.ID_EXIT)
75
76     @callback
77     def close(self, event):
78         pass
79
80
81 class ViewMenu (Menu):
82     def __init__(self, callbacks=None, **kwargs):
83         super(ViewMenu, self).__init__(**kwargs)
84         if callbacks == None:
85             callbacks = {}
86         self._callbacks = callbacks
87         self._c = {}
88         for i,panelname in enumerate(sorted(panel.PANELS.keys())):
89             text = '%s\tF%d' % (panelname.capitalize(), i+5)
90             self._c[panelname] = self.AppendCheckItem(id=wx.ID_ANY, text=text)
91         for item in self._c.values():
92             item.Check()
93             self.Bind(event=wx.EVT_MENU, handler=self.view_panel, id=item.GetId())
94
95     def view_panel(self, event):
96         _id = event.GetId()
97         item = self.FindItemById(_id)
98         label = item.GetLabel()
99         selected = item.IsChecked()
100         in_callback(self, panel_name=label, visible=selected)
101
102
103 class PerspectiveMenu (Menu):
104     def __init__(self, callbacks=None, **kwargs):
105         super(PerspectiveMenu, self).__init__(**kwargs)
106         if callbacks == None:
107             callbacks = {}
108         self._callbacks = callbacks
109         self._c = {}
110
111     def update(self, perspectives, selected):
112         """Rebuild the perspectives menu.
113         """
114         self._unbind_all_items()
115         for item in self.GetMenuItems():
116             self.DeleteItem(item)
117         self._c = {
118             'save': self.Append(id=wx.ID_ANY, text='Save Perspective'),
119             'delete': self.Append(id=wx.ID_ANY, text='Delete Perspective'),
120             }
121         self.Bind(event=wx.EVT_MENU, handler=self.save_perspective,
122                   id=self._c['save'].GetId())
123         self.Bind(event=wx.EVT_MENU, handler=self.delete_perspective,
124                   id=self._c['delete'].GetId())
125         self.AppendSeparator()
126         for label in perspectives:
127             self._c[label] = self.AppendRadioItem(id=wx.ID_ANY, text=label)
128             self.Bind(event=wx.EVT_MENU, handler=self.select_perspective,
129                       id=self._c[label].GetId())
130             if label == selected:
131                 self._c[label].Check(True)
132
133     @callback
134     def save_perspective(self, event):
135         pass
136
137     @callback
138     def delete_perspective(self, event):
139         pass
140
141     def select_perspective(self, event):
142         _id = event.GetId()
143         item = self.FindItemById(_id)
144         label = item.GetLabel()
145         selected = item.IsChecked()
146         assert selected == True, label
147         in_callback(self, name=label)
148
149
150 class HelpMenu (Menu):
151     def __init__(self, callbacks=None, **kwargs):
152         super(HelpMenu, self).__init__(**kwargs)
153         if callbacks == None:
154             callbacks = {}
155         self._callbacks = callbacks
156         self._c = {'about': self.Append(id=wx.ID_ABOUT)}
157         self.Bind(event=wx.EVT_MENU, handler=self.about, id=wx.ID_ABOUT)
158
159     @callback
160     def about(self, event):
161         pass
162
163
164 class HookeMenuBar (MenuBar):
165     def __init__(self, callbacks=None, **kwargs):
166         super(HookeMenuBar, self).__init__(**kwargs)
167         if callbacks == None:
168             callbacks = {}
169         self._callbacks = callbacks
170         self._c = {}
171
172         # Attach *Menu() instances
173         for key in ['file', 'view', 'perspective', 'help']:
174             cap_key = key.capitalize()
175             _class = globals()['%sMenu' % cap_key]
176             self._c[key] = _class(parent=self, callbacks=callbacks)
177             self.Append(self._c[key], cap_key)