Skip to content
Snippets Groups Projects
FeatureList.java 29.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • tjc's avatar
    tjc committed
    /* FeatureList.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/FeatureList.java,v 1.27 2008-10-23 14:38:55 tjc Exp $
    
    tjc's avatar
    tjc committed
     */
    
    package uk.ac.sanger.artemis.components;
    
    import uk.ac.sanger.artemis.*;
    import uk.ac.sanger.artemis.sequence.*;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.plot.CodonUsageAlgorithm;
    
    tjc's avatar
    tjc committed
    
    
    import uk.ac.sanger.artemis.components.genebuilder.GeneUtils;
    
    import uk.ac.sanger.artemis.io.GFFStreamFeature;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.io.Qualifier;
    import uk.ac.sanger.artemis.io.InvalidRelationException;
    import uk.ac.sanger.artemis.io.EntryInformation;
    import uk.ac.sanger.artemis.io.QualifierInfo;
    import uk.ac.sanger.artemis.io.QualifierVector;
    import uk.ac.sanger.artemis.io.StreamQualifier;
    
    import uk.ac.sanger.artemis.util.DatabaseDocument;
    
    tjc's avatar
    tjc committed
    import uk.ac.sanger.artemis.util.StringVector;
    
    
    tjc's avatar
    tjc committed
    import java.awt.event.MouseEvent;
    import java.awt.event.InputEvent;
    import java.awt.event.MouseAdapter;
    
    tjc's avatar
    tjc committed
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    tjc's avatar
    tjc committed
    import java.awt.Container;
    import java.awt.Color;
    import java.awt.Point;
    import java.awt.Graphics;
    import java.awt.Dimension;
    
    tjc's avatar
    tjc committed
    import java.awt.Toolkit;
    
    tjc's avatar
    tjc committed
    import java.text.NumberFormat;
    
    tjc's avatar
    tjc committed
    
    import javax.swing.JFrame;
    
    tjc's avatar
    tjc committed
    import javax.swing.JScrollPane;
    import javax.swing.JViewport;
    import javax.swing.JComponent;
    
    tjc's avatar
    tjc committed
    
    /**
     *  This component gives the user a list containing the details the current
     *  Features.
     *
     *  @author Kim Rutherford
    
     *  @version $Id: FeatureList.java,v 1.27 2008-10-23 14:38:55 tjc Exp $
    
    tjc's avatar
    tjc committed
     *
     **/
    
    public class FeatureList extends EntryGroupPanel
      implements EntryGroupChangeListener,
                 EntryChangeListener, FeatureChangeListener,
                 SelectionChangeListener, DisplayComponent
    {
    
    tjc's avatar
    tjc committed
    
    
      /** true if correlation scores should be shown */
    
    tjc's avatar
    tjc committed
      private boolean show_correlation_scores = false;
    
    
      /** set to true by selectionChanged() and used by paintComponent(). */
    
    tjc's avatar
    tjc committed
      private boolean selection_changed_flag = false;
    
      /** colour used to draw the background. */
      private Color background_colour = Color.white;
    
      /**
    
    tjc's avatar
    tjc committed
       *  If true this component will show Feature.getIDString() (ie /gene or
    
    tjc's avatar
    tjc committed
       *  /label) instead of the key.
       **/
      private boolean show_gene_names = false;
    
    
    tjc's avatar
    tjc committed
      private String user_defined_qualifier = null;
    
    
      /** show Feature.getSystematicName() */
      private boolean show_systematic_names = false;
       
    
    tjc's avatar
    tjc committed
      /** show the /product qualifier instead of /note field. */
    
    tjc's avatar
    tjc committed
      private boolean show_products = false;
    
    
    tjc's avatar
    tjc committed
      /** show all the qualifiers after the note. */
    
    tjc's avatar
    tjc committed
      private boolean show_qualifiers = false;
    
      /**
       *  The is the maximum width of the strings containing the feature start and
       *  stop positions.  Set in the constructor.
       **/
      private int max_base_pos_width;
    
    
    tjc's avatar
    tjc committed
      /** JScrollPane viewport that this panel is the view of */
      private JViewport viewport = null;
    
    
      private boolean isDatabaseGroup = false;
      
    
    tjc's avatar
    tjc committed
      /**
       *  Create a new FeatureList with the default number of rows.
       *  @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
    
    tjc's avatar
    tjc committed
       *    gotoBase().
    
    tjc's avatar
    tjc committed
       **/
    
      public FeatureList(final EntryGroup entry_group,
                         final Selection selection,
                         final GotoEventSource goto_event_source,
    
    tjc's avatar
    tjc committed
                         final BasePlotGroup base_plot_group)
    
    tjc's avatar
    tjc committed
      {
        super(entry_group, selection, goto_event_source, base_plot_group);
    
    tjc's avatar
    tjc committed
    
    
        isDatabaseGroup = GeneUtils.isDatabaseEntry(getEntryGroup());
    
    
    tjc's avatar
    tjc committed
        addMouseListener(new MouseAdapter() 
    
    tjc's avatar
    tjc committed
        {
    
    tjc's avatar
    tjc committed
          private FeaturePopup popup = null;
    
    
    tjc's avatar
    tjc committed
          /**
           *  Listen for mouse press events so that we can do popup menus and
           *  selection.
           **/
    
    tjc's avatar
    tjc committed
          public void mousePressed(MouseEvent event)
          {
            if(isMenuTrigger(event)) 
            {
    
    tjc's avatar
    tjc committed
              if(popup == null)
                popup = new FeaturePopup(FeatureList.this,
                                         getEntryGroup(),
                                         getSelection(),
                                         getGotoEventSource(),
                                         getBasePlotGroup());
    
    tjc's avatar
    tjc committed
              final JComponent parent = (JComponent)event.getSource();
    
              popup.show(parent, event.getX(), event.getY());
            } 
            else 
              handleCanvasMousePress(event);
    
    tjc's avatar
    tjc committed
          }
        });
    
    
    tjc's avatar
    tjc committed
        getSelection().addSelectionChangeListener(this);
    
    tjc's avatar
    tjc committed
    
        // changes to the EntryGroup will be noticed by listening for EntryChange
        // and FeatureChange events.
    
    
    tjc's avatar
    tjc committed
        getEntryGroup().addEntryGroupChangeListener(this);
        getEntryGroup().addEntryChangeListener(this);
        getEntryGroup().addFeatureChangeListener(this);
    
    tjc's avatar
    tjc committed
    
        // find the maximum posible width for the high and low positions
    
    tjc's avatar
    tjc committed
        final int sequence_length = getEntryGroup().getSequenceLength();
        max_base_pos_width = (int)(Math.log(sequence_length)/Math.log(10)) + 1;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(max_base_pos_width < 4) 
    
    tjc's avatar
    tjc committed
          max_base_pos_width = 4;
    
    
    tjc's avatar
    tjc committed
        setBackground(background_colour);
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Remove this component from all the listener lists it is on.
       **/
    
    tjc's avatar
    tjc committed
      void stopListening()
      {
    
    tjc's avatar
    tjc committed
        getSelection().removeSelectionChangeListener(this);
        getEntryGroup().removeEntryGroupChangeListener(this);
        getEntryGroup().removeEntryChangeListener(this);
        getEntryGroup().removeFeatureChangeListener(this);
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Set value of the show correlation scores flag.
       *  @param show_correlation_scores Show correlation scores in the list if
       *    and only if this argument is true.
       **/
    
    tjc's avatar
    tjc committed
      protected void setCorrelationScores(final boolean show_correlation_scores) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_correlation_scores != show_correlation_scores) 
        {
    
    tjc's avatar
    tjc committed
          this.show_correlation_scores = show_correlation_scores;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        } 
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Get the value of the "show correlation scores" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getCorrelationScores() 
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        return show_correlation_scores;
      }
    
      /**
       *  Set value of the show /gene flag.
       *  @param show_gene_names If true this component will show the /gene (really
    
    tjc's avatar
    tjc committed
       *    Feature.getIDString()) instead of the key.
    
    tjc's avatar
    tjc committed
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowGenes(final boolean show_gene_names) 
    
    tjc's avatar
    tjc committed
      {
    
        if(this.show_gene_names != show_gene_names) 
    
    tjc's avatar
    tjc committed
        {
    
    tjc's avatar
    tjc committed
          this.show_gene_names = show_gene_names;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        }
      }
    
    
    
      /**
       *  Set value of the show /systematic_id flag.
       *  @param show_systematic_names If true this component will show the /gene (really
       *    Feature.getSystematicName()) instead of the key.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowSystematicID(final boolean show_systematic_names)
    
      {
        if(this.show_systematic_names != show_systematic_names)
        {
          this.show_systematic_names = show_systematic_names;
          repaint();
        }
      }
    
    
    
    tjc's avatar
    tjc committed
      protected void setShowUserDefinedQualifier(final String user_defined_qualifier)
      {
        this.user_defined_qualifier = user_defined_qualifier;
        repaint();
      }
    
      
      protected StringVector getShowUserDefinedQualifier()
      {
        if(user_defined_qualifier == null)
          return null;
        return StringVector.getStrings(user_defined_qualifier);
      }
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
      /**
       *  Get the value of the "show genes" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowGenes() 
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        return show_gene_names;
      }
    
    
    
      /**
       *  Get the value of the "show systematic id" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowSysID()
    
      {
        return show_systematic_names;
      }
    
    
    
    tjc's avatar
    tjc committed
      /**
       *  Set value of the show qualifiers flag.
       *  @param show_quailfiers If true this component will show all the
       *    qualifiers after the note.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowQualifiers(final boolean show_qualifiers) 
    
    tjc's avatar
    tjc committed
      {
        if(this.show_qualifiers != show_qualifiers) 
        {
    
    tjc's avatar
    tjc committed
          if(show_qualifiers)
            user_defined_qualifier = null;
    
    
    tjc's avatar
    tjc committed
          this.show_qualifiers = show_qualifiers;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        }
      }
    
      /**
       *  Get the value of the "show qualifiers" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowQualifiers() 
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        return show_qualifiers;
      }
    
      /**
       *  Set value of the show /product flag.
       *  @param show_products If true this component will show the /product
       *    qualifier instead of the /note.
       **/
    
    tjc's avatar
    tjc committed
      protected void setShowProducts(final boolean show_products) 
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        if(this.show_products != show_products) 
    
    tjc's avatar
    tjc committed
        {
    
    tjc's avatar
    tjc committed
          if(show_products)
            user_defined_qualifier = null;
    
    
    tjc's avatar
    tjc committed
          this.show_products = show_products;
    
    tjc's avatar
    tjc committed
          repaint();
    
    tjc's avatar
    tjc committed
        }
      }
    
      /**
       *  Get the value of the "show products" flag.
       **/
    
    tjc's avatar
    tjc committed
      protected boolean getShowProducts() 
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        return show_products;
      }
    
    tjc's avatar
    tjc committed
      
    
    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.
       **/
    
    tjc's avatar
    tjc committed
      public void entryGroupChanged(EntryGroupChangeEvent event) 
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        final int hgt = getEntryGroup().getAllFeaturesCount() *
                                   getLineHeight();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        setPreferredSize(new Dimension(getSize().width*4,hgt));
    
    tjc's avatar
    tjc committed
        revalidate();
        repaint();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Implementation of the FeatureChangeListener interface.
       **/
    
    tjc's avatar
    tjc committed
      public void featureChanged(FeatureChangeEvent event) 
    
    tjc's avatar
    tjc committed
      {
        if(!isVisible()) 
    
    tjc's avatar
    tjc committed
          return;
    
    
    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 list if features are added
       *  or deleted.
       **/
    
    tjc's avatar
    tjc committed
      public void entryChanged(EntryChangeEvent event) 
    
    tjc's avatar
    tjc committed
      {
        if(!isVisible()) 
    
    tjc's avatar
    tjc committed
          return;
    
    
    tjc's avatar
    tjc committed
        repaint();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Implementation of the SelectionChangeListener interface.  We listen to
       *  SelectionChange events so that we can update the list to reflect the
       *  current selection.
       **/
    
    tjc's avatar
    tjc committed
      public void selectionChanged(SelectionChangeEvent event) 
    
    tjc's avatar
    tjc committed
      {
        if(!isVisible())
    
    tjc's avatar
    tjc committed
          return;
    
    
    tjc's avatar
    tjc committed
        // don't bother with events we sent ourself
    
    tjc's avatar
    tjc committed
        if(event.getSource() == this) 
    
    tjc's avatar
    tjc committed
          return;
    
    
    tjc's avatar
    tjc committed
        // if the selected range changes we don't care
    
    tjc's avatar
    tjc committed
        if(getSelection().getMarkerRange() != null &&
           event.getType() == SelectionChangeEvent.OBJECT_CHANGED) 
    
    tjc's avatar
    tjc committed
          return;
    
        selection_changed_flag = true;
    
    tjc's avatar
    tjc committed
    
        onSelectionChange();
    
    tjc's avatar
    tjc committed
        repaint();
    
    tjc's avatar
    tjc committed
      }
    
      /**
    
    tjc's avatar
    tjc committed
      * Return the JViewport that this component is contained in.
      */
    
      private JViewport getViewport()
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        if(viewport != null)
          return viewport;
    
    
    tjc's avatar
    tjc committed
        Container container = getParent();
        while(!(container instanceof JScrollPane))
          container = container.getParent();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        viewport = ((JScrollPane)container).getViewport();
        return viewport;
    
    tjc's avatar
    tjc committed
      }
    
      /**
    
    tjc's avatar
    tjc committed
      *
      * Find the point at the top right hand corner of the
      * scroll pane.
      *
      */
      private Point getScrollPoint()
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        return getViewport().getViewPosition();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Handle a mouse press event on the drawing canvas - select on click,
       *  select and broadcast it on double click.
       **/
    
    tjc's avatar
    tjc committed
      private void handleCanvasMousePress(final MouseEvent event)
      {
        if(event.getID() != MouseEvent.MOUSE_PRESSED) 
    
    tjc's avatar
    tjc committed
          return;
    
    
    tjc's avatar
    tjc committed
        requestFocus();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(!event.isShiftDown()) 
    
    tjc's avatar
    tjc committed
          getSelection().clear();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final int clicked_feature_index = event.getY()/getLineHeight();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(clicked_feature_index < getEntryGroup().getAllFeaturesCount())
        {
    
    tjc's avatar
    tjc committed
          final FeatureVector selected_features =
    
    tjc's avatar
    tjc committed
            getSelection().getAllFeatures();
    
    tjc's avatar
    tjc committed
    
          final Feature clicked_feature =
    
    tjc's avatar
    tjc committed
            getEntryGroup().featureAt(clicked_feature_index);
    
          if(selected_features.contains(clicked_feature)) 
          {
    
    tjc's avatar
    tjc committed
            getSelection().remove(clicked_feature);
            getSelection().removeSegmentsOf(clicked_feature);
    
    tjc's avatar
    tjc committed
          } 
          else 
            getSelection().add(clicked_feature);
    
          if(event.getClickCount() == 2) 
          {
            makeSelectionVisible();
    
            if((event.getModifiers() & InputEvent.BUTTON2_MASK) != 0 ||
                event.isAltDown()) 
            {
              if(Options.readWritePossible()) 
    
    tjc's avatar
    tjc committed
              {
                final JFrame frame = new JFrame("Artemis Feature Edit: " + 
                    clicked_feature.getIDString() +
                    (clicked_feature.isReadOnly() ?
                        "  -  (read only)" :
                        ""));
                
                final FeatureEdit fe = new FeatureEdit(clicked_feature, getEntryGroup(),
                                           getSelection(), getGotoEventSource(), frame);
                frame.addWindowListener(new WindowAdapter() 
                {
                  public void windowClosing(WindowEvent event) 
                  {
                    fe.stopListening();
                    frame.dispose();
                  }
                });
                
                frame.getContentPane().add(fe);
                frame.pack();
    
                final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
                frame.setLocation(new Point((screen.width - getSize().width)/2,
                                            (screen.height - getSize().height)/2));
                frame.setVisible(true);
              }
    
    tjc's avatar
    tjc committed
            }
          }
    
    tjc's avatar
    tjc committed
        }
      }
    
    
    tjc's avatar
    tjc committed
      private void onSelectionChange()
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        if(!selection_changed_flag)
    
    tjc's avatar
    tjc committed
          return;
    
    
    tjc's avatar
    tjc committed
        selection_changed_flag = false;
        final FeatureVector selected_features =
                             getSelection().getAllFeatures();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(selected_features.size() > 0)
        {
          Point viewPoint = getScrollPoint();
    
    tjc's avatar
    tjc committed
          final EntryGroup entry_group = getEntryGroup();
          final int feature_count = entry_group.getAllFeaturesCount();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          // set to true if any of the selected features is visible
          boolean a_selected_feature_is_visible = false;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          int first_line_in_view = viewPoint.y/getLineHeight();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          if(first_line_in_view == -1)
            first_line_in_view = 0;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          int numberLines = linesInView();
          for(int i = first_line_in_view;
    
    tjc's avatar
    tjc committed
              i < feature_count && i <= first_line_in_view + numberLines;
    
    tjc's avatar
    tjc committed
            final Feature this_feature = entry_group.featureAt(i);
    
    tjc's avatar
    tjc committed
            if(selected_features.contains(this_feature))
    
    tjc's avatar
    tjc committed
            {
    
    tjc's avatar
    tjc committed
              a_selected_feature_is_visible = true;
              break;
    
    tjc's avatar
    tjc committed
            }
    
    tjc's avatar
    tjc committed
          }
    
          if(!a_selected_feature_is_visible)
          {
            // make the first selected feature visible
            final Feature first_selected_feature =
              selected_features.elementAt(0);
    
            final int index_of_first_selected_feature =
    
    tjc's avatar
    tjc committed
                             entry_group.indexOf(first_selected_feature);
    
    tjc's avatar
    tjc committed
    
    
            if( index_of_first_selected_feature > -1 &&
               (index_of_first_selected_feature < first_line_in_view ||
                index_of_first_selected_feature >= first_line_in_view + numberLines))
    
    tjc's avatar
    tjc committed
            {
    
    tjc's avatar
    tjc committed
              getViewport().setViewPosition(new Point(0,
                                    index_of_first_selected_feature * getLineHeight()));
    
    tjc's avatar
    tjc committed
            }
          }
        }
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
      /**
       *  The main paint function for the canvas.  An off screen image used for
       *  double buffering when drawing the canvas.
       *  @param g The Graphics object of the canvas.
       **/
      protected void paintComponent(Graphics g) 
      {
        super.paintComponent(g);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(!isVisible()) 
          return;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        Point viewPoint = getScrollPoint();
        final int feature_count = getEntryGroup().getAllFeaturesCount();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        // must check size in case new features/entries added
        if(feature_count*getLineHeight() > getPreferredSize().height)
        {
          final int hgt = feature_count * getLineHeight();
          setPreferredSize(new Dimension(getSize().width*4,hgt));
    
    tjc's avatar
    tjc committed
          revalidate();
    
    tjc's avatar
    tjc committed
        }
    
    
    tjc's avatar
    tjc committed
        if(feature_count != 0) 
    
    tjc's avatar
    tjc committed
        {
    
    tjc's avatar
    tjc committed
          final int lines_in_view = linesInView()+1;
          int first_index_in_view = (viewPoint.y/getLineHeight());
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          if(first_index_in_view == -1) 
    
    tjc's avatar
    tjc committed
            first_index_in_view = 0;
    
    
    tjc's avatar
    tjc committed
          int last_index_in_view;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          if(lines_in_view < feature_count - first_index_in_view) 
    
    tjc's avatar
    tjc committed
            last_index_in_view = first_index_in_view + lines_in_view;
    
    tjc's avatar
    tjc committed
          else 
    
    tjc's avatar
    tjc committed
            last_index_in_view = feature_count - 1;
    
          final FeatureVector features_in_view =
    
    tjc's avatar
    tjc committed
            getEntryGroup().getFeaturesInIndexRange(first_index_in_view,
                                                    last_index_in_view);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          g.setFont(getFont());
    
    tjc's avatar
    tjc committed
    
          final int features_in_view_size = features_in_view.size();
          for(int i = 0; i < features_in_view_size; i++)
    
    tjc's avatar
    tjc committed
          {
    
    tjc's avatar
    tjc committed
            final Feature this_feature  = features_in_view.elementAt(i);
    
    tjc's avatar
    tjc committed
            final String feature_string = makeFeatureString(this_feature, false);
    
    tjc's avatar
    tjc committed
            drawFeatureLine(g, this_feature, feature_string);
    
    tjc's avatar
    tjc committed
          }
        }
      }
    
    
    tjc's avatar
    tjc committed
      /**
       *  Return the number of visible text lines on canvas.
       **/
    
    tjc's avatar
    tjc committed
      private int linesInView() 
      {
    
    tjc's avatar
    tjc committed
        return getViewport().getExtentSize().height/getLineHeight();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Draw the given Feature at the given line of the list, taking the
       *  selection into account.
       **/
    
    tjc's avatar
    tjc committed
      private void drawFeatureLine(final Graphics g,
                                   final Feature feature,
    
    tjc's avatar
    tjc committed
                                   final String feature_string)
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        // width of coloured blob at the left of the text
    
    tjc's avatar
    tjc committed
        final int BOX_WIDTH = getLineHeight();
    
    tjc's avatar
    tjc committed
        final int y_pos = getEntryGroup().indexOf(feature)*BOX_WIDTH;
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final Color feature_colour = feature.getColour();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        // default colour is white
        if(feature_colour == null) 
    
    tjc's avatar
    tjc committed
          g.setColor(Color.white);
    
    tjc's avatar
    tjc committed
        else 
    
    tjc's avatar
    tjc committed
          g.setColor(feature_colour);
    
    tjc's avatar
    tjc committed
        
    
    tjc's avatar
    tjc committed
        g.fillRect(1, y_pos+1,
                   BOX_WIDTH, BOX_WIDTH - 1);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        g.setColor(Color.black);
    
    tjc's avatar
    tjc committed
        if(getSelection().contains(feature)) 
        {
    
    tjc's avatar
    tjc committed
          // draw in reverse
    
    tjc's avatar
    tjc committed
          g.fillRect(BOX_WIDTH + 4, y_pos,
                     getSize().width + getScrollPoint().x,
    
    tjc's avatar
    tjc committed
                     getLineHeight());
          g.setColor(background_colour);
        } 
    
        
        if( feature.getEmblFeature() instanceof GFFStreamFeature &&
            !getSelection().contains(feature) &&
            !((GFFStreamFeature)feature.getEmblFeature()).isVisible() )
        {
          //
          // use gray for the key if the feature is NOT visible
          g.setColor(Color.gray);
          int ind = feature_string.indexOf(' ');
          final String keyString = feature_string.substring(0, ind);
          g.drawString(keyString,
              BOX_WIDTH + 5,
              y_pos + getFontAscent());
          
          g.setColor(Color.black);
          g.drawString(feature_string.substring(ind),
              BOX_WIDTH + 5 + getFontMetrics(getFont()).stringWidth(keyString),
              y_pos + getFontAscent());
        }
        else
          g.drawString(feature_string,
    
    tjc's avatar
    tjc committed
                     BOX_WIDTH + 5,
    
    tjc's avatar
    tjc committed
                     y_pos + getFontAscent());
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Return a String object suitable for displaying in the list of features.
       *  @param dont_truncate if true the gene name / key field won't be
       *    truncated if it is longer than the field width
       **/
    
    tjc's avatar
    tjc committed
      private String makeFeatureString(final Feature feature,
                                       final boolean dont_truncate) 
      {
    
    tjc's avatar
    tjc committed
        String key_string;
        final int KEY_FIELD_WIDTH = 15;
    
    
        if(show_gene_names)
    
    tjc's avatar
    tjc committed
        {
    
    tjc's avatar
    tjc committed
          key_string = feature.getGeneName();
    
          if(key_string == null)
            key_string = feature.getSystematicName();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          if(key_string.length() > KEY_FIELD_WIDTH && !dont_truncate) 
    
            key_string = key_string.substring(0, KEY_FIELD_WIDTH);
    
    tjc's avatar
    tjc committed
        } 
    
        else if(show_systematic_names)
        {
          key_string = feature.getSystematicName();
    
          if(key_string.length() > KEY_FIELD_WIDTH && !dont_truncate)
            key_string = key_string.substring(0, KEY_FIELD_WIDTH);
        }
    
    tjc's avatar
    tjc committed
        else 
    
    tjc's avatar
    tjc committed
          key_string = feature.getKey().toString();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final Marker low_marker  = feature.getFirstBaseMarker();
    
    tjc's avatar
    tjc committed
        final Marker high_marker = feature.getLastBaseMarker();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final StringBuffer description_string_buffer = new StringBuffer();
    
    tjc's avatar
    tjc committed
    
    
        if(user_defined_qualifier != null && !user_defined_qualifier.equals(""))
    
    tjc's avatar
    tjc committed
        {
          try
    
          { 
            StringVector sv = StringVector.getStrings(user_defined_qualifier);
            for(int i=0; i<sv.size(); i++)
            {
              final String user_defined_qualifier_string = 
                feature.getValueOfQualifier((String)sv.get(i));
              if(user_defined_qualifier_string != null)
                description_string_buffer.append("/"+sv.get(i)+"="+user_defined_qualifier_string+" ");
            }
    
    tjc's avatar
    tjc committed
          }
          catch(InvalidRelationException ire){}
        }
        else if(show_products) 
    
    tjc's avatar
    tjc committed
        {
          final String product_string = feature.getProductString();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          if(product_string == null) 
    
    tjc's avatar
    tjc committed
          {
    
    tjc's avatar
    tjc committed
            // description is not blank
            if(feature.isCDS())
              description_string_buffer.append("[no /product]");
    
    tjc's avatar
    tjc committed
          } 
          else 
    
    tjc's avatar
    tjc committed
            description_string_buffer.append(product_string);
    
    tjc's avatar
    tjc committed
        }
        else 
        {
    
          String note = null;
          if(isDatabaseGroup)
          {
            try
            {
              note = feature.getValueOfQualifier("comment");
            }
            catch(InvalidRelationException e){}
          }
          
          if(note == null)
            note = feature.getNote();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          if(note != null && note.length() != 0) 
          {
    
    tjc's avatar
    tjc committed
            final int QUALIFIER_COLUMN = 10;
    
            final String note_string =
    
              padRightWithSpaces(note, QUALIFIER_COLUMN);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
            description_string_buffer.append(note_string);
            description_string_buffer.append("   ");
    
    tjc's avatar
    tjc committed
          }
    
    
    tjc's avatar
    tjc committed
          if(show_qualifiers) 
    
    tjc's avatar
    tjc committed
            description_string_buffer.append(getQualifierString(feature));
    
    tjc's avatar
    tjc committed
        }
    
        final String low_pos;
        final String high_pos;
    
    
    tjc's avatar
    tjc committed
        if(low_marker == null || high_marker == null) 
        {
    
    tjc's avatar
    tjc committed
          low_pos  = "unknown";
    
    tjc's avatar
    tjc committed
          high_pos = "unknown";
    
    tjc's avatar
    tjc committed
        }
        else 
        {
          if(low_marker.getRawPosition() < high_marker.getRawPosition()) 
          {
            low_pos = String.valueOf(low_marker.getRawPosition());
            high_pos = String.valueOf(high_marker.getRawPosition());
          } 
          else
          {
            low_pos = String.valueOf(high_marker.getRawPosition());
            high_pos = String.valueOf(low_marker.getRawPosition());
    
    tjc's avatar
    tjc committed
          }
        }
    
    
    tjc's avatar
    tjc committed
        StringBuffer new_list_line = new StringBuffer();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        new_list_line.append(padRightWithSpaces(key_string, KEY_FIELD_WIDTH));
        new_list_line.append(" ");
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        new_list_line.append(padLeftWithSpaces(low_pos, max_base_pos_width));
        new_list_line.append(" ");
        new_list_line.append(padLeftWithSpaces(high_pos, max_base_pos_width));
    
    tjc's avatar
    tjc committed
        new_list_line.append(" ");
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(feature.isForwardFeature()) 
          new_list_line.append("   ");
        else
          new_list_line.append("c  ");
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(show_correlation_scores)
        {
    
          if(feature.isCDS() || 
             feature.getKey().getKeyString().equals(DatabaseDocument.EXONMODEL)) 
    
    tjc's avatar
    tjc committed
          {
    
    tjc's avatar
    tjc committed
            new_list_line.append(getScoresString(feature));
            new_list_line.append("  ");
    
    tjc's avatar
    tjc committed
          } 
          else 
          {
            new_list_line.append("                         ");
    
    tjc's avatar
    tjc committed
            if(getBasePlotGroup().getCodonUsageAlgorithm() != null) 
              new_list_line.append("      ");
    
    tjc's avatar
    tjc committed
          }
        }
    
    
    tjc's avatar
    tjc committed
        new_list_line.append(description_string_buffer.toString());
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        return new_list_line.toString();
    
    tjc's avatar
    tjc committed
      }
    
    
      /**
       *  Return a String containing the given Qualifier and it's values (in EMBL
       *  format).
       *  @param start_index ignore the values before this index
       **/
    
    tjc's avatar
    tjc committed
      private String formatQualifier(final String qualifier_name,
                                     final Feature feature,
                                     final int start_index) 
      {
        final StringBuffer buffer = new StringBuffer();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        try 
        {
    
    tjc's avatar
    tjc committed
          final Qualifier qualifier = feature.getQualifierByName(qualifier_name);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          if(qualifier != null) 
          {
    
    tjc's avatar
    tjc committed
            final EntryInformation entry_information =
    
    tjc's avatar
    tjc committed
              feature.getEntry().getEntryInformation();
    
    tjc's avatar
    tjc committed
    
            final QualifierInfo qualifier_info =
    
    tjc's avatar
    tjc committed
              entry_information.getQualifierInfo(qualifier_name);
    
    tjc's avatar
    tjc committed
    
            final StringVector qualifier_strings =
    
    tjc's avatar
    tjc committed
              StreamQualifier.toStringVector(qualifier_info,
                                             qualifier);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
            final int qualifier_strings_size = qualifier_strings.size();
            for(int i = start_index; i < qualifier_strings_size; ++i)
    
    tjc's avatar
    tjc committed
            {
    
    tjc's avatar
    tjc committed
              final String qualifier_string = (String)qualifier_strings.elementAt(i);
    
    tjc's avatar
    tjc committed
              buffer.append(qualifier_string + " ");
    
    tjc's avatar
    tjc committed
            }
          }
    
    tjc's avatar
    tjc committed
        } 
    
    tjc's avatar
    tjc committed
        catch(InvalidRelationException e) {}
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        return buffer.toString();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Return a String containing all the qualifiers of the given Feature
       *  (except /note) in EMBL format.  Any /similarity qualifier will come
       *  first.
       **/
    
    tjc's avatar
    tjc committed
      private String getQualifierString(final Feature feature) 
      {
        final StringBuffer buffer = new StringBuffer();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final QualifierVector qualifiers = feature.getQualifiers();
    
    tjc's avatar
    tjc committed
    
        // if there is a /note and it has more than one value put it next (without
        // the first value)
        final Qualifier note_qualifier =
    
    tjc's avatar
    tjc committed
          qualifiers.getQualifierByName("note");
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(note_qualifier != null && note_qualifier.getValues().size() > 1) 
        {
          buffer.append(formatQualifier("note", feature, 1));
          buffer.append(" ");
    
    tjc's avatar
    tjc committed
        }
    
        // put /similarity before all but the /note qualifier
        final Qualifier similarity_qualifier =
    
    tjc's avatar
    tjc committed
          qualifiers.getQualifierByName("similarity");
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(similarity_qualifier != null) 
        {
    
    tjc's avatar
    tjc committed
          buffer.append(formatQualifier("similarity", feature, 0));
          buffer.append(" ");
    
    tjc's avatar
    tjc committed
        }
    
    
    tjc's avatar
    tjc committed
        final int qualifiers_size = qualifiers.size();
        for(int i = 0 ; i < qualifiers_size; ++i) 
    
    tjc's avatar
    tjc committed
        {
    
    tjc's avatar
    tjc committed
          final Qualifier this_qualifier = (Qualifier)qualifiers.elementAt(i);
    
    tjc's avatar
    tjc committed
          final String this_qualifier_name = this_qualifier.getName();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
          if(!this_qualifier_name.equals("note") &&
             !this_qualifier_name.equals("similarity")) 
    
    tjc's avatar
    tjc committed
          {
    
    tjc's avatar
    tjc committed
            buffer.append(formatQualifier(this_qualifier_name, feature, 0));
            buffer.append(" ");
    
    tjc's avatar
    tjc committed
          }
        }
    
    
    tjc's avatar
    tjc committed
        return buffer.toString();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Return a String containing the correlation scores.
       **/
    
    tjc's avatar
    tjc committed
      protected String getScoresString(final Feature feature)
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        final int base_total = feature.getTranslationBases().length();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final int c_total = feature.getBaseCount(Bases.getIndexOfBase('c'));
        final int g_total = feature.getBaseCount(Bases.getIndexOfBase('g'));
    
    tjc's avatar
    tjc committed
    
        final int g1_count =
    
    tjc's avatar
    tjc committed
          feature.getPositionalBaseCount(0, Bases.getIndexOfBase('g'));
    
    tjc's avatar
    tjc committed
    
        final int c3_count =
    
    tjc's avatar
    tjc committed
          feature.getPositionalBaseCount(2, Bases.getIndexOfBase('c'));
    
    tjc's avatar
    tjc committed
        final int g3_count =
    
    tjc's avatar
    tjc committed
          feature.getPositionalBaseCount(2, Bases.getIndexOfBase('g'));
    
    tjc's avatar
    tjc committed
    
        final double c3_score = 100.0 * (3 * c3_count - c_total) / c_total;
        final double g1_score = 100.0 * (3 * g1_count - g_total) / g_total;
        final double g3_score = 100.0 * (3 * g3_count - g_total) / g_total;
    
    
    tjc's avatar
    tjc committed
        final double cor1_2_score = feature.get12CorrelationScore();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final NumberFormat number_format = NumberFormat.getNumberInstance();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        number_format.setMaximumFractionDigits(1);
        number_format.setMinimumFractionDigits(1);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        final String cor1_2_score_string = number_format.format(cor1_2_score);
    
    tjc's avatar
    tjc committed
        final String c3_score_string;
        final String g1_score_string;
        final String g3_score_string;
    
    
    
    tjc's avatar
    tjc committed
        if(c_total == 0) 
    
    tjc's avatar
    tjc committed
          c3_score_string = "ALL";
    
    tjc's avatar
    tjc committed
        else 
    
    tjc's avatar
    tjc committed
          c3_score_string = number_format.format(c3_score);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(g_total == 0) 
    
    tjc's avatar
    tjc committed
          g1_score_string = "ALL";
    
    tjc's avatar
    tjc committed
        else 
    
    tjc's avatar
    tjc committed
          g1_score_string = number_format.format(g1_score);
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(g_total == 0)
    
    tjc's avatar
    tjc committed
          g3_score_string = "ALL";
    
    tjc's avatar
    tjc committed
        else
    
    tjc's avatar
    tjc committed
          g3_score_string = number_format.format(g3_score);
    
    tjc's avatar
    tjc committed
    
        String codon_usage_score_string = "";
    
        final CodonUsageAlgorithm codon_usage_alg =
    
    tjc's avatar
    tjc committed
          getBasePlotGroup().getCodonUsageAlgorithm();
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        if(codon_usage_alg != null) 
        {
    
    tjc's avatar
    tjc committed
          number_format.setMaximumFractionDigits(3);
          number_format.setMinimumFractionDigits(3);
    
    tjc's avatar
    tjc committed
    
          codon_usage_score_string =
    
    tjc's avatar
    tjc committed
            number_format.format(codon_usage_alg.getFeatureScore(feature)) + " ";
    
    tjc's avatar
    tjc committed
        }
    
    
    tjc's avatar
    tjc committed
        return codon_usage_score_string +
               padRightWithSpaces(cor1_2_score_string, 5) + " " +
               padRightWithSpaces(c3_score_string, 5) + " " +
               padRightWithSpaces(g1_score_string, 5) + " " +
               padRightWithSpaces(g3_score_string, 5);
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Return the given string padded with spaces to the given width.  The
       *  spaces are added on the right of the string.
       **/
    
    tjc's avatar
    tjc committed
      private String padRightWithSpaces(final String string, final int width) 
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        final int len = string.length();
        if(len == width)
    
    tjc's avatar
    tjc committed
          return string;
    
    
    tjc's avatar
    tjc committed
        final StringBuffer buffer = new StringBuffer(string);
    
    tjc's avatar
    tjc committed
        for(int i = 0 ; i < width - len; ++i) 
    
    tjc's avatar
    tjc committed
          buffer.append(' ');
    
    tjc's avatar
    tjc committed
    
    
    tjc's avatar
    tjc committed
        return buffer.toString();
    
    tjc's avatar
    tjc committed
      }
    
      /**
       *  Return the given string padded with spaces to the given width.  The
       *  spaces are added on the left of the string.
       **/
    
    tjc's avatar
    tjc committed
      private String padLeftWithSpaces(final String string, final int width) 
    
    tjc's avatar
    tjc committed
      {
    
    tjc's avatar
    tjc committed
        final int len = string.length();
        if(len == width) 
    
    tjc's avatar
    tjc committed
          return string;
    
    
    tjc's avatar
    tjc committed
        final StringBuffer buffer = new StringBuffer();