home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2000 / MacHack 2000.toast / pc / The Hacks / MacHacksBug / Python 1.5.2c1 / Mac / Tools / IDE / Wbase.py < prev    next >
Encoding:
Python Source  |  2000-06-23  |  18.3 KB  |  721 lines

  1. import Qd
  2. import Win
  3. import QuickDraw
  4. import Evt
  5. import string
  6. from types import *
  7. import sys
  8.  
  9. WidgetsError = "WidgetsError"
  10.  
  11. DEBUG = 0
  12.  
  13. class Widget:
  14.     
  15.     """Base class for all widgets."""
  16.     
  17.     _selectable = 0
  18.     
  19.     def __init__(self, possize):
  20.         self._widgets = []
  21.         self._widgetsdict = {}
  22.         self._possize = possize
  23.         self._bounds = None
  24.         self._visible = 1
  25.         self._enabled = 0
  26.         self._selected = 0
  27.         self._activated = 0
  28.         self._callback = None
  29.         self._parent = None
  30.         self._parentwindow = None
  31.         self._bindings = {}
  32.         self._backcolor = None
  33.     
  34.     def show(self, onoff):
  35.         self._visible = onoff
  36.         for w in self._widgets:
  37.             w.show(onoff)
  38.         if self._parentwindow is not None and self._parentwindow.wid is not None:
  39.             self.SetPort()
  40.             if onoff:
  41.                 self.draw()
  42.             else:
  43.                 Qd.EraseRect(self._bounds)
  44.     
  45.     def draw(self, visRgn = None):
  46.         if self._visible:
  47.             # draw your stuff here
  48.             pass
  49.     
  50.     def getpossize(self):
  51.         return self._possize
  52.     
  53.     def getbounds(self):
  54.         return self._bounds
  55.     
  56.     def move(self, x, y = None):
  57.         """absolute move"""
  58.         if y == None:
  59.             x, y = x
  60.         if type(self._possize) <> TupleType:
  61.             raise WidgetsError, "can't move widget with bounds function"
  62.         l, t, r, b = self._possize
  63.         self.resize(x, y, r, b)
  64.     
  65.     def rmove(self, x, y = None):
  66.         """relative move"""
  67.         if y == None:
  68.             x, y = x
  69.         if type(self._possize) <> TupleType:
  70.             raise WidgetsError, "can't move widget with bounds function"
  71.         l, t, r, b = self._possize
  72.         self.resize(l + x, t + y, r, b)
  73.         
  74.     def resize(self, *args):
  75.         if len(args) == 1:
  76.             if type(args[0]) == FunctionType or type(args[0]) == MethodType:
  77.                 self._possize = args[0]
  78.             else:
  79.                 apply(self.resize, args[0])
  80.         elif len(args) == 2:
  81.             self._possize = (0, 0) + args
  82.         elif len(args) == 4:
  83.             self._possize = args
  84.         else:
  85.             raise TypeError, "wrong number of arguments"
  86.         self._calcbounds()
  87.     
  88.     def open(self):
  89.         self._calcbounds()
  90.     
  91.     def close(self):
  92.         del self._callback
  93.         del self._possize
  94.         del self._bindings
  95.         del self._parent
  96.         del self._parentwindow
  97.     
  98.     def bind(self, key, callback):
  99.         """bind a key or an 'event' to a callback"""
  100.         if callback:
  101.             self._bindings[key] = callback
  102.         elif self._bindings.has_key(key):
  103.             del self._bindings[key]
  104.     
  105.     def adjust(self, oldbounds):
  106.         self.SetPort()
  107.         Win.InvalRect(oldbounds)
  108.         Win.InvalRect(self._bounds)
  109.     
  110.     def _calcbounds(self):
  111.         # calculate absolute bounds relative to the window origin from our
  112.         # abstract _possize attribute, which is either a 4-tuple or a callable object
  113.         oldbounds = self._bounds
  114.         pl, pt, pr, pb = self._parent._bounds
  115.         if callable(self._possize):
  116.             # _possize is callable, let it figure it out by itself: it should return 
  117.             # the bounds relative to our parent widget.
  118.             width = pr - pl
  119.             height = pb - pt
  120.             self._bounds = Qd.OffsetRect(self._possize(width, height), pl, pt)
  121.         else:
  122.             # _possize must be a 4-tuple. This is where the algorithm by Peter Kriens and
  123.             # Petr van Blokland kicks in. (*** Parts of this algorithm are applied for 
  124.             # patents by Ericsson, Sweden ***)
  125.             l, t, r, b = self._possize
  126.             # depending on the values of l(eft), t(op), r(right) and b(ottom), 
  127.             # they mean different things:
  128.             if l < -1:
  129.                 # l is less than -1, this mean it measures from the *right* of it's parent
  130.                 l = pr + l
  131.             else:
  132.                 # l is -1 or greater, this mean it measures from the *left* of it's parent
  133.                 l = pl + l
  134.             if t < -1:
  135.                 # t is less than -1, this mean it measures from the *bottom* of it's parent
  136.                 t = pb + t
  137.             else:
  138.                 # t is -1 or greater, this mean it measures from the *top* of it's parent
  139.                 t = pt + t
  140.             if r > 1:
  141.                 # r is greater than 1, this means r is the *width* of the widget
  142.                 r = l + r
  143.             else:
  144.                 # r is less than 1, this means it measures from the *right* of it's parent
  145.                 r = pr + r
  146.             if b > 1:
  147.                 # b is greater than 1, this means b is the *height* of the widget
  148.                 b = t + b
  149.             else:
  150.                 # b is less than 1, this means it measures from the *bottom* of it's parent
  151.                 b = pb + b
  152.             self._bounds = (l, t, r, b)
  153.         if oldbounds and oldbounds <> self._bounds:
  154.             self.adjust(oldbounds)
  155.         for w in self._widgets:
  156.             w._calcbounds()
  157.     
  158.     def test(self, point):
  159.         if Qd.PtInRect(point, self._bounds):
  160.             return 1
  161.     
  162.     def click(self, point, modifiers):
  163.         pass
  164.     
  165.     def findwidget(self, point, onlyenabled = 1):
  166.         if self.test(point):
  167.             for w in self._widgets:
  168.                 widget = w.findwidget(point)
  169.                 if widget is not None:
  170.                     return widget
  171.             if self._enabled or not onlyenabled:
  172.                 return self
  173.     
  174.     def forall(self, methodname, *args):
  175.         for w in self._widgets:
  176.             rv = apply(w.forall, (methodname,) + args)
  177.             if rv: 
  178.                 return rv
  179.         if self._bindings.has_key("<" + methodname + ">"):
  180.             callback = self._bindings["<" + methodname + ">"]
  181.             rv = apply(callback, args)
  182.             if rv: 
  183.                 return rv
  184.         if hasattr(self, methodname):
  185.             method = getattr(self, methodname)
  186.             return apply(method, args)
  187.     
  188.     def forall_butself(self, methodname, *args):
  189.         for w in self._widgets:
  190.             rv = apply(w.forall, (methodname,) + args)
  191.             if rv: 
  192.                 return rv
  193.     
  194.     def forall_frombottom(self, methodname, *args):
  195.         if self._bindings.has_key("<" + methodname + ">"):
  196.             callback = self._bindings["<" + methodname + ">"]
  197.             rv = apply(callback, args)
  198.             if rv: 
  199.                 return rv
  200.         if hasattr(self, methodname):
  201.             method = getattr(self, methodname)
  202.             rv = apply(method, args)
  203.             if rv: 
  204.                 return rv
  205.         for w in self._widgets:
  206.             rv = apply(w.forall_frombottom, (methodname,) + args)
  207.             if rv: 
  208.                 return rv
  209.     
  210.     def _addwidget(self, key, widget):
  211.         if widget in self._widgets:
  212.             raise ValueError, "duplicate widget"
  213.         if self._widgetsdict.has_key(key):
  214.             self._removewidget(key)
  215.         self._widgets.append(widget)
  216.         self._widgetsdict[key] = widget
  217.         widget._parent = self
  218.         self._setparentwindow(widget)
  219.         if self._parentwindow and self._parentwindow.wid:
  220.             widget.forall_frombottom("open")
  221.             Win.InvalRect(widget._bounds)
  222.     
  223.     def _setparentwindow(self, widget):
  224.         widget._parentwindow = self._parentwindow
  225.         for w in widget._widgets:
  226.             self._setparentwindow(w)
  227.     
  228.     def _removewidget(self, key):
  229.         if not self._widgetsdict.has_key(key):
  230.             raise KeyError, "no widget with key " + `key`
  231.         widget = self._widgetsdict[key]
  232.         for k in widget._widgetsdict.keys():
  233.             widget._removewidget(k)
  234.         if self._parentwindow._currentwidget == widget:
  235.             widget.select(0)
  236.             self._parentwindow._currentwidget = None
  237.         self.SetPort()
  238.         Win.InvalRect(widget._bounds)
  239.         widget.close()
  240.         del self._widgetsdict[key]
  241.         self._widgets.remove(widget)
  242.     
  243.     def __setattr__(self, attr, value):
  244.         if type(value) == InstanceType and isinstance(value, Widget) and    \
  245.                 attr not in ("_currentwidget", "_lastrollover", 
  246.                 "_parent", "_parentwindow", "_defaultbutton"):
  247.             if hasattr(self, attr):
  248.                 raise ValueError, "Can't replace existing attribute: " + attr
  249.             self._addwidget(attr, value)
  250.         self.__dict__[attr] = value
  251.     
  252.     def __delattr__(self, attr):
  253.         if attr == "_widgetsdict":
  254.             raise AttributeError, "cannot delete attribute _widgetsdict"
  255.         if self._widgetsdict.has_key(attr):
  256.             self._removewidget(attr)
  257.             if self.__dict__.has_key(attr):
  258.                 del self.__dict__[attr]
  259.         elif self.__dict__.has_key(attr):
  260.             del self.__dict__[attr]
  261.         else:
  262.             raise AttributeError, attr
  263.     
  264.     def __setitem__(self, key, value):
  265.         self._addwidget(key, value)
  266.     
  267.     def __getitem__(self, key):
  268.         if not self._widgetsdict.has_key(key):
  269.             raise KeyError, key
  270.         return self._widgetsdict[key]
  271.     
  272.     def __delitem__(self, key):
  273.         self._removewidget(key)
  274.     
  275.     def SetPort(self):
  276.         self._parentwindow.SetPort()
  277.     
  278.     def __del__(self):
  279.         if DEBUG:
  280.             print "%s instance deleted" % self.__class__.__name__
  281.     
  282.     def _drawbounds(self):
  283.         Qd.FrameRect(self._bounds)
  284.  
  285.  
  286. class ClickableWidget(Widget):
  287.     
  288.     """Base class for clickable widgets. (note: self._enabled must be true to receive click events.)"""
  289.     
  290.     def click(self, point, modifiers):
  291.         pass
  292.     
  293.     def enable(self, onoff):
  294.         self._enabled = onoff
  295.         self.SetPort()
  296.         self.draw()
  297.     
  298.     def callback(self):
  299.         if self._callback:
  300.             return CallbackCall(self._callback, 1)
  301.     
  302.  
  303. class SelectableWidget(ClickableWidget):
  304.  
  305.     """Base class for selectable widgets."""
  306.     
  307.     _selectable = 1
  308.     
  309.     def select(self, onoff, isclick = 0):
  310.         if onoff == self._selected:
  311.             return 1
  312.         if self._bindings.has_key("<select>"):
  313.             callback = self._bindings["<select>"]
  314.             if callback(onoff):
  315.                 return 1
  316.         self._selected = onoff
  317.         if onoff:
  318.             if self._parentwindow._currentwidget is not None:
  319.                 self._parentwindow._currentwidget.select(0)
  320.             self._parentwindow._currentwidget = self
  321.         else:
  322.             self._parentwindow._currentwidget = None
  323.     
  324.     def key(self, char, event):
  325.         pass
  326.     
  327.     def drawselframe(self, onoff):
  328.         if not self._parentwindow._hasselframes:
  329.             return
  330.         thickrect = Qd.InsetRect(self._bounds, -3, -3)
  331.         state = Qd.GetPenState()
  332.         Qd.PenSize(2, 2)
  333.         if onoff:
  334.             Qd.PenPat(Qd.qd.black)
  335.         else:
  336.             Qd.PenPat(Qd.qd.white)
  337.         Qd.FrameRect(thickrect)
  338.         Qd.SetPenState(state)
  339.     
  340.     def adjust(self, oldbounds):
  341.         self.SetPort()
  342.         if self._selected:
  343.             Win.InvalRect(Qd.InsetRect(oldbounds, -3, -3))
  344.             Win.InvalRect(Qd.InsetRect(self._bounds, -3, -3))
  345.         else:
  346.             Win.InvalRect(oldbounds)
  347.             Win.InvalRect(self._bounds)
  348.  
  349.  
  350. class _Line(Widget):
  351.     
  352.     def __init__(self, possize, thickness = 1):
  353.         Widget.__init__(self, possize)
  354.         self._thickness = thickness
  355.     
  356.     def open(self):
  357.         self._calcbounds()
  358.         self.SetPort()
  359.         self.draw()
  360.     
  361.     def draw(self, visRgn = None):
  362.         if self._visible:
  363.             Qd.PaintRect(self._bounds)
  364.     
  365.     def _drawbounds(self):
  366.         pass
  367.  
  368. class HorizontalLine(_Line):
  369.     
  370.     def _calcbounds(self):
  371.         Widget._calcbounds(self)
  372.         l, t, r, b = self._bounds
  373.         self._bounds = l, t, r, t + self._thickness
  374.  
  375. class VerticalLine(_Line):
  376.     
  377.     def _calcbounds(self):
  378.         Widget._calcbounds(self)
  379.         l, t, r, b = self._bounds
  380.         self._bounds = l, t, l + self._thickness, b
  381.  
  382.  
  383. class Frame(Widget):
  384.     
  385.     def __init__(self, possize, pattern = Qd.qd.black, color = (0, 0, 0)):
  386.         Widget.__init__(self, possize)
  387.         self._framepattern = pattern
  388.         self._framecolor = color
  389.     
  390.     def setcolor(self, color):
  391.         self._framecolor = color
  392.         self.SetPort()
  393.         self.draw()
  394.     
  395.     def setpattern(self, pattern):
  396.         self._framepattern = pattern
  397.         self.SetPort()
  398.         self.draw()
  399.         
  400.     def draw(self, visRgn = None):
  401.         if self._visible:
  402.             penstate = Qd.GetPenState()
  403.             Qd.PenPat(self._framepattern)
  404.             Qd.RGBForeColor(self._framecolor)
  405.             Qd.FrameRect(self._bounds)
  406.             Qd.RGBForeColor((0, 0, 0))
  407.             Qd.SetPenState(penstate)
  408.  
  409. def _darkencolor((r, g, b)):
  410.     return 0.75 * r, 0.75 * g, 0.75 * b
  411.  
  412. class BevelBox(Widget):
  413.     
  414.     """'Platinum' beveled rectangle."""
  415.     
  416.     def __init__(self, possize, color = (0xe000, 0xe000, 0xe000)):
  417.         Widget.__init__(self, possize)
  418.         self._color = color
  419.         self._darkercolor = _darkencolor(color)
  420.     
  421.     def setcolor(self, color):
  422.         self._color = color
  423.         self.SetPort()
  424.         self.draw()
  425.     
  426.     def draw(self, visRgn = None):
  427.         if self._visible:
  428.             l, t, r, b = Qd.InsetRect(self._bounds, 1, 1)
  429.             Qd.RGBForeColor(self._color)
  430.             Qd.PaintRect((l, t, r, b))
  431.             Qd.RGBForeColor(self._darkercolor)
  432.             Qd.MoveTo(l, b)
  433.             Qd.LineTo(r, b)
  434.             Qd.LineTo(r, t)
  435.             Qd.RGBForeColor((0, 0, 0))
  436.  
  437.  
  438. class Group(Widget):
  439.     
  440.     """A container for subwidgets"""
  441.  
  442.  
  443. class HorizontalPanes(Widget):
  444.     
  445.     """Panes, a.k.a. frames. Works a bit like a group. Devides the widget area into "panes",
  446.     which can be resized by the user by clicking and dragging between the subwidgets."""
  447.     
  448.     _direction = 1
  449.     
  450.     def __init__(self, possize, panesizes = None, gutter = 8):
  451.         """panesizes should be a tuple of numbers. The length of the tuple is the number of panes, 
  452.         the items in the tuple are the relative sizes of these panes; these numbers should add up 
  453.         to 1 (the total size of all panes)."""
  454.         ClickableWidget.__init__(self, possize)
  455.         self._panesizes = panesizes
  456.         self._gutter = gutter
  457.         self._enabled = 1
  458.         self.setuppanes()
  459.     
  460.     #def open(self):
  461.     #    self.installbounds()
  462.     #    ClickableWidget.open(self)
  463.     
  464.     def _calcbounds(self):
  465.         # hmmm. It should not neccesary be override _calcbounds :-(
  466.         self.installbounds()
  467.         Widget._calcbounds(self)
  468.     
  469.     def setuppanes(self):
  470.         panesizes = self._panesizes
  471.         total = 0
  472.         if panesizes is not None:
  473.             #if len(self._widgets) <> len(panesizes):
  474.             #    raise TypeError, 'number of widgets does not match number of panes'
  475.             for panesize in panesizes:
  476.                 if not 0 < panesize < 1:
  477.                     raise TypeError, 'pane sizes must be between 0 and 1, not including.'
  478.                 total = total + panesize
  479.             if round(total, 4) <> 1.0:
  480.                 raise TypeError, 'pane sizes must add up to 1'
  481.         else:
  482.             # XXX does not work!
  483.             step = 1.0 / len(self._widgets)
  484.             panesizes = []
  485.             for i in range(len(self._widgets)):
  486.                 panesizes.append(step)
  487.         current = 0
  488.         self._panesizes = []
  489.         self._gutters = []
  490.         for panesize in panesizes:
  491.             if current:
  492.                 self._gutters.append(current)
  493.             self._panesizes.append(current, current + panesize)
  494.             current = current + panesize
  495.         self.makepanebounds()
  496.     
  497.     def getpanesizes(self):
  498.         return map(lambda (fr, to): to-fr,  self._panesizes)
  499.     
  500.     boundstemplate = "lambda width, height: (0, height * %s + %d, width, height * %s + %d)"
  501.     
  502.     def makepanebounds(self):
  503.         halfgutter = self._gutter / 2
  504.         self._panebounds = []
  505.         for i in range(len(self._panesizes)):
  506.             panestart, paneend = self._panesizes[i]
  507.             boundsstring = self.boundstemplate % (`panestart`, panestart and halfgutter, 
  508.                             `paneend`, (paneend <> 1.0) and -halfgutter)
  509.             self._panebounds.append(eval(boundsstring))
  510.     
  511.     def installbounds(self):
  512.         #self.setuppanes()
  513.         for i in range(len(self._widgets)):
  514.             w = self._widgets[i]
  515.             w._possize = self._panebounds[i]
  516.             #if hasattr(w, "setuppanes"):
  517.             #    w.setuppanes()
  518.             if hasattr(w, "installbounds"):
  519.                 w.installbounds()
  520.     
  521.     def rollover(self, point, onoff):
  522.         if onoff:
  523.             orgmouse = point[self._direction]
  524.             halfgutter = self._gutter / 2
  525.             l, t, r, b = self._bounds
  526.             if self._direction:
  527.                 begin, end = t, b
  528.             else:
  529.                 begin, end = l, r
  530.             
  531.             i = self.findgutter(orgmouse, begin, end)
  532.             if i is None:
  533.                 SetCursor("arrow")
  534.             else:
  535.                 SetCursor(self._direction and 'vmover' or 'hmover')
  536.     
  537.     def findgutter(self, orgmouse, begin, end):
  538.         tolerance = max(4, self._gutter) / 2
  539.         for i in range(len(self._gutters)):
  540.             pos = begin + (end - begin) * self._gutters[i]
  541.             if abs(orgmouse - pos) <= tolerance:
  542.                 break
  543.         else:
  544.             return
  545.         return i
  546.     
  547.     def click(self, point, modifiers):
  548.         # what a mess...
  549.         orgmouse = point[self._direction]
  550.         halfgutter = self._gutter / 2
  551.         l, t, r, b = self._bounds
  552.         if self._direction:
  553.             begin, end = t, b
  554.         else:
  555.             begin, end = l, r
  556.         
  557.         i = self.findgutter(orgmouse, begin, end)
  558.         if i is None:
  559.             return
  560.         
  561.         pos = orgpos = begin + (end - begin) * self._gutters[i]    # init pos too, for fast click on border, bug done by Petr
  562.         
  563.         minpos = self._panesizes[i][0]
  564.         maxpos = self._panesizes[i+1][1]
  565.         minpos = begin + (end - begin) * minpos + 64
  566.         maxpos = begin + (end - begin) * maxpos - 64
  567.         if minpos > orgpos and maxpos < orgpos:
  568.             return
  569.         
  570.         #SetCursor("fist")
  571.         self.SetPort()
  572.         if self._direction:
  573.             rect = l, orgpos - 1, r, orgpos
  574.         else:
  575.             rect = orgpos - 1, t, orgpos, b
  576.         
  577.         # track mouse --- XXX  move to separate method?
  578.         Qd.PenMode(QuickDraw.srcXor)
  579.         Qd.PenPat(Qd.qd.gray)
  580.         Qd.PaintRect(rect)
  581.         lastpos = None
  582.         while Evt.Button():
  583.             pos = orgpos - orgmouse + Evt.GetMouse()[self._direction]
  584.             pos = max(pos, minpos)
  585.             pos = min(pos, maxpos)
  586.             if pos == lastpos:
  587.                 continue
  588.             Qd.PenPat(Qd.qd.gray)
  589.             Qd.PaintRect(rect)
  590.             if self._direction:
  591.                 rect = l, pos - 1, r, pos
  592.             else:
  593.                 rect = pos - 1, t, pos, b
  594.             Qd.PenPat(Qd.qd.gray)
  595.             Qd.PaintRect(rect)
  596.             lastpos = pos
  597.         Qd.PaintRect(rect)
  598.         Qd.PenNormal()
  599.         SetCursor("watch")
  600.         
  601.         newpos = (pos - begin) / float(end - begin)
  602.         self._gutters[i] = newpos
  603.         self._panesizes[i] = self._panesizes[i][0], newpos
  604.         self._panesizes[i+1] = newpos, self._panesizes[i+1][1]
  605.         self.makepanebounds()
  606.         self.installbounds()
  607.         self._calcbounds()
  608.     
  609.  
  610. class VerticalPanes(HorizontalPanes):
  611.     """see HorizontalPanes"""
  612.     _direction = 0
  613.     boundstemplate = "lambda width, height: (width * %s + %d, 0, width * %s + %d, height)"
  614.  
  615.  
  616. class ColorPicker(ClickableWidget):
  617.     
  618.     """Color picker widget. Allows the user to choose a color."""
  619.     
  620.     def __init__(self, possize, color = (0, 0, 0), callback = None):
  621.         ClickableWidget.__init__(self, possize)
  622.         self._color = color
  623.         self._callback = callback
  624.         self._enabled = 1
  625.     
  626.     def click(self, point, modifiers):
  627.         if not self._enabled:
  628.             return
  629.         import ColorPicker
  630.         newcolor, ok = ColorPicker.GetColor("", self._color)
  631.         if ok:
  632.             self._color = newcolor
  633.             self.SetPort()
  634.             self.draw()
  635.             if self._callback:
  636.                 return CallbackCall(self._callback, 0, self._color)
  637.     
  638.     def set(self, color):
  639.         self._color = color
  640.         self.SetPort()
  641.         self.draw()
  642.     
  643.     def get(self):
  644.         return self._color
  645.     
  646.     def draw(self, visRgn=None):
  647.         if self._visible:
  648.             if not visRgn:
  649.                 visRgn = self._parentwindow.wid.GetWindowPort().visRgn
  650.             Qd.PenPat(Qd.qd.gray)
  651.             rect = self._bounds
  652.             Qd.FrameRect(rect)
  653.             rect = Qd.InsetRect(rect, 3, 3)
  654.             Qd.PenNormal()
  655.             Qd.RGBForeColor(self._color)
  656.             Qd.PaintRect(rect)
  657.             Qd.RGBForeColor((0, 0, 0))
  658.             
  659.  
  660. # misc utils
  661.  
  662. def CallbackCall(callback, mustfit, *args):
  663.     """internal helper routine for W"""
  664.     # XXX this function should die.
  665.     if type(callback) == FunctionType:
  666.         func = callback
  667.         maxargs = func.func_code.co_argcount
  668.     elif type(callback) == MethodType:
  669.         func = callback.im_func
  670.         maxargs = func.func_code.co_argcount - 1
  671.     else:
  672.         if callable(callback):
  673.             return apply(callback, args)
  674.         else:
  675.             raise TypeError, "uncallable callback object"
  676.     
  677.     if func.func_defaults:
  678.         minargs = maxargs - len(func.func_defaults)
  679.     else:
  680.         minargs = maxargs
  681.     if minargs <= len(args) <= maxargs:
  682.         return apply(callback, args)
  683.     elif not mustfit and minargs == 0:
  684.         return callback()
  685.     else:
  686.         if mustfit:
  687.             raise TypeError, "callback accepts wrong number of arguments: " + `len(args)`
  688.         else:
  689.             raise TypeError, "callback accepts wrong number of arguments: 0 or " + `len(args)`
  690.  
  691.  
  692. def HasBaseClass(obj, class_):
  693.     try:
  694.         raise obj
  695.     except class_:
  696.         return 1
  697.     except:
  698.         pass
  699.     return 0
  700.  
  701.  
  702. _cursors = {
  703.     "watch"    : Qd.GetCursor(QuickDraw.watchCursor).data,
  704.     "arrow"    : Qd.qd.arrow,
  705.     "iBeam"    : Qd.GetCursor(QuickDraw.iBeamCursor).data,
  706.     "cross"    : Qd.GetCursor(QuickDraw.crossCursor).data,
  707.     "plus"        : Qd.GetCursor(QuickDraw.plusCursor).data,
  708.     "hand"    : Qd.GetCursor(468).data,
  709.     "fist"        : Qd.GetCursor(469).data,
  710.     "hmover"    : Qd.GetCursor(470).data,
  711.     "vmover"    : Qd.GetCursor(471).data,
  712.     "zoomin"    : Qd.GetCursor(472).data,
  713.     "zoomout"    : Qd.GetCursor(473).data,
  714.     "zoom"    : Qd.GetCursor(474).data,
  715. }
  716.  
  717. def SetCursor(what):
  718.     """Set the cursorshape to any of these: 'arrow', 'cross', 'fist', 'hand', 'hmover', 'iBeam', 
  719.     'plus', 'vmover', 'watch', 'zoom', 'zoomin', 'zoomout'."""
  720.     Qd.SetCursor(_cursors[what])
  721.