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