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