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