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