From a7dbc4cf6eaaddb0fcb07dfc85eb851beefe314d Mon Sep 17 00:00:00 2001 From: Chrriis Date: Sat, 30 Jun 2012 15:58:12 +0200 Subject: [PATCH] [#1249] Add breakpoint capability to jOOQ Console - data tips on tree. --- .../jooq/debug/console/misc/TreeDataTip.java | 206 ++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 jOOQ-console/src/main/java/org/jooq/debug/console/misc/TreeDataTip.java diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/misc/TreeDataTip.java b/jOOQ-console/src/main/java/org/jooq/debug/console/misc/TreeDataTip.java new file mode 100644 index 0000000000..f4a3577ccd --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/misc/TreeDataTip.java @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug.console.misc; + +import java.awt.AWTEvent; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.MouseEvent; + +import javax.swing.CellRendererPane; +import javax.swing.JComponent; +import javax.swing.JToolTip; +import javax.swing.JTree; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.SwingUtilities; +import javax.swing.event.MouseInputAdapter; +import javax.swing.event.MouseInputListener; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; + +public class TreeDataTip { + + private TreeDataTip() {} + + public static void activate(final JTree tree) { + MouseInputListener dataTipListener = new MouseInputAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + showTip(e.getPoint()); + } + @Override + public void mouseMoved(MouseEvent e) { + showTip(e.getPoint()); + } + @Override + public void mouseExited(MouseEvent e) { + hideTip(); + } + @Override + public void mousePressed(MouseEvent e) { + hideTip(); + } + private Popup popup; + private int currentRow = -1; + private void hideTip() { + if(popup != null) { + popup.hide(); + popup = null; + } + } + private void showTip(Point mousePosition) { + int row = tree.getRowForLocation(mousePosition.x, mousePosition.y); + if(row != currentRow) { + hideTip(); + currentRow = row; + if(row >= 0) { + TreePath treePath = tree.getPathForRow(row); + TreeCellRenderer renderer = tree.getCellRenderer(); + boolean isSelected = false;//breakpointTree.isPathSelected(treePath); + boolean isExpanded = tree.isExpanded(treePath); + boolean hasFocus = false;//tree.hasFocus() && rowIndex == tree.getLeadSelectionRow(); + Object item = treePath.getLastPathComponent(); + boolean isLeaf = tree.getModel().isLeaf(item); + final JComponent rendererComponent = (JComponent)renderer.getTreeCellRendererComponent(tree, item, isSelected, isExpanded, isLeaf, row, hasFocus); + rendererComponent.setFont(tree.getFont()); + Rectangle visRect = tree.getVisibleRect(); + Rectangle cellBounds = tree.getPathBounds(treePath); + Rectangle visibleCellRectangle = cellBounds.intersection(visRect); + if (!visibleCellRectangle.contains(mousePosition)) { + return; + } + Dimension rendCompDim = rendererComponent.getMinimumSize(); + Rectangle rendCompBounds = new Rectangle(cellBounds.getLocation(), rendCompDim); + visRect.x -= 1000; + visRect.width += 1000; + visRect.y -= 1000; + visRect.height += 2000; + // We do not care about vertical visibility. + rendCompBounds.height = 1; + if(cellBounds.contains(rendCompBounds) && visRect.contains(rendCompBounds)) { + return; + } + Dimension preferredSize = rendererComponent.getPreferredSize(); + Point tipPosition = cellBounds.getLocation(); + int width = Math.max(cellBounds.width, preferredSize.width); + int height = cellBounds.height;//Math.max(cellBounds.height, preferredSize.height); // We don't care about vertical visibility + Dimension tipDimension = new Dimension(width, height); + final Color backgroundColor = tree.getBackground(); + class DataTipComponent extends JToolTip { + private CellRendererPane rendererPane = new CellRendererPane(); + private boolean isHeavyWeight; + public DataTipComponent() { + add(rendererPane); + setFocusable(false); + setBorder(null); + enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK); + } + @Override + public void updateUI() { + } + @Override + public boolean contains(int x, int y) { + return isHeavyWeight; + } + @Override + public void paintComponent(Graphics g) { + // Leave the component's opacity settings as is, just paint the background myself. + // This seems to be the only viable solution: DefaultTableCellRenderer overrides isOpaque() and returns + // true only if renderer color does not equal parent color. The problem is that rendererPane.paintComponent() + // re-parents the renderer. + g.setColor(backgroundColor); + int width = getWidth(); + int height = getHeight(); + g.fillRect(0, 0, width, height); + g.setColor(Color.black); + g.drawRect(0, 0, width - 1, height - 1); + g.setClip(1, 1, width - 2, height - 2); + int row = currentRow; + TreePath treePath = tree.getPathForRow(row); + boolean isSelected = false;//breakpointTree.isPathSelected(treePath); + boolean isExpanded = tree.isExpanded(treePath); + boolean hasFocus = false;//tree.hasFocus() && rowIndex == tree.getLeadSelectionRow(); + Object item = treePath.getLastPathComponent(); + boolean isLeaf = tree.getModel().isLeaf(item); + TreeCellRenderer renderer = tree.getCellRenderer(); + JComponent rendererComponent = (JComponent)renderer.getTreeCellRendererComponent(tree, item, isSelected, isExpanded, isLeaf, row, hasFocus); + rendererComponent.setFont(tree.getFont()); + rendererPane.paintComponent(g, rendererComponent, this, 1, 1, width - 1, height - 1); + } + public void setHeavyWeight(boolean isHeavyWeight) { + this.isHeavyWeight = isHeavyWeight; + } + } + DataTipComponent dataTipComponent = new DataTipComponent(); + Dimension tipDimensionClipped = new Dimension(tipDimension.width, tipDimension.height); + Window windowAncestor = SwingUtilities.getWindowAncestor(tree); + GraphicsConfiguration gc = windowAncestor.getGraphicsConfiguration(); + Rectangle screenBounds = gc.getBounds(); + // Chrriis: we add the border around, not in + Point tipScreenPosition = new Point(tipPosition.x, tipPosition.y); + SwingUtilities.convertPointToScreen(tipScreenPosition, tree); + Point tipPositionClipped = new Point(); + tipPositionClipped.x = Math.max(tipScreenPosition.x - 1, screenBounds.x); + tipPositionClipped.y = Math.max(tipScreenPosition.y - 1, screenBounds.y); + // Chrriis: we add the border around, not in + tipDimensionClipped.width = Math.min(screenBounds.x + screenBounds.width - tipPositionClipped.x, tipDimensionClipped.width + 2); + tipDimensionClipped.height = Math.min(screenBounds.y + screenBounds.height - tipPositionClipped.y, tipDimensionClipped.height + 2); + SwingUtilities.convertPointFromScreen(tipPositionClipped, tree); + dataTipComponent.setPreferredSize(tipDimensionClipped); + SwingUtilities.convertPointToScreen(tipPosition, tree); + PopupFactory popupFactory = PopupFactory.getSharedInstance(); + popup = popupFactory.getPopup(tree, dataTipComponent, tipPosition.x - 1, tipPosition.y - 1); + popup.show(); + Window componentWindow = SwingUtilities.windowForComponent(tree); + Window tipWindow = SwingUtilities.windowForComponent(dataTipComponent); + boolean isHeavyWeight = tipWindow != null && tipWindow != componentWindow; + dataTipComponent.setHeavyWeight(isHeavyWeight); + } + } + } + }; + tree.addMouseListener(dataTipListener); + tree.addMouseMotionListener(dataTipListener); + } + +}