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 | |
Jack Jansen | 362c7cd0 | 2002-11-30 00:01:29 +0000 | [diff] [blame] | 379 | def __init__(self, possize, pattern = Qd.GetQDGlobalsBlack(), color = (0, 0, 0)): |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 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) |
Jack Jansen | 362c7cd0 | 2002-11-30 00:01:29 +0000 | [diff] [blame] | 573 | Qd.PenPat(Qd.GetQDGlobalsGray()) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 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 |
Jack Jansen | 362c7cd0 | 2002-11-30 00:01:29 +0000 | [diff] [blame] | 582 | Qd.PenPat(Qd.GetQDGlobalsGray()) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 583 | Qd.PaintRect(rect) |
| 584 | if self._direction: |
| 585 | rect = l, pos - 1, r, pos |
| 586 | else: |
| 587 | rect = pos - 1, t, pos, b |
Jack Jansen | 362c7cd0 | 2002-11-30 00:01:29 +0000 | [diff] [blame] | 588 | Qd.PenPat(Qd.GetQDGlobalsGray()) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 589 | Qd.PaintRect(rect) |
| 590 | lastpos = pos |
Jack Jansen | 362c7cd0 | 2002-11-30 00:01:29 +0000 | [diff] [blame] | 591 | self._parentwindow.wid.GetWindowPort().QDFlushPortBuffer(None) |
Just van Rossum | 1f1e727 | 2001-12-08 10:37:40 +0000 | [diff] [blame] | 592 | Evt.WaitNextEvent(0, 3) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 593 | Qd.PaintRect(rect) |
| 594 | Qd.PenNormal() |
| 595 | SetCursor("watch") |
| 596 | |
| 597 | newpos = (pos - begin) / float(end - begin) |
| 598 | self._gutters[i] = newpos |
| 599 | self._panesizes[i] = self._panesizes[i][0], newpos |
| 600 | self._panesizes[i+1] = newpos, self._panesizes[i+1][1] |
| 601 | self.makepanebounds() |
| 602 | self.installbounds() |
| 603 | self._calcbounds() |
Just van Rossum | 1f1e727 | 2001-12-08 10:37:40 +0000 | [diff] [blame] | 604 | |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 605 | |
| 606 | class VerticalPanes(HorizontalPanes): |
| 607 | """see HorizontalPanes""" |
| 608 | _direction = 0 |
| 609 | boundstemplate = "lambda width, height: (width * %s + %d, 0, width * %s + %d, height)" |
| 610 | |
| 611 | |
| 612 | class ColorPicker(ClickableWidget): |
| 613 | |
| 614 | """Color picker widget. Allows the user to choose a color.""" |
| 615 | |
| 616 | def __init__(self, possize, color = (0, 0, 0), callback = None): |
| 617 | ClickableWidget.__init__(self, possize) |
| 618 | self._color = color |
| 619 | self._callback = callback |
| 620 | self._enabled = 1 |
| 621 | |
| 622 | def click(self, point, modifiers): |
| 623 | if not self._enabled: |
| 624 | return |
| 625 | import ColorPicker |
| 626 | newcolor, ok = ColorPicker.GetColor("", self._color) |
| 627 | if ok: |
| 628 | self._color = newcolor |
| 629 | self.SetPort() |
| 630 | self.draw() |
| 631 | if self._callback: |
| 632 | return CallbackCall(self._callback, 0, self._color) |
| 633 | |
| 634 | def set(self, color): |
| 635 | self._color = color |
| 636 | self.SetPort() |
| 637 | self.draw() |
| 638 | |
| 639 | def get(self): |
| 640 | return self._color |
| 641 | |
| 642 | def draw(self, visRgn=None): |
| 643 | if self._visible: |
| 644 | if not visRgn: |
| 645 | visRgn = self._parentwindow.wid.GetWindowPort().visRgn |
Jack Jansen | 362c7cd0 | 2002-11-30 00:01:29 +0000 | [diff] [blame] | 646 | Qd.PenPat(Qd.GetQDGlobalsGray()) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 647 | rect = self._bounds |
| 648 | Qd.FrameRect(rect) |
| 649 | rect = Qd.InsetRect(rect, 3, 3) |
| 650 | Qd.PenNormal() |
| 651 | Qd.RGBForeColor(self._color) |
| 652 | Qd.PaintRect(rect) |
| 653 | Qd.RGBForeColor((0, 0, 0)) |
| 654 | |
| 655 | |
| 656 | # misc utils |
| 657 | |
| 658 | def CallbackCall(callback, mustfit, *args): |
| 659 | """internal helper routine for W""" |
| 660 | # XXX this function should die. |
| 661 | if type(callback) == FunctionType: |
| 662 | func = callback |
| 663 | maxargs = func.func_code.co_argcount |
| 664 | elif type(callback) == MethodType: |
| 665 | func = callback.im_func |
| 666 | maxargs = func.func_code.co_argcount - 1 |
| 667 | else: |
| 668 | if callable(callback): |
| 669 | return apply(callback, args) |
| 670 | else: |
| 671 | raise TypeError, "uncallable callback object" |
| 672 | |
| 673 | if func.func_defaults: |
| 674 | minargs = maxargs - len(func.func_defaults) |
| 675 | else: |
| 676 | minargs = maxargs |
| 677 | if minargs <= len(args) <= maxargs: |
| 678 | return apply(callback, args) |
| 679 | elif not mustfit and minargs == 0: |
| 680 | return callback() |
| 681 | else: |
| 682 | if mustfit: |
| 683 | raise TypeError, "callback accepts wrong number of arguments: " + `len(args)` |
| 684 | else: |
| 685 | raise TypeError, "callback accepts wrong number of arguments: 0 or " + `len(args)` |
| 686 | |
| 687 | |
| 688 | def HasBaseClass(obj, class_): |
| 689 | try: |
| 690 | raise obj |
| 691 | except class_: |
| 692 | return 1 |
| 693 | except: |
| 694 | pass |
| 695 | return 0 |
| 696 | |
| 697 | |
Just van Rossum | 761b609 | 2002-11-24 01:01:07 +0000 | [diff] [blame] | 698 | # |
| 699 | # To remove the dependence of Widgets.rsrc we hardcode the cursor |
| 700 | # data below. |
| 701 | #_cursors = { |
| 702 | # "watch" : Qd.GetCursor(QuickDraw.watchCursor).data, |
Jack Jansen | 362c7cd0 | 2002-11-30 00:01:29 +0000 | [diff] [blame] | 703 | # "arrow" : Qd.GetQDGlobalsArrow(), |
Just van Rossum | 761b609 | 2002-11-24 01:01:07 +0000 | [diff] [blame] | 704 | # "iBeam" : Qd.GetCursor(QuickDraw.iBeamCursor).data, |
| 705 | # "cross" : Qd.GetCursor(QuickDraw.crossCursor).data, |
| 706 | # "plus" : Qd.GetCursor(QuickDraw.plusCursor).data, |
| 707 | # "hand" : Qd.GetCursor(468).data, |
| 708 | # "fist" : Qd.GetCursor(469).data, |
| 709 | # "hmover" : Qd.GetCursor(470).data, |
| 710 | # "vmover" : Qd.GetCursor(471).data, |
| 711 | # "zoomin" : Qd.GetCursor(472).data, |
| 712 | # "zoomout" : Qd.GetCursor(473).data, |
| 713 | # "zoom" : Qd.GetCursor(474).data, |
| 714 | #} |
| 715 | |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 716 | _cursors = { |
Just van Rossum | 761b609 | 2002-11-24 01:01:07 +0000 | [diff] [blame] | 717 | 'arrow': |
| 718 | '\x00\x00\x40\x00\x60\x00\x70\x00\x78\x00\x7c\x00\x7e\x00\x7f\x00' |
| 719 | '\x7f\x80\x7c\x00\x6c\x00\x46\x00\x06\x00\x03\x00\x03\x00\x00\x00' |
| 720 | '\xc0\x00\xe0\x00\xf0\x00\xf8\x00\xfc\x00\xfe\x00\xff\x00\xff\x80' |
| 721 | '\xff\xc0\xff\xe0\xfe\x00\xef\x00\xcf\x00\x87\x80\x07\x80\x03\x80' |
| 722 | '\x00\x01\x00\x01', |
| 723 | 'cross': |
| 724 | '\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\xff\xe0\x04\x00\x04\x00' |
| 725 | '\x04\x00\x04\x00\x04\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
| 726 | '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
| 727 | '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
| 728 | '\x00\x05\x00\x05', |
| 729 | 'fist': |
| 730 | '\x00\x00\x00\x00\x0d\x80\x12\x70\x12\x4c\x12\x4a\x28\x0a\x28\x02' |
| 731 | '\x48\x02\x40\x02\x20\x02\x20\x04\x10\x04\x08\x08\x04\x08\x04\x08' |
| 732 | '\x00\x00\x00\x00\x0d\x80\x1f\xf0\x1f\xfc\x1f\xfe\x3f\xfe\x3f\xfe' |
| 733 | '\x7f\xfe\x7f\xfe\x3f\xfe\x3f\xfc\x1f\xfc\x0f\xf8\x07\xf8\x07\xf8' |
| 734 | '\x00\x09\x00\x08', |
| 735 | 'hand': |
| 736 | '\x01\x80\x1a\x70\x26\x48\x26\x4a\x12\x4d\x12\x49\x68\x09\x98\x01' |
| 737 | '\x88\x02\x40\x02\x20\x02\x20\x04\x10\x04\x08\x08\x04\x08\x04\x08' |
| 738 | '\x01\x80\x1b\xf0\x3f\xf8\x3f\xfa\x1f\xff\x1f\xff\x6f\xff\xff\xff' |
| 739 | '\xff\xfe\x7f\xfe\x3f\xfe\x3f\xfc\x1f\xfc\x0f\xf8\x07\xf8\x07\xf8' |
| 740 | '\x00\x09\x00\x08', |
| 741 | 'hmover': |
| 742 | '\x00\x00\x01\x80\x01\x80\x01\x80\x01\x80\x11\x88\x31\x8c\x7f\xfe' |
| 743 | '\x31\x8c\x11\x88\x01\x80\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00' |
| 744 | '\x03\xc0\x03\xc0\x03\xc0\x03\xc0\x1b\xd8\x3b\xdc\x7f\xfe\xff\xff' |
| 745 | '\x7f\xfe\x3b\xdc\x1b\xd8\x03\xc0\x03\xc0\x03\xc0\x03\xc0\x00\x00' |
| 746 | '\x00\x07\x00\x07', |
| 747 | 'iBeam': |
| 748 | '\x0c\x60\x02\x80\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00' |
| 749 | '\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x02\x80\x0c\x60' |
| 750 | '\x0c\x60\x02\x80\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00' |
| 751 | '\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x02\x80\x0c\x60' |
| 752 | '\x00\x04\x00\x07', |
| 753 | 'plus': |
| 754 | '\x00\x00\x07\xc0\x04\x60\x04\x60\x04\x60\x7c\x7c\x43\x86\x42\x86' |
| 755 | '\x43\x86\x7c\x7e\x3c\x7e\x04\x60\x04\x60\x07\xe0\x03\xe0\x00\x00' |
| 756 | '\x0f\xc0\x0f\xe0\x0f\xf0\x0f\xf0\xff\xff\xff\xfe\xfc\x7f\xfc\x7f' |
| 757 | '\xfc\x7f\xff\xff\x7f\xff\x7f\xff\x0f\xf0\x0f\xf0\x07\xf0\x03\xe0' |
| 758 | '\x00\x08\x00\x08', |
| 759 | 'vmover': |
| 760 | '\x00\x00\x01\x00\x03\x80\x07\xc0\x01\x00\x01\x00\x01\x00\x7f\xfc' |
| 761 | '\x7f\xfc\x01\x00\x01\x00\x01\x00\x07\xc0\x03\x80\x01\x00\x00\x00' |
| 762 | '\x01\x00\x03\x80\x07\xc0\x0f\xe0\x0f\xe0\x03\x80\xff\xfe\xff\xfe' |
| 763 | '\xff\xfe\xff\xfe\x03\x80\x0f\xe0\x0f\xe0\x07\xc0\x03\x80\x01\x00' |
| 764 | '\x00\x07\x00\x07', |
| 765 | 'watch': |
| 766 | '\x3f\x00\x3f\x00\x3f\x00\x3f\x00\x40\x80\x84\x40\x84\x40\x84\x60' |
| 767 | '\x9c\x60\x80\x40\x80\x40\x40\x80\x3f\x00\x3f\x00\x3f\x00\x3f\x00' |
| 768 | '\x3f\x00\x3f\x00\x3f\x00\x3f\x00\x7f\x80\xff\xc0\xff\xc0\xff\xc0' |
| 769 | '\xff\xc0\xff\xc0\xff\xc0\x7f\x80\x3f\x00\x3f\x00\x3f\x00\x3f\x00' |
| 770 | '\x00\x08\x00\x08', |
| 771 | 'zoom': |
| 772 | '\x0f\x00\x30\xc0\x40\x20\x40\x20\x80\x10\x80\x10\x80\x10\x80\x10' |
| 773 | '\x40\x20\x40\x20\x30\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 774 | '\x0f\x00\x3f\xc0\x7f\xe0\x7f\xe0\xff\xf0\xff\xf0\xff\xf0\xff\xf0' |
| 775 | '\x7f\xe0\x7f\xe0\x3f\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 776 | '\x00\x06\x00\x06', |
| 777 | 'zoomin': |
| 778 | '\x0f\x00\x30\xc0\x40\x20\x46\x20\x86\x10\x9f\x90\x9f\x90\x86\x10' |
| 779 | '\x46\x20\x40\x20\x30\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 780 | '\x0f\x00\x3f\xc0\x7f\xe0\x7f\xe0\xff\xf0\xff\xf0\xff\xf0\xff\xf0' |
| 781 | '\x7f\xe0\x7f\xe0\x3f\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 782 | '\x00\x06\x00\x06', |
| 783 | 'zoomout': |
| 784 | '\x0f\x00\x30\xc0\x40\x20\x40\x20\x80\x10\x9f\x90\x9f\x90\x80\x10' |
| 785 | '\x40\x20\x40\x20\x30\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 786 | '\x0f\x00\x3f\xc0\x7f\xe0\x7f\xe0\xff\xf0\xff\xf0\xff\xf0\xff\xf0' |
| 787 | '\x7f\xe0\x7f\xe0\x3f\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 788 | '\x00\x06\x00\x06', |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 789 | } |
| 790 | |
| 791 | def SetCursor(what): |
| 792 | """Set the cursorshape to any of these: 'arrow', 'cross', 'fist', 'hand', 'hmover', 'iBeam', |
| 793 | 'plus', 'vmover', 'watch', 'zoom', 'zoomin', 'zoomout'.""" |
| 794 | Qd.SetCursor(_cursors[what]) |