Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
AlignViewport | 82 | 274 | 123 |
1 | /* | |
2 | * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) | |
3 | * Copyright (C) $$Year-Rel$$ The Jalview Authors | |
4 | * | |
5 | * This file is part of Jalview. | |
6 | * | |
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. | |
11 | * | |
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. | |
16 | * | |
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. | |
20 | */ | |
21 | package jalview.gui; | |
22 | ||
23 | import java.awt.Container; | |
24 | import java.awt.Dimension; | |
25 | import java.awt.Font; | |
26 | import java.awt.FontMetrics; | |
27 | import java.awt.Rectangle; | |
28 | import java.io.File; | |
29 | import java.util.ArrayList; | |
30 | import java.util.Hashtable; | |
31 | import java.util.List; | |
32 | ||
33 | import javax.swing.JInternalFrame; | |
34 | ||
35 | import jalview.analysis.AlignmentUtils; | |
36 | import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; | |
37 | import jalview.api.AlignViewportI; | |
38 | import jalview.api.AlignmentViewPanel; | |
39 | import jalview.api.FeatureColourI; | |
40 | import jalview.api.FeatureSettingsModelI; | |
41 | import jalview.api.FeaturesDisplayedI; | |
42 | import jalview.api.ViewStyleI; | |
43 | import jalview.bin.Cache; | |
44 | import jalview.bin.Console; | |
45 | import jalview.commands.CommandI; | |
46 | import jalview.datamodel.AlignedCodonFrame; | |
47 | import jalview.datamodel.Alignment; | |
48 | import jalview.datamodel.AlignmentI; | |
49 | import jalview.datamodel.ColumnSelection; | |
50 | import jalview.datamodel.ContactMatrixI; | |
51 | import jalview.datamodel.HiddenColumns; | |
52 | import jalview.datamodel.SearchResults; | |
53 | import jalview.datamodel.SearchResultsI; | |
54 | import jalview.datamodel.SequenceGroup; | |
55 | import jalview.datamodel.SequenceI; | |
56 | import jalview.io.AppletFormatAdapter; | |
57 | import jalview.io.DataSourceType; | |
58 | import jalview.io.FileFormatException; | |
59 | import jalview.io.FileFormatI; | |
60 | import jalview.io.FileFormats; | |
61 | import jalview.io.FileLoader; | |
62 | import jalview.io.IdentifyFile; | |
63 | import jalview.renderer.ResidueShader; | |
64 | import jalview.schemes.ColourSchemeI; | |
65 | import jalview.schemes.ColourSchemeProperty; | |
66 | import jalview.schemes.ResidueColourScheme; | |
67 | import jalview.schemes.UserColourScheme; | |
68 | import jalview.structure.SelectionSource; | |
69 | import jalview.structure.StructureSelectionManager; | |
70 | import jalview.structure.VamsasSource; | |
71 | import jalview.util.ColorUtils; | |
72 | import jalview.util.MessageManager; | |
73 | import jalview.viewmodel.AlignmentViewport; | |
74 | import jalview.ws.params.AutoCalcSetting; | |
75 | ||
76 | /** | |
77 | * DOCUMENT ME! | |
78 | * | |
79 | * @author $author$ | |
80 | * @version $Revision: 1.141 $ | |
81 | */ | |
82 | public class AlignViewport extends AlignmentViewport | |
83 | implements SelectionSource | |
84 | { | |
85 | Font font; | |
86 | ||
87 | boolean cursorMode = false; | |
88 | ||
89 | boolean antiAlias = false; | |
90 | ||
91 | private Rectangle explodedGeometry = null; | |
92 | ||
93 | private String viewName = null; | |
94 | ||
95 | /* | |
96 | * Flag set true on the view that should 'gather' multiple views of the same | |
97 | * sequence set id when a project is reloaded. Set false on all views when | |
98 | * they are 'exploded' into separate windows. Set true on the current view | |
99 | * when 'Gather' is performed, and also on the first tab when the first new | |
100 | * view is created. | |
101 | */ | |
102 | private boolean gatherViewsHere = false; | |
103 | ||
104 | private AnnotationColumnChooser annotationColumnSelectionState; | |
105 | ||
106 | /** | |
107 | * Creates a new AlignViewport object. | |
108 | * | |
109 | * @param al | |
110 | * alignment to view | |
111 | */ | |
112 | 38 | public AlignViewport(AlignmentI al) |
113 | { | |
114 | 38 | super(al); |
115 | 38 | init(); |
116 | } | |
117 | ||
118 | /** | |
119 | * Create a new AlignViewport object with a specific sequence set ID | |
120 | * | |
121 | * @param al | |
122 | * @param seqsetid | |
123 | * (may be null - but potential for ambiguous constructor exception) | |
124 | */ | |
125 | 0 | public AlignViewport(AlignmentI al, String seqsetid) |
126 | { | |
127 | 0 | this(al, seqsetid, null); |
128 | } | |
129 | ||
130 | 0 | public AlignViewport(AlignmentI al, String seqsetid, String viewid) |
131 | { | |
132 | 0 | super(al); |
133 | 0 | sequenceSetID = seqsetid; |
134 | 0 | viewId = viewid; |
135 | // TODO remove these once 2.4.VAMSAS release finished | |
136 | 0 | if (seqsetid != null) |
137 | { | |
138 | 0 | Console.debug( |
139 | "Setting viewport's sequence set id : " + sequenceSetID); | |
140 | } | |
141 | 0 | if (viewId != null) |
142 | { | |
143 | 0 | Console.debug("Setting viewport's view id : " + viewId); |
144 | } | |
145 | 0 | init(); |
146 | ||
147 | } | |
148 | ||
149 | /** | |
150 | * Create a new AlignViewport with hidden regions | |
151 | * | |
152 | * @param al | |
153 | * AlignmentI | |
154 | * @param hiddenColumns | |
155 | * ColumnSelection | |
156 | */ | |
157 | 28 | public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns) |
158 | { | |
159 | 28 | super(al); |
160 | 28 | if (hiddenColumns != null) |
161 | { | |
162 | 28 | al.setHiddenColumns(hiddenColumns); |
163 | } | |
164 | 28 | init(); |
165 | } | |
166 | ||
167 | /** | |
168 | * New viewport with hidden columns and an existing sequence set id | |
169 | * | |
170 | * @param al | |
171 | * @param hiddenColumns | |
172 | * @param seqsetid | |
173 | * (may be null) | |
174 | */ | |
175 | 0 | public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns, |
176 | String seqsetid) | |
177 | { | |
178 | 0 | this(al, hiddenColumns, seqsetid, null); |
179 | } | |
180 | ||
181 | /** | |
182 | * New viewport with hidden columns and an existing sequence set id and viewid | |
183 | * | |
184 | * @param al | |
185 | * @param hiddenColumns | |
186 | * @param seqsetid | |
187 | * (may be null) | |
188 | * @param viewid | |
189 | * (may be null) | |
190 | */ | |
191 | 442 | public AlignViewport(AlignmentI al, HiddenColumns hiddenColumns, |
192 | String seqsetid, String viewid) | |
193 | { | |
194 | 442 | super(al); |
195 | 442 | sequenceSetID = seqsetid; |
196 | 442 | viewId = viewid; |
197 | // TODO remove these once 2.4.VAMSAS release finished | |
198 | 442 | if (seqsetid != null) |
199 | { | |
200 | 85 | Console.debug( |
201 | "Setting viewport's sequence set id : " + sequenceSetID); | |
202 | } | |
203 | 442 | if (viewId != null) |
204 | { | |
205 | 75 | Console.debug("Setting viewport's view id : " + viewId); |
206 | } | |
207 | ||
208 | 442 | if (hiddenColumns != null) |
209 | { | |
210 | 0 | al.setHiddenColumns(hiddenColumns); |
211 | } | |
212 | 442 | init(); |
213 | } | |
214 | ||
215 | /** | |
216 | * Apply any settings saved in user preferences | |
217 | */ | |
218 | 508 | private void applyViewProperties() |
219 | { | |
220 | 508 | antiAlias = Cache.getDefault("ANTI_ALIAS", true); |
221 | ||
222 | 508 | viewStyle.setShowJVSuffix(Cache.getDefault("SHOW_JVSUFFIX", true)); |
223 | 508 | setShowAnnotation(Cache.getDefault("SHOW_ANNOTATIONS", true)); |
224 | ||
225 | 508 | setRightAlignIds(Cache.getDefault("RIGHT_ALIGN_IDS", false)); |
226 | 508 | setCentreColumnLabels(Cache.getDefault("CENTRE_COLUMN_LABELS", false)); |
227 | 508 | autoCalculateConsensus = Cache.getDefault("AUTO_CALC_CONSENSUS", true); |
228 | ||
229 | 508 | setPadGaps(Cache.getDefault("PAD_GAPS", true)); |
230 | 508 | setShowNPFeats(Cache.getDefault("SHOW_NPFEATS_TOOLTIP", true)); |
231 | 508 | setShowDBRefs(Cache.getDefault("SHOW_DBREFS_TOOLTIP", true)); |
232 | 508 | viewStyle.setSeqNameItalics(Cache.getDefault("ID_ITALICS", true)); |
233 | 508 | viewStyle.setWrapAlignment(Cache.getDefault("WRAP_ALIGNMENT", false)); |
234 | 508 | viewStyle.setShowUnconserved( |
235 | Cache.getDefault("SHOW_UNCONSERVED", false)); | |
236 | 508 | sortByTree = Cache.getDefault("SORT_BY_TREE", false); |
237 | 508 | followSelection = Cache.getDefault("FOLLOW_SELECTIONS", true); |
238 | 508 | sortAnnotationsBy = SequenceAnnotationOrder |
239 | .valueOf(Cache.getDefault(Preferences.SORT_ANNOTATIONS, | |
240 | SequenceAnnotationOrder.NONE.name())); | |
241 | 508 | showAutocalculatedAbove = Cache |
242 | .getDefault(Preferences.SHOW_AUTOCALC_ABOVE, false); | |
243 | 508 | viewStyle.setScaleProteinAsCdna( |
244 | Cache.getDefault(Preferences.SCALE_PROTEIN_TO_CDNA, true)); | |
245 | 508 | viewStyle.setShowStructureProvider( |
246 | Cache.getDefault(Preferences.SHOW_STRUC_PROVIDER, false)); | |
247 | } | |
248 | ||
249 | 508 | void init() |
250 | { | |
251 | 508 | applyViewProperties(); |
252 | ||
253 | 508 | String fontName = Cache.getDefault("FONT_NAME", "SansSerif"); |
254 | 508 | String fontStyle = Cache.getDefault("FONT_STYLE", Font.PLAIN + ""); |
255 | 508 | String fontSize = Cache.getDefault("FONT_SIZE", "10"); |
256 | ||
257 | 508 | int style = 0; |
258 | ||
259 | 508 | if (fontStyle.equals("bold")) |
260 | { | |
261 | 0 | style = 1; |
262 | } | |
263 | 508 | else if (fontStyle.equals("italic")) |
264 | { | |
265 | 0 | style = 2; |
266 | } | |
267 | ||
268 | 508 | setFont(new Font(fontName, style, Integer.parseInt(fontSize)), true); |
269 | ||
270 | 508 | alignment |
271 | .setGapCharacter(Cache.getDefault("GAP_SYMBOL", "-").charAt(0)); | |
272 | ||
273 | // We must set conservation and consensus before setting colour, | |
274 | // as Blosum and Clustal require this to be done | |
275 | 508 | if (hconsensus == null && !isDataset) |
276 | { | |
277 | 508 | if (!alignment.isNucleotide()) |
278 | { | |
279 | 397 | showConservation = Cache.getDefault("SHOW_CONSERVATION", true); |
280 | 397 | showQuality = Cache.getDefault("SHOW_QUALITY", true); |
281 | 397 | showGroupConservation = Cache.getDefault("SHOW_GROUP_CONSERVATION", |
282 | false); | |
283 | } | |
284 | 508 | showConsensusHistogram = Cache.getDefault("SHOW_CONSENSUS_HISTOGRAM", |
285 | true); | |
286 | 508 | showSequenceLogo = Cache.getDefault("SHOW_CONSENSUS_LOGO", true); |
287 | 508 | normaliseSequenceLogo = Cache.getDefault("NORMALISE_CONSENSUS_LOGO", |
288 | false); | |
289 | 508 | showGroupConsensus = Cache.getDefault("SHOW_GROUP_CONSENSUS", false); |
290 | 508 | showConsensus = Cache.getDefault("SHOW_IDENTITY", true); |
291 | ||
292 | 508 | showSSConsensus = Cache.getDefault("SHOW_SS_CONSENSUS", false); |
293 | ||
294 | 508 | showOccupancy = Cache.getDefault(Preferences.SHOW_OCCUPANCY, true); |
295 | } | |
296 | 508 | initAutoAnnotation(); |
297 | 508 | String colourProperty = alignment.isNucleotide() |
298 | ? Preferences.DEFAULT_COLOUR_NUC | |
299 | : Preferences.DEFAULT_COLOUR_PROT; | |
300 | 508 | String schemeName = Cache.getProperty(colourProperty); |
301 | 508 | if (schemeName == null) |
302 | { | |
303 | // only DEFAULT_COLOUR available in Jalview before 2.9 | |
304 | 497 | schemeName = Cache.getDefault(Preferences.DEFAULT_COLOUR, |
305 | ResidueColourScheme.NONE); | |
306 | } | |
307 | 508 | ColourSchemeI colourScheme = ColourSchemeProperty.getColourScheme(this, |
308 | alignment, schemeName); | |
309 | 508 | residueShading = new ResidueShader(colourScheme); |
310 | ||
311 | 508 | if (colourScheme instanceof UserColourScheme) |
312 | { | |
313 | 0 | residueShading = new ResidueShader( |
314 | UserDefinedColours.loadDefaultColours()); | |
315 | 0 | residueShading.setThreshold(0, isIgnoreGapsConsensus()); |
316 | } | |
317 | ||
318 | 508 | if (residueShading != null) |
319 | { | |
320 | 508 | residueShading.setConsensus(hconsensus); |
321 | 508 | residueShading.setSSConsensusProfileMap(hSSConsensusProfileMap); |
322 | } | |
323 | 508 | setColourAppliesToAllGroups(true); |
324 | } | |
325 | ||
326 | boolean validCharWidth; | |
327 | ||
328 | /** | |
329 | * {@inheritDoc} | |
330 | */ | |
331 | 682 | @Override |
332 | public void setFont(Font f, boolean setGrid) | |
333 | { | |
334 | 682 | font = f; |
335 | ||
336 | 682 | Container c = new Container(); |
337 | ||
338 | 682 | if (setGrid) |
339 | { | |
340 | 554 | FontMetrics fm = c.getFontMetrics(font); |
341 | 554 | int ww = fm.charWidth('M'); |
342 | 554 | setCharHeight(fm.getHeight()); |
343 | 554 | setCharWidth(ww); |
344 | } | |
345 | 682 | viewStyle.setFontName(font.getName()); |
346 | 682 | viewStyle.setFontStyle(font.getStyle()); |
347 | 682 | viewStyle.setFontSize(font.getSize()); |
348 | ||
349 | 682 | validCharWidth = true; |
350 | } | |
351 | ||
352 | 86 | @Override |
353 | public void setViewStyle(ViewStyleI settingsForView) | |
354 | { | |
355 | 86 | super.setViewStyle(settingsForView); |
356 | 86 | setFont(new Font(viewStyle.getFontName(), viewStyle.getFontStyle(), |
357 | viewStyle.getFontSize()), false); | |
358 | } | |
359 | ||
360 | /** | |
361 | * DOCUMENT ME! | |
362 | * | |
363 | * @return DOCUMENT ME! | |
364 | */ | |
365 | 31118 | public Font getFont() |
366 | { | |
367 | 31118 | return font; |
368 | } | |
369 | ||
370 | /** | |
371 | * DOCUMENT ME! | |
372 | * | |
373 | * @param align | |
374 | * DOCUMENT ME! | |
375 | */ | |
376 | 761 | @Override |
377 | public void setAlignment(AlignmentI align) | |
378 | { | |
379 | 761 | replaceMappings(align); |
380 | 761 | super.setAlignment(align); |
381 | } | |
382 | ||
383 | /** | |
384 | * Replace any codon mappings for this viewport with those for the given | |
385 | * viewport | |
386 | * | |
387 | * @param align | |
388 | */ | |
389 | 771 | public void replaceMappings(AlignmentI align) |
390 | { | |
391 | ||
392 | /* | |
393 | * Deregister current mappings (if any) | |
394 | */ | |
395 | 771 | deregisterMappings(); |
396 | ||
397 | /* | |
398 | * Register new mappings (if any) | |
399 | */ | |
400 | 771 | if (align != null) |
401 | { | |
402 | 518 | StructureSelectionManager ssm = StructureSelectionManager |
403 | .getStructureSelectionManager(Desktop.instance); | |
404 | 518 | ssm.registerMappings(align.getCodonFrames()); |
405 | } | |
406 | ||
407 | /* | |
408 | * replace mappings on our alignment | |
409 | */ | |
410 | 771 | if (alignment != null && align != null) |
411 | { | |
412 | 10 | alignment.setCodonFrames(align.getCodonFrames()); |
413 | } | |
414 | } | |
415 | ||
416 | 771 | protected void deregisterMappings() |
417 | { | |
418 | 771 | AlignmentI al = getAlignment(); |
419 | 771 | if (al != null) |
420 | { | |
421 | 263 | List<AlignedCodonFrame> mappings = al.getCodonFrames(); |
422 | 263 | if (mappings != null) |
423 | { | |
424 | 263 | StructureSelectionManager ssm = StructureSelectionManager |
425 | .getStructureSelectionManager(Desktop.instance); | |
426 | 263 | for (AlignedCodonFrame acf : mappings) |
427 | { | |
428 | 125 | if (noReferencesTo(acf)) |
429 | { | |
430 | 27 | ssm.deregisterMapping(acf); |
431 | } | |
432 | } | |
433 | } | |
434 | } | |
435 | } | |
436 | ||
437 | /** | |
438 | * DOCUMENT ME! | |
439 | * | |
440 | * @return DOCUMENT ME! | |
441 | */ | |
442 | 32 | @Override |
443 | public char getGapCharacter() | |
444 | { | |
445 | 32 | return getAlignment().getGapCharacter(); |
446 | } | |
447 | ||
448 | /** | |
449 | * DOCUMENT ME! | |
450 | * | |
451 | * @param gap | |
452 | * DOCUMENT ME! | |
453 | */ | |
454 | 0 | public void setGapCharacter(char gap) |
455 | { | |
456 | 0 | if (getAlignment() != null) |
457 | { | |
458 | 0 | getAlignment().setGapCharacter(gap); |
459 | } | |
460 | } | |
461 | ||
462 | /** | |
463 | * get hash of undo and redo list for the alignment | |
464 | * | |
465 | * @return long[] { historyList.hashCode, redoList.hashCode }; | |
466 | */ | |
467 | 1 | public long[] getUndoRedoHash() |
468 | { | |
469 | // TODO: JAL-1126 | |
470 | 1 | if (historyList == null || redoList == null) |
471 | { | |
472 | 0 | return new long[] { -1, -1 }; |
473 | } | |
474 | 1 | return new long[] { historyList.hashCode(), this.redoList.hashCode() }; |
475 | } | |
476 | ||
477 | /** | |
478 | * test if a particular set of hashcodes are different to the hashcodes for | |
479 | * the undo and redo list. | |
480 | * | |
481 | * @param undoredo | |
482 | * the stored set of hashcodes as returned by getUndoRedoHash | |
483 | * @return true if the hashcodes differ (ie the alignment has been edited) or | |
484 | * the stored hashcode array differs in size | |
485 | */ | |
486 | 0 | public boolean isUndoRedoHashModified(long[] undoredo) |
487 | { | |
488 | 0 | if (undoredo == null) |
489 | { | |
490 | 0 | return true; |
491 | } | |
492 | 0 | long[] cstate = getUndoRedoHash(); |
493 | 0 | if (cstate.length != undoredo.length) |
494 | { | |
495 | 0 | return true; |
496 | } | |
497 | ||
498 | 0 | for (int i = 0; i < cstate.length; i++) |
499 | { | |
500 | 0 | if (cstate[i] != undoredo[i]) |
501 | { | |
502 | 0 | return true; |
503 | } | |
504 | } | |
505 | 0 | return false; |
506 | } | |
507 | ||
508 | public boolean followSelection = true; | |
509 | ||
510 | /** | |
511 | * @return true if view selection should always follow the selections | |
512 | * broadcast by other selection sources | |
513 | */ | |
514 | 0 | public boolean getFollowSelection() |
515 | { | |
516 | 0 | return followSelection; |
517 | } | |
518 | ||
519 | /** | |
520 | * Send the current selection to be broadcast to any selection listeners. | |
521 | */ | |
522 | 23 | @Override |
523 | public void sendSelection() | |
524 | { | |
525 | 23 | jalview.structure.StructureSelectionManager |
526 | .getStructureSelectionManager(Desktop.instance) | |
527 | .sendSelection(new SequenceGroup(getSelectionGroup()), | |
528 | new ColumnSelection(getColumnSelection()), | |
529 | new HiddenColumns(getAlignment().getHiddenColumns()), | |
530 | this); | |
531 | } | |
532 | ||
533 | /** | |
534 | * return the alignPanel containing the given viewport. Use this to get the | |
535 | * components currently handling the given viewport. | |
536 | * | |
537 | * @param av | |
538 | * @return null or an alignPanel guaranteed to have non-null alignFrame | |
539 | * reference | |
540 | */ | |
541 | 1732 | public AlignmentPanel getAlignPanel() |
542 | { | |
543 | 1732 | AlignmentPanel[] aps = PaintRefresher |
544 | .getAssociatedPanels(this.getSequenceSetId()); | |
545 | 2250 | for (int p = 0; aps != null && p < aps.length; p++) |
546 | { | |
547 | 1803 | if (aps[p].av == this) |
548 | { | |
549 | 1285 | return aps[p]; |
550 | } | |
551 | } | |
552 | 447 | return null; |
553 | } | |
554 | ||
555 | 16 | public boolean getSortByTree() |
556 | { | |
557 | 16 | return sortByTree; |
558 | } | |
559 | ||
560 | 3 | public void setSortByTree(boolean sort) |
561 | { | |
562 | 3 | sortByTree = sort; |
563 | } | |
564 | ||
565 | /** | |
566 | * Returns the (Desktop) instance of the StructureSelectionManager | |
567 | */ | |
568 | 4515 | @Override |
569 | public StructureSelectionManager getStructureSelectionManager() | |
570 | { | |
571 | 4515 | return StructureSelectionManager |
572 | .getStructureSelectionManager(Desktop.instance); | |
573 | } | |
574 | ||
575 | 3222 | @Override |
576 | public boolean isNormaliseSequenceLogo() | |
577 | { | |
578 | 3222 | return normaliseSequenceLogo; |
579 | } | |
580 | ||
581 | 85 | public void setNormaliseSequenceLogo(boolean state) |
582 | { | |
583 | 85 | normaliseSequenceLogo = state; |
584 | } | |
585 | ||
586 | /** | |
587 | * | |
588 | * @return true if alignment characters should be displayed | |
589 | */ | |
590 | 32399 | @Override |
591 | public boolean isValidCharWidth() | |
592 | { | |
593 | 32399 | return validCharWidth; |
594 | } | |
595 | ||
596 | private Hashtable<String, AutoCalcSetting> calcIdParams = new Hashtable<>(); | |
597 | ||
598 | 9 | public AutoCalcSetting getCalcIdSettingsFor(String calcId) |
599 | { | |
600 | 9 | return calcIdParams.get(calcId); |
601 | } | |
602 | ||
603 | 0 | public void setCalcIdSettingsFor(String calcId, AutoCalcSetting settings, |
604 | boolean needsUpdate) | |
605 | { | |
606 | 0 | calcIdParams.put(calcId, settings); |
607 | // TODO: create a restart list to trigger any calculations that need to be | |
608 | // restarted after load | |
609 | // calculator.getRegisteredWorkersOfClass(settings.getWorkerClass()) | |
610 | 0 | if (needsUpdate) |
611 | { | |
612 | 0 | Console.debug("trigger update for " + calcId); |
613 | } | |
614 | } | |
615 | ||
616 | /** | |
617 | * Method called when another alignment's edit (or possibly other) command is | |
618 | * broadcast to here. | |
619 | * | |
620 | * To allow for sequence mappings (e.g. protein to cDNA), we have to first | |
621 | * 'unwind' the command on the source sequences (in simulation, not in fact), | |
622 | * and then for each edit in turn: | |
623 | * <ul> | |
624 | * <li>compute the equivalent edit on the mapped sequences</li> | |
625 | * <li>apply the mapped edit</li> | |
626 | * <li>'apply' the source edit to the working copy of the source | |
627 | * sequences</li> | |
628 | * </ul> | |
629 | * | |
630 | * @param command | |
631 | * @param undo | |
632 | * @param ssm | |
633 | */ | |
634 | 0 | @Override |
635 | public void mirrorCommand(CommandI command, boolean undo, | |
636 | StructureSelectionManager ssm, VamsasSource source) | |
637 | { | |
638 | /* | |
639 | * Do nothing unless we are a 'complement' of the source. May replace this | |
640 | * with direct calls not via SSM. | |
641 | */ | |
642 | 0 | if (source instanceof AlignViewportI |
643 | && ((AlignViewportI) source).getCodingComplement() == this) | |
644 | { | |
645 | // ok to continue; | |
646 | } | |
647 | else | |
648 | { | |
649 | 0 | return; |
650 | } | |
651 | ||
652 | 0 | CommandI mappedCommand = ssm.mapCommand(command, undo, getAlignment(), |
653 | getGapCharacter()); | |
654 | 0 | if (mappedCommand != null) |
655 | { | |
656 | 0 | AlignmentI[] views = getAlignPanel().alignFrame.getViewAlignments(); |
657 | 0 | mappedCommand.doCommand(views); |
658 | 0 | getAlignPanel().alignmentChanged(); |
659 | } | |
660 | } | |
661 | ||
662 | /** | |
663 | * Add the sequences from the given alignment to this viewport. Optionally, | |
664 | * may give the user the option to open a new frame, or split panel, with cDNA | |
665 | * and protein linked. | |
666 | * | |
667 | * @param toAdd | |
668 | * @param title | |
669 | */ | |
670 | 21 | public void addAlignment(AlignmentI toAdd, String title) |
671 | { | |
672 | // TODO: promote to AlignViewportI? applet CutAndPasteTransfer is different | |
673 | ||
674 | // JBPComment: title is a largely redundant parameter at the moment | |
675 | // JBPComment: this really should be an 'insert/pre/append' controller | |
676 | // JBPComment: but the DNA/Protein check makes it a bit more complex | |
677 | ||
678 | // refactored from FileLoader / CutAndPasteTransfer / SequenceFetcher with | |
679 | // this comment: | |
680 | // TODO: create undo object for this JAL-1101 | |
681 | ||
682 | /* | |
683 | * Ensure datasets are created for the new alignment as | |
684 | * mappings operate on dataset sequences | |
685 | */ | |
686 | 21 | toAdd.setDataset(null); |
687 | ||
688 | /* | |
689 | * Check if any added sequence could be the object of a mapping or | |
690 | * cross-reference; if so, make the mapping explicit | |
691 | */ | |
692 | 21 | getAlignment().realiseMappings(toAdd.getSequences()); |
693 | ||
694 | /* | |
695 | * If any cDNA/protein mappings exist or can be made between the alignments, | |
696 | * offer to open a split frame with linked alignments | |
697 | */ | |
698 | 21 | if (Cache.getDefault(Preferences.ENABLE_SPLIT_FRAME, true)) |
699 | { | |
700 | 21 | if (AlignmentUtils.isMappable(toAdd, getAlignment())) |
701 | { | |
702 | 0 | openLinkedAlignment(toAdd, title); |
703 | 0 | return; |
704 | } | |
705 | } | |
706 | 21 | addDataToAlignment(toAdd); |
707 | } | |
708 | ||
709 | /** | |
710 | * adds sequences to this alignment | |
711 | * | |
712 | * @param toAdd | |
713 | */ | |
714 | 21 | void addDataToAlignment(AlignmentI toAdd) |
715 | { | |
716 | // TODO: JAL-407 regardless of above - identical sequences (based on ID and | |
717 | // provenance) should share the same dataset sequence | |
718 | ||
719 | 21 | AlignmentI al = getAlignment(); |
720 | 21 | String gap = String.valueOf(al.getGapCharacter()); |
721 | 79 | for (int i = 0; i < toAdd.getHeight(); i++) |
722 | { | |
723 | 58 | SequenceI seq = toAdd.getSequenceAt(i); |
724 | /* | |
725 | * experimental! | |
726 | * - 'align' any mapped sequences as per existing | |
727 | * e.g. cdna to genome, domain hit to protein sequence | |
728 | * very experimental! (need a separate menu option for this) | |
729 | * - only add mapped sequences ('select targets from a dataset') | |
730 | */ | |
731 | 58 | if (true /*AlignmentUtils.alignSequenceAs(seq, al, gap, true, true)*/) |
732 | { | |
733 | 58 | al.addSequence(seq); |
734 | } | |
735 | } | |
736 | 21 | for (ContactMatrixI cm : toAdd.getContactMaps()) |
737 | { | |
738 | 0 | al.addContactList(cm); |
739 | } | |
740 | 21 | ranges.setEndSeq(getAlignment().getHeight() - 1); // BH 2019.04.18 |
741 | 21 | firePropertyChange("alignment", null, getAlignment().getSequences()); |
742 | } | |
743 | ||
744 | /** | |
745 | * Load a File into this AlignViewport attempting to detect format if not | |
746 | * given or given as null. | |
747 | * | |
748 | * @param file | |
749 | * @param format | |
750 | */ | |
751 | 0 | public void addFile(File file, FileFormatI format) |
752 | { | |
753 | 0 | addFile(file, format, true); |
754 | } | |
755 | ||
756 | 0 | public void addFile(File file, FileFormatI format, boolean async) |
757 | { | |
758 | 0 | DataSourceType protocol = AppletFormatAdapter.checkProtocol(file); |
759 | ||
760 | 0 | if (format == null) |
761 | { | |
762 | 0 | try |
763 | { | |
764 | 0 | format = new IdentifyFile().identify(file, protocol); |
765 | } catch (FileFormatException e1) | |
766 | { | |
767 | 0 | jalview.bin.Console.error("Unknown file format for '" + file + "'"); |
768 | } | |
769 | } | |
770 | 0 | else if (FileFormats.getInstance().isIdentifiable(format)) |
771 | { | |
772 | 0 | try |
773 | { | |
774 | 0 | format = new IdentifyFile().identify(file, protocol); |
775 | } catch (FileFormatException e) | |
776 | { | |
777 | 0 | jalview.bin.Console.error("Unknown file format for '" + file + "'", |
778 | e); | |
779 | } | |
780 | } | |
781 | ||
782 | 0 | new FileLoader().LoadFile(this, file, DataSourceType.FILE, format, |
783 | async); | |
784 | } | |
785 | ||
786 | 0 | public void addFile(File file) |
787 | { | |
788 | 0 | addFile(file, null); |
789 | } | |
790 | ||
791 | /** | |
792 | * Show a dialog with the option to open and link (cDNA <-> protein) as a new | |
793 | * alignment, either as a standalone alignment or in a split frame. Returns | |
794 | * true if the new alignment was opened, false if not, because the user | |
795 | * declined the offer. | |
796 | * | |
797 | * @param al | |
798 | * @param title | |
799 | */ | |
800 | 0 | protected void openLinkedAlignment(AlignmentI al, String title) |
801 | { | |
802 | 0 | String[] options = new String[] { MessageManager.getString("action.no"), |
803 | MessageManager.getString("label.split_window"), | |
804 | MessageManager.getString("label.new_window"), }; | |
805 | 0 | final String question = JvSwingUtils.wrapTooltip(true, |
806 | MessageManager.getString("label.open_split_window?")); | |
807 | 0 | final AlignViewport us = this; |
808 | ||
809 | /* | |
810 | * options No, Split Window, New Window correspond to | |
811 | * dialog responses 0, 1, 2 (even though JOptionPane shows them | |
812 | * in reverse order) | |
813 | */ | |
814 | 0 | JvOptionPane dialog = JvOptionPane.newOptionDialog(Desktop.desktop) |
815 | .setResponseHandler(0, () -> { | |
816 | 0 | addDataToAlignment(al); |
817 | }).setResponseHandler(1, () -> { | |
818 | 0 | us.openLinkedAlignmentAs(al, title, true); |
819 | }).setResponseHandler(2, () -> { | |
820 | 0 | us.openLinkedAlignmentAs(al, title, false); |
821 | }); | |
822 | 0 | dialog.showDialog(question, |
823 | MessageManager.getString("label.open_split_window"), | |
824 | JvOptionPane.DEFAULT_OPTION, JvOptionPane.PLAIN_MESSAGE, null, | |
825 | options, options[0]); | |
826 | } | |
827 | ||
828 | 0 | protected void openLinkedAlignmentAs(AlignmentI al, String title, |
829 | boolean newWindowOrSplitPane) | |
830 | { | |
831 | /* | |
832 | * Identify protein and dna alignments. Make a copy of this one if opening | |
833 | * in a new split pane. | |
834 | */ | |
835 | 0 | AlignmentI thisAlignment = newWindowOrSplitPane |
836 | ? new Alignment(getAlignment()) | |
837 | : getAlignment(); | |
838 | 0 | AlignmentI protein = al.isNucleotide() ? thisAlignment : al; |
839 | 0 | final AlignmentI cdna = al.isNucleotide() ? al : thisAlignment; |
840 | ||
841 | /* | |
842 | * Map sequences. At least one should get mapped as we have already passed | |
843 | * the test for 'mappability'. Any mappings made will be added to the | |
844 | * protein alignment. Note creating dataset sequences on the new alignment | |
845 | * is a pre-requisite for building mappings. | |
846 | */ | |
847 | 0 | al.setDataset(null); |
848 | 0 | AlignmentUtils.mapProteinAlignmentToCdna(protein, cdna); |
849 | ||
850 | /* | |
851 | * Create the AlignFrame for the added alignment. If it is protein, mappings | |
852 | * are registered with StructureSelectionManager as a side-effect. | |
853 | */ | |
854 | 0 | AlignFrame newAlignFrame = new AlignFrame(al, AlignFrame.DEFAULT_WIDTH, |
855 | AlignFrame.DEFAULT_HEIGHT); | |
856 | 0 | newAlignFrame.setTitle(title); |
857 | 0 | newAlignFrame.setStatus(MessageManager |
858 | .formatMessage("label.successfully_loaded_file", new Object[] | |
859 | { title })); | |
860 | ||
861 | // TODO if we want this (e.g. to enable reload of the alignment from file), | |
862 | // we will need to add parameters to the stack. | |
863 | // if (!protocol.equals(DataSourceType.PASTE)) | |
864 | // { | |
865 | // alignFrame.setFileName(file, format); | |
866 | // } | |
867 | ||
868 | 0 | if (!newWindowOrSplitPane) |
869 | { | |
870 | 0 | Desktop.addInternalFrame(newAlignFrame, title, |
871 | AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); | |
872 | } | |
873 | ||
874 | 0 | try |
875 | { | |
876 | 0 | newAlignFrame.setMaximum(Cache.getDefault("SHOW_FULLSCREEN", false)); |
877 | } catch (java.beans.PropertyVetoException ex) | |
878 | { | |
879 | } | |
880 | ||
881 | 0 | if (newWindowOrSplitPane) |
882 | { | |
883 | 0 | al.alignAs(thisAlignment); |
884 | 0 | protein = openSplitFrame(newAlignFrame, thisAlignment); |
885 | } | |
886 | } | |
887 | ||
888 | /** | |
889 | * Helper method to open a new SplitFrame holding linked dna and protein | |
890 | * alignments. | |
891 | * | |
892 | * @param newAlignFrame | |
893 | * containing a new alignment to be shown | |
894 | * @param complement | |
895 | * cdna/protein complement alignment to show in the other split half | |
896 | * @return the protein alignment in the split frame | |
897 | */ | |
898 | 1 | protected AlignmentI openSplitFrame(AlignFrame newAlignFrame, |
899 | AlignmentI complement) | |
900 | { | |
901 | /* | |
902 | * Make a new frame with a copy of the alignment we are adding to. If this | |
903 | * is protein, the mappings to cDNA will be registered with | |
904 | * StructureSelectionManager as a side-effect. | |
905 | */ | |
906 | 1 | AlignFrame copyMe = new AlignFrame(complement, AlignFrame.DEFAULT_WIDTH, |
907 | AlignFrame.DEFAULT_HEIGHT); | |
908 | 1 | copyMe.setTitle(getAlignPanel().alignFrame.getTitle()); |
909 | ||
910 | 1 | AlignmentI al = newAlignFrame.viewport.getAlignment(); |
911 | 1 | final AlignFrame proteinFrame = al.isNucleotide() ? copyMe |
912 | : newAlignFrame; | |
913 | 1 | final AlignFrame cdnaFrame = al.isNucleotide() ? newAlignFrame : copyMe; |
914 | 1 | cdnaFrame.setVisible(true); |
915 | 1 | proteinFrame.setVisible(true); |
916 | 1 | String linkedTitle = MessageManager |
917 | .getString("label.linked_view_title"); | |
918 | ||
919 | /* | |
920 | * Open in split pane. DNA sequence above, protein below. | |
921 | */ | |
922 | 1 | JInternalFrame splitFrame = new SplitFrame(cdnaFrame, proteinFrame); |
923 | 1 | Desktop.addInternalFrame(splitFrame, linkedTitle, -1, -1); |
924 | ||
925 | 1 | return proteinFrame.viewport.getAlignment(); |
926 | } | |
927 | ||
928 | 7 | public AnnotationColumnChooser getAnnotationColumnSelectionState() |
929 | { | |
930 | 7 | return annotationColumnSelectionState; |
931 | } | |
932 | ||
933 | 1 | public void setAnnotationColumnSelectionState( |
934 | AnnotationColumnChooser currentAnnotationColumnSelectionState) | |
935 | { | |
936 | 1 | this.annotationColumnSelectionState = currentAnnotationColumnSelectionState; |
937 | } | |
938 | ||
939 | 970 | @Override |
940 | public void setIdWidth(int i) | |
941 | { | |
942 | 970 | super.setIdWidth(i); |
943 | 970 | AlignmentPanel ap = getAlignPanel(); |
944 | 970 | if (ap != null) |
945 | { | |
946 | // modify GUI elements to reflect geometry change | |
947 | 523 | Dimension idw = ap.getIdPanel().getIdCanvas().getPreferredSize(); |
948 | 523 | idw.width = i; |
949 | 523 | ap.getIdPanel().getIdCanvas().setPreferredSize(idw); |
950 | } | |
951 | } | |
952 | ||
953 | 51 | public Rectangle getExplodedGeometry() |
954 | { | |
955 | 51 | return explodedGeometry; |
956 | } | |
957 | ||
958 | 53 | public void setExplodedGeometry(Rectangle explodedPosition) |
959 | { | |
960 | 53 | this.explodedGeometry = explodedPosition; |
961 | } | |
962 | ||
963 | 121 | public boolean isGatherViewsHere() |
964 | { | |
965 | 121 | return gatherViewsHere; |
966 | } | |
967 | ||
968 | 162 | public void setGatherViewsHere(boolean gatherViewsHere) |
969 | { | |
970 | 162 | this.gatherViewsHere = gatherViewsHere; |
971 | } | |
972 | ||
973 | /** | |
974 | * If this viewport has a (Protein/cDNA) complement, then scroll the | |
975 | * complementary alignment to match this one. | |
976 | */ | |
977 | 637 | public void scrollComplementaryAlignment() |
978 | { | |
979 | /* | |
980 | * Populate a SearchResults object with the mapped location to scroll to. If | |
981 | * there is no complement, or it is not following highlights, or no mapping | |
982 | * is found, the result will be empty. | |
983 | */ | |
984 | 637 | SearchResultsI sr = new SearchResults(); |
985 | 637 | int verticalOffset = findComplementScrollTarget(sr); |
986 | 637 | if (!sr.isEmpty()) |
987 | { | |
988 | // TODO would like next line without cast but needs more refactoring... | |
989 | 4 | final AlignmentPanel complementPanel = ((AlignViewport) getCodingComplement()) |
990 | .getAlignPanel(); | |
991 | 4 | complementPanel.setToScrollComplementPanel(false); |
992 | 4 | complementPanel.scrollToCentre(sr, verticalOffset); |
993 | 4 | complementPanel.setToScrollComplementPanel(true); |
994 | } | |
995 | } | |
996 | ||
997 | /** | |
998 | * Answers true if no alignment holds a reference to the given mapping | |
999 | * | |
1000 | * @param acf | |
1001 | * @return | |
1002 | */ | |
1003 | 125 | protected boolean noReferencesTo(AlignedCodonFrame acf) |
1004 | { | |
1005 | 125 | AlignFrame[] frames = Desktop.getDesktopAlignFrames(); |
1006 | 125 | if (frames == null) |
1007 | { | |
1008 | 24 | return true; |
1009 | } | |
1010 | 101 | for (AlignFrame af : frames) |
1011 | { | |
1012 | 108 | if (!af.isClosed()) |
1013 | { | |
1014 | 108 | for (AlignmentViewPanel ap : af.getAlignPanels()) |
1015 | { | |
1016 | 108 | AlignmentI al = ap.getAlignment(); |
1017 | 108 | if (al != null && al.getCodonFrames().contains(acf)) |
1018 | { | |
1019 | 98 | return false; |
1020 | } | |
1021 | } | |
1022 | } | |
1023 | } | |
1024 | 3 | return true; |
1025 | } | |
1026 | ||
1027 | /** | |
1028 | * Applies the supplied feature settings descriptor to currently known | |
1029 | * features. This supports an 'initial configuration' of feature colouring | |
1030 | * based on a preset or user favourite. This may then be modified in the usual | |
1031 | * way using the Feature Settings dialogue. | |
1032 | * | |
1033 | * @param featureSettings | |
1034 | */ | |
1035 | 81 | @Override |
1036 | public void applyFeaturesStyle(FeatureSettingsModelI featureSettings) | |
1037 | { | |
1038 | 81 | transferFeaturesStyles(featureSettings, false); |
1039 | } | |
1040 | ||
1041 | /** | |
1042 | * Applies the supplied feature settings descriptor to currently known | |
1043 | * features. This supports an 'initial configuration' of feature colouring | |
1044 | * based on a preset or user favourite. This may then be modified in the usual | |
1045 | * way using the Feature Settings dialogue. | |
1046 | * | |
1047 | * @param featureSettings | |
1048 | */ | |
1049 | 2 | @Override |
1050 | public void mergeFeaturesStyle(FeatureSettingsModelI featureSettings) | |
1051 | { | |
1052 | 2 | transferFeaturesStyles(featureSettings, true); |
1053 | } | |
1054 | ||
1055 | /** | |
1056 | * when mergeOnly is set, then group and feature visibility or feature colours | |
1057 | * are not modified for features and groups already known to the feature | |
1058 | * renderer. Feature ordering is always adjusted, and transparency is always | |
1059 | * set regardless. | |
1060 | * | |
1061 | * @param featureSettings | |
1062 | * @param mergeOnly | |
1063 | */ | |
1064 | 83 | private void transferFeaturesStyles(FeatureSettingsModelI featureSettings, |
1065 | boolean mergeOnly) | |
1066 | { | |
1067 | 83 | if (featureSettings == null) |
1068 | { | |
1069 | 18 | return; |
1070 | } | |
1071 | ||
1072 | 65 | FeatureRenderer fr = getAlignPanel().getSeqPanel().seqCanvas |
1073 | .getFeatureRenderer(); | |
1074 | 65 | List<String> origRenderOrder = new ArrayList<>(); |
1075 | 65 | List<String> origGroups = new ArrayList<>(); |
1076 | // preserve original render order - allows differentiation between user | |
1077 | // configured colours and autogenerated ones | |
1078 | 65 | origRenderOrder.addAll(fr.getRenderOrder()); |
1079 | 65 | origGroups.addAll(fr.getFeatureGroups()); |
1080 | ||
1081 | 65 | fr.findAllFeatures(true); |
1082 | 65 | List<String> renderOrder = fr.getRenderOrder(); |
1083 | 65 | FeaturesDisplayedI displayed = fr.getFeaturesDisplayed(); |
1084 | 65 | if (!mergeOnly) |
1085 | { | |
1086 | // only clear displayed features if we are mergeing | |
1087 | // displayed.clear(); | |
1088 | } | |
1089 | // TODO this clears displayed.featuresRegistered - do we care? | |
1090 | // | |
1091 | // JAL-3330 - JBP - yes we do - calling applyFeatureStyle to a view where | |
1092 | // feature visibility has already been configured is not very friendly | |
1093 | /* | |
1094 | * set feature colour if specified by feature settings | |
1095 | * set visibility of all features | |
1096 | */ | |
1097 | 65 | for (String type : renderOrder) |
1098 | { | |
1099 | 209 | FeatureColourI preferredColour = featureSettings |
1100 | .getFeatureColour(type); | |
1101 | 209 | FeatureColourI origColour = fr.getFeatureStyle(type); |
1102 | 209 | if (!mergeOnly || (!origRenderOrder.contains(type) |
1103 | || origColour == null | |
1104 | || (!origColour.isGraduatedColour() | |
1105 | && origColour.getColour() != null | |
1106 | && origColour.getColour().equals( | |
1107 | ColorUtils.createColourFromName(type))))) | |
1108 | { | |
1109 | // if we are merging, only update if there wasn't already a colour | |
1110 | // defined for | |
1111 | // this type | |
1112 | 205 | if (preferredColour != null) |
1113 | { | |
1114 | 12 | fr.setColour(type, preferredColour); |
1115 | } | |
1116 | 205 | if (featureSettings.isFeatureDisplayed(type)) |
1117 | { | |
1118 | 0 | displayed.setVisible(type); |
1119 | } | |
1120 | 205 | else if (featureSettings.isFeatureHidden(type)) |
1121 | { | |
1122 | 61 | displayed.setHidden(type); |
1123 | } | |
1124 | } | |
1125 | } | |
1126 | ||
1127 | /* | |
1128 | * set visibility of feature groups | |
1129 | */ | |
1130 | 65 | for (String group : fr.getFeatureGroups()) |
1131 | { | |
1132 | 116 | if (!mergeOnly || !origGroups.contains(group)) |
1133 | { | |
1134 | // when merging, display groups only if the aren't already marked as not | |
1135 | // visible | |
1136 | 108 | fr.setGroupVisibility(group, |
1137 | featureSettings.isGroupDisplayed(group)); | |
1138 | } | |
1139 | } | |
1140 | ||
1141 | /* | |
1142 | * order the features | |
1143 | */ | |
1144 | 65 | if (featureSettings.optimiseOrder()) |
1145 | { | |
1146 | // TODO not supported (yet?) | |
1147 | } | |
1148 | else | |
1149 | { | |
1150 | 65 | fr.orderFeatures(featureSettings); |
1151 | } | |
1152 | 65 | fr.setTransparency(featureSettings.getTransparency()); |
1153 | ||
1154 | 65 | fr.notifyFeaturesChanged(); |
1155 | } | |
1156 | ||
1157 | 3960 | public String getViewName() |
1158 | { | |
1159 | 3960 | return viewName; |
1160 | } | |
1161 | ||
1162 | 78 | public void setViewName(String viewName) |
1163 | { | |
1164 | 78 | this.viewName = viewName; |
1165 | } | |
1166 | ||
1167 | } |