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.bin.Jalview;
26 import jalview.commands.EditCommand;
27 import jalview.commands.EditCommand.Action;
28 import jalview.commands.EditCommand.Edit;
29 import jalview.datamodel.AlignmentI;
30 import jalview.datamodel.ColumnSelection;
31 import jalview.datamodel.HiddenColumns;
32 import jalview.datamodel.SearchResultMatchI;
33 import jalview.datamodel.SearchResults;
34 import jalview.datamodel.SearchResultsI;
35 import jalview.datamodel.Sequence;
36 import jalview.datamodel.SequenceFeature;
37 import jalview.datamodel.SequenceGroup;
38 import jalview.datamodel.SequenceI;
39 import jalview.io.SequenceAnnotationReport;
40 import jalview.renderer.ResidueShaderI;
41 import jalview.schemes.ResidueProperties;
42 import jalview.structure.SelectionListener;
43 import jalview.structure.SelectionSource;
44 import jalview.structure.SequenceListener;
45 import jalview.structure.StructureSelectionManager;
46 import jalview.structure.VamsasSource;
47 import jalview.util.Comparison;
48 import jalview.util.MappingUtils;
49 import jalview.util.MessageManager;
50 import jalview.util.Platform;
51 import jalview.viewmodel.AlignmentViewport;
52 import jalview.viewmodel.ViewportRanges;
54 import java.awt.BorderLayout;
55 import java.awt.Color;
57 import java.awt.FontMetrics;
58 import java.awt.Point;
59 import java.awt.event.ActionEvent;
60 import java.awt.event.ActionListener;
61 import java.awt.event.MouseEvent;
62 import java.awt.event.MouseListener;
63 import java.awt.event.MouseMotionListener;
64 import java.awt.event.MouseWheelEvent;
65 import java.awt.event.MouseWheelListener;
66 import java.util.Collections;
67 import java.util.List;
69 import javax.swing.JLabel;
70 import javax.swing.JPanel;
71 import javax.swing.JToolTip;
72 import javax.swing.SwingUtilities;
73 import javax.swing.Timer;
74 import javax.swing.ToolTipManager;
80 * @version $Revision: 1.130 $
82 public class SeqPanel extends JPanel
83 implements MouseListener, MouseMotionListener, MouseWheelListener,
84 SequenceListener, SelectionListener
86 private static final int MAX_TOOLTIP_LENGTH = 300;
88 public SeqCanvas seqCanvas;
90 public AlignmentPanel ap;
93 * last column position for mouseMoved event
95 private int lastMouseColumn;
98 * last sequence offset for mouseMoved event
100 private int lastMouseSeq;
102 protected int lastres;
104 protected int startseq;
106 protected AlignViewport av;
108 ScrollThread scrollThread = null;
110 boolean mouseDragging = false;
112 boolean editingSeqs = false;
114 boolean groupEditing = false;
116 // ////////////////////////////////////////
117 // ///Everything below this is for defining the boundary of the rubberband
118 // ////////////////////////////////////////
121 boolean changeEndSeq = false;
123 boolean changeStartSeq = false;
125 boolean changeEndRes = false;
127 boolean changeStartRes = false;
129 SequenceGroup stretchGroup = null;
131 boolean remove = false;
133 Point lastMousePress;
135 boolean mouseWheelPressed = false;
137 StringBuffer keyboardNo1;
139 StringBuffer keyboardNo2;
141 java.net.URL linkImageURL;
143 private final SequenceAnnotationReport seqARep;
145 StringBuilder tooltipText = new StringBuilder();
149 EditCommand editCommand;
151 StructureSelectionManager ssm;
153 SearchResultsI lastSearchResults;
156 * Creates a new SeqPanel object
161 public SeqPanel(AlignViewport viewport, AlignmentPanel alignPanel)
163 linkImageURL = getClass().getResource("/images/link.gif");
164 seqARep = new SequenceAnnotationReport(linkImageURL.toString());
165 ToolTipManager.sharedInstance().registerComponent(this);
166 ToolTipManager.sharedInstance().setInitialDelay(0);
167 ToolTipManager.sharedInstance().setDismissDelay(10000);
171 setBackground(Color.white);
173 seqCanvas = new SeqCanvas(alignPanel);
174 setLayout(new BorderLayout());
175 add(seqCanvas, BorderLayout.CENTER);
177 this.ap = alignPanel;
179 if (!viewport.isDataset())
181 addMouseMotionListener(this);
182 addMouseListener(this);
183 addMouseWheelListener(this);
184 ssm = viewport.getStructureSelectionManager();
185 ssm.addStructureViewerListener(this);
186 ssm.addSelectionListener(this);
189 lastMouseColumn = -1;
193 int startWrapBlock = -1;
195 int wrappedBlock = -1;
198 * Returns the aligned sequence position (base 0) at the mouse position, or
199 * the closest visible one
204 int findColumn(MouseEvent evt)
209 int startRes = av.getRanges().getStartRes();
210 if (av.getWrapAlignment())
213 int hgap = av.getCharHeight();
214 if (av.getScaleAboveWrapped())
216 hgap += av.getCharHeight();
219 int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
220 + hgap + seqCanvas.getAnnotationHeight();
223 y = Math.max(0, y - hgap);
224 x = Math.max(0, x - seqCanvas.getLabelWidthWest());
226 int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());
232 wrappedBlock = y / cHeight;
233 wrappedBlock += startRes / cwidth;
234 // allow for wrapped view scrolled right (possible from Overview)
235 int startOffset = startRes % cwidth;
236 res = wrappedBlock * cwidth + startOffset
237 + +Math.min(cwidth - 1, x / av.getCharWidth());
241 if (x > seqCanvas.getX() + seqCanvas.getWidth())
243 // make sure we calculate relative to visible alignment, rather than
245 x = seqCanvas.getX() + seqCanvas.getWidth();
247 res = (x / av.getCharWidth()) + startRes;
248 if (res > av.getRanges().getEndRes())
251 res = av.getRanges().getEndRes();
255 if (av.hasHiddenColumns())
257 res = av.getAlignment().getHiddenColumns()
258 .visibleToAbsoluteColumn(res);
265 int findSeq(MouseEvent evt)
270 if (av.getWrapAlignment())
272 int hgap = av.getCharHeight();
273 if (av.getScaleAboveWrapped())
275 hgap += av.getCharHeight();
278 int cHeight = av.getAlignment().getHeight() * av.getCharHeight()
279 + hgap + seqCanvas.getAnnotationHeight();
283 seq = Math.min((y % cHeight) / av.getCharHeight(),
284 av.getAlignment().getHeight() - 1);
289 (y / av.getCharHeight()) + av.getRanges().getStartSeq(),
290 av.getAlignment().getHeight() - 1);
297 * When all of a sequence of edits are complete, put the resulting edit list
298 * on the history stack (undo list), and reset flags for editing in progress.
304 if (editCommand != null && editCommand.getSize() > 0)
306 ap.alignFrame.addHistoryItem(editCommand);
307 av.firePropertyChange("alignment", null,
308 av.getAlignment().getSequences());
313 * Tidy up come what may...
318 groupEditing = false;
327 seqCanvas.cursorY = getKeyboardNo1() - 1;
328 scrollToVisible(true);
331 void setCursorColumn()
333 seqCanvas.cursorX = getKeyboardNo1() - 1;
334 scrollToVisible(true);
337 void setCursorRowAndColumn()
339 if (keyboardNo2 == null)
341 keyboardNo2 = new StringBuffer();
345 seqCanvas.cursorX = getKeyboardNo1() - 1;
346 seqCanvas.cursorY = getKeyboardNo2() - 1;
347 scrollToVisible(true);
351 void setCursorPosition()
353 SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
355 seqCanvas.cursorX = sequence.findIndex(getKeyboardNo1()) - 1;
356 scrollToVisible(true);
359 void moveCursor(int dx, int dy)
361 seqCanvas.cursorX += dx;
362 seqCanvas.cursorY += dy;
364 HiddenColumns hidden = av.getAlignment().getHiddenColumns();
366 if (av.hasHiddenColumns() && !hidden.isVisible(seqCanvas.cursorX))
368 int original = seqCanvas.cursorX - dx;
369 int maxWidth = av.getAlignment().getWidth();
371 if (!hidden.isVisible(seqCanvas.cursorX))
373 int visx = hidden.absoluteToVisibleColumn(seqCanvas.cursorX - dx);
374 int[] region = hidden.getRegionWithEdgeAtRes(visx);
376 if (region != null) // just in case
381 seqCanvas.cursorX = region[1] + 1;
386 seqCanvas.cursorX = region[0] - 1;
389 seqCanvas.cursorX = (seqCanvas.cursorX < 0) ? 0 : seqCanvas.cursorX;
392 if (seqCanvas.cursorX >= maxWidth
393 || !hidden.isVisible(seqCanvas.cursorX))
395 seqCanvas.cursorX = original;
399 scrollToVisible(false);
403 * Scroll to make the cursor visible in the viewport.
406 * just jump to the location rather than scrolling
408 void scrollToVisible(boolean jump)
410 if (seqCanvas.cursorX < 0)
412 seqCanvas.cursorX = 0;
414 else if (seqCanvas.cursorX > av.getAlignment().getWidth() - 1)
416 seqCanvas.cursorX = av.getAlignment().getWidth() - 1;
419 if (seqCanvas.cursorY < 0)
421 seqCanvas.cursorY = 0;
423 else if (seqCanvas.cursorY > av.getAlignment().getHeight() - 1)
425 seqCanvas.cursorY = av.getAlignment().getHeight() - 1;
430 boolean repaintNeeded = true;
433 // only need to repaint if the viewport did not move, as otherwise it will
435 repaintNeeded = !av.getRanges().setViewportLocation(seqCanvas.cursorX,
440 if (av.getWrapAlignment())
442 // scrollToWrappedVisible expects x-value to have hidden cols subtracted
443 int x = av.getAlignment().getHiddenColumns()
444 .absoluteToVisibleColumn(seqCanvas.cursorX);
445 av.getRanges().scrollToWrappedVisible(x);
449 av.getRanges().scrollToVisible(seqCanvas.cursorX,
454 if (av.getAlignment().getHiddenColumns().isVisible(seqCanvas.cursorX))
456 setStatusMessage(av.getAlignment().getSequenceAt(seqCanvas.cursorY),
457 seqCanvas.cursorX, seqCanvas.cursorY);
467 void setSelectionAreaAtCursor(boolean topLeft)
469 SequenceI sequence = av.getAlignment().getSequenceAt(seqCanvas.cursorY);
471 if (av.getSelectionGroup() != null)
473 SequenceGroup sg = av.getSelectionGroup();
474 // Find the top and bottom of this group
475 int min = av.getAlignment().getHeight(), max = 0;
476 for (int i = 0; i < sg.getSize(); i++)
478 int index = av.getAlignment().findIndex(sg.getSequenceAt(i));
493 sg.setStartRes(seqCanvas.cursorX);
494 if (sg.getEndRes() < seqCanvas.cursorX)
496 sg.setEndRes(seqCanvas.cursorX);
499 min = seqCanvas.cursorY;
503 sg.setEndRes(seqCanvas.cursorX);
504 if (sg.getStartRes() > seqCanvas.cursorX)
506 sg.setStartRes(seqCanvas.cursorX);
509 max = seqCanvas.cursorY + 1;
514 // Only the user can do this
515 av.setSelectionGroup(null);
519 // Now add any sequences between min and max
520 sg.getSequences(null).clear();
521 for (int i = min; i < max; i++)
523 sg.addSequence(av.getAlignment().getSequenceAt(i), false);
528 if (av.getSelectionGroup() == null)
530 SequenceGroup sg = new SequenceGroup();
531 sg.setStartRes(seqCanvas.cursorX);
532 sg.setEndRes(seqCanvas.cursorX);
533 sg.addSequence(sequence, false);
534 av.setSelectionGroup(sg);
537 ap.paintAlignment(false, false);
541 void insertGapAtCursor(boolean group)
543 groupEditing = group;
544 startseq = seqCanvas.cursorY;
545 lastres = seqCanvas.cursorX;
546 editSequence(true, false, seqCanvas.cursorX + getKeyboardNo1());
550 void deleteGapAtCursor(boolean group)
552 groupEditing = group;
553 startseq = seqCanvas.cursorY;
554 lastres = seqCanvas.cursorX + getKeyboardNo1();
555 editSequence(false, false, seqCanvas.cursorX);
559 void insertNucAtCursor(boolean group, String nuc)
561 // TODO not called - delete?
562 groupEditing = group;
563 startseq = seqCanvas.cursorY;
564 lastres = seqCanvas.cursorX;
565 editSequence(false, true, seqCanvas.cursorX + getKeyboardNo1());
569 void numberPressed(char value)
571 if (keyboardNo1 == null)
573 keyboardNo1 = new StringBuffer();
576 if (keyboardNo2 != null)
578 keyboardNo2.append(value);
582 keyboardNo1.append(value);
590 if (keyboardNo1 != null)
592 int value = Integer.parseInt(keyboardNo1.toString());
596 } catch (Exception x)
607 if (keyboardNo2 != null)
609 int value = Integer.parseInt(keyboardNo2.toString());
613 } catch (Exception x)
627 public void mouseReleased(MouseEvent evt)
629 boolean didDrag = mouseDragging; // did we come here after a drag
630 mouseDragging = false;
631 mouseWheelPressed = false;
633 if (evt.isPopupTrigger()) // Windows: mouseReleased
646 doMouseReleasedDefineMode(evt, didDrag);
657 public void mousePressed(MouseEvent evt)
659 lastMousePress = evt.getPoint();
661 if (SwingUtilities.isMiddleMouseButton(evt))
663 mouseWheelPressed = true;
667 boolean isControlDown = Platform.isControlDown(evt);
668 if (evt.isShiftDown() || isControlDown)
678 doMousePressedDefineMode(evt);
682 int seq = findSeq(evt);
683 int res = findColumn(evt);
685 if (seq < 0 || res < 0)
690 if ((seq < av.getAlignment().getHeight())
691 && (res < av.getAlignment().getSequenceAt(seq).getLength()))
707 private String formattedTooltipText;
710 public void mouseOverSequence(SequenceI sequence, int index, int pos)
712 String tmp = sequence.hashCode() + " " + index + " " + pos;
714 if (lastMessage == null || !lastMessage.equals(tmp))
716 // System.err.println("mouseOver Sequence: "+tmp);
717 ssm.mouseOverSequence(sequence, index, pos, av);
723 * Highlight the mapped region described by the search results object (unless
724 * unchanged). This supports highlight of protein while mousing over linked
725 * cDNA and vice versa. The status bar is also updated to show the location of
726 * the start of the highlighted region.
729 public void highlightSequence(SearchResultsI results)
731 if (results == null || results.equals(lastSearchResults))
735 lastSearchResults = results;
737 boolean wasScrolled = false;
739 if (av.isFollowHighlight())
741 // don't allow highlight of protein/cDNA to also scroll a complementary
742 // panel,as this sets up a feedback loop (scrolling panel 1 causes moused
743 // over residue to change abruptly, causing highlighted residue in panel 2
744 // to change, causing a scroll in panel 1 etc)
745 ap.setToScrollComplementPanel(false);
746 wasScrolled = ap.scrollToPosition(results, false);
749 seqCanvas.revalidate();
751 ap.setToScrollComplementPanel(true);
754 boolean fastPaint = !(wasScrolled && av.getWrapAlignment());
755 if (seqCanvas.highlightSearchResults(results, fastPaint))
757 setStatusMessage(results);
762 public VamsasSource getVamsasSource()
764 return this.ap == null ? null : this.ap.av;
768 public void updateColours(SequenceI seq, int index)
770 System.out.println("update the seqPanel colours");
775 * Action on mouse movement is to update the status bar to show the current
776 * sequence position, and (if features are shown) to show any features at the
777 * position in a tooltip. Does nothing if the mouse move does not change
783 public void mouseMoved(MouseEvent evt)
787 // This is because MacOSX creates a mouseMoved
788 // If control is down, other platforms will not.
792 final int column = findColumn(evt);
793 final int seq = findSeq(evt);
795 if (column < 0 || seq < 0 || seq >= av.getAlignment().getHeight())
800 if (column == lastMouseColumn && seq == lastMouseSeq)
803 * just a pixel move without change of residue
807 lastMouseColumn = column;
810 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
812 if (column >= sequence.getLength())
818 * set status bar message, returning residue position in sequence
820 boolean isGapped = Comparison.isGap(sequence.getCharAt(column));
821 final int pos = setStatusMessage(sequence, column, seq);
822 if (ssm != null && !isGapped)
824 mouseOverSequence(sequence, column, pos);
827 tooltipText.setLength(0);
829 SequenceGroup[] groups = av.getAlignment().findAllGroups(sequence);
832 for (int g = 0; g < groups.length; g++)
834 if (groups[g].getStartRes() <= column
835 && groups[g].getEndRes() >= column)
837 if (!groups[g].getName().startsWith("JTreeGroup")
838 && !groups[g].getName().startsWith("JGroup"))
840 tooltipText.append(groups[g].getName());
843 if (groups[g].getDescription() != null)
845 tooltipText.append(": " + groups[g].getDescription());
852 * add any features at the position to the tooltip; if over a gap, only
853 * add features that straddle the gap (pos may be the residue before or
856 if (av.isShowSequenceFeatures())
858 List<SequenceFeature> features = ap.getFeatureRenderer()
859 .findFeaturesAtColumn(sequence, column + 1);
860 seqARep.appendFeatures(tooltipText, pos, features,
861 this.ap.getSeqPanel().seqCanvas.fr);
863 if (tooltipText.length() == 0) // <html>
865 setToolTipText(null);
870 if (tooltipText.length() > MAX_TOOLTIP_LENGTH) // constant
872 tooltipText.setLength(MAX_TOOLTIP_LENGTH);
873 tooltipText.append("...");
875 String textString = tooltipText.toString();
876 if (lastTooltip == null || !lastTooltip.equals(textString))
878 formattedTooltipText = JvSwingUtils.wrapTooltip(true,
880 setToolTipText(formattedTooltipText);
881 lastTooltip = textString;
887 private Point lastp = null;
889 private JToolTip tempTip = new JLabel().createToolTip();
894 * @see javax.swing.JComponent#getToolTipLocation(java.awt.event.MouseEvent)
897 public Point getToolTipLocation(MouseEvent event)
901 if (tooltipText == null || tooltipText.length() == 6)
904 if (lastp != null && event.isShiftDown())
908 int x = event.getX();
909 int y = event.getY();
912 tempTip.setTipText(formattedTooltipText);
913 int tipWidth = (int) tempTip.getPreferredSize().getWidth();
915 // was x += (w - x < 200) ? -(w / 2) : 5;
916 x = (x + tipWidth < w ? x + 10 : w - tipWidth);
917 p = new Point(x, y + 20); // BH 2018 was - 20?
919 * TODO: try to modify position region is not obcured by tooltip
930 * set when the current UI interaction has resulted in a change that requires
931 * shading in overviews and structures to be recalculated. this could be
932 * changed to a something more expressive that indicates what actually has
933 * changed, so selective redraws can be applied (ie. only structures, only
936 private boolean updateOverviewAndStructs = false; // TODO: refactor to avcontroller
939 * set if av.getSelectionGroup() refers to a group that is defined on the
940 * alignment view, rather than a transient selection
942 // private boolean editingDefinedGroup = false; // TODO: refactor to
943 // avcontroller or viewModel
946 * Sets the status message in alignment panel, showing the sequence number
947 * (index) and id, and residue and residue position if not at a gap, for the
948 * given sequence and column position. Returns the residue position returned
949 * by Sequence.findPosition. Note this may be for the nearest adjacent residue
950 * if at a gapped position.
953 * aligned sequence object
957 * index of sequence in alignment
958 * @return sequence position of residue at column, or adjacent residue if at a
961 int setStatusMessage(SequenceI sequence, final int column, int seqIndex)
963 char sequenceChar = sequence.getCharAt(column);
964 int pos = sequence.findPosition(column);
965 setStatusMessage(sequence, seqIndex, sequenceChar, pos);
971 * Builds the status message for the current cursor location and writes it to
972 * the status bar, for example
975 * Sequence 3 ID: FER1_SOLLC
976 * Sequence 5 ID: FER1_PEA Residue: THR (4)
977 * Sequence 5 ID: FER1_PEA Residue: B (3)
978 * Sequence 6 ID: O.niloticus.3 Nucleotide: Uracil (2)
983 * sequence position in the alignment (1..)
984 * @param sequenceChar
985 * the character under the cursor
987 * the sequence residue position (if not over a gap)
989 protected void setStatusMessage(SequenceI sequence, int seqIndex,
990 char sequenceChar, int residuePos)
992 StringBuilder text = new StringBuilder(32);
995 * Sequence number (if known), and sequence name.
997 String seqno = seqIndex == -1 ? "" : " " + (seqIndex + 1);
998 text.append("Sequence").append(seqno).append(" ID: ")
999 .append(sequence.getName());
1001 String residue = null;
1004 * Try to translate the display character to residue name (null for gap).
1006 boolean isGapped = Comparison.isGap(sequenceChar);
1010 boolean nucleotide = av.getAlignment().isNucleotide();
1011 String displayChar = String.valueOf(sequenceChar);
1014 residue = ResidueProperties.nucleotideName.get(displayChar);
1018 residue = "X".equalsIgnoreCase(displayChar) ? "X"
1019 : ("*".equals(displayChar) ? "STOP"
1020 : ResidueProperties.aa2Triplet.get(displayChar));
1022 text.append(" ").append(nucleotide ? "Nucleotide" : "Residue")
1023 .append(": ").append(residue == null ? displayChar : residue);
1025 text.append(" (").append(Integer.toString(residuePos)).append(")");
1027 ap.alignFrame.setStatus(text.toString());
1031 * Set the status bar message to highlight the first matched position in
1036 private void setStatusMessage(SearchResultsI results)
1038 AlignmentI al = this.av.getAlignment();
1039 int sequenceIndex = al.findIndex(results);
1040 if (sequenceIndex == -1)
1044 SequenceI ds = al.getSequenceAt(sequenceIndex).getDatasetSequence();
1045 for (SearchResultMatchI m : results.getResults())
1047 SequenceI seq = m.getSequence();
1048 if (seq.getDatasetSequence() != null)
1050 seq = seq.getDatasetSequence();
1055 int start = m.getStart();
1056 setStatusMessage(seq, sequenceIndex, seq.getCharAt(start - 1),
1067 public void mouseDragged(MouseEvent evt)
1069 if (mouseWheelPressed)
1071 boolean inSplitFrame = ap.av.getCodingComplement() != null;
1072 boolean copyChanges = inSplitFrame && av.isProteinFontAsCdna();
1074 int oldWidth = av.getCharWidth();
1076 // Which is bigger, left-right or up-down?
1077 if (Math.abs(evt.getY() - lastMousePress.getY()) > Math
1078 .abs(evt.getX() - lastMousePress.getX()))
1081 * on drag up or down, decrement or increment font size
1083 int fontSize = av.font.getSize();
1084 boolean fontChanged = false;
1086 if (evt.getY() < lastMousePress.getY())
1091 else if (evt.getY() > lastMousePress.getY())
1104 Font newFont = new Font(av.font.getName(), av.font.getStyle(),
1106 av.setFont(newFont, true);
1107 av.setCharWidth(oldWidth);
1111 ap.av.getCodingComplement().setFont(newFont, true);
1112 SplitFrame splitFrame = (SplitFrame) ap.alignFrame
1113 .getSplitViewContainer();
1114 splitFrame.adjustLayout();
1115 splitFrame.repaint();
1122 * on drag left or right, decrement or increment character width
1125 if (evt.getX() < lastMousePress.getX() && av.getCharWidth() > 1)
1127 newWidth = av.getCharWidth() - 1;
1128 av.setCharWidth(newWidth);
1130 else if (evt.getX() > lastMousePress.getX())
1132 newWidth = av.getCharWidth() + 1;
1133 av.setCharWidth(newWidth);
1137 ap.paintAlignment(false, false);
1141 * need to ensure newWidth is set on cdna, regardless of which
1142 * panel the mouse drag happened in; protein will compute its
1143 * character width as 1:1 or 3:1
1145 av.getCodingComplement().setCharWidth(newWidth);
1146 SplitFrame splitFrame = (SplitFrame) ap.alignFrame
1147 .getSplitViewContainer();
1148 splitFrame.adjustLayout();
1149 splitFrame.repaint();
1154 FontMetrics fm = getFontMetrics(av.getFont());
1155 av.validCharWidth = fm.charWidth('M') <= av.getCharWidth();
1157 lastMousePress = evt.getPoint();
1164 doMouseDraggedDefineMode(evt);
1168 int res = findColumn(evt);
1175 if ((lastres == -1) || (lastres == res))
1180 if ((res < av.getAlignment().getWidth()) && (res < lastres))
1182 // dragLeft, delete gap
1183 editSequence(false, false, res);
1187 editSequence(true, false, res);
1190 mouseDragging = true;
1191 if (scrollThread != null)
1193 scrollThread.setMousePosition(evt.getPoint());
1197 // TODO: Make it more clever than many booleans
1198 synchronized void editSequence(boolean insertGap, boolean editSeq,
1202 int fixedRight = -1;
1203 boolean fixedColumns = false;
1204 SequenceGroup sg = av.getSelectionGroup();
1206 SequenceI seq = av.getAlignment().getSequenceAt(startseq);
1208 // No group, but the sequence may represent a group
1209 if (!groupEditing && av.hasHiddenRows())
1211 if (av.isHiddenRepSequence(seq))
1213 sg = av.getRepresentedSequences(seq);
1214 groupEditing = true;
1218 StringBuilder message = new StringBuilder(64);
1221 message.append("Edit group:");
1222 if (editCommand == null)
1224 editCommand = new EditCommand(
1225 MessageManager.getString("action.edit_group"));
1230 message.append("Edit sequence: " + seq.getName());
1231 String label = seq.getName();
1232 if (label.length() > 10)
1234 label = label.substring(0, 10);
1236 if (editCommand == null)
1238 editCommand = new EditCommand(MessageManager
1239 .formatMessage("label.edit_params", new String[]
1246 message.append(" insert ");
1250 message.append(" delete ");
1253 message.append(Math.abs(startres - lastres) + " gaps.");
1254 ap.alignFrame.setStatus(message.toString());
1256 // Are we editing within a selection group?
1257 if (groupEditing || (sg != null
1258 && sg.getSequences(av.getHiddenRepSequences()).contains(seq)))
1260 fixedColumns = true;
1262 // sg might be null as the user may only see 1 sequence,
1263 // but the sequence represents a group
1266 if (!av.isHiddenRepSequence(seq))
1271 sg = av.getRepresentedSequences(seq);
1274 fixedLeft = sg.getStartRes();
1275 fixedRight = sg.getEndRes();
1277 if ((startres < fixedLeft && lastres >= fixedLeft)
1278 || (startres >= fixedLeft && lastres < fixedLeft)
1279 || (startres > fixedRight && lastres <= fixedRight)
1280 || (startres <= fixedRight && lastres > fixedRight))
1286 if (fixedLeft > startres)
1288 fixedRight = fixedLeft - 1;
1291 else if (fixedRight < startres)
1293 fixedLeft = fixedRight;
1298 if (av.hasHiddenColumns())
1300 fixedColumns = true;
1301 int y1 = av.getAlignment().getHiddenColumns()
1302 .getNextHiddenBoundary(true, startres);
1303 int y2 = av.getAlignment().getHiddenColumns()
1304 .getNextHiddenBoundary(false, startres);
1306 if ((insertGap && startres > y1 && lastres < y1)
1307 || (!insertGap && startres < y2 && lastres > y2))
1313 // System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");
1314 // Selection spans a hidden region
1315 if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))
1323 fixedRight = y2 - 1;
1330 List<SequenceI> vseqs = sg.getSequences(av.getHiddenRepSequences());
1331 int g, groupSize = vseqs.size();
1332 SequenceI[] groupSeqs = new SequenceI[groupSize];
1333 for (g = 0; g < groupSeqs.length; g++)
1335 groupSeqs[g] = vseqs.get(g);
1341 // If the user has selected the whole sequence, and is dragging to
1342 // the right, we can still extend the alignment and selectionGroup
1343 if (sg.getStartRes() == 0 && sg.getEndRes() == fixedRight
1344 && sg.getEndRes() == av.getAlignment().getWidth() - 1)
1346 sg.setEndRes(av.getAlignment().getWidth() + startres - lastres);
1347 fixedRight = sg.getEndRes();
1350 // Is it valid with fixed columns??
1351 // Find the next gap before the end
1352 // of the visible region boundary
1353 boolean blank = false;
1354 for (; fixedRight > lastres; fixedRight--)
1358 for (g = 0; g < groupSize; g++)
1360 for (int j = 0; j < startres - lastres; j++)
1362 if (!Comparison.isGap(groupSeqs[g].getCharAt(fixedRight - j)))
1377 if (sg.getSize() == av.getAlignment().getHeight())
1379 if ((av.hasHiddenColumns() && startres < av.getAlignment()
1381 .getNextHiddenBoundary(false, startres)))
1387 int alWidth = av.getAlignment().getWidth();
1388 if (av.hasHiddenRows())
1390 int hwidth = av.getAlignment().getHiddenSequences()
1392 if (hwidth > alWidth)
1397 // We can still insert gaps if the selectionGroup
1398 // contains all the sequences
1399 sg.setEndRes(sg.getEndRes() + startres - lastres);
1400 fixedRight = alWidth + startres - lastres;
1411 else if (!insertGap)
1413 // / Are we able to delete?
1414 // ie are all columns blank?
1416 for (g = 0; g < groupSize; g++)
1418 for (int j = startres; j < lastres; j++)
1420 if (groupSeqs[g].getLength() <= j)
1425 if (!Comparison.isGap(groupSeqs[g].getCharAt(j)))
1427 // Not a gap, block edit not valid
1437 // dragging to the right
1438 if (fixedColumns && fixedRight != -1)
1440 for (int j = lastres; j < startres; j++)
1442 insertChar(j, groupSeqs, fixedRight);
1447 appendEdit(Action.INSERT_GAP, groupSeqs, startres,
1448 startres - lastres);
1453 // dragging to the left
1454 if (fixedColumns && fixedRight != -1)
1456 for (int j = lastres; j > startres; j--)
1458 deleteChar(startres, groupSeqs, fixedRight);
1463 appendEdit(Action.DELETE_GAP, groupSeqs, startres,
1464 lastres - startres);
1470 // ///Editing a single sequence///////////
1474 // dragging to the right
1475 if (fixedColumns && fixedRight != -1)
1477 for (int j = lastres; j < startres; j++)
1479 insertChar(j, new SequenceI[] { seq }, fixedRight);
1484 appendEdit(Action.INSERT_GAP, new SequenceI[] { seq }, lastres,
1485 startres - lastres);
1492 // dragging to the left
1493 if (fixedColumns && fixedRight != -1)
1495 for (int j = lastres; j > startres; j--)
1497 if (!Comparison.isGap(seq.getCharAt(startres)))
1502 deleteChar(startres, new SequenceI[] { seq }, fixedRight);
1507 // could be a keyboard edit trying to delete none gaps
1509 for (int m = startres; m < lastres; m++)
1511 if (!Comparison.isGap(seq.getCharAt(m)))
1520 appendEdit(Action.DELETE_GAP, new SequenceI[] { seq },
1526 {// insertGap==false AND editSeq==TRUE;
1527 if (fixedColumns && fixedRight != -1)
1529 for (int j = lastres; j < startres; j++)
1531 insertChar(j, new SequenceI[] { seq }, fixedRight);
1536 appendEdit(Action.INSERT_NUC, new SequenceI[] { seq }, lastres,
1537 startres - lastres);
1544 seqCanvas.repaint();
1547 void insertChar(int j, SequenceI[] seq, int fixedColumn)
1549 int blankColumn = fixedColumn;
1550 for (int s = 0; s < seq.length; s++)
1552 // Find the next gap before the end of the visible region boundary
1553 // If lastCol > j, theres a boundary after the gap insertion
1555 for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)
1557 if (Comparison.isGap(seq[s].getCharAt(blankColumn)))
1559 // Theres a space, so break and insert the gap
1564 if (blankColumn <= j)
1566 blankColumn = fixedColumn;
1572 appendEdit(Action.DELETE_GAP, seq, blankColumn, 1);
1574 appendEdit(Action.INSERT_GAP, seq, j, 1);
1579 * Helper method to add and perform one edit action.
1586 protected void appendEdit(Action action, SequenceI[] seq, int pos,
1590 final Edit edit = new EditCommand().new Edit(action, seq, pos, count,
1591 av.getAlignment().getGapCharacter());
1593 editCommand.appendEdit(edit, av.getAlignment(), true, null);
1596 void deleteChar(int j, SequenceI[] seq, int fixedColumn)
1599 appendEdit(Action.DELETE_GAP, seq, j, 1);
1601 appendEdit(Action.INSERT_GAP, seq, fixedColumn, 1);
1605 * On reentering the panel, stops any scrolling that was started on dragging
1611 public void mouseEntered(MouseEvent e)
1621 * On leaving the panel, if the mouse is being dragged, starts a thread to
1622 * scroll it until the mouse is released (in unwrapped mode only)
1627 public void mouseExited(MouseEvent e)
1631 startScrolling(e.getPoint());
1636 * Handler for double-click on a position with one or more sequence features.
1637 * Opens the Amend Features dialog to allow feature details to be amended, or
1638 * the feature deleted.
1641 public void mouseClicked(MouseEvent evt)
1643 SequenceGroup sg = null;
1644 SequenceI sequence = av.getAlignment().getSequenceAt(findSeq(evt));
1645 if (evt.getClickCount() > 1)
1647 sg = av.getSelectionGroup();
1648 if (sg != null && sg.getSize() == 1
1649 && sg.getEndRes() - sg.getStartRes() < 2)
1651 av.setSelectionGroup(null);
1654 int column = findColumn(evt);
1657 * find features at the position (if not gapped), or straddling
1658 * the position (if at a gap)
1660 List<SequenceFeature> features = seqCanvas.getFeatureRenderer()
1661 .findFeaturesAtColumn(sequence, column + 1);
1663 if (!features.isEmpty())
1666 * highlight the first feature at the position on the alignment
1668 SearchResultsI highlight = new SearchResults();
1669 highlight.addResult(sequence, features.get(0).getBegin(), features
1671 seqCanvas.highlightSearchResults(highlight, true);
1674 * open the Amend Features dialog
1676 new FeatureEditor(ap, Collections.singletonList(sequence), features,
1677 false).showDialog();
1683 public void mouseWheelMoved(MouseWheelEvent e)
1686 double wheelRotation = e.getPreciseWheelRotation();
1687 if (wheelRotation > 0)
1689 if (e.isShiftDown())
1691 av.getRanges().scrollRight(true);
1696 av.getRanges().scrollUp(false);
1699 else if (wheelRotation < 0)
1701 if (e.isShiftDown())
1703 av.getRanges().scrollRight(false);
1707 av.getRanges().scrollUp(true);
1712 * update status bar and tooltip for new position
1713 * (need to synthesize a mouse movement to refresh tooltip)
1716 ToolTipManager.sharedInstance().mouseMoved(e);
1725 public void doMousePressedDefineMode(MouseEvent evt)
1727 final int res = findColumn(evt);
1728 final int seq = findSeq(evt);
1730 updateOverviewAndStructs = false;
1732 startWrapBlock = wrappedBlock;
1734 if (av.getWrapAlignment() && seq > av.getAlignment().getHeight())
1736 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1737 MessageManager.getString(
1738 "label.cannot_edit_annotations_in_wrapped_view"),
1739 MessageManager.getString("label.wrapped_view_no_edit"),
1740 JvOptionPane.WARNING_MESSAGE);
1744 if (seq < 0 || res < 0)
1749 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
1751 if ((sequence == null) || (res > sequence.getLength()))
1756 stretchGroup = av.getSelectionGroup();
1758 if (stretchGroup == null || !stretchGroup.contains(sequence, res))
1760 stretchGroup = av.getAlignment().findGroup(sequence, res);
1761 if (stretchGroup != null)
1763 // only update the current selection if the popup menu has a group to
1765 av.setSelectionGroup(stretchGroup);
1770 * defer right-mouse click handling to mouseReleased on Windows
1771 * (where isPopupTrigger() will answer true)
1772 * NB isRightMouseButton is also true for Cmd-click on Mac
1774 if (Platform.isWinRightButton(evt))
1779 if (evt.isPopupTrigger()) // Mac: mousePressed
1787 seqCanvas.cursorX = findColumn(evt);
1788 seqCanvas.cursorY = findSeq(evt);
1789 seqCanvas.repaint();
1793 if (stretchGroup == null)
1795 createStretchGroup(res, sequence);
1798 if (stretchGroup != null)
1800 stretchGroup.addPropertyChangeListener(seqCanvas);
1803 seqCanvas.repaint();
1806 private void createStretchGroup(int res, SequenceI sequence)
1808 // Only if left mouse button do we want to change group sizes
1809 // define a new group here
1810 SequenceGroup sg = new SequenceGroup();
1811 sg.setStartRes(res);
1813 sg.addSequence(sequence, false);
1814 av.setSelectionGroup(sg);
1817 if (av.getConservationSelected())
1819 SliderPanel.setConservationSlider(ap, av.getResidueShading(),
1823 if (av.getAbovePIDThreshold())
1825 SliderPanel.setPIDSliderSource(ap, av.getResidueShading(),
1828 // TODO: stretchGroup will always be not null. Is this a merge error ?
1829 // or is there a threading issue here?
1830 if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))
1832 // Edit end res position of selected group
1833 changeEndRes = true;
1835 else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))
1837 // Edit end res position of selected group
1838 changeStartRes = true;
1840 stretchGroup.getWidth();
1845 * Build and show a pop-up menu at the right-click mouse position
1851 void showPopupMenu(MouseEvent evt)
1853 final int column = findColumn(evt);
1854 final int seq = findSeq(evt);
1855 SequenceI sequence = av.getAlignment().getSequenceAt(seq);
1856 List<SequenceFeature> features = ap.getFeatureRenderer()
1857 .findFeaturesAtColumn(sequence, column + 1);
1859 PopupMenu pop = new PopupMenu(ap, null, features);
1860 pop.show(this, evt.getX(), evt.getY());
1864 * Update the display after mouse up on a selection or group
1867 * mouse released event details
1869 * true if this event is happening after a mouse drag (rather than a
1872 public void doMouseReleasedDefineMode(MouseEvent evt, boolean afterDrag)
1874 if (stretchGroup == null)
1879 stretchGroup.removePropertyChangeListener(seqCanvas);
1881 // always do this - annotation has own state
1882 // but defer colourscheme update until hidden sequences are passed in
1883 boolean vischange = stretchGroup.recalcConservation(true);
1884 updateOverviewAndStructs |= vischange && av.isSelectionDefinedGroup()
1886 if (stretchGroup.cs != null)
1888 stretchGroup.cs.alignmentChanged(stretchGroup,
1889 av.getHiddenRepSequences());
1891 ResidueShaderI groupColourScheme = stretchGroup
1892 .getGroupColourScheme();
1893 String name = stretchGroup.getName();
1894 if (stretchGroup.cs.conservationApplied())
1896 SliderPanel.setConservationSlider(ap, groupColourScheme, name);
1898 if (stretchGroup.cs.getThreshold() > 0)
1900 SliderPanel.setPIDSliderSource(ap, groupColourScheme, name);
1903 PaintRefresher.Refresh(this, av.getSequenceSetId());
1904 // TODO: structure colours only need updating if stretchGroup used to or now
1905 // does contain sequences with structure views
1906 ap.paintAlignment(updateOverviewAndStructs, updateOverviewAndStructs);
1907 updateOverviewAndStructs = false;
1908 changeEndRes = false;
1909 changeStartRes = false;
1910 stretchGroup = null;
1920 public void doMouseDraggedDefineMode(MouseEvent evt)
1922 int res = findColumn(evt);
1923 int y = findSeq(evt);
1925 if (wrappedBlock != startWrapBlock)
1930 if (stretchGroup == null)
1935 res = Math.min(res, av.getAlignment().getWidth()-1);
1937 if (stretchGroup.getEndRes() == res)
1939 // Edit end res position of selected group
1940 changeEndRes = true;
1942 else if (stretchGroup.getStartRes() == res)
1944 // Edit start res position of selected group
1945 changeStartRes = true;
1948 if (res < av.getRanges().getStartRes())
1950 res = av.getRanges().getStartRes();
1955 if (res > (stretchGroup.getStartRes() - 1))
1957 stretchGroup.setEndRes(res);
1958 updateOverviewAndStructs |= av.isSelectionDefinedGroup();
1961 else if (changeStartRes)
1963 if (res < (stretchGroup.getEndRes() + 1))
1965 stretchGroup.setStartRes(res);
1966 updateOverviewAndStructs |= av.isSelectionDefinedGroup();
1970 int dragDirection = 0;
1976 else if (y < oldSeq)
1981 while ((y != oldSeq) && (oldSeq > -1)
1982 && (y < av.getAlignment().getHeight()))
1984 // This routine ensures we don't skip any sequences, as the
1985 // selection is quite slow.
1986 Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1988 oldSeq += dragDirection;
1995 Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);
1997 if (stretchGroup.getSequences(null).contains(nextSeq))
1999 stretchGroup.deleteSequence(seq, false);
2000 updateOverviewAndStructs |= av.isSelectionDefinedGroup();
2006 stretchGroup.addSequence(seq, false);
2009 stretchGroup.addSequence(nextSeq, false);
2010 updateOverviewAndStructs |= av.isSelectionDefinedGroup();
2019 mouseDragging = true;
2021 if (scrollThread != null)
2023 scrollThread.setMousePosition(evt.getPoint());
2028 * Stops the scroll thread if it is running
2030 void stopScrolling()
2032 if (scrollThread != null)
2034 scrollThread.stopScrolling();
2035 scrollThread = null;
2037 mouseDragging = false;
2041 * Starts a thread to scroll the alignment, towards a given mouse position
2042 * outside the panel bounds, unless the alignment is in wrapped mode
2046 void startScrolling(Point mousePos)
2049 * set this.mouseDragging in case this was called from
2050 * a drag in ScalePanel or AnnotationPanel
2052 mouseDragging = true;
2053 if (!av.getWrapAlignment() && scrollThread == null)
2055 scrollThread = new ScrollThread();
2056 scrollThread.setMousePosition(mousePos);
2057 if (!Jalview.isJS())
2060 * Java - run in a new thread
2062 scrollThread.start();
2067 * Javascript - run every 20ms until scrolling stopped
2068 * or reaches the limit of scrollable alignment
2070 // java.util.Timer version:
2071 // Timer t = new Timer("ScrollThreadTimer", true);
2072 // TimerTask task = new TimerTask()
2075 // public void run()
2077 // if (!scrollThread.scrollOnce())
2083 // t.schedule(task, 20, 20);
2084 Timer t = new Timer(20, new ActionListener()
2087 public void actionPerformed(ActionEvent e)
2089 if (scrollThread != null)
2091 // if (!scrollOnce() {t.stop();}) gives compiler error :-(
2092 scrollThread.scrollOnce();
2096 t.addActionListener(new ActionListener()
2099 public void actionPerformed(ActionEvent e)
2101 if (scrollThread == null)
2103 // finished and nulled itself
2114 * Performs scrolling of the visible alignment left, right, up or down, until
2115 * scrolling is stopped by calling stopScrolling, mouse drag is ended, or the
2116 * limit of the alignment is reached
2118 class ScrollThread extends Thread
2120 private Point mousePos;
2122 private volatile boolean keepRunning = true;
2127 public ScrollThread()
2129 setName("SeqPanel$ScrollThread");
2133 * Sets the position of the mouse that determines the direction of the
2134 * scroll to perform. If this is called as the mouse moves, scrolling should
2135 * respond accordingly. For example, if the mouse is dragged right, scroll
2136 * right should start; if the drag continues down, scroll down should also
2141 public void setMousePosition(Point p)
2147 * Sets a flag that will cause the thread to exit
2149 public void stopScrolling()
2151 keepRunning = false;
2155 * Scrolls the alignment left or right, and/or up or down, depending on the
2156 * last notified mouse position, until the limit of the alignment is
2157 * reached, or a flag is set to stop the scroll
2164 if (mousePos != null)
2166 keepRunning = scrollOnce();
2171 } catch (Exception ex)
2175 SeqPanel.this.scrollThread = null;
2181 * <li>one row up, if the mouse is above the panel</li>
2182 * <li>one row down, if the mouse is below the panel</li>
2183 * <li>one column left, if the mouse is left of the panel</li>
2184 * <li>one column right, if the mouse is right of the panel</li>
2186 * Answers true if a scroll was performed, false if not - meaning either
2187 * that the mouse position is within the panel, or the edge of the alignment
2190 boolean scrollOnce()
2193 * quit after mouseUp ensures interrupt in JalviewJS
2200 boolean scrolled = false;
2201 ViewportRanges ranges = SeqPanel.this.av.getRanges();
2208 // mouse is above this panel - try scroll up
2209 scrolled = ranges.scrollUp(true);
2211 else if (mousePos.y >= getHeight())
2213 // mouse is below this panel - try scroll down
2214 scrolled = ranges.scrollUp(false);
2218 * scroll left or right
2222 scrolled |= ranges.scrollRight(false);
2224 else if (mousePos.x >= getWidth())
2226 scrolled |= ranges.scrollRight(true);
2233 * modify current selection according to a received message.
2236 public void selection(SequenceGroup seqsel, ColumnSelection colsel,
2237 HiddenColumns hidden, SelectionSource source)
2239 // TODO: fix this hack - source of messages is align viewport, but SeqPanel
2240 // handles selection messages...
2241 // TODO: extend config options to allow user to control if selections may be
2242 // shared between viewports.
2243 boolean iSentTheSelection = (av == source
2244 || (source instanceof AlignViewport
2245 && ((AlignmentViewport) source).getSequenceSetId()
2246 .equals(av.getSequenceSetId())));
2248 if (iSentTheSelection)
2250 // respond to our own event by updating dependent dialogs
2251 if (ap.getCalculationDialog() != null)
2253 ap.getCalculationDialog().validateCalcTypes();
2259 // process further ?
2260 if (!av.followSelection)
2266 * Ignore the selection if there is one of our own pending.
2268 if (av.isSelectionGroupChanged(false) || av.isColSelChanged(false))
2274 * Check for selection in a view of which this one is a dna/protein
2277 if (selectionFromTranslation(seqsel, colsel, hidden, source))
2282 // do we want to thread this ? (contention with seqsel and colsel locks, I
2285 * only copy colsel if there is a real intersection between
2286 * sequence selection and this panel's alignment
2288 boolean repaint = false;
2289 boolean copycolsel = false;
2291 SequenceGroup sgroup = null;
2292 if (seqsel != null && seqsel.getSize() > 0)
2294 if (av.getAlignment() == null)
2296 Cache.log.warn("alignviewport av SeqSetId=" + av.getSequenceSetId()
2297 + " ViewId=" + av.getViewId()
2298 + " 's alignment is NULL! returning immediately.");
2301 sgroup = seqsel.intersect(av.getAlignment(),
2302 (av.hasHiddenRows()) ? av.getHiddenRepSequences() : null);
2303 if ((sgroup != null && sgroup.getSize() > 0))
2308 if (sgroup != null && sgroup.getSize() > 0)
2310 av.setSelectionGroup(sgroup);
2314 av.setSelectionGroup(null);
2316 av.isSelectionGroupChanged(true);
2321 // the current selection is unset or from a previous message
2322 // so import the new colsel.
2323 if (colsel == null || colsel.isEmpty())
2325 if (av.getColumnSelection() != null)
2327 av.getColumnSelection().clear();
2333 // TODO: shift colSel according to the intersecting sequences
2334 if (av.getColumnSelection() == null)
2336 av.setColumnSelection(new ColumnSelection(colsel));
2340 av.getColumnSelection().setElementsFrom(colsel,
2341 av.getAlignment().getHiddenColumns());
2344 av.isColSelChanged(true);
2348 if (copycolsel && av.hasHiddenColumns()
2349 && (av.getAlignment().getHiddenColumns() == null))
2351 System.err.println("Bad things");
2353 if (repaint) // always true!
2355 // probably finessing with multiple redraws here
2356 PaintRefresher.Refresh(this, av.getSequenceSetId());
2357 // ap.paintAlignment(false);
2360 // lastly, update dependent dialogs
2361 if (ap.getCalculationDialog() != null)
2363 ap.getCalculationDialog().validateCalcTypes();
2369 * If this panel is a cdna/protein translation view of the selection source,
2370 * tries to map the source selection to a local one, and returns true. Else
2377 protected boolean selectionFromTranslation(SequenceGroup seqsel,
2378 ColumnSelection colsel, HiddenColumns hidden,
2379 SelectionSource source)
2381 if (!(source instanceof AlignViewportI))
2385 final AlignViewportI sourceAv = (AlignViewportI) source;
2386 if (sourceAv.getCodingComplement() != av
2387 && av.getCodingComplement() != sourceAv)
2393 * Map sequence selection
2395 SequenceGroup sg = MappingUtils.mapSequenceGroup(seqsel, sourceAv, av);
2396 av.setSelectionGroup(sg);
2397 av.isSelectionGroupChanged(true);
2400 * Map column selection
2402 // ColumnSelection cs = MappingUtils.mapColumnSelection(colsel, sourceAv,
2404 ColumnSelection cs = new ColumnSelection();
2405 HiddenColumns hs = new HiddenColumns();
2406 MappingUtils.mapColumnSelection(colsel, hidden, sourceAv, av, cs, hs);
2407 av.setColumnSelection(cs);
2408 av.getAlignment().setHiddenColumns(hs);
2410 // lastly, update any dependent dialogs
2411 if (ap.getCalculationDialog() != null)
2413 ap.getCalculationDialog().validateCalcTypes();
2416 PaintRefresher.Refresh(this, av.getSequenceSetId());
2423 * @return null or last search results handled by this panel
2425 public SearchResultsI getLastSearchResults()
2427 return lastSearchResults;