removeGaps method can record changes in alignment columns to pass to ColumnSelection
[jalview.git] / src / jalview / datamodel / ColumnSelection.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2005 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.datamodel;
20
21 import jalview.util.ShiftList;
22
23 import java.util.*;
24
25 /**
26  * NOTE: Columns are zero based.
27  */
28 public class ColumnSelection
29 {
30     Vector selected = new Vector();
31
32     //Vector of int [] {startCol, endCol}
33     Vector hiddenColumns;
34
35     /**
36      * Add a column to the selection
37      *
38      * @param col index of column
39      */
40     public void addElement(int col)
41     {
42         Integer column = new Integer(col);
43         if (!selected.contains(column))
44         {
45             selected.addElement(column);
46         }
47     }
48
49     /**
50      * clears column selection
51      */
52     public void clear()
53     {
54         selected.removeAllElements();
55     }
56
57     /**
58      * removes col from selection
59      *
60      * @param col index of column to be removed
61      */
62     public void removeElement(int col)
63     {
64         Integer colInt = new Integer(col);
65
66         if (selected.contains(colInt))
67         {
68             selected.removeElement(colInt);
69         }
70     }
71
72     /**
73      * removes a range of columns from the selection
74      * @param start int - first column in range to be removed
75      * @param end int - last col
76      */
77     public void removeElements(int start, int end)
78     {
79       Integer colInt;
80       for(int i=start; i<end; i++)
81       {
82         colInt = new Integer(i);
83         if (selected.contains(colInt))
84         {
85             selected.removeElement(colInt);
86         }
87       }
88     }
89     /**
90      *
91      * @return Vector containing selected columns as Integers
92      */
93     public Vector getSelected()
94     {
95       return selected;
96     }
97
98     /**
99      *
100      * @param col index to search for in column selection
101      *
102      * @return true if Integer(col) is in selection.
103      */
104     public boolean contains(int col)
105     {
106         return selected.contains(new Integer(col));
107     }
108
109     /**
110      * DOCUMENT ME!
111      *
112      * @param i DOCUMENT ME!
113      *
114      * @return DOCUMENT ME!
115      */
116     public int columnAt(int i)
117     {
118         return ((Integer) selected.elementAt(i)).intValue();
119     }
120
121     /**
122      * DOCUMENT ME!
123      *
124      * @return DOCUMENT ME!
125      */
126     public int size()
127     {
128         return selected.size();
129     }
130
131     /**
132      * DOCUMENT ME!
133      *
134      * @return DOCUMENT ME!
135      */
136     public int getMax()
137     {
138         int max = -1;
139
140         for (int i = 0; i < selected.size(); i++)
141         {
142             if (columnAt(i) > max)
143             {
144                 max = columnAt(i);
145             }
146         }
147
148         return max;
149     }
150
151     /**
152      * DOCUMENT ME!
153      *
154      * @return DOCUMENT ME!
155      */
156     public int getMin()
157     {
158         int min = 1000000000;
159
160         for (int i = 0; i < selected.size(); i++)
161         {
162             if (columnAt(i) < min)
163             {
164                 min = columnAt(i);
165             }
166         }
167
168         return min;
169     }
170
171
172     /**
173      * propagate shift in alignment columns to column selection
174      *
175      * @param start beginning of edit
176      * @param change shift in edit (-ve or +ve number of columns)
177      */
178     public void compensateForEdit(int start, int change)
179     {
180         for (int i = 0; i < size(); i++)
181         {
182             int temp = columnAt(i);
183
184             if (temp >= start)
185             {
186                 selected.setElementAt(new Integer(temp - change), i);
187             }
188         }
189
190         if(hiddenColumns!=null)
191         {
192           for(int i=0; i<hiddenColumns.size(); i++)
193           {
194             int[] region = (int[]) hiddenColumns.elementAt(i);
195             if(region[0] > start)
196             {
197               region[0] -= change;
198               region[1] -= change;
199             }
200             if(region[0]<0)
201               region[0] = 0;
202             if(region[1] <0)
203              region[1] = 0;
204           }
205         }
206     }
207     public ShiftList compensateForEdits(ShiftList shiftrecord) {
208       if (shiftrecord!=null) {
209         Vector shifts = shiftrecord.shifts;
210         if (shifts!=null && shifts.size()>0) {
211           for (int i=0,j=shifts.size(); i<j; i++) {
212             int[] sh = (int[]) shifts.get(i);
213             compensateForEdit(sh[0], sh[1]);
214           }
215         }
216         return shiftrecord.getInverse();
217       }
218       return null;
219     }
220     /**
221      * This Method is used to return all the HiddenColumn regions
222      * less than the given index.
223      * @param end int
224      * @return Vector
225      */
226     public Vector getHiddenColumns()
227     {
228       return hiddenColumns;
229     }
230     /**
231      * Return absolute column index for a visible column index
232      * @param column int column index in alignment view
233      * @return alignment column index for column
234      */
235     public int adjustForHiddenColumns(int column)
236     {
237       int result = column;
238       if (hiddenColumns != null)
239       {
240         for (int i = 0; i < hiddenColumns.size(); i++)
241         {
242           int[] region = (int[]) hiddenColumns.elementAt(i);
243           if (result >= region[0])
244           {
245             result += region[1] - region[0] + 1;
246           }
247         }
248       }
249       return result;
250     }
251
252     /**
253      * Use this method to find out where a visible column is in the alignment
254      * when hidden columns exist
255      * @param hiddenColumn int
256      * @return int
257      */
258     public int findColumnPosition(int hiddenColumn)
259     {
260       int result = hiddenColumn;
261       if (hiddenColumns != null)
262       {
263         int index = 0;
264         int gaps = 0;
265         do
266         {
267           int[] region = (int[]) hiddenColumns.elementAt(index);
268           if (hiddenColumn > region[1])
269           {
270             result -= region[1]+1-region[0];
271           }
272           index++;
273         }
274         while (index < hiddenColumns.size());
275
276         result -= gaps;
277       }
278
279       return result;
280     }
281
282     /**
283      * Use this method to determine where the next hiddenRegion starts
284     */
285     public int findHiddenRegionPosition(int hiddenRegion)
286     {
287       int result = 0;
288       if (hiddenColumns != null)
289       {
290         int index = 0;
291         int gaps = 0;
292         do
293         {
294           int[] region = (int[]) hiddenColumns.elementAt(index);
295           if(hiddenRegion==0)
296           {
297             return region[0];
298           }
299
300             gaps +=  region[1] +1 - region[0];
301             result = region[1] +1;
302             index++;
303         }
304         while(index < hiddenRegion+1);
305
306         result -= gaps;
307       }
308
309       return result;
310     }
311
312     /**
313      * THis method returns the rightmost limit of a
314      * region of an alignment with hidden columns.
315      * In otherwords, the next hidden column.
316      * @param index int
317      */
318     public int getHiddenBoundaryRight(int alPos)
319     {
320       if (hiddenColumns != null)
321       {
322         int index = 0;
323         do
324         {
325           int[] region = (int[]) hiddenColumns.elementAt(index);
326           if(alPos < region[0])
327             return region[0];
328
329           index++;
330         }
331         while(index < hiddenColumns.size());
332       }
333
334       return alPos;
335
336     }
337     /**
338      * THis method returns the rightmost limit of a
339      * region of an alignment with hidden columns.
340      * In otherwords, the next hidden column.
341      * @param index int
342      */
343     public int getHiddenBoundaryLeft(int alPos)
344     {
345       if (hiddenColumns != null)
346       {
347         int index = hiddenColumns.size()-1;
348         do
349         {
350           int[] region = (int[]) hiddenColumns.elementAt(index);
351           if(alPos > region[1])
352             return region[1];
353
354           index--;
355         }
356         while(index >-1);
357       }
358
359       return alPos;
360
361     }
362
363     public void hideSelectedColumns()
364     {
365       while (size() > 0)
366       {
367         int column = ( (Integer) getSelected().firstElement()).intValue();
368         hideColumns(column);
369       }
370
371     }
372
373     public void hideColumns(int start, int end)
374     {
375       if(hiddenColumns==null)
376         hiddenColumns = new Vector();
377
378       boolean added = false;
379       boolean overlap = false;
380
381       for (int i = 0; i < hiddenColumns.size(); i++)
382       {
383         int[] region = (int[]) hiddenColumns.elementAt(i);
384         if ( start<=region[1] && end>=region[0])
385         {
386           hiddenColumns.removeElementAt(i);
387           overlap = true;
388           break;
389         }
390         else if (end < region[0] && start < region[0])
391         {
392           hiddenColumns.insertElementAt(new int[]
393                                         {start, end}, i);
394           added = true;
395           break;
396         }
397       }
398
399       if(overlap)
400       {
401          hideColumns(start, end);
402       }
403       else if (!added)
404         hiddenColumns.addElement(new int[] {start, end});
405
406     }
407
408     /**
409      * This method will find a range of selected columns
410      * around the column specified
411      * @param res int
412      */
413     public void hideColumns(int col)
414     {
415       // First find out range of columns to hide
416       int min = col, max = col+1;
417       while( contains(min) )
418       {  removeElement(min); min --;  }
419
420       while( contains(max) )
421       { removeElement(max);  max ++;  }
422
423       min++; max--;
424
425       hideColumns(min, max);
426     }
427
428     public void revealAllHiddenColumns()
429     {
430       if(hiddenColumns!=null)
431       {
432         for (int i = 0; i < hiddenColumns.size(); i++)
433         {
434           int[] region = (int[]) hiddenColumns.elementAt(i);
435           for (int j = region[0]; j < region[1]+1; j++)
436           {
437             addElement(j);
438           }
439         }
440       }
441
442       hiddenColumns = null;
443     }
444
445     public void revealHiddenColumns(int res)
446     {
447       for(int i=0; i<hiddenColumns.size(); i++)
448       {
449         int [] region = (int[])hiddenColumns.elementAt(i);
450         if( res == region[0])
451         {
452           for (int j = region[0]; j < region[1]+1; j++)
453           {
454             addElement(j);
455           }
456
457           hiddenColumns.removeElement(region);
458           break;
459         }
460       }
461       if(hiddenColumns.size()==0)
462         hiddenColumns = null;
463     }
464
465     public boolean isVisible(int column)
466     {
467       for(int i=0; i<hiddenColumns.size(); i++)
468       {
469         int [] region = (int[])hiddenColumns.elementAt(i);
470         if( column >= region[0] && column <= region[1])
471         {
472           return false;
473         }
474       }
475       return true;
476     }
477     /**
478      * Copy constructor
479      * @param copy
480      */
481     public ColumnSelection(ColumnSelection copy) {
482       if (copy!=null) {
483         if (copy.selected!=null) {
484           selected = new Vector();
485           for (int i=0,j=copy.selected.size(); i<j; i++) {
486             selected.set(i, ((Integer) copy.selected.get(i)));
487           }
488         }
489         if (copy.hiddenColumns!=null) {
490           hiddenColumns=new Vector();
491           for (int i=0,j=copy.hiddenColumns.size(); i<j; i++) {
492             int[] rh,cp;
493             rh = (int[])copy.hiddenColumns.get(i);
494             if (rh!=null) {
495               cp = new int[rh.length];
496               System.arraycopy(rh, 0, cp, 0, rh.length);
497               hiddenColumns.set(i, cp);
498             }
499           }
500         }
501       }
502     }
503
504   /**
505    * ColumnSelection
506    */
507   public ColumnSelection()
508   {
509   }
510 }