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