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