/* * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview 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. * * Jalview 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 Jalview. If not, see . * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.gui; import jalview.api.AlignViewportI; import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.renderer.OverviewRenderer; import jalview.renderer.OverviewResColourFinder; import jalview.viewmodel.OverviewDimensions; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.image.BufferedImage; import javax.swing.JPanel; @SuppressWarnings("serial") public class OverviewCanvas extends JPanel { private static final Color TRANS_GREY = new Color(100, 100, 100, 25); // This is set true if the alignment view changes whilst // the overview is being calculated private volatile boolean restart = false; private volatile boolean updaterunning = false; private boolean disposed = false; private BufferedImage lastMiniMe = null; // Can set different properties in this seqCanvas than // main visible SeqCanvas private SequenceRenderer sr; private jalview.renderer.seqfeatures.FeatureRenderer fr; private OverviewDimensions od; private OverviewRenderer or = null; private AlignViewportI av; private OverviewResColourFinder cf; private ProgressPanel progressPanel; private boolean showSequenceFeatures; private boolean showAnnotation; private jalview.api.FeatureRenderer featureRenderer; private OverviewPanel panel; public OverviewCanvas(OverviewPanel panel, OverviewDimensions overviewDims, AlignViewportI alignvp, ProgressPanel pp) { this.panel = panel; od = overviewDims; av = alignvp; progressPanel = pp; sr = new SequenceRenderer(av); sr.renderGaps = false; fr = new jalview.renderer.seqfeatures.FeatureRenderer(av); boolean useLegacy = Cache.getDefault(Preferences.USE_LEGACY_GAP, false); Color gapCol = Cache.getDefaultColour(Preferences.GAP_COLOUR, jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_GAP); Color hiddenCol = Cache.getDefaultColour(Preferences.HIDDEN_COLOUR, jalview.renderer.OverviewResColourFinder.OVERVIEW_DEFAULT_HIDDEN); cf = new OverviewResColourFinder(useLegacy, gapCol, hiddenCol); setSize(od.getWidth(), od.getHeight()); setPreferredSize(getSize()); // BH 2019.07.29 added } /** * Update the overview dimensions object used by the canvas (e.g. if we change * from showing hidden columns to hiding them or vice versa) * * @param overviewDims */ public void resetOviewDims(OverviewDimensions overviewDims) { od = overviewDims; } /** * Signals to drawing code that the associated alignment viewport has changed * and a redraw will be required */ public boolean restartDraw() { synchronized (this) { if (updaterunning) { setRestart("restartDraw"); } else { updaterunning = true; } return restart; } } private void setRestart(String why) { // System.out.println("OC restart true " + why); restart = true; if (or != null) { or.setRedraw(true); } } /** * Draw the overview sequences * * @param showSequenceFeatures * true if sequence features are to be shown * @param showAnnotation * true if the annotation is to be shown * @param featureRenderer * the renderer to transfer feature colouring from */ public void draw(boolean showSequenceFeatures, boolean showAnnotation, jalview.api.FeatureRenderer featureRenderer) { this.showSequenceFeatures = showSequenceFeatures; this.showAnnotation = showAnnotation; this.featureRenderer = featureRenderer; // System.out.println("OC draw " + ++ndraw + " showseqf=" // + showSequenceFeatures + " showAnno=" + showAnnotation); if (showSequenceFeatures) { fr.transferSettings(featureRenderer); } setPreferredSize(new Dimension(od.getWidth(), od.getHeight())); AlignmentI al = av.getAlignment(); or = new OverviewRenderer(panel.ap, fr, od, al, av.getResidueShading(), cf, progressPanel != null); if (progressPanel != null) { or.addPropertyChangeListener(progressPanel); } or.draw(od.getRows(al), od.getColumns(al)); } void finalizeDraw(BufferedImage miniMe) { Graphics mg = miniMe.getGraphics(); if (showAnnotation) { mg.translate(0, od.getSequencesHeight()); or.drawGraph(mg, av.getAlignmentConservationAnnotation(), od.getGraphHeight(), od.getColumns(av.getAlignment())); mg.translate(0, -od.getSequencesHeight()); } mg.dispose(); // BH 2019 if (progressPanel != null) { or.removePropertyChangeListener(progressPanel); } or = null; if (restart) { restart = false; if (!disposed) { draw(showSequenceFeatures, showAnnotation, featureRenderer); } } else { updaterunning = false; lastMiniMe = miniMe; repaint(); } } @Override public void paintComponent(Graphics g) { int w = getWidth(); int h = getHeight(); if (w == 0 || od.getBoxWidth() <= 0) { // BH 2019.07.27 removes two unnecessary paints, since boxwidth can be -1 // or 0 during early-stage painting return; } boolean drawMe = (lastMiniMe != null); if (restart) { if (drawMe) { g.drawImage(lastMiniMe, 0, 0, w, h, this); } else { g.setColor(Color.white); g.fillRect(0, 0, w, h); } g.setColor(TRANS_GREY); g.fillRect(0, 0, w, h); drawMe = false; } else if (drawMe) { // is this a resize? if (w != od.getWidth() || h != od.getHeight()) { // if there is annotation, scale the alignment and annotation // separately if (od.getGraphHeight() <= 0 && od.getSequencesHeight() <= 0) { od.setWidth(w); od.setHeight(h); return; } // System.out.println("OC new subimages"); BufferedImage topImage = lastMiniMe.getSubimage(0, 0, od.getWidth(), od.getSequencesHeight()); BufferedImage bottomImage = lastMiniMe.getSubimage(0, od.getSequencesHeight(), od.getWidth(), od.getGraphHeight()); // must be done at this point as we rely on using old width/height // above, and new width/height below od.setWidth(w); od.setHeight(h); // stick the images back together so lastMiniMe is consistent in the // event of a repaint - BUT probably not thread safe // System.out.println("OC new lastminime " + w + " " + h); lastMiniMe = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); Graphics lg = lastMiniMe.getGraphics(); lg.drawImage(topImage, 0, 0, w, od.getSequencesHeight(), null); lg.drawImage(bottomImage, 0, od.getSequencesHeight(), w, od.getGraphHeight(), this); lg.dispose(); // BH 2019: removed -- this is now taken care of using vpbox in // OverviewDimension // // make sure the box is in the right place // od.setBoxPosition(av.getAlignment().getHiddenSequences(), // av.getAlignment().getHiddenColumns()); } } if (drawMe) { g.drawImage(lastMiniMe, 0, 0, w, h, this); } // draw the box g.setColor(Color.red); // System.out.println("OC paintComponent nd=" + ndraw + " nr=" + nrepaint // + " np=" + ++npaint); od.drawBox(g); } private int ndraw, npaint, nrepaint; // @Override // public void repaint() // { // System.out.println("OC repaint " + (++nrepaint)); // super.repaint(); // } public void dispose() { disposed = true; od = null; synchronized (this) { setRestart("dispose"); } } }