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