PageRenderTime 1679ms CodeModel.GetById 91ms app.highlight 1450ms RepoModel.GetById 65ms app.codeStats 0ms

/library/ui.py

http://pyjamas.googlecode.com/
Python | 2048 lines | 1972 code | 40 blank | 36 comment | 38 complexity | 4b04681a7447a7c2a260bda6a04419da MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1# Copyright 2006 James Tauber and contributors

   2#

   3# Licensed under the Apache License, Version 2.0 (the "License");

   4# you may not use this file except in compliance with the License.

   5# You may obtain a copy of the License at

   6#

   7#     http://www.apache.org/licenses/LICENSE-2.0

   8#

   9# Unless required by applicable law or agreed to in writing, software

  10# distributed under the License is distributed on an "AS IS" BASIS,

  11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

  12# See the License for the specific language governing permissions and

  13# limitations under the License.

  14from __pyjamas__ import JS, console
  15import DOM
  16import pygwt
  17from DeferredCommand import DeferredCommand
  18import pyjslib
  19from History import History
  20import Window
  21from sets import Set
  22
  23
  24class Event:
  25    """

  26    This class contains flags and integer values used by the event system.

  27    

  28    It is not meant to be subclassed or instantiated.

  29    """
  30    BUTTON_LEFT   = 1
  31    BUTTON_MIDDLE = 4
  32    BUTTON_RIGHT  = 2
  33    
  34    ONBLUR        = 0x01000
  35    ONCHANGE      = 0x00400
  36    ONCLICK       = 0x00001
  37    ONDBLCLICK    = 0x00002
  38    ONERROR       = 0x10000
  39    ONFOCUS       = 0x00800
  40    ONKEYDOWN     = 0x00080
  41    ONKEYPRESS    = 0x00100
  42    ONKEYUP       = 0x00200
  43    ONLOAD        = 0x08000
  44    ONLOSECAPTURE = 0x02000
  45    ONMOUSEDOWN   = 0x00004
  46    ONMOUSEMOVE   = 0x00040
  47    ONMOUSEOUT    = 0x00020
  48    ONMOUSEOVER   = 0x00010
  49    ONMOUSEUP     = 0x00008
  50    ONSCROLL      = 0x04000
  51    
  52    FOCUSEVENTS   = 0x01800 # ONFOCUS | ONBLUR

  53    KEYEVENTS     = 0x00380 # ONKEYDOWN | ONKEYPRESS | ONKEYUP

  54    MOUSEEVENTS   = 0x0007C # ONMOUSEDOWN | ONMOUSEUP | ONMOUSEMOVE | ONMOUSEOVER | ONMOUSEOUT

  55
  56
  57# FocusListenerCollection

  58class FocusListener:
  59    def fireFocusEvent(self, listeners, sender, event):
  60        type = DOM.eventGetType(event)
  61        if type == "focus":
  62            for listener in listeners:
  63                listener.onFocus(sender)
  64        elif type == "blur":
  65            for listener in listeners:
  66                listener.onLostFocus(sender)
  67
  68
  69# KeyboardListener + KeyboardListenerCollection

  70class KeyboardListener:
  71    KEY_ALT = 18
  72    KEY_BACKSPACE = 8
  73    KEY_CTRL = 17
  74    KEY_DELETE = 46
  75    KEY_DOWN = 40
  76    KEY_END = 35
  77    KEY_ENTER = 13
  78    KEY_ESCAPE = 27
  79    KEY_HOME = 36
  80    KEY_LEFT = 37
  81    KEY_PAGEDOWN = 34
  82    KEY_PAGEUP = 33
  83    KEY_RIGHT = 39
  84    KEY_SHIFT = 16
  85    KEY_TAB = 9
  86    KEY_UP = 38
  87    
  88    MODIFIER_ALT = 4
  89    MODIFIER_CTRL = 2
  90    MODIFIER_SHIFT = 1
  91
  92    def getKeyboardModifiers(self, event):
  93        shift = 0
  94        ctrl = 0
  95        alt = 0
  96        
  97        if DOM.eventGetShiftKey(event):
  98            shift = KeyboardListener.MODIFIER_SHIFT
  99    
 100        if DOM.eventGetCtrlKey(event):
 101            ctrl = KeyboardListener.MODIFIER_CTRL
 102            
 103        if DOM.eventGetAltKey(event):
 104            alt = KeyboardListener.MODIFIER_ALT
 105    
 106        return shift | ctrl | alt
 107
 108
 109    def fireKeyboardEvent(self, listeners, sender, event):
 110        modifiers = KeyboardListener.getKeyboardModifiers(self, event)
 111    
 112        type = DOM.eventGetType(event)
 113        if type == "keydown":
 114            for listener in listeners:
 115                listener.onKeyDown(sender, DOM.eventGetKeyCode(event), modifiers)
 116        elif type == "keyup":
 117            for listener in listeners:
 118                listener.onKeyUp(sender, DOM.eventGetKeyCode(event), modifiers)
 119        elif type == "keypress":
 120            for listener in listeners:
 121                listener.onKeyPress(sender, DOM.eventGetKeyCode(event), modifiers)
 122
 123
 124# MouseListenerCollection

 125class MouseListener:
 126    def fireMouseEvent(self, listeners, sender, event):
 127        x = DOM.eventGetClientX(event) - DOM.getAbsoluteLeft(sender.getElement())
 128        y = DOM.eventGetClientY(event) - DOM.getAbsoluteTop(sender.getElement())
 129    
 130        type = DOM.eventGetType(event)
 131        if type == "mousedown":
 132            for listener in listeners:
 133                listener.onMouseDown(sender, x, y)
 134        elif type == "mouseup":
 135            for listener in listeners:
 136                listener.onMouseUp(sender, x, y)
 137        elif type == "mousemove":
 138            for listener in listeners:
 139                listener.onMouseMove(sender, x, y)
 140        elif type == "mouseover":
 141            from_element = DOM.eventGetFromElement(event)
 142            if not DOM.isOrHasChild(sender.getElement(), from_element):
 143                for listener in listeners:
 144                    listener.onMouseEnter(sender)
 145        elif type == "mouseout":
 146            to_element = DOM.eventGetToElement(event)
 147            if not DOM.isOrHasChild(sender.getElement(), to_element):
 148                for listener in listeners:
 149                    listener.onMouseLeave(sender)
 150
 151
 152class UIObject:
 153
 154    def getAbsoluteLeft(self):
 155        return DOM.getAbsoluteLeft(self.getElement())
 156
 157    def getAbsoluteTop(self):
 158        return DOM.getAbsoluteTop(self.getElement())
 159
 160    def getElement(self):
 161        """Get the DOM element associated with the UIObject, if any"""
 162        return self.element
 163
 164    def getOffsetHeight(self):
 165        return DOM.getIntAttribute(self.element, "offsetHeight")
 166    
 167    def getOffsetWidth(self):
 168        return DOM.getIntAttribute(self.element, "offsetWidth")
 169
 170    def getStyleName(self):
 171        return DOM.getAttribute(self.element, "className")
 172
 173    def getTitle(self):
 174        return DOM.getAttribute(self.element, "title")
 175
 176    def setElement(self, element):
 177        """Set the DOM element associated with the UIObject."""
 178        self.element = element
 179
 180    def setHeight(self, height):
 181        """Set the height of the element associated with this UIObject.  The

 182           value should be given as a CSS value, such as 100px, 30%, or 50pi"""
 183        DOM.setStyleAttribute(self.element, "height", height)
 184
 185    def setPixelSize(self, width, height):
 186        """Set the width and height of the element associated with this UIObject

 187           in pixels.  Width and height should be numbers."""
 188        if width >= 0:
 189            self.setWidth(width + "px")
 190        if height >= 0:
 191            self.setHeight(height + "px")
 192
 193    def setSize(self, width, height):
 194        """Set the width and height of the element associated with this UIObject.  The

 195           values should be given as a CSS value, such as 100px, 30%, or 50pi"""
 196        self.setWidth(width)
 197        self.setHeight(height)
 198
 199    def addStyleName(self, style):
 200        """Append a style to the element associated with this UIObject.  This is

 201        a CSS class name.  It will be added after any already-assigned CSS class for

 202        the element."""
 203        self.setStyleName(self.element, style, True)
 204
 205    def removeStyleName(self, style):
 206        """Remove a style from the element associated with this UIObject.  This is

 207        a CSS class name."""
 208        self.setStyleName(self.element, style, False)
 209
 210    # also callable as: setStyleName(self, style)

 211    def setStyleName(self, element, style=None, add=True):
 212        """When called with a single argument, this replaces all the CSS classes

 213        associated with this UIObject's element with the given parameter.  Otherwise,

 214        this is assumed to be a worker function for addStyleName and removeStyleName."""
 215        # emulate setStyleName(self, style)

 216        if style == None:
 217            style = element
 218            DOM.setAttribute(self.element, "className", style)
 219            return
 220        
 221        oldStyle = DOM.getAttribute(element, "className")
 222        if oldStyle == None:
 223            oldStyle = ""
 224        idx = oldStyle.find(style)
 225
 226        # Calculate matching index

 227        lastPos = len(oldStyle)
 228        while idx != -1:
 229            if idx == 0 or (oldStyle[idx - 1] == " "):
 230                last = idx + len(style)
 231                if (last == lastPos) or ((last < lastPos) and (oldStyle[last] == " ")):
 232                    break
 233            idx = oldStyle.find(style, idx + 1)
 234
 235        if add:
 236            if idx == -1:
 237                DOM.setAttribute(element, "className", oldStyle + " " + style)
 238        else:
 239            if idx != -1:
 240                begin = oldStyle[:idx]
 241                end = oldStyle[idx + len(style):]
 242                DOM.setAttribute(element, "className", begin + end)
 243
 244    def setTitle(self, title):
 245        DOM.setAttribute(self.element, "title", title)
 246
 247    def setWidth(self, width):
 248        """Set the width of the element associated with this UIObject.  The

 249           value should be given as a CSS value, such as 100px, 30%, or 50pi"""
 250        DOM.setStyleAttribute(self.element, "width", width)
 251
 252    def sinkEvents(self, eventBitsToAdd):
 253        """Request that the given events be delivered to the event handler for this

 254        element.  The event bits passed are added (using inclusive OR) to the events

 255        already "sunk" for the element associated with the UIObject.  The event bits

 256        are a combination of values from class L{Event}."""
 257        if self.element:
 258            DOM.sinkEvents(self.getElement(), eventBitsToAdd | DOM.getEventsSunk(self.getElement()))
 259
 260    def isVisible(self, element=None):
 261        """Determine whether this element is currently visible, by checking the CSS

 262        property 'display'"""
 263        if not element:
 264            element = self.element
 265        return element.style.display != "none"
 266
 267    # also callable as: setVisible(visible)

 268    def setVisible(self, element, visible=None):
 269        """Set whether this element is visible or not.  If a single parameter is

 270        given, the self.element is used.  This modifies the CSS property 'display',

 271        which means that an invisible element not only is not drawn, but doesn't

 272        occupy any space on the page."""
 273        if visible==None:
 274            visible = element
 275            element = self.element
 276
 277        if visible:
 278            element.style.display = ""
 279        else:
 280            element.style.display = "none"
 281
 282    def unsinkEvents(self, eventBitsToRemove):
 283        """Reverse the operation of sinkEvents.  See L{UIObject.sinkevents}."""
 284        DOM.sinkEvents(self.getElement(), ~eventBitsToRemove & DOM.getEventsSunk(self.getElement()))
 285
 286
 287class Widget(UIObject):
 288    """

 289        Base class for most of the UI classes.  This class provides basic services

 290        used by any Widget, including management of parents and adding/removing the

 291        event handler association with the DOM.

 292    """
 293    def __init__(self):
 294        self.attached = False
 295        self.parent = None
 296        self.layoutData = None
 297
 298    def getLayoutData(self):
 299        return self.layoutData
 300    
 301    def getParent(self):
 302        """Widgets are kept in a hierarchy, and widgets that have been added to a panel

 303        will have a parent widget that contains them.  This retrieves the containing

 304        widget for this widget."""
 305        return self.parent
 306
 307    def isAttached(self):
 308        """Return whether or not this widget has been attached to the document."""
 309        return self.attached
 310
 311    def onBrowserEvent(self, event):
 312        pass
 313
 314    def onLoad(self):
 315        pass
 316
 317    def onAttach(self):
 318        """Called when this widget has an element, and that element is on the document's

 319        DOM tree, and we have a parent widget."""
 320        if self.attached:
 321            return
 322        self.attached = True
 323        DOM.setEventListener(self.getElement(), self)
 324        self.onLoad()
 325        
 326    def onDetach(self):
 327        """Called when this widget is being removed from the DOM tree of the document."""
 328        if not self.attached:
 329            return
 330        self.attached = False
 331        DOM.setEventListener(self.getElement(), None)
 332
 333    def setLayoutData(self, layoutData):
 334        self.layoutData = layoutData
 335
 336    def setParent(self, parent):
 337        """Update the parent attribute.  If the parent is currently attached to the DOM this

 338        assumes we are being attached also and calls onAttach()."""
 339        self.parent = parent
 340        if parent == None:
 341            self.onDetach()
 342        elif parent.attached:
 343            self.onAttach()
 344
 345    def removeFromParent(self):
 346        """Remove ourself from our parent.  The parent widget will call setParent(None) on

 347        us automatically"""
 348        if hasattr(self.parent, "remove"):
 349            self.parent.remove(self)
 350
 351    def getID(self):
 352        """Get the id attribute of the associated DOM element."""
 353        return DOM.getAttribute(self.getElement(), "id")
 354
 355    def setID(self, id):
 356        """Set the id attribute of the associated DOM element."""
 357        DOM.setAttribute(self.getElement(), "id", id)
 358
 359
 360class FocusWidget(Widget):
 361
 362    def __init__(self, element):
 363        Widget.__init__(self)
 364        self.clickListeners = []
 365        self.focusListeners = []
 366        self.keyboardListeners = []
 367
 368        self.setElement(element)
 369        self.sinkEvents(Event.ONCLICK | Event.FOCUSEVENTS | Event.KEYEVENTS)
 370
 371    def addClickListener(self, listener):
 372        self.clickListeners.append(listener)
 373        
 374    def addFocusListener(self, listener):
 375        self.focusListeners.append(listener)
 376
 377    def addKeyboardListener(self, listener):
 378        self.keyboardListeners.append(listener)
 379
 380    def getTabIndex(self):
 381        return Focus.getTabIndex(self, self.getElement())
 382
 383    def isEnabled(self):
 384        return not DOM.getBooleanAttribute(self.getElement(), "disabled")
 385
 386    def onBrowserEvent(self, event):
 387        type = DOM.eventGetType(event)
 388        if type == "click":
 389            for listener in self.clickListeners:
 390                if listener.onClick: listener.onClick(self, event)
 391                else: listener(self, event)
 392        elif type == "blur" or type == "focus":
 393            FocusListener.fireFocusEvent(self, self.focusListeners, self, event)
 394        elif type == "keydown" or type == "keypress" or type == "keyup":
 395            KeyboardListener.fireKeyboardEvent(self, self.keyboardListeners, self, event)
 396
 397    def removeClickListener(self, listener):
 398        self.clickListeners.remove(listener)
 399
 400    def removeFocusListener(self, listener):
 401        self.focusListeners.remove(listener)
 402
 403    def removeKeyboardListener(self, listener):
 404        self.keyboardListeners.remove(listener)
 405
 406    def setAccessKey(self, key):
 407        DOM.setAttribute(self.getElement(), "accessKey", "" + key)
 408        
 409    def setEnabled(self, enabled):
 410        DOM.setBooleanAttribute(self.getElement(), "disabled", not enabled)
 411
 412    def setFocus(self, focused):
 413        if (focused):
 414            Focus.focus(self, self.getElement())
 415        else:
 416            Focus.blur(self, self.getElement())
 417
 418    def setTabIndex(self, index):
 419        Focus.setTabIndex(self, self.getElement(), index)
 420
 421
 422class ButtonBase(FocusWidget):
 423
 424    def __init__(self, element):
 425        FocusWidget.__init__(self, element)
 426
 427    def getHTML(self):
 428        return DOM.getInnerHTML(self.getElement())
 429
 430    def getText(self):
 431        return DOM.getInnerText(self.getElement())
 432
 433    def setHTML(self, html):
 434        DOM.setInnerHTML(self.getElement(), html)
 435
 436    def setText(self, text):
 437        DOM.setInnerText(self.getElement(), text)
 438
 439
 440class Button(ButtonBase):
 441
 442    def __init__(self, html=None, listener=None):
 443        """

 444        Create a new button widget.

 445        

 446        @param html: Html content (e.g. the button label); see setHTML()

 447        @param listener: A new click listener; see addClickListener()

 448        

 449        """
 450        ButtonBase.__init__(self, DOM.createButton())
 451        self.adjustType(self.getElement())
 452        self.setStyleName("gwt-Button")
 453        if html:
 454            self.setHTML(html)
 455        if listener:
 456            self.addClickListener(listener)
 457
 458    def adjustType(self, button):
 459        JS("""

 460        if (button.type == 'submit') {

 461            try { button.setAttribute("type", "button"); } catch (e) { }

 462        }

 463        """)
 464
 465    def click(self):
 466        """

 467        Simulate a button click.

 468        """
 469        self.getElement().click()
 470
 471
 472class CheckBox(ButtonBase):
 473    
 474    def __init__(self, label=None, asHTML=False):
 475        self.initElement(DOM.createInputCheck())
 476        
 477        self.setStyleName("gwt-CheckBox")
 478        if label:
 479            if asHTML:
 480                self.setHTML(label)
 481            else:
 482                self.setText(label)
 483
 484    def initElement(self, element):
 485        ButtonBase.__init__(self, DOM.createSpan())
 486        self.inputElem = element
 487        self.labelElem = DOM.createLabel()
 488
 489        self.unsinkEvents(Event.FOCUSEVENTS| Event.ONCLICK)
 490        DOM.sinkEvents(self.inputElem, Event.FOCUSEVENTS | Event.ONCLICK | DOM.getEventsSunk(self.inputElem))
 491        
 492        DOM.appendChild(self.getElement(), self.inputElem)
 493        DOM.appendChild(self.getElement(), self.labelElem)
 494        
 495        uid = "check" + self.getUniqueID()
 496        DOM.setAttribute(self.inputElem, "id", uid)
 497        DOM.setAttribute(self.labelElem, "htmlFor", uid)
 498
 499    # emulate static

 500    def getUniqueID(self):
 501        JS("""

 502        _CheckBox_unique_id++;

 503        return _CheckBox_unique_id;

 504        };

 505        var _CheckBox_unique_id=0;

 506        {

 507        """)
 508
 509    def getHTML(self):
 510        return DOM.getInnerHTML(self.labelElem)
 511
 512    def getName(self):
 513        return DOM.getAttribute(self.inputElem, "name")
 514
 515    def getText(self):
 516        return DOM.getInnerText(self.labelElem)
 517
 518    def setChecked(self, checked):
 519        DOM.setBooleanAttribute(self.inputElem, "checked", checked)
 520        DOM.setBooleanAttribute(self.inputElem, "defaultChecked", checked)
 521
 522    def isChecked(self):
 523        if self.attached:
 524            propName = "checked"
 525        else:
 526            propName = "defaultChecked"
 527            
 528        return DOM.getBooleanAttribute(self.inputElem, propName)
 529
 530    def isEnabled(self):
 531        return not DOM.getBooleanAttribute(self.inputElem, "disabled")
 532
 533    def setEnabled(self, enabled):
 534        DOM.setBooleanAttribute(self.inputElem, "disabled", not enabled)
 535
 536    def setFocus(self, focused):
 537        if focused:
 538            Focus.focus(self, self.inputElem)
 539        else:
 540            Focus.blur(self, self.inputElem)
 541
 542    def setHTML(self, html):
 543        DOM.setInnerHTML(self.labelElem, html)
 544
 545    def setName(self, name):
 546        DOM.setAttribute(self.inputElem, "name", name)
 547
 548    def setTabIndex(self, index):
 549        Focus.setTabIndex(self, self.inputElem, index)
 550
 551    def setText(self, text):
 552        DOM.setInnerText(self.labelElem, text)
 553
 554    def onDetach(self):
 555        self.setChecked(self.isChecked())
 556        ButtonBase.onDetach(self)
 557
 558
 559class RadioButton(CheckBox):
 560    def __init__(self, group, label=None, asHTML=False):
 561        self.initElement(DOM.createInputRadio(group))
 562
 563        self.setStyleName("gwt-RadioButton")
 564        if label:
 565            if asHTML:
 566                self.setHTML(label)
 567            else:
 568                self.setText(label)
 569
 570
 571class Composite(Widget):
 572    def __init__(self):
 573        Widget.__init__(self)
 574        self.widget = None
 575
 576    def initWidget(self, widget):
 577        if self.widget != None:
 578            return
 579
 580        widget.removeFromParent()
 581        self.setElement(widget.getElement())
 582
 583        self.widget = widget
 584        widget.setParent(self)
 585
 586    def onAttach(self):
 587        Widget.onAttach(self)
 588        self.widget.onAttach()
 589        
 590    def onDetach(self):
 591        Widget.onDetach(self)
 592        self.widget.onDetach()
 593        
 594    def setWidget(self, widget):
 595        self.initWidget(widget)
 596
 597
 598class Panel(Widget):
 599    def __init__(self):
 600        Widget.__init__(self)
 601        self.children = []
 602
 603    def add(self):
 604        console.error("This panel does not support no-arg add()")
 605
 606    # TODO: fix iterator remove()

 607    def clear(self):
 608        for child in list(self):
 609            # should be iterator.remove() ==> self.remove(self.children[0]) or self.remove(child) ???

 610            self.remote(child)
 611
 612    def disown(self, widget):
 613        if widget.getParent() != self:
 614            console.error("widget %o is not a child of this panel %o", widget, self)
 615        else:
 616            element = widget.getElement()
 617            widget.setParent(None)
 618            parentElement = DOM.getParent(element)
 619            if parentElement:
 620                DOM.removeChild(parentElement, element)
 621
 622    def adopt(self, widget, container):
 623        widget.removeFromParent()
 624        if container:
 625            DOM.appendChild(container, widget.getElement())
 626        widget.setParent(self)
 627
 628    def remove(self, widget):
 629        pass
 630
 631    def onAttach(self):
 632        Widget.onAttach(self)
 633        for child in self:
 634            child.onAttach()
 635
 636    def onDetach(self):
 637        Widget.onDetach(self)
 638        for child in self:
 639            child.onDetach()
 640
 641    def __iter__(self):
 642        return self.children.__iter__()
 643
 644
 645class CellFormatter:
 646    
 647    def __init__(self, outer):
 648        self.outer = outer
 649    
 650    def addStyleName(self, row, column, styleName):
 651        self.outer.prepareCell(row, column)
 652        self.outer.setStyleName(self.getElement(row, column), styleName, True)
 653
 654    def getElement(self, row, column):
 655        self.outer.checkCellBounds(row, column)
 656        return DOM.getChild(self.outer.rowFormatter.getRow(self.outer.bodyElem, row), column)
 657
 658    def getStyleName(self, row, column):
 659        return DOM.getAttribute(self.getElement(row, column), "className")
 660
 661    def isVisible(self, row, column):
 662        element = self.getElement(row, column)
 663        return self.outer.isVisible(element)
 664
 665    def removeStyleName(self, row, column, styleName):
 666        self.checkCellBounds(row, column)
 667        self.outer.setStyleName(self.getElement(row, column), styleName, False)
 668
 669    def setAlignment(self, row, column, hAlign, vAlign):
 670        self.setHorizontalAlignment(row, column, hAlign)
 671        self.setVerticalAlignment(row, column, vAlign)
 672
 673    def setHeight(self, row, column, height):
 674        self.outer.prepareCell(row, column)
 675        element = self.getCellElement(self.outer.bodyElem, row, column)
 676        DOM.setStyleAttribute(element, "height", height)
 677
 678    def setHorizontalAlignment(self, row, column, align):
 679        self.outer.prepareCell(row, column)
 680        element = self.getCellElement(self.outer.bodyElem, row, column)
 681        DOM.setAttribute(element, "align", align)
 682
 683    def setStyleName(self, row, column, styleName):
 684        self.outer.prepareCell(row, column)
 685        self.setAttr(row, column, "className", styleName)
 686
 687    def setVerticalAlignment(self, row, column, align):
 688        self.outer.prepareCell(row, column)
 689        DOM.setStyleAttribute(self.getCellElement(self.outer.bodyElem, row, column), "verticalAlign", align)
 690
 691    def setVisible(self, row, column, visible):
 692        element = self.ensureElement(row, column)
 693        self.outer.setVisible(element, visible)
 694
 695    def setWidth(self, row, column, width):
 696        self.outer.prepareCell(row, column)
 697        DOM.setStyleAttribute(self.getCellElement(self.outer.bodyElem, row, column), "width", width)
 698
 699    def setWordWrap(self, row, column, wrap):
 700        self.outer.prepareCell(row, column)
 701        if wrap:
 702            wrap_str = ""
 703        else:
 704            wrap_str = "nowrap"
 705        
 706        DOM.setStyleAttribute(self.getElement(row, column), "whiteSpace", wrap_str)
 707
 708    def getCellElement(self, table, row, col):
 709        JS("""

 710        var out = table.rows[row].cells[col];

 711        return (out == null ? null : out);

 712        """)
 713
 714    def getRawElement(self, row, column):
 715        return self.getCellElement(self.outer.bodyElem, row, column)
 716
 717    def ensureElement(self, row, column):
 718        self.outer.prepareCell(row, column)
 719        return DOM.getChild(self.outer.rowFormatter.ensureElement(row), column)
 720
 721    def getAttr(self, row, column, attr):
 722        elem = self.getElement(row, column)
 723        return DOM.getAttribute(elem, attr)
 724
 725    def setAttr(self, row, column, attrName, value):
 726        elem = self.getElement(row, column)
 727        DOM.setAttribute(elem, attrName, value)
 728
 729
 730
 731class RowFormatter:
 732
 733    def __init__(self, outer):
 734        self.outer = outer
 735
 736    def addStyleName(self, row, styleName):
 737        self.outer.setStyleName(self.ensureElement(row), styleName, True)
 738
 739    def getElement(self, row):
 740        self.outer.checkRowBounds(row)
 741        return self.getRow(self.outer.bodyElem, row)
 742        
 743    def getStyleName(self, row):
 744        return DOM.getAttribute(self.getElement(row), "className")
 745
 746    def isVisible(self, row):
 747        element = self.getElement(row)
 748        return self.outer.isVisible(element)
 749
 750    def removeStyleName(self, row, styleName):
 751        self.outer.setStyleName(self.getElement(row), styleName, False)
 752
 753    def setStyleName(self, row, styleName):
 754        elem = self.ensureElement(row)
 755        DOM.setAttribute(elem, "className", styleName)
 756        
 757    def setVerticalAlign(self, row, align):
 758        DOM.setStyleAttribute(self.ensureElement(row), "verticalAlign", align)
 759
 760    def setVisible(self, row, visible):
 761        element = self.ensureElement(row)
 762        self.outer.setVisible(element, visible)
 763
 764    def ensureElement(self, row):
 765        self.outer.prepareRow(row)
 766        return self.getRow(self.outer.bodyElem, row)
 767
 768    def getRow(self, element, row):
 769        JS("""

 770        return element.rows[row];

 771        """)
 772
 773    def setAttr(self, row, attrName, value):
 774        element = self.ensureElement(row)
 775        DOM.setAttribute(element, attrName, value)
 776
 777
 778class HTMLTable(Panel):
 779    
 780    def __init__(self):
 781        Panel.__init__(self)
 782        self.cellFormatter = CellFormatter(self)
 783        self.rowFormatter = RowFormatter(self)
 784        self.tableListeners = []
 785        self.widgetMap = {}
 786
 787        self.tableElem = DOM.createTable()
 788        self.bodyElem = DOM.createTBody()
 789        DOM.appendChild(self.tableElem, self.bodyElem)
 790        self.setElement(self.tableElem)
 791        self.sinkEvents(Event.ONCLICK)
 792
 793    def addTableListener(self, listener):
 794        self.tableListeners.append(listener)
 795
 796    def clear(self):
 797        for row in range(self.getRowCount()):
 798            for col in range(self.getCellCount(row)):
 799                child = self.getWidget(row, col)
 800                if child != None:
 801                    self.removeWidget(child)
 802        # assert len(self.widgetMap) == 0

 803
 804    def clearCell(self, row, column):
 805        td = self.cellFormatter.getElement(row, column)
 806        return self.internalClearCell(td)
 807
 808    def getCellCount(self, row):
 809        return 0
 810
 811    def getCellFormatter(self):
 812        return self.cellFormatter
 813    
 814    def getCellPadding(self):
 815        return DOM.getIntAttribute(self.tableElem, "cellPadding")
 816    
 817    def getCellSpacing(self):
 818        return DOM.getIntAttribute(self.tableElem, "cellSpacing")
 819
 820    def getHTML(self, row, column):
 821        element = self.cellFormatter.getElement(row, column)
 822        return DOM.getInnerHTML(element)
 823
 824    def getRowCount(self):
 825        return 0
 826        
 827    def getRowFormatter(self):
 828        return self.rowFormatter
 829        
 830    def getText(self, row, column):
 831        self.checkCellBounds(row, column)
 832        element = self.cellFormatter.getElement(row, column)
 833        return DOM.getInnerText(element)
 834
 835    # also callable as getWidget(widgetElement)

 836    def getWidget(self, row, column=None):
 837        if column == None:
 838            key = self.computeKeyForElement(row)
 839        else:
 840            self.checkCellBounds(row, column)
 841            key = self.computeKey(row, column)
 842
 843        if key == None:
 844            return None
 845        return self.widgetMap[key]
 846
 847    def isCellPresent(self, row, column):
 848        # GWT uses "and", possibly a bug

 849        if row >= self.getRowCount() or row < 0:
 850            return False
 851        
 852        if column < 0 or column >= self.getCellCount(row):
 853            return False
 854        
 855        return True
 856
 857    def __iter__(self):
 858        return self.widgetMap.itervalues()
 859
 860    def onBrowserEvent(self, event):
 861        if DOM.eventGetType(event) == "click":
 862            td = self.getEventTargetCell(event)
 863            if not td:
 864                return
 865
 866            tr = DOM.getParent(td)
 867            body = DOM.getParent(tr)
 868            row = DOM.getChildIndex(body, tr)
 869            column = DOM.getChildIndex(tr, td)
 870        
 871            for listener in self.tableListeners:
 872                if listener.onCellClicked:
 873                    listener.onCellClicked(self, row, column)
 874                else:
 875                    listener(self)
 876
 877    def remove(self, widget):
 878        if widget.getParent() != self:
 879            return False
 880        
 881        self.removeWidget(widget)
 882        return True
 883
 884    def removeTableListener(self, listener):
 885        self.tableListeners.remove(listener)
 886
 887    def setBorderWidth(self, width):
 888        DOM.setAttribute(self.tableElem, "border", width)
 889
 890    def setCellPadding(self, padding):
 891        DOM.setIntAttribute(self.tableElem, "cellPadding", padding)
 892
 893    def setCellSpacing(self, spacing):
 894        DOM.setIntAttribute(self.tableElem, "cellSpacing", spacing)
 895
 896    def setHTML(self, row, column, html):
 897        self.prepareCell(row, column)
 898        td = self.cleanCell(row, column)
 899        if html != None:
 900            DOM.setInnerHTML(td, html)
 901
 902    def setText(self, row, column, text):
 903        self.prepareCell(row, column)
 904        td = self.cleanCell(row, column)
 905        if text != None:
 906            DOM.setInnerText(td, text)
 907
 908    def setWidget(self, row, column, widget):
 909        self.prepareCell(row, column)
 910        if widget == None:
 911            return
 912
 913        widget.removeFromParent()
 914        td = self.cleanCell(row, column)
 915        widget_hash = hash(widget)
 916        element = widget.getElement()
 917        DOM.setAttribute(element, "__hash", widget_hash)
 918        self.widgetMap[widget_hash] = widget
 919        self.adopt(widget, td)
 920
 921    def cleanCell(self, row, column):
 922        td = self.cellFormatter.getRawElement(row, column)
 923        self.internalClearCell(td)
 924        return td
 925
 926    def computeKey(self, row, column):
 927        element = self.cellFormatter.getRawElement(row, column)
 928        child = DOM.getFirstChild(element)
 929        if child == None:
 930            return None
 931
 932        return self.computeKeyForElement(child)
 933
 934    def computeKeyForElement(self, widgetElement):
 935        return DOM.getAttribute(widgetElement, "__hash")
 936
 937    def removeWidget(self, widget):
 938        self.disown(widget)
 939
 940        del self.widgetMap[self.computeKeyForElement(widget.getElement())]
 941        return True
 942
 943    def checkCellBounds(self, row, column):
 944        self.checkRowBounds(row)
 945        #if column<0: raise IndexError, "Column " + column + " must be non-negative: " + column

 946
 947        cellSize = self.getCellCount(row)
 948        #if cellSize<column: raise IndexError, "Column " + column + " does not exist, col at row " + row + " size is " + self.getCellCount(row) + "cell(s)"

 949
 950    def checkRowBounds(self, row):
 951        rowSize = self.getRowCount()
 952        #if row >= rowSize or row < 0: raise IndexError, "Row " + row + " does not exist, row size is " + self.getRowCount()

 953
 954    def createCell(self):
 955        return DOM.createTD()
 956        
 957    def getBodyElement(self):
 958        return self.bodyElem
 959
 960    # also callable as getDOMCellCount(row)

 961    def getDOMCellCount(self, element, row=None):
 962        if row == None:
 963            return self.getDOMCellCountImpl(self.bodyElem, element)
 964        return self.getDOMCellCountImpl(element, row)
 965
 966    def getDOMCellCountImpl(self, element, row):
 967        JS("""

 968        return element.rows[row].cells.length;

 969        """)
 970
 971    # also callable as getDOMRowCount(element)

 972    def getDOMRowCount(self, element=None):
 973        if element == None:
 974            element = self.bodyElem
 975        return self.getDOMRowCountImpl(element)
 976
 977    def getDOMRowCountImpl(self, element):
 978        JS("""

 979        return element.rows.length;

 980        """)
 981
 982    def getEventTargetCell(self, event):
 983        td = DOM.eventGetTarget(event)
 984        while td != None:
 985            if DOM.getAttribute(td, "tagName").lower() == "td":
 986                tr = DOM.getParent(td)
 987                body = DOM.getParent(tr)
 988                if DOM.compare(body, self.bodyElem):
 989                    return td
 990            if DOM.compare(td, self.bodyElem):
 991                return None
 992            td = DOM.getParent(td)
 993        
 994        return None
 995
 996    def insertCell(self, row, column):
 997        tr = self.rowFormatter.getRow(self.bodyElem, row)
 998        td = self.createCell()
 999        DOM.insertChild(tr, td, column)
