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