optimize repaints
[jalview.git] / src / jalview / gui / AlignViewport.java
1  /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.util.*;
22
23 import java.awt.*;
24
25 import jalview.analysis.*;
26
27 import jalview.bin.*;
28
29 import jalview.datamodel.*;
30
31 import jalview.schemes.*;
32
33 /**
34  * DOCUMENT ME!
35  *
36  * @author $author$
37  * @version $Revision$
38  */
39 public class AlignViewport
40 {
41     int startRes;
42     int endRes;
43     int startSeq;
44     int endSeq;
45     boolean showJVSuffix = true;
46     boolean showText = true;
47     boolean showColourText = false;
48     boolean showBoxes = true;
49     boolean wrapAlignment = false;
50     boolean renderGaps = true;
51     boolean showSequenceFeatures = false;
52     boolean showAnnotation = true;
53     boolean colourAppliesToAllGroups = true;
54     ColourSchemeI globalColourScheme = null;
55     boolean conservationColourSelected = false;
56     boolean abovePIDThreshold = false;
57     SequenceGroup selectionGroup;
58     int charHeight;
59     int charWidth;
60     boolean validCharWidth;
61     int wrappedWidth;
62     Font font;
63     boolean seqNameItalics;
64     AlignmentI alignment;
65     ColumnSelection colSel = new ColumnSelection();
66     int threshold;
67     int increment;
68     NJTree currentTree = null;
69     boolean scaleAboveWrapped = false;
70     boolean scaleLeftWrapped = true;
71     boolean scaleRightWrapped = true;
72     boolean hasHiddenColumns = false;
73     boolean hasHiddenRows = false;
74     boolean showHiddenMarkers = true;
75
76     boolean cursorMode = false;
77
78     // The following vector holds the features which are
79     // currently visible, in the correct order or rendering
80     Hashtable featuresDisplayed = null;
81
82
83     /** DOCUMENT ME!! */
84     public Hashtable [] hconsensus;
85     AlignmentAnnotation consensus;
86     AlignmentAnnotation conservation;
87     AlignmentAnnotation quality;
88     boolean autoCalculateConsensus = true;
89
90     /** DOCUMENT ME!! */
91     public int ConsPercGaps = 25; // JBPNote : This should be a scalable property!
92
93     // JBPNote Prolly only need this in the applet version.
94   private java.beans.PropertyChangeSupport changeSupport = new java.beans.
95       PropertyChangeSupport(this);
96
97     boolean ignoreGapsInConsensusCalculation = false;
98
99     boolean isDataset = false;
100
101     boolean antiAlias = false;
102
103     boolean padGaps = false;
104
105     Rectangle explodedPosition;
106
107     String viewName;
108
109     String sequenceSetID;
110
111     boolean gatherViewsHere = false;
112
113     Stack historyList = new Stack();
114     Stack redoList = new Stack();
115
116     Hashtable sequenceColours;
117
118     int thresholdTextColour = 0;
119     Color textColour = Color.black;
120     Color textColour2 = Color.white;
121
122     boolean rightAlignIds = false;
123
124     Hashtable hiddenRepSequences;
125
126
127     /**
128      * Creates a new AlignViewport object.
129      *
130      * @param al DOCUMENT ME!
131      */
132     public AlignViewport(AlignmentI al)
133     {
134         setAlignment(al);
135         init();
136     }
137     /**
138      * Create a new AlignViewport with hidden regions
139      * @param al AlignmentI
140      * @param hiddenColumns ColumnSelection
141      */
142   public AlignViewport(AlignmentI al, ColumnSelection hiddenColumns)
143   {
144       setAlignment(al);
145     if (hiddenColumns != null)
146     {
147         this.colSel = hiddenColumns;
148         if (hiddenColumns.getHiddenColumns() != null)
149       {
150           hasHiddenColumns = true;
151       }
152     }
153       init();
154     }
155
156     void init()
157     {
158         this.startRes = 0;
159         this.endRes = alignment.getWidth() - 1;
160         this.startSeq = 0;
161         this.endSeq = alignment.getHeight() - 1;
162
163       antiAlias = Cache.getDefault("ANTI_ALIAS", false);
164
165       showJVSuffix = Cache.getDefault("SHOW_JVSUFFIX", true);
166       showAnnotation = Cache.getDefault("SHOW_ANNOTATIONS", true);
167
168       rightAlignIds = Cache.getDefault("RIGHT_ALIGN_IDS", false);
169
170       autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true);
171
172       padGaps = Cache.getDefault("PAD_GAPS", true);
173
174        String fontName = Cache.getDefault("FONT_NAME", "SansSerif");
175        String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + "") ;
176        String fontSize = Cache.getDefault("FONT_SIZE", "10");
177
178        seqNameItalics = Cache.getDefault("ID_ITALICS", true);
179
180        int style = 0;
181
182        if (fontStyle.equals("bold"))
183        {
184          style = 1;
185        }
186        else if (fontStyle.equals("italic"))
187        {
188          style = 2;
189        }
190
191        setFont(new Font(fontName, style, Integer.parseInt(fontSize)));
192
193        alignment.setGapCharacter( Cache.getDefault("GAP_SYMBOL", "-").charAt(0) );
194
195
196         // We must set conservation and consensus before setting colour,
197         // as Blosum and Clustal require this to be done
198         if(hconsensus==null && !isDataset)
199         {
200           if(!alignment.isNucleotide())
201           {
202             conservation = new AlignmentAnnotation("Conservation",
203                 "Conservation of total alignment less than " +
204                 ConsPercGaps + "% gaps",
205                 new Annotation[1], 0f,
206                 11f,
207                 AlignmentAnnotation.BAR_GRAPH);
208             conservation.hasText = true;
209             conservation.autoCalculated=true;
210
211
212             if (Cache.getDefault("SHOW_CONSERVATION", true))
213             {
214               alignment.addAnnotation(conservation);
215             }
216
217             if (Cache.getDefault("SHOW_QUALITY", true))
218             {
219               quality = new AlignmentAnnotation("Quality",
220                                                 "Alignment Quality based on Blosum62 scores",
221                                                 new Annotation[1],
222                                                 0f,
223                                                 11f,
224                                                 AlignmentAnnotation.BAR_GRAPH);
225               quality.hasText = true;
226               quality.autoCalculated=true;
227
228               alignment.addAnnotation(quality);
229             }
230           }
231
232           consensus = new AlignmentAnnotation("Consensus", "PID",
233                                                new Annotation[1], 0f, 100f,
234                                                AlignmentAnnotation.BAR_GRAPH);
235           consensus.hasText = true;
236           consensus.autoCalculated=true;
237
238            if (Cache.getDefault("SHOW_IDENTITY", true))
239            {
240              alignment.addAnnotation(consensus);
241            }
242         }
243
244         if (jalview.bin.Cache.getProperty("DEFAULT_COLOUR") != null)
245         {
246           globalColourScheme = ColourSchemeProperty.getColour(alignment,
247               jalview.bin.Cache.getProperty("DEFAULT_COLOUR"));
248
249             if (globalColourScheme instanceof UserColourScheme)
250             {
251                 globalColourScheme = UserDefinedColours.loadDefaultColours();
252         ( (UserColourScheme) globalColourScheme).setThreshold(0,
253             getIgnoreGapsConsensus());
254             }
255
256             if (globalColourScheme != null)
257             {
258                 globalColourScheme.setConsensus(hconsensus);
259             }
260         }
261
262         wrapAlignment = jalview.bin.Cache.getDefault("WRAP_ALIGNMENT", false);
263     }
264
265
266
267     /**
268      * DOCUMENT ME!
269      *
270      * @param b DOCUMENT ME!
271      */
272     public void setShowSequenceFeatures(boolean b)
273     {
274         showSequenceFeatures = b;
275     }
276
277     public boolean getShowSequenceFeatures()
278     {
279       return showSequenceFeatures;
280     }
281
282   class ConservationThread
283       extends Thread
284     {
285       AlignmentPanel ap;
286       public ConservationThread(AlignmentPanel ap)
287       {
288         this.ap = ap;
289       }
290
291       public void run()
292       {
293         try
294         {
295           updatingConservation = true;
296
297           while (UPDATING_CONSERVATION)
298           {
299             try
300             {
301               if (ap != null)
302               {
303                 ap.paintAlignment(false);
304               }
305               Thread.sleep(200);
306             }
307             catch (Exception ex)
308             {
309               ex.printStackTrace();
310             }
311           }
312
313           UPDATING_CONSERVATION = true;
314
315
316           int alWidth = alignment.getWidth();
317           if(alWidth<0)
318         {
319             return;
320         }
321
322           Conservation cons = new jalview.analysis.Conservation("All",
323               jalview.schemes.ResidueProperties.propHash, 3,
324               alignment.getSequences(), 0, alWidth -1);
325
326           cons.calculate();
327           cons.verdict(false, ConsPercGaps);
328
329           if (quality!=null)
330           {
331             cons.findQuality();
332           }
333
334           char [] sequence = cons.getConsSequence().getSequence();
335           float minR;
336           float minG;
337           float minB;
338           float maxR;
339           float maxG;
340           float maxB;
341           minR = 0.3f;
342           minG = 0.0f;
343           minB = 0f;
344           maxR = 1.0f - minR;
345           maxG = 0.9f - minG;
346           maxB = 0f - minB; // scalable range for colouring both Conservation and Quality
347
348           float min = 0f;
349           float max = 11f;
350           float qmin = 0f;
351           float qmax = 0f;
352
353           char c;
354
355           conservation.annotations = new Annotation[alWidth];
356
357           if (quality!=null)
358           {
359             quality.graphMax = cons.qualityRange[1].floatValue();
360             quality.annotations = new Annotation[alWidth];
361             qmin = cons.qualityRange[0].floatValue();
362             qmax = cons.qualityRange[1].floatValue();
363           }
364
365           for (int i = 0; i < alWidth; i++)
366           {
367             float value = 0;
368
369             c = sequence[i];
370
371             if (Character.isDigit(c))
372           {
373               value = (int) (c - '0');
374           }
375             else if (c == '*')
376           {
377               value = 11;
378           }
379             else if (c == '+')
380           {
381               value = 10;
382           }
383
384             float vprop = value - min;
385             vprop /= max;
386             conservation.annotations[i] =
387                 new Annotation(String.valueOf(c),
388                                String.valueOf(value), ' ', value,
389                                new Color(minR + (maxR * vprop),
390                                          minG + (maxG * vprop),
391                                          minB + (maxB * vprop)));
392
393             // Quality calc
394             if (quality!=null)
395             {
396               value = ( (Double) cons.quality.get(i)).floatValue();
397               vprop = value - qmin;
398               vprop /= qmax;
399             quality.annotations[i] = new Annotation(" ", String.valueOf(value),
400                 ' ',
401                                                value,
402                                                new Color(minR + (maxR * vprop),
403                   minG + (maxG * vprop),
404                   minB + (maxB * vprop)));
405             }
406           }
407         }
408         catch (OutOfMemoryError error)
409         {
410           javax.swing.SwingUtilities.invokeLater(new Runnable()
411           {
412
413
414             public void run()
415             {
416               javax.swing.JOptionPane.showInternalMessageDialog(Desktop.desktop,
417                   "Out of memory calculating conservation!!"
418                   +
419                   "\nSee help files for increasing Java Virtual Machine memory."
420                   , "Out of memory",
421                   javax.swing.JOptionPane.WARNING_MESSAGE);
422             }
423           });
424
425           conservation = null;
426           quality = null;
427
428           System.out.println("Conservation calculation: " + error);
429           System.gc();
430
431         }
432
433         UPDATING_CONSERVATION = false;
434         updatingConservation = false;
435
436         if(ap!=null)
437         {
438         ap.paintAlignment(true);
439         }
440
441       }
442     }
443
444
445     ConservationThread conservationThread;
446
447     ConsensusThread consensusThread;
448
449     boolean consUpdateNeeded = false;
450
451     static boolean UPDATING_CONSENSUS = false;
452
453     static boolean UPDATING_CONSERVATION = false;
454
455     boolean updatingConsensus = false;
456
457     boolean updatingConservation = false;
458
459     /**
460      * DOCUMENT ME!
461      */
462     public void updateConservation(final AlignmentPanel ap)
463     {
464       if (alignment.isNucleotide() || conservation==null)
465     {
466         return;
467     }
468
469       conservationThread = new ConservationThread(ap);
470       conservationThread.start();
471     }
472
473     /**
474      * DOCUMENT ME!
475      */
476     public void updateConsensus(final AlignmentPanel ap)
477     {
478       consensusThread = new ConsensusThread(ap);
479       consensusThread.start();
480     }
481
482   class ConsensusThread
483       extends Thread
484     {
485       AlignmentPanel ap;
486       public ConsensusThread(AlignmentPanel ap)
487       {
488         this.ap = ap;
489       }
490       public void run()
491       {
492         updatingConsensus = true;
493         while (UPDATING_CONSENSUS)
494         {
495           try
496           {
497             if (ap != null)
498             {
499             ap.paintAlignment(false);
500             }
501
502             Thread.sleep(200);
503           }
504           catch (Exception ex)
505           {
506             ex.printStackTrace();
507           }
508         }
509
510
511         UPDATING_CONSENSUS = true;
512
513         try
514         {
515           int aWidth = alignment.getWidth();
516           if(aWidth<0)
517         {
518             return;
519         }
520
521           consensus.annotations = null;
522           consensus.annotations = new Annotation[aWidth];
523
524
525           hconsensus = new Hashtable[aWidth];
526           AAFrequency.calculate(alignment.getSequencesArray(),
527                                 0,
528                                 alignment.getWidth(),
529                                 hconsensus);
530
531           for (int i = 0; i < aWidth; i++)
532           {
533             float value = 0;
534             if (ignoreGapsInConsensusCalculation)
535           {
536               value = ( (Float) hconsensus[i].get(AAFrequency.PID_NOGAPS)).
537                   floatValue();
538           }
539             else
540           {
541               value = ( (Float) hconsensus[i].get(AAFrequency.PID_GAPS)).
542                   floatValue();
543           }
544
545             String maxRes = hconsensus[i].get(AAFrequency.MAXRESIDUE).toString();
546             String mouseOver = hconsensus[i].get(AAFrequency.MAXRESIDUE) + " ";
547
548             if (maxRes.length() > 1)
549             {
550               mouseOver = "[" + maxRes + "] ";
551               maxRes = "+";
552             }
553
554             mouseOver += ( (int) value + "%");
555           consensus.annotations[i] = new Annotation(maxRes, mouseOver, ' ',
556               value);
557           }
558
559
560           if (globalColourScheme != null)
561         {
562             globalColourScheme.setConsensus(hconsensus);
563         }
564
565         }
566         catch (OutOfMemoryError error)
567         {
568           alignment.deleteAnnotation(consensus);
569
570           consensus = null;
571           hconsensus = null;
572           javax.swing.SwingUtilities.invokeLater(new Runnable()
573           {
574             public void run()
575             {
576               javax.swing.JOptionPane.showInternalMessageDialog(Desktop.desktop,
577                   "Out of memory calculating consensus!!"
578                   +
579                   "\nSee help files for increasing Java Virtual Machine memory."
580                   , "Out of memory",
581                   javax.swing.JOptionPane.WARNING_MESSAGE);
582             }
583           });
584
585           System.out.println("Consensus calculation: " + error);
586           System.gc();
587         }
588         UPDATING_CONSENSUS = false;
589         updatingConsensus = false;
590
591         if (ap != null)
592         {
593         ap.paintAlignment(true);
594         }
595       }
596     }
597     /**
598      * get the consensus sequence as displayed under the PID consensus annotation row.
599      * @return consensus sequence as a new sequence object
600      */
601   public SequenceI getConsensusSeq()
602   {
603       if (consensus==null)
604     {
605         updateConsensus(null);
606     }
607       if (consensus==null)
608     {
609         return null;
610     }
611       StringBuffer seqs=new StringBuffer();
612     for (int i = 0; i < consensus.annotations.length; i++)
613     {
614       if (consensus.annotations[i] != null)
615       {
616           if (consensus.annotations[i].description.charAt(0) == '[')
617         {
618             seqs.append(consensus.annotations[i].description.charAt(1));
619         }
620           else
621         {
622             seqs.append(consensus.annotations[i].displayCharacter);
623         }
624       }
625     }
626
627       SequenceI sq = new Sequence("Consensus", seqs.toString());
628     sq.setDescription("Percentage Identity Consensus " +
629                       ( (ignoreGapsInConsensusCalculation) ? " without gaps" :
630                        ""));
631       return sq;
632     }
633     /**
634      * DOCUMENT ME!
635      *
636      * @return DOCUMENT ME!
637      */
638     public SequenceGroup getSelectionGroup()
639     {
640         return selectionGroup;
641     }
642
643     /**
644      * DOCUMENT ME!
645      *
646      * @param sg DOCUMENT ME!
647      */
648     public void setSelectionGroup(SequenceGroup sg)
649     {
650         selectionGroup = sg;
651     }
652
653     /**
654      * DOCUMENT ME!
655      *
656      * @return DOCUMENT ME!
657      */
658     public boolean getConservationSelected()
659     {
660         return conservationColourSelected;
661     }
662
663     /**
664      * DOCUMENT ME!
665      *
666      * @param b DOCUMENT ME!
667      */
668     public void setConservationSelected(boolean b)
669     {
670         conservationColourSelected = b;
671     }
672
673     /**
674      * DOCUMENT ME!
675      *
676      * @return DOCUMENT ME!
677      */
678     public boolean getAbovePIDThreshold()
679     {
680         return abovePIDThreshold;
681     }
682
683     /**
684      * DOCUMENT ME!
685      *
686      * @param b DOCUMENT ME!
687      */
688     public void setAbovePIDThreshold(boolean b)
689     {
690         abovePIDThreshold = b;
691     }
692
693     /**
694      * DOCUMENT ME!
695      *
696      * @return DOCUMENT ME!
697      */
698     public int getStartRes()
699     {
700         return startRes;
701     }
702
703     /**
704      * DOCUMENT ME!
705      *
706      * @return DOCUMENT ME!
707      */
708     public int getEndRes()
709     {
710         return endRes;
711     }
712
713     /**
714      * DOCUMENT ME!
715      *
716      * @return DOCUMENT ME!
717      */
718     public int getStartSeq()
719     {
720         return startSeq;
721     }
722
723     /**
724      * DOCUMENT ME!
725      *
726      * @param cs DOCUMENT ME!
727      */
728     public void setGlobalColourScheme(ColourSchemeI cs)
729     {
730         globalColourScheme = cs;
731     }
732
733     /**
734      * DOCUMENT ME!
735      *
736      * @return DOCUMENT ME!
737      */
738     public ColourSchemeI getGlobalColourScheme()
739     {
740         return globalColourScheme;
741     }
742
743     /**
744      * DOCUMENT ME!
745      *
746      * @param res DOCUMENT ME!
747      */
748     public void setStartRes(int res)
749     {
750         this.startRes = res;
751     }
752
753     /**
754      * DOCUMENT ME!
755      *
756      * @param seq DOCUMENT ME!
757      */
758     public void setStartSeq(int seq)
759     {
760         this.startSeq = seq;
761     }
762
763     /**
764      * DOCUMENT ME!
765      *
766      * @param res DOCUMENT ME!
767      */
768     public void setEndRes(int res)
769     {
770         if (res > (alignment.getWidth() - 1))
771         {
772             // log.System.out.println(" Corrected res from " + res + " to maximum " + (alignment.getWidth()-1));
773             res = alignment.getWidth() - 1;
774         }
775
776         if (res < 0)
777         {
778             res = 0;
779         }
780
781         this.endRes = res;
782     }
783
784     /**
785      * DOCUMENT ME!
786      *
787      * @param seq DOCUMENT ME!
788      */
789     public void setEndSeq(int seq)
790     {
791         if (seq > alignment.getHeight())
792         {
793             seq = alignment.getHeight();
794         }
795
796         if (seq < 0)
797         {
798             seq = 0;
799         }
800
801         this.endSeq = seq;
802     }
803
804     /**
805      * DOCUMENT ME!
806      *
807      * @return DOCUMENT ME!
808      */
809     public int getEndSeq()
810     {
811         return endSeq;
812     }
813
814     /**
815      * DOCUMENT ME!
816      *
817      * @param f DOCUMENT ME!
818      */
819     public void setFont(Font f)
820     {
821         font = f;
822
823         Container c = new Container();
824
825         java.awt.FontMetrics fm = c.getFontMetrics(font);
826         setCharHeight(fm.getHeight());
827         setCharWidth(fm.charWidth('M'));
828         validCharWidth = true;
829     }
830
831     /**
832      * DOCUMENT ME!
833      *
834      * @return DOCUMENT ME!
835      */
836     public Font getFont()
837     {
838         return font;
839     }
840
841     /**
842      * DOCUMENT ME!
843      *
844      * @param w DOCUMENT ME!
845      */
846     public void setCharWidth(int w)
847     {
848         this.charWidth = w;
849     }
850
851     /**
852      * DOCUMENT ME!
853      *
854      * @return DOCUMENT ME!
855      */
856     public int getCharWidth()
857     {
858         return charWidth;
859     }
860
861     /**
862      * DOCUMENT ME!
863      *
864      * @param h DOCUMENT ME!
865      */
866     public void setCharHeight(int h)
867     {
868         this.charHeight = h;
869     }
870
871     /**
872      * DOCUMENT ME!
873      *
874      * @return DOCUMENT ME!
875      */
876     public int getCharHeight()
877     {
878         return charHeight;
879     }
880
881     /**
882      * DOCUMENT ME!
883      *
884      * @param w DOCUMENT ME!
885      */
886     public void setWrappedWidth(int w)
887     {
888         this.wrappedWidth = w;
889     }
890
891     /**
892      * DOCUMENT ME!
893      *
894      * @return DOCUMENT ME!
895      */
896     public int getWrappedWidth()
897     {
898         return wrappedWidth;
899     }
900
901
902     /**
903      * DOCUMENT ME!
904      *
905      * @return DOCUMENT ME!
906      */
907     public AlignmentI getAlignment()
908     {
909         return alignment;
910     }
911
912     /**
913      * DOCUMENT ME!
914      *
915      * @param align DOCUMENT ME!
916      */
917     public void setAlignment(AlignmentI align)
918     {
919         this.alignment = align;
920     }
921
922     /**
923      * DOCUMENT ME!
924      *
925      * @param state DOCUMENT ME!
926      */
927     public void setWrapAlignment(boolean state)
928     {
929         wrapAlignment = state;
930     }
931
932     /**
933      * DOCUMENT ME!
934      *
935      * @param state DOCUMENT ME!
936      */
937     public void setShowText(boolean state)
938     {
939         showText = state;
940     }
941
942     /**
943      * DOCUMENT ME!
944      *
945      * @param state DOCUMENT ME!
946      */
947     public void setRenderGaps(boolean state)
948     {
949         renderGaps = state;
950     }
951
952     /**
953      * DOCUMENT ME!
954      *
955      * @return DOCUMENT ME!
956      */
957     public boolean getColourText()
958     {
959         return showColourText;
960     }
961
962     /**
963      * DOCUMENT ME!
964      *
965      * @param state DOCUMENT ME!
966      */
967     public void setColourText(boolean state)
968     {
969         showColourText = state;
970     }
971
972     /**
973      * DOCUMENT ME!
974      *
975      * @param state DOCUMENT ME!
976      */
977     public void setShowBoxes(boolean state)
978     {
979         showBoxes = state;
980     }
981
982     /**
983      * DOCUMENT ME!
984      *
985      * @return DOCUMENT ME!
986      */
987     public boolean getWrapAlignment()
988     {
989         return wrapAlignment;
990     }
991
992     /**
993      * DOCUMENT ME!
994      *
995      * @return DOCUMENT ME!
996      */
997     public boolean getShowText()
998     {
999         return showText;
1000     }
1001
1002     /**
1003      * DOCUMENT ME!
1004      *
1005      * @return DOCUMENT ME!
1006      */
1007     public boolean getShowBoxes()
1008     {
1009         return showBoxes;
1010     }
1011
1012     /**
1013      * DOCUMENT ME!
1014      *
1015      * @return DOCUMENT ME!
1016      */
1017     public char getGapCharacter()
1018     {
1019         return getAlignment().getGapCharacter();
1020     }
1021
1022     /**
1023      * DOCUMENT ME!
1024      *
1025      * @param gap DOCUMENT ME!
1026      */
1027     public void setGapCharacter(char gap)
1028     {
1029         if (getAlignment() != null)
1030         {
1031             getAlignment().setGapCharacter(gap);
1032         }
1033     }
1034
1035     /**
1036      * DOCUMENT ME!
1037      *
1038      * @param thresh DOCUMENT ME!
1039      */
1040     public void setThreshold(int thresh)
1041     {
1042         threshold = thresh;
1043     }
1044
1045     /**
1046      * DOCUMENT ME!
1047      *
1048      * @return DOCUMENT ME!
1049      */
1050     public int getThreshold()
1051     {
1052         return threshold;
1053     }
1054
1055     /**
1056      * DOCUMENT ME!
1057      *
1058      * @param inc DOCUMENT ME!
1059      */
1060     public void setIncrement(int inc)
1061     {
1062         increment = inc;
1063     }
1064
1065     /**
1066      * DOCUMENT ME!
1067      *
1068      * @return DOCUMENT ME!
1069      */
1070     public int getIncrement()
1071     {
1072         return increment;
1073     }
1074
1075
1076     /**
1077      * DOCUMENT ME!
1078      *
1079      * @return DOCUMENT ME!
1080      */
1081     public ColumnSelection getColumnSelection()
1082     {
1083         return colSel;
1084     }
1085
1086
1087     /**
1088      * DOCUMENT ME!
1089      *
1090      * @param tree DOCUMENT ME!
1091      */
1092     public void setCurrentTree(NJTree tree)
1093     {
1094         currentTree = tree;
1095     }
1096
1097     /**
1098      * DOCUMENT ME!
1099      *
1100      * @return DOCUMENT ME!
1101      */
1102     public NJTree getCurrentTree()
1103     {
1104         return currentTree;
1105     }
1106
1107     /**
1108      * DOCUMENT ME!
1109      *
1110      * @param b DOCUMENT ME!
1111      */
1112     public void setColourAppliesToAllGroups(boolean b)
1113     {
1114         colourAppliesToAllGroups = b;
1115     }
1116
1117     /**
1118      * DOCUMENT ME!
1119      *
1120      * @return DOCUMENT ME!
1121      */
1122     public boolean getColourAppliesToAllGroups()
1123     {
1124         return colourAppliesToAllGroups;
1125     }
1126
1127     /**
1128      * DOCUMENT ME!
1129      *
1130      * @return DOCUMENT ME!
1131      */
1132     public boolean getShowJVSuffix()
1133     {
1134         return showJVSuffix;
1135     }
1136
1137     /**
1138      * DOCUMENT ME!
1139      *
1140      * @param b DOCUMENT ME!
1141      */
1142     public void setShowJVSuffix(boolean b)
1143     {
1144         showJVSuffix = b;
1145     }
1146
1147
1148     /**
1149      * DOCUMENT ME!
1150      *
1151      * @return DOCUMENT ME!
1152      */
1153     public boolean getShowAnnotation()
1154     {
1155         return showAnnotation;
1156     }
1157
1158     /**
1159      * DOCUMENT ME!
1160      *
1161      * @param b DOCUMENT ME!
1162      */
1163     public void setShowAnnotation(boolean b)
1164     {
1165         showAnnotation = b;
1166     }
1167
1168     /**
1169      * DOCUMENT ME!
1170      *
1171      * @return DOCUMENT ME!
1172      */
1173     public boolean getScaleAboveWrapped()
1174     {
1175         return scaleAboveWrapped;
1176     }
1177
1178     /**
1179      * DOCUMENT ME!
1180      *
1181      * @return DOCUMENT ME!
1182      */
1183     public boolean getScaleLeftWrapped()
1184     {
1185         return scaleLeftWrapped;
1186     }
1187
1188     /**
1189      * DOCUMENT ME!
1190      *
1191      * @return DOCUMENT ME!
1192      */
1193     public boolean getScaleRightWrapped()
1194     {
1195         return scaleRightWrapped;
1196     }
1197
1198     /**
1199      * DOCUMENT ME!
1200      *
1201      * @param b DOCUMENT ME!
1202      */
1203     public void setScaleAboveWrapped(boolean b)
1204     {
1205         scaleAboveWrapped = b;
1206     }
1207
1208     /**
1209      * DOCUMENT ME!
1210      *
1211      * @param b DOCUMENT ME!
1212      */
1213     public void setScaleLeftWrapped(boolean b)
1214     {
1215         scaleLeftWrapped = b;
1216     }
1217
1218     /**
1219      * DOCUMENT ME!
1220      *
1221      * @param b DOCUMENT ME!
1222      */
1223     public void setScaleRightWrapped(boolean b)
1224     {
1225         scaleRightWrapped = b;
1226     }
1227
1228     /**
1229      * Property change listener for changes in alignment
1230      *
1231      * @param listener DOCUMENT ME!
1232      */
1233     public void addPropertyChangeListener(
1234         java.beans.PropertyChangeListener listener)
1235     {
1236         changeSupport.addPropertyChangeListener(listener);
1237     }
1238
1239     /**
1240      * DOCUMENT ME!
1241      *
1242      * @param listener DOCUMENT ME!
1243      */
1244     public void removePropertyChangeListener(
1245         java.beans.PropertyChangeListener listener)
1246     {
1247         changeSupport.removePropertyChangeListener(listener);
1248     }
1249
1250     /**
1251      * Property change listener for changes in alignment
1252      *
1253      * @param prop DOCUMENT ME!
1254      * @param oldvalue DOCUMENT ME!
1255      * @param newvalue DOCUMENT ME!
1256      */
1257     public void firePropertyChange(String prop, Object oldvalue, Object newvalue)
1258     {
1259         changeSupport.firePropertyChange(prop, oldvalue, newvalue);
1260     }
1261
1262     public void setIgnoreGapsConsensus(boolean b, AlignmentPanel ap)
1263     {
1264       ignoreGapsInConsensusCalculation = b;
1265       updateConsensus(ap);
1266       if(globalColourScheme!=null)
1267       {
1268       globalColourScheme.setThreshold(globalColourScheme.getThreshold(),
1269                                       ignoreGapsInConsensusCalculation);
1270       }
1271     }
1272
1273     public boolean getIgnoreGapsConsensus()
1274     {
1275      return ignoreGapsInConsensusCalculation;
1276     }
1277
1278     public void setDataset(boolean b)
1279     {
1280       isDataset = b;
1281     }
1282
1283     public boolean isDataset()
1284     {
1285       return isDataset;
1286     }
1287
1288
1289     public void hideSelectedColumns()
1290     {
1291       if (colSel.size() < 1)
1292     {
1293         return;
1294     }
1295
1296       colSel.hideSelectedColumns();
1297       setSelectionGroup(null);
1298
1299       hasHiddenColumns = true;
1300     }
1301
1302
1303     public void hideColumns(int start, int end)
1304     {
1305       if(start==end)
1306     {
1307         colSel.hideColumns(start);
1308     }
1309       else
1310     {
1311         colSel.hideColumns(start, end);
1312     }
1313
1314       hasHiddenColumns = true;
1315     }
1316
1317     public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
1318     {
1319       int sSize = sg.getSize();
1320       if(sSize < 2)
1321     {
1322         return;
1323     }
1324
1325       if(hiddenRepSequences==null)
1326     {
1327         hiddenRepSequences = new Hashtable();
1328     }
1329
1330        hiddenRepSequences.put(repSequence, sg);
1331
1332       //Hide all sequences except the repSequence
1333       SequenceI [] seqs = new SequenceI[sSize-1];
1334       int index = 0;
1335       for(int i=0; i<sSize; i++)
1336     {
1337         if(sg.getSequenceAt(i)!=repSequence)
1338         {
1339           if(index==sSize-1)
1340         {
1341             return;
1342         }
1343
1344           seqs[index++] = sg.getSequenceAt(i);
1345         }
1346     }
1347
1348       hideSequence(seqs);
1349
1350     }
1351
1352     public void hideAllSelectedSeqs()
1353     {
1354     if (selectionGroup == null || selectionGroup.getSize()<1)
1355     {
1356         return;
1357     }
1358
1359       SequenceI[] seqs = selectionGroup.getSequencesInOrder(alignment);
1360
1361       hideSequence(seqs);
1362
1363       setSelectionGroup(null);
1364     }
1365
1366     public void hideSequence(SequenceI [] seq)
1367     {
1368       if(seq!=null)
1369       {
1370         for (int i = 0; i < seq.length; i++)
1371         {
1372           alignment.getHiddenSequences().hideSequence(seq[i]);
1373         }
1374         hasHiddenRows = true;
1375         firePropertyChange("alignment", null, alignment.getSequences());
1376       }
1377     }
1378
1379     public void showSequence(int index)
1380     {
1381       Vector tmp = alignment.getHiddenSequences().showSequence(index
1382           , hiddenRepSequences);
1383       if(tmp.size()>0)
1384       {
1385         if(selectionGroup==null)
1386         {
1387           selectionGroup = new SequenceGroup();
1388           selectionGroup.setEndRes(alignment.getWidth()-1);
1389         }
1390
1391         for (int t = 0; t < tmp.size(); t++)
1392         {
1393           selectionGroup.addSequence(
1394               (SequenceI) tmp.elementAt(t), false
1395               );
1396         }
1397         firePropertyChange("alignment", null, alignment.getSequences());
1398       }
1399
1400       if(alignment.getHiddenSequences().getSize()<1)
1401     {
1402         hasHiddenRows = false;
1403     }
1404   }
1405
1406     public void showColumn(int col)
1407     {
1408       colSel.revealHiddenColumns(col);
1409       if(colSel.getHiddenColumns()==null)
1410     {
1411         hasHiddenColumns = false;
1412     }
1413   }
1414
1415     public void showAllHiddenColumns()
1416     {
1417       colSel.revealAllHiddenColumns();
1418       hasHiddenColumns = false;
1419     }
1420
1421     public void showAllHiddenSeqs()
1422     {
1423       if(alignment.getHiddenSequences().getSize()>0)
1424       {
1425         if(selectionGroup==null)
1426         {
1427           selectionGroup = new SequenceGroup();
1428           selectionGroup.setEndRes(alignment.getWidth()-1);
1429         }
1430         Vector tmp = alignment.getHiddenSequences().showAll(hiddenRepSequences);
1431         for(int t=0; t<tmp.size(); t++)
1432         {
1433           selectionGroup.addSequence(
1434               (SequenceI)tmp.elementAt(t), false
1435               );
1436         }
1437         firePropertyChange("alignment", null, alignment.getSequences());
1438         hasHiddenRows = false;
1439         hiddenRepSequences = null;
1440       }
1441     }
1442
1443
1444
1445     public void invertColumnSelection()
1446     {
1447       for(int i=0; i<alignment.getWidth(); i++)
1448       {
1449         if(colSel.contains(i))
1450       {
1451           colSel.removeElement(i);
1452       }
1453         else
1454         {
1455           if (!hasHiddenColumns || colSel.isVisible(i))
1456           {
1457             colSel.addElement(i);
1458           }
1459         }
1460       }
1461     }
1462
1463     public int adjustForHiddenSeqs(int alignmentIndex)
1464     {
1465       return alignment.getHiddenSequences().adjustForHiddenSeqs(alignmentIndex);
1466     }
1467
1468     /**
1469      * This method returns an array of new SequenceI objects
1470      * derived from the whole alignment or just the current
1471      * selection with start and end points adjusted
1472      * @note if you need references to the actual SequenceI objects in the alignment or currently selected then use getSequenceSelection()
1473      * @return String[]
1474      */
1475     public SequenceI[] getSelectionAsNewSequence()
1476     {
1477       SequenceI[] sequences;
1478
1479       if (selectionGroup == null)
1480     {
1481         sequences = alignment.getSequencesArray();
1482         AlignmentAnnotation[] annots = alignment.getAlignmentAnnotation();
1483         for (int i=0; i<sequences.length; i++)
1484         {
1485           sequences[i] = new Sequence(sequences[i], annots); // construct new sequence with subset of visible annotation
1486         }
1487     }
1488       else
1489     {
1490         sequences = selectionGroup.getSelectionAsNewSequences(alignment);
1491     }
1492
1493       return sequences;
1494     }
1495     /**
1496      * get the currently selected sequence objects or all the sequences in the alignment.
1497      * @return array of references to sequence objects
1498      */
1499     public SequenceI[] getSequenceSelection()
1500     {
1501       SequenceI[] sequences;
1502       if (selectionGroup==null)
1503       {
1504         sequences = alignment.getSequencesArray();
1505       }
1506       else
1507       {
1508         sequences = selectionGroup.getSequencesInOrder(alignment);
1509       }
1510       return sequences;
1511     }
1512     /**
1513      * This method returns the visible alignment as text, as
1514      * seen on the GUI, ie if columns are hidden they will not
1515      * be returned in the result.
1516      * Use this for calculating trees, PCA, redundancy etc on views
1517      * which contain hidden columns.
1518      * @return String[]
1519      */
1520   public jalview.datamodel.CigarArray getViewAsCigars(boolean
1521       selectedRegionOnly)
1522     {
1523       CigarArray selection=null;
1524       SequenceI [] seqs= null;
1525       int i, iSize;
1526       int start = 0, end = 0;
1527       if(selectedRegionOnly && selectionGroup!=null)
1528       {
1529         iSize = selectionGroup.getSize();
1530         seqs = selectionGroup.getSequencesInOrder(alignment);
1531         start = selectionGroup.getStartRes();
1532         end = selectionGroup.getEndRes(); // inclusive for start and end in SeqCigar constructor
1533       }
1534       else
1535       {
1536         iSize = alignment.getHeight();
1537         seqs = alignment.getSequencesArray();
1538         end = alignment.getWidth()-1;
1539       }
1540       SeqCigar[] selseqs = new SeqCigar[iSize];
1541       for(i=0; i<iSize; i++)
1542       {
1543         selseqs[i] = new SeqCigar(seqs[i], start, end);
1544       }
1545       selection=new CigarArray(selseqs);
1546       // now construct the CigarArray operations
1547     if (hasHiddenColumns)
1548     {
1549         Vector regions = colSel.getHiddenColumns();
1550         int [] region;
1551         int hideStart, hideEnd;
1552         int last=start;
1553         for (int j = 0; last<end & j < regions.size(); j++)
1554         {
1555           region = (int[]) regions.elementAt(j);
1556           hideStart = region[0];
1557           hideEnd = region[1];
1558           // edit hidden regions to selection range
1559         if (hideStart < last)
1560         {
1561             if (hideEnd > last)
1562             {
1563               hideStart = last;
1564           }
1565           else
1566           {
1567               continue;
1568           }
1569         }
1570
1571           if (hideStart>end)
1572         {
1573             break;
1574         }
1575
1576           if (hideEnd>end)
1577         {
1578             hideEnd=end;
1579         }
1580
1581           if (hideStart>hideEnd)
1582         {
1583             break;
1584         }
1585           /**
1586            * form operations...
1587            */
1588           if (last<hideStart)
1589         {
1590             selection.addOperation(CigarArray.M, hideStart-last);
1591         }
1592           selection.addOperation(CigarArray.D, 1+hideEnd-hideStart);
1593           last = hideEnd+1;
1594         }
1595         // Final match if necessary.
1596         if (last<end)
1597       {
1598           selection.addOperation(CigarArray.M, end-last+1);
1599       }
1600     }
1601     else
1602     {
1603         selection.addOperation(CigarArray.M, end-start+1);
1604       }
1605       return selection;
1606     }
1607     /**
1608      * return a compact representation of the current alignment selection to
1609      * pass to an analysis function
1610      * @param selectedOnly boolean true to just return the selected view
1611      * @return AlignmentView
1612      */
1613   jalview.datamodel.AlignmentView getAlignmentView(boolean selectedOnly)
1614   {
1615       // JBPNote:
1616       // this is here because the AlignmentView constructor modifies the CigarArray
1617       // object. Refactoring of Cigar and alignment view representation should
1618       // be done to remove redundancy.
1619       CigarArray aligview = getViewAsCigars(selectedOnly);
1620     if (aligview != null)
1621     {
1622         return new AlignmentView(aligview,
1623                                (selectedOnly && selectionGroup != null) ?
1624                                selectionGroup.getStartRes() : 0);
1625       }
1626       return null;
1627     }
1628     /**
1629      * This method returns the visible alignment as text, as
1630      * seen on the GUI, ie if columns are hidden they will not
1631      * be returned in the result.
1632      * Use this for calculating trees, PCA, redundancy etc on views
1633      * which contain hidden columns.
1634      * @return String[]
1635      */
1636     public String [] getViewAsString(boolean selectedRegionOnly)
1637     {
1638       String [] selection = null;
1639       SequenceI [] seqs= null;
1640       int i, iSize;
1641       int start = 0, end = 0;
1642       if(selectedRegionOnly && selectionGroup!=null)
1643       {
1644         iSize = selectionGroup.getSize();
1645         seqs = selectionGroup.getSequencesInOrder(alignment);
1646         start = selectionGroup.getStartRes();
1647         end = selectionGroup.getEndRes()+1;
1648       }
1649       else
1650       {
1651         iSize = alignment.getHeight();
1652         seqs = alignment.getSequencesArray();
1653         end = alignment.getWidth();
1654       }
1655
1656       selection = new String[iSize];
1657     if (hasHiddenColumns)
1658     {
1659         selection = colSel.getVisibleSequenceStrings(start, end, seqs);
1660     }
1661     else
1662     {
1663         for(i=0; i<iSize; i++)
1664         {
1665           selection[i] = seqs[i].getSequenceAsString(start, end);
1666         }
1667
1668       }
1669       return selection;
1670     }
1671
1672   public int [][] getVisibleRegionBoundaries(int min, int max)
1673   {
1674     Vector regions = new Vector();
1675     int start = min;
1676     int end = max;
1677
1678     do
1679     {
1680       if (hasHiddenColumns)
1681       {
1682         if (start == 0)
1683         {
1684           start = colSel.adjustForHiddenColumns(start);
1685         }
1686
1687         end = colSel.getHiddenBoundaryRight(start);
1688         if (start == end)
1689         {
1690           end = max;
1691         }
1692         if (end > max)
1693         {
1694           end = max;
1695         }
1696       }
1697
1698       regions.addElement(new int[]
1699                          {start, end});
1700
1701       if (hasHiddenColumns)
1702       {
1703         start = colSel.adjustForHiddenColumns(end);
1704         start = colSel.getHiddenBoundaryLeft(start) + 1;
1705       }
1706     }
1707     while (end < max);
1708
1709     int[][] startEnd = new int[regions.size()][2];
1710
1711     regions.copyInto(startEnd);
1712
1713     return startEnd;
1714
1715   }
1716
1717     public boolean getShowHiddenMarkers()
1718     {
1719       return showHiddenMarkers;
1720     }
1721
1722     public void setShowHiddenMarkers(boolean show)
1723     {
1724       showHiddenMarkers = show;
1725     }
1726
1727     public String getSequenceSetId()
1728     {
1729       if(sequenceSetID==null)
1730     {
1731         sequenceSetID =  alignment.hashCode()+"";
1732     }
1733
1734       return sequenceSetID;
1735     }
1736
1737     public void alignmentChanged(AlignmentPanel ap)
1738     {
1739         if (padGaps)
1740     {
1741           alignment.padGaps();
1742     }
1743
1744         if (hconsensus != null && autoCalculateConsensus)
1745         {
1746           updateConsensus(ap);
1747           updateConservation(ap);
1748         }
1749
1750         //Reset endRes of groups if beyond alignment width
1751         int alWidth = alignment.getWidth();
1752         Vector groups = alignment.getGroups();
1753         if(groups!=null)
1754         {
1755           for(int i=0; i<groups.size(); i++)
1756           {
1757             SequenceGroup sg = (SequenceGroup)groups.elementAt(i);
1758             if(sg.getEndRes()>alWidth)
1759         {
1760               sg.setEndRes(alWidth-1);
1761           }
1762         }
1763     }
1764
1765         if(selectionGroup!=null && selectionGroup.getEndRes()>alWidth)
1766     {
1767           selectionGroup.setEndRes(alWidth-1);
1768     }
1769
1770         resetAllColourSchemes();
1771
1772        // alignment.adjustSequenceAnnotations();
1773     }
1774
1775
1776     void resetAllColourSchemes()
1777     {
1778       ColourSchemeI cs = globalColourScheme;
1779       if(cs!=null)
1780       {
1781         if (cs instanceof ClustalxColourScheme)
1782         {
1783           ( (ClustalxColourScheme) cs).
1784               resetClustalX(alignment.getSequences(),
1785                             alignment.getWidth());
1786         }
1787
1788         cs.setConsensus(hconsensus);
1789         if (cs.conservationApplied())
1790         {
1791           Alignment al = (Alignment) alignment;
1792           Conservation c = new Conservation("All",
1793                                             ResidueProperties.propHash, 3,
1794                                             al.getSequences(), 0,
1795                                             al.getWidth() - 1);
1796           c.calculate();
1797           c.verdict(false, ConsPercGaps);
1798
1799           cs.setConservation(c);
1800         }
1801       }
1802
1803       int s, sSize = alignment.getGroups().size();
1804       for(s=0; s<sSize; s++)
1805       {
1806         SequenceGroup sg = (SequenceGroup)alignment.getGroups().elementAt(s);
1807         if(sg.cs!=null && sg.cs instanceof ClustalxColourScheme)
1808         {
1809           ((ClustalxColourScheme)sg.cs).resetClustalX(
1810               sg.getSequences(hiddenRepSequences), sg.getWidth());
1811         }
1812         sg.recalcConservation();
1813       }
1814     }
1815
1816
1817     public Color getSequenceColour(SequenceI seq)
1818     {
1819       if(sequenceColours==null || !sequenceColours.containsKey(seq))
1820     {
1821         return Color.white;
1822     }
1823       else
1824     {
1825         return (Color)sequenceColours.get(seq);
1826     }
1827   }
1828
1829     public void setSequenceColour(SequenceI seq, Color col)
1830     {
1831       if(sequenceColours==null)
1832     {
1833         sequenceColours = new Hashtable();
1834     }
1835
1836       if(col == null)
1837     {
1838         sequenceColours.remove(seq);
1839     }
1840       else
1841     {
1842         sequenceColours.put(seq, col);
1843     }
1844     }
1845     /**
1846      * returns the visible column regions of the alignment
1847      * @param selectedRegionOnly true to just return the contigs intersecting with the selected area
1848      * @return
1849      */
1850     public int[] getViewAsVisibleContigs(boolean selectedRegionOnly) {
1851       int[] viscontigs=null;
1852       int start = 0, end = 0;
1853       if(selectedRegionOnly && selectionGroup!=null)
1854       {
1855         start = selectionGroup.getStartRes();
1856         end = selectionGroup.getEndRes()+1;
1857       }
1858       else
1859       {
1860         end = alignment.getWidth();
1861       }
1862       viscontigs = colSel.getVisibleContigs(start, end);
1863       return viscontigs;
1864     }
1865
1866
1867 }