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