blob: 4c16794e454df64c28821418f15586ae52fa600b [file] [log] [blame]
Just van Rossum40f9b7b1999-01-30 22:39:17 +00001import Qd
2import Win
3import Evt
4import Fm
5import FrameWork
6import Windows
7import Events
8import Wbase
9import Dlg
10import MacOS
11import Menu
12import struct
13import traceback
14
15from types import *
16
17
18class Window(FrameWork.Window, Wbase.SelectableWidget):
19
20 windowkind = Windows.documentProc
21
22 def __init__(self, possize, title = "", minsize = None, maxsize = None, tabbable = 1, show = 1):
23 import W
24 W.SelectableWidget.__init__(self, possize)
25 self._globalbounds = l, t, r, b = self.getwindowbounds(possize, minsize)
26 self._bounds = (0, 0, r - l, b - t)
27 self._tabchain = []
28 self._currentwidget = None
29 self.title = title
30 self._parentwindow = self
31 self._tabbable = tabbable
32 self._defaultbutton = None
33 self._drawwidgetbounds = 0
34 self._show = show
35 self._lastrollover = None
36 # XXX the following is not really compatible with the
37 # new (system >= 7.5) window procs.
38 if minsize:
39 self._hasgrowbox = 1
40 self.windowkind = self.windowkind | 8
41 l, t = minsize
42 if maxsize:
43 r, b = maxsize[0] + 1, maxsize[1] + 1
44 else:
45 r, b = 32000, 32000
46 self.growlimit = (l, t, r, b)
47 else:
48 self._hasgrowbox = 0
49 if (self.windowkind == 0 or self.windowkind >= 8) and self.windowkind < 1000:
50 self.windowkind = self.windowkind | 4
51 FrameWork.Window.__init__(self, W.getapplication())
52
53 def gettitle(self):
54 return self.title
55
56 def settitle(self, title):
57 self.title = title
58 if self.wid:
59 self.wid.SetWTitle(title)
60
61 def getwindowbounds(self, size, minsize = None):
62 return windowbounds(size, minsize)
63
64 def getcurrentwidget(self):
65 return self._currentwidget
66
67 def show(self, onoff):
68 if onoff:
69 self.wid.ShowWindow()
70 else:
71 self.wid.HideWindow()
72
73 def isvisible(self):
74 return self.wid.IsWindowVisible()
75
76 def getbounds(self):
77 if 0: #self.isvisible():
78 self.wid.GetWindowContentRgn(scratchRegion)
79 self._globalbounds = GetRgnBounds(scratchRegion)
80 return self._globalbounds
81
82 def select(self):
83 self.wid.SelectWindow()
84 # not sure if this is the best place, I need it when
85 # an editor gets selected, and immediately scrolled
86 # to a certain line, waste scroll assumes everything
87 # to be in tact.
88 self.do_rawupdate(self.wid, "DummyEvent")
89
90 def open(self):
91 self.wid = Win.NewCWindow(self._globalbounds, self.title, self._show,
92 self.windowkind, -1, 1, 0)
93 self.SetPort()
94 fnum = Fm.GetFNum("Python-Sans")
95 if fnum == 0:
96 fnum = Fm.GetFNum("Geneva")
97 Qd.TextFont(fnum) # XXX font&size from a function?
98 Qd.TextSize(9) # XXX font&size from a function?
99 if self._bindings.has_key("<open>"):
100 callback = self._bindings["<open>"]
101 callback()
102 for w in self._widgets:
103 w.forall_frombottom("open")
104 self._maketabchain()
105 if self._tabchain:
106 self._tabchain[0].select(1)
107 if self._tabbable:
108 self.bind('tab', self.nextwidget)
109 self.bind('shifttab', self.previouswidget)
110 self.do_postopen()
111
112 def close(self):
113 if not self.wid:
114 return # we are already closed
115 if self._bindings.has_key("<close>"):
116 callback = self._bindings["<close>"]
117 try:
118 rv = callback()
119 except:
120 print 'error in <close> callback'
121 traceback.print_exc()
122 else:
123 if rv:
124 return rv
125 #for key in self._widgetsdict.keys():
126 # self._removewidget(key)
127 self.forall_butself("close")
128 Wbase.SelectableWidget.close(self)
129 self._tabchain = []
130 self._currentwidget = None
131 self.wid.HideWindow()
132 self.do_postclose()
133
134 def domenu_close(self, *args):
135 self.close()
136
137 def move(self, x, y = None):
138 """absolute move"""
139 if y == None:
140 x, y = x
141 self.wid.MoveWindow(x, y, 0)
142
143 def resize(self, x, y = None):
144 if y == None:
145 x, y = x
146 if self._hasgrowbox:
147 self.SetPort()
148 Win.InvalRect(self.getgrowrect())
149 self.wid.SizeWindow(x, y, 1)
150 self._calcbounds()
151
152 def test(self, point):
153 return 1
154
155 def draw(self, visRgn = None):
156 if self._hasgrowbox:
157 self.tempcliprect(self.getgrowrect())
158 self.wid.DrawGrowIcon()
159 self.restoreclip()
160
161 def idle(self, *args):
162 self.SetPort()
163 point = Evt.GetMouse()
164 widget = self.findwidget(point, 0)
165 if self._bindings.has_key("<idle>"):
166 callback = self._bindings["<idle>"]
167 if callback():
168 return
169 if self._currentwidget is not None and hasattr(self._currentwidget, "idle"):
170 if self._currentwidget._bindings.has_key("<idle>"):
171 callback = self._currentwidget._bindings["<idle>"]
172 if callback():
173 return
174 if self._currentwidget.idle():
175 return
176 if widget is not None and hasattr(widget, "rollover"):
177 if 1: #self._lastrollover <> widget:
178 if self._lastrollover:
179 self._lastrollover.rollover(point, 0)
180 self._lastrollover = widget
181 self._lastrollover.rollover(point, 1)
182 else:
183 if self._lastrollover:
184 self._lastrollover.rollover(point, 0)
185 self._lastrollover = None
186 Wbase.SetCursor("arrow")
187
188 def xxx___select(self, widget):
189 if self._currentwidget == widget:
190 return
191 if self._bindings.has_key("<select>"):
192 callback = self._bindings["<select>"]
193 if callback(widget):
194 return
195 if widget is None:
196 if self._currentwidget is not None:
197 self._currentwidget.select(0)
198 elif type(widget) == InstanceType and widget._selectable:
199 widget.select(1)
200 elif widget == -1 or widget == 1:
201 if len(self._tabchain) <= 1:
202 return
203 temp = self._tabchain[(self._tabchain.index(self._currentwidget) + widget) % len(self._tabchain)]
204 temp.select(1)
205 else:
206 raise TypeError, "Widget is not selectable"
207
208 def setdefaultbutton(self, newdefaultbutton = None, *keys):
209 if newdefaultbutton == self._defaultbutton:
210 return
211 if self._defaultbutton:
212 self._defaultbutton._setdefault(0)
213 if not newdefaultbutton:
214 self.bind("return", None)
215 self.bind("enter", None)
216 return
217 import Wcontrols
218 if not isinstance(newdefaultbutton, Wcontrols.Button):
219 raise TypeError, "widget is not a button"
220 self._defaultbutton = newdefaultbutton
221 self._defaultbutton._setdefault(1)
222 if not keys:
223 self.bind("return", self._defaultbutton.push)
224 self.bind("enter", self._defaultbutton.push)
225 else:
226 for key in keys:
227 self.bind(key, self._defaultbutton.push)
228
229 def nextwidget(self):
230 self.xxx___select(1)
231
232 def previouswidget(self):
233 self.xxx___select(-1)
234
235 def drawwidgetbounds(self, onoff):
236 self._drawwidgetbounds = onoff
237 self.SetPort()
238 Win.InvalRect(self._bounds)
239
240 def _drawbounds(self):
241 pass
242
243 def _maketabchain(self):
244 # XXX This has to change, it's no good when we are adding or deleting widgets.
245 # XXX Perhaps we shouldn't keep a "tabchain" at all.
246 self._hasselframes = 0
247 self._collectselectablewidgets(self._widgets)
248 if self._hasselframes and len(self._tabchain) > 1:
249 self._hasselframes = 1
250 else:
251 self._hasselframes = 0
252
253 def _collectselectablewidgets(self, widgets):
254 import W
255 for w in widgets:
256 if w._selectable:
257 self._tabchain.append(w)
258 if isinstance(w, W.List):
259 self._hasselframes = 1
260 self._collectselectablewidgets(w._widgets)
261
262 def _calcbounds(self):
263 self._possize = self.wid.GetWindowPort().portRect[2:]
264 w, h = self._possize
265 self._bounds = (0, 0, w, h)
266 self.wid.GetWindowContentRgn(scratchRegion)
267 l, t, r, b = GetRgnBounds(scratchRegion)
268 self._globalbounds = l, t, l + w, t + h
269 for w in self._widgets:
270 w._calcbounds()
271
272 # FrameWork override methods
273 def do_inDrag(self, partcode, window, event):
274 where = event[3]
275 self.wid.GetWindowContentRgn(scratchRegion)
276 was_l, was_t, r, b = GetRgnBounds(scratchRegion)
277 window.DragWindow(where, self.draglimit)
278 self.wid.GetWindowContentRgn(scratchRegion)
279 is_l, is_t, r, b = GetRgnBounds(scratchRegion)
280 self._globalbounds = Qd.OffsetRect(self._globalbounds,
281 is_l - was_l, is_t - was_t)
282
283 def do_char(self, char, event):
284 import Wkeys
285 (what, message, when, where, modifiers) = event
286 key = char
287 if Wkeys.keynames.has_key(key):
288 key = Wkeys.keynames[char]
289 if modifiers & Events.shiftKey:
290 key = 'shift' + key
291 if modifiers & Events.cmdKey:
292 key = 'cmd' + key
293 if modifiers & Events.controlKey:
294 key = 'control' + key
295 if self._bindings.has_key("<key>"):
296 callback = self._bindings["<key>"]
297 if Wbase.CallbackCall(callback, 0, char, event):
298 return
299 if self._bindings.has_key(key):
300 callback = self._bindings[key]
301 Wbase.CallbackCall(callback, 0, char, event)
302 elif self._currentwidget is not None:
303 if self._currentwidget._bindings.has_key(key):
304 callback = self._currentwidget._bindings[key]
305 Wbase.CallbackCall(callback, 0, char, event)
306 else:
307 if self._currentwidget._bindings.has_key("<key>"):
308 callback = self._currentwidget._bindings["<key>"]
309 if Wbase.CallbackCall(callback, 0, char, event):
310 return
311 self._currentwidget.key(char, event)
312
313 def do_contentclick(self, point, modifiers, event):
314 widget = self.findwidget(point)
315 if widget is not None:
316 if self._bindings.has_key("<click>"):
317 callback = self._bindings["<click>"]
318 if Wbase.CallbackCall(callback, 0, point, modifiers):
319 return
320 if widget._bindings.has_key("<click>"):
321 callback = widget._bindings["<click>"]
322 if Wbase.CallbackCall(callback, 0, point, modifiers):
323 return
324 if widget._selectable:
325 widget.select(1, 1)
326 widget.click(point, modifiers)
327
328 def do_update(self, window, event):
329 Qd.EraseRgn(window.GetWindowPort().visRgn)
330 self.forall_frombottom("draw", window.GetWindowPort().visRgn)
331 if self._drawwidgetbounds:
332 self.forall_frombottom("_drawbounds")
333
334 def do_activate(self, onoff, event):
335 if not onoff:
336 if self._lastrollover:
337 self._lastrollover.rollover((0, 0), 0)
338 self._lastrollover = None
339 self.SetPort()
340 self.forall("activate", onoff)
341 self.draw()
342
343 def do_postresize(self, width, height, window):
344 Win.InvalRect(self.getgrowrect())
345 self._calcbounds()
346
347 def do_inGoAway(self, partcode, window, event):
348 where = event[3]
349 closeall = event[4] & Events.optionKey
350 if window.TrackGoAway(where):
351 if not closeall:
352 self.close()
353 else:
354 for window in self.parent._windows.values():
355 rv = window.close()
356 if rv and rv > 0:
357 return
358
359 # utilities
360 def tempcliprect(self, tempcliprect):
361 tempclip = Qd.NewRgn()
362 Qd.RectRgn(tempclip, tempcliprect)
363 self.tempclip(tempclip)
364 Qd.DisposeRgn(tempclip)
365
366 def tempclip(self, tempclip):
367 if not hasattr(self, "saveclip"):
368 self.saveclip = []
369 saveclip = Qd.NewRgn()
370 Qd.GetClip(saveclip)
371 self.saveclip.append(saveclip)
372 Qd.SetClip(tempclip)
373
374 def restoreclip(self):
375 Qd.SetClip(self.saveclip[-1])
376 Qd.DisposeRgn(self.saveclip[-1])
377 del self.saveclip[-1]
378
379 def getgrowrect(self):
380 l, t, r, b = self.wid.GetWindowPort().portRect
381 return (r - 15, b - 15, r, b)
382
383 def has_key(self, key):
384 return self._widgetsdict.has_key(key)
385
386 def __getattr__(self, attr):
387 global _successcount, _failcount, _magiccount
388 if self._widgetsdict.has_key(attr):
389 _successcount = _successcount + 1
390 return self._widgetsdict[attr]
391 if self._currentwidget is None or (attr[:7] <> 'domenu_' and
392 attr[:4] <> 'can_' and attr <> 'insert'):
393 _failcount = _failcount + 1
394 raise AttributeError, attr
395 # special case: if a domenu_xxx, can_xxx or insert method is asked for,
396 # see if the active widget supports it
397 _magiccount = _magiccount + 1
398 return getattr(self._currentwidget, attr)
399
400_successcount = 0
401_failcount = 0
402_magiccount = 0
403
404class Dialog(Window):
405
406 windowkind = Windows.movableDBoxProc
407
408 # this __init__ seems redundant, but it's not: it has less args
409 def __init__(self, possize, title = ""):
410 Window.__init__(self, possize, title)
411
412 def can_close(self, *args):
413 return 0
414
415 def getwindowbounds(self, size, minsize = None):
416 screenbounds = sl, st, sr, sb = Qd.qd.screenBits.bounds
417 w, h = size
418 l = sl + (sr - sl - w) / 2
419 t = st + (sb - st - h) / 3
420 return l, t, l + w, t + h
421
422
423class ModalDialog(Dialog):
424
425 def __init__(self, possize, title = ""):
426 Dialog.__init__(self, possize, title)
427 if title:
428 self.windowkind = Windows.movableDBoxProc
429 else:
430 self.windowkind = Windows.dBoxProc
431
432 def open(self):
433 import W
434 Dialog.open(self)
435 self.app = W.getapplication()
436 self.done = 0
437 Menu.HiliteMenu(0)
438 app = self.parent
439 app.enablemenubar(0)
440 try:
441 self.mainloop()
442 finally:
443 app.enablemenubar(1)
444
445 def close(self):
446 if not self.wid:
447 return # we are already closed
448 self.done = 1
449 del self.app
450 Dialog.close(self)
451
452 def mainloop(self):
453 saveyield = MacOS.EnableAppswitch(-1)
454 while not self.done:
455 #self.do1event()
456 self.do1event( Events.keyDownMask +
457 Events.autoKeyMask +
458 Events.activMask +
459 Events.updateMask +
460 Events.mDownMask +
461 Events.mUpMask,
462 10)
463 MacOS.EnableAppswitch(saveyield)
464
465 def do1event(self, mask = Events.everyEvent, wait = 0):
466 ok, event = self.app.getevent(mask, wait)
467 if Dlg.IsDialogEvent(event):
468 if self.app.do_dialogevent(event):
469 return
470 if ok:
471 self.dispatch(event)
472 else:
473 self.app.idle(event)
474
475 def do_keyDown(self, event):
476 self.do_key(event)
477
478 def do_autoKey(self, event):
479 if not event[-1] & Events.cmdKey:
480 self.do_key(event)
481
482 def do_key(self, event):
483 (what, message, when, where, modifiers) = event
484 w = Win.FrontWindow()
485 if w <> self.wid:
486 return
487 c = chr(message & Events.charCodeMask)
488 if modifiers & Events.cmdKey:
489 self.app.checkmenus(self)
490 result = Menu.MenuKey(ord(c))
491 id = (result>>16) & 0xffff # Hi word
492 item = result & 0xffff # Lo word
493 if id:
494 self.app.do_rawmenu(id, item, None, event)
495 return
496 self.do_char(c, event)
497
498 def do_mouseDown(self, event):
499 (what, message, when, where, modifiers) = event
500 partcode, wid = Win.FindWindow(where)
501 #
502 # Find the correct name.
503 #
504 if FrameWork.partname.has_key(partcode):
505 name = "do_" + FrameWork.partname[partcode]
506 else:
507 name = "do_%d" % partcode
508
509 if name == "do_inDesk":
510 MacOS.HandleEvent(event)
511 return
512 if wid == self.wid:
513 try:
514 handler = getattr(self, name)
515 except AttributeError:
516 handler = self.app.do_unknownpartcode
517 else:
518 #MacOS.HandleEvent(event)
519 if name == 'do_inMenuBar':
520 handler = getattr(self.parent, name)
521 else:
522 return
523 handler(partcode, wid, event)
524
525 def dispatch(self, event):
526 (what, message, when, where, modifiers) = event
527 if FrameWork.eventname.has_key(what):
528 name = "do_" + FrameWork.eventname[what]
529 else:
530 name = "do_%d" % what
531 try:
532 handler = getattr(self, name)
533 except AttributeError:
534 try:
535 handler = getattr(self.app, name)
536 except AttributeError:
537 handler = self.app.do_unknownevent
538 handler(event)
539
540
541def FrontWindowInsert(stuff):
542 if not stuff:
543 return
544 if type(stuff) <> StringType:
545 raise TypeError, 'string expected'
546 import W
547 app = W.getapplication()
548 wid = Win.FrontWindow()
549 if wid and app._windows.has_key(wid):
550 window = app._windows[wid]
551 if hasattr(window, "insert"):
552 try:
553 window.insert(stuff)
554 return
555 except:
556 pass
557 import EasyDialogs
558 if EasyDialogs.AskYesNoCancel(
559 "CanÕt find window or widget to insert text into; copy to clipboard instead?",
560 1) == 1:
561 import Scrap
562 Scrap.ZeroScrap()
563 Scrap.PutScrap('TEXT', stuff)
564
565
566# not quite based on the same function in FrameWork
567_windowcounter = 0
568
569def getnextwindowpos():
570 global _windowcounter
571 rows = 8
572 l = 4 * (rows + 1 - (_windowcounter % rows) + _windowcounter / rows)
573 t = 44 + 20 * (_windowcounter % rows)
574 _windowcounter = _windowcounter + 1
575 return l, t
576
577def windowbounds(preferredsize, minsize = None):
578 "Return sensible window bounds"
579
580 global _windowcounter
581 if len(preferredsize) == 4:
582 bounds = l, t, r, b = preferredsize
583 union = Qd.UnionRect(bounds, Qd.qd.screenBits.bounds)
584 if union == Qd.qd.screenBits.bounds:
585 return bounds
586 else:
587 preferredsize = r - l, b - t
588 if not minsize:
589 minsize = preferredsize
590 minwidth, minheight = minsize
591 width, height = preferredsize
592
593 sl, st, sr, sb = screenbounds = Qd.InsetRect(Qd.qd.screenBits.bounds, 4, 4)
594 l, t = getnextwindowpos()
595 if (l + width) > sr:
596 _windowcounter = 0
597 l, t = getnextwindowpos()
598 r = l + width
599 b = t + height
600 if (t + height) > sb:
601 b = sb
602 if (b - t) < minheight:
603 b = t + minheight
604 return l, t, r, b
605
606scratchRegion = Qd.NewRgn()
607
608# util -- move somewhere convenient???
609def GetRgnBounds(the_Rgn):
610 (t, l, b, r) = struct.unpack("hhhh", the_Rgn.data[2:10])
611 return (l, t, r, b)