/** Scrolls the window to the start/end of the next annotation to the right. */
  public void nextAnnotation() {
    int basepair = getVisibleBase();
    if (getType().equals("AA")) {
      basepair += 3 * getAnnotationPanel().getStrand().toInt();
    }
    int position =
        getAnnotationPanel()
            .tierPositionToPixelPosition(getAnnotationPanel().basePairToTierPosition(basepair));
    SeqFeatureI feature = getAnnotationPanel().getNextFeature(position);

    if (feature != null) {

      if (getAnnotationPanel().getOrientation() == Orientation.FIVE_TO_THREE) {
        scrollToBase(feature.getStart());
      } else {
        scrollToBase(feature.getEnd());
      }

      AnnotatedFeatureI selection = null;
      if (feature instanceof AnnotatedFeatureI) {
        selection = getTransOrOneLevelAnn((AnnotatedFeatureI) feature);
      }

      if (selection != null
          && selection.isTranscript()
          && ((Transcript) selection).haveWholeSequence()) {
        setSelection((AnnotatedFeatureI) feature);
        getCurationState().getSelectionManager().select(feature, this);
      } else if (selection != null && selection != getSelection()) {
        nextAnnotation();
      }
    }
  }
  /**
   * Sets up transcriptComboBox (pulldown list) with transcript and its parent gene's other
   * transcripts, with transcript selected
   */
  private void setupTranscriptComboBox(AnnotatedFeatureI annot) {
    // could also check for gene change before doing a removeAll
    if (transcriptComboBox.getSelectedItem() == annot) return;
    // adding and removing items causes item events to fire so need to remove
    // listener here - is there any other way to supress firing?
    transcriptComboBox.removeItemListener(transItemListener);
    transcriptComboBox.removeAllItems();
    if (annot == null) {
      transcriptComboBox.addItem("<no feature selected>");
      return;
    }

    // 1 LEVEL ANNOT
    if (annot.isAnnotTop()) {
      transcriptComboBox.addItem(annot);
      return;
    }

    // TRANSCRIPT
    SeqFeatureI gene = annot.getRefFeature();
    Vector transcripts = gene.getFeatures();
    for (int i = 0; i < transcripts.size(); i++)
      transcriptComboBox.addItem(transcripts.elementAt(i));
    transcriptComboBox.setSelectedItem(annot); // transcript
    transcriptComboBox.addItemListener(transItemListener);
  }
  /* Need to special-case some URLs.  It's yucky to do this,
   * but that's the only way this will work for some of the Drosophila
   * result types.  The assumption in the code is that there is one URL
   * per type, but for us it depends on which database the hit is on. */
  protected String getURLPrefix(FeatureProperty prop, SeqFeatureI f, String id) {
    String defaultURL = prop.getURLString();

    if (!(f instanceof FeaturePair)) return (defaultURL);

    FeaturePair fp = (FeaturePair) f;
    SeqFeatureI fs = (SeqFeatureI) fp.getRefFeature();
    String database = fs.getDatabase();

    String url = defaultURL;

    /* Here we go--a bunch of special cases.
     * It sucks that these are in the code--
     * these should be in the style file or something! */
    String nucleotide = "nucleotide";
    String protein = "protein";
    String ENTREZ_N =
        "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=search&db="
            + nucleotide
            + "&doptcmdl=GenBank&term=";
    String ENTREZ_P =
        "http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=search&db="
            + protein
            + "&doptcmdl=GenBank&term=";
    // In most cases, the IDs for BLASTX Similarity to Fly seem to work better
    // with the default SWALL URL; however, the AA ones seem not to be in SWALL
    // and we have to look in Entrez.  Yuck.
    //    if (prop.typeEquals("BLASTX Similarity to Fly") && id.startsWith("AA")) {
    if (prop.getDisplayType().startsWith("BLASTX") && id.startsWith("AA")) {
      url = ENTREZ_P;
    } else if (prop.typeEquals("Rodent") && (database.indexOf("unigene") >= 0)) {
      url = ENTREZ_N;
    } else if (prop.typeEquals("Insect") && (database.indexOf("dbEST") >= 0)) {
      url = ENTREZ_N;
    }
    // This isn't working right yet.  The tRNA ids look like:
    // gb|AE002593|AE002593.trna5-ArgTCG
    // and we want just the part between the first two ||s.
    else {
      String acc;
      if (prop.typeEquals("tRNA-result") && (database.indexOf("na_tRNA.dros") >= 0)) {
        acc = id.substring(id.indexOf("|") + 1);
        acc = acc.substring(0, acc.indexOf("|"));
      } else if (prop.typeEquals("New Fly Sequence")) {
        // These accs look like gi||gb|AY135216|AY135216
        acc = id;
        if (acc.indexOf("|") >= 0) acc = acc.substring(acc.lastIndexOf("|") + 1);

        /* We've already appended the correct acc,
         * but the URL constructor will automatically
         * slap the uncorrected one at the end.
         * The last # is so that when the whole ID is appended at the end, it
         * won't have any effect. */
        url = ENTREZ_N + acc + "#";
      }
    }
    return url;
  }
  /**
   * Left Mouse dragging drags end of feature if drag type right or left boundary. dragType is
   * figured in mousePressed
   */
  public void mouseDragged(MouseEvent e) {
    if (!MouseButtonEvent.isLeftMouseClick(e)) return;
    // dragFeature set in mousePressed
    // not needed
    if (dragging == false && dragFeature != null) {
      // changer = baseEditorPanel.getAnnotationEditor().beginRangeChange(dragFeature);// ??
      preDragStartBasePair = dragFeature.getStart();
      preDragEndBasePair = dragFeature.getEnd();
    }
    dragging = true;
    // dragType determined in MousePressed
    if (dragType == baseEditorPanel.START_BOUNDARY)
      baseEditorPanel.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
    else if (dragType == baseEditorPanel.END_BOUNDARY)
      baseEditorPanel.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
    else if (dragType == baseEditorPanel.SEQ_SELECT_DRAG) {
      baseEditorPanel.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
    }
    setPos(e.getX(), e.getY());
    startPos = pos;
    // going right
    // substract the one because during dragging
    // the person operating the mouse drags it
    // one position beyond where they want the
    // drop to occur.
    // pos--;

    if (dragType == baseEditorPanel.START_BOUNDARY) {
      // Is this position okay to use as the new
      // low end or will it make the feature too short?
      if (baseEditorPanel.notTooSmall(
          pos, baseEditorPanel.basePairToPos((int) dragFeature.getEnd())))
        // this actually changes the model - do we really need to do that
        // with every mouse drag - cant we just change it at the end of the
        // drag? No - the EDE relies on the feature being changed - bummer
        baseEditorPanel.changeStart(dragFeature, pos - dragStartPos, limit_5prime, limit_3prime);
    } else if (dragType == baseEditorPanel.END_BOUNDARY) {
      // Is this position okay to use as the new
      // high end or will it make the feature too short?
      if (baseEditorPanel.notTooSmall(
          baseEditorPanel.basePairToPos((int) dragFeature.getStart()), pos))
        // This changes the actual exon model! EDE relies on this
        baseEditorPanel.changeEnd(dragFeature, pos - dragStartPos, limit_5prime, limit_3prime);
    } else if (dragType == baseEditorPanel.SEQ_SELECT_DRAG) {
      int redrawLowPos = baseEditorPanel.selectLowPos();
      int redrawHighPos = baseEditorPanel.selectHighPos();
      if (pos < redrawLowPos) redrawLowPos = pos;
      if (pos > redrawHighPos) redrawHighPos = pos;
      // selectCurrentPos = pos;
      baseEditorPanel.setSelectCurrentPos(pos);
      baseEditorPanel.repaint(redrawLowPos, redrawHighPos);
      return;
    }
    // repaint with exon seq feature ends changed
    baseEditorPanel.repaint(pos, dragStartPos);
    dragStartPos = pos;
  }
  /**
   * Adds a collection of results to the list of results (but not to the result panel)
   *
   * @param features a list of results
   * @return the number of results added
   */
  public int addResults(java.util.Collection<SeqFeatureI> features) {
    int count = 0;

    for (SeqFeatureI feature : features) {
      if (!feature.isAnnot() && !getResults().contains(feature)) {
        getResults().add(feature);
        count++;
      }
    }
    return count;
  }
 /* Establish the currentRange of bases covered by the
    currentFeature of this renderer
 */
 public double[] establishRange(SeqFeatureI sf, int pos, int tier, boolean use_set) {
   double range[];
   if (sf == null || (!use_set && (sf.canHaveChildren()))) {
     range = baseEditor.getRangeAtPosition(tier, pos);
   } else {
     range = new double[2];
     range[0] = (double) baseEditor.basePairToPos(sf.getLow());
     range[1] = (double) baseEditor.basePairToPos(sf.getHigh());
   }
   return range;
 }
 /** Does all the handle selection checks for BaseFineEditor and BaseEditorPanel */
 boolean canHandleSelection(FeatureSelectionEvent evt, Object self) {
   if ((noExternalSelection() && isExternalSelection(evt)) && !evt.forceSelection()) return false;
   if (evt.getSource() == self) return false;
   if (!this.isVisible()) return false;
   if (evt.getFeatures().size() == 0) return false;
   SeqFeatureI sf = evt.getFeatures().getFeature(0);
   // if strand of selection is not our strand return
   boolean is_reverse = (sf.getStrand() == -1);
   if (is_reverse != editorPanel.getReverseStrand()) return false;
   if (!(sf instanceof AnnotatedFeatureI)) return false; // repaint transVw?
   else return true;
 }
  /**
   * Gets all the results from a particular feature set that pass the filter
   *
   * @param featureSet
   * @param f the filter used to decide which results to get
   * @return
   */
  public static Set<SeqFeatureI> getResults(SeqFeatureI featureSet, Filter f) {
    HashSet<SeqFeatureI> result = new HashSet<SeqFeatureI>();

    if (featureSet instanceof FeaturePairI && f.keep(featureSet)) {
      result.add(featureSet);
    } else if (featureSet.getFeatures() != null) {
      for (int i = 0, size = featureSet.getFeatures().size(); i < size; i++) {
        SeqFeatureI feature = featureSet.getFeatureAt(i);
        result.addAll(getResults(feature, f));
      }
    }

    return result;
  }
 /** True if feat or feats refFeat is fs */
 private boolean featSetContainsFeat(FeatureSetI fs, SeqFeatureI feat) {
   if (fs == null || feat == null) return false;
   if (fs == feat) return true;
   if (fs == feat.getRefFeature()) return true;
   // This should probably be generalized(recursive) for whole ancestor tree and added to
   // FeatureSetI - for now only need to do feat and ref feat
   // could use FeatureSet.find i guess - is that what thats intended for?
   return false;
 }
  private String generateAnalysisURL(SeqFeatureI f) {
    // Figure out URL for getting more information about this feature.
    // If more than one feature was selected, use the LAST one.
    String id = getIdForURL(f);
    FeatureProperty prop = Config.getPropertyScheme().getFeatureProperty(f.getFeatureType());

    String urlPrefix = getURLPrefix(prop, f, id);

    if (urlPrefix == null) {
      String m =
          "Sorry, no URL registered for type "
              + f.getFeatureType()
              + "\nin tiers file "
              + Config.getStyle().getTiersFile();
      JOptionPane.showMessageDialog(null, m);
      return null;
    }
    return URLQueryGenerator.getURL(urlPrefix, id);
  }
  private boolean isSequencingErrorPosition(int bp, SeqFeatureI feature, SequenceType type) {
    boolean result = false;
    if (type == SequenceType.DNA) {
      result = curationState.getCurationSet().getRefSequence().isSequencingErrorPosition(bp);
    } else {
      result =
          curationState.getCurationSet().getRefSequence().isSequencingErrorPosition(bp)
              || curationState
                  .getCurationSet()
                  .getRefSequence()
                  .isSequencingErrorPosition(bp + feature.getStrand())
              || curationState
                  .getCurationSet()
                  .getRefSequence()
                  .isSequencingErrorPosition(bp + (2 * feature.getStrand()));
    }

    return result;
  }
 /**
  * This just returns sf.getBioType() (capitalized) unless its a transcript. For transcript it only
  * returns "Transcript" if its part of a Gene. Otherwise it returns its parent's bio type +
  * transcript. (should it not tack on the transcript?) Thus a tRNA's transcript would display
  * "tRNA transcript", where a gene's transcript would display just "Transcript". Should it
  * actually display "Gene transcript"? rename getTypeForDisplay?
  */
 public String getBioTypeForDisplay(SeqFeatureI sf) {
   String typeDisplay;
   if (sf instanceof AnnotatedFeatureI) {
     typeDisplay = sf.getTopLevelType();
     typeDisplay = capitalize(typeDisplay);
     if (!sf.getTopLevelType().equalsIgnoreCase(sf.getFeatureType()))
       typeDisplay += " " + capitalize(sf.getFeatureType());
   } else {
     String db = sf.getDatabase();
     // Don't bother showing db name if it is "dummy"
     if (db != null && !db.equals("") && !db.equals("dummy"))
       typeDisplay = sf.getProgramName() + ":" + sf.getDatabase();
     // yet again, the users have requested a change
     // Now instead of an enumeration it is the range of the feature
     else {
       typeDisplay = (sf.getProgramName() + ":" + sf.getStart() + "-" + sf.getEnd());
     }
   }
   return typeDisplay;
 }
  /**
   * Get component to be rendered, if pos outside of current range getFeatureAtPosition and reset
   * currentRange, if feature is non null and not an instance of FeatureSetI then its an exon, and
   * set isExon flag
   */
  public Component getBaseRendererComponent(char base, int pos, int tier, SequenceI seq) {
    init(base, pos, tier, seq);

    hatchColor = null;
    if (((BaseEditorPanel) baseEditor).getShowHitZones()) {
      Vector hitZones = ((BaseEditorPanel) baseEditor).hitZones;

      for (int hitIndex = 0; hitIndex < hitZones.size(); hitIndex++) {
        int[] hitZone = (int[]) hitZones.elementAt(hitIndex);

        if (pos >= hitZone[0] && pos < hitZone[1]) {
          hatchColor = Color.yellow;
          break;
        }
      }
    }

    currentFeature = baseEditor.getFeatureAtPosition(pos, tier);
    double[] range = establishRange(currentFeature, pos, tier, false);

    if (range[1] != currentRange[1] || range[0] != currentRange[0]) {
      currentRange[0] = range[0];
      currentRange[1] = range[1];

      currentFeatureSet = baseEditor.getFeatureSetAtPosition(pos, tier);
      range = establishRange(currentFeatureSet, pos, tier, true);
      transcriptRange[0] = range[0];
      transcriptRange[1] = range[1];

      if (currentFeature != null) {
        // This decides the index into the color array, transcripts
        // can have different colors for their features
        transcriptColorIndex =
            (baseEditor.getRangeIndex(tier, (int) currentRange[0], (int) currentRange[1]))
                % transcriptColorList.length;
        exonColorIndex =
            (baseEditor.getExonRangeIndex(tier, (int) currentRange[0], (int) currentRange[1]))
                % transcriptColorList[transcriptColorIndex].length;
      }
      if (currentFeature == null) {
        isExon = false;
        isIntron = false;
        // } else if (currentFeature.canHaveChildren()) {
      } else if (currentFeature.hasKids()) { // 1 level annots can but dont
        isExon = false;
        isIntron = true;
      } else {
        isExon = true;
        isIntron = false;
      }
    }
    return this;
  }
  private boolean isShiftPosition(int bp, SeqFeatureI feature, SequenceType type) {
    boolean result = false;

    // Can shifts only happen on exons?
    if (feature != null) {
      FeatureSet fs = (FeatureSet) feature.getRefFeature();
      result = fs.plus1FrameShiftPosition() == bp || fs.minus1FrameShiftPosition() == bp;
      if (type == SequenceType.AA) {
        bp += feature.getStrand();
        result =
            result || fs.plus1FrameShiftPosition() == bp || fs.minus1FrameShiftPosition() == bp;
      }
      if (type == SequenceType.AA) {
        bp += feature.getStrand();
        result =
            result || fs.plus1FrameShiftPosition() == bp || fs.minus1FrameShiftPosition() == bp;
      }
    }

    return result;
  }
  protected String getIdForURL(SeqFeatureI f) {
    /* 02/06/2004: If user selected a whole result (a FeatureSet),
     * neither the id nor the name is what we want.
     * So just look at the first child of the FeatureSet
     * (which is probably a FeaturePair).  Is this bad?  --NH */
    if (f instanceof FeatureSet) {
      f = ((FeatureSet) f).getFeatureAt(0);
    }
    String id;
    if (f instanceof FeaturePair) {
      FeaturePair fp = (FeaturePair) f;
      SeqFeatureI sbjct = (SeqFeatureI) fp.getHitFeature();
      id = sbjct.getName();
      if (id.equals("") || id.equals("no_name")) id = f.getName();
      //      System.out.println("getIdForURL: for feature pair " + f.getName() + ", subject name =
      // " + id); // DEL
    } else {
      id = getDisplayName(f);
      //      System.out.println("getIdForURL: for feature " + f.getName() + ", display name = " +
      // id); // DEL
    }
    // Special case:  BDGP EST IDs look like "LD18592.5prime" rather than "LD18592",
    // which isn't what we want.  Need to leave out the .[53]prime part.
    // (Sima says there aren't any cases where we'd want to preserve that part.)
    if (id.endsWith(".3prime") || id.endsWith(".5prime")) {
      //      System.out.println("Truncating EST/cDNA ID " + id + " to remove .[35]prime");
      id = id.substring(0, id.lastIndexOf("."));
    }
    // Some ESTs IDs have :contig1 at the end
    if (id.indexOf(":contig") > 0) {
      id = id.substring(0, id.indexOf(":contig"));
      // In r4.1, I see EST IDs like UNKNOWN_RE01983:contig1 and INVERTED_GH07123:contig1
      if (id.indexOf("_") > 0) id = id.substring(id.indexOf("_") + 1);
    }
    // Some ESTs have _revcomp at the end, e.g. BE975849_revcomp
    if (id.indexOf("_revcomp") > 0) id = id.substring(0, id.indexOf("_revcomp"));

    return id;
  }
  /**
   * Gets all the results from a particular feature set Don't think this is used...
   *
   * @param type the type of results wanted, ether AA or DNA
   * @param featureSet
   * @return
   */
  public static Set<SeqFeatureI> getResults(String type, SeqFeatureI featureSet) {
    HashSet<SeqFeatureI> result = new HashSet<SeqFeatureI>();

    if (featureSet instanceof FeaturePairI) {
      FeaturePairI feature = (FeaturePairI) featureSet;
      SequenceI sequence = feature.getFeatureSequence();
      String seqType = sequence.getResidueType();
      String alignment = feature.getExplicitAlignment();

      if (alignment != null
          && (type.equals(seqType) || type.equals(SequenceI.AA) == isAminoAcid(alignment))) {
        result.add(featureSet);
      }

    } else if (featureSet.getFeatures() != null) {
      for (int i = 0, size = featureSet.getFeatures().size(); i < size; i++) {
        SeqFeatureI feature = featureSet.getFeatureAt(i);
        result.addAll(getResults(type, feature));
      }
    }

    return result;
  }
  /**
   * Gets all the annotations in a particular feature set
   *
   * @param featureSet
   * @return
   */
  public static Set<SeqFeatureI> getAnnotations(SeqFeatureI featureSet) {
    HashSet<SeqFeatureI> result = new HashSet<SeqFeatureI>();

    if (featureSet.isTranscript()) {

      if (featureSet.isContainedByRefSeq())
        // only add if its fully in range
        result.add(featureSet);

    } else if (featureSet.isAnnotTop() && !featureSet.hasKids()) {
      // only add if its fully in range
      if (featureSet.isContainedByRefSeq()) result.add(featureSet);

    } else {
      for (int i = 0, size = featureSet.getFeatures().size(); i < size; i++) {
        SeqFeatureI feature = featureSet.getFeatureAt(i);
        if (feature.canHaveChildren()) {
          result.addAll(getAnnotations(feature));
        }
      }
    }

    return result;
  }
  /* only call on an exon position */
  private char getPeptide(int bp, SeqFeatureI sf) {
    char base = '\0';
    SequenceI peptides = null;
    FeatureSetI parent = sf.getParent();

    int pepPos = -1;
    if (parent != null && parent.hasPeptideSequence()) {
      peptides = parent.getPeptideSequence();
      if (parent instanceof FeatureSet) {
        FeatureSet fs = (FeatureSet) parent;
        pepPos = fs.getPeptidePosForGenomicPos(bp);
      }

      if (pepPos >= 0 && pepPos < peptides.getLength()) {
        base = peptides.getBaseAt(pepPos);
      }
    }

    return base;
  }
  /**
   * @param position
   * @param tier
   * @return
   */
  public static int getOffset(int position, TierI tier) {

    SeqFeatureI sf = null;

    int offset = 0;
    if (tier.featureExsitsAt(position, Level.BOTTOM)) {
      sf = tier.featureAt(position, Level.BOTTOM);
      ReadingFrame exonFrame = ReadingFrame.valueOf(sf.getFrame());
      ReadingFrame tierFrame = tier.getReadingFrame();

      if (exonFrame == ReadingFrame.NONE
          && sf.getParent() != null
          && sf.getParent().isTranscript()) {
        FeatureSetI parent = sf.getParent();
        Transcript parentClone = (Transcript) sf.getParent().clone();

        int start = parentClone.getEnd();

        // the transcript start might be different than the lowest exon start
        // when in the middle of an edit
        if (parentClone.canHaveChildren()) {
          for (int i = 0; i < parentClone.size(); i++) {
            SeqFeatureI f = parentClone.getFeatureAt(i);
            if ((Strand.valueOf(f.getStrand()) == Strand.FORWARD && f.getStart() < start)
                || (Strand.valueOf(f.getStrand()) == Strand.REVERSE && f.getStart() > start))
              start = f.getStart();
          }
        }

        int translationStart = parentClone.getTranslationStart();
        int translationPos = parentClone.getFeaturePosition(translationStart);
        int oldstart = start;
        start += ((translationPos - 1) % 3) * parentClone.getStrand();

        parentClone.setTranslationStart(start);
        parentClone.setTranslationEnd(parentClone.getEnd());
        parentClone.setPeptideSequence(null);
        String translation = parentClone.translate();
        SequenceI sequence = parentClone.getPeptideSequence();

        int index = parent.getFeatureIndex(sf);
        exonFrame = ReadingFrame.valueOf(parentClone.getFeatureAt(index).getFrame());
      }

      if (tierFrame != exonFrame && sf.getParent() != null && sf.getParent().isTranscript()) {
        if (tierFrame == ReadingFrame.THREE) {
          offset = exonFrame == ReadingFrame.ONE ? -2 : -1;
        } else if (tierFrame == ReadingFrame.TWO) {
          offset = exonFrame == ReadingFrame.ONE ? -1 : 1;
        } else if (tierFrame == ReadingFrame.ONE) {
          offset = exonFrame == ReadingFrame.TWO ? 1 : 2;
        }
      }

      if (exonFrame == ReadingFrame.NONE) {
        offset = 0;
      }
    }

    return offset;
  }
 public boolean canRender(SeqFeatureI feature) {
   return feature.isAnnot();
 }
 public boolean keep(SeqFeatureI f) {
   FeatureProperty fp = Config.getPropertyScheme().getFeatureProperty(f.getTopLevelType());
   return displayType.equalsIgnoreCase(fp.getDisplayType());
 }
  /**
   * Get component to be rendered, if pos outside of current range getFeatureAtPosition and reset
   * currentRange, if feature is non null and not an instance of FeatureSetI then its an exon, and
   * set isExon flag
   */
  public Component getBaseComponent(int position, TierI tier, Orientation o) {

    transcript = null;

    int bp = tier.getBasePair(position);
    int oldPos = position;

    // We need to change the position if dealing with aa view
    if (tier.getType() == SequenceType.AA) {
      int offset = getOffset(position, tier);
      bp += offset * tier.getStrand().toInt();
      position = tier.getPosition(bp);
    }

    char base = tier.charAt(position);

    if (tier.featureExsitsAt(position, TierI.Level.BOTTOM)) {
      this.state = State.EXON;
      SeqFeatureI feature = tier.featureAt(position, TierI.Level.BOTTOM);
      Transcript parentClone = null;
      Transcript otherParentClone = null;

      if (tier.getType() == SequenceType.AA
          && feature.getParent() != null
          && feature.getParent().isTranscript()) {

        FeatureSetI parent = feature.getParent();
        int index = parent.getFeatureIndex(feature);

        int start = parent.getEnd();
        // the transcript start might be different than the lowest exon start
        // when in the middle of an edit
        if (parent.canHaveChildren()) {
          for (int i = 0; i < parent.size(); i++) {
            SeqFeatureI sf = parent.getFeatureAt(i);
            if ((Strand.valueOf(sf.getStrand()) == Strand.FORWARD && sf.getStart() < start)
                || (Strand.valueOf(sf.getStrand()) == Strand.REVERSE && sf.getStart() > start))
              start = sf.getStart();
          }
        }

        int translationStart = parent.getTranslationStart();
        int translationEnd = parent.getTranslationEnd();
        int translationPos = parent.getFeaturePosition(translationStart);
        int oldstart = start;
        start += ((translationPos - 1) % 3) * parent.getStrand();

        // Tring to avoid all the recalculation...
        if (feature.getParent() == currentParent
            && start == currentStart
            && featureStart == feature.getStart()
            && featureEnd == feature.getEnd()) {
          parentClone = currentParentClone;
          otherParentClone = currentParentClone2;
        } else {
          // need to get a full translation so that we can get the amino acids
          // in the UTR region
          parentClone = (Transcript) feature.getParent().clone();
          otherParentClone = (Transcript) feature.getParent().clone();

          if (!parentClone.setTranslationStart(start)) {
            start = oldstart;
            TranslationI cds = otherParentClone.getTranslation();
            cds.calcTranslationStartForLongestPeptide();
            translationStart = otherParentClone.getTranslationStart();
            translationPos = otherParentClone.getFeaturePosition(translationStart);
            start += ((translationPos - 1) % 3) * otherParentClone.getStrand();
            parentClone.setTranslationStart(start);
          }

          parentClone.setTranslationEnd(parentClone.getEnd());
          parentClone.setPeptideSequence(null);

          String translation = parentClone.translate();
          SequenceI sequence = parentClone.getPeptideSequence();

          currentParent = (Transcript) feature.getParent();
          currentParentClone = parentClone;
          currentParentClone2 = otherParentClone;
          currentStart = start;
          featureStart = feature.getStart();
          featureEnd = feature.getEnd();

          if (otherParentClone.getFeatureContaining(translationEnd) == null) {
            TranslationI cds = otherParentClone.getTranslation();
            cds.setTranslationEndFromStart();
          }
        }

        SeqFeatureI sf = parentClone.getFeatureAt(index);

        if (bp >= sf.getLow() && bp <= sf.getHigh()) {
          base = getPeptide(bp, sf);
        } else {
          base = '\0';
        }

        if (otherParentClone != null) {
          feature = otherParentClone.getFeatureAt(index);
        }

      } else if (tier.getType() == SequenceType.AA
          && feature.getParent() != null
          && !feature.getParent().isTranscript()) {
        base = '\0';
      }

      if (isUTR(bp, feature, tier)) {
        this.state = State.UTR;
      }

      if (isTranslationStart(bp, feature)) {
        this.state = State.TRANSLATION_START;
      }

      if (isTranslationEnd(bp, feature)) {
        this.state = State.TRANSLATION_END;
      }

      // Can have an error or a shift on a position but not both
      if (isSequencingErrorPosition(bp, feature, tier.getType())) {
        this.state = State.ERROR;
        // set base to the base of the new sequence?
      }

      if (isShiftPosition(bp, feature, tier.getType())) {
        this.state = State.SHIFT;
      }

    } else if (tier.featureExsitsAt(position, TierI.Level.TOP)) {
      SeqFeatureI feature = tier.featureAt(position, Level.TOP);
      this.state = State.INTRON;
      if (tier.getType() == SequenceType.AA) {
        base = '\0';
      }

      int start = feature.getEnd();
      int end = feature.getStart();

      if (feature.canHaveChildren()) {
        for (int i = 0; i < feature.size(); i++) {
          SeqFeatureI sf = feature.getFeatureAt(i);
          if ((Strand.valueOf(sf.getStrand()) == Strand.FORWARD && sf.getStart() < start)
              || (Strand.valueOf(sf.getStrand()) == Strand.REVERSE && sf.getStart() > start))
            start = sf.getStart();

          if ((Strand.valueOf(sf.getStrand()) == Strand.FORWARD && sf.getEnd() > end)
              || (Strand.valueOf(sf.getStrand()) == Strand.REVERSE && sf.getEnd() < end))
            end = sf.getEnd();
        }
      }

      if (bp <= Math.min(start, end) || bp >= Math.max(start, end)) {
        this.state = State.OUT;
        base = '\0';
      }
    } else {
      this.state = State.OUT;
      base = '\0';
    }

    if (!(state == State.SHIFT || state == State.ERROR)
        && getRegionLow() <= position
        && position <= getRegionHigh()) {
      this.state = State.SELECTED;
      base = tier.charAt(oldPos);
    }

    init(base);
    return this;
  }
  /**
   * Display AnnotatedFeatureI feature. Exon, Transcript, and Gene are all GenericAnnotationI. No
   * selection event is fired. (selectAnnot fires and displays)
   */
  private void displayAnnot(AnnotatedFeatureI annot) {
    currentAnnot = annot;
    if (currentAnnot == null) {
      transcriptComboBox.removeAllItems();
      transcriptComboBox.addItem("<no feature selected>");
      lengthLabel.setText("Translation length: <no feature selected>");
      upstream_button.setLabel("");
      downstream_button.setLabel("");
      translationViewer.setTranscript(null, editorPanel.getSelectedTier()); // ??
      return;
    }

    // else {
    setupTranscriptComboBox(currentAnnot);

    SeqFeatureI topAnnot = currentAnnot;
    if (topAnnot.isTranscript()) topAnnot = currentAnnot.getRefFeature();
    if (topAnnot.isProteinCodingGene()) {
      String translation = currentAnnot.translate();
      if (translation == null) {
        lengthLabel.setText("Translation length: <no start selected>");
      } else {
        lengthLabel.setText("Translation length: " + currentAnnot.translate().length());
      }
    } else {
      lengthLabel.setText(topAnnot.getFeatureType() + " annotation");
    }
    FeatureSetI holder = (FeatureSetI) topAnnot.getRefFeature();
    neighbor_up = null;
    neighbor_down = null;
    if (holder != null) {

      int index = holder.getFeatureIndex(topAnnot);
      // get next neighbor up that has whole sequence
      for (int i = index - 1; i >= 0 && neighbor_up == null; i--) {
        FeatureSetI gene_sib = (FeatureSetI) holder.getFeatureAt(i);
        if (gene_sib.getFeatureAt(0) instanceof Transcript) {
          Transcript trans = (Transcript) gene_sib.getFeatureAt(0);
          if (trans.haveWholeSequence()) // szap.getCurationSet()))
          neighbor_up = trans;
        }
      }

      // get next neighbor down that has whole sequence
      for (int i = index + 1; i < holder.size() && neighbor_down == null; i++) {
        FeatureSetI gene_sib = (FeatureSetI) holder.getFeatureAt(i);
        if (gene_sib.getFeatureAt(0) instanceof Transcript) {
          Transcript trans = (Transcript) gene_sib.getFeatureAt(0);
          if (trans.haveWholeSequence()) // szap.getCurationSet()))
          neighbor_down = trans;
        }
      }
    }
    upstream_button.setLabel(
        neighbor_up == null
            ? ""
            : "Go to next 5' annotation (" + neighbor_up.getParent().getName() + ")");
    upstream_button.setVisible(neighbor_up != null);
    downstream_button.setLabel(
        neighbor_down == null
            ? ""
            : "Go to next 3' annotation (" + neighbor_down.getParent().getName() + ")");
    downstream_button.setVisible(neighbor_down != null);
    // }
    // todo - translationViewer take in 1 level annot
    if (currentAnnot.isTranscript())
      translationViewer.setTranscript((Transcript) currentAnnot, editorPanel.getSelectedTier());
  }
  /**
   * If right mouse deselect base If end of feature drag notify AnnotationEditor (endRangeChange)
   * and recalc translation end from start
   */
  public void mouseReleased(MouseEvent e) {

    // RIGHT MOUSE CLICK
    if (MouseButtonEvent.isRightMouseClick(e)) {
      BaseRenderer rend = baseEditorPanel.getRendererAt(tier);
      if (rend instanceof SelectableDNARenderer) {
        ((SelectableDNARenderer) rend).setTargetPos(-1, -1);
        baseEditorPanel.repaint();
      }
    }

    // SEQ SELECT DRAG
    if (dragType == baseEditorPanel.SEQ_SELECT_DRAG) {
      SequenceI seq = baseEditorPanel.getSequenceForTier(startDragTier);
      int lowBP = baseEditorPanel.posToResidue(baseEditorPanel.selectLowPos());
      int highBP = baseEditorPanel.posToResidue(baseEditorPanel.selectHighPos());
      String sequence = seq.getResidues(lowBP, highBP);

      String header = " Arbitrary selection (" + seq.getName() + ": " + lowBP + "," + highBP + ")";

      // controllingWindow.copySeqToClipboard(new Sequence (header, sequence));
      ClipboardUtil.copySeqToClipboard(new Sequence(header, sequence));

      resetDragState();
      baseEditorPanel.repaint();
      return;
    }

    // NOT SEQ SELECT DRAG
    else {

      // NOT DRAGGING - RETURN
      if (!MouseButtonEvent.isLeftMouseClick(e) || !dragging || dragFeature == null) {
        resetDragState();
        return;
      }

      // DRAGGING ANNOT ENDS
      if (dragging && dragFeature != null && dragFeature.isAnnot()) {

        // NOTIFY ANNOTATION EDITOR (generates transaction)
        int oldStart = (preDragStartBasePair == -1) ? dragFeature.getStart() : preDragStartBasePair;
        int oldEnd = (preDragEndBasePair == -1) ? dragFeature.getEnd() : preDragEndBasePair;
        AnnotationEditor ae = baseEditorPanel.getAnnotationEditor();
        ae.setAnnotTerminus(
            dragFeature.getAnnotatedFeature(),
            oldStart,
            oldEnd,
            dragFeature.getStart(),
            dragFeature.getEnd());

        // RECALC CDS (if exon/transcript - not for 1 level annots)
        if (dragFeature.isExon()
            && dragFeature.getRefFeature() != null
            && (dragFeature.getRefFeature().isTranscript())) {
          // ExonI exon = (ExonI)dragFeature;
          // Transcript t = (Transcript)exon.getRefFeature();
          SeqFeatureI transcript = dragFeature.getRefFeature();
          TranslationI cds = transcript.getTranslation();
          int transStart = transcript.getStart();
          int cdsStart = cds.getTranslationStart();
          boolean isForward = transcript.isForwardStrand();
          if (cds.isMissing5prime()
              && ((isForward && transStart < cdsStart) || (!isForward && transStart > cdsStart))) {
            cds.calcTranslationStartForLongestPeptide(); // missing start
          } else {
            cds.setTranslationEndFromStart(); // got start - set end
          }
        }

        baseEditorPanel.repaint();
      }
    }

    resetDragState();
    baseEditorPanel.getCurationState().getSZAP().repaint();
  }
  /**
   * Right mouse: highlight base. Left mouse: figure dragType, dragFeature, dragStartPos,
   * startDragTier
   */
  public void mousePressed(MouseEvent e) {
    setPos(e.getX(), e.getY());

    // show base at right mouse click highlighted (on release - popup menu)
    if (MouseButtonEvent.isRightMouseClickNoShift(e)) {
      BaseRenderer rend = baseEditorPanel.getRendererAt(tier);
      if (rend instanceof SelectableDNARenderer) {
        ((SelectableDNARenderer) rend).setTargetPos(pos, tier);
        baseEditorPanel.repaint();
        return;
      }
    }

    if (!MouseButtonEvent.isLeftMouseClick(e)) return;

    if (dragStartPos == -1) {

      if (e.isControlDown()) dragType = baseEditorPanel.SEQ_SELECT_DRAG;
      else dragType = baseEditorPanel.getBoundaryType(pos, tier);

      dragFeature = baseEditorPanel.getFeatureAtPosition(pos, tier);

      dragStartPos = pos;
      startPos = pos;
      startDragTier = tier;
      if (dragFeature == null
          && ((dragType != baseEditorPanel.SEQ_SELECT_DRAG)
              || startDragTier > baseEditorPanel.getTierCount()
              || startDragTier < 3)) {
        resetDragState();
        return;
      }

      if (dragType == baseEditorPanel.START_BOUNDARY) {
        /* the 5 prime edge of the feature (exon) can
        be moved within limits. These are no farther
        in the 3prime direction than the end of the
        feature and no farther in the 5prime direction
        than the beginning (5prime) of the preceding
        intron */
        limit_3prime = (int) dragFeature.getEnd();
        int lowBound = baseEditorPanel.basePairToPos(dragFeature.getStart());
        // subtract one to move into the preceding intron
        double[] lowRange = baseEditorPanel.getRangeAtPosition(tier, lowBound - 1);
        if (lowRange[0] < 0) limit_5prime = baseEditorPanel.posToBasePair(0);
        else limit_5prime = baseEditorPanel.posToBasePair((int) lowRange[0]);
        baseEditorPanel.setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR));
      } else if (dragType == baseEditorPanel.END_BOUNDARY) {
        limit_5prime = (int) dragFeature.getStart();
        int highBound = baseEditorPanel.basePairToPos((int) dragFeature.getEnd());
        double[] highRange = baseEditorPanel.getRangeAtPosition(tier, highBound + 1);
        SequenceI seq = baseEditorPanel.getSequenceForTier(tier);
        if (highRange[1] > seq.getLength())
          limit_3prime = baseEditorPanel.posToBasePair(seq.getLength());
        else limit_3prime = baseEditorPanel.posToBasePair((int) highRange[1]);
        baseEditorPanel.setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
      } else if (dragType == baseEditorPanel.SEQ_SELECT_DRAG) {
        baseEditorPanel.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
        baseEditorPanel.setSelectBeginPos(pos);
        baseEditorPanel.setSelectCurrentPos(pos);
      }
    }
  }
  private String getDisplayName(SeqFeatureI sf) {
    String display_name = "";

    // Annot
    if (sf instanceof AnnotatedFeatureI) {
      display_name = sf.getName();
    }

    // Not Annot
    else {
      // FeatPair
      FeatureSetI fset =
          (sf.canHaveChildren() ? (FeatureSetI) sf : (FeatureSetI) sf.getRefFeature());
      // cant do as seqfeature as getHitSequence() is in FeatureSet and not
      // SeqFeature and furthermore cant do a getHitFeature().getRefSequence()
      // as FeatureSet has no hit feature yet it has hit sequence
      // SeqFeatureI fset = sf.canHaveChildren() ? sf : sf.getRefFeature();
      SequenceI seq = (fset != null ? fset.getHitSequence() : null);
      SeqFeatureI featPair = null; // FeaturePairI fp;

      if (fset != null && fset.size() > 0 && (fset.getFeatureAt(0).hasHitFeature())) {
        featPair = fset.getFeatureAt(0);
      } else if (sf.hasHitFeature()) {
        featPair = sf; // fp = (FeaturePairI) sf;
      } // else {//fp = null; } // not necasary - already null

      if (seq == null && featPair != null) seq = featPair.getHitFeature().getRefSequence();
      if (seq == null) seq = sf.getFeatureSequence();

      if (seq != null) {
        display_name = seq.getName() != null ? seq.getName() : "";
      } else if (featPair != null) {
        SeqFeatureI hit = featPair.getHitFeature();
        display_name = hit.getName() != null ? hit.getName() : "";
      }
      /* NOT FeaturePair
         this will only be reached if not an annot, not a FS w fps
         and not a FeaturePair (or if fp hit name is "")
         in otherwords a seqfeature result
         added getName in for chado gene predics which are leaf seq feats
      */
      // shouldnt we check getName != null before using biotype??
      else display_name = getBioTypeForDisplay(sf);
    }
    return display_name;
  }