2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
23 import jalview.api.AlignViewportI;
24 import jalview.bin.Cache;
25 import jalview.commands.EditCommand;
26 import jalview.commands.EditCommand.Action;
27 import jalview.commands.EditCommand.Edit;
28 import jalview.datamodel.AlignmentI;
29 import jalview.datamodel.ColumnSelection;
30 import jalview.datamodel.HiddenColumns;
31 import jalview.datamodel.SearchResultMatchI;
32 import jalview.datamodel.SearchResults;
33 import jalview.datamodel.SearchResultsI;
34 import jalview.datamodel.Sequence;
35 import jalview.datamodel.SequenceFeature;
36 import jalview.datamodel.SequenceGroup;
37 import jalview.datamodel.SequenceI;
38 import jalview.io.SequenceAnnotationReport;
39 import jalview.renderer.ResidueShaderI;
40 import jalview.schemes.ResidueProperties;
41 import jalview.structure.SelectionListener;
42 import jalview.structure.SelectionSource;
43 import jalview.structure.SequenceListener;
44 import jalview.structure.StructureSelectionManager;
45 import jalview.structure.VamsasSource;
46 import jalview.util.Comparison;
47 import jalview.util.MappingUtils;
48 import jalview.util.MessageManager;
49 import jalview.util.Platform;
50 import jalview.viewmodel.AlignmentViewport;
51 import jalview.viewmodel.ViewportRanges;
53 import java.awt.BorderLayout;
54 import java.awt.Color;
56 import java.awt.FontMetrics;
57 import java.awt.Point;
58 import java.awt.event.ActionEvent;
59 import java.awt.event.ActionListener;
60 import java.awt.event.MouseEvent;
61 import java.awt.event.MouseListener;
62 import java.awt.event.MouseMotionListener;
63 import java.awt.event.MouseWheelEvent;
64 import java.awt.event.MouseWheelListener;
65 import java.util.Collections;
66 import java.util.List;
68 import javax.swing.JLabel;
69 import javax.swing.JPanel;
70 import javax.swing.JToolTip;
71 import javax.swing.SwingUtilities;
72 import javax.swing.Timer;
73 import javax.swing.ToolTipManager;
79 * @version $Revision: 1.130 $
81 public class SeqPanel extends JPanel
82 implements MouseListener, MouseMotionListener, MouseWheelListener,
83 SequenceListener, SelectionListener
85 private static final int MAX_TOOLTIP_LENGTH = 300;
87 public SeqCanvas seqCanvas;
89 public AlignmentPanel ap;
92 * last column position for mouseMoved event
94 private int lastMouseColumn;
97 * last sequence offset for mouseMoved event
99 private int lastMouseSeq;
101 protected int lastres;
103 protected int startseq;
105 protected AlignViewport av;
107 ScrollThread scrollThread = null;
109 boolean mouseDragging = false;
111 boolean editingSeqs = false;
113 boolean groupEditing = false;
115 // ////////////////////////////////////////
116 // ///Everything below this is for defining the boundary of the rubberband
117 // ////////////////////////////////////////
120 boolean changeEndSeq = false;
122 boolean changeStartSeq = false;
124 boolean changeEndRes = false;
126 boolean changeStartRes = false;
128 SequenceGroup stretchGroup = null;
130 boolean remove = false;
132 Point lastMousePress;
134 boolean mouseWheelPressed = false;
136 StringBuffer keyboardNo1;
138 StringBuffer keyboardNo2;
140 java.net.URL linkImageURL;
142 private final SequenceAnnotationReport seqARep;
144 StringBuilder tooltipText = new StringBuilder();
148 EditCommand editCommand;
150 StructureSelectionManager ssm;
152 SearchResultsI lastSearchResults;
155 * Creates a new SeqPanel object
160 public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel)
162 linkImageURL = getClass().getResource("/images/link.gif");
163 seqARep = new SequenceAnnotationReport(linkImageURL.toString());
164 ToolTipManager.sharedInstance().registerComponent(this);
165 ToolTipManager.sharedInstance().setInitialDelay(0);
166 ToolTipManager.sharedInstance().setDismissDelay(10000);
170 setBackground(Color.white);
172 seqCanvas = new SeqCanvas(alignPanel);
173 setLayout(new BorderLayout());
174 add(seqCanvas, BorderLayout.CENTER);
176 this.ap = alignPanel;
178 if (!viewport.isDataset())
180 addMouseMotionListener(this);
181 addMouseListener(this);
182 addMouseWheelListener(this);
183 ssm = viewport.getStructureSelectionManager();
184 ssm.addStructureViewerListener(this);
185 ssm.addSelectionListener(this);
188 lastMouseColumn = -1;
192 int startWrapBlock = -1;
194 int wrappedBlock = -1;
197 * Returns the aligned sequence position (base 0) at the mouse position, or
198 * the closest visible one
203 int findColumn(MouseEvent evt)
208 int startRes = av.getRanges().getStartRes();
209 if (av.getWrapAlignment())
212 int hgap = av.getCharHeight();
213 if (av.getScaleAboveWrapped())
215 hgap += av.getCharHeight();
218 int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
219 + hgap + seqCanvas.getAnnotationHeight();
222 y = Math.max(0, y - hgap);
223 x = Math.max(0, x - seqCanvas.getLabelWidthWest());
225 int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
231 wrappedBlock = y / cHeight;
232 wrappedBlock += startRes / cwidth;
233 // allow for wrapped view scrolled right (possible from Overview)
234 int startOffset = startRes % cwidth;
235 res = wrappedBlock * cwidth + startOffset
236 + +Math.min(cwidth - 1, x / av.getCharWidth());
240 if (x > seqCanvas.getX() + seqCanvas.getWidth())
242 // make sure we calculate relative to visible alignment, rather than
244 x = seqCanvas.getX() + seqCanvas.getWidth();
246 res = (x / av.getCharWidth()) + startRes;
247 if (res > av.getRanges().getEndRes())
250 res = av.getRanges().getEndRes();
254 if (av.hasHiddenColumns())
256 res = av.getAlignment().getHiddenColumns()
257 .visibleToAbsoluteColumn(res);
264 int findSeq(MouseEvent evt)
269 if (av.getWrapAlignment())
271 int hgap = av.getCharHeight();
272 if (av.getScaleAboveWrapped())
274 hgap += av.getCharHeight();
277 int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
278 + hgap + seqCanvas.getAnnotationHeight();
282 seq = Math.min((y % cHeight) / av.getCharHeight(),
283 av.getAlignment().getHeight() - 1);
288 (y / av.getCharHeight()) + av.getRanges().getStartSeq(),
289 av.getAlignment().getHeight() - 1);
296 * When all of a sequence of edits are complete, put the resulting edit list
297 * on the history stack (undo list), and reset flags for editing in progress.
303 if (editCommand != null && editCommand.getSize() > 0)
305 ap.alignFrame.addHistoryItem(editCommand);
306 av.firePropertyChange("alignment", null,
307 av.getAlignment().getSequences());
312 * Tidy up come what may...
317 groupEditing = false;
326 seqCanvas.cursorY = getKeyboardNo1() - 1;
327 scrollToVisible(true);
330 void setCursorColumn()
332 seqCanvas.cursorX = getKeyboardNo1() - 1;
333 scrollToVisible(true);
336 void setCursorRowAndColumn()
338 if (keyboardNo2 == null)
340 keyboardNo2 = new StringBuffer();
344 seqCanvas.cursorX = getKeyboardNo1() - 1;
345 seqCanvas.cursorY = getKeyboardNo2() - 1;
346 scrollToVisible(true);
350 void setCursorPosition()
352 SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
354 seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
355 scrollToVisible(true);
358 void moveCursor(int dx, int dy)
360 seqCanvas.cursorX += dx;
361 seqCanvas.cursorY += dy;
363 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
365 if (av.hasHiddenColumns() && !hidden.isVisible(seqCanvas.cursorX))
367 int original = seqCanvas.cursorX - dx;
368 int maxWidth = av.getAlignment().getWidth();
370 if (!hidden.isVisible(seqCanvas.cursorX))
372 int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx);
373 int[] region = hidden.getRegionWithEdgeAtRes(visx);
375 if (region != null) // just in case
380 seqCanvas.cursorX = region[1] + 1;
385 seqCanvas.cursorX = region[0] - 1;
388 seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX;
391 if (seqCanvas.cursorX >= maxWidth
392 || !hidden.isVisible(seqCanvas.cursorX))
394 seqCanvas.cursorX = original;
398 scrollToVisible(false);
402 * Scroll to make the cursor visible in the viewport.
405 * just jump to the location rather than scrolling
407 void scrollToVisible(boolean jump)
409 if (seqCanvas.cursorX < 0)
411 seqCanvas.cursorX = 0;
413 else if (seqCanvas.cursorX > av.getAlignment().getWidth() - 1)
415 seqCanvas.cursorX = av.getAlignment().getWidth() - 1;
418 if (seqCanvas.cursorY < 0)
420 seqCanvas.cursorY = 0;
422 else if (seqCanvas.cursorY > av.getAlignment().getHeight() - 1)
424 seqCanvas.cursorY = av.getAlignment().getHeight() - 1;
429 boolean repaintNeeded = true;
432 // only need to repaint if the viewport did not move, as otherwise it will
434 repaintNeeded = !av.getRanges().setViewportLocation(seqCanvas.cursorX,
439 if (av.getWrapAlignment())
441 // scrollToWrappedVisible expects x-value to have hidden cols subtracted
442 int x = av.getAlignment().getHiddenColumns()
443 .absoluteToVisibleColumn(seqCanvas.cursorX);
444 av.getRanges().scrollToWrappedVisible(x);
448 av.getRanges().scrollToVisible(seqCanvas.cursorX,
453 if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX))
455 setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
456 seqCanvas.cursorX, seqCanvas.cursorY);
466 void setSelectionAreaAtCursor(boolean topLeft)
468 SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
470 if (av.getSelectionGroup() != null)
472 SequenceGroup sg = av.getSelectionGroup();
473 // Find the top and bottom of this group
474 int min = av.getAlignment().getHeight(), max = 0;
475 for (int i = 0; i < sg.getSize(); i++)
477 int index = av.getAlignment().findIndex(sg.getSequenceAt(i));
492 sg.setStartRes(seqCanvas.cursorX);
493 if (sg.getEndRes() < seqCanvas.cursorX)
495 sg.setEndRes(seqCanvas.cursorX);
498 min = seqCanvas.cursorY;
502 sg.setEndRes(seqCanvas.cursorX);
503 if (sg.getStartRes() > seqCanvas.cursorX)
505 sg.setStartRes(seqCanvas.cursorX);
508 max = seqCanvas.cursorY + 1;
513 // Only the user can do this
514 av.setSelectionGroup(null);
518 // Now add any sequences between min and max
519 sg.getSequences(null).clear();
520 for (int i = min; i < max; i++)
522 sg.addSequence(av.getAlignment().getSequenceAt(i), false);
527 if (av.getSelectionGroup() == null)
529 SequenceGroup sg = new SequenceGroup();
530 sg.setStartRes(seqCanvas.cursorX);
531 sg.setEndRes(seqCanvas.cursorX);
532 sg.addSequence(sequence, false);
533 av.setSelectionGroup(sg);
536 ap.paintAlignment(false, false);
540 void insertGapAtCursor(boolean group)
542 groupEditing = group;
543 startseq = seqCanvas.cursorY;
544 lastres = seqCanvas.cursorX;
545 editSequence(true, false, seqCanvas.cursorX + getKeyboardNo1());
549 void deleteGapAtCursor(boolean group)
551 groupEditing = group;
552 startseq = seqCanvas.cursorY;
553 lastres = seqCanvas.cursorX + getKeyboardNo1();
554 editSequence(false, false, seqCanvas.cursorX);
558 void insertNucAtCursor(boolean group, String nuc)
560 // TODO not called - delete?
561 groupEditing = group;
562 startseq = seqCanvas.cursorY;
563 lastres = seqCanvas.cursorX;
564 editSequence(false, true, seqCanvas.cursorX + getKeyboardNo1());
568 void numberPressed(char value)
570 if (keyboardNo1 == null)
572 keyboardNo1 = new StringBuffer();
575 if (keyboardNo2 != null)
577 keyboardNo2.append(value);
581 keyboardNo1.append(value);
589 if (keyboardNo1 != null)
591 int value = Integer.parseInt(keyboardNo1.toString());
595 } catch (Exception x)
606 if (keyboardNo2 != null)
608 int value = Integer.parseInt(keyboardNo2.toString());
612 } catch (Exception x)
626 public void mouseReleased(MouseEvent evt)
628 boolean didDrag = mouseDragging; // did we come here after a drag
629 mouseDragging = false;
630 mouseWheelPressed = false;
632 if (evt.isPopupTrigger()) // Windows: mouseReleased
645 doMouseReleasedDefineMode(evt, didDrag);
656 public void mousePressed(MouseEvent evt)
658 lastMousePress = evt.getPoint();
660 if (SwingUtilities.isMiddleMouseButton(evt))
662 mouseWheelPressed = true;
666 boolean isControlDown = Platform.isControlDown(evt);
667 if (evt.isShiftDown() || isControlDown)
677 doMousePressedDefineMode(evt);
681 int seq = findSeq(evt);
682 int res = findColumn(evt);
684 if (seq < 0 || res < 0)
689 if ((seq < av.getAlignment().getHeight())
690 && (res < av.getAlignment().getSequenceAt(seq).getLength()))
706 private String formattedTooltipText;
709 public void mouseOverSequence(SequenceI sequence, int index, int pos)
711 String tmp = sequence.hashCode() + " " + index + " " + pos;
713 if (lastMessage == null || !lastMessage.equals(tmp))
715 // System.err.println("mouseOver Sequence: "+tmp);
716 ssm.mouseOverSequence(sequence, index, pos, av);
722 * Highlight the mapped region described by the search results object (unless
723 * unchanged). This supports highlight of protein while mousing over linked
724 * cDNA and vice versa. The status bar is also updated to show the location of
725 * the start of the highlighted region.
728 public void highlightSequence(SearchResultsI results)
730 if (results == null || results.equals(lastSearchResults))
734 lastSearchResults = results;
736 boolean wasScrolled = false;
738 if (av.isFollowHighlight())
740 // don't allow highlight of protein/cDNA to also scroll a complementary
741 // panel,as this sets up a feedback loop (scrolling panel 1 causes moused
742 // over residue to change abruptly, causing highlighted residue in panel 2
743 // to change, causing a scroll in panel 1 etc)
744 ap.setToScrollComplementPanel(false);
745 wasScrolled = ap.scrollToPosition(results, false);
748 seqCanvas.revalidate();
750 ap.setToScrollComplementPanel(true);
753 boolean fastPaint = !(wasScrolled && av.getWrapAlignment());
754 if (seqCanvas.highlightSearchResults(results, fastPaint))
756 setStatusMessage(results);
761 public VamsasSource getVamsasSource()
763 return this.ap == null ? null : this.ap.av;
767 public void updateColours(SequenceI seq, int index)
769 System.out.println("update the seqPanel colours");
774 * Action on mouse movement is to update the status bar to show the current
775 * sequence position, and (if features are shown) to show any features at the
776 * position in a tooltip. Does nothing if the mouse move does not change
782 public void mouseMoved(MouseEvent evt)
786 // This is because MacOSX creates a mouseMoved
787 // If control is down, other platforms will not.
791 final int column = findColumn(evt);
792 final int seq = findSeq(evt);
794 if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
799 if (column == lastMouseColumn && seq == lastMouseSeq)
802 * just a pixel move without change of residue
806 lastMouseColumn = column;
809 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
811 if (column >= sequence.getLength())
817 * set status bar message, returning residue position in sequence
819 boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
820 final int pos = setStatusMessage(sequence, column, seq);
821 if (ssm != null && !isGapped)
823 mouseOverSequence(sequence, column, pos);
826 tooltipText.setLength(6); // "<html>"
828 SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence);
831 for (int g = 0; g < groups.length; g++)
833 if (groups[g].getStartRes() <= column
834 && groups[g].getEndRes() >= column)
836 if (!groups[g].getName().startsWith("JTreeGroup")
837 && !groups[g].getName().startsWith("JGroup"))
839 tooltipText.append(groups[g].getName());
842 if (groups[g].getDescription() != null)
844 tooltipText.append(": " + groups[g].getDescription());
851 * add any features at the position to the tooltip; if over a gap, only
852 * add features that straddle the gap (pos may be the residue before or
855 if (av.isShowSequenceFeatures())
857 List<SequenceFeature> features = ap.getFeatureRenderer()
858 .findFeaturesAtColumn(sequence, column + 1);
859 seqARep.appendFeatures(tooltipText, pos, features,
860 this.ap.getSeqPanel().seqCanvas.fr);
862 if (tooltipText.length() == 6) // <html>
864 setToolTipText(null);
869 if (tooltipText.length() > MAX_TOOLTIP_LENGTH) // constant
871 tooltipText.setLength(MAX_TOOLTIP_LENGTH);
872 tooltipText.append("...");
874 String textString = tooltipText.toString();
875 if (lastTooltip == null || !lastTooltip.equals(textString))
877 formattedTooltipText = JvSwingUtils.wrapTooltip(true,
879 setToolTipText(formattedTooltipText);
880 lastTooltip = textString;
886 private Point lastp = null;
888 private JToolTip tempTip = new JLabel().createToolTip();
893 * @see javax.swing.JComponent#getToolTipLocation(java.awt.event.MouseEvent)
896 public Point getToolTipLocation(MouseEvent event)
900 if (tooltipText == null || tooltipText.length() == 6)
903 if (lastp != null && event.isShiftDown())
907 int x = event.getX();
908 int y = event.getY();
911 tempTip.setTipText(formattedTooltipText);
912 int tipWidth = (int) tempTip.getPreferredSize().getWidth();
914 // was x += (w - x < 200) ? -(w / 2) : 5;
915 x = (x + tipWidth < w ? x + 10 : w - tipWidth);
916 p = new Point(x, y + 20); // BH 2018 was - 20?
918 * TODO: try to modify position region is not obcured by tooltip
929 * set when the current UI interaction has resulted in a change that requires
930 * shading in overviews and structures to be recalculated. this could be
931 * changed to a something more expressive that indicates what actually has
932 * changed, so selective redraws can be applied (ie. only structures, only
935 private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller
938 * set if av.getSelectionGroup() refers to a group that is defined on the
939 * alignment view, rather than a transient selection
941 // private boolean editingDefinedGroup = false; // TODO: refactor to
942 // avcontroller or viewModel
945 * Sets the status message in alignment panel, showing the sequence number
946 * (index) and id, and residue and residue position if not at a gap, for the
947 * given sequence and column position. Returns the residue position returned
948 * by Sequence.findPosition. Note this may be for the nearest adjacent residue
949 * if at a gapped position.
952 * aligned sequence object
956 * index of sequence in alignment
957 * @return sequence position of residue at column, or adjacent residue if at a
960 int setStatusMessage(SequenceI sequence, final int column, int seqIndex)
962 char sequenceChar = sequence.getCharAt(column);
963 int pos = sequence.findPosition(column);
964 setStatusMessage(sequence, seqIndex, sequenceChar, pos);
970 * Builds the status message for the current cursor location and writes it to
971 * the status bar, for example
974 * Sequence 3 ID: FER1_SOLLC
975 * Sequence 5 ID: FER1_PEA Residue: THR (4)
976 * Sequence 5 ID: FER1_PEA Residue: B (3)
977 * Sequence 6 ID: O.niloticus.3 Nucleotide: Uracil (2)
982 * sequence position in the alignment (1..)
983 * @param sequenceChar
984 * the character under the cursor
986 * the sequence residue position (if not over a gap)
988 protected void setStatusMessage(SequenceI sequence, int seqIndex,
989 char sequenceChar, int residuePos)
991 StringBuilder text = new StringBuilder(32);
994 * Sequence number (if known), and sequence name.
996 String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1);
997 text.append("Sequence").append(seqno).append(" ID: ")
998 .append(sequence.getName());
1000 String residue = null;
1003 * Try to translate the display character to residue name (null for gap).
1005 boolean isGapped = Comparison.isGap(sequenceChar);
1009 boolean nucleotide = av.getAlignment().isNucleotide();
1010 String displayChar = String.valueOf(sequenceChar);
1013 residue = ResidueProperties.nucleotideName.get(displayChar);
1017 residue = "X".equalsIgnoreCase(displayChar) ? "X"
1018 : ("*".equals(displayChar) ? "STOP"
1019 : ResidueProperties.aa2Triplet.get(displayChar));
1021 text.append(" ").append(nucleotide ? "Nucleotide" : "Residue")
1022 .append(": ").append(residue == null ? displayChar : residue);
1024 text.append(" (").append(Integer.toString(residuePos)).append(")");
1026 ap.alignFrame.setStatus(text.toString());
1030 * Set the status bar message to highlight the first matched position in
1035 private void setStatusMessage(SearchResultsI results)
1037 AlignmentI al = this.av.getAlignment();
1038 int sequenceIndex = al.findIndex(results);
1039 if (sequenceIndex == -1)
1043 SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence();
1044 for (SearchResultMatchI m : results.getResults())
1046 SequenceI seq = m.getSequence();
1047 if (seq.getDatasetSequence() != null)
1049 seq = seq.getDatasetSequence();
1054 int start = m.getStart();
1055 setStatusMessage(seq, sequenceIndex, seq.getCharAt(start - 1),
1066 public void mouseDragged(MouseEvent evt)
1068 if (mouseWheelPressed)
1070 boolean inSplitFrame = ap.av.getCodingComplement() != null;
1071 boolean copyChanges = inSplitFrame && av.isProteinFontAsCdna();
1073 int oldWidth = av.getCharWidth();
1075 // Which is bigger, left-right or up-down?
1076 if (Math.abs(evt.getY() - lastMousePress.getY()) > Math
1077 .abs(evt.getX() - lastMousePress.getX()))
1080 * on drag up or down, decrement or increment font size
1082 int fontSize = av.font.getSize();
1083 boolean fontChanged = false;
1085 if (evt.getY() < lastMousePress.getY())
1090 else if (evt.getY() > lastMousePress.getY())
1103 Font newFont = new Font(av.font.getName(), av.font.getStyle(),
1105 av.setFont(newFont, true);
1106 av.setCharWidth(oldWidth);
1110 ap.av.getCodingComplement().setFont(newFont, true);
1111 SplitFrame splitFrame = (SplitFrame) ap.alignFrame
1112 .getSplitViewContainer();
1113 splitFrame.adjustLayout();
1114 splitFrame.repaint();
1121 * on drag left or right, decrement or increment character width
1124 if (evt.getX() < lastMousePress.getX() && av.getCharWidth() > 1)
1126 newWidth = av.getCharWidth() - 1;
1127 av.setCharWidth(newWidth);
1129 else if (evt.getX() > lastMousePress.getX())
1131 newWidth = av.getCharWidth() + 1;
1132 av.setCharWidth(newWidth);
1136 ap.paintAlignment(false, false);
1140 * need to ensure newWidth is set on cdna, regardless of which
1141 * panel the mouse drag happened in; protein will compute its
1142 * character width as 1:1 or 3:1
1144 av.getCodingComplement().setCharWidth(newWidth);
1145 SplitFrame splitFrame = (SplitFrame) ap.alignFrame
1146 .getSplitViewContainer();
1147 splitFrame.adjustLayout();
1148 splitFrame.repaint();
1153 FontMetrics fm = getFontMetrics(av.getFont());
1154 av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
1156 lastMousePress = evt.getPoint();
1163 doMouseDraggedDefineMode(evt);
1167 int res = findColumn(evt);
1174 if ((lastres == -1) || (lastres == res))
1179 if ((res < av.getAlignment().getWidth()) && (res < lastres))
1181 // dragLeft, delete gap
1182 editSequence(false, false, res);
1186 editSequence(true, false, res);
1189 mouseDragging = true;
1190 if (scrollThread != null)
1192 scrollThread.setMousePosition(evt.getPoint());
1196 // TODO: Make it more clever than many booleans
1197 synchronized void editSequence(boolean insertGap, boolean editSeq,
1201 int fixedRight = -1;
1202 boolean fixedColumns = false;
1203 SequenceGroup sg = av.getSelectionGroup();
1205 SequenceI seq = av.getAlignment().getSequenceAt(startseq);
1207 // No group, but the sequence may represent a group
1208 if (!groupEditing && av.hasHiddenRows())
1210 if (av.isHiddenRepSequence(seq))
1212 sg = av.getRepresentedSequences(seq);
1213 groupEditing = true;
1217 StringBuilder message = new StringBuilder(64);
1220 message.append("Edit group:");
1221 if (editCommand == null)
1223 editCommand = new EditCommand(
1224 MessageManager.getString("action.edit_group"));
1229 message.append("Edit sequence: " + seq.getName());
1230 String label = seq.getName();
1231 if (label.length() > 10)
1233 label = label.substring(0, 10);
1235 if (editCommand == null)
1237 editCommand = new EditCommand(MessageManager
1238 .formatMessage("label.edit_params", new String[]
1245 message.append(" insert ");
1249 message.append(" delete ");
1252 message.append(Math.abs(startres - lastres) + " gaps.");
1253 ap.alignFrame.setStatus(message.toString());
1255 // Are we editing within a selection group?
1256 if (groupEditing || (sg != null
1257 && sg.getSequences(av.getHiddenRepSequences()).contains(seq)))
1259 fixedColumns = true;
1261 // sg might be null as the user may only see 1 sequence,
1262 // but the sequence represents a group
1265 if (!av.isHiddenRepSequence(seq))
1270 sg = av.getRepresentedSequences(seq);
1273 fixedLeft = sg.getStartRes();
1274 fixedRight = sg.getEndRes();
1276 if ((startres < fixedLeft && lastres >= fixedLeft)
1277 || (startres >= fixedLeft && lastres < fixedLeft)
1278 || (startres > fixedRight && lastres <= fixedRight)
1279 || (startres <= fixedRight && lastres > fixedRight))
1285 if (fixedLeft > startres)
1287 fixedRight = fixedLeft - 1;
1290 else if (fixedRight < startres)
1292 fixedLeft = fixedRight;
1297 if (av.hasHiddenColumns())
1299 fixedColumns = true;
1300 int y1 = av.getAlignment().getHiddenColumns()
1301 .getNextHiddenBoundary(true, startres);
1302 int y2 = av.getAlignment().getHiddenColumns()
1303 .getNextHiddenBoundary(false, startres);
1305 if ((insertGap && startres > y1 && lastres < y1)
1306 || (!insertGap && startres < y2 && lastres > y2))
1312 // System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");
1313 // Selection spans a hidden region
1314 if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))
1322 fixedRight = y2 - 1;
1329 List<SequenceI> vseqs = sg.getSequences(av.getHiddenRepSequences());
1330 int g, groupSize = vseqs.size();
1331 SequenceI[] groupSeqs = new SequenceI[groupSize];
1332 for (g = 0; g < groupSeqs.length; g++)
1334 groupSeqs[g] = vseqs.get(g);
1340 // If the user has selected the whole sequence, and is dragging to
1341 // the right, we can still extend the alignment and selectionGroup
1342 if (sg.getStartRes() == 0 && sg.getEndRes() == fixedRight
1343 && sg.getEndRes() == av.getAlignment().getWidth() - 1)
1345 sg.setEndRes(av.getAlignment().getWidth() + startres - lastres);
1346 fixedRight = sg.getEndRes();
1349 // Is it valid with fixed columns??
1350 // Find the next gap before the end
1351 // of the visible region boundary
1352 boolean blank = false;
1353 for (; fixedRight > lastres; fixedRight--)
1357 for (g = 0; g < groupSize; g++)
1359 for (int j = 0; j < startres - lastres; j++)
1361 if (!Comparison.isGap(groupSeqs[g].getCharAt(fixedRight - j)))
1376 if (sg.getSize() == av.getAlignment().getHeight())
1378 if ((av.hasHiddenColumns() && startres < av.getAlignment()
1380 .getNextHiddenBoundary(false, startres)))
1386 int alWidth = av.getAlignment().getWidth();
1387 if (av.hasHiddenRows())
1389 int hwidth = av.getAlignment().getHiddenSequences()
1391 if (hwidth > alWidth)
1396 // We can still insert gaps if the selectionGroup
1397 // contains all the sequences
1398 sg.setEndRes(sg.getEndRes() + startres - lastres);
1399 fixedRight = alWidth + startres - lastres;
1410 else if (!insertGap)
1412 // / Are we able to delete?
1413 // ie are all columns blank?
1415 for (g = 0; g < groupSize; g++)
1417 for (int j = startres; j < lastres; j++)
1419 if (groupSeqs[g].getLength() <= j)
1424 if (!Comparison.isGap(groupSeqs[g].getCharAt(j)))
1426 // Not a gap, block edit not valid
1436 // dragging to the right
1437 if (fixedColumns && fixedRight != -1)
1439 for (int j = lastres; j < startres; j++)
1441 insertChar(j, groupSeqs, fixedRight);
1446 appendEdit(Action.INSERT_GAP, groupSeqs, startres,
1447 startres - lastres);
1452 // dragging to the left
1453 if (fixedColumns && fixedRight != -1)
1455 for (int j = lastres; j > startres; j--)
1457 deleteChar(startres, groupSeqs, fixedRight);
1462 appendEdit(Action.DELETE_GAP, groupSeqs, startres,
1463 lastres - startres);
1469 // ///Editing a single sequence///////////
1473 // dragging to the right
1474 if (fixedColumns && fixedRight != -1)
1476 for (int j = lastres; j < startres; j++)
1478 insertChar(j, new SequenceI[] { seq }, fixedRight);
1483 appendEdit(Action.INSERT_GAP, new SequenceI[] { seq }, lastres,
1484 startres - lastres);
1491 // dragging to the left
1492 if (fixedColumns && fixedRight != -1)
1494 for (int j = lastres; j > startres; j--)
1496 if (!Comparison.isGap(seq.getCharAt(startres)))
1501 deleteChar(startres, new SequenceI[] { seq }, fixedRight);
1506 // could be a keyboard edit trying to delete none gaps
1508 for (int m = startres; m < lastres; m++)
1510 if (!Comparison.isGap(seq.getCharAt(m)))
1519 appendEdit(Action.DELETE_GAP, new SequenceI[] { seq },
1525 {// insertGap==false AND editSeq==TRUE;
1526 if (fixedColumns && fixedRight != -1)
1528 for (int j = lastres; j < startres; j++)
1530 insertChar(j, new SequenceI[] { seq }, fixedRight);
1535 appendEdit(Action.INSERT_NUC, new SequenceI[] { seq }, lastres,
1536 startres - lastres);
1543 seqCanvas.repaint();
1546 void insertChar(int j, SequenceI[] seq, int fixedColumn)
1548 int blankColumn = fixedColumn;
1549 for (int s = 0; s < seq.length; s++)
1551 // Find the next gap before the end of the visible region boundary
1552 // If lastCol > j, theres a boundary after the gap insertion
1554 for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)
1556 if (Comparison.isGap(seq[s].getCharAt(blankColumn)))
1558 // Theres a space, so break and insert the gap
1563 if (blankColumn <= j)
1565 blankColumn = fixedColumn;
1571 appendEdit(Action.DELETE_GAP, seq, blankColumn, 1);
1573 appendEdit(Action.INSERT_GAP, seq, j, 1);
1578 * Helper method to add and perform one edit action.
1585 protected void appendEdit(Action action, SequenceI[] seq, int pos,
1589 final Edit edit = new EditCommand().new Edit(action, seq, pos, count,
1590 av.getAlignment().getGapCharacter());
1592 editCommand.appendEdit(edit, av.getAlignment(), true, null);
1595 void deleteChar(int j, SequenceI[] seq, int fixedColumn)
1598 appendEdit(Action.DELETE_GAP, seq, j, 1);
1600 appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1);
1604 * On reentering the panel, stops any scrolling that was started on dragging
1610 public void mouseEntered(MouseEvent e)
1620 * On leaving the panel, if the mouse is being dragged, starts a thread to
1621 * scroll it until the mouse is released (in unwrapped mode only)
1626 public void mouseExited(MouseEvent e)
1630 startScrolling(e.getPoint());
1635 * Handler for double-click on a position with one or more sequence features.
1636 * Opens the Amend Features dialog to allow feature details to be amended, or
1637 * the feature deleted.
1640 public void mouseClicked(MouseEvent evt)
1642 SequenceGroup sg = null;
1643 SequenceI sequence = av.getAlignment().getSequenceAt(findSeq(evt));
1644 if (evt.getClickCount() > 1)
1646 sg = av.getSelectionGroup();
1647 if (sg != null && sg.getSize() == 1
1648 && sg.getEndRes() - sg.getStartRes() < 2)
1650 av.setSelectionGroup(null);
1653 int column = findColumn(evt);
1656 * find features at the position (if not gapped), or straddling
1657 * the position (if at a gap)
1659 List<SequenceFeature> features = seqCanvas.getFeatureRenderer()
1660 .findFeaturesAtColumn(sequence, column + 1);
1662 if (!features.isEmpty())
1665 * highlight the first feature at the position on the alignment
1667 SearchResultsI highlight = new SearchResults();
1668 highlight.addResult(sequence, features.get(0).getBegin(), features
1670 seqCanvas.highlightSearchResults(highlight, true);
1673 * open the Amend Features dialog
1675 new FeatureEditor(ap, Collections.singletonList(sequence), features,
1676 false).showDialog();
1682 public void mouseWheelMoved(MouseWheelEvent e)
1685 double wheelRotation = e.getPreciseWheelRotation();
1686 if (wheelRotation > 0)
1688 if (e.isShiftDown())
1690 av.getRanges().scrollRight(true);
1695 av.getRanges().scrollUp(false);
1698 else if (wheelRotation < 0)
1700 if (e.isShiftDown())
1702 av.getRanges().scrollRight(false);
1706 av.getRanges().scrollUp(true);
1711 * update status bar and tooltip for new position
1712 * (need to synthesize a mouse movement to refresh tooltip)
1715 ToolTipManager.sharedInstance().mouseMoved(e);
1724 public void doMousePressedDefineMode(MouseEvent evt)
1726 final int res = findColumn(evt);
1727 final int seq = findSeq(evt);
1729 updateOverviewAndStructs = false;
1731 startWrapBlock = wrappedBlock;
1733 if (av.getWrapAlignment() && seq > av.getAlignment().getHeight())
1735 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1736 MessageManager.getString(
1737 "label.cannot_edit_annotations_in_wrapped_view"),
1738 MessageManager.getString("label.wrapped_view_no_edit"),
1739 JvOptionPane.WARNING_MESSAGE);
1743 if (seq < 0 || res < 0)
1748 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
1750 if ((sequence == null) || (res > sequence.getLength()))
1755 stretchGroup = av.getSelectionGroup();
1757 if (stretchGroup == null || !stretchGroup.contains(sequence, res))
1759 stretchGroup = av.getAlignment().findGroup(sequence, res);
1760 if (stretchGroup != null)
1762 // only update the current selection if the popup menu has a group to
1764 av.setSelectionGroup(stretchGroup);
1769 * defer right-mouse click handling to mouseReleased on Windows
1770 * (where isPopupTrigger() will answer true)
1771 * NB isRightMouseButton is also true for Cmd-click on Mac
1773 if (Platform.isWinRightButton(evt))
1778 if (evt.isPopupTrigger()) // Mac: mousePressed
1786 seqCanvas.cursorX = findColumn(evt);
1787 seqCanvas.cursorY = findSeq(evt);
1788 seqCanvas.repaint();
1792 if (stretchGroup == null)
1794 createStretchGroup(res, sequence);
1797 if (stretchGroup != null)
1799 stretchGroup.addPropertyChangeListener(seqCanvas);
1802 seqCanvas.repaint();
1805 private void createStretchGroup(int res, SequenceI sequence)
1807 // Only if left mouse button do we want to change group sizes
1808 // define a new group here
1809 SequenceGroup sg = new SequenceGroup();
1810 sg.setStartRes(res);
1812 sg.addSequence(sequence, false);
1813 av.setSelectionGroup(sg);
1816 if (av.getConservationSelected())
1818 SliderPanel.setConservationSlider(ap, av.getResidueShading(),
1822 if (av.getAbovePIDThreshold())
1824 SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
1827 // TODO: stretchGroup will always be not null. Is this a merge error ?
1828 // or is there a threading issue here?
1829 if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
1831 // Edit end res position of selected group
1832 changeEndRes = true;
1834 else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))
1836 // Edit end res position of selected group
1837 changeStartRes = true;
1839 stretchGroup.getWidth();
1844 * Build and show a pop-up menu at the right-click mouse position
1850 void showPopupMenu(MouseEvent evt)
1852 final int column = findColumn(evt);
1853 final int seq = findSeq(evt);
1854 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
1855 List<SequenceFeature> features = ap.getFeatureRenderer()
1856 .findFeaturesAtColumn(sequence, column + 1);
1858 PopupMenu pop = new PopupMenu(ap, null, features);
1859 pop.show(this, evt.getX(), evt.getY());
1863 * Update the display after mouse up on a selection or group
1866 * mouse released event details
1868 * true if this event is happening after a mouse drag (rather than a
1871 public void doMouseReleasedDefineMode(MouseEvent evt, boolean afterDrag)
1873 if (stretchGroup == null)
1878 stretchGroup.removePropertyChangeListener(seqCanvas);
1880 // always do this - annotation has own state
1881 // but defer colourscheme update until hidden sequences are passed in
1882 boolean vischange = stretchGroup.recalcConservation(true);
1883 updateOverviewAndStructs |= vischange && av.isSelectionDefinedGroup()
1885 if (stretchGroup.cs != null)
1887 stretchGroup.cs.alignmentChanged(stretchGroup,
1888 av.getHiddenRepSequences());
1890 ResidueShaderI groupColourScheme = stretchGroup
1891 .getGroupColourScheme();
1892 String name = stretchGroup.getName();
1893 if (stretchGroup.cs.conservationApplied())
1895 SliderPanel.setConservationSlider(ap, groupColourScheme, name);
1897 if (stretchGroup.cs.getThreshold() > 0)
1899 SliderPanel.setPIDSliderSource(ap, groupColourScheme, name);
1902 PaintRefresher.Refresh(this, av.getSequenceSetId());
1903 // TODO: structure colours only need updating if stretchGroup used to or now
1904 // does contain sequences with structure views
1905 ap.paintAlignment(updateOverviewAndStructs, updateOverviewAndStructs);
1906 updateOverviewAndStructs = false;
1907 changeEndRes = false;
1908 changeStartRes = false;
1909 stretchGroup = null;
1919 public void doMouseDraggedDefineMode(MouseEvent evt)
1921 int res = findColumn(evt);
1922 int y = findSeq(evt);
1924 if (wrappedBlock != startWrapBlock)
1929 if (stretchGroup == null)
1934 res = Math.min(res, av.getAlignment().getWidth()-1);
1936 if (stretchGroup.getEndRes() == res)
1938 // Edit end res position of selected group
1939 changeEndRes = true;
1941 else if (stretchGroup.getStartRes() == res)
1943 // Edit start res position of selected group
1944 changeStartRes = true;
1947 if (res < av.getRanges().getStartRes())
1949 res = av.getRanges().getStartRes();
1954 if (res > (stretchGroup.getStartRes() - 1))
1956 stretchGroup.setEndRes(res);
1957 updateOverviewAndStructs |= av.isSelectionDefinedGroup();
1960 else if (changeStartRes)
1962 if (res < (stretchGroup.getEndRes() + 1))
1964 stretchGroup.setStartRes(res);
1965 updateOverviewAndStructs |= av.isSelectionDefinedGroup();
1969 int dragDirection = 0;
1975 else if (y < oldSeq)
1980 while ((y != oldSeq) && (oldSeq > -1)
1981 && (y < av.getAlignment().getHeight()))
1983 // This routine ensures we don't skip any sequences, as the
1984 // selection is quite slow.
1985 Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1987 oldSeq += dragDirection;
1994 Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1996 if (stretchGroup.getSequences(null).contains(nextSeq))
1998 stretchGroup.deleteSequence(seq, false);
1999 updateOverviewAndStructs |= av.isSelectionDefinedGroup();
2005 stretchGroup.addSequence(seq, false);
2008 stretchGroup.addSequence(nextSeq, false);
2009 updateOverviewAndStructs |= av.isSelectionDefinedGroup();
2018 mouseDragging = true;
2020 if (scrollThread != null)
2022 scrollThread.setMousePosition(evt.getPoint());
2027 * Stops the scroll thread if it is running
2029 void stopScrolling()
2031 if (scrollThread != null)
2033 scrollThread.stopScrolling();
2034 scrollThread = null;
2036 mouseDragging = false;
2040 * Starts a thread to scroll the alignment, towards a given mouse position
2041 * outside the panel bounds, unless the alignment is in wrapped mode
2045 void startScrolling(Point mousePos)
2048 * set this.mouseDragging in case this was called from
2049 * a drag in ScalePanel or AnnotationPanel
2051 mouseDragging = true;
2052 if (!av.getWrapAlignment() && scrollThread == null)
2054 scrollThread = new ScrollThread();
2055 scrollThread.setMousePosition(mousePos);
2056 if (!Platform.isJS())
2059 * Java - run in a new thread
2061 scrollThread.start();
2066 * Javascript - run every 20ms until scrolling stopped
2067 * or reaches the limit of scrollable alignment
2069 // java.util.Timer version:
2070 // Timer t = new Timer("ScrollThreadTimer", true);
2071 // TimerTask task = new TimerTask()
2074 // public void run()
2076 // if (!scrollThread.scrollOnce())
2082 // t.schedule(task, 20, 20);
2083 Timer t = new Timer(20, new ActionListener()
2086 public void actionPerformed(ActionEvent e)
2088 if (scrollThread != null)
2090 // if (!scrollOnce() {t.stop();}) gives compiler error :-(
2091 scrollThread.scrollOnce();
2095 t.addActionListener(new ActionListener()
2098 public void actionPerformed(ActionEvent e)
2100 if (scrollThread == null)
2102 // finished and nulled itself
2113 * Performs scrolling of the visible alignment left, right, up or down, until
2114 * scrolling is stopped by calling stopScrolling, mouse drag is ended, or the
2115 * limit of the alignment is reached
2117 class ScrollThread extends Thread
2119 private Point mousePos;
2121 private volatile boolean keepRunning = true;
2126 public ScrollThread()
2128 setName("SeqPanel$ScrollThread");
2132 * Sets the position of the mouse that determines the direction of the
2133 * scroll to perform. If this is called as the mouse moves, scrolling should
2134 * respond accordingly. For example, if the mouse is dragged right, scroll
2135 * right should start; if the drag continues down, scroll down should also
2140 public void setMousePosition(Point p)
2146 * Sets a flag that will cause the thread to exit
2148 public void stopScrolling()
2150 keepRunning = false;
2154 * Scrolls the alignment left or right, and/or up or down, depending on the
2155 * last notified mouse position, until the limit of the alignment is
2156 * reached, or a flag is set to stop the scroll
2163 if (mousePos != null)
2165 keepRunning = scrollOnce();
2170 } catch (Exception ex)
2174 SeqPanel.this.scrollThread = null;
2180 * <li>one row up, if the mouse is above the panel</li>
2181 * <li>one row down, if the mouse is below the panel</li>
2182 * <li>one column left, if the mouse is left of the panel</li>
2183 * <li>one column right, if the mouse is right of the panel</li>
2185 * Answers true if a scroll was performed, false if not - meaning either
2186 * that the mouse position is within the panel, or the edge of the alignment
2189 boolean scrollOnce()
2192 * quit after mouseUp ensures interrupt in JalviewJS
2199 boolean scrolled = false;
2200 ViewportRanges ranges = SeqPanel.this.av.getRanges();
2207 // mouse is above this panel - try scroll up
2208 scrolled = ranges.scrollUp(true);
2210 else if (mousePos.y >= getHeight())
2212 // mouse is below this panel - try scroll down
2213 scrolled = ranges.scrollUp(false);
2217 * scroll left or right
2221 scrolled |= ranges.scrollRight(false);
2223 else if (mousePos.x >= getWidth())
2225 scrolled |= ranges.scrollRight(true);
2232 * modify current selection according to a received message.
2235 public void selection(SequenceGroup seqsel, ColumnSelection colsel,
2236 HiddenColumns hidden, SelectionSource source)
2238 // TODO: fix this hack - source of messages is align viewport, but SeqPanel
2239 // handles selection messages...
2240 // TODO: extend config options to allow user to control if selections may be
2241 // shared between viewports.
2242 boolean iSentTheSelection = (av == source
2243 || (source instanceof AlignViewport
2244 && ((AlignmentViewport) source).getSequenceSetId()
2245 .equals(av.getSequenceSetId())));
2247 if (iSentTheSelection)
2249 // respond to our own event by updating dependent dialogs
2250 if (ap.getCalculationDialog() != null)
2252 ap.getCalculationDialog().validateCalcTypes();
2258 // process further ?
2259 if (!av.followSelection)
2265 * Ignore the selection if there is one of our own pending.
2267 if (av.isSelectionGroupChanged(false) || av.isColSelChanged(false))
2273 * Check for selection in a view of which this one is a dna/protein
2276 if (selectionFromTranslation(seqsel, colsel, hidden, source))
2281 // do we want to thread this ? (contention with seqsel and colsel locks, I
2284 * only copy colsel if there is a real intersection between
2285 * sequence selection and this panel's alignment
2287 boolean repaint = false;
2288 boolean copycolsel = false;
2290 SequenceGroup sgroup = null;
2291 if (seqsel != null && seqsel.getSize() > 0)
2293 if (av.getAlignment() == null)
2295 Cache.log.warn("alignviewport av SeqSetId=" + av.getSequenceSetId()
2296 + " ViewId=" + av.getViewId()
2297 + " 's alignment is NULL! returning immediately.");
2300 sgroup = seqsel.intersect(av.getAlignment(),
2301 (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
2302 if ((sgroup != null && sgroup.getSize() > 0))
2307 if (sgroup != null && sgroup.getSize() > 0)
2309 av.setSelectionGroup(sgroup);
2313 av.setSelectionGroup(null);
2315 av.isSelectionGroupChanged(true);
2320 // the current selection is unset or from a previous message
2321 // so import the new colsel.
2322 if (colsel == null || colsel.isEmpty())
2324 if (av.getColumnSelection() != null)
2326 av.getColumnSelection().clear();
2332 // TODO: shift colSel according to the intersecting sequences
2333 if (av.getColumnSelection() == null)
2335 av.setColumnSelection(new ColumnSelection(colsel));
2339 av.getColumnSelection().setElementsFrom(colsel,
2340 av.getAlignment().getHiddenColumns());
2343 av.isColSelChanged(true);
2347 if (copycolsel && av.hasHiddenColumns()
2348 && (av.getAlignment().getHiddenColumns() == null))
2350 System.err.println("Bad things");
2352 if (repaint) // always true!
2354 // probably finessing with multiple redraws here
2355 PaintRefresher.Refresh(this, av.getSequenceSetId());
2356 // ap.paintAlignment(false);
2359 // lastly, update dependent dialogs
2360 if (ap.getCalculationDialog() != null)
2362 ap.getCalculationDialog().validateCalcTypes();
2368 * If this panel is a cdna/protein translation view of the selection source,
2369 * tries to map the source selection to a local one, and returns true. Else
2376 protected boolean selectionFromTranslation(SequenceGroup seqsel,
2377 ColumnSelection colsel, HiddenColumns hidden,
2378 SelectionSource source)
2380 if (!(source instanceof AlignViewportI))
2384 final AlignViewportI sourceAv = (AlignViewportI) source;
2385 if (sourceAv.getCodingComplement() != av
2386 && av.getCodingComplement() != sourceAv)
2392 * Map sequence selection
2394 SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
2395 av.setSelectionGroup(sg);
2396 av.isSelectionGroupChanged(true);
2399 * Map column selection
2401 // ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
2403 ColumnSelection cs = new ColumnSelection();
2404 HiddenColumns hs = new HiddenColumns();
2405 MappingUtils.mapColumnSelection(colsel, hidden, sourceAv, av, cs, hs);
2406 av.setColumnSelection(cs);
2407 av.getAlignment().setHiddenColumns(hs);
2409 // lastly, update any dependent dialogs
2410 if (ap.getCalculationDialog() != null)
2412 ap.getCalculationDialog().validateCalcTypes();
2415 PaintRefresher.Refresh(this, av.getSequenceSetId());
2422 * @return null or last search results handled by this panel
2424 public SearchResultsI getLastSearchResults()
2426 return lastSearchResults;