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