Jack Jansen | 5a6fdcd | 2001-08-25 12:15:04 +0000 | [diff] [blame] | 1 | from Carbon import Qd |
| 2 | from Carbon import Win |
| 3 | from Carbon import QuickDraw |
| 4 | from Carbon import Evt |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 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() |
Jack Jansen | 7302340 | 2001-01-23 14:58:20 +0000 | [diff] [blame] | 107 | self.GetWindow().InvalWindowRect(oldbounds) |
| 108 | self.GetWindow().InvalWindowRect(self._bounds) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 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") |
Jack Jansen | 7302340 | 2001-01-23 14:58:20 +0000 | [diff] [blame] | 221 | self.GetWindow().InvalWindowRect(widget._bounds) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 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() |
Jack Jansen | 7302340 | 2001-01-23 14:58:20 +0000 | [diff] [blame] | 238 | self.GetWindow().InvalWindowRect(widget._bounds) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 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() |
Jack Jansen | 7302340 | 2001-01-23 14:58:20 +0000 | [diff] [blame] | 277 | |
| 278 | |
| 279 | def GetWindow(self): |
| 280 | return self._parentwindow.GetWindow() |
| 281 | |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 282 | def __del__(self): |
| 283 | if DEBUG: |
| 284 | print "%s instance deleted" % self.__class__.__name__ |
| 285 | |
| 286 | def _drawbounds(self): |
| 287 | Qd.FrameRect(self._bounds) |
| 288 | |
| 289 | |
| 290 | class ClickableWidget(Widget): |
| 291 | |
| 292 | """Base class for clickable widgets. (note: self._enabled must be true to receive click events.)""" |
| 293 | |
| 294 | def click(self, point, modifiers): |
| 295 | pass |
| 296 | |
| 297 | def enable(self, onoff): |
| 298 | self._enabled = onoff |
| 299 | self.SetPort() |
| 300 | self.draw() |
| 301 | |
| 302 | def callback(self): |
| 303 | if self._callback: |
| 304 | return CallbackCall(self._callback, 1) |
| 305 | |
| 306 | |
| 307 | class SelectableWidget(ClickableWidget): |
| 308 | |
| 309 | """Base class for selectable widgets.""" |
| 310 | |
| 311 | _selectable = 1 |
| 312 | |
| 313 | def select(self, onoff, isclick = 0): |
| 314 | if onoff == self._selected: |
| 315 | return 1 |
| 316 | if self._bindings.has_key("<select>"): |
| 317 | callback = self._bindings["<select>"] |
| 318 | if callback(onoff): |
| 319 | return 1 |
| 320 | self._selected = onoff |
| 321 | if onoff: |
| 322 | if self._parentwindow._currentwidget is not None: |
| 323 | self._parentwindow._currentwidget.select(0) |
| 324 | self._parentwindow._currentwidget = self |
| 325 | else: |
| 326 | self._parentwindow._currentwidget = None |
| 327 | |
| 328 | def key(self, char, event): |
| 329 | pass |
| 330 | |
| 331 | def drawselframe(self, onoff): |
| 332 | if not self._parentwindow._hasselframes: |
| 333 | return |
| 334 | thickrect = Qd.InsetRect(self._bounds, -3, -3) |
| 335 | state = Qd.GetPenState() |
| 336 | Qd.PenSize(2, 2) |
| 337 | if onoff: |
| 338 | Qd.PenPat(Qd.qd.black) |
| 339 | else: |
| 340 | Qd.PenPat(Qd.qd.white) |
| 341 | Qd.FrameRect(thickrect) |
| 342 | Qd.SetPenState(state) |
| 343 | |
| 344 | def adjust(self, oldbounds): |
| 345 | self.SetPort() |
| 346 | if self._selected: |
Jack Jansen | 7302340 | 2001-01-23 14:58:20 +0000 | [diff] [blame] | 347 | self.GetWindow().InvalWindowRect(Qd.InsetRect(oldbounds, -3, -3)) |
| 348 | self.GetWindow().InvalWindowRect(Qd.InsetRect(self._bounds, -3, -3)) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 349 | else: |
Jack Jansen | 7302340 | 2001-01-23 14:58:20 +0000 | [diff] [blame] | 350 | self.GetWindow().InvalWindowRect(oldbounds) |
| 351 | self.GetWindow().InvalWindowRect(self._bounds) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 352 | |
| 353 | |
| 354 | class _Line(Widget): |
| 355 | |
| 356 | def __init__(self, possize, thickness = 1): |
| 357 | Widget.__init__(self, possize) |
| 358 | self._thickness = thickness |
| 359 | |
| 360 | def open(self): |
| 361 | self._calcbounds() |
| 362 | self.SetPort() |
| 363 | self.draw() |
| 364 | |
| 365 | def draw(self, visRgn = None): |
| 366 | if self._visible: |
| 367 | Qd.PaintRect(self._bounds) |
| 368 | |
| 369 | def _drawbounds(self): |
| 370 | pass |
| 371 | |
| 372 | class HorizontalLine(_Line): |
| 373 | |
| 374 | def _calcbounds(self): |
| 375 | Widget._calcbounds(self) |
| 376 | l, t, r, b = self._bounds |
| 377 | self._bounds = l, t, r, t + self._thickness |
| 378 | |
| 379 | class VerticalLine(_Line): |
| 380 | |
| 381 | def _calcbounds(self): |
| 382 | Widget._calcbounds(self) |
| 383 | l, t, r, b = self._bounds |
| 384 | self._bounds = l, t, l + self._thickness, b |
| 385 | |
| 386 | |
| 387 | class Frame(Widget): |
| 388 | |
| 389 | def __init__(self, possize, pattern = Qd.qd.black, color = (0, 0, 0)): |
| 390 | Widget.__init__(self, possize) |
| 391 | self._framepattern = pattern |
| 392 | self._framecolor = color |
| 393 | |
| 394 | def setcolor(self, color): |
| 395 | self._framecolor = color |
| 396 | self.SetPort() |
| 397 | self.draw() |
| 398 | |
| 399 | def setpattern(self, pattern): |
| 400 | self._framepattern = pattern |
| 401 | self.SetPort() |
| 402 | self.draw() |
| 403 | |
| 404 | def draw(self, visRgn = None): |
| 405 | if self._visible: |
| 406 | penstate = Qd.GetPenState() |
| 407 | Qd.PenPat(self._framepattern) |
| 408 | Qd.RGBForeColor(self._framecolor) |
| 409 | Qd.FrameRect(self._bounds) |
| 410 | Qd.RGBForeColor((0, 0, 0)) |
| 411 | Qd.SetPenState(penstate) |
| 412 | |
| 413 | def _darkencolor((r, g, b)): |
| 414 | return 0.75 * r, 0.75 * g, 0.75 * b |
| 415 | |
| 416 | class BevelBox(Widget): |
| 417 | |
| 418 | """'Platinum' beveled rectangle.""" |
| 419 | |
| 420 | def __init__(self, possize, color = (0xe000, 0xe000, 0xe000)): |
| 421 | Widget.__init__(self, possize) |
| 422 | self._color = color |
| 423 | self._darkercolor = _darkencolor(color) |
| 424 | |
| 425 | def setcolor(self, color): |
| 426 | self._color = color |
| 427 | self.SetPort() |
| 428 | self.draw() |
| 429 | |
| 430 | def draw(self, visRgn = None): |
| 431 | if self._visible: |
| 432 | l, t, r, b = Qd.InsetRect(self._bounds, 1, 1) |
| 433 | Qd.RGBForeColor(self._color) |
| 434 | Qd.PaintRect((l, t, r, b)) |
| 435 | Qd.RGBForeColor(self._darkercolor) |
| 436 | Qd.MoveTo(l, b) |
| 437 | Qd.LineTo(r, b) |
| 438 | Qd.LineTo(r, t) |
| 439 | Qd.RGBForeColor((0, 0, 0)) |
| 440 | |
| 441 | |
| 442 | class Group(Widget): |
| 443 | |
| 444 | """A container for subwidgets""" |
| 445 | |
| 446 | |
| 447 | class HorizontalPanes(Widget): |
| 448 | |
| 449 | """Panes, a.k.a. frames. Works a bit like a group. Devides the widget area into "panes", |
| 450 | which can be resized by the user by clicking and dragging between the subwidgets.""" |
| 451 | |
| 452 | _direction = 1 |
| 453 | |
| 454 | def __init__(self, possize, panesizes = None, gutter = 8): |
| 455 | """panesizes should be a tuple of numbers. The length of the tuple is the number of panes, |
| 456 | the items in the tuple are the relative sizes of these panes; these numbers should add up |
| 457 | to 1 (the total size of all panes).""" |
| 458 | ClickableWidget.__init__(self, possize) |
| 459 | self._panesizes = panesizes |
| 460 | self._gutter = gutter |
| 461 | self._enabled = 1 |
| 462 | self.setuppanes() |
| 463 | |
| 464 | #def open(self): |
| 465 | # self.installbounds() |
| 466 | # ClickableWidget.open(self) |
| 467 | |
| 468 | def _calcbounds(self): |
| 469 | # hmmm. It should not neccesary be override _calcbounds :-( |
| 470 | self.installbounds() |
| 471 | Widget._calcbounds(self) |
| 472 | |
| 473 | def setuppanes(self): |
| 474 | panesizes = self._panesizes |
| 475 | total = 0 |
| 476 | if panesizes is not None: |
| 477 | #if len(self._widgets) <> len(panesizes): |
| 478 | # raise TypeError, 'number of widgets does not match number of panes' |
| 479 | for panesize in panesizes: |
| 480 | if not 0 < panesize < 1: |
| 481 | raise TypeError, 'pane sizes must be between 0 and 1, not including.' |
| 482 | total = total + panesize |
| 483 | if round(total, 4) <> 1.0: |
| 484 | raise TypeError, 'pane sizes must add up to 1' |
| 485 | else: |
| 486 | # XXX does not work! |
| 487 | step = 1.0 / len(self._widgets) |
| 488 | panesizes = [] |
| 489 | for i in range(len(self._widgets)): |
| 490 | panesizes.append(step) |
| 491 | current = 0 |
| 492 | self._panesizes = [] |
| 493 | self._gutters = [] |
| 494 | for panesize in panesizes: |
| 495 | if current: |
| 496 | self._gutters.append(current) |
Jack Jansen | 34d11f0 | 2000-03-07 23:40:13 +0000 | [diff] [blame] | 497 | self._panesizes.append((current, current + panesize)) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 498 | current = current + panesize |
| 499 | self.makepanebounds() |
| 500 | |
| 501 | def getpanesizes(self): |
| 502 | return map(lambda (fr, to): to-fr, self._panesizes) |
| 503 | |
| 504 | boundstemplate = "lambda width, height: (0, height * %s + %d, width, height * %s + %d)" |
| 505 | |
| 506 | def makepanebounds(self): |
| 507 | halfgutter = self._gutter / 2 |
| 508 | self._panebounds = [] |
| 509 | for i in range(len(self._panesizes)): |
| 510 | panestart, paneend = self._panesizes[i] |
| 511 | boundsstring = self.boundstemplate % (`panestart`, panestart and halfgutter, |
| 512 | `paneend`, (paneend <> 1.0) and -halfgutter) |
| 513 | self._panebounds.append(eval(boundsstring)) |
| 514 | |
| 515 | def installbounds(self): |
| 516 | #self.setuppanes() |
| 517 | for i in range(len(self._widgets)): |
| 518 | w = self._widgets[i] |
| 519 | w._possize = self._panebounds[i] |
| 520 | #if hasattr(w, "setuppanes"): |
| 521 | # w.setuppanes() |
| 522 | if hasattr(w, "installbounds"): |
| 523 | w.installbounds() |
| 524 | |
| 525 | def rollover(self, point, onoff): |
| 526 | if onoff: |
| 527 | orgmouse = point[self._direction] |
| 528 | halfgutter = self._gutter / 2 |
| 529 | l, t, r, b = self._bounds |
| 530 | if self._direction: |
| 531 | begin, end = t, b |
| 532 | else: |
| 533 | begin, end = l, r |
| 534 | |
| 535 | i = self.findgutter(orgmouse, begin, end) |
| 536 | if i is None: |
| 537 | SetCursor("arrow") |
| 538 | else: |
| 539 | SetCursor(self._direction and 'vmover' or 'hmover') |
| 540 | |
| 541 | def findgutter(self, orgmouse, begin, end): |
| 542 | tolerance = max(4, self._gutter) / 2 |
| 543 | for i in range(len(self._gutters)): |
| 544 | pos = begin + (end - begin) * self._gutters[i] |
| 545 | if abs(orgmouse - pos) <= tolerance: |
| 546 | break |
| 547 | else: |
| 548 | return |
| 549 | return i |
| 550 | |
| 551 | def click(self, point, modifiers): |
| 552 | # what a mess... |
| 553 | orgmouse = point[self._direction] |
| 554 | halfgutter = self._gutter / 2 |
| 555 | l, t, r, b = self._bounds |
| 556 | if self._direction: |
| 557 | begin, end = t, b |
| 558 | else: |
| 559 | begin, end = l, r |
| 560 | |
| 561 | i = self.findgutter(orgmouse, begin, end) |
| 562 | if i is None: |
| 563 | return |
| 564 | |
| 565 | pos = orgpos = begin + (end - begin) * self._gutters[i] # init pos too, for fast click on border, bug done by Petr |
| 566 | |
| 567 | minpos = self._panesizes[i][0] |
| 568 | maxpos = self._panesizes[i+1][1] |
| 569 | minpos = begin + (end - begin) * minpos + 64 |
| 570 | maxpos = begin + (end - begin) * maxpos - 64 |
| 571 | if minpos > orgpos and maxpos < orgpos: |
| 572 | return |
| 573 | |
| 574 | #SetCursor("fist") |
| 575 | self.SetPort() |
| 576 | if self._direction: |
| 577 | rect = l, orgpos - 1, r, orgpos |
| 578 | else: |
| 579 | rect = orgpos - 1, t, orgpos, b |
| 580 | |
| 581 | # track mouse --- XXX move to separate method? |
| 582 | Qd.PenMode(QuickDraw.srcXor) |
| 583 | Qd.PenPat(Qd.qd.gray) |
| 584 | Qd.PaintRect(rect) |
| 585 | lastpos = None |
| 586 | while Evt.Button(): |
| 587 | pos = orgpos - orgmouse + Evt.GetMouse()[self._direction] |
| 588 | pos = max(pos, minpos) |
| 589 | pos = min(pos, maxpos) |
| 590 | if pos == lastpos: |
| 591 | continue |
| 592 | Qd.PenPat(Qd.qd.gray) |
| 593 | Qd.PaintRect(rect) |
| 594 | if self._direction: |
| 595 | rect = l, pos - 1, r, pos |
| 596 | else: |
| 597 | rect = pos - 1, t, pos, b |
| 598 | Qd.PenPat(Qd.qd.gray) |
| 599 | Qd.PaintRect(rect) |
| 600 | lastpos = pos |
| 601 | Qd.PaintRect(rect) |
| 602 | Qd.PenNormal() |
| 603 | SetCursor("watch") |
| 604 | |
| 605 | newpos = (pos - begin) / float(end - begin) |
| 606 | self._gutters[i] = newpos |
| 607 | self._panesizes[i] = self._panesizes[i][0], newpos |
| 608 | self._panesizes[i+1] = newpos, self._panesizes[i+1][1] |
| 609 | self.makepanebounds() |
| 610 | self.installbounds() |
| 611 | self._calcbounds() |
| 612 | |
| 613 | |
| 614 | class VerticalPanes(HorizontalPanes): |
| 615 | """see HorizontalPanes""" |
| 616 | _direction = 0 |
| 617 | boundstemplate = "lambda width, height: (width * %s + %d, 0, width * %s + %d, height)" |
| 618 | |
| 619 | |
| 620 | class ColorPicker(ClickableWidget): |
| 621 | |
| 622 | """Color picker widget. Allows the user to choose a color.""" |
| 623 | |
| 624 | def __init__(self, possize, color = (0, 0, 0), callback = None): |
| 625 | ClickableWidget.__init__(self, possize) |
| 626 | self._color = color |
| 627 | self._callback = callback |
| 628 | self._enabled = 1 |
| 629 | |
| 630 | def click(self, point, modifiers): |
| 631 | if not self._enabled: |
| 632 | return |
| 633 | import ColorPicker |
| 634 | newcolor, ok = ColorPicker.GetColor("", self._color) |
| 635 | if ok: |
| 636 | self._color = newcolor |
| 637 | self.SetPort() |
| 638 | self.draw() |
| 639 | if self._callback: |
| 640 | return CallbackCall(self._callback, 0, self._color) |
| 641 | |
| 642 | def set(self, color): |
| 643 | self._color = color |
| 644 | self.SetPort() |
| 645 | self.draw() |
| 646 | |
| 647 | def get(self): |
| 648 | return self._color |
| 649 | |
| 650 | def draw(self, visRgn=None): |
| 651 | if self._visible: |
| 652 | if not visRgn: |
| 653 | visRgn = self._parentwindow.wid.GetWindowPort().visRgn |
| 654 | Qd.PenPat(Qd.qd.gray) |
| 655 | rect = self._bounds |
| 656 | Qd.FrameRect(rect) |
| 657 | rect = Qd.InsetRect(rect, 3, 3) |
| 658 | Qd.PenNormal() |
| 659 | Qd.RGBForeColor(self._color) |
| 660 | Qd.PaintRect(rect) |
| 661 | Qd.RGBForeColor((0, 0, 0)) |
| 662 | |
| 663 | |
| 664 | # misc utils |
| 665 | |
| 666 | def CallbackCall(callback, mustfit, *args): |
| 667 | """internal helper routine for W""" |
| 668 | # XXX this function should die. |
| 669 | if type(callback) == FunctionType: |
| 670 | func = callback |
| 671 | maxargs = func.func_code.co_argcount |
| 672 | elif type(callback) == MethodType: |
| 673 | func = callback.im_func |
| 674 | maxargs = func.func_code.co_argcount - 1 |
| 675 | else: |
| 676 | if callable(callback): |
| 677 | return apply(callback, args) |
| 678 | else: |
| 679 | raise TypeError, "uncallable callback object" |
| 680 | |
| 681 | if func.func_defaults: |
| 682 | minargs = maxargs - len(func.func_defaults) |
| 683 | else: |
| 684 | minargs = maxargs |
| 685 | if minargs <= len(args) <= maxargs: |
| 686 | return apply(callback, args) |
| 687 | elif not mustfit and minargs == 0: |
| 688 | return callback() |
| 689 | else: |
| 690 | if mustfit: |
| 691 | raise TypeError, "callback accepts wrong number of arguments: " + `len(args)` |
| 692 | else: |
| 693 | raise TypeError, "callback accepts wrong number of arguments: 0 or " + `len(args)` |
| 694 | |
| 695 | |
| 696 | def HasBaseClass(obj, class_): |
| 697 | try: |
| 698 | raise obj |
| 699 | except class_: |
| 700 | return 1 |
| 701 | except: |
| 702 | pass |
| 703 | return 0 |
| 704 | |
| 705 | |
| 706 | _cursors = { |
| 707 | "watch" : Qd.GetCursor(QuickDraw.watchCursor).data, |
| 708 | "arrow" : Qd.qd.arrow, |
| 709 | "iBeam" : Qd.GetCursor(QuickDraw.iBeamCursor).data, |
| 710 | "cross" : Qd.GetCursor(QuickDraw.crossCursor).data, |
| 711 | "plus" : Qd.GetCursor(QuickDraw.plusCursor).data, |
| 712 | "hand" : Qd.GetCursor(468).data, |
| 713 | "fist" : Qd.GetCursor(469).data, |
| 714 | "hmover" : Qd.GetCursor(470).data, |
| 715 | "vmover" : Qd.GetCursor(471).data, |
| 716 | "zoomin" : Qd.GetCursor(472).data, |
| 717 | "zoomout" : Qd.GetCursor(473).data, |
| 718 | "zoom" : Qd.GetCursor(474).data, |
| 719 | } |
| 720 | |
| 721 | def SetCursor(what): |
| 722 | """Set the cursorshape to any of these: 'arrow', 'cross', 'fist', 'hand', 'hmover', 'iBeam', |
| 723 | 'plus', 'vmover', 'watch', 'zoom', 'zoomin', 'zoomout'.""" |
| 724 | Qd.SetCursor(_cursors[what]) |