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