changeset 1721:56d0c53547c1

Prevent TreeMapComponent from zooming in to leaf nodes Also prevent leaf nodes from being highlighted on click. When the mouse hovers over an internal (non-leaf) node, the cursor is changed to a zoom icon to indicate that double-clicking this region will zoom the view in. Reviewed-by: omajid, jerboaa Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2015-August/015084.html PR2577
author Andrew Azores <aazores@redhat.com>
date Wed, 12 Aug 2015 16:46:08 -0400
parents 53812b60ea0f
children 601243b928f6
files client/swing/src/main/java/com/redhat/thermostat/client/swing/ThermostatSwingCursors.java vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapComponent.java vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapNode.java vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapNodeTest.java
diffstat 4 files changed, 126 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/swing/src/main/java/com/redhat/thermostat/client/swing/ThermostatSwingCursors.java	Wed Aug 12 16:46:08 2015 -0400
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2015 Red Hat, Inc.
+ *
+ * This file is part of Thermostat.
+ *
+ * Thermostat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * Thermostat is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Thermostat; see the file COPYING.  If not see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Linking this code with other modules is making a combined work
+ * based on this code.  Thus, the terms and conditions of the GNU
+ * General Public License cover the whole combination.
+ *
+ * As a special exception, the copyright holders of this code give
+ * you permission to link this code with independent modules to
+ * produce an executable, regardless of the license terms of these
+ * independent modules, and to copy and distribute the resulting
+ * executable under terms of your choice, provided that you also
+ * meet, for each linked independent module, the terms and conditions
+ * of the license of that module.  An independent module is a module
+ * which is not derived from or based on this code.  If you modify
+ * this code, you may extend this exception to your version of the
+ * library, but you are not obligated to do so.  If you do not wish
+ * to do so, delete this exception statement from your version.
+ */
+
+package com.redhat.thermostat.client.swing;
+
+import com.redhat.thermostat.client.swing.components.FontAwesomeIcon;
+
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.Toolkit;
+
+public final class ThermostatSwingCursors {
+
+    private static final Dimension BEST_CURSOR_DIMENSION = Toolkit.getDefaultToolkit().getBestCursorSize(18, 18);
+    private static final int CURSOR_SIZE = (int) Math.min(BEST_CURSOR_DIMENSION.getWidth(), BEST_CURSOR_DIMENSION.getHeight());
+
+    private ThermostatSwingCursors() {}
+
+    public static Cursor getZoomIconCursor() {
+        char zoomIconId = '\uf00e';
+        Point zoomCursorHotspot = new Point(CURSOR_SIZE / 2, CURSOR_SIZE / 2);
+        String zoomCursorName = "zoom-cursor";
+        return Toolkit.getDefaultToolkit().createCustomCursor(
+                new FontAwesomeIcon(zoomIconId, CURSOR_SIZE).getImage(),
+                zoomCursorHotspot,
+                zoomCursorName
+        );
+    }
+
+}
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapComponent.java	Tue Aug 11 16:44:11 2015 +0200
+++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapComponent.java	Wed Aug 12 16:46:08 2015 -0400
@@ -40,6 +40,7 @@
 import java.awt.Color;
 import java.awt.Component;
 import java.awt.Container;
+import java.awt.Cursor;
 import java.awt.Dimension;
 import java.awt.Font;
 import java.awt.Graphics;
@@ -65,6 +66,7 @@
 import javax.swing.border.EtchedBorder;
 import javax.swing.border.LineBorder;
 
+import com.redhat.thermostat.client.swing.ThermostatSwingCursors;
 import com.redhat.thermostat.vm.heap.analysis.common.ObjectHistogram;
 
 /**
@@ -584,6 +586,7 @@
             super();
             thisComponent = this;
             addClickListener(this);
+            addMouseListener(this);
         }
 
         @Override
@@ -642,7 +645,9 @@
                     // two middle click: zoom full
                     if (e.getClickCount() == 2) {
                         if (SwingUtilities.isLeftMouseButton(e)) {
-                            zoomIn(getNode());
+                            if (!getNode().isLeaf()) {
+                                zoomIn(getNode());
+                            }
                         } else if (SwingUtilities.isRightMouseButton(e)) {
                             zoomOut();
                         } else {
@@ -655,16 +660,57 @@
         }
 
         /**
+         * Add a mouse motion listener to this component. This allows for the mouse cursor to be changed into a
+         * magnifying glass icon when the cursor enters a zoomable component, and back to a default cursor when it
+         * exits a zoomable component.
+         * @param component the component which will have the mouse motion listener.
+         */
+        private void addMouseListener(final JComponent component) {
+            MouseListener listener = new MouseAdapter() {
+                @Override
+                public void mouseEntered(MouseEvent e) {
+                    if (getNode().isLeaf()) {
+                        setDefaultCursor();
+                    } else {
+                        setZoomableCursor();
+                    }
+                }
+
+                @Override
+                public void mouseExited(MouseEvent e) {
+                    if (!getNode().isLeaf()) {
+                        setDefaultCursor();
+                    } else {
+                        setZoomableCursor();
+                    }
+                }
+
+                private void setZoomableCursor() {
+                    component.setCursor(ThermostatSwingCursors.getZoomIconCursor());
+                }
+
+                private void setDefaultCursor() {
+                    component.setCursor(Cursor.getDefaultCursor());
+                }
+            };
+            component.addMouseListener(listener);
+        }
+
+        /**
          * This method gives a darker color to this component and restore the
          * original color to the last selected component.
          */
         private void selectComp() {
             if (lastClicked != null) {
-                lastClicked.setColor(lastClicked.getColor().brighter());
+                if (!lastClicked.getNode().isLeaf()) {
+                    lastClicked.setColor(lastClicked.getColor().brighter());
+                }
                 lastClicked.repaint();
             } 
             lastClicked = thisComponent;
-            setColor(getColor().darker());
+            if (!getNode().isLeaf()) {
+                setColor(getColor().darker());
+            }
             repaint();
         }
     }
--- a/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapNode.java	Tue Aug 11 16:44:11 2015 +0200
+++ b/vm-heap-analysis/client-swing/src/main/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapNode.java	Wed Aug 12 16:46:08 2015 -0400
@@ -225,6 +225,10 @@
     public List<TreeMapNode> getChildren() {
         return this.children;
     }
+
+    public boolean isLeaf() {
+        return getChildren().isEmpty();
+    }
     
     /**
      * Set as children list of this object the list given in input.
--- a/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapNodeTest.java	Tue Aug 11 16:44:11 2015 +0200
+++ b/vm-heap-analysis/client-swing/src/test/java/com/redhat/thermostat/vm/heap/analysis/client/swing/internal/TreeMapNodeTest.java	Wed Aug 12 16:46:08 2015 -0400
@@ -45,6 +45,7 @@
 import java.awt.Color;
 import java.awt.geom.Rectangle2D;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -97,6 +98,14 @@
         assertTrue(1 == node.getChildren().size());
     }
 
+    @Test
+    public final void testIsLeaf() {
+        assertTrue(node.isLeaf());
+        node.addChild(new TreeMapNode(null, 1));
+        assertFalse(node.isLeaf());
+        node.setChildren(Collections.<TreeMapNode>emptyList());
+        assertTrue(node.isLeaf());
+    }
 
     @Test
     public final void testGetAddInfo() {