1 /* DefaultTreeCellEditor.java --
2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
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)
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.
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
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
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. */
39 package javax
.swing
.tree
;
41 import java
.awt
.Color
;
42 import java
.awt
.Component
;
43 import java
.awt
.Container
;
44 import java
.awt
.Dimension
;
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
;
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
;
72 * DefaultTreeCellEditor
73 * @author Andrew Selkirk
75 public class DefaultTreeCellEditor
76 implements ActionListener
, TreeCellEditor
, TreeSelectionListener
81 public class EditorContainer
extends Container
84 * Creates an <code>EditorContainer</code> object.
86 public EditorContainer()
92 * This method only exists for API compatibility and is useless as it does
93 * nothing. It got probably introduced by accident.
95 public void EditorContainer()
101 * Returns the preferred size for the Container.
103 * @return Dimension of EditorContainer
105 public Dimension
getPreferredSize()
107 Dimension containerSize
= super.getPreferredSize();
108 containerSize
.width
+= DefaultTreeCellEditor
.this.offset
;
109 return containerSize
;
113 * Overrides Container.paint to paint the node's icon and use the selection
114 * color for the background.
117 * the specified Graphics window
119 public void paint(Graphics g
)
121 Rectangle tr
= tree
.getPathBounds(lastPath
);
124 Insets i
= ((DefaultTextField
) editingComponent
).getBorder()
125 .getBorderInsets(this);
130 if (editingIcon
!= null)
132 editingIcon
.paintIcon(this, g
, tr
.x
- editingIcon
.
133 getIconWidth()/2, tr
.y
+ i
.top
+ i
.bottom
);
134 tr
.x
+= editingIcon
.getIconWidth()/2 + textIconGap
;
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
);
149 * Lays out this Container. If editing, the editor will be placed at offset
150 * in the x direction and 0 for y.
152 public void doLayout()
154 if (DefaultTreeCellEditor
.this.tree
.isEditing())
155 setLocation(offset
, 0);
163 public class DefaultTextField
extends JTextField
168 protected Border border
;
171 * Creates a <code>DefaultTextField</code> object.
173 * @param border the border to use
175 public DefaultTextField(Border border
)
177 this.border
= border
;
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).
186 public Font
getFont()
188 Font font
= super.getFont();
191 Component parent
= getParent();
193 return parent
.getFont();
200 * Returns the border of the text field.
204 public Border
getBorder()
210 * Overrides JTextField.getPreferredSize to return the preferred size
211 * based on current font, if set, or else use renderer's font.
213 * @return the Dimension of this textfield.
215 public Dimension
getPreferredSize()
217 String s
= getText();
223 FontMetrics fm
= getToolkit().getFontMetrics(f
);
225 return new Dimension(SwingUtilities
.computeStringWidth(fm
, s
),
228 return renderer
.getPreferredSize();
232 private EventListenerList listenerList
= new EventListenerList();
235 * Editor handling the editing.
237 protected TreeCellEditor realEditor
;
240 * Renderer, used to get border and offsets from.
242 protected DefaultTreeCellRenderer renderer
;
245 * Editing container, will contain the editorComponent.
247 protected Container editingContainer
;
250 * Component used in editing, obtained from the editingContainer.
252 protected transient Component editingComponent
;
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.
259 protected boolean canEdit
;
262 * Used in editing. Indicates x position to place editingComponent.
264 protected transient int offset
;
267 * JTree instance listening too.
269 protected transient JTree tree
;
272 * Last path that was selected.
274 protected transient TreePath lastPath
;
277 * Used before starting the editing session.
279 protected transient javax
.swing
.Timer timer
;
282 * Row that was last passed into getTreeCellEditorComponent.
284 protected transient int lastRow
;
287 * True if the border selection color should be drawn.
289 protected Color borderSelectionColor
;
292 * Icon to use when editing.
294 protected transient Icon editingIcon
;
297 * Font to paint with, null indicates font of renderer is to be used.
302 * Helper field used to save the last path seen while the timer was
305 private TreePath tPath
;
308 * Constructs a DefaultTreeCellEditor object for a JTree using the
309 * specified renderer and a default editor. (Use this constructor
310 * for normal editing.)
312 * @param tree - a JTree object
313 * @param renderer - a DefaultTreeCellRenderer object
315 public DefaultTreeCellEditor(JTree tree
, DefaultTreeCellRenderer renderer
)
317 this(tree
, renderer
, null);
321 * Constructs a DefaultTreeCellEditor object for a JTree using the specified
322 * renderer and the specified editor. (Use this constructor
323 * for specialized editing.)
325 * @param tree - a JTree object
326 * @param renderer - a DefaultTreeCellRenderer object
327 * @param editor - a TreeCellEditor object
329 public DefaultTreeCellEditor(JTree tree
, DefaultTreeCellRenderer renderer
,
330 TreeCellEditor editor
)
333 this.renderer
= renderer
;
336 editor
= createTreeCellEditor();
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);
349 * Configures the editing component whenever it is null.
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
355 private void configureEditingComponent(JTree tree
,
356 DefaultTreeCellRenderer renderer
,
357 TreeCellEditor editor
)
359 if (tree
!= null && lastPath
!= null)
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
);
368 renderer
.setIcon(renderer
.getLeafIcon());
370 renderer
.setIcon(renderer
.getOpenIcon());
372 renderer
.setIcon(renderer
.getClosedIcon());
373 editingIcon
= renderer
.getIcon();
375 editingComponent
= getTreeCellEditorComponent(tree
, val
, true,
376 expanded
, isLeaf
, lastRow
);
385 * @exception IOException
388 private void writeObject(ObjectOutputStream value0
) throws IOException
396 * @exception IOException TODO
397 * @exception ClassNotFoundException TODO
399 private void readObject(ObjectInputStream value0
)
400 throws IOException
, ClassNotFoundException
406 * Sets the color to use for the border.
407 * @param newColor - the new border color
409 public void setBorderSelectionColor(Color newColor
)
411 this.borderSelectionColor
= newColor
;
415 * Returns the color the border is drawn.
418 public Color
getBorderSelectionColor()
420 return borderSelectionColor
;
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.
430 * @param font - the editing Font
432 public void setFont(Font font
)
437 this.font
= renderer
.getFont();
441 * Gets the font used for editing.
443 * @return the editing font
445 public Font
getFont()
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.
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
466 * @return the component for editing
468 public Component
getTreeCellEditorComponent(JTree tree
, Object value
,
469 boolean isSelected
, boolean expanded
,
470 boolean leaf
, int row
)
472 if (realEditor
== null)
473 createTreeCellEditor();
475 return realEditor
.getTreeCellEditorComponent(tree
, value
, isSelected
,
476 expanded
, leaf
, row
);
480 * Returns the value currently being edited.
482 * @return the value currently being edited
484 public Object
getCellEditorValue()
486 return editingComponent
;
490 * If the realEditor returns true to this message, prepareForEditing
491 * is messaged and true is returned.
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
496 public boolean isCellEditable(EventObject event
)
498 if (editingComponent
== null)
499 configureEditingComponent(tree
, renderer
, realEditor
);
501 if (editingComponent
!= null && realEditor
.isCellEditable(event
))
507 // Cell may not be currently editable, but may need to start timer.
508 if (shouldStartEditingTimer(event
))
510 else if (timer
.isRunning())
516 * Messages the realEditor for the return value.
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
523 public boolean shouldSelectCell(EventObject event
)
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
533 public boolean stopCellEditing()
535 if (editingComponent
!= null && realEditor
.stopCellEditing())
544 * Messages cancelCellEditing to the realEditor and removes it
545 * from this instance.
547 public void cancelCellEditing()
549 if (editingComponent
!= null)
552 realEditor
.cancelCellEditing();
557 * Adds a <code>CellEditorListener</code> object to this editor.
559 * @param listener the listener to add
561 public void addCellEditorListener(CellEditorListener listener
)
563 realEditor
.addCellEditorListener(listener
);
567 * Removes a <code>CellEditorListener</code> object.
569 * @param listener the listener to remove
571 public void removeCellEditorListener(CellEditorListener listener
)
573 realEditor
.removeCellEditorListener(listener
);
577 * Returns all added <code>CellEditorListener</code> objects to this editor.
579 * @return an array of listeners
583 public CellEditorListener
[] getCellEditorListeners()
585 return (CellEditorListener
[]) listenerList
.getListeners(CellEditorListener
.class);
591 * @param e - the event that characterizes the change.
593 public void valueChanged(TreeSelectionEvent e
)
596 lastPath
= e
.getNewLeadSelectionPath();
597 lastRow
= tree
.getRowForPath(lastPath
);
598 configureEditingComponent(tree
, renderer
, realEditor
);
602 * Messaged when the timer fires, this will start the editing session.
604 * @param e the event that characterizes the action.
606 public void actionPerformed(ActionEvent e
)
608 if (lastPath
!= null && tPath
!= null && tPath
.equals(lastPath
))
610 tree
.startEditingAtPath(lastPath
);
616 * Sets the tree currently editing for. This is needed to add a selection
620 * the new tree to be edited
622 protected void setTree(JTree newTree
)
628 * Returns true if event is a MouseEvent and the click count is 1.
630 * @param event - the event being studied
631 * @return true if editing should start
633 protected boolean shouldStartEditingTimer(EventObject event
)
635 if ((event
instanceof MouseEvent
) &&
636 ((MouseEvent
) event
).getClickCount() == 1)
642 * Starts the editing timer.
644 protected void startEditingTimer()
647 timer
= new javax
.swing
.Timer(1200, this);
648 if (!timer
.isRunning())
653 * Returns true if event is null, or it is a MouseEvent with
654 * a click count > 2 and inHitRegion returns true.
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
660 protected boolean canEditImmediately(EventObject event
)
662 if (event
== null || !(event
instanceof MouseEvent
) || (((MouseEvent
) event
).
663 getClickCount() > 2 && inHitRegion(((MouseEvent
) event
).getX(),
664 ((MouseEvent
) event
).getY())))
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
677 * @param x - the x-coordinate of the point
678 * @param y - the y-coordinate of the point
680 * @return true if the passed in location is a valid mouse location
682 protected boolean inHitRegion(int x
, int y
)
684 Rectangle bounds
= tree
.getPathBounds(lastPath
);
686 return bounds
.contains(x
, y
);
693 * @param isSelected -
698 protected void determineOffset(JTree tree
, Object value
, boolean isSelected
,
699 boolean expanded
, boolean leaf
, int row
)
701 renderer
.getTreeCellRendererComponent(tree
, value
, isSelected
, expanded
,
703 Icon c
= renderer
.getIcon();
705 offset
= renderer
.getIconTextGap() + c
.getIconWidth();
711 * Invoked just before editing is to start. Will add the
712 * editingComponent to the editingContainer.
714 protected void prepareForEditing()
716 editingContainer
.add(editingComponent
);
720 * Creates the container to manage placement of editingComponent.
722 * @return the container to manage the placement of the editingComponent.
724 protected Container
createContainer()
726 return new DefaultTreeCellEditor
.EditorContainer();
730 * This is invoked if a TreeCellEditor is not supplied in the constructor.
731 * It returns a TextField editor.
733 * @return a new TextField editor
735 protected TreeCellEditor
createTreeCellEditor()
737 realEditor
= new DefaultCellEditor(new DefaultTreeCellEditor
.DefaultTextField(
738 UIManager
.getBorder("Tree.selectionBorder")));