]>
Commit | Line | Data |
---|---|---|
d14a1e28 RD |
1 | # Name: tools.py |
2 | # Purpose: XRC editor, toolbar | |
3 | # Author: Roman Rolinsky <rolinsky@mema.ucl.ac.be> | |
4 | # Created: 19.03.2003 | |
5 | # RCS-ID: $Id$ | |
1fded56b | 6 | |
d14a1e28 RD |
7 | from xxx import * # xxx imports globals and params |
8 | from tree import ID_NEW | |
1fded56b | 9 | |
d14a1e28 RD |
10 | # Icons |
11 | import images | |
12 | ||
13 | # Groups of controls | |
14 | GROUPNUM = 4 | |
15 | GROUP_WINDOWS, GROUP_MENUS, GROUP_SIZERS, GROUP_CONTROLS = range(GROUPNUM) | |
16 | ||
17 | # States depending on current selection and Control/Shift keys | |
c032d94e | 18 | STATE_ROOT, STATE_MENUBAR, STATE_TOOLBAR, STATE_MENU, STATE_STDDLGBTN, STATE_ELSE = range(6) |
d14a1e28 | 19 | # Left toolbar for GUI elements |
29a41103 | 20 | class Tools(wx.Panel): |
d14a1e28 RD |
21 | TOOL_SIZE = (30, 30) |
22 | def __init__(self, parent): | |
29a41103 RD |
23 | if wx.Platform == '__WXGTK__': |
24 | wx.Panel.__init__(self, parent, -1, | |
25 | style=wx.RAISED_BORDER|wx.WANTS_CHARS) | |
d14a1e28 | 26 | else: |
29a41103 | 27 | wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS) |
d14a1e28 | 28 | # Create sizer for groups |
29a41103 | 29 | self.sizer = wx.BoxSizer(wx.VERTICAL) |
d14a1e28 RD |
30 | # Data to create buttons |
31 | pullDownMenu = g.pullDownMenu | |
32 | self.groups = [] | |
33 | self.ctrl = self.shift = False | |
34 | # Current state (what to enable/disable) | |
35 | self.state = None | |
36 | groups = [ | |
37 | ["Windows", | |
38 | (ID_NEW.FRAME, images.getToolFrameBitmap()), | |
39 | (ID_NEW.DIALOG, images.getToolDialogBitmap()), | |
40 | (ID_NEW.PANEL, images.getToolPanelBitmap())], | |
41 | ["Menus", | |
42 | (ID_NEW.TOOL_BAR, images.getToolToolBarBitmap()), | |
43 | (ID_NEW.MENU_BAR, images.getToolMenuBarBitmap()), | |
44 | (ID_NEW.MENU, images.getToolMenuBitmap()), | |
45 | (ID_NEW.TOOL, images.getToolToolBitmap()), | |
46 | (ID_NEW.MENU_ITEM, images.getToolMenuItemBitmap()), | |
47 | (ID_NEW.SEPARATOR, images.getToolSeparatorBitmap())], | |
48 | ["Sizers", | |
49 | (ID_NEW.BOX_SIZER, images.getToolBoxSizerBitmap()), | |
50 | (ID_NEW.STATIC_BOX_SIZER, images.getToolStaticBoxSizerBitmap()), | |
51 | (ID_NEW.GRID_SIZER, images.getToolGridSizerBitmap()), | |
52 | (ID_NEW.FLEX_GRID_SIZER, images.getToolFlexGridSizerBitmap()), | |
a4c013b2 | 53 | (ID_NEW.GRID_BAG_SIZER, images.getToolGridBagSizerBitmap()), |
d14a1e28 RD |
54 | (ID_NEW.SPACER, images.getToolSpacerBitmap())], |
55 | ["Controls", | |
56 | (ID_NEW.STATIC_TEXT, images.getToolStaticTextBitmap()), | |
57 | (ID_NEW.STATIC_BITMAP, images.getToolStaticBitmapBitmap()), | |
58 | (ID_NEW.STATIC_LINE, images.getToolStaticLineBitmap()), | |
59 | ||
60 | (ID_NEW.BUTTON, images.getToolButtonBitmap()), | |
61 | (ID_NEW.BITMAP_BUTTON, images.getToolBitmapButtonBitmap()), | |
62 | (ID_NEW.STATIC_BOX, images.getToolStaticBoxBitmap()), | |
63 | ||
64 | (ID_NEW.TEXT_CTRL, images.getToolTextCtrlBitmap()), | |
65 | (ID_NEW.COMBO_BOX, images.getToolComboBoxBitmap()), | |
66 | (ID_NEW.CHOICE, images.getToolChoiceBitmap()), | |
67 | ||
68 | (ID_NEW.RADIO_BUTTON, images.getToolRadioButtonBitmap()), | |
69 | (ID_NEW.CHECK_BOX, images.getToolCheckBoxBitmap()), | |
70 | (ID_NEW.RADIO_BOX, images.getToolRadioBoxBitmap()), | |
71 | ||
72 | (ID_NEW.SPIN_CTRL, images.getToolSpinCtrlBitmap()), | |
73 | (ID_NEW.SPIN_BUTTON, images.getToolSpinButtonBitmap()), | |
74 | (ID_NEW.SCROLL_BAR, images.getToolScrollBarBitmap()), | |
75 | ||
76 | (ID_NEW.SLIDER, images.getToolSliderBitmap()), | |
77 | (ID_NEW.GAUGE, images.getToolGaugeBitmap()), | |
78 | (ID_NEW.TREE_CTRL, images.getToolTreeCtrlBitmap()), | |
79 | ||
80 | (ID_NEW.LIST_BOX, images.getToolListBoxBitmap()), | |
81 | (ID_NEW.CHECK_LIST, images.getToolCheckListBitmap()), | |
82 | (ID_NEW.LIST_CTRL, images.getToolListCtrlBitmap()), | |
83 | ||
84 | (ID_NEW.NOTEBOOK, images.getToolNotebookBitmap()), | |
68ae5821 | 85 | (ID_NEW.SPLITTER_WINDOW, images.getToolSplitterWindowBitmap()), |
d14a1e28 RD |
86 | |
87 | (ID_NEW.UNKNOWN, images.getToolUnknownBitmap())] | |
88 | ] | |
552240ce | 89 | self.boxes = {} |
d14a1e28 RD |
90 | for grp in groups: |
91 | self.AddGroup(grp[0]) | |
92 | for b in grp[1:]: | |
93 | self.AddButton(b[0], b[1], g.pullDownMenu.createMap[b[0]]) | |
d14a1e28 RD |
94 | self.SetSizerAndFit(self.sizer) |
95 | # Allow to be resized in vertical direction only | |
96 | self.SetSizeHints(self.GetSize()[0], -1) | |
97 | # Events | |
29a41103 | 98 | wx.EVT_COMMAND_RANGE(self, ID_NEW.PANEL, ID_NEW.LAST, |
5c82ed90 | 99 | wx.wxEVT_COMMAND_BUTTON_CLICKED, g.frame.OnCreate) |
29a41103 RD |
100 | wx.EVT_KEY_DOWN(self, self.OnKeyDown) |
101 | wx.EVT_KEY_UP(self, self.OnKeyUp) | |
62602fbd RR |
102 | # wxMSW does not generate click events for StaticBox |
103 | if wx.Platform == '__WXMSW__': | |
104 | self.Bind(wx.EVT_LEFT_DOWN, self.OnClickBox) | |
d14a1e28 | 105 | |
ed650677 RR |
106 | self.drag = None |
107 | ||
d14a1e28 | 108 | def AddButton(self, id, image, text): |
29a41103 RD |
109 | from wx.lib import buttons |
110 | button = buttons.GenBitmapButton(self, id, image, size=self.TOOL_SIZE, | |
5c82ed90 | 111 | style=wx.NO_BORDER|wx.WANTS_CHARS) |
5e8b10e9 | 112 | button.SetBezelWidth(0) |
29a41103 RD |
113 | wx.EVT_KEY_DOWN(button, self.OnKeyDown) |
114 | wx.EVT_KEY_UP(button, self.OnKeyUp) | |
ed650677 RR |
115 | wx.EVT_LEFT_DOWN(button, self.OnLeftDownOnButton) |
116 | wx.EVT_MOTION(button, self.OnMotionOnButton) | |
d14a1e28 RD |
117 | button.SetToolTipString(text) |
118 | self.curSizer.Add(button) | |
119 | self.groups[-1][1][id] = button | |
120 | ||
121 | def AddGroup(self, name): | |
122 | # Each group is inside box | |
552240ce RR |
123 | id = wx.NewId() |
124 | box = wx.StaticBox(self, id, '[+] '+name, style=wx.WANTS_CHARS) | |
9e20825c RR |
125 | box.SetForegroundColour(wx.Colour(64, 64, 64)) |
126 | # box.SetFont(g.smallerFont()) | |
552240ce RR |
127 | box.show = True |
128 | box.name = name | |
129 | box.gnum = len(self.groups) | |
552240ce | 130 | box.Bind(wx.EVT_LEFT_DOWN, self.OnClickBox) |
29a41103 | 131 | boxSizer = wx.StaticBoxSizer(box, wx.VERTICAL) |
d14a1e28 | 132 | boxSizer.Add((0, 4)) |
552240ce | 133 | self.boxes[id] = box |
29a41103 | 134 | self.curSizer = wx.GridSizer(0, 3) |
d14a1e28 | 135 | boxSizer.Add(self.curSizer) |
552240ce | 136 | self.sizer.Add(boxSizer, 0, wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, 4) |
d14a1e28 RD |
137 | self.groups.append((box,{})) |
138 | ||
139 | # Enable/disable group | |
140 | def EnableGroup(self, gnum, enable = True): | |
141 | grp = self.groups[gnum] | |
9e20825c | 142 | #grp[0].Enable(enable) |
d14a1e28 RD |
143 | for b in grp[1].values(): b.Enable(enable) |
144 | ||
552240ce RR |
145 | # Show/hide group |
146 | def ShowGroup(self, gnum, show = True): | |
147 | grp = self.groups[gnum] | |
148 | grp[0].show = show | |
149 | for b in grp[1].values(): b.Show(show) | |
150 | ||
d14a1e28 RD |
151 | # Enable/disable group item |
152 | def EnableGroupItem(self, gnum, id, enable = True): | |
153 | grp = self.groups[gnum] | |
154 | grp[1][id].Enable(enable) | |
155 | ||
156 | # Enable/disable group items | |
157 | def EnableGroupItems(self, gnum, ids, enable = True): | |
158 | grp = self.groups[gnum] | |
159 | for id in ids: | |
160 | grp[1][id].Enable(enable) | |
161 | ||
552240ce | 162 | def OnClickBox(self, evt): |
9e20825c RR |
163 | if wx.Platform == '__WXMSW__': |
164 | box = None | |
165 | for id,b in self.boxes.items(): | |
62602fbd | 166 | # How to detect a click on a label? |
9e20825c RR |
167 | if b.GetRect().Inside(evt.GetPosition()): |
168 | box = b | |
169 | break | |
62602fbd RR |
170 | if not box: |
171 | evt.Skip() | |
172 | return | |
9e20825c RR |
173 | else: |
174 | box = self.boxes[evt.GetId()] | |
552240ce RR |
175 | # Collapse/restore static box, change label |
176 | self.ShowGroup(box.gnum, not box.show) | |
177 | if box.show: box.SetLabel('[+] ' + box.name) | |
178 | else: box.SetLabel('[-] ' + box.name) | |
179 | self.Layout() | |
9e20825c RR |
180 | self.Refresh() |
181 | #for b in self.boxes.items(): | |
552240ce | 182 | |
ed650677 RR |
183 | # DaD |
184 | def OnLeftDownOnButton(self, evt): | |
185 | self.posDown = evt.GetPosition() | |
186 | self.idDown = evt.GetId() | |
187 | self.btnDown = evt.GetEventObject() | |
188 | evt.Skip() | |
189 | ||
190 | def OnMotionOnButton(self, evt): | |
191 | # Detect dragging | |
192 | if evt.Dragging() and evt.LeftIsDown(): | |
193 | d = evt.GetPosition() - self.posDown | |
194 | if max(abs(d[0]), abs(d[1])) >= 5: | |
195 | if self.btnDown.HasCapture(): | |
196 | # Generate up event to release mouse | |
197 | evt = wx.MouseEvent(wx.EVT_LEFT_UP.typeId) | |
198 | evt.SetId(self.idDown) | |
199 | # Set flag to prevent normal button operation this time | |
200 | self.drag = True | |
201 | self.btnDown.ProcessEvent(evt) | |
202 | self.StartDrag() | |
203 | evt.Skip() | |
204 | ||
205 | def StartDrag(self): | |
5c82ed90 | 206 | do = MyDataObject() |
ed650677 RR |
207 | do.SetData(str(self.idDown)) |
208 | bm = self.btnDown.GetBitmapLabel() | |
a7d8ec1e RR |
209 | # wxGTK requires wxIcon cursor, wxWIN and wxMAC require wxCursor |
210 | if wx.Platform == '__WXGTK__': | |
5c82ed90 RR |
211 | icon = wx.EmptyIcon() |
212 | icon.CopyFromBitmap(bm) | |
213 | dragSource = wx.DropSource(self, icon) | |
a7d8ec1e | 214 | else: |
5c82ed90 RR |
215 | curs = wx.CursorFromImage(wx.ImageFromBitmap(bm)) |
216 | dragSource = wx.DropSource(self, curs) | |
217 | dragSource.SetData(do) | |
218 | g.frame.SetStatusText('Release the mouse button over the test window') | |
219 | dragSource.DoDragDrop() | |
220 | ||
d14a1e28 RD |
221 | # Process key events |
222 | def OnKeyDown(self, evt): | |
29a41103 | 223 | if evt.GetKeyCode() == wx.WXK_CONTROL: |
d14a1e28 | 224 | g.tree.ctrl = True |
29a41103 | 225 | elif evt.GetKeyCode() == wx.WXK_SHIFT: |
d14a1e28 RD |
226 | g.tree.shift = True |
227 | self.UpdateIfNeeded() | |
228 | evt.Skip() | |
229 | ||
230 | def OnKeyUp(self, evt): | |
29a41103 | 231 | if evt.GetKeyCode() == wx.WXK_CONTROL: |
d14a1e28 | 232 | g.tree.ctrl = False |
29a41103 | 233 | elif evt.GetKeyCode() == wx.WXK_SHIFT: |
d14a1e28 RD |
234 | g.tree.shift = False |
235 | self.UpdateIfNeeded() | |
236 | evt.Skip() | |
237 | ||
238 | def OnMouse(self, evt): | |
239 | # Update control and shift states | |
240 | g.tree.ctrl = evt.ControlDown() | |
241 | g.tree.shift = evt.ShiftDown() | |
242 | self.UpdateIfNeeded() | |
243 | evt.Skip() | |
244 | ||
245 | # Update UI after key presses, if necessary | |
246 | def UpdateIfNeeded(self): | |
247 | tree = g.tree | |
248 | if self.ctrl != tree.ctrl or self.shift != tree.shift: | |
249 | # Enabling is needed only for ctrl | |
250 | if self.ctrl != tree.ctrl: self.UpdateUI() | |
251 | self.ctrl = tree.ctrl | |
252 | self.shift = tree.shift | |
253 | if tree.ctrl: | |
254 | status = 'SBL' | |
255 | elif tree.shift: | |
256 | status = 'INS' | |
257 | else: | |
258 | status = '' | |
259 | g.frame.SetStatusText(status, 1) | |
260 | ||
261 | # Update interface | |
262 | def UpdateUI(self): | |
263 | if not self.IsShown(): return | |
264 | # Update status bar | |
265 | pullDownMenu = g.pullDownMenu | |
266 | tree = g.tree | |
267 | item = tree.selection | |
268 | # If nothing selected, disable everything and return | |
269 | if not item: | |
270 | # Disable everything | |
271 | for grp in range(GROUPNUM): | |
272 | self.EnableGroup(grp, False) | |
273 | self.state = None | |
274 | return | |
275 | if tree.ctrl: needInsert = True | |
276 | else: needInsert = tree.NeedInsert(item) | |
277 | # Enable depending on selection | |
278 | if item == tree.root or needInsert and tree.GetItemParent(item) == tree.root: | |
279 | state = STATE_ROOT | |
280 | else: | |
281 | xxx = tree.GetPyData(item).treeObject() | |
282 | # Check parent for possible child nodes if inserting sibling | |
283 | if needInsert: xxx = xxx.parent | |
284 | if xxx.__class__ == xxxMenuBar: | |
285 | state = STATE_MENUBAR | |
286 | elif xxx.__class__ in [xxxToolBar, xxxTool] or \ | |
287 | xxx.__class__ == xxxSeparator and xxx.parent.__class__ == xxxToolBar: | |
288 | state = STATE_TOOLBAR | |
289 | elif xxx.__class__ in [xxxMenu, xxxMenuItem]: | |
290 | state = STATE_MENU | |
c032d94e RR |
291 | elif xxx.__class__ == xxxStdDialogButtonSizer: |
292 | state = STATE_STDDLGBTN | |
d14a1e28 RD |
293 | else: |
294 | state = STATE_ELSE | |
295 | ||
296 | # Enable depending on selection | |
297 | if state != self.state: | |
298 | # Disable everything | |
299 | for grp in range(GROUPNUM): | |
300 | self.EnableGroup(grp, False) | |
301 | # Enable some | |
302 | if state == STATE_ROOT: | |
303 | self.EnableGroup(GROUP_WINDOWS, True) | |
304 | self.EnableGroup(GROUP_MENUS, True) | |
305 | # But disable items | |
306 | self.EnableGroupItems(GROUP_MENUS, | |
307 | [ ID_NEW.TOOL, | |
308 | ID_NEW.MENU_ITEM, | |
309 | ID_NEW.SEPARATOR ], | |
310 | False) | |
306b6fe9 | 311 | elif state == STATE_STDDLGBTN: |
c032d94e | 312 | pass # nothing can be added from toolbar |
d14a1e28 RD |
313 | elif state == STATE_MENUBAR: |
314 | self.EnableGroup(GROUP_MENUS) | |
315 | self.EnableGroupItems(GROUP_MENUS, | |
316 | [ ID_NEW.TOOL_BAR, | |
317 | ID_NEW.MENU_BAR, | |
318 | ID_NEW.TOOL ], | |
319 | False) | |
320 | elif state == STATE_TOOLBAR: | |
321 | self.EnableGroup(GROUP_MENUS) | |
322 | self.EnableGroupItems(GROUP_MENUS, | |
323 | [ ID_NEW.TOOL_BAR, | |
324 | ID_NEW.MENU, | |
325 | ID_NEW.MENU_BAR, | |
326 | ID_NEW.MENU_ITEM ], | |
327 | False) | |
328 | self.EnableGroup(GROUP_CONTROLS) | |
329 | self.EnableGroupItems(GROUP_CONTROLS, | |
330 | [ ID_NEW.TREE_CTRL, | |
68ae5821 RD |
331 | ID_NEW.NOTEBOOK, |
332 | ID_NEW.SPLITTER_WINDOW ], | |
d14a1e28 RD |
333 | False) |
334 | elif state == STATE_MENU: | |
335 | self.EnableGroup(GROUP_MENUS) | |
336 | self.EnableGroupItems(GROUP_MENUS, | |
337 | [ ID_NEW.TOOL_BAR, | |
338 | ID_NEW.MENU_BAR, | |
339 | ID_NEW.TOOL ], | |
340 | False) | |
341 | else: | |
342 | self.EnableGroup(GROUP_WINDOWS) | |
343 | self.EnableGroupItems(GROUP_WINDOWS, | |
344 | [ ID_NEW.FRAME, | |
345 | ID_NEW.DIALOG ], | |
346 | False) | |
347 | self.EnableGroup(GROUP_MENUS) | |
348 | self.EnableGroupItems(GROUP_MENUS, | |
349 | [ ID_NEW.MENU_BAR, | |
350 | ID_NEW.MENU_BAR, | |
351 | ID_NEW.MENU, | |
352 | ID_NEW.MENU_ITEM, | |
353 | ID_NEW.TOOL, | |
354 | ID_NEW.SEPARATOR ], | |
355 | False) | |
356 | self.EnableGroup(GROUP_SIZERS) | |
357 | self.EnableGroup(GROUP_CONTROLS) | |
306b6fe9 | 358 | # Special case for *book (always executed) |
d14a1e28 | 359 | if state == STATE_ELSE: |
306b6fe9 | 360 | if xxx.__class__ in [xxxNotebook, xxxChoicebook, xxxListbook]: |
d14a1e28 RD |
361 | self.EnableGroup(GROUP_SIZERS, False) |
362 | else: | |
363 | self.EnableGroup(GROUP_SIZERS) | |
364 | if not (xxx.isSizer or xxx.parent and xxx.parent.isSizer): | |
365 | self.EnableGroupItem(GROUP_SIZERS, ID_NEW.SPACER, False) | |
f65bb0f8 RD |
366 | if xxx.__class__ == xxxFrame: |
367 | self.EnableGroupItem(GROUP_MENUS, ID_NEW.MENU_BAR) | |
d14a1e28 RD |
368 | # Save state |
369 | self.state = state |