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 jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
24 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField;
26 import java.util.ArrayList;
27 import java.util.BitSet;
28 import java.util.Collections;
29 import java.util.List;
32 * Data class holding the selected columns and hidden column ranges for a view.
35 public class ColumnSelection
38 * A class to hold an efficient representation of selected columns
43 * list of selected columns (ordered by selection order, not column order)
45 private List<Integer> order;
48 * an unmodifiable view of the selected columns list
50 private List<Integer> _uorder;
53 * bitfield for column selection - allows quick lookup
55 private BitSet selected;
62 order = new ArrayList<Integer>();
63 _uorder = Collections.unmodifiableList(order);
64 selected = new BitSet();
72 IntList(IntList other)
78 for (int i = 0; i < j; i++)
80 add(other.elementAt(i));
86 * adds a new column i to the selection - only if i is not already selected
94 order.add(Integer.valueOf(i));
108 Integer colInt = new Integer(col);
110 if (selected.get(col))
112 // if this ever changes to List.remove(), ensure Integer not int
114 // as List.remove(int i) removes the i'th item which is wrong
115 order.remove(colInt);
120 boolean contains(Integer colInt)
122 return selected.get(colInt);
127 return order.isEmpty();
131 * Returns a read-only view of the selected columns list
135 List<Integer> getList()
146 * gets the column that was selected first, second or i'th
156 protected boolean pruneColumnList(final List<int[]> shifts)
158 int s = 0, t = shifts.size();
159 int[] sr = shifts.get(s++);
160 boolean pruned = false;
161 int i = 0, j = order.size();
162 while (i < j && s <= t)
164 int c = order.get(i++).intValue();
167 if (sr[1] + sr[0] >= c)
168 { // sr[1] -ve means inseriton.
187 * shift every selected column at or above start by change
190 * - leftmost column to be shifted
194 void compensateForEdits(int start, int change)
196 BitSet mask = new BitSet();
197 for (int i = 0; i < order.size(); i++)
199 int temp = order.get(i);
203 // clear shifted bits and update List of selected columns
204 selected.clear(temp);
205 mask.set(temp - change);
206 order.set(i, new Integer(temp - change));
209 // lastly update the bitfield all at once
213 boolean isSelected(int column)
215 return selected.get(column);
220 return selected.length() - 1;
225 return selected.get(0) ? 0 : selected.nextSetBit(0);
229 * @return a series of selection intervals along the range
231 List<int[]> getRanges()
233 List<int[]> rlist = new ArrayList<int[]>();
234 if (selected.isEmpty())
238 int next = selected.nextSetBit(0), clear = -1;
241 clear = selected.nextClearBit(next);
242 rlist.add(new int[] { next, clear - 1 });
243 next = selected.nextSetBit(clear);
249 public int hashCode()
251 // TODO Auto-generated method stub
252 return selected.hashCode();
256 public boolean equals(Object obj)
258 if (obj instanceof IntList)
260 return ((IntList) obj).selected.equals(selected);
266 IntList selection = new IntList();
269 * Add a column to the selection
274 public void addElement(int col)
280 * clears column selection
288 * Removes value 'col' from the selection (not the col'th item)
291 * index of column to be removed
293 public void removeElement(int col)
295 selection.remove(col);
299 * removes a range of columns from the selection
302 * int - first column in range to be removed
306 public void removeElements(int start, int end)
309 for (int i = start; i < end; i++)
311 colInt = new Integer(i);
312 if (selection.contains(colInt))
314 selection.remove(colInt);
320 * Returns a read-only view of the (possibly empty) list of selected columns
322 * The list contains no duplicates but is not necessarily ordered. It also may
323 * include columns hidden from the current view. To modify (for example sort)
324 * the list, you should first make a copy.
326 * The list is not thread-safe: iterating over it could result in
327 * ConcurrentModificationException if it is modified by another thread.
329 public List<Integer> getSelected()
331 return selection.getList();
335 * @return list of int arrays containing start and end column position for
336 * runs of selected columns ordered from right to left.
338 public List<int[]> getSelectedRanges()
340 return selection.getRanges();
346 * index to search for in column selection
348 * @return true if col is selected
350 public boolean contains(int col)
352 return (col > -1) ? selection.isSelected(col) : false;
356 * Answers true if no columns are selected, else false
358 public boolean isEmpty()
360 return selection == null || selection.isEmpty();
364 * rightmost selected column
366 * @return rightmost column in alignment that is selected
370 if (selection.isEmpty())
374 return selection.getMaxColumn();
378 * Leftmost column in selection
380 * @return column index of leftmost column in selection
384 if (selection.isEmpty())
388 return selection.getMinColumn();
391 public void hideSelectedColumns(AlignmentI al)
393 synchronized (selection)
395 for (int[] selregions : selection.getRanges())
397 al.getHiddenColumns().hideColumns(selregions[0], selregions[1]);
406 * Hides the specified column and any adjacent selected columns
411 public void hideSelectedColumns(int col, HiddenColumns hidden)
414 * deselect column (whether selected or not!)
419 * find adjacent selected columns
421 int min = col - 1, max = col + 1;
422 while (contains(min))
428 while (contains(max))
435 * min, max are now the closest unselected columns
444 hidden.hideColumns(min, max);
456 public ColumnSelection(ColumnSelection copy)
460 selection = new IntList(copy.selection);
467 public ColumnSelection()
477 * Invert the column selection from first to end-1. leaves hiddenColumns
478 * untouched (and unselected)
483 public void invertColumnSelection(int first, int width, AlignmentI al)
485 boolean hasHidden = al.getHiddenColumns().hasHidden();
486 for (int i = first; i < width; i++)
494 if (!hasHidden || al.getHiddenColumns().isVisible(i))
503 * set the selected columns to the given column selection, excluding any
504 * columns that are hidden.
508 public void setElementsFrom(ColumnSelection colsel,
509 HiddenColumns hiddenColumns)
511 selection = new IntList();
512 if (colsel.selection != null && colsel.selection.size() > 0)
514 if (hiddenColumns.hasHidden())
516 // only select visible columns in this columns selection
517 for (Integer col : colsel.getSelected())
519 if (hiddenColumns != null
520 && hiddenColumns.isVisible(col.intValue()))
528 // add everything regardless
529 for (Integer col : colsel.getSelected())
540 * @return true if there are columns marked
542 public boolean hasSelectedColumns()
544 return (selection != null && selection.size() > 0);
549 public boolean filterAnnotations(Annotation[] annotations,
550 AnnotationFilterParameter filterParams)
552 // JBPNote - this method needs to be refactored to become independent of
558 if (annotations[count] != null)
561 boolean itemMatched = false;
563 if (filterParams.getThresholdType() == AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD
564 && annotations[count].value >= filterParams
565 .getThresholdValue())
569 if (filterParams.getThresholdType() == AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD
570 && annotations[count].value <= filterParams
571 .getThresholdValue())
576 if (filterParams.isFilterAlphaHelix()
577 && annotations[count].secondaryStructure == 'H')
582 if (filterParams.isFilterBetaSheet()
583 && annotations[count].secondaryStructure == 'E')
588 if (filterParams.isFilterTurn()
589 && annotations[count].secondaryStructure == 'S')
594 String regexSearchString = filterParams.getRegexString();
595 if (regexSearchString != null
596 && !filterParams.getRegexSearchFields().isEmpty())
598 List<SearchableAnnotationField> fields = filterParams
599 .getRegexSearchFields();
602 if (fields.contains(SearchableAnnotationField.DISPLAY_STRING)
603 && annotations[count].displayCharacter
604 .matches(regexSearchString))
608 } catch (java.util.regex.PatternSyntaxException pse)
610 if (annotations[count].displayCharacter
611 .equals(regexSearchString))
616 if (fields.contains(SearchableAnnotationField.DESCRIPTION)
617 && annotations[count].description != null
618 && annotations[count].description
619 .matches(regexSearchString))
627 this.addElement(count);
631 } while (count < annotations.length);
636 * Returns a hashCode built from selected columns ranges
639 public int hashCode()
641 return selection.hashCode();
645 * Answers true if comparing to a ColumnSelection with the same selected
646 * columns and hidden columns, else false
649 public boolean equals(Object obj)
651 if (!(obj instanceof ColumnSelection))
655 ColumnSelection that = (ColumnSelection) obj;
658 * check columns selected are either both null, or match
660 if (this.selection == null)
662 if (that.selection != null)
667 if (!this.selection.equals(that.selection))
676 * Updates the column selection depending on the parameters, and returns true
677 * if any change was made to the selection
679 * @param markedColumns
680 * a set identifying marked columns (base 0)
682 * the first column of the range to operate over (base 0)
684 * the last column of the range to operate over (base 0)
686 * if true, deselect marked columns and select unmarked
687 * @param extendCurrent
688 * if true, extend rather than replacing the current column selection
690 * if true, toggle the selection state of marked columns
694 public boolean markColumns(BitSet markedColumns, int startCol,
695 int endCol, boolean invert, boolean extendCurrent, boolean toggle)
697 boolean changed = false;
698 if (!extendCurrent && !toggle)
700 changed = !this.isEmpty();
705 // invert only in the currently selected sequence region
706 int i = markedColumns.nextClearBit(startCol);
707 int ibs = markedColumns.nextSetBit(startCol);
708 while (i >= startCol && i <= endCol)
710 if (ibs < 0 || i < ibs)
713 if (toggle && contains(i))
724 i = markedColumns.nextClearBit(ibs);
725 ibs = markedColumns.nextSetBit(i);
731 int i = markedColumns.nextSetBit(startCol);
732 while (i >= startCol && i <= endCol)
735 if (toggle && contains(i))
743 i = markedColumns.nextSetBit(i + 1);
750 * Adjusts column selections, and the given selection group, to match the
751 * range of a stretch (e.g. mouse drag) operation
753 * Method refactored from ScalePanel.mouseDragged
756 * current column position, adjusted for hidden columns
758 * current selection group
760 * start position of the stretch group
762 * end position of the stretch group
764 public void stretchGroup(int res, SequenceGroup sg, int min, int max)
771 if (res > sg.getStartRes())
773 // expand selection group to the right
776 if (res < sg.getStartRes())
778 // expand selection group to the left
783 * expand or shrink column selection to match the
784 * range of the drag operation
786 for (int col = min; col <= max; col++)
788 if (col < sg.getStartRes() || col > sg.getEndRes())
790 // shrinking drag - remove from selection
795 // expanding drag - add to selection