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