blob: c32651c334837bc66374236bcd6a405b5397ef5f [file] [log] [blame]
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +00001"A sort of application framework for the Mac"
2
Jack Jansen7a583361995-08-14 12:39:54 +00003DEBUG=0
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +00004
5import MacOS
6import traceback
7
8from addpack import addpack
Jack Jansene4b40381995-07-17 13:25:15 +00009addpack('Tools')
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +000010addpack('bgen')
11addpack('ae')
Jack Jansen7a583361995-08-14 12:39:54 +000012addpack('ctl')
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +000013addpack('dlg')
14addpack('evt')
15addpack('menu')
Jack Jansen7a583361995-08-14 12:39:54 +000016addpack('qd')
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +000017#addpack('res')
18#addpack('snd')
19addpack('win')
20
21from AE import *
22from AppleEvents import *
Jack Jansen7a583361995-08-14 12:39:54 +000023from Ctl import *
24from Controls import *
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +000025from Dlg import *
26from Dialogs import *
27from Evt import *
28from Events import *
29from Menu import *
30from Menus import *
Jack Jansen7a583361995-08-14 12:39:54 +000031from Qd import *
32from QuickDraw import *
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +000033#from Res import *
34#from Resources import *
35#from Snd import *
36#from Sound import *
37from Win import *
38from Windows import *
39
40import EasyDialogs
41
42kHighLevelEvent = 23 # Don't know what header file this should come from
43
44
45# Map event 'what' field to strings
46eventname = {}
47eventname[1] = 'mouseDown'
48eventname[2] = 'mouseUp'
49eventname[3] = 'keyDown'
50eventname[4] = 'keyUp'
51eventname[5] = 'autoKey'
52eventname[6] = 'updateEvt'
53eventname[7] = 'diskEvt'
54eventname[8] = 'activateEvt'
55eventname[15] = 'osEvt'
56eventname[23] = 'kHighLevelEvent'
57
58# Map part codes returned by WhichWindow() to strings
59partname = {}
60partname[0] = 'inDesk'
61partname[1] = 'inMenuBar'
62partname[2] = 'inSysWindow'
63partname[3] = 'inContent'
64partname[4] = 'inDrag'
65partname[5] = 'inGrow'
66partname[6] = 'inGoAway'
67partname[7] = 'inZoomIn'
68partname[8] = 'inZoomOut'
69
70# A rectangle that's bigger than the screen,
71# but not so big that adding the screen size to it will cause 16-bit overflow
72everywhere = (-16000, -16000, 16000, 16000)
73
74
75class Application:
76
77 "Application framework -- your application should be a derived class"
78
79 def __init__(self):
Jack Jansen7e0da901995-08-17 14:18:20 +000080 self._windows = {}
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +000081 self.makemenubar()
82
83 def makemenubar(self):
84 self.menubar = MenuBar()
85 AppleMenu(self.menubar, self.getabouttext(), self.do_about)
86 self.makeusermenus()
Jack Jansen7e0da901995-08-17 14:18:20 +000087
88 def appendwindow(self, wid, window):
89 self._windows[wid] = window
90
91 def removewindow(self, wid):
92 del self._windows[wid]
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +000093
94 def getabouttext(self):
95 return "About %s..." % self.__class__.__name__
96
97 def do_about(self, id, item, window, event):
98 EasyDialogs.Message("Hello, world!" + "\015(%s)" % self.__class__.__name__)
99
100 # The main event loop is broken up in several simple steps.
101 # This is done so you can override each individual part,
102 # if you have a need to do extra processing independent of the
103 # event type.
104 # Normally, however, you'd just define handlers for individual
105 # events.
106 # (XXX I'm not sure if using default parameter values is the right
107 # way to define the mask and wait time passed to WaitNextEvent.)
108
109 def mainloop(self, mask = everyEvent, wait = 0):
110 saveyield = MacOS.EnableAppswitch(self.yield)
111 try:
112 while 1:
113 try:
114 self.do1event(mask, wait)
115 except (Application, SystemExit):
116 break
117 finally:
118 MacOS.EnableAppswitch(saveyield)
119
120 yield = -1
121
122 def do1event(self, mask = everyEvent, wait = 0):
Jack Jansen13dc4f71995-08-31 13:38:01 +0000123 ok, event = self.getevent(mask, wait)
124 if IsDialogEvent(event):
125 if self.do_dialogevent(event):
126 return
127 if ok:
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000128 self.dispatch(event)
129
130 def getevent(self, mask = everyEvent, wait = 0):
131 ok, event = WaitNextEvent(mask, wait)
Jack Jansen13dc4f71995-08-31 13:38:01 +0000132 return ok, event
133
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000134 def dispatch(self, event):
135 (what, message, when, where, modifiers) = event
136 if eventname.has_key(what):
137 name = "do_" + eventname[what]
138 else:
139 name = "do_%d" % what
140 try:
141 handler = getattr(self, name)
142 except AttributeError:
143 handler = self.do_unknownevent
144 handler(event)
Jack Jansen7e0da901995-08-17 14:18:20 +0000145
146 def do_dialogevent(self, event):
147 gotone, window, item = DialogSelect(event)
148 if gotone:
149 if self._windows.has_key(window):
Jack Jansen13dc4f71995-08-31 13:38:01 +0000150 self._windows[window].do_itemhit(item, event)
Jack Jansen7e0da901995-08-17 14:18:20 +0000151 else:
152 print 'Dialog event for unknown dialog'
Jack Jansen13dc4f71995-08-31 13:38:01 +0000153 return 1
154 return 0
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000155
156 def do_mouseDown(self, event):
157 (what, message, when, where, modifiers) = event
Jack Jansen7e0da901995-08-17 14:18:20 +0000158 partcode, wid = FindWindow(where)
159
160 #
161 # Find the correct name.
162 #
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000163 if partname.has_key(partcode):
164 name = "do_" + partname[partcode]
165 else:
166 name = "do_%d" % partcode
Jack Jansen7e0da901995-08-17 14:18:20 +0000167
168 if wid == None:
169 # No window, or a non-python window
170 try:
171 handler = getattr(self, name)
172 except AttributeError:
173 # Not menubar or something, so assume someone
174 # else's window
175 MacOS.HandleEvent(event)
176 return
177 elif self._windows.has_key(wid):
178 # It is a window. Hand off to correct window.
179 window = self._windows[wid]
180 try:
181 handler = getattr(window, name)
182 except AttributeError:
183 handler = self.do_unknownpartcode
184 else:
185 # It is a python-toolbox window, but not ours.
186 handler = self.do_unknownwindow
187 handler(partcode, wid, event)
188
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000189 def do_inSysWindow(self, partcode, window, event):
Jack Jansen7a583361995-08-14 12:39:54 +0000190 MacOS.HandleEvent(event)
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000191
192 def do_inDesk(self, partcode, window, event):
Jack Jansen7a583361995-08-14 12:39:54 +0000193 MacOS.HandleEvent(event)
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000194
195 def do_inMenuBar(self, partcode, window, event):
196 (what, message, when, where, modifiers) = event
197 result = MenuSelect(where)
198 id = (result>>16) & 0xffff # Hi word
199 item = result & 0xffff # Lo word
200 self.do_rawmenu(id, item, window, event)
201
202 def do_rawmenu(self, id, item, window, event):
203 try:
204 self.do_menu(id, item, window, event)
205 finally:
206 HiliteMenu(0)
207
208 def do_menu(self, id, item, window, event):
209 self.menubar.dispatch(id, item, window, event)
210
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000211
212 def do_unknownpartcode(self, partcode, window, event):
213 (what, message, when, where, modifiers) = event
Jack Jansen7a583361995-08-14 12:39:54 +0000214 if DEBUG: print "Mouse down at global:", where
215 if DEBUG: print "\tUnknown part code:", partcode
Jack Jansen7e0da901995-08-17 14:18:20 +0000216 if DEBUG: print "\tEvent:", self.printevent(event)
217 MacOS.HandleEvent(event)
218
219 def do_unknownwindow(self, partcode, window, event):
220 if DEBUG: print 'Unknown window:', window
Jack Jansen7a583361995-08-14 12:39:54 +0000221 MacOS.HandleEvent(event)
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000222
223 def do_keyDown(self, event):
224 self.do_key(event)
225
226 def do_autoKey(self, event):
227 if not event[-1] & cmdKey:
228 self.do_key(event)
229
230 def do_key(self, event):
231 (what, message, when, where, modifiers) = event
232 c = chr(message & charCodeMask)
233 if modifiers & cmdKey:
234 if c == '.':
235 raise self
236 else:
237 result = MenuKey(ord(c))
238 id = (result>>16) & 0xffff # Hi word
239 item = result & 0xffff # Lo word
240 if id:
241 self.do_rawmenu(id, item, None, event)
Jack Jansen7e0da901995-08-17 14:18:20 +0000242# elif c == 'w':
243# w = FrontWindow()
244# if w:
245# self.do_close(w)
246# else:
247# if DEBUG: print 'Command-W without front window'
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000248 else:
Jack Jansen7a583361995-08-14 12:39:54 +0000249 if DEBUG: print "Command-" +`c`
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000250 else:
Jack Jansen7e0da901995-08-17 14:18:20 +0000251 # See whether the front window wants it
252 w = FrontWindow()
253 if w and self._windows.has_key(w):
254 window = self._windows[w]
255 try:
256 do_char = window.do_char
257 except AttributeError:
258 do_char = self.do_char
259 do_char(c, event)
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000260
261 def do_char(self, c, event):
Jack Jansen7a583361995-08-14 12:39:54 +0000262 if DEBUG: print "Character", `c`
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000263
264 def do_updateEvt(self, event):
Jack Jansen7e0da901995-08-17 14:18:20 +0000265 (what, message, when, where, modifiers) = event
266 wid = WhichWindow(message)
267 if wid and self._windows.has_key(wid):
268 window = self._windows[wid]
269 window.do_rawupdate(wid, event)
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000270 else:
Jack Jansen7a583361995-08-14 12:39:54 +0000271 MacOS.HandleEvent(event)
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000272
Jack Jansen7e0da901995-08-17 14:18:20 +0000273 def do_activateEvt(self, event):
274 (what, message, when, where, modifiers) = event
275 wid = WhichWindow(message)
276 if wid and self._windows.has_key(wid):
277 window = self._windows[wid]
278 window.do_activate(modifiers & 1, event)
279 else:
280 MacOS.HandleEvent(event)
281
282 def do_osEvt(self, event):
283 (what, message, when, where, modifiers) = event
284 which = (message >> 24) & 0xff
285 if which == 1: # suspend/resume
286 self.do_suspendresume(event)
287 else:
288 if DEBUG:
289 print 'unknown osEvt:',
290 self.printevent(event)
291
292 def do_suspendresume(self, event):
293 # Is this a good idea???
294 (what, message, when, where, modifiers) = event
295 w = FrontWindow()
296 if w:
297 nev = (activateEvt, w, when, where, message&1)
298 self.do_activateEvt(self, nev)
299
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000300 def do_kHighLevelEvent(self, event):
301 (what, message, when, where, modifiers) = event
Jack Jansen7a583361995-08-14 12:39:54 +0000302 if DEBUG:
303 print "High Level Event:",
304 self.printevent(event)
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000305 try:
306 AEProcessAppleEvent(event)
307 except:
308 print "AEProcessAppleEvent error:"
309 traceback.print_exc()
310
311 def do_unknownevent(self, event):
Jack Jansen7e0da901995-08-17 14:18:20 +0000312 if DEBUG:
313 print "Unhandled event:",
314 self.printevent(event)
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000315
316 def printevent(self, event):
317 (what, message, when, where, modifiers) = event
318 nicewhat = `what`
319 if eventname.has_key(what):
320 nicewhat = eventname[what]
321 print nicewhat,
322 if what == kHighLevelEvent:
323 h, v = where
324 print `ostypecode(message)`, hex(when), `ostypecode(h | (v<<16))`,
325 else:
326 print hex(message), hex(when), where,
327 print hex(modifiers)
328
329
330class MenuBar:
331 """Represent a set of menus in a menu bar.
332
333 Interface:
334
335 - (constructor)
336 - (destructor)
337 - addmenu
338 - addpopup (normally used internally)
339 - dispatch (called from Application)
340 """
341
342 nextid = 1 # Necessarily a class variable
343
344 def getnextid(self):
345 id = self.nextid
346 self.nextid = id+1
347 return id
348
349 def __init__(self):
350 ClearMenuBar()
351 self.bar = GetMenuBar()
352 self.menus = {}
353
354 def addmenu(self, title, after = 0):
355 id = self.getnextid()
356 m = NewMenu(id, title)
357 m.InsertMenu(after)
358 DrawMenuBar()
359 return id, m
360
361 def addpopup(self, title = ''):
362 return self.addmenu(title, -1)
363
364 def install(self):
365 self.bar.SetMenuBar()
366 DrawMenuBar()
367
368 def dispatch(self, id, item, window, event):
369 if self.menus.has_key(id):
370 self.menus[id].dispatch(id, item, window, event)
371 else:
Jack Jansen7a583361995-08-14 12:39:54 +0000372 if DEBUG: print "MenuBar.dispatch(%d, %d, %s, %s)" % \
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000373 (id, item, window, event)
374
375
376# XXX Need a way to get menus as resources and bind them to callbacks
377
378class Menu:
379 "One menu."
380
381 def __init__(self, bar, title, after=0):
382 self.bar = bar
383 self.id, self.menu = self.bar.addmenu(title, after)
384 bar.menus[self.id] = self
385 self.items = []
386
387 def additem(self, label, shortcut=None, callback=None, kind=None):
388 self.menu.AppendMenu('x') # add a dummy string
389 self.items.append(label, shortcut, callback, kind)
390 item = len(self.items)
Jack Jansene4b40381995-07-17 13:25:15 +0000391 self.menu.SetMenuItemText(item, label) # set the actual text
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000392 if shortcut:
393 self.menu.SetItemCmd(item, ord(shortcut))
394 return item
395
396 def addcheck(self, label, shortcut=None, callback=None):
397 return self.additem(label, shortcut, callback, 'check')
398
399 def addradio(self, label, shortcut=None, callback=None):
400 return self.additem(label, shortcut, callback, 'radio')
401
402 def addseparator(self):
403 self.menu.AppendMenu('(-')
404 self.items.append('', None, None, 'separator')
405
406 def addsubmenu(self, label, title=''):
407 sub = Menu(self.bar, title, -1)
408 item = self.additem(label, '\x1B', None, 'submenu')
409 self.menu.SetItemMark(item, sub.id)
410 return sub
411
412 def dispatch(self, id, item, window, event):
413 title, shortcut, callback, type = self.items[item-1]
414 if callback:
415 callback(id, item, window, event)
416
417
418class MenuItem:
419 def __init__(self, menu, title, shortcut=None, callback=None, kind=None):
420 self.item = menu.additem(title, shortcut, callback)
421
422class RadioItem(MenuItem):
423 def __init__(self, menu, title, shortcut=None, callback=None):
424 MenuItem.__init__(self, menu, title, shortcut, callback, 'radio')
425
426class CheckItem(MenuItem):
427 def __init__(self, menu, title, shortcut=None, callback=None):
428 MenuItem.__init__(self, menu, title, shortcut, callback, 'check')
429
430def Separator(menu):
431 menu.addseparator()
432
433def SubMenu(menu, label, title=''):
434 return menu.addsubmenu(label, title)
435
436
437class AppleMenu(Menu):
438
439 def __init__(self, bar, abouttext="About me...", aboutcallback=None):
440 Menu.__init__(self, bar, "\024")
441 self.additem(abouttext, None, aboutcallback)
442 self.addseparator()
Jack Jansene4b40381995-07-17 13:25:15 +0000443 self.menu.AppendResMenu('DRVR')
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000444
445 def dispatch(self, id, item, window, event):
446 if item == 1:
447 Menu.dispatch(self, id, item, window, event)
448 else:
449 name = self.menu.GetItem(item)
450 OpenDeskAcc(name)
451
Jack Jansen7e0da901995-08-17 14:18:20 +0000452class Window:
453 """A single window belonging to an application"""
454
455 def __init__(self, parent):
456 self.wid = None
457 self.parent = parent
458
459 def open(self):
460 self.wid = NewWindow((40, 40, 400, 400), self.__class__.__name__, 1,
461 0, -1, 1, 0)
462 self.do_postopen()
463
464 def do_postopen(self):
465 """Tell our parent we exist"""
466 self.parent.appendwindow(self.wid, self)
467
468 def close(self):
469 pass
470 self.do_postclose()
471
472 def do_postclose(self):
473 self.parent.removewindow(self.wid)
474 self.parent = None
475 self.wid = None
476
477 def do_inDrag(self, partcode, window, event):
478 where = event[3]
479 window.DragWindow(where, self.draglimit)
480
481 draglimit = everywhere
482
483 def do_inGoAway(self, partcode, window, event):
484 where = event[3]
485 if window.TrackGoAway(where):
486 self.close()
487
488 def do_inZoom(self, partcode, window, event):
489 (what, message, when, where, modifiers) = event
490 if window.TrackBox(where, partcode):
491 window.ZoomWindow(partcode, 1)
492
493 def do_inZoomIn(self, partcode, window, event):
494 SetPort(window) # !!!
495 self.do_inZoom(partcode, window, event)
496
497 def do_inZoomOut(self, partcode, window, event):
498 SetPort(window) # !!!
499 self.do_inZoom(partcode, window, event)
500
501 def do_inGrow(self, partcode, window, event):
502 (what, message, when, where, modifiers) = event
503 result = window.GrowWindow(where, self.growlimit)
504 if result:
505 height = (result>>16) & 0xffff # Hi word
506 width = result & 0xffff # Lo word
507 self.do_resize(width, height, window)
508
509 growlimit = everywhere
510
511 def do_resize(self, width, height, window):
512 window.SizeWindow(width, height, 0)
513 self.do_postresize(width, height, window)
514
515 def do_postresize(self, width, height, window):
516 SetPort(window)
517 InvalRect(everywhere)
518
519 def do_inContent(self, partcode, window, event):
520 #
521 # If we're not frontmost, select ourselves and wait for
522 # the activate event.
523 #
524 if FrontWindow() <> window:
525 window.SelectWindow()
526 return
527 # We are. Handle the event.
528 (what, message, when, where, modifiers) = event
529 SetPort(window)
530 local = GlobalToLocal(where)
531 self.do_contentclick(local, modifiers, event)
532
533 def do_contentclick(self, local, modifiers, event):
534 print 'Click in contents at %s, modifiers %s'%(local, modifiers)
535
536 def do_rawupdate(self, window, event):
537 if DEBUG: print "raw update for", window
538 window.BeginUpdate()
539 self.do_update(window, event)
540 window.EndUpdate()
541
542 def do_update(self, window, event):
543 EraseRect(everywhere)
544
545 def do_activate(self, activate, event):
546 if DEBUG: print 'Activate %d for %s'%(activate, self.wid)
547
548class ControlsWindow(Window):
549
550 def do_rawupdate(self, window, event):
551 if DEBUG: print "raw update for", window
552 window.BeginUpdate()
553 self.do_update(window, event)
554 DrawControls(window)
555 window.DrawGrowIcon()
556 window.EndUpdate()
557
558 def do_controlhit(self, window, control, pcode, event):
559 if DEBUG: print "control hit in", window, "on", control, "; pcode =", pcode
560
561 def do_inContent(self, partcode, window, event):
562 (what, message, when, where, modifiers) = event
563 local = GlobalToLocal(where)
564 ctltype, control = FindControl(local, window)
565 if ctltype and control:
566 pcode = control.TrackControl(local)
567 if pcode:
568 self.do_controlhit(window, control, pcode, event)
569 else:
570 if DEBUG: print "FindControl(%s, %s) -> (%s, %s)" % \
571 (local, window, ctltype, control)
572
573class DialogWindow(Window):
574 """A modeless dialog window"""
575
576 def open(self, resid):
577 self.wid = GetNewDialog(resid, -1)
578 self.do_postopen()
579
580 def close(self):
Jack Jansen7e0da901995-08-17 14:18:20 +0000581 self.do_postclose()
582
583 def do_itemhit(self, item, event):
584 print 'Dialog %s, item %d hit'%(self.wid, item)
585
586 def do_rawupdate(self, window, event):
587 pass
Guido van Rossum8f4b6ad1995-04-05 09:18:35 +0000588
589def ostypecode(x):
590 "Convert a long int to the 4-character code it really is"
591 s = ''
592 for i in range(4):
593 x, c = divmod(x, 256)
594 s = chr(c) + s
595 return s
596
597
598class TestApp(Application):
599
600 "This class is used by the test() function"
601
602 def makeusermenus(self):
603 self.filemenu = m = Menu(self.menubar, "File")
604 self.saveitem = MenuItem(m, "Save", "S", self.save)
605 Separator(m)
606 self.optionsmenu = mm = SubMenu(m, "Options")
607 self.opt1 = CheckItem(mm, "Arguments")
608 self.opt2 = CheckItem(mm, "Being hit on the head lessons")
609 self.opt3 = CheckItem(mm, "Complaints")
610 Separator(m)
611 self.quititem = MenuItem(m, "Quit", "Q", self.quit)
612
613 def save(self, *args):
614 print "Save"
615
616 def quit(self, *args):
617 raise self
618
619
620def test():
621 "Test program"
622 app = TestApp()
623 app.mainloop()
624
625
626if __name__ == '__main__':
627 test()