GNU Classpath (0.20) | |
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: setFont(UIManager.getFont("Tree.font")); 343: setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor")); 344: editingIcon = renderer.getIcon(); 345: timer = new javax.swing.Timer(1200, this); 346: } 347: 348: /** 349: * Configures the editing component whenever it is null. 350: * 351: * @param tree the tree to configure to component for. 352: * @param renderer the renderer used to set up the nodes 353: * @param editor the editor used 354: */ 355: private void configureEditingComponent(JTree tree, 356: DefaultTreeCellRenderer renderer, 357: TreeCellEditor editor) 358: { 359: if (tree != null && lastPath != null) 360: { 361: Object val = lastPath.getLastPathComponent(); 362: boolean isLeaf = tree.getModel().isLeaf(val); 363: boolean expanded = tree.isExpanded(lastPath); 364: determineOffset(tree, val, true, expanded, isLeaf, lastRow); 365: 366: // set up icon 367: if (isLeaf) 368: renderer.setIcon(renderer.getLeafIcon()); 369: else if (expanded) 370: renderer.setIcon(renderer.getOpenIcon()); 371: else 372: renderer.setIcon(renderer.getClosedIcon()); 373: editingIcon = renderer.getIcon(); 374: 375: editingComponent = getTreeCellEditorComponent(tree, val, true, 376: expanded, isLeaf, lastRow); 377: } 378: } 379: 380: /** 381: * writeObject 382: * 383: * @param value0 384: * TODO 385: * @exception IOException 386: * TODO 387: */ 388: private void writeObject(ObjectOutputStream value0) throws IOException 389: { 390: // TODO 391: } 392: 393: /** 394: * readObject 395: * @param value0 TODO 396: * @exception IOException TODO 397: * @exception ClassNotFoundException TODO 398: */ 399: private void readObject(ObjectInputStream value0) 400: throws IOException, ClassNotFoundException 401: { 402: // TODO 403: } 404: 405: /** 406: * Sets the color to use for the border. 407: * @param newColor - the new border color 408: */ 409: public void setBorderSelectionColor(Color newColor) 410: { 411: this.borderSelectionColor = newColor; 412: } 413: 414: /** 415: * Returns the color the border is drawn. 416: * @return Color 417: */ 418: public Color getBorderSelectionColor() 419: { 420: return borderSelectionColor; 421: } 422: 423: /** 424: * Sets the font to edit with. null indicates the renderers 425: * font should be used. This will NOT override any font you have 426: * set in the editor the receiver was instantied with. If null for 427: * an editor was passed in, a default editor will be created that 428: * will pick up this font. 429: * 430: * @param font - the editing Font 431: */ 432: public void setFont(Font font) 433: { 434: if (font != null) 435: this.font = font; 436: else 437: this.font = renderer.getFont(); 438: } 439: 440: /** 441: * Gets the font used for editing. 442: * 443: * @return the editing font 444: */ 445: public Font getFont() 446: { 447: return font; 448: } 449: 450: /** 451: * Configures the editor. Passed onto the realEditor. 452: * Sets an initial value for the editor. This will cause 453: * the editor to stopEditing and lose any partially edited value 454: * if the editor is editing when this method is called. 455: * Returns the component that should be added to the client's Component 456: * hierarchy. Once installed in the client's hierarchy this component will 457: * then be able to draw and receive user input. 458: * 459: * @param tree - the JTree that is asking the editor to edit; this parameter can be null 460: * @param value - the value of the cell to be edited 461: * @param isSelected - true is the cell is to be rendered with selection highlighting 462: * @param expanded - true if the node is expanded 463: * @param leaf - true if the node is a leaf node 464: * @param row - the row index of the node being edited 465: * 466: * @return the component for editing 467: */ 468: public Component getTreeCellEditorComponent(JTree tree, Object value, 469: boolean isSelected, boolean expanded, 470: boolean leaf, int row) 471: { 472: if (realEditor == null) 473: createTreeCellEditor(); 474: 475: return realEditor.getTreeCellEditorComponent(tree, value, isSelected, 476: expanded, leaf, row); 477: } 478: 479: /** 480: * Returns the value currently being edited. 481: * 482: * @return the value currently being edited 483: */ 484: public Object getCellEditorValue() 485: { 486: return editingComponent; 487: } 488: 489: /** 490: * If the realEditor returns true to this message, prepareForEditing 491: * is messaged and true is returned. 492: * 493: * @param event - the event the editor should use to consider whether to begin editing or not 494: * @return true if editing can be started 495: */ 496: public boolean isCellEditable(EventObject event) 497: { 498: if (editingComponent == null) 499: configureEditingComponent(tree, renderer, realEditor); 500: 501: if (editingComponent != null && realEditor.isCellEditable(event)) 502: { 503: prepareForEditing(); 504: return true; 505: } 506: 507: // Cell may not be currently editable, but may need to start timer. 508: if (shouldStartEditingTimer(event)) 509: startEditingTimer(); 510: else if (timer.isRunning()) 511: timer.stop(); 512: return false; 513: } 514: 515: /** 516: * Messages the realEditor for the return value. 517: * 518: * @param event - 519: * the event the editor should use to start editing 520: * @return true if the editor would like the editing cell to be selected; 521: * otherwise returns false 522: */ 523: public boolean shouldSelectCell(EventObject event) 524: { 525: return true; 526: } 527: 528: /** 529: * If the realEditor will allow editing to stop, the realEditor 530: * is removed and true is returned, otherwise false is returned. 531: * @return true if editing was stopped; false otherwise 532: */ 533: public boolean stopCellEditing() 534: { 535: if (editingComponent != null && realEditor.stopCellEditing()) 536: { 537: timer.stop(); 538: return true; 539: } 540: return false; 541: } 542: 543: /** 544: * Messages cancelCellEditing to the realEditor and removes it 545: * from this instance. 546: */ 547: public void cancelCellEditing() 548: { 549: if (editingComponent != null) 550: { 551: timer.stop(); 552: realEditor.cancelCellEditing(); 553: } 554: } 555: 556: /** 557: * Adds a <code>CellEditorListener</code> object to this editor. 558: * 559: * @param listener the listener to add 560: */ 561: public void addCellEditorListener(CellEditorListener listener) 562: { 563: realEditor.addCellEditorListener(listener); 564: } 565: 566: /** 567: * Removes a <code>CellEditorListener</code> object. 568: * 569: * @param listener the listener to remove 570: */ 571: public void removeCellEditorListener(CellEditorListener listener) 572: { 573: realEditor.removeCellEditorListener(listener); 574: } 575: 576: /** 577: * Returns all added <code>CellEditorListener</code> objects to this editor. 578: * 579: * @return an array of listeners 580: * 581: * @since 1.4 582: */ 583: public CellEditorListener[] getCellEditorListeners() 584: { 585: return (CellEditorListener[]) listenerList.getListeners(CellEditorListener.class); 586: } 587: 588: /** 589: * Resets lastPath. 590: * 591: * @param e - the event that characterizes the change. 592: */ 593: public void valueChanged(TreeSelectionEvent e) 594: { 595: tPath = lastPath; 596: lastPath = e.getNewLeadSelectionPath(); 597: lastRow = tree.getRowForPath(lastPath); 598: configureEditingComponent(tree, renderer, realEditor); 599: } 600: 601: /** 602: * Messaged when the timer fires, this will start the editing session. 603: * 604: * @param e the event that characterizes the action. 605: */ 606: public void actionPerformed(ActionEvent e) 607: { 608: if (lastPath != null && tPath != null && tPath.equals(lastPath)) 609: { 610: tree.startEditingAtPath(lastPath); 611: timer.stop(); 612: } 613: } 614: 615: /** 616: * Sets the tree currently editing for. This is needed to add a selection 617: * listener. 618: * 619: * @param newTree - 620: * the new tree to be edited 621: */ 622: protected void setTree(JTree newTree) 623: { 624: tree = newTree; 625: } 626: 627: /** 628: * Returns true if event is a MouseEvent and the click count is 1. 629: * 630: * @param event - the event being studied 631: * @return true if editing should start 632: */ 633: protected boolean shouldStartEditingTimer(EventObject event) 634: { 635: if ((event instanceof MouseEvent) && 636: ((MouseEvent) event).getClickCount() == 1) 637: return true; 638: return false; 639: } 640: 641: /** 642: * Starts the editing timer. 643: */ 644: protected void startEditingTimer() 645: { 646: if (timer == null) 647: timer = new javax.swing.Timer(1200, this); 648: if (!timer.isRunning()) 649: timer.start(); 650: } 651: 652: /** 653: * Returns true if event is null, or it is a MouseEvent with 654: * a click count > 2 and inHitRegion returns true. 655: * 656: * @param event - the event being studied 657: * @return true if event is null, or it is a MouseEvent with 658: * a click count > 2 and inHitRegion returns true 659: */ 660: protected boolean canEditImmediately(EventObject event) 661: { 662: if (event == null || !(event instanceof MouseEvent) || (((MouseEvent) event). 663: getClickCount() > 2 && inHitRegion(((MouseEvent) event).getX(), 664: ((MouseEvent) event).getY()))) 665: return true; 666: return false; 667: } 668: 669: /** 670: * Returns true if the passed in location is a valid mouse location 671: * to start editing from. This is implemented to return false if x is 672: * less than or equal to the width of the icon and icon 673: * gap displayed by the renderer. In other words this returns true if 674: * the user clicks over the text part displayed by the renderer, and 675: * false otherwise. 676: * 677: * @param x - the x-coordinate of the point 678: * @param y - the y-coordinate of the point 679: * 680: * @return true if the passed in location is a valid mouse location 681: */ 682: protected boolean inHitRegion(int x, int y) 683: { 684: Rectangle bounds = tree.getPathBounds(lastPath); 685: 686: return bounds.contains(x, y); 687: } 688: 689: /** 690: * determineOffset 691: * @param tree - 692: * @param value - 693: * @param isSelected - 694: * @param expanded - 695: * @param leaf - 696: * @param row - 697: */ 698: protected void determineOffset(JTree tree, Object value, boolean isSelected, 699: boolean expanded, boolean leaf, int row) 700: { 701: renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded, 702: leaf, row, true); 703: Icon c = renderer.getIcon(); 704: if (c != null) 705: offset = renderer.getIconTextGap() + c.getIconWidth(); 706: else 707: offset = 0; 708: } 709: 710: /** 711: * Invoked just before editing is to start. Will add the 712: * editingComponent to the editingContainer. 713: */ 714: protected void prepareForEditing() 715: { 716: editingContainer.add(editingComponent); 717: } 718: 719: /** 720: * Creates the container to manage placement of editingComponent. 721: * 722: * @return the container to manage the placement of the editingComponent. 723: */ 724: protected Container createContainer() 725: { 726: return new DefaultTreeCellEditor.EditorContainer(); 727: } 728: 729: /** 730: * This is invoked if a TreeCellEditor is not supplied in the constructor. 731: * It returns a TextField editor. 732: * 733: * @return a new TextField editor 734: */ 735: protected TreeCellEditor createTreeCellEditor() 736: { 737: realEditor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField( 738: UIManager.getBorder("Tree.selectionBorder"))); 739: return realEditor; 740: } 741: }
GNU Classpath (0.20) |