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