29c388c74df3fd3cb43835fb35de126b447d5ad6
[jalview.git] / src / jalview / gui / AppJmol.java
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.bin.Cache;
24 import jalview.datamodel.Alignment;
25 import jalview.datamodel.AlignmentI;
26 import jalview.datamodel.ColumnSelection;
27 import jalview.datamodel.PDBEntry;
28 import jalview.datamodel.SequenceI;
29 import jalview.gui.StructureViewer.ViewerType;
30 import jalview.io.JalviewFileChooser;
31 import jalview.io.JalviewFileView;
32 import jalview.schemes.BuriedColourScheme;
33 import jalview.schemes.ColourSchemeI;
34 import jalview.schemes.HelixColourScheme;
35 import jalview.schemes.HydrophobicColourScheme;
36 import jalview.schemes.PurinePyrimidineColourScheme;
37 import jalview.schemes.StrandColourScheme;
38 import jalview.schemes.TaylorColourScheme;
39 import jalview.schemes.TurnColourScheme;
40 import jalview.schemes.ZappoColourScheme;
41 import jalview.structures.models.AAStructureBindingModel;
42 import jalview.util.MessageManager;
43 import jalview.util.Platform;
44 import jalview.ws.dbsources.Pdb;
45
46 import java.awt.BorderLayout;
47 import java.awt.Color;
48 import java.awt.Dimension;
49 import java.awt.Font;
50 import java.awt.Graphics;
51 import java.awt.Rectangle;
52 import java.awt.event.ActionEvent;
53 import java.awt.event.ActionListener;
54 import java.awt.event.ItemEvent;
55 import java.awt.event.ItemListener;
56 import java.awt.event.MouseAdapter;
57 import java.awt.event.MouseEvent;
58 import java.io.BufferedReader;
59 import java.io.File;
60 import java.io.FileOutputStream;
61 import java.io.FileReader;
62 import java.io.IOException;
63 import java.io.PrintWriter;
64 import java.util.ArrayList;
65 import java.util.List;
66 import java.util.Vector;
67
68 import javax.swing.JCheckBoxMenuItem;
69 import javax.swing.JColorChooser;
70 import javax.swing.JInternalFrame;
71 import javax.swing.JMenu;
72 import javax.swing.JMenuItem;
73 import javax.swing.JPanel;
74 import javax.swing.JSplitPane;
75 import javax.swing.SwingUtilities;
76 import javax.swing.event.InternalFrameAdapter;
77 import javax.swing.event.InternalFrameEvent;
78 import javax.swing.event.MenuEvent;
79 import javax.swing.event.MenuListener;
80
81 public class AppJmol extends StructureViewerBase
82 {
83   // ms to wait for Jmol to load files
84   private static final int JMOL_LOAD_TIMEOUT = 20000;
85
86   private static final String SPACE = " ";
87
88   private static final String BACKSLASH = "\"";
89
90   AppJmolBinding jmb;
91
92   JPanel scriptWindow;
93
94   JSplitPane splitPane;
95
96   RenderPanel renderPanel;
97
98   ViewSelectionMenu seqColourBy;
99
100   /**
101    * 
102    * @param files
103    * @param ids
104    * @param seqs
105    * @param ap
106    * @param usetoColour
107    *          - add the alignment panel to the list used for colouring these
108    *          structures
109    * @param useToAlign
110    *          - add the alignment panel to the list used for aligning these
111    *          structures
112    * @param leaveColouringToJmol
113    *          - do not update the colours from any other source. Jmol is
114    *          handling them
115    * @param loadStatus
116    * @param bounds
117    * @param viewid
118    */
119   public AppJmol(String[] files, String[] ids, SequenceI[][] seqs,
120           AlignmentPanel ap, boolean usetoColour, boolean useToAlign,
121           boolean leaveColouringToJmol, String loadStatus,
122           Rectangle bounds, String viewid)
123   {
124     PDBEntry[] pdbentrys = new PDBEntry[files.length];
125     for (int i = 0; i < pdbentrys.length; i++)
126     {
127       // PDBEntry pdbentry = new PDBEntry(files[i], ids[i]);
128       PDBEntry pdbentry = new PDBEntry(ids[i], null, PDBEntry.Type.PDB,
129               files[i]);
130       pdbentrys[i] = pdbentry;
131     }
132     // / TODO: check if protocol is needed to be set, and if chains are
133     // autodiscovered.
134     jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
135             pdbentrys, seqs, null);
136
137     jmb.setLoadingFromArchive(true);
138     addAlignmentPanel(ap);
139     if (useToAlign)
140     {
141       useAlignmentPanelForSuperposition(ap);
142     }
143     if (leaveColouringToJmol || !usetoColour)
144     {
145       jmb.setColourBySequence(false);
146       seqColour.setSelected(false);
147       viewerColour.setSelected(true);
148     }
149     else if (usetoColour)
150     {
151       useAlignmentPanelForColourbyseq(ap);
152       jmb.setColourBySequence(true);
153       seqColour.setSelected(true);
154       viewerColour.setSelected(false);
155     }
156     this.setBounds(bounds);
157     initMenus();
158     setViewId(viewid);
159     // jalview.gui.Desktop.addInternalFrame(this, "Loading File",
160     // bounds.width,bounds.height);
161
162     this.addInternalFrameListener(new InternalFrameAdapter()
163     {
164       @Override
165       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
166       {
167         closeViewer(false);
168       }
169     });
170     initJmol(loadStatus); // pdbentry, seq, JBPCHECK!
171
172   }
173
174   private void initMenus()
175   {
176     String jmol = MessageManager.getString("label.jmol");
177     viewerActionMenu.setText(jmol);
178     seqColour.setSelected(jmb.isColourBySequence());
179     viewerColour.setSelected(!jmb.isColourBySequence());
180     if (_colourwith == null)
181     {
182       _colourwith = new Vector<AlignmentPanel>();
183     }
184     if (_alignwith == null)
185     {
186       _alignwith = new Vector<AlignmentPanel>();
187     }
188
189     seqColourBy = new ViewSelectionMenu(
190             MessageManager.getString("label.colour_by"), this, _colourwith,
191             new ItemListener()
192             {
193
194               @Override
195               public void itemStateChanged(ItemEvent e)
196               {
197                 if (!seqColour.isSelected())
198                 {
199                   seqColour.doClick();
200                 }
201                 else
202                 {
203                   // update the jmol display now.
204                   seqColour_actionPerformed(null);
205                 }
206               }
207             });
208     viewMenu.add(seqColourBy);
209     final ItemListener handler;
210     JMenu alpanels = new ViewSelectionMenu(
211             MessageManager.getString("label.superpose_with"), this,
212             _alignwith, handler = new ItemListener()
213             {
214
215               @Override
216               public void itemStateChanged(ItemEvent e)
217               {
218                 alignStructs.setEnabled(_alignwith.size() > 0);
219                 alignStructs.setToolTipText(MessageManager
220                         .formatMessage(
221                                 "label.align_structures_using_linked_alignment_views",
222                                 new String[] { new Integer(_alignwith
223                                         .size()).toString() }));
224               }
225             });
226     handler.itemStateChanged(null);
227     viewerActionMenu.add(alpanels);
228     viewerActionMenu.addMenuListener(new MenuListener()
229     {
230
231       @Override
232       public void menuSelected(MenuEvent e)
233       {
234         handler.itemStateChanged(null);
235       }
236
237       @Override
238       public void menuDeselected(MenuEvent e)
239       {
240         // TODO Auto-generated method stub
241
242       }
243
244       @Override
245       public void menuCanceled(MenuEvent e)
246       {
247         // TODO Auto-generated method stub
248
249       }
250     });
251
252     JMenuItem writeFeatures = new JMenuItem(
253             MessageManager.getString("label.create_viewer_attributes"));
254     writeFeatures.setToolTipText(MessageManager.formatMessage(
255             "label.create_viewer_attributes_tip", jmol));
256     writeFeatures.addActionListener(new ActionListener()
257     {
258       @Override
259       public void actionPerformed(ActionEvent e)
260       {
261         sendFeaturesToViewer();
262       }
263     });
264     viewerActionMenu.add(writeFeatures);
265
266     final JMenu fetchAttributes = new JMenu(MessageManager.formatMessage(
267             "label.fetch_viewer_attributes", jmol));
268     fetchAttributes.setToolTipText(MessageManager.formatMessage(
269             "label.fetch_viewer_attributes_tip", jmol));
270     fetchAttributes.addMouseListener(new MouseAdapter()
271     {
272
273       @Override
274       public void mouseEntered(MouseEvent e)
275       {
276         buildAttributesMenu(fetchAttributes);
277       }
278     });
279     viewerActionMenu.add(fetchAttributes);
280   }
281
282   IProgressIndicator progressBar = null;
283
284   /**
285    * add a single PDB structure to a new or existing Jmol view
286    * 
287    * @param pdbentry
288    * @param seq
289    * @param chains
290    * @param ap
291    */
292   public AppJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
293           final AlignmentPanel ap)
294   {
295     progressBar = ap.alignFrame;
296     String pdbId = pdbentry.getId();
297
298     /*
299      * If the PDB file is already loaded, the user may just choose to add to an
300      * existing viewer (or cancel)
301      */
302     if (addAlreadyLoadedFile(seq, chains, ap, pdbId))
303     {
304       return;
305     }
306
307     /*
308      * Check if there are other Jmol views involving this alignment and prompt
309      * user about adding this molecule to one of them
310      */
311     if (addToExistingViewer(pdbentry, seq, chains, ap, pdbId))
312     {
313       return;
314     }
315
316     /*
317      * If the options above are declined or do not apply, open a new viewer
318      */
319     openNewJmol(ap, new PDBEntry[] { pdbentry }, new SequenceI[][] { seq });
320   }
321
322   /**
323    * Answers true if this viewer already involves the given PDB ID
324    */
325   @Override
326   protected boolean hasPdbId(String pdbId)
327   {
328     return jmb.hasPdbId(pdbId);
329   }
330
331   private void openNewJmol(AlignmentPanel ap, PDBEntry[] pdbentrys,
332           SequenceI[][] seqs)
333   {
334     progressBar = ap.alignFrame;
335     jmb = new AppJmolBinding(this, ap.getStructureSelectionManager(),
336             pdbentrys, seqs, null);
337     addAlignmentPanel(ap);
338     useAlignmentPanelForColourbyseq(ap);
339     if (pdbentrys.length > 1)
340     {
341       alignAddedStructures = true;
342       useAlignmentPanelForSuperposition(ap);
343     }
344     jmb.setColourBySequence(true);
345     setSize(400, 400); // probably should be a configurable/dynamic default here
346     initMenus();
347     addingStructures = false;
348     worker = new Thread(this);
349     worker.start();
350
351     this.addInternalFrameListener(new InternalFrameAdapter()
352     {
353       @Override
354       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
355       {
356         closeViewer(false);
357       }
358     });
359
360   }
361
362   /**
363    * create a new Jmol containing several structures superimposed using the
364    * given alignPanel.
365    * 
366    * @param ap
367    * @param pe
368    * @param seqs
369    */
370   public AppJmol(AlignmentPanel ap, PDBEntry[] pe, SequenceI[][] seqs)
371   {
372     openNewJmol(ap, pe, seqs);
373   }
374
375   /**
376    * Returns a list of any Jmol viewers. The list is restricted to those linked
377    * to the given alignment panel if it is not null.
378    */
379   @Override
380   protected List<StructureViewerBase> getViewersFor(AlignmentPanel apanel)
381   {
382     List<StructureViewerBase> result = new ArrayList<StructureViewerBase>();
383     JInternalFrame[] frames = Desktop.instance.getAllFrames();
384
385     for (JInternalFrame frame : frames)
386     {
387       if (frame instanceof AppJmol)
388       {
389         if (apanel == null
390                 || ((StructureViewerBase) frame).isLinkedWith(apanel))
391         {
392           result.add((StructureViewerBase) frame);
393         }
394       }
395     }
396     return result;
397   }
398
399   void initJmol(String command)
400   {
401     jmb.setFinishedInit(false);
402     renderPanel = new RenderPanel();
403     // TODO: consider waiting until the structure/view is fully loaded before
404     // displaying
405     this.getContentPane().add(renderPanel, java.awt.BorderLayout.CENTER);
406     jalview.gui.Desktop.addInternalFrame(this, jmb.getViewerTitle(),
407             getBounds().width, getBounds().height);
408     if (scriptWindow == null)
409     {
410       BorderLayout bl = new BorderLayout();
411       bl.setHgap(0);
412       bl.setVgap(0);
413       scriptWindow = new JPanel(bl);
414       scriptWindow.setVisible(false);
415     }
416
417     jmb.allocateViewer(renderPanel, true, "", null, null, "",
418             scriptWindow, null);
419     // jmb.newJmolPopup("Jmol");
420     if (command == null)
421     {
422       command = "";
423     }
424     jmb.evalStateCommand(command);
425     jmb.evalStateCommand("set hoverDelay=0.1");
426     jmb.setFinishedInit(true);
427   }
428
429
430
431   boolean allChainsSelected = false;
432
433   @Override
434   void showSelectedChains()
435   {
436     Vector<String> toshow = new Vector<String>();
437     for (int i = 0; i < chainMenu.getItemCount(); i++)
438     {
439       if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
440       {
441         JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
442         if (item.isSelected())
443         {
444           toshow.addElement(item.getText());
445         }
446       }
447     }
448     jmb.centerViewer(toshow);
449   }
450
451   @Override
452   public void closeViewer(boolean closeExternalViewer)
453   {
454     // Jmol does not use an external viewer
455     if (jmb != null)
456     {
457       jmb.closeViewer();
458     }
459     setAlignmentPanel(null);
460     _aps.clear();
461     _alignwith.clear();
462     _colourwith.clear();
463     // TODO: check for memory leaks where instance isn't finalised because jmb
464     // holds a reference to the window
465     jmb = null;
466   }
467
468   @Override
469   public void run()
470   {
471     _started = true;
472     try
473     {
474       List<String> files = fetchPdbFiles();
475       if (files.size() > 0)
476       {
477         showFilesInViewer(files);
478       }
479     } finally
480     {
481       _started = false;
482       worker = null;
483     }
484   }
485
486   /**
487    * Either adds the given files to a structure viewer or opens a new viewer to
488    * show them
489    * 
490    * @param files
491    *          list of absolute paths to structure files
492    */
493   void showFilesInViewer(List<String> files)
494   {
495     long lastnotify = jmb.getLoadNotifiesHandled();
496     StringBuilder fileList = new StringBuilder();
497     for (String s : files)
498     {
499       fileList.append(SPACE).append(BACKSLASH)
500               .append(Platform.escapeString(s)).append(BACKSLASH);
501     }
502     String filesString = fileList.toString();
503
504     if (!addingStructures)
505     {
506       try
507       {
508         initJmol("load FILES " + filesString);
509       } catch (OutOfMemoryError oomerror)
510       {
511         new OOMWarning("When trying to open the Jmol viewer!", oomerror);
512         Cache.log.debug("File locations are " + filesString);
513       } catch (Exception ex)
514       {
515         Cache.log.error("Couldn't open Jmol viewer!", ex);
516       }
517     }
518     else
519     {
520       StringBuilder cmd = new StringBuilder();
521       cmd.append("loadingJalviewdata=true\nload APPEND ");
522       cmd.append(filesString);
523       cmd.append("\nloadingJalviewdata=null");
524       final String command = cmd.toString();
525       lastnotify = jmb.getLoadNotifiesHandled();
526
527       try
528       {
529         jmb.evalStateCommand(command);
530       } catch (OutOfMemoryError oomerror)
531       {
532         new OOMWarning("When trying to add structures to the Jmol viewer!",
533                 oomerror);
534         Cache.log.debug("File locations are " + filesString);
535       } catch (Exception ex)
536       {
537         Cache.log.error("Couldn't add files to Jmol viewer!", ex);
538       }
539     }
540
541     // need to wait around until script has finished
542     int waitMax = JMOL_LOAD_TIMEOUT;
543     int waitFor = 35;
544     int waitTotal = 0;
545     while (addingStructures ? lastnotify >= jmb.getLoadNotifiesHandled()
546             : !(jmb.isFinishedInit() && jmb.getPdbFile() != null && jmb
547                     .getPdbFile().length == files.size()))
548     {
549       try
550       {
551         Cache.log.debug("Waiting around for jmb notify.");
552         Thread.sleep(waitFor);
553         waitTotal += waitFor;
554       } catch (Exception e)
555       {
556       }
557       if (waitTotal > waitMax)
558       {
559         System.err
560                 .println("Timed out waiting for Jmol to load files after "
561                         + waitTotal + "ms");
562 //        System.err.println("finished: " + jmb.isFinishedInit()
563 //                + "; loaded: " + Arrays.toString(jmb.getPdbFile())
564 //                + "; files: " + files.toString());
565         jmb.getPdbFile();
566         break;
567       }
568     }
569
570     // refresh the sequence colours for the new structure(s)
571     for (AlignmentPanel ap : _colourwith)
572     {
573       jmb.updateColours(ap);
574     }
575     // do superposition if asked to
576     if (Cache.getDefault("AUTOSUPERIMPOSE", true) && alignAddedStructures)
577     {
578       alignAddedStructures();
579     }
580     addingStructures = false;
581   }
582
583   /**
584    * Queues a thread to align structures with Jalview alignments
585    */
586   void alignAddedStructures()
587   {
588     javax.swing.SwingUtilities.invokeLater(new Runnable()
589     {
590       @Override
591       public void run()
592       {
593         if (jmb.viewer.isScriptExecuting())
594         {
595           SwingUtilities.invokeLater(this);
596           try
597           {
598             Thread.sleep(5);
599           } catch (InterruptedException q)
600           {
601           }
602           return;
603         }
604         else
605         {
606           alignStructs_withAllAlignPanels();
607         }
608       }
609     });
610     alignAddedStructures = false;
611   }
612
613   /**
614    * Retrieves and saves as file any modelled PDB entries for which we do not
615    * already have a file saved. Returns a list of absolute paths to structure
616    * files which were either retrieved, or already stored but not modelled in
617    * the structure viewer (i.e. files to add to the viewer display).
618    * 
619    * @return
620    */
621   List<String> fetchPdbFiles()
622   {
623     // todo - record which pdbids were successfully imported.
624     StringBuilder errormsgs = new StringBuilder();
625
626     List<String> files = new ArrayList<String>();
627     String pdbid = "";
628     try
629     {
630       String[] filesInViewer = jmb.getPdbFile();
631       // TODO: replace with reference fetching/transfer code (validate PDBentry
632       // as a DBRef?)
633       Pdb pdbclient = new Pdb();
634       for (int pi = 0; pi < jmb.getPdbCount(); pi++)
635       {
636         String file = jmb.getPdbEntry(pi).getFile();
637         if (file == null)
638         {
639           // retrieve the pdb and store it locally
640           AlignmentI pdbseq = null;
641           pdbid = jmb.getPdbEntry(pi).getId();
642           long hdl = pdbid.hashCode() - System.currentTimeMillis();
643           if (progressBar != null)
644           {
645             progressBar.setProgressBar(MessageManager.formatMessage(
646                     "status.fetching_pdb", new String[] { pdbid }), hdl);
647           }
648           try
649           {
650             pdbseq = pdbclient.getSequenceRecords(pdbid);
651           } catch (OutOfMemoryError oomerror)
652           {
653             new OOMWarning("Retrieving PDB id " + pdbid, oomerror);
654           } catch (Exception ex)
655           {
656             ex.printStackTrace();
657             errormsgs.append("'").append(pdbid).append("'");
658           } finally
659           {
660             if (progressBar != null)
661             {
662               progressBar.setProgressBar(
663                       MessageManager.getString("label.state_completed"),
664                       hdl);
665             }
666           }
667           if (pdbseq != null)
668           {
669             // just transfer the file name from the first sequence's first
670             // PDBEntry
671             file = new File(pdbseq.getSequenceAt(0).getAllPDBEntries()
672                     .elementAt(0).getFile()).getAbsolutePath();
673             jmb.getPdbEntry(pi).setFile(file);
674             files.add(file);
675           }
676           else
677           {
678             errormsgs.append("'").append(pdbid).append("' ");
679           }
680         }
681         else
682         {
683           if (filesInViewer != null && filesInViewer.length > 0)
684           {
685             addingStructures = true; // already files loaded.
686             for (int c = 0; c < filesInViewer.length; c++)
687             {
688               if (filesInViewer[c].equals(file))
689               {
690                 file = null;
691                 break;
692               }
693             }
694           }
695           if (file != null)
696           {
697             files.add(file);
698           }
699         }
700       }
701     } catch (OutOfMemoryError oomerror)
702     {
703       new OOMWarning("Retrieving PDB files: " + pdbid, oomerror);
704     } catch (Exception ex)
705     {
706       ex.printStackTrace();
707       errormsgs.append("When retrieving pdbfiles : current was: '")
708               .append(pdbid).append("'");
709     }
710     if (errormsgs.length() > 0)
711     {
712       JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
713               .formatMessage("label.pdb_entries_couldnt_be_retrieved",
714                       new String[] { errormsgs.toString() }),
715               MessageManager.getString("label.couldnt_load_file"),
716               JvOptionPane.ERROR_MESSAGE);
717     }
718     return files;
719   }
720
721   @Override
722   public void pdbFile_actionPerformed(ActionEvent actionEvent)
723   {
724     JalviewFileChooser chooser = new JalviewFileChooser(
725             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
726
727     chooser.setFileView(new JalviewFileView());
728     chooser.setDialogTitle(MessageManager.getString("label.save_pdb_file"));
729     chooser.setToolTipText(MessageManager.getString("action.save"));
730
731     int value = chooser.showSaveDialog(this);
732
733     if (value == JalviewFileChooser.APPROVE_OPTION)
734     {
735       BufferedReader in = null;
736       try
737       {
738         // TODO: cope with multiple PDB files in view
739         in = new BufferedReader(new FileReader(jmb.getPdbFile()[0]));
740         File outFile = chooser.getSelectedFile();
741
742         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
743         String data;
744         while ((data = in.readLine()) != null)
745         {
746           if (!(data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1))
747           {
748             out.println(data);
749           }
750         }
751         out.close();
752       } catch (Exception ex)
753       {
754         ex.printStackTrace();
755       } finally
756       {
757         if (in != null)
758         {
759           try
760           {
761             in.close();
762           } catch (IOException e)
763           {
764             // ignore
765           }
766         }
767       }
768     }
769   }
770
771   @Override
772   public void viewMapping_actionPerformed(ActionEvent actionEvent)
773   {
774     jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();
775     try
776     {
777       cap.appendText(jmb.printMappings());
778     } catch (OutOfMemoryError e)
779     {
780       new OOMWarning(
781               "composing sequence-structure alignments for display in text box.",
782               e);
783       cap.dispose();
784       return;
785     }
786     jalview.gui.Desktop.addInternalFrame(cap,
787             MessageManager.getString("label.pdb_sequence_mapping"), 550,
788             600);
789   }
790
791   @Override
792   public void eps_actionPerformed(ActionEvent e)
793   {
794     makePDBImage(jalview.util.ImageMaker.TYPE.EPS);
795   }
796
797   @Override
798   public void png_actionPerformed(ActionEvent e)
799   {
800     makePDBImage(jalview.util.ImageMaker.TYPE.PNG);
801   }
802
803   void makePDBImage(jalview.util.ImageMaker.TYPE type)
804   {
805     int width = getWidth();
806     int height = getHeight();
807
808     jalview.util.ImageMaker im;
809
810     if (type == jalview.util.ImageMaker.TYPE.PNG)
811     {
812       im = new jalview.util.ImageMaker(this,
813               jalview.util.ImageMaker.TYPE.PNG, "Make PNG image from view",
814               width, height, null, null, null, 0, false);
815     }
816     else if (type == jalview.util.ImageMaker.TYPE.EPS)
817     {
818       im = new jalview.util.ImageMaker(this,
819               jalview.util.ImageMaker.TYPE.EPS, "Make EPS file from view",
820               width, height, null, this.getTitle(), null, 0, false);
821     }
822     else
823     {
824
825       im = new jalview.util.ImageMaker(this,
826               jalview.util.ImageMaker.TYPE.SVG, "Make SVG file from PCA",
827               width, height, null, this.getTitle(), null, 0, false);
828     }
829
830     if (im.getGraphics() != null)
831     {
832       jmb.viewer.renderScreenImage(im.getGraphics(), width, height);
833       im.writeImage();
834     }
835   }
836
837   @Override
838   public void viewerColour_actionPerformed(ActionEvent actionEvent)
839   {
840     if (viewerColour.isSelected())
841     {
842       // disable automatic sequence colouring.
843       jmb.setColourBySequence(false);
844     }
845   }
846
847   @Override
848   public void seqColour_actionPerformed(ActionEvent actionEvent)
849   {
850     jmb.setColourBySequence(seqColour.isSelected());
851     if (_colourwith == null)
852     {
853       _colourwith = new Vector<AlignmentPanel>();
854     }
855     if (jmb.isColourBySequence())
856     {
857       if (!jmb.isLoadingFromArchive())
858       {
859         if (_colourwith.size() == 0 && getAlignmentPanel() != null)
860         {
861           // Make the currently displayed alignment panel the associated view
862           _colourwith.add(getAlignmentPanel().alignFrame.alignPanel);
863         }
864       }
865       // Set the colour using the current view for the associated alignframe
866       for (AlignmentPanel ap : _colourwith)
867       {
868         jmb.colourBySequence(ap);
869       }
870     }
871   }
872
873   @Override
874   public void chainColour_actionPerformed(ActionEvent actionEvent)
875   {
876     chainColour.setSelected(true);
877     jmb.colourByChain();
878   }
879
880   @Override
881   public void chargeColour_actionPerformed(ActionEvent actionEvent)
882   {
883     chargeColour.setSelected(true);
884     jmb.colourByCharge();
885   }
886
887   @Override
888   public void zappoColour_actionPerformed(ActionEvent actionEvent)
889   {
890     zappoColour.setSelected(true);
891     jmb.setJalviewColourScheme(new ZappoColourScheme());
892   }
893
894   @Override
895   public void taylorColour_actionPerformed(ActionEvent actionEvent)
896   {
897     taylorColour.setSelected(true);
898     jmb.setJalviewColourScheme(new TaylorColourScheme());
899   }
900
901   @Override
902   public void hydroColour_actionPerformed(ActionEvent actionEvent)
903   {
904     hydroColour.setSelected(true);
905     jmb.setJalviewColourScheme(new HydrophobicColourScheme());
906   }
907
908   @Override
909   public void helixColour_actionPerformed(ActionEvent actionEvent)
910   {
911     helixColour.setSelected(true);
912     jmb.setJalviewColourScheme(new HelixColourScheme());
913   }
914
915   @Override
916   public void strandColour_actionPerformed(ActionEvent actionEvent)
917   {
918     strandColour.setSelected(true);
919     jmb.setJalviewColourScheme(new StrandColourScheme());
920   }
921
922   @Override
923   public void turnColour_actionPerformed(ActionEvent actionEvent)
924   {
925     turnColour.setSelected(true);
926     jmb.setJalviewColourScheme(new TurnColourScheme());
927   }
928
929   @Override
930   public void buriedColour_actionPerformed(ActionEvent actionEvent)
931   {
932     buriedColour.setSelected(true);
933     jmb.setJalviewColourScheme(new BuriedColourScheme());
934   }
935
936   @Override
937   public void purinePyrimidineColour_actionPerformed(ActionEvent actionEvent)
938   {
939     setJalviewColourScheme(new PurinePyrimidineColourScheme());
940   }
941
942   @Override
943   public void userColour_actionPerformed(ActionEvent actionEvent)
944   {
945     userColour.setSelected(true);
946     new UserDefinedColours(this, null);
947   }
948
949   @Override
950   public void backGround_actionPerformed(ActionEvent actionEvent)
951   {
952     java.awt.Color col = JColorChooser
953             .showDialog(this, MessageManager
954                     .getString("label.select_backgroud_colour"), null);
955     if (col != null)
956     {
957       jmb.setBackgroundColour(col);
958     }
959   }
960
961   @Override
962   public void showHelp_actionPerformed(ActionEvent actionEvent)
963   {
964     try
965     {
966       jalview.util.BrowserLauncher
967               .openURL("http://jmol.sourceforge.net/docs/JmolUserGuide/");
968     } catch (Exception ex)
969     {
970     }
971   }
972
973   public void showConsole(boolean showConsole)
974   {
975
976     if (showConsole)
977     {
978       if (splitPane == null)
979       {
980         splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
981         splitPane.setTopComponent(renderPanel);
982         splitPane.setBottomComponent(scriptWindow);
983         this.getContentPane().add(splitPane, BorderLayout.CENTER);
984         splitPane.setDividerLocation(getHeight() - 200);
985         scriptWindow.setVisible(true);
986         scriptWindow.validate();
987         splitPane.validate();
988       }
989
990     }
991     else
992     {
993       if (splitPane != null)
994       {
995         splitPane.setVisible(false);
996       }
997
998       splitPane = null;
999
1000       this.getContentPane().add(renderPanel, BorderLayout.CENTER);
1001     }
1002
1003     validate();
1004   }
1005
1006   class RenderPanel extends JPanel
1007   {
1008     final Dimension currentSize = new Dimension();
1009
1010     @Override
1011     public void paintComponent(Graphics g)
1012     {
1013       getSize(currentSize);
1014
1015       if (jmb != null && jmb.fileLoadingError != null)
1016       {
1017         g.setColor(Color.black);
1018         g.fillRect(0, 0, currentSize.width, currentSize.height);
1019         g.setColor(Color.white);
1020         g.setFont(new Font("Verdana", Font.BOLD, 14));
1021         g.drawString(MessageManager.getString("label.error_loading_file")
1022                 + "...", 20, currentSize.height / 2);
1023         StringBuffer sb = new StringBuffer();
1024         int lines = 0;
1025         for (int e = 0; e < jmb.getPdbCount(); e++)
1026         {
1027           sb.append(jmb.getPdbEntry(e).getId());
1028           if (e < jmb.getPdbCount() - 1)
1029           {
1030             sb.append(",");
1031           }
1032
1033           if (e == jmb.getPdbCount() - 1 || sb.length() > 20)
1034           {
1035             lines++;
1036             g.drawString(sb.toString(), 20, currentSize.height / 2 - lines
1037                     * g.getFontMetrics().getHeight());
1038           }
1039         }
1040       }
1041       else if (jmb == null || jmb.viewer == null || !jmb.isFinishedInit())
1042       {
1043         g.setColor(Color.black);
1044         g.fillRect(0, 0, currentSize.width, currentSize.height);
1045         g.setColor(Color.white);
1046         g.setFont(new Font("Verdana", Font.BOLD, 14));
1047         g.drawString(MessageManager.getString("label.retrieving_pdb_data"),
1048                 20, currentSize.height / 2);
1049       }
1050       else
1051       {
1052         jmb.viewer.renderScreenImage(g, currentSize.width,
1053                 currentSize.height);
1054       }
1055     }
1056   }
1057
1058   public void updateTitleAndMenus()
1059   {
1060     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
1061     {
1062       repaint();
1063       return;
1064     }
1065     setChainMenuItems(jmb.getChainNames());
1066
1067     this.setTitle(jmb.getViewerTitle());
1068     // if (jmb.getPdbFile().length > 1 && jmb.getSequence().length > 1)
1069     // {
1070       viewerActionMenu.setVisible(true);
1071     // }
1072     if (!jmb.isLoadingFromArchive())
1073     {
1074       seqColour_actionPerformed(null);
1075     }
1076   }
1077
1078   /*
1079    * (non-Javadoc)
1080    * 
1081    * @see
1082    * jalview.jbgui.GStructureViewer#alignStructs_actionPerformed(java.awt.event
1083    * .ActionEvent)
1084    */
1085   @Override
1086   protected void alignStructs_actionPerformed(ActionEvent actionEvent)
1087   {
1088     alignStructs_withAllAlignPanels();
1089   }
1090
1091   private void alignStructs_withAllAlignPanels()
1092   {
1093     if (getAlignmentPanel() == null)
1094     {
1095       return;
1096     }
1097     ;
1098     if (_alignwith.size() == 0)
1099     {
1100       _alignwith.add(getAlignmentPanel());
1101     }
1102     ;
1103     try
1104     {
1105       AlignmentI[] als = new Alignment[_alignwith.size()];
1106       ColumnSelection[] alc = new ColumnSelection[_alignwith.size()];
1107       int[] alm = new int[_alignwith.size()];
1108       int a = 0;
1109
1110       for (AlignmentPanel ap : _alignwith)
1111       {
1112         als[a] = ap.av.getAlignment();
1113         alm[a] = -1;
1114         alc[a++] = ap.av.getColumnSelection();
1115       }
1116       jmb.superposeStructures(als, alm, alc);
1117     } catch (Exception e)
1118     {
1119       StringBuffer sp = new StringBuffer();
1120       for (AlignmentPanel ap : _alignwith)
1121       {
1122         sp.append("'" + ap.alignFrame.getTitle() + "' ");
1123       }
1124       Cache.log.info("Couldn't align structures with the " + sp.toString()
1125               + "associated alignment panels.", e);
1126
1127     }
1128
1129   }
1130
1131   @Override
1132   public void setJalviewColourScheme(ColourSchemeI ucs)
1133   {
1134     jmb.setJalviewColourScheme(ucs);
1135
1136   }
1137
1138   /**
1139    * 
1140    * @param alignment
1141    * @return first alignment panel displaying given alignment, or the default
1142    *         alignment panel
1143    */
1144   public AlignmentPanel getAlignmentPanelFor(AlignmentI alignment)
1145   {
1146     for (AlignmentPanel ap : getAllAlignmentPanels())
1147     {
1148       if (ap.av.getAlignment() == alignment)
1149       {
1150         return ap;
1151       }
1152     }
1153     return getAlignmentPanel();
1154   }
1155
1156   @Override
1157   public AAStructureBindingModel getBinding()
1158   {
1159     return this.jmb;
1160   }
1161
1162   @Override
1163   public String getStateInfo()
1164   {
1165     return jmb == null ? null : jmb.viewer.getStateInfo();
1166   }
1167
1168   @Override
1169   public ViewerType getViewerType()
1170   {
1171     return ViewerType.JMOL;
1172   }
1173
1174   @Override
1175   protected AAStructureBindingModel getBindingModel()
1176   {
1177     return jmb;
1178   }
1179
1180   @Override
1181   protected void sendFeaturesToViewer()
1182   {
1183     /*
1184      * Atom properties may also be set directly using {atom expression}.xxxx = y.
1185      */
1186   }
1187
1188   @Override
1189   protected void getResidueAttributes(String attName)
1190   {
1191     /*
1192      * getproperty atominfo returns properties for all atoms
1193      * - could parse this for unique/specific property names?
1194      *
1195      * _ipt atomID atomIndex atomno bondCount chain clickabilityFlags colix color coord 
1196      * element elemno formalCharge groupID info model name occupancy partialCharge 
1197      * polymerLength radius resname resno shape spacefill structure sym temp 
1198      * visibilityFlags visible x y z
1199      *
1200      * getproperty chaininfo is residue level info
1201      * no sign of average bfactor for a residue :-(
1202      */
1203   }
1204
1205   @Override
1206   protected List<String> getResidueAttributeNames()
1207   {
1208     // TODO Auto-generated method stub
1209     return null;
1210   }
1211
1212 }