Skip to content
Snippets Groups Projects
FeatureDisplay.java 156 KiB
Newer Older
  • Learn to ignore specific revisions
  • tjc's avatar
    tjc committed
    /* FeatureDisplay.java
     *
     * created: Fri Oct  9 1998
     *
     * This file is part of Artemis
     *
     * Copyright(C) 1998,1999,2000,2001,2002  Genome Research Limited
     *
     * 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 2
     * 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, write to the Free Software
     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     *
    
     * $Header: //tmp/pathsoft/artemis/uk/ac/sanger/artemis/components/FeatureDisplay.java,v 1.66 2009-06-12 13:50:35 tjc Exp $
    
    tjc's avatar
    tjc committed
     */
    
    package uk.ac.sanger.artemis.components;
    
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.components.filetree.*;
    
    import uk.ac.sanger.artemis.util.DatabaseDocument;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.RemoteFileDocument;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.ReadOnlyException;
    import uk.ac.sanger.artemis.util.FileDocument;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.OutOfRangeException;
    import uk.ac.sanger.artemis.util.StringVector;
    
    import uk.ac.sanger.artemis.io.ChadoCanonicalGene;
    import uk.ac.sanger.artemis.io.Qualifier;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.Range;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.EntryInformation;
    import uk.ac.sanger.artemis.io.SimpleEntryInformation;
    
    import uk.ac.sanger.artemis.io.Key;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.GFFStreamFeature;
    
    import uk.ac.sanger.artemis.io.RawStreamSequence;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.FastaStreamSequence;
    import uk.ac.sanger.artemis.io.Sequence;
    
    import uk.ac.sanger.artemis.io.Location;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.*;
    import uk.ac.sanger.artemis.sequence.*;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.components.genebuilder.*;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
    import java.io.IOException;
    import java.io.File;
    
    tjc's avatar
    tjc committed
    import java.awt.event.*;
    import java.awt.*;
    import java.lang.Math;
    import java.util.Vector;
    import java.util.Comparator;
    
    tjc's avatar
    tjc committed
    import java.util.Enumeration;
    
    tjc's avatar
    tjc committed
    import javax.swing.border.Border;
    import javax.swing.border.BevelBorder;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
    import java.awt.datatransfer.*;
    import java.awt.dnd.*;
    
    import javax.swing.Box;
    
    import javax.swing.JOptionPane;
    
    tjc's avatar
    tjc committed
    import javax.swing.JScrollBar;
    import javax.swing.JComponent;
    
    import javax.swing.UIManager;
    
    tjc's avatar
    tjc committed
    import javax.swing.ImageIcon;
    
    tjc's avatar
    tjc committed
    import javax.swing.JFrame;
    
    tjc's avatar
    tjc committed
    
    /**
     *  This component is used for displaying an Entry.
     *
     *  @author Kim Rutherford
    
     *  @version $Id: FeatureDisplay.java,v 1.66 2009-06-12 13:50:35 tjc Exp $
    
    tjc's avatar
    tjc committed
     **/
    
    public class FeatureDisplay extends EntryGroupPanel
      implements EntryGroupChangeListener,
                 EntryChangeListener, FeatureChangeListener,
                 SelectionChangeListener, GotoListener, SequenceChangeListener,
    
    tjc's avatar
    tjc committed
                 DisplayComponent, OptionChangeListener, DisplayAdjustmentListener,
                 DragGestureListener, DropTargetListener,
                 DragSourceListener
    
    tjc's avatar
    tjc committed
    {
    
    
    tjc's avatar
    tjc committed
      /** */
      private static final long serialVersionUID = 1L;
    
    
    tjc's avatar
    tjc committed
      private int highlight_drop_base = -1;
    
    
    tjc's avatar
    tjc committed
      /** Key code for calling zoomToSelection(). */
      final static public int ZOOM_TO_SELECTION_KEY = KeyEvent.VK_Z;
    
      final static public int SCROLLBAR_AT_TOP = 1;
      final static public int SCROLLBAR_AT_BOTTOM = 2;
    
    
    tjc's avatar
    tjc committed
      private final static int FORWARD = Bases.FORWARD;
      private final static int REVERSE = Bases.REVERSE;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
      private final static int NO_FRAME        = FeatureSegment.NO_FRAME;
      private final static int FORWARD_STRAND  = FeatureSegment.FORWARD_STRAND;
      private final static int REVERSE_STRAND  = FeatureSegment.REVERSE_STRAND;
      private final static int FORWARD_FRAME_1 = FeatureSegment.FORWARD_FRAME_1;
      private final static int FORWARD_FRAME_2 = FeatureSegment.FORWARD_FRAME_2;
      private final static int FORWARD_FRAME_3 = FeatureSegment.FORWARD_FRAME_3;
      private final static int REVERSE_FRAME_3 = FeatureSegment.REVERSE_FRAME_3;
      private final static int REVERSE_FRAME_2 = FeatureSegment.REVERSE_FRAME_2;
      private final static int REVERSE_FRAME_1 = FeatureSegment.REVERSE_FRAME_1;
      private final static int SCALE_LINE      = FeatureSegment.SCALE_LINE;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
      /**
       *  The JScrollBar for this FeatureDisplay object.  We create the scrollbar
       *  as part of this object rather than in the EntryEdit component because we
       *  may need to change the parameters of the scrollbar later.
       **/
      private JScrollBar scrollbar = null;
    
      /** A scroll bar for changing the viewing scale. */
    
      private ZoomScrollBar scale_changer = null;
    
    tjc's avatar
    tjc committed
    
      /** Used to colour the frames. */
      private Color light_grey = new Color(240, 240, 240);
    
      /** Used to colour sequence line. */
      private Color not_so_light_grey = new Color(200, 200, 200);
    
      /**
       *  The colour used for the active entry line when 
       *  one_line_per_entry is set.
       **/
      private Color active_entry_colour = new Color(255, 255, 140);
    
      /**
       *  This Vector containing the references of those features that are
       *  currently visible.
       **/
      private FeatureVector visible_features = new FeatureVector();
    
      /**
       *  If true updateVisibleFeatureVector() will be called by paint().
       *  updateVisibleFeatureVector() sets this to false,
       *  needVisibleFeatureVectorUpdate() sets this to true.
       **/
      private boolean update_visible_features = true;
    
      /** Contains those objects listening for adjustment events. */
      final private Vector adjustment_listener_list = new Vector();
    
      /**
       *  The index of the first base that we are displaying.  
       *  Can be negative if hard_left_edge is true.
       **/
      private int left_edge_base = 1;
    
      /** See getScaleFactor(). */
      private int scale_factor = 3;
    
      /** See getScaleFactor(). */
      private float scale_value = 1;
    
      /** true if labels should be shown. */
      private boolean show_labels = true;
    
      /**
       *  This variable is true if the forward frame lines(or forward entry lines
       *  - see one_line_per_entry) should be drawn.
       **/
      private boolean show_forward_lines = true;
    
      /**
       *  This variable is true if the reverse frame lines(or reverse entry lines
       *  - see one_line_per_entry) should be drawn.
       **/
      private boolean show_reverse_lines = true;
    
      /**
       *  If true draw all features, sequence and scale lines reverse complemented.
       **/
      private boolean rev_comp_display = false;
    
      /**
       *  This variable is true if(for each strand) each entry should be on a
       *  separate line.
       **/
      private boolean one_line_per_entry = false;
    
      /**
       *  If true the there will never be a gap between the left edge of the
       *  screen and the first visible base.
       **/
      private boolean hard_left_edge = true;
    
      /** true if source features should be shown. */
      private boolean show_source_features = false;
    
      /** true if stop codons should be shown.     */
      private boolean show_stop_codons = true;
    
      /** true if start codons should be shown.    */
      private boolean show_start_codons = false;
    
      /** true if directional arrows should be shown on features. */
      private boolean show_feature_arrows;
    
      /** true a black border will be drawn around each feature.  */
      private boolean show_feature_borders;
    
      /**
       *  This variable is true if each base should be drawn in a different colour
       *  at scale feature 1.
       **/
      private boolean show_base_colours = false;
    
      /**
       *  All features(not just CDS features) will be drawn on the frame lines if
       *  and only if this variable is true.  See setFrameFeaturesFlag() and
       *  getFrameFeaturesFlag().
       **/
      private boolean frame_features_flag = false;
    
      /**
       *  The position(s) of the last mouse click on the dna line.  The
       *  MarkerRange contains a reference to the appropriate Strand and contains
       *  the base positions.  See getMarkerRangeFromPosition() to understand why
       *  one click can give multiple bases.
       **/
      private MarkerRange click_range = null;
    
      /**
       *  The last(FeatureSegment) Marker that the user clicked on.  This is used
       *  for dragging the ends of segments.
       **/
      private Marker click_segment_marker = null;
    
      /**
       *  This is true if click_segment_marker is the Marker at the start of
       *  segment false otherwise.  The value is only useful if
       *  click_segment_marker is set.
       **/
      private boolean click_segment_marker_is_start_marker = false;
    
      /**
       *  When a FeatureSegment Marker drag starts, this is set to the Marker at
       *  the other end of the segment.  This is used to check that the drag has
       *  not move the Marker too far(past the end of the segment).
       **/
      private Marker other_end_of_segment_marker = null;
    
      /**
       *  Features with a /score qualifier less than this value will not be shown.
       **/
      private int current_min_score = 0;
    
      /**
       *  Features with a /score qualifier greater than this value will not be
       *  shown.
       **/
      private int current_max_score = 100;
    
      private MouseEvent last_mouse_press_event;
    
      /**
       *  If set no DisplayAdjustment events will be sent.  This is set by
       *  displayAdjustmentValueChanged() to prevent an event we send from
       *  returning to us(a FeatureDisplay can listen for DisplayAdjustment
       *  events from another FeatureDisplay).
       **/
      private boolean disable_display_events = false;
    
      /**
       *  Set to true by selectionChanged() to tell updateVisibleFeatureVector()
       *  to raise the contents of the select before updating.
       **/
      private boolean raise_selection_flag = false;
     
      /** the minimum distance in pixels between the labels. */
      private final static int MINIMUM_LABEL_SPACING = 80;
     
      /** colour used for A. */
      private static Color dark_green = new Color(0, 150, 0);
    
    
    tjc's avatar
    tjc committed
      private final int scrollbar_style;
    
    tjc's avatar
    tjc committed
      
      private static Vector contigKeys;
    
    tjc's avatar
    tjc committed
    
    
      private Object[] protein_keys = { "CDS", 
                                        "BLASTCDS",
    
                                        "polypeptide",
                                        "pseudogenic_exon"};
    
    tjc's avatar
    tjc committed
      /**
       *  Create a new FeatureDisplay object with the horizontal scrollbar at the
       *  bottom of the component.
       *  @param entry_group The EntryGroup that this component will display.
       *  @param selection The Selection object for this component.  Selected
       *    objects will be highlighted.
       *  @param goto_event_source The object to use when we need to call
       *    gotoBase().
       *  @param base_plot_group The BasePlotGroup associated with this JMenu -
       *    needed to call getCodonUsageAlgorithm()
       **/
      public FeatureDisplay(final EntryGroup entry_group,
                            final Selection selection,
                            final GotoEventSource goto_event_source,
                            final BasePlotGroup base_plot_group) 
      {
        this(entry_group, selection, goto_event_source,
    
    tjc's avatar
    tjc committed
             base_plot_group, SCROLLBAR_AT_BOTTOM);
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Create a new FeatureDisplay object.
       *  @param entry_group The EntryGroup that this component will display.
       *  @param owning_component The EntryEdit object that contains the selection
       *    that this component uses.
       *  @param scrollbar_at_top If true the horizontal scrollbar will be at the
       *    top of component.
       *  @param base_plot_group The BasePlotGroup associated with this JMenu -
       *    needed to call getCodonUsageAlgorithm()
       *  @param scrollbar_style Controls the type of horizontal scrollbar.  Must
       *    be one of SCROLLBAR_AT_TOP, SCROLLBAR_AT_BOTTOM or NO_SCROLLBAR.
       **/
      public FeatureDisplay(final EntryGroup entry_group,
                            final Selection selection,
                            final GotoEventSource goto_event_source,
                            final BasePlotGroup base_plot_group,
                            final int scrollbar_style) 
      {
        super(entry_group, selection, goto_event_source, base_plot_group);
    
    
    tjc's avatar
    tjc committed
        this.scrollbar_style = scrollbar_style;
    
    
    tjc's avatar
    tjc committed
        show_feature_arrows =
          Options.getOptions().getPropertyTruthValue("draw_feature_arrows");
    
        show_feature_borders =
          Options.getOptions().getPropertyTruthValue("draw_feature_borders");
    
        frame_features_flag =
          Options.getOptions().getPropertyTruthValue("features_on_frame_lines");
    
        one_line_per_entry =
          Options.getOptions().getPropertyTruthValue("one_line_per_entry");
    
        show_labels =
          Options.getOptions().getPropertyTruthValue("feature_labels");
    
    
        show_reverse_lines =
          Options.getOptions().getPropertyTruthValue("show_reverse_lines");
        
        show_forward_lines =
          Options.getOptions().getPropertyTruthValue("show_forward_lines");
    
        
        final StringVector frame_line_features = 
          Options.getOptions().getOptionValues("frame_line_features");
        if(frame_line_features != null)
          protein_keys = frame_line_features.toArray();
        
    
    tjc's avatar
    tjc committed
        addComponentListener(new ComponentAdapter()
        {
          public void componentResized(ComponentEvent e) 
          {
            // update the scroll bar as soon as we know the size of the canvas
            fixScrollbar();
            needVisibleFeatureVectorUpdate();
            fireAdjustmentEvent(DisplayAdjustmentEvent.ALL_CHANGE_ADJUST_EVENT);
          }
    
          public void componentShown(ComponentEvent e) 
          {
            // update the scroll bar as soon as we know the size of the canvas
            fixScrollbar();
            needVisibleFeatureVectorUpdate();
            fireAdjustmentEvent(DisplayAdjustmentEvent.ALL_CHANGE_ADJUST_EVENT);
          }
        });
    
        setScaleValue();
    
        if(scrollbar_style == SCROLLBAR_AT_TOP) 
          createScrollbar(true);
    
    tjc's avatar
    tjc committed
        else if(scrollbar_style == SCROLLBAR_AT_BOTTOM)
          createScrollbar(false);
    
    tjc's avatar
    tjc committed
    
        createScaleScrollbar();
        addListeners();
    
        needVisibleFeatureVectorUpdate();
    
        getSelection().addSelectionChangeListener(this);
        getGotoEventSource().addGotoListener(this);
    
        getEntryGroup().addEntryGroupChangeListener(this);
        getEntryGroup().addEntryChangeListener(this);
        getEntryGroup().addFeatureChangeListener(this);
    
        getBases().addSequenceChangeListener(this, Bases.MIN_PRIORITY);
    
        Options.getOptions().addOptionChangeListener(this);
    
    tjc's avatar
    tjc committed
        setBackground(Color.white);
    
    tjc's avatar
    tjc committed
    
        DragSource dragSource = DragSource.getDefaultDragSource();
    
        dragSource.createDefaultDragGestureRecognizer(
           this,                             // component where drag originates
           DnDConstants.ACTION_COPY_OR_MOVE, // actions
           this);                            // drag gesture recognizer
    
        setDropTarget(new DropTarget(this,this));
    
    tjc's avatar
    tjc committed
      }
    
    
      /**
       *  Overriden to call fixCanvasSize()
       **/
      public void setVisible(final boolean visible) 
      {
        super.setVisible(visible);
        fixCanvasSize();
      }
    
      /**
       *  Set value of the show label flag.
       *  @param show_label Show labels if and only if this argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowLabels(boolean show_labels) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_labels != show_labels) 
        {
          this.show_labels = show_labels;
          fixCanvasSize();
        } 
      }
    
      /**
       *  Get the value of the "show label" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowLabels() 
    
    tjc's avatar
    tjc committed
      {
        return show_labels;
      }
    
      /**
       *  Set value of the "show forward frame lines" flag.
       *  @param show_forward_lines Show forward frame lines if and only if
       *    this argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowForwardFrameLines(boolean show_forward_lines) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_forward_lines != show_forward_lines) 
        {
          this.show_forward_lines = show_forward_lines;
          fixCanvasSize();
        } 
      }
    
      /**
       *  Get the value of the "show forward frame lines" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowForwardFrameLines() 
    
    tjc's avatar
    tjc committed
      {
        return show_forward_lines;
      }
    
      /**
       *  Set value of the "show reverse frame lines" flag.
       *  @param show_reverse_lines Show frame lines if and only if this
       *    argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowReverseFrameLines(boolean show_reverse_lines) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_reverse_lines != show_reverse_lines) 
        {
          this.show_reverse_lines = show_reverse_lines;
          fixCanvasSize();
        }
      }
    
      /**
       *  Get the value of the "show source features" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowSourceFeatures() 
    
    tjc's avatar
    tjc committed
      {
        return show_source_features;
      }
    
      /**
       *  Set value of the "show source features" flag.
       *  @param show_source_features Show features with a "source" key if and
       *    only if this argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowSourceFeatures(boolean show_source_features) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_source_features != show_source_features) 
        {
          this.show_source_features = show_source_features;
          needVisibleFeatureVectorUpdate();
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        } 
      }
    
      /**
       *  Get the value of the "show frame lines" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowReverseFrameLines()
    
    tjc's avatar
    tjc committed
      {
        return show_reverse_lines;
      }
    
      /**
       *  Set value of the show base colours flag.
       *  @param show_base_colours At scale_factor less than two show each base in
       *    a different colour if and only if this argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowBaseColours(boolean show_base_colours) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_base_colours != show_base_colours) 
        {
          this.show_base_colours = show_base_colours;
          if(getScaleFactor() > 1) 
            setScaleFactor(1);
          repaint();
        } 
      }
    
      /**
       *  Get the value of the "show base colours" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowBaseColours() 
    
    tjc's avatar
    tjc committed
      {
        return show_base_colours;
      }
    
      /**
       *  Set value of the "one line per entry" flag.
       *  @param one_line_per_entry If true then each entry will be shown on a
       *    different line, instead of showing frame lines.
       **/
    
    tjc's avatar
    tjc committed
      protected void setOneLinePerEntry(final boolean one_line_per_entry)
    
    tjc's avatar
    tjc committed
      {
        if(this.one_line_per_entry != one_line_per_entry) 
        {
          this.one_line_per_entry = one_line_per_entry;
          fixCanvasSize();
        }
      }
    
      /**
       *  Get the value of the "one line per entry" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getOneLinePerEntryFlag() 
    
    tjc's avatar
    tjc committed
      {
        return one_line_per_entry;
      }
    
      /**
       *  Set value of the "hard left edge" flag.
       *  @param hard_left_edge If true the there will never be a gap between the
       *    left edge of the screen and the first visible base.  If false base one
       *    can be moved to the centre of the display.
       **/
    
    tjc's avatar
    tjc committed
      protected void setHardLeftEdge(final boolean hard_left_edge) 
    
    tjc's avatar
    tjc committed
      {
        if(this.hard_left_edge != hard_left_edge) 
        {
          this.hard_left_edge = hard_left_edge;
          if(hard_left_edge && getForwardBaseAtLeftEdge() < 1) 
            setFirstVisibleForwardBase(1);
          
          fixScrollbar();
        } 
      }
    
      /**
       *  Set value of the show stop codons flag.
       *  @param show_stop_codons Show stop codons if and only if this argument is
       *    true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowStopCodons(boolean show_stop_codons) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_stop_codons != show_stop_codons) 
        {
          this.show_stop_codons = show_stop_codons;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        } 
      }
    
      /**
       *  Return the value of the "show stop codons" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowStopCodons() 
      { 
    
    tjc's avatar
    tjc committed
        return show_stop_codons;
      }
    
      /**
       *  Set value of the show start codons flag.
       *  @param show_start_codons Show start codons if and only if this argument
       *    is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowStartCodons(boolean show_start_codons) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_start_codons != show_start_codons) 
        {
          this.show_start_codons = show_start_codons;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        } 
      }
    
      /**
       *  Return the value of the "show start codons" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowStartCodons() 
    
    tjc's avatar
    tjc committed
      {
        return show_start_codons;
      }
    
      /**
       *  Set value of the reverse complement display flag.
       *  @param show_start_codons Draw all features and sequence reverse
       *    complemented if and only if this argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setRevCompDisplay(boolean rev_comp_display) 
    
    tjc's avatar
    tjc committed
      {
        if(this.rev_comp_display != rev_comp_display) 
        {
          this.rev_comp_display = rev_comp_display;
          int remember_position = getCentreForwardBase();
    
          // we want to keep the selection visible after the flip, so
          // that will override the centre position
          final Marker first_base_marker =
            getSelection().getStartBaseOfSelection();
    
          if(first_base_marker != null && baseVisible(first_base_marker)) 
            remember_position = first_base_marker.getRawPosition();
    
          final Marker last_base_marker =
            getSelection().getStartBaseOfSelection();
    
          if(last_base_marker != null && baseVisible(last_base_marker)) 
            remember_position = last_base_marker.getRawPosition();
    
          fireAdjustmentEvent(DisplayAdjustmentEvent.REV_COMP_EVENT);
    
          makeBaseVisibleInternal(remember_position, isRevCompDisplay(), true);
    
          needVisibleFeatureVectorUpdate();
          fixScrollbar();
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        }
      }
    
      /**
       *  Return the value of the "reverse complement display" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean isRevCompDisplay() 
    
    tjc's avatar
    tjc committed
      {
        return rev_comp_display;
      }
    
      /**
       *  Set value of the show feature arrows flag.
       *  @param show_feature_arrows Show directional arrows if and only if this
       *    argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowFeatureArrows(boolean show_feature_arrows)
    
    tjc's avatar
    tjc committed
      {
        if(this.show_feature_arrows != show_feature_arrows)
        {
          this.show_feature_arrows = show_feature_arrows;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        } 
      }
    
      /**
       *  Return the value of the "show feature arrows" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowFeatureArrows() 
    
    tjc's avatar
    tjc committed
      {
        return show_feature_arrows;
      }
    
      /**
       *  Set value of the show feature borders flag.
       *  @param show_feature_borders Draw a border around each feature if and
       *    only if this argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowFeatureBorders(boolean show_feature_borders) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_feature_borders != show_feature_borders) 
        {
          this.show_feature_borders = show_feature_borders;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        } 
      }
    
      /**
       *  Return the value of the "show feature borders" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowFeatureBorders() 
    
    tjc's avatar
    tjc committed
      {
        return show_feature_borders;
      }
    
      /**
       *  Set value of the show frame features flag.
       *  @param frame_features_flag All features(not just CDS features) will be
       *    drawn on the frame lines if and only if this argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setFrameFeaturesFlag(boolean frame_features_flag) 
    
    tjc's avatar
    tjc committed
      {
        if(this.frame_features_flag != frame_features_flag) 
        {
          this.frame_features_flag = frame_features_flag;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        } 
      }
    
      /**
       *  Return the value of the "show frame features" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getFrameFeaturesFlag() 
    
    tjc's avatar
    tjc committed
      {
        return frame_features_flag;
      }
    
      /**
       *  Set the value of the minimum score for this FeatureDisplay - features
       *  that have a /score lower than this value are never shown.
       **/
    
    tjc's avatar
    tjc committed
      protected void setMinimumScore(final int minimum_score) 
    
    tjc's avatar
    tjc committed
      {
        current_min_score = minimum_score;
        needVisibleFeatureVectorUpdate();
    
    tjc's avatar
    tjc committed
        repaint();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Return the value of the minimum score for this FeatureDisplay - see
       *  setMinimumScore().
       **/
    
    tjc's avatar
    tjc committed
      private int getMinimumScore() 
    
    tjc's avatar
    tjc committed
      {
        return current_min_score;
      }
    
      /**
       *  Set the value of the maximum score for this FeatureDisplay - features
       *  that have a /score higher than this value are never shown.
       **/
    
    tjc's avatar
    tjc committed
      protected void setMaximumScore(final int maximum_score) 
    
    tjc's avatar
    tjc committed
      {
        current_max_score = maximum_score;
        needVisibleFeatureVectorUpdate();
    
    tjc's avatar
    tjc committed
        repaint();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Return the value of the maximum score for this FeatureDisplay - see
       *  setMaximumScore().
       **/
    
    tjc's avatar
    tjc committed
      private int getMaximumScore() 
    
    tjc's avatar
    tjc committed
      {
        return current_max_score;
      }
    
      /**
       *  Adds the specified event adjustment listener to receive adjustment
       *  change events from this object.
       *  @param l the event change listener.
       **/
    
    tjc's avatar
    tjc committed
      protected void addDisplayAdjustmentListener(DisplayAdjustmentListener l) 
    
    tjc's avatar
    tjc committed
      {
        adjustment_listener_list.addElement(l);
      }
    
      /**
       *  Removes the specified event listener so that it no longer receives
       *  adjustment change events from this object.
       *  @param l the event change listener.
       **/
    
    tjc's avatar
    tjc committed
      protected void removeDisplayAdjustmentListener(DisplayAdjustmentListener l) 
    
    tjc's avatar
    tjc committed
      {
        adjustment_listener_list.removeElement(l);
      }
    
      /**
       *  Handle key press events.  This is static because making it non-static
       *  triggered a java.lang.VerifyError
       **/
      private static void handleKeyPress(final FeatureDisplay feature_display,
                                          final KeyEvent event) 
      {
        // this is done so that menu shortcuts don't cause each action to be
        // performed twice
        if(event.getModifiers() != 0) 
          return;
    
        switch(event.getKeyCode()) 
        {
          case ZOOM_TO_SELECTION_KEY:
            FeaturePopup.zoomToSelection(feature_display);
            break;
          default:
            break;
        }
      }
    
      /**
       *  Set the scale factor and update the display if the scale factor has
       *  changed.  A factor of zero means the full translation will be visible.
       *  At higher scale factors only stop codons are visible, and a bigger
       *  number will mean more bases are visible.
       **/
    
    tjc's avatar
    tjc committed
      protected void setScaleFactor(int scale_factor) 
    
    tjc's avatar
    tjc committed
      {
        if(this.scale_factor != scale_factor) 
        {
          // we will try to keep the base in the centre of the view to stay where
          // it is, so we save it's position in remember_position.
          int remember_position = getCentreForwardBase();
    
          // if the first base is visible then keep it visible
          if(hard_left_edge && getFirstVisibleForwardBase() == 1) 
            remember_position = 1;
    
          if(!getSelection().isEmpty()) 
          {
            // but, we want to keep the selection visible after a scale change, so
            // that will override the centre position
            final Marker first_base_marker =
              getSelection().getStartBaseOfSelection();
    
            final int first_base_marker_raw_position =
                      first_base_marker.getRawPosition();
            final int first_base_marker_position;
    
            if(isRevCompDisplay()) 
              first_base_marker_position =
                  getBases().getComplementPosition(first_base_marker_raw_position);
            else 
              first_base_marker_position = first_base_marker_raw_position;
    
            final Marker last_base_marker =
    
    tjc's avatar
    tjc committed
                                       getSelection().getEndBaseOfSelection();
    
    tjc's avatar
    tjc committed
    
            final int last_base_marker_raw_position =
                                       last_base_marker.getRawPosition();
    
            final int last_base_marker_position;
    
            if(isRevCompDisplay()) 
              last_base_marker_position =
                getBases().getComplementPosition(last_base_marker_raw_position);
            else 
              last_base_marker_position = last_base_marker_raw_position;
    
            final int lowest_visible_base = getFirstVisibleForwardBase();
            final int highest_visible_base = getLastVisibleForwardBase();
    
            // first selected base or first visible base, whichever is greater
            int restricted_first_selected_base = lowest_visible_base;
    
            // last selected base or last visible base, whichever is smaller
            int restricted_last_selected_base = highest_visible_base;
    
            if(first_base_marker != null) 
            {
              if(first_base_marker_position > lowest_visible_base &&
                  first_base_marker_position < highest_visible_base) 
                restricted_first_selected_base = first_base_marker_position;
            }
    
            if(last_base_marker != null) 
            {
              if(last_base_marker_position < highest_visible_base &&
                  last_base_marker_position > lowest_visible_base) 
                restricted_last_selected_base = last_base_marker_position;
            }
    
            if(getSelection().getMarkerRange() == null) 
              remember_position = restricted_first_selected_base;
            else 
            {
              // keep the centre of the selection in the middle of the display if
              // a range of bases is selected
              remember_position = restricted_first_selected_base +
                                 (restricted_last_selected_base -
                                   restricted_first_selected_base) / 2;
            }
          }
    
          this.scale_factor = scale_factor;
    
          setScaleValue();
    
    tjc's avatar
    tjc committed
          
          if(scale_changer != null)
            scale_changer.setValue(scale_factor);
    
    tjc's avatar
    tjc committed
          setCentreVisibleForwardBase(remember_position);
          fixScrollbar();
          fireAdjustmentEvent(DisplayAdjustmentEvent.SCALE_ADJUST_EVENT);
          needVisibleFeatureVectorUpdate();
    
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        }
      }
    
      /**
       *  Implementation of the FeatureChangeListener interface.  We listen to
       *  FeatureChange events so that we can update the display if qualifiers
       *  change.
       **/
      public void featureChanged(final FeatureChangeEvent event) 
      {
        final Feature event_feature = event.getFeature();
    
        // the feature isn't in an active entry
        if(!getEntryGroup().contains(event_feature)) 
          return;
    
        // if the feature is visible now or is in the list of visible features
        //(ie. it was visible previously) then redisplay.
        if(featureVisible(event_feature) ||
           getVisibleFeatures().contains(event_feature)) 
        {
          // update the visible_features vector
          if(getVisibleFeatures().contains(event_feature) &&
             !featureVisible(event_feature)) 
            getVisibleFeatures().remove(event_feature);
          else 
          {
            // the visibility of the feature has changed
            if(!getVisibleFeatures().contains(event_feature) &&
                featureVisible(event_feature)) 
              getVisibleFeatures().add(event_feature);
          }
    
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        }
      }
    
      /**
       *  Implementation of the EntryGroupChangeListener interface.  We listen to
       *  EntryGroupChange events so that we can update the display if entries
       *  are added or deleted.
       **/
      public void entryGroupChanged(final EntryGroupChangeEvent event) 
      {
        switch(event.getType()) 
        {
          case EntryGroupChangeEvent.ENTRY_ADDED:
          case EntryGroupChangeEvent.ENTRY_ACTIVE:
          case EntryGroupChangeEvent.ENTRY_DELETED:
          case EntryGroupChangeEvent.ENTRY_INACTIVE:
          if(getOneLinePerEntryFlag()) 
            fixCanvasSize();
          
          needVisibleFeatureVectorUpdate();
          break;
        }
    
    
    tjc's avatar
    tjc committed
        repaint();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Implementation of the EntryChangeListener interface.  We listen to
       *  EntryChange events so that we can update the display if features are
       *  added or deleted.
       **/
      public void entryChanged(final EntryChangeEvent event) 
      {
        switch(event.getType()) 
        {
          case EntryChangeEvent.FEATURE_DELETED:
            remove(event.getFeature());
            break;
          case EntryChangeEvent.FEATURE_ADDED:
            add(event.getFeature());
            break;
        }
      }
    
      /**
       *  Implementation of the SelectionChangeListener interface.  We listen to
       *  SelectionChange events so that we can update the list to reflect the
       *  current selection.
       **/
      public void selectionChanged(final SelectionChangeEvent event) 
      {
        // don't bother with events we sent ourself
        if(event.getSource() == this) 
          return;
    
        needVisibleFeatureVectorUpdate();
    
        if(event.getType() == SelectionChangeEvent.SELECTION_CHANGED) 
          raise_selection_flag = true;