1000
1001    def insertCells(self, row, column, count):
1002        tr = self.rowFormatter.getRow(self.bodyElem, row)
1003        for i in range(column, column + count):
1004            td = self.createCell()
1005            DOM.insertChild(tr, td, i)
1006
1007    def insertRow(self, beforeRow):
1008        if beforeRow != self.getRowCount():
1009            self.checkRowBounds(beforeRow)
1010        
1011        tr = DOM.createTR()
1012        DOM.insertChild(self.bodyElem, tr, beforeRow)
1013        return beforeRow
1014
1015    def internalClearCell(self, td):
1016        maybeChild = DOM.getFirstChild(td)
1017        widget = None
1018        if maybeChild != None:
1019            widget = self.getWidget(maybeChild)
1020
1021        if widget != None:
1022            self.removeWidget(widget)
1023            return True
1024
1025        DOM.setInnerHTML(td, "")
1026        return False
1027
1028    def prepareCell(self, row, column):
1029        pass
1030
1031    def prepareRow(self, row):
1032        pass
1033
1034    def removeCell(self, row, column):
1035        self.checkCellBounds(row, column)
1036        td = self.cleanCell(row, column)
1037        tr = self.rowFormatter.getRow(self.bodyElem, row)
1038        DOM.removeChild(tr, td)
1039
1040    def removeRow(self, row):
1041        for column in range(self.getCellCount(row)):
1042            self.cleanCell(row, column)
1043        DOM.removeChild(self.bodyElem, self.rowFormatter.getRow(self.bodyElem, row))
1044
1045    def setCellFormatter(self, cellFormatter):
1046        self.cellFormatter = cellFormatter
1047
1048    def setRowFormatter(self, rowFormatter):
1049        self.rowFormatter = rowFormatter
1050    
1051
1052class Grid(HTMLTable):
1053    
1054    def __init__(self, rows=0, columns=0):
1055        HTMLTable.__init__(self)
1056        self.cellFormatter = CellFormatter(self)
1057        self.rowFormatter = RowFormatter(self)
1058        self.numColumns = 0
1059        self.numRows = 0
1060        if rows > 0 or columns > 0:
1061            self.resize(rows, columns)
1062
1063    def resize(self, rows, columns):
1064        self.resizeColumns(columns)
1065        self.resizeRows(rows)
1066
1067    def resizeColumns(self, columns):
1068        if self.numColumns == columns:
1069            return
1070        
1071        if self.numColumns > columns:
1072            for i in range(0, self.numRows):
1073                for j in range(self.numColumns - 1, columns - 1, -1):
1074                    self.removeCell(i, j)
1075        else:
1076            for i in range(self.numRows):
1077                for j in range(self.numColumns, columns):
1078                    self.insertCell(i, j)
1079        self.numColumns = columns
1080
1081    def resizeRows(self, rows):
1082        if self.numRows == rows:
1083            return
1084
1085        if self.numRows < rows:
1086            self.addRows(self.getBodyElement(), rows - self.numRows, self.numColumns)
1087            self.numRows = rows
1088        else:
1089            while self.numRows > rows:
1090                self.numRows -= 1
1091                self.removeRow(self.numRows)
1092
1093    def createCell(self):
1094        td = HTMLTable.createCell(self)
1095        DOM.setInnerHTML(td, "&nbsp;")
1096        return td
1097
1098    def clearCell(self, row, column):
1099        td = self.cellFormatter.getElement(row, column)
1100        b = HTMLTable.internalClearCell(self, td)
1101        DOM.setInnerHTML(td, "&nbsp;")
1102        return b
1103
1104    def prepareCell(self, row, column):
1105        pass
1106
1107    def prepareRow(self, row):
1108        pass
1109
1110    def getCellCount(self, row):
1111        return self.numColumns
1112    
1113    def getColumnCount(self):
1114        return self.numColumns
1115    
1116    def getRowCount(self):
1117        return self.numRows
1118
1119    def addRows(self, table, numRows, columns):
1120        JS("""

1121        var td = $doc.createElement("td");

1122        td.innerHTML = "&nbsp;";

1123        var row = $doc.createElement("tr");

1124        for(var cellNum = 0; cellNum < columns; cellNum++) {

1125            var cell = td.cloneNode(true);

1126            row.appendChild(cell);

1127        }

1128        table.appendChild(row);

1129        for(var rowNum = 1; rowNum < numRows; rowNum++) {

1130            table.appendChild(row.cloneNode(true));

1131        }

1132        """)
1133
1134
1135class FlexCellFormatter(CellFormatter):
1136    def __init__(self, outer):
1137        CellFormatter.__init__(self, outer)
1138    
1139    def getColSpan(self, row, column):
1140        return DOM.getIntAttribute(self.getElement(row, column), "colSpan")
1141
1142    def getRowSpan(self, row, column):
1143        return DOM.getIntAttribute(self.getElement(row, column), "rowSpan")
1144        
1145    def setColSpan(self, row, column, colSpan):
1146        DOM.setIntAttribute(self.ensureElement(row, column), "colSpan", colSpan)
1147
1148    def setRowSpan(self, row, column, rowSpan):
1149        DOM.setIntAttribute(self.ensureElement(row, column), "rowSpan", rowSpan)
1150
1151
1152class FlexTable(HTMLTable):
1153    def __init__(self):
1154        HTMLTable.__init__(self)
1155        self.cellFormatter = FlexCellFormatter(self)
1156        self.rowFormatter = RowFormatter(self)
1157
1158    def addCell(self, row):
1159        self.insertCell(row, self.getCellCount(row))
1160
1161    def getCellCount(self, row):
1162        self.checkRowBounds(row)
1163        return self.getDOMCellCount(self.getBodyElement(), row)
1164
1165    def getFlexCellFormatter(self):
1166        return self.getCellFormatter()
1167
1168    def getRowCount(self):
1169        return self.getDOMRowCount()
1170
1171    def removeCells(self, row, column, num):
1172        for i in range(i):
1173            self.removeCell(row, column)
1174
1175    def prepareCell(self, row, column):
1176        self.prepareRow(row)
1177        #if column < 0: throw new IndexOutOfBoundsException("Cannot create a column with a negative index: " + column);

