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