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.controller;
23 import java.awt.Color;
24 import java.util.BitSet;
25 import java.util.List;
27 import jalview.analysis.AlignmentSorter;
28 import jalview.api.AlignViewControllerGuiI;
29 import jalview.api.AlignViewControllerI;
30 import jalview.api.AlignViewportI;
31 import jalview.api.AlignmentViewPanel;
32 import jalview.api.FeatureRenderer;
33 import jalview.commands.OrderCommand;
34 import jalview.datamodel.AlignmentI;
35 import jalview.datamodel.ColumnSelection;
36 import jalview.datamodel.SearchResultsI;
37 import jalview.datamodel.SequenceCollectionI;
38 import jalview.datamodel.SequenceFeature;
39 import jalview.datamodel.SequenceGroup;
40 import jalview.datamodel.SequenceI;
41 import jalview.gui.Desktop;
42 import jalview.io.DataSourceType;
43 import jalview.io.FeaturesFile;
44 import jalview.schemes.ColourSchemeI;
45 import jalview.util.MessageManager;
47 public class AlignViewController implements AlignViewControllerI
49 AlignViewportI viewport = null;
51 AlignmentViewPanel alignPanel = null;
54 * the GUI container that is handling interactions with the user
56 private AlignViewControllerGuiI avcg;
58 public AlignViewController(AlignViewControllerGuiI alignFrame,
59 AlignViewportI vp, AlignmentViewPanel ap)
61 this.avcg = alignFrame;
67 public void setViewportAndAlignmentPanel(AlignViewportI vp,
68 AlignmentViewPanel ap)
75 public boolean makeGroupsFromSelection()
77 SequenceGroup sg = viewport.getSelectionGroup();
78 ColumnSelection cs = viewport.getColumnSelection();
79 SequenceGroup[] gps = null;
80 if (sg != null && (cs == null || cs.isEmpty()))
82 gps = jalview.analysis.Grouping.makeGroupsFrom(
83 viewport.getSequenceSelection(),
84 viewport.getAlignmentView(true)
85 .getSequenceStrings(viewport.getGapCharacter()),
86 viewport.getAlignment().getGroups());
92 gps = jalview.analysis.Grouping.makeGroupsFromCols(
93 (sg == null) ? viewport.getAlignment().getSequencesArray()
94 : sg.getSequences().toArray(new SequenceI[0]),
95 cs, viewport.getAlignment().getGroups());
100 viewport.getAlignment().deleteAllGroups();
101 viewport.clearSequenceColours();
102 viewport.setSelectionGroup(null);
103 ColourSchemeI colours = viewport.getGlobalColourScheme();
104 // set view properties for each group
105 for (int g = 0; g < gps.length; g++)
107 // gps[g].setShowunconserved(viewport.getShowUnconserved());
108 gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
109 viewport.getAlignment().addGroup(gps[g]);
112 gps[g].setColourScheme(colours.getInstance(viewport, gps[g]));
114 Color col = new Color((int) (Math.random() * 255),
115 (int) (Math.random() * 255), (int) (Math.random() * 255));
116 gps[g].idColour = col;
117 viewport.setUpdateStructures(true);
118 viewport.addSequenceGroup(gps[g]);
126 public boolean createGroup()
129 SequenceGroup sg = viewport.getSelectionGroup();
132 viewport.getAlignment().addGroup(sg);
139 public boolean unGroup()
141 SequenceGroup sg = viewport.getSelectionGroup();
144 viewport.getAlignment().deleteGroup(sg);
151 public boolean deleteGroups()
153 if (viewport.getAlignment().getGroups() != null
154 && viewport.getAlignment().getGroups().size() > 0)
156 viewport.getAlignment().deleteAllGroups();
157 viewport.clearSequenceColours();
158 viewport.setSelectionGroup(null);
165 public boolean markColumnsContainingFeatures(boolean invert,
166 boolean extendCurrent, boolean toggle, String featureType)
168 // JBPNote this routine could also mark rows, not just columns.
169 // need a decent query structure to allow all types of feature searches
170 BitSet bs = new BitSet();
171 boolean searchSelection = viewport.getSelectionGroup() != null
173 SequenceCollectionI sqcol = searchSelection
174 ? viewport.getSelectionGroup()
175 : viewport.getAlignment();
177 int nseq = findColumnsWithFeature(featureType, sqcol, bs);
179 ColumnSelection cs = viewport.getColumnSelection();
182 cs = new ColumnSelection();
185 if (bs.cardinality() > 0 || invert)
187 boolean changed = cs.markColumns(bs, sqcol.getStartRes(),
188 sqcol.getEndRes(), invert, extendCurrent, toggle);
191 viewport.setColumnSelection(cs);
192 alignPanel.paintAlignment(false, false);
193 int columnCount = invert
194 ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
197 avcg.setStatus(MessageManager.formatMessage(
198 "label.view_controller_toggled_marked", new String[]
199 { toggle ? MessageManager.getString("label.toggled")
200 : MessageManager.getString("label.marked"),
201 String.valueOf(columnCount),
202 invert ? MessageManager
203 .getString("label.not_containing")
204 : MessageManager.getString("label.containing"),
205 featureType, Integer.valueOf(nseq).toString() }));
211 String key = searchSelection ? "label.no_feature_found_selection"
212 : "label.no_feature_of_type_found";
214 MessageManager.formatMessage(key, new String[]
219 alignPanel.paintAlignment(false, false);
226 * Sets a bit in the BitSet for each column (base 0) in the sequence
227 * collection which includes a visible feature of the specified feature type.
228 * Returns the number of sequences which have the feature visible in the
236 int findColumnsWithFeature(String featureType, SequenceCollectionI sqcol,
239 FeatureRenderer fr = alignPanel == null ? null
240 : alignPanel.getFeatureRenderer();
242 final int startColumn = sqcol.getStartRes() + 1; // converted to base 1
243 final int endColumn = sqcol.getEndRes() + 1;
244 List<SequenceI> seqs = sqcol.getSequences();
246 for (SequenceI sq : seqs)
250 // int ist = sq.findPosition(sqcol.getStartRes());
251 List<SequenceFeature> sfs = sq.findFeatures(startColumn, endColumn,
254 boolean found = false;
255 for (SequenceFeature sf : sfs)
257 if (fr.getColour(sf) == null)
267 int sfStartCol = sq.findIndex(sf.getBegin());
268 int sfEndCol = sq.findIndex(sf.getEnd());
270 if (sf.isContactFeature())
273 * 'contact' feature - check for 'start' or 'end'
274 * position within the selected region
276 if (sfStartCol >= startColumn && sfStartCol <= endColumn)
278 bs.set(sfStartCol - 1);
280 if (sfEndCol >= startColumn && sfEndCol <= endColumn)
282 bs.set(sfEndCol - 1);
288 * contiguous feature - select feature positions (if any)
289 * within the selected region
291 if (sfStartCol < startColumn)
293 sfStartCol = startColumn;
295 // not sure what the point of this is
296 // if (sfStartCol < ist)
300 if (sfEndCol > endColumn)
302 sfEndCol = endColumn;
304 for (; sfStartCol <= sfEndCol; sfStartCol++)
306 bs.set(sfStartCol - 1); // convert to base 0
315 public void sortAlignmentByFeatureDensity(List<String> typ)
317 String methodText = MessageManager.getString("label.sort_by_density");
318 sortByFeatures(typ, methodText, AlignmentSorter.FEATURE_DENSITY);
322 * Sorts the alignment (or current selection) by either average score or
323 * density of the specified feature types, and adds to the command history. If
324 * {@code types} is null, all visible feature types are used for the sort. If
325 * no feature types apply, does nothing.
329 * - text shown in Undo/Redo command
331 * - passed to jalview.analysis.AlignmentSorter.sortByFeatures()
333 protected void sortByFeatures(List<String> types, String methodText,
336 FeatureRenderer fr = alignPanel.getFeatureRenderer();
337 if (types == null && fr != null)
339 types = fr.getDisplayedFeatureTypes();
343 return; // nothing to do
345 List<String> gps = null;
348 gps = fr.getDisplayedFeatureGroups();
350 AlignmentI al = viewport.getAlignment();
353 SequenceGroup sg = viewport.getSelectionGroup();
356 start = sg.getStartRes();
357 stop = sg.getEndRes();
362 stop = al.getWidth();
364 SequenceI[] oldOrder = al.getSequencesArray();
365 AlignmentSorter.sortByFeature(types, gps, start, stop, al, method);
366 avcg.addHistoryItem(new OrderCommand(methodText, oldOrder,
367 viewport.getAlignment()));
368 alignPanel.paintAlignment(true, false);
373 public void sortAlignmentByFeatureScore(List<String> typ)
375 String methodText = MessageManager.getString("label.sort_by_score");
376 sortByFeatures(typ, methodText, AlignmentSorter.FEATURE_SCORE);
380 public boolean parseFeaturesFile(Object file, DataSourceType protocol,
381 boolean relaxedIdMatching)
383 boolean featuresAdded = false;
384 FeatureRenderer fr = alignPanel.getFeatureRenderer();
387 featuresAdded = new FeaturesFile(false, file, protocol).parse(
388 viewport.getAlignment().getDataset(), fr.getFeatureColours(),
389 fr.getFeatureFilters(), false, relaxedIdMatching);
390 } catch (Exception ex)
392 ex.printStackTrace();
397 avcg.refreshFeatureUI(true);
400 // update the min/max ranges where necessary
401 fr.findAllFeatures(true);
403 if (avcg.getFeatureSettingsUI() != null)
405 avcg.getFeatureSettingsUI().discoverAllFeatureData();
407 alignPanel.paintAlignment(true, true);
410 return featuresAdded;
415 public boolean markHighlightedColumns(boolean invert,
416 boolean extendCurrent, boolean toggle)
418 if (!viewport.hasSearchResults())
420 // do nothing if no selection exists
423 // JBPNote this routine could also mark rows, not just columns.
424 BitSet bs = new BitSet();
425 SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null
426 || extendCurrent) ? viewport.getAlignment()
427 : viewport.getSelectionGroup();
429 // this could be a lambda... - the remains of the method is boilerplate,
430 // except for the different messages for reporting selection.
431 int nseq = viewport.getSearchResults().markColumns(sqcol, bs);
433 ColumnSelection cs = viewport.getColumnSelection();
436 cs = new ColumnSelection();
439 if (bs.cardinality() > 0 || invert)
441 boolean changed = cs.markColumns(bs, sqcol.getStartRes(),
442 sqcol.getEndRes(), invert, extendCurrent, toggle);
445 viewport.setColumnSelection(cs);
446 alignPanel.paintAlignment(false, false);
447 int columnCount = invert
448 ? (sqcol.getEndRes() - sqcol.getStartRes() + 1)
451 avcg.setStatus(MessageManager.formatMessage(
452 "label.view_controller_toggled_marked", new String[]
453 { toggle ? MessageManager.getString("label.toggled")
454 : MessageManager.getString("label.marked"),
455 String.valueOf(columnCount),
456 invert ? MessageManager
457 .getString("label.not_containing")
458 : MessageManager.getString("label.containing"),
459 "Highlight", Integer.valueOf(nseq).toString() }));
465 avcg.setStatus(MessageManager
466 .getString("label.no_highlighted_regions_marked"));
470 alignPanel.paintAlignment(false, false);
477 public boolean copyHighlightedRegionsToClipboard()
479 if (!viewport.hasSearchResults())
481 // do nothing if no selection exists
485 SearchResultsI searchResults = viewport.getSearchResults();
486 if (searchResults.isEmpty())
488 return false; // shouldn't happen
490 List<SequenceI> seqs = searchResults.getMatchingSubSequences();
492 // TODO: pass in hiddenColumns according to intersection of searchResults
493 // and visible columns. Currently this isn't done, since each contig becomes
494 // a single subsequence
495 Desktop.jalviewClipboard = new Object[] {
496 seqs.toArray(new SequenceI[0]),
497 alignPanel.getAlignment().getDataset(), null };
498 avcg.setStatus(MessageManager.formatMessage(
499 "label.copied_sequences_to_clipboard", seqs.size()));
500 // Technically we should return false, since view has not changed