1178        
1179        cellCount = self.getCellCount(row)
1180        required = column + 1 - cellCount
1181        if required > 0:
1182            self.addCells(self.getBodyElement(), row, required)
1183
1184    def prepareRow(self, row):
1185        #if row < 0: throw new IndexOutOfBoundsException("Cannot create a row with a negative index: " + row);

1186
1187        rowCount = self.getRowCount()
1188        for i in range(rowCount, row + 1):
1189            self.insertRow(i)
1190
1191    def addCells(self, table, row, num):
1192        JS("""

1193        var rowElem = table.rows[row];

1194        for(var i = 0; i < num; i++){

1195            var cell = $doc.createElement("td");

1196            rowElem.appendChild(cell);

1197        }

1198        """)
1199
1200
1201class ComplexPanel(Panel):
1202    """

1203        Superclass for widgets with multiple children.

1204    """
1205    def __init__(self):
1206        Panel.__init__(self)
1207        self.children = []
1208    
1209    def add(self, widget, container):
1210        self.insert(widget, container, len(self.children))
1211
1212    def getChildren(self):
1213        return self.children
1214    
1215    def insert(self, widget, container, beforeIndex):
1216        if widget.getParent() == self:
1217            return
1218
1219        self.adopt(widget, container)
1220        self.children.insert(beforeIndex, widget)
1221
1222    def remove(self, widget):
1223        if widget not in self.children:
1224            return False
1225
1226        self.disown(widget)
1227        self.children.remove(widget)
1228        return True
1229
1230
1231class AbsolutePanel(ComplexPanel):
1232
1233    def __init__(self):
1234        ComplexPanel.__init__(self)
1235        self.setElement(DOM.createDiv())
1236        DOM.setStyleAttribute(self.getElement(), "position", "relative")
1237        DOM.setStyleAttribute(self.getElement(), "overflow", "hidden")
1238
1239    def add(self, widget, left=None, top=None):
1240        ComplexPanel.add(self, widget, self.getElement())
1241
1242        if left != None:
1243            self.setWidgetPosition(widget, left, top)
1244
1245    def setWidgetPosition(self, widget, left, top):
1246        self.checkWidgetParent(widget)
1247        
1248        h = widget.getElement()
1249        if (left == -1) and (top == -1):
1250            DOM.setStyleAttribute(h, "left", "")
1251            DOM.setStyleAttribute(h, "top", "")
1252            DOM.setStyleAttribute(h, "position", "static")
1253        else:
1254            DOM.setStyleAttribute(h, "position", "absolute")
1255            DOM.setStyleAttribute(h, "left", left + "px")
1256            DOM.setStyleAttribute(h, "top", top + "px")
1257
1258    def getWidgetLeft(self, widget):
1259        self.checkWidgetParent(widget)
1260        return DOM.getIntAttribute(widget.getElement(), "offsetLeft")
1261
1262    def getWidgetTop(self, widget):
1263        self.checkWidgetParent(widget)
1264        return DOM.getIntAttribute(widget.getElement(), "offsetTop")
1265
1266    def checkWidgetParent(self, widget):
1267        if widget.getParent() != self:
1268            console.error("Widget must be a child of this panel.")
1269
1270
1271class Label(Widget):
1272
1273    def __init__(self, text=None, wordWrap=True):
1274        Widget.__init__(self)
1275        self.horzAlign = ""
1276        self.clickListeners = []
1277        self.mouseListeners = []
1278        
1279        self.setElement(DOM.createDiv())
1280        self.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS)
1281        self.setStyleName("gwt-Label")
1282        if text:
1283            self.setText(text)
1284
1285        self.setWordWrap(wordWrap)
1286            
1287    def addClickListener(self, listener):
1288        self.clickListeners.append(listener)
1289
1290    def addMouseListener(self, listener):
1291        self.mouseListeners.append(listener)
1292
1293    def getHorizontalAlignment(self):
1294        return self.horzAlign
1295
1296    def getText(self):
1297        return DOM.getInnerText(self.getElement())
1298
1299    def getWordWrap(self):
1300        return not (DOM.getStyleAttribute(self.getElement(), "whiteSpace") == "nowrap")
1301
1302    def onBrowserEvent(self, event):
1303        type = DOM.eventGetType(event)
1304        if type == "click":
1305            for listener in self.clickListeners:
1306                if listener.onClick: listener.onClick(self, event)
1307                else: listener(self, event)
1308        elif type == "mousedown" or type == "mouseup" or type == "mousemove" or type == "mouseover" or type == "mouseout":
1309            MouseListener.fireMouseEvent(self, self.mouseListeners, self, event)
1310
1311    def removeClickListener(self, listener):
1312        self.clickListeners.remove(listener)
1313
1314    def removeMouseListener(self, listener):
1315        self.mouseListeners.remove(listener)
1316
1317    def setHorizontalAlignment(self, align):
1318        self.horzAlign = align
1319        DOM.setStyleAttribute(self.getElement(), "textAlign", align)
1320
1321    def setText(self, text):
1322        DOM.setInnerText(self.getElement(), text)
1323
1324    def setWordWrap(self, wrap):
1325        if wrap:
1326            style = "normal"
1327        else:
1328            style = "nowrap"
1329        DOM.setStyleAttribute(self.getElement(), "whiteSpace", style)
1330
1331
1332class HTML(Label):
1333    
1334    def __init__(self, html=None, wordWrap=True):
1335        Label.__init__(self)
1336    
1337        self.setElement(DOM.createDiv())
1338        self.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS)
1339        self.setStyleName("gwt-HTML")
1340        if html:
1341            self.setHTML(html)
1342            
1343        self.setWordWrap(wordWrap)
1344    
1345    def getHTML(self):
1346        return DOM.getInnerHTML(self.getElement())
1347
1348    def setHTML(self, html):
1349        DOM.setInnerHTML(self.getElement(), html)
1350
1351
1352class HasHorizontalAlignment:
1353    ALIGN_LEFT = "left"
1354    ALIGN_CENTER = "center"
1355    ALIGN_RIGHT = "right"
1356
1357
1358class HasVerticalAlignment:
1359    ALIGN_TOP = "top"
1360    ALIGN_MIDDLE = "middle"
1361    ALIGN_BOTTOM = "bottom"
1362
1363
1364class HasAlignment:
1365    ALIGN_BOTTOM = "bottom"
1366    ALIGN_MIDDLE = "middle"
1367    ALIGN_TOP = "top"
1368    ALIGN_CENTER = "center"
1369    ALIGN_LEFT = "left"
1370    ALIGN_RIGHT = "right"
1371
1372
1373class CellPanel(ComplexPanel):
1374    
1375    def __init__(self):
1376        ComplexPanel.__init__(self)
1377        
1378        self.table = DOM.createTable()
1379        self.body = DOM.createTBody()
1380        DOM.appendChild(self.table, self.body)
1381        self.setElement(self.table)
1382
1383    def getTable(self):
1384        return self.table
1385
1386    def getBody(self):
1387        return self.body
1388
1389    def getSpacing(self):
1390        return self.spacing
1391
1392    def getWidgetTd(self, widget):
1393        if widget.getParent() != self:
1394            return None
1395        return DOM.getParent(widget.getElement())
1396
1397    def setBorderWidth(self, width):
1398        DOM.setAttribute(self.table, "border", "" + width)
1399
1400    def setCellHeight(self, widget, height):
1401        td = DOM.getParent(widget.getElement())
1402        DOM.setAttribute(td, "height", height)
1403
1404    def setCellHorizontalAlignment(self, widget, align):
1405        td = self.getWidgetTd(widget)
1406        if td != None:
1407            DOM.setAttribute(td, "align", align)
1408
1409    def setCellVerticalAlignment(self, widget, align):
1410        td = self.getWidgetTd(widget)
1411        if td != None:
1412            DOM.setStyleAttribute(td, "verticalAlign", align)
1413
1414    def setCellWidth(self, widget, width):
1415        td = DOM.getParent(widget.getElement())
1416        DOM.setAttribute(td, "width", width)
1417
1418    def setSpacing(self, spacing):
1419        self.spacing = spacing
1420        DOM.setIntAttribute(self.table, "cellSpacing", spacing)
1421
1422
1423class HorizontalPanel(CellPanel):
1424    
1425    def __init__(self):
1426        CellPanel.__init__(self)
1427
1428        self.horzAlign = HasHorizontalAlignment.ALIGN_LEFT
1429        self.vertAlign = HasVerticalAlignment.ALIGN_TOP
1430        
1431        self.tableRow = DOM.createTR()
1432        DOM.appendChild(self.getBody(), self.tableRow)
1433        
1434        DOM.setAttribute(self.getTable(), "cellSpacing", "0")
1435        DOM.setAttribute(self.getTable(), "cellPadding", "0")
1436
1437    def add(self, widget):
1438        self.insert(widget, self.getWidgetCount())
1439
1440    def getHorizontalAlignment(self):
1441        return self.horzAlign
1442    
1443    def getVerticalAlignment(self):
1444        return self.vertAlign
1445
1446    def getWidget(self, index):
1447        return self.children[index]
1448
1449    def getWidgetCount(self):
1450        return len(self.children)
1451
1452    def getWidgetIndex(self, child):
1453        return self.children.index(child)
1454
1455    def insert(self, widget, beforeIndex):
1456        widget.removeFromParent()
1457        
1458        td = DOM.createTD()
1459        DOM.insertChild(self.tableRow, td, beforeIndex)
1460        
1461        CellPanel.insert(self, widget, td, beforeIndex)
1462        
1463        self.setCellHorizontalAlignment(widget, self.horzAlign)
1464        self.setCellVerticalAlignment(widget, self.vertAlign)
1465
1466    def remove(self, widget):
1467        if widget.getParent() != self:
1468            return False
1469
1470        td = DOM.getParent(widget.getElement())
1471        DOM.removeChild(self.tableRow, td)
1472
1473        CellPanel.remove(widget)
1474        return True
1475
1476    def setHorizontalAlignment(self, align):
1477        self.horzAlign = align
1478
1479    def setVerticalAlignment(self, align):
1480        self.vertAlign = align
1481
1482
1483class VerticalPanel(CellPanel):
1484    
1485    def __init__(self):
1486        CellPanel.__init__(self)
1487
1488        self.horzAlign = HasHorizontalAlignment.ALIGN_LEFT
1489        self.vertAlign = HasVerticalAlignment.ALIGN_TOP
1490        
1491        DOM.setAttribute(self.getTable(), "cellSpacing", "0")
1492        DOM.setAttribute(self.getTable(), "cellPadding", "0")
1493
1494    def add(self, widget):
1495        self.insert(widget, self.getWidgetCount())
1496    
1497    def getHorizontalAlignment(self):
1498        return self.horzAlign
1499    
1500    def getVerticalAlignment(self):
1501        return self.vertAlign
1502
1503    def getWidget(self, index):
1504        return self.children[index]
1505
1506    def getWidgetCount(self):
1507        return len(self.children)
1508
1509    def getWidgetIndex(self, child):
1510        return self.children.index(child)
1511    
1512    def setWidget(self, index, widget):
1513        """Replace the widget at the given index with a new one"""
1514        existing = self.getWidget(index)
1515        if existing:
1516            self.remove(existing)
1517        self.insert(widget, index)
1518        
1519    def insert(self, widget, beforeIndex):
1520        widget.removeFromParent()
1521        
1522        tr = DOM.createTR()
1523        td = DOM.createTD()
1524        
1525        DOM.insertChild(self.getBody(), tr, beforeIndex)
1526        DOM.appendChild(tr, td)
1527        
1528        CellPanel.insert(self, widget, td, beforeIndex)
1529        
1530        self.setCellHorizontalAlignment(widget, self.horzAlign)
1531        self.setCellVerticalAlignment(widget, self.vertAlign)
1532
1533    def remove(self, widget):
1534        if pyjslib.isNumber(widget):
1535            widget = self.getWidget(widget)
1536        
1537        if widget.getParent() != self:
1538            return False
1539
1540        td = DOM.getParent(widget.getElement())
1541        tr = DOM.getParent(td)
1542        DOM.removeChild(self.getBody(), tr)
1543        
1544        CellPanel.remove(self, widget)
1545        return True
1546
1547    def setHorizontalAlignment(self, align):
1548        self.horzAlign = align
1549
1550    def setVerticalAlig

Large files files are truncated, but you can click here to view the full file