Merge from mainline.
[official-gcc.git] / libjava / classpath / javax / swing / tree / DefaultTreeCellEditor.java
blobcc19501d2b68f6aa436dbee08574d57368482f66
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)
9 any later version.
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
19 02110-1301 USA.
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.
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;
45 import java.awt.Font;
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;
70 /**
71 * Participates in the tree cell editing.
73 * @author Andrew Selkirk
74 * @author Audrius Meskauskas
76 public class DefaultTreeCellEditor
77 implements ActionListener, TreeCellEditor, TreeSelectionListener
79 /**
80 * The number of the fast mouse clicks, required to start the editing
81 * session.
83 static int CLICK_COUNT_TO_START = 3;
85 /**
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
92 /**
93 * Use v 1.5 serial version UID for interoperability.
95 static final long serialVersionUID = 6470339600449699810L;
97 /**
98 * Creates an <code>EditorContainer</code> object.
100 public EditorContainer()
102 // Do nothing here.
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()
111 // Do nothing here.
114 public void setBounds(Rectangle bounds)
116 super.setBounds(bounds);
117 doLayout();
121 * Overrides Container.paint to paint the node's icon and use the selection
122 * color for the background.
124 * @param g -
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);
135 super.paint(g);
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.
145 int eOffset;
147 // Move the component to the left, leaving room for the editing icon:
148 if (editingIcon != null)
149 eOffset = editingIcon.getIconWidth();
150 else
151 eOffset = 0;
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)
190 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();
202 if (font == null)
204 Component parent = getParent();
205 if (parent != null)
206 return parent.getFont();
207 return null;
209 return font;
213 * Returns the border of the text field.
215 * @return the border
217 public Border getBorder()
219 return border;
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();
232 Font f = getFont();
234 if (f != null)
236 FontMetrics fm = getToolkit().getFontMetrics(f);
238 return new Dimension(SwingUtilities.computeStringWidth(fm, s),
239 fm.getHeight());
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)
256 cancelCellEditing();
260 * The method is called after completing the editing session.
262 * @param event unused
264 public void editingStopped(ChangeEvent event)
266 stopCellEditing();
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.
337 protected Font font;
340 * Helper field used to save the last path seen while the timer was
341 * running.
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)
370 setTree(tree);
371 this.renderer = renderer;
373 if (editor == null)
374 editor = createTreeCellEditor();
375 else
376 editor.addCellEditorListener(new RealEditorListener());
378 realEditor = editor;
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);
406 // set up icon
407 if (isLeaf)
408 renderer.setIcon(renderer.getLeafIcon());
409 else if (expanded)
410 renderer.setIcon(renderer.getOpenIcon());
411 else
412 renderer.setIcon(renderer.getClosedIcon());
413 editingIcon = renderer.getIcon();
415 editingComponent = getTreeCellEditorComponent(tree, val, true,
416 expanded, isLeaf, lastRow);
421 * writeObject
423 * @param value0
424 * TODO
425 * @exception IOException
426 * TODO
428 private void writeObject(ObjectOutputStream value0) throws IOException
430 // TODO
434 * readObject
435 * @param value0 TODO
436 * @exception IOException TODO
437 * @exception ClassNotFoundException TODO
439 private void readObject(ObjectInputStream value0)
440 throws IOException, ClassNotFoundException
442 // TODO
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.
456 * @return Color
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)
474 if (font != null)
475 this.font = font;
476 else
477 this.font = renderer.getFont();
481 * Gets the font used for editing.
483 * @return the editing font
485 public Font getFont()
487 return font;
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))
545 prepareForEditing();
546 return true;
548 return false;
552 * Messages the realEditor for the return value.
554 * @param event -
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)
561 return true;
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)
573 stopEditingTimer();
574 tree.stopEditing();
575 editingComponent = null;
576 return true;
578 return false;
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;
592 stopEditingTimer();
596 * Stop the editing timer, if it is installed and running.
598 private void stopEditingTimer()
600 if (timer != null && timer.isRunning())
601 timer.stop();
605 * Adds a <code>CellEditorListener</code> object to this editor.
607 * @param listener
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
630 * @since 1.4
632 public CellEditorListener[] getCellEditorListeners()
634 return (CellEditorListener[]) listenerList.getListeners(CellEditorListener.class);
638 * Resets lastPath.
640 * @param e - the event that characterizes the change.
642 public void valueChanged(TreeSelectionEvent e)
644 tPath = lastPath;
645 lastPath = e.getNewLeadSelectionPath();
646 lastRow = tree.getRowForPath(lastPath);
647 stopCellEditing();
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
661 * listener.
663 * @param newTree -
664 * the new tree to be edited
666 protected void setTree(JTree newTree)
668 tree = 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)
681 return true;
682 return false;
686 * Starts the editing timer (if one installed).
688 protected void startEditingTimer()
690 if (timer != null)
691 timer.start();
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())))
707 return true;
708 return false;
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
717 * false otherwise.
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);
732 * determineOffset
733 * @param tree -
734 * @param value -
735 * @param isSelected -
736 * @param expanded -
737 * @param leaf -
738 * @param row -
740 protected void determineOffset(JTree tree, Object value, boolean isSelected,
741 boolean expanded, boolean leaf, int row)
743 renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded,
744 leaf, row, true);
745 Icon c = renderer.getIcon();
746 if (c != null)
747 offset = renderer.getIconTextGap() + c.getIconWidth();
748 else
749 offset = 0;
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);
784 realEditor = editor;
785 return editor;