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
.Rectangle
;
49 import java
.awt
.event
.ActionEvent
;
50 import java
.awt
.event
.ActionListener
;
51 import java
.awt
.event
.MouseEvent
;
52 import java
.io
.IOException
;
53 import java
.io
.ObjectInputStream
;
54 import java
.io
.ObjectOutputStream
;
55 import java
.util
.EventObject
;
57 import javax
.swing
.DefaultCellEditor
;
58 import javax
.swing
.Icon
;
59 import javax
.swing
.JTextField
;
60 import javax
.swing
.JTree
;
61 import javax
.swing
.SwingUtilities
;
62 import javax
.swing
.UIManager
;
63 import javax
.swing
.border
.Border
;
64 import javax
.swing
.event
.CellEditorListener
;
65 import javax
.swing
.event
.ChangeEvent
;
66 import javax
.swing
.event
.EventListenerList
;
67 import javax
.swing
.event
.TreeSelectionEvent
;
68 import javax
.swing
.event
.TreeSelectionListener
;
71 * Participates in the tree cell editing.
73 * @author Andrew Selkirk
74 * @author Audrius Meskauskas
76 public class DefaultTreeCellEditor
77 implements ActionListener
, TreeCellEditor
, TreeSelectionListener
80 * The gap between the icon and editing component during editing.
82 static int ICON_TEXT_GAP
= 3;
85 * The left margin of the editing container (the gap between the tree and
86 * the editing component of the editing icon.
88 static int TREE_ICON_GAP
= ICON_TEXT_GAP
;
91 * The number of the fast mouse clicks, required to start the editing
94 static int CLICK_COUNT_TO_START
= 3;
97 * This container that appears on the tree during editing session.
98 * It contains the editing component displays various other editor -
99 * specific parts like editing icon.
101 public class EditorContainer
extends Container
104 * Use v 1.5 serial version UID for interoperability.
106 static final long serialVersionUID
= 6470339600449699810L;
109 * Creates an <code>EditorContainer</code> object.
111 public EditorContainer()
117 * This method only exists for API compatibility and is useless as it does
118 * nothing. It got probably introduced by accident.
120 public void EditorContainer()
125 public void setBounds(Rectangle bounds
)
127 super.setBounds(bounds
);
132 * Overrides Container.paint to paint the node's icon and use the selection
133 * color for the background.
136 * the specified Graphics window
138 public void paint(Graphics g
)
140 if (editingIcon
!= null)
142 // From the previous version, the left margin is taken as half
143 // of the icon width.
144 editingIcon
.paintIcon(this, g
, TREE_ICON_GAP
, 0);
150 * Lays out this Container, moving the editor component to the left
151 * (leaving place for the icon).
153 public void doLayout()
155 // The offset of the editing component.
158 // Move the component to the left, leaving room for the editing icon:
159 if (editingIcon
!= null)
160 eOffset
= TREE_ICON_GAP
+ editingIcon
.getIconWidth() + ICON_TEXT_GAP
;
164 Rectangle bounds
= getBounds();
165 Component c
= getComponent(0);
166 c
.setLocation(eOffset
, 0);
168 // Span the editing component near over all window width.
169 c
.setSize(bounds
.width
- eOffset
- TREE_ICON_GAP
, bounds
.height
);
171 * @specnote the Sun sets some more narrow editing component width (it is
172 * not documented how does it is calculated). However as our text field is
173 * still not able to auto - scroll horizontally, replicating such strategy
174 * would prevent adding extra characters to the text being edited.
180 * The default text field, used in the editing sessions.
182 public class DefaultTextField
extends JTextField
185 * Use v 1.5 serial version UID for interoperability.
187 static final long serialVersionUID
= -6629304544265300143L;
190 * The border of the text field.
192 protected Border border
;
195 * Creates a <code>DefaultTextField</code> object.
197 * @param aBorder the border to use
199 public DefaultTextField(Border aBorder
)
205 * Gets the font of this component.
206 * @return this component's font; if a font has not been set for
207 * this component, the font of its parent is returned (if the parent
208 * is not null, otherwise null is returned).
210 public Font
getFont()
212 Font font
= super.getFont();
215 Component parent
= getParent();
217 return parent
.getFont();
224 * Returns the border of the text field.
228 public Border
getBorder()
234 * Overrides JTextField.getPreferredSize to return the preferred size
235 * based on current font, if set, or else use renderer's font.
237 * @return the Dimension of this textfield.
239 public Dimension
getPreferredSize()
241 String s
= getText();
247 FontMetrics fm
= getToolkit().getFontMetrics(f
);
249 return new Dimension(SwingUtilities
.computeStringWidth(fm
, s
),
252 return renderer
.getPreferredSize();
257 * Listens for the events from the realEditor.
259 class RealEditorListener
implements CellEditorListener
262 * The method is called when the editing has been cancelled.
263 * @param event unused
265 public void editingCanceled(ChangeEvent event
)
271 * The method is called after completing the editing session.
273 * @param event unused
275 public void editingStopped(ChangeEvent event
)
281 private EventListenerList listenerList
= new EventListenerList();
284 * Editor handling the editing.
286 protected TreeCellEditor realEditor
;
289 * Renderer, used to get border and offsets from.
291 protected DefaultTreeCellRenderer renderer
;
294 * Editing container, will contain the editorComponent.
296 protected Container editingContainer
;
299 * Component used in editing, obtained from the editingContainer.
301 protected transient Component editingComponent
;
304 * As of Java 2 platform v1.4 this field should no longer be used.
305 * If you wish to provide similar behavior you should directly
306 * override isCellEditable.
308 protected boolean canEdit
;
311 * Used in editing. Indicates x position to place editingComponent.
313 protected transient int offset
;
316 * JTree instance listening too.
318 protected transient JTree tree
;
321 * Last path that was selected.
323 protected transient TreePath lastPath
;
326 * Used before starting the editing session.
328 protected transient javax
.swing
.Timer timer
;
331 * Row that was last passed into getTreeCellEditorComponent.
333 protected transient int lastRow
;
336 * True if the border selection color should be drawn.
338 protected Color borderSelectionColor
;
341 * Icon to use when editing.
343 protected transient Icon editingIcon
;
346 * Font to paint with, null indicates font of renderer is to be used.
351 * Helper field used to save the last path seen while the timer was
354 private TreePath tPath
;
357 * Constructs a DefaultTreeCellEditor object for a JTree using the
358 * specified renderer and a default editor. (Use this constructor
359 * for normal editing.)
361 * @param tree - a JTree object
362 * @param renderer - a DefaultTreeCellRenderer object
364 public DefaultTreeCellEditor(JTree tree
, DefaultTreeCellRenderer renderer
)
366 this(tree
, renderer
, null);
370 * Constructs a DefaultTreeCellEditor object for a JTree using the specified
371 * renderer and the specified editor. (Use this constructor
372 * for specialized editing.)
374 * @param tree - a JTree object
375 * @param renderer - a DefaultTreeCellRenderer object
376 * @param editor - a TreeCellEditor object
378 public DefaultTreeCellEditor(JTree tree
, DefaultTreeCellRenderer renderer
,
379 TreeCellEditor editor
)
382 this.renderer
= renderer
;
385 editor
= createTreeCellEditor();
387 editor
.addCellEditorListener(new RealEditorListener());
391 lastPath
= tree
.getLeadSelectionPath();
392 tree
.addTreeSelectionListener(this);
393 editingContainer
= createContainer();
394 setFont(UIManager
.getFont("Tree.font"));
395 setBorderSelectionColor(UIManager
.getColor("Tree.selectionBorderColor"));
396 editingIcon
= renderer
.getIcon();
400 * Configures the editing component whenever it is null.
402 * @param tree the tree to configure to component for.
403 * @param renderer the renderer used to set up the nodes
404 * @param editor the editor used
406 private void configureEditingComponent(JTree tree
,
407 DefaultTreeCellRenderer renderer
,
408 TreeCellEditor editor
)
410 if (tree
!= null && lastPath
!= null)
412 Object val
= lastPath
.getLastPathComponent();
413 boolean isLeaf
= tree
.getModel().isLeaf(val
);
414 boolean expanded
= tree
.isExpanded(lastPath
);
415 determineOffset(tree
, val
, true, expanded
, isLeaf
, lastRow
);
419 renderer
.setIcon(renderer
.getLeafIcon());
421 renderer
.setIcon(renderer
.getOpenIcon());
423 renderer
.setIcon(renderer
.getClosedIcon());
424 editingIcon
= renderer
.getIcon();
426 editingComponent
= getTreeCellEditorComponent(tree
, val
, true,
427 expanded
, isLeaf
, lastRow
);
436 * @exception IOException
439 private void writeObject(ObjectOutputStream value0
) throws IOException
447 * @exception IOException TODO
448 * @exception ClassNotFoundException TODO
450 private void readObject(ObjectInputStream value0
)
451 throws IOException
, ClassNotFoundException
457 * Sets the color to use for the border.
458 * @param newColor - the new border color
460 public void setBorderSelectionColor(Color newColor
)
462 this.borderSelectionColor
= newColor
;
466 * Returns the color the border is drawn.
469 public Color
getBorderSelectionColor()
471 return borderSelectionColor
;
475 * Sets the font to edit with. null indicates the renderers
476 * font should be used. This will NOT override any font you have
477 * set in the editor the receiver was instantied with. If null for
478 * an editor was passed in, a default editor will be created that
479 * will pick up this font.
481 * @param font - the editing Font
483 public void setFont(Font font
)
488 this.font
= renderer
.getFont();
492 * Gets the font used for editing.
494 * @return the editing font
496 public Font
getFont()
502 * Configures the editor. Passed onto the realEditor.
503 * Sets an initial value for the editor. This will cause
504 * the editor to stopEditing and lose any partially edited value
505 * if the editor is editing when this method is called.
506 * Returns the component that should be added to the client's Component
507 * hierarchy. Once installed in the client's hierarchy this component will
508 * then be able to draw and receive user input.
510 * @param tree - the JTree that is asking the editor to edit; this parameter can be null
511 * @param value - the value of the cell to be edited
512 * @param isSelected - true is the cell is to be rendered with selection highlighting
513 * @param expanded - true if the node is expanded
514 * @param leaf - true if the node is a leaf node
515 * @param row - the row index of the node being edited
517 * @return the component for editing
519 public Component
getTreeCellEditorComponent(JTree tree
, Object value
,
520 boolean isSelected
, boolean expanded
,
521 boolean leaf
, int row
)
523 if (realEditor
== null)
524 realEditor
= createTreeCellEditor();
526 return realEditor
.getTreeCellEditorComponent(tree
, value
, isSelected
,
527 expanded
, leaf
, row
);
531 * Returns the value currently being edited (requests it from the
532 * {@link realEditor}.
534 * @return the value currently being edited
536 public Object
getCellEditorValue()
538 return realEditor
.getCellEditorValue();
542 * If the realEditor returns true to this message, prepareForEditing
543 * is messaged and true is returned.
545 * @param event - the event the editor should use to consider whether to begin editing or not
546 * @return true if editing can be started
548 public boolean isCellEditable(EventObject event
)
550 if (editingComponent
== null)
551 configureEditingComponent(tree
, renderer
, realEditor
);
553 if (editingComponent
!= null && realEditor
.isCellEditable(event
))
562 * Messages the realEditor for the return value.
565 * the event the editor should use to start editing
566 * @return true if the editor would like the editing cell to be selected;
567 * otherwise returns false
569 public boolean shouldSelectCell(EventObject event
)
575 * If the realEditor will allow editing to stop, the realEditor
576 * is removed and true is returned, otherwise false is returned.
577 * @return true if editing was stopped; false otherwise
579 public boolean stopCellEditing()
581 if (editingComponent
!= null)
585 editingComponent
= null;
592 * Messages cancelCellEditing to the realEditor and removes it
593 * from this instance.
595 public void cancelCellEditing()
597 if (editingComponent
!= null)
599 tree
.cancelEditing();
600 editingComponent
= null;
606 * Stop the editing timer, if it is installed and running.
608 private void stopEditingTimer()
610 if (timer
!= null && timer
.isRunning())
615 * Adds a <code>CellEditorListener</code> object to this editor.
618 * the listener to add
620 public void addCellEditorListener(CellEditorListener listener
)
622 realEditor
.addCellEditorListener(listener
);
626 * Removes a <code>CellEditorListener</code> object.
628 * @param listener the listener to remove
630 public void removeCellEditorListener(CellEditorListener listener
)
632 realEditor
.removeCellEditorListener(listener
);
636 * Returns all added <code>CellEditorListener</code> objects to this editor.
638 * @return an array of listeners
642 public CellEditorListener
[] getCellEditorListeners()
644 return (CellEditorListener
[]) listenerList
.getListeners(CellEditorListener
.class);
650 * @param e - the event that characterizes the change.
652 public void valueChanged(TreeSelectionEvent e
)
655 lastPath
= e
.getNewLeadSelectionPath();
656 lastRow
= tree
.getRowForPath(lastPath
);
661 * Messaged when the timer fires.
663 * @param e the event that characterizes the action.
665 public void actionPerformed(ActionEvent e
)
670 * Sets the tree currently editing for. This is needed to add a selection
674 * the new tree to be edited
676 protected void setTree(JTree newTree
)
682 * Returns true if event is a MouseEvent and the click count is 1.
684 * @param event - the event being studied
685 * @return true if editing should start
687 protected boolean shouldStartEditingTimer(EventObject event
)
689 if ((event
instanceof MouseEvent
) &&
690 ((MouseEvent
) event
).getClickCount() == 1)
696 * Starts the editing timer (if one installed).
698 protected void startEditingTimer()
705 * Returns true if event is null, or it is a MouseEvent with
706 * a click count > 2 and inHitRegion returns true.
708 * @param event - the event being studied
709 * @return true if event is null, or it is a MouseEvent with
710 * a click count > 2 and inHitRegion returns true
712 protected boolean canEditImmediately(EventObject event
)
714 if (event
== null || !(event
instanceof MouseEvent
) || (((MouseEvent
) event
).
715 getClickCount() > 2 && inHitRegion(((MouseEvent
) event
).getX(),
716 ((MouseEvent
) event
).getY())))
722 * Returns true if the passed in location is a valid mouse location
723 * to start editing from. This is implemented to return false if x is
724 * less than or equal to the width of the icon and icon
725 * gap displayed by the renderer. In other words this returns true if
726 * the user clicks over the text part displayed by the renderer, and
729 * @param x - the x-coordinate of the point
730 * @param y - the y-coordinate of the point
732 * @return true if the passed in location is a valid mouse location
734 protected boolean inHitRegion(int x
, int y
)
736 Rectangle bounds
= tree
.getPathBounds(lastPath
);
738 return bounds
.contains(x
, y
);
745 * @param isSelected -
750 protected void determineOffset(JTree tree
, Object value
, boolean isSelected
,
751 boolean expanded
, boolean leaf
, int row
)
753 renderer
.getTreeCellRendererComponent(tree
, value
, isSelected
, expanded
,
755 Icon c
= renderer
.getIcon();
757 offset
= renderer
.getIconTextGap() + c
.getIconWidth();
763 * Invoked just before editing is to start. Will add the
764 * editingComponent to the editingContainer.
766 protected void prepareForEditing()
768 editingContainer
.removeAll();
769 editingContainer
.add(editingComponent
);
773 * Creates the container to manage placement of editingComponent.
775 * @return the container to manage the placement of the editingComponent.
777 protected Container
createContainer()
779 return new DefaultTreeCellEditor
.EditorContainer();
783 * This is invoked if a TreeCellEditor is not supplied in the constructor.
784 * It returns a TextField editor.
786 * @return a new TextField editor
788 protected TreeCellEditor
createTreeCellEditor()
790 DefaultCellEditor editor
= new DefaultCellEditor(new DefaultTreeCellEditor
.DefaultTextField(
791 UIManager
.getBorder("Tree.selectionBorder")));
792 editor
.addCellEditorListener(new RealEditorListener());
793 editor
.setClickCountToStart(CLICK_COUNT_TO_START
);