/* VisualReferenceDisplay.java created 2010-10-24 * */ package org.signalml.app.view.montage.visualreference; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.geom.Rectangle2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JViewport; import javax.swing.Scrollable; /** VisualReferenceDisplay * * * @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o. */ public abstract class VisualReferenceDisplay extends JComponent implements VisualReferenceListener, PropertyChangeListener, Scrollable { private static final long serialVersionUID = 1L; public static final int MAX_CHANNEL_LABEL_LENGTH = 7; public static final int BIN_SPACING = 10; protected VisualReferenceModel model; protected Font binLabelFont; protected Font channelLabelFont; protected FontMetrics binLabelFontMetrics; protected FontMetrics channelLabelFontMetrics; protected Stroke defaultStroke; protected Stroke activeChannelStroke; protected JViewport viewport; protected Dimension requiredSize = null; public VisualReferenceDisplay(VisualReferenceModel model) { super(); setAutoscrolls(true); if (model == null) { throw new NullPointerException("No model"); } this.model = model; setFocusable(true); model.addPropertyChangeListener(this); model.addVisualReferenceListener(this); } public JViewport getViewport() { return viewport; } public void setViewport(JViewport viewport) { this.viewport = viewport; } @Override public boolean isOpaque() { return true; } protected abstract void paintBinContents(VisualReferenceBin bin, Graphics2D g); protected void paintGivenChannel(String label, int perPrimarySize, Shape shape, Shape outlineShape, Color fill, Color outline, boolean boldBorder, Graphics2D g) { g.setColor(fill); g.fill(shape); g.setColor(outline); if (boldBorder) g.setStroke(activeChannelStroke); g.draw(outlineShape); g.setStroke(defaultStroke); g.setColor(Color.BLACK); g.setFont(channelLabelFont); String abbrLabel; if (label.length() > MAX_CHANNEL_LABEL_LENGTH) { abbrLabel = label.substring(0,MAX_CHANNEL_LABEL_LENGTH-2) + "..."; } else { abbrLabel = label; } Rectangle2D rect = channelLabelFontMetrics.getStringBounds(abbrLabel, g); int textHeight = (int)(rect.getHeight()); int textWidth = (int)(rect.getWidth()); Rectangle r = shape.getBounds(); g.drawString(abbrLabel, r.x + (r.width - textWidth) / 2, r.y + (r.height - textHeight) / 2 + channelLabelFontMetrics.getAscent()); if (perPrimarySize > 1) { abbrLabel = "(" + Integer.toString(perPrimarySize) + ")"; rect = channelLabelFontMetrics.getStringBounds(abbrLabel, g); textWidth = (int)(rect.getWidth()); g.drawString(abbrLabel, r.x + (r.width - textWidth) / 2, r.y + (r.height - textHeight) / 2 + channelLabelFontMetrics.getAscent() + textHeight); } } protected void paintBin(VisualReferenceBin bin, Graphics2D g) { Dimension size = bin.getSize(); Point location = bin.getLocation(); String name = bin.getName(); g.setColor(Color.DARK_GRAY); g.drawRect(location.x, location.y, size.width-1, size.height-1); g.fillRect(location.x, location.y, size.width, VisualReferenceBin.HEADER_HEIGHT); if (name != null && !name.isEmpty()) { g.setColor(Color.WHITE); g.setFont(binLabelFont); Rectangle2D rect = binLabelFontMetrics.getStringBounds(name, g); int textHeight = (int)(rect.getHeight()); int textWidth = (int)(rect.getWidth()); g.drawString(name, location.x + (size.width - textWidth) / 2, location.y + (VisualReferenceBin.HEADER_HEIGHT - textHeight) / 2 + binLabelFontMetrics.getAscent()); } if (bin instanceof VisualReferencePositionedBin) { VisualReferencePositionedBin positionedBin = (VisualReferencePositionedBin) bin; Insets backdropMargin = positionedBin.getBackdropMargin(); if (backdropMargin == null) { backdropMargin = new Insets(0,0,0,0); } Insets margin = positionedBin.getMargin(); int width = size.width - (margin.left + margin.right + backdropMargin.left + backdropMargin.right); int height = size.height - (VisualReferenceBin.HEADER_HEIGHT + margin.top + margin.bottom + backdropMargin.top + backdropMargin.bottom); Image backdrop = model.getMontage().getSignalTypeConfigurer().getMatrixBackdrop(width, height); if (backdrop != null) { g.drawImage(backdrop, location.x + margin.left + backdropMargin.left, location.y + VisualReferenceBin.HEADER_HEIGHT + margin.top + backdropMargin.top, null); } } } protected Graphics2D Get2DGraphics(Graphics gOrig) { Graphics2D g = (Graphics2D) gOrig; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); if (defaultStroke == null) { defaultStroke = g.getStroke(); activeChannelStroke = new BasicStroke(3); } if (binLabelFont == null || channelLabelFont == null) { binLabelFont = g.getFont(); channelLabelFont = new Font(Font.DIALOG, Font.PLAIN, 9); binLabelFontMetrics = g.getFontMetrics(binLabelFont); channelLabelFontMetrics = g.getFontMetrics(channelLabelFont); } return g; } @Override protected void paintComponent(Graphics gOrig) { Graphics2D g = Get2DGraphics(gOrig); Rectangle clip = g.getClipBounds(); g.setColor(getBackground()); g.fill(clip); VisualReferenceBin othersBin = model.getOthersBin(); VisualReferencePositionedBin positionedBin = model.getPositionedBin(); paintBin(positionedBin, g); if (!othersBin.isEmpty()) { paintBin(othersBin, g); } paintBinContents(positionedBin, g); if (!othersBin.isEmpty()) { paintBinContents(othersBin, g); } } @Override public boolean isDoubleBuffered() { return true; } @Override public void invalidate() { super.invalidate(); requiredSize = null; } @Override public Dimension getPreferredSize() { if (requiredSize == null) { requiredSize = calculateRequiredSize(); } return requiredSize; } @Override public Dimension getMinimumSize() { return super.getPreferredSize(); } protected Dimension calculateRequiredSize() { Dimension viewportSize = viewport.getExtentSize(); int avHeight = viewportSize.height - (2 * BIN_SPACING); VisualReferenceBin othersBin = model.getOthersBin(); VisualReferencePositionedBin positionedBin = model.getPositionedBin(); int minimumHeight = 0; int minBinHeight; if (!othersBin.isEmpty()) { minBinHeight = othersBin.getMinHeight(); if (minBinHeight > minimumHeight) { minimumHeight = minBinHeight; } } minimumHeight += (2 * BIN_SPACING); if (avHeight < minimumHeight) { avHeight = minimumHeight; } Dimension size; positionedBin.setMaxHeight(avHeight); size = positionedBin.getSize(); int width = 2*BIN_SPACING + size.width; if (!othersBin.isEmpty()) { othersBin.setMaxHeight(avHeight); size = othersBin.getSize(); width += (BIN_SPACING + size.width); } int centeringOffset = 0; if (viewportSize.width > width) { centeringOffset = (viewportSize.width - width) / 2; } // now position the bins and re-validate them int topX = centeringOffset + BIN_SPACING; if (!othersBin.isEmpty()) { othersBin.setLocation(new Point(topX, BIN_SPACING)); if (!othersBin.isPositioned()) { othersBin.reposition(); } size = othersBin.getSize(); topX += (size.width + BIN_SPACING); } positionedBin.setLocation(new Point(topX, BIN_SPACING)); if (!positionedBin.isPositioned()) { positionedBin.reposition(); } return new Dimension(width, viewportSize.height); } public VisualReferenceModel getModel() { return model; } @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return 100; } @Override public boolean getScrollableTracksViewportHeight() { return true; } @Override public boolean getScrollableTracksViewportWidth() { return (getPreferredSize().width < viewport.getExtentSize().width); } @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 10; } @Override public void propertyChange(PropertyChangeEvent evt) { Object source = evt.getSource(); String name = evt.getPropertyName(); if (source == model) { if (VisualReferenceModel.ACTIVE_ARROW_PROPERTY.equals(name)) { repaint(); } else if (VisualReferenceModel.ACTIVE_CHANNEL_PROPERTY.equals(name)) { repaint(); } } } @Override public void montageChannelsChanged(VisualReferenceEvent ev) { revalidate(); repaint(); } @Override public void montageStructureChanged(VisualReferenceEvent ev) { revalidate(); repaint(); } @Override public void referenceChanged(VisualReferenceEvent ev) { } @Override public void sourceChannelsChanged(VisualReferenceEvent ev) { revalidate(); repaint(); } }