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