2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.datamodel;
23 import java.util.List;
24 import java.util.concurrent.atomic.AtomicReference;
26 public class HiddenColumnsCursor
28 // absolute position of first hidden column
29 private int firstColumn;
31 private List<int[]> hiddenColumns;
33 // AtomicReference to hold the current region index and hidden column count
34 // Could be done with synchronisation but benchmarking shows this way is 2x
36 private final AtomicReference<HiddenCursorPosition> cursorPos = new AtomicReference<>(
37 new HiddenCursorPosition(0, 0));
39 protected HiddenColumnsCursor()
45 * Reset the cursor with a new hidden columns collection. Calls to resetCursor
46 * should be made from within a writeLock in the HiddenColumns class - since
47 * changes to the hiddenColumns collection require a writeLock the lock should
52 protected void resetCursor(List<int[]> hiddenCols)
54 hiddenColumns = hiddenCols;
55 if ((hiddenCols != null) && (!hiddenCols.isEmpty()))
57 firstColumn = hiddenColumns.get(0)[0];
58 HiddenCursorPosition oldpos = cursorPos.get();
59 HiddenCursorPosition newpos = new HiddenCursorPosition(0, 0);
60 cursorPos.compareAndSet(oldpos, newpos);
65 * Delete the region the cursor is currently at. Avoids having to reset the
66 * cursor just because we deleted a region.
68 * Calls to updateForDeletedRegion should be made from within a writeLock in
69 * the HiddenColumns class - since changes to the hiddenColumns collection
70 * require a writeLock the lock should already exist.
74 protected void updateForDeletedRegion(List<int[]> hiddenCols)
77 if ((hiddenCols != null) && (!hiddenCols.isEmpty()))
79 // if there is a region to the right of the current region,
80 // nothing changes; otherwise
81 // we deleted the last region (index=hiddenCols.size()-1)
82 // or the index was at the end of the alignment (index=hiddenCols.size())
83 HiddenCursorPosition oldpos = cursorPos.get();
85 int index = oldpos.getRegionIndex();
86 if (index >= hiddenColumns.size() - 1)
88 // deleted last region, index is now end of alignment
89 index = hiddenCols.size();
92 // update the cursor position
93 HiddenCursorPosition newpos = new HiddenCursorPosition(index,
94 oldpos.getHiddenSoFar());
95 cursorPos.compareAndSet(oldpos, newpos);
97 hiddenColumns = hiddenCols;
101 * Get the index of the region that column is within (if column is hidden) or
102 * which is to the right of column (if column is visible). If no hidden
103 * columns are to the right, will return size of hiddenColumns. If hidden
104 * columns is empty returns -1.
107 * absolute position of a column in the alignment
108 * @return region index
110 protected HiddenCursorPosition findRegionForColumn(int column)
112 if (hiddenColumns == null)
117 HiddenCursorPosition oldpos = cursorPos.get();
118 int index = oldpos.getRegionIndex();
119 int hiddenCount = oldpos.getHiddenSoFar();
121 if (index == hiddenColumns.size())
123 // went past the end of hiddenColumns collection last time
125 int[] region = hiddenColumns.get(index);
126 hiddenCount -= region[1] - region[0] + 1;
129 if ((hiddenColumns.get(index)[0] <= column)
130 && hiddenColumns.get(index)[1] >= column)
132 // column is in the current region
133 // we hit the jackpot
134 // don't need to move index
136 else if (column < firstColumn)
141 // column is after current region
142 else if (column > hiddenColumns.get(index)[1]) // includes if column >
145 // iterate from where we are now, if we're lucky we'll be close by
146 // (but still better than iterating from 0)
147 // stop when we find the region *before* column
148 // i.e. the next region starts after column or if not, ends after column
149 while ((index < hiddenColumns.size())
150 && (column > hiddenColumns.get(index)[1]))
152 int[] region = hiddenColumns.get(index);
153 hiddenCount += region[1] - region[0] + 1;
158 // column is before current region
159 else if (column < hiddenColumns.get(index)[0])
161 // column is before or in the previous region
162 if ((index > 0) && (hiddenColumns.get(index - 1)[1] >= column))
164 while ((index > 0) && (hiddenColumns.get(index - 1)[1] >= column))
167 int[] region = hiddenColumns.get(index);
168 hiddenCount -= region[1] - region[0] + 1;
172 HiddenCursorPosition newpos = new HiddenCursorPosition(index,
174 cursorPos.compareAndSet(oldpos, newpos);
179 * Get the number of hidden columns in regions before column i.e. excludes
180 * hidden columns in the region column is in, if any
183 * index of column in visible alignment
186 protected HiddenCursorPosition getHiddenOffset(int column)
188 if (hiddenColumns == null)
193 HiddenCursorPosition oldpos = cursorPos.get();
194 int index = oldpos.getRegionIndex();
195 int hiddenCount = oldpos.getHiddenSoFar();
197 if (column < firstColumn)
202 else if ((index < hiddenColumns.size())
203 && (hiddenColumns.get(index)[0] <= column + hiddenCount))
205 // iterate from where we are now, if we're lucky we'll be close by
206 // (but still better than iterating from 0)
207 while ((index < hiddenColumns.size())
208 && (hiddenColumns.get(index)[0] <= column + hiddenCount))
210 int[] region = hiddenColumns.get(index);
211 hiddenCount += region[1] - region[0] + 1;
218 && (hiddenColumns.get(index - 1)[1] >= column + hiddenCount))
221 int[] region = hiddenColumns.get(index);
222 hiddenCount -= region[1] - region[0] + 1;
227 HiddenCursorPosition newpos = new HiddenCursorPosition(index,
229 cursorPos.compareAndSet(oldpos, newpos);