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