GNU Classpath (0.19) | ||
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.Insets; 49: import java.awt.Rectangle; 50: import java.awt.event.ActionEvent; 51: import java.awt.event.ActionListener; 52: import java.awt.event.MouseEvent; 53: import java.io.IOException; 54: import java.io.ObjectInputStream; 55: import java.io.ObjectOutputStream; 56: import java.util.EventObject; 57: 58: import javax.swing.DefaultCellEditor; 59: import javax.swing.Icon; 60: import javax.swing.JTextField; 61: import javax.swing.JTree; 62: import javax.swing.SwingUtilities; 63: import javax.swing.UIDefaults; 64: import javax.swing.UIManager; 65: import javax.swing.border.Border; 66: import javax.swing.event.CellEditorListener; 67: import javax.swing.event.EventListenerList; 68: import javax.swing.event.TreeSelectionEvent; 69: import javax.swing.event.TreeSelectionListener; 70: 71: /** 72: * DefaultTreeCellEditor 73: * @author Andrew Selkirk 74: */ 75: public class DefaultTreeCellEditor 76: implements ActionListener, TreeCellEditor, TreeSelectionListener 77: { 78: /** 79: * EditorContainer 80: */ 81: public class EditorContainer extends Container 82: { 83: /** 84: * Creates an <code>EditorContainer</code> object. 85: */ 86: public EditorContainer() 87: { 88: // Do nothing here. 89: } 90: 91: /** 92: * This method only exists for API compatibility and is useless as it does 93: * nothing. It got probably introduced by accident. 94: */ 95: public void EditorContainer() 96: { 97: // Do nothing here. 98: } 99: 100: /** 101: * Returns the preferred size for the Container. 102: * 103: * @return Dimension of EditorContainer 104: */ 105: public Dimension getPreferredSize() 106: { 107: Dimension containerSize = super.getPreferredSize(); 108: containerSize.width += DefaultTreeCellEditor.this.offset; 109: return containerSize; 110: } 111: 112: /** 113: * Overrides Container.paint to paint the node's icon and use the selection 114: * color for the background. 115: * 116: * @param g - 117: * the specified Graphics window 118: */ 119: public void paint(Graphics g) 120: { 121: Rectangle tr = tree.getPathBounds(lastPath); 122: if (tr != null) 123: { 124: Insets i = ((DefaultTextField) editingComponent).getBorder() 125: .getBorderInsets(this); 126: int textIconGap = 3; 127: tr.x -= i.left; 128: 129: // paints icon 130: if (editingIcon != null) 131: { 132: editingIcon.paintIcon(this, g, tr.x - editingIcon. 133: getIconWidth()/2, tr.y + i.top + i.bottom); 134: tr.x += editingIcon.getIconWidth()/2 + textIconGap; 135: } 136: 137: tr.width += offset; 138: 139: // paint background 140: g.translate(tr.x, tr.y); 141: editingComponent.setSize(new Dimension(tr.width, tr.height)); 142: editingComponent.paint(g); 143: g.translate(-tr.x, -tr.y); 144: } 145: super.paint(g); 146: } 147: 148: /** 149: * Lays out this Container. If editing, the editor will be placed at offset 150: * in the x direction and 0 for y. 151: */ 152: public void doLayout() 153: { 154: if (DefaultTreeCellEditor.this.tree.isEditing()) 155: setLocation(offset, 0); 156: super.doLayout(); 157: } 158: } 159: 160: /** 161: * DefaultTextField 162: */ 163: public class DefaultTextField extends JTextField 164: { 165: /** 166: * border 167: */ 168: protected Border border; 169: 170: /** 171: * Creates a <code>DefaultTextField</code> object. 172: * 173: * @param border the border to use 174: */ 175: public DefaultTextField(Border border) 176: { 177: this.border = border; 178: } 179: 180: /** 181: * Gets the font of this component. 182: * @return this component's font; if a font has not been set for 183: * this component, the font of its parent is returned (if the parent 184: * is not null, otherwise null is returned). 185: */ 186: public Font getFont() 187: { 188: Font font = super.getFont(); 189: if (font == null) 190: { 191: Component parent = getParent(); 192: if (parent != null) 193: return parent.getFont(); 194: return null; 195: } 196: return font; 197: } 198: 199: /** 200: * Returns the border of the text field. 201: * 202: * @return the border 203: */ 204: public Border getBorder() 205: { 206: return border; 207: } 208: 209: /** 210: * Overrides JTextField.getPreferredSize to return the preferred size 211: * based on current font, if set, or else use renderer's font. 212: * 213: * @return the Dimension of this textfield. 214: */ 215: public Dimension getPreferredSize() 216: { 217: String s = getText(); 218: 219: Font f = getFont(); 220: 221: if (f != null) 222: { 223: FontMetrics fm = getToolkit().getFontMetrics(f); 224: 225: return new Dimension(SwingUtilities.computeStringWidth(fm, s), 226: fm.getHeight()); 227: } 228: return renderer.getPreferredSize(); 229: } 230: } 231: 232: private EventListenerList listenerList = new EventListenerList(); 233: 234: /** 235: * Editor handling the editing. 236: */ 237: protected TreeCellEditor realEditor; 238: 239: /** 240: * Renderer, used to get border and offsets from. 241: */ 242: protected DefaultTreeCellRenderer renderer; 243: 244: /** 245: * Editing container, will contain the editorComponent. 246: */ 247: protected Container editingContainer; 248: 249: /** 250: * Component used in editing, obtained from the editingContainer. 251: */ 252: protected transient Component editingComponent; 253: 254: /** 255: * As of Java 2 platform v1.4 this field should no longer be used. 256: * If you wish to provide similar behavior you should directly 257: * override isCellEditable. 258: */ 259: protected boolean canEdit; 260: 261: /** 262: * Used in editing. Indicates x position to place editingComponent. 263: */ 264: protected transient int offset; 265: 266: /** 267: * JTree instance listening too. 268: */ 269: protected transient JTree tree; 270: 271: /** 272: * Last path that was selected. 273: */ 274: protected transient TreePath lastPath; 275: 276: /** 277: * Used before starting the editing session. 278: */ 279: protected transient javax.swing.Timer timer; 280: 281: /** 282: * Row that was last passed into getTreeCellEditorComponent. 283: */ 284: protected transient int lastRow; 285: 286: /** 287: * True if the border selection color should be drawn. 288: */ 289: protected Color borderSelectionColor; 290: 291: /** 292: * Icon to use when editing. 293: */ 294: protected transient Icon editingIcon; 295: 296: /** 297: * Font to paint with, null indicates font of renderer is to be used. 298: */ 299: protected Font font; 300: 301: /** 302: * Helper field used to save the last path seen while the timer was 303: * running. 304: */ 305: private TreePath tPath; 306: 307: /** 308: * Constructs a DefaultTreeCellEditor object for a JTree using the 309: * specified renderer and a default editor. (Use this constructor 310: * for normal editing.) 311: * 312: * @param tree - a JTree object 313: * @param renderer - a DefaultTreeCellRenderer object 314: */ 315: public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer) 316: { 317: this(tree, renderer, null); 318: } 319: 320: /** 321: * Constructs a DefaultTreeCellEditor object for a JTree using the specified 322: * renderer and the specified editor. (Use this constructor 323: * for specialized editing.) 324: * 325: * @param tree - a JTree object 326: * @param renderer - a DefaultTreeCellRenderer object 327: * @param editor - a TreeCellEditor object 328: */ 329: public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer, 330: TreeCellEditor editor) 331: { 332: setTree(tree); 333: this.renderer = renderer; 334: 335: if (editor == null) 336: editor = createTreeCellEditor(); 337: realEditor = editor; 338: 339: lastPath = tree.getLeadSelectionPath(); 340: tree.addTreeSelectionListener(this); 341: editingContainer = createContainer(); 342: UIDefaults defaults = UIManager.getLookAndFeelDefaults(); 343: setFont(defaults.getFont("Tree.font")); 344: setBorderSelectionColor(defaults.getColor("Tree.selectionBorderColor")); 345: editingIcon = renderer.getIcon(); 346: timer = new javax.swing.Timer(1200, this); 347: } 348: 349: /** 350: * Configures the editing component whenever it is null. 351: * 352: * @param tree the tree to configure to component for. 353: * @param renderer the renderer used to set up the nodes 354: * @param editor the editor used 355: */ 356: private void configureEditingComponent(JTree tree, 357: DefaultTreeCellRenderer renderer, 358: TreeCellEditor editor) 359: { 360: if (tree != null && lastPath != null) 361: { 362: Object val = lastPath.getLastPathComponent(); 363: boolean isLeaf = tree.getModel().isLeaf(val); 364: boolean expanded = tree.isExpanded(lastPath); 365: determineOffset(tree, val, true, expanded, isLeaf, lastRow); 366: 367: // set up icon 368: if (isLeaf) 369: renderer.setIcon(renderer.getLeafIcon()); 370: else if (expanded) 371: renderer.setIcon(renderer.getOpenIcon()); 372: else 373: renderer.setIcon(renderer.getClosedIcon()); 374: editingIcon = renderer.getIcon(); 375: 376: editingComponent = getTreeCellEditorComponent(tree, val, true, 377: expanded, isLeaf, lastRow); 378: } 379: } 380: 381: /** 382: * writeObject 383: * 384: * @param value0 385: * TODO 386: * @exception IOException 387: * TODO 388: */ 389: private void writeObject(ObjectOutputStream value0) throws IOException 390: { 391: // TODO 392: } 393: 394: /** 395: * readObject 396: * @param value0 TODO 397: * @exception IOException TODO 398: * @exception ClassNotFoundException TODO 399: */ 400: private void readObject(ObjectInputStream value0) 401: throws IOException, ClassNotFoundException 402: { 403: // TODO 404: } 405: 406: /** 407: * Sets the color to use for the border. 408: * @param newColor - the new border color 409: */ 410: public void setBorderSelectionColor(Color newColor) 411: { 412: this.borderSelectionColor = newColor; 413: } 414: 415: /** 416: * Returns the color the border is drawn. 417: * @return Color 418: */ 419: public Color getBorderSelectionColor() 420: { 421: return borderSelectionColor; 422: } 423: 424: /** 425: * Sets the font to edit with. null indicates the renderers 426: * font should be used. This will NOT override any font you have 427: * set in the editor the receiver was instantied with. If null for 428: * an editor was passed in, a default editor will be created that 429: * will pick up this font. 430: * 431: * @param font - the editing Font 432: */ 433: public void setFont(Font font) 434: { 435: if (font != null) 436: this.font = font; 437: else 438: this.font = renderer.getFont(); 439: } 440: 441: /** 442: * Gets the font used for editing. 443: * 444: * @return the editing font 445: */ 446: public Font getFont() 447: { 448: return font; 449: } 450: 451: /** 452: * Configures the editor. Passed onto the realEditor. 453: * Sets an initial value for the editor. This will cause 454: * the editor to stopEditing and lose any partially edited value 455: * if the editor is editing when this method is called. 456: * Returns the component that should be added to the client's Component 457: * hierarchy. Once installed in the client's hierarchy this component will 458: * then be able to draw and receive user input. 459: * 460: * @param tree - the JTree that is asking the editor to edit; this parameter can be null 461: * @param value - the value of the cell to be edited 462: * @param isSelected - true is the cell is to be rendered with selection highlighting 463: * @param expanded - true if the node is expanded 464: * @param leaf - true if the node is a leaf node 465: * @param row - the row index of the node being edited 466: * 467: * @return the component for editing 468: */ 469: public Component getTreeCellEditorComponent(JTree tree, Object value, 470: boolean isSelected, boolean expanded, 471: boolean leaf, int row) 472: { 473: if (realEditor == null) 474: createTreeCellEditor(); 475: 476: return realEditor.getTreeCellEditorComponent(tree, value, isSelected, 477: expanded, leaf, row); 478: } 479: 480: /** 481: * Returns the value currently being edited. 482: * 483: * @return the value currently being edited 484: */ 485: public Object getCellEditorValue() 486: { 487: return editingComponent; 488: } 489: 490: /** 491: * If the realEditor returns true to this message, prepareForEditing 492: * is messaged and true is returned. 493: * 494: * @param event - the event the editor should use to consider whether to begin editing or not 495: * @return true if editing can be started 496: */ 497: public boolean isCellEditable(EventObject event) 498: { 499: if (editingComponent == null) 500: configureEditingComponent(tree, renderer, realEditor); 501: 502: if (editingComponent != null && realEditor.isCellEditable(event)) 503: { 504: prepareForEditing(); 505: return true; 506: } 507: 508: // Cell may not be currently editable, but may need to start timer. 509: if (shouldStartEditingTimer(event)) 510: startEditingTimer(); 511: else if (timer.isRunning()) 512: timer.stop(); 513: return false; 514: } 515: 516: /** 517: * Messages the realEditor for the return value. 518: * 519: * @param event - 520: * the event the editor should use to start editing 521: * @return true if the editor would like the editing cell to be selected; 522: * otherwise returns false 523: */ 524: public boolean shouldSelectCell(EventObject event) 525: { 526: return true; 527: } 528: 529: /** 530: * If the realEditor will allow editing to stop, the realEditor 531: * is removed and true is returned, otherwise false is returned. 532: * @return true if editing was stopped; false otherwise 533: */ 534: public boolean stopCellEditing() 535: { 536: if (editingComponent != null && realEditor.stopCellEditing()) 537: { 538: timer.stop(); 539: return true; 540: } 541: return false; 542: } 543: 544: /** 545: * Messages cancelCellEditing to the realEditor and removes it 546: * from this instance. 547: */ 548: public void cancelCellEditing() 549: { 550: if (editingComponent != null) 551: { 552: timer.stop(); 553: realEditor.cancelCellEditing(); 554: } 555: } 556: 557: /** 558: * Adds a <code>CellEditorListener</code> object to this editor. 559: * 560: * @param listener the listener to add 561: */ 562: public void addCellEditorListener(CellEditorListener listener) 563: { 564: realEditor.addCellEditorListener(listener); 565: } 566: 567: /** 568: * Removes a <code>CellEditorListener</code> object. 569: * 570: * @param listener the listener to remove 571: */ 572: public void removeCellEditorListener(CellEditorListener listener) 573: { 574: realEditor.removeCellEditorListener(listener); 575: } 576: 577: /** 578: * Returns all added <code>CellEditorListener</code> objects to this editor. 579: * 580: * @return an array of listeners 581: * 582: * @since 1.4 583: */ 584: public CellEditorListener[] getCellEditorListeners() 585: { 586: return (CellEditorListener[]) listenerList.getListeners(CellEditorListener.class); 587: } 588: 589: /** 590: * Resets lastPath. 591: * 592: * @param e - the event that characterizes the change. 593: */ 594: public void valueChanged(TreeSelectionEvent e) 595: { 596: tPath = lastPath; 597: lastPath = e.getNewLeadSelectionPath(); 598: lastRow = tree.getRowForPath(lastPath); 599: configureEditingComponent(tree, renderer, realEditor); 600: } 601: 602: /** 603: * Messaged when the timer fires, this will start the editing session. 604: * 605: * @param e the event that characterizes the action. 606: */ 607: public void actionPerformed(ActionEvent e) 608: { 609: if (lastPath != null && tPath != null && tPath.equals(lastPath)) 610: { 611: tree.startEditingAtPath(lastPath); 612: timer.stop(); 613: } 614: } 615: 616: /** 617: * Sets the tree currently editing for. This is needed to add a selection 618: * listener. 619: * 620: * @param newTree - 621: * the new tree to be edited 622: */ 623: protected void setTree(JTree newTree) 624: { 625: tree = newTree; 626: } 627: 628: /** 629: * Returns true if event is a MouseEvent and the click count is 1. 630: * 631: * @param event - the event being studied 632: * @return true if editing should start 633: */ 634: protected boolean shouldStartEditingTimer(EventObject event) 635: { 636: if ((event instanceof MouseEvent) && 637: ((MouseEvent) event).getClickCount() == 1) 638: return true; 639: return false; 640: } 641: 642: /** 643: * Starts the editing timer. 644: */ 645: protected void startEditingTimer() 646: { 647: if (timer == null) 648: timer = new javax.swing.Timer(1200, this); 649: if (!timer.isRunning()) 650: timer.start(); 651: } 652: 653: /** 654: * Returns true if event is null, or it is a MouseEvent with 655: * a click count > 2 and inHitRegion returns true. 656: * 657: * @param event - the event being studied 658: * @return true if event is null, or it is a MouseEvent with 659: * a click count > 2 and inHitRegion returns true 660: */ 661: protected boolean canEditImmediately(EventObject event) 662: { 663: if (event == null || !(event instanceof MouseEvent) || (((MouseEvent) event). 664: getClickCount() > 2 && inHitRegion(((MouseEvent) event).getX(), 665: ((MouseEvent) event).getY()))) 666: return true; 667: return false; 668: } 669: 670: /** 671: * Returns true if the passed in location is a valid mouse location 672: * to start editing from. This is implemented to return false if x is 673: * less than or equal to the width of the icon and icon 674: * gap displayed by the renderer. In other words this returns true if 675: * the user clicks over the text part displayed by the renderer, and 676: * false otherwise. 677: * 678: * @param x - the x-coordinate of the point 679: * @param y - the y-coordinate of the point 680: * 681: * @return true if the passed in location is a valid mouse location 682: */ 683: protected boolean inHitRegion(int x, int y) 684: { 685: Rectangle bounds = tree.getPathBounds(lastPath); 686: 687: return bounds.contains(x, y); 688: } 689: 690: /** 691: * determineOffset 692: * @param tree - 693: * @param value - 694: * @param isSelected - 695: * @param expanded - 696: * @param leaf - 697: * @param row - 698: */ 699: protected void determineOffset(JTree tree, Object value, boolean isSelected, 700: boolean expanded, boolean leaf, int row) 701: { 702: renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded, 703: leaf, row, true); 704: Icon c = renderer.getIcon(); 705: if (c != null) 706: offset = renderer.getIconTextGap() + c.getIconWidth(); 707: else 708: offset = 0; 709: } 710: 711: /** 712: * Invoked just before editing is to start. Will add the 713: * editingComponent to the editingContainer. 714: */ 715: protected void prepareForEditing() 716: { 717: editingContainer.add(editingComponent); 718: } 719: 720: /** 721: * Creates the container to manage placement of editingComponent. 722: * 723: * @return the container to manage the placement of the editingComponent. 724: */ 725: protected Container createContainer() 726: { 727: return new DefaultTreeCellEditor.EditorContainer(); 728: } 729: 730: /** 731: * This is invoked if a TreeCellEditor is not supplied in the constructor. 732: * It returns a TextField editor. 733: * 734: * @return a new TextField editor 735: */ 736: protected TreeCellEditor createTreeCellEditor() 737: { 738: UIDefaults defaults = UIManager.getLookAndFeelDefaults(); 739: realEditor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField( 740: defaults.getBorder("Tree.selectionBorder"))); 741: return realEditor; 742: } 743: }
GNU Classpath (0.19) |