ff3e329577ba03053cc77e7df33db231536aef6c
[jalview.git] / src / jalview / gui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
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.
11  *  
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.
16  * 
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.
20  */
21 /*
22  * Jalview - A Sequence Alignment Editor and Viewer
23  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
24  *
25  * This program is free software; you can redistribute it and/or
26  * modify it under the terms of the GNU General Public License
27  * as published by the Free Software Foundation; either version 2
28  * of the License, or (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  *
35  * You should have received a copy of the GNU General Public License
36  * along with this program; if not, write to the Free Software
37  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
38  */
39 package jalview.gui;
40
41 import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder;
42 import jalview.analysis.NJTree;
43 import jalview.api.AlignViewportI;
44 import jalview.bin.Cache;
45 import jalview.commands.CommandI;
46 import jalview.commands.EditCommand;
47 import jalview.datamodel.AlignmentI;
48 import jalview.datamodel.ColumnSelection;
49 import jalview.datamodel.PDBEntry;
50 import jalview.datamodel.Sequence;
51 import jalview.datamodel.SequenceGroup;
52 import jalview.datamodel.SequenceI;
53 import jalview.schemes.ColourSchemeProperty;
54 import jalview.schemes.UserColourScheme;
55 import jalview.structure.CommandListener;
56 import jalview.structure.SelectionSource;
57 import jalview.structure.StructureSelectionManager;
58 import jalview.structure.VamsasSource;
59 import jalview.viewmodel.AlignmentViewport;
60 import jalview.ws.params.AutoCalcSetting;
61
62 import java.awt.Color;
63 import java.awt.Container;
64 import java.awt.Font;
65 import java.awt.Rectangle;
66 import java.util.ArrayDeque;
67 import java.util.ArrayList;
68 import java.util.Deque;
69 import java.util.Hashtable;
70 import java.util.List;
71 import java.util.Vector;
72
73 /**
74  * DOCUMENT ME!
75  * 
76  * @author $author$
77  * @version $Revision: 1.141 $
78  */
79 public class AlignViewport extends AlignmentViewport implements
80         SelectionSource, VamsasSource, AlignViewportI, CommandListener
81 {
82   int startRes;
83
84   int endRes;
85
86   int startSeq;
87
88   int endSeq;
89
90   boolean showJVSuffix = true;
91
92   boolean showText = true;
93
94   boolean showColourText = false;
95
96   boolean showBoxes = true;
97
98   boolean wrapAlignment = false;
99
100   boolean renderGaps = true;
101
102   boolean showSequenceFeatures = false;
103
104   boolean showAnnotation = true;
105
106   SequenceAnnotationOrder sortAnnotationsBy = null;
107
108   int charHeight;
109
110   int charWidth;
111
112   boolean validCharWidth;
113
114   int wrappedWidth;
115
116   Font font;
117
118   boolean seqNameItalics;
119
120   NJTree currentTree = null;
121
122   boolean scaleAboveWrapped = false;
123
124   boolean scaleLeftWrapped = true;
125
126   boolean scaleRightWrapped = true;
127
128   boolean showHiddenMarkers = true;
129
130   boolean cursorMode = false;
131
132   /**
133    * Keys are the feature types which are currently visible. Note: Values are
134    * not used!
135    */
136   Hashtable featuresDisplayed = null;
137
138   boolean antiAlias = false;
139
140   Rectangle explodedPosition;
141
142   String viewName;
143
144   boolean gatherViewsHere = false;
145
146   private Deque<CommandI> historyList = new ArrayDeque<CommandI>();
147
148   private Deque<CommandI> redoList = new ArrayDeque<CommandI>();
149
150   int thresholdTextColour = 0;
151
152   Color textColour = Color.black;
153
154   Color textColour2 = Color.white;
155
156   boolean rightAlignIds = false;
157
158   /**
159    * Creates a new AlignViewport object.
160    * 
161    * @param al
162    *          alignment to view
163    */
164   public AlignViewport(AlignmentI al)
165   {
166     setAlignment(al);
167     init();
168   }
169
170   /**
171    * Create a new AlignViewport object with a specific sequence set ID
172    * 
173    * @param al
174    * @param seqsetid
175    *          (may be null - but potential for ambiguous constructor exception)
176    */
177   public AlignViewport(AlignmentI al, String seqsetid)
178   {
179     this(al, seqsetid, null);
180   }
181
182   public AlignViewport(AlignmentI al, String seqsetid, String viewid)
183   {
184     sequenceSetID = seqsetid;
185     viewId = viewid;
186     // TODO remove these once 2.4.VAMSAS release finished
187     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
188     {
189       Cache.log.debug("Setting viewport's sequence set id : "
190               + sequenceSetID);
191     }
192     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
193     {
194       Cache.log.debug("Setting viewport's view id : " + viewId);
195     }
196     setAlignment(al);
197     init();
198   }
199
200   /**
201    * Create a new AlignViewport with hidden regions
202    * 
203    * @param al
204    *          AlignmentI
205    * @param hiddenColumns
206    *          ColumnSelection
207    */
208   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns)
209   {
210     setAlignment(al);
211     if (hiddenColumns != null)
212     {
213       this.colSel = hiddenColumns;
214       if (hiddenColumns.getHiddenColumns() != null
215               && hiddenColumns.getHiddenColumns().size() > 0)
216       {
217         hasHiddenColumns = true;
218       }
219       else
220       {
221         hasHiddenColumns = false;
222       }
223     }
224     init();
225   }
226
227   /**
228    * New viewport with hidden columns and an existing sequence set id
229    * 
230    * @param al
231    * @param hiddenColumns
232    * @param seqsetid
233    *          (may be null)
234    */
235   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
236           String seqsetid)
237   {
238     this(al, hiddenColumns, seqsetid, null);
239   }
240
241   /**
242    * New viewport with hidden columns and an existing sequence set id and viewid
243    * 
244    * @param al
245    * @param hiddenColumns
246    * @param seqsetid
247    *          (may be null)
248    * @param viewid
249    *          (may be null)
250    */
251   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns,
252           String seqsetid, String viewid)
253   {
254     sequenceSetID = seqsetid;
255     viewId = viewid;
256     // TODO remove these once 2.4.VAMSAS release finished
257     if (Cache.log != null && Cache.log.isDebugEnabled() && seqsetid != null)
258     {
259       Cache.log.debug("Setting viewport's sequence set id : "
260               + sequenceSetID);
261     }
262     if (Cache.log != null && Cache.log.isDebugEnabled() && viewId != null)
263     {
264       Cache.log.debug("Setting viewport's view id : " + viewId);
265     }
266     setAlignment(al);
267     if (hiddenColumns != null)
268     {
269       this.colSel = hiddenColumns;
270       if (hiddenColumns.getHiddenColumns() != null
271               && hiddenColumns.getHiddenColumns().size() > 0)
272       {
273         hasHiddenColumns = true;
274       }
275       else
276       {
277         hasHiddenColumns = false;
278       }
279     }
280     init();
281   }
282
283   void init()
284   {
285     this.startRes = 0;
286     this.endRes = alignment.getWidth() - 1;
287     this.startSeq = 0;
288     this.endSeq = alignment.getHeight() - 1;
289
290     antiAlias = Cache.getDefault("ANTI_ALIAS", false);
291
292     showJVSuffix = Cache.getDefault("SHOW_JVSUFFIX", true);
293     showAnnotation = Cache.getDefault("SHOW_ANNOTATIONS", true);
294
295     rightAlignIds = Cache.getDefault("RIGHT_ALIGN_IDS", false);
296     centreColumnLabels = Cache.getDefault("CENTRE_COLUMN_LABELS", false);
297     autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
298
299     setPadGaps(Cache.getDefault("PAD_GAPS", true));
300     shownpfeats = Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true);
301     showdbrefs = Cache.getDefault("SHOW_DBREFS_TOOLTIP", true);
302
303     String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
304     String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "");
305     String fontSize = Cache.getDefault("FONT_SIZE", "10");
306
307     seqNameItalics = Cache.getDefault("ID_ITALICS", true);
308
309     int style = 0;
310
311     if (fontStyle.equals("bold"))
312     {
313       style = 1;
314     }
315     else if (fontStyle.equals("italic"))
316     {
317       style = 2;
318     }
319
320     setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
321
322     alignment
323             .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0));
324
325     // We must set conservation and consensus before setting colour,
326     // as Blosum and Clustal require this to be done
327     if (hconsensus == null && !isDataset)
328     {
329       if (!alignment.isNucleotide())
330       {
331         showConservation = Cache.getDefault("SHOW_CONSERVATION", true);
332         showQuality = Cache.getDefault("SHOW_QUALITY", true);
333         showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION",
334                 false);
335       }
336       showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM",
337               true);
338       showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", false);
339       normaliseSequenceLogo = Cache.getDefault("NORMALISE_CONSENSUS_LOGO",
340               false);
341       showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false);
342       showConsensus = Cache.getDefault("SHOW_IDENTITY", true);
343     }
344     initAutoAnnotation();
345     if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
346     {
347       globalColourScheme = ColourSchemeProperty.getColour(alignment,
348               jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
349
350       if (globalColourScheme instanceof UserColourScheme)
351       {
352         globalColourScheme = UserDefinedColours.loadDefaultColours();
353         ((UserColourScheme) globalColourScheme).setThreshold(0,
354                 getIgnoreGapsConsensus());
355       }
356
357       if (globalColourScheme != null)
358       {
359         globalColourScheme.setConsensus(hconsensus);
360       }
361     }
362
363     wrapAlignment = Cache.getDefault("WRAP_ALIGNMENT", false);
364     showUnconserved = Cache.getDefault("SHOW_UNCONSERVED", false);
365     sortByTree = Cache.getDefault("SORT_BY_TREE", false);
366     followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true);
367     sortAnnotationsBy = SequenceAnnotationOrder.valueOf(Cache.getDefault(
368             Preferences.SORT_ANNOTATIONS,
369             SequenceAnnotationOrder.NONE.name()));
370     showAutocalculatedAbove = Cache.getDefault(
371             Preferences.SHOW_AUTOCALC_ABOVE, false);
372   }
373
374   /**
375    * set the flag
376    * 
377    * @param b
378    *          features are displayed if true
379    */
380   public void setShowSequenceFeatures(boolean b)
381   {
382     showSequenceFeatures = b;
383   }
384
385   public boolean getShowSequenceFeatures()
386   {
387     return showSequenceFeatures;
388   }
389
390   /**
391    * centre columnar annotation labels in displayed alignment annotation TODO:
392    * add to jalviewXML and annotation display settings
393    */
394   boolean centreColumnLabels = false;
395
396   private boolean showdbrefs;
397
398   private boolean shownpfeats;
399
400   // --------END Structure Conservation
401
402   /**
403    * get the consensus sequence as displayed under the PID consensus annotation
404    * row.
405    * 
406    * @return consensus sequence as a new sequence object
407    */
408   public SequenceI getConsensusSeq()
409   {
410     if (consensus == null)
411     {
412       updateConsensus(null);
413     }
414     if (consensus == null)
415     {
416       return null;
417     }
418     StringBuffer seqs = new StringBuffer();
419     for (int i = 0; i < consensus.annotations.length; i++)
420     {
421       if (consensus.annotations[i] != null)
422       {
423         if (consensus.annotations[i].description.charAt(0) == '[')
424         {
425           seqs.append(consensus.annotations[i].description.charAt(1));
426         }
427         else
428         {
429           seqs.append(consensus.annotations[i].displayCharacter);
430         }
431       }
432     }
433
434     SequenceI sq = new Sequence("Consensus", seqs.toString());
435     sq.setDescription("Percentage Identity Consensus "
436             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
437     return sq;
438   }
439
440   /**
441    * DOCUMENT ME!
442    * 
443    * @return DOCUMENT ME!
444    */
445   public int getStartRes()
446   {
447     return startRes;
448   }
449
450   /**
451    * DOCUMENT ME!
452    * 
453    * @return DOCUMENT ME!
454    */
455   public int getEndRes()
456   {
457     return endRes;
458   }
459
460   /**
461    * DOCUMENT ME!
462    * 
463    * @return DOCUMENT ME!
464    */
465   public int getStartSeq()
466   {
467     return startSeq;
468   }
469
470   /**
471    * DOCUMENT ME!
472    * 
473    * @param res
474    *          DOCUMENT ME!
475    */
476   public void setStartRes(int res)
477   {
478     this.startRes = res;
479   }
480
481   /**
482    * DOCUMENT ME!
483    * 
484    * @param seq
485    *          DOCUMENT ME!
486    */
487   public void setStartSeq(int seq)
488   {
489     this.startSeq = seq;
490   }
491
492   /**
493    * DOCUMENT ME!
494    * 
495    * @param res
496    *          DOCUMENT ME!
497    */
498   public void setEndRes(int res)
499   {
500     if (res > (alignment.getWidth() - 1))
501     {
502       // log.System.out.println(" Corrected res from " + res + " to maximum " +
503       // (alignment.getWidth()-1));
504       res = alignment.getWidth() - 1;
505     }
506
507     if (res < 0)
508     {
509       res = 0;
510     }
511
512     this.endRes = res;
513   }
514
515   /**
516    * DOCUMENT ME!
517    * 
518    * @param seq
519    *          DOCUMENT ME!
520    */
521   public void setEndSeq(int seq)
522   {
523     if (seq > alignment.getHeight())
524     {
525       seq = alignment.getHeight();
526     }
527
528     if (seq < 0)
529     {
530       seq = 0;
531     }
532
533     this.endSeq = seq;
534   }
535
536   /**
537    * DOCUMENT ME!
538    * 
539    * @return DOCUMENT ME!
540    */
541   public int getEndSeq()
542   {
543     return endSeq;
544   }
545
546   /**
547    * DOCUMENT ME!
548    * 
549    * @param f
550    *          DOCUMENT ME!
551    */
552   public void setFont(Font f)
553   {
554     font = f;
555
556     Container c = new Container();
557
558     java.awt.FontMetrics fm = c.getFontMetrics(font);
559     setCharHeight(fm.getHeight());
560     setCharWidth(fm.charWidth('M'));
561     validCharWidth = true;
562   }
563
564   /**
565    * DOCUMENT ME!
566    * 
567    * @return DOCUMENT ME!
568    */
569   public Font getFont()
570   {
571     return font;
572   }
573
574   /**
575    * DOCUMENT ME!
576    * 
577    * @param w
578    *          DOCUMENT ME!
579    */
580   public void setCharWidth(int w)
581   {
582     this.charWidth = w;
583   }
584
585   /**
586    * DOCUMENT ME!
587    * 
588    * @return DOCUMENT ME!
589    */
590   public int getCharWidth()
591   {
592     return charWidth;
593   }
594
595   /**
596    * DOCUMENT ME!
597    * 
598    * @param h
599    *          DOCUMENT ME!
600    */
601   public void setCharHeight(int h)
602   {
603     this.charHeight = h;
604   }
605
606   /**
607    * DOCUMENT ME!
608    * 
609    * @return DOCUMENT ME!
610    */
611   public int getCharHeight()
612   {
613     return charHeight;
614   }
615
616   /**
617    * DOCUMENT ME!
618    * 
619    * @param w
620    *          DOCUMENT ME!
621    */
622   public void setWrappedWidth(int w)
623   {
624     this.wrappedWidth = w;
625   }
626
627   /**
628    * DOCUMENT ME!
629    * 
630    * @return DOCUMENT ME!
631    */
632   public int getWrappedWidth()
633   {
634     return wrappedWidth;
635   }
636
637   /**
638    * DOCUMENT ME!
639    * 
640    * @return DOCUMENT ME!
641    */
642   public AlignmentI getAlignment()
643   {
644     return alignment;
645   }
646
647   /**
648    * DOCUMENT ME!
649    * 
650    * @param align
651    *          DOCUMENT ME!
652    */
653   public void setAlignment(AlignmentI align)
654   {
655     if (alignment != null && alignment.getCodonFrames() != null)
656     {
657       StructureSelectionManager.getStructureSelectionManager(
658               Desktop.instance).removeMappings(alignment.getCodonFrames());
659     }
660     this.alignment = align;
661     if (alignment != null && alignment.getCodonFrames() != null)
662     {
663       StructureSelectionManager.getStructureSelectionManager(
664               Desktop.instance).addMappings(alignment.getCodonFrames());
665     }
666   }
667
668   /**
669    * DOCUMENT ME!
670    * 
671    * @param state
672    *          DOCUMENT ME!
673    */
674   public void setWrapAlignment(boolean state)
675   {
676     wrapAlignment = state;
677   }
678
679   /**
680    * DOCUMENT ME!
681    * 
682    * @param state
683    *          DOCUMENT ME!
684    */
685   public void setShowText(boolean state)
686   {
687     showText = state;
688   }
689
690   /**
691    * DOCUMENT ME!
692    * 
693    * @param state
694    *          DOCUMENT ME!
695    */
696   public void setRenderGaps(boolean state)
697   {
698     renderGaps = state;
699   }
700
701   /**
702    * DOCUMENT ME!
703    * 
704    * @return DOCUMENT ME!
705    */
706   public boolean getColourText()
707   {
708     return showColourText;
709   }
710
711   /**
712    * DOCUMENT ME!
713    * 
714    * @param state
715    *          DOCUMENT ME!
716    */
717   public void setColourText(boolean state)
718   {
719     showColourText = state;
720   }
721
722   /**
723    * DOCUMENT ME!
724    * 
725    * @param state
726    *          DOCUMENT ME!
727    */
728   public void setShowBoxes(boolean state)
729   {
730     showBoxes = state;
731   }
732
733   /**
734    * DOCUMENT ME!
735    * 
736    * @return DOCUMENT ME!
737    */
738   public boolean getWrapAlignment()
739   {
740     return wrapAlignment;
741   }
742
743   /**
744    * DOCUMENT ME!
745    * 
746    * @return DOCUMENT ME!
747    */
748   public boolean getShowText()
749   {
750     return showText;
751   }
752
753   /**
754    * DOCUMENT ME!
755    * 
756    * @return DOCUMENT ME!
757    */
758   public boolean getShowBoxes()
759   {
760     return showBoxes;
761   }
762
763   /**
764    * DOCUMENT ME!
765    * 
766    * @return DOCUMENT ME!
767    */
768   public char getGapCharacter()
769   {
770     return getAlignment().getGapCharacter();
771   }
772
773   /**
774    * DOCUMENT ME!
775    * 
776    * @param gap
777    *          DOCUMENT ME!
778    */
779   public void setGapCharacter(char gap)
780   {
781     if (getAlignment() != null)
782     {
783       getAlignment().setGapCharacter(gap);
784     }
785   }
786
787   /**
788    * DOCUMENT ME!
789    * 
790    * @return DOCUMENT ME!
791    */
792   public ColumnSelection getColumnSelection()
793   {
794     return colSel;
795   }
796
797   /**
798    * DOCUMENT ME!
799    * 
800    * @param tree
801    *          DOCUMENT ME!
802    */
803   public void setCurrentTree(NJTree tree)
804   {
805     currentTree = tree;
806   }
807
808   /**
809    * DOCUMENT ME!
810    * 
811    * @return DOCUMENT ME!
812    */
813   public NJTree getCurrentTree()
814   {
815     return currentTree;
816   }
817
818   /**
819    * DOCUMENT ME!
820    * 
821    * @return DOCUMENT ME!
822    */
823   public boolean getShowJVSuffix()
824   {
825     return showJVSuffix;
826   }
827
828   /**
829    * DOCUMENT ME!
830    * 
831    * @param b
832    *          DOCUMENT ME!
833    */
834   public void setShowJVSuffix(boolean b)
835   {
836     showJVSuffix = b;
837   }
838
839   /**
840    * DOCUMENT ME!
841    * 
842    * @return DOCUMENT ME!
843    */
844   public boolean getShowAnnotation()
845   {
846     return showAnnotation;
847   }
848
849   /**
850    * DOCUMENT ME!
851    * 
852    * @param b
853    *          DOCUMENT ME!
854    */
855   public void setShowAnnotation(boolean b)
856   {
857     showAnnotation = b;
858   }
859
860   /**
861    * DOCUMENT ME!
862    * 
863    * @return DOCUMENT ME!
864    */
865   public boolean getScaleAboveWrapped()
866   {
867     return scaleAboveWrapped;
868   }
869
870   /**
871    * DOCUMENT ME!
872    * 
873    * @return DOCUMENT ME!
874    */
875   public boolean getScaleLeftWrapped()
876   {
877     return scaleLeftWrapped;
878   }
879
880   /**
881    * DOCUMENT ME!
882    * 
883    * @return DOCUMENT ME!
884    */
885   public boolean getScaleRightWrapped()
886   {
887     return scaleRightWrapped;
888   }
889
890   /**
891    * DOCUMENT ME!
892    * 
893    * @param b
894    *          DOCUMENT ME!
895    */
896   public void setScaleAboveWrapped(boolean b)
897   {
898     scaleAboveWrapped = b;
899   }
900
901   /**
902    * DOCUMENT ME!
903    * 
904    * @param b
905    *          DOCUMENT ME!
906    */
907   public void setScaleLeftWrapped(boolean b)
908   {
909     scaleLeftWrapped = b;
910   }
911
912   /**
913    * DOCUMENT ME!
914    * 
915    * @param b
916    *          DOCUMENT ME!
917    */
918   public void setScaleRightWrapped(boolean b)
919   {
920     scaleRightWrapped = b;
921   }
922
923   public void setDataset(boolean b)
924   {
925     isDataset = b;
926   }
927
928   public boolean isDataset()
929   {
930     return isDataset;
931   }
932
933   public boolean getShowHiddenMarkers()
934   {
935     return showHiddenMarkers;
936   }
937
938   public void setShowHiddenMarkers(boolean show)
939   {
940     showHiddenMarkers = show;
941   }
942
943   /**
944    * returns the visible column regions of the alignment
945    * 
946    * @param selectedRegionOnly
947    *          true to just return the contigs intersecting with the selected
948    *          area
949    * @return
950    */
951   public int[] getViewAsVisibleContigs(boolean selectedRegionOnly)
952   {
953     int[] viscontigs = null;
954     int start = 0, end = 0;
955     if (selectedRegionOnly && selectionGroup != null)
956     {
957       start = selectionGroup.getStartRes();
958       end = selectionGroup.getEndRes() + 1;
959     }
960     else
961     {
962       end = alignment.getWidth();
963     }
964     viscontigs = colSel.getVisibleContigs(start, end);
965     return viscontigs;
966   }
967
968   /**
969    * get hash of undo and redo list for the alignment
970    * 
971    * @return long[] { historyList.hashCode, redoList.hashCode };
972    */
973   public long[] getUndoRedoHash()
974   {
975     // TODO: JAL-1126
976     if (historyList == null || redoList == null)
977     {
978       return new long[]
979       { -1, -1 };
980     }
981     return new long[]
982     { historyList.hashCode(), this.redoList.hashCode() };
983   }
984
985   /**
986    * test if a particular set of hashcodes are different to the hashcodes for
987    * the undo and redo list.
988    * 
989    * @param undoredo
990    *          the stored set of hashcodes as returned by getUndoRedoHash
991    * @return true if the hashcodes differ (ie the alignment has been edited) or
992    *         the stored hashcode array differs in size
993    */
994   public boolean isUndoRedoHashModified(long[] undoredo)
995   {
996     if (undoredo == null)
997     {
998       return true;
999     }
1000     long[] cstate = getUndoRedoHash();
1001     if (cstate.length != undoredo.length)
1002     {
1003       return true;
1004     }
1005
1006     for (int i = 0; i < cstate.length; i++)
1007     {
1008       if (cstate[i] != undoredo[i])
1009       {
1010         return true;
1011       }
1012     }
1013     return false;
1014   }
1015
1016   public boolean getCentreColumnLabels()
1017   {
1018     return centreColumnLabels;
1019   }
1020
1021   public void setCentreColumnLabels(boolean centrecolumnlabels)
1022   {
1023     centreColumnLabels = centrecolumnlabels;
1024   }
1025
1026   /**
1027    * enable or disable the display of Database Cross References in the sequence
1028    * ID tooltip
1029    */
1030   public void setShowDbRefs(boolean show)
1031   {
1032     showdbrefs = show;
1033   }
1034
1035   /**
1036    * 
1037    * @return true if Database References are to be displayed on tooltips.
1038    */
1039   public boolean isShowDbRefs()
1040   {
1041     return showdbrefs;
1042   }
1043
1044   /**
1045    * 
1046    * @return true if Non-positional features are to be displayed on tooltips.
1047    */
1048   public boolean isShowNpFeats()
1049   {
1050     return shownpfeats;
1051   }
1052
1053   /**
1054    * enable or disable the display of Non-Positional sequence features in the
1055    * sequence ID tooltip
1056    * 
1057    * @param show
1058    */
1059   public void setShowNpFeats(boolean show)
1060   {
1061     shownpfeats = show;
1062   }
1063
1064   /**
1065    * 
1066    * @return true if view has hidden rows
1067    */
1068   public boolean hasHiddenRows()
1069   {
1070     return hasHiddenRows;
1071   }
1072
1073   /**
1074    * 
1075    * @return true if view has hidden columns
1076    */
1077   public boolean hasHiddenColumns()
1078   {
1079     return hasHiddenColumns;
1080   }
1081
1082   /**
1083    * when set, view will scroll to show the highlighted position
1084    */
1085   public boolean followHighlight = true;
1086
1087   /**
1088    * @return true if view should scroll to show the highlighted region of a
1089    *         sequence
1090    * @return
1091    */
1092   public boolean getFollowHighlight()
1093   {
1094     return followHighlight;
1095   }
1096
1097   public boolean followSelection = true;
1098
1099   /**
1100    * @return true if view selection should always follow the selections
1101    *         broadcast by other selection sources
1102    */
1103   public boolean getFollowSelection()
1104   {
1105     return followSelection;
1106   }
1107
1108   boolean showSeqFeaturesHeight;
1109
1110   public void sendSelection()
1111   {
1112     jalview.structure.StructureSelectionManager
1113             .getStructureSelectionManager(Desktop.instance).sendSelection(
1114                     new SequenceGroup(getSelectionGroup()),
1115                     new ColumnSelection(getColumnSelection()), this);
1116   }
1117
1118   public void setShowSequenceFeaturesHeight(boolean selected)
1119   {
1120     showSeqFeaturesHeight = selected;
1121   }
1122
1123   public boolean getShowSequenceFeaturesHeight()
1124   {
1125     return showSeqFeaturesHeight;
1126   }
1127
1128   /**
1129    * return the alignPanel containing the given viewport. Use this to get the
1130    * components currently handling the given viewport.
1131    * 
1132    * @param av
1133    * @return null or an alignPanel guaranteed to have non-null alignFrame
1134    *         reference
1135    */
1136   public AlignmentPanel getAlignPanel()
1137   {
1138     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(this
1139             .getSequenceSetId());
1140     AlignmentPanel ap = null;
1141     for (int p = 0; aps != null && p < aps.length; p++)
1142     {
1143       if (aps[p].av == this)
1144       {
1145         return aps[p];
1146       }
1147     }
1148     return null;
1149   }
1150
1151   public boolean getSortByTree()
1152   {
1153     return sortByTree;
1154   }
1155
1156   public void setSortByTree(boolean sort)
1157   {
1158     sortByTree = sort;
1159   }
1160
1161   /**
1162    * synthesize a column selection if none exists so it covers the given
1163    * selection group. if wholewidth is false, no column selection is made if the
1164    * selection group covers the whole alignment width.
1165    * 
1166    * @param sg
1167    * @param wholewidth
1168    */
1169   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
1170   {
1171     int sgs, sge;
1172     if (sg != null
1173             && (sgs = sg.getStartRes()) >= 0
1174             && sg.getStartRes() <= (sge = sg.getEndRes())
1175             && (colSel == null || colSel.getSelected() == null || colSel
1176                     .getSelected().size() == 0))
1177     {
1178       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
1179       {
1180         // do nothing
1181         return;
1182       }
1183       if (colSel == null)
1184       {
1185         colSel = new ColumnSelection();
1186       }
1187       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
1188       {
1189         colSel.addElement(cspos);
1190       }
1191     }
1192   }
1193
1194   public StructureSelectionManager getStructureSelectionManager()
1195   {
1196     return StructureSelectionManager
1197             .getStructureSelectionManager(Desktop.instance);
1198   }
1199
1200   /**
1201    * 
1202    * @param pdbEntries
1203    * @return a series of SequenceI arrays, one for each PDBEntry, listing which
1204    *         sequence in the alignment holds a reference to it
1205    */
1206   public SequenceI[][] collateForPDB(PDBEntry[] pdbEntries)
1207   {
1208     ArrayList<SequenceI[]> seqvectors = new ArrayList<SequenceI[]>();
1209     for (PDBEntry pdb : pdbEntries)
1210     {
1211       ArrayList<SequenceI> seqs = new ArrayList<SequenceI>();
1212       for (int i = 0; i < alignment.getHeight(); i++)
1213       {
1214         Vector pdbs = alignment.getSequenceAt(i).getDatasetSequence()
1215                 .getPDBId();
1216         if (pdbs == null)
1217         {
1218           continue;
1219         }
1220         SequenceI sq;
1221         for (int p = 0; p < pdbs.size(); p++)
1222         {
1223           PDBEntry p1 = (PDBEntry) pdbs.elementAt(p);
1224           if (p1.getId().equals(pdb.getId()))
1225           {
1226             if (!seqs.contains(sq = alignment.getSequenceAt(i)))
1227             {
1228               seqs.add(sq);
1229             }
1230
1231             continue;
1232           }
1233         }
1234       }
1235       seqvectors.add(seqs.toArray(new SequenceI[seqs.size()]));
1236     }
1237     return seqvectors.toArray(new SequenceI[seqvectors.size()][]);
1238   }
1239
1240   public boolean isNormaliseSequenceLogo()
1241   {
1242     return normaliseSequenceLogo;
1243   }
1244
1245   public void setNormaliseSequenceLogo(boolean state)
1246   {
1247     normaliseSequenceLogo = state;
1248   }
1249
1250   /**
1251    * 
1252    * @return true if alignment characters should be displayed
1253    */
1254   public boolean isValidCharWidth()
1255   {
1256     return validCharWidth;
1257   }
1258
1259   private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<String, AutoCalcSetting>();
1260
1261   private boolean showAutocalculatedAbove;
1262
1263   public AutoCalcSetting getCalcIdSettingsFor(String calcId)
1264   {
1265     return calcIdParams.get(calcId);
1266   }
1267
1268   public void setCalcIdSettingsFor(String calcId, AutoCalcSetting settings,
1269           boolean needsUpdate)
1270   {
1271     calcIdParams.put(calcId, settings);
1272     // TODO: create a restart list to trigger any calculations that need to be
1273     // restarted after load
1274     // calculator.getRegisteredWorkersOfClass(settings.getWorkerClass())
1275     if (needsUpdate)
1276     {
1277       Cache.log.debug("trigger update for " + calcId);
1278     }
1279   }
1280
1281   protected SequenceAnnotationOrder getSortAnnotationsBy()
1282   {
1283     return sortAnnotationsBy;
1284   }
1285
1286   protected void setSortAnnotationsBy(SequenceAnnotationOrder sortAnnotationsBy)
1287   {
1288     this.sortAnnotationsBy = sortAnnotationsBy;
1289   }
1290
1291   protected boolean isShowAutocalculatedAbove()
1292   {
1293     return showAutocalculatedAbove;
1294   }
1295
1296   protected void setShowAutocalculatedAbove(boolean showAutocalculatedAbove)
1297   {
1298     this.showAutocalculatedAbove = showAutocalculatedAbove;
1299   }
1300
1301   /**
1302    * Method called when another alignment's edit (or possibly other) command is
1303    * broadcast to here.
1304    *
1305    * To allow for sequence mappings (e.g. protein to cDNA), we have to first
1306    * 'unwind' the command on the source sequences (in simulation, not in fact),
1307    * and then for each edit in turn:
1308    * <ul>
1309    * <li>compute the equivalent edit on the mapped sequences</li>
1310    * <li>apply the mapped edit</li>
1311    * <li>'apply' the source edit to the working copy of the source sequences</li>
1312    * </ul>
1313    * 
1314    * @param command
1315    * @param undo
1316    * @param ssm
1317    */
1318   @Override
1319   public void mirrorCommand(CommandI command, boolean undo,
1320           StructureSelectionManager ssm)
1321   {
1322     /*
1323      * Only EditCommand is currently handled by listeners.
1324      */
1325     if (!(command instanceof EditCommand))
1326     {
1327       return;
1328     }
1329     EditCommand edit = (EditCommand) command;
1330
1331     List<SequenceI> seqs = getAlignment().getSequences();
1332     EditCommand mappedCommand = ssm.mapEditCommand(edit, undo, seqs,
1333             getGapCharacter());
1334     AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments();
1335     mappedCommand.performEdit(0, views);
1336     getAlignPanel().alignmentChanged();
1337   }
1338
1339   @Override
1340   public VamsasSource getVamsasSource()
1341   {
1342     return this;
1343   }
1344
1345   /**
1346    * Add one command to the command history list.
1347    * 
1348    * @param command
1349    */
1350   public void addToHistoryList(CommandI command)
1351   {
1352     if (this.historyList != null)
1353     {
1354       this.historyList.push(command);
1355       broadcastCommand(command, false);
1356     }
1357   }
1358
1359   protected void broadcastCommand(CommandI command, boolean undo)
1360   {
1361     getStructureSelectionManager().commandPerformed(command, undo, getVamsasSource());
1362   }
1363
1364   /**
1365    * Add one command to the command redo list.
1366    * 
1367    * @param command
1368    */
1369   public void addToRedoList(CommandI command)
1370   {
1371     if (this.redoList != null)
1372     {
1373       this.redoList.push(command);
1374     }
1375     broadcastCommand(command, true);
1376   }
1377
1378   /**
1379    * Clear the command redo list.
1380    */
1381   public void clearRedoList()
1382   {
1383     if (this.redoList != null)
1384     {
1385       this.redoList.clear();
1386     }
1387   }
1388
1389   public void setHistoryList(Deque<CommandI> list)
1390   {
1391     this.historyList = list;
1392   }
1393
1394   public Deque<CommandI> getHistoryList()
1395   {
1396     return this.historyList;
1397   }
1398
1399   public void setRedoList(Deque<CommandI> list)
1400   {
1401     this.redoList = list;
1402   }
1403
1404   public Deque<CommandI> getRedoList()
1405   {
1406     return this.redoList;
1407   }
1408 }