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 | 8d97b9b | 2003-05-25 20:44:37 +0000 | [diff] [blame] | 11 | def _intRect((l, t, r, b)): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 12 | return (int(l), int(t), int(r), int(b)) |
Just van Rossum | 8d97b9b | 2003-05-25 20:44:37 +0000 | [diff] [blame] | 13 | |
| 14 | |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 15 | class Widget: |
Jack Jansen | 7302340 | 2001-01-23 14:58:20 +0000 | [diff] [blame] | 16 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 17 | """Base class for all widgets.""" |
| 18 | |
| 19 | _selectable = 0 |
| 20 | |
| 21 | def __init__(self, possize): |
| 22 | self._widgets = [] |
| 23 | self._widgetsdict = {} |
| 24 | self._possize = possize |
| 25 | self._bounds = None |
| 26 | self._visible = 1 |
| 27 | self._enabled = 0 |
| 28 | self._selected = 0 |
| 29 | self._activated = 0 |
| 30 | self._callback = None |
| 31 | self._parent = None |
| 32 | self._parentwindow = None |
| 33 | self._bindings = {} |
| 34 | self._backcolor = None |
| 35 | |
| 36 | def show(self, onoff): |
| 37 | self._visible = onoff |
| 38 | for w in self._widgets: |
| 39 | w.show(onoff) |
| 40 | if self._parentwindow is not None and self._parentwindow.wid is not None: |
| 41 | self.SetPort() |
| 42 | if onoff: |
| 43 | self.draw() |
| 44 | else: |
| 45 | Qd.EraseRect(self._bounds) |
| 46 | |
| 47 | def draw(self, visRgn = None): |
| 48 | if self._visible: |
| 49 | # draw your stuff here |
| 50 | pass |
| 51 | |
| 52 | def getpossize(self): |
| 53 | return self._possize |
| 54 | |
| 55 | def getbounds(self): |
| 56 | return self._bounds |
| 57 | |
| 58 | def move(self, x, y = None): |
| 59 | """absolute move""" |
| 60 | if y == None: |
| 61 | x, y = x |
| 62 | if type(self._possize) <> TupleType: |
| 63 | raise WidgetsError, "can't move widget with bounds function" |
| 64 | l, t, r, b = self._possize |
| 65 | self.resize(x, y, r, b) |
| 66 | |
| 67 | def rmove(self, x, y = None): |
| 68 | """relative move""" |
| 69 | if y == None: |
| 70 | x, y = x |
| 71 | if type(self._possize) <> TupleType: |
| 72 | raise WidgetsError, "can't move widget with bounds function" |
| 73 | l, t, r, b = self._possize |
| 74 | self.resize(l + x, t + y, r, b) |
| 75 | |
| 76 | def resize(self, *args): |
| 77 | if len(args) == 1: |
| 78 | if type(args[0]) == FunctionType or type(args[0]) == MethodType: |
| 79 | self._possize = args[0] |
| 80 | else: |
| 81 | apply(self.resize, args[0]) |
| 82 | elif len(args) == 2: |
| 83 | self._possize = (0, 0) + args |
| 84 | elif len(args) == 4: |
| 85 | self._possize = args |
| 86 | else: |
| 87 | raise TypeError, "wrong number of arguments" |
| 88 | self._calcbounds() |
| 89 | |
| 90 | def open(self): |
| 91 | self._calcbounds() |
| 92 | |
| 93 | def close(self): |
| 94 | del self._callback |
| 95 | del self._possize |
| 96 | del self._bindings |
| 97 | del self._parent |
| 98 | del self._parentwindow |
| 99 | |
| 100 | def bind(self, key, callback): |
| 101 | """bind a key or an 'event' to a callback""" |
| 102 | if callback: |
| 103 | self._bindings[key] = callback |
| 104 | elif self._bindings.has_key(key): |
| 105 | del self._bindings[key] |
| 106 | |
| 107 | def adjust(self, oldbounds): |
| 108 | self.SetPort() |
| 109 | self.GetWindow().InvalWindowRect(oldbounds) |
| 110 | self.GetWindow().InvalWindowRect(self._bounds) |
| 111 | |
| 112 | def _calcbounds(self): |
| 113 | # calculate absolute bounds relative to the window origin from our |
| 114 | # abstract _possize attribute, which is either a 4-tuple or a callable object |
| 115 | oldbounds = self._bounds |
| 116 | pl, pt, pr, pb = self._parent._bounds |
| 117 | if callable(self._possize): |
| 118 | # _possize is callable, let it figure it out by itself: it should return |
| 119 | # the bounds relative to our parent widget. |
| 120 | width = pr - pl |
| 121 | height = pb - pt |
| 122 | self._bounds = Qd.OffsetRect(_intRect(self._possize(width, height)), pl, pt) |
| 123 | else: |
| 124 | # _possize must be a 4-tuple. This is where the algorithm by Peter Kriens and |
| 125 | # Petr van Blokland kicks in. (*** Parts of this algorithm are applied for |
| 126 | # patents by Ericsson, Sweden ***) |
| 127 | l, t, r, b = self._possize |
| 128 | # depending on the values of l(eft), t(op), r(right) and b(ottom), |
| 129 | # they mean different things: |
| 130 | if l < -1: |
| 131 | # l is less than -1, this mean it measures from the *right* of it's parent |
| 132 | l = pr + l |
| 133 | else: |
| 134 | # l is -1 or greater, this mean it measures from the *left* of it's parent |
| 135 | l = pl + l |
| 136 | if t < -1: |
| 137 | # t is less than -1, this mean it measures from the *bottom* of it's parent |
| 138 | t = pb + t |
| 139 | else: |
| 140 | # t is -1 or greater, this mean it measures from the *top* of it's parent |
| 141 | t = pt + t |
| 142 | if r > 1: |
| 143 | # r is greater than 1, this means r is the *width* of the widget |
| 144 | r = l + r |
| 145 | else: |
| 146 | # r is less than 1, this means it measures from the *right* of it's parent |
| 147 | r = pr + r |
| 148 | if b > 1: |
| 149 | # b is greater than 1, this means b is the *height* of the widget |
| 150 | b = t + b |
| 151 | else: |
| 152 | # b is less than 1, this means it measures from the *bottom* of it's parent |
| 153 | b = pb + b |
| 154 | self._bounds = (l, t, r, b) |
| 155 | if oldbounds and oldbounds <> self._bounds: |
| 156 | self.adjust(oldbounds) |
| 157 | for w in self._widgets: |
| 158 | w._calcbounds() |
| 159 | |
| 160 | def test(self, point): |
| 161 | if Qd.PtInRect(point, self._bounds): |
| 162 | return 1 |
| 163 | |
| 164 | def click(self, point, modifiers): |
| 165 | pass |
| 166 | |
| 167 | def findwidget(self, point, onlyenabled = 1): |
| 168 | if self.test(point): |
| 169 | for w in self._widgets: |
| 170 | widget = w.findwidget(point) |
| 171 | if widget is not None: |
| 172 | return widget |
| 173 | if self._enabled or not onlyenabled: |
| 174 | return self |
| 175 | |
| 176 | def forall(self, methodname, *args): |
| 177 | for w in self._widgets: |
| 178 | rv = apply(w.forall, (methodname,) + args) |
| 179 | if rv: |
| 180 | return rv |
| 181 | if self._bindings.has_key("<" + methodname + ">"): |
| 182 | callback = self._bindings["<" + methodname + ">"] |
| 183 | rv = apply(callback, args) |
| 184 | if rv: |
| 185 | return rv |
| 186 | if hasattr(self, methodname): |
| 187 | method = getattr(self, methodname) |
| 188 | return apply(method, args) |
| 189 | |
| 190 | def forall_butself(self, methodname, *args): |
| 191 | for w in self._widgets: |
| 192 | rv = apply(w.forall, (methodname,) + args) |
| 193 | if rv: |
| 194 | return rv |
| 195 | |
| 196 | def forall_frombottom(self, methodname, *args): |
| 197 | if self._bindings.has_key("<" + methodname + ">"): |
| 198 | callback = self._bindings["<" + methodname + ">"] |
| 199 | rv = apply(callback, args) |
| 200 | if rv: |
| 201 | return rv |
| 202 | if hasattr(self, methodname): |
| 203 | method = getattr(self, methodname) |
| 204 | rv = apply(method, args) |
| 205 | if rv: |
| 206 | return rv |
| 207 | for w in self._widgets: |
| 208 | rv = apply(w.forall_frombottom, (methodname,) + args) |
| 209 | if rv: |
| 210 | return rv |
| 211 | |
| 212 | def _addwidget(self, key, widget): |
| 213 | if widget in self._widgets: |
| 214 | raise ValueError, "duplicate widget" |
| 215 | if self._widgetsdict.has_key(key): |
| 216 | self._removewidget(key) |
| 217 | self._widgets.append(widget) |
| 218 | self._widgetsdict[key] = widget |
| 219 | widget._parent = self |
| 220 | self._setparentwindow(widget) |
| 221 | if self._parentwindow and self._parentwindow.wid: |
| 222 | widget.forall_frombottom("open") |
| 223 | self.GetWindow().InvalWindowRect(widget._bounds) |
| 224 | |
| 225 | def _setparentwindow(self, widget): |
| 226 | widget._parentwindow = self._parentwindow |
| 227 | for w in widget._widgets: |
| 228 | self._setparentwindow(w) |
| 229 | |
| 230 | def _removewidget(self, key): |
| 231 | if not self._widgetsdict.has_key(key): |
| 232 | raise KeyError, "no widget with key %r" % (key,) |
| 233 | widget = self._widgetsdict[key] |
| 234 | for k in widget._widgetsdict.keys(): |
| 235 | widget._removewidget(k) |
| 236 | if self._parentwindow._currentwidget == widget: |
| 237 | widget.select(0) |
| 238 | self._parentwindow._currentwidget = None |
| 239 | self.SetPort() |
| 240 | self.GetWindow().InvalWindowRect(widget._bounds) |
| 241 | widget.close() |
| 242 | del self._widgetsdict[key] |
| 243 | self._widgets.remove(widget) |
| 244 | |
| 245 | def __setattr__(self, attr, value): |
| 246 | if type(value) == InstanceType and isinstance(value, Widget) and \ |
| 247 | attr not in ("_currentwidget", "_lastrollover", |
| 248 | "_parent", "_parentwindow", "_defaultbutton"): |
| 249 | if hasattr(self, attr): |
| 250 | raise ValueError, "Can't replace existing attribute: " + attr |
| 251 | self._addwidget(attr, value) |
| 252 | self.__dict__[attr] = value |
| 253 | |
| 254 | def __delattr__(self, attr): |
| 255 | if attr == "_widgetsdict": |
| 256 | raise AttributeError, "cannot delete attribute _widgetsdict" |
| 257 | if self._widgetsdict.has_key(attr): |
| 258 | self._removewidget(attr) |
| 259 | if self.__dict__.has_key(attr): |
| 260 | del self.__dict__[attr] |
| 261 | elif self.__dict__.has_key(attr): |
| 262 | del self.__dict__[attr] |
| 263 | else: |
| 264 | raise AttributeError, attr |
| 265 | |
| 266 | def __setitem__(self, key, value): |
| 267 | self._addwidget(key, value) |
| 268 | |
| 269 | def __getitem__(self, key): |
| 270 | if not self._widgetsdict.has_key(key): |
| 271 | raise KeyError, key |
| 272 | return self._widgetsdict[key] |
| 273 | |
| 274 | def __delitem__(self, key): |
| 275 | self._removewidget(key) |
| 276 | |
| 277 | def SetPort(self): |
| 278 | self._parentwindow.SetPort() |
| 279 | |
| 280 | |
| 281 | def GetWindow(self): |
| 282 | return self._parentwindow.GetWindow() |
| 283 | |
| 284 | def __del__(self): |
| 285 | if DEBUG: |
| 286 | print "%s instance deleted" % self.__class__.__name__ |
| 287 | |
| 288 | def _drawbounds(self): |
| 289 | Qd.FrameRect(self._bounds) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 290 | |
| 291 | |
| 292 | class ClickableWidget(Widget): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 293 | |
| 294 | """Base class for clickable widgets. (note: self._enabled must be true to receive click events.)""" |
| 295 | |
| 296 | def click(self, point, modifiers): |
| 297 | pass |
| 298 | |
| 299 | def enable(self, onoff): |
| 300 | self._enabled = onoff |
| 301 | self.SetPort() |
| 302 | self.draw() |
| 303 | |
| 304 | def callback(self): |
| 305 | if self._callback: |
| 306 | return CallbackCall(self._callback, 1) |
| 307 | |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 308 | |
| 309 | class SelectableWidget(ClickableWidget): |
| 310 | |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 311 | """Base class for selectable widgets.""" |
| 312 | |
| 313 | _selectable = 1 |
| 314 | |
| 315 | def select(self, onoff, isclick = 0): |
| 316 | if onoff == self._selected: |
| 317 | return 1 |
| 318 | if self._bindings.has_key("<select>"): |
| 319 | callback = self._bindings["<select>"] |
| 320 | if callback(onoff): |
| 321 | return 1 |
| 322 | self._selected = onoff |
| 323 | if onoff: |
| 324 | if self._parentwindow._currentwidget is not None: |
| 325 | self._parentwindow._currentwidget.select(0) |
| 326 | self._parentwindow._currentwidget = self |
| 327 | else: |
| 328 | self._parentwindow._currentwidget = None |
| 329 | |
| 330 | def key(self, char, event): |
| 331 | pass |
| 332 | |
| 333 | def drawselframe(self, onoff): |
| 334 | if not self._parentwindow._hasselframes: |
| 335 | return |
| 336 | App.DrawThemeFocusRect(self._bounds, onoff) |
| 337 | |
| 338 | def adjust(self, oldbounds): |
| 339 | self.SetPort() |
| 340 | if self._selected: |
| 341 | self.GetWindow().InvalWindowRect(Qd.InsetRect(oldbounds, -3, -3)) |
| 342 | self.GetWindow().InvalWindowRect(Qd.InsetRect(self._bounds, -3, -3)) |
| 343 | else: |
| 344 | self.GetWindow().InvalWindowRect(oldbounds) |
| 345 | self.GetWindow().InvalWindowRect(self._bounds) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 346 | |
| 347 | |
| 348 | class _Line(Widget): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 349 | |
| 350 | def __init__(self, possize, thickness = 1): |
| 351 | Widget.__init__(self, possize) |
| 352 | self._thickness = thickness |
| 353 | |
| 354 | def open(self): |
| 355 | self._calcbounds() |
| 356 | self.SetPort() |
| 357 | self.draw() |
| 358 | |
| 359 | def draw(self, visRgn = None): |
| 360 | if self._visible: |
| 361 | Qd.PaintRect(self._bounds) |
| 362 | |
| 363 | def _drawbounds(self): |
| 364 | pass |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 365 | |
| 366 | class HorizontalLine(_Line): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 367 | |
| 368 | def _calcbounds(self): |
| 369 | Widget._calcbounds(self) |
| 370 | l, t, r, b = self._bounds |
| 371 | self._bounds = l, t, r, t + self._thickness |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 372 | |
| 373 | class VerticalLine(_Line): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 374 | |
| 375 | def _calcbounds(self): |
| 376 | Widget._calcbounds(self) |
| 377 | l, t, r, b = self._bounds |
| 378 | self._bounds = l, t, l + self._thickness, b |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 379 | |
| 380 | |
| 381 | class Frame(Widget): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 382 | |
| 383 | def __init__(self, possize, pattern = Qd.GetQDGlobalsBlack(), color = (0, 0, 0)): |
| 384 | Widget.__init__(self, possize) |
| 385 | self._framepattern = pattern |
| 386 | self._framecolor = color |
| 387 | |
| 388 | def setcolor(self, color): |
| 389 | self._framecolor = color |
| 390 | self.SetPort() |
| 391 | self.draw() |
| 392 | |
| 393 | def setpattern(self, pattern): |
| 394 | self._framepattern = pattern |
| 395 | self.SetPort() |
| 396 | self.draw() |
| 397 | |
| 398 | def draw(self, visRgn = None): |
| 399 | if self._visible: |
| 400 | penstate = Qd.GetPenState() |
| 401 | Qd.PenPat(self._framepattern) |
| 402 | Qd.RGBForeColor(self._framecolor) |
| 403 | Qd.FrameRect(self._bounds) |
| 404 | Qd.RGBForeColor((0, 0, 0)) |
| 405 | Qd.SetPenState(penstate) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 406 | |
| 407 | def _darkencolor((r, g, b)): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 408 | return int(0.75 * r), int(0.75 * g), int(0.75 * b) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 409 | |
| 410 | class BevelBox(Widget): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 411 | |
| 412 | """'Platinum' beveled rectangle.""" |
| 413 | |
| 414 | def __init__(self, possize, color = (0xe000, 0xe000, 0xe000)): |
| 415 | Widget.__init__(self, possize) |
| 416 | self._color = color |
| 417 | self._darkercolor = _darkencolor(color) |
| 418 | |
| 419 | def setcolor(self, color): |
| 420 | self._color = color |
| 421 | self.SetPort() |
| 422 | self.draw() |
| 423 | |
| 424 | def draw(self, visRgn = None): |
| 425 | if self._visible: |
| 426 | l, t, r, b = Qd.InsetRect(self._bounds, 1, 1) |
| 427 | Qd.RGBForeColor(self._color) |
| 428 | Qd.PaintRect((l, t, r, b)) |
| 429 | Qd.RGBForeColor(self._darkercolor) |
| 430 | Qd.MoveTo(l, b) |
| 431 | Qd.LineTo(r, b) |
| 432 | Qd.LineTo(r, t) |
| 433 | Qd.RGBForeColor((0, 0, 0)) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 434 | |
| 435 | |
| 436 | class Group(Widget): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 437 | |
| 438 | """A container for subwidgets""" |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 439 | |
| 440 | |
| 441 | class HorizontalPanes(Widget): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 442 | |
| 443 | """Panes, a.k.a. frames. Works a bit like a group. Devides the widget area into "panes", |
| 444 | which can be resized by the user by clicking and dragging between the subwidgets.""" |
| 445 | |
| 446 | _direction = 1 |
| 447 | |
| 448 | def __init__(self, possize, panesizes = None, gutter = 8): |
| 449 | """panesizes should be a tuple of numbers. The length of the tuple is the number of panes, |
| 450 | the items in the tuple are the relative sizes of these panes; these numbers should add up |
| 451 | to 1 (the total size of all panes).""" |
| 452 | Widget.__init__(self, possize) |
| 453 | self._panesizes = panesizes |
| 454 | self._gutter = gutter |
| 455 | self._enabled = 1 |
| 456 | self.setuppanes() |
| 457 | |
| 458 | #def open(self): |
| 459 | # self.installbounds() |
| 460 | # ClickableWidget.open(self) |
| 461 | |
| 462 | def _calcbounds(self): |
| 463 | # hmmm. It should not neccesary be override _calcbounds :-( |
| 464 | self.installbounds() |
| 465 | Widget._calcbounds(self) |
| 466 | |
| 467 | def setuppanes(self): |
| 468 | panesizes = self._panesizes |
| 469 | total = 0 |
| 470 | if panesizes is not None: |
| 471 | #if len(self._widgets) <> len(panesizes): |
| 472 | # raise TypeError, 'number of widgets does not match number of panes' |
| 473 | for panesize in panesizes: |
| 474 | if not 0 < panesize < 1: |
| 475 | raise TypeError, 'pane sizes must be between 0 and 1, not including.' |
| 476 | total = total + panesize |
| 477 | if round(total, 4) <> 1.0: |
| 478 | raise TypeError, 'pane sizes must add up to 1' |
| 479 | else: |
| 480 | # XXX does not work! |
| 481 | step = 1.0 / len(self._widgets) |
| 482 | panesizes = [] |
| 483 | for i in range(len(self._widgets)): |
| 484 | panesizes.append(step) |
| 485 | current = 0 |
| 486 | self._panesizes = [] |
| 487 | self._gutters = [] |
| 488 | for panesize in panesizes: |
| 489 | if current: |
| 490 | self._gutters.append(current) |
| 491 | self._panesizes.append((current, current + panesize)) |
| 492 | current = current + panesize |
| 493 | self.makepanebounds() |
| 494 | |
| 495 | def getpanesizes(self): |
| 496 | return map(lambda (fr, to): to-fr, self._panesizes) |
| 497 | |
| 498 | boundstemplate = "lambda width, height: (0, height * %s + %d, width, height * %s + %d)" |
| 499 | |
| 500 | def makepanebounds(self): |
| 501 | halfgutter = self._gutter / 2 |
| 502 | self._panebounds = [] |
| 503 | for i in range(len(self._panesizes)): |
| 504 | panestart, paneend = self._panesizes[i] |
| 505 | boundsstring = self.boundstemplate % (repr(panestart), panestart and halfgutter, |
| 506 | repr(paneend), (paneend <> 1.0) and -halfgutter) |
| 507 | self._panebounds.append(eval(boundsstring)) |
| 508 | |
| 509 | def installbounds(self): |
| 510 | #self.setuppanes() |
| 511 | for i in range(len(self._widgets)): |
| 512 | w = self._widgets[i] |
| 513 | w._possize = self._panebounds[i] |
| 514 | #if hasattr(w, "setuppanes"): |
| 515 | # w.setuppanes() |
| 516 | if hasattr(w, "installbounds"): |
| 517 | w.installbounds() |
| 518 | |
| 519 | def rollover(self, point, onoff): |
| 520 | if onoff: |
| 521 | orgmouse = point[self._direction] |
| 522 | halfgutter = self._gutter / 2 |
| 523 | l, t, r, b = self._bounds |
| 524 | if self._direction: |
| 525 | begin, end = t, b |
| 526 | else: |
| 527 | begin, end = l, r |
| 528 | |
| 529 | i = self.findgutter(orgmouse, begin, end) |
| 530 | if i is None: |
| 531 | SetCursor("arrow") |
| 532 | else: |
| 533 | SetCursor(self._direction and 'vmover' or 'hmover') |
| 534 | |
| 535 | def findgutter(self, orgmouse, begin, end): |
| 536 | tolerance = max(4, self._gutter) / 2 |
| 537 | for i in range(len(self._gutters)): |
| 538 | pos = begin + (end - begin) * self._gutters[i] |
| 539 | if abs(orgmouse - pos) <= tolerance: |
| 540 | break |
| 541 | else: |
| 542 | return |
| 543 | return i |
| 544 | |
| 545 | def click(self, point, modifiers): |
| 546 | # what a mess... |
| 547 | orgmouse = point[self._direction] |
| 548 | halfgutter = self._gutter / 2 |
| 549 | l, t, r, b = self._bounds |
| 550 | if self._direction: |
| 551 | begin, end = t, b |
| 552 | else: |
| 553 | begin, end = l, r |
| 554 | |
| 555 | i = self.findgutter(orgmouse, begin, end) |
| 556 | if i is None: |
| 557 | return |
| 558 | |
| 559 | pos = orgpos = begin + (end - begin) * self._gutters[i] # init pos too, for fast click on border, bug done by Petr |
| 560 | |
| 561 | minpos = self._panesizes[i][0] |
| 562 | maxpos = self._panesizes[i+1][1] |
| 563 | minpos = begin + (end - begin) * minpos + 64 |
| 564 | maxpos = begin + (end - begin) * maxpos - 64 |
| 565 | if minpos > orgpos and maxpos < orgpos: |
| 566 | return |
| 567 | |
| 568 | #SetCursor("fist") |
| 569 | self.SetPort() |
| 570 | if self._direction: |
| 571 | rect = l, orgpos - 1, r, orgpos |
| 572 | else: |
| 573 | rect = orgpos - 1, t, orgpos, b |
| 574 | |
| 575 | # track mouse --- XXX move to separate method? |
| 576 | Qd.PenMode(QuickDraw.srcXor) |
| 577 | Qd.PenPat(Qd.GetQDGlobalsGray()) |
| 578 | Qd.PaintRect(_intRect(rect)) |
| 579 | lastpos = None |
| 580 | while Evt.Button(): |
| 581 | pos = orgpos - orgmouse + Evt.GetMouse()[self._direction] |
| 582 | pos = max(pos, minpos) |
| 583 | pos = min(pos, maxpos) |
| 584 | if pos == lastpos: |
| 585 | continue |
| 586 | Qd.PenPat(Qd.GetQDGlobalsGray()) |
| 587 | Qd.PaintRect(_intRect(rect)) |
| 588 | if self._direction: |
| 589 | rect = l, pos - 1, r, pos |
| 590 | else: |
| 591 | rect = pos - 1, t, pos, b |
| 592 | Qd.PenPat(Qd.GetQDGlobalsGray()) |
| 593 | Qd.PaintRect(_intRect(rect)) |
| 594 | lastpos = pos |
| 595 | self._parentwindow.wid.GetWindowPort().QDFlushPortBuffer(None) |
| 596 | Evt.WaitNextEvent(0, 3) |
| 597 | Qd.PaintRect(_intRect(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() |
Just van Rossum | 1f1e727 | 2001-12-08 10:37:40 +0000 | [diff] [blame] | 608 | |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 609 | |
| 610 | class VerticalPanes(HorizontalPanes): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 611 | """see HorizontalPanes""" |
| 612 | _direction = 0 |
| 613 | boundstemplate = "lambda width, height: (width * %s + %d, 0, width * %s + %d, height)" |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 614 | |
| 615 | |
| 616 | class ColorPicker(ClickableWidget): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 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.GetQDGlobalsGray()) |
| 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 | |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 659 | |
| 660 | # misc utils |
| 661 | |
| 662 | def CallbackCall(callback, mustfit, *args): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 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: %r" % len(args) |
| 688 | else: |
| 689 | raise TypeError, "callback accepts wrong number of arguments: 0 or %r" % len(args) |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 690 | |
| 691 | |
| 692 | def HasBaseClass(obj, class_): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 693 | try: |
| 694 | raise obj |
| 695 | except class_: |
| 696 | return 1 |
| 697 | except: |
| 698 | pass |
| 699 | return 0 |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 700 | |
| 701 | |
Just van Rossum | 761b609 | 2002-11-24 01:01:07 +0000 | [diff] [blame] | 702 | # |
| 703 | # To remove the dependence of Widgets.rsrc we hardcode the cursor |
| 704 | # data below. |
| 705 | #_cursors = { |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 706 | # "watch" : Qd.GetCursor(QuickDraw.watchCursor).data, |
| 707 | # "arrow" : Qd.GetQDGlobalsArrow(), |
| 708 | # "iBeam" : Qd.GetCursor(QuickDraw.iBeamCursor).data, |
| 709 | # "cross" : Qd.GetCursor(QuickDraw.crossCursor).data, |
| 710 | # "plus" : Qd.GetCursor(QuickDraw.plusCursor).data, |
| 711 | # "hand" : Qd.GetCursor(468).data, |
| 712 | # "fist" : Qd.GetCursor(469).data, |
| 713 | # "hmover" : Qd.GetCursor(470).data, |
| 714 | # "vmover" : Qd.GetCursor(471).data, |
| 715 | # "zoomin" : Qd.GetCursor(472).data, |
| 716 | # "zoomout" : Qd.GetCursor(473).data, |
| 717 | # "zoom" : Qd.GetCursor(474).data, |
Just van Rossum | 761b609 | 2002-11-24 01:01:07 +0000 | [diff] [blame] | 718 | #} |
| 719 | |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 720 | _cursors = { |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 721 | 'arrow': |
| 722 | '\x00\x00\x40\x00\x60\x00\x70\x00\x78\x00\x7c\x00\x7e\x00\x7f\x00' |
| 723 | '\x7f\x80\x7c\x00\x6c\x00\x46\x00\x06\x00\x03\x00\x03\x00\x00\x00' |
| 724 | '\xc0\x00\xe0\x00\xf0\x00\xf8\x00\xfc\x00\xfe\x00\xff\x00\xff\x80' |
| 725 | '\xff\xc0\xff\xe0\xfe\x00\xef\x00\xcf\x00\x87\x80\x07\x80\x03\x80' |
| 726 | '\x00\x01\x00\x01', |
| 727 | 'cross': |
| 728 | '\x04\x00\x04\x00\x04\x00\x04\x00\x04\x00\xff\xe0\x04\x00\x04\x00' |
| 729 | '\x04\x00\x04\x00\x04\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
| 730 | '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
| 731 | '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' |
| 732 | '\x00\x05\x00\x05', |
| 733 | 'fist': |
| 734 | '\x00\x00\x00\x00\x0d\x80\x12\x70\x12\x4c\x12\x4a\x28\x0a\x28\x02' |
| 735 | '\x48\x02\x40\x02\x20\x02\x20\x04\x10\x04\x08\x08\x04\x08\x04\x08' |
| 736 | '\x00\x00\x00\x00\x0d\x80\x1f\xf0\x1f\xfc\x1f\xfe\x3f\xfe\x3f\xfe' |
| 737 | '\x7f\xfe\x7f\xfe\x3f\xfe\x3f\xfc\x1f\xfc\x0f\xf8\x07\xf8\x07\xf8' |
| 738 | '\x00\x09\x00\x08', |
| 739 | 'hand': |
| 740 | '\x01\x80\x1a\x70\x26\x48\x26\x4a\x12\x4d\x12\x49\x68\x09\x98\x01' |
| 741 | '\x88\x02\x40\x02\x20\x02\x20\x04\x10\x04\x08\x08\x04\x08\x04\x08' |
| 742 | '\x01\x80\x1b\xf0\x3f\xf8\x3f\xfa\x1f\xff\x1f\xff\x6f\xff\xff\xff' |
| 743 | '\xff\xfe\x7f\xfe\x3f\xfe\x3f\xfc\x1f\xfc\x0f\xf8\x07\xf8\x07\xf8' |
| 744 | '\x00\x09\x00\x08', |
| 745 | 'hmover': |
| 746 | '\x00\x00\x01\x80\x01\x80\x01\x80\x01\x80\x11\x88\x31\x8c\x7f\xfe' |
| 747 | '\x31\x8c\x11\x88\x01\x80\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00' |
| 748 | '\x03\xc0\x03\xc0\x03\xc0\x03\xc0\x1b\xd8\x3b\xdc\x7f\xfe\xff\xff' |
| 749 | '\x7f\xfe\x3b\xdc\x1b\xd8\x03\xc0\x03\xc0\x03\xc0\x03\xc0\x00\x00' |
| 750 | '\x00\x07\x00\x07', |
| 751 | 'iBeam': |
| 752 | '\x0c\x60\x02\x80\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00' |
| 753 | '\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x02\x80\x0c\x60' |
| 754 | '\x0c\x60\x02\x80\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00' |
| 755 | '\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x02\x80\x0c\x60' |
| 756 | '\x00\x04\x00\x07', |
| 757 | 'plus': |
| 758 | '\x00\x00\x07\xc0\x04\x60\x04\x60\x04\x60\x7c\x7c\x43\x86\x42\x86' |
| 759 | '\x43\x86\x7c\x7e\x3c\x7e\x04\x60\x04\x60\x07\xe0\x03\xe0\x00\x00' |
| 760 | '\x0f\xc0\x0f\xe0\x0f\xf0\x0f\xf0\xff\xff\xff\xfe\xfc\x7f\xfc\x7f' |
| 761 | '\xfc\x7f\xff\xff\x7f\xff\x7f\xff\x0f\xf0\x0f\xf0\x07\xf0\x03\xe0' |
| 762 | '\x00\x08\x00\x08', |
| 763 | 'vmover': |
| 764 | '\x00\x00\x01\x00\x03\x80\x07\xc0\x01\x00\x01\x00\x01\x00\x7f\xfc' |
| 765 | '\x7f\xfc\x01\x00\x01\x00\x01\x00\x07\xc0\x03\x80\x01\x00\x00\x00' |
| 766 | '\x01\x00\x03\x80\x07\xc0\x0f\xe0\x0f\xe0\x03\x80\xff\xfe\xff\xfe' |
| 767 | '\xff\xfe\xff\xfe\x03\x80\x0f\xe0\x0f\xe0\x07\xc0\x03\x80\x01\x00' |
| 768 | '\x00\x07\x00\x07', |
| 769 | 'watch': |
| 770 | '\x3f\x00\x3f\x00\x3f\x00\x3f\x00\x40\x80\x84\x40\x84\x40\x84\x60' |
| 771 | '\x9c\x60\x80\x40\x80\x40\x40\x80\x3f\x00\x3f\x00\x3f\x00\x3f\x00' |
| 772 | '\x3f\x00\x3f\x00\x3f\x00\x3f\x00\x7f\x80\xff\xc0\xff\xc0\xff\xc0' |
| 773 | '\xff\xc0\xff\xc0\xff\xc0\x7f\x80\x3f\x00\x3f\x00\x3f\x00\x3f\x00' |
| 774 | '\x00\x08\x00\x08', |
| 775 | 'zoom': |
| 776 | '\x0f\x00\x30\xc0\x40\x20\x40\x20\x80\x10\x80\x10\x80\x10\x80\x10' |
| 777 | '\x40\x20\x40\x20\x30\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 778 | '\x0f\x00\x3f\xc0\x7f\xe0\x7f\xe0\xff\xf0\xff\xf0\xff\xf0\xff\xf0' |
| 779 | '\x7f\xe0\x7f\xe0\x3f\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 780 | '\x00\x06\x00\x06', |
| 781 | 'zoomin': |
| 782 | '\x0f\x00\x30\xc0\x40\x20\x46\x20\x86\x10\x9f\x90\x9f\x90\x86\x10' |
| 783 | '\x46\x20\x40\x20\x30\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 784 | '\x0f\x00\x3f\xc0\x7f\xe0\x7f\xe0\xff\xf0\xff\xf0\xff\xf0\xff\xf0' |
| 785 | '\x7f\xe0\x7f\xe0\x3f\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 786 | '\x00\x06\x00\x06', |
| 787 | 'zoomout': |
| 788 | '\x0f\x00\x30\xc0\x40\x20\x40\x20\x80\x10\x9f\x90\x9f\x90\x80\x10' |
| 789 | '\x40\x20\x40\x20\x30\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 790 | '\x0f\x00\x3f\xc0\x7f\xe0\x7f\xe0\xff\xf0\xff\xf0\xff\xf0\xff\xf0' |
| 791 | '\x7f\xe0\x7f\xe0\x3f\xf0\x0f\x38\x00\x1c\x00\x0e\x00\x07\x00\x02' |
| 792 | '\x00\x06\x00\x06', |
Just van Rossum | 40f9b7b | 1999-01-30 22:39:17 +0000 | [diff] [blame] | 793 | } |
| 794 | |
| 795 | def SetCursor(what): |
Tim Peters | 182b5ac | 2004-07-18 06:16:08 +0000 | [diff] [blame^] | 796 | """Set the cursorshape to any of these: 'arrow', 'cross', 'fist', 'hand', 'hmover', 'iBeam', |
| 797 | 'plus', 'vmover', 'watch', 'zoom', 'zoomin', 'zoomout'.""" |
| 798 | Qd.SetCursor(_cursors[what]) |