001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 * Created on 24.05.2004
021 * @author Andreas Prlic
022 *
023 */
024package org.biojava.nbio.structure.align.gui.jmol;
025
026import org.biojava.nbio.structure.*;
027import org.biojava.nbio.structure.align.gui.AlignmentGui;
028import org.biojava.nbio.structure.align.gui.DisplayAFP;
029import org.biojava.nbio.structure.align.gui.MenuCreator;
030import org.biojava.nbio.structure.align.model.AFPChain;
031import org.biojava.nbio.structure.align.model.AfpChainWriter;
032import org.biojava.nbio.structure.align.util.AtomCache;
033import org.biojava.nbio.structure.align.util.ResourceManager;
034import org.biojava.nbio.structure.align.util.UserConfiguration;
035import org.biojava.nbio.structure.align.webstart.AligUIManager;
036import org.biojava.nbio.structure.gui.util.color.ColorUtils;
037import org.biojava.nbio.structure.jama.Matrix;
038
039import javax.swing.*;
040
041import java.awt.*;
042import java.awt.event.*;
043import java.io.StringWriter;
044import java.util.ArrayList;
045import java.util.Arrays;
046import java.util.List;
047
048/** A class that provides a simple GUI for Jmol
049 *
050 * @author Andreas Prlic
051 * @since 1.6
052 *
053 */
054public class StructureAlignmentJmol extends AbstractAlignmentJmol {
055
056        private Atom[] ca1;
057        private Atom[] ca2;
058        private AFPChain afpChain;
059
060        private static final String LIGAND_DISPLAY_SCRIPT =
061                        ResourceManager.getResourceManager("ce").
062                        getString("default.ligand.jmol.script");
063
064        public static void main(String[] args){
065
066                try {
067
068                        UserConfiguration config = new UserConfiguration();
069                        AtomCache cache = new AtomCache(config);
070
071                        Structure struc = cache.getStructure("5pti");
072
073                        StructureAlignmentJmol jmolPanel =
074                                        new StructureAlignmentJmol(null,null,null);
075
076                        jmolPanel.setStructure(struc);
077
078                        // send some RASMOL style commands to Jmol
079                        jmolPanel.evalString("select * ; color chain;");
080                        jmolPanel.evalString("select *; spacefill off; wireframe off; backbone 0.4;  ");
081
082                } catch (Exception e){
083                        e.printStackTrace();
084                }
085        }
086
087        public StructureAlignmentJmol(){
088                // don;t have an afpChain, set it to null...
089                this(null, null, null);
090        }
091
092        public StructureAlignmentJmol(AFPChain afpChain, Atom[] ca1, Atom[] ca2) {
093
094                AligUIManager.setLookAndFeel();
095
096                nrOpenWindows++;
097                jmolPanel = new JmolPanel();
098
099                frame = new JFrame();
100
101                JMenuBar menu = MenuCreator.initJmolMenu(frame,this, afpChain, null);
102
103                frame.setJMenuBar(menu);
104                //frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
105                this.afpChain = afpChain;
106                this.ca1 = ca1;
107                this.ca2 = ca2;
108
109                frame.addWindowListener( new WindowAdapter()
110                {
111
112                        @Override
113                        public void windowClosing(WindowEvent e) {
114
115                                nrOpenWindows--;
116
117                                destroy();
118
119                                if ( nrOpenWindows > 0) {
120
121                                        frame.dispose();
122                                }
123                                else  {
124                                        // check if AlignmentGUI is visible..
125
126                                        AlignmentGui gui = AlignmentGui.getInstanceNoVisibilityChange();
127                                        if ( gui.isVisible()) {
128                                                frame.dispose();
129                                                gui.requestFocus();
130                                        } else {
131                                                System.exit(0);
132                                        }
133                                }
134                        }
135                });
136
137                Container contentPane = frame.getContentPane();
138
139                Box vBox = Box.createVerticalBox();
140
141                //try {
142
143
144
145                jmolPanel.addMouseMotionListener(this);
146                jmolPanel.addMouseListener(this);
147
148                //              } catch (ClassNotFoundException e){
149                //                      e.printStackTrace();
150                //                      System.err.println("Could not find Jmol in classpath, please install first. http://www.jmol.org");
151                //                      return;
152                //              }
153                jmolPanel.setPreferredSize(new Dimension(DEFAULT_WIDTH,DEFAULT_HEIGHT));
154                vBox.add(jmolPanel);
155
156
157                /// USER SCRIPTING COMMAND
158                JTextField field = new JTextField();
159
160                field.setMaximumSize(new Dimension(Short.MAX_VALUE,30));
161                field.setText(COMMAND_LINE_HELP);
162                RasmolCommandListener listener = new RasmolCommandListener(jmolPanel,field) ;
163
164                field.addActionListener(listener);
165                field.addMouseListener(listener);
166                field.addKeyListener(listener);
167                vBox.add(field);
168
169
170                /// COMBO BOXES
171                Box hBox1 = Box.createHorizontalBox();
172                hBox1.add(Box.createGlue());
173
174                String[] styles = new String[] { "Cartoon", "Backbone", "CPK", "Ball and Stick", "Ligands","Ligands and Pocket"};
175                JComboBox style = new JComboBox(styles);
176
177                hBox1.setMaximumSize(new Dimension(Short.MAX_VALUE,30));
178
179                hBox1.add(new JLabel("Style"));
180                hBox1.add(style);
181                vBox.add(hBox1);
182                contentPane.add(vBox);
183
184                style.addActionListener(jmolPanel);
185
186                String[] colorModes = new String[] { "Secondary Structure", "By Chain", "Rainbow", "By Element", "By Amino Acid", "Hydrophobicity" ,"Suggest Domains" , "Show SCOP Domains"};
187                JComboBox colors = new JComboBox(colorModes);
188                colors.addActionListener(jmolPanel);
189                hBox1.add(Box.createGlue());
190                hBox1.add(new JLabel("Color"));
191                hBox1.add(colors);
192
193
194                // CHeck boxes
195                Box hBox2 = Box.createHorizontalBox();
196                hBox2.setMaximumSize(new Dimension(Short.MAX_VALUE,30));
197
198                JButton resetDisplay = new JButton("Reset Display");
199
200                resetDisplay.addActionListener(new ActionListener() {
201
202                        @Override
203                        public void actionPerformed(ActionEvent e) {
204                                System.out.println("reset!!");
205                                jmolPanel.executeCmd("restore STATE state_1");
206
207                        }
208                });
209
210                hBox2.add(resetDisplay);
211                hBox2.add(Box.createGlue());
212
213
214                JCheckBox toggleSelection = new JCheckBox("Show Selection");
215                toggleSelection.addItemListener(
216                                new ItemListener() {
217
218                                        @Override
219                                        public void itemStateChanged(ItemEvent e) {
220                                                boolean showSelection = (e.getStateChange() == ItemEvent.SELECTED);
221
222                                                if (showSelection){
223                                                        jmolPanel.executeCmd("set display selected");
224                                                } else {
225                                                        jmolPanel.executeCmd("set display off");
226                                                }
227
228                                        }
229                                }
230                                );
231
232
233
234                hBox2.add(toggleSelection);
235
236                hBox2.add(Box.createGlue());
237                vBox.add(hBox2);
238
239
240                // STATUS DISPLAY
241
242                Box hBox = Box.createHorizontalBox();
243
244                status = new JTextField();
245                status.setBackground(Color.white);
246                status.setEditable(false);
247                status.setMaximumSize(new Dimension(Short.MAX_VALUE,30));
248                status.setPreferredSize(new Dimension(DEFAULT_WIDTH / 2,30));
249                status.setMinimumSize(new Dimension(DEFAULT_WIDTH / 2,30));
250                hBox.add(status);
251                text = new JTextField();
252                text.setBackground(Color.white);
253                text.setMaximumSize(new Dimension(Short.MAX_VALUE,30));
254                text.setPreferredSize(new Dimension(DEFAULT_WIDTH / 2,30));
255                text.setMinimumSize(new Dimension(DEFAULT_WIDTH / 2,30));
256                text.setText("Display of Atom info");
257                text.setEditable(false);
258                hBox.add(text);
259
260                vBox.add(hBox);
261
262
263
264                contentPane.add(vBox);
265                MyJmolStatusListener li = (MyJmolStatusListener) jmolPanel.getStatusListener();
266                li.setTextField(status);
267                frame.pack();
268                frame.setVisible(true);
269
270
271                // init coordinates
272
273                initCoords();
274
275                resetDisplay();
276
277        }
278        @Override
279        protected void initCoords(){
280                try {
281                        if ( ca1 == null || ca2 == null ){
282                                if ( structure != null)
283                                        setStructure(structure);
284                                else  {
285                                        //System.err.println("could not find anything to display!");
286                                        return;
287                                }
288                        }
289                        Structure artificial = DisplayAFP.getAlignedStructure(ca1,ca2);
290                        PDBHeader header = new PDBHeader();
291                        String title =  afpChain.getAlgorithmName() + " V." +afpChain.getVersion() + " : " + afpChain.getName1() + " vs. " + afpChain.getName2();
292                        header.setTitle(title);
293                        artificial.setPDBHeader(header);
294                        setStructure(artificial);
295                } catch (StructureException e){
296                        e.printStackTrace();
297                }
298        }
299
300        @Override
301        public void destroy(){
302                super.destroy();
303                afpChain =null;
304                ca1 = null;
305                ca2 = null;
306        }
307
308        @Override
309        public void actionPerformed(ActionEvent e) {
310                String cmd = e.getActionCommand();
311                if ( cmd.equals(MenuCreator.TEXT_ONLY)) {
312                        if ( afpChain == null) {
313                                System.err.println("Currently not viewing an alignment!");
314                                return;
315                        }
316                        //Clone the AFPChain to not override the FatCat numbers in alnsymb
317                        AFPChain textAFP = (AFPChain) afpChain.clone();
318                        String result = AfpChainWriter.toWebSiteDisplay(textAFP, ca1, ca2) ;
319
320                        DisplayAFP.showAlignmentImage(afpChain, result);
321
322                } else if ( cmd.equals(MenuCreator.PAIRS_ONLY)) {
323                        if ( afpChain == null) {
324                                System.err.println("Currently not viewing an alignment!");
325                                return;
326                        }
327                        String result = AfpChainWriter.toAlignedPairs(afpChain, ca1, ca2) ;
328
329                        DisplayAFP.showAlignmentImage(afpChain, result);
330
331                } else if (cmd.equals(MenuCreator.ALIGNMENT_PANEL)){
332                        if ( afpChain == null) {
333                                System.err.println("Currently not viewing an alignment!");
334                                return;
335                        }
336                        try {
337                                DisplayAFP.showAlignmentPanel(afpChain, ca1, ca2, this);
338                        } catch (Exception e1) {
339                                e1.printStackTrace();
340                                return;
341                        }
342
343                } else if (cmd.equals(MenuCreator.FATCAT_TEXT)){
344                        if ( afpChain == null) {
345                                System.err.println("Currently not viewing an alignment!");
346                                return;
347                        }
348                        String result = afpChain.toFatcat(ca1, ca2) ;
349                        result += AFPChain.newline;
350                        result += afpChain.toRotMat();
351                        DisplayAFP.showAlignmentImage(afpChain, result);
352                }
353        }
354
355        public static String getJmolString(AFPChain afpChain, Atom[] ca1, Atom[] ca2){
356
357                if ( afpChain.getBlockNum() > 1){
358                        return getMultiBlockJmolScript( afpChain,  ca1,  ca2);
359                }
360
361
362
363                StringBuffer j = new StringBuffer();
364                j.append(DEFAULT_SCRIPT);
365
366
367                // now color the equivalent residues ...
368                StringBuffer sel = new StringBuffer();
369                List<String> pdb1 = DisplayAFP.getPDBresnum(0,afpChain,ca1);
370                sel.append("select ");
371                int pos = 0;
372                for (String res :pdb1 ){
373                        if ( pos > 0)
374                                sel.append(",");
375                        pos++;
376
377                        sel.append(res);
378                        sel.append("/1");
379                }
380                if ( pos == 0)
381                        sel.append("none");
382                sel.append(";");
383                sel.append("backbone 0.6 ;   color orange;");
384                sel.append("select */2; color lightgrey; model 2; ");
385                //jmol.evalString("select */2; color lightgrey; model 2; ");
386                List<String> pdb2 = DisplayAFP.getPDBresnum(1,afpChain,ca2);
387                sel.append("select ");
388                pos = 0;
389                for (String res :pdb2 ){
390                        if ( pos > 0)
391                                sel.append(",");
392                        pos++;
393
394                        sel.append(res);
395                        sel.append("/2");
396                }
397                if ( pos == 0)
398                        sel.append("none");
399                sel.append("; backbone 0.6 ;   color cyan;");
400                //System.out.println(sel);
401                j.append(sel);
402                // now show both models again.
403                j.append("model 0;  ");
404                j.append(LIGAND_DISPLAY_SCRIPT);
405                //color [object] cpk , set defaultColors Jmol , set defaultColors Rasmol
406
407                // and now select the aligned residues...
408                StringBuffer buf = new StringBuffer("select ");
409                int count = 0;
410                for (String res : pdb1 ){
411                        if ( count > 0)
412                                buf.append(",");
413                        buf.append(res);
414                        buf.append("/1");
415                        count++;
416                }
417
418                for (String res :pdb2 ){
419                        buf.append(",");
420                        buf.append(res);
421                        buf.append("/2");
422                }
423                //buf.append("; set display selected;");
424
425                j.append(buf);
426
427                return j.toString();
428        }
429
430        public static String getJmolScript4Block(AFPChain afpChain, Atom[] ca1, Atom[] ca2, int blockNr){
431                int blockNum = afpChain.getBlockNum();
432
433                if ( blockNr >= blockNum)
434                        return DEFAULT_SCRIPT;
435
436                int[] optLen = afpChain.getOptLen();
437                int[][][] optAln = afpChain.getOptAln();
438
439                if ( optLen == null)
440                        return DEFAULT_SCRIPT;
441
442                StringWriter jmol = new StringWriter();
443                jmol.append(DEFAULT_SCRIPT);
444
445                jmol.append("select */2; color lightgrey; model 2; ");
446
447                printJmolScript4Block(ca1, ca2, blockNum, optLen, optAln, jmol, blockNr);
448
449                jmol.append("model 0;  ");
450                jmol.append(LIGAND_DISPLAY_SCRIPT);
451                //System.out.println(jmol);
452                return jmol.toString();
453
454
455        }
456
457
458        private static String getMultiBlockJmolScript(AFPChain afpChain, Atom[] ca1, Atom[] ca2) {
459
460                int blockNum = afpChain.getBlockNum();
461                int[] optLen = afpChain.getOptLen();
462                int[][][] optAln = afpChain.getOptAln();
463
464                if ( optLen == null)
465                        return DEFAULT_SCRIPT;
466
467                StringWriter jmol = new StringWriter();
468                jmol.append(DEFAULT_SCRIPT);
469
470                jmol.append("select */2; color lightgrey; model 2; ");
471
472                for(int bk = 0; bk < blockNum; bk ++)       {
473
474                        printJmolScript4Block(ca1, ca2, blockNum, optLen, optAln, jmol, bk);
475                }
476                jmol.append("model 0;  ");
477
478                jmol.append(LIGAND_DISPLAY_SCRIPT);
479                //System.out.println(jmol);
480                return jmol.toString();
481
482
483        }
484
485        private static void printJmolScript4Block(Atom[] ca1, Atom[] ca2, int blockNum,
486                        int[] optLen, int[][][] optAln, StringWriter jmol, int bk) {
487                //the block nr determines the color...
488                int colorPos = bk;
489
490                Color c1;
491                Color c2;
492                //If the colors for the block are specified in AFPChain use them, otherwise the default ones are calculated
493
494                if ( colorPos > ColorUtils.colorWheel.length){
495                        colorPos = ColorUtils.colorWheel.length % colorPos ;
496                }
497
498                Color end1 = ColorUtils.rotateHue(ColorUtils.orange,  (1.0f  / 24.0f) * blockNum  );
499                Color end2 = ColorUtils.rotateHue(ColorUtils.cyan,    (1.0f  / 24.0f) * (blockNum +1)  ) ;
500
501                c1   = ColorUtils.getIntermediate(ColorUtils.orange, end1, blockNum, bk);
502                c2   = ColorUtils.getIntermediate(ColorUtils.cyan, end2, blockNum, bk);
503
504                List<String> pdb1 = new ArrayList<String>();
505                List<String> pdb2 = new ArrayList<String>();
506                for ( int i=0;i< optLen[bk];i++) {
507                        ///
508                        int pos1 = optAln[bk][0][i];
509                        pdb1.add(JmolTools.getPdbInfo(ca1[pos1]));
510                        int pos2 = optAln[bk][1][i];
511                        pdb2.add(JmolTools.getPdbInfo(ca2[pos2]));
512                }
513
514                // and now select the aligned residues...
515                StringBuffer buf = new StringBuffer("select ");
516                int count = 0;
517                for (String res : pdb1 ){
518                        if ( count > 0)
519                                buf.append(",");
520                        buf.append(res);
521                        buf.append("/1");
522                        count++;
523                }
524
525                buf.append("; backbone 0.6 ; color [" + c1.getRed() +"," + c1.getGreen() +"," +c1.getBlue()+"]; select ");
526
527                count = 0;
528                for (String res :pdb2 ){
529                        if ( count > 0)
530                                buf.append(",");
531
532                        buf.append(res);
533                        buf.append("/2");
534                        count++;
535                }
536                //buf.append("; set display selected;");
537
538                buf.append("; backbone 0.6 ; color [" + c2.getRed() +"," + c2.getGreen() +"," +c2.getBlue()+"];");
539
540                // now color this block:
541                jmol.append(buf);
542        }
543
544        @Override
545        public void resetDisplay(){
546
547                if (afpChain != null && ca1 != null && ca2 != null) {
548                        String script = getJmolString( afpChain,ca1,ca2);
549                        //System.out.println(script);
550                        evalString(script);
551                        jmolPanel.evalString("save STATE state_1");
552                }
553        }
554
555        @Override
556        public List<Matrix> getDistanceMatrices() {
557                if (afpChain == null) return null;
558                else return Arrays.asList(afpChain.getDisTable1(), afpChain.getDisTable2());
559        }
560}