/******************************************************************************* * GenPlay, Einstein Genome Analyzer * Copyright (C) 2009, 2014 Albert Einstein College of Medicine * * 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/>. * Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu> * Nicolas Fourel <nicolas.fourel@einstein.yu.edu> * Eric Bouhassira <eric.bouhassira@einstein.yu.edu> * * Website: <http://genplay.einstein.yu.edu> ******************************************************************************/ package edu.yu.einstein.genplay.gui.track.layer.foreground; import java.awt.Color; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import edu.yu.einstein.genplay.dataStructure.enums.VariantType; import edu.yu.einstein.genplay.gui.MGDisplaySettings.VariantLayerDisplaySettings; import edu.yu.einstein.genplay.gui.track.Drawer; import edu.yu.einstein.genplay.gui.track.GraphicsPanel; import edu.yu.einstein.genplay.gui.track.TrackConstants; import edu.yu.einstein.genplay.gui.track.layer.ColoredLayer; import edu.yu.einstein.genplay.gui.track.layer.Layer; import edu.yu.einstein.genplay.gui.track.layer.variantLayer.VariantLayer; import edu.yu.einstein.genplay.util.colors.Colors; /** * @author Nicolas Fourel * @version 0.1 */ public class LegendDrawer implements Drawer, MouseListener, MouseMotionListener { /** * This class processes the opening & the closing of the legend * @author Nicolas Fourel * @version 0.1 */ private class Rolling extends Thread { /** Option to open the legend */ public static final int OPEN = 1; /** Option to close the legend */ public static final int CLOSE = 0; /** The lower, the faster! */ private static final int SPEED = 1; private final int motion; // Option to open or close the legend /** * Constructor of {@link Rolling} * @param option to open or close the legend */ public Rolling (int option) { motion = option; } /** * Close the legend */ private void close () { while (legendWidth >= 0) { legendWidth--; repaint(); } } /** * Open the legend */ private void open () { while (legendWidth <= originalLegendWidth) { legendWidth++; repaint(); } } /** * Repaint the legend */ private void repaint () { parent.getTrack().getGraphicsPanel().repaint(); try { Thread.sleep(SPEED); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { if (motion == OPEN) { open(); isVisible = true; } else if (motion == CLOSE) { isVisible = false; close(); } } } private final Layer<?> parent; private final int widthOffset = 2; // Space between the border of the rectangle and the text. private final int heightOffset = 2; // Space between the border of the rectangle and the top of the track. private final int transparency = 240; // Transparency of the legend and the roller. private final int rollerWidth = 7; // Width of the roller. private final int rollerHeight = 15; // Height of the roller. private Graphics graphic; // The track graphic. private Graphics gRoller; // The graphic of the roller. private Graphics gLegend; // The graphic of the legend. private String trackName; // The track name private Layer<?>[] layers; // The layers of the track. private Layer<?> activeLayer; // The active layer of the track. private int trackWidth; // Width of the track. private int originalLegendWidth; // Original width of the legend. private int legendWidth; // Width of the legend. private int legendHeight; // Height of the legend. private boolean isVisible; // If the legend is visible or not. private boolean isCursorOverLegend; // true if the cursor is over the legend /** * Constructor of {@link LegendDrawer} * @param parent the parent layer */ public LegendDrawer (Layer<?> parent) { this.parent = parent; isVisible = true; registerListener(parent.getTrack().getGraphicsPanel()); } /** * Close the legend */ private void closeLegend () { Thread rolling = new Rolling(Rolling.CLOSE); rolling.start(); parent.getTrack().updateGraphicCursor(); } /** * Draw the legend and the roller button * @param g */ public void draw (Graphics g) { graphic = g; // Get the right graphics gRoller = getTopRightRollerGraphic(); gLegend = getTopRightLegendGraphic(); // Draw the legend elements drawLegendArea(); drawTrackName(); drawLayersName(); // Draw the roller drawRoller(); } @Override public void draw(Graphics g, int width, int height) { g.setFont(TrackConstants.FONT_LEGEND); initialize(g, width); draw(g); } /** * Draw the name of the layers */ private void drawLayersName () { // Set the metrics FontMetrics fm = gLegend.getFontMetrics(); int y = fm.getHeight(); // height of the text if (trackName != null) { y *= 2; } // Draw the layers for (Layer<?> layer: layers) { if (layer.getName() != null){ // Set the current font if ((activeLayer != null) && layer.equals(activeLayer)) { gLegend.setFont(TrackConstants.FONT_LEGEND_ACTIVE_LAYER); } else { gLegend.setFont(TrackConstants.FONT_LEGEND); } // Set the current color if (layer instanceof ColoredLayer) { gLegend.setColor(Colors.removeTransparency(((ColoredLayer)layer).getColor())); } else { gLegend.setColor(Colors.BLACK); } // Draw the current layer name if ((layer instanceof VariantLayer) && (layer.getName().equals(((VariantLayer) layer).getData().getDescription()))) { drawVariantLayerName((VariantLayer) layer, y); } else { gLegend.drawString(layer.getName(), widthOffset, y); } // Update the metrics y += fm.getHeight(); } } } /** * Draw the legend area elements (borders and rectangle content). */ private void drawLegendArea () { Color rect = Colors.addTransparency(Colors.TRACK_BACKGROUND, transparency); gLegend.setColor(rect); gLegend.fillRect(0, 0, legendWidth, legendHeight - 1); gLegend.setColor(Colors.BLACK); gLegend.drawRect(0, 0, legendWidth, legendHeight - 1); } /** * Draw the roller graphic */ private void drawRoller () { // Set metrics int dotDiameter = 3; int dotNumber = 3; int dotOffset = (rollerHeight / dotNumber) - 1; int dotX = (rollerWidth - dotDiameter) / 2; // Draw background Color backgroundColor = Colors.addTransparency(Colors.LIGHT_GREY, transparency); gRoller.setColor(backgroundColor); gRoller.fillRect(0, 0, rollerWidth, rollerHeight); // Draw dots gRoller.setColor(Colors.BLACK); int currentDotY = 2; for (int i = 0; i < dotNumber; i++) { gRoller.fillOval(dotX, currentDotY, dotDiameter, dotDiameter); currentDotY += dotOffset; } } /** * Draw the name of the track */ private void drawTrackName () { if (trackName != null) { FontMetrics fm = gLegend.getFontMetrics(); int textHeight = fm.getHeight(); // height of the text // Draw the track name gLegend.drawString(trackName, widthOffset, textHeight); } } /** * Draw a {@link VariantLayer} name * @param layer * @param y */ private void drawVariantLayerName (VariantLayer layer, int y) { VariantLayerDisplaySettings data = layer.getData(); if (data != null) { int x = widthOffset; String s = data.getGenome() + " ("; x = printString(s, x, y, Colors.BLACK); for (int i = 0; i < data.getVariationTypeList().size(); i++) { VariantType type = data.getVariationTypeList().get(i); if (type == VariantType.INSERTION) { s = "I"; } else if (type == VariantType.DELETION) { s = "D"; } else if (type == VariantType.SNPS) { s = "SNPs"; } x = printString(s, x, y, data.getColorList().get(i)); if (i < (data.getVariationTypeList().size() - 1)) { x = printString(", ", x, y, Colors.BLACK); } } printString(")", x, y, Colors.BLACK); } } /** * @return the height of the legend area */ private int getHeight () { int lines = 0; if (trackName != null) { lines++; } lines += layers.length; FontMetrics fm = graphic.getFontMetrics(); int height = (lines * fm.getHeight()) + (heightOffset * 2); return height; } /** * @return the X position where the legend starts on the track */ private int getLegendX () { return trackWidth - legendWidth; } /** * @return the longest name in this graphic between the track name and the layers */ private int getMaxWidth () { FontMetrics fm = graphic.getFontMetrics(TrackConstants.FONT_LEGEND_ACTIVE_LAYER); int width = 0; if (trackName != null) { width = fm.stringWidth(trackName); } for (Layer<?> layer: layers) { if (layer.getName() != null) { width = Math.max(width, fm.stringWidth(layer.getName())); } } width += (widthOffset * 2); return width; } /** * @return the Y position where the roller starts on the track */ private int getRollerX () { return getLegendX() - rollerWidth; } /** * @return the graphic matching the top right corner of the given graphic to draw the legend */ private Graphics getTopRightLegendGraphic () { return graphic.create(getLegendX(), 1, legendWidth, legendHeight); } /** * @return the graphic matching the top right corner of the given graphic to draw the button */ private Graphics getTopRightRollerGraphic () { return graphic.create(getRollerX(), 1, rollerWidth, rollerHeight); } /** * Initialize all the drawing attributes * @param g * @param width */ private void initialize (Graphics g, int width) { graphic = g; trackWidth = width; // Set names trackName = null; activeLayer = null; if ((parent.getTrack().getName() != null) && (!parent.getTrack().getName().trim().isEmpty())) { trackName = parent.getTrack().getName(); activeLayer = parent.getTrack().getActiveLayer(); } layers = parent.getTrack().getLayers().getLayers(); // Get area dimensions if (isVisible) { legendWidth = getMaxWidth(); originalLegendWidth = legendWidth; } legendHeight = getHeight(); } /** * @return true if the cursor is over the legend */ public boolean isCursorOverLegend() { return isCursorOverLegend; } /** * @param p a {@link Point} on the track * @return true if the point is in the roller, false otherwise */ private boolean isInRoller (Point p) { int x = p.x; int y = p.y; int rollerX1 = getRollerX(); int rollerX2 = rollerX1 + rollerWidth; int rollerY1 = 1; int rollerY2 = rollerY1 + rollerHeight; boolean contain = false; if ((x >= rollerX1) && (x <= rollerX2) && (y >= rollerY1) && (y <= rollerY2)) { contain = true; } return contain; } @Override public void mouseClicked(MouseEvent arg0) { if ((arg0.getButton() == MouseEvent.BUTTON1) && isInRoller(arg0.getPoint())) { if (isVisible) { closeLegend(); } else { openLegend(); } } } @Override public void mouseDragged(MouseEvent e) {} @Override public void mouseEntered(MouseEvent arg0) {} @Override public void mouseExited(MouseEvent arg0) {} @Override public void mouseMoved(MouseEvent e) { isCursorOverLegend = isInRoller(e.getPoint()); parent.getTrack().updateGraphicCursor(); } @Override public void mousePressed(MouseEvent arg0) {} @Override public void mouseReleased(MouseEvent arg0) {} /** * Open the legend */ private void openLegend () { Thread rolling = new Rolling(Rolling.OPEN); rolling.start(); } /** * Print a string * @param s the string * @param x the x position * @param y the y position * @param color the color of the text * @return the x position the string continues */ private int printString (String s, int x, int y, Color color) { gLegend.setColor(color); gLegend.drawString(s, x, y); return x += gLegend.getFontMetrics().stringWidth(s); } /** * Register the {@link ForegroundLayer} listeners to the {@link GraphicsPanel} * @param graphicsPanel */ private void registerListener (GraphicsPanel graphicsPanel) { graphicsPanel.addMouseListener(this); graphicsPanel.addMouseMotionListener(this); } }