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