001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Anton Avtamonov
019: * @version $Revision$
020: */package javax.swing.tree;
021:
022: import java.awt.Color;
023: import java.awt.Component;
024: import java.awt.Container;
025: import java.awt.Dimension;
026: import java.awt.Font;
027: import java.awt.Graphics;
028: import java.awt.Rectangle;
029: import java.awt.event.ActionEvent;
030: import java.awt.event.ActionListener;
031: import java.awt.event.MouseEvent;
032: import java.util.EventObject;
033:
034: import javax.swing.DefaultCellEditor;
035: import javax.swing.Icon;
036: import javax.swing.JTextField;
037: import javax.swing.JTree;
038: import javax.swing.SwingUtilities;
039: import javax.swing.Timer;
040: import javax.swing.UIManager;
041: import javax.swing.border.Border;
042: import javax.swing.event.CellEditorListener;
043: import javax.swing.event.TreeSelectionEvent;
044: import javax.swing.event.TreeSelectionListener;
045:
046: public class DefaultTreeCellEditor implements ActionListener,
047: TreeCellEditor, TreeSelectionListener {
048: public class DefaultTextField extends JTextField {
049: protected Border border;
050:
051: public DefaultTextField(final Border border) {
052: setBorder(border);
053: }
054:
055: public void setBorder(final Border border) {
056: this .border = border;
057: super .setBorder(border);
058: }
059:
060: public Border getBorder() {
061: return border;
062: }
063:
064: public Font getFont() {
065: return super .getFont();
066: }
067:
068: public Dimension getPreferredSize() {
069: Dimension result = super .getPreferredSize();
070: if (renderer != null) {
071: result.height = renderer.getPreferredSize().height;
072: }
073:
074: return result;
075: }
076: }
077:
078: public class EditorContainer extends Container {
079: public EditorContainer() {
080: }
081:
082: //This method looks like a misprint or bug in specification, however we have
083: // to put it here to be API-compatible.
084: public void EditorContainer() {
085: }
086:
087: public void paint(final Graphics g) {
088: if (icon != null) {
089: Rectangle viewR = new Rectangle(0, 0, getWidth(),
090: getHeight());
091: Rectangle iconR = new Rectangle();
092: Rectangle textR = new Rectangle();
093:
094: SwingUtilities.layoutCompoundLabel(renderer, renderer
095: .getFontMetrics(renderer.getFont()), renderer
096: .getText(), icon, renderer
097: .getVerticalAlignment(), renderer
098: .getHorizontalAlignment(), renderer
099: .getVerticalTextPosition(), renderer
100: .getHorizontalTextPosition(), viewR, iconR,
101: textR, renderer.getIconTextGap());
102:
103: if (!tree.getComponentOrientation().isLeftToRight()) {
104: iconR.x = getWidth() - offset
105: + (offset - icon.getIconWidth()) / 2;
106: }
107:
108: icon.paintIcon(this , g, iconR.x, iconR.y);
109: }
110: super .paint(g);
111: }
112:
113: public void doLayout() {
114: if (tree.getComponentOrientation().isLeftToRight()) {
115: editingComponent.setBounds(offset, 0, this .getWidth()
116: - offset, this .getHeight());
117: } else {
118: editingComponent.setBounds(0, 0, this .getWidth()
119: - offset, this .getHeight());
120: }
121: }
122:
123: public void requestFocus() {
124: if (editingComponent != null) {
125: editingComponent.requestFocus();
126: } else {
127: super .requestFocus();
128: }
129: }
130:
131: public Dimension getPreferredSize() {
132: if (editingComponent != null) {
133: Dimension editorPrefSize = editingComponent
134: .getPreferredSize();
135: editorPrefSize.width = Math.max(editorPrefSize.width
136: + offset + 5, 100);
137: if (renderer != null) {
138: editorPrefSize.height = Math.max(
139: editorPrefSize.height, renderer
140: .getPreferredSize().height);
141: }
142: return editorPrefSize;
143: }
144: return new Dimension();
145: }
146: }
147:
148: protected TreeCellEditor realEditor;
149: protected DefaultTreeCellRenderer renderer;
150: protected Container editingContainer;
151: protected transient Component editingComponent;
152: protected boolean canEdit;
153: protected transient int offset;
154: protected transient JTree tree;
155: protected transient TreePath lastPath;
156: protected transient Timer timer;
157: protected transient int lastRow;
158: protected Color borderSelectionColor;
159: protected transient Icon editingIcon;
160: protected Font font;
161:
162: private Icon icon;
163:
164: private static final int EDITING_DELAY = 1200;
165:
166: public DefaultTreeCellEditor(final JTree tree,
167: final DefaultTreeCellRenderer renderer) {
168: this (tree, renderer, null);
169: }
170:
171: public DefaultTreeCellEditor(final JTree tree,
172: final DefaultTreeCellRenderer renderer,
173: final TreeCellEditor editor) {
174: setTree(tree);
175: this .renderer = renderer;
176: this .realEditor = editor != null ? editor
177: : createTreeCellEditor();
178:
179: editingContainer = createContainer();
180: }
181:
182: public void setBorderSelectionColor(final Color color) {
183: borderSelectionColor = color;
184: }
185:
186: public Color getBorderSelectionColor() {
187: return borderSelectionColor;
188: }
189:
190: public void setFont(final Font font) {
191: this .font = font;
192: }
193:
194: public Font getFont() {
195: return font;
196: }
197:
198: public Component getTreeCellEditorComponent(final JTree tree,
199: final Object value, final boolean isSelected,
200: final boolean expanded, final boolean leaf, final int row) {
201:
202: editingComponent = realEditor.getTreeCellEditorComponent(tree,
203: value, isSelected, expanded, leaf, row);
204: Font font = getFont();
205: if (font == null && renderer != null) {
206: font = renderer.getFont();
207: }
208: if (font == null) {
209: font = tree.getFont();
210: }
211: editingContainer.setFont(font);
212: editingComponent.setFont(font);
213:
214: determineOffset(tree, value, isSelected, expanded, leaf, row);
215: prepareForEditing();
216:
217: return editingContainer;
218: }
219:
220: public Object getCellEditorValue() {
221: return realEditor.getCellEditorValue();
222: }
223:
224: public boolean isCellEditable(final EventObject event) {
225: if (realEditor.isCellEditable(event)
226: && canEditImmediately(event)) {
227: prepareForEditing();
228: return true;
229: }
230:
231: if (event instanceof MouseEvent) {
232: MouseEvent me = (MouseEvent) event;
233: TreePath clickPath = tree.getPathForLocation(me.getX(), me
234: .getY());
235:
236: if (lastPath != null && lastPath.equals(clickPath)
237: && shouldStartEditingTimer(event)) {
238: startEditingTimer();
239: } else if (timer != null) {
240: timer.stop();
241: }
242: }
243:
244: return false;
245: }
246:
247: public boolean shouldSelectCell(final EventObject event) {
248: return realEditor.shouldSelectCell(event);
249: }
250:
251: public boolean stopCellEditing() {
252: if (realEditor.stopCellEditing()) {
253: removeEditor();
254: return true;
255: }
256:
257: return false;
258: }
259:
260: public void cancelCellEditing() {
261: realEditor.cancelCellEditing();
262: removeEditor();
263: }
264:
265: public void addCellEditorListener(final CellEditorListener l) {
266: realEditor.addCellEditorListener(l);
267: }
268:
269: public void removeCellEditorListener(final CellEditorListener l) {
270: realEditor.removeCellEditorListener(l);
271: }
272:
273: public CellEditorListener[] getCellEditorListeners() {
274: return ((DefaultCellEditor) realEditor)
275: .getCellEditorListeners();
276: }
277:
278: public void valueChanged(final TreeSelectionEvent e) {
279: lastPath = e.getNewLeadSelectionPath();
280: }
281:
282: public void actionPerformed(final ActionEvent e) {
283: lastRow = tree.getRowForPath(lastPath);
284: tree.startEditingAtPath(lastPath);
285: }
286:
287: protected void setTree(final JTree tree) {
288: this .tree = tree;
289: tree.addTreeSelectionListener(this );
290: }
291:
292: protected boolean shouldStartEditingTimer(final EventObject event) {
293: if (!(event instanceof MouseEvent)) {
294: return false;
295: }
296: MouseEvent me = (MouseEvent) event;
297: if (me.getClickCount() != 1
298: || !SwingUtilities.isLeftMouseButton(me)) {
299: return false;
300: }
301: return inHitRegion(me.getX(), me.getY());
302: }
303:
304: protected void startEditingTimer() {
305: if (timer == null) {
306: timer = new Timer(EDITING_DELAY, this );
307: timer.setRepeats(false);
308: timer.start();
309: } else {
310: timer.restart();
311: }
312: }
313:
314: protected boolean canEditImmediately(final EventObject event) {
315: if (event == null) {
316: return true;
317: }
318:
319: if (event instanceof MouseEvent) {
320: MouseEvent me = (MouseEvent) event;
321: return SwingUtilities.isLeftMouseButton(me)
322: && me.getClickCount() > 2
323: && realEditor.isCellEditable(event)
324: && inHitRegion(me.getX(), me.getY());
325: }
326:
327: return false;
328: }
329:
330: protected boolean inHitRegion(final int x, final int y) {
331: TreePath hitPath = tree.getPathForLocation(x, y);
332: if (hitPath == null) {
333: return false;
334: }
335: Rectangle pathBounds = tree.getPathBounds(hitPath);
336:
337: return x > pathBounds.x + offset && y >= 0;
338: }
339:
340: protected void determineOffset(final JTree tree,
341: final Object value, final boolean isSelected,
342: final boolean expanded, final boolean leaf, final int row) {
343:
344: if (renderer != null) {
345: renderer.getTreeCellRendererComponent(tree, value,
346: isSelected, expanded, leaf, row, false);
347: icon = renderer.getIcon();
348: offset = icon != null ? icon.getIconWidth()
349: + renderer.getIconTextGap() : 0;
350: } else {
351: offset = 0;
352: }
353: }
354:
355: protected void prepareForEditing() {
356: if (editingComponent != null) {
357: editingContainer.add(editingComponent);
358: }
359: }
360:
361: protected Container createContainer() {
362: return new EditorContainer();
363: }
364:
365: protected TreeCellEditor createTreeCellEditor() {
366: DefaultCellEditor result = new DefaultCellEditor(
367: new DefaultTextField(UIManager
368: .getBorder("Tree.editorBorder")));
369: result.setClickCountToStart(1);
370:
371: return result;
372: }
373:
374: private void removeEditor() {
375: editingContainer.remove(editingComponent);
376: editingComponent = null;
377: }
378: }
|