|
1 "A sort of application framework for the Mac" |
|
2 |
|
3 DEBUG=0 |
|
4 |
|
5 from warnings import warnpy3k |
|
6 warnpy3k("In 3.x, the FrameWork module is removed.", stacklevel=2) |
|
7 |
|
8 import MacOS |
|
9 import traceback |
|
10 |
|
11 from Carbon.AE import * |
|
12 from Carbon.AppleEvents import * |
|
13 from Carbon.Ctl import * |
|
14 from Carbon.Controls import * |
|
15 from Carbon.Dlg import * |
|
16 from Carbon.Dialogs import * |
|
17 from Carbon.Evt import * |
|
18 from Carbon.Events import * |
|
19 from Carbon.Help import * |
|
20 from Carbon.Menu import * |
|
21 from Carbon.Menus import * |
|
22 from Carbon.Qd import * |
|
23 from Carbon.QuickDraw import * |
|
24 #from Carbon.Res import * |
|
25 #from Carbon.Resources import * |
|
26 #from Carbon.Snd import * |
|
27 #from Carbon.Sound import * |
|
28 from Carbon.Win import * |
|
29 from Carbon.Windows import * |
|
30 import types |
|
31 |
|
32 import EasyDialogs |
|
33 |
|
34 try: |
|
35 MyFrontWindow = FrontNonFloatingWindow |
|
36 except NameError: |
|
37 MyFrontWindow = FrontWindow |
|
38 |
|
39 kHighLevelEvent = 23 # Don't know what header file this should come from |
|
40 SCROLLBARWIDTH = 16 # Again, not a clue... |
|
41 |
|
42 # Trick to forestall a set of SIOUX menus being added to our menubar |
|
43 SIOUX_APPLEMENU_ID=32000 |
|
44 |
|
45 |
|
46 # Map event 'what' field to strings |
|
47 eventname = {} |
|
48 eventname[1] = 'mouseDown' |
|
49 eventname[2] = 'mouseUp' |
|
50 eventname[3] = 'keyDown' |
|
51 eventname[4] = 'keyUp' |
|
52 eventname[5] = 'autoKey' |
|
53 eventname[6] = 'updateEvt' |
|
54 eventname[7] = 'diskEvt' |
|
55 eventname[8] = 'activateEvt' |
|
56 eventname[15] = 'osEvt' |
|
57 eventname[23] = 'kHighLevelEvent' |
|
58 |
|
59 # Map part codes returned by WhichWindow() to strings |
|
60 partname = {} |
|
61 partname[0] = 'inDesk' |
|
62 partname[1] = 'inMenuBar' |
|
63 partname[2] = 'inSysWindow' |
|
64 partname[3] = 'inContent' |
|
65 partname[4] = 'inDrag' |
|
66 partname[5] = 'inGrow' |
|
67 partname[6] = 'inGoAway' |
|
68 partname[7] = 'inZoomIn' |
|
69 partname[8] = 'inZoomOut' |
|
70 |
|
71 # |
|
72 # The useable portion of the screen |
|
73 # ## but what happens with multiple screens? jvr |
|
74 screenbounds = GetQDGlobalsScreenBits().bounds |
|
75 screenbounds = screenbounds[0]+4, screenbounds[1]+4, \ |
|
76 screenbounds[2]-4, screenbounds[3]-4 |
|
77 |
|
78 next_window_x = 16 # jvr |
|
79 next_window_y = 44 # jvr |
|
80 |
|
81 def windowbounds(width, height): |
|
82 "Return sensible window bounds" |
|
83 global next_window_x, next_window_y |
|
84 r, b = next_window_x+width, next_window_y+height |
|
85 if r > screenbounds[2]: |
|
86 next_window_x = 16 |
|
87 if b > screenbounds[3]: |
|
88 next_window_y = 44 |
|
89 l, t = next_window_x, next_window_y |
|
90 r, b = next_window_x+width, next_window_y+height |
|
91 next_window_x, next_window_y = next_window_x + 8, next_window_y + 20 # jvr |
|
92 return l, t, r, b |
|
93 |
|
94 _watch = None |
|
95 def setwatchcursor(): |
|
96 global _watch |
|
97 |
|
98 if _watch is None: |
|
99 _watch = GetCursor(4).data |
|
100 SetCursor(_watch) |
|
101 |
|
102 def setarrowcursor(): |
|
103 SetCursor(GetQDGlobalsArrow()) |
|
104 |
|
105 class Application: |
|
106 |
|
107 "Application framework -- your application should be a derived class" |
|
108 |
|
109 def __init__(self, nomenubar=0): |
|
110 self._doing_asyncevents = 0 |
|
111 self.quitting = 0 |
|
112 self.needmenubarredraw = 0 |
|
113 self._windows = {} |
|
114 self._helpmenu = None |
|
115 if nomenubar: |
|
116 self.menubar = None |
|
117 else: |
|
118 self.makemenubar() |
|
119 |
|
120 def __del__(self): |
|
121 if self._doing_asyncevents: |
|
122 self._doing_asyncevents = 0 |
|
123 MacOS.SetEventHandler() |
|
124 |
|
125 def makemenubar(self): |
|
126 self.menubar = MenuBar(self) |
|
127 AppleMenu(self.menubar, self.getabouttext(), self.do_about) |
|
128 self.makeusermenus() |
|
129 |
|
130 def makeusermenus(self): |
|
131 self.filemenu = m = Menu(self.menubar, "File") |
|
132 self._quititem = MenuItem(m, "Quit", "Q", self._quit) |
|
133 |
|
134 def gethelpmenu(self): |
|
135 if self._helpmenu is None: |
|
136 self._helpmenu = HelpMenu(self.menubar) |
|
137 return self._helpmenu |
|
138 |
|
139 def _quit(self, *args): |
|
140 self.quitting = 1 |
|
141 |
|
142 def cleanup(self): |
|
143 for w in self._windows.values(): |
|
144 w.do_close() |
|
145 return self._windows == {} |
|
146 |
|
147 def appendwindow(self, wid, window): |
|
148 self._windows[wid] = window |
|
149 |
|
150 def removewindow(self, wid): |
|
151 del self._windows[wid] |
|
152 |
|
153 def getabouttext(self): |
|
154 return "About %s..." % self.__class__.__name__ |
|
155 |
|
156 def do_about(self, id, item, window, event): |
|
157 EasyDialogs.Message("Hello, world!" + "\015(%s)" % self.__class__.__name__) |
|
158 |
|
159 # The main event loop is broken up in several simple steps. |
|
160 # This is done so you can override each individual part, |
|
161 # if you have a need to do extra processing independent of the |
|
162 # event type. |
|
163 # Normally, however, you'd just define handlers for individual |
|
164 # events. |
|
165 |
|
166 schedparams = (0, 0) # By default disable Python's event handling |
|
167 default_wait = None # By default we wait GetCaretTime in WaitNextEvent |
|
168 |
|
169 def mainloop(self, mask = everyEvent, wait = None): |
|
170 self.quitting = 0 |
|
171 if hasattr(MacOS, 'SchedParams'): |
|
172 saveparams = MacOS.SchedParams(*self.schedparams) |
|
173 try: |
|
174 while not self.quitting: |
|
175 try: |
|
176 self.do1event(mask, wait) |
|
177 except (Application, SystemExit): |
|
178 # Note: the raising of "self" is old-fashioned idiom to |
|
179 # exit the mainloop. Calling _quit() is better for new |
|
180 # applications. |
|
181 break |
|
182 finally: |
|
183 if hasattr(MacOS, 'SchedParams'): |
|
184 MacOS.SchedParams(*saveparams) |
|
185 |
|
186 def dopendingevents(self, mask = everyEvent): |
|
187 """dopendingevents - Handle all pending events""" |
|
188 while self.do1event(mask, wait=0): |
|
189 pass |
|
190 |
|
191 def do1event(self, mask = everyEvent, wait = None): |
|
192 ok, event = self.getevent(mask, wait) |
|
193 if IsDialogEvent(event): |
|
194 if self.do_dialogevent(event): |
|
195 return |
|
196 if ok: |
|
197 self.dispatch(event) |
|
198 else: |
|
199 self.idle(event) |
|
200 |
|
201 def idle(self, event): |
|
202 pass |
|
203 |
|
204 def getevent(self, mask = everyEvent, wait = None): |
|
205 if self.needmenubarredraw: |
|
206 DrawMenuBar() |
|
207 self.needmenubarredraw = 0 |
|
208 if wait is None: |
|
209 wait = self.default_wait |
|
210 if wait is None: |
|
211 wait = GetCaretTime() |
|
212 ok, event = WaitNextEvent(mask, wait) |
|
213 return ok, event |
|
214 |
|
215 def dispatch(self, event): |
|
216 # The following appears to be double work (already done in do1event) |
|
217 # but we need it for asynchronous event handling |
|
218 if IsDialogEvent(event): |
|
219 if self.do_dialogevent(event): |
|
220 return |
|
221 (what, message, when, where, modifiers) = event |
|
222 if eventname.has_key(what): |
|
223 name = "do_" + eventname[what] |
|
224 else: |
|
225 name = "do_%d" % what |
|
226 try: |
|
227 handler = getattr(self, name) |
|
228 except AttributeError: |
|
229 handler = self.do_unknownevent |
|
230 handler(event) |
|
231 |
|
232 def asyncevents(self, onoff): |
|
233 """asyncevents - Set asynchronous event handling on or off""" |
|
234 if MacOS.runtimemodel == 'macho': |
|
235 raise 'Unsupported in MachoPython' |
|
236 old = self._doing_asyncevents |
|
237 if old: |
|
238 MacOS.SetEventHandler() |
|
239 MacOS.SchedParams(*self.schedparams) |
|
240 if onoff: |
|
241 MacOS.SetEventHandler(self.dispatch) |
|
242 doint, dummymask, benice, howoften, bgyield = \ |
|
243 self.schedparams |
|
244 MacOS.SchedParams(doint, everyEvent, benice, |
|
245 howoften, bgyield) |
|
246 self._doing_asyncevents = onoff |
|
247 return old |
|
248 |
|
249 def do_dialogevent(self, event): |
|
250 gotone, dlg, item = DialogSelect(event) |
|
251 if gotone: |
|
252 window = dlg.GetDialogWindow() |
|
253 if self._windows.has_key(window): |
|
254 self._windows[window].do_itemhit(item, event) |
|
255 else: |
|
256 print 'Dialog event for unknown dialog' |
|
257 return 1 |
|
258 return 0 |
|
259 |
|
260 def do_mouseDown(self, event): |
|
261 (what, message, when, where, modifiers) = event |
|
262 partcode, wid = FindWindow(where) |
|
263 |
|
264 # |
|
265 # Find the correct name. |
|
266 # |
|
267 if partname.has_key(partcode): |
|
268 name = "do_" + partname[partcode] |
|
269 else: |
|
270 name = "do_%d" % partcode |
|
271 |
|
272 if wid is None: |
|
273 # No window, or a non-python window |
|
274 try: |
|
275 handler = getattr(self, name) |
|
276 except AttributeError: |
|
277 # Not menubar or something, so assume someone |
|
278 # else's window |
|
279 if hasattr(MacOS, 'HandleEvent'): |
|
280 MacOS.HandleEvent(event) |
|
281 return |
|
282 elif self._windows.has_key(wid): |
|
283 # It is a window. Hand off to correct window. |
|
284 window = self._windows[wid] |
|
285 try: |
|
286 handler = getattr(window, name) |
|
287 except AttributeError: |
|
288 handler = self.do_unknownpartcode |
|
289 else: |
|
290 # It is a python-toolbox window, but not ours. |
|
291 handler = self.do_unknownwindow |
|
292 handler(partcode, wid, event) |
|
293 |
|
294 def do_inSysWindow(self, partcode, window, event): |
|
295 if hasattr(MacOS, 'HandleEvent'): |
|
296 MacOS.HandleEvent(event) |
|
297 |
|
298 def do_inDesk(self, partcode, window, event): |
|
299 if hasattr(MacOS, 'HandleEvent'): |
|
300 MacOS.HandleEvent(event) |
|
301 |
|
302 def do_inMenuBar(self, partcode, window, event): |
|
303 if not self.menubar: |
|
304 if hasattr(MacOS, 'HandleEvent'): |
|
305 MacOS.HandleEvent(event) |
|
306 return |
|
307 (what, message, when, where, modifiers) = event |
|
308 result = MenuSelect(where) |
|
309 id = (result>>16) & 0xffff # Hi word |
|
310 if id >= 0x8000: |
|
311 id = -65536 + id |
|
312 item = result & 0xffff # Lo word |
|
313 self.do_rawmenu(id, item, window, event) |
|
314 |
|
315 def do_rawmenu(self, id, item, window, event): |
|
316 try: |
|
317 self.do_menu(id, item, window, event) |
|
318 finally: |
|
319 HiliteMenu(0) |
|
320 |
|
321 def do_menu(self, id, item, window, event): |
|
322 if hasattr(MacOS, 'OutputSeen'): |
|
323 MacOS.OutputSeen() |
|
324 self.menubar.dispatch(id, item, window, event) |
|
325 |
|
326 |
|
327 def do_unknownpartcode(self, partcode, window, event): |
|
328 (what, message, when, where, modifiers) = event |
|
329 if DEBUG: print "Mouse down at global:", where |
|
330 if DEBUG: print "\tUnknown part code:", partcode |
|
331 if DEBUG: print "\tEvent:", self.printevent(event) |
|
332 if hasattr(MacOS, 'HandleEvent'): |
|
333 MacOS.HandleEvent(event) |
|
334 |
|
335 def do_unknownwindow(self, partcode, window, event): |
|
336 if DEBUG: print 'Unknown window:', window |
|
337 if hasattr(MacOS, 'HandleEvent'): |
|
338 MacOS.HandleEvent(event) |
|
339 |
|
340 def do_keyDown(self, event): |
|
341 self.do_key(event) |
|
342 |
|
343 def do_autoKey(self, event): |
|
344 if not event[-1] & cmdKey: |
|
345 self.do_key(event) |
|
346 |
|
347 def do_key(self, event): |
|
348 (what, message, when, where, modifiers) = event |
|
349 c = chr(message & charCodeMask) |
|
350 if self.menubar: |
|
351 result = MenuEvent(event) |
|
352 id = (result>>16) & 0xffff # Hi word |
|
353 item = result & 0xffff # Lo word |
|
354 if id: |
|
355 self.do_rawmenu(id, item, None, event) |
|
356 return |
|
357 # Otherwise we fall-through |
|
358 if modifiers & cmdKey: |
|
359 if c == '.': |
|
360 raise self |
|
361 else: |
|
362 if not self.menubar: |
|
363 if hasattr(MacOS, 'HandleEvent'): |
|
364 MacOS.HandleEvent(event) |
|
365 return |
|
366 else: |
|
367 # See whether the front window wants it |
|
368 w = MyFrontWindow() |
|
369 if w and self._windows.has_key(w): |
|
370 window = self._windows[w] |
|
371 try: |
|
372 do_char = window.do_char |
|
373 except AttributeError: |
|
374 do_char = self.do_char |
|
375 do_char(c, event) |
|
376 # else it wasn't for us, sigh... |
|
377 |
|
378 def do_char(self, c, event): |
|
379 if DEBUG: print "Character", repr(c) |
|
380 |
|
381 def do_updateEvt(self, event): |
|
382 (what, message, when, where, modifiers) = event |
|
383 wid = WhichWindow(message) |
|
384 if wid and self._windows.has_key(wid): |
|
385 window = self._windows[wid] |
|
386 window.do_rawupdate(wid, event) |
|
387 else: |
|
388 if hasattr(MacOS, 'HandleEvent'): |
|
389 MacOS.HandleEvent(event) |
|
390 |
|
391 def do_activateEvt(self, event): |
|
392 (what, message, when, where, modifiers) = event |
|
393 wid = WhichWindow(message) |
|
394 if wid and self._windows.has_key(wid): |
|
395 window = self._windows[wid] |
|
396 window.do_activate(modifiers & 1, event) |
|
397 else: |
|
398 if hasattr(MacOS, 'HandleEvent'): |
|
399 MacOS.HandleEvent(event) |
|
400 |
|
401 def do_osEvt(self, event): |
|
402 (what, message, when, where, modifiers) = event |
|
403 which = (message >> 24) & 0xff |
|
404 if which == 1: # suspend/resume |
|
405 self.do_suspendresume(event) |
|
406 else: |
|
407 if DEBUG: |
|
408 print 'unknown osEvt:', |
|
409 self.printevent(event) |
|
410 |
|
411 def do_suspendresume(self, event): |
|
412 (what, message, when, where, modifiers) = event |
|
413 wid = MyFrontWindow() |
|
414 if wid and self._windows.has_key(wid): |
|
415 window = self._windows[wid] |
|
416 window.do_activate(message & 1, event) |
|
417 |
|
418 def do_kHighLevelEvent(self, event): |
|
419 (what, message, when, where, modifiers) = event |
|
420 if DEBUG: |
|
421 print "High Level Event:", |
|
422 self.printevent(event) |
|
423 try: |
|
424 AEProcessAppleEvent(event) |
|
425 except: |
|
426 pass |
|
427 #print "AEProcessAppleEvent error:" |
|
428 #traceback.print_exc() |
|
429 |
|
430 def do_unknownevent(self, event): |
|
431 if DEBUG: |
|
432 print "Unhandled event:", |
|
433 self.printevent(event) |
|
434 |
|
435 def printevent(self, event): |
|
436 (what, message, when, where, modifiers) = event |
|
437 nicewhat = repr(what) |
|
438 if eventname.has_key(what): |
|
439 nicewhat = eventname[what] |
|
440 print nicewhat, |
|
441 if what == kHighLevelEvent: |
|
442 h, v = where |
|
443 print repr(ostypecode(message)), hex(when), repr(ostypecode(h | (v<<16))), |
|
444 else: |
|
445 print hex(message), hex(when), where, |
|
446 print hex(modifiers) |
|
447 |
|
448 |
|
449 class MenuBar: |
|
450 """Represent a set of menus in a menu bar. |
|
451 |
|
452 Interface: |
|
453 |
|
454 - (constructor) |
|
455 - (destructor) |
|
456 - addmenu |
|
457 - addpopup (normally used internally) |
|
458 - dispatch (called from Application) |
|
459 """ |
|
460 |
|
461 nextid = 1 # Necessarily a class variable |
|
462 |
|
463 def getnextid(self): |
|
464 id = MenuBar.nextid |
|
465 MenuBar.nextid = id+1 |
|
466 return id |
|
467 |
|
468 def __init__(self, parent=None): |
|
469 self.parent = parent |
|
470 ClearMenuBar() |
|
471 self.bar = GetMenuBar() |
|
472 self.menus = {} |
|
473 |
|
474 # XXX necessary? |
|
475 def close(self): |
|
476 self.parent = None |
|
477 self.bar = None |
|
478 self.menus = None |
|
479 |
|
480 def addmenu(self, title, after = 0, id=None): |
|
481 if id is None: |
|
482 id = self.getnextid() |
|
483 if DEBUG: print 'Newmenu', title, id # XXXX |
|
484 m = NewMenu(id, title) |
|
485 m.InsertMenu(after) |
|
486 if after >= 0: |
|
487 if self.parent: |
|
488 self.parent.needmenubarredraw = 1 |
|
489 else: |
|
490 DrawMenuBar() |
|
491 return id, m |
|
492 |
|
493 def delmenu(self, id): |
|
494 if DEBUG: print 'Delmenu', id # XXXX |
|
495 DeleteMenu(id) |
|
496 |
|
497 def addpopup(self, title = ''): |
|
498 return self.addmenu(title, -1) |
|
499 |
|
500 # Useless: |
|
501 # def install(self): |
|
502 # if not self.bar: return |
|
503 # SetMenuBar(self.bar) |
|
504 # if self.parent: |
|
505 # self.parent.needmenubarredraw = 1 |
|
506 # else: |
|
507 # DrawMenuBar() |
|
508 |
|
509 def fixmenudimstate(self): |
|
510 for m in self.menus.keys(): |
|
511 menu = self.menus[m] |
|
512 if menu.__class__ == FrameWork.AppleMenu: |
|
513 continue |
|
514 for i in range(len(menu.items)): |
|
515 label, shortcut, callback, kind = menu.items[i] |
|
516 if type(callback) == types.StringType: |
|
517 wid = MyFrontWindow() |
|
518 if wid and self.parent._windows.has_key(wid): |
|
519 window = self.parent._windows[wid] |
|
520 if hasattr(window, "domenu_" + callback): |
|
521 menu.menu.EnableMenuItem(i + 1) |
|
522 elif hasattr(self.parent, "domenu_" + callback): |
|
523 menu.menu.EnableMenuItem(i + 1) |
|
524 else: |
|
525 menu.menu.DisableMenuItem(i + 1) |
|
526 elif hasattr(self.parent, "domenu_" + callback): |
|
527 menu.menu.EnableMenuItem(i + 1) |
|
528 else: |
|
529 menu.menu.DisableMenuItem(i + 1) |
|
530 elif callback: |
|
531 pass |
|
532 |
|
533 def dispatch(self, id, item, window, event): |
|
534 if self.menus.has_key(id): |
|
535 self.menus[id].dispatch(id, item, window, event) |
|
536 else: |
|
537 if DEBUG: print "MenuBar.dispatch(%d, %d, %s, %s)" % \ |
|
538 (id, item, window, event) |
|
539 |
|
540 |
|
541 # XXX Need a way to get menus as resources and bind them to callbacks |
|
542 |
|
543 class Menu: |
|
544 "One menu." |
|
545 |
|
546 def __init__(self, bar, title, after=0, id=None): |
|
547 self.bar = bar |
|
548 self.id, self.menu = self.bar.addmenu(title, after, id) |
|
549 bar.menus[self.id] = self |
|
550 self.items = [] |
|
551 self._parent = None |
|
552 |
|
553 def delete(self): |
|
554 self.bar.delmenu(self.id) |
|
555 del self.bar.menus[self.id] |
|
556 self.menu.DisposeMenu() |
|
557 del self.bar |
|
558 del self.items |
|
559 del self.menu |
|
560 del self.id |
|
561 del self._parent |
|
562 |
|
563 def additem(self, label, shortcut=None, callback=None, kind=None): |
|
564 self.menu.AppendMenu('x') # add a dummy string |
|
565 self.items.append((label, shortcut, callback, kind)) |
|
566 item = len(self.items) |
|
567 if isinstance(label, unicode): |
|
568 self.menu.SetMenuItemTextWithCFString(item, label) |
|
569 else: |
|
570 self.menu.SetMenuItemText(item, label) |
|
571 if shortcut and type(shortcut) == type(()): |
|
572 modifiers, char = shortcut[:2] |
|
573 self.menu.SetItemCmd(item, ord(char)) |
|
574 self.menu.SetMenuItemModifiers(item, modifiers) |
|
575 if len(shortcut) > 2: |
|
576 self.menu.SetMenuItemKeyGlyph(item, shortcut[2]) |
|
577 elif shortcut: |
|
578 self.menu.SetItemCmd(item, ord(shortcut)) |
|
579 return item |
|
580 |
|
581 def delitem(self, item): |
|
582 if item != len(self.items): |
|
583 raise 'Can only delete last item of a menu' |
|
584 self.menu.DeleteMenuItem(item) |
|
585 del self.items[item-1] |
|
586 |
|
587 def addcheck(self, label, shortcut=None, callback=None): |
|
588 return self.additem(label, shortcut, callback, 'check') |
|
589 |
|
590 def addradio(self, label, shortcut=None, callback=None): |
|
591 return self.additem(label, shortcut, callback, 'radio') |
|
592 |
|
593 def addseparator(self): |
|
594 self.menu.AppendMenu('(-') |
|
595 self.items.append(('', None, None, 'separator')) |
|
596 |
|
597 def addsubmenu(self, label, title=''): |
|
598 sub = Menu(self.bar, title, -1) |
|
599 item = self.additem(label, '\x1B', None, 'submenu') |
|
600 self.menu.SetItemMark(item, sub.id) |
|
601 sub._parent = self |
|
602 sub._parent_item = item |
|
603 return sub |
|
604 |
|
605 def dispatch(self, id, item, window, event): |
|
606 title, shortcut, callback, mtype = self.items[item-1] |
|
607 if callback: |
|
608 if not self.bar.parent or type(callback) <> types.StringType: |
|
609 menuhandler = callback |
|
610 else: |
|
611 # callback is string |
|
612 wid = MyFrontWindow() |
|
613 if wid and self.bar.parent._windows.has_key(wid): |
|
614 window = self.bar.parent._windows[wid] |
|
615 if hasattr(window, "domenu_" + callback): |
|
616 menuhandler = getattr(window, "domenu_" + callback) |
|
617 elif hasattr(self.bar.parent, "domenu_" + callback): |
|
618 menuhandler = getattr(self.bar.parent, "domenu_" + callback) |
|
619 else: |
|
620 # nothing we can do. we shouldn't have come this far |
|
621 # since the menu item should have been disabled... |
|
622 return |
|
623 elif hasattr(self.bar.parent, "domenu_" + callback): |
|
624 menuhandler = getattr(self.bar.parent, "domenu_" + callback) |
|
625 else: |
|
626 # nothing we can do. we shouldn't have come this far |
|
627 # since the menu item should have been disabled... |
|
628 return |
|
629 menuhandler(id, item, window, event) |
|
630 |
|
631 def enable(self, onoff): |
|
632 if onoff: |
|
633 self.menu.EnableMenuItem(0) |
|
634 if self._parent: |
|
635 self._parent.menu.EnableMenuItem(self._parent_item) |
|
636 else: |
|
637 self.menu.DisableMenuItem(0) |
|
638 if self._parent: |
|
639 self._parent.menu.DisableMenuItem(self._parent_item) |
|
640 if self.bar and self.bar.parent: |
|
641 self.bar.parent.needmenubarredraw = 1 |
|
642 |
|
643 class PopupMenu(Menu): |
|
644 def __init__(self, bar): |
|
645 Menu.__init__(self, bar, '(popup)', -1) |
|
646 |
|
647 def popup(self, x, y, event, default=1, window=None): |
|
648 # NOTE that x and y are global coordinates, and they should probably |
|
649 # be topleft of the button the user clicked (not mouse-coordinates), |
|
650 # so the popup nicely overlaps. |
|
651 reply = self.menu.PopUpMenuSelect(x, y, default) |
|
652 if not reply: |
|
653 return |
|
654 id = (reply >> 16) & 0xffff |
|
655 item = reply & 0xffff |
|
656 if not window: |
|
657 wid = MyFrontWindow() |
|
658 try: |
|
659 window = self.bar.parent._windows[wid] |
|
660 except: |
|
661 pass # If we can't find the window we pass None |
|
662 self.dispatch(id, item, window, event) |
|
663 |
|
664 class MenuItem: |
|
665 def __init__(self, menu, title, shortcut=None, callback=None, kind=None): |
|
666 self.item = menu.additem(title, shortcut, callback) |
|
667 self.menu = menu |
|
668 |
|
669 def delete(self): |
|
670 self.menu.delitem(self.item) |
|
671 del self.menu |
|
672 del self.item |
|
673 |
|
674 def check(self, onoff): |
|
675 self.menu.menu.CheckMenuItem(self.item, onoff) |
|
676 |
|
677 def enable(self, onoff): |
|
678 if onoff: |
|
679 self.menu.menu.EnableMenuItem(self.item) |
|
680 else: |
|
681 self.menu.menu.DisableMenuItem(self.item) |
|
682 |
|
683 def settext(self, text): |
|
684 self.menu.menu.SetMenuItemText(self.item, text) |
|
685 |
|
686 def setstyle(self, style): |
|
687 self.menu.menu.SetItemStyle(self.item, style) |
|
688 |
|
689 def seticon(self, icon): |
|
690 self.menu.menu.SetItemIcon(self.item, icon) |
|
691 |
|
692 def setcmd(self, cmd): |
|
693 self.menu.menu.SetItemCmd(self.item, cmd) |
|
694 |
|
695 def setmark(self, cmd): |
|
696 self.menu.menu.SetItemMark(self.item, cmd) |
|
697 |
|
698 |
|
699 class RadioItem(MenuItem): |
|
700 def __init__(self, menu, title, shortcut=None, callback=None): |
|
701 MenuItem.__init__(self, menu, title, shortcut, callback, 'radio') |
|
702 |
|
703 class CheckItem(MenuItem): |
|
704 def __init__(self, menu, title, shortcut=None, callback=None): |
|
705 MenuItem.__init__(self, menu, title, shortcut, callback, 'check') |
|
706 |
|
707 def Separator(menu): |
|
708 menu.addseparator() |
|
709 |
|
710 def SubMenu(menu, label, title=''): |
|
711 return menu.addsubmenu(label, title) |
|
712 |
|
713 |
|
714 class AppleMenu(Menu): |
|
715 |
|
716 def __init__(self, bar, abouttext="About me...", aboutcallback=None): |
|
717 Menu.__init__(self, bar, "\024", id=SIOUX_APPLEMENU_ID) |
|
718 if MacOS.runtimemodel == 'ppc': |
|
719 self.additem(abouttext, None, aboutcallback) |
|
720 self.addseparator() |
|
721 self.menu.AppendResMenu('DRVR') |
|
722 else: |
|
723 # Additem()'s tricks do not work for "apple" menu under Carbon |
|
724 self.menu.InsertMenuItem(abouttext, 0) |
|
725 self.items.append((abouttext, None, aboutcallback, None)) |
|
726 |
|
727 def dispatch(self, id, item, window, event): |
|
728 if item == 1: |
|
729 Menu.dispatch(self, id, item, window, event) |
|
730 elif MacOS.runtimemodel == 'ppc': |
|
731 name = self.menu.GetMenuItemText(item) |
|
732 OpenDeskAcc(name) |
|
733 |
|
734 class HelpMenu(Menu): |
|
735 def __init__(self, bar): |
|
736 # Note we don't call Menu.__init__, we do the necessary things by hand |
|
737 self.bar = bar |
|
738 self.menu, index = HMGetHelpMenu() |
|
739 self.id = self.menu.GetMenuID() |
|
740 bar.menus[self.id] = self |
|
741 # The next line caters for the entries the system already handles for us |
|
742 self.items = [None]*(index-1) |
|
743 self._parent = None |
|
744 |
|
745 |
|
746 class Window: |
|
747 """A single window belonging to an application""" |
|
748 |
|
749 def __init__(self, parent): |
|
750 self.wid = None |
|
751 self.parent = parent |
|
752 |
|
753 def open(self, bounds=(40, 40, 400, 400), resid=None): |
|
754 if resid <> None: |
|
755 self.wid = GetNewWindow(resid, -1) |
|
756 else: |
|
757 self.wid = NewWindow(bounds, self.__class__.__name__, 1, |
|
758 8, -1, 1, 0) # changed to proc id 8 to include zoom box. jvr |
|
759 self.do_postopen() |
|
760 |
|
761 def do_postopen(self): |
|
762 """Tell our parent we exist""" |
|
763 self.parent.appendwindow(self.wid, self) |
|
764 |
|
765 def close(self): |
|
766 self.do_postclose() |
|
767 |
|
768 def do_postclose(self): |
|
769 self.parent.removewindow(self.wid) |
|
770 self.parent = None |
|
771 self.wid = None |
|
772 |
|
773 def SetPort(self): |
|
774 # Convinience method |
|
775 SetPort(self.wid) |
|
776 |
|
777 def GetWindow(self): |
|
778 return self.wid |
|
779 |
|
780 def do_inDrag(self, partcode, window, event): |
|
781 where = event[3] |
|
782 window.DragWindow(where, self.draglimit) |
|
783 |
|
784 draglimit = screenbounds |
|
785 |
|
786 def do_inGoAway(self, partcode, window, event): |
|
787 where = event[3] |
|
788 if window.TrackGoAway(where): |
|
789 self.close() |
|
790 |
|
791 def do_inZoom(self, partcode, window, event): |
|
792 (what, message, when, where, modifiers) = event |
|
793 if window.TrackBox(where, partcode): |
|
794 window.ZoomWindow(partcode, 1) |
|
795 rect = window.GetWindowUserState() # so that zoom really works... jvr |
|
796 self.do_postresize(rect[2] - rect[0], rect[3] - rect[1], window) # jvr |
|
797 |
|
798 def do_inZoomIn(self, partcode, window, event): |
|
799 SetPort(window) # !!! |
|
800 self.do_inZoom(partcode, window, event) |
|
801 |
|
802 def do_inZoomOut(self, partcode, window, event): |
|
803 SetPort(window) # !!! |
|
804 self.do_inZoom(partcode, window, event) |
|
805 |
|
806 def do_inGrow(self, partcode, window, event): |
|
807 (what, message, when, where, modifiers) = event |
|
808 result = window.GrowWindow(where, self.growlimit) |
|
809 if result: |
|
810 height = (result>>16) & 0xffff # Hi word |
|
811 width = result & 0xffff # Lo word |
|
812 self.do_resize(width, height, window) |
|
813 |
|
814 growlimit = (50, 50, screenbounds[2] - screenbounds[0], screenbounds[3] - screenbounds[1]) # jvr |
|
815 |
|
816 def do_resize(self, width, height, window): |
|
817 l, t, r, b = self.wid.GetWindowPort().GetPortBounds() # jvr, forGrowIcon |
|
818 self.SetPort() # jvr |
|
819 self.wid.InvalWindowRect((r - SCROLLBARWIDTH + 1, b - SCROLLBARWIDTH + 1, r, b)) # jvr |
|
820 window.SizeWindow(width, height, 1) # changed updateFlag to true jvr |
|
821 self.do_postresize(width, height, window) |
|
822 |
|
823 def do_postresize(self, width, height, window): |
|
824 SetPort(window) |
|
825 self.wid.InvalWindowRect(window.GetWindowPort().GetPortBounds()) |
|
826 |
|
827 def do_inContent(self, partcode, window, event): |
|
828 # |
|
829 # If we're not frontmost, select ourselves and wait for |
|
830 # the activate event. |
|
831 # |
|
832 if MyFrontWindow() <> window: |
|
833 window.SelectWindow() |
|
834 return |
|
835 # We are. Handle the event. |
|
836 (what, message, when, where, modifiers) = event |
|
837 SetPort(window) |
|
838 local = GlobalToLocal(where) |
|
839 self.do_contentclick(local, modifiers, event) |
|
840 |
|
841 def do_contentclick(self, local, modifiers, event): |
|
842 if DEBUG: |
|
843 print 'Click in contents at %s, modifiers %s'%(local, modifiers) |
|
844 |
|
845 def do_rawupdate(self, window, event): |
|
846 if DEBUG: print "raw update for", window |
|
847 SetPort(window) |
|
848 window.BeginUpdate() |
|
849 self.do_update(window, event) |
|
850 window.EndUpdate() |
|
851 |
|
852 def do_update(self, window, event): |
|
853 if DEBUG: |
|
854 import time |
|
855 for i in range(8): |
|
856 time.sleep(0.1) |
|
857 InvertRgn(window.GetWindowPort().visRgn) |
|
858 FillRgn(window.GetWindowPort().visRgn, GetQDGlobalsGray()) |
|
859 else: |
|
860 EraseRgn(window.GetWindowPort().visRgn) |
|
861 |
|
862 def do_activate(self, activate, event): |
|
863 if DEBUG: print 'Activate %d for %s'%(activate, self.wid) |
|
864 |
|
865 class ControlsWindow(Window): |
|
866 |
|
867 def do_rawupdate(self, window, event): |
|
868 if DEBUG: print "raw update for", window |
|
869 SetPort(window) |
|
870 window.BeginUpdate() |
|
871 self.do_update(window, event) |
|
872 #DrawControls(window) # jvr |
|
873 UpdateControls(window, window.GetWindowPort().visRgn) # jvr |
|
874 window.DrawGrowIcon() |
|
875 window.EndUpdate() |
|
876 |
|
877 def do_controlhit(self, window, control, pcode, event): |
|
878 if DEBUG: print "control hit in", window, "on", control, "; pcode =", pcode |
|
879 |
|
880 def do_inContent(self, partcode, window, event): |
|
881 if MyFrontWindow() <> window: |
|
882 window.SelectWindow() |
|
883 return |
|
884 (what, message, when, where, modifiers) = event |
|
885 SetPort(window) # XXXX Needed? |
|
886 local = GlobalToLocal(where) |
|
887 pcode, control = FindControl(local, window) |
|
888 if pcode and control: |
|
889 self.do_rawcontrolhit(window, control, pcode, local, event) |
|
890 else: |
|
891 if DEBUG: print "FindControl(%s, %s) -> (%s, %s)" % \ |
|
892 (local, window, pcode, control) |
|
893 self.do_contentclick(local, modifiers, event) |
|
894 |
|
895 def do_rawcontrolhit(self, window, control, pcode, local, event): |
|
896 pcode = control.TrackControl(local) |
|
897 if pcode: |
|
898 self.do_controlhit(window, control, pcode, event) |
|
899 |
|
900 class ScrolledWindow(ControlsWindow): |
|
901 def __init__(self, parent): |
|
902 self.barx = self.bary = None |
|
903 self.barx_enabled = self.bary_enabled = 1 |
|
904 self.activated = 1 |
|
905 ControlsWindow.__init__(self, parent) |
|
906 |
|
907 def scrollbars(self, wantx=1, wanty=1): |
|
908 SetPort(self.wid) |
|
909 self.barx = self.bary = None |
|
910 self.barx_enabled = self.bary_enabled = 1 |
|
911 x0, y0, x1, y1 = self.wid.GetWindowPort().GetPortBounds() |
|
912 vx, vy = self.getscrollbarvalues() |
|
913 if vx is None: self.barx_enabled, vx = 0, 0 |
|
914 if vy is None: self.bary_enabled, vy = 0, 0 |
|
915 if wantx: |
|
916 rect = x0-1, y1-(SCROLLBARWIDTH-1), x1-(SCROLLBARWIDTH-2), y1+1 |
|
917 self.barx = NewControl(self.wid, rect, "", 1, vx, 0, 32767, 16, 0) |
|
918 if not self.barx_enabled: self.barx.HiliteControl(255) |
|
919 ## self.wid.InvalWindowRect(rect) |
|
920 if wanty: |
|
921 rect = x1-(SCROLLBARWIDTH-1), y0-1, x1+1, y1-(SCROLLBARWIDTH-2) |
|
922 self.bary = NewControl(self.wid, rect, "", 1, vy, 0, 32767, 16, 0) |
|
923 if not self.bary_enabled: self.bary.HiliteControl(255) |
|
924 ## self.wid.InvalWindowRect(rect) |
|
925 |
|
926 def do_postclose(self): |
|
927 self.barx = self.bary = None |
|
928 ControlsWindow.do_postclose(self) |
|
929 |
|
930 def do_activate(self, onoff, event): |
|
931 self.activated = onoff |
|
932 if onoff: |
|
933 if self.barx and self.barx_enabled: |
|
934 self.barx.ShowControl() # jvr |
|
935 if self.bary and self.bary_enabled: |
|
936 self.bary.ShowControl() # jvr |
|
937 else: |
|
938 if self.barx: |
|
939 self.barx.HideControl() # jvr; An inactive window should have *hidden* |
|
940 # scrollbars, not just dimmed (no matter what |
|
941 # BBEdit does... look at the Finder) |
|
942 if self.bary: |
|
943 self.bary.HideControl() # jvr |
|
944 self.wid.DrawGrowIcon() # jvr |
|
945 |
|
946 def do_postresize(self, width, height, window): |
|
947 l, t, r, b = self.wid.GetWindowPort().GetPortBounds() |
|
948 self.SetPort() |
|
949 if self.barx: |
|
950 self.barx.HideControl() # jvr |
|
951 self.barx.MoveControl(l-1, b-(SCROLLBARWIDTH-1)) |
|
952 self.barx.SizeControl((r-l)-(SCROLLBARWIDTH-3), SCROLLBARWIDTH) # jvr |
|
953 if self.bary: |
|
954 self.bary.HideControl() # jvr |
|
955 self.bary.MoveControl(r-(SCROLLBARWIDTH-1), t-1) |
|
956 self.bary.SizeControl(SCROLLBARWIDTH, (b-t)-(SCROLLBARWIDTH-3)) # jvr |
|
957 if self.barx: |
|
958 self.barx.ShowControl() # jvr |
|
959 self.wid.ValidWindowRect((l, b - SCROLLBARWIDTH + 1, r - SCROLLBARWIDTH + 2, b)) # jvr |
|
960 if self.bary: |
|
961 self.bary.ShowControl() # jvr |
|
962 self.wid.ValidWindowRect((r - SCROLLBARWIDTH + 1, t, r, b - SCROLLBARWIDTH + 2)) # jvr |
|
963 self.wid.InvalWindowRect((r - SCROLLBARWIDTH + 1, b - SCROLLBARWIDTH + 1, r, b)) # jvr, growicon |
|
964 |
|
965 |
|
966 def do_rawcontrolhit(self, window, control, pcode, local, event): |
|
967 if control == self.barx: |
|
968 which = 'x' |
|
969 elif control == self.bary: |
|
970 which = 'y' |
|
971 else: |
|
972 return 0 |
|
973 if pcode in (inUpButton, inDownButton, inPageUp, inPageDown): |
|
974 # We do the work for the buttons and grey area in the tracker |
|
975 dummy = control.TrackControl(local, self.do_controltrack) |
|
976 else: |
|
977 # but the thumb is handled here |
|
978 pcode = control.TrackControl(local) |
|
979 if pcode == inThumb: |
|
980 value = control.GetControlValue() |
|
981 print 'setbars', which, value #DBG |
|
982 self.scrollbar_callback(which, 'set', value) |
|
983 self.updatescrollbars() |
|
984 else: |
|
985 print 'funny part', pcode #DBG |
|
986 return 1 |
|
987 |
|
988 def do_controltrack(self, control, pcode): |
|
989 if control == self.barx: |
|
990 which = 'x' |
|
991 elif control == self.bary: |
|
992 which = 'y' |
|
993 else: |
|
994 return |
|
995 |
|
996 if pcode == inUpButton: |
|
997 what = '-' |
|
998 elif pcode == inDownButton: |
|
999 what = '+' |
|
1000 elif pcode == inPageUp: |
|
1001 what = '--' |
|
1002 elif pcode == inPageDown: |
|
1003 what = '++' |
|
1004 else: |
|
1005 return |
|
1006 self.scrollbar_callback(which, what, None) |
|
1007 self.updatescrollbars() |
|
1008 |
|
1009 def updatescrollbars(self): |
|
1010 SetPort(self.wid) |
|
1011 vx, vy = self.getscrollbarvalues() |
|
1012 if self.barx: |
|
1013 if vx is None: |
|
1014 self.barx.HiliteControl(255) |
|
1015 self.barx_enabled = 0 |
|
1016 else: |
|
1017 if not self.barx_enabled: |
|
1018 self.barx_enabled = 1 |
|
1019 if self.activated: |
|
1020 self.barx.HiliteControl(0) |
|
1021 self.barx.SetControlValue(vx) |
|
1022 if self.bary: |
|
1023 if vy is None: |
|
1024 self.bary.HiliteControl(255) |
|
1025 self.bary_enabled = 0 |
|
1026 else: |
|
1027 if not self.bary_enabled: |
|
1028 self.bary_enabled = 1 |
|
1029 if self.activated: |
|
1030 self.bary.HiliteControl(0) |
|
1031 self.bary.SetControlValue(vy) |
|
1032 |
|
1033 # Auxiliary function: convert standard text/image/etc coordinate |
|
1034 # to something palatable as getscrollbarvalues() return |
|
1035 def scalebarvalue(self, absmin, absmax, curmin, curmax): |
|
1036 if curmin <= absmin and curmax >= absmax: |
|
1037 return None |
|
1038 if curmin <= absmin: |
|
1039 return 0 |
|
1040 if curmax >= absmax: |
|
1041 return 32767 |
|
1042 perc = float(curmin-absmin)/float(absmax-absmin) |
|
1043 return int(perc*32767) |
|
1044 |
|
1045 # To be overridden: |
|
1046 |
|
1047 def getscrollbarvalues(self): |
|
1048 return 0, 0 |
|
1049 |
|
1050 def scrollbar_callback(self, which, what, value): |
|
1051 print 'scroll', which, what, value |
|
1052 |
|
1053 class DialogWindow(Window): |
|
1054 """A modeless dialog window""" |
|
1055 |
|
1056 def open(self, resid): |
|
1057 self.dlg = GetNewDialog(resid, -1) |
|
1058 self.wid = self.dlg.GetDialogWindow() |
|
1059 self.do_postopen() |
|
1060 |
|
1061 def close(self): |
|
1062 self.do_postclose() |
|
1063 |
|
1064 def do_postclose(self): |
|
1065 self.dlg = None |
|
1066 Window.do_postclose(self) |
|
1067 |
|
1068 def do_itemhit(self, item, event): |
|
1069 print 'Dialog %s, item %d hit'%(self.dlg, item) |
|
1070 |
|
1071 def do_rawupdate(self, window, event): |
|
1072 pass |
|
1073 |
|
1074 def ostypecode(x): |
|
1075 "Convert a long int to the 4-character code it really is" |
|
1076 s = '' |
|
1077 for i in range(4): |
|
1078 x, c = divmod(x, 256) |
|
1079 s = chr(c) + s |
|
1080 return s |
|
1081 |
|
1082 |
|
1083 class TestApp(Application): |
|
1084 |
|
1085 "This class is used by the test() function" |
|
1086 |
|
1087 def makeusermenus(self): |
|
1088 self.filemenu = m = Menu(self.menubar, "File") |
|
1089 self.saveitem = MenuItem(m, "Save", "S", self.save) |
|
1090 Separator(m) |
|
1091 self.optionsmenu = mm = SubMenu(m, "Options") |
|
1092 self.opt1 = CheckItem(mm, "Arguments", "A") |
|
1093 self.opt2 = CheckItem(mm, "Being hit on the head lessons", (kMenuOptionModifier, "A")) |
|
1094 self.opt3 = CheckItem(mm, "Complaints", (kMenuOptionModifier|kMenuNoCommandModifier, "A")) |
|
1095 Separator(m) |
|
1096 self.itemeh = MenuItem(m, "Enable Help", None, self.enablehelp) |
|
1097 self.itemdbg = MenuItem(m, "Debug", None, self.debug) |
|
1098 Separator(m) |
|
1099 self.quititem = MenuItem(m, "Quit", "Q", self.quit) |
|
1100 |
|
1101 def save(self, *args): |
|
1102 print "Save" |
|
1103 |
|
1104 def quit(self, *args): |
|
1105 raise self |
|
1106 |
|
1107 def enablehelp(self, *args): |
|
1108 hm = self.gethelpmenu() |
|
1109 self.nohelpitem = MenuItem(hm, "There isn't any", None, self.nohelp) |
|
1110 |
|
1111 def nohelp(self, *args): |
|
1112 print "I told you there isn't any!" |
|
1113 |
|
1114 def debug(self, *args): |
|
1115 import pdb |
|
1116 pdb.set_trace() |
|
1117 |
|
1118 |
|
1119 def test(): |
|
1120 "Test program" |
|
1121 app = TestApp() |
|
1122 app.mainloop() |
|
1123 |
|
1124 |
|
1125 if __name__ == '__main__': |
|
1126 test() |