/** 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 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;
  }
  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;
  }
  /**
   * 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;
      boolean goodUser = true;
      if (dragFeature != null)
        goodUser =
            baseEditorPanel.testUser(((AnnotatedFeatureI) dragFeature.getRefFeature()).getOwner());

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

      if (dragType == baseEditorPanel.LEFT_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.RIGHT_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);
      }
    }
  }