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