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