Refactored alignment viewport to use common base, extended viewport API with getters...
[jalview.git] / src / jalview / appletgui / AlignViewport.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.appletgui;
19
20 import java.util.*;
21
22 import java.awt.*;
23
24 import jalview.analysis.*;
25 import jalview.api.AlignCalcManagerI;
26 import jalview.api.AlignViewportI;
27 import jalview.bin.*;
28 import jalview.datamodel.*;
29 import jalview.schemes.*;
30 import jalview.structure.SelectionSource;
31 import jalview.structure.VamsasSource;
32 import jalview.workers.ConservationThread;
33
34 public class AlignViewport extends AlignmentViewport implements AlignViewportI, SelectionSource, VamsasSource 
35 {
36   int startRes;
37
38   int endRes;
39
40   int startSeq;
41
42   int endSeq;
43
44   boolean cursorMode = false;
45
46   boolean showJVSuffix = true;
47
48   boolean showText = true;
49
50   boolean showColourText = false;
51
52   boolean showBoxes = true;
53
54   boolean wrapAlignment = false;
55
56   boolean renderGaps = true;
57
58   boolean showSequenceFeatures = false;
59
60   boolean showAnnotation = true;
61
62   boolean showConservation = true;
63
64   boolean showQuality = true;
65
66   boolean showConsensus = true;
67
68   boolean upperCasebold = false;
69
70   boolean colourAppliesToAllGroups = true;
71
72   ColourSchemeI globalColourScheme = null;
73
74   boolean conservationColourSelected = false;
75
76   boolean abovePIDThreshold = false;
77
78   SequenceGroup selectionGroup;
79
80   int charHeight;
81
82   int charWidth;
83
84   int wrappedWidth;
85
86   Font font = new Font("SansSerif", Font.PLAIN, 10);
87
88   boolean validCharWidth = true;
89
90   AlignmentI alignment;
91
92   ColumnSelection colSel = new ColumnSelection();
93
94   int threshold;
95
96   int increment;
97
98   NJTree currentTree = null;
99
100   boolean scaleAboveWrapped = true;
101
102   boolean scaleLeftWrapped = true;
103
104   boolean scaleRightWrapped = true;
105
106   // The following vector holds the features which are
107   // currently visible, in the correct order or rendering
108   public Hashtable featuresDisplayed;
109
110   boolean hasHiddenColumns = false;
111
112   boolean hasHiddenRows = false;
113
114   boolean showHiddenMarkers = true;
115
116   public Hashtable[] hconsensus;
117
118   AlignmentAnnotation consensus;
119
120   AlignmentAnnotation conservation;
121
122   AlignmentAnnotation quality;
123
124   AlignmentAnnotation[] groupConsensus;
125
126   AlignmentAnnotation[] groupConservation;
127
128   boolean autocalculateConsensus = true;
129
130   private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
131           this);
132
133   boolean ignoreGapsInConsensusCalculation = false;
134
135   public jalview.bin.JalviewLite applet;
136
137   Hashtable sequenceColours;
138
139   boolean MAC = false;
140
141   Stack historyList = new Stack();
142
143   Stack redoList = new Stack();
144
145   String sequenceSetID;
146
147   Hashtable hiddenRepSequences;
148   
149   AlignCalcManagerI calculator=new jalview.workers.AlignCalcManager();
150   
151   public void finalize() {
152     applet=null;
153     quality=null;
154     alignment=null;
155     colSel=null;
156   }
157
158   public AlignViewport(AlignmentI al, JalviewLite applet)
159   {
160     this.applet = applet;
161     setAlignment(al);
162     this.startRes = 0;
163     this.endRes = al.getWidth() - 1;
164     this.startSeq = 0;
165     this.endSeq = al.getHeight() - 1;
166     if (applet != null)
167     {
168       // get the width and height scaling factors if they were specified
169       String param = applet.getParameter("widthScale");
170       if (param != null)
171       {
172         try
173         {
174           widthScale = new Float(param).floatValue();
175         } catch (Exception e)
176         {
177         }
178         if (widthScale <= 1.0)
179         {
180           System.err
181                   .println("Invalid alignment character width scaling factor ("
182                           + widthScale + "). Ignoring.");
183           widthScale = 1;
184         }
185         if (applet.debug)
186         {
187           System.err
188                   .println("Alignment character width scaling factor is now "
189                           + widthScale);
190         }
191       }
192       param = applet.getParameter("heightScale");
193       if (param != null)
194       {
195         try
196         {
197           heightScale = new Float(param).floatValue();
198         } catch (Exception e)
199         {
200         }
201         if (heightScale <= 1.0)
202         {
203           System.err
204                   .println("Invalid alignment character height scaling factor ("
205                           + heightScale + "). Ignoring.");
206           heightScale = 1;
207         }
208         if (applet.debug)
209         {
210           System.err
211                   .println("Alignment character height scaling factor is now "
212                           + heightScale);
213         }
214       }
215     }
216     setFont(font);
217
218     MAC = new jalview.util.Platform().isAMac();
219
220     if (applet != null)
221     {
222       showJVSuffix = applet.getDefaultParameter("showFullId", showJVSuffix);
223
224       showAnnotation = applet.getDefaultParameter("showAnnotation", showAnnotation);
225       
226       showConservation = applet.getDefaultParameter("showConservation", showConservation);
227       
228       showQuality = applet.getDefaultParameter("showQuality", showQuality);
229
230       showConsensus = applet.getDefaultParameter("showConsensus", showConsensus);
231
232       showUnconserved = applet.getDefaultParameter("showUnconserved", showUnconserved);
233
234       String param = applet.getParameter("upperCase");
235       if (param != null)
236       {
237         if (param.equalsIgnoreCase("bold"))
238         {
239           upperCasebold = true;
240         }
241       }
242       sortByTree = applet.getDefaultParameter("sortByTree", sortByTree);
243
244       followHighlight = applet.getDefaultParameter("automaticScrolling",followHighlight);
245       followSelection = followHighlight;
246
247       showSequenceLogo = applet.getDefaultParameter("showSequenceLogo", showSequenceLogo);
248
249       normaliseSequenceLogo = applet.getDefaultParameter("normaliseSequenceLogo", normaliseSequenceLogo);
250
251       showGroupConsensus = applet.getDefaultParameter("showGroupConsensus", showGroupConsensus);
252       
253       showGroupConservation = applet.getDefaultParameter("showGroupConservation", showGroupConservation);
254         
255       showConsensusHistogram = applet.getDefaultParameter("showConsensusHistogram", showConsensusHistogram);
256       
257     }
258
259     if (applet != null)
260     {
261       String colour = applet.getParameter("defaultColour");
262
263       if (colour == null)
264       {
265         colour = applet.getParameter("userDefinedColour");
266         if (colour != null)
267         {
268           colour = "User Defined";
269         }
270       }
271
272       if (colour != null)
273       {
274         globalColourScheme = ColourSchemeProperty.getColour(alignment,
275                 colour);
276         if (globalColourScheme != null)
277         {
278           globalColourScheme.setConsensus(hconsensus);
279         }
280       }
281
282       if (applet.getParameter("userDefinedColour") != null)
283       {
284         ((UserColourScheme) globalColourScheme).parseAppletParameter(applet
285                 .getParameter("userDefinedColour"));
286       }
287     }
288     if (hconsensus == null)
289     {
290       if (!alignment.isNucleotide())
291       {
292         conservation = new AlignmentAnnotation("Conservation",
293                 "Conservation of total alignment less than " + getConsPercGaps()
294                         + "% gaps", new Annotation[1], 0f, 11f,
295                 AlignmentAnnotation.BAR_GRAPH);
296         conservation.hasText = true;
297         conservation.autoCalculated = true;
298
299         if (showConservation)
300         {
301           alignment.addAnnotation(conservation);
302         }
303
304         if (showQuality)
305         {
306           quality = new AlignmentAnnotation("Quality",
307                   "Alignment Quality based on Blosum62 scores",
308                   new Annotation[1], 0f, 11f, AlignmentAnnotation.BAR_GRAPH);
309           quality.hasText = true;
310           quality.autoCalculated = true;
311
312           alignment.addAnnotation(quality);
313         }
314       }
315
316       consensus = new AlignmentAnnotation("Consensus", "PID",
317               new Annotation[1], 0f, 100f, AlignmentAnnotation.BAR_GRAPH);
318       consensus.hasText = true;
319       consensus.autoCalculated = true;
320
321       if (showConsensus)
322       {
323         alignment.addAnnotation(consensus);
324       }
325     }
326
327   }
328
329   public void showSequenceFeatures(boolean b)
330   {
331     showSequenceFeatures = b;
332   }
333
334   public boolean getShowSequenceFeatures()
335   {
336     return showSequenceFeatures;
337   }
338
339
340   ConsensusThread consensusThread;
341   
342 //  StrucConsensusThread strucConsensusThread;
343
344
345   boolean consUpdateNeeded = false;
346
347   static boolean UPDATING_CONSENSUS = false;
348
349   static boolean UPDATING_STRUC_CONSENSUS = false;
350
351   static boolean UPDATING_CONSERVATION = false;
352
353   boolean updatingConsensus = false;
354
355   boolean updatingStrucConsensus = false;
356
357   boolean updatingConservation = false;
358
359   /**
360    * DOCUMENT ME!
361    */
362   public void updateConservation(final AlignmentPanel ap)
363   {
364     if (alignment.isNucleotide() || conservation == null)
365     {
366       return;
367     }
368
369     calculator.startWorker(new ConservationThread(this, ap));
370   }
371
372   /**
373    * DOCUMENT ME!
374    */
375   public void updateConsensus(final AlignmentPanel ap)
376   {
377     consensusThread = new ConsensusThread(ap);
378     consensusThread.start();
379   }
380
381   class ConsensusThread extends Thread
382   {
383     AlignmentPanel ap;
384
385     public ConsensusThread(AlignmentPanel ap)
386     {
387       this.ap = ap;
388     }
389
390     public void run()
391     {
392       updatingConsensus = true;
393       while (UPDATING_CONSENSUS)
394       {
395         try
396         {
397           if (ap != null)
398           {
399             ap.paintAlignment(false);
400           }
401
402           Thread.sleep(200);
403         } catch (Exception ex)
404         {
405           ex.printStackTrace();
406         }
407       }
408
409       UPDATING_CONSENSUS = true;
410
411       try
412       {
413         int aWidth = alignment==null ? -1 : alignment.getWidth();
414         if (aWidth < 0)
415         {
416           UPDATING_CONSENSUS = false;
417           updatingConsensus = false;
418           return;
419         }
420
421         consensus.annotations = null;
422         consensus.annotations = new Annotation[aWidth];
423
424         hconsensus = new Hashtable[aWidth];
425         AAFrequency.calculate(alignment.getSequencesArray(), 0,
426                 alignment.getWidth(), hconsensus, true); // always calculate the
427                                                          // full profile
428         updateAnnotation(true);
429         //AAFrequency.completeConsensus(consensus, hconsensus, 0, aWidth,
430         //        ignoreGapsInConsensusCalculation,
431         //        true);
432         
433         if (globalColourScheme != null)
434         {
435           globalColourScheme.setConsensus(hconsensus);
436         }
437
438       } catch (OutOfMemoryError error)
439       {
440         alignment.deleteAnnotation(consensus);
441
442         consensus = null;
443         hconsensus = null;
444         System.out.println("Out of memory calculating consensus!!");
445         System.gc();
446       }
447       UPDATING_CONSENSUS = false;
448       updatingConsensus = false;
449
450       if (ap != null)
451       {
452         ap.paintAlignment(true);
453       }
454     }
455
456     /**
457      * update the consensus annotation from the sequence profile data using
458      * current visualization settings.
459      */
460     public void updateAnnotation()
461     {
462       updateAnnotation(false);
463     }
464
465     protected void updateAnnotation(boolean immediate)
466     {
467       // TODO: make calls thread-safe, so if another thread calls this method,
468       // it will either return or wait until one calculation is finished.
469       if (immediate
470               || (!updatingConsensus && consensus != null && hconsensus != null))
471       {
472         AAFrequency.completeConsensus(consensus, hconsensus, 0,
473                 hconsensus.length, ignoreGapsInConsensusCalculation,
474                 showSequenceLogo);
475       }
476     }
477   }
478
479   /**
480    * get the consensus sequence as displayed under the PID consensus annotation
481    * row.
482    * 
483    * @return consensus sequence as a new sequence object
484    */
485   public SequenceI getConsensusSeq()
486   {
487     if (consensus == null)
488     {
489       updateConsensus(null);
490     }
491     if (consensus == null)
492     {
493       return null;
494     }
495     StringBuffer seqs = new StringBuffer();
496     for (int i = 0; i < consensus.annotations.length; i++)
497     {
498       if (consensus.annotations[i] != null)
499       {
500         if (consensus.annotations[i].description.charAt(0) == '[')
501         {
502           seqs.append(consensus.annotations[i].description.charAt(1));
503         }
504         else
505         {
506           seqs.append(consensus.annotations[i].displayCharacter);
507         }
508       }
509     }
510     SequenceI sq = new Sequence("Consensus", seqs.toString());
511     sq.setDescription("Percentage Identity Consensus "
512             + ((ignoreGapsInConsensusCalculation) ? " without gaps" : ""));
513     return sq;
514   }
515
516   public SequenceGroup getSelectionGroup()
517   {
518     return selectionGroup;
519   }
520
521   public void setSelectionGroup(SequenceGroup sg)
522   {
523     selectionGroup = sg;
524   }
525
526   public boolean getConservationSelected()
527   {
528     return conservationColourSelected;
529   }
530
531   public void setConservationSelected(boolean b)
532   {
533     conservationColourSelected = b;
534   }
535
536   public boolean getAbovePIDThreshold()
537   {
538     return abovePIDThreshold;
539   }
540
541   public void setAbovePIDThreshold(boolean b)
542   {
543     abovePIDThreshold = b;
544   }
545
546   public int getStartRes()
547   {
548     return startRes;
549   }
550
551   public int getEndRes()
552   {
553     return endRes;
554   }
555
556   public int getStartSeq()
557   {
558     return startSeq;
559   }
560
561   public void setGlobalColourScheme(ColourSchemeI cs)
562   {
563     globalColourScheme = cs;
564   }
565
566   public ColourSchemeI getGlobalColourScheme()
567   {
568     return globalColourScheme;
569   }
570
571   public void setStartRes(int res)
572   {
573     this.startRes = res;
574   }
575
576   public void setStartSeq(int seq)
577   {
578     this.startSeq = seq;
579   }
580
581   public void setEndRes(int res)
582   {
583     if (res > alignment.getWidth() - 1)
584     {
585       // log.System.out.println(" Corrected res from " + res + " to maximum " +
586       // (alignment.getWidth()-1));
587       res = alignment.getWidth() - 1;
588     }
589     if (res < 0)
590     {
591       res = 0;
592     }
593     this.endRes = res;
594   }
595
596   public void setEndSeq(int seq)
597   {
598     if (seq > alignment.getHeight())
599     {
600       seq = alignment.getHeight();
601     }
602     if (seq < 0)
603     {
604       seq = 0;
605     }
606     this.endSeq = seq;
607   }
608
609   public int getEndSeq()
610   {
611     return endSeq;
612   }
613
614   java.awt.Frame nullFrame;
615
616   protected FeatureSettings featureSettings = null;
617
618   private float heightScale = 1, widthScale = 1;
619
620   public void setFont(Font f)
621   {
622     font = f;
623     if (nullFrame == null)
624     {
625       nullFrame = new java.awt.Frame();
626       nullFrame.addNotify();
627     }
628
629     java.awt.FontMetrics fm = nullFrame.getGraphics().getFontMetrics(font);
630     setCharHeight((int) (heightScale * fm.getHeight()));
631     charWidth = (int) (widthScale * fm.charWidth('M'));
632
633     if (upperCasebold)
634     {
635       Font f2 = new Font(f.getName(), Font.BOLD, f.getSize());
636       fm = nullFrame.getGraphics().getFontMetrics(f2);
637       charWidth = (int) (widthScale * (fm.stringWidth("MMMMMMMMMMM") / 10));
638     }
639   }
640
641   public Font getFont()
642   {
643     return font;
644   }
645
646   public int getCharWidth()
647   {
648     return charWidth;
649   }
650
651   public void setCharHeight(int h)
652   {
653     this.charHeight = h;
654   }
655
656   public int getCharHeight()
657   {
658     return charHeight;
659   }
660
661   public void setWrappedWidth(int w)
662   {
663     this.wrappedWidth = w;
664   }
665
666   public int getwrappedWidth()
667   {
668     return wrappedWidth;
669   }
670
671   public AlignmentI getAlignment()
672   {
673     return alignment;
674   }
675
676   public void setAlignment(AlignmentI align)
677   {
678     this.alignment = align;
679   }
680
681   public void setWrapAlignment(boolean state)
682   {
683     wrapAlignment = state;
684   }
685
686   public void setShowText(boolean state)
687   {
688     showText = state;
689   }
690
691   public void setRenderGaps(boolean state)
692   {
693     renderGaps = state;
694   }
695
696   public boolean getColourText()
697   {
698     return showColourText;
699   }
700
701   public void setColourText(boolean state)
702   {
703     showColourText = state;
704   }
705
706   public void setShowBoxes(boolean state)
707   {
708     showBoxes = state;
709   }
710
711   public boolean getWrapAlignment()
712   {
713     return wrapAlignment;
714   }
715
716   public boolean getShowText()
717   {
718     return showText;
719   }
720
721   public boolean getShowBoxes()
722   {
723     return showBoxes;
724   }
725
726   public char getGapCharacter()
727   {
728     return getAlignment().getGapCharacter();
729   }
730
731   public void setGapCharacter(char gap)
732   {
733     if (getAlignment() != null)
734     {
735       getAlignment().setGapCharacter(gap);
736     }
737   }
738
739   public void setThreshold(int thresh)
740   {
741     threshold = thresh;
742   }
743
744   public int getThreshold()
745   {
746     return threshold;
747   }
748
749   public void setIncrement(int inc)
750   {
751     increment = inc;
752   }
753
754   public int getIncrement()
755   {
756     return increment;
757   }
758
759   public void setHiddenColumns(ColumnSelection colsel)
760   {
761     this.colSel = colsel;
762     if (colSel.getHiddenColumns() != null)
763     {
764       hasHiddenColumns = true;
765     }
766   }
767
768   public ColumnSelection getColumnSelection()
769   {
770     return colSel;
771   }
772
773   public void resetSeqLimits(int height)
774   {
775     setEndSeq(height / getCharHeight());
776   }
777
778   public void setCurrentTree(NJTree tree)
779   {
780     currentTree = tree;
781   }
782
783   public NJTree getCurrentTree()
784   {
785     return currentTree;
786   }
787
788   public void setColourAppliesToAllGroups(boolean b)
789   {
790     colourAppliesToAllGroups = b;
791   }
792
793   public boolean getColourAppliesToAllGroups()
794   {
795     return colourAppliesToAllGroups;
796   }
797
798   public boolean getShowJVSuffix()
799   {
800     return showJVSuffix;
801   }
802
803   public void setShowJVSuffix(boolean b)
804   {
805     showJVSuffix = b;
806   }
807
808   public boolean getShowAnnotation()
809   {
810     return showAnnotation;
811   }
812
813   public void setShowAnnotation(boolean b)
814   {
815     showAnnotation = b;
816   }
817
818   public boolean getScaleAboveWrapped()
819   {
820     return scaleAboveWrapped;
821   }
822
823   public boolean getScaleLeftWrapped()
824   {
825     return scaleLeftWrapped;
826   }
827
828   public boolean getScaleRightWrapped()
829   {
830     return scaleRightWrapped;
831   }
832
833   public void setScaleAboveWrapped(boolean b)
834   {
835     scaleAboveWrapped = b;
836   }
837
838   public void setScaleLeftWrapped(boolean b)
839   {
840     scaleLeftWrapped = b;
841   }
842
843   public void setScaleRightWrapped(boolean b)
844   {
845     scaleRightWrapped = b;
846   }
847
848   public void setIgnoreGapsConsensus(boolean b)
849   {
850     ignoreGapsInConsensusCalculation = b;
851     updateConsensus(null);
852     if (globalColourScheme != null)
853     {
854       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
855               ignoreGapsInConsensusCalculation);
856
857     }
858   }
859
860   /**
861    * Property change listener for changes in alignment
862    * 
863    * @param listener
864    *          DOCUMENT ME!
865    */
866   public void addPropertyChangeListener(
867           java.beans.PropertyChangeListener listener)
868   {
869     changeSupport.addPropertyChangeListener(listener);
870   }
871
872   /**
873    * DOCUMENT ME!
874    * 
875    * @param listener
876    *          DOCUMENT ME!
877    */
878   public void removePropertyChangeListener(
879           java.beans.PropertyChangeListener listener)
880   {
881     changeSupport.removePropertyChangeListener(listener);
882   }
883
884   /**
885    * Property change listener for changes in alignment
886    * 
887    * @param prop
888    *          DOCUMENT ME!
889    * @param oldvalue
890    *          DOCUMENT ME!
891    * @param newvalue
892    *          DOCUMENT ME!
893    */
894   public void firePropertyChange(String prop, Object oldvalue,
895           Object newvalue)
896   {
897     changeSupport.firePropertyChange(prop, oldvalue, newvalue);
898   }
899
900   public boolean getIgnoreGapsConsensus()
901   {
902     return ignoreGapsInConsensusCalculation;
903   }
904
905   public void hideSelectedColumns()
906   {
907     if (colSel.size() < 1)
908     {
909       return;
910     }
911
912     colSel.hideSelectedColumns();
913     setSelectionGroup(null);
914
915     hasHiddenColumns = true;
916   }
917
918   public void invertColumnSelection()
919   {
920     for (int i = 0; i < alignment.getWidth(); i++)
921     {
922       if (colSel.contains(i))
923       {
924         colSel.removeElement(i);
925       }
926       else
927       {
928         if (!hasHiddenColumns || colSel.isVisible(i))
929         {
930           colSel.addElement(i);
931         }
932       }
933     }
934   }
935
936   public void hideColumns(int start, int end)
937   {
938     if (start == end)
939     {
940       colSel.hideColumns(start);
941     }
942     else
943     {
944       colSel.hideColumns(start, end);
945     }
946
947     hasHiddenColumns = true;
948   }
949
950   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
951   {
952     int sSize = sg.getSize();
953     if (sSize < 2)
954     {
955       return;
956     }
957
958     if (hiddenRepSequences == null)
959     {
960       hiddenRepSequences = new Hashtable();
961     }
962
963     hiddenRepSequences.put(repSequence, sg);
964
965     // Hide all sequences except the repSequence
966     SequenceI[] seqs = new SequenceI[sSize - 1];
967     int index = 0;
968     for (int i = 0; i < sSize; i++)
969     {
970       if (sg.getSequenceAt(i) != repSequence)
971       {
972         if (index == sSize - 1)
973         {
974           return;
975         }
976
977         seqs[index++] = sg.getSequenceAt(i);
978       }
979     }
980
981     hideSequence(seqs);
982
983   }
984
985   public void hideAllSelectedSeqs()
986   {
987     if (selectionGroup == null || selectionGroup.getSize() < 1)
988     {
989       return;
990     }
991
992     SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
993
994     hideSequence(seqs);
995
996     setSelectionGroup(null);
997   }
998
999   public void hideSequence(SequenceI[] seq)
1000   {
1001     if (seq != null)
1002     {
1003       for (int i = 0; i < seq.length; i++)
1004       {
1005         alignment.getHiddenSequences().hideSequence(seq[i]);
1006       }
1007
1008       hasHiddenRows = true;
1009       firePropertyChange("alignment", null, alignment.getSequences());
1010     }
1011   }
1012   public void showSequence(int index)
1013   {
1014     Vector tmp = alignment.getHiddenSequences().showSequence(index,
1015             hiddenRepSequences);
1016     if (tmp.size() > 0)
1017     {
1018       if (selectionGroup == null)
1019       {
1020         selectionGroup = new SequenceGroup();
1021         selectionGroup.setEndRes(alignment.getWidth() - 1);
1022       }
1023
1024       for (int t = 0; t < tmp.size(); t++)
1025       {
1026         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1027       }
1028       firePropertyChange("alignment", null, alignment.getSequences());
1029       sendSelection();
1030     }
1031
1032     if (alignment.getHiddenSequences().getSize() < 1)
1033     {
1034       hasHiddenRows = false;
1035     }
1036   }
1037   public void showColumn(int col)
1038   {
1039     colSel.revealHiddenColumns(col);
1040     if (colSel.getHiddenColumns() == null)
1041     {
1042       hasHiddenColumns = false;
1043     }
1044   }
1045
1046   public void showAllHiddenColumns()
1047   {
1048     colSel.revealAllHiddenColumns();
1049     hasHiddenColumns = false;
1050   }
1051
1052   public void showAllHiddenSeqs()
1053   {
1054     if (alignment.getHiddenSequences().getSize() > 0)
1055     {
1056       if (selectionGroup == null)
1057       {
1058         selectionGroup = new SequenceGroup();
1059         selectionGroup.setEndRes(alignment.getWidth() - 1);
1060       }
1061       Vector tmp = alignment.getHiddenSequences().showAll(
1062               hiddenRepSequences);
1063       for (int t = 0; t < tmp.size(); t++)
1064       {
1065         selectionGroup.addSequence((SequenceI) tmp.elementAt(t), false);
1066       }
1067       firePropertyChange("alignment", null, alignment.getSequences());
1068       hasHiddenRows = false;
1069       hiddenRepSequences = null;
1070       sendSelection();
1071     }
1072   }
1073
1074   public int adjustForHiddenSeqs(int alignmentIndex)
1075   {
1076     return alignment.getHiddenSequences().adjustForHiddenSeqs(
1077             alignmentIndex);
1078   }
1079
1080   /**
1081    * This method returns the a new SequenceI [] with the selection sequence and
1082    * start and end points adjusted
1083    * 
1084    * @return String[]
1085    */
1086   public SequenceI[] getSelectionAsNewSequence()
1087   {
1088     SequenceI[] sequences;
1089
1090     if (selectionGroup == null)
1091     {
1092       sequences = alignment.getSequencesArray();
1093     }
1094     else
1095     {
1096       sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1097     }
1098
1099     return sequences;
1100   }
1101
1102   /**
1103    * get the currently selected sequence objects or all the sequences in the
1104    * alignment.
1105    * 
1106    * @return array of references to sequence objects
1107    */
1108   public SequenceI[] getSequenceSelection()
1109   {
1110     SequenceI[] sequences = null;
1111     if (selectionGroup != null)
1112     {
1113       sequences = selectionGroup.getSequencesInOrder(alignment);
1114     }
1115     if (sequences == null)
1116     {
1117       sequences = alignment.getSequencesArray();
1118     }
1119     return sequences;
1120   }
1121
1122   /**
1123    * This method returns the visible alignment as text, as seen on the GUI, ie
1124    * if columns are hidden they will not be returned in the result. Use this for
1125    * calculating trees, PCA, redundancy etc on views which contain hidden
1126    * columns.
1127    * 
1128    * @return String[]
1129    */
1130   public jalview.datamodel.CigarArray getViewAsCigars(
1131           boolean selectedRegionOnly)
1132   {
1133     return new jalview.datamodel.CigarArray(alignment, (hasHiddenColumns ? colSel : null), (selectedRegionOnly ? selectionGroup : null));
1134   }
1135
1136   /**
1137    * return a compact representation of the current alignment selection to pass
1138    * to an analysis function
1139    * 
1140    * @param selectedOnly
1141    *          boolean true to just return the selected view
1142    * @return AlignmentView
1143    */
1144   jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1145   {    
1146     return getAlignmentView(selectedOnly, false);
1147   }
1148   
1149   /**
1150    * return a compact representation of the current alignment selection to pass
1151    * to an analysis function
1152    * 
1153    * @param selectedOnly
1154    *          boolean true to just return the selected view
1155    * @param markGroups
1156    *          boolean true to annotate the alignment view with groups on the alignment (and intersecting with selected region if selectedOnly is true) 
1157    * @return AlignmentView
1158    */
1159   public jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly, boolean markGroups)
1160   {
1161     return new AlignmentView(alignment, colSel, selectionGroup, hasHiddenColumns, selectedOnly, markGroups);
1162   }
1163   /**
1164    * This method returns the visible alignment as text, as seen on the GUI, ie
1165    * if columns are hidden they will not be returned in the result. Use this for
1166    * calculating trees, PCA, redundancy etc on views which contain hidden
1167    * columns.
1168    * 
1169    * @return String[]
1170    */
1171   public String[] getViewAsString(boolean selectedRegionOnly)
1172   {
1173     String[] selection = null;
1174     SequenceI[] seqs = null;
1175     int i, iSize;
1176     int start = 0, end = 0;
1177     if (selectedRegionOnly && selectionGroup != null)
1178     {
1179       iSize = selectionGroup.getSize();
1180       seqs = selectionGroup.getSequencesInOrder(alignment);
1181       start = selectionGroup.getStartRes();
1182       end = selectionGroup.getEndRes() + 1;
1183     }
1184     else
1185     {
1186       iSize = alignment.getHeight();
1187       seqs = alignment.getSequencesArray();
1188       end = alignment.getWidth();
1189     }
1190
1191     selection = new String[iSize];
1192
1193     for (i = 0; i < iSize; i++)
1194     {
1195       if (hasHiddenColumns)
1196       {
1197         StringBuffer visibleSeq = new StringBuffer();
1198         Vector regions = colSel.getHiddenColumns();
1199
1200         int blockStart = start, blockEnd = end;
1201         int[] region;
1202         int hideStart, hideEnd;
1203
1204         for (int j = 0; j < regions.size(); j++)
1205         {
1206           region = (int[]) regions.elementAt(j);
1207           hideStart = region[0];
1208           hideEnd = region[1];
1209
1210           if (hideStart < start)
1211           {
1212             continue;
1213           }
1214
1215           blockStart = Math.min(blockStart, hideEnd + 1);
1216           blockEnd = Math.min(blockEnd, hideStart);
1217
1218           if (blockStart > blockEnd)
1219           {
1220             break;
1221           }
1222
1223           visibleSeq.append(seqs[i].getSequence(blockStart, blockEnd));
1224
1225           blockStart = hideEnd + 1;
1226           blockEnd = end;
1227         }
1228
1229         if (end > blockStart)
1230         {
1231           visibleSeq.append(seqs[i].getSequence(blockStart, end));
1232         }
1233
1234         selection[i] = visibleSeq.toString();
1235       }
1236       else
1237       {
1238         selection[i] = seqs[i].getSequenceAsString(start, end);
1239       }
1240     }
1241
1242     return selection;
1243   }
1244
1245   public boolean getShowHiddenMarkers()
1246   {
1247     return showHiddenMarkers;
1248   }
1249
1250   public void setShowHiddenMarkers(boolean show)
1251   {
1252     showHiddenMarkers = show;
1253   }
1254
1255   public Color getSequenceColour(SequenceI seq)
1256   {
1257     if (sequenceColours == null || !sequenceColours.containsKey(seq))
1258     {
1259       return Color.white;
1260     }
1261     else
1262     {
1263       return (Color) sequenceColours.get(seq);
1264     }
1265   }
1266
1267   public void setSequenceColour(SequenceI seq, Color col)
1268   {
1269     if (sequenceColours == null)
1270     {
1271       sequenceColours = new Hashtable();
1272     }
1273
1274     if (col == null)
1275     {
1276       sequenceColours.remove(seq);
1277     }
1278     else
1279     {
1280       sequenceColours.put(seq, col);
1281     }
1282   }
1283
1284   public String getSequenceSetId()
1285   {
1286     if (sequenceSetID == null)
1287     {
1288       sequenceSetID = alignment.hashCode() + "";
1289     }
1290
1291     return sequenceSetID;
1292   }
1293   /**
1294    * unique viewId for synchronizing state (e.g. with stored Jalview Project)
1295    * 
1296    */
1297   private String viewId = null;
1298
1299   public String getViewId()
1300   {
1301     if (viewId == null)
1302     {
1303       viewId = this.getSequenceSetId() + "." + this.hashCode() + "";
1304     }
1305     return viewId;
1306   }
1307
1308   public void alignmentChanged(AlignmentPanel ap)
1309   {
1310     alignment.padGaps();
1311
1312     if (hconsensus != null && autocalculateConsensus)
1313     {
1314       updateConsensus(ap);
1315       updateConservation(ap);
1316     }
1317
1318     // Reset endRes of groups if beyond alignment width
1319     int alWidth = alignment.getWidth();
1320     Vector groups = alignment.getGroups();
1321     if (groups != null)
1322     {
1323       for (int i = 0; i < groups.size(); i++)
1324       {
1325         SequenceGroup sg = (SequenceGroup) groups.elementAt(i);
1326         if (sg.getEndRes() > alWidth)
1327         {
1328           sg.setEndRes(alWidth - 1);
1329         }
1330       }
1331     }
1332
1333     if (selectionGroup != null && selectionGroup.getEndRes() > alWidth)
1334     {
1335       selectionGroup.setEndRes(alWidth - 1);
1336     }
1337
1338     resetAllColourSchemes();
1339
1340     // AW alignment.adjustSequenceAnnotations();
1341   }
1342
1343   void resetAllColourSchemes()
1344   {
1345     ColourSchemeI cs = globalColourScheme;
1346     if (cs != null)
1347     {
1348       if (cs instanceof ClustalxColourScheme)
1349       {
1350         ((ClustalxColourScheme) cs).resetClustalX(alignment.getSequences(),
1351                 alignment.getWidth());
1352       }
1353
1354       cs.setConsensus(hconsensus);
1355       if (cs.conservationApplied())
1356       {
1357         cs.setConservation(Conservation.calculateConservation("All",
1358                 ResidueProperties.propHash, 3, alignment.getSequences(), 0,
1359                 alignment.getWidth(), false, getConsPercGaps(), false));
1360       }
1361     }
1362
1363     int s, sSize = alignment.getGroups().size();
1364     for (s = 0; s < sSize; s++)
1365     {
1366       SequenceGroup sg = (SequenceGroup) alignment.getGroups().elementAt(s);
1367       if (sg.cs != null && sg.cs instanceof ClustalxColourScheme)
1368       {
1369         ((ClustalxColourScheme) sg.cs).resetClustalX(
1370                 sg.getSequences(hiddenRepSequences), sg.getWidth());
1371       }
1372       sg.recalcConservation();
1373     }
1374   }
1375
1376   boolean centreColumnLabels;
1377
1378   public boolean getCentreColumnLabels()
1379   {
1380     return centreColumnLabels;
1381   }
1382
1383   public void updateSequenceIdColours()
1384   {
1385     Vector groups = alignment.getGroups();
1386     for (int ig = 0, igSize = groups.size(); ig < igSize; ig++)
1387     {
1388       SequenceGroup sg = (SequenceGroup) groups.elementAt(ig);
1389       if (sg.idColour != null)
1390       {
1391         Vector sqs = sg.getSequences(hiddenRepSequences);
1392         for (int s = 0, sSize = sqs.size(); s < sSize; s++)
1393         {
1394           this.setSequenceColour((SequenceI) sqs.elementAt(s), sg.idColour);
1395         }
1396       }
1397     }
1398   }
1399
1400   public boolean followHighlight = true;
1401
1402   public boolean getFollowHighlight()
1403   {
1404     return followHighlight;
1405   }
1406
1407   public boolean followSelection = true;
1408
1409   /**
1410    * @return true if view selection should always follow the selections
1411    *         broadcast by other selection sources
1412    */
1413   public boolean getFollowSelection()
1414   {
1415     return followSelection;
1416   }
1417
1418   private long sgrouphash = -1, colselhash = -1;
1419
1420   /**
1421    * checks current SelectionGroup against record of last hash value, and
1422    * updates record.
1423    * 
1424    * @return true if SelectionGroup changed since last call
1425    */
1426   boolean isSelectionGroupChanged()
1427   {
1428     int hc = (selectionGroup == null) ? -1 : selectionGroup.hashCode();
1429     if (hc != sgrouphash)
1430     {
1431       sgrouphash = hc;
1432       return true;
1433     }
1434     return false;
1435   }
1436
1437   /**
1438    * checks current colsel against record of last hash value, and updates
1439    * record.
1440    * 
1441    * @return true if colsel changed since last call
1442    */
1443   boolean isColSelChanged()
1444   {
1445     int hc = (colSel == null) ? -1 : colSel.hashCode();
1446     if (hc != colselhash)
1447     {
1448       colselhash = hc;
1449       return true;
1450     }
1451     return false;
1452   }
1453   public void sendSelection()
1454   {
1455     jalview.structure.StructureSelectionManager
1456             .getStructureSelectionManager(applet).sendSelection(
1457                     new SequenceGroup(getSelectionGroup()),
1458                     new ColumnSelection(getColumnSelection()), this);
1459   }
1460
1461
1462
1463
1464   /**
1465    * show non-conserved residues only
1466    */
1467   public boolean showUnconserved = false;
1468
1469   /**
1470    * when set, alignment should be reordered according to a newly opened tree
1471    */
1472   public boolean sortByTree = false;
1473
1474   /**
1475    * @return the showUnconserved
1476    */
1477   public boolean getShowunconserved()
1478   {
1479     return showUnconserved;
1480   }
1481
1482   /**
1483    * @param showNonconserved
1484    *          the showUnconserved to set
1485    */
1486   public void setShowunconserved(boolean displayNonconserved)
1487   {
1488     this.showUnconserved = displayNonconserved;
1489   }
1490
1491   /**
1492    * should conservation rows be shown for groups
1493    */
1494   boolean showGroupConservation = false;
1495
1496   /**
1497    * should consensus rows be shown for groups
1498    */
1499   boolean showGroupConsensus = false;
1500
1501   /**
1502    * should consensus profile be rendered by default
1503    */
1504   public boolean showSequenceLogo = false;
1505   /**
1506    * should consensus profile be rendered normalised to row height
1507    */
1508   public  boolean normaliseSequenceLogo = false;
1509
1510   /**
1511    * should consensus histograms be rendered by default
1512    */
1513   public boolean showConsensusHistogram = true;
1514
1515   /**
1516    * @return the showConsensusProfile
1517    */
1518   public boolean isShowSequenceLogo()
1519   {
1520     return showSequenceLogo;
1521   }
1522
1523   /**
1524    * @param showSequenceLogo
1525    *          the new value
1526    */
1527   public void setShowSequenceLogo(boolean showSequenceLogo)
1528   {
1529     if (showSequenceLogo != this.showSequenceLogo)
1530     {
1531       // TODO: decouple settings setting from calculation when refactoring
1532       // annotation update method from alignframe to viewport
1533       this.showSequenceLogo = showSequenceLogo;
1534       if (consensusThread != null)
1535       {
1536         consensusThread.updateAnnotation();
1537       }
1538     }
1539     this.showSequenceLogo = showSequenceLogo;
1540   }
1541
1542   /**
1543    * @param showConsensusHistogram
1544    *          the showConsensusHistogram to set
1545    */
1546   public void setShowConsensusHistogram(boolean showConsensusHistogram)
1547   {
1548     this.showConsensusHistogram = showConsensusHistogram;
1549   }
1550
1551   /**
1552    * @return the showGroupConservation
1553    */
1554   public boolean isShowGroupConservation()
1555   {
1556     return showGroupConservation;
1557   }
1558
1559   /**
1560    * @param showGroupConservation
1561    *          the showGroupConservation to set
1562    */
1563   public void setShowGroupConservation(boolean showGroupConservation)
1564   {
1565     this.showGroupConservation = showGroupConservation;
1566   }
1567
1568   /**
1569    * @return the showGroupConsensus
1570    */
1571   public boolean isShowGroupConsensus()
1572   {
1573     return showGroupConsensus;
1574   }
1575
1576   /**
1577    * @param showGroupConsensus
1578    *          the showGroupConsensus to set
1579    */
1580   public void setShowGroupConsensus(boolean showGroupConsensus)
1581   {
1582     this.showGroupConsensus = showGroupConsensus;
1583   }
1584
1585   /**
1586    * 
1587    * @return flag to indicate if the consensus histogram should be rendered by
1588    *         default
1589    */
1590   public boolean isShowConsensusHistogram()
1591   {
1592     return this.showConsensusHistogram;
1593   }
1594
1595   /**
1596    * synthesize a column selection if none exists so it covers the given
1597    * selection group. if wholewidth is false, no column selection is made if the
1598    * selection group covers the whole alignment width.
1599    * 
1600    * @param sg
1601    * @param wholewidth
1602    */
1603   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
1604   {
1605     int sgs, sge;
1606     if (sg != null
1607             && (sgs = sg.getStartRes()) >= 0
1608             && sg.getStartRes() <= (sge = sg.getEndRes())
1609             && (colSel == null || colSel.getSelected() == null || colSel
1610                     .getSelected().size() == 0))
1611     {
1612       if (!wholewidth && alignment.getWidth() == (1 + sge - sgs))
1613       {
1614         // do nothing
1615         return;
1616       }
1617       if (colSel == null)
1618       {
1619         colSel = new ColumnSelection();
1620       }
1621       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
1622       {
1623         colSel.addElement(cspos);
1624       }
1625     }
1626   }
1627
1628   @Override
1629   public boolean hasHiddenColumns()
1630   {
1631     return hasHiddenColumns;
1632   }
1633   
1634   public boolean isNormaliseSequenceLogo()
1635   {
1636     return normaliseSequenceLogo;
1637   }
1638
1639   public void setNormaliseSequenceLogo(boolean state)
1640   {
1641     normaliseSequenceLogo = state;
1642   }
1643
1644   public boolean isCalcInProgress()
1645   {
1646     // TODO generalise to iterate over all calculators associated with av
1647     return updatingConsensus || updatingConservation || updatingStrucConsensus;
1648   }
1649
1650   public boolean isCalculationInProgress(
1651           AlignmentAnnotation alignmentAnnotation)
1652   {
1653     if (!alignmentAnnotation.autoCalculated)
1654       return false;
1655     if ((updatingConsensus && consensus==alignmentAnnotation)
1656             || (updatingConservation && (conservation==alignmentAnnotation || quality==alignmentAnnotation))
1657             // || (updatingStrucConsensus && strucConsensus==alignmentAnnotation)
1658             )
1659     {
1660       return true;
1661     }
1662     return false;
1663   }
1664
1665   /**
1666    * 
1667    * @return true if alignment characters should be displayed 
1668    */
1669   public boolean isValidCharWidth()
1670   {
1671     return validCharWidth;
1672   }
1673
1674   @Override
1675   public Hashtable[] getSequenceConsensusHash()
1676   {
1677     return hconsensus;
1678   }
1679
1680   @Override
1681   public Hashtable[] getRnaStructureConsensusHash()
1682   {
1683     return null; // TODO: JAL-891 port to jvlite : refactor and introduce hStrucConsensus;
1684   }
1685
1686   @Override
1687   public AlignmentAnnotation getAlignmentQualityAnnot()
1688   {
1689     // TODO Auto-generated method stub
1690     return null;
1691   }
1692
1693   @Override
1694   public AlignmentAnnotation getAlignmentConservationAnnotation()
1695   {
1696     // TODO Auto-generated method stub
1697     return null;
1698   }
1699
1700   @Override
1701   public boolean isClosed()
1702   {
1703     System.err.println("Alignment is "+alignment==null ? "closed" : "open");
1704     return alignment==null;
1705   }
1706
1707   @Override
1708   public AlignCalcManagerI getCalcManager()
1709   {
1710     // TODO Auto-generated method stub
1711     return null;
1712   }
1713 }