1 /*
   2  * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package javax.swing.tree;
  27 
  28 import javax.swing.*;
  29 import javax.swing.border.*;
  30 import javax.swing.event.*;
  31 import javax.swing.plaf.FontUIResource;
  32 import java.awt.*;
  33 import java.awt.event.*;
  34 import java.util.EventObject;
  35 
  36 /**
  37  * A <code>TreeCellEditor</code>. You need to supply an
  38  * instance of <code>DefaultTreeCellRenderer</code>
  39  * so that the icons can be obtained. You can optionally supply
  40  * a <code>TreeCellEditor</code> that will be layed out according
  41  * to the icon in the <code>DefaultTreeCellRenderer</code>.
  42  * If you do not supply a <code>TreeCellEditor</code>,
  43  * a <code>TextField</code> will be used. Editing is started
  44  * on a triple mouse click, or after a click, pause, click and
  45  * a delay of 1200 milliseconds.
  46  *<p>
  47  * <strong>Warning:</strong>
  48  * Serialized objects of this class will not be compatible with
  49  * future Swing releases. The current serialization support is
  50  * appropriate for short term storage or RMI between applications running
  51  * the same version of Swing.  As of 1.4, support for long term storage
  52  * of all JavaBeans&trade;
  53  * has been added to the <code>java.beans</code> package.
  54  * Please see {@link java.beans.XMLEncoder}.
  55  *
  56  * @see javax.swing.JTree
  57  *
  58  * @author Scott Violet
  59  */
  60 public class DefaultTreeCellEditor implements ActionListener, TreeCellEditor,
  61             TreeSelectionListener {
  62     /** Editor handling the editing. */
  63     protected TreeCellEditor               realEditor;
  64 
  65     /** Renderer, used to get border and offsets from. */
  66     protected DefaultTreeCellRenderer      renderer;
  67 
  68     /** Editing container, will contain the <code>editorComponent</code>. */
  69     protected Container                    editingContainer;
  70 
  71     /**
  72      * Component used in editing, obtained from the
  73      * <code>editingContainer</code>.
  74      */
  75     transient protected Component          editingComponent;
  76 
  77     /**
  78      * As of Java 2 platform v1.4 this field should no longer be used. If
  79      * you wish to provide similar behavior you should directly override
  80      * <code>isCellEditable</code>.
  81      */
  82     protected boolean                      canEdit;
  83 
  84     /**
  85      * Used in editing. Indicates x position to place
  86      * <code>editingComponent</code>.
  87      */
  88     protected transient int                offset;
  89 
  90     /** <code>JTree</code> instance listening too. */
  91     protected transient JTree              tree;
  92 
  93     /** Last path that was selected. */
  94     protected transient TreePath           lastPath;
  95 
  96     /** Used before starting the editing session. */
  97     protected transient Timer              timer;
  98 
  99     /**
 100      * Row that was last passed into
 101      * <code>getTreeCellEditorComponent</code>.
 102      */
 103     protected transient int                lastRow;
 104 
 105     /** True if the border selection color should be drawn. */
 106     protected Color                        borderSelectionColor;
 107 
 108     /** Icon to use when editing. */
 109     protected transient Icon               editingIcon;
 110 
 111     /**
 112      * Font to paint with, <code>null</code> indicates
 113      * font of renderer is to be used.
 114      */
 115     protected Font                         font;
 116 
 117 
 118     /**
 119      * Constructs a <code>DefaultTreeCellEditor</code>
 120      * object for a JTree using the specified renderer and
 121      * a default editor. (Use this constructor for normal editing.)
 122      *
 123      * @param tree      a <code>JTree</code> object
 124      * @param renderer  a <code>DefaultTreeCellRenderer</code> object
 125      */
 126     public DefaultTreeCellEditor(JTree tree,
 127                                  DefaultTreeCellRenderer renderer) {
 128         this(tree, renderer, null);
 129     }
 130 
 131     /**
 132      * Constructs a <code>DefaultTreeCellEditor</code>
 133      * object for a <code>JTree</code> using the
 134      * specified renderer and the specified editor. (Use this constructor
 135      * for specialized editing.)
 136      *
 137      * @param tree      a <code>JTree</code> object
 138      * @param renderer  a <code>DefaultTreeCellRenderer</code> object
 139      * @param editor    a <code>TreeCellEditor</code> object
 140      */
 141     public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer,
 142                                  TreeCellEditor editor) {
 143         this.renderer = renderer;
 144         realEditor = editor;
 145         if(realEditor == null)
 146             realEditor = createTreeCellEditor();
 147         editingContainer = createContainer();
 148         setTree(tree);
 149         setBorderSelectionColor(UIManager.getColor
 150                                 ("Tree.editorBorderSelectionColor"));
 151     }
 152 
 153     /**
 154       * Sets the color to use for the border.
 155       * @param newColor the new border color
 156       */
 157     public void setBorderSelectionColor(Color newColor) {
 158         borderSelectionColor = newColor;
 159     }
 160 
 161     /**
 162       * Returns the color the border is drawn.
 163       * @return the border selection color
 164       */
 165     public Color getBorderSelectionColor() {
 166         return borderSelectionColor;
 167     }
 168 
 169     /**
 170      * Sets the font to edit with. <code>null</code> indicates
 171      * the renderers font should be used. This will NOT
 172      * override any font you have set in the editor
 173      * the receiver was instantiated with. If <code>null</code>
 174      * for an editor was passed in a default editor will be
 175      * created that will pick up this font.
 176      *
 177      * @param font  the editing <code>Font</code>
 178      * @see #getFont
 179      */
 180     public void setFont(Font font) {
 181         this.font = font;
 182     }
 183 
 184     /**
 185      * Gets the font used for editing.
 186      *
 187      * @return the editing <code>Font</code>
 188      * @see #setFont
 189      */
 190     public Font getFont() {
 191         return font;
 192     }
 193 
 194     //
 195     // TreeCellEditor
 196     //
 197 
 198     /**
 199      * Configures the editor.  Passed onto the <code>realEditor</code>.
 200      */
 201     public Component getTreeCellEditorComponent(JTree tree, Object value,
 202                                                 boolean isSelected,
 203                                                 boolean expanded,
 204                                                 boolean leaf, int row) {
 205         setTree(tree);
 206         lastRow = row;
 207         determineOffset(tree, value, isSelected, expanded, leaf, row);
 208 
 209         if (editingComponent != null) {
 210             editingContainer.remove(editingComponent);
 211         }
 212         editingComponent = realEditor.getTreeCellEditorComponent(tree, value,
 213                                         isSelected, expanded,leaf, row);
 214 
 215 
 216         // this is kept for backwards compatibility but isn't really needed
 217         // with the current BasicTreeUI implementation.
 218         TreePath        newPath = tree.getPathForRow(row);
 219 
 220         canEdit = (lastPath != null && newPath != null &&
 221                    lastPath.equals(newPath));
 222 
 223         Font            font = getFont();
 224 
 225         if(font == null) {
 226             if(renderer != null)
 227                 font = renderer.getFont();
 228             if(font == null)
 229                 font = tree.getFont();
 230         }
 231         editingContainer.setFont(font);
 232         prepareForEditing();
 233         return editingContainer;
 234     }
 235 
 236     /**
 237      * Returns the value currently being edited.
 238      * @return the value currently being edited
 239      */
 240     public Object getCellEditorValue() {
 241         return realEditor.getCellEditorValue();
 242     }
 243 
 244     /**
 245      * If the <code>realEditor</code> returns true to this
 246      * message, <code>prepareForEditing</code>
 247      * is messaged and true is returned.
 248      */
 249     public boolean isCellEditable(EventObject event) {
 250         boolean            retValue = false;
 251         boolean            editable = false;
 252 
 253         if (event != null) {
 254             if (event.getSource() instanceof JTree) {
 255                 setTree((JTree)event.getSource());
 256                 if (event instanceof MouseEvent) {
 257                     TreePath path = tree.getPathForLocation(
 258                                          ((MouseEvent)event).getX(),
 259                                          ((MouseEvent)event).getY());
 260                     editable = (lastPath != null && path != null &&
 261                                lastPath.equals(path));
 262                     if (path!=null) {
 263                         lastRow = tree.getRowForPath(path);
 264                         Object value = path.getLastPathComponent();
 265                         boolean isSelected = tree.isRowSelected(lastRow);
 266                         boolean expanded = tree.isExpanded(path);
 267                         TreeModel treeModel = tree.getModel();
 268                         boolean leaf = treeModel.isLeaf(value);
 269                         determineOffset(tree, value, isSelected,
 270                                         expanded, leaf, lastRow);
 271                     }
 272                 }
 273             }
 274         }
 275         if(!realEditor.isCellEditable(event))
 276             return false;
 277         if(canEditImmediately(event))
 278             retValue = true;
 279         else if(editable && shouldStartEditingTimer(event)) {
 280             startEditingTimer();
 281         }
 282         else if(timer != null && timer.isRunning())
 283             timer.stop();
 284         if(retValue)
 285             prepareForEditing();
 286         return retValue;
 287     }
 288 
 289     /**
 290      * Messages the <code>realEditor</code> for the return value.
 291      */
 292     public boolean shouldSelectCell(EventObject event) {
 293         return realEditor.shouldSelectCell(event);
 294     }
 295 
 296     /**
 297      * If the <code>realEditor</code> will allow editing to stop,
 298      * the <code>realEditor</code> is removed and true is returned,
 299      * otherwise false is returned.
 300      */
 301     public boolean stopCellEditing() {
 302         if(realEditor.stopCellEditing()) {
 303             cleanupAfterEditing();
 304             return true;
 305         }
 306         return false;
 307     }
 308 
 309     /**
 310      * Messages <code>cancelCellEditing</code> to the
 311      * <code>realEditor</code> and removes it from this instance.
 312      */
 313     public void cancelCellEditing() {
 314         realEditor.cancelCellEditing();
 315         cleanupAfterEditing();
 316     }
 317 
 318     /**
 319      * Adds the <code>CellEditorListener</code>.
 320      * @param l the listener to be added
 321      */
 322     public void addCellEditorListener(CellEditorListener l) {
 323         realEditor.addCellEditorListener(l);
 324     }
 325 
 326     /**
 327       * Removes the previously added <code>CellEditorListener</code>.
 328       * @param l the listener to be removed
 329       */
 330     public void removeCellEditorListener(CellEditorListener l) {
 331         realEditor.removeCellEditorListener(l);
 332     }
 333 
 334     /**
 335      * Returns an array of all the <code>CellEditorListener</code>s added
 336      * to this DefaultTreeCellEditor with addCellEditorListener().
 337      *
 338      * @return all of the <code>CellEditorListener</code>s added or an empty
 339      *         array if no listeners have been added
 340      * @since 1.4
 341      */
 342     public CellEditorListener[] getCellEditorListeners() {
 343         return ((DefaultCellEditor)realEditor).getCellEditorListeners();
 344     }
 345 
 346     //
 347     // TreeSelectionListener
 348     //
 349 
 350     /**
 351      * Resets <code>lastPath</code>.
 352      */
 353     public void valueChanged(TreeSelectionEvent e) {
 354         if(tree != null) {
 355             if(tree.getSelectionCount() == 1)
 356                 lastPath = tree.getSelectionPath();
 357             else
 358                 lastPath = null;
 359         }
 360         if(timer != null) {
 361             timer.stop();
 362         }
 363     }
 364 
 365     //
 366     // ActionListener (for Timer).
 367     //
 368 
 369     /**
 370      * Messaged when the timer fires, this will start the editing
 371      * session.
 372      */
 373     public void actionPerformed(ActionEvent e) {
 374         if(tree != null && lastPath != null) {
 375             tree.startEditingAtPath(lastPath);
 376         }
 377     }
 378 
 379     //
 380     // Local methods
 381     //
 382 
 383     /**
 384      * Sets the tree currently editing for. This is needed to add
 385      * a selection listener.
 386      * @param newTree the new tree to be edited
 387      */
 388     protected void setTree(JTree newTree) {
 389         if(tree != newTree) {
 390             if(tree != null)
 391                 tree.removeTreeSelectionListener(this);
 392             tree = newTree;
 393             if(tree != null)
 394                 tree.addTreeSelectionListener(this);
 395             if(timer != null) {
 396                 timer.stop();
 397             }
 398         }
 399     }
 400 
 401     /**
 402      * Returns true if <code>event</code> is a <code>MouseEvent</code>
 403      * and the click count is 1.
 404      *
 405      * @param event the event being studied
 406      * @return whether {@code event} should starts the editing timer
 407      */
 408     protected boolean shouldStartEditingTimer(EventObject event) {
 409         if((event instanceof MouseEvent) &&
 410             SwingUtilities.isLeftMouseButton((MouseEvent)event)) {
 411             MouseEvent        me = (MouseEvent)event;
 412 
 413             return (me.getClickCount() == 1 &&
 414                     inHitRegion(me.getX(), me.getY()));
 415         }
 416         return false;
 417     }
 418 
 419     /**
 420      * Starts the editing timer.
 421      */
 422     protected void startEditingTimer() {
 423         if(timer == null) {
 424             timer = new Timer(1200, this);
 425             timer.setRepeats(false);
 426         }
 427         timer.start();
 428     }
 429 
 430     /**
 431      * Returns true if <code>event</code> is <code>null</code>,
 432      * or it is a <code>MouseEvent</code> with a click count &gt; 2
 433      * and <code>inHitRegion</code> returns true.
 434      *
 435      * @param event the event being studied
 436      * @return whether editing can be started for the given {@code event}
 437      */
 438     protected boolean canEditImmediately(EventObject event) {
 439         if((event instanceof MouseEvent) &&
 440            SwingUtilities.isLeftMouseButton((MouseEvent)event)) {
 441             MouseEvent       me = (MouseEvent)event;
 442 
 443             return ((me.getClickCount() > 2) &&
 444                     inHitRegion(me.getX(), me.getY()));
 445         }
 446         return (event == null);
 447     }
 448 
 449     /**
 450      * Returns true if the passed in location is a valid mouse location
 451      * to start editing from. This is implemented to return false if
 452      * <code>x</code> is &lt;= the width of the icon and icon gap displayed
 453      * by the renderer. In other words this returns true if the user
 454      * clicks over the text part displayed by the renderer, and false
 455      * otherwise.
 456      * @param x the x-coordinate of the point
 457      * @param y the y-coordinate of the point
 458      * @return true if the passed in location is a valid mouse location
 459      */
 460     protected boolean inHitRegion(int x, int y) {
 461         if(lastRow != -1 && tree != null) {
 462             Rectangle bounds = tree.getRowBounds(lastRow);
 463             ComponentOrientation treeOrientation = tree.getComponentOrientation();
 464 
 465             if ( treeOrientation.isLeftToRight() ) {
 466                 if (bounds != null && x <= (bounds.x + offset) &&
 467                     offset < (bounds.width - 5)) {
 468                     return false;
 469                 }
 470             } else if ( bounds != null &&
 471                         ( x >= (bounds.x+bounds.width-offset+5) ||
 472                           x <= (bounds.x + 5) ) &&
 473                         offset < (bounds.width - 5) ) {
 474                 return false;
 475             }
 476         }
 477         return true;
 478     }
 479 
 480     protected void determineOffset(JTree tree, Object value,
 481                                    boolean isSelected, boolean expanded,
 482                                    boolean leaf, int row) {
 483         if(renderer != null) {
 484             if(leaf)
 485                 editingIcon = renderer.getLeafIcon();
 486             else if(expanded)
 487                 editingIcon = renderer.getOpenIcon();
 488             else
 489                 editingIcon = renderer.getClosedIcon();
 490             if(editingIcon != null)
 491                 offset = renderer.getIconTextGap() +
 492                          editingIcon.getIconWidth();
 493             else
 494                 offset = renderer.getIconTextGap();
 495         }
 496         else {
 497             editingIcon = null;
 498             offset = 0;
 499         }
 500     }
 501 
 502     /**
 503      * Invoked just before editing is to start. Will add the
 504      * <code>editingComponent</code> to the
 505      * <code>editingContainer</code>.
 506      */
 507     protected void prepareForEditing() {
 508         if (editingComponent != null) {
 509             editingContainer.add(editingComponent);
 510         }
 511     }
 512 
 513     /**
 514      * Creates the container to manage placement of
 515      * <code>editingComponent</code>.
 516      *
 517      * @return new Container object
 518      */
 519     protected Container createContainer() {
 520         return new EditorContainer();
 521     }
 522 
 523     /**
 524      * This is invoked if a <code>TreeCellEditor</code>
 525      * is not supplied in the constructor.
 526      * It returns a <code>TextField</code> editor.
 527      * @return a new <code>TextField</code> editor
 528      */
 529     protected TreeCellEditor createTreeCellEditor() {
 530         Border              aBorder = UIManager.getBorder("Tree.editorBorder");
 531         @SuppressWarnings("serial") // Safe: outer class is non-serializable
 532         DefaultCellEditor   editor = new DefaultCellEditor
 533             (new DefaultTextField(aBorder)) {
 534             public boolean shouldSelectCell(EventObject event) {
 535                 boolean retValue = super.shouldSelectCell(event);
 536                 return retValue;
 537             }
 538         };
 539 
 540         // One click to edit.
 541         editor.setClickCountToStart(1);
 542         return editor;
 543     }
 544 
 545     /**
 546      * Cleans up any state after editing has completed. Removes the
 547      * <code>editingComponent</code> the <code>editingContainer</code>.
 548      */
 549     private void cleanupAfterEditing() {
 550         if (editingComponent != null) {
 551             editingContainer.remove(editingComponent);
 552         }
 553         editingComponent = null;
 554     }
 555 
 556     /**
 557      * <code>TextField</code> used when no editor is supplied.
 558      * This textfield locks into the border it is constructed with.
 559      * It also prefers its parents font over its font. And if the
 560      * renderer is not <code>null</code> and no font
 561      * has been specified the preferred height is that of the renderer.
 562      */
 563     @SuppressWarnings("serial") // Safe: outer class is non-serializable
 564     public class DefaultTextField extends JTextField {
 565         /** Border to use. */
 566         protected Border         border;
 567 
 568         /**
 569          * Constructs a
 570          * <code>DefaultTreeCellEditor.DefaultTextField</code> object.
 571          *
 572          * @param border  a <code>Border</code> object
 573          * @since 1.4
 574          */
 575         public DefaultTextField(Border border) {
 576             setBorder(border);
 577         }
 578 
 579         /**
 580          * Sets the border of this component.<p>
 581          * This is a bound property.
 582          *
 583          * @param border the border to be rendered for this component
 584          * @see Border
 585          * @see CompoundBorder
 586          * @beaninfo
 587          *        bound: true
 588          *    preferred: true
 589          *    attribute: visualUpdate true
 590          *  description: The component's border.
 591          */
 592         public void setBorder(Border border) {
 593             super.setBorder(border);
 594             this.border = border;
 595         }
 596 
 597         /**
 598          * Overrides <code>JComponent.getBorder</code> to
 599          * returns the current border.
 600          */
 601         public Border getBorder() {
 602             return border;
 603         }
 604 
 605         // implements java.awt.MenuContainer
 606         public Font getFont() {
 607             Font     font = super.getFont();
 608 
 609             // Prefer the parent containers font if our font is a
 610             // FontUIResource
 611             if(font instanceof FontUIResource) {
 612                 Container     parent = getParent();
 613 
 614                 if(parent != null && parent.getFont() != null)
 615                     font = parent.getFont();
 616             }
 617             return font;
 618         }
 619 
 620         /**
 621          * Overrides <code>JTextField.getPreferredSize</code> to
 622          * return the preferred size based on current font, if set,
 623          * or else use renderer's font.
 624          * @return a <code>Dimension</code> object containing
 625          *   the preferred size
 626          */
 627         public Dimension getPreferredSize() {
 628             Dimension      size = super.getPreferredSize();
 629 
 630             // If not font has been set, prefer the renderers height.
 631             if(renderer != null &&
 632                DefaultTreeCellEditor.this.getFont() == null) {
 633                 Dimension     rSize = renderer.getPreferredSize();
 634 
 635                 size.height = rSize.height;
 636             }
 637             return size;
 638         }
 639     }
 640 
 641 
 642     /**
 643      * Container responsible for placing the <code>editingComponent</code>.
 644      */
 645     @SuppressWarnings("serial") // Safe: outer class is non-serializable
 646     public class EditorContainer extends Container {
 647         /**
 648          * Constructs an <code>EditorContainer</code> object.
 649          */
 650         public EditorContainer() {
 651             setLayout(null);
 652         }
 653 
 654         // This should not be used. It will be removed when new API is
 655         // allowed.
 656         public void EditorContainer() {
 657             setLayout(null);
 658         }
 659 
 660         /**
 661          * Overrides <code>Container.paint</code> to paint the node's
 662          * icon and use the selection color for the background.
 663          */
 664         public void paint(Graphics g) {
 665             int width = getWidth();
 666             int height = getHeight();
 667 
 668             // Then the icon.
 669             if(editingIcon != null) {
 670                 int yLoc = calculateIconY(editingIcon);
 671 
 672                 if (getComponentOrientation().isLeftToRight()) {
 673                     editingIcon.paintIcon(this, g, 0, yLoc);
 674                 } else {
 675                     editingIcon.paintIcon(
 676                             this, g, width - editingIcon.getIconWidth(),
 677                             yLoc);
 678                 }
 679             }
 680 
 681             // Border selection color
 682             Color       background = getBorderSelectionColor();
 683             if(background != null) {
 684                 g.setColor(background);
 685                 g.drawRect(0, 0, width - 1, height - 1);
 686             }
 687             super.paint(g);
 688         }
 689 
 690         /**
 691          * Lays out this <code>Container</code>.  If editing,
 692          * the editor will be placed at
 693          * <code>offset</code> in the x direction and 0 for y.
 694          */
 695         public void doLayout() {
 696             if(editingComponent != null) {
 697                 int width = getWidth();
 698                 int height = getHeight();
 699                 if (getComponentOrientation().isLeftToRight()) {
 700                     editingComponent.setBounds(
 701                             offset, 0, width - offset, height);
 702                 } else {
 703                     editingComponent.setBounds(
 704                         0, 0, width - offset, height);
 705                 }
 706             }
 707         }
 708 
 709         /**
 710          * Calculate the y location for the icon.
 711          */
 712         private int calculateIconY(Icon icon) {
 713             // To make sure the icon position matches that of the
 714             // renderer, use the same algorithm as JLabel
 715             // (SwingUtilities.layoutCompoundLabel).
 716             int iconHeight = icon.getIconHeight();
 717             int textHeight = editingComponent.getFontMetrics(
 718                 editingComponent.getFont()).getHeight();
 719             int textY = iconHeight / 2 - textHeight / 2;
 720             int totalY = Math.min(0, textY);
 721             int totalHeight = Math.max(iconHeight, textY + textHeight) -
 722                 totalY;
 723             return getHeight() / 2 - (totalY + (totalHeight / 2));
 724         }
 725 
 726         /**
 727          * Returns the preferred size for the <code>Container</code>.
 728          * This will be at least preferred size of the editor plus
 729          * <code>offset</code>.
 730          * @return a <code>Dimension</code> containing the preferred
 731          *   size for the <code>Container</code>; if
 732          *   <code>editingComponent</code> is <code>null</code> the
 733          *   <code>Dimension</code> returned is 0, 0
 734          */
 735         public Dimension getPreferredSize() {
 736             if(editingComponent != null) {
 737                 Dimension         pSize = editingComponent.getPreferredSize();
 738 
 739                 pSize.width += offset + 5;
 740 
 741                 Dimension         rSize = (renderer != null) ?
 742                                           renderer.getPreferredSize() : null;
 743 
 744                 if(rSize != null)
 745                     pSize.height = Math.max(pSize.height, rSize.height);
 746                 if(editingIcon != null)
 747                     pSize.height = Math.max(pSize.height,
 748                                             editingIcon.getIconHeight());
 749 
 750                 // Make sure width is at least 100.
 751                 pSize.width = Math.max(pSize.width, 100);
 752                 return pSize;
 753             }
 754             return new Dimension(0, 0);
 755         }
 756     }
 757 }