jmol update
[jalview.git] / src / jalview / gui / AppJmol.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
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 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import java.util.regex.*;
21 import java.util.*;
22 import java.awt.*;
23 import javax.swing.*;
24 import javax.swing.event.*;
25 import java.awt.event.*;
26 import java.io.*;
27
28 import jalview.jbgui.GStructureViewer;
29 import jalview.api.SequenceStructureBinding;
30 import jalview.bin.Cache;
31 import jalview.datamodel.*;
32 import jalview.gui.*;
33 import jalview.structure.*;
34 import jalview.datamodel.PDBEntry;
35 import jalview.io.*;
36 import jalview.schemes.*;
37 import jalview.ws.ebi.EBIFetchClient;
38
39 import org.jmol.api.*;
40 import org.jmol.adapter.smarter.SmarterJmolAdapter;
41 import org.jmol.popup.*;
42 import org.jmol.viewer.JmolConstants;
43 import org.openscience.jmol.app.jmolpanel.AppConsole;
44
45 public class AppJmol extends GStructureViewer implements Runnable,
46         SequenceStructureBinding
47
48 {
49   AppJmolBinding jmb;
50
51   JPanel scriptWindow;
52   
53   JSplitPane splitPane;
54
55   RenderPanel renderPanel;
56
57   AlignmentPanel ap;
58
59   Vector atomsPicked = new Vector();
60
61   private boolean addingStructures = false;
62
63   /**
64    * 
65    * @param file
66    * @param id
67    * @param seq
68    * @param ap
69    * @param loadStatus
70    * @param bounds
71    * @deprecated defaults to AppJmol(String[] files, ... , viewid);
72    */
73   public AppJmol(String file, String id, SequenceI[] seq,
74           AlignmentPanel ap, String loadStatus, Rectangle bounds)
75   {
76     this(file, id, seq, ap, loadStatus, bounds, null);
77   }
78
79   /**
80    * @deprecated
81    */
82   public AppJmol(String file, String id, SequenceI[] seq,
83           AlignmentPanel ap, String loadStatus, Rectangle bounds,
84           String viewid)
85   {
86     this(new String[]
87     { file }, new String[]
88     { id }, new SequenceI[][]
89     { seq }, ap, loadStatus, bounds, viewid);
90   }
91
92   /**
93    * 
94    * @param files
95    * @param ids
96    * @param seqs
97    * @param ap
98    * @param loadStatus
99    * @param bounds
100    * @param viewid
101    */
102   public AppJmol(String[] files, String[] ids, SequenceI[][] seqs,
103           AlignmentPanel ap, String loadStatus, Rectangle bounds,
104           String viewid)
105   {
106     PDBEntry[] pdbentrys = new PDBEntry[files.length];
107     for (int i = 0; i < pdbentrys.length; i++)
108     {
109       PDBEntry pdbentry = new PDBEntry();
110       pdbentry.setFile(files[i]);
111       pdbentry.setId(ids[i]);
112       pdbentrys[i] = pdbentry;
113     }
114     // / TODO: check if protocol is needed to be set, and if chains are
115     // autodiscovered.
116     jmb = new AppJmolBinding(this, pdbentrys, seqs, null, null);
117
118     jmb.setLoadingFromArchive(true);
119     this.ap = ap;
120     this.setBounds(bounds);
121     jmb.setColourBySequence(false);
122     seqColour.setSelected(false);
123     viewId = viewid;
124     // jalview.gui.Desktop.addInternalFrame(this, "Loading File",
125     // bounds.width,bounds.height);
126
127     this.addInternalFrameListener(new InternalFrameAdapter()
128     {
129       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
130       {
131         closeViewer();
132       }
133     });
134     initJmol(loadStatus); // pdbentry, seq, JBPCHECK!
135
136   }
137
138   IProgressIndicator progressBar = null;
139
140   public AppJmol(PDBEntry pdbentry, SequenceI[] seq, String[] chains,
141           AlignmentPanel ap)
142   {
143     progressBar = ap.alignFrame;
144     // ////////////////////////////////
145     // Is the pdb file already loaded?
146     String alreadyMapped = StructureSelectionManager
147             .getStructureSelectionManager().alreadyMappedToFile(
148                     pdbentry.getId());
149
150     if (alreadyMapped != null)
151     {
152       int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
153               pdbentry.getId() + " is already displayed."
154                       + "\nDo you want to re-use this viewer ?",
155               "Map Sequences to Visible Window: " + pdbentry.getId(),
156               JOptionPane.YES_NO_OPTION);
157
158       if (option == JOptionPane.YES_OPTION)
159       {
160         StructureSelectionManager.getStructureSelectionManager()
161                 .setMapping(seq, chains, alreadyMapped,
162                         AppletFormatAdapter.FILE);
163         if (ap.seqPanel.seqCanvas.fr != null)
164         {
165           ap.seqPanel.seqCanvas.fr.featuresAdded();
166           ap.paintAlignment(true);
167         }
168
169         // Now this AppJmol is mapped to new sequences. We must add them to
170         // the exisiting array
171         JInternalFrame[] frames = Desktop.instance.getAllFrames();
172
173         for (int i = 0; i < frames.length; i++)
174         {
175           if (frames[i] instanceof AppJmol)
176           {
177             AppJmol topJmol = ((AppJmol) frames[i]);
178             // JBPNOTE: this looks like a binding routine, rather than a gui
179             // routine
180             for (int pe = 0; pe < topJmol.jmb.pdbentry.length; pe++)
181             {
182               if (topJmol.jmb.pdbentry[pe].getFile().equals(alreadyMapped))
183               {
184                 topJmol.jmb.addSequence(pe, seq);
185                 break;
186               }
187             }
188           }
189         }
190
191         return;
192       }
193     }
194     // /////////////////////////////////
195     // Check if there are other Jmol views involving this alignment
196     // and prompt user about adding this molecule to one of them
197     Vector existingViews = getJmolsFor(ap);
198     if (existingViews.size() > 0)
199     {
200       Enumeration jm = existingViews.elements();
201       while (jm.hasMoreElements())
202       {
203         AppJmol topJmol = (AppJmol) jm.nextElement();
204         // TODO: highlight topJmol in view somehow
205         int option = JOptionPane.showInternalConfirmDialog(Desktop.desktop,
206                 "Do you want to add " + pdbentry.getId()
207                         + " to the view called\n'" + topJmol.getTitle()
208                         + "'\n", "Align to existing structure view",
209                 JOptionPane.YES_NO_OPTION);
210         if (option == JOptionPane.YES_OPTION)
211         {
212           topJmol.addStructure(pdbentry, seq, chains, true, ap.alignFrame);
213           return;
214         }
215       }
216     }
217     // /////////////////////////////////
218
219     jmb = new AppJmolBinding(this, new PDBEntry[]
220     { pdbentry }, new SequenceI[][]
221     { seq }, null, null);
222     this.ap = ap;
223     setSize(400, 400); // probably should be a configurable/dynamic default here
224
225     if (pdbentry.getFile() != null)
226     {
227       initJmol("load \"" + pdbentry.getFile() + "\"");
228     }
229     else
230     {
231       addingStructures = false;
232       worker = new Thread(this);
233       worker.start();
234     }
235
236     this.addInternalFrameListener(new InternalFrameAdapter()
237     {
238       public void internalFrameClosing(InternalFrameEvent internalFrameEvent)
239       {
240         closeViewer();
241       }
242     });
243
244   }
245
246   /**
247    * pdb retrieval thread.
248    */
249   private Thread worker = null;
250
251   /**
252    * add a new structure (with associated sequences and chains) to this viewer,
253    * retrieving it if necessary first.
254    * 
255    * @param pdbentry
256    * @param seq
257    * @param chains
258    * @param alignFrame 
259    * @param align
260    *          if true, new structure(s) will be align using associated alignment
261    */
262   private void addStructure(final PDBEntry pdbentry, final SequenceI[] seq,
263           final String[] chains, final boolean b, final IProgressIndicator alignFrame)
264   {
265     if (pdbentry.getFile() == null)
266     {
267       if (worker != null && worker.isAlive())
268       {
269         // a retrieval is in progress, wait around and add ourselves to the
270         // queue.
271         new Thread(new Runnable()
272         {
273           public void run()
274           {
275             while (worker != null && worker.isAlive() && _started)
276             {
277               try
278               {
279                 Thread.sleep(100 + ((int) Math.random() * 100));
280
281               } catch (Exception e)
282               {
283               }
284
285             }
286             // and call ourselves again.
287             addStructure(pdbentry, seq, chains, b, alignFrame);
288           }
289         }).start();
290         return;
291       }
292     }
293     // otherwise, start adding the structure.
294     jmb.addSequenceAndChain(new PDBEntry[]
295     { pdbentry }, new SequenceI[][]
296     { seq }, new String[][]
297     { chains });
298     addingStructures = true;
299     _started = false;
300     alignAddedStructures = b;
301     progressBar = alignFrame; // visual indication happens on caller frame. 
302     (worker = new Thread(this)).start();
303     return;
304   }
305
306   private Vector getJmolsFor(AlignmentPanel ap2)
307   {
308     Vector otherJmols = new Vector();
309     // Now this AppJmol is mapped to new sequences. We must add them to
310     // the exisiting array
311     JInternalFrame[] frames = Desktop.instance.getAllFrames();
312
313     for (int i = 0; i < frames.length; i++)
314     {
315       if (frames[i] instanceof AppJmol)
316       {
317         AppJmol topJmol = ((AppJmol) frames[i]);
318         if (topJmol.ap == ap2)
319         {
320           otherJmols.addElement(topJmol);
321         }
322       }
323     }
324     return otherJmols;
325   }
326
327   void initJmol(String command)
328   {
329     jmb.setFinishedInit(false);
330     renderPanel = new RenderPanel();
331     // TODO: consider waiting until the structure/view is fully loaded before
332     // displaying
333     this.getContentPane().add(renderPanel, java.awt.BorderLayout.CENTER);
334     jalview.gui.Desktop.addInternalFrame(this, jmb.getViewerTitle(),
335             getBounds().width, getBounds().height);
336     if (scriptWindow == null)
337     {
338       BorderLayout bl = new BorderLayout();
339       bl.setHgap(0);
340       bl.setVgap(0);
341       scriptWindow = new JPanel(bl);
342       scriptWindow.setVisible(false);
343     };
344     jmb.allocateViewer(renderPanel, true, "", null, null, "", scriptWindow, null);
345     jmb.newJmolPopup(true, "Jmol", true);
346     jmb.evalStateCommand(command);
347     jmb.setFinishedInit(true);
348   }
349
350   void setChainMenuItems(Vector chains)
351   {
352     chainMenu.removeAll();
353     if (chains == null)
354     {
355       return;
356     }
357     JMenuItem menuItem = new JMenuItem("All");
358     menuItem.addActionListener(new ActionListener()
359     {
360       public void actionPerformed(ActionEvent evt)
361       {
362         allChainsSelected = true;
363         for (int i = 0; i < chainMenu.getItemCount(); i++)
364         {
365           if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
366             ((JCheckBoxMenuItem) chainMenu.getItem(i)).setSelected(true);
367         }
368         centerViewer();
369         allChainsSelected = false;
370       }
371     });
372
373     chainMenu.add(menuItem);
374
375     for (int c = 0; c < chains.size(); c++)
376     {
377       menuItem = new JCheckBoxMenuItem(chains.elementAt(c).toString(), true);
378       menuItem.addItemListener(new ItemListener()
379       {
380         public void itemStateChanged(ItemEvent evt)
381         {
382           if (!allChainsSelected)
383             centerViewer();
384         }
385       });
386
387       chainMenu.add(menuItem);
388     }
389   }
390
391   boolean allChainsSelected = false;
392
393   private boolean alignAddedStructures = false;
394
395   void centerViewer()
396   {
397     Vector toshow = new Vector();
398     String lbl;
399     int mlength, p, mnum;
400     for (int i = 0; i < chainMenu.getItemCount(); i++)
401     {
402       if (chainMenu.getItem(i) instanceof JCheckBoxMenuItem)
403       {
404         JCheckBoxMenuItem item = (JCheckBoxMenuItem) chainMenu.getItem(i);
405         if (item.isSelected())
406         {
407           toshow.addElement(item.getText());
408         }
409       }
410     }
411     jmb.centerViewer(toshow);
412   }
413
414   void closeViewer()
415   {
416     jmb.closeViewer();
417     
418     // TODO: check for memory leaks where instance isn't finalised because jmb
419     // holds a reference to the window
420     jmb = null;
421   }
422
423   /**
424    * state flag for PDB retrieval thread
425    */
426   private boolean _started = false;
427
428   public void run()
429   {
430     _started = true;
431     String pdbid = "";
432     // todo - record which pdbids were successfuly imported.
433     StringBuffer errormsgs = new StringBuffer(), files = new StringBuffer();
434     try
435     {
436       String[] curfiles = jmb.getPdbFile(); // files currently in viewer
437       // TODO: replace with reference fetching/transfer code (validate PDBentry
438       // as a DBRef?)
439       jalview.ws.dbsources.Pdb pdbclient = new jalview.ws.dbsources.Pdb();
440       for (int pi = 0; pi < jmb.pdbentry.length; pi++)
441       {
442         String file = jmb.pdbentry[pi].getFile();
443         if (file == null)
444         {
445           // retrieve the pdb and store it locally
446           AlignmentI pdbseq = null;
447           pdbid = jmb.pdbentry[pi].getId();
448           long hdl=pdbid.hashCode()-System.currentTimeMillis();
449           if (progressBar != null)
450           {
451             progressBar.setProgressBar("Fetching PDB " + pdbid,
452                     hdl);
453           }
454           try
455           {
456             pdbseq = pdbclient.getSequenceRecords(pdbid = jmb.pdbentry[pi]
457                     .getId());
458           } catch (OutOfMemoryError oomerror)
459           {
460             new OOMWarning("Retrieving PDB id " + pdbid, oomerror);
461           } catch (Exception ex)
462           {
463             ex.printStackTrace();
464             errormsgs.append("'" + pdbid + "'");
465           }
466           if (progressBar != null)
467           {
468             progressBar.setProgressBar("Finished.", hdl);
469           }
470           if (pdbseq != null)
471           {
472             // just transfer the file name from the first sequence's first
473             // PDBEntry
474             jmb.pdbentry[pi].setFile(file = ((PDBEntry) pdbseq
475                     .getSequenceAt(0).getPDBId().elementAt(0)).getFile());
476             files.append(" \"" + file + "\"");
477           }
478           else
479           {
480             errormsgs.append("'" + pdbid + "' ");
481           }
482         }
483         else
484         {
485           if (curfiles != null && curfiles.length > 0)
486           {
487             addingStructures = true; // already files loaded.
488             for (int c = 0; c < curfiles.length; c++)
489             {
490               if (curfiles[c].equals(file))
491               {
492                 file = null;
493                 break;
494               }
495             }
496           }
497           if (file != null)
498           {
499             files.append(" \"" + file + "\"");
500           }
501         }
502       }
503     } catch (OutOfMemoryError oomerror)
504     {
505       new OOMWarning("Retrieving PDB files: " + pdbid, oomerror);
506     } catch (Exception ex)
507     {
508       ex.printStackTrace();
509       errormsgs.append("When retrieving pdbfiles : current was: '" + pdbid
510               + "'");
511     }
512     if (errormsgs.length() > 0)
513     {
514
515       JOptionPane.showInternalMessageDialog(Desktop.desktop,
516               "The following pdb entries could not be retrieved from the PDB:\n"
517                       + errormsgs.toString()
518                       + "\nPlease try downloading them manually.",
519               "Couldn't load file", JOptionPane.ERROR_MESSAGE);
520
521     }
522     if (files.length() > 0)
523     {
524       if (!addingStructures)
525       {
526
527         try
528         {
529           initJmol("load FILES " + files.toString());
530         } catch (OutOfMemoryError oomerror)
531         {
532           new OOMWarning("When trying to open the Jmol viewer!", oomerror);
533           Cache.log.debug("File locations are " + files);
534         } catch (Exception ex)
535         {
536           Cache.log.error("Couldn't open Jmol viewer!", ex);
537         }
538       }
539       else
540       {
541         StringBuffer cmd = new StringBuffer();
542         cmd.append("loadingJalviewdata=true\nload APPEND ");
543         cmd.append(files.toString());
544         cmd.append("\nloadingJalviewdata=null");
545         final String command = cmd.toString();
546         cmd = null;
547         try
548         {
549           jmb.evalStateCommand(command);
550         } catch (OutOfMemoryError oomerror)
551         {
552           new OOMWarning(
553                   "When trying to add structures to the Jmol viewer!",
554                   oomerror);
555           Cache.log.debug("File locations are " + files);
556         } catch (Exception ex)
557         {
558           Cache.log.error("Couldn't add files to Jmol viewer!", ex);
559         }
560           long lastnotify=jmb.getLoadNotifiesHandled();
561           // need to wait around until script has finished
562           while (lastnotify>=jmb.getLoadNotifiesHandled()); 
563           {
564             try
565             {
566               Thread.sleep(35);
567             } catch (Exception e)
568             {
569             }
570           }
571           // refresh the sequence colours for the new structure(s)
572           jmb.updateColours(ap);
573           // do superposition if asked to
574           if (alignAddedStructures)
575           {
576             javax.swing.SwingUtilities.invokeLater(new Runnable()
577           {
578             public void run()
579             {
580               jmb.superposeStructures(ap.av.getAlignment(), -1, null);
581             }
582           });
583           alignAddedStructures = false;
584         }
585         addingStructures = false;
586       }
587     }
588     _started = false;
589     worker = null;
590   }
591
592   public void pdbFile_actionPerformed(ActionEvent actionEvent)
593   {
594     JalviewFileChooser chooser = new JalviewFileChooser(
595             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
596
597     chooser.setFileView(new JalviewFileView());
598     chooser.setDialogTitle("Save PDB File");
599     chooser.setToolTipText("Save");
600
601     int value = chooser.showSaveDialog(this);
602
603     if (value == JalviewFileChooser.APPROVE_OPTION)
604     {
605       try
606       {
607         // TODO: cope with multiple PDB files in view
608         BufferedReader in = new BufferedReader(new FileReader(
609                 jmb.getPdbFile()[0]));
610         File outFile = chooser.getSelectedFile();
611
612         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
613         String data;
614         while ((data = in.readLine()) != null)
615         {
616           if (!(data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1))
617           {
618             out.println(data);
619           }
620         }
621         out.close();
622       } catch (Exception ex)
623       {
624         ex.printStackTrace();
625       }
626     }
627   }
628
629   public void viewMapping_actionPerformed(ActionEvent actionEvent)
630   {
631     jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();
632     try
633     {
634       for (int pdbe = 0; pdbe < jmb.pdbentry.length; pdbe++)
635       {
636         cap.appendText(StructureSelectionManager
637                 .getStructureSelectionManager().printMapping(
638                         jmb.pdbentry[pdbe].getFile()));
639         cap.appendText("\n");
640       }
641     } catch (OutOfMemoryError e)
642     {
643       new OOMWarning(
644               "composing sequence-structure alignments for display in text box.",
645               e);
646       cap.dispose();
647       return;
648     }
649     jalview.gui.Desktop.addInternalFrame(cap, "PDB - Sequence Mapping",
650             550, 600);
651   }
652
653   /**
654    * DOCUMENT ME!
655    * 
656    * @param e
657    *          DOCUMENT ME!
658    */
659   public void eps_actionPerformed(ActionEvent e)
660   {
661     makePDBImage(jalview.util.ImageMaker.EPS);
662   }
663
664   /**
665    * DOCUMENT ME!
666    * 
667    * @param e
668    *          DOCUMENT ME!
669    */
670   public void png_actionPerformed(ActionEvent e)
671   {
672     makePDBImage(jalview.util.ImageMaker.PNG);
673   }
674
675   void makePDBImage(int type)
676   {
677     int width = getWidth();
678     int height = getHeight();
679
680     jalview.util.ImageMaker im;
681
682     if (type == jalview.util.ImageMaker.PNG)
683     {
684       im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.PNG,
685               "Make PNG image from view", width, height, null, null);
686     }
687     else
688     {
689       im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.EPS,
690               "Make EPS file from view", width, height, null,
691               this.getTitle());
692     }
693
694     if (im.getGraphics() != null)
695     {
696       Rectangle rect = new Rectangle(width, height);
697       jmb.viewer.renderScreenImage(im.getGraphics(), rect.getSize(), rect);
698       im.writeImage();
699     }
700   }
701
702   public void seqColour_actionPerformed(ActionEvent actionEvent)
703   {
704     jmb.setColourBySequence(seqColour.isSelected());
705     // Set the colour using the current view for the associated alignframe
706     jmb.colourBySequence(ap.alignFrame.viewport.showSequenceFeatures,
707             ap.alignFrame.viewport.alignment);
708   }
709
710   public void chainColour_actionPerformed(ActionEvent actionEvent)
711   {
712     chainColour.setSelected(true);
713     jmb.colourByChain();
714   }
715
716   public void chargeColour_actionPerformed(ActionEvent actionEvent)
717   {
718     chargeColour.setSelected(true);
719     jmb.colourByCharge();
720   }
721
722   public void zappoColour_actionPerformed(ActionEvent actionEvent)
723   {
724     zappoColour.setSelected(true);
725     jmb.setJalviewColourScheme(new ZappoColourScheme());
726   }
727
728   public void taylorColour_actionPerformed(ActionEvent actionEvent)
729   {
730     taylorColour.setSelected(true);
731     jmb.setJalviewColourScheme(new TaylorColourScheme());
732   }
733
734   public void hydroColour_actionPerformed(ActionEvent actionEvent)
735   {
736     hydroColour.setSelected(true);
737     jmb.setJalviewColourScheme(new HydrophobicColourScheme());
738   }
739
740   public void helixColour_actionPerformed(ActionEvent actionEvent)
741   {
742     helixColour.setSelected(true);
743     jmb.setJalviewColourScheme(new HelixColourScheme());
744   }
745
746   public void strandColour_actionPerformed(ActionEvent actionEvent)
747   {
748     strandColour.setSelected(true);
749     jmb.setJalviewColourScheme(new StrandColourScheme());
750   }
751
752   public void turnColour_actionPerformed(ActionEvent actionEvent)
753   {
754     turnColour.setSelected(true);
755     jmb.setJalviewColourScheme(new TurnColourScheme());
756   }
757
758   public void buriedColour_actionPerformed(ActionEvent actionEvent)
759   {
760     buriedColour.setSelected(true);
761     jmb.setJalviewColourScheme(new BuriedColourScheme());
762   }
763
764   public void userColour_actionPerformed(ActionEvent actionEvent)
765   {
766     userColour.setSelected(true);
767     new UserDefinedColours(this, null);
768   }
769
770   public void backGround_actionPerformed(ActionEvent actionEvent)
771   {
772     java.awt.Color col = JColorChooser.showDialog(this,
773             "Select Background Colour", null);
774     if (col != null)
775     {
776       jmb.setBackgroundColour(col);
777     }
778   }
779
780   public void jmolHelp_actionPerformed(ActionEvent actionEvent)
781   {
782     try
783     {
784       jalview.util.BrowserLauncher
785               .openURL("http://jmol.sourceforge.net/docs/JmolUserGuide/");
786     } catch (Exception ex)
787     {
788     }
789   }
790 public void showConsole(boolean showConsole)
791   {
792
793     if (showConsole)
794     {
795       if (splitPane == null)
796       {
797         splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
798         splitPane.setTopComponent(renderPanel);
799         splitPane.setBottomComponent(scriptWindow);
800         this.getContentPane().add(splitPane, BorderLayout.CENTER);
801         splitPane.setDividerLocation(getHeight() - 200);
802         scriptWindow.setVisible(true);
803         scriptWindow.validate();
804         splitPane.validate();
805       }
806
807     }
808     else
809     {
810       if (splitPane != null)
811         {
812           splitPane.setVisible(false);
813         }
814
815       splitPane = null;
816
817       this.getContentPane().add(renderPanel, BorderLayout.CENTER);
818     }
819
820     validate();
821   }
822   class RenderPanel extends JPanel
823   {
824     final Dimension currentSize = new Dimension();
825
826     final Rectangle rectClip = new Rectangle();
827
828     public void paintComponent(Graphics g)
829     {
830       getSize(currentSize);
831       g.getClipBounds(rectClip);
832
833       if (jmb.fileLoadingError != null)
834       {
835         g.setColor(Color.black);
836         g.fillRect(0, 0, currentSize.width, currentSize.height);
837         g.setColor(Color.white);
838         g.setFont(new Font("Verdana", Font.BOLD, 14));
839         g.drawString("Error loading file...", 20, currentSize.height / 2);
840         StringBuffer sb = new StringBuffer();
841         int lines = 0;
842         for (int e = 0; e < jmb.pdbentry.length; e++)
843         {
844           sb.append(jmb.pdbentry[e].getId());
845           if (e < jmb.pdbentry.length - 1)
846           {
847             sb.append(",");
848           }
849
850           if (e == jmb.pdbentry.length - 1 || sb.length() > 20)
851           {
852             lines++;
853             g.drawString(sb.toString(), 20, currentSize.height / 2 - lines
854                     * g.getFontMetrics().getHeight());
855           }
856         }
857       }
858       else if (jmb == null || jmb.viewer == null || !jmb.isFinishedInit())
859       {
860         g.setColor(Color.black);
861         g.fillRect(0, 0, currentSize.width, currentSize.height);
862         g.setColor(Color.white);
863         g.setFont(new Font("Verdana", Font.BOLD, 14));
864         g.drawString("Retrieving PDB data....", 20, currentSize.height / 2);
865       }
866       else
867       {
868         jmb.viewer.renderScreenImage(g, currentSize, rectClip);
869       }
870     }
871   }
872
873   String viewId = null;
874
875   public String getViewId()
876   {
877     if (viewId == null)
878     {
879       viewId = System.currentTimeMillis() + "." + this.hashCode();
880     }
881     return viewId;
882   }
883
884   public void updateTitleAndMenus()
885   {
886     if (jmb.fileLoadingError != null && jmb.fileLoadingError.length() > 0)
887     {
888       repaint();
889       return;
890     }
891     setChainMenuItems(jmb.chainNames);
892     jmb.colourBySequence(ap.av.getShowSequenceFeatures(), ap.av.alignment);
893
894     this.setTitle(jmb.getViewerTitle());
895     if (jmb.getPdbFile().length > 1 && jmb.sequence.length > 1)
896     {
897       jmolActionMenu.setVisible(true);
898     }
899   }
900
901   /*
902    * (non-Javadoc)
903    * 
904    * @see
905    * jalview.jbgui.GStructureViewer#alignStructs_actionPerformed(java.awt.event
906    * .ActionEvent)
907    */
908   @Override
909   protected void alignStructs_actionPerformed(ActionEvent actionEvent)
910   {
911
912     try
913     {
914       jmb.superposeStructures(ap.av.getAlignment(), -1,
915               ap.av.getColumnSelection());
916     } catch (Exception e)
917     {
918       Cache.log.info("Couldn't align structures in alignframe "
919               + ap.alignFrame.getTitle(), e);
920
921     }
922   }
923
924   public void setJalviewColourScheme(ColourSchemeI ucs)
925   {
926     jmb.setJalviewColourScheme(ucs);
927
928   }
929
930 }