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