JAL-2388 Preliminary refactor check-in, does not compile
[jalview.git] / src / jalview / viewmodel / ViewportPositionProps.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.viewmodel;
22
23 import jalview.api.ViewStyleI;
24 import jalview.datamodel.AlignmentAnnotation;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.ColumnSelection;
27 import jalview.datamodel.SequenceCollectionI;
28 import jalview.datamodel.SequenceGroup;
29 import jalview.datamodel.SequenceI;
30
31 import java.util.ArrayList;
32 import java.util.Hashtable;
33 import java.util.List;
34 import java.util.Map;
35
36 /**
37  * Supplies and updates viewport properties relating to position such as: start
38  * and end residues and sequences, hidden column/row adjustments, ratio of
39  * viewport to alignment etc
40  */
41 public class ViewportPositionProps extends ViewportProperties
42 {
43   // start residue of viewport
44   private int startRes;
45
46   // end residue of viewport
47   private int endRes;
48
49   // start sequence of viewport
50   private int startSeq;
51
52   // end sequence of viewport
53   private int endSeq;
54
55   // character height
56   private int charHeight;
57
58   // character width
59   private int charWidth;
60
61   // alignment
62   private AlignmentI al;
63
64   // viewstyle
65   private ViewStyleI viewstyle;
66
67   // hidden column data
68   protected ColumnSelection colSel = new ColumnSelection();
69
70   private long colselhash = -1;
71
72   // hidden sequence data
73   private Map<SequenceI, SequenceCollectionI> hiddenRepSequences;
74
75   /**
76    * Constructor
77    * @param alignment TODO
78    */
79   public ViewportPositionProps(AlignmentI alignment, ViewStyleI vstyle)
80   {
81     // initial values of viewport settings
82     this.startRes = 0;
83     this.endRes = alignment.getWidth() - 1;
84     this.startSeq = 0;
85     this.endSeq = alignment.getHeight() - 1;
86     this.al = alignment;
87     this.viewstyle = vstyle;
88   }
89
90   public void setCharHeight(int h)
91   {
92     viewstyle.setCharHeight(h);
93   }
94
95   public void setCharWidth(int w)
96   {
97     viewstyle.setCharWidth(w);
98   }
99
100   // ways to update values
101
102   // ways to notify of changes
103
104   // ways to supply positional information
105
106   /**
107    * Get alignment width
108    */
109   public int getAlignmentWidthInCols()
110   {
111     return al.getWidth();
112   }
113
114   /**
115    * Get alignment height
116    */
117   public int getAlignmentHeightInRows()
118   {
119     return al.getHeight();
120   }
121
122   public void setStartRes(int res)
123   {
124     if (res > al.getWidth() - 1)
125     {
126       res = al.getWidth() - 1;
127     }
128     else if (res < 0)
129     {
130       res = 0;
131     }
132     this.startRes = res;
133   }
134
135   public void setEndRes(int res)
136   {
137     if (res > al.getWidth() - 1)
138     {
139       res = al.getWidth() - 1;
140     }
141     else if (res < 0)
142     {
143       res = 0;
144     }
145     this.endRes = res;
146   }
147
148   public void setStartSeq(int seq)
149   {
150     if (seq > al.getHeight())
151     {
152       seq = al.getHeight();
153     }
154     else if (seq < 0)
155     {
156       seq = 0;
157     }
158     this.startSeq = seq;
159   }
160
161   public void setEndSeq(int seq)
162   {
163     if (seq > al.getHeight())
164     {
165       seq = al.getHeight();
166     }
167     else if (seq < 0)
168     {
169       seq = 0;
170     }
171     this.endSeq = seq;
172   }
173
174   /**
175    * Get start residue of viewport
176    */
177   public int getStartRes()
178   {
179     return startRes;
180   }
181
182   /**
183    * Get end residue of viewport
184    */
185   public int getEndRes()
186   {
187     return endRes;
188   }
189
190   /**
191    * Get start sequence of viewport
192    */
193   public int getStartSeq()
194   {
195     return startSeq;
196   }
197
198   /**
199    * Get end sequence of viewport
200    */
201   public int getEndSeq()
202   {
203     return endSeq;
204   }
205   /**
206    * Get start residue of viewport
207    */
208   public int getStartRes(boolean countHidden)
209   {
210     if (countHidden)
211     {
212       return 0; // av.getColumnSelection().adjustForHiddenColumns(startRes);
213     }
214     else
215     {
216       return startRes;
217     }
218   }
219
220   /**
221    * Convert distance x in viewport pixels to a distance in number of residues
222    * 
223    * @param x
224    *          number of pixels
225    * @return number of residues
226    */
227   public int convertPixelsToResidues(int x)
228   {
229     return Math.round((float) x / viewstyle.getCharWidth());
230   }
231
232   /**
233    * Convert distance y in viewport pixels to a distance in number of sequences
234    * 
235    * @param y
236    *          number of pixels
237    * @return number of sequences
238    */
239   public int convertPixelsToSequences(int y)
240   {
241     return Math.round((float) y / viewstyle.getCharHeight());
242   }
243   
244   /**
245    * Convert number of sequences s to a height in viewport pixels
246    * 
247    * @param s
248    *          number of sequences
249    * @return number of pixels
250    */
251   public int convertSequencesToPixels(int s)
252   {
253     return (s * viewstyle.getCharHeight());
254   }
255
256   /**
257    * Convert number of residues r to a width in viewport pixels
258    * 
259    * @param r
260    *          number of residues
261    * @return number of pixels
262    */
263   public int convertResiduesToPixels(int r)
264   {
265     return (r * viewstyle.getCharWidth());
266   }
267
268   public void setHiddenColumns(ColumnSelection colsel)
269   {
270     this.colSel = colsel;
271   }
272
273   public ColumnSelection getColumnSelection()
274   {
275     return colSel;
276   }
277
278   public void setColumnSelection(ColumnSelection colSel)
279   {
280     this.colSel = colSel;
281     if (colSel != null)
282     {
283       // updateHiddenColumns(); - does nothing
284     }
285     isColSelChanged(true);
286   }
287
288   public boolean hasHiddenColumns()
289   {
290     return colSel != null && colSel.hasHiddenColumns();
291   }
292
293   /**
294    * checks current colsel against record of last hash value, and optionally
295    * updates record.
296    * 
297    * @param b
298    *          update the record of last hash value
299    * @return true if colsel changed since last call (when b is true)
300    */
301   public boolean isColSelChanged(boolean b)
302   {
303     int hc = (colSel == null || colSel.isEmpty()) ? -1 : colSel.hashCode();
304     if (hc != -1 && hc != colselhash)
305     {
306       if (b)
307       {
308         colselhash = hc;
309       }
310       return true;
311     }
312     return false;
313   }
314
315   public void hideColumns(int start, int end)
316   {
317     if (start == end)
318     {
319       colSel.hideColumns(start);
320     }
321     else
322     {
323       colSel.hideColumns(start, end);
324     }
325     isColSelChanged(true);
326   }
327
328   public void showColumn(int col)
329   {
330     colSel.revealHiddenColumns(col);
331     isColSelChanged(true);
332   }
333
334   public void showAllHiddenColumns()
335   {
336     colSel.revealAllHiddenColumns();
337     isColSelChanged(true);
338   }
339
340   public void invertColumnSelection()
341   {
342     colSel.invertColumnSelection(0, al.getWidth());
343   }
344
345   public List<int[]> getVisibleRegionBoundaries(int min, int max)
346   {
347     ArrayList<int[]> regions = new ArrayList<int[]>();
348     int start = min;
349     int end = max;
350
351     do
352     {
353       if (hasHiddenColumns())
354       {
355         if (start == 0)
356         {
357           start = colSel.adjustForHiddenColumns(start);
358         }
359
360         end = colSel.getHiddenBoundaryRight(start);
361         if (start == end)
362         {
363           end = max;
364         }
365         if (end > max)
366         {
367           end = max;
368         }
369       }
370
371       regions.add(new int[] { start, end });
372
373       if (hasHiddenColumns())
374       {
375         start = colSel.adjustForHiddenColumns(end);
376         start = colSel.getHiddenBoundaryLeft(start) + 1;
377       }
378     } while (end < max);
379
380     int[][] startEnd = new int[regions.size()][2];
381
382     return regions;
383   }
384
385   /**
386    * synthesize a column selection if none exists so it covers the given
387    * selection group. if wholewidth is false, no column selection is made if the
388    * selection group covers the whole alignment width.
389    * 
390    * @param sg
391    * @param wholewidth
392    */
393   public void expandColSelection(SequenceGroup sg, boolean wholewidth)
394   {
395     int sgs, sge;
396     if (sg != null && (sgs = sg.getStartRes()) >= 0
397             && sg.getStartRes() <= (sge = sg.getEndRes()))
398     {
399       if (!wholewidth && al.getWidth() == (1 + sge - sgs))
400       {
401         // do nothing
402         return;
403       }
404       if (colSel == null)
405       {
406         colSel = new ColumnSelection();
407       }
408       for (int cspos = sg.getStartRes(); cspos <= sg.getEndRes(); cspos++)
409       {
410         colSel.addElement(cspos);
411       }
412     }
413   }
414
415   public List<AlignmentAnnotation> getVisibleAlignmentAnnotation(
416           boolean selectedOnly, SequenceGroup selectionGroup)
417   {
418     ArrayList<AlignmentAnnotation> ala = new ArrayList<AlignmentAnnotation>();
419     AlignmentAnnotation[] aa;
420     if ((aa = al.getAlignmentAnnotation()) != null)
421     {
422       for (AlignmentAnnotation annot : aa)
423       {
424         AlignmentAnnotation clone = new AlignmentAnnotation(annot);
425         if (selectedOnly && selectionGroup != null)
426         {
427           colSel.makeVisibleAnnotation(selectionGroup.getStartRes(),
428                   selectionGroup.getEndRes(), clone);
429         }
430         else
431         {
432           colSel.makeVisibleAnnotation(clone);
433         }
434         ala.add(clone);
435       }
436     }
437     return ala;
438   }
439
440   public String[] getVisibleSequenceStrings(int start, int end,
441           SequenceI[] seqs)
442   {
443     return colSel.getVisibleSequenceStrings(start, end, seqs);
444   }
445
446   /**
447    * Set visibility for any annotations for the given sequence.
448    * 
449    * @param sequenceI
450    */
451   protected void setSequenceAnnotationsVisible(SequenceI sequenceI,
452           boolean visible)
453   {
454     AlignmentAnnotation[] anns = al.getAlignmentAnnotation();
455     if (anns != null)
456     {
457       for (AlignmentAnnotation ann : anns)
458       {
459         if (ann.sequenceRef == sequenceI)
460         {
461           ann.visible = visible;
462         }
463       }
464     }
465   }
466
467   public void setHiddenRepSequences(
468           Map<SequenceI, SequenceCollectionI> hiddenRepSequences)
469   {
470     this.hiddenRepSequences = hiddenRepSequences;
471   }
472
473   public Map<SequenceI, SequenceCollectionI> getHiddenRepSequences()
474   {
475     return hiddenRepSequences;
476   }
477
478   // common hide/show seq stuff
479   public SequenceGroup showAllHiddenSeqs(SequenceGroup selectionGroup)
480   {
481     if (al.getHiddenSequences().getSize() > 0)
482     {
483       if (selectionGroup == null)
484       {
485         selectionGroup = new SequenceGroup();
486         selectionGroup.setEndRes(al.getWidth() - 1);
487       }
488       List<SequenceI> tmp = al.getHiddenSequences().showAll(
489               hiddenRepSequences);
490       for (SequenceI seq : tmp)
491       {
492         selectionGroup.addSequence(seq, false);
493         setSequenceAnnotationsVisible(seq, true);
494       }
495
496       hiddenRepSequences = null;
497
498       firePropertyChange("alignment", null, al.getSequences());
499       // used to set hasHiddenRows/hiddenRepSequences here, after the property
500       // changed event
501       sendSelection();
502     }
503   }
504
505   public void showSequence(int index, SequenceGroup selectionGroup)
506   {
507     List<SequenceI> tmp = al.getHiddenSequences().showSequence(index,
508             hiddenRepSequences);
509     if (tmp.size() > 0)
510     {
511       if (selectionGroup == null)
512       {
513         selectionGroup = new SequenceGroup();
514         selectionGroup.setEndRes(al.getWidth() - 1);
515       }
516
517       for (SequenceI seq : tmp)
518       {
519         selectionGroup.addSequence(seq, false);
520         setSequenceAnnotationsVisible(seq, true);
521       }
522       firePropertyChange("alignment", null, al.getSequences());
523       sendSelection();
524     }
525   }
526
527   public void hideAllSelectedSeqs(SequenceGroup selectionGroup)
528   {
529     if (selectionGroup == null || selectionGroup.getSize() < 1)
530     {
531       return;
532     }
533
534     SequenceI[] seqs = selectionGroup.getSequencesInOrder(al);
535
536     hideSequence(seqs);
537
538     setSelectionGroup(null);
539   }
540
541   public void hideSequence(SequenceI[] seq)
542   {
543     if (seq != null)
544     {
545       for (int i = 0; i < seq.length; i++)
546       {
547         al.getHiddenSequences().hideSequence(seq[i]);
548         setSequenceAnnotationsVisible(seq[i], false);
549       }
550       firePropertyChange("alignment", null, al.getSequences());
551     }
552   }
553
554   /**
555    * Hides the specified sequence, or the sequences it represents
556    * 
557    * @param sequence
558    *          the sequence to hide, or keep as representative
559    * @param representGroup
560    *          if true, hide the current selection group except for the
561    *          representative sequence
562    */
563   public void hideSequences(SequenceI sequence, boolean representGroup,
564           SequenceGroup selectionGroup)
565   {
566     if (selectionGroup == null || selectionGroup.getSize() < 1)
567     {
568       hideSequence(new SequenceI[] { sequence });
569       return;
570     }
571
572     if (representGroup)
573     {
574       hideRepSequences(sequence, selectionGroup);
575       setSelectionGroup(null);
576       return;
577     }
578
579     int gsize = selectionGroup.getSize();
580     SequenceI[] hseqs = selectionGroup.getSequences().toArray(
581             new SequenceI[gsize]);
582
583     hideSequence(hseqs);
584     setSelectionGroup(null);
585     sendSelection();
586   }
587
588   public void hideRepSequences(SequenceI repSequence, SequenceGroup sg)
589   {
590     int sSize = sg.getSize();
591     if (sSize < 2)
592     {
593       return;
594     }
595
596     if (hiddenRepSequences == null)
597     {
598       hiddenRepSequences = new Hashtable<SequenceI, SequenceCollectionI>();
599     }
600
601     hiddenRepSequences.put(repSequence, sg);
602
603     // Hide all sequences except the repSequence
604     SequenceI[] seqs = new SequenceI[sSize - 1];
605     int index = 0;
606     for (int i = 0; i < sSize; i++)
607     {
608       if (sg.getSequenceAt(i) != repSequence)
609       {
610         if (index == sSize - 1)
611         {
612           return;
613         }
614
615         seqs[index++] = sg.getSequenceAt(i);
616       }
617     }
618     sg.setSeqrep(repSequence); // note: not done in 2.7applet
619     sg.setHidereps(true); // note: not done in 2.7applet
620     hideSequence(seqs);
621
622   }
623
624   /**
625    * 
626    * @param seq
627    * @return true if there are sequences represented by this sequence that are
628    *         currently hidden
629    */
630   public boolean isHiddenRepSequence(SequenceI seq)
631   {
632     return (hiddenRepSequences != null && hiddenRepSequences
633             .containsKey(seq));
634   }
635
636   /**
637    * 
638    * @param seq
639    * @return null or a sequence group containing the sequences that seq
640    *         represents
641    */
642   public SequenceGroup getRepresentedSequences(SequenceI seq)
643   {
644     return (SequenceGroup) (hiddenRepSequences == null ? null
645             : hiddenRepSequences.get(seq));
646   }
647
648   public int adjustForHiddenSeqs(int alignmentIndex)
649   {
650     return al.getHiddenSequences().adjustForHiddenSeqs(alignmentIndex);
651   }
652 }