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