Guido van Rossum | 8f4b6ad | 1995-04-05 09:18:35 +0000 | [diff] [blame] | 1 | "A sort of application framework for the Mac" |
| 2 | |
| 3 | |
| 4 | import MacOS |
| 5 | import traceback |
| 6 | |
| 7 | from addpack import addpack |
| 8 | addpack('Demo') |
| 9 | addpack('bgen') |
| 10 | addpack('ae') |
| 11 | #addpack('ctl') |
| 12 | addpack('dlg') |
| 13 | addpack('evt') |
| 14 | addpack('menu') |
| 15 | #addpack('qd') |
| 16 | #addpack('res') |
| 17 | #addpack('snd') |
| 18 | addpack('win') |
| 19 | |
| 20 | from AE import * |
| 21 | from AppleEvents import * |
| 22 | #from Ctl import * |
| 23 | #from Controls import * |
| 24 | from Dlg import * |
| 25 | from Dialogs import * |
| 26 | from Evt import * |
| 27 | from Events import * |
| 28 | from Menu import * |
| 29 | from Menus import * |
| 30 | #from Qd import * |
| 31 | #from QuickDraw import * |
| 32 | #from Res import * |
| 33 | #from Resources import * |
| 34 | #from Snd import * |
| 35 | #from Sound import * |
| 36 | from Win import * |
| 37 | from Windows import * |
| 38 | |
| 39 | import EasyDialogs |
| 40 | |
| 41 | kHighLevelEvent = 23 # Don't know what header file this should come from |
| 42 | |
| 43 | |
| 44 | # Map event 'what' field to strings |
| 45 | eventname = {} |
| 46 | eventname[1] = 'mouseDown' |
| 47 | eventname[2] = 'mouseUp' |
| 48 | eventname[3] = 'keyDown' |
| 49 | eventname[4] = 'keyUp' |
| 50 | eventname[5] = 'autoKey' |
| 51 | eventname[6] = 'updateEvt' |
| 52 | eventname[7] = 'diskEvt' |
| 53 | eventname[8] = 'activateEvt' |
| 54 | eventname[15] = 'osEvt' |
| 55 | eventname[23] = 'kHighLevelEvent' |
| 56 | |
| 57 | # Map part codes returned by WhichWindow() to strings |
| 58 | partname = {} |
| 59 | partname[0] = 'inDesk' |
| 60 | partname[1] = 'inMenuBar' |
| 61 | partname[2] = 'inSysWindow' |
| 62 | partname[3] = 'inContent' |
| 63 | partname[4] = 'inDrag' |
| 64 | partname[5] = 'inGrow' |
| 65 | partname[6] = 'inGoAway' |
| 66 | partname[7] = 'inZoomIn' |
| 67 | partname[8] = 'inZoomOut' |
| 68 | |
| 69 | # A rectangle that's bigger than the screen, |
| 70 | # but not so big that adding the screen size to it will cause 16-bit overflow |
| 71 | everywhere = (-16000, -16000, 16000, 16000) |
| 72 | |
| 73 | |
| 74 | class Application: |
| 75 | |
| 76 | "Application framework -- your application should be a derived class" |
| 77 | |
| 78 | def __init__(self): |
| 79 | self.makemenubar() |
| 80 | |
| 81 | def makemenubar(self): |
| 82 | self.menubar = MenuBar() |
| 83 | AppleMenu(self.menubar, self.getabouttext(), self.do_about) |
| 84 | self.makeusermenus() |
| 85 | |
| 86 | def getabouttext(self): |
| 87 | return "About %s..." % self.__class__.__name__ |
| 88 | |
| 89 | def do_about(self, id, item, window, event): |
| 90 | EasyDialogs.Message("Hello, world!" + "\015(%s)" % self.__class__.__name__) |
| 91 | |
| 92 | # The main event loop is broken up in several simple steps. |
| 93 | # This is done so you can override each individual part, |
| 94 | # if you have a need to do extra processing independent of the |
| 95 | # event type. |
| 96 | # Normally, however, you'd just define handlers for individual |
| 97 | # events. |
| 98 | # (XXX I'm not sure if using default parameter values is the right |
| 99 | # way to define the mask and wait time passed to WaitNextEvent.) |
| 100 | |
| 101 | def mainloop(self, mask = everyEvent, wait = 0): |
| 102 | saveyield = MacOS.EnableAppswitch(self.yield) |
| 103 | try: |
| 104 | while 1: |
| 105 | try: |
| 106 | self.do1event(mask, wait) |
| 107 | except (Application, SystemExit): |
| 108 | break |
| 109 | finally: |
| 110 | MacOS.EnableAppswitch(saveyield) |
| 111 | |
| 112 | yield = -1 |
| 113 | |
| 114 | def do1event(self, mask = everyEvent, wait = 0): |
| 115 | event = self.getevent(mask, wait) |
| 116 | if event: |
| 117 | self.dispatch(event) |
| 118 | |
| 119 | def getevent(self, mask = everyEvent, wait = 0): |
| 120 | ok, event = WaitNextEvent(mask, wait) |
| 121 | if ok: |
| 122 | return event |
| 123 | else: |
| 124 | return None |
| 125 | |
| 126 | def dispatch(self, event): |
| 127 | (what, message, when, where, modifiers) = event |
| 128 | if eventname.has_key(what): |
| 129 | name = "do_" + eventname[what] |
| 130 | else: |
| 131 | name = "do_%d" % what |
| 132 | try: |
| 133 | handler = getattr(self, name) |
| 134 | except AttributeError: |
| 135 | handler = self.do_unknownevent |
| 136 | handler(event) |
| 137 | |
| 138 | def do_mouseDown(self, event): |
| 139 | (what, message, when, where, modifiers) = event |
| 140 | partcode, window = FindWindow(where) |
| 141 | if partname.has_key(partcode): |
| 142 | name = "do_" + partname[partcode] |
| 143 | else: |
| 144 | name = "do_%d" % partcode |
| 145 | try: |
| 146 | handler = getattr(self, name) |
| 147 | except AttrinuteError: |
| 148 | handler = self.do_unknownpartcode |
| 149 | handler(partcode, window, event) |
| 150 | |
| 151 | def do_inDrag(self, partcode, window, event): |
| 152 | window.DragWindow(where, self.draglimit) |
| 153 | |
| 154 | draglimit = everywhere |
| 155 | |
| 156 | def do_inGoAway(self, partcode, window, event): |
| 157 | if window.TrackGoAway(where): |
| 158 | self.do_close(window) |
| 159 | |
| 160 | def do_close(self, window): |
| 161 | print "Should close window:", window |
| 162 | |
| 163 | def do_inZoom(self, partcode, window, event): |
| 164 | (what, message, when, where, modifiers) = event |
| 165 | if window.TrackBox(where, partcode): |
| 166 | window.ZoomWindow(partcode, 1) |
| 167 | |
| 168 | def do_inZoomIn(self, partcode, window, event): |
| 169 | SetPort(window) # !!! |
| 170 | self.do_inZoom(partcode, window, event) |
| 171 | |
| 172 | def do_inZoomOut(self, partcode, window, event): |
| 173 | SetPort(window) # !!! |
| 174 | self.do_inZoom(partcode, window, event) |
| 175 | |
| 176 | def do_inSysWindow(self, partcode, window, event): |
| 177 | print "SystemClick", event, window |
| 178 | # SystemClick(event, window) # XXX useless, window is None |
| 179 | |
| 180 | def do_inDesk(self, partcode, window, event): |
| 181 | print "inDesk" |
| 182 | # XXX what to do with it? |
| 183 | |
| 184 | def do_inMenuBar(self, partcode, window, event): |
| 185 | (what, message, when, where, modifiers) = event |
| 186 | result = MenuSelect(where) |
| 187 | id = (result>>16) & 0xffff # Hi word |
| 188 | item = result & 0xffff # Lo word |
| 189 | self.do_rawmenu(id, item, window, event) |
| 190 | |
| 191 | def do_rawmenu(self, id, item, window, event): |
| 192 | try: |
| 193 | self.do_menu(id, item, window, event) |
| 194 | finally: |
| 195 | HiliteMenu(0) |
| 196 | |
| 197 | def do_menu(self, id, item, window, event): |
| 198 | self.menubar.dispatch(id, item, window, event) |
| 199 | |
| 200 | def do_inGrow(self, partcode, window, event): |
| 201 | (what, message, when, where, modifiers) = event |
| 202 | result = window.GrowWindow(where, self.growlimit) |
| 203 | if result: |
| 204 | height = (result>>16) & 0xffff # Hi word |
| 205 | width = result & 0xffff # Lo word |
| 206 | self.do_resize(width, height, window) |
| 207 | |
| 208 | growlimit = everywhere |
| 209 | |
| 210 | def do_resize(self, width, height, window): |
| 211 | window.SizeWindow(width, height, 0) |
| 212 | self.do_postresize(width, height, window) |
| 213 | |
| 214 | def do_postresize(self, width, height, window): |
| 215 | SetPort(window) |
| 216 | InvalRect(everywhere) |
| 217 | |
| 218 | def do_inContent(self, partcode, window, event): |
| 219 | (what, message, when, where, modifiers) = event |
| 220 | local = GlobalToLocal(where) |
| 221 | ctltype, control = FindControl(local, window) |
| 222 | if ctltype and control: |
| 223 | pcode = control.TrackControl(local) |
| 224 | if pcode: |
| 225 | self.do_controlhit(window, control, pcode, event) |
| 226 | else: |
| 227 | print "FindControl(%s, %s) -> (%s, %s)" % \ |
| 228 | (local, window, ctltype, control) |
| 229 | |
| 230 | def do_controlhit(self, window, control, pcode, event): |
| 231 | print "control hit in", window, "on", control, "; pcode =", pcode |
| 232 | |
| 233 | def do_unknownpartcode(self, partcode, window, event): |
| 234 | (what, message, when, where, modifiers) = event |
| 235 | print "Mouse down at global:", where |
| 236 | print "\tUnknown part code:", partcode |
| 237 | |
| 238 | def do_keyDown(self, event): |
| 239 | self.do_key(event) |
| 240 | |
| 241 | def do_autoKey(self, event): |
| 242 | if not event[-1] & cmdKey: |
| 243 | self.do_key(event) |
| 244 | |
| 245 | def do_key(self, event): |
| 246 | (what, message, when, where, modifiers) = event |
| 247 | c = chr(message & charCodeMask) |
| 248 | if modifiers & cmdKey: |
| 249 | if c == '.': |
| 250 | raise self |
| 251 | else: |
| 252 | result = MenuKey(ord(c)) |
| 253 | id = (result>>16) & 0xffff # Hi word |
| 254 | item = result & 0xffff # Lo word |
| 255 | if id: |
| 256 | self.do_rawmenu(id, item, None, event) |
| 257 | elif c == 'w': |
| 258 | w = FrontWindow() |
| 259 | if w: |
| 260 | self.do_close(w) |
| 261 | else: |
| 262 | print 'Command-W without front window' |
| 263 | else: |
| 264 | print "Command-" +`c` |
| 265 | else: |
| 266 | self.do_char(c, event) |
| 267 | |
| 268 | def do_char(self, c, event): |
| 269 | print "Character", `c` |
| 270 | |
| 271 | def do_updateEvt(self, event): |
| 272 | print "do_update", |
| 273 | self.printevent(event) |
| 274 | window = FrontWindow() # XXX This is wrong! |
| 275 | if window: |
| 276 | self.do_rawupdate(window, event) |
| 277 | else: |
| 278 | print "no window for do_updateEvt" |
| 279 | |
| 280 | def do_rawupdate(self, window, event): |
| 281 | print "raw update for", window |
| 282 | window.BeginUpdate() |
| 283 | self.do_update(window, event) |
| 284 | DrawControls(window) |
| 285 | window.DrawGrowIcon() |
| 286 | window.EndUpdate() |
| 287 | |
| 288 | def do_update(self, window, event): |
| 289 | EraseRect(everywhere) |
| 290 | |
| 291 | def do_kHighLevelEvent(self, event): |
| 292 | (what, message, when, where, modifiers) = event |
| 293 | print "High Level Event:", |
| 294 | self.printevent(event) |
| 295 | try: |
| 296 | AEProcessAppleEvent(event) |
| 297 | except: |
| 298 | print "AEProcessAppleEvent error:" |
| 299 | traceback.print_exc() |
| 300 | |
| 301 | def do_unknownevent(self, event): |
| 302 | print "Unknown event:", |
| 303 | self.printevent(event) |
| 304 | |
| 305 | def printevent(self, event): |
| 306 | (what, message, when, where, modifiers) = event |
| 307 | nicewhat = `what` |
| 308 | if eventname.has_key(what): |
| 309 | nicewhat = eventname[what] |
| 310 | print nicewhat, |
| 311 | if what == kHighLevelEvent: |
| 312 | h, v = where |
| 313 | print `ostypecode(message)`, hex(when), `ostypecode(h | (v<<16))`, |
| 314 | else: |
| 315 | print hex(message), hex(when), where, |
| 316 | print hex(modifiers) |
| 317 | |
| 318 | |
| 319 | class MenuBar: |
| 320 | """Represent a set of menus in a menu bar. |
| 321 | |
| 322 | Interface: |
| 323 | |
| 324 | - (constructor) |
| 325 | - (destructor) |
| 326 | - addmenu |
| 327 | - addpopup (normally used internally) |
| 328 | - dispatch (called from Application) |
| 329 | """ |
| 330 | |
| 331 | nextid = 1 # Necessarily a class variable |
| 332 | |
| 333 | def getnextid(self): |
| 334 | id = self.nextid |
| 335 | self.nextid = id+1 |
| 336 | return id |
| 337 | |
| 338 | def __init__(self): |
| 339 | ClearMenuBar() |
| 340 | self.bar = GetMenuBar() |
| 341 | self.menus = {} |
| 342 | |
| 343 | def addmenu(self, title, after = 0): |
| 344 | id = self.getnextid() |
| 345 | m = NewMenu(id, title) |
| 346 | m.InsertMenu(after) |
| 347 | DrawMenuBar() |
| 348 | return id, m |
| 349 | |
| 350 | def addpopup(self, title = ''): |
| 351 | return self.addmenu(title, -1) |
| 352 | |
| 353 | def install(self): |
| 354 | self.bar.SetMenuBar() |
| 355 | DrawMenuBar() |
| 356 | |
| 357 | def dispatch(self, id, item, window, event): |
| 358 | if self.menus.has_key(id): |
| 359 | self.menus[id].dispatch(id, item, window, event) |
| 360 | else: |
| 361 | print "MenuBar.dispatch(%d, %d, %s, %s)" % \ |
| 362 | (id, item, window, event) |
| 363 | |
| 364 | |
| 365 | # XXX Need a way to get menus as resources and bind them to callbacks |
| 366 | |
| 367 | class Menu: |
| 368 | "One menu." |
| 369 | |
| 370 | def __init__(self, bar, title, after=0): |
| 371 | self.bar = bar |
| 372 | self.id, self.menu = self.bar.addmenu(title, after) |
| 373 | bar.menus[self.id] = self |
| 374 | self.items = [] |
| 375 | |
| 376 | def additem(self, label, shortcut=None, callback=None, kind=None): |
| 377 | self.menu.AppendMenu('x') # add a dummy string |
| 378 | self.items.append(label, shortcut, callback, kind) |
| 379 | item = len(self.items) |
| 380 | self.menu.SetItem(item, label) # set the actual text |
| 381 | if shortcut: |
| 382 | self.menu.SetItemCmd(item, ord(shortcut)) |
| 383 | return item |
| 384 | |
| 385 | def addcheck(self, label, shortcut=None, callback=None): |
| 386 | return self.additem(label, shortcut, callback, 'check') |
| 387 | |
| 388 | def addradio(self, label, shortcut=None, callback=None): |
| 389 | return self.additem(label, shortcut, callback, 'radio') |
| 390 | |
| 391 | def addseparator(self): |
| 392 | self.menu.AppendMenu('(-') |
| 393 | self.items.append('', None, None, 'separator') |
| 394 | |
| 395 | def addsubmenu(self, label, title=''): |
| 396 | sub = Menu(self.bar, title, -1) |
| 397 | item = self.additem(label, '\x1B', None, 'submenu') |
| 398 | self.menu.SetItemMark(item, sub.id) |
| 399 | return sub |
| 400 | |
| 401 | def dispatch(self, id, item, window, event): |
| 402 | title, shortcut, callback, type = self.items[item-1] |
| 403 | if callback: |
| 404 | callback(id, item, window, event) |
| 405 | |
| 406 | |
| 407 | class MenuItem: |
| 408 | def __init__(self, menu, title, shortcut=None, callback=None, kind=None): |
| 409 | self.item = menu.additem(title, shortcut, callback) |
| 410 | |
| 411 | class RadioItem(MenuItem): |
| 412 | def __init__(self, menu, title, shortcut=None, callback=None): |
| 413 | MenuItem.__init__(self, menu, title, shortcut, callback, 'radio') |
| 414 | |
| 415 | class CheckItem(MenuItem): |
| 416 | def __init__(self, menu, title, shortcut=None, callback=None): |
| 417 | MenuItem.__init__(self, menu, title, shortcut, callback, 'check') |
| 418 | |
| 419 | def Separator(menu): |
| 420 | menu.addseparator() |
| 421 | |
| 422 | def SubMenu(menu, label, title=''): |
| 423 | return menu.addsubmenu(label, title) |
| 424 | |
| 425 | |
| 426 | class AppleMenu(Menu): |
| 427 | |
| 428 | def __init__(self, bar, abouttext="About me...", aboutcallback=None): |
| 429 | Menu.__init__(self, bar, "\024") |
| 430 | self.additem(abouttext, None, aboutcallback) |
| 431 | self.addseparator() |
| 432 | self.menu.AddResMenu('DRVR') |
| 433 | |
| 434 | def dispatch(self, id, item, window, event): |
| 435 | if item == 1: |
| 436 | Menu.dispatch(self, id, item, window, event) |
| 437 | else: |
| 438 | name = self.menu.GetItem(item) |
| 439 | OpenDeskAcc(name) |
| 440 | |
| 441 | |
| 442 | def ostypecode(x): |
| 443 | "Convert a long int to the 4-character code it really is" |
| 444 | s = '' |
| 445 | for i in range(4): |
| 446 | x, c = divmod(x, 256) |
| 447 | s = chr(c) + s |
| 448 | return s |
| 449 | |
| 450 | |
| 451 | class TestApp(Application): |
| 452 | |
| 453 | "This class is used by the test() function" |
| 454 | |
| 455 | def makeusermenus(self): |
| 456 | self.filemenu = m = Menu(self.menubar, "File") |
| 457 | self.saveitem = MenuItem(m, "Save", "S", self.save) |
| 458 | Separator(m) |
| 459 | self.optionsmenu = mm = SubMenu(m, "Options") |
| 460 | self.opt1 = CheckItem(mm, "Arguments") |
| 461 | self.opt2 = CheckItem(mm, "Being hit on the head lessons") |
| 462 | self.opt3 = CheckItem(mm, "Complaints") |
| 463 | Separator(m) |
| 464 | self.quititem = MenuItem(m, "Quit", "Q", self.quit) |
| 465 | |
| 466 | def save(self, *args): |
| 467 | print "Save" |
| 468 | |
| 469 | def quit(self, *args): |
| 470 | raise self |
| 471 | |
| 472 | |
| 473 | def test(): |
| 474 | "Test program" |
| 475 | app = TestApp() |
| 476 | app.mainloop() |
| 477 | |
| 478 | |
| 479 | if __name__ == '__main__': |
| 480 | test() |