/*
* Copyright (C) 2015 Patryk Strach
*
* This file is part of Virtual Slide Viewer.
*
* Virtual Slide Viewer 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.
*
* Virtual Slide Viewer 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 Virtual Slide Viewer.
* If not, see <http://www.gnu.org/licenses/>.
*/
package virtualslideviewer.ui.imagelist.view;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.Box;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import virtualslideviewer.core.VirtualSlide;
import virtualslideviewer.core.VirtualSlideImage;
import virtualslideviewer.ui.imagelist.ImageListView;
/**
* A Swing panel which displays the thumbnails of virtual slide's images along with their names.
*/
public class ImageListSwingPanel extends JScrollPane implements ImageListView
{
private static final long serialVersionUID = -4643457695900323340L;
private static final int RENAME_IMAGE_KEY = KeyEvent.VK_F2;
private static final int SHOW_IMAGE_KEY = KeyEvent.VK_ENTER;
private static final int REMOVE_IMAGE_KEY = KeyEvent.VK_DELETE;
protected JTable mThumbnailTable = new JTable();
protected JPopupMenu mContextMenu = new JPopupMenu();
protected List<Listener> mListeners = new ArrayList<>();
/**
* @param maxThumbnailSize The size of area to which the thumbnail of a single image will be fit.
*/
public ImageListSwingPanel(Dimension maxThumbnailSize)
{
mThumbnailTable.setOpaque(false);
mThumbnailTable.setShowGrid(false);
mThumbnailTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
mThumbnailTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mThumbnailTable.setRowMargin(20);
// Some of JTable's embedded key bindings conflict with our needs.
mThumbnailTable.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(RENAME_IMAGE_KEY, 0), "none");
mThumbnailTable.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(SHOW_IMAGE_KEY, 0), "none");
mThumbnailTable.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(REMOVE_IMAGE_KEY, 0), "none");
mThumbnailTable.addKeyListener(new ThumbnailTableKeyHandler());
mThumbnailTable.addMouseListener(new ThumbnailTableMouseListener());
mThumbnailTable.setDefaultRenderer(VirtualSlideImage.class, new ThumbnailTableCellRenderer(maxThumbnailSize));
mThumbnailTable.setDefaultEditor( VirtualSlideImage.class, new ThumbnailNameEditor()
{
private static final long serialVersionUID = -2457231613728535089L;
@Override
public boolean stopCellEditing()
{
mListeners.forEach(l -> l.onImageNameConfirmed());
return false;
}
});
layoutComponents();
initializeContextMenu();
super.getVerticalScrollBar().setUnitIncrement(maxThumbnailSize.height);
}
private void layoutComponents()
{
JPanel mainPanel = new JPanel(new GridBagLayout());
GridBagConstraints thumbnailTableConstraints = new GridBagConstraints();
thumbnailTableConstraints.gridx = 0;
thumbnailTableConstraints.gridy = 0;
thumbnailTableConstraints.weighty = 0.0;
mainPanel.add(mThumbnailTable, thumbnailTableConstraints);
GridBagConstraints bottomGlueConstraints = new GridBagConstraints();
bottomGlueConstraints.gridx = 0;
bottomGlueConstraints.gridy = 1;
bottomGlueConstraints.weighty = 1.0;
mainPanel.add(Box.createGlue(), bottomGlueConstraints);
super.setViewportView(mainPanel);
}
private void initializeContextMenu()
{
JMenuItem showImageMenu = new JMenuItem("Show");
JMenuItem renameImageMenu = new JMenuItem("Rename");
JMenuItem removeImageMenu = new JMenuItem("Remove");
// Mainly in order to show the shortcuts to a user.
showImageMenu. setAccelerator(KeyStroke.getKeyStroke(SHOW_IMAGE_KEY, 0));
renameImageMenu.setAccelerator(KeyStroke.getKeyStroke(RENAME_IMAGE_KEY, 0));
removeImageMenu.setAccelerator(KeyStroke.getKeyStroke(REMOVE_IMAGE_KEY, 0));
showImageMenu. addActionListener((x) -> mListeners.forEach(l -> l.onImageShow()));
renameImageMenu.addActionListener((x) -> mListeners.forEach(l -> l.onImageRename()));
removeImageMenu.addActionListener((x) -> mListeners.forEach(l -> l.onImageRemove()));
mContextMenu.add(showImageMenu);
mContextMenu.add(renameImageMenu);
mContextMenu.add(removeImageMenu);
}
@Override
public void addListener(Listener listener)
{
if(listener == null)
throw new IllegalArgumentException("listener cannot be null.");
mListeners.add(listener);
}
@Override
public void setSourceVirtualSlide(VirtualSlide virtualSlide)
{
if(virtualSlide == null)
throw new IllegalArgumentException("virtualSlide cannot be null.");
mThumbnailTable.setModel(new VirtualSlideImagesTableModel(virtualSlide));
}
@Override
public int getSelectedImageIndex()
{
return mThumbnailTable.getSelectedRow();
}
@Override
public void setSelectedImageIndex(int index)
{
selectRow(index);
}
@Override
public void showNameEditor(int imageToRenameIndex)
{
mThumbnailTable.editCellAt(imageToRenameIndex, 0);
}
@Override
public int getEditedImageIndex()
{
return mThumbnailTable.getEditingRow();
}
@Override
public String getNameEditorValue()
{
return (String)mThumbnailTable.getCellEditor().getCellEditorValue();
}
@Override
public void closeNameEditor()
{
mThumbnailTable.editingStopped(null);
}
@Override
public void displayErrorMessage(String error)
{
JOptionPane.showMessageDialog(this, error);
}
/**
* Returns a JTable used internally to display thumbnails of the images.
*/
public JTable getThumbnailTable()
{
return mThumbnailTable;
}
/**
* Returns a context menu which is displayed to a user after right clicking on some image entry.
*/
public JPopupMenu getContextMenu()
{
return mContextMenu;
}
private void selectRow(int rowIndex)
{
mThumbnailTable.requestFocusInWindow();
mThumbnailTable.changeSelection(rowIndex, 0, false, false);
}
private class ThumbnailTableMouseListener extends MouseAdapter
{
@Override
public void mousePressed(MouseEvent e)
{
mListeners.forEach(l -> l.onImageNameConfirmed());
if(SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2)
{
mListeners.forEach(l -> l.onImageShow());
}
if(e.isPopupTrigger())
{
mContextMenu.show(mThumbnailTable, e.getX(), e.getY());
}
// Right clicking should select images too
selectRow(mThumbnailTable.rowAtPoint(e.getPoint()));
}
@Override
public void mouseReleased(MouseEvent e)
{
if(e.isPopupTrigger())
{
mContextMenu.show(mThumbnailTable, e.getX(), e.getY());
}
}
}
private class ThumbnailTableKeyHandler extends KeyAdapter
{
@Override
public void keyPressed(KeyEvent e)
{
switch(e.getKeyCode())
{
case SHOW_IMAGE_KEY:
mListeners.forEach(l -> l.onImageShow());
break;
case RENAME_IMAGE_KEY:
mListeners.forEach(l -> l.onImageRename());
break;
case REMOVE_IMAGE_KEY:
mListeners.forEach(l -> l.onImageRemove());
break;
}
}
}
}