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