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