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