/*
* Copyright (C) 2016 by Array Systems Computing Inc. http://www.array.ca
*
* This program 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 3 of the License, or (at your option)
* any later version.
* This program 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 this program; if not, see http://www.gnu.org/licenses/
*/
package org.esa.snap.rcp.quicklooks;
import com.bc.ceres.core.ProgressMonitor;
import net.coobird.thumbnailator.makers.FixedSizeThumbnailMaker;
import org.esa.snap.core.datamodel.quicklooks.Thumbnail;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
/**
* Displays a panel of thumbnails
*/
public class ThumbnailPanel extends JPanel {
private final static int imgWidth = 200;
private final static int imgHeight = 200;
private final static int margin = 6;
private final static BasicStroke thickStroke = new BasicStroke(5);
private final static String vkControl = "VK_CONTROL";
private enum SelectionMode {CHECK, RECT}
private final boolean multiRow;
private SelectionMode selectionMode;
private List<ThumbnailDrawing> selection;
private boolean ctrlPressed;
public ThumbnailPanel(final boolean multiRow) {
super(new FlowLayout(FlowLayout.LEADING));
this.multiRow = multiRow;
this.selectionMode = SelectionMode.RECT;
this.selection = new ArrayList<>();
final DragScrollListener dragScrollListener = new DragScrollListener(this);
dragScrollListener.setDraggableElements(DragScrollListener.DRAGABLE_VERTICAL_SCROLL_BAR);
addMouseListener(dragScrollListener);
addMouseMotionListener(dragScrollListener);
setKeyBindings();
}
private void setKeyBindings() {
final InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_CONTROL, KeyEvent.CTRL_DOWN_MASK), vkControl+"DOWN");
inputMap.put(KeyStroke.getKeyStroke("released CONTROL"), vkControl+"UP");
final ActionMap actionMap = getActionMap();
actionMap.put(vkControl+"DOWN", new KeyAction(vkControl+"DOWN"));
actionMap.put(vkControl+"UP", new KeyAction(vkControl+"UP"));
}
private class KeyAction extends AbstractAction {
public KeyAction(String actionCommand) {
putValue(ACTION_COMMAND_KEY, actionCommand);
}
@Override
public void actionPerformed(ActionEvent e) {
final String cmd = e.getActionCommand();
ctrlPressed = cmd.equals(vkControl+"DOWN");
}
}
public void update(final Thumbnail[] imageList) {
this.removeAll();
final Insets insets = getInsets();
final int width = getWidth() - (insets.left + insets.right);
if (imageList.length == 0) {
setPreferredSize(new Dimension(width, imgHeight + 2 * margin));
JLabel label = new JLabel("");
this.add(label);
} else {
if (multiRow) {
int numImages = 1;
int effectiveImageWidth = imgWidth * numImages + margin;
int numCol = Math.max(width / effectiveImageWidth, 1);
int numRow = (int)Math.ceil(imageList.length / (double)numCol);
int preferredWidth = effectiveImageWidth * numCol + margin;
int preferredHeight = (imgHeight + margin) * numRow + margin;
setPreferredSize(new Dimension(preferredWidth, preferredHeight));
}
for (Thumbnail thumbnail : imageList) {
this.add(new ThumbnailDrawing(this, thumbnail));
}
}
updateUI();
}
private void setSelection(final ThumbnailDrawing item) {
if(selection.contains(item)) {
selection.remove(item);
} else {
if(ctrlPressed) {
selection.add(item);
} else {
selection.clear();
selection.add(item);
}
}
onSelectionChanged();
}
public void onSelectionChanged() {
}
public void onOpenAction() {
}
public void selectAll() {
selection.clear();
for(Component component : this.getComponents()) {
selection.add((ThumbnailDrawing)component);
}
repaint();
}
public void clearSelection() {
selection.clear();
repaint();
}
public ThumbnailDrawing[] getSelection() {
return selection.toArray(new ThumbnailDrawing[selection.size()]);
}
private boolean isSelected(final ThumbnailDrawing item) {
return selection.contains(item);
}
public class ThumbnailDrawing extends JLabel implements MouseListener, Thumbnail.ThumbnailListener {
private final ThumbnailPanel parent;
private final Thumbnail thumbnail;
public ThumbnailDrawing(final ThumbnailPanel parent, final Thumbnail thumbnail) {
this.parent = parent;
this.thumbnail = thumbnail;
this.thumbnail.addListener(this);
setPreferredSize(new Dimension(imgWidth, imgHeight));
setToolTipText("");
addMouseListener(this);
}
public Thumbnail getThumbnail() {
return thumbnail;
}
public void notifyImageUpdated(Thumbnail thumbnail) {
parent.repaint();
}
@Override
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
final Graphics2D g = (Graphics2D) graphics;
g.setColor(Color.BLACK);
g.fillRect(0,0, imgWidth, imgHeight);
if(thumbnail.hasImage() || thumbnail.hasCachedImage()) {
drawIcon(g, thumbnail.getImage(ProgressMonitor.NULL));
} else {
drawIcon(g, null);
}
if (isSelected(this) && selectionMode == SelectionMode.RECT) {
drawSelected(g);
}
}
private void drawIcon(Graphics2D g, BufferedImage icon) {
if (icon != null) {
BufferedImage img = new FixedSizeThumbnailMaker()
.size(imgWidth, imgHeight)
.keepAspectRatio(true)
.fitWithinDimensions(true)
.make(icon);
int xOff = (imgWidth - img.getWidth())/2;
int yOff = (imgHeight - img.getHeight())/2;
g.drawImage(img, xOff, yOff, img.getWidth(), img.getHeight(), null);
} else {
// Draw cross to indicate missing image
g.setColor(Color.DARK_GRAY);
g.setStroke(thickStroke);
g.drawLine(0, 0, 0 + imgWidth, imgHeight);
g.drawLine(0 + imgWidth, 0, 0, imgHeight);
g.drawRect(0, 0, imgWidth-1, imgHeight-1);
}
}
private void drawSelected(Graphics2D g) {
g.setColor(new Color(0,100,255));
g.setStroke(thickStroke);
g.drawRect(0, 0, imgWidth, imgHeight);
for(int i=0; i <= 20; ++i) {
int alpha = 40-(i*2);
g.setColor(new Color(0,100,255,alpha));
g.drawRoundRect(i, i, imgWidth-i-i, imgHeight-i-i, 25, 25);
}
}
@Override
public Point getToolTipLocation(MouseEvent e) {
return new Point(-700, 0);
}
@Override
public JToolTip createToolTip() {
if(!thumbnail.hasImage() || !thumbnail.hasCachedImage()) {
return super.createToolTip();
}
final BufferedImage thumbnailImage = thumbnail.getImage(ProgressMonitor.NULL);
BufferedImage img = new FixedSizeThumbnailMaker()
.size(imgWidth*3, imgHeight*3)
.keepAspectRatio(true)
.fitWithinDimensions(true)
.make(thumbnailImage);
final JToolTip toolTip = new JToolTip() {
{
setLayout(new BorderLayout());
add(new JLabel(new ImageIcon(img)));
}
public Dimension getPreferredSize() {
return new Dimension(img.getWidth(), img.getHeight());
}
};
return toolTip;
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2) {
onOpenAction();
} else if (e.getButton() == MouseEvent.BUTTON1) {
setSelection(this);
parent.repaint();
} else if (e.getButton() == MouseEvent.BUTTON3) {
}
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
}