/* * Copyright (c) 2005-2016 Laf-Widget Kirill Grouchnikov. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * o Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * o 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. * * o Neither the name of Laf-Widget Kirill Grouchnikov 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.pushingpixels.lafwidget.desktop; import java.awt.*; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.HashMap; import java.util.Map; import javax.swing.*; import javax.swing.JInternalFrame.JDesktopIcon; import javax.swing.border.Border; import javax.swing.event.MouseInputAdapter; import javax.swing.plaf.basic.BasicInternalFrameUI; import org.pushingpixels.lafwidget.*; import org.pushingpixels.lafwidget.contrib.intellij.UIUtil; import org.pushingpixels.lafwidget.icon.HiDpiAwareIcon; /** * Adds internal frame thumbnail preview on desktop icon mouse hover. * * @author Kirill Grouchnikov */ public class DesktopIconHoverPreviewWidget extends LafWidgetAdapter<JDesktopIcon> { /** * The component that initiates the desktop icon preview (when the mouse * hover above it). */ protected JComponent compToHover; /** * Listens on the changes to the ancestor. */ protected PropertyChangeListener internalFramePropertyListener; /** * Snapshot map. */ private BufferedImage snapshot; /** * Preview window (activated on hover). */ private JWindow previewWindow; /** * Indicates whether the corresponding desktop icon is dragged. */ private boolean isInDrag; /** * Mouse handler for the {@link #compToHover}. */ protected TitleMouseHandler titleMouseHandler; /** * Mouse handler for showing / hiding the preview window. * * @author Kirill Grouchnikov. */ protected class TitleMouseHandler extends MouseInputAdapter { @Override public void mouseEntered(MouseEvent e) { if (DesktopIconHoverPreviewWidget.this.isInDrag) return; BufferedImage previewImage = snapshot; if (previewImage != null) { int scaleFactor = UIUtil.getScaleFactor(); DesktopIconHoverPreviewWidget.this.previewWindow.getContentPane().removeAll(); JLabel previewLabel = new JLabel(new HiDpiAwareIcon(previewImage)); DesktopIconHoverPreviewWidget.this.previewWindow.getContentPane().add(previewLabel, BorderLayout.CENTER); DesktopIconHoverPreviewWidget.this.previewWindow.setSize( previewImage.getWidth() / scaleFactor, previewImage.getHeight() / scaleFactor); DesktopIconHoverPreviewWidget.this.syncPreviewWindow(true); DesktopIconHoverPreviewWidget.this.previewWindow.setVisible(true); } } @Override public void mouseExited(MouseEvent e) { DesktopIconHoverPreviewWidget.this.isInDrag = false; DesktopIconHoverPreviewWidget.this.previewWindow.dispose(); } @Override public void mousePressed(MouseEvent e) { DesktopIconHoverPreviewWidget.this.previewWindow.dispose(); } @Override public void mouseReleased(MouseEvent e) { DesktopIconHoverPreviewWidget.this.isInDrag = false; DesktopIconHoverPreviewWidget.this.syncPreviewWindow(true); DesktopIconHoverPreviewWidget.this.previewWindow.setVisible(true); } @Override public void mouseDragged(MouseEvent e) { DesktopIconHoverPreviewWidget.this.isInDrag = true; if (DesktopIconHoverPreviewWidget.this.previewWindow.isVisible()) { DesktopIconHoverPreviewWidget.this.syncPreviewWindow(false); DesktopIconHoverPreviewWidget.this.previewWindow.dispose(); } } } /* * (non-Javadoc) * * @see org.pushingpixels.lafwidget.LafWidgetAdapter#installComponents() */ @Override public void installComponents() { this.previewWindow = new JWindow(); this.previewWindow.getContentPane().setLayout(new BorderLayout()); } /* * (non-Javadoc) * * @see org.pushingpixels.lafwidget.LafWidgetAdapter#installListeners() */ @Override public void installListeners() { this.internalFramePropertyListener = (PropertyChangeEvent evt) -> { if ("ancestor".equals(evt.getPropertyName())) { updateSnapshot(jcomp.getInternalFrame()); } }; jcomp.getInternalFrame().addPropertyChangeListener(this.internalFramePropertyListener); this.titleMouseHandler = new TitleMouseHandler(); LafWidgetSupport lafSupport = LafWidgetRepository.getRepository().getLafSupport(); this.compToHover = lafSupport.getComponentForHover(jcomp); if (this.compToHover != null) { this.compToHover.addMouseMotionListener(this.titleMouseHandler); this.compToHover.addMouseListener(this.titleMouseHandler); } } /* * (non-Javadoc) * * @see org.pushingpixels.lafwidget.LafWidgetAdapter#uninstallListeners() */ @Override public void uninstallListeners() { jcomp.getInternalFrame().removePropertyChangeListener(this.internalFramePropertyListener); this.internalFramePropertyListener = null; if (this.compToHover != null) { this.compToHover.removeMouseMotionListener(this.titleMouseHandler); this.compToHover.removeMouseListener(this.titleMouseHandler); } this.titleMouseHandler = null; } /** * Synchronizes the preview window. * * @param toShow * Indication whether the preview window is shown. */ private void syncPreviewWindow(boolean toShow) { if (toShow) { int x = jcomp.getLocationOnScreen().x; int y = jcomp.getLocationOnScreen().y; this.previewWindow.setLocation(x, y - this.previewWindow.getHeight()); } } /** * Updates the snapshot of the specified internal frame. * * @param frame * Internal frame. */ private void updateSnapshot(JInternalFrame frame) { if (!frame.isShowing()) return; // Draw the current state of the internal frame to a // temp image (w/o border and decorations). It would be nice // to use Robot, but this frame may be partially obscured, // so we take our chances that the frame will be properly // drawn by the user code. int frameWidth = frame.getWidth(); int frameHeight = frame.getHeight(); int dx = 0; int dy = 0; // Now we need to remove the border and the title pane :) Border internalFrameBorder = UIManager.getBorder("InternalFrame.border"); Insets borderInsets = internalFrameBorder.getBorderInsets(frame); dx += borderInsets.left; dy += borderInsets.top; frameWidth -= (borderInsets.left + borderInsets.right); frameHeight -= (borderInsets.top + borderInsets.bottom); BasicInternalFrameUI frameUI = (BasicInternalFrameUI) frame.getUI(); JComponent frameTitlePane = frameUI.getNorthPane(); if (frameTitlePane != null) { dy += frameTitlePane.getHeight(); frameHeight -= frameTitlePane.getHeight(); } // fix for defect 112 - checking frame height and width if ((frameWidth > 0) && (frameHeight > 0)) { // draw frame (note the canvas translation) BufferedImage tempCanvas = new BufferedImage(frameWidth, frameHeight, BufferedImage.TYPE_INT_ARGB); Graphics tempCanvasGraphics = tempCanvas.getGraphics(); tempCanvasGraphics.translate(-dx, -dy); Map<Component, Boolean> dbSnapshot = new HashMap<Component, Boolean>(); LafWidgetUtilities.makePreviewable(frame, dbSnapshot); frame.paint(tempCanvasGraphics); LafWidgetUtilities.restorePreviewable(frame, dbSnapshot); int maxWidth = UIManager.getInt("DesktopIcon.width"); int maxHeight = maxWidth; // check if need to scale down double coef = Math.min((double) maxWidth / (double) frameWidth, (double) maxHeight / (double) frameHeight); if (coef < 1.0) { int sdWidth = (int) (coef * frameWidth); BufferedImage scaledDown = LafWidgetUtilities.createThumbnail(tempCanvas, sdWidth); snapshot = scaledDown; } else { snapshot = tempCanvas; } } } /** * Returns the snapshot of the specified internal frame. * * @param frame * Internal frame. * @return The snapshot of the specified internal frame. */ public synchronized BufferedImage getSnapshot(JInternalFrame frame) { return this.snapshot; } /* * (non-Javadoc) * * @see org.pushingpixels.lafwidget.LafWidget#requiresCustomLafSupport() */ public boolean requiresCustomLafSupport() { return false; } }