001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.ui.components;
042:
043: import org.netbeans.lib.profiler.ui.UIUtils;
044: import org.netbeans.lib.profiler.ui.components.tree.CheckTreeCellRenderer;
045: import org.netbeans.lib.profiler.ui.components.tree.CheckTreeNode;
046: import org.netbeans.lib.profiler.ui.components.tree.TreeCellRendererPersistent;
047: import java.awt.BorderLayout;
048: import java.awt.Component;
049: import java.awt.Dimension;
050: import java.awt.Point;
051: import java.awt.Rectangle;
052: import java.awt.event.KeyEvent;
053: import java.awt.event.KeyListener;
054: import java.awt.event.MouseEvent;
055: import java.awt.event.MouseListener;
056: import java.util.Collection;
057: import java.util.LinkedList;
058: import java.util.Vector;
059: import javax.swing.JFrame;
060: import javax.swing.JScrollPane;
061: import javax.swing.UIManager;
062: import javax.swing.plaf.basic.BasicTreeUI;
063: import javax.swing.tree.DefaultTreeModel;
064: import javax.swing.tree.TreeCellRenderer;
065: import javax.swing.tree.TreeModel;
066: import javax.swing.tree.TreePath;
067:
068: /**
069: *
070: * @author Jiri Sedlacek
071: */
072: public class JCheckTree extends JExtendedTree {
073: //~ Inner Interfaces ---------------------------------------------------------------------------------------------------------
074:
075: // ---------------------------------------------------------------------------
076:
077: // --- CheckTreeListener interface definition --------------------------------
078: public static interface CheckTreeListener {
079: //~ Methods --------------------------------------------------------------------------------------------------------------
080:
081: void checkNodeToggled(TreePath node, boolean before);
082:
083: void checkTreeChanged(Collection<CheckTreeNode> changedNodes);
084: }
085:
086: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
087:
088: // ---------------------------------------------------------------------------
089:
090: // --- Custom TreeUI implementation ------------------------------------------
091: private class CheckTreeUI extends BasicTreeUI {
092: //~ Methods --------------------------------------------------------------------------------------------------------------
093:
094: protected boolean isToggleEvent(MouseEvent event) {
095: if (isOverCheckBox(event.getX(), event.getY())) {
096: return false;
097: }
098:
099: return super .isToggleEvent(event);
100: }
101: }
102:
103: // ---------------------------------------------------------------------------
104:
105: // --- Listeners implementation ----------------------------------------------
106: private class PrivateComponentListener implements MouseListener,
107: KeyListener {
108: //~ Methods --------------------------------------------------------------------------------------------------------------
109:
110: public void keyPressed(KeyEvent e) {
111: if (e.getKeyCode() == KeyEvent.VK_SPACE) {
112: TreePath[] paths = getSelectionPaths();
113:
114: if ((paths != null) && (paths.length > 0)) {
115: Collection changedNodes = new LinkedList();
116:
117: for (int i = 0; i < paths.length; i++) {
118: TreePath path = paths[i];
119:
120: if ((path != null)
121: && (path.getPathCount() > 0)
122: && path.getLastPathComponent() instanceof CheckTreeNode
123: && (((CheckTreeNode) path
124: .getLastPathComponent())
125: .isLeaf() || (i == (paths.length - 1)))) {
126: fireNodeToggled(path, true);
127: }
128:
129: changedNodes.addAll(togglePathState(path));
130: fireNodeToggled(path, false);
131: }
132:
133: treeDidChange();
134: fireCheckTreeChanged(changedNodes);
135: }
136: }
137: }
138:
139: public void keyReleased(KeyEvent e) {
140: }
141:
142: public void keyTyped(KeyEvent e) {
143: }
144:
145: public void mouseClicked(MouseEvent e) {
146: }
147:
148: public void mouseEntered(MouseEvent e) {
149: }
150:
151: public void mouseExited(MouseEvent e) {
152: }
153:
154: public void mousePressed(MouseEvent e) {
155: if (isOverCheckBox(e.getX(), e.getY())) {
156: TreePath path = getPathForLocation(e.getX(), e.getY());
157: fireNodeToggled(path, true);
158:
159: Collection changedNodes = togglePathState(getPathForLocation(
160: e.getX(), e.getY()));
161: treeDidChange();
162: fireNodeToggled(path, false);
163: fireCheckTreeChanged(changedNodes);
164: }
165: }
166:
167: public void mouseReleased(MouseEvent e) {
168: }
169: }
170:
171: //~ Instance fields ----------------------------------------------------------------------------------------------------------
172:
173: private PrivateComponentListener componentListener = new PrivateComponentListener();
174:
175: // ---------------------------------------------------------------------------
176:
177: // --- CheckTreeListener implementation --------------------------------------
178: private Vector checkTreeListeners = new Vector();
179:
180: //~ Constructors -------------------------------------------------------------------------------------------------------------
181:
182: /**
183: * Creates a new instance of JCheckTree
184: */
185: public JCheckTree() {
186: if (!UIUtils.isGTKLookAndFeel()) {
187: setUI(new CheckTreeUI());
188: }
189:
190: setCellRenderer(new CheckTreeCellRenderer());
191: setModel(getDefaultTreeModel());
192: addMouseListener(componentListener);
193: addKeyListener(componentListener);
194: }
195:
196: //~ Methods ------------------------------------------------------------------------------------------------------------------
197:
198: // --- CellTip support -------------------------------------------------------
199: public Point getCellTipLocation() {
200: if (rendererRect == null) {
201: return null;
202: }
203:
204: if (getCellRenderer() instanceof TreeCellRendererPersistent) {
205: return new Point(
206: (rendererRect.getLocation().x + CheckTreeCellRenderer
207: .getCheckBoxDimension().width) - 1,
208: rendererRect.getLocation().y - 1);
209: } else {
210: return super .getCellTipLocation();
211: }
212: }
213:
214: public void addCheckTreeListener(CheckTreeListener listener) {
215: if ((listener != null)
216: && !checkTreeListeners.contains(listener)) {
217: checkTreeListeners.add(listener);
218: }
219: }
220:
221: // ---------------------------------------------------------------------------
222:
223: // --- Static test frame -----------------------------------------------------
224: public static void main(String[] args) {
225: JFrame frame = new JFrame("JCheckTreeTester"); // NOI18N
226:
227: try {
228: UIManager
229: .setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); // NOI18N
230: } catch (Exception e) {
231: }
232:
233: ;
234:
235: JCheckTree checkTree = new JCheckTree();
236: checkTree.addCheckTreeListener(new CheckTreeListener() {
237: public void checkTreeChanged(Collection changedNodes) {
238: System.out.println(changedNodes);
239: }
240:
241: public void checkNodeToggled(TreePath path, boolean before) {
242: System.out.println("Node toggled"); // NOI18N
243: }
244: });
245:
246: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
247: frame.getContentPane().add(new JScrollPane(checkTree));
248: frame.pack();
249: frame.setVisible(true);
250: }
251:
252: public void processMouseEvent(MouseEvent e) {
253: super .processMouseEvent(e);
254: }
255:
256: public void removeCheckTreeListener(CheckTreeListener listener) {
257: checkTreeListeners.remove(listener);
258: }
259:
260: // ---------------------------------------------------------------------------
261:
262: // --- Default model definition ----------------------------------------------
263: protected static TreeModel getDefaultTreeModel() {
264: CheckTreeNode root = new CheckTreeNode("JTree"); // NOI18N
265: CheckTreeNode parent;
266:
267: parent = new CheckTreeNode("colors"); // NOI18N
268: root.add(parent);
269: parent.add(new CheckTreeNode("blue")); // NOI18N
270: parent.add(new CheckTreeNode("violet")); // NOI18N
271: parent.add(new CheckTreeNode("red")); // NOI18N
272: parent.add(new CheckTreeNode("yellow")); // NOI18N
273:
274: parent = new CheckTreeNode("sports"); // NOI18N
275: root.add(parent);
276: parent.add(new CheckTreeNode("basketball")); // NOI18N
277: parent.add(new CheckTreeNode("soccer")); // NOI18N
278: parent.add(new CheckTreeNode("football")); // NOI18N
279: parent.add(new CheckTreeNode("hockey")); // NOI18N
280:
281: parent = new CheckTreeNode("food"); // NOI18N
282: root.add(parent);
283: parent.add(new CheckTreeNode("hot dogs")); // NOI18N
284: parent.add(new CheckTreeNode("pizza")); // NOI18N
285: parent.add(new CheckTreeNode("ravioli")); // NOI18N
286: parent.add(new CheckTreeNode("bananas")); // NOI18N
287:
288: return new DefaultTreeModel(root);
289: }
290:
291: protected void processCellTipMouseMove(MouseEvent e) {
292: // Identify treetable row and column at cursor
293: TreePath currentTreePath = null;
294:
295: try {
296: currentTreePath = getPathForLocation(e.getX(), e.getY()); // workaround for random Issue 113634
297: } catch (Exception ex) {
298: }
299:
300: // Return if cursor isn't at any cell
301: if (currentTreePath == null) {
302: CellTipManager.sharedInstance().setEnabled(false);
303: lastTreePath = currentTreePath;
304:
305: return;
306: }
307:
308: Component cellRenderer;
309: Component cellRendererPersistent;
310: int row = getRowForPath(lastTreePath);
311:
312: TreeCellRenderer treeCellRenderer = getCellRenderer();
313:
314: if (!(treeCellRenderer instanceof TreeCellRendererPersistent)) {
315: return;
316: }
317:
318: cellRenderer = treeCellRenderer.getTreeCellRendererComponent(
319: JCheckTree.this ,
320: currentTreePath.getLastPathComponent(), false,
321: isExpanded(row), getModel().isLeaf(
322: currentTreePath.getLastPathComponent()), row,
323: false);
324: cellRendererPersistent = ((TreeCellRendererPersistent) treeCellRenderer)
325: .getTreeCellRendererComponentPersistent(
326: JCheckTree.this ,
327: currentTreePath.getLastPathComponent(),
328: false,
329: isExpanded(row),
330: getModel().isLeaf(
331: currentTreePath.getLastPathComponent()),
332: row, false);
333:
334: // Return if celltip is not supported for the cell
335: if (cellRenderer == null) {
336: CellTipManager.sharedInstance().setEnabled(false);
337: lastTreePath = currentTreePath;
338:
339: return;
340: }
341:
342: Point cellStart = getPathBounds(currentTreePath).getLocation();
343: rendererRect = new Rectangle(cellStart.x, cellStart.y,
344: cellRenderer.getPreferredSize().width, cellRenderer
345: .getPreferredSize().height + 2);
346:
347: // Reset lastTreePath if over checkbox
348: if (e.getX() <= (rendererRect.x + CheckTreeCellRenderer
349: .getCheckBoxDimension().width)) {
350: lastTreePath = new TreePath(new Object());
351: CellTipManager.sharedInstance().setEnabled(false);
352:
353: return;
354: }
355:
356: // Return if treetable cell is the same as in previous event
357: if (currentTreePath == lastTreePath) {
358: return;
359: }
360:
361: lastTreePath = currentTreePath;
362:
363: if (!rendererRect.contains(e.getPoint())) {
364: CellTipManager.sharedInstance().setEnabled(false);
365:
366: return;
367: }
368:
369: // Return if cell contents is fully visible
370: Rectangle visibleRect = getVisibleRect();
371:
372: if (((rendererRect.x + CheckTreeCellRenderer
373: .getCheckBoxDimension().width) >= visibleRect.x)
374: && ((rendererRect.x + rendererRect.width) <= (visibleRect.x + visibleRect.width))) {
375: CellTipManager.sharedInstance().setEnabled(false);
376:
377: return;
378: }
379:
380: while (cellTip.getComponentCount() > 0) {
381: cellTip.remove(0);
382: }
383:
384: cellTip.add(cellRendererPersistent, BorderLayout.CENTER);
385: cellTip.setPreferredSize(new Dimension(cellRendererPersistent
386: .getPreferredSize().width + 2, getRowHeight() + 2));
387:
388: CellTipManager.sharedInstance().setEnabled(true);
389: }
390:
391: protected boolean shouldShowCellTipAt(Point position) {
392: if (rendererRect == null) {
393: return false;
394: }
395:
396: return position.x > (rendererRect.x + CheckTreeCellRenderer
397: .getCheckBoxDimension().width);
398: }
399:
400: // ---------------------------------------------------------------------------
401:
402: // --- Utility methods -------------------------------------------------------
403: private boolean isOverCheckBox(int x, int y) {
404: TreePath path = getPathForLocation(x, y);
405:
406: if ((path == null)
407: || (path.getPathCount() == 0)
408: || !(path.getLastPathComponent() instanceof CheckTreeNode)
409: || (x > (getPathBounds(path).x + CheckTreeCellRenderer
410: .getCheckBoxDimension().width))) {
411: return false;
412: }
413:
414: return true;
415: }
416:
417: private void fireCheckTreeChanged(Collection changedNodes) {
418: if (changedNodes.size() > 0) {
419: for (int i = 0; i < checkTreeListeners.size(); i++) {
420: ((CheckTreeListener) checkTreeListeners.get(i))
421: .checkTreeChanged(changedNodes);
422: }
423: }
424: }
425:
426: private void fireNodeToggled(TreePath path, boolean before) {
427: for (int i = 0; i < checkTreeListeners.size(); i++) {
428: ((CheckTreeListener) checkTreeListeners.get(i))
429: .checkNodeToggled(path, before);
430: }
431: }
432:
433: private Collection togglePathState(TreePath path) {
434: return ((CheckTreeNode) path.getLastPathComponent())
435: .toggleState();
436: }
437:
438: // ---------------------------------------------------------------------------
439: }
|