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