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