1 16 package com.google.gwt.user.client.ui; 17 18 import com.google.gwt.core.client.GWT; 19 import com.google.gwt.user.client.DOM; 20 import com.google.gwt.user.client.Element; 21 import com.google.gwt.user.client.Event; 22 23 import java.util.ArrayList ; 24 import java.util.HashSet ; 25 import java.util.Iterator ; 26 import java.util.List ; 27 import java.util.Set ; 28 29 47 public class Tree extends Widget implements HasWidgets, SourcesTreeEvents, 48 HasFocus { 49 50 57 private static class ImagesFromImageBase implements TreeImages { 58 63 private class Prototype extends AbstractImagePrototype { 64 private final String imageUrl; 65 66 Prototype(String url) { 67 imageUrl = url; 68 } 69 70 public void applyTo(Image image) { 71 image.setUrl(baseUrl + imageUrl); 72 } 73 74 public Image createImage() { 75 throw new UnsupportedOperationException ("createImage is unsupported."); 78 } 79 80 public String getHTML() { 81 throw new UnsupportedOperationException ("getHTML is unsupported."); 84 } 85 } 86 87 private final String baseUrl; 88 89 ImagesFromImageBase(String baseUrl) { 90 this.baseUrl = baseUrl; 91 } 92 93 public AbstractImagePrototype treeClosed() { 94 return new Prototype("tree_closed.gif"); 95 } 96 97 public AbstractImagePrototype treeLeaf() { 98 return new Prototype("tree_white.gif"); 99 } 100 101 public AbstractImagePrototype treeOpen() { 102 return new Prototype("tree_open.gif"); 103 } 104 105 String getBaseUrl() { 106 return baseUrl; 107 } 108 } 109 110 113 private final Set childWidgets = new HashSet (); 114 private TreeItem curSelection; 115 private final Element focusable; 116 private FocusListenerCollection focusListeners; 117 private TreeImages images; 118 private KeyboardListenerCollection keyboardListeners; 119 private TreeListenerCollection listeners; 120 private MouseListenerCollection mouseListeners = null; 121 private final TreeItem root; 122 123 127 private int lastEventType; 128 129 132 public Tree() { 133 this((TreeImages) GWT.create(TreeImages.class)); 134 } 135 136 141 public Tree(TreeImages images) { 142 this.images = images; 143 setElement(DOM.createDiv()); 144 DOM.setStyleAttribute(getElement(), "position", "relative"); 145 focusable = FocusPanel.impl.createFocusable(); 146 DOM.setStyleAttribute(focusable, "fontSize", "0"); 147 DOM.setStyleAttribute(focusable, "position", "absolute"); 148 DOM.setIntStyleAttribute(focusable, "zIndex", -1); 149 DOM.appendChild(getElement(), focusable); 150 151 sinkEvents(Event.MOUSEEVENTS | Event.ONCLICK | Event.KEYEVENTS); 152 DOM.sinkEvents(focusable, Event.FOCUSEVENTS); 153 154 root = new TreeItem() { 157 public void addItem(TreeItem item) { 158 if ((item.getParentItem() != null) || (item.getTree() != null)) { 160 item.remove(); 161 } 162 item.setTree(this.getTree()); 163 164 item.setParentItem(null); 166 getChildren().add(item); 167 168 DOM.setIntStyleAttribute(item.getElement(), "marginLeft", 0); 170 } 171 172 public void removeItem(TreeItem item) { 173 if (!getChildren().contains(item)) { 174 return; 175 } 176 item.setTree(null); 178 item.setParentItem(null); 179 getChildren().remove(item); 180 } 181 }; 182 root.setTree(this); 183 setStyleName("gwt-Tree"); 184 } 185 186 192 public void add(Widget widget) { 193 addItem(widget); 194 } 195 196 public void addFocusListener(FocusListener listener) { 197 if (focusListeners == null) { 198 focusListeners = new FocusListenerCollection(); 199 } 200 focusListeners.add(listener); 201 } 202 203 209 public TreeItem addItem(String itemText) { 210 TreeItem ret = new TreeItem(itemText); 211 addItem(ret); 212 213 return ret; 214 } 215 216 221 public void addItem(TreeItem item) { 222 root.addItem(item); 223 DOM.appendChild(getElement(), item.getElement()); 224 } 225 226 231 public TreeItem addItem(Widget widget) { 232 TreeItem item = root.addItem(widget); 233 DOM.appendChild(getElement(), item.getElement()); 234 return item; 235 } 236 237 public void addKeyboardListener(KeyboardListener listener) { 238 if (keyboardListeners == null) { 239 keyboardListeners = new KeyboardListenerCollection(); 240 } 241 keyboardListeners.add(listener); 242 } 243 244 public void addMouseListener(MouseListener listener) { 245 if (mouseListeners == null) { 246 mouseListeners = new MouseListenerCollection(); 247 } 248 mouseListeners.add(listener); 249 } 250 251 public void addTreeListener(TreeListener listener) { 252 if (listeners == null) { 253 listeners = new TreeListenerCollection(); 254 } 255 listeners.add(listener); 256 } 257 258 261 public void clear() { 262 int size = root.getChildCount(); 263 for (int i = size - 1; i >= 0; i--) { 264 root.getChild(i).remove(); 265 } 266 } 267 268 272 public void ensureSelectedItemVisible() { 273 if (curSelection == null) { 274 return; 275 } 276 277 TreeItem parent = curSelection.getParentItem(); 278 while (parent != null) { 279 parent.setState(true); 280 parent = parent.getParentItem(); 281 } 282 } 283 284 293 public String getImageBase() { 294 return (images instanceof ImagesFromImageBase) 295 ? ((ImagesFromImageBase) images).getBaseUrl() : GWT.getModuleBaseURL(); 296 } 297 298 304 public TreeItem getItem(int index) { 305 return root.getChild(index); 306 } 307 308 313 public int getItemCount() { 314 return root.getChildCount(); 315 } 316 317 322 public TreeItem getSelectedItem() { 323 return curSelection; 324 } 325 326 public int getTabIndex() { 327 return FocusPanel.impl.getTabIndex(focusable); 328 } 329 330 336 public Iterator iterator() { 337 return getChildWidgets().iterator(); 338 } 339 340 public void onBrowserEvent(Event event) { 341 int eventType = DOM.eventGetType(event); 342 switch (eventType) { 343 case Event.ONCLICK: { 344 Element e = DOM.eventGetTarget(event); 345 if (shouldTreeDelegateFocusToElement(e)) { 346 } else { 350 setFocus(true); 351 } 352 break; 353 } 354 case Event.ONMOUSEDOWN: { 355 if (mouseListeners != null) { 356 mouseListeners.fireMouseEvent(this, event); 357 } 358 elementClicked(root, DOM.eventGetTarget(event)); 359 break; 360 } 361 362 case Event.ONMOUSEUP: { 363 if (mouseListeners != null) { 364 mouseListeners.fireMouseEvent(this, event); 365 } 366 break; 367 } 368 369 case Event.ONMOUSEMOVE: { 370 if (mouseListeners != null) { 371 mouseListeners.fireMouseEvent(this, event); 372 } 373 break; 374 } 375 376 case Event.ONMOUSEOVER: { 377 if (mouseListeners != null) { 378 mouseListeners.fireMouseEvent(this, event); 379 } 380 break; 381 } 382 383 case Event.ONMOUSEOUT: { 384 if (mouseListeners != null) { 385 mouseListeners.fireMouseEvent(this, event); 386 } 387 break; 388 } 389 390 case Event.ONFOCUS: 391 if (focusListeners != null) { 393 focusListeners.fireFocusEvent(this, event); 394 } 395 break; 396 397 case Event.ONBLUR: { 398 if (focusListeners != null) { 399 focusListeners.fireFocusEvent(this, event); 400 } 401 402 break; 403 } 404 405 case Event.ONKEYDOWN: 406 if (curSelection == null) { 408 if (root.getChildCount() > 0) { 409 onSelection(root.getChild(0), true, true); 410 } 411 super.onBrowserEvent(event); 412 return; 413 } 414 415 if (lastEventType == Event.ONKEYDOWN) { 416 return; 420 } 421 422 if (isKeyboardNavigationEnabled(curSelection)) { 424 switch (DOM.eventGetKeyCode(event)) { 425 case KeyboardListener.KEY_UP: { 426 moveSelectionUp(curSelection); 427 DOM.eventPreventDefault(event); 428 break; 429 } 430 case KeyboardListener.KEY_DOWN: { 431 moveSelectionDown(curSelection, true); 432 DOM.eventPreventDefault(event); 433 break; 434 } 435 case KeyboardListener.KEY_LEFT: { 436 if (curSelection.getState()) { 437 curSelection.setState(false); 438 } else { 439 TreeItem parent = curSelection.getParentItem(); 440 if (parent != null) { 441 setSelectedItem(parent); 442 } 443 } 444 DOM.eventPreventDefault(event); 445 break; 446 } 447 case KeyboardListener.KEY_RIGHT: { 448 if (!curSelection.getState()) { 449 curSelection.setState(true); 450 } else if (curSelection.getChildCount() > 0) { 451 setSelectedItem(curSelection.getChild(0)); 452 } 453 DOM.eventPreventDefault(event); 454 break; 455 } 456 } 457 } 458 459 case Event.ONKEYUP: 461 if (eventType == Event.ONKEYUP) { 462 if (DOM.eventGetKeyCode(event) == KeyboardListener.KEY_TAB) { 465 ArrayList chain = new ArrayList (); 466 collectElementChain(chain, getElement(), DOM.eventGetTarget(event)); 467 TreeItem item = findItemByChain(chain, 0, root); 468 if (item != getSelectedItem()) { 469 setSelectedItem(item, true); 470 } 471 } 472 } 473 474 case Event.ONKEYPRESS: { 476 if (keyboardListeners != null) { 477 keyboardListeners.fireKeyboardEvent(this, event); 478 } 479 break; 480 } 481 } 482 483 super.onBrowserEvent(event); 485 lastEventType = eventType; 486 } 487 488 public boolean remove(Widget w) { 489 throw new UnsupportedOperationException ( 490 "Widgets should never be directly removed from a tree"); 491 } 492 493 public void removeFocusListener(FocusListener listener) { 494 if (focusListeners != null) { 495 focusListeners.remove(listener); 496 } 497 } 498 499 504 public void removeItem(TreeItem item) { 505 root.removeItem(item); 506 DOM.removeChild(getElement(), item.getElement()); 507 } 508 509 512 public void removeItems() { 513 while (getItemCount() > 0) { 514 removeItem(getItem(0)); 515 } 516 } 517 518 public void removeKeyboardListener(KeyboardListener listener) { 519 if (keyboardListeners != null) { 520 keyboardListeners.remove(listener); 521 } 522 } 523 524 public void removeTreeListener(TreeListener listener) { 525 if (listeners != null) { 526 listeners.remove(listener); 527 } 528 } 529 530 public void setAccessKey(char key) { 531 FocusPanel.impl.setAccessKey(focusable, key); 532 } 533 534 public void setFocus(boolean focus) { 535 if (focus) { 536 FocusPanel.impl.focus(focusable); 537 } else { 538 FocusPanel.impl.blur(focusable); 539 } 540 } 541 542 552 public void setImageBase(String baseUrl) { 553 images = new ImagesFromImageBase(baseUrl); 554 root.updateStateRecursive(); 555 } 556 557 563 public void setSelectedItem(TreeItem item) { 564 setSelectedItem(item, true); 565 } 566 567 574 public void setSelectedItem(TreeItem item, boolean fireEvents) { 575 if (item == null) { 576 if (curSelection == null) { 577 return; 578 } 579 curSelection.setSelected(false); 580 curSelection = null; 581 return; 582 } 583 584 onSelection(item, fireEvents, true); 585 } 586 587 public void setTabIndex(int index) { 588 FocusPanel.impl.setTabIndex(focusable, index); 589 } 590 591 594 public Iterator treeItemIterator() { 595 List accum = new ArrayList (); 596 root.addTreeItems(accum); 597 return accum.iterator(); 598 } 599 600 609 protected boolean isKeyboardNavigationEnabled(TreeItem currentItem) { 610 return true; 611 } 612 613 protected void onAttach() { 614 super.onAttach(); 615 616 for (Iterator it = iterator(); it.hasNext();) { 618 Widget child = (Widget) it.next(); 619 child.onAttach(); 620 } 621 DOM.setEventListener(focusable, this); 622 } 623 624 protected void onDetach() { 625 super.onDetach(); 626 627 for (Iterator it = iterator(); it.hasNext();) { 629 Widget child = (Widget) it.next(); 630 child.onDetach(); 631 } 632 DOM.setEventListener(focusable, null); 633 } 634 635 protected void onLoad() { 636 root.updateStateRecursive(); 637 } 638 639 void adopt(TreeItem.ContentPanel content) { 640 getChildWidgets().add(content); 641 content.treeSetParent(this); 642 } 643 644 void disown(TreeItem.ContentPanel item) { 645 getChildWidgets().remove(item); 646 item.treeSetParent(null); 647 } 648 649 void fireStateChanged(TreeItem item) { 650 if (listeners != null) { 651 listeners.fireItemStateChanged(item); 652 } 653 } 654 655 661 Set getChildWidgets() { 662 return childWidgets; 663 } 664 665 TreeImages getImages() { 666 return images; 667 } 668 669 672 private void collectElementChain(ArrayList chain, Element hRoot, Element hElem) { 673 if ((hElem == null) || DOM.compare(hElem, hRoot)) { 674 return; 675 } 676 677 collectElementChain(chain, hRoot, DOM.getParent(hElem)); 678 chain.add(hElem); 679 } 680 681 private boolean elementClicked(TreeItem root, Element hElem) { 682 ArrayList chain = new ArrayList (); 683 collectElementChain(chain, getElement(), hElem); 684 685 TreeItem item = findItemByChain(chain, 0, root); 686 if (item != null) { 687 if (DOM.isOrHasChild(item.getImageElement(), hElem)) { 688 item.setState(!item.getState(), true); 689 return true; 690 } else if (DOM.isOrHasChild(item.getElement(), hElem)) { 691 onSelection(item, true, !shouldTreeDelegateFocusToElement(hElem)); 692 return true; 693 } 694 } 695 696 return false; 697 } 698 699 private TreeItem findDeepestOpenChild(TreeItem item) { 700 if (!item.getState()) { 701 return item; 702 } 703 return findDeepestOpenChild(item.getChild(item.getChildCount() - 1)); 704 } 705 706 private TreeItem findItemByChain(ArrayList chain, int idx, TreeItem root) { 707 if (idx == chain.size()) { 708 return root; 709 } 710 711 Element hCurElem = (Element) chain.get(idx); 712 for (int i = 0, n = root.getChildCount(); i < n; ++i) { 713 TreeItem child = root.getChild(i); 714 if (DOM.compare(child.getElement(), hCurElem)) { 715 TreeItem retItem = findItemByChain(chain, idx + 1, root.getChild(i)); 716 if (retItem == null) { 717 return child; 718 } 719 return retItem; 720 } 721 } 722 723 return findItemByChain(chain, idx + 1, root); 724 } 725 726 731 private void moveFocus(TreeItem selection) { 732 HasFocus focusableWidget = selection.getFocusableWidget(); 733 if (focusableWidget != null) { 734 focusableWidget.setFocus(true); 735 DOM.scrollIntoView(((Widget) focusableWidget).getElement()); 736 } else { 737 Element selectedElem = selection.getContentElem(); 740 int containerLeft = getAbsoluteLeft(); 741 int containerTop = getAbsoluteTop(); 742 743 int left = DOM.getAbsoluteLeft(selectedElem) - containerLeft; 744 int top = DOM.getAbsoluteTop(selectedElem) - containerTop; 745 int width = DOM.getElementPropertyInt(selectedElem, "offsetWidth"); 746 int height = DOM.getElementPropertyInt(selectedElem, "offsetHeight"); 747 748 DOM.setIntStyleAttribute(focusable, "left", left); 751 DOM.setIntStyleAttribute(focusable, "top", top); 752 DOM.setIntStyleAttribute(focusable, "width", width); 753 DOM.setIntStyleAttribute(focusable, "height", height); 754 755 DOM.scrollIntoView(focusable); 757 758 FocusPanel.impl.focus(focusable); 761 } 762 } 763 764 767 private void moveSelectionDown(TreeItem sel, boolean dig) { 768 if (sel == root) { 769 return; 770 } 771 772 TreeItem parent = sel.getParentItem(); 773 if (parent == null) { 774 parent = root; 775 } 776 int idx = parent.getChildIndex(sel); 777 778 if (!dig || !sel.getState()) { 779 if (idx < parent.getChildCount() - 1) { 780 onSelection(parent.getChild(idx + 1), true, true); 781 } else { 782 moveSelectionDown(parent, false); 783 } 784 } else if (sel.getChildCount() > 0) { 785 onSelection(sel.getChild(0), true, true); 786 } 787 } 788 789 792 private void moveSelectionUp(TreeItem sel) { 793 TreeItem parent = sel.getParentItem(); 794 if (parent == null) { 795 parent = root; 796 } 797 int idx = parent.getChildIndex(sel); 798 799 if (idx > 0) { 800 TreeItem sibling = parent.getChild(idx - 1); 801 onSelection(findDeepestOpenChild(sibling), true, true); 802 } else { 803 onSelection(parent, true, true); 804 } 805 } 806 807 private void onSelection(TreeItem item, boolean fireEvents, boolean moveFocus) { 808 809 if (item == root) { 812 return; 813 } 814 815 if (curSelection != null) { 816 curSelection.setSelected(false); 817 } 818 819 curSelection = item; 820 821 if (moveFocus && curSelection != null) { 822 moveFocus(curSelection); 823 824 curSelection.setSelected(true); 826 if (fireEvents && (listeners != null)) { 827 listeners.fireItemSelected(curSelection); 828 } 829 } 830 } 831 832 private native boolean shouldTreeDelegateFocusToElement(Element elem) ; 841 } 842 | Popular Tags |