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