Class |
Line # |
Actions |
|||
---|---|---|---|---|---|
CalculationChooser | 80 | 255 | 88 |
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 jalview.analysis.TreeBuilder; | |
24 | import jalview.analysis.scoremodels.ScoreModels; | |
25 | import jalview.analysis.scoremodels.SimilarityParams; | |
26 | import jalview.api.analysis.ScoreModelI; | |
27 | import jalview.api.analysis.SimilarityParamsI; | |
28 | import jalview.bin.Cache; | |
29 | import jalview.datamodel.SequenceGroup; | |
30 | import jalview.util.MessageManager; | |
31 | import jalview.util.Platform; | |
32 | import jalview.viewmodel.AlignmentViewport; | |
33 | import java.awt.BorderLayout; | |
34 | import java.awt.Color; | |
35 | import java.awt.Component; | |
36 | import java.awt.Dimension; | |
37 | import java.awt.FlowLayout; | |
38 | import java.awt.Font; | |
39 | import java.awt.GridLayout; | |
40 | import java.awt.Insets; | |
41 | import java.awt.event.ActionEvent; | |
42 | import java.awt.event.ActionListener; | |
43 | import java.awt.event.FocusEvent; | |
44 | import java.awt.event.FocusListener; | |
45 | import java.awt.event.MouseAdapter; | |
46 | import java.awt.event.MouseEvent; | |
47 | import java.beans.PropertyVetoException; | |
48 | import java.util.ArrayList; | |
49 | import java.util.List; | |
50 | ||
51 | import javax.swing.BorderFactory; | |
52 | import javax.swing.ButtonGroup; | |
53 | import javax.swing.DefaultComboBoxModel; | |
54 | import javax.swing.JButton; | |
55 | import javax.swing.JCheckBox; | |
56 | import javax.swing.JComboBox; | |
57 | import javax.swing.JInternalFrame; | |
58 | import javax.swing.JLabel; | |
59 | import javax.swing.JLayeredPane; | |
60 | import javax.swing.JPanel; | |
61 | import javax.swing.JRadioButton; | |
62 | import javax.swing.event.InternalFrameAdapter; | |
63 | import javax.swing.event.InternalFrameEvent; | |
64 | ||
65 | import jalview.analysis.AlignmentUtils; | |
66 | import jalview.analysis.TreeBuilder; | |
67 | import jalview.analysis.scoremodels.ScoreMatrix; | |
68 | import jalview.analysis.scoremodels.ScoreModels; | |
69 | import jalview.analysis.scoremodels.SimilarityParams; | |
70 | import jalview.api.analysis.ScoreModelI; | |
71 | import jalview.api.analysis.SimilarityParamsI; | |
72 | import jalview.bin.Cache; | |
73 | import jalview.datamodel.AlignmentAnnotation; | |
74 | import jalview.datamodel.SequenceGroup; | |
75 | import jalview.util.MessageManager; | |
76 | ||
77 | /** | |
78 | * A dialog where a user can choose and action Tree or PCA calculation options | |
79 | */ | |
80 | public class CalculationChooser extends JPanel | |
81 | { | |
82 | /* | |
83 | * flag for whether gap matches residue in the PID calculation for a Tree | |
84 | * - true gives Jalview 2.10.1 behaviour | |
85 | * - set to false (using Groovy) for a more correct tree | |
86 | * (JAL-374) | |
87 | */ | |
88 | private static boolean treeMatchGaps = true; | |
89 | ||
90 | private static final Font VERDANA_11PT = new Font("Verdana", 0, 11); | |
91 | ||
92 | private static final int MIN_PAIRWISE_SELECTION = 2; | |
93 | ||
94 | private static final int MIN_TREE_SELECTION = 3; | |
95 | ||
96 | private static final int MIN_PCA_SELECTION = 4; | |
97 | ||
98 | private String secondaryStructureModelName; | |
99 | ||
100 | 0 | private void getSecondaryStructureModelName() |
101 | { | |
102 | ||
103 | 0 | ScoreModels scoreModels = ScoreModels.getInstance(); |
104 | 0 | for (ScoreModelI sm : scoreModels.getModels()) |
105 | { | |
106 | 0 | if (sm.isSecondaryStructure()) |
107 | { | |
108 | 0 | secondaryStructureModelName = sm.getName(); |
109 | } | |
110 | } | |
111 | ||
112 | } | |
113 | ||
114 | /** | |
115 | * minimum number of sequences needed for PASIMAP is 9 (so each has 8 | |
116 | * connections) | |
117 | */ | |
118 | private static final int MIN_PASIMAP_SELECTION = 9; | |
119 | ||
120 | AlignFrame af; | |
121 | ||
122 | JRadioButton pairwise; | |
123 | ||
124 | JRadioButton pca; | |
125 | ||
126 | JRadioButton pasimap; | |
127 | ||
128 | JRadioButton neighbourJoining; | |
129 | ||
130 | JRadioButton averageDistance; | |
131 | ||
132 | JComboBox<String> modelNames; | |
133 | ||
134 | JComboBox<String> ssSourceDropdown; | |
135 | ||
136 | JButton calculate; | |
137 | ||
138 | private JInternalFrame frame; | |
139 | ||
140 | private JCheckBox includeGaps; | |
141 | ||
142 | private JCheckBox matchGaps; | |
143 | ||
144 | private JCheckBox includeGappedColumns; | |
145 | ||
146 | private JCheckBox shorterSequence; | |
147 | ||
148 | final ComboBoxTooltipRenderer renderer = new ComboBoxTooltipRenderer(); | |
149 | ||
150 | List<String> tips = new ArrayList<>(); | |
151 | ||
152 | /* | |
153 | * the most recently opened PCA results panel | |
154 | */ | |
155 | private PCAPanel pcaPanel; | |
156 | ||
157 | private PaSiMapPanel pasimapPanel; | |
158 | ||
159 | /** | |
160 | * Constructor | |
161 | * | |
162 | * @param af | |
163 | */ | |
164 | 0 | public CalculationChooser(AlignFrame alignFrame) |
165 | { | |
166 | 0 | this.af = alignFrame; |
167 | 0 | init(); |
168 | 0 | af.alignPanel.setCalculationDialog(this); |
169 | ||
170 | } | |
171 | ||
172 | /** | |
173 | * Lays out the panel and adds it to the desktop | |
174 | */ | |
175 | 0 | void init() |
176 | { | |
177 | 0 | getSecondaryStructureModelName(); |
178 | 0 | setLayout(new BorderLayout()); |
179 | 0 | frame = new JInternalFrame(); |
180 | 0 | frame.setFrameIcon(null); |
181 | 0 | frame.setContentPane(this); |
182 | 0 | this.setBackground(Color.white); |
183 | 0 | frame.addFocusListener(new FocusListener() |
184 | { | |
185 | ||
186 | 0 | @Override |
187 | public void focusLost(FocusEvent e) | |
188 | { | |
189 | } | |
190 | ||
191 | 0 | @Override |
192 | public void focusGained(FocusEvent e) | |
193 | { | |
194 | 0 | validateCalcTypes(); |
195 | } | |
196 | }); | |
197 | /* | |
198 | * Layout consists of 3 or 4 panels: | |
199 | * - first with choice of PCA or tree method NJ or AV | |
200 | * - second with choice of score model | |
201 | * - third with score model parameter options [suppressed] | |
202 | * - fourth with OK and Cancel | |
203 | */ | |
204 | 0 | pca = new JRadioButton( |
205 | MessageManager.getString("label.principal_component_analysis")); | |
206 | 0 | pca.setOpaque(false); |
207 | ||
208 | 0 | pasimap = new JRadioButton( // create the JRadioButton for pasimap with |
209 | // label.pasimap as its text | |
210 | MessageManager.getString("label.pasimap")); | |
211 | 0 | pasimap.setOpaque(false); |
212 | ||
213 | 0 | neighbourJoining = new JRadioButton( |
214 | MessageManager.getString("label.tree_calc_nj")); | |
215 | 0 | neighbourJoining.setSelected(true); |
216 | 0 | neighbourJoining.setOpaque(false); |
217 | ||
218 | 0 | averageDistance = new JRadioButton( |
219 | MessageManager.getString("label.tree_calc_av")); | |
220 | 0 | averageDistance.setOpaque(false); |
221 | ||
222 | 0 | pairwise = new JRadioButton( |
223 | MessageManager.getString("action.pairwise_alignment")); | |
224 | 0 | pairwise.setOpaque(false); |
225 | ||
226 | 0 | JPanel calcChoicePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); |
227 | 0 | calcChoicePanel.setOpaque(false); |
228 | ||
229 | // first create the Tree calculation's border panel | |
230 | 0 | JPanel treePanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); |
231 | 0 | treePanel.setOpaque(false); |
232 | ||
233 | 0 | JvSwingUtils.createTitledBorder(treePanel, |
234 | MessageManager.getString("label.tree"), true); | |
235 | ||
236 | // then copy the inset dimensions for the border-less PCA panel | |
237 | 0 | JPanel pcaBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT)); |
238 | 0 | Insets b = treePanel.getBorder().getBorderInsets(treePanel); |
239 | 0 | pcaBorderless.setBorder( |
240 | BorderFactory.createEmptyBorder(2, b.left, 2, b.right)); | |
241 | 0 | pcaBorderless.setOpaque(false); |
242 | ||
243 | 0 | pcaBorderless.add(pca, FlowLayout.LEFT); |
244 | 0 | calcChoicePanel.add(pcaBorderless, FlowLayout.LEFT); |
245 | ||
246 | // create pasimap panel | |
247 | 0 | JPanel pasimapBorderless = new JPanel(new FlowLayout(FlowLayout.LEFT)); // create |
248 | // new | |
249 | // JPanel | |
250 | // (button) | |
251 | // for | |
252 | // pasimap | |
253 | 0 | pasimapBorderless.setBorder( |
254 | BorderFactory.createEmptyBorder(2, b.left, 2, b.right)); // set | |
255 | // border | |
256 | // (margin) | |
257 | // for | |
258 | // button | |
259 | // (same as | |
260 | // treePanel | |
261 | // and pca) | |
262 | 0 | pasimapBorderless.setOpaque(false); // false -> stops every pixel inside |
263 | // border from being painted | |
264 | 0 | pasimapBorderless.add(pasimap, FlowLayout.LEFT); // add pasimap button to |
265 | // the JPanel | |
266 | 0 | if (!Platform.isJS()) |
267 | { | |
268 | // FIXME JAL-4443 | |
269 | 0 | calcChoicePanel.add(pasimapBorderless, FlowLayout.LEFT); // add button |
270 | // with | |
271 | // border and | |
272 | // everything to | |
273 | // the overall | |
274 | // ChoicePanel | |
275 | } | |
276 | ||
277 | 0 | treePanel.add(neighbourJoining); |
278 | 0 | treePanel.add(averageDistance); |
279 | ||
280 | 0 | calcChoicePanel.add(treePanel); |
281 | 0 | calcChoicePanel.add(pairwise, FlowLayout.CENTER); |
282 | ||
283 | 0 | ButtonGroup calcTypes = new ButtonGroup(); |
284 | 0 | calcTypes.add(pca); |
285 | 0 | if (!Platform.isJS()) |
286 | { | |
287 | // FIXME JAL-4443 | |
288 | 0 | calcTypes.add(pasimap); |
289 | } | |
290 | 0 | calcTypes.add(neighbourJoining); |
291 | 0 | calcTypes.add(averageDistance); |
292 | 0 | calcTypes.add(pairwise); |
293 | ||
294 | 0 | ActionListener calcChanged = new ActionListener() |
295 | { | |
296 | 0 | @Override |
297 | public void actionPerformed(ActionEvent e) | |
298 | { | |
299 | 0 | validateCalcTypes(); |
300 | } | |
301 | }; | |
302 | 0 | pca.addActionListener(calcChanged); |
303 | 0 | pasimap.addActionListener(calcChanged); // add the calcChanged |
304 | // ActionListener to pasimap --> | |
305 | // <++> idk | |
306 | 0 | neighbourJoining.addActionListener(calcChanged); |
307 | 0 | averageDistance.addActionListener(calcChanged); |
308 | ||
309 | // to do | |
310 | 0 | ssSourceDropdown = buildSSSourcesOptionsList(); |
311 | 0 | ssSourceDropdown.setVisible(false); // Initially hide the dropdown |
312 | 0 | pairwise.addActionListener(calcChanged); |
313 | /* | |
314 | * score models drop-down - with added tooltips! | |
315 | */ | |
316 | 0 | modelNames = buildModelOptionsList(); |
317 | ||
318 | // Step 3: Show or Hide Dropdown Based on Selection | |
319 | 0 | modelNames.addActionListener(new ActionListener() |
320 | { | |
321 | 0 | @Override |
322 | public void actionPerformed(ActionEvent e) | |
323 | { | |
324 | 0 | String selectedModel = modelNames.getSelectedItem().toString(); |
325 | ||
326 | 0 | if (selectedModel.equals(secondaryStructureModelName)) |
327 | { | |
328 | 0 | ssSourceDropdown.setVisible(true); |
329 | } | |
330 | else | |
331 | { | |
332 | 0 | ssSourceDropdown.setVisible(false); |
333 | } | |
334 | } | |
335 | }); | |
336 | ||
337 | 0 | JPanel scoreModelPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); |
338 | 0 | scoreModelPanel.setOpaque(false); |
339 | 0 | scoreModelPanel.add(modelNames); |
340 | 0 | scoreModelPanel.add(ssSourceDropdown); |
341 | ||
342 | /* | |
343 | * score model parameters | |
344 | */ | |
345 | 0 | JPanel paramsPanel = new JPanel(new GridLayout(5, 1)); |
346 | 0 | paramsPanel.setOpaque(false); |
347 | 0 | includeGaps = new JCheckBox("Include gaps"); |
348 | 0 | matchGaps = new JCheckBox("Match gaps"); |
349 | 0 | includeGappedColumns = new JCheckBox("Include gapped columns"); |
350 | 0 | shorterSequence = new JCheckBox("Match on shorter sequence"); |
351 | 0 | paramsPanel.add(new JLabel("Pairwise sequence scoring options")); |
352 | 0 | paramsPanel.add(includeGaps); |
353 | 0 | paramsPanel.add(matchGaps); |
354 | 0 | paramsPanel.add(includeGappedColumns); |
355 | 0 | paramsPanel.add(shorterSequence); |
356 | ||
357 | /* | |
358 | * OK / Cancel buttons | |
359 | */ | |
360 | 0 | calculate = new JButton(MessageManager.getString("action.calculate")); |
361 | 0 | calculate.setFont(VERDANA_11PT); |
362 | 0 | calculate.addActionListener(new java.awt.event.ActionListener() |
363 | { | |
364 | 0 | @Override |
365 | public void actionPerformed(ActionEvent e) | |
366 | { | |
367 | 0 | calculate_actionPerformed(); |
368 | } | |
369 | }); | |
370 | 0 | JButton close = new JButton(MessageManager.getString("action.close")); |
371 | 0 | close.setFont(VERDANA_11PT); |
372 | 0 | close.addActionListener(new java.awt.event.ActionListener() |
373 | { | |
374 | 0 | @Override |
375 | public void actionPerformed(ActionEvent e) | |
376 | { | |
377 | 0 | close_actionPerformed(); |
378 | } | |
379 | }); | |
380 | 0 | JPanel actionPanel = new JPanel(); |
381 | 0 | actionPanel.setOpaque(false); |
382 | 0 | actionPanel.add(calculate); |
383 | 0 | actionPanel.add(close); |
384 | ||
385 | 0 | boolean includeParams = false; |
386 | 0 | this.add(calcChoicePanel, BorderLayout.CENTER); |
387 | 0 | calcChoicePanel.add(scoreModelPanel); |
388 | 0 | if (includeParams) |
389 | { | |
390 | 0 | scoreModelPanel.add(paramsPanel); |
391 | } | |
392 | 0 | this.add(actionPanel, BorderLayout.SOUTH); |
393 | ||
394 | 0 | int width = 375; |
395 | 0 | int height = includeParams ? 420 : 240; |
396 | ||
397 | 0 | setMinimumSize(new Dimension(325, height - 10)); |
398 | 0 | String title = MessageManager.getString("label.choose_calculation"); |
399 | 0 | if (af.getViewport().getViewName() != null) |
400 | { | |
401 | 0 | title = title + " (" + af.getViewport().getViewName() + ")"; |
402 | } | |
403 | ||
404 | 0 | Desktop.addInternalFrame(frame, title, width, height, false); |
405 | 0 | calcChoicePanel.doLayout(); |
406 | 0 | revalidate(); |
407 | /* | |
408 | * null the AlignmentPanel's reference to the dialog when it is closed | |
409 | */ | |
410 | 0 | frame.addInternalFrameListener(new InternalFrameAdapter() |
411 | { | |
412 | 0 | @Override |
413 | public void internalFrameClosed(InternalFrameEvent evt) | |
414 | { | |
415 | 0 | af.alignPanel.setCalculationDialog(null); |
416 | }; | |
417 | }); | |
418 | ||
419 | 0 | validateCalcTypes(); |
420 | 0 | frame.setLayer(JLayeredPane.PALETTE_LAYER); |
421 | } | |
422 | ||
423 | /** | |
424 | * enable calculations applicable for the current alignment or selection. | |
425 | */ | |
426 | 0 | protected void validateCalcTypes() |
427 | { | |
428 | 0 | int size = af.getViewport().getAlignment().getHeight(); |
429 | 0 | if (af.getViewport().getSelectionGroup() != null) |
430 | { | |
431 | 0 | size = af.getViewport().getSelectionGroup().getSize(); |
432 | } | |
433 | ||
434 | /* | |
435 | * disable calc options for which there is insufficient input data | |
436 | * return value of true means enabled and selected | |
437 | */ | |
438 | 0 | boolean checkPca = checkEnabled(pca, size, MIN_PCA_SELECTION); |
439 | 0 | boolean checkPasimap = checkEnabled(pasimap, size, |
440 | MIN_PASIMAP_SELECTION); // check if pasimap is enabled and min_size | |
441 | // is fulfilled | |
442 | 0 | boolean checkNeighbourJoining = checkEnabled(neighbourJoining, size, |
443 | MIN_TREE_SELECTION); | |
444 | 0 | boolean checkAverageDistance = checkEnabled(averageDistance, size, |
445 | MIN_TREE_SELECTION); | |
446 | 0 | boolean checkPairwise = checkEnabled(pairwise, size, |
447 | MIN_PAIRWISE_SELECTION); | |
448 | ||
449 | 0 | if (checkPca || checkPasimap || checkPca || checkNeighbourJoining |
450 | || checkAverageDistance || checkPairwise) | |
451 | { | |
452 | 0 | calculate.setToolTipText(null); |
453 | 0 | calculate.setEnabled(true); |
454 | } | |
455 | else | |
456 | { | |
457 | 0 | calculate.setEnabled(false); |
458 | } | |
459 | 0 | updateScoreModels(modelNames, tips); |
460 | } | |
461 | ||
462 | /** | |
463 | * Check the input and disable a calculation's radio button if necessary. A | |
464 | * tooltip is shown for disabled calculations. | |
465 | * | |
466 | * @param calc | |
467 | * - radio button for the calculation being validated | |
468 | * @param size | |
469 | * - size of input to calculation | |
470 | * @param minsize | |
471 | * - minimum size for calculation | |
472 | * @return true if size >= minsize and calc.isSelected | |
473 | */ | |
474 | 0 | private boolean checkEnabled(JRadioButton calc, int size, int minsize) |
475 | { | |
476 | 0 | String ttip = MessageManager |
477 | .formatMessage("label.you_need_at_least_n_sequences", minsize); | |
478 | ||
479 | 0 | calc.setEnabled(size >= minsize); |
480 | 0 | if (!calc.isEnabled()) |
481 | { | |
482 | 0 | calc.setToolTipText(ttip); |
483 | } | |
484 | else | |
485 | { | |
486 | 0 | calc.setToolTipText(null); |
487 | } | |
488 | 0 | if (calc.isSelected()) |
489 | { | |
490 | 0 | modelNames.setEnabled(calc.isEnabled()); |
491 | 0 | if (calc.isEnabled()) |
492 | { | |
493 | 0 | return true; |
494 | } | |
495 | else | |
496 | { | |
497 | 0 | calculate.setToolTipText(ttip); |
498 | } | |
499 | } | |
500 | 0 | return false; |
501 | } | |
502 | ||
503 | /** | |
504 | * A rather elaborate helper method (blame Swing, not me) that builds a | |
505 | * drop-down list of score models (by name) with descriptions as tooltips. | |
506 | * There is also a tooltip shown for the currently selected item when hovering | |
507 | * over it (without opening the list). | |
508 | */ | |
509 | 0 | protected JComboBox<String> buildModelOptionsList() |
510 | { | |
511 | 0 | final JComboBox<String> scoreModelsCombo = new JComboBox<>(); |
512 | 0 | scoreModelsCombo.setRenderer(renderer); |
513 | ||
514 | /* | |
515 | * show tooltip on mouse over the combobox | |
516 | * note the listener has to be on the components that make up | |
517 | * the combobox, doesn't work if just on the combobox | |
518 | */ | |
519 | 0 | final MouseAdapter mouseListener = new MouseAdapter() |
520 | { | |
521 | 0 | @Override |
522 | public void mouseEntered(MouseEvent e) | |
523 | { | |
524 | 0 | scoreModelsCombo.setToolTipText( |
525 | tips.get(scoreModelsCombo.getSelectedIndex())); | |
526 | } | |
527 | ||
528 | 0 | @Override |
529 | public void mouseExited(MouseEvent e) | |
530 | { | |
531 | 0 | scoreModelsCombo.setToolTipText(null); |
532 | } | |
533 | }; | |
534 | 0 | for (Component c : scoreModelsCombo.getComponents()) |
535 | { | |
536 | 0 | c.addMouseListener(mouseListener); |
537 | } | |
538 | ||
539 | 0 | updateScoreModels(scoreModelsCombo, tips); |
540 | ||
541 | /* | |
542 | * set the list of tooltips on the combobox's renderer | |
543 | */ | |
544 | 0 | renderer.setTooltips(tips); |
545 | ||
546 | 0 | return scoreModelsCombo; |
547 | } | |
548 | ||
549 | 0 | private JComboBox<String> buildSSSourcesOptionsList() |
550 | { | |
551 | 0 | final JComboBox<String> comboBox = new JComboBox<>(); |
552 | 0 | Object curSel = comboBox.getSelectedItem(); |
553 | 0 | DefaultComboBoxModel<String> sourcesModel = new DefaultComboBoxModel<>(); |
554 | ||
555 | 0 | List<String> ssSources = getApplicableSecondaryStructureSources(); |
556 | ||
557 | 0 | if (ssSources == null) |
558 | { | |
559 | 0 | return comboBox; |
560 | } | |
561 | 0 | ssSources.add(0, MessageManager.getString("option.ss_providers_all")); |
562 | ||
563 | 0 | boolean selectedIsPresent = false; |
564 | 0 | for (String source : ssSources) |
565 | { | |
566 | 0 | if (curSel != null && source.equals(curSel)) |
567 | { | |
568 | 0 | selectedIsPresent = true; |
569 | 0 | curSel = source; |
570 | } | |
571 | 0 | sourcesModel.addElement(source); |
572 | ||
573 | } | |
574 | ||
575 | 0 | if (selectedIsPresent) |
576 | { | |
577 | 0 | sourcesModel.setSelectedItem(curSel); |
578 | } | |
579 | 0 | comboBox.setModel(sourcesModel); |
580 | ||
581 | 0 | return comboBox; |
582 | } | |
583 | ||
584 | 0 | private void updateScoreModels(JComboBox<String> comboBox, |
585 | List<String> toolTips) | |
586 | { | |
587 | 0 | Object curSel = comboBox.getSelectedItem(); |
588 | 0 | toolTips.clear(); |
589 | 0 | DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>(); |
590 | ||
591 | /* | |
592 | * select the score models applicable to the alignment type | |
593 | */ | |
594 | 0 | boolean nucleotide = af.getViewport().getAlignment().isNucleotide(); |
595 | 0 | AlignmentAnnotation[] alignmentAnnotations = af.getViewport() |
596 | .getAlignment().getAlignmentAnnotation(); | |
597 | ||
598 | 0 | boolean ssPresent = AlignmentUtils |
599 | .isSecondaryStructurePresent(alignmentAnnotations); | |
600 | ||
601 | 0 | List<ScoreModelI> models = getApplicableScoreModels(nucleotide, |
602 | pca.isSelected(), ssPresent, (pasimap.isSelected() || pairwise.isSelected())); | |
603 | ||
604 | /* | |
605 | * now we can actually add entries to the combobox, | |
606 | * remembering their descriptions for tooltips | |
607 | */ | |
608 | 0 | boolean selectedIsPresent = false; |
609 | 0 | for (ScoreModelI sm : models) |
610 | { | |
611 | 0 | if (curSel != null && sm.getName().equals(curSel)) |
612 | { | |
613 | 0 | selectedIsPresent = true; |
614 | 0 | curSel = sm.getName(); |
615 | } | |
616 | 0 | model.addElement(sm.getName()); |
617 | ||
618 | /* | |
619 | * tooltip is description if provided, else text lookup with | |
620 | * fallback on the model name | |
621 | */ | |
622 | 0 | String tooltip = sm.getDescription(); |
623 | 0 | if (tooltip == null) |
624 | { | |
625 | 0 | tooltip = MessageManager.getStringOrReturn("label.score_model_", |
626 | sm.getName()); | |
627 | } | |
628 | 0 | toolTips.add(tooltip); |
629 | } | |
630 | ||
631 | 0 | if (selectedIsPresent) |
632 | { | |
633 | 0 | model.setSelectedItem(curSel); |
634 | } | |
635 | // finally, update the model | |
636 | 0 | comboBox.setModel(model); |
637 | 0 | comboBox.setEnabled(model.getSize() > 0); |
638 | ||
639 | } | |
640 | ||
641 | /** | |
642 | * Builds a list of score models which are applicable for the alignment and | |
643 | * calculation type (peptide or generic models for protein, nucleotide or | |
644 | * generic models for nucleotide). | |
645 | * <p> | |
646 | * As a special case, includes BLOSUM62 as an extra option for nucleotide PCA. | |
647 | * This is for backwards compatibility with Jalview prior to 2.8 when BLOSUM62 | |
648 | * was the only score matrix supported. This is included if property | |
649 | * BLOSUM62_PCA_FOR_NUCLEOTIDE is set to true in the Jalview properties file. | |
650 | * | |
651 | * @param nucleotide | |
652 | * @param forPca | |
653 | * @param ssPresent | |
654 | * - include secondary structure similarity model | |
655 | * @param forPasimap | |
656 | * - limit to ScoreMatrix based models - allows use of AlignSeq | |
657 | * @return | |
658 | */ | |
659 | 16 | protected static List<ScoreModelI> getApplicableScoreModels( |
660 | boolean nucleotide, boolean forPca, boolean ssPresent, | |
661 | boolean forPasimap) | |
662 | { | |
663 | 16 | List<ScoreModelI> filtered = new ArrayList<>(); |
664 | ||
665 | 16 | ScoreModels scoreModels = ScoreModels.getInstance(); |
666 | 16 | for (ScoreModelI sm : scoreModels.getModels()) |
667 | { | |
668 | 96 | if ((!forPasimap || sm instanceof ScoreMatrix) |
669 | && (!nucleotide && sm.isProtein() || nucleotide && sm.isDNA() | |
670 | || sm.isSecondaryStructure() && ssPresent)) | |
671 | ||
672 | { | |
673 | 38 | filtered.add(sm); |
674 | } | |
675 | } | |
676 | ||
677 | /* | |
678 | * special case: add BLOSUM62 as last option for nucleotide PCA, | |
679 | * for backwards compatibility with Jalview < 2.8 (JAL-2962) | |
680 | */ | |
681 | 16 | if (!forPasimap && nucleotide && forPca |
682 | && Cache.getDefault("BLOSUM62_PCA_FOR_NUCLEOTIDE", false)) | |
683 | { | |
684 | 1 | filtered.add(scoreModels.getBlosum62()); |
685 | } | |
686 | ||
687 | 16 | return filtered; |
688 | } | |
689 | ||
690 | 0 | protected List<String> getApplicableSecondaryStructureSources() |
691 | { | |
692 | 0 | AlignmentAnnotation[] annotations = af.getViewport().getAlignment() |
693 | .getAlignmentAnnotation(); | |
694 | ||
695 | 0 | List<String> ssSources = AlignmentUtils |
696 | .extractSSSourceInAlignmentAnnotation(annotations); | |
697 | 0 | return ssSources; |
698 | } | |
699 | ||
700 | /** | |
701 | * Open and calculate the selected tree or PCA on 'OK' | |
702 | */ | |
703 | 0 | protected void calculate_actionPerformed() |
704 | { | |
705 | 0 | boolean doPCA = pca.isSelected(); |
706 | 0 | boolean doPaSiMap = pasimap.isSelected(); |
707 | 0 | boolean doPairwise = pairwise.isSelected(); |
708 | 0 | String modelName = modelNames.getSelectedItem() == null ? "" |
709 | : modelNames.getSelectedItem().toString(); | |
710 | ||
711 | 0 | String ssSource = null; |
712 | ||
713 | 0 | if (modelName.equals(secondaryStructureModelName)) |
714 | { | |
715 | 0 | Object selectedItem = ssSourceDropdown.getSelectedItem(); |
716 | 0 | if (selectedItem != null) |
717 | { | |
718 | 0 | ssSource = selectedItem.toString(); |
719 | } | |
720 | } | |
721 | 0 | SimilarityParams params = getSimilarityParameters(doPCA); |
722 | 0 | params.setSecondaryStructureSource(ssSource); |
723 | ||
724 | 0 | if (doPCA) |
725 | { | |
726 | 0 | openPcaPanel(modelName, params); |
727 | } | |
728 | 0 | else if (doPaSiMap) |
729 | { | |
730 | 0 | openPasimapPanel(modelName, params); |
731 | } | |
732 | 0 | else if (doPairwise) |
733 | { | |
734 | 0 | openPairwisePanel(modelName, params); |
735 | } | |
736 | else | |
737 | { | |
738 | 0 | openTreePanel(modelName, params); |
739 | } | |
740 | ||
741 | 0 | closeFrame(); |
742 | } | |
743 | ||
744 | 0 | private void openPairwisePanel(String modelName, SimilarityParamsI params) |
745 | { | |
746 | 0 | ScoreModelI sm = ScoreModels.getInstance().getScoreModel(modelName, |
747 | af.alignPanel); | |
748 | 0 | if (sm == null || !(sm instanceof ScoreMatrix)) |
749 | { | |
750 | 0 | return; |
751 | } | |
752 | 0 | new Thread(new Runnable() |
753 | { | |
754 | 0 | @Override |
755 | public void run() | |
756 | { | |
757 | 0 | String pairwise_alignment_title = af.formCalculationTitle( |
758 | MessageManager.getString("action.pairwise_alignment") | |
759 | + " with " + sm.getName(), | |
760 | af.getViewport().getSelectionGroup() != null, | |
761 | af.getTitle()); | |
762 | 0 | JInternalFrame frame = new JInternalFrame(); |
763 | 0 | frame.setFrameIcon(null); |
764 | 0 | frame.setContentPane( |
765 | new PairwiseAlignPanel(af.getViewport(), (ScoreMatrix) sm)); | |
766 | 0 | Desktop.addInternalFrame(frame, pairwise_alignment_title, 600, 500); |
767 | } | |
768 | }).start(); | |
769 | } | |
770 | ||
771 | /** | |
772 | * Open a new Tree panel on the desktop | |
773 | * | |
774 | * @param modelName | |
775 | * @param params | |
776 | */ | |
777 | 0 | protected void openTreePanel(String modelName, SimilarityParamsI params) |
778 | { | |
779 | /* | |
780 | * gui validation shouldn't allow insufficient sequences here, but leave | |
781 | * this check in in case this method gets exposed programmatically in future | |
782 | */ | |
783 | 0 | AlignViewport viewport = af.getViewport(); |
784 | 0 | SequenceGroup sg = viewport.getSelectionGroup(); |
785 | 0 | if (sg != null && sg.getSize() < MIN_TREE_SELECTION) |
786 | { | |
787 | 0 | JvOptionPane.showMessageDialog(Desktop.desktop, |
788 | MessageManager.formatMessage( | |
789 | "label.you_need_at_least_n_sequences", | |
790 | MIN_TREE_SELECTION), | |
791 | MessageManager.getString("label.not_enough_sequences"), | |
792 | JvOptionPane.WARNING_MESSAGE); | |
793 | 0 | return; |
794 | } | |
795 | ||
796 | 0 | String treeType = neighbourJoining.isSelected() |
797 | ? TreeBuilder.NEIGHBOUR_JOINING | |
798 | : TreeBuilder.AVERAGE_DISTANCE; | |
799 | 0 | af.newTreePanel(treeType, modelName, params); |
800 | } | |
801 | ||
802 | /** | |
803 | * Open a new PCA panel on the desktop | |
804 | * | |
805 | * @param modelName | |
806 | * @param params | |
807 | */ | |
808 | 0 | protected void openPcaPanel(String modelName, SimilarityParamsI params) |
809 | { | |
810 | 0 | AlignViewport viewport = af.getViewport(); |
811 | ||
812 | /* | |
813 | * gui validation shouldn't allow insufficient sequences here, but leave | |
814 | * this check in in case this method gets exposed programmatically in future | |
815 | */ | |
816 | 0 | if (((viewport.getSelectionGroup() != null) |
817 | && (viewport.getSelectionGroup().getSize() < MIN_PCA_SELECTION) | |
818 | && (viewport.getSelectionGroup().getSize() > 0)) | |
819 | || (viewport.getAlignment().getHeight() < MIN_PCA_SELECTION)) | |
820 | { | |
821 | 0 | JvOptionPane.showInternalMessageDialog(this, |
822 | MessageManager.formatMessage( | |
823 | "label.you_need_at_least_n_sequences", | |
824 | MIN_PCA_SELECTION), | |
825 | MessageManager | |
826 | .getString("label.sequence_selection_insufficient"), | |
827 | JvOptionPane.WARNING_MESSAGE); | |
828 | 0 | return; |
829 | } | |
830 | ||
831 | /* | |
832 | * construct the panel and kick off its calculation thread | |
833 | */ | |
834 | 0 | pcaPanel = new PCAPanel(af.alignPanel, modelName, params); |
835 | 0 | new Thread(pcaPanel).start(); |
836 | ||
837 | } | |
838 | ||
839 | /** | |
840 | * Open a new PaSiMap panel on the desktop | |
841 | * | |
842 | * @param modelName | |
843 | * @param params | |
844 | */ | |
845 | 0 | protected void openPasimapPanel(String modelName, |
846 | SimilarityParamsI params) | |
847 | { | |
848 | 0 | AlignViewport viewport = af.getViewport(); |
849 | ||
850 | /* | |
851 | * gui validation shouldn't allow insufficient sequences here, but leave | |
852 | * this check in in case this method gets exposed programmatically in future | |
853 | */ | |
854 | 0 | if (((viewport.getSelectionGroup() != null) |
855 | && (viewport.getSelectionGroup() | |
856 | .getSize() < MIN_PASIMAP_SELECTION) | |
857 | && (viewport.getSelectionGroup().getSize() > 0)) | |
858 | || (viewport.getAlignment() | |
859 | .getHeight() < MIN_PASIMAP_SELECTION)) | |
860 | { | |
861 | 0 | JvOptionPane.showInternalMessageDialog(this, |
862 | MessageManager.formatMessage( | |
863 | "label.you_need_at_least_n_sequences", | |
864 | MIN_PASIMAP_SELECTION), | |
865 | MessageManager | |
866 | .getString("label.sequence_selection_insufficient"), | |
867 | JvOptionPane.WARNING_MESSAGE); | |
868 | 0 | return; |
869 | } | |
870 | ||
871 | /* | |
872 | * construct the panel and kick off its calculation thread | |
873 | */ | |
874 | 0 | pasimapPanel = new PaSiMapPanel(af.alignPanel, modelName); |
875 | 0 | new Thread(pasimapPanel).start(); |
876 | ||
877 | } | |
878 | ||
879 | /** | |
880 | * | |
881 | */ | |
882 | 0 | protected void closeFrame() |
883 | { | |
884 | 0 | try |
885 | { | |
886 | 0 | frame.setClosed(true); |
887 | } catch (PropertyVetoException ex) | |
888 | { | |
889 | } | |
890 | } | |
891 | ||
892 | /** | |
893 | * Returns a data bean holding parameters for similarity (or distance) model | |
894 | * calculation | |
895 | * | |
896 | * @param doPCA | |
897 | * @return | |
898 | */ | |
899 | 0 | protected SimilarityParams getSimilarityParameters(boolean doPCA) |
900 | { | |
901 | // commented out: parameter choices read from gui widgets | |
902 | // SimilarityParamsI params = new SimilarityParams( | |
903 | // includeGappedColumns.isSelected(), matchGaps.isSelected(), | |
904 | // includeGaps.isSelected(), shorterSequence.isSelected()); | |
905 | ||
906 | 0 | boolean includeGapGap = true; |
907 | 0 | boolean includeGapResidue = true; |
908 | 0 | boolean matchOnShortestLength = false; |
909 | ||
910 | /* | |
911 | * 'matchGaps' flag is only used in the PID calculation | |
912 | * - set to false for PCA so that PCA using PID reproduces SeqSpace PCA | |
913 | * - set to true for Tree to reproduce Jalview 2.10.1 calculation | |
914 | * - set to false for Tree for a more correct calculation (JAL-374) | |
915 | */ | |
916 | 0 | boolean matchGap = doPCA ? false : treeMatchGaps; |
917 | ||
918 | 0 | return new SimilarityParams(includeGapGap, matchGap, includeGapResidue, |
919 | matchOnShortestLength); | |
920 | } | |
921 | ||
922 | /** | |
923 | * Closes dialog on Close button press | |
924 | */ | |
925 | 0 | protected void close_actionPerformed() |
926 | { | |
927 | 0 | try |
928 | { | |
929 | 0 | frame.setClosed(true); |
930 | } catch (Exception ex) | |
931 | { | |
932 | } | |
933 | } | |
934 | ||
935 | 0 | public PCAPanel getPcaPanel() |
936 | { | |
937 | 0 | return pcaPanel; |
938 | } | |
939 | } |