GNU Classpath (0.91) | |
Frames | No Frames |
1: /* DefaultTreeCellEditor.java -- 2: Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing.tree; 40: 41: import java.awt.Color; 42: import java.awt.Component; 43: import java.awt.Container; 44: import java.awt.Dimension; 45: import java.awt.Font; 46: import java.awt.FontMetrics; 47: import java.awt.Graphics; 48: import java.awt.Rectangle; 49: import java.awt.event.ActionEvent; 50: import java.awt.event.ActionListener; 51: import java.awt.event.MouseEvent; 52: import java.io.IOException; 53: import java.io.ObjectInputStream; 54: import java.io.ObjectOutputStream; 55: import java.util.EventObject; 56: 57: import javax.swing.DefaultCellEditor; 58: import javax.swing.Icon; 59: import javax.swing.JTextField; 60: import javax.swing.JTree; 61: import javax.swing.SwingUtilities; 62: import javax.swing.UIManager; 63: import javax.swing.border.Border; 64: import javax.swing.event.CellEditorListener; 65: import javax.swing.event.ChangeEvent; 66: import javax.swing.event.EventListenerList; 67: import javax.swing.event.TreeSelectionEvent; 68: import javax.swing.event.TreeSelectionListener; 69: 70: /** 71: * Participates in the tree cell editing. 72: * 73: * @author Andrew Selkirk 74: * @author Audrius Meskauskas 75: */ 76: public class DefaultTreeCellEditor 77: implements ActionListener, TreeCellEditor, TreeSelectionListener 78: { 79: /** 80: * The number of the fast mouse clicks, required to start the editing 81: * session. 82: */ 83: static int CLICK_COUNT_TO_START = 3; 84: 85: /** 86: * This container that appears on the tree during editing session. 87: * It contains the editing component displays various other editor - 88: * specific parts like editing icon. 89: */ 90: public class EditorContainer extends Container 91: { 92: /** 93: * Use v 1.5 serial version UID for interoperability. 94: */ 95: static final long serialVersionUID = 6470339600449699810L; 96: 97: /** 98: * Creates an <code>EditorContainer</code> object. 99: */ 100: public EditorContainer() 101: { 102: // Do nothing here. 103: } 104: 105: /** 106: * This method only exists for API compatibility and is useless as it does 107: * nothing. It got probably introduced by accident. 108: */ 109: public void EditorContainer() 110: { 111: // Do nothing here. 112: } 113: 114: public void setBounds(Rectangle bounds) 115: { 116: super.setBounds(bounds); 117: doLayout(); 118: } 119: 120: /** 121: * Overrides Container.paint to paint the node's icon and use the selection 122: * color for the background. 123: * 124: * @param g - 125: * the specified Graphics window 126: */ 127: public void paint(Graphics g) 128: { 129: if (editingIcon != null) 130: { 131: // From the previous version, the left margin is taken as half 132: // of the icon width. 133: editingIcon.paintIcon(this, g, 0, 0); 134: } 135: super.paint(g); 136: } 137: 138: /** 139: * Lays out this Container, moving the editor component to the left 140: * (leaving place for the icon). 141: */ 142: public void doLayout() 143: { 144: // The offset of the editing component. 145: int eOffset; 146: 147: // Move the component to the left, leaving room for the editing icon: 148: if (editingIcon != null) 149: eOffset = editingIcon.getIconWidth(); 150: else 151: eOffset = 0; 152: 153: Rectangle bounds = getBounds(); 154: Component c = getComponent(0); 155: c.setLocation(eOffset, 0); 156: 157: // Span the editing component near over all window width. 158: c.setSize(bounds.width - eOffset, bounds.height); 159: /* 160: * @specnote the Sun sets some more narrow editing component width (it is 161: * not documented how does it is calculated). However as our text field is 162: * still not able to auto - scroll horizontally, replicating such strategy 163: * would prevent adding extra characters to the text being edited. 164: */ 165: } 166: } 167: 168: /** 169: * The default text field, used in the editing sessions. 170: */ 171: public class DefaultTextField extends JTextField 172: { 173: /** 174: * Use v 1.5 serial version UID for interoperability. 175: */ 176: static final long serialVersionUID = -6629304544265300143L; 177: 178: /** 179: * The border of the text field. 180: */ 181: protected Border border; 182: 183: /** 184: * Creates a <code>DefaultTextField</code> object. 185: * 186: * @param aBorder the border to use 187: */ 188: public DefaultTextField(Border aBorder) 189: { 190: border = aBorder; 191: } 192: 193: /** 194: * Gets the font of this component. 195: * @return this component's font; if a font has not been set for 196: * this component, the font of its parent is returned (if the parent 197: * is not null, otherwise null is returned). 198: */ 199: public Font getFont() 200: { 201: Font font = super.getFont(); 202: if (font == null) 203: { 204: Component parent = getParent(); 205: if (parent != null) 206: return parent.getFont(); 207: return null; 208: } 209: return font; 210: } 211: 212: /** 213: * Returns the border of the text field. 214: * 215: * @return the border 216: */ 217: public Border getBorder() 218: { 219: return border; 220: } 221: 222: /** 223: * Overrides JTextField.getPreferredSize to return the preferred size 224: * based on current font, if set, or else use renderer's font. 225: * 226: * @return the Dimension of this textfield. 227: */ 228: public Dimension getPreferredSize() 229: { 230: String s = getText(); 231: 232: Font f = getFont(); 233: 234: if (f != null) 235: { 236: FontMetrics fm = getToolkit().getFontMetrics(f); 237: 238: return new Dimension(SwingUtilities.computeStringWidth(fm, s), 239: fm.getHeight()); 240: } 241: return renderer.getPreferredSize(); 242: } 243: } 244: 245: /** 246: * Listens for the events from the realEditor. 247: */ 248: class RealEditorListener implements CellEditorListener 249: { 250: /** 251: * The method is called when the editing has been cancelled. 252: * @param event unused 253: */ 254: public void editingCanceled(ChangeEvent event) 255: { 256: cancelCellEditing(); 257: } 258: 259: /** 260: * The method is called after completing the editing session. 261: * 262: * @param event unused 263: */ 264: public void editingStopped(ChangeEvent event) 265: { 266: stopCellEditing(); 267: } 268: } 269: 270: private EventListenerList listenerList = new EventListenerList(); 271: 272: /** 273: * Editor handling the editing. 274: */ 275: protected TreeCellEditor realEditor; 276: 277: /** 278: * Renderer, used to get border and offsets from. 279: */ 280: protected DefaultTreeCellRenderer renderer; 281: 282: /** 283: * Editing container, will contain the editorComponent. 284: */ 285: protected Container editingContainer; 286: 287: /** 288: * Component used in editing, obtained from the editingContainer. 289: */ 290: protected transient Component editingComponent; 291: 292: /** 293: * As of Java 2 platform v1.4 this field should no longer be used. 294: * If you wish to provide similar behavior you should directly 295: * override isCellEditable. 296: */ 297: protected boolean canEdit; 298: 299: /** 300: * Used in editing. Indicates x position to place editingComponent. 301: */ 302: protected transient int offset; 303: 304: /** 305: * JTree instance listening too. 306: */ 307: protected transient JTree tree; 308: 309: /** 310: * Last path that was selected. 311: */ 312: protected transient TreePath lastPath; 313: 314: /** 315: * Used before starting the editing session. 316: */ 317: protected transient javax.swing.Timer timer; 318: 319: /** 320: * Row that was last passed into getTreeCellEditorComponent. 321: */ 322: protected transient int lastRow; 323: 324: /** 325: * True if the border selection color should be drawn. 326: */ 327: protected Color borderSelectionColor; 328: 329: /** 330: * Icon to use when editing. 331: */ 332: protected transient Icon editingIcon; 333: 334: /** 335: * Font to paint with, null indicates font of renderer is to be used. 336: */ 337: protected Font font; 338: 339: /** 340: * Helper field used to save the last path seen while the timer was 341: * running. 342: */ 343: private TreePath tPath; 344: 345: /** 346: * Constructs a DefaultTreeCellEditor object for a JTree using the 347: * specified renderer and a default editor. (Use this constructor 348: * for normal editing.) 349: * 350: * @param tree - a JTree object 351: * @param renderer - a DefaultTreeCellRenderer object 352: */ 353: public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) 354: { 355: this(tree, renderer, null); 356: } 357: 358: /** 359: * Constructs a DefaultTreeCellEditor object for a JTree using the specified 360: * renderer and the specified editor. (Use this constructor 361: * for specialized editing.) 362: * 363: * @param tree - a JTree object 364: * @param renderer - a DefaultTreeCellRenderer object 365: * @param editor - a TreeCellEditor object 366: */ 367: public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer, 368: TreeCellEditor editor) 369: { 370: setTree(tree); 371: this.renderer = renderer; 372: 373: if (editor == null) 374: editor = createTreeCellEditor(); 375: else 376: editor.addCellEditorListener(new RealEditorListener()); 377: 378: realEditor = editor; 379: 380: lastPath = tree.getLeadSelectionPath(); 381: tree.addTreeSelectionListener(this); 382: editingContainer = createContainer(); 383: setFont(UIManager.getFont("Tree.font")); 384: setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor")); 385: editingIcon = renderer.getIcon(); 386: } 387: 388: /** 389: * Configures the editing component whenever it is null. 390: * 391: * @param tree the tree to configure to component for. 392: * @param renderer the renderer used to set up the nodes 393: * @param editor the editor used 394: */ 395: private void configureEditingComponent(JTree tree, 396: DefaultTreeCellRenderer renderer, 397: TreeCellEditor editor) 398: { 399: if (tree != null && lastPath != null) 400: { 401: Object val = lastPath.getLastPathComponent(); 402: boolean isLeaf = tree.getModel().isLeaf(val); 403: boolean expanded = tree.isExpanded(lastPath); 404: determineOffset(tree, val, true, expanded, isLeaf, lastRow); 405: 406: // set up icon 407: if (isLeaf) 408: renderer.setIcon(renderer.getLeafIcon()); 409: else if (expanded) 410: renderer.setIcon(renderer.getOpenIcon()); 411: else 412: renderer.setIcon(renderer.getClosedIcon()); 413: editingIcon = renderer.getIcon(); 414: 415: editingComponent = getTreeCellEditorComponent(tree, val, true, 416: expanded, isLeaf, lastRow); 417: } 418: } 419: 420: /** 421: * writeObject 422: * 423: * @param value0 424: * TODO 425: * @exception IOException 426: * TODO 427: */ 428: private void writeObject(ObjectOutputStream value0) throws IOException 429: { 430: // TODO 431: } 432: 433: /** 434: * readObject 435: * @param value0 TODO 436: * @exception IOException TODO 437: * @exception ClassNotFoundException TODO 438: */ 439: private void readObject(ObjectInputStream value0) 440: throws IOException, ClassNotFoundException 441: { 442: // TODO 443: } 444: 445: /** 446: * Sets the color to use for the border. 447: * @param newColor - the new border color 448: */ 449: public void setBorderSelectionColor(Color newColor) 450: { 451: this.borderSelectionColor = newColor; 452: } 453: 454: /** 455: * Returns the color the border is drawn. 456: * @return Color 457: */ 458: public Color getBorderSelectionColor() 459: { 460: return borderSelectionColor; 461: } 462: 463: /** 464: * Sets the font to edit with. null indicates the renderers 465: * font should be used. This will NOT override any font you have 466: * set in the editor the receiver was instantied with. If null for 467: * an editor was passed in, a default editor will be created that 468: * will pick up this font. 469: * 470: * @param font - the editing Font 471: */ 472: public void setFont(Font font) 473: { 474: if (font != null) 475: this.font = font; 476: else 477: this.font = renderer.getFont(); 478: } 479: 480: /** 481: * Gets the font used for editing. 482: * 483: * @return the editing font 484: */ 485: public Font getFont() 486: { 487: return font; 488: } 489: 490: /** 491: * Configures the editor. Passed onto the realEditor. 492: * Sets an initial value for the editor. This will cause 493: * the editor to stopEditing and lose any partially edited value 494: * if the editor is editing when this method is called. 495: * Returns the component that should be added to the client's Component 496: * hierarchy. Once installed in the client's hierarchy this component will 497: * then be able to draw and receive user input. 498: * 499: * @param tree - the JTree that is asking the editor to edit; this parameter can be null 500: * @param value - the value of the cell to be edited 501: * @param isSelected - true is the cell is to be rendered with selection highlighting 502: * @param expanded - true if the node is expanded 503: * @param leaf - true if the node is a leaf node 504: * @param row - the row index of the node being edited 505: * 506: * @return the component for editing 507: */ 508: public Component getTreeCellEditorComponent(JTree tree, Object value, 509: boolean isSelected, boolean expanded, 510: boolean leaf, int row) 511: { 512: if (realEditor == null) 513: realEditor = createTreeCellEditor(); 514: 515: return realEditor.getTreeCellEditorComponent(tree, value, isSelected, 516: expanded, leaf, row); 517: } 518: 519: /** 520: * Returns the value currently being edited (requests it from the 521: * {@link realEditor}. 522: * 523: * @return the value currently being edited 524: */ 525: public Object getCellEditorValue() 526: { 527: return realEditor.getCellEditorValue(); 528: } 529: 530: /** 531: * If the realEditor returns true to this message, prepareForEditing 532: * is messaged and true is returned. 533: * 534: * @param event - the event the editor should use to consider whether to 535: * begin editing or not 536: * @return true if editing can be started 537: */ 538: public boolean isCellEditable(EventObject event) 539: { 540: if (editingComponent == null) 541: configureEditingComponent(tree, renderer, realEditor); 542: 543: if (editingComponent != null && realEditor.isCellEditable(event)) 544: { 545: prepareForEditing(); 546: return true; 547: } 548: return false; 549: } 550: 551: /** 552: * Messages the realEditor for the return value. 553: * 554: * @param event - 555: * the event the editor should use to start editing 556: * @return true if the editor would like the editing cell to be selected; 557: * otherwise returns false 558: */ 559: public boolean shouldSelectCell(EventObject event) 560: { 561: return true; 562: } 563: 564: /** 565: * If the realEditor will allow editing to stop, the realEditor 566: * is removed and true is returned, otherwise false is returned. 567: * @return true if editing was stopped; false otherwise 568: */ 569: public boolean stopCellEditing() 570: { 571: if (editingComponent != null) 572: { 573: stopEditingTimer(); 574: tree.stopEditing(); 575: editingComponent = null; 576: return true; 577: } 578: return false; 579: } 580: 581: /** 582: * Messages cancelCellEditing to the realEditor and removes it 583: * from this instance. 584: */ 585: public void cancelCellEditing() 586: { 587: if (editingComponent != null) 588: { 589: tree.cancelEditing(); 590: editingComponent = null; 591: } 592: stopEditingTimer(); 593: } 594: 595: /** 596: * Stop the editing timer, if it is installed and running. 597: */ 598: private void stopEditingTimer() 599: { 600: if (timer != null && timer.isRunning()) 601: timer.stop(); 602: } 603: 604: /** 605: * Adds a <code>CellEditorListener</code> object to this editor. 606: * 607: * @param listener 608: * the listener to add 609: */ 610: public void addCellEditorListener(CellEditorListener listener) 611: { 612: realEditor.addCellEditorListener(listener); 613: } 614: 615: /** 616: * Removes a <code>CellEditorListener</code> object. 617: * 618: * @param listener the listener to remove 619: */ 620: public void removeCellEditorListener(CellEditorListener listener) 621: { 622: realEditor.removeCellEditorListener(listener); 623: } 624: 625: /** 626: * Returns all added <code>CellEditorListener</code> objects to this editor. 627: * 628: * @return an array of listeners 629: * 630: * @since 1.4 631: */ 632: public CellEditorListener[] getCellEditorListeners() 633: { 634: return (CellEditorListener[]) listenerList.getListeners(CellEditorListener.class); 635: } 636: 637: /** 638: * Resets lastPath. 639: * 640: * @param e - the event that characterizes the change. 641: */ 642: public void valueChanged(TreeSelectionEvent e) 643: { 644: tPath = lastPath; 645: lastPath = e.getNewLeadSelectionPath(); 646: lastRow = tree.getRowForPath(lastPath); 647: stopCellEditing(); 648: } 649: 650: /** 651: * Messaged when the timer fires. 652: * 653: * @param e the event that characterizes the action. 654: */ 655: public void actionPerformed(ActionEvent e) 656: { 657: } 658: 659: /** 660: * Sets the tree currently editing for. This is needed to add a selection 661: * listener. 662: * 663: * @param newTree - 664: * the new tree to be edited 665: */ 666: protected void setTree(JTree newTree) 667: { 668: tree = newTree; 669: } 670: 671: /** 672: * Returns true if event is a MouseEvent and the click count is 1. 673: * 674: * @param event - the event being studied 675: * @return true if editing should start 676: */ 677: protected boolean shouldStartEditingTimer(EventObject event) 678: { 679: if ((event instanceof MouseEvent) && 680: ((MouseEvent) event).getClickCount() == 1) 681: return true; 682: return false; 683: } 684: 685: /** 686: * Starts the editing timer (if one installed). 687: */ 688: protected void startEditingTimer() 689: { 690: if (timer != null) 691: timer.start(); 692: } 693: 694: /** 695: * Returns true if event is null, or it is a MouseEvent with 696: * a click count > 2 and inHitRegion returns true. 697: * 698: * @param event - the event being studied 699: * @return true if event is null, or it is a MouseEvent with 700: * a click count > 2 and inHitRegion returns true 701: */ 702: protected boolean canEditImmediately(EventObject event) 703: { 704: if (event == null || !(event instanceof MouseEvent) || (((MouseEvent) event). 705: getClickCount() > 2 && inHitRegion(((MouseEvent) event).getX(), 706: ((MouseEvent) event).getY()))) 707: return true; 708: return false; 709: } 710: 711: /** 712: * Returns true if the passed in location is a valid mouse location 713: * to start editing from. This is implemented to return false if x is 714: * less than or equal to the width of the icon and icon 715: * gap displayed by the renderer. In other words this returns true if 716: * the user clicks over the text part displayed by the renderer, and 717: * false otherwise. 718: * 719: * @param x - the x-coordinate of the point 720: * @param y - the y-coordinate of the point 721: * 722: * @return true if the passed in location is a valid mouse location 723: */ 724: protected boolean inHitRegion(int x, int y) 725: { 726: Rectangle bounds = tree.getPathBounds(lastPath); 727: 728: return bounds.contains(x, y); 729: } 730: 731: /** 732: * determineOffset 733: * @param tree - 734: * @param value - 735: * @param isSelected - 736: * @param expanded - 737: * @param leaf - 738: * @param row - 739: */ 740: protected void determineOffset(JTree tree, Object value, boolean isSelected, 741: boolean expanded, boolean leaf, int row) 742: { 743: renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded, 744: leaf, row, true); 745: Icon c = renderer.getIcon(); 746: if (c != null) 747: offset = renderer.getIconTextGap() + c.getIconWidth(); 748: else 749: offset = 0; 750: } 751: 752: /** 753: * Invoked just before editing is to start. Will add the 754: * editingComponent to the editingContainer. 755: */ 756: protected void prepareForEditing() 757: { 758: editingContainer.removeAll(); 759: editingContainer.add(editingComponent); 760: } 761: 762: /** 763: * Creates the container to manage placement of editingComponent. 764: * 765: * @return the container to manage the placement of the editingComponent. 766: */ 767: protected Container createContainer() 768: { 769: return new DefaultTreeCellEditor.EditorContainer(); 770: } 771: 772: /** 773: * This is invoked if a TreeCellEditor is not supplied in the constructor. 774: * It returns a TextField editor. 775: * 776: * @return a new TextField editor 777: */ 778: protected TreeCellEditor createTreeCellEditor() 779: { 780: DefaultCellEditor editor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField( 781: UIManager.getBorder("Tree.selectionBorder"))); 782: editor.addCellEditorListener(new RealEditorListener()); 783: editor.setClickCountToStart(CLICK_COUNT_TO_START); 784: realEditor = editor; 785: return editor; 786: } 787: }
GNU Classpath (0.91) |