1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (2.11.1.4)
3  * Copyright (C) 2021 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.scoremodels.ScoreModels;
24 import jalview.api.AlignViewportI;
25 import jalview.api.analysis.ScoreModelI;
26 import jalview.api.analysis.SimilarityParamsI;
27 import jalview.bin.Cache;
28 import jalview.datamodel.Alignment;
29 import jalview.datamodel.AlignmentI;
30 import jalview.datamodel.AlignmentView;
31 import jalview.datamodel.HiddenColumns;
32 import jalview.datamodel.SequenceI;
33 import jalview.jbgui.GPCAPanel;
34 import jalview.math.RotatableMatrix.Axis;
35 import jalview.util.ImageMaker;
36 import jalview.util.MessageManager;
37 import jalview.viewmodel.AlignmentViewport;
38 import jalview.viewmodel.PCAModel;
39 
40 import java.awt.BorderLayout;
41 import java.awt.Color;
42 import java.awt.Dimension;
43 import java.awt.Graphics;
44 import java.awt.event.ActionEvent;
45 import java.awt.event.ActionListener;
46 import java.awt.print.PageFormat;
47 import java.awt.print.Printable;
48 import java.awt.print.PrinterException;
49 import java.awt.print.PrinterJob;
50 
51 import javax.swing.ButtonGroup;
52 import javax.swing.JColorChooser;
53 import javax.swing.JMenuItem;
54 import javax.swing.JRadioButtonMenuItem;
55 import javax.swing.event.InternalFrameAdapter;
56 import javax.swing.event.InternalFrameEvent;
57 
58 /**
59  * The panel holding the Principal Component Analysis 3-D visualisation
60  */
61 public class PCAPanel extends GPCAPanel
62         implements Runnable, IProgressIndicator
63 {
64   private static final int MIN_WIDTH = 470;
65 
66   private static final int MIN_HEIGHT = 250;
67 
68   private RotatableCanvas rc;
69 
70   AlignmentPanel ap;
71 
72   AlignmentViewport av;
73 
74   private PCAModel pcaModel;
75 
76   private int top = 0;
77 
78   private IProgressIndicator progressBar;
79 
80   private boolean working;
81 
82   /**
83    * Constructor given sequence data, a similarity (or distance) score model
84    * name, and score calculation parameters
85    *
86    * @param alignPanel
87    * @param modelName
88    * @param params
89    */
PCAPanel(AlignmentPanel alignPanel, String modelName, SimilarityParamsI params)90   public PCAPanel(AlignmentPanel alignPanel, String modelName,
91           SimilarityParamsI params)
92   {
93     super();
94     this.av = alignPanel.av;
95     this.ap = alignPanel;
96     boolean nucleotide = av.getAlignment().isNucleotide();
97 
98     progressBar = new ProgressBar(statusPanel, statusBar);
99 
100     addInternalFrameListener(new InternalFrameAdapter()
101     {
102       @Override
103       public void internalFrameClosed(InternalFrameEvent e)
104       {
105         close_actionPerformed();
106       }
107     });
108 
109     boolean selected = av.getSelectionGroup() != null
110             && av.getSelectionGroup().getSize() > 0;
111     AlignmentView seqstrings = av.getAlignmentView(selected);
112     SequenceI[] seqs;
113     if (!selected)
114     {
115       seqs = av.getAlignment().getSequencesArray();
116     }
117     else
118     {
119       seqs = av.getSelectionGroup().getSequencesInOrder(av.getAlignment());
120     }
121 
122     ScoreModelI scoreModel = ScoreModels.getInstance()
123             .getScoreModel(modelName, ap);
124     setPcaModel(new PCAModel(seqstrings, seqs, nucleotide, scoreModel,
125             params));
126     PaintRefresher.Register(this, av.getSequenceSetId());
127 
128     setRotatableCanvas(new RotatableCanvas(alignPanel));
129     this.getContentPane().add(getRotatableCanvas(), BorderLayout.CENTER);
130 
131     addKeyListener(getRotatableCanvas());
132     validate();
133 
134     this.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
135   }
136 
137   /**
138    * Ensure references to potentially very large objects (the PCA matrices) are
139    * nulled when the frame is closed
140    */
close_actionPerformed()141   protected void close_actionPerformed()
142   {
143     setPcaModel(null);
144     if (this.rc != null)
145     {
146       this.rc.sequencePoints = null;
147       this.rc.setAxisEndPoints(null);
148       this.rc = null;
149     }
150   }
151 
152   @Override
bgcolour_actionPerformed()153   protected void bgcolour_actionPerformed()
154   {
155     Color col = JColorChooser.showDialog(this,
156             MessageManager.getString("label.select_background_colour"),
157             getRotatableCanvas().getBgColour());
158 
159     if (col != null)
160     {
161       getRotatableCanvas().setBgColour(col);
162     }
163     getRotatableCanvas().repaint();
164   }
165 
166   /**
167    * Calculates the PCA and displays the results
168    */
169   @Override
run()170   public void run()
171   {
172     working = true;
173     long progId = System.currentTimeMillis();
174     IProgressIndicator progress = this;
175     String message = MessageManager.getString("label.pca_recalculating");
176     if (getParent() == null)
177     {
178       progress = ap.alignFrame;
179       message = MessageManager.getString("label.pca_calculating");
180     }
181     progress.setProgressBar(message, progId);
182     try
183     {
184       getPcaModel().calculate();
185 
186       xCombobox.setSelectedIndex(0);
187       yCombobox.setSelectedIndex(1);
188       zCombobox.setSelectedIndex(2);
189 
190       getPcaModel().updateRc(getRotatableCanvas());
191       // rc.invalidate();
192       setTop(getPcaModel().getTop());
193 
194     } catch (OutOfMemoryError er)
195     {
196       new OOMWarning("calculating PCA", er);
197       working = false;
198       return;
199     } finally
200     {
201       progress.setProgressBar("", progId);
202     }
203 
204     repaint();
205     if (getParent() == null)
206     {
207       Desktop.addInternalFrame(this,
208               MessageManager.formatMessage("label.calc_title", "PCA",
209                       getPcaModel().getScoreModelName()),
210               475, 450);
211     }
212     working = false;
213   }
214 
215   /**
216    * Updates the PCA display after a change of component to use for x, y or z
217    * axis
218    */
219   @Override
doDimensionChange()220   protected void doDimensionChange()
221   {
222     if (getTop() == 0)
223     {
224       return;
225     }
226 
227     int dim1 = getTop() - xCombobox.getSelectedIndex();
228     int dim2 = getTop() - yCombobox.getSelectedIndex();
229     int dim3 = getTop() - zCombobox.getSelectedIndex();
230     getPcaModel().updateRcView(dim1, dim2, dim3);
231     getRotatableCanvas().resetView();
232   }
233 
234   /**
235    * Sets the selected checkbox item index for PCA dimension (1, 2, 3...) for
236    * the given axis (X/Y/Z)
237    *
238    * @param index
239    * @param axis
240    */
setSelectedDimensionIndex(int index, Axis axis)241   public void setSelectedDimensionIndex(int index, Axis axis)
242   {
243     switch (axis)
244     {
245     case X:
246       xCombobox.setSelectedIndex(index);
247       break;
248     case Y:
249       yCombobox.setSelectedIndex(index);
250       break;
251     case Z:
252       zCombobox.setSelectedIndex(index);
253       break;
254     default:
255     }
256   }
257 
258   @Override
outputValues_actionPerformed()259   protected void outputValues_actionPerformed()
260   {
261     CutAndPasteTransfer cap = new CutAndPasteTransfer();
262     try
263     {
264       cap.setText(getPcaModel().getDetails());
265       Desktop.addInternalFrame(cap,
266               MessageManager.getString("label.pca_details"), 500, 500);
267     } catch (OutOfMemoryError oom)
268     {
269       new OOMWarning("opening PCA details", oom);
270       cap.dispose();
271     }
272   }
273 
274   @Override
showLabels_actionPerformed()275   protected void showLabels_actionPerformed()
276   {
277     getRotatableCanvas().showLabels(showLabels.getState());
278   }
279 
280   @Override
print_actionPerformed()281   protected void print_actionPerformed()
282   {
283     PCAPrinter printer = new PCAPrinter();
284     printer.start();
285   }
286 
287   /**
288    * If available, shows the data which formed the inputs for the PCA as a new
289    * alignment
290    */
291   @Override
originalSeqData_actionPerformed()292   public void originalSeqData_actionPerformed()
293   {
294     // JAL-2647 disabled after load from project (until save to project done)
295     if (getPcaModel().getInputData() == null)
296     {
297       Cache.log.info(
298               "Unexpected call to originalSeqData_actionPerformed - should have hidden this menu action.");
299       return;
300     }
301     // decide if av alignment is sufficiently different to original data to
302     // warrant a new window to be created
303     // create new alignment window with hidden regions (unhiding hidden regions
304     // yields unaligned seqs)
305     // or create a selection box around columns in alignment view
306     // test Alignment(SeqCigar[])
307     char gc = '-';
308     try
309     {
310       // we try to get the associated view's gap character
311       // but this may fail if the view was closed...
312       gc = av.getGapCharacter();
313     } catch (Exception ex)
314     {
315     }
316 
317     Object[] alAndColsel = getPcaModel().getInputData()
318             .getAlignmentAndHiddenColumns(gc);
319 
320     if (alAndColsel != null && alAndColsel[0] != null)
321     {
322       // AlignmentOrder origorder = new AlignmentOrder(alAndColsel[0]);
323 
324       AlignmentI al = new Alignment((SequenceI[]) alAndColsel[0]);
325       AlignmentI dataset = (av != null && av.getAlignment() != null)
326               ? av.getAlignment().getDataset()
327               : null;
328       if (dataset != null)
329       {
330         al.setDataset(dataset);
331       }
332 
333       if (true)
334       {
335         // make a new frame!
336         AlignFrame af = new AlignFrame(al, (HiddenColumns) alAndColsel[1],
337                 AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
338 
339         // >>>This is a fix for the moment, until a better solution is
340         // found!!<<<
341         // af.getFeatureRenderer().transferSettings(alignFrame.getFeatureRenderer());
342 
343         // af.addSortByOrderMenuItem(ServiceName + " Ordering",
344         // msaorder);
345 
346         Desktop.addInternalFrame(af, MessageManager.formatMessage(
347                 "label.original_data_for_params", new String[]
348                 { this.title }), AlignFrame.DEFAULT_WIDTH,
349                 AlignFrame.DEFAULT_HEIGHT);
350       }
351     }
352     /*
353      * CutAndPasteTransfer cap = new CutAndPasteTransfer(); for (int i = 0; i <
354      * seqs.length; i++) { cap.appendText(new jalview.util.Format("%-" + 15 +
355      * "s").form( seqs[i].getName())); cap.appendText(" " + seqstrings[i] +
356      * "\n"); }
357      *
358      * Desktop.addInternalFrame(cap, "Original Data", 400, 400);
359      */
360   }
361 
362   class PCAPrinter extends Thread implements Printable
363   {
364     @Override
run()365     public void run()
366     {
367       PrinterJob printJob = PrinterJob.getPrinterJob();
368       PageFormat defaultPage = printJob.defaultPage();
369       PageFormat pf = printJob.pageDialog(defaultPage);
370 
371       if (defaultPage == pf)
372       {
373         /*
374          * user cancelled
375          */
376         return;
377       }
378 
379       printJob.setPrintable(this, pf);
380 
381       if (printJob.printDialog())
382       {
383         try
384         {
385           printJob.print();
386         } catch (Exception PrintException)
387         {
388           PrintException.printStackTrace();
389         }
390       }
391     }
392 
393     @Override
print(Graphics pg, PageFormat pf, int pi)394     public int print(Graphics pg, PageFormat pf, int pi)
395             throws PrinterException
396     {
397       pg.translate((int) pf.getImageableX(), (int) pf.getImageableY());
398 
399       getRotatableCanvas().drawBackground(pg);
400       getRotatableCanvas().drawScene(pg);
401       if (getRotatableCanvas().drawAxes)
402       {
403         getRotatableCanvas().drawAxes(pg);
404       }
405 
406       if (pi == 0)
407       {
408         return Printable.PAGE_EXISTS;
409       }
410       else
411       {
412         return Printable.NO_SUCH_PAGE;
413       }
414     }
415   }
416 
417   /**
418    * Handler for 'Save as EPS' option
419    */
420   @Override
eps_actionPerformed()421   protected void eps_actionPerformed()
422   {
423     makePCAImage(ImageMaker.TYPE.EPS);
424   }
425 
426   /**
427    * Handler for 'Save as PNG' option
428    */
429   @Override
png_actionPerformed()430   protected void png_actionPerformed()
431   {
432     makePCAImage(ImageMaker.TYPE.PNG);
433   }
434 
makePCAImage(ImageMaker.TYPE type)435   void makePCAImage(ImageMaker.TYPE type)
436   {
437     int width = getRotatableCanvas().getWidth();
438     int height = getRotatableCanvas().getHeight();
439 
440     ImageMaker im;
441 
442     switch (type)
443     {
444     case PNG:
445       im = new ImageMaker(this, ImageMaker.TYPE.PNG,
446               "Make PNG image from PCA", width, height, null, null, null, 0,
447               false);
448       break;
449     case EPS:
450       im = new ImageMaker(this, ImageMaker.TYPE.EPS,
451               "Make EPS file from PCA", width, height, null,
452               this.getTitle(), null, 0, false);
453       break;
454     default:
455       im = new ImageMaker(this, ImageMaker.TYPE.SVG,
456               "Make SVG file from PCA", width, height, null,
457               this.getTitle(), null, 0, false);
458     }
459 
460     if (im.getGraphics() != null)
461     {
462       getRotatableCanvas().drawBackground(im.getGraphics());
463       getRotatableCanvas().drawScene(im.getGraphics());
464       if (getRotatableCanvas().drawAxes)
465       {
466         getRotatableCanvas().drawAxes(im.getGraphics());
467       }
468       im.writeImage();
469     }
470   }
471 
472   @Override
viewMenu_menuSelected()473   protected void viewMenu_menuSelected()
474   {
475     buildAssociatedViewMenu();
476   }
477 
478   /**
479    * Builds the menu showing the choice of possible views (for the associated
480    * sequence data) to which the PCA may be linked
481    */
buildAssociatedViewMenu()482   void buildAssociatedViewMenu()
483   {
484     AlignmentPanel[] aps = PaintRefresher
485             .getAssociatedPanels(av.getSequenceSetId());
486     if (aps.length == 1 && getRotatableCanvas().av == aps[0].av)
487     {
488       associateViewsMenu.setVisible(false);
489       return;
490     }
491 
492     associateViewsMenu.setVisible(true);
493 
494     if ((viewMenu
495             .getItem(viewMenu.getItemCount() - 2) instanceof JMenuItem))
496     {
497       viewMenu.insertSeparator(viewMenu.getItemCount() - 1);
498     }
499 
500     associateViewsMenu.removeAll();
501 
502     JRadioButtonMenuItem item;
503     ButtonGroup buttonGroup = new ButtonGroup();
504     int iSize = aps.length;
505 
506     for (int i = 0; i < iSize; i++)
507     {
508       final AlignmentPanel panel = aps[i];
509       item = new JRadioButtonMenuItem(panel.av.getViewName(),
510               panel.av == getRotatableCanvas().av);
511       buttonGroup.add(item);
512       item.addActionListener(new ActionListener()
513       {
514         @Override
515         public void actionPerformed(ActionEvent evt)
516         {
517           selectAssociatedView(panel);
518         }
519       });
520 
521       associateViewsMenu.add(item);
522     }
523 
524     final JRadioButtonMenuItem itemf = new JRadioButtonMenuItem(
525             "All Views");
526 
527     buttonGroup.add(itemf);
528 
529     itemf.setSelected(getRotatableCanvas().isApplyToAllViews());
530     itemf.addActionListener(new ActionListener()
531     {
532       @Override
533       public void actionPerformed(ActionEvent evt)
534       {
535         getRotatableCanvas().setApplyToAllViews(itemf.isSelected());
536       }
537     });
538     associateViewsMenu.add(itemf);
539 
540   }
541 
542   /*
543    * (non-Javadoc)
544    *
545    * @see
546    * jalview.jbgui.GPCAPanel#outputPoints_actionPerformed(java.awt.event.ActionEvent
547    * )
548    */
549   @Override
outputPoints_actionPerformed()550   protected void outputPoints_actionPerformed()
551   {
552     CutAndPasteTransfer cap = new CutAndPasteTransfer();
553     try
554     {
555       cap.setText(getPcaModel().getPointsasCsv(false,
556               xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
557               zCombobox.getSelectedIndex()));
558       Desktop.addInternalFrame(cap, MessageManager
559               .formatMessage("label.points_for_params", new String[]
560               { this.getTitle() }), 500, 500);
561     } catch (OutOfMemoryError oom)
562     {
563       new OOMWarning("exporting PCA points", oom);
564       cap.dispose();
565     }
566   }
567 
568   /*
569    * (non-Javadoc)
570    *
571    * @see
572    * jalview.jbgui.GPCAPanel#outputProjPoints_actionPerformed(java.awt.event
573    * .ActionEvent)
574    */
575   @Override
outputProjPoints_actionPerformed()576   protected void outputProjPoints_actionPerformed()
577   {
578     CutAndPasteTransfer cap = new CutAndPasteTransfer();
579     try
580     {
581       cap.setText(getPcaModel().getPointsasCsv(true,
582               xCombobox.getSelectedIndex(), yCombobox.getSelectedIndex(),
583               zCombobox.getSelectedIndex()));
584       Desktop.addInternalFrame(cap, MessageManager.formatMessage(
585               "label.transformed_points_for_params", new String[]
586               { this.getTitle() }), 500, 500);
587     } catch (OutOfMemoryError oom)
588     {
589       new OOMWarning("exporting transformed PCA points", oom);
590       cap.dispose();
591     }
592   }
593 
594   /*
595    * (non-Javadoc)
596    *
597    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
598    */
599   @Override
setProgressBar(String message, long id)600   public void setProgressBar(String message, long id)
601   {
602     progressBar.setProgressBar(message, id);
603     // if (progressBars == null)
604     // {
605     // progressBars = new Hashtable();
606     // progressBarHandlers = new Hashtable();
607     // }
608     //
609     // JPanel progressPanel;
610     // Long lId = Long.valueOf(id);
611     // GridLayout layout = (GridLayout) statusPanel.getLayout();
612     // if (progressBars.get(lId) != null)
613     // {
614     // progressPanel = (JPanel) progressBars.get(Long.valueOf(id));
615     // statusPanel.remove(progressPanel);
616     // progressBars.remove(lId);
617     // progressPanel = null;
618     // if (message != null)
619     // {
620     // statusBar.setText(message);
621     // }
622     // if (progressBarHandlers.contains(lId))
623     // {
624     // progressBarHandlers.remove(lId);
625     // }
626     // layout.setRows(layout.getRows() - 1);
627     // }
628     // else
629     // {
630     // progressPanel = new JPanel(new BorderLayout(10, 5));
631     //
632     // JProgressBar progressBar = new JProgressBar();
633     // progressBar.setIndeterminate(true);
634     //
635     // progressPanel.add(new JLabel(message), BorderLayout.WEST);
636     // progressPanel.add(progressBar, BorderLayout.CENTER);
637     //
638     // layout.setRows(layout.getRows() + 1);
639     // statusPanel.add(progressPanel);
640     //
641     // progressBars.put(lId, progressPanel);
642     // }
643     // // update GUI
644     // // setMenusForViewport();
645     // validate();
646   }
647 
648   @Override
registerHandler(final long id, final IProgressIndicatorHandler handler)649   public void registerHandler(final long id,
650           final IProgressIndicatorHandler handler)
651   {
652     progressBar.registerHandler(id, handler);
653     // if (progressBarHandlers == null || !progressBars.contains(Long.valueOf(id)))
654     // {
655     // throw new
656     // Error(MessageManager.getString("error.call_setprogressbar_before_registering_handler"));
657     // }
658     // progressBarHandlers.put(Long.valueOf(id), handler);
659     // final JPanel progressPanel = (JPanel) progressBars.get(Long.valueOf(id));
660     // if (handler.canCancel())
661     // {
662     // JButton cancel = new JButton(
663     // MessageManager.getString("action.cancel"));
664     // final IProgressIndicator us = this;
665     // cancel.addActionListener(new ActionListener()
666     // {
667     //
668     // @Override
669     // public void actionPerformed(ActionEvent e)
670     // {
671     // handler.cancelActivity(id);
672     // us.setProgressBar(MessageManager.formatMessage("label.cancelled_params",
673     // new String[]{((JLabel) progressPanel.getComponent(0)).getText()}), id);
674     // }
675     // });
676     // progressPanel.add(cancel, BorderLayout.EAST);
677     // }
678   }
679 
680   /**
681    *
682    * @return true if any progress bars are still active
683    */
684   @Override
operationInProgress()685   public boolean operationInProgress()
686   {
687     return progressBar.operationInProgress();
688   }
689 
690   @Override
resetButton_actionPerformed()691   protected void resetButton_actionPerformed()
692   {
693     int t = getTop();
694     setTop(0); // ugly - prevents dimensionChanged events from being processed
695     xCombobox.setSelectedIndex(0);
696     yCombobox.setSelectedIndex(1);
697     setTop(t);
698     zCombobox.setSelectedIndex(2);
699   }
700 
701   /**
702    * Answers true if PCA calculation is in progress, else false
703    *
704    * @return
705    */
isWorking()706   public boolean isWorking()
707   {
708     return working;
709   }
710 
711   /**
712    * Answers the selected checkbox item index for PCA dimension for the X, Y or
713    * Z axis of the display
714    *
715    * @param axis
716    * @return
717    */
getSelectedDimensionIndex(Axis axis)718   public int getSelectedDimensionIndex(Axis axis)
719   {
720     switch (axis)
721     {
722     case X:
723       return xCombobox.getSelectedIndex();
724     case Y:
725       return yCombobox.getSelectedIndex();
726     default:
727       return zCombobox.getSelectedIndex();
728     }
729   }
730 
setShowLabels(boolean show)731   public void setShowLabels(boolean show)
732   {
733     showLabels.setSelected(show);
734   }
735 
736   /**
737    * Sets the input data used to calculate the PCA. This is provided for
738    * 'restore from project', which does not currently support this (AL-2647), so
739    * sets the value to null, and hides the menu option for "Input Data...". J
740    *
741    * @param data
742    */
setInputData(AlignmentView data)743   public void setInputData(AlignmentView data)
744   {
745     getPcaModel().setInputData(data);
746     originalSeqData.setVisible(data != null);
747   }
748 
getAlignViewport()749   public AlignViewportI getAlignViewport()
750   {
751     return av;
752   }
753 
getPcaModel()754   public PCAModel getPcaModel()
755   {
756     return pcaModel;
757   }
758 
setPcaModel(PCAModel pcaModel)759   public void setPcaModel(PCAModel pcaModel)
760   {
761     this.pcaModel = pcaModel;
762   }
763 
getRotatableCanvas()764   public RotatableCanvas getRotatableCanvas()
765   {
766     return rc;
767   }
768 
setRotatableCanvas(RotatableCanvas rc)769   public void setRotatableCanvas(RotatableCanvas rc)
770   {
771     this.rc = rc;
772   }
773 
getTop()774   public int getTop()
775   {
776     return top;
777   }
778 
setTop(int top)779   public void setTop(int top)
780   {
781     this.top = top;
782   }
783 
784   /**
785    * set the associated view for this PCA.
786    *
787    * @param panel
788    */
selectAssociatedView(AlignmentPanel panel)789   public void selectAssociatedView(AlignmentPanel panel)
790   {
791     getRotatableCanvas().setApplyToAllViews(false);
792 
793     ap = panel;
794     av = panel.av;
795 
796     getRotatableCanvas().av = panel.av;
797     getRotatableCanvas().ap = panel;
798     PaintRefresher.Register(PCAPanel.this, panel.av.getSequenceSetId());
799   }
800 }
801