Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
TreePanel | 97 | 235 | 93 | ||
TreePanel.TreeLoader | 422 | 25 | 9 |
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.BorderLayout; | |
24 | import java.awt.Color; | |
25 | import java.awt.Dimension; | |
26 | import java.awt.FlowLayout; | |
27 | import java.awt.Font; | |
28 | import java.awt.FontMetrics; | |
29 | import java.awt.Graphics; | |
30 | import java.awt.GridLayout; | |
31 | import java.awt.event.ActionEvent; | |
32 | import java.awt.event.ActionListener; | |
33 | import java.awt.event.KeyAdapter; | |
34 | import java.awt.event.KeyEvent; | |
35 | import java.beans.PropertyChangeEvent; | |
36 | import java.beans.PropertyChangeListener; | |
37 | import java.io.File; | |
38 | import java.io.FileOutputStream; | |
39 | import java.util.ArrayList; | |
40 | import java.util.HashMap; | |
41 | import java.util.List; | |
42 | import java.util.Locale; | |
43 | import java.util.Map; | |
44 | ||
45 | import javax.swing.ButtonGroup; | |
46 | import javax.swing.JLabel; | |
47 | import javax.swing.JMenuItem; | |
48 | import javax.swing.JPanel; | |
49 | import javax.swing.JRadioButtonMenuItem; | |
50 | import javax.swing.JScrollPane; | |
51 | import javax.swing.SwingConstants; | |
52 | import javax.swing.event.InternalFrameAdapter; | |
53 | import javax.swing.event.InternalFrameEvent; | |
54 | ||
55 | import org.jibble.epsgraphics.EpsGraphics2D; | |
56 | ||
57 | import jalview.analysis.AlignmentSorter; | |
58 | import jalview.analysis.AlignmentUtils; | |
59 | import jalview.analysis.AverageDistanceTree; | |
60 | import jalview.analysis.NJTree; | |
61 | import jalview.analysis.TreeBuilder; | |
62 | import jalview.analysis.TreeModel; | |
63 | import jalview.analysis.scoremodels.ScoreModels; | |
64 | import jalview.api.analysis.ScoreModelI; | |
65 | import jalview.api.analysis.SimilarityParamsI; | |
66 | import jalview.bin.Cache; | |
67 | import jalview.bin.Console; | |
68 | import jalview.commands.CommandI; | |
69 | import jalview.commands.OrderCommand; | |
70 | import jalview.datamodel.Alignment; | |
71 | import jalview.datamodel.AlignmentAnnotation; | |
72 | import jalview.datamodel.AlignmentI; | |
73 | import jalview.datamodel.AlignmentView; | |
74 | import jalview.datamodel.BinaryNode; | |
75 | import jalview.datamodel.DBRefEntry; | |
76 | import jalview.datamodel.HiddenColumns; | |
77 | import jalview.datamodel.NodeTransformI; | |
78 | import jalview.datamodel.SequenceFeature; | |
79 | import jalview.datamodel.SequenceI; | |
80 | import jalview.datamodel.SequenceNode; | |
81 | import jalview.gui.ImageExporter.ImageWriterI; | |
82 | import jalview.io.JalviewFileChooser; | |
83 | import jalview.io.JalviewFileView; | |
84 | import jalview.io.NewickFile; | |
85 | import jalview.io.exceptions.ImageOutputException; | |
86 | import jalview.jbgui.GTreePanel; | |
87 | import jalview.util.ImageMaker.TYPE; | |
88 | import jalview.util.MessageManager; | |
89 | import jalview.viewmodel.AlignmentViewport; | |
90 | ||
91 | /** | |
92 | * DOCUMENT ME! | |
93 | * | |
94 | * @author $author$ | |
95 | * @version $Revision$ | |
96 | */ | |
97 | public class TreePanel extends GTreePanel | |
98 | { | |
99 | String treeType; | |
100 | ||
101 | String scoreModelName; // if tree computed | |
102 | ||
103 | String treeTitle; // if tree loaded | |
104 | ||
105 | SimilarityParamsI similarityParams; | |
106 | ||
107 | private TreeCanvas treeCanvas; | |
108 | ||
109 | TreeModel tree; | |
110 | ||
111 | private AlignViewport av; | |
112 | ||
113 | // New JLabel for subtitle | |
114 | private JLabel subtitleLabel; | |
115 | ||
116 | private Map<String, Color> secondaryStructureProviderColorMap; | |
117 | ||
118 | /** | |
119 | * Creates a new TreePanel object. | |
120 | * | |
121 | * @param ap | |
122 | * @param type | |
123 | * @param modelName | |
124 | * @param options | |
125 | */ | |
126 | 0 | public TreePanel(AlignmentPanel ap, String type, String modelName, |
127 | SimilarityParamsI options) | |
128 | { | |
129 | 0 | super(); |
130 | 0 | this.setFrameIcon(null); |
131 | 0 | this.similarityParams = options; |
132 | 0 | initTreePanel(ap, type, modelName, null, null, null); |
133 | ||
134 | // We know this tree has distances. JBPNote TODO: prolly should add this as | |
135 | // a userdefined default | |
136 | // showDistances(true); | |
137 | } | |
138 | ||
139 | 14 | public TreePanel(AlignmentPanel alignPanel, NewickFile newtree, |
140 | String theTitle, AlignmentView inputData) | |
141 | { | |
142 | 14 | this(alignPanel, newtree, theTitle, inputData, null, null); |
143 | } | |
144 | ||
145 | /** | |
146 | * when true, leaf labels are annotations | |
147 | */ | |
148 | boolean forAnnotation = false; | |
149 | ||
150 | 14 | public TreePanel(AlignmentPanel alignPanel, NewickFile newtree, |
151 | String theTitle, AlignmentView inputData, | |
152 | AlignmentAnnotation[] leafAnnotations, String subTitle) | |
153 | { | |
154 | 14 | super(); |
155 | 14 | this.forAnnotation = leafAnnotations != null |
156 | && leafAnnotations.length > 0; | |
157 | 14 | this.setFrameIcon(null); |
158 | 14 | this.treeTitle = theTitle; |
159 | 14 | initTreePanel(alignPanel, null, null, newtree, inputData, null); |
160 | } | |
161 | ||
162 | /** | |
163 | * columnwise tree associated with positions in aa | |
164 | * | |
165 | * @param alignPanel | |
166 | * @param fin | |
167 | * @param title | |
168 | * @param aa | |
169 | */ | |
170 | 0 | public TreePanel(AlignmentPanel alignPanel, NewickFile fin, |
171 | AlignmentAnnotation aa, String title) | |
172 | { | |
173 | 0 | super(); |
174 | 0 | columnWise = true; |
175 | 0 | assocAnnotation = aa; |
176 | 0 | this.setFrameIcon(null); |
177 | 0 | this.treeTitle = title; |
178 | 0 | initTreePanel(alignPanel, null, null, fin, null, null); |
179 | } | |
180 | ||
181 | boolean columnWise = false; | |
182 | ||
183 | AlignmentAnnotation assocAnnotation = null; | |
184 | ||
185 | 174 | public boolean isColumnWise() |
186 | { | |
187 | 174 | return columnWise; |
188 | } | |
189 | ||
190 | 14 | public AlignmentAnnotation getAssocAnnotation() |
191 | { | |
192 | 14 | return assocAnnotation; |
193 | } | |
194 | ||
195 | 0 | public AlignmentI getAlignment() |
196 | { | |
197 | 0 | return getTreeCanvas().getViewport().getAlignment(); |
198 | } | |
199 | ||
200 | 0 | public AlignmentViewport getViewPort() |
201 | { | |
202 | // @Mungo - Why don't we return our own viewport ??? | |
203 | 0 | return getTreeCanvas().getViewport(); |
204 | } | |
205 | ||
206 | ||
207 | 0 | public Map<String, Color> getSecondaryStructureProviderColorMap() |
208 | { | |
209 | 0 | return secondaryStructureProviderColorMap; |
210 | } | |
211 | ||
212 | 0 | private void addSubtitlePanel(String subTitle) |
213 | { | |
214 | 0 | subtitleLabel = new JLabel(subTitle, SwingConstants.LEFT); |
215 | ||
216 | 0 | JPanel panel = new JPanel(new BorderLayout()); |
217 | 0 | panel.add(subtitleLabel, BorderLayout.NORTH); |
218 | 0 | panel.add(scrollPane, BorderLayout.CENTER); |
219 | ||
220 | 0 | this.add(panel); |
221 | } | |
222 | ||
223 | 14 | void initTreePanel(AlignmentPanel ap, String type, String modelName, |
224 | NewickFile newTree, AlignmentView inputData, | |
225 | AlignmentAnnotation[] leafAnnotations) | |
226 | { | |
227 | ||
228 | 14 | av = ap.av; |
229 | 14 | this.treeType = type; |
230 | 14 | this.scoreModelName = modelName; |
231 | ||
232 | 14 | treeCanvas = new TreeCanvas(this, ap, scrollPane); |
233 | 14 | scrollPane.setViewportView(treeCanvas); |
234 | ||
235 | 14 | if (similarityParams != null |
236 | && similarityParams.getSecondaryStructureSource() != null) | |
237 | { | |
238 | //setting showSecondaryStructureProviderMenu to true if the | |
239 | //similarity is based on secondary structure | |
240 | 0 | showSecondaryStructureProviderMenu.setVisible(true); |
241 | 0 | addSubtitlePanel(" Secondary Structure Provider : " |
242 | + similarityParams.getSecondaryStructureSource()); | |
243 | ||
244 | } | |
245 | else { | |
246 | //setting showSecondaryStructureProviderMenu to false if the | |
247 | //similarity is not based on secondary structure | |
248 | 14 | showSecondaryStructureProviderMenu.setVisible(false); |
249 | } | |
250 | 14 | if (leafAnnotations != null) |
251 | { | |
252 | 0 | forAnnotation = true; |
253 | } | |
254 | 14 | if (columnWise) |
255 | { | |
256 | 0 | bootstrapMenu.setVisible(false); |
257 | 0 | placeholdersMenu.setState(false); |
258 | 0 | placeholdersMenu.setVisible(false); |
259 | 0 | fitToWindow.setState(false); |
260 | 0 | sortAssocViews.setVisible(false); |
261 | } | |
262 | ||
263 | 14 | addKeyListener(new KeyAdapter() |
264 | { | |
265 | 0 | @Override |
266 | public void keyPressed(KeyEvent e) | |
267 | { | |
268 | 0 | switch (e.getKeyCode()) |
269 | { | |
270 | 0 | case 27: // escape |
271 | 0 | treeCanvas.clearSelectedLeaves(); |
272 | 0 | e.consume(); |
273 | 0 | break; |
274 | ||
275 | } | |
276 | ||
277 | } | |
278 | }); | |
279 | 14 | PaintRefresher.Register(this, ap.av.getSequenceSetId()); |
280 | ||
281 | 14 | buildAssociatedViewMenu(); |
282 | ||
283 | 14 | final PropertyChangeListener listener = addAlignmentListener(); |
284 | ||
285 | /* | |
286 | * remove listener when window is closed, so that this | |
287 | * panel can be garbage collected | |
288 | */ | |
289 | 14 | addInternalFrameListener(new InternalFrameAdapter() |
290 | { | |
291 | 12 | @Override |
292 | public void internalFrameClosed(InternalFrameEvent evt) | |
293 | { | |
294 | 12 | if (av != null) |
295 | { | |
296 | 12 | av.removePropertyChangeListener(listener); |
297 | } | |
298 | 12 | releaseReferences(); |
299 | } | |
300 | }); | |
301 | ||
302 | 14 | TreeLoader tl = new TreeLoader(newTree, inputData, leafAnnotations); |
303 | 14 | tl.start(); |
304 | ||
305 | } | |
306 | ||
307 | /** | |
308 | * Ensure any potentially large object references are nulled | |
309 | */ | |
310 | 12 | public void releaseReferences() |
311 | { | |
312 | 12 | this.tree = null; |
313 | 12 | this.treeCanvas.tree = null; |
314 | 12 | this.treeCanvas.nodeHash = null; |
315 | 12 | this.treeCanvas.nameHash = null; |
316 | } | |
317 | ||
318 | /** | |
319 | * @return | |
320 | */ | |
321 | 14 | protected PropertyChangeListener addAlignmentListener() |
322 | { | |
323 | 14 | final PropertyChangeListener listener = new PropertyChangeListener() |
324 | { | |
325 | 0 | @Override |
326 | public void propertyChange(PropertyChangeEvent evt) | |
327 | { | |
328 | 0 | if (evt.getPropertyName().equals("alignment")) |
329 | { | |
330 | 0 | if (tree == null) |
331 | { | |
332 | 0 | jalview.bin.Console.outPrintln("tree is null"); |
333 | // TODO: deal with case when a change event is received whilst a | |
334 | // tree is still being calculated - should save reference for | |
335 | // processing message later. | |
336 | 0 | return; |
337 | } | |
338 | 0 | if (evt.getNewValue() == null) |
339 | { | |
340 | 0 | jalview.bin.Console.outPrintln( |
341 | "new alignment sequences vector value is null"); | |
342 | } | |
343 | ||
344 | 0 | tree.updatePlaceHolders((List<SequenceI>) evt.getNewValue()); |
345 | 0 | treeCanvas.nameHash.clear(); // reset the mapping between canvas |
346 | // rectangles and leafnodes | |
347 | 0 | repaint(); |
348 | } | |
349 | } | |
350 | }; | |
351 | 14 | av.addPropertyChangeListener(listener); |
352 | 14 | return listener; |
353 | } | |
354 | ||
355 | 0 | @Override |
356 | public void viewMenu_menuSelected() | |
357 | { | |
358 | 0 | buildAssociatedViewMenu(); |
359 | } | |
360 | ||
361 | 14 | void buildAssociatedViewMenu() |
362 | { | |
363 | 14 | AlignmentPanel[] aps = PaintRefresher |
364 | .getAssociatedPanels(av.getSequenceSetId()); | |
365 | 14 | if (aps.length == 1 && getTreeCanvas().getAssociatedPanel() == aps[0]) |
366 | { | |
367 | 6 | associateLeavesMenu.setVisible(false); |
368 | 6 | return; |
369 | } | |
370 | ||
371 | 8 | associateLeavesMenu.setVisible(true); |
372 | ||
373 | 8 | if ((viewMenu |
374 | .getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem)) | |
375 | { | |
376 | 8 | viewMenu.insertSeparator(viewMenu.getItemCount() - 1); |
377 | } | |
378 | ||
379 | 8 | associateLeavesMenu.removeAll(); |
380 | ||
381 | 8 | JRadioButtonMenuItem item; |
382 | 8 | ButtonGroup buttonGroup = new ButtonGroup(); |
383 | 8 | int i, iSize = aps.length; |
384 | 8 | final TreePanel thisTreePanel = this; |
385 | 32 | for (i = 0; i < iSize; i++) |
386 | { | |
387 | 24 | final AlignmentPanel ap = aps[i]; |
388 | 24 | item = new JRadioButtonMenuItem(ap.av.getViewName(), |
389 | ap == treeCanvas.getAssociatedPanel()); | |
390 | 24 | buttonGroup.add(item); |
391 | 24 | item.addActionListener(new ActionListener() |
392 | { | |
393 | 0 | @Override |
394 | public void actionPerformed(ActionEvent evt) | |
395 | { | |
396 | 0 | treeCanvas.applyToAllViews = false; |
397 | 0 | treeCanvas.setAssociatedPanel(ap); |
398 | 0 | treeCanvas.setViewport(ap.av); |
399 | 0 | PaintRefresher.Register(thisTreePanel, ap.av.getSequenceSetId()); |
400 | } | |
401 | }); | |
402 | ||
403 | 24 | associateLeavesMenu.add(item); |
404 | } | |
405 | ||
406 | 8 | final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem( |
407 | MessageManager.getString("label.all_views")); | |
408 | 8 | buttonGroup.add(itemf); |
409 | 8 | itemf.setSelected(treeCanvas.applyToAllViews); |
410 | 8 | itemf.addActionListener(new ActionListener() |
411 | { | |
412 | 0 | @Override |
413 | public void actionPerformed(ActionEvent evt) | |
414 | { | |
415 | 0 | treeCanvas.applyToAllViews = itemf.isSelected(); |
416 | } | |
417 | }); | |
418 | 8 | associateLeavesMenu.add(itemf); |
419 | ||
420 | } | |
421 | ||
422 | class TreeLoader extends Thread | |
423 | { | |
424 | private NewickFile newtree; | |
425 | ||
426 | private AlignmentView odata = null; | |
427 | ||
428 | private AlignmentAnnotation[] leafAnnotations; | |
429 | ||
430 | 14 | public TreeLoader(NewickFile newickFile, AlignmentView inputData, |
431 | AlignmentAnnotation[] leafAnnotations) | |
432 | { | |
433 | 14 | this.newtree = newickFile; |
434 | 14 | this.odata = inputData; |
435 | 14 | this.leafAnnotations = leafAnnotations; |
436 | ||
437 | 14 | if (newickFile != null) |
438 | { | |
439 | // Must be outside run(), as Jalview2XML tries to | |
440 | // update distance/bootstrap visibility at the same time | |
441 | 14 | showBootstrap(newickFile.HasBootstrap()); |
442 | 14 | showDistances(newickFile.HasDistances()); |
443 | } | |
444 | } | |
445 | ||
446 | 14 | @Override |
447 | public void run() | |
448 | { | |
449 | ||
450 | 14 | if (newtree != null) |
451 | { | |
452 | 14 | tree = new TreeModel(av.getAlignment().getSequencesArray(), odata, |
453 | newtree, leafAnnotations); | |
454 | 14 | if (tree.getOriginalData() == null) |
455 | { | |
456 | 14 | originalSeqData.setVisible(false); |
457 | } | |
458 | } | |
459 | else | |
460 | { | |
461 | 0 | ScoreModelI sm = ScoreModels.getInstance().getScoreModel( |
462 | scoreModelName, treeCanvas.getAssociatedPanel()); | |
463 | 0 | TreeBuilder njtree = treeType.equals(TreeBuilder.NEIGHBOUR_JOINING) |
464 | ? new NJTree(av, sm, similarityParams) | |
465 | : new AverageDistanceTree(av, sm, similarityParams); | |
466 | 0 | List<String> labels = njtree.getLabels(); |
467 | 0 | if(labels != null && labels.size()>0) { |
468 | 0 | secondaryStructureProviderColorMap = new HashMap<String, Color>(); |
469 | 0 | AlignmentUtils.assignSecondaryStructureProviderColor(secondaryStructureProviderColorMap, labels); |
470 | } | |
471 | 0 | tree = new TreeModel(njtree); |
472 | // don't display distances for columnwise trees | |
473 | } | |
474 | 14 | showDistances(!columnWise); |
475 | 14 | tree.reCount(tree.getTopNode()); |
476 | 14 | tree.findHeight(tree.getTopNode()); |
477 | 14 | treeCanvas.setTree(tree); |
478 | 14 | treeCanvas.repaint(); |
479 | 14 | av.setCurrentTree(tree); |
480 | 14 | if (av.getSortByTree()) |
481 | { | |
482 | 1 | sortByTree_actionPerformed(); |
483 | } | |
484 | } | |
485 | } | |
486 | ||
487 | 40 | public void showDistances(boolean b) |
488 | { | |
489 | 40 | treeCanvas.setShowDistances(b); |
490 | 40 | distanceMenu.setSelected(b); |
491 | } | |
492 | ||
493 | 26 | public void showBootstrap(boolean b) |
494 | { | |
495 | 26 | treeCanvas.setShowBootstrap(b); |
496 | 26 | bootstrapMenu.setSelected(b); |
497 | } | |
498 | ||
499 | 12 | public void showPlaceholders(boolean b) |
500 | { | |
501 | 12 | placeholdersMenu.setState(b); |
502 | 12 | treeCanvas.setMarkPlaceholders(b); |
503 | } | |
504 | ||
505 | /** | |
506 | * DOCUMENT ME! | |
507 | * | |
508 | * @return DOCUMENT ME! | |
509 | */ | |
510 | 22 | public TreeModel getTree() |
511 | { | |
512 | 22 | return tree; |
513 | } | |
514 | ||
515 | /** | |
516 | * DOCUMENT ME! | |
517 | * | |
518 | * @param e | |
519 | * DOCUMENT ME! | |
520 | */ | |
521 | 0 | @Override |
522 | public void textbox_actionPerformed(ActionEvent e) | |
523 | { | |
524 | 0 | CutAndPasteTransfer cap = new CutAndPasteTransfer(); |
525 | ||
526 | 0 | String newTitle = getPanelTitle(); |
527 | ||
528 | 0 | NewickFile fout = new NewickFile(tree.getTopNode()); |
529 | 0 | try |
530 | { | |
531 | 0 | cap.setText(fout.print(tree.hasBootstrap(), tree.hasDistances(), |
532 | tree.hasRootDistance())); | |
533 | 0 | Desktop.addInternalFrame(cap, newTitle, 500, 100); |
534 | } catch (OutOfMemoryError oom) | |
535 | { | |
536 | 0 | new OOMWarning("generating newick tree file", oom); |
537 | 0 | cap.dispose(); |
538 | } | |
539 | ||
540 | } | |
541 | ||
542 | /** | |
543 | * DOCUMENT ME! | |
544 | * | |
545 | * @param e | |
546 | * DOCUMENT ME! | |
547 | */ | |
548 | 0 | @Override |
549 | public void saveAsNewick_actionPerformed(ActionEvent e) | |
550 | { | |
551 | // TODO: JAL-3048 save newick file for Jalview-JS | |
552 | 0 | JalviewFileChooser chooser = new JalviewFileChooser( |
553 | Cache.getProperty("LAST_DIRECTORY")); | |
554 | 0 | chooser.setFileView(new JalviewFileView()); |
555 | 0 | chooser.setDialogTitle( |
556 | MessageManager.getString("label.save_tree_as_newick")); | |
557 | 0 | chooser.setToolTipText(MessageManager.getString("action.save")); |
558 | ||
559 | 0 | int value = chooser.showSaveDialog(null); |
560 | ||
561 | 0 | if (value == JalviewFileChooser.APPROVE_OPTION) |
562 | { | |
563 | 0 | String choice = chooser.getSelectedFile().getPath(); |
564 | 0 | Cache.setProperty("LAST_DIRECTORY", |
565 | chooser.getSelectedFile().getParent()); | |
566 | ||
567 | 0 | try |
568 | { | |
569 | 0 | jalview.io.NewickFile fout = new jalview.io.NewickFile( |
570 | tree.getTopNode()); | |
571 | 0 | String output = fout.print(tree.hasBootstrap(), tree.hasDistances(), |
572 | tree.hasRootDistance()); | |
573 | 0 | java.io.PrintWriter out = new java.io.PrintWriter( |
574 | new java.io.FileWriter(choice)); | |
575 | 0 | out.println(output); |
576 | 0 | out.close(); |
577 | } catch (Exception ex) | |
578 | { | |
579 | 0 | ex.printStackTrace(); |
580 | } | |
581 | } | |
582 | } | |
583 | ||
584 | /** | |
585 | * DOCUMENT ME! | |
586 | * | |
587 | * @param e | |
588 | * DOCUMENT ME! | |
589 | */ | |
590 | 0 | @Override |
591 | public void printMenu_actionPerformed(ActionEvent e) | |
592 | { | |
593 | // Putting in a thread avoids Swing painting problems | |
594 | 0 | treeCanvas.startPrinting(); |
595 | } | |
596 | ||
597 | 0 | @Override |
598 | public void originalSeqData_actionPerformed(ActionEvent e) | |
599 | { | |
600 | 0 | AlignmentView originalData = tree.getOriginalData(); |
601 | 0 | if (originalData == null) |
602 | { | |
603 | 0 | Console.info( |
604 | "Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action."); | |
605 | 0 | return; |
606 | } | |
607 | // decide if av alignment is sufficiently different to original data to | |
608 | // warrant a new window to be created | |
609 | // create new alignmnt window with hidden regions (unhiding hidden regions | |
610 | // yields unaligned seqs) | |
611 | // or create a selection box around columns in alignment view | |
612 | // test Alignment(SeqCigar[]) | |
613 | 0 | char gc = '-'; |
614 | 0 | try |
615 | { | |
616 | // we try to get the associated view's gap character | |
617 | // but this may fail if the view was closed... | |
618 | 0 | gc = av.getGapCharacter(); |
619 | ||
620 | } catch (Exception ex) | |
621 | { | |
622 | } | |
623 | ||
624 | 0 | Object[] alAndColsel = originalData.getAlignmentAndHiddenColumns(gc); |
625 | ||
626 | 0 | if (alAndColsel != null && alAndColsel[0] != null) |
627 | { | |
628 | // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]); | |
629 | ||
630 | 0 | AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]); |
631 | 0 | AlignmentI dataset = (av != null && av.getAlignment() != null) |
632 | ? av.getAlignment().getDataset() | |
633 | : null; | |
634 | 0 | if (dataset != null) |
635 | { | |
636 | 0 | al.setDataset(dataset); |
637 | } | |
638 | ||
639 | 0 | if (true) |
640 | { | |
641 | // make a new frame! | |
642 | 0 | AlignFrame af = new AlignFrame(al, (HiddenColumns) alAndColsel[1], |
643 | AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT); | |
644 | ||
645 | // >>>This is a fix for the moment, until a better solution is | |
646 | // found!!<<< | |
647 | // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer()); | |
648 | ||
649 | // af.addSortByOrderMenuItem(ServiceName + " Ordering", | |
650 | // msaorder); | |
651 | ||
652 | 0 | Desktop.addInternalFrame(af, MessageManager.formatMessage( |
653 | "label.original_data_for_params", new Object[] | |
654 | { this.title }), AlignFrame.DEFAULT_WIDTH, | |
655 | AlignFrame.DEFAULT_HEIGHT); | |
656 | } | |
657 | } | |
658 | } | |
659 | ||
660 | /** | |
661 | * DOCUMENT ME! | |
662 | * | |
663 | * @param e | |
664 | * DOCUMENT ME! | |
665 | */ | |
666 | 12 | @Override |
667 | public void fitToWindow_actionPerformed(ActionEvent e) | |
668 | { | |
669 | 12 | treeCanvas.fitToWindow = fitToWindow.isSelected(); |
670 | 12 | repaint(); |
671 | } | |
672 | ||
673 | /** | |
674 | * sort the associated alignment view by the current tree. | |
675 | * | |
676 | * @param e | |
677 | */ | |
678 | 1 | @Override |
679 | public void sortByTree_actionPerformed() | |
680 | { | |
681 | ||
682 | 1 | if (treeCanvas.applyToAllViews) |
683 | { | |
684 | 0 | final ArrayList<CommandI> commands = new ArrayList<>(); |
685 | 0 | for (AlignmentPanel ap : PaintRefresher |
686 | .getAssociatedPanels(av.getSequenceSetId())) | |
687 | { | |
688 | 0 | commands.add(sortAlignmentIn(ap.av.getAlignPanel())); |
689 | } | |
690 | 0 | av.getAlignPanel().alignFrame.addHistoryItem(new CommandI() |
691 | { | |
692 | ||
693 | 0 | @Override |
694 | public void undoCommand(AlignmentI[] views) | |
695 | { | |
696 | 0 | for (CommandI tsort : commands) |
697 | { | |
698 | 0 | tsort.undoCommand(views); |
699 | } | |
700 | } | |
701 | ||
702 | 0 | @Override |
703 | public int getSize() | |
704 | { | |
705 | 0 | return commands.size(); |
706 | } | |
707 | ||
708 | 0 | @Override |
709 | public String getDescription() | |
710 | { | |
711 | 0 | return "Tree Sort (many views)"; |
712 | } | |
713 | ||
714 | 0 | @Override |
715 | public void doCommand(AlignmentI[] views) | |
716 | { | |
717 | ||
718 | 0 | for (CommandI tsort : commands) |
719 | { | |
720 | 0 | tsort.doCommand(views); |
721 | } | |
722 | } | |
723 | }); | |
724 | 0 | for (AlignmentPanel ap : PaintRefresher |
725 | .getAssociatedPanels(av.getSequenceSetId())) | |
726 | { | |
727 | // ensure all the alignFrames refresh their GI after adding an undo item | |
728 | 0 | ap.alignFrame.updateEditMenuBar(); |
729 | } | |
730 | } | |
731 | else | |
732 | { | |
733 | 1 | treeCanvas.getAssociatedPanel().alignFrame.addHistoryItem( |
734 | sortAlignmentIn(treeCanvas.getAssociatedPanel())); | |
735 | } | |
736 | ||
737 | } | |
738 | ||
739 | 1 | public CommandI sortAlignmentIn(AlignmentPanel ap) |
740 | { | |
741 | // TODO: move to alignment view controller | |
742 | 1 | AlignmentViewport viewport = ap.av; |
743 | 1 | SequenceI[] oldOrder = viewport.getAlignment().getSequencesArray(); |
744 | 1 | AlignmentSorter.sortByTree(viewport.getAlignment(), tree); |
745 | 1 | CommandI undo; |
746 | 1 | undo = new OrderCommand("Tree Sort", oldOrder, viewport.getAlignment()); |
747 | ||
748 | 1 | ap.paintAlignment(true, false); |
749 | 1 | return undo; |
750 | } | |
751 | ||
752 | /** | |
753 | * DOCUMENT ME! | |
754 | * | |
755 | * @param e | |
756 | * DOCUMENT ME! | |
757 | */ | |
758 | 0 | @Override |
759 | public void font_actionPerformed(ActionEvent e) | |
760 | { | |
761 | 0 | if (treeCanvas == null) |
762 | { | |
763 | 0 | return; |
764 | } | |
765 | ||
766 | 0 | new FontChooser(this); |
767 | } | |
768 | ||
769 | 12 | public Font getTreeFont() |
770 | { | |
771 | 12 | return treeCanvas.font; |
772 | } | |
773 | ||
774 | 12 | public void setTreeFont(Font f) |
775 | { | |
776 | 12 | if (treeCanvas != null) |
777 | { | |
778 | 12 | treeCanvas.setFont(f); |
779 | } | |
780 | } | |
781 | ||
782 | /** | |
783 | * DOCUMENT ME! | |
784 | * | |
785 | * @param e | |
786 | * DOCUMENT ME! | |
787 | */ | |
788 | 0 | @Override |
789 | public void distanceMenu_actionPerformed(ActionEvent e) | |
790 | { | |
791 | 0 | treeCanvas.setShowDistances(distanceMenu.isSelected()); |
792 | } | |
793 | ||
794 | 0 | @Override |
795 | public void hideStructureProviders_actionPerformed(ActionEvent e) | |
796 | { | |
797 | 0 | treeCanvas.hideStructureProviders(hideStructureProviders.isSelected()); |
798 | } | |
799 | ||
800 | 0 | @Override |
801 | public void showStructureProviderColouredLines_actionPerformed(ActionEvent e) | |
802 | { | |
803 | 0 | treeCanvas.setShowStructureProviderColouredLines(showStructureProviderColouredLines.isSelected()); |
804 | } | |
805 | ||
806 | 0 | @Override |
807 | public void showStructureProviderLabels_actionPerformed(ActionEvent e) | |
808 | { | |
809 | 0 | treeCanvas.setShowStructureProviderLabels(showStructureProviderLabels.isSelected()); |
810 | } | |
811 | ||
812 | /** | |
813 | * DOCUMENT ME! | |
814 | * | |
815 | * @param e | |
816 | * DOCUMENT ME! | |
817 | */ | |
818 | 0 | @Override |
819 | public void bootstrapMenu_actionPerformed(ActionEvent e) | |
820 | { | |
821 | 0 | treeCanvas.setShowBootstrap(bootstrapMenu.isSelected()); |
822 | } | |
823 | ||
824 | /** | |
825 | * DOCUMENT ME! | |
826 | * | |
827 | * @param e | |
828 | * DOCUMENT ME! | |
829 | */ | |
830 | 0 | @Override |
831 | public void placeholdersMenu_actionPerformed(ActionEvent e) | |
832 | { | |
833 | 0 | treeCanvas.setMarkPlaceholders(placeholdersMenu.isSelected()); |
834 | } | |
835 | ||
836 | /** | |
837 | * Outputs the Tree in image format (currently EPS or PNG). The user is | |
838 | * prompted for the file to save to, and for EPS (unless a preference is | |
839 | * already set) for the choice of Text or Lineart for character rendering. | |
840 | */ | |
841 | 0 | @Override |
842 | public void writeTreeImage(TYPE imageFormat) | |
843 | { | |
844 | 0 | int width = treeCanvas.getWidth(); |
845 | 0 | int height = treeCanvas.getHeight(); |
846 | 0 | ImageWriterI writer = new ImageWriterI() |
847 | { | |
848 | 0 | @Override |
849 | public void exportImage(Graphics g) throws Exception | |
850 | { | |
851 | 0 | treeCanvas.draw(g, width, height); |
852 | } | |
853 | }; | |
854 | 0 | String tree = MessageManager.getString("label.tree"); |
855 | 0 | ImageExporter exporter = new ImageExporter(writer, null, imageFormat, |
856 | tree); | |
857 | 0 | try |
858 | { | |
859 | 0 | exporter.doExport(null, this, width, height, |
860 | tree.toLowerCase(Locale.ROOT)); | |
861 | } catch (ImageOutputException ioex) | |
862 | { | |
863 | 0 | Console.error( |
864 | "Unexpected error whilst writing " + imageFormat.toString(), | |
865 | ioex); | |
866 | } | |
867 | } | |
868 | ||
869 | /** | |
870 | * change node labels to the annotation referred to by labelClass TODO: | |
871 | * promote to a datamodel modification that can be undone TODO: make argument | |
872 | * one case of a generic transformation function ie { undoStep = apply(Tree, | |
873 | * TransformFunction)}; | |
874 | * | |
875 | * @param labelClass | |
876 | */ | |
877 | 0 | public void changeNames(final String labelClass) |
878 | { | |
879 | 0 | tree.applyToNodes(new NodeTransformI() |
880 | { | |
881 | ||
882 | 0 | @Override |
883 | public void transform(BinaryNode node) | |
884 | { | |
885 | 0 | if (node instanceof SequenceNode |
886 | && !((SequenceNode) node).isPlaceholder() | |
887 | && !((SequenceNode) node).isDummy()) | |
888 | { | |
889 | 0 | String newname = null; |
890 | 0 | SequenceI sq = (SequenceI) ((BinaryNode) node).element(); |
891 | 0 | if (sq != null) |
892 | { | |
893 | // search dbrefs, features and annotation | |
894 | 0 | List<DBRefEntry> refs = jalview.util.DBRefUtils |
895 | .selectRefs(sq.getDBRefs(), new String[] | |
896 | { labelClass.toUpperCase(Locale.ROOT) }); | |
897 | 0 | if (refs != null) |
898 | { | |
899 | 0 | for (int i = 0, ni = refs.size(); i < ni; i++) |
900 | { | |
901 | 0 | if (newname == null) |
902 | { | |
903 | 0 | newname = new String(refs.get(i).getAccessionId()); |
904 | } | |
905 | else | |
906 | { | |
907 | 0 | newname += "; " + refs.get(i).getAccessionId(); |
908 | } | |
909 | } | |
910 | } | |
911 | 0 | if (newname == null) |
912 | { | |
913 | 0 | List<SequenceFeature> features = sq.getFeatures() |
914 | .getPositionalFeatures(labelClass); | |
915 | 0 | for (SequenceFeature feature : features) |
916 | { | |
917 | 0 | if (newname == null) |
918 | { | |
919 | 0 | newname = feature.getDescription(); |
920 | } | |
921 | else | |
922 | { | |
923 | 0 | newname = newname + "; " + feature.getDescription(); |
924 | } | |
925 | } | |
926 | } | |
927 | } | |
928 | 0 | if (newname != null) |
929 | { | |
930 | // String oldname = ((SequenceNode) node).getName(); | |
931 | // TODO : save oldname in the undo object for this modification. | |
932 | 0 | ((BinaryNode) node).setName(newname); |
933 | } | |
934 | } | |
935 | } | |
936 | }); | |
937 | } | |
938 | ||
939 | /** | |
940 | * Formats a localised title for the tree panel, like | |
941 | * <p> | |
942 | * Neighbour Joining Using BLOSUM62 | |
943 | * <p> | |
944 | * For a tree loaded from file, just uses the file name | |
945 | * | |
946 | * @return | |
947 | */ | |
948 | 0 | public String getPanelTitle() |
949 | { | |
950 | 0 | if (treeTitle != null) |
951 | { | |
952 | 0 | return treeTitle; |
953 | } | |
954 | ||
955 | /* | |
956 | * i18n description of Neighbour Joining or Average Distance method | |
957 | */ | |
958 | 0 | String treecalcnm = MessageManager.getString( |
959 | "label.tree_calc_" + treeType.toLowerCase(Locale.ROOT)); | |
960 | ||
961 | /* | |
962 | * short score model name (long description can be too long) | |
963 | */ | |
964 | 0 | String smn = scoreModelName; |
965 | ||
966 | /* | |
967 | * put them together as <method> Using <model> | |
968 | */ | |
969 | 0 | final String ttl = MessageManager.formatMessage("label.calc_title", |
970 | treecalcnm, smn); | |
971 | 0 | return ttl; |
972 | } | |
973 | ||
974 | /** | |
975 | * Builds an EPS image and writes it to the specified file. | |
976 | * | |
977 | * @param outFile | |
978 | * @param textOption | |
979 | * true for Text character rendering, false for Lineart | |
980 | */ | |
981 | 0 | protected void writeEpsFile(File outFile, boolean textOption) |
982 | { | |
983 | 0 | try |
984 | { | |
985 | 0 | int width = treeCanvas.getWidth(); |
986 | 0 | int height = treeCanvas.getHeight(); |
987 | ||
988 | 0 | FileOutputStream out = new FileOutputStream(outFile); |
989 | 0 | EpsGraphics2D pg = new EpsGraphics2D("Tree", out, 0, 0, width, |
990 | height); | |
991 | 0 | pg.setAccurateTextMode(!textOption); |
992 | 0 | treeCanvas.draw(pg, width, height); |
993 | ||
994 | 0 | pg.flush(); |
995 | 0 | pg.close(); |
996 | } catch (Exception ex) | |
997 | { | |
998 | 0 | jalview.bin.Console.errPrintln("Error writing tree as EPS"); |
999 | 0 | ex.printStackTrace(); |
1000 | } | |
1001 | } | |
1002 | ||
1003 | 0 | public AlignViewport getViewport() |
1004 | { | |
1005 | 0 | return av; |
1006 | } | |
1007 | ||
1008 | 0 | public void setViewport(AlignViewport av) |
1009 | { | |
1010 | 0 | this.av = av; |
1011 | } | |
1012 | ||
1013 | 42 | public TreeCanvas getTreeCanvas() |
1014 | { | |
1015 | 42 | return treeCanvas; |
1016 | } | |
1017 | ||
1018 | 0 | public static TreePanel newTreeForAnnotations(AlignmentPanel alignPanel, |
1019 | NewickFile nf, String treeTitle2, AlignmentView input) | |
1020 | { | |
1021 | // TODO - recover subpanel name | |
1022 | 0 | TreePanel tp = new TreePanel(alignPanel, nf, treeTitle2, input, null, |
1023 | "Annotation Tree"); | |
1024 | 0 | return tp; |
1025 | } | |
1026 | } |