+/*
+ * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
+ * Copyright (C) $$Year-Rel$$ The Jalview Authors
+ *
+ * This file is part of Jalview.
+ *
+ * Jalview is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * Jalview is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE. See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
+ * The Jalview Authors are detailed in the 'AUTHORS' file.
+ */
package jalview.datamodel;
+import java.util.ArrayList;
import java.util.List;
public class HiddenColumnsCursor
// absolute position of first hidden column
private int firstColumn;
- // absolute position of last hidden column
- private int lastColumn;
+ private List<int[]> hiddenColumns = new ArrayList<>();
- // index of last visited region
- private int regionIndex;
+ private HiddenCursorPosition cursorPos = new HiddenCursorPosition(0, 0);
- // number of hidden columns before last visited region
- private int hiddenSoFar;
+ protected HiddenColumnsCursor()
+ {
- private List<int[]> hiddenColumns;
+ }
- protected HiddenColumnsCursor()
+ protected HiddenColumnsCursor(List<int[]> hiddenCols)
{
+ resetCursor(hiddenCols, 0, 0);
+ }
+ protected HiddenColumnsCursor(List<int[]> hiddenCols, int index,
+ int hiddencount)
+ {
+ resetCursor(hiddenCols, index, hiddencount);
}
/**
- * Set the cursor to a position
+ * Reset the cursor with a new hidden columns collection, where we know in
+ * advance the index and hidden columns count of a particular location.
*
- * @param first
- * absolute position of first hidden column
- * @param last
- * absolute position of last hidden column
+ * @param hiddenCols
+ * new hidden columns collection
* @param index
- * index of last visited region
- * @param hiddenCount
- * number of hidden columns before last visited region
+ * cursor index to reset to
+ * @param hiddencount
+ * hidden columns count to reset to
*/
- protected void resetCursor(List<int[]> hiddenCols)
- {
- synchronized (this)
- {
- if ((hiddenCols != null) && (!hiddenCols.isEmpty()))
- {
- hiddenColumns = hiddenCols;
- firstColumn = hiddenColumns.get(0)[0];
- lastColumn = hiddenColumns.get(hiddenColumns.size() - 1)[1];
- regionIndex = 0;
- hiddenSoFar = 0;
- }
- }
- }
-
- protected void updateCursor(int index, int hiddenCount)
+ private void resetCursor(List<int[]> hiddenCols, int index,
+ int hiddencount)
{
- synchronized (this)
+ hiddenColumns = hiddenCols;
+ if (!hiddenCols.isEmpty())
{
- regionIndex = index;
- hiddenSoFar = hiddenCount;
+ firstColumn = hiddenColumns.get(0)[0];
+ cursorPos = new HiddenCursorPosition(index, hiddencount);
}
}
- private synchronized int getIndex()
- {
- return regionIndex;
- }
-
- private synchronized int getHiddenSoFar()
- {
- return hiddenSoFar;
- }
-
-
/**
- * Get the index of the region that column is within (if column is hidden) or
- * which is to the right of column (if column is visible). If no hidden
- * columns are to the right, will return size of hiddenColumns. If hidden
- * columns is empty returns -1.
+ * Get the cursor pointing to the hidden region that column is within (if
+ * column is hidden) or which is to the right of column (if column is
+ * visible). If no hidden columns are to the right, returns a cursor pointing
+ * to an imaginary hidden region beyond the end of the hidden columns
+ * collection (this ensures the count of previous hidden columns is correct).
+ * If hidden columns is empty returns null.
*
* @param column
- * absolute position of a column in the alignment
- * @return region index
+ * index of column in visible or absolute coordinates
+ * @param useVisible
+ * true if column is in visible coordinates, false if absolute
+ * @return cursor pointing to hidden region containing the column (if hidden)
+ * or to the right of the column (if visible)
*/
- protected int findRegionForColumn(int column)
+ protected HiddenCursorPosition findRegionForColumn(int column,
+ boolean useVisible)
{
- if (hiddenColumns == null)
+ if (hiddenColumns.isEmpty())
{
- return -1;
+ return null;
}
- int index = regionIndex;
- int hiddenCount = hiddenSoFar;
+ // used to add in hiddenColumns offset when working with visible columns
+ int offset = (useVisible ? 1 : 0);
- if (index == hiddenColumns.size())
- {
- // went past the end of hiddenColumns collection last time
- index--;
- int[] region = hiddenColumns.get(index);
- hiddenCount -= region[1] - region[0] + 1;
- }
+ HiddenCursorPosition pos = cursorPos;
+ int index = pos.getRegionIndex();
+ int hiddenCount = pos.getHiddenSoFar();
- if ((hiddenColumns.get(index)[0] <= column)
- && hiddenColumns.get(index)[1] >= column)
- {
- // column is in the current region
- // we hit the jackpot
- // don't need to move index
- }
- else if (column < firstColumn)
+ if (column < firstColumn)
{
- index = 0;
- hiddenCount = 0;
+ pos = new HiddenCursorPosition(0, 0);
}
+
// column is after current region
- else if (column > hiddenColumns.get(index)[1]) // includes if column >
- // lastColumn
+ else if ((index < hiddenColumns.size())
+ && (hiddenColumns.get(index)[0] <= column
+ + offset * hiddenCount))
{
// iterate from where we are now, if we're lucky we'll be close by
// (but still better than iterating from 0)
// stop when we find the region *before* column
// i.e. the next region starts after column or if not, ends after column
- while ((index < hiddenColumns.size())
- && (column > hiddenColumns.get(index)[1]))
- {
- int[] region = hiddenColumns.get(index);
- hiddenCount += region[1] - region[0] + 1;
- index++;
- }
+ pos = searchForward(pos, column, useVisible);
}
// column is before current region
- else if (column < hiddenColumns.get(index)[0])
+ else
{
- while ((index > 0) && (hiddenColumns.get(index)[1] > column))
- {
- index--;
- int[] region = hiddenColumns.get(index);
- hiddenCount -= region[1] - region[0] + 1;
- }
+ pos = searchBackward(pos, column, useVisible);
}
- updateCursor(index, hiddenCount);
- return index;
+ cursorPos = pos;
+ return pos;
}
- protected int getHiddenOffset(int column)
+ /**
+ * Search forwards through the hidden columns collection to find the hidden
+ * region immediately before a column
+ *
+ * @param pos
+ * current position
+ * @param column
+ * column to locate
+ * @param useVisible
+ * whether using visible or absolute coordinates
+ * @return position of region before column
+ */
+ private HiddenCursorPosition searchForward(HiddenCursorPosition pos,
+ int column, boolean useVisible)
{
- if (hiddenColumns == null)
- {
- return -1;
- }
-
- int index = getIndex();
- int hiddenCount = getHiddenSoFar();
-
- if (column < firstColumn)
+ HiddenCursorPosition p = pos;
+ if (useVisible)
{
- index = 0;
- hiddenCount = 0;
- }
- else if ((index < hiddenColumns.size())
- && (hiddenColumns.get(index)[0] <= column + hiddenCount))
- {
- // iterate from where we are now, if we're lucky we'll be close by
- // (but still better than iterating from 0)
- while ((index < hiddenColumns.size())
- && (hiddenColumns.get(index)[0] <= column + hiddenCount))
+ while ((p.getRegionIndex() < hiddenColumns.size())
+ && hiddenColumns.get(p.getRegionIndex())[0] <= column
+ + p.getHiddenSoFar())
{
- int[] region = hiddenColumns.get(index);
- hiddenCount += region[1] - region[0] + 1;
- index++;
+ p = stepForward(p);
}
}
- else if (index < hiddenColumns.size())
+ else
{
- while ((index > 0)
- && (hiddenColumns.get(index - 1)[1] >= column + hiddenCount))
+ while ((p.getRegionIndex() < hiddenColumns.size())
+ && hiddenColumns.get(p.getRegionIndex())[1] < column)
{
- index--;
- int[] region = hiddenColumns.get(index);
- hiddenCount -= region[1] - region[0] + 1;
+ p = stepForward(p);
}
+ }
+ return p;
+ }
+ /**
+ * Move to the next (rightwards) hidden region after a given cursor position
+ *
+ * @param p
+ * current position of cursor
+ * @return new position of cursor at next region
+ */
+ private HiddenCursorPosition stepForward(HiddenCursorPosition p)
+ {
+ int[] region = hiddenColumns.get(p.getRegionIndex());
+
+ // increment the index, and add this region's hidden columns to the hidden
+ // column count
+ return new HiddenCursorPosition(p.getRegionIndex() + 1,
+ p.getHiddenSoFar() + region[1] - region[0] + 1);
+ }
+
+ /**
+ * Search backwards through the hidden columns collection to find the hidden
+ * region immediately before (left of) a given column
+ *
+ * @param pos
+ * current position
+ * @param column
+ * column to locate
+ * @param useVisible
+ * whether using visible or absolute coordinates
+ * @return position of region immediately to left of column
+ */
+ private HiddenCursorPosition searchBackward(HiddenCursorPosition p,
+ int column, boolean useVisible)
+ {
+ int i = p.getRegionIndex();
+ int h = p.getHiddenSoFar();
+
+ // used to add in hiddenColumns offset when working with visible columns
+ int offset = (useVisible ? 1 : 0);
+
+ while ((i > 0) && (hiddenColumns.get(i - 1)[1] >= column + offset * h))
+ {
+ i--;
+ int[] region = hiddenColumns.get(i);
+ h -= region[1] - region[0] + 1;
}
- updateCursor(index, hiddenCount);
- return hiddenCount;
+ return new HiddenCursorPosition(i, h);
}
+
}