+ // set up cursor reset values
+ HiddenCursorPosition cursorPos = cursor.findRegionForColumn(start, false);
+ regionindex = cursorPos.getRegionIndex();
+
+ if (regionindex > 0)
+ {
+ // get previous index and hidden count for updating the cursor later
+ previndex = regionindex - 1;
+ int[] prevRegion = hiddenColumns.get(previndex);
+ prevHiddenCount = cursorPos.getHiddenSoFar()
+ - (prevRegion[1] - prevRegion[0] + 1);
+ }
+ }
+
+ // new range follows everything else; check first to avoid looping over
+ // whole hiddenColumns collection
+ if (hiddenColumns.isEmpty()
+ || start > hiddenColumns.get(hiddenColumns.size() - 1)[1])
+ {
+ hiddenColumns.add(new int[] { start, end });
+ numColumns += end - start + 1;
+ }
+ else
+ {
+ /*
+ * traverse existing hidden ranges and insert / amend / append as
+ * appropriate
+ */
+ boolean added = false;
+ if (regionindex > 0)
+ {
+ added = insertRangeAtRegion(regionindex - 1, start, end);
+ }
+ if (!added && regionindex < hiddenColumns.size())
+ {
+ insertRangeAtRegion(regionindex, start, end);
+ }
+ }
+
+ // reset the cursor to just before our insertion point: this saves
+ // a lot of reprocessing in large alignments
+ cursor = new HiddenColumnsCursor(hiddenColumns, previndex,
+ prevHiddenCount);
+ } finally
+ {
+ LOCK.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Insert [start, range] at the region at index i in hiddenColumns, if
+ * feasible
+ *
+ * @param i
+ * index to insert at
+ * @param start
+ * start of range to insert
+ * @param end
+ * end of range to insert
+ * @return true if range was successfully inserted
+ */
+ private boolean insertRangeAtRegion(int i, int start, int end)
+ {
+ boolean added = false;
+
+ int[] region = hiddenColumns.get(i);
+ if (end < region[0] - 1)
+ {
+ /*
+ * insert discontiguous preceding range
+ */
+ hiddenColumns.add(i, new int[] { start, end });
+ numColumns += end - start + 1;
+ added = true;
+ }
+ else if (end <= region[1])
+ {
+ /*
+ * new range overlaps existing, or is contiguous preceding it - adjust
+ * start column
+ */
+ int oldstart = region[0];
+ region[0] = Math.min(region[0], start);
+ numColumns += oldstart - region[0]; // new columns are between old and
+ // adjusted starts
+ added = true;
+ }
+ else if (start <= region[1] + 1)
+ {
+ /*
+ * new range overlaps existing, or is contiguous following it - adjust
+ * start and end columns
+ */
+ insertRangeAtOverlap(i, start, end, region);
+ added = true;
+ }
+ return added;
+ }
+
+ /**
+ * Insert a range whose start position overlaps an existing region and/or is
+ * contiguous to the right of the region
+ *
+ * @param i
+ * index to insert at
+ * @param start
+ * start of range to insert
+ * @param end
+ * end of range to insert
+ * @param region
+ * the overlapped/continued region
+ */
+ private void insertRangeAtOverlap(int i, int start, int end, int[] region)
+ {
+ int oldstart = region[0];
+ int oldend = region[1];
+ region[0] = Math.min(region[0], start);
+ region[1] = Math.max(region[1], end);
+
+ numColumns += oldstart - region[0];
+
+ /*
+ * also update or remove any subsequent ranges
+ * that are overlapped
+ */
+ int endi = i;
+ while (endi < hiddenColumns.size() - 1)
+ {
+ int[] nextRegion = hiddenColumns.get(endi + 1);
+ if (nextRegion[0] > end + 1)
+ {
+ /*
+ * gap to next hidden range - no more to update
+ */
+ break;
+ }
+ numColumns -= nextRegion[1] - nextRegion[0] + 1;
+ region[1] = Math.max(nextRegion[1], end);
+ endi++;
+ }
+ numColumns += region[1] - oldend;
+ hiddenColumns.subList(i + 1, endi + 1).clear();
+ }
+
+ /**
+ * hide a list of ranges
+ *
+ * @param ranges
+ */
+ public void hideList(List<int[]> ranges)
+ {
+ try
+ {
+ LOCK.writeLock().lock();
+ for (int[] r : ranges)
+ {
+ hideColumns(r[0], r[1]);
+ }
+ cursor = new HiddenColumnsCursor(hiddenColumns);
+
+ } finally
+ {
+ LOCK.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Unhides, and adds to the selection list, all hidden columns
+ */
+ public void revealAllHiddenColumns(ColumnSelection sel)
+ {
+ try
+ {
+ LOCK.writeLock().lock();
+
+ for (int[] region : hiddenColumns)
+ {
+ for (int j = region[0]; j < region[1] + 1; j++)
+ {
+ sel.addElement(j);
+ }
+ }
+ hiddenColumns.clear();
+ cursor = new HiddenColumnsCursor(hiddenColumns);
+ numColumns = 0;
+
+ } finally
+ {
+ LOCK.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Reveals, and marks as selected, the hidden column range with the given
+ * start column
+ *
+ * @param start
+ * the start column to look for
+ * @param sel
+ * the column selection to add the hidden column range to
+ */
+ public void revealHiddenColumns(int start, ColumnSelection sel)
+ {
+ try
+ {
+ LOCK.writeLock().lock();
+
+ if (!hiddenColumns.isEmpty())
+ {
+ int regionIndex = cursor.findRegionForColumn(start, false)
+ .getRegionIndex();
+
+ if (regionIndex != -1 && regionIndex != hiddenColumns.size())