JAL-1645 Version-Rel Version 2.9 Year-Rel 2015 Licensing glob
[jalview.git] / src / jalview / gui / PCAPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.9)
3  * Copyright (C) 2015 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.datamodel.Alignment;
24 import jalview.datamodel.AlignmentView;
25 import jalview.datamodel.ColumnSelection;
26 import jalview.datamodel.SeqCigar;
27 import jalview.datamodel.SequenceI;
28 import jalview.jbgui.GPCAPanel;
29 import jalview.schemes.ResidueProperties;
30 import jalview.util.MessageManager;
31 import jalview.viewmodel.AlignmentViewport;
32 import jalview.viewmodel.PCAModel;
33
34 import java.awt.BorderLayout;
35 import java.awt.Color;
36 import java.awt.Graphics;
37 import java.awt.event.ActionEvent;
38 import java.awt.event.ActionListener;
39 import java.awt.print.PageFormat;
40 import java.awt.print.Printable;
41 import java.awt.print.PrinterException;
42 import java.awt.print.PrinterJob;
43
44 import javax.swing.ButtonGroup;
45 import javax.swing.JCheckBoxMenuItem;
46 import javax.swing.JColorChooser;
47 import javax.swing.JMenuItem;
48 import javax.swing.JOptionPane;
49 import javax.swing.JRadioButtonMenuItem;
50
51 /**
52  * DOCUMENT ME!
53  * 
54  * @author $author$
55  * @version $Revision$
56  */
57 public class PCAPanel extends GPCAPanel implements Runnable,
58         IProgressIndicator
59 {
60
61   private IProgressIndicator progressBar;
62
63   RotatableCanvas rc;
64
65   AlignmentPanel ap;
66
67   AlignmentViewport av;
68
69   PCAModel pcaModel;
70
71   int top = 0;
72
73   /**
74    * Creates a new PCAPanel object.
75    * 
76    * @param av
77    *          DOCUMENT ME!
78    * @param s
79    *          DOCUMENT ME!
80    */
81   public PCAPanel(AlignmentPanel ap)
82   {
83     this.av = ap.av;
84     this.ap = ap;
85
86     progressBar = new ProgressBar(statusPanel, statusBar);
87
88     boolean sameLength = true;
89     boolean selected = av.getSelectionGroup() != null
90             && av.getSelectionGroup().getSize() > 0;
91     AlignmentView seqstrings = av.getAlignmentView(selected);
92     boolean nucleotide = av.getAlignment().isNucleotide();
93     SequenceI[] seqs;
94     if (!selected)
95     {
96       seqs = av.getAlignment().getSequencesArray();
97     }
98     else
99     {
100       seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment());
101     }
102     SeqCigar sq[] = seqstrings.getSequences();
103     int length = sq[0].getWidth();
104
105     for (int i = 0; i < seqs.length; i++)
106     {
107       if (sq[i].getWidth() != length)
108       {
109         sameLength = false;
110         break;
111       }
112     }
113
114     if (!sameLength)
115     {
116       JOptionPane.showMessageDialog(Desktop.desktop,
117               MessageManager.getString("label.pca_sequences_not_aligned"),
118               MessageManager.getString("label.sequences_not_aligned"),
119               JOptionPane.WARNING_MESSAGE);
120
121       return;
122     }
123     pcaModel = new PCAModel(seqstrings, seqs, nucleotide);
124     PaintRefresher.Register(this, av.getSequenceSetId());
125
126     rc = new RotatableCanvas(ap);
127     this.getContentPane().add(rc, BorderLayout.CENTER);
128     Thread worker = new Thread(this);
129     worker.start();
130   }
131
132   @Override
133   protected void scoreMatrix_menuSelected()
134   {
135     scoreMatrixMenu.removeAll();
136     for (final String sm : ResidueProperties.scoreMatrices.keySet())
137     {
138       if (ResidueProperties.getScoreMatrix(sm) != null)
139       {
140         // create an entry for this score matrix for use in PCA
141         JCheckBoxMenuItem jm = new JCheckBoxMenuItem();
142         jm.setText(MessageManager
143                 .getStringOrReturn("label.score_model", sm));
144         jm.setSelected(pcaModel.getScore_matrix().equals(sm));
145         if ((ResidueProperties.scoreMatrices.get(sm).isDNA() && ResidueProperties.scoreMatrices
146                 .get(sm).isProtein())
147                 || pcaModel.isNucleotide() == ResidueProperties.scoreMatrices
148                         .get(sm).isDNA())
149         {
150           final PCAPanel us = this;
151           jm.addActionListener(new ActionListener()
152           {
153             @Override
154             public void actionPerformed(ActionEvent e)
155             {
156               if (!pcaModel.getScore_matrix().equals(sm))
157               {
158                 pcaModel.setScore_matrix(sm);
159                 Thread worker = new Thread(us);
160                 worker.start();
161               }
162             }
163           });
164           scoreMatrixMenu.add(jm);
165         }
166       }
167     }
168   }
169
170   public void bgcolour_actionPerformed(ActionEvent e)
171   {
172     Color col = JColorChooser.showDialog(this,
173             MessageManager.getString("label.select_backgroud_colour"),
174             rc.bgColour);
175
176     if (col != null)
177     {
178       rc.bgColour = col;
179     }
180     rc.repaint();
181   }
182
183   /**
184    * DOCUMENT ME!
185    */
186   public void run()
187   {
188     long progId = System.currentTimeMillis();
189     IProgressIndicator progress = this;
190     String message = MessageManager.getString("label.pca_recalculating");
191     if (getParent() == null)
192     {
193       progress = ap.alignFrame;
194       message = MessageManager.getString("label.pca_calculating");
195     }
196     progress.setProgressBar(message, progId);
197     try
198     {
199       calcSettings.setEnabled(false);
200       pcaModel.run();
201       // ////////////////
202       xCombobox.setSelectedIndex(0);
203       yCombobox.setSelectedIndex(1);
204       zCombobox.setSelectedIndex(2);
205
206       pcaModel.updateRc(rc);
207       // rc.invalidate();
208       nuclSetting.setSelected(pcaModel.isNucleotide());
209       protSetting.setSelected(!pcaModel.isNucleotide());
210       jvVersionSetting.setSelected(pcaModel.isJvCalcMode());
211       top = pcaModel.getTop();
212
213     } catch (OutOfMemoryError er)
214     {
215       new OOMWarning("calculating PCA", er);
216       return;
217     } finally
218     {
219       progress.setProgressBar("", progId);
220     }
221     calcSettings.setEnabled(true);
222     repaint();
223     if (getParent() == null)
224     {
225       addKeyListener(rc);
226       Desktop.addInternalFrame(this, MessageManager
227               .getString("label.principal_component_analysis"), 475, 450);
228     }
229   }
230
231   @Override
232   protected void nuclSetting_actionPerfomed(ActionEvent arg0)
233   {
234     if (!pcaModel.isNucleotide())
235     {
236       pcaModel.setNucleotide(true);
237       pcaModel.setScore_matrix("DNA");
238       Thread worker = new Thread(this);
239       worker.start();
240     }
241
242   }
243
244   @Override
245   protected void protSetting_actionPerfomed(ActionEvent arg0)
246   {
247
248     if (pcaModel.isNucleotide())
249     {
250       pcaModel.setNucleotide(false);
251       pcaModel.setScore_matrix("BLOSUM62");
252       Thread worker = new Thread(this);
253       worker.start();
254     }
255   }
256
257   @Override
258   protected void jvVersionSetting_actionPerfomed(ActionEvent arg0)
259   {
260     pcaModel.setJvCalcMode(jvVersionSetting.isSelected());
261     Thread worker = new Thread(this);
262     worker.start();
263   }
264
265   /**
266    * DOCUMENT ME!
267    */
268   void doDimensionChange()
269   {
270     if (top == 0)
271     {
272       return;
273     }
274
275     int dim1 = top - xCombobox.getSelectedIndex();
276     int dim2 = top - yCombobox.getSelectedIndex();
277     int dim3 = top - zCombobox.getSelectedIndex();
278     pcaModel.updateRcView(dim1, dim2, dim3);
279     rc.img = null;
280     rc.rotmat.setIdentity();
281     rc.initAxes();
282     rc.paint(rc.getGraphics());
283   }
284
285   /**
286    * DOCUMENT ME!
287    * 
288    * @param e
289    *          DOCUMENT ME!
290    */
291   protected void xCombobox_actionPerformed(ActionEvent e)
292   {
293     doDimensionChange();
294   }
295
296   /**
297    * DOCUMENT ME!
298    * 
299    * @param e
300    *          DOCUMENT ME!
301    */
302   protected void yCombobox_actionPerformed(ActionEvent e)
303   {
304     doDimensionChange();
305   }
306
307   /**
308    * DOCUMENT ME!
309    * 
310    * @param e
311    *          DOCUMENT ME!
312    */
313   protected void zCombobox_actionPerformed(ActionEvent e)
314   {
315     doDimensionChange();
316   }
317
318   public void outputValues_actionPerformed(ActionEvent e)
319   {
320     CutAndPasteTransfer cap = new CutAndPasteTransfer();
321     try
322     {
323       cap.setText(pcaModel.getDetails());
324       Desktop.addInternalFrame(cap,
325               MessageManager.getString("label.pca_details"), 500, 500);
326     } catch (OutOfMemoryError oom)
327     {
328       new OOMWarning("opening PCA details", oom);
329       cap.dispose();
330     }
331   }
332
333   public void showLabels_actionPerformed(ActionEvent e)
334   {
335     rc.showLabels(showLabels.getState());
336   }
337
338   public void print_actionPerformed(ActionEvent e)
339   {
340     PCAPrinter printer = new PCAPrinter();
341     printer.start();
342   }
343
344   public void originalSeqData_actionPerformed(ActionEvent e)
345   {
346     // this was cut'n'pasted from the equivalent TreePanel method - we should
347     // make this an abstract function of all jalview analysis windows
348     if (pcaModel.getSeqtrings() == null)
349     {
350       jalview.bin.Cache.log
351               .info("Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
352       return;
353     }
354     // decide if av alignment is sufficiently different to original data to
355     // warrant a new window to be created
356     // create new alignmnt window with hidden regions (unhiding hidden regions
357     // yields unaligned seqs)
358     // or create a selection box around columns in alignment view
359     // test Alignment(SeqCigar[])
360     char gc = '-';
361     try
362     {
363       // we try to get the associated view's gap character
364       // but this may fail if the view was closed...
365       gc = av.getGapCharacter();
366     } catch (Exception ex)
367     {
368     }
369     ;
370     Object[] alAndColsel = pcaModel.getSeqtrings()
371             .getAlignmentAndColumnSelection(gc);
372
373     if (alAndColsel != null && alAndColsel[0] != null)
374     {
375       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
376
377       Alignment al = new Alignment((SequenceI[]) alAndColsel[0]);
378       Alignment dataset = (av != null && av.getAlignment() != null) ? av
379               .getAlignment().getDataset() : null;
380       if (dataset != null)
381       {
382         al.setDataset(dataset);
383       }
384
385       if (true)
386       {
387         // make a new frame!
388         AlignFrame af = new AlignFrame(al,
389                 (ColumnSelection) alAndColsel[1], AlignFrame.DEFAULT_WIDTH,
390                 AlignFrame.DEFAULT_HEIGHT);
391
392         // >>>This is a fix for the moment, until a better solution is
393         // found!!<<<
394         // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
395
396         // af.addSortByOrderMenuItem(ServiceName + " Ordering",
397         // msaorder);
398
399         Desktop.addInternalFrame(af, MessageManager.formatMessage(
400                 "label.original_data_for_params",
401                 new String[] { this.title }), AlignFrame.DEFAULT_WIDTH,
402                 AlignFrame.DEFAULT_HEIGHT);
403       }
404     }
405     /*
406      * CutAndPasteTransfer cap = new CutAndPasteTransfer(); for (int i = 0; i <
407      * seqs.length; i++) { cap.appendText(new jalview.util.Format("%-" + 15 +
408      * "s").form( seqs[i].getName())); cap.appendText(" " + seqstrings[i] +
409      * "\n"); }
410      * 
411      * Desktop.addInternalFrame(cap, "Original Data", 400, 400);
412      */
413   }
414
415   class PCAPrinter extends Thread implements Printable
416   {
417     public void run()
418     {
419       PrinterJob printJob = PrinterJob.getPrinterJob();
420       PageFormat pf = printJob.pageDialog(printJob.defaultPage());
421
422       printJob.setPrintable(this, pf);
423
424       if (printJob.printDialog())
425       {
426         try
427         {
428           printJob.print();
429         } catch (Exception PrintException)
430         {
431           PrintException.printStackTrace();
432         }
433       }
434     }
435
436     public int print(Graphics pg, PageFormat pf, int pi)
437             throws PrinterException
438     {
439       pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
440
441       rc.drawBackground(pg, rc.bgColour);
442       rc.drawScene(pg);
443       if (rc.drawAxes == true)
444       {
445         rc.drawAxes(pg);
446       }
447
448       if (pi == 0)
449       {
450         return Printable.PAGE_EXISTS;
451       }
452       else
453       {
454         return Printable.NO_SUCH_PAGE;
455       }
456     }
457   }
458
459   /**
460    * DOCUMENT ME!
461    * 
462    * @param e
463    *          DOCUMENT ME!
464    */
465   public void eps_actionPerformed(ActionEvent e)
466   {
467     makePCAImage(jalview.util.ImageMaker.TYPE.EPS);
468   }
469
470   /**
471    * DOCUMENT ME!
472    * 
473    * @param e
474    *          DOCUMENT ME!
475    */
476   public void png_actionPerformed(ActionEvent e)
477   {
478     makePCAImage(jalview.util.ImageMaker.TYPE.PNG);
479   }
480
481   void makePCAImage(jalview.util.ImageMaker.TYPE type)
482   {
483     int width = rc.getWidth();
484     int height = rc.getHeight();
485
486     jalview.util.ImageMaker im;
487
488     if (type == jalview.util.ImageMaker.TYPE.PNG)
489     {
490       im = new jalview.util.ImageMaker(this,
491               jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from PCA",
492               width, height, null, null);
493     }
494     else if (type == jalview.util.ImageMaker.TYPE.EPS)
495     {
496       im = new jalview.util.ImageMaker(this,
497               jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from PCA",
498               width, height, null, this.getTitle());
499     }
500     else
501     {
502       im = new jalview.util.ImageMaker(this,
503               jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
504               width, height, null, this.getTitle());
505
506     }
507
508     if (im.getGraphics() != null)
509     {
510       rc.drawBackground(im.getGraphics(), Color.black);
511       rc.drawScene(im.getGraphics());
512       if (rc.drawAxes == true)
513       {
514         rc.drawAxes(im.getGraphics());
515       }
516       im.writeImage();
517     }
518   }
519
520   public void viewMenu_menuSelected()
521   {
522     buildAssociatedViewMenu();
523   }
524
525   void buildAssociatedViewMenu()
526   {
527     AlignmentPanel[] aps = PaintRefresher.getAssociatedPanels(av
528             .getSequenceSetId());
529     if (aps.length == 1 && rc.av == aps[0].av)
530     {
531       associateViewsMenu.setVisible(false);
532       return;
533     }
534
535     associateViewsMenu.setVisible(true);
536
537     if ((viewMenu.getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
538     {
539       viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
540     }
541
542     associateViewsMenu.removeAll();
543
544     JRadioButtonMenuItem item;
545     ButtonGroup buttonGroup = new ButtonGroup();
546     int i, iSize = aps.length;
547     final PCAPanel thisPCAPanel = this;
548     for (i = 0; i < iSize; i++)
549     {
550       final AlignmentPanel ap = aps[i];
551       item = new JRadioButtonMenuItem(ap.av.viewName, ap.av == rc.av);
552       buttonGroup.add(item);
553       item.addActionListener(new ActionListener()
554       {
555         public void actionPerformed(ActionEvent evt)
556         {
557           rc.applyToAllViews = false;
558           rc.av = ap.av;
559           rc.ap = ap;
560           PaintRefresher.Register(thisPCAPanel, ap.av.getSequenceSetId());
561         }
562       });
563
564       associateViewsMenu.add(item);
565     }
566
567     final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem("All Views");
568
569     buttonGroup.add(itemf);
570
571     itemf.setSelected(rc.applyToAllViews);
572     itemf.addActionListener(new ActionListener()
573     {
574       public void actionPerformed(ActionEvent evt)
575       {
576         rc.applyToAllViews = itemf.isSelected();
577       }
578     });
579     associateViewsMenu.add(itemf);
580
581   }
582
583   /*
584    * (non-Javadoc)
585    * 
586    * @see
587    * jalview.jbgui.GPCAPanel#outputPoints_actionPerformed(java.awt.event.ActionEvent
588    * )
589    */
590   protected void outputPoints_actionPerformed(ActionEvent e)
591   {
592     CutAndPasteTransfer cap = new CutAndPasteTransfer();
593     try
594     {
595       cap.setText(pcaModel.getPointsasCsv(false,
596               xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
597               zCombobox.getSelectedIndex()));
598       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
599               "label.points_for_params", new String[] { this.getTitle() }),
600               500, 500);
601     } catch (OutOfMemoryError oom)
602     {
603       new OOMWarning("exporting PCA points", oom);
604       cap.dispose();
605     }
606   }
607
608   /*
609    * (non-Javadoc)
610    * 
611    * @see
612    * jalview.jbgui.GPCAPanel#outputProjPoints_actionPerformed(java.awt.event
613    * .ActionEvent)
614    */
615   protected void outputProjPoints_actionPerformed(ActionEvent e)
616   {
617     CutAndPasteTransfer cap = new CutAndPasteTransfer();
618     try
619     {
620       cap.setText(pcaModel.getPointsasCsv(true,
621               xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
622               zCombobox.getSelectedIndex()));
623       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
624               "label.transformed_points_for_params",
625               new String[] { this.getTitle() }), 500, 500);
626     } catch (OutOfMemoryError oom)
627     {
628       new OOMWarning("exporting transformed PCA points", oom);
629       cap.dispose();
630     }
631   }
632
633   /*
634    * (non-Javadoc)
635    * 
636    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
637    */
638   @Override
639   public void setProgressBar(String message, long id)
640   {
641     progressBar.setProgressBar(message, id);
642     // if (progressBars == null)
643     // {
644     // progressBars = new Hashtable();
645     // progressBarHandlers = new Hashtable();
646     // }
647     //
648     // JPanel progressPanel;
649     // Long lId = new Long(id);
650     // GridLayout layout = (GridLayout) statusPanel.getLayout();
651     // if (progressBars.get(lId) != null)
652     // {
653     // progressPanel = (JPanel) progressBars.get(new Long(id));
654     // statusPanel.remove(progressPanel);
655     // progressBars.remove(lId);
656     // progressPanel = null;
657     // if (message != null)
658     // {
659     // statusBar.setText(message);
660     // }
661     // if (progressBarHandlers.contains(lId))
662     // {
663     // progressBarHandlers.remove(lId);
664     // }
665     // layout.setRows(layout.getRows() - 1);
666     // }
667     // else
668     // {
669     // progressPanel = new JPanel(new BorderLayout(10, 5));
670     //
671     // JProgressBar progressBar = new JProgressBar();
672     // progressBar.setIndeterminate(true);
673     //
674     // progressPanel.add(new JLabel(message), BorderLayout.WEST);
675     // progressPanel.add(progressBar, BorderLayout.CENTER);
676     //
677     // layout.setRows(layout.getRows() + 1);
678     // statusPanel.add(progressPanel);
679     //
680     // progressBars.put(lId, progressPanel);
681     // }
682     // // update GUI
683     // // setMenusForViewport();
684     // validate();
685   }
686
687   @Override
688   public void registerHandler(final long id,
689           final IProgressIndicatorHandler handler)
690   {
691     progressBar.registerHandler(id, handler);
692     // if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
693     // {
694     // throw new
695     // Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
696     // }
697     // progressBarHandlers.put(new Long(id), handler);
698     // final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
699     // if (handler.canCancel())
700     // {
701     // JButton cancel = new JButton(
702     // MessageManager.getString("action.cancel"));
703     // final IProgressIndicator us = this;
704     // cancel.addActionListener(new ActionListener()
705     // {
706     //
707     // @Override
708     // public void actionPerformed(ActionEvent e)
709     // {
710     // handler.cancelActivity(id);
711     // us.setProgressBar(MessageManager.formatMessage("label.cancelled_params",
712     // new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
713     // }
714     // });
715     // progressPanel.add(cancel, BorderLayout.EAST);
716     // }
717   }
718
719   /**
720    * 
721    * @return true if any progress bars are still active
722    */
723   @Override
724   public boolean operationInProgress()
725   {
726     return progressBar.operationInProgress();
727   }
728
729   @Override
730   protected void resetButton_actionPerformed(ActionEvent e)
731   {
732     int t = top;
733     top = 0; // ugly - prevents dimensionChanged events from being processed
734     xCombobox.setSelectedIndex(0);
735     yCombobox.setSelectedIndex(1);
736     top = t;
737     zCombobox.setSelectedIndex(2);
738   }
739 }