patched Jmol and code for embedding JmolAppConsole in a JPanel (JAL-638)
[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     };
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     // TODO: check for memory leaks where instance isn't finalised because jmb
418     // holds a reference to the window
419     jmb = null;
420   }
421
422   /**
423    * state flag for PDB retrieval thread
424    */
425   private boolean _started = false;
426
427   public void run()
428   {
429     _started = true;
430     String pdbid = "";
431     // todo - record which pdbids were successfuly imported.
432     StringBuffer errormsgs = new StringBuffer(), files = new StringBuffer();
433     try
434     {
435       String[] curfiles = jmb.getPdbFile(); // files currently in viewer
436       // TODO: replace with reference fetching/transfer code (validate PDBentry
437       // as a DBRef?)
438       jalview.ws.dbsources.Pdb pdbclient = new jalview.ws.dbsources.Pdb();
439       for (int pi = 0; pi < jmb.pdbentry.length; pi++)
440       {
441         String file = jmb.pdbentry[pi].getFile();
442         if (file == null)
443         {
444           // retrieve the pdb and store it locally
445           AlignmentI pdbseq = null;
446           pdbid = jmb.pdbentry[pi].getId();
447           long hdl=pdbid.hashCode()-System.currentTimeMillis();
448           if (progressBar != null)
449           {
450             progressBar.setProgressBar("Fetching PDB " + pdbid,
451                     hdl);
452           }
453           try
454           {
455             pdbseq = pdbclient.getSequenceRecords(pdbid = jmb.pdbentry[pi]
456                     .getId());
457           } catch (OutOfMemoryError oomerror)
458           {
459             new OOMWarning("Retrieving PDB id " + pdbid, oomerror);
460           } catch (Exception ex)
461           {
462             ex.printStackTrace();
463             errormsgs.append("'" + pdbid + "'");
464           }
465           if (progressBar != null)
466           {
467             progressBar.setProgressBar("Finished.", hdl);
468           }
469           if (pdbseq != null)
470           {
471             // just transfer the file name from the first sequence's first
472             // PDBEntry
473             jmb.pdbentry[pi].setFile(file = ((PDBEntry) pdbseq
474                     .getSequenceAt(0).getPDBId().elementAt(0)).getFile());
475             files.append(" \"" + file + "\"");
476           }
477           else
478           {
479             errormsgs.append("'" + pdbid + "' ");
480           }
481         }
482         else
483         {
484           if (curfiles != null && curfiles.length > 0)
485           {
486             addingStructures = true; // already files loaded.
487             for (int c = 0; c < curfiles.length; c++)
488             {
489               if (curfiles[c].equals(file))
490               {
491                 file = null;
492                 break;
493               }
494             }
495           }
496           if (file != null)
497           {
498             files.append(" \"" + file + "\"");
499           }
500         }
501       }
502     } catch (OutOfMemoryError oomerror)
503     {
504       new OOMWarning("Retrieving PDB files: " + pdbid, oomerror);
505     } catch (Exception ex)
506     {
507       ex.printStackTrace();
508       errormsgs.append("When retrieving pdbfiles : current was: '" + pdbid
509               + "'");
510     }
511     if (errormsgs.length() > 0)
512     {
513
514       JOptionPane.showInternalMessageDialog(Desktop.desktop,
515               "The following pdb entries could not be retrieved from the PDB:\n"
516                       + errormsgs.toString()
517                       + "\nPlease try downloading them manually.",
518               "Couldn't load file", JOptionPane.ERROR_MESSAGE);
519
520     }
521     if (files.length() > 0)
522     {
523       if (!addingStructures)
524       {
525
526         try
527         {
528           initJmol("load FILES " + files.toString());
529         } catch (OutOfMemoryError oomerror)
530         {
531           new OOMWarning("When trying to open the Jmol viewer!", oomerror);
532           Cache.log.debug("File locations are " + files);
533         } catch (Exception ex)
534         {
535           Cache.log.error("Couldn't open Jmol viewer!", ex);
536         }
537       }
538       else
539       {
540         StringBuffer cmd = new StringBuffer();
541         cmd.append("loadingJalviewdata=true\nload APPEND ");
542         cmd.append(files.toString());
543         cmd.append("\nloadingJalviewdata=null");
544         final String command = cmd.toString();
545         cmd = null;
546         try
547         {
548           jmb.evalStateCommand(command);
549         } catch (OutOfMemoryError oomerror)
550         {
551           new OOMWarning(
552                   "When trying to add structures to the Jmol viewer!",
553                   oomerror);
554           Cache.log.debug("File locations are " + files);
555         } catch (Exception ex)
556         {
557           Cache.log.error("Couldn't add files to Jmol viewer!", ex);
558         }
559           long lastnotify=jmb.getLoadNotifiesHandled();
560           // need to wait around until script has finished
561           while (lastnotify>=jmb.getLoadNotifiesHandled()); 
562           {
563             try
564             {
565               Thread.sleep(35);
566             } catch (Exception e)
567             {
568             }
569           }
570           // refresh the sequence colours for the new structure(s)
571           jmb.updateColours(ap);
572           // do superposition if asked to
573           if (alignAddedStructures)
574           {
575             javax.swing.SwingUtilities.invokeLater(new Runnable()
576           {
577             public void run()
578             {
579               jmb.superposeStructures(ap.av.getAlignment(), -1, null);
580             }
581           });
582           alignAddedStructures = false;
583         }
584         addingStructures = false;
585       }
586     }
587     _started = false;
588     worker = null;
589   }
590
591   public void pdbFile_actionPerformed(ActionEvent actionEvent)
592   {
593     JalviewFileChooser chooser = new JalviewFileChooser(
594             jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
595
596     chooser.setFileView(new JalviewFileView());
597     chooser.setDialogTitle("Save PDB File");
598     chooser.setToolTipText("Save");
599
600     int value = chooser.showSaveDialog(this);
601
602     if (value == JalviewFileChooser.APPROVE_OPTION)
603     {
604       try
605       {
606         // TODO: cope with multiple PDB files in view
607         BufferedReader in = new BufferedReader(new FileReader(
608                 jmb.getPdbFile()[0]));
609         File outFile = chooser.getSelectedFile();
610
611         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
612         String data;
613         while ((data = in.readLine()) != null)
614         {
615           if (!(data.indexOf("<PRE>") > -1 || data.indexOf("</PRE>") > -1))
616           {
617             out.println(data);
618           }
619         }
620         out.close();
621       } catch (Exception ex)
622       {
623         ex.printStackTrace();
624       }
625     }
626   }
627
628   public void viewMapping_actionPerformed(ActionEvent actionEvent)
629   {
630     jalview.gui.CutAndPasteTransfer cap = new jalview.gui.CutAndPasteTransfer();
631     try
632     {
633       for (int pdbe = 0; pdbe < jmb.pdbentry.length; pdbe++)
634       {
635         cap.appendText(StructureSelectionManager
636                 .getStructureSelectionManager().printMapping(
637                         jmb.pdbentry[pdbe].getFile()));
638         cap.appendText("\n");
639       }
640     } catch (OutOfMemoryError e)
641     {
642       new OOMWarning(
643               "composing sequence-structure alignments for display in text box.",
644               e);
645       cap.dispose();
646       return;
647     }
648     jalview.gui.Desktop.addInternalFrame(cap, "PDB - Sequence Mapping",
649             550, 600);
650   }
651
652   /**
653    * DOCUMENT ME!
654    * 
655    * @param e
656    *          DOCUMENT ME!
657    */
658   public void eps_actionPerformed(ActionEvent e)
659   {
660     makePDBImage(jalview.util.ImageMaker.EPS);
661   }
662
663   /**
664    * DOCUMENT ME!
665    * 
666    * @param e
667    *          DOCUMENT ME!
668    */
669   public void png_actionPerformed(ActionEvent e)
670   {
671     makePDBImage(jalview.util.ImageMaker.PNG);
672   }
673
674   void makePDBImage(int type)
675   {
676     int width = getWidth();
677     int height = getHeight();
678
679     jalview.util.ImageMaker im;
680
681     if (type == jalview.util.ImageMaker.PNG)
682     {
683       im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.PNG,
684               "Make PNG image from view", width, height, null, null);
685     }
686     else
687     {
688       im = new jalview.util.ImageMaker(this, jalview.util.ImageMaker.EPS,
689               "Make EPS file from view", width, height, null,
690               this.getTitle());
691     }
692
693     if (im.getGraphics() != null)
694     {
695       Rectangle rect = new Rectangle(width, height);
696       jmb.viewer.renderScreenImage(im.getGraphics(), rect.getSize(), rect);
697       im.writeImage();
698     }
699   }
700
701   public void seqColour_actionPerformed(ActionEvent actionEvent)
702   {
703     jmb.setColourBySequence(seqColour.isSelected());
704     // Set the colour using the current view for the associated alignframe
705     jmb.colourBySequence(ap.alignFrame.viewport.showSequenceFeatures,
706             ap.alignFrame.viewport.alignment);
707   }
708
709   public void chainColour_actionPerformed(ActionEvent actionEvent)
710   {
711     chainColour.setSelected(true);
712     jmb.colourByChain();
713   }
714
715   public void chargeColour_actionPerformed(ActionEvent actionEvent)
716   {
717     chargeColour.setSelected(true);
718     jmb.colourByCharge();
719   }
720
721   public void zappoColour_actionPerformed(ActionEvent actionEvent)
722   {
723     zappoColour.setSelected(true);
724     jmb.setJalviewColourScheme(new ZappoColourScheme());
725   }
726
727   public void taylorColour_actionPerformed(ActionEvent actionEvent)
728   {
729     taylorColour.setSelected(true);
730     jmb.setJalviewColourScheme(new TaylorColourScheme());
731   }
732
733   public void hydroColour_actionPerformed(ActionEvent actionEvent)
734   {
735     hydroColour.setSelected(true);
736     jmb.setJalviewColourScheme(new HydrophobicColourScheme());
737   }
738
739   public void helixColour_actionPerformed(ActionEvent actionEvent)
740   {
741     helixColour.setSelected(true);
742     jmb.setJalviewColourScheme(new HelixColourScheme());
743   }
744
745   public void strandColour_actionPerformed(ActionEvent actionEvent)
746   {
747     strandColour.setSelected(true);
748     jmb.setJalviewColourScheme(new StrandColourScheme());
749   }
750
751   public void turnColour_actionPerformed(ActionEvent actionEvent)
752   {
753     turnColour.setSelected(true);
754     jmb.setJalviewColourScheme(new TurnColourScheme());
755   }
756
757   public void buriedColour_actionPerformed(ActionEvent actionEvent)
758   {
759     buriedColour.setSelected(true);
760     jmb.setJalviewColourScheme(new BuriedColourScheme());
761   }
762
763   public void userColour_actionPerformed(ActionEvent actionEvent)
764   {
765     userColour.setSelected(true);
766     new UserDefinedColours(this, null);
767   }
768
769   public void backGround_actionPerformed(ActionEvent actionEvent)
770   {
771     java.awt.Color col = JColorChooser.showDialog(this,
772             "Select Background Colour", null);
773     if (col != null)
774     {
775       jmb.setBackgroundColour(col);
776     }
777   }
778
779   public void jmolHelp_actionPerformed(ActionEvent actionEvent)
780   {
781     try
782     {
783       jalview.util.BrowserLauncher
784               .openURL("http://jmol.sourceforge.net/docs/JmolUserGuide/");
785     } catch (Exception ex)
786     {
787     }
788   }
789   public void showConsole(boolean showConsole)
790   {
791
792     if (showConsole)
793     {
794       if (splitPane == null)
795       {
796         splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
797         splitPane.setTopComponent(renderPanel);
798         splitPane.setBottomComponent(scriptWindow);
799         this.getContentPane().add(splitPane, BorderLayout.CENTER);
800         splitPane.setDividerLocation(getHeight() - 200);
801         scriptWindow.setVisible(true);
802         scriptWindow.validate();
803         splitPane.validate();
804       }
805
806     }
807     else
808     {
809       if (splitPane != null)
810         {
811           splitPane.setVisible(false);
812         }
813
814       splitPane = null;
815
816       this.getContentPane().add(renderPanel, BorderLayout.CENTER);
817     }
818
819     validate();
820   }
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 }