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]);
405 * Hides the specified column and any adjacent selected columns
410 public void hideSelectedColumns(int col, HiddenColumns hidden)
413 * deselect column (whether selected or not!)
418 * find adjacent selected columns
420 int min = col - 1, max = col + 1;
421 while (contains(min))
427 while (contains(max))
434 * min, max are now the closest unselected columns
443 hidden.hideColumns(min, max);
451 public ColumnSelection(ColumnSelection copy)
455 selection = new IntList(copy.selection);
462 public ColumnSelection()
467 * Invert the column selection from first to end-1. leaves hiddenColumns
468 * untouched (and unselected)
473 public void invertColumnSelection(int first, int width, AlignmentI al)
475 boolean hasHidden = al.getHiddenColumns().hasHiddenColumns();
476 for (int i = first; i < width; i++)
484 if (!hasHidden || al.getHiddenColumns().isVisible(i))
493 * set the selected columns to the given column selection, excluding any
494 * columns that are hidden.
498 public void setElementsFrom(ColumnSelection colsel,
499 HiddenColumns hiddenColumns)
501 selection = new IntList();
502 if (colsel.selection != null && colsel.selection.size() > 0)
504 if (hiddenColumns.hasHiddenColumns())
506 // only select visible columns in this columns selection
507 for (Integer col : colsel.getSelected())
509 if (hiddenColumns != null
510 && hiddenColumns.isVisible(col.intValue()))
518 // add everything regardless
519 for (Integer col : colsel.getSelected())
529 * @return true if there are columns marked
531 public boolean hasSelectedColumns()
533 return (selection != null && selection.size() > 0);
536 public boolean filterAnnotations(Annotation[] annotations,
537 AnnotationFilterParameter filterParams)
539 // JBPNote - this method needs to be refactored to become independent of
545 if (annotations[count] != null)
548 boolean itemMatched = false;
551 .getThresholdType() == AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD
552 && annotations[count].value >= filterParams
553 .getThresholdValue())
558 .getThresholdType() == AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD
559 && annotations[count].value <= filterParams
560 .getThresholdValue())
565 if (filterParams.isFilterAlphaHelix()
566 && annotations[count].secondaryStructure == 'H')
571 if (filterParams.isFilterBetaSheet()
572 && annotations[count].secondaryStructure == 'E')
577 if (filterParams.isFilterTurn()
578 && annotations[count].secondaryStructure == 'S')
583 String regexSearchString = filterParams.getRegexString();
584 if (regexSearchString != null
585 && !filterParams.getRegexSearchFields().isEmpty())
587 List<SearchableAnnotationField> fields = filterParams
588 .getRegexSearchFields();
591 if (fields.contains(SearchableAnnotationField.DISPLAY_STRING)
592 && annotations[count].displayCharacter
593 .matches(regexSearchString))
597 } catch (java.util.regex.PatternSyntaxException pse)
599 if (annotations[count].displayCharacter
600 .equals(regexSearchString))
605 if (fields.contains(SearchableAnnotationField.DESCRIPTION)
606 && annotations[count].description != null
607 && annotations[count].description
608 .matches(regexSearchString))
616 this.addElement(count);
620 } while (count < annotations.length);
625 * Returns a hashCode built from selected columns ranges
628 public int hashCode()
630 return selection.hashCode();
634 * Answers true if comparing to a ColumnSelection with the same selected
635 * columns and hidden columns, else false
638 public boolean equals(Object obj)
640 if (!(obj instanceof ColumnSelection))
644 ColumnSelection that = (ColumnSelection) obj;
647 * check columns selected are either both null, or match
649 if (this.selection == null)
651 if (that.selection != null)
656 if (!this.selection.equals(that.selection))
665 * Updates the column selection depending on the parameters, and returns true
666 * if any change was made to the selection
668 * @param markedColumns
669 * a set identifying marked columns (base 0)
671 * the first column of the range to operate over (base 0)
673 * the last column of the range to operate over (base 0)
675 * if true, deselect marked columns and select unmarked
676 * @param extendCurrent
677 * if true, extend rather than replacing the current column selection
679 * if true, toggle the selection state of marked columns
683 public boolean markColumns(BitSet markedColumns, int startCol, int endCol,
684 boolean invert, boolean extendCurrent, boolean toggle)
686 boolean changed = false;
687 if (!extendCurrent && !toggle)
689 changed = !this.isEmpty();
694 // invert only in the currently selected sequence region
695 int i = markedColumns.nextClearBit(startCol);
696 int ibs = markedColumns.nextSetBit(startCol);
697 while (i >= startCol && i <= endCol)
699 if (ibs < 0 || i < ibs)
702 if (toggle && contains(i))
713 i = markedColumns.nextClearBit(ibs);
714 ibs = markedColumns.nextSetBit(i);
720 int i = markedColumns.nextSetBit(startCol);
721 while (i >= startCol && i <= endCol)
724 if (toggle && contains(i))
732 i = markedColumns.nextSetBit(i + 1);
739 * Adjusts column selections, and the given selection group, to match the
740 * range of a stretch (e.g. mouse drag) operation
742 * Method refactored from ScalePanel.mouseDragged
745 * current column position, adjusted for hidden columns
747 * current selection group
749 * start position of the stretch group
751 * end position of the stretch group
753 public void stretchGroup(int res, SequenceGroup sg, int min, int max)
760 if (res > sg.getStartRes())
762 // expand selection group to the right
765 if (res < sg.getStartRes())
767 // expand selection group to the left
772 * expand or shrink column selection to match the
773 * range of the drag operation
775 for (int col = min; col <= max; col++)
777 if (col < sg.getStartRes() || col > sg.getEndRes())
779 // shrinking drag - remove from selection
784 // expanding drag - add to selection