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 number of the fast mouse clicks, required to start the editing
83 static int CLICK_COUNT_TO_START
= 3;
86 * This container that appears on the tree during editing session.
87 * It contains the editing component displays various other editor -
88 * specific parts like editing icon.
90 public class EditorContainer
extends Container
93 * Use v 1.5 serial version UID for interoperability.
95 static final long serialVersionUID
= 6470339600449699810L;
98 * Creates an <code>EditorContainer</code> object.
100 public EditorContainer()
106 * This method only exists for API compatibility and is useless as it does
107 * nothing. It got probably introduced by accident.
109 public void EditorContainer()
114 public void setBounds(Rectangle bounds
)
116 super.setBounds(bounds
);
121 * Overrides Container.paint to paint the node's icon and use the selection
122 * color for the background.
125 * the specified Graphics window
127 public void paint(Graphics g
)
129 if (editingIcon
!= null)
131 // From the previous version, the left margin is taken as half
132 // of the icon width.
133 editingIcon
.paintIcon(this, g
, 0, 0);
139 * Lays out this Container, moving the editor component to the left
140 * (leaving place for the icon).
142 public void doLayout()
144 // The offset of the editing component.
147 // Move the component to the left, leaving room for the editing icon:
148 if (editingIcon
!= null)
149 eOffset
= editingIcon
.getIconWidth();
153 Rectangle bounds
= getBounds();
154 Component c
= getComponent(0);
155 c
.setLocation(eOffset
, 0);
157 // Span the editing component near over all window width.
158 c
.setSize(bounds
.width
- eOffset
, bounds
.height
);
160 * @specnote the Sun sets some more narrow editing component width (it is
161 * not documented how does it is calculated). However as our text field is
162 * still not able to auto - scroll horizontally, replicating such strategy
163 * would prevent adding extra characters to the text being edited.
169 * The default text field, used in the editing sessions.
171 public class DefaultTextField
extends JTextField
174 * Use v 1.5 serial version UID for interoperability.
176 static final long serialVersionUID
= -6629304544265300143L;
179 * The border of the text field.
181 protected Border border
;
184 * Creates a <code>DefaultTextField</code> object.
186 * @param aBorder the border to use
188 public DefaultTextField(Border aBorder
)
194 * Gets the font of this component.
195 * @return this component's font; if a font has not been set for
196 * this component, the font of its parent is returned (if the parent
197 * is not null, otherwise null is returned).
199 public Font
getFont()
201 Font font
= super.getFont();
204 Component parent
= getParent();
206 return parent
.getFont();
213 * Returns the border of the text field.
217 public Border
getBorder()
223 * Overrides JTextField.getPreferredSize to return the preferred size
224 * based on current font, if set, or else use renderer's font.
226 * @return the Dimension of this textfield.
228 public Dimension
getPreferredSize()
230 String s
= getText();
236 FontMetrics fm
= getToolkit().getFontMetrics(f
);
238 return new Dimension(SwingUtilities
.computeStringWidth(fm
, s
),
241 return renderer
.getPreferredSize();
246 * Listens for the events from the realEditor.
248 class RealEditorListener
implements CellEditorListener
251 * The method is called when the editing has been cancelled.
252 * @param event unused
254 public void editingCanceled(ChangeEvent event
)
260 * The method is called after completing the editing session.
262 * @param event unused
264 public void editingStopped(ChangeEvent event
)
270 private EventListenerList listenerList
= new EventListenerList();
273 * Editor handling the editing.
275 protected TreeCellEditor realEditor
;
278 * Renderer, used to get border and offsets from.
280 protected DefaultTreeCellRenderer renderer
;
283 * Editing container, will contain the editorComponent.
285 protected Container editingContainer
;
288 * Component used in editing, obtained from the editingContainer.
290 protected transient Component editingComponent
;
293 * As of Java 2 platform v1.4 this field should no longer be used.
294 * If you wish to provide similar behavior you should directly
295 * override isCellEditable.
297 protected boolean canEdit
;
300 * Used in editing. Indicates x position to place editingComponent.
302 protected transient int offset
;
305 * JTree instance listening too.
307 protected transient JTree tree
;
310 * Last path that was selected.
312 protected transient TreePath lastPath
;
315 * Used before starting the editing session.
317 protected transient javax
.swing
.Timer timer
;
320 * Row that was last passed into getTreeCellEditorComponent.
322 protected transient int lastRow
;
325 * True if the border selection color should be drawn.
327 protected Color borderSelectionColor
;
330 * Icon to use when editing.
332 protected transient Icon editingIcon
;
335 * Font to paint with, null indicates font of renderer is to be used.
340 * Helper field used to save the last path seen while the timer was
343 private TreePath tPath
;
346 * Constructs a DefaultTreeCellEditor object for a JTree using the
347 * specified renderer and a default editor. (Use this constructor
348 * for normal editing.)
350 * @param tree - a JTree object
351 * @param renderer - a DefaultTreeCellRenderer object
353 public DefaultTreeCellEditor(JTree tree
, DefaultTreeCellRenderer renderer
)
355 this(tree
, renderer
, null);
359 * Constructs a DefaultTreeCellEditor object for a JTree using the specified
360 * renderer and the specified editor. (Use this constructor
361 * for specialized editing.)
363 * @param tree - a JTree object
364 * @param renderer - a DefaultTreeCellRenderer object
365 * @param editor - a TreeCellEditor object
367 public DefaultTreeCellEditor(JTree tree
, DefaultTreeCellRenderer renderer
,
368 TreeCellEditor editor
)
371 this.renderer
= renderer
;
374 editor
= createTreeCellEditor();
376 editor
.addCellEditorListener(new RealEditorListener());
380 lastPath
= tree
.getLeadSelectionPath();
381 tree
.addTreeSelectionListener(this);
382 editingContainer
= createContainer();
383 setFont(UIManager
.getFont("Tree.font"));
384 setBorderSelectionColor(UIManager
.getColor("Tree.selectionBorderColor"));
385 editingIcon
= renderer
.getIcon();
389 * Configures the editing component whenever it is null.
391 * @param tree the tree to configure to component for.
392 * @param renderer the renderer used to set up the nodes
393 * @param editor the editor used
395 private void configureEditingComponent(JTree tree
,
396 DefaultTreeCellRenderer renderer
,
397 TreeCellEditor editor
)
399 if (tree
!= null && lastPath
!= null)
401 Object val
= lastPath
.getLastPathComponent();
402 boolean isLeaf
= tree
.getModel().isLeaf(val
);
403 boolean expanded
= tree
.isExpanded(lastPath
);
404 determineOffset(tree
, val
, true, expanded
, isLeaf
, lastRow
);
408 renderer
.setIcon(renderer
.getLeafIcon());
410 renderer
.setIcon(renderer
.getOpenIcon());
412 renderer
.setIcon(renderer
.getClosedIcon());
413 editingIcon
= renderer
.getIcon();
415 editingComponent
= getTreeCellEditorComponent(tree
, val
, true,
416 expanded
, isLeaf
, lastRow
);
425 * @exception IOException
428 private void writeObject(ObjectOutputStream value0
) throws IOException
436 * @exception IOException TODO
437 * @exception ClassNotFoundException TODO
439 private void readObject(ObjectInputStream value0
)
440 throws IOException
, ClassNotFoundException
446 * Sets the color to use for the border.
447 * @param newColor - the new border color
449 public void setBorderSelectionColor(Color newColor
)
451 this.borderSelectionColor
= newColor
;
455 * Returns the color the border is drawn.
458 public Color
getBorderSelectionColor()
460 return borderSelectionColor
;
464 * Sets the font to edit with. null indicates the renderers
465 * font should be used. This will NOT override any font you have
466 * set in the editor the receiver was instantied with. If null for
467 * an editor was passed in, a default editor will be created that
468 * will pick up this font.
470 * @param font - the editing Font
472 public void setFont(Font font
)
477 this.font
= renderer
.getFont();
481 * Gets the font used for editing.
483 * @return the editing font
485 public Font
getFont()
491 * Configures the editor. Passed onto the realEditor.
492 * Sets an initial value for the editor. This will cause
493 * the editor to stopEditing and lose any partially edited value
494 * if the editor is editing when this method is called.
495 * Returns the component that should be added to the client's Component
496 * hierarchy. Once installed in the client's hierarchy this component will
497 * then be able to draw and receive user input.
499 * @param tree - the JTree that is asking the editor to edit; this parameter can be null
500 * @param value - the value of the cell to be edited
501 * @param isSelected - true is the cell is to be rendered with selection highlighting
502 * @param expanded - true if the node is expanded
503 * @param leaf - true if the node is a leaf node
504 * @param row - the row index of the node being edited
506 * @return the component for editing
508 public Component
getTreeCellEditorComponent(JTree tree
, Object value
,
509 boolean isSelected
, boolean expanded
,
510 boolean leaf
, int row
)
512 if (realEditor
== null)
513 realEditor
= createTreeCellEditor();
515 return realEditor
.getTreeCellEditorComponent(tree
, value
, isSelected
,
516 expanded
, leaf
, row
);
520 * Returns the value currently being edited (requests it from the
521 * {@link realEditor}.
523 * @return the value currently being edited
525 public Object
getCellEditorValue()
527 return realEditor
.getCellEditorValue();
531 * If the realEditor returns true to this message, prepareForEditing
532 * is messaged and true is returned.
534 * @param event - the event the editor should use to consider whether to
535 * begin editing or not
536 * @return true if editing can be started
538 public boolean isCellEditable(EventObject event
)
540 if (editingComponent
== null)
541 configureEditingComponent(tree
, renderer
, realEditor
);
543 if (editingComponent
!= null && realEditor
.isCellEditable(event
))
552 * Messages the realEditor for the return value.
555 * the event the editor should use to start editing
556 * @return true if the editor would like the editing cell to be selected;
557 * otherwise returns false
559 public boolean shouldSelectCell(EventObject event
)
565 * If the realEditor will allow editing to stop, the realEditor
566 * is removed and true is returned, otherwise false is returned.
567 * @return true if editing was stopped; false otherwise
569 public boolean stopCellEditing()
571 if (editingComponent
!= null)
575 editingComponent
= null;
582 * Messages cancelCellEditing to the realEditor and removes it
583 * from this instance.
585 public void cancelCellEditing()
587 if (editingComponent
!= null)
589 tree
.cancelEditing();
590 editingComponent
= null;
596 * Stop the editing timer, if it is installed and running.
598 private void stopEditingTimer()
600 if (timer
!= null && timer
.isRunning())
605 * Adds a <code>CellEditorListener</code> object to this editor.
608 * the listener to add
610 public void addCellEditorListener(CellEditorListener listener
)
612 realEditor
.addCellEditorListener(listener
);
616 * Removes a <code>CellEditorListener</code> object.
618 * @param listener the listener to remove
620 public void removeCellEditorListener(CellEditorListener listener
)
622 realEditor
.removeCellEditorListener(listener
);
626 * Returns all added <code>CellEditorListener</code> objects to this editor.
628 * @return an array of listeners
632 public CellEditorListener
[] getCellEditorListeners()
634 return (CellEditorListener
[]) listenerList
.getListeners(CellEditorListener
.class);
640 * @param e - the event that characterizes the change.
642 public void valueChanged(TreeSelectionEvent e
)
645 lastPath
= e
.getNewLeadSelectionPath();
646 lastRow
= tree
.getRowForPath(lastPath
);
651 * Messaged when the timer fires.
653 * @param e the event that characterizes the action.
655 public void actionPerformed(ActionEvent e
)
660 * Sets the tree currently editing for. This is needed to add a selection
664 * the new tree to be edited
666 protected void setTree(JTree newTree
)
672 * Returns true if event is a MouseEvent and the click count is 1.
674 * @param event - the event being studied
675 * @return true if editing should start
677 protected boolean shouldStartEditingTimer(EventObject event
)
679 if ((event
instanceof MouseEvent
) &&
680 ((MouseEvent
) event
).getClickCount() == 1)
686 * Starts the editing timer (if one installed).
688 protected void startEditingTimer()
695 * Returns true if event is null, or it is a MouseEvent with
696 * a click count > 2 and inHitRegion returns true.
698 * @param event - the event being studied
699 * @return true if event is null, or it is a MouseEvent with
700 * a click count > 2 and inHitRegion returns true
702 protected boolean canEditImmediately(EventObject event
)
704 if (event
== null || !(event
instanceof MouseEvent
) || (((MouseEvent
) event
).
705 getClickCount() > 2 && inHitRegion(((MouseEvent
) event
).getX(),
706 ((MouseEvent
) event
).getY())))
712 * Returns true if the passed in location is a valid mouse location
713 * to start editing from. This is implemented to return false if x is
714 * less than or equal to the width of the icon and icon
715 * gap displayed by the renderer. In other words this returns true if
716 * the user clicks over the text part displayed by the renderer, and
719 * @param x - the x-coordinate of the point
720 * @param y - the y-coordinate of the point
722 * @return true if the passed in location is a valid mouse location
724 protected boolean inHitRegion(int x
, int y
)
726 Rectangle bounds
= tree
.getPathBounds(lastPath
);
728 return bounds
.contains(x
, y
);
735 * @param isSelected -
740 protected void determineOffset(JTree tree
, Object value
, boolean isSelected
,
741 boolean expanded
, boolean leaf
, int row
)
743 renderer
.getTreeCellRendererComponent(tree
, value
, isSelected
, expanded
,
745 Icon c
= renderer
.getIcon();
747 offset
= renderer
.getIconTextGap() + c
.getIconWidth();
753 * Invoked just before editing is to start. Will add the
754 * editingComponent to the editingContainer.
756 protected void prepareForEditing()
758 editingContainer
.removeAll();
759 editingContainer
.add(editingComponent
);
763 * Creates the container to manage placement of editingComponent.
765 * @return the container to manage the placement of the editingComponent.
767 protected Container
createContainer()
769 return new DefaultTreeCellEditor
.EditorContainer();
773 * This is invoked if a TreeCellEditor is not supplied in the constructor.
774 * It returns a TextField editor.
776 * @return a new TextField editor
778 protected TreeCellEditor
createTreeCellEditor()
780 DefaultCellEditor editor
= new DefaultCellEditor(new DefaultTreeCellEditor
.DefaultTextField(
781 UIManager
.getBorder("Tree.selectionBorder")));
782 editor
.addCellEditorListener(new RealEditorListener());
783 editor
.setClickCountToStart(CLICK_COUNT_TO_START
);