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