PageRenderTime 116ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 1ms

/library/ui.py

http://pyjamas.googlecode.com/
Python | 2048 lines | 1972 code | 40 blank | 36 comment | 32 complexity | 4b04681a7447a7c2a260bda6a04419da MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0
  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.
  14. from __pyjamas__ import JS, console
  15. import DOM
  16. import pygwt
  17. from DeferredCommand import DeferredCommand
  18. import pyjslib
  19. from History import History
  20. import Window
  21. from sets import Set
  22. class Event:
  23. """
  24. This class contains flags and integer values used by the event system.
  25. It is not meant to be subclassed or instantiated.
  26. """
  27. BUTTON_LEFT = 1
  28. BUTTON_MIDDLE = 4
  29. BUTTON_RIGHT = 2
  30. ONBLUR = 0x01000
  31. ONCHANGE = 0x00400
  32. ONCLICK = 0x00001
  33. ONDBLCLICK = 0x00002
  34. ONERROR = 0x10000
  35. ONFOCUS = 0x00800
  36. ONKEYDOWN = 0x00080
  37. ONKEYPRESS = 0x00100
  38. ONKEYUP = 0x00200
  39. ONLOAD = 0x08000
  40. ONLOSECAPTURE = 0x02000
  41. ONMOUSEDOWN = 0x00004
  42. ONMOUSEMOVE = 0x00040
  43. ONMOUSEOUT = 0x00020
  44. ONMOUSEOVER = 0x00010
  45. ONMOUSEUP = 0x00008
  46. ONSCROLL = 0x04000
  47. FOCUSEVENTS = 0x01800 # ONFOCUS | ONBLUR
  48. KEYEVENTS = 0x00380 # ONKEYDOWN | ONKEYPRESS | ONKEYUP
  49. MOUSEEVENTS = 0x0007C # ONMOUSEDOWN | ONMOUSEUP | ONMOUSEMOVE | ONMOUSEOVER | ONMOUSEOUT
  50. # FocusListenerCollection
  51. class FocusListener:
  52. def fireFocusEvent(self, listeners, sender, event):
  53. type = DOM.eventGetType(event)
  54. if type == "focus":
  55. for listener in listeners:
  56. listener.onFocus(sender)
  57. elif type == "blur":
  58. for listener in listeners:
  59. listener.onLostFocus(sender)
  60. # KeyboardListener + KeyboardListenerCollection
  61. class KeyboardListener:
  62. KEY_ALT = 18
  63. KEY_BACKSPACE = 8
  64. KEY_CTRL = 17
  65. KEY_DELETE = 46
  66. KEY_DOWN = 40
  67. KEY_END = 35
  68. KEY_ENTER = 13
  69. KEY_ESCAPE = 27
  70. KEY_HOME = 36
  71. KEY_LEFT = 37
  72. KEY_PAGEDOWN = 34
  73. KEY_PAGEUP = 33
  74. KEY_RIGHT = 39
  75. KEY_SHIFT = 16
  76. KEY_TAB = 9
  77. KEY_UP = 38
  78. MODIFIER_ALT = 4
  79. MODIFIER_CTRL = 2
  80. MODIFIER_SHIFT = 1
  81. def getKeyboardModifiers(self, event):
  82. shift = 0
  83. ctrl = 0
  84. alt = 0
  85. if DOM.eventGetShiftKey(event):
  86. shift = KeyboardListener.MODIFIER_SHIFT
  87. if DOM.eventGetCtrlKey(event):
  88. ctrl = KeyboardListener.MODIFIER_CTRL
  89. if DOM.eventGetAltKey(event):
  90. alt = KeyboardListener.MODIFIER_ALT
  91. return shift | ctrl | alt
  92. def fireKeyboardEvent(self, listeners, sender, event):
  93. modifiers = KeyboardListener.getKeyboardModifiers(self, event)
  94. type = DOM.eventGetType(event)
  95. if type == "keydown":
  96. for listener in listeners:
  97. listener.onKeyDown(sender, DOM.eventGetKeyCode(event), modifiers)
  98. elif type == "keyup":
  99. for listener in listeners:
  100. listener.onKeyUp(sender, DOM.eventGetKeyCode(event), modifiers)
  101. elif type == "keypress":
  102. for listener in listeners:
  103. listener.onKeyPress(sender, DOM.eventGetKeyCode(event), modifiers)
  104. # MouseListenerCollection
  105. class MouseListener:
  106. def fireMouseEvent(self, listeners, sender, event):
  107. x = DOM.eventGetClientX(event) - DOM.getAbsoluteLeft(sender.getElement())
  108. y = DOM.eventGetClientY(event) - DOM.getAbsoluteTop(sender.getElement())
  109. type = DOM.eventGetType(event)
  110. if type == "mousedown":
  111. for listener in listeners:
  112. listener.onMouseDown(sender, x, y)
  113. elif type == "mouseup":
  114. for listener in listeners:
  115. listener.onMouseUp(sender, x, y)
  116. elif type == "mousemove":
  117. for listener in listeners:
  118. listener.onMouseMove(sender, x, y)
  119. elif type == "mouseover":
  120. from_element = DOM.eventGetFromElement(event)
  121. if not DOM.isOrHasChild(sender.getElement(), from_element):
  122. for listener in listeners:
  123. listener.onMouseEnter(sender)
  124. elif type == "mouseout":
  125. to_element = DOM.eventGetToElement(event)
  126. if not DOM.isOrHasChild(sender.getElement(), to_element):
  127. for listener in listeners:
  128. listener.onMouseLeave(sender)
  129. class UIObject:
  130. def getAbsoluteLeft(self):
  131. return DOM.getAbsoluteLeft(self.getElement())
  132. def getAbsoluteTop(self):
  133. return DOM.getAbsoluteTop(self.getElement())
  134. def getElement(self):
  135. """Get the DOM element associated with the UIObject, if any"""
  136. return self.element
  137. def getOffsetHeight(self):
  138. return DOM.getIntAttribute(self.element, "offsetHeight")
  139. def getOffsetWidth(self):
  140. return DOM.getIntAttribute(self.element, "offsetWidth")
  141. def getStyleName(self):
  142. return DOM.getAttribute(self.element, "className")
  143. def getTitle(self):
  144. return DOM.getAttribute(self.element, "title")
  145. def setElement(self, element):
  146. """Set the DOM element associated with the UIObject."""
  147. self.element = element
  148. def setHeight(self, height):
  149. """Set the height of the element associated with this UIObject. The
  150. value should be given as a CSS value, such as 100px, 30%, or 50pi"""
  151. DOM.setStyleAttribute(self.element, "height", height)
  152. def setPixelSize(self, width, height):
  153. """Set the width and height of the element associated with this UIObject
  154. in pixels. Width and height should be numbers."""
  155. if width >= 0:
  156. self.setWidth(width + "px")
  157. if height >= 0:
  158. self.setHeight(height + "px")
  159. def setSize(self, width, height):
  160. """Set the width and height of the element associated with this UIObject. The
  161. values should be given as a CSS value, such as 100px, 30%, or 50pi"""
  162. self.setWidth(width)
  163. self.setHeight(height)
  164. def addStyleName(self, style):
  165. """Append a style to the element associated with this UIObject. This is
  166. a CSS class name. It will be added after any already-assigned CSS class for
  167. the element."""
  168. self.setStyleName(self.element, style, True)
  169. def removeStyleName(self, style):
  170. """Remove a style from the element associated with this UIObject. This is
  171. a CSS class name."""
  172. self.setStyleName(self.element, style, False)
  173. # also callable as: setStyleName(self, style)
  174. def setStyleName(self, element, style=None, add=True):
  175. """When called with a single argument, this replaces all the CSS classes
  176. associated with this UIObject's element with the given parameter. Otherwise,
  177. this is assumed to be a worker function for addStyleName and removeStyleName."""
  178. # emulate setStyleName(self, style)
  179. if style == None:
  180. style = element
  181. DOM.setAttribute(self.element, "className", style)
  182. return
  183. oldStyle = DOM.getAttribute(element, "className")
  184. if oldStyle == None:
  185. oldStyle = ""
  186. idx = oldStyle.find(style)
  187. # Calculate matching index
  188. lastPos = len(oldStyle)
  189. while idx != -1:
  190. if idx == 0 or (oldStyle[idx - 1] == " "):
  191. last = idx + len(style)
  192. if (last == lastPos) or ((last < lastPos) and (oldStyle[last] == " ")):
  193. break
  194. idx = oldStyle.find(style, idx + 1)
  195. if add:
  196. if idx == -1:
  197. DOM.setAttribute(element, "className", oldStyle + " " + style)
  198. else:
  199. if idx != -1:
  200. begin = oldStyle[:idx]
  201. end = oldStyle[idx + len(style):]
  202. DOM.setAttribute(element, "className", begin + end)
  203. def setTitle(self, title):
  204. DOM.setAttribute(self.element, "title", title)
  205. def setWidth(self, width):
  206. """Set the width of the element associated with this UIObject. The
  207. value should be given as a CSS value, such as 100px, 30%, or 50pi"""
  208. DOM.setStyleAttribute(self.element, "width", width)
  209. def sinkEvents(self, eventBitsToAdd):
  210. """Request that the given events be delivered to the event handler for this
  211. element. The event bits passed are added (using inclusive OR) to the events
  212. already "sunk" for the element associated with the UIObject. The event bits
  213. are a combination of values from class L{Event}."""
  214. if self.element:
  215. DOM.sinkEvents(self.getElement(), eventBitsToAdd | DOM.getEventsSunk(self.getElement()))
  216. def isVisible(self, element=None):
  217. """Determine whether this element is currently visible, by checking the CSS
  218. property 'display'"""
  219. if not element:
  220. element = self.element
  221. return element.style.display != "none"
  222. # also callable as: setVisible(visible)
  223. def setVisible(self, element, visible=None):
  224. """Set whether this element is visible or not. If a single parameter is
  225. given, the self.element is used. This modifies the CSS property 'display',
  226. which means that an invisible element not only is not drawn, but doesn't
  227. occupy any space on the page."""
  228. if visible==None:
  229. visible = element
  230. element = self.element
  231. if visible:
  232. element.style.display = ""
  233. else:
  234. element.style.display = "none"
  235. def unsinkEvents(self, eventBitsToRemove):
  236. """Reverse the operation of sinkEvents. See L{UIObject.sinkevents}."""
  237. DOM.sinkEvents(self.getElement(), ~eventBitsToRemove & DOM.getEventsSunk(self.getElement()))
  238. class Widget(UIObject):
  239. """
  240. Base class for most of the UI classes. This class provides basic services
  241. used by any Widget, including management of parents and adding/removing the
  242. event handler association with the DOM.
  243. """
  244. def __init__(self):
  245. self.attached = False
  246. self.parent = None
  247. self.layoutData = None
  248. def getLayoutData(self):
  249. return self.layoutData
  250. def getParent(self):
  251. """Widgets are kept in a hierarchy, and widgets that have been added to a panel
  252. will have a parent widget that contains them. This retrieves the containing
  253. widget for this widget."""
  254. return self.parent
  255. def isAttached(self):
  256. """Return whether or not this widget has been attached to the document."""
  257. return self.attached
  258. def onBrowserEvent(self, event):
  259. pass
  260. def onLoad(self):
  261. pass
  262. def onAttach(self):
  263. """Called when this widget has an element, and that element is on the document's
  264. DOM tree, and we have a parent widget."""
  265. if self.attached:
  266. return
  267. self.attached = True
  268. DOM.setEventListener(self.getElement(), self)
  269. self.onLoad()
  270. def onDetach(self):
  271. """Called when this widget is being removed from the DOM tree of the document."""
  272. if not self.attached:
  273. return
  274. self.attached = False
  275. DOM.setEventListener(self.getElement(), None)
  276. def setLayoutData(self, layoutData):
  277. self.layoutData = layoutData
  278. def setParent(self, parent):
  279. """Update the parent attribute. If the parent is currently attached to the DOM this
  280. assumes we are being attached also and calls onAttach()."""
  281. self.parent = parent
  282. if parent == None:
  283. self.onDetach()
  284. elif parent.attached:
  285. self.onAttach()
  286. def removeFromParent(self):
  287. """Remove ourself from our parent. The parent widget will call setParent(None) on
  288. us automatically"""
  289. if hasattr(self.parent, "remove"):
  290. self.parent.remove(self)
  291. def getID(self):
  292. """Get the id attribute of the associated DOM element."""
  293. return DOM.getAttribute(self.getElement(), "id")
  294. def setID(self, id):
  295. """Set the id attribute of the associated DOM element."""
  296. DOM.setAttribute(self.getElement(), "id", id)
  297. class FocusWidget(Widget):
  298. def __init__(self, element):
  299. Widget.__init__(self)
  300. self.clickListeners = []
  301. self.focusListeners = []
  302. self.keyboardListeners = []
  303. self.setElement(element)
  304. self.sinkEvents(Event.ONCLICK | Event.FOCUSEVENTS | Event.KEYEVENTS)
  305. def addClickListener(self, listener):
  306. self.clickListeners.append(listener)
  307. def addFocusListener(self, listener):
  308. self.focusListeners.append(listener)
  309. def addKeyboardListener(self, listener):
  310. self.keyboardListeners.append(listener)
  311. def getTabIndex(self):
  312. return Focus.getTabIndex(self, self.getElement())
  313. def isEnabled(self):
  314. return not DOM.getBooleanAttribute(self.getElement(), "disabled")
  315. def onBrowserEvent(self, event):
  316. type = DOM.eventGetType(event)
  317. if type == "click":
  318. for listener in self.clickListeners:
  319. if listener.onClick: listener.onClick(self, event)
  320. else: listener(self, event)
  321. elif type == "blur" or type == "focus":
  322. FocusListener.fireFocusEvent(self, self.focusListeners, self, event)
  323. elif type == "keydown" or type == "keypress" or type == "keyup":
  324. KeyboardListener.fireKeyboardEvent(self, self.keyboardListeners, self, event)
  325. def removeClickListener(self, listener):
  326. self.clickListeners.remove(listener)
  327. def removeFocusListener(self, listener):
  328. self.focusListeners.remove(listener)
  329. def removeKeyboardListener(self, listener):
  330. self.keyboardListeners.remove(listener)
  331. def setAccessKey(self, key):
  332. DOM.setAttribute(self.getElement(), "accessKey", "" + key)
  333. def setEnabled(self, enabled):
  334. DOM.setBooleanAttribute(self.getElement(), "disabled", not enabled)
  335. def setFocus(self, focused):
  336. if (focused):
  337. Focus.focus(self, self.getElement())
  338. else:
  339. Focus.blur(self, self.getElement())
  340. def setTabIndex(self, index):
  341. Focus.setTabIndex(self, self.getElement(), index)
  342. class ButtonBase(FocusWidget):
  343. def __init__(self, element):
  344. FocusWidget.__init__(self, element)
  345. def getHTML(self):
  346. return DOM.getInnerHTML(self.getElement())
  347. def getText(self):
  348. return DOM.getInnerText(self.getElement())
  349. def setHTML(self, html):
  350. DOM.setInnerHTML(self.getElement(), html)
  351. def setText(self, text):
  352. DOM.setInnerText(self.getElement(), text)
  353. class Button(ButtonBase):
  354. def __init__(self, html=None, listener=None):
  355. """
  356. Create a new button widget.
  357. @param html: Html content (e.g. the button label); see setHTML()
  358. @param listener: A new click listener; see addClickListener()
  359. """
  360. ButtonBase.__init__(self, DOM.createButton())
  361. self.adjustType(self.getElement())
  362. self.setStyleName("gwt-Button")
  363. if html:
  364. self.setHTML(html)
  365. if listener:
  366. self.addClickListener(listener)
  367. def adjustType(self, button):
  368. JS("""
  369. if (button.type == 'submit') {
  370. try { button.setAttribute("type", "button"); } catch (e) { }
  371. }
  372. """)
  373. def click(self):
  374. """
  375. Simulate a button click.
  376. """
  377. self.getElement().click()
  378. class CheckBox(ButtonBase):
  379. def __init__(self, label=None, asHTML=False):
  380. self.initElement(DOM.createInputCheck())
  381. self.setStyleName("gwt-CheckBox")
  382. if label:
  383. if asHTML:
  384. self.setHTML(label)
  385. else:
  386. self.setText(label)
  387. def initElement(self, element):
  388. ButtonBase.__init__(self, DOM.createSpan())
  389. self.inputElem = element
  390. self.labelElem = DOM.createLabel()
  391. self.unsinkEvents(Event.FOCUSEVENTS| Event.ONCLICK)
  392. DOM.sinkEvents(self.inputElem, Event.FOCUSEVENTS | Event.ONCLICK | DOM.getEventsSunk(self.inputElem))
  393. DOM.appendChild(self.getElement(), self.inputElem)
  394. DOM.appendChild(self.getElement(), self.labelElem)
  395. uid = "check" + self.getUniqueID()
  396. DOM.setAttribute(self.inputElem, "id", uid)
  397. DOM.setAttribute(self.labelElem, "htmlFor", uid)
  398. # emulate static
  399. def getUniqueID(self):
  400. JS("""
  401. _CheckBox_unique_id++;
  402. return _CheckBox_unique_id;
  403. };
  404. var _CheckBox_unique_id=0;
  405. {
  406. """)
  407. def getHTML(self):
  408. return DOM.getInnerHTML(self.labelElem)
  409. def getName(self):
  410. return DOM.getAttribute(self.inputElem, "name")
  411. def getText(self):
  412. return DOM.getInnerText(self.labelElem)
  413. def setChecked(self, checked):
  414. DOM.setBooleanAttribute(self.inputElem, "checked", checked)
  415. DOM.setBooleanAttribute(self.inputElem, "defaultChecked", checked)
  416. def isChecked(self):
  417. if self.attached:
  418. propName = "checked"
  419. else:
  420. propName = "defaultChecked"
  421. return DOM.getBooleanAttribute(self.inputElem, propName)
  422. def isEnabled(self):
  423. return not DOM.getBooleanAttribute(self.inputElem, "disabled")
  424. def setEnabled(self, enabled):
  425. DOM.setBooleanAttribute(self.inputElem, "disabled", not enabled)
  426. def setFocus(self, focused):
  427. if focused:
  428. Focus.focus(self, self.inputElem)
  429. else:
  430. Focus.blur(self, self.inputElem)
  431. def setHTML(self, html):
  432. DOM.setInnerHTML(self.labelElem, html)
  433. def setName(self, name):
  434. DOM.setAttribute(self.inputElem, "name", name)
  435. def setTabIndex(self, index):
  436. Focus.setTabIndex(self, self.inputElem, index)
  437. def setText(self, text):
  438. DOM.setInnerText(self.labelElem, text)
  439. def onDetach(self):
  440. self.setChecked(self.isChecked())
  441. ButtonBase.onDetach(self)
  442. class RadioButton(CheckBox):
  443. def __init__(self, group, label=None, asHTML=False):
  444. self.initElement(DOM.createInputRadio(group))
  445. self.setStyleName("gwt-RadioButton")
  446. if label:
  447. if asHTML:
  448. self.setHTML(label)
  449. else:
  450. self.setText(label)
  451. class Composite(Widget):
  452. def __init__(self):
  453. Widget.__init__(self)
  454. self.widget = None
  455. def initWidget(self, widget):
  456. if self.widget != None:
  457. return
  458. widget.removeFromParent()
  459. self.setElement(widget.getElement())
  460. self.widget = widget
  461. widget.setParent(self)
  462. def onAttach(self):
  463. Widget.onAttach(self)
  464. self.widget.onAttach()
  465. def onDetach(self):
  466. Widget.onDetach(self)
  467. self.widget.onDetach()
  468. def setWidget(self, widget):
  469. self.initWidget(widget)
  470. class Panel(Widget):
  471. def __init__(self):
  472. Widget.__init__(self)
  473. self.children = []
  474. def add(self):
  475. console.error("This panel does not support no-arg add()")
  476. # TODO: fix iterator remove()
  477. def clear(self):
  478. for child in list(self):
  479. # should be iterator.remove() ==> self.remove(self.children[0]) or self.remove(child) ???
  480. self.remote(child)
  481. def disown(self, widget):
  482. if widget.getParent() != self:
  483. console.error("widget %o is not a child of this panel %o", widget, self)
  484. else:
  485. element = widget.getElement()
  486. widget.setParent(None)
  487. parentElement = DOM.getParent(element)
  488. if parentElement:
  489. DOM.removeChild(parentElement, element)
  490. def adopt(self, widget, container):
  491. widget.removeFromParent()
  492. if container:
  493. DOM.appendChild(container, widget.getElement())
  494. widget.setParent(self)
  495. def remove(self, widget):
  496. pass
  497. def onAttach(self):
  498. Widget.onAttach(self)
  499. for child in self:
  500. child.onAttach()
  501. def onDetach(self):
  502. Widget.onDetach(self)
  503. for child in self:
  504. child.onDetach()
  505. def __iter__(self):
  506. return self.children.__iter__()
  507. class CellFormatter:
  508. def __init__(self, outer):
  509. self.outer = outer
  510. def addStyleName(self, row, column, styleName):
  511. self.outer.prepareCell(row, column)
  512. self.outer.setStyleName(self.getElement(row, column), styleName, True)
  513. def getElement(self, row, column):
  514. self.outer.checkCellBounds(row, column)
  515. return DOM.getChild(self.outer.rowFormatter.getRow(self.outer.bodyElem, row), column)
  516. def getStyleName(self, row, column):
  517. return DOM.getAttribute(self.getElement(row, column), "className")
  518. def isVisible(self, row, column):
  519. element = self.getElement(row, column)
  520. return self.outer.isVisible(element)
  521. def removeStyleName(self, row, column, styleName):
  522. self.checkCellBounds(row, column)
  523. self.outer.setStyleName(self.getElement(row, column), styleName, False)
  524. def setAlignment(self, row, column, hAlign, vAlign):
  525. self.setHorizontalAlignment(row, column, hAlign)
  526. self.setVerticalAlignment(row, column, vAlign)
  527. def setHeight(self, row, column, height):
  528. self.outer.prepareCell(row, column)
  529. element = self.getCellElement(self.outer.bodyElem, row, column)
  530. DOM.setStyleAttribute(element, "height", height)
  531. def setHorizontalAlignment(self, row, column, align):
  532. self.outer.prepareCell(row, column)
  533. element = self.getCellElement(self.outer.bodyElem, row, column)
  534. DOM.setAttribute(element, "align", align)
  535. def setStyleName(self, row, column, styleName):
  536. self.outer.prepareCell(row, column)
  537. self.setAttr(row, column, "className", styleName)
  538. def setVerticalAlignment(self, row, column, align):
  539. self.outer.prepareCell(row, column)
  540. DOM.setStyleAttribute(self.getCellElement(self.outer.bodyElem, row, column), "verticalAlign", align)
  541. def setVisible(self, row, column, visible):
  542. element = self.ensureElement(row, column)
  543. self.outer.setVisible(element, visible)
  544. def setWidth(self, row, column, width):
  545. self.outer.prepareCell(row, column)
  546. DOM.setStyleAttribute(self.getCellElement(self.outer.bodyElem, row, column), "width", width)
  547. def setWordWrap(self, row, column, wrap):
  548. self.outer.prepareCell(row, column)
  549. if wrap:
  550. wrap_str = ""
  551. else:
  552. wrap_str = "nowrap"
  553. DOM.setStyleAttribute(self.getElement(row, column), "whiteSpace", wrap_str)
  554. def getCellElement(self, table, row, col):
  555. JS("""
  556. var out = table.rows[row].cells[col];
  557. return (out == null ? null : out);
  558. """)
  559. def getRawElement(self, row, column):
  560. return self.getCellElement(self.outer.bodyElem, row, column)
  561. def ensureElement(self, row, column):
  562. self.outer.prepareCell(row, column)
  563. return DOM.getChild(self.outer.rowFormatter.ensureElement(row), column)
  564. def getAttr(self, row, column, attr):
  565. elem = self.getElement(row, column)
  566. return DOM.getAttribute(elem, attr)
  567. def setAttr(self, row, column, attrName, value):
  568. elem = self.getElement(row, column)
  569. DOM.setAttribute(elem, attrName, value)
  570. class RowFormatter:
  571. def __init__(self, outer):
  572. self.outer = outer
  573. def addStyleName(self, row, styleName):
  574. self.outer.setStyleName(self.ensureElement(row), styleName, True)
  575. def getElement(self, row):
  576. self.outer.checkRowBounds(row)
  577. return self.getRow(self.outer.bodyElem, row)
  578. def getStyleName(self, row):
  579. return DOM.getAttribute(self.getElement(row), "className")
  580. def isVisible(self, row):
  581. element = self.getElement(row)
  582. return self.outer.isVisible(element)
  583. def removeStyleName(self, row, styleName):
  584. self.outer.setStyleName(self.getElement(row), styleName, False)
  585. def setStyleName(self, row, styleName):
  586. elem = self.ensureElement(row)
  587. DOM.setAttribute(elem, "className", styleName)
  588. def setVerticalAlign(self, row, align):
  589. DOM.setStyleAttribute(self.ensureElement(row), "verticalAlign", align)
  590. def setVisible(self, row, visible):
  591. element = self.ensureElement(row)
  592. self.outer.setVisible(element, visible)
  593. def ensureElement(self, row):
  594. self.outer.prepareRow(row)
  595. return self.getRow(self.outer.bodyElem, row)
  596. def getRow(self, element, row):
  597. JS("""
  598. return element.rows[row];
  599. """)
  600. def setAttr(self, row, attrName, value):
  601. element = self.ensureElement(row)
  602. DOM.setAttribute(element, attrName, value)
  603. class HTMLTable(Panel):
  604. def __init__(self):
  605. Panel.__init__(self)
  606. self.cellFormatter = CellFormatter(self)
  607. self.rowFormatter = RowFormatter(self)
  608. self.tableListeners = []
  609. self.widgetMap = {}
  610. self.tableElem = DOM.createTable()
  611. self.bodyElem = DOM.createTBody()
  612. DOM.appendChild(self.tableElem, self.bodyElem)
  613. self.setElement(self.tableElem)
  614. self.sinkEvents(Event.ONCLICK)
  615. def addTableListener(self, listener):
  616. self.tableListeners.append(listener)
  617. def clear(self):
  618. for row in range(self.getRowCount()):
  619. for col in range(self.getCellCount(row)):
  620. child = self.getWidget(row, col)
  621. if child != None:
  622. self.removeWidget(child)
  623. # assert len(self.widgetMap) == 0
  624. def clearCell(self, row, column):
  625. td = self.cellFormatter.getElement(row, column)
  626. return self.internalClearCell(td)
  627. def getCellCount(self, row):
  628. return 0
  629. def getCellFormatter(self):
  630. return self.cellFormatter
  631. def getCellPadding(self):
  632. return DOM.getIntAttribute(self.tableElem, "cellPadding")
  633. def getCellSpacing(self):
  634. return DOM.getIntAttribute(self.tableElem, "cellSpacing")
  635. def getHTML(self, row, column):
  636. element = self.cellFormatter.getElement(row, column)
  637. return DOM.getInnerHTML(element)
  638. def getRowCount(self):
  639. return 0
  640. def getRowFormatter(self):
  641. return self.rowFormatter
  642. def getText(self, row, column):
  643. self.checkCellBounds(row, column)
  644. element = self.cellFormatter.getElement(row, column)
  645. return DOM.getInnerText(element)
  646. # also callable as getWidget(widgetElement)
  647. def getWidget(self, row, column=None):
  648. if column == None:
  649. key = self.computeKeyForElement(row)
  650. else:
  651. self.checkCellBounds(row, column)
  652. key = self.computeKey(row, column)
  653. if key == None:
  654. return None
  655. return self.widgetMap[key]
  656. def isCellPresent(self, row, column):
  657. # GWT uses "and", possibly a bug
  658. if row >= self.getRowCount() or row < 0:
  659. return False
  660. if column < 0 or column >= self.getCellCount(row):
  661. return False
  662. return True
  663. def __iter__(self):
  664. return self.widgetMap.itervalues()
  665. def onBrowserEvent(self, event):
  666. if DOM.eventGetType(event) == "click":
  667. td = self.getEventTargetCell(event)
  668. if not td:
  669. return
  670. tr = DOM.getParent(td)
  671. body = DOM.getParent(tr)
  672. row = DOM.getChildIndex(body, tr)
  673. column = DOM.getChildIndex(tr, td)
  674. for listener in self.tableListeners:
  675. if listener.onCellClicked:
  676. listener.onCellClicked(self, row, column)
  677. else:
  678. listener(self)
  679. def remove(self, widget):
  680. if widget.getParent() != self:
  681. return False
  682. self.removeWidget(widget)
  683. return True
  684. def removeTableListener(self, listener):
  685. self.tableListeners.remove(listener)
  686. def setBorderWidth(self, width):
  687. DOM.setAttribute(self.tableElem, "border", width)
  688. def setCellPadding(self, padding):
  689. DOM.setIntAttribute(self.tableElem, "cellPadding", padding)
  690. def setCellSpacing(self, spacing):
  691. DOM.setIntAttribute(self.tableElem, "cellSpacing", spacing)
  692. def setHTML(self, row, column, html):
  693. self.prepareCell(row, column)
  694. td = self.cleanCell(row, column)
  695. if html != None:
  696. DOM.setInnerHTML(td, html)
  697. def setText(self, row, column, text):
  698. self.prepareCell(row, column)
  699. td = self.cleanCell(row, column)
  700. if text != None:
  701. DOM.setInnerText(td, text)
  702. def setWidget(self, row, column, widget):
  703. self.prepareCell(row, column)
  704. if widget == None:
  705. return
  706. widget.removeFromParent()
  707. td = self.cleanCell(row, column)
  708. widget_hash = hash(widget)
  709. element = widget.getElement()
  710. DOM.setAttribute(element, "__hash", widget_hash)
  711. self.widgetMap[widget_hash] = widget
  712. self.adopt(widget, td)
  713. def cleanCell(self, row, column):
  714. td = self.cellFormatter.getRawElement(row, column)
  715. self.internalClearCell(td)
  716. return td
  717. def computeKey(self, row, column):
  718. element = self.cellFormatter.getRawElement(row, column)
  719. child = DOM.getFirstChild(element)
  720. if child == None:
  721. return None
  722. return self.computeKeyForElement(child)
  723. def computeKeyForElement(self, widgetElement):
  724. return DOM.getAttribute(widgetElement, "__hash")
  725. def removeWidget(self, widget):
  726. self.disown(widget)
  727. del self.widgetMap[self.computeKeyForElement(widget.getElement())]
  728. return True
  729. def checkCellBounds(self, row, column):
  730. self.checkRowBounds(row)
  731. #if column<0: raise IndexError, "Column " + column + " must be non-negative: " + column
  732. cellSize = self.getCellCount(row)
  733. #if cellSize<column: raise IndexError, "Column " + column + " does not exist, col at row " + row + " size is " + self.getCellCount(row) + "cell(s)"
  734. def checkRowBounds(self, row):
  735. rowSize = self.getRowCount()
  736. #if row >= rowSize or row < 0: raise IndexError, "Row " + row + " does not exist, row size is " + self.getRowCount()
  737. def createCell(self):
  738. return DOM.createTD()
  739. def getBodyElement(self):
  740. return self.bodyElem
  741. # also callable as getDOMCellCount(row)
  742. def getDOMCellCount(self, element, row=None):
  743. if row == None:
  744. return self.getDOMCellCountImpl(self.bodyElem, element)
  745. return self.getDOMCellCountImpl(element, row)
  746. def getDOMCellCountImpl(self, element, row):
  747. JS("""
  748. return element.rows[row].cells.length;
  749. """)
  750. # also callable as getDOMRowCount(element)
  751. def getDOMRowCount(self, element=None):
  752. if element == None:
  753. element = self.bodyElem
  754. return self.getDOMRowCountImpl(element)
  755. def getDOMRowCountImpl(self, element):
  756. JS("""
  757. return element.rows.length;
  758. """)
  759. def getEventTargetCell(self, event):
  760. td = DOM.eventGetTarget(event)
  761. while td != None:
  762. if DOM.getAttribute(td, "tagName").lower() == "td":
  763. tr = DOM.getParent(td)
  764. body = DOM.getParent(tr)
  765. if DOM.compare(body, self.bodyElem):
  766. return td
  767. if DOM.compare(td, self.bodyElem):
  768. return None
  769. td = DOM.getParent(td)
  770. return None
  771. def insertCell(self, row, column):
  772. tr = self.rowFormatter.getRow(self.bodyElem, row)
  773. td = self.createCell()
  774. DOM.insertChild(tr, td, column)
  775. def insertCells(self, row, column, count):
  776. tr = self.rowFormatter.getRow(self.bodyElem, row)
  777. for i in range(column, column + count):
  778. td = self.createCell()
  779. DOM.insertChild(tr, td, i)
  780. def insertRow(self, beforeRow):
  781. if beforeRow != self.getRowCount():
  782. self.checkRowBounds(beforeRow)
  783. tr = DOM.createTR()
  784. DOM.insertChild(self.bodyElem, tr, beforeRow)
  785. return beforeRow
  786. def internalClearCell(self, td):
  787. maybeChild = DOM.getFirstChild(td)
  788. widget = None
  789. if maybeChild != None:
  790. widget = self.getWidget(maybeChild)
  791. if widget != None:
  792. self.removeWidget(widget)
  793. return True
  794. DOM.setInnerHTML(td, "")
  795. return False
  796. def prepareCell(self, row, column):
  797. pass
  798. def prepareRow(self, row):
  799. pass
  800. def removeCell(self, row, column):
  801. self.checkCellBounds(row, column)
  802. td = self.cleanCell(row, column)
  803. tr = self.rowFormatter.getRow(self.bodyElem, row)
  804. DOM.removeChild(tr, td)
  805. def removeRow(self, row):
  806. for column in range(self.getCellCount(row)):
  807. self.cleanCell(row, column)
  808. DOM.removeChild(self.bodyElem, self.rowFormatter.getRow(self.bodyElem, row))
  809. def setCellFormatter(self, cellFormatter):
  810. self.cellFormatter = cellFormatter
  811. def setRowFormatter(self, rowFormatter):
  812. self.rowFormatter = rowFormatter
  813. class Grid(HTMLTable):
  814. def __init__(self, rows=0, columns=0):
  815. HTMLTable.__init__(self)
  816. self.cellFormatter = CellFormatter(self)
  817. self.rowFormatter = RowFormatter(self)
  818. self.numColumns = 0
  819. self.numRows = 0
  820. if rows > 0 or columns > 0:
  821. self.resize(rows, columns)
  822. def resize(self, rows, columns):
  823. self.resizeColumns(columns)
  824. self.resizeRows(rows)
  825. def resizeColumns(self, columns):
  826. if self.numColumns == columns:
  827. return
  828. if self.numColumns > columns:
  829. for i in range(0, self.numRows):
  830. for j in range(self.numColumns - 1, columns - 1, -1):
  831. self.removeCell(i, j)
  832. else:
  833. for i in range(self.numRows):
  834. for j in range(self.numColumns, columns):
  835. self.insertCell(i, j)
  836. self.numColumns = columns
  837. def resizeRows(self, rows):
  838. if self.numRows == rows:
  839. return
  840. if self.numRows < rows:
  841. self.addRows(self.getBodyElement(), rows - self.numRows, self.numColumns)
  842. self.numRows = rows
  843. else:
  844. while self.numRows > rows:
  845. self.numRows -= 1
  846. self.removeRow(self.numRows)
  847. def createCell(self):
  848. td = HTMLTable.createCell(self)
  849. DOM.setInnerHTML(td, "&nbsp;")
  850. return td
  851. def clearCell(self, row, column):
  852. td = self.cellFormatter.getElement(row, column)
  853. b = HTMLTable.internalClearCell(self, td)
  854. DOM.setInnerHTML(td, "&nbsp;")
  855. return b
  856. def prepareCell(self, row, column):
  857. pass
  858. def prepareRow(self, row):
  859. pass
  860. def getCellCount(self, row):
  861. return self.numColumns
  862. def getColumnCount(self):
  863. return self.numColumns
  864. def getRowCount(self):
  865. return self.numRows
  866. def addRows(self, table, numRows, columns):
  867. JS("""
  868. var td = $doc.createElement("td");
  869. td.innerHTML = "&nbsp;";
  870. var row = $doc.createElement("tr");
  871. for(var cellNum = 0; cellNum < columns; cellNum++) {
  872. var cell = td.cloneNode(true);
  873. row.appendChild(cell);
  874. }
  875. table.appendChild(row);
  876. for(var rowNum = 1; rowNum < numRows; rowNum++) {
  877. table.appendChild(row.cloneNode(true));
  878. }
  879. """)
  880. class FlexCellFormatter(CellFormatter):
  881. def __init__(self, outer):
  882. CellFormatter.__init__(self, outer)
  883. def getColSpan(self, row, column):
  884. return DOM.getIntAttribute(self.getElement(row, column), "colSpan")
  885. def getRowSpan(self, row, column):
  886. return DOM.getIntAttribute(self.getElement(row, column), "rowSpan")
  887. def setColSpan(self, row, column, colSpan):
  888. DOM.setIntAttribute(self.ensureElement(row, column), "colSpan", colSpan)
  889. def setRowSpan(self, row, column, rowSpan):
  890. DOM.setIntAttribute(self.ensureElement(row, column), "rowSpan", rowSpan)
  891. class FlexTable(HTMLTable):
  892. def __init__(self):
  893. HTMLTable.__init__(self)
  894. self.cellFormatter = FlexCellFormatter(self)
  895. self.rowFormatter = RowFormatter(self)
  896. def addCell(self, row):
  897. self.insertCell(row, self.getCellCount(row))
  898. def getCellCount(self, row):
  899. self.checkRowBounds(row)
  900. return self.getDOMCellCount(self.getBodyElement(), row)
  901. def getFlexCellFormatter(self):
  902. return self.getCellFormatter()
  903. def getRowCount(self):
  904. return self.getDOMRowCount()
  905. def removeCells(self, row, column, num):
  906. for i in range(i):
  907. self.removeCell(row, column)
  908. def prepareCell(self, row, column):
  909. self.prepareRow(row)
  910. #if column < 0: throw new IndexOutOfBoundsException("Cannot create a column with a negative index: " + column);
  911. cellCount = self.getCellCount(row)
  912. required = column + 1 - cellCount
  913. if required > 0:
  914. self.addCells(self.getBodyElement(), row, required)
  915. def prepareRow(self, row):
  916. #if row < 0: throw new IndexOutOfBoundsException("Cannot create a row with a negative index: " + row);
  917. rowCount = self.getRowCount()
  918. for i in range(rowCount, row + 1):
  919. self.insertRow(i)
  920. def addCells(self, table, row, num):
  921. JS("""
  922. var rowElem = table.rows[row];
  923. for(var i = 0; i < num; i++){
  924. var cell = $doc.createElement("td");
  925. rowElem.appendChild(cell);
  926. }
  927. """)
  928. class ComplexPanel(Panel):
  929. """
  930. Superclass for widgets with multiple children.
  931. """
  932. def __init__(self):
  933. Panel.__init__(self)
  934. self.children = []
  935. def add(self, widget, container):
  936. self.insert(widget, container, len(self.children))
  937. def getChildren(self):
  938. return self.children
  939. def insert(self, widget, container, beforeIndex):
  940. if widget.getParent() == self:
  941. return
  942. self.adopt(widget, container)
  943. self.children.insert(beforeIndex, widget)
  944. def remove(self, widget):
  945. if widget not in self.children:
  946. return False
  947. self.disown(widget)
  948. self.children.remove(widget)
  949. return True
  950. class AbsolutePanel(ComplexPanel):
  951. def __init__(self):
  952. ComplexPanel.__init__(self)
  953. self.setElement(DOM.createDiv())
  954. DOM.setStyleAttribute(self.getElement(), "position", "relative")
  955. DOM.setStyleAttribute(self.getElement(), "overflow", "hidden")
  956. def add(self, widget, left=None, top=None):
  957. ComplexPanel.add(self, widget, self.getElement())
  958. if left != None:
  959. self.setWidgetPosition(widget, left, top)
  960. def setWidgetPosition(self, widget, left, top):
  961. self.checkWidgetParent(widget)
  962. h = widget.getElement()
  963. if (left == -1) and (top == -1):
  964. DOM.setStyleAttribute(h, "left", "")
  965. DOM.setStyleAttribute(h, "top", "")
  966. DOM.setStyleAttribute(h, "position", "static")
  967. else:
  968. DOM.setStyleAttribute(h, "position", "absolute")
  969. DOM.setStyleAttribute(h, "left", left + "px")
  970. DOM.setStyleAttribute(h, "top", top + "px")
  971. def getWidgetLeft(self, widget):
  972. self.checkWidgetParent(widget)
  973. return DOM.getIntAttribute(widget.getElement(), "offsetLeft")
  974. def getWidgetTop(self, widget):
  975. self.checkWidgetParent(widget)
  976. return DOM.getIntAttribute(widget.getElement(), "offsetTop")
  977. def checkWidgetParent(self, widget):
  978. if widget.getParent() != self:
  979. console.error("Widget must be a child of this panel.")
  980. class Label(Widget):
  981. def __init__(self, text=None, wordWrap=True):
  982. Widget.__init__(self)
  983. self.horzAlign = ""
  984. self.clickListeners = []
  985. self.mouseListeners = []
  986. self.setElement(DOM.createDiv())
  987. self.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS)
  988. self.setStyleName("gwt-Label")
  989. if text:
  990. self.setText(text)
  991. self.setWordWrap(wordWrap)
  992. def addClickListener(self, listener):
  993. self.clickListeners.append(listener)
  994. def addMouseListener(self, listener):
  995. self.mouseListeners.append(listener)
  996. def getHorizontalAlignment(self):
  997. return self.horzAlign
  998. def getText(self):
  999. return DOM.getInnerText(self.getElement())
  1000. def getWordWrap(self):
  1001. return not (DOM.getStyleAttribute(self.getElement(), "whiteSpace") == "nowrap")
  1002. def onBrowserEvent(self, event):
  1003. type = DOM.eventGetType(event)
  1004. if type == "click":
  1005. for listener in self.clickListeners:
  1006. if listener.onClick: listener.onClick(self, event)
  1007. else: listener(self, event)
  1008. elif type == "mousedown" or type == "mouseup" or type == "mousemove" or type == "mouseover" or type == "mouseout":
  1009. MouseListener.fireMouseEvent(self, self.mouseListeners, self, event)
  1010. def removeClickListener(self, listener):
  1011. self.clickListeners.remove(listener)
  1012. def removeMouseListener(self, listener):
  1013. self.mouseListeners.remove(listener)
  1014. def setHorizontalAlignment(self, align):
  1015. self.horzAlign = align
  1016. DOM.setStyleAttribute(self.getElement(), "textAlign", align)
  1017. def setText(self, text):
  1018. DOM.setInnerText(self.getElement(), text)
  1019. def setWordWrap(self, wrap):
  1020. if wrap:
  1021. style = "normal"
  1022. else:
  1023. style = "nowrap"
  1024. DOM.setStyleAttribute(self.getElement(), "whiteSpace", style)
  1025. class HTML(Label):
  1026. def __init__(self, html=None, wordWrap=True):
  1027. Label.__init__(self)
  1028. self.setElement(DOM.createDiv())
  1029. self.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS)
  1030. self.setStyleName("gwt-HTML")
  1031. if html:
  1032. self.setHTML(html)
  1033. self.setWordWrap(wordWrap)
  1034. def getHTML(self):
  1035. return DOM.getInnerHTML(self.getElement())
  1036. def setHTML(self, html):
  1037. DOM.setInnerHTML(self.getElement(), html)
  1038. class HasHorizontalAlignment:
  1039. ALIGN_LEFT = "left"
  1040. ALIGN_CENTER = "center"
  1041. ALIGN_RIGHT = "right"
  1042. class HasVerticalAlignment:
  1043. ALIGN_TOP = "top"
  1044. ALIGN_MIDDLE = "middle"
  1045. ALIGN_BOTTOM = "bottom"
  1046. class HasAlignment:
  1047. ALIGN_BOTTOM = "bottom"
  1048. ALIGN_MIDDLE = "middle"
  1049. ALIGN_TOP = "top"
  1050. ALIGN_CENTER = "center"
  1051. ALIGN_LEFT = "left"
  1052. ALIGN_RIGHT = "right"
  1053. class CellPanel(ComplexPanel):
  1054. def __init__(self):
  1055. ComplexPanel.__init__(self)
  1056. self.table = DOM.createTable()
  1057. self.body = DOM.createTBody()
  1058. DOM.appendChild(self.table, self.body)
  1059. self.setElement(self.table)
  1060. def getTable(self):
  1061. return self.table
  1062. def getBody(self):
  1063. return self.body
  1064. def getSpacing(self):
  1065. return self.spacing
  1066. def getWidgetTd(self, widget):
  1067. if widget.getParent() != self:
  1068. return None
  1069. return DOM.getParent(widget.getElement())
  1070. def setBorderWidth(self, width):
  1071. DOM.setAttribute(self.table, "border", "" + width)
  1072. def setCellHeight(self, widget, height):
  1073. td = DOM.getParent(widget.getElement())
  1074. DOM.setAttribute(td, "height", height)
  1075. def setCellHorizontalAlignment(self, widget, align):
  1076. td = self.getWidgetTd(widget)
  1077. if td != None:
  1078. DOM.setAttribute(td, "align", align)
  1079. def setCellVerticalAlignment(self, widget, align):
  1080. td = self.getWidgetTd(widget)
  1081. if td != None:
  1082. DOM.setStyleAttribute(td, "verticalAlign", align)
  1083. def setCellWidth(self, widget, width):
  1084. td = DOM.getParent(widget.getElement())
  1085. DOM.setAttribute(td, "width", width)
  1086. def setSpacing(self, spacing):
  1087. self.spacing = spacing
  1088. DOM.setIntAttribute(self.table, "cellSpacing", spacing)
  1089. class HorizontalPanel(CellPanel):
  1090. def __init__(self):
  1091. CellPanel.__init__(self)
  1092. self.horzAlign = HasHorizontalAlignment.ALIGN_LEFT
  1093. self.vertAlign = HasVerticalAlignment.ALIGN_TOP
  1094. self.tableRow = DOM.createTR()
  1095. DOM.appendChild(self.getBody(), self.tableRow)
  1096. DOM.setAttribute(self.getTable(), "cellSpacing", "0")
  1097. DOM.setAttribute(self.getTable(), "cellPadding", "0")
  1098. def add(self, widget):
  1099. self.insert(widget, self.getWidgetCount())
  1100. def getHorizontalAlignment(self):
  1101. return self.horzAlign
  1102. def getVerticalAlignment(self):
  1103. return self.vertAlign
  1104. def getWidget(self, index):
  1105. return self.children[index]
  1106. def getWidgetCount(self):
  1107. return len(self.children)
  1108. def getWidgetIndex(self, child):
  1109. return self.children.index(child)
  1110. def insert(self, widget, beforeIndex):
  1111. widget.removeFromParent()
  1112. td = DOM.createTD()
  1113. DOM.insertChild(self.tableRow, td, beforeIndex)
  1114. CellPanel.insert(self, widget, td, beforeIndex)
  1115. self.setCellHorizontalAlignment(widget, self.horzAlign)
  1116. self.setCellVerticalAlignment(widget, self.vertAlign)
  1117. def remove(self, widget):
  1118. if widget.getParent() != self:
  1119. return False
  1120. td = DOM.getParent(widget.getElement())
  1121. DOM.removeChild(self.tableRow, td)
  1122. CellPanel.remove(widget)
  1123. return True
  1124. def setHorizontalAlignment(self, align):
  1125. self.horzAlign = align
  1126. def setVerticalAlignment(self, align):
  1127. self.vertAlign = align
  1128. class VerticalPanel(CellPanel):
  1129. def __init__(self):
  1130. CellPanel.__init__(self)
  1131. self.horzAlign = HasHorizontalAlignment.ALIGN_LEFT
  1132. self.vertAlign = HasVerticalAlignment.ALIGN_TOP
  1133. DOM.setAttribute(self.getTable(), "cellSpacing", "0")
  1134. DOM.setAttribute(self.getTable(), "cellPadding", "0")
  1135. def add(self, widget):
  1136. self.insert(widget, self.getWidgetCount())
  1137. def getHorizontalAlignment(self):
  1138. return self.horzAlign
  1139. def getVerticalAlignment(self):
  1140. return self.vertAlign
  1141. def getWidget(self, index):
  1142. return self.children[index]
  1143. def getWidgetCount(self):
  1144. return len(self.children)
  1145. def getWidgetIndex(self, child):
  1146. return self.children.index(child)
  1147. def setWidget(self, index, widget):
  1148. """Replace the widget at the given index with a new one"""
  1149. existing = self.getWidget(index)
  1150. if existing:
  1151. self.remove(existing)
  1152. self.insert(widget, index)
  1153. def insert(self, widget, beforeIndex):
  1154. widget.removeFromParent()
  1155. tr = DOM.createTR()
  1156. td = DOM.createTD()
  1157. DOM.insertChild(self.getBody(), tr, beforeIndex)
  1158. DOM.appendChild(tr, td)
  1159. CellPanel.insert(self, widget, td, beforeIndex)
  1160. self.setCellHorizontalAlignment(widget, self.horzAlign)
  1161. self.setCellVerticalAlignment(widget, self.vertAlign)
  1162. def remove(self, widget):
  1163. if pyjslib.isNumber(widget):
  1164. widget = self.getWidget(widget)
  1165. if widget.getParent() != self:
  1166. return False
  1167. td = DOM.getParent(widget.getElement())
  1168. tr = DOM.getParent(td)
  1169. DOM.removeChild(self.getBody(), tr)
  1170. CellPanel.remove(self, widget)
  1171. return True
  1172. def setHorizontalAlignment(self, align):
  1173. self.horzAlign = align
  1174. def setVerticalAlignment(self, align):
  1175. self.vertAlign = align
  1176. class LayoutData:
  1177. def __init__(self, direction):
  1178. self.direction = direction
  1179. self.hAlign = "left"
  1180. self.height = ""
  1181. self.td = None
  1182. self.vAlign = "top"
  1183. self.width = ""
  1184. class DockPanel(CellPanel):
  1185. CENTER = "center"
  1186. EAST = "east"
  1187. NORTH = "north"
  1188. SOUTH = "south"
  1189. WEST = "west"
  1190. def __init__(self):
  1191. CellPanel.__init__(self)
  1192. self.horzAlign = HasHorizontalAlignment.ALIGN_LEFT
  1193. self.vertAlign = HasVerticalAlignment.ALIGN_TOP
  1194. self.center = None
  1195. self.dock_children = [] # TODO: can self.children be used instead?
  1196. DOM.setIntAttribute(self.getTable(), "cellSpacing", 0)
  1197. DOM.setIntAttribute(self.getTable(), "cellPadding", 0)
  1198. def add(self, widget, direction):
  1199. if direction == self.CENTER:
  1200. if self.center != None:
  1201. console.error("Only one CENTER widget may be added")
  1202. self.center = widget
  1203. layout = LayoutData(direction)
  1204. widget.setLayoutData(layout)
  1205. self.setCellHorizontalAlignment(widget, self.horzAlign)
  1206. self.setCellVerticalAlignment(widget, self.vertAlign)
  1207. self.dock_children.append(widget)
  1208. self.realizeTable(widget)
  1209. def getHorizontalAlignment(self):
  1210. return self.horzAlign
  1211. def getVerticalAlignment(self):
  1212. return self.vertAlign
  1213. def getWidgetDirection(self, widget):
  1214. if widget.getParent() != self:
  1215. return None
  1216. return widget.getLayoutData().direction
  1217. def remove(self, widget):
  1218. if widget == self.center:
  1219. self.center = None
  1220. ret = CellPanel.remove(self, widget)
  1221. if ret:
  1222. self.dock_children.remove(widget)
  1223. self.realizeTable(None)
  1224. return ret
  1225. def setCellHeight(self, widget, height):
  1226. data = widget.getLayoutData()
  1227. data.height = height
  1228. if data.td:
  1229. DOM.setStyleAttribute(data.td, "height", data.height)
  1230. def setCellHorizontalAlignment(self, widget, align):
  1231. data = widget.getLayoutData()
  1232. data.hAlign = align
  1233. if data.td:
  1234. DOM.setAttribute(data.td, "align", data.hAlign)
  1235. def setCellVerticalAlignment(self, widget, align):
  1236. data = widget.getLayoutData()
  1237. data.vAlign = align
  1238. if data.td:
  1239. DOM.setStyleAttribute(data.td, "verticalAlign", data.vAlign)
  1240. def setCellWidth(self, widget, width):
  1241. data = widget.getLayoutData()
  1242. data.width = width
  1243. if data.td:
  1244. DOM.setStyleAttribute(data.td, "width", data.width)
  1245. def setHorizontalAlignment(self, align):
  1246. self.horzAlign = align
  1247. def setVerticalAlignment(self, align):
  1248. self.vertAlign = align
  1249. def realizeTable(self, beingAdded):
  1250. bodyElement = self.getBody()
  1251. while DOM.getChildCount(bodyElement) > 0:
  1252. DOM.removeChild(bodyElement, DOM.getChild(bodyElement, 0))
  1253. rowCount = 1
  1254. colCount = 1
  1255. for child in self.dock_children:
  1256. dir = child.getLayoutData().direction
  1257. if dir == self.NORTH or dir == self.SOUTH:
  1258. rowCount += 1
  1259. elif dir == self.EAST or dir == self.WEST:
  1260. colCount += 1
  1261. rows = []
  1262. for i in range(rowCount):
  1263. rows[i] = DockPanelTmpRow()
  1264. rows[i].tr = DOM.createTR()
  1265. DOM.appendChild(bodyElement, rows[i].tr)
  1266. westCol = 0
  1267. eastCol = colCount - 1
  1268. northRow = 0
  1269. southRow = rowCount - 1
  1270. centerTd = None
  1271. for child in self.dock_children:
  1272. layout = child.getLayoutData()
  1273. td = DOM.createTD()
  1274. layout.td = td
  1275. DOM.setAttribute(layout.td, "align", layout.hAlign)
  1276. DOM.setStyleAttribute(layout.td, "verticalAlign", layout.vAlign)
  1277. DOM.setAttribute(layout.td, "width", layout.width)
  1278. DOM.setAttribute(layout.td, "height", layout.height)
  1279. if layout.direction == self.NORTH:
  1280. DOM.insertChild(rows[northRow].tr, td, rows[northRow].center)
  1281. self.appendAndMaybeAdopt(td, child.getElement(), beingAdded)
  1282. DOM.setIntAttribute(td, "colSpan", eastCol - westCol + 1)
  1283. northRow += 1
  1284. elif layout.direction == self.SOUTH:
  1285. DOM.insertChild(rows[southRow].tr, td, rows[southRow].center)
  1286. self.appendAndMaybeAdopt(td, child.getElement(), beingAdded)
  1287. DOM.setIntAttribute(td, "colSpan", eastCol - westCol + 1)
  1288. southRow -= 1
  1289. elif layout.direction == self.WEST:
  1290. row = rows[northRow]
  1291. DOM.insertChild(row.tr, td, row.center)
  1292. row.center += 1
  1293. self.appendAndMaybeAdopt(td, child.getElement(), beingAdded)
  1294. DOM.setIntAttribute(td, "rowSpan", southRow - northRow + 1)
  1295. westCol += 1
  1296. elif layout.direction == self.EAST:
  1297. row = rows[northRow]
  1298. DOM.insertChild(row.tr, td, row.center)
  1299. self.appendAndMaybeAdopt(td, child.getElement(), beingAdded)
  1300. DOM.setIntAttribute(td, "rowSpan", southRow - northRow + 1)
  1301. eastCol -= 1
  1302. elif layout.direction == self.CENTER:
  1303. centerTd = td
  1304. if self.center != None:
  1305. row = rows[northRow]
  1306. DOM.insertChild(row.tr, centerTd, row.center)
  1307. self.appendAndMaybeAdopt(centerTd, self.center.getElement(), beingAdded)
  1308. def appendAndMaybeAdopt(self, parent, child, beingAdded):
  1309. if beingAdded != None:
  1310. if DOM.compare(child, beingAdded.getElement()):
  1311. CellPanel.add(self, beingAdded, parent)
  1312. return
  1313. DOM.appendChild(parent, child)
  1314. class DockPanelTmpRow:
  1315. center = 0
  1316. tr = None
  1317. rootPanels = {}
  1318. class RootPanel(AbsolutePanel):
  1319. def __init__(self, element=None):
  1320. if pyjslib.isString(element):
  1321. return self.get(element)
  1322. AbsolutePanel.__init__(self)
  1323. if element == None:
  1324. element = self.getBodyElement()
  1325. self.setElement(element)
  1326. self.onAttach()
  1327. def getBodyElement(self):
  1328. JS("""
  1329. return $doc.body;
  1330. """)
  1331. @classmethod
  1332. def get(cls, id=None):
  1333. """
  1334. """
  1335. global rootPanels
  1336. if rootPanels.has_key(id):
  1337. return rootPanels[id]
  1338. element = None
  1339. if id:
  1340. element = DOM.getElementById(id)
  1341. if not element:
  1342. return None
  1343. if len(rootPanels) < 1:
  1344. cls.hookWindowClosing()
  1345. panel = RootPanel(element)
  1346. rootPanels[id] = panel
  1347. return panel
  1348. @classmethod
  1349. def hookWindowClosing(cls):
  1350. Window.addWindowCloseListener(cls)
  1351. @classmethod
  1352. def onWindowClosed(cls):
  1353. global rootPanels
  1354. for panel in rootPanels.itervalues():
  1355. panel.onDetach()
  1356. @classmethod
  1357. def onWindowClosing(cls):
  1358. return None
  1359. class Hyperlink(Widget):
  1360. def __init__(self, text="", asHTML=False, targetHistoryToken=""):
  1361. Widget.__init__(self)
  1362. self.clickListeners = []
  1363. self.targetHistoryToken = ""
  1364. self.setElement(DOM.createDiv())
  1365. self.anchorElem = DOM.createAnchor()
  1366. DOM.appendChild(self.getElement(), self.anchorElem)
  1367. self.sinkEvents(Event.ONCLICK)
  1368. self.setStyleName("gwt-Hyperlink")
  1369. if asHTML:
  1370. self.setHTML(text)
  1371. else:
  1372. self.setText(text)
  1373. if targetHistoryToken:
  1374. self.setTargetHistoryToken(targetHistoryToken)
  1375. def addClickListener(self, listener):
  1376. self.clickListeners.append(listener)
  1377. def getHTML(self):
  1378. return DOM.getInnerHTML(self.anchorElem)
  1379. def getTargetHistoryToken(self):
  1380. return self.targetHistoryToken
  1381. def getText(self):
  1382. return DOM.getInnerText(self.anchorElem)
  1383. def onBrowserEvent(self, event):
  1384. if DOM.eventGetType(event) == "click":
  1385. for listener in self.clickListeners:
  1386. if listener.onClick: listener.onClick(self, event)
  1387. else: listener(self, event)
  1388. History().newItem(self.targetHistoryToken)
  1389. DOM.eventPreventDefault(event)
  1390. def removeClickListener(self, listener):
  1391. self.clickListeners.remove(listener)
  1392. def setHTML(self, html):
  1393. DOM.setInnerHTML(self.anchorElem, html)
  1394. def setTargetHistoryToken(self, targetHistoryToken):
  1395. self.targetHistoryToken = targetHistoryToken
  1396. DOM.setAttribute(self.anchorElem, "href", "#" + targetHistoryToken)
  1397. def setText(self, text):
  1398. DOM.setInnerText(self.anchorElem, text)
  1399. prefetchImages = {}
  1400. class Image(Widget):
  1401. def __init__(self, url=""):
  1402. Widget.__init__(self)
  1403. self.clickListeners = []
  1404. self.loadListeners = []
  1405. self.mouseListeners = []
  1406. self.setElement(DOM.createImg())
  1407. self.sinkEvents(Event.ONCLICK | Event.MOUSEEVENTS | Event.ONLOAD | Event.ONERROR)
  1408. self.setStyleName("gwt-Image")
  1409. if url:
  1410. self.setUrl(url)
  1411. def addClickListener(self, listener):
  1412. self.clickListeners.append(listener)
  1413. def addLoadListener(self, listener):
  1414. self.loadListeners.append(listener)
  1415. def addMouseListener(self, listener):
  1416. self.mouseListeners.append(listener)
  1417. def getUrl(self):
  1418. return DOM.getAttribute(self.getElement(), "src")
  1419. def onBrowserEvent(self, event):
  1420. type = DOM.eventGetType(event)
  1421. if type == "click":
  1422. for listener in self.clickListeners:
  1423. if listener.onClick: listener.onClick(self, event)
  1424. else: listener(self, event)
  1425. elif type == "mousedown" or type == "mouseup" or type == "mousemove" or type == "mouseover" or type == "mouseout":
  1426. MouseListener.fireMouseEvent(self, self.mouseListeners, self, event)
  1427. elif type == "load":
  1428. for listener in self.loadListeners:
  1429. listener.onLoad(self)
  1430. elif type == "error":
  1431. for listener in self.loadListeners:
  1432. listener.onError(self)
  1433. def prefetch(self, url):
  1434. global prefetchImages
  1435. img = DOM.createImg()
  1436. DOM.setAttribute(img, "src", url)
  1437. prefetchImages[url] = img
  1438. def setUrl(self, url):
  1439. DOM.setAttribute(self.getElement(), "src", url)
  1440. class FlowPanel(ComplexPanel):
  1441. def __init__(self):
  1442. ComplexPanel.__init__(self)
  1443. self.setElement(DOM.createDiv())
  1444. def add(self, w):
  1445. ComplexPanel.add(self, w, self.getElement())
  1446. def getWidget(self, index):
  1447. return self.children[index]
  1448. def getWidgetCount(self):
  1449. return len(self.children)
  1450. def getWidgetIndex(self, child):
  1451. return self.children.index(child)
  1452. def remove(self, index):
  1453. if pyjslib.isNumber(index):
  1454. index = self.getWidget(index)
  1455. return ComplexPanel.remove(self, index)
  1456. HTMLPanel_sUid = 0
  1457. class HTMLPanel(ComplexPanel):
  1458. def __init__(self, html):
  1459. ComplexPanel.__init__(self)
  1460. self.setElement(DOM.createDiv())
  1461. DOM.setInnerHTML(self.getElement(), html)
  1462. def add(self, widget, id):
  1463. element = self.getElementById(self.getElement(), id)
  1464. if element == None:
  1465. # throw new NoSuchElementException()
  1466. return
  1467. ComplexPanel.add(self, widget, element)
  1468. def createUniqueId(self):
  1469. global HTMLPanel_sUid
  1470. HTMLPanel_sUid += 1
  1471. return "HTMLPanel_" + HTMLPanel_sUid
  1472. def getElementById(self, element, id):
  1473. element_id = DOM.getAttribute(element, "id")
  1474. if element_id != None and element_id == id:
  1475. return element
  1476. child = DOM.getFirstChild(element)
  1477. while child != None:
  1478. ret = self.getElementById(child, id)
  1479. if ret != None:
  1480. return ret
  1481. child = DOM.getNextSibling(child)
  1482. return None
  1483. class DeckPanel(ComplexPanel):
  1484. def __init__(self):
  1485. ComplexPanel.__init__(self)
  1486. self.visibleWidget = None
  1487. self.setElement(DOM.createDiv())
  1488. def add(self, widget):
  1489. self.insert(widget, self.getWidgetCount())
  1490. def getVisibleWidget(self):
  1491. return self.getWidgetIndex(self.visibleWidget)
  1492. def getWidget(self, index):
  1493. return self.children[index]
  1494. def getWidgetCount(self):
  1495. return len(self.children)
  1496. def getWidgetIndex(self, child):
  1497. return self.children.index(child)
  1498. def insert(self, widget, beforeIndex):
  1499. if (self.beforeIndex < 0) or (self.beforeIndex > self.getWidgetCount()):
  1500. # throw new IndexOutOfBoundsException();
  1501. return
  1502. ComplexPanel.insert(self, widget, self.getElement(), beforeIndex)
  1503. child = widget.getElement()
  1504. DOM.setStyleAttribute(child, "width", "100%")
  1505. DOM.setStyleAttribute(child, "height", "100%")
  1506. widget.setVisible(False)
  1507. def remove(self, widget):
  1508. if pyjslib.isNumber(widget):
  1509. widget = self.getWidget(widget)
  1510. if not ComplexPanel.remove(self, widget):
  1511. return False
  1512. if self.visibleWidget == widget:
  1513. self.visibleWidget = None
  1514. return True
  1515. def showWidget(self, index):
  1516. self.checkIndex(index)
  1517. if self.visibleWidget != None:
  1518. self.visibleWidget.setVisible(False)
  1519. self.visibleWidget = self.getWidget(index)
  1520. self.visibleWidget.setVisible(True)
  1521. def checkIndex(self, index):
  1522. if (index < 0) or (index >= self.getWidgetCount()):
  1523. # throw new IndexOutOfBoundsException();
  1524. pass
  1525. class SimplePanel(Panel):
  1526. """
  1527. A panel which contains a single widget. Useful if you have an area where
  1528. you'd like to be able to replace the widget with another, or if you need to
  1529. wrap something in a DIV.
  1530. """
  1531. def __init__(self, element=None):
  1532. Panel.__init__(self)
  1533. if element == None:
  1534. element = DOM.createDiv()
  1535. self.setElement(element)
  1536. def add(self, widget):
  1537. if self.getWidget() != None:
  1538. console.error("SimplePanel can only contain one child widget")
  1539. return
  1540. self.setWidget(widget)
  1541. def getWidget(self):
  1542. if len(self.children):
  1543. return self.children[0]
  1544. return None
  1545. def remove(self, widget):
  1546. if self.getWidget() == widget:
  1547. self.disown(widget)
  1548. del self.children[0]
  1549. return True
  1550. return False
  1551. def getContainerElement(self):
  1552. return self.getElement()
  1553. def setWidget(self, w