/* * Copyright 2010, 2011 Institut Pasteur. * Copyright 2012, 2013 Nicolas Hervé. * * This file is part of NHerve Main Toolbox, which is an ICY plugin. * * NHerve Main Toolbox 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. * * NHerve Main Toolbox 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 NHerve Main Toolbox. If not, see <http://www.gnu.org/licenses/>. */ package plugins.nherve.toolbox.genericgrid; import icy.gui.util.GuiUtil; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.event.AdjustmentEvent; import java.awt.event.AdjustmentListener; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.ScrollPaneConstants; import javax.swing.Scrollable; import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class GridPanel<T extends GridCell> extends JPanel implements ComponentListener, ChangeListener, ItemListener, AdjustmentListener, MouseWheelListener { private class InternalGrid extends JPanel implements Scrollable, MouseWheelListener { private static final long serialVersionUID = -4811144385819002930L; public InternalGrid() { super(); addMouseWheelListener(this); } @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return pageLength; } @Override public boolean getScrollableTracksViewportHeight() { return false; } @Override public boolean getScrollableTracksViewportWidth() { return false; } @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return realFullHeight / 2; } @Override public void paintComponent(Graphics g) { super.paintComponent(g); if (cellsJustSet) { updateLbNbCells(); cellsJustSet = false; } } @Override public void mouseWheelMoved(MouseWheelEvent e) { if (e.isControlDown()) { GridPanel.this.mouseWheelMoved(e); } else { JScrollBar bar = GridPanel.this.scroll.getVerticalScrollBar(); if (bar != null) { bar.setValue(bar.getValue() + e.getWheelRotation() * realFullHeight / 2); e.consume(); } } } } public static final int DEFAULT_CELL_LENGTH = 150; public static final int DEFAULT_CELL_SPACING = 10; public static final int DEFAULT_ZOOM_SMOOTH = 100; public static final int DEFAULT_MAX_ZOOM = 200; public static final double DEFAULT_MAX_ZOOM_FACTOR = DEFAULT_MAX_ZOOM / DEFAULT_ZOOM_SMOOTH; public static final int DEFAULT_MIN_ZOOM = 25; private static final String EMPTY_LABEL = "nothing to display"; private static final long serialVersionUID = -3551019605947008673L; private JCheckBox cbZoomOnFocus; private JCheckBox cbDisplayName; private int cellWidth; private int cellHeight; private Font font; private GridCellCollection<T> cells; private boolean cellsJustSet; private int cellSpacing; private InternalGrid grid; private JLabel lbNbCells; private int pageLength; private int realFullWidth; private int realFullHeight; private JScrollPane scroll; private JSlider slZoom; private int smoothZoom; private double zoomFactor; private boolean zoomOnFocus; private boolean displayName; private WaitingAnimation wa; public GridPanel() { this(true); } public GridPanel(boolean zoomOnFocus) { this(zoomOnFocus, true); } public GridPanel(boolean zoomOnFocus, boolean showBottomLine) { this(DEFAULT_CELL_LENGTH, DEFAULT_CELL_SPACING, zoomOnFocus, showBottomLine, DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM, DEFAULT_ZOOM_SMOOTH); } public GridPanel(int cellLength, int cellSpacing, boolean zoomOnFocus, boolean showBottomLine) { this(cellLength, cellSpacing, zoomOnFocus, showBottomLine, DEFAULT_MIN_ZOOM, DEFAULT_MAX_ZOOM, DEFAULT_ZOOM_SMOOTH); } public GridPanel(int cellLength, int cellSpacing, boolean zoomOnFocus, boolean showBottomLine, int minZoom, int maxZoom, int smoothZoom) { this(cellLength, cellLength, cellSpacing, zoomOnFocus, showBottomLine, showBottomLine, minZoom, maxZoom, smoothZoom); } public GridPanel(int cellWidth, int cellHeight, int cellSpacing, boolean zoomOnFocus, boolean displayName, boolean showBottomLine, int minZoom, int maxZoom, int smoothZoom) { super(); zoomFactor = 1.0; slZoom = null; lbNbCells = null; cellsJustSet = false; wa = new WaitingAnimation(this); this.cellWidth = cellWidth; this.cellHeight = cellHeight; this.smoothZoom = smoothZoom; this.cellSpacing = cellSpacing; realFullWidth = cellWidth + cellSpacing; realFullHeight = cellHeight + cellSpacing; this.zoomOnFocus = zoomOnFocus; this.displayName = displayName; addComponentListener(this); addMouseWheelListener(this); setLayout(new BorderLayout()); setBorder(BorderFactory.createEtchedBorder()); font = new Font("SansSerif", Font.PLAIN, 12); grid = new InternalGrid(); grid.setLayout(null); scroll = new JScrollPane(grid, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); add(scroll, BorderLayout.CENTER); scroll.getVerticalScrollBar().addAdjustmentListener(this); if (showBottomLine) { cbZoomOnFocus = new JCheckBox("Zoom"); cbZoomOnFocus.setSelected(zoomOnFocus); cbZoomOnFocus.addItemListener(this); cbDisplayName = new JCheckBox("Show names"); cbDisplayName.setSelected(displayName); cbDisplayName.addItemListener(this); slZoom = new JSlider(SwingConstants.HORIZONTAL, minZoom, maxZoom, smoothZoom); slZoom.addChangeListener(this); lbNbCells = new JLabel(EMPTY_LABEL); JPanel bottom = GuiUtil.createLineBoxPanel(cbDisplayName, Box.createHorizontalGlue(), cbZoomOnFocus, slZoom, Box.createHorizontalGlue(), lbNbCells); add(bottom, BorderLayout.SOUTH); } } @Override public void adjustmentValueChanged(AdjustmentEvent arg0) { updateLbNbCells(); } @Override public void componentHidden(ComponentEvent e) { } @Override public void componentMoved(ComponentEvent e) { } @Override public void componentResized(ComponentEvent e) { organizeCells(); } @Override public void componentShown(ComponentEvent e) { } public GridCellCollection<T> getCells() { return cells; } public int getZoomValue() { return slZoom.getValue(); } public boolean isWaitingAnimation() { return wa.isRunning(); } public boolean isZoomOnFocus() { return zoomOnFocus; } @Override public void itemStateChanged(ItemEvent e) { Object o = e.getSource(); if (o == null) { return; } if (o instanceof JCheckBox) { JCheckBox c = (JCheckBox) e.getSource(); if (c == cbZoomOnFocus) { zoomOnFocus = cbZoomOnFocus.isSelected(); if (cells != null) { cells.setZoomOnFocus(zoomOnFocus); } } if (c == cbDisplayName) { displayName = cbDisplayName.isSelected(); if (cells != null) { cells.setDisplayName(displayName); cells.notifyDisplayParametersChanged(); } } organizeCells(); grid.revalidate(); grid.repaint(); } } @Override public void mouseWheelMoved(MouseWheelEvent e) { if (slZoom != null) { slZoom.setValue(slZoom.getValue() + e.getWheelRotation() * 5); e.consume(); } } private synchronized void organizeCells() { if (cells != null) { int realCellWidth = (int) (cellWidth * zoomFactor); int nbCol = (int) Math.floor((double) (getWidth() - cellSpacing) / (double) (realCellWidth + cellSpacing)); realFullWidth = realCellWidth + cellSpacing; int realCellHeight = (int) (cellHeight * zoomFactor); realFullHeight = realCellHeight + cellSpacing; int row = 0; int col = 0; int spaceForName = 0; if (displayName) { FontMetrics fm = getGraphics().getFontMetrics(font); spaceForName = fm.getHeight() + fm.getMaxDescent(); } for (GridCell cell : cells) { if (col == nbCol) { row++; col = 0; } Rectangle bounds = new Rectangle(cellSpacing + (col * realFullWidth), cellSpacing + (row * realFullHeight), realCellWidth, realCellHeight); cell.setBounds(bounds); cell.setHeightForName(spaceForName); col++; } if (zoomOnFocus) { row += 2; } else { row += 1; } pageLength = (getHeight() / realFullHeight) * realFullHeight; grid.setPreferredSize(new Dimension(getWidth(), cellSpacing + (row * realFullHeight))); updateLbNbCells(); } else { grid.setPreferredSize(null); } } @Override public void paint(Graphics g) { super.paint(g); if (wa.isRunning()) { Graphics2D g2 = (Graphics2D) g; int w = getWidth() / 4; int h = getHeight() / 4; int x = (getWidth() - w) / 2; int y = (getHeight() - h) / 2; wa.paintAnimation(g2, w, h, x, y); } } public void setCells(GridCellCollection<T> cells) { if (this.cells != null) { this.cells.clear(); } this.cells = cells; cellsJustSet = true; grid.removeAll(); if (cells != null) { for (GridCell cell : cells) { grid.add(cell); } cells.setZoomOnFocus(zoomOnFocus); cells.setDisplayName(displayName); cells.setNameFont(font); organizeCells(); } else { grid.setPreferredSize(null); } grid.revalidate(); grid.repaint(); } public void setWaitingAnimation(boolean waiting) { if (waiting) { if (!wa.isRunning()) { wa.start(); } } else { if (wa.isRunning()) { wa.stop(); } } } public void setZoomValue(int n) { slZoom.setValue(n); } @Override public void stateChanged(ChangeEvent e) { Object o = e.getSource(); if (o == null) { return; } if (o instanceof JSlider) { JSlider s = (JSlider) e.getSource(); if (s == slZoom) { zoomFactor = slZoom.getValue() / (double) smoothZoom; organizeCells(); if (cells != null) { cells.notifyDisplayParametersChanged(); } grid.revalidate(); // updateViewport(); grid.repaint(); } } } private void updateLbNbCells() { if (lbNbCells != null) { if (cells == null) { lbNbCells.setText(EMPTY_LABEL); } else { int countVisible = 0; for (GridCell cell : cells) { if (cell.isOnScreen()) { countVisible++; } } lbNbCells.setText(countVisible + " / " + cells.size() + " files"); } } } }