JAL-2089 patch broken merge to master for Release 2.10.0b1
[jalview.git] / src / MCview / AppletPDBCanvas.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package MCview;
22
23 import jalview.analysis.AlignSeq;
24 import jalview.appletgui.AlignmentPanel;
25 import jalview.appletgui.FeatureRenderer;
26 import jalview.appletgui.SequenceRenderer;
27 import jalview.datamodel.PDBEntry;
28 import jalview.datamodel.SequenceI;
29 import jalview.io.StructureFile;
30 import jalview.structure.AtomSpec;
31 import jalview.structure.StructureListener;
32 import jalview.structure.StructureMapping;
33 import jalview.structure.StructureSelectionManager;
34 import jalview.util.MessageManager;
35
36 import java.awt.Color;
37 import java.awt.Dimension;
38 import java.awt.Event;
39 import java.awt.Font;
40 import java.awt.Graphics;
41 import java.awt.Image;
42 // JBPNote TODO: This class is quite noisy - needs proper log.info/log.debug
43 import java.awt.Panel;
44 import java.awt.event.KeyAdapter;
45 import java.awt.event.KeyEvent;
46 import java.awt.event.MouseEvent;
47 import java.awt.event.MouseListener;
48 import java.awt.event.MouseMotionListener;
49 import java.io.PrintStream;
50 import java.util.List;
51 import java.util.Vector;
52
53 public class AppletPDBCanvas extends Panel implements MouseListener,
54         MouseMotionListener, StructureListener
55 {
56
57   MCMatrix idmat = new MCMatrix(3, 3);
58
59   MCMatrix objmat = new MCMatrix(3, 3);
60
61   boolean redrawneeded = true;
62
63   int omx = 0;
64
65   int mx = 0;
66
67   int omy = 0;
68
69   int my = 0;
70
71   public StructureFile pdb;
72
73   PDBEntry pdbentry;
74
75   int bsize;
76
77   Image img;
78
79   Graphics ig;
80
81   Dimension prefsize;
82
83   float[] centre = new float[3];
84
85   float[] width = new float[3];
86
87   float maxwidth;
88
89   float scale;
90
91   String inStr;
92
93   String inType;
94
95   boolean bysequence = true;
96
97   boolean depthcue = true;
98
99   boolean wire = false;
100
101   boolean bymolecule = false;
102
103   boolean zbuffer = true;
104
105   boolean dragging;
106
107   int xstart;
108
109   int xend;
110
111   int ystart;
112
113   int yend;
114
115   int xmid;
116
117   int ymid;
118
119   Font font = new Font("Helvetica", Font.PLAIN, 10);
120
121   public SequenceI[] sequence;
122
123   final StringBuffer mappingDetails = new StringBuffer();
124
125   String appletToolTip = null;
126
127   int toolx, tooly;
128
129   PDBChain mainchain;
130
131   Vector<String> highlightRes;
132
133   boolean pdbAction = false;
134
135   Bond highlightBond1, highlightBond2;
136
137   boolean errorLoading = false;
138
139   boolean seqColoursReady = false;
140
141   FeatureRenderer fr;
142
143   AlignmentPanel ap;
144
145   StructureSelectionManager ssm;
146
147   public AppletPDBCanvas(PDBEntry pdbentry, SequenceI[] seq,
148           String[] chains, AlignmentPanel ap, String protocol)
149
150   {
151     this.ap = ap;
152     this.pdbentry = pdbentry;
153     this.sequence = seq;
154
155     ssm = StructureSelectionManager
156             .getStructureSelectionManager(ap.av.applet);
157
158     try
159     {
160       pdb = ssm.setMapping(seq, chains, pdbentry.getFile(), protocol);
161
162       if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
163       {
164         pdbentry.setFile("INLINE" + pdb.getId());
165       }
166
167     } catch (Exception ex)
168     {
169       ex.printStackTrace();
170       return;
171     }
172
173     pdbentry.setId(pdb.getId());
174
175     ssm.addStructureViewerListener(this);
176
177     colourBySequence();
178
179     int max = -10;
180     int maxchain = -1;
181     int pdbstart = 0;
182     int pdbend = 0;
183     int seqstart = 0;
184     int seqend = 0;
185
186     // JUST DEAL WITH ONE SEQUENCE FOR NOW
187     SequenceI sequence = seq[0];
188
189     for (int i = 0; i < pdb.getChains().size(); i++)
190     {
191
192       mappingDetails
193               .append("\n\nPDB Sequence is :\nSequence = "
194                       + pdb.getChains().elementAt(i).sequence
195                               .getSequenceAsString());
196       mappingDetails.append("\nNo of residues = "
197               + pdb.getChains().elementAt(i).residues.size() + "\n\n");
198
199       // Now lets compare the sequences to get
200       // the start and end points.
201       // Align the sequence to the pdb
202       // TODO: DNa/Pep switch
203       AlignSeq as = new AlignSeq(sequence,
204               pdb.getChains().elementAt(i).sequence, pdb.getChains()
205                       .elementAt(i).isNa ? AlignSeq.DNA : AlignSeq.PEP);
206       as.calcScoreMatrix();
207       as.traceAlignment();
208       PrintStream ps = new PrintStream(System.out)
209       {
210         @Override
211         public void print(String x)
212         {
213           mappingDetails.append(x);
214         }
215
216         @Override
217         public void println()
218         {
219           mappingDetails.append("\n");
220         }
221       };
222
223       as.printAlignment(ps);
224
225       if (as.maxscore > max)
226       {
227         max = as.maxscore;
228         maxchain = i;
229
230         pdbstart = as.seq2start;
231         pdbend = as.seq2end;
232         seqstart = as.seq1start + sequence.getStart() - 1;
233         seqend = as.seq1end + sequence.getEnd() - 1;
234       }
235
236       mappingDetails.append("\nPDB start/end " + pdbstart + " " + pdbend);
237       mappingDetails.append("\nSEQ start/end " + seqstart + " " + seqend);
238     }
239
240     mainchain = pdb.getChains().elementAt(maxchain);
241
242     mainchain.pdbstart = pdbstart;
243     mainchain.pdbend = pdbend;
244     mainchain.seqstart = seqstart;
245     mainchain.seqend = seqend;
246     mainchain.isVisible = true;
247     // mainchain.makeExactMapping(maxAlignseq, sequence);
248     // mainchain.transferRESNUMFeatures(sequence, null);
249     this.pdb = pdb;
250     this.prefsize = new Dimension(getSize().width, getSize().height);
251
252     // Initialize the matrices to identity
253     for (int i = 0; i < 3; i++)
254     {
255       for (int j = 0; j < 3; j++)
256       {
257         if (i != j)
258         {
259           idmat.addElement(i, j, 0);
260           objmat.addElement(i, j, 0);
261         }
262         else
263         {
264           idmat.addElement(i, j, 1);
265           objmat.addElement(i, j, 1);
266         }
267       }
268     }
269
270     addMouseMotionListener(this);
271     addMouseListener(this);
272
273     addKeyListener(new KeyAdapter()
274     {
275       @Override
276       public void keyPressed(KeyEvent evt)
277       {
278         doKeyPressed(evt);
279       }
280     });
281
282     findCentre();
283     findWidth();
284
285     setupBonds();
286
287     scale = findScale();
288   }
289
290   Vector<Bond> visiblebonds;
291
292   void setupBonds()
293   {
294     seqColoursReady = false;
295     // Sort the bonds by z coord
296     visiblebonds = new Vector<Bond>();
297
298     for (int ii = 0; ii < pdb.getChains().size(); ii++)
299     {
300       if (pdb.getChains().elementAt(ii).isVisible)
301       {
302         Vector<Bond> tmp = pdb.getChains().elementAt(ii).bonds;
303
304         for (int i = 0; i < tmp.size(); i++)
305         {
306           visiblebonds.addElement(tmp.elementAt(i));
307         }
308       }
309     }
310     seqColoursReady = true;
311     colourBySequence();
312     redrawneeded = true;
313     repaint();
314   }
315
316   public void findWidth()
317   {
318     float[] max = new float[3];
319     float[] min = new float[3];
320
321     max[0] = (float) -1e30;
322     max[1] = (float) -1e30;
323     max[2] = (float) -1e30;
324
325     min[0] = (float) 1e30;
326     min[1] = (float) 1e30;
327     min[2] = (float) 1e30;
328
329     for (int ii = 0; ii < pdb.getChains().size(); ii++)
330     {
331       if (pdb.getChains().elementAt(ii).isVisible)
332       {
333         Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
334
335         for (Bond tmp : bonds)
336         {
337           if (tmp.start[0] >= max[0])
338           {
339             max[0] = tmp.start[0];
340           }
341
342           if (tmp.start[1] >= max[1])
343           {
344             max[1] = tmp.start[1];
345           }
346
347           if (tmp.start[2] >= max[2])
348           {
349             max[2] = tmp.start[2];
350           }
351
352           if (tmp.start[0] <= min[0])
353           {
354             min[0] = tmp.start[0];
355           }
356
357           if (tmp.start[1] <= min[1])
358           {
359             min[1] = tmp.start[1];
360           }
361
362           if (tmp.start[2] <= min[2])
363           {
364             min[2] = tmp.start[2];
365           }
366
367           if (tmp.end[0] >= max[0])
368           {
369             max[0] = tmp.end[0];
370           }
371
372           if (tmp.end[1] >= max[1])
373           {
374             max[1] = tmp.end[1];
375           }
376
377           if (tmp.end[2] >= max[2])
378           {
379             max[2] = tmp.end[2];
380           }
381
382           if (tmp.end[0] <= min[0])
383           {
384             min[0] = tmp.end[0];
385           }
386
387           if (tmp.end[1] <= min[1])
388           {
389             min[1] = tmp.end[1];
390           }
391
392           if (tmp.end[2] <= min[2])
393           {
394             min[2] = tmp.end[2];
395           }
396         }
397       }
398     }
399
400     width[0] = Math.abs(max[0] - min[0]);
401     width[1] = Math.abs(max[1] - min[1]);
402     width[2] = Math.abs(max[2] - min[2]);
403
404     maxwidth = width[0];
405
406     if (width[1] > width[0])
407     {
408       maxwidth = width[1];
409     }
410
411     if (width[2] > width[1])
412     {
413       maxwidth = width[2];
414     }
415
416     // System.out.println("Maxwidth = " + maxwidth);
417   }
418
419   public float findScale()
420   {
421     int dim;
422     int width;
423     int height;
424
425     if (getSize().width != 0)
426     {
427       width = getSize().width;
428       height = getSize().height;
429     }
430     else
431     {
432       width = prefsize.width;
433       height = prefsize.height;
434     }
435
436     if (width < height)
437     {
438       dim = width;
439     }
440     else
441     {
442       dim = height;
443     }
444
445     return (float) (dim / (1.5d * maxwidth));
446   }
447
448   public void findCentre()
449   {
450     float xtot = 0;
451     float ytot = 0;
452     float ztot = 0;
453
454     int bsize = 0;
455
456     // Find centre coordinate
457     for (int ii = 0; ii < pdb.getChains().size(); ii++)
458     {
459       if (pdb.getChains().elementAt(ii).isVisible)
460       {
461         Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
462
463         bsize += bonds.size();
464
465         for (Bond b : bonds)
466         {
467           xtot = xtot + b.start[0] + b.end[0];
468           ytot = ytot + b.start[1] + b.end[1];
469           ztot = ztot + b.start[2] + b.end[2];
470         }
471       }
472     }
473
474     centre[0] = xtot / (2 * (float) bsize);
475     centre[1] = ytot / (2 * (float) bsize);
476     centre[2] = ztot / (2 * (float) bsize);
477   }
478
479   @Override
480   public void paint(Graphics g)
481   {
482
483     if (errorLoading)
484     {
485       g.setColor(Color.white);
486       g.fillRect(0, 0, getSize().width, getSize().height);
487       g.setColor(Color.black);
488       g.setFont(new Font("Verdana", Font.BOLD, 14));
489       g.drawString(
490               MessageManager.getString("label.error_loading_pdb_data"), 50,
491               getSize().height / 2);
492       return;
493     }
494
495     if (!seqColoursReady)
496     {
497       g.setColor(Color.black);
498       g.setFont(new Font("Verdana", Font.BOLD, 14));
499       g.drawString(MessageManager.getString("label.fetching_pdb_data"), 50,
500               getSize().height / 2);
501       return;
502     }
503
504     // Only create the image at the beginning -
505     // this saves much memory usage
506     if ((img == null) || (prefsize.width != getSize().width)
507             || (prefsize.height != getSize().height))
508     {
509
510       try
511       {
512         prefsize.width = getSize().width;
513         prefsize.height = getSize().height;
514
515         scale = findScale();
516         img = createImage(prefsize.width, prefsize.height);
517         ig = img.getGraphics();
518
519         redrawneeded = true;
520       } catch (Exception ex)
521       {
522         ex.printStackTrace();
523       }
524     }
525
526     if (redrawneeded)
527     {
528       drawAll(ig, prefsize.width, prefsize.height);
529       redrawneeded = false;
530     }
531     if (appletToolTip != null)
532     {
533       ig.setColor(Color.red);
534       ig.drawString(appletToolTip, toolx, tooly);
535     }
536
537     g.drawImage(img, 0, 0, this);
538
539     pdbAction = false;
540   }
541
542   public void drawAll(Graphics g, int width, int height)
543   {
544     ig.setColor(Color.black);
545     ig.fillRect(0, 0, width, height);
546     drawScene(ig);
547     drawLabels(ig);
548   }
549
550   public void setColours(jalview.schemes.ColourSchemeI cs)
551   {
552     bysequence = false;
553     pdb.setColours(cs);
554     redrawneeded = true;
555     repaint();
556   }
557
558   // This method has been taken out of PDBChain to allow
559   // Applet and Application specific sequence renderers to be used
560   void colourBySequence()
561   {
562     SequenceRenderer sr = new SequenceRenderer(ap.av);
563
564     StructureMapping[] mapping = ssm.getMapping(pdbentry.getFile());
565
566     boolean showFeatures = false;
567     if (ap.av.isShowSequenceFeatures())
568     {
569       if (fr == null)
570       {
571         fr = new jalview.appletgui.FeatureRenderer(ap.av);
572       }
573
574       fr.transferSettings(ap.getFeatureRenderer());
575
576       showFeatures = true;
577     }
578
579     PDBChain chain;
580     if (bysequence && pdb != null)
581     {
582       for (int ii = 0; ii < pdb.getChains().size(); ii++)
583       {
584         chain = pdb.getChains().elementAt(ii);
585
586         for (int i = 0; i < chain.bonds.size(); i++)
587         {
588           Bond tmp = chain.bonds.elementAt(i);
589           tmp.startCol = Color.lightGray;
590           tmp.endCol = Color.lightGray;
591           if (chain != mainchain)
592           {
593             continue;
594           }
595
596           for (int s = 0; s < sequence.length; s++)
597           {
598             for (int m = 0; m < mapping.length; m++)
599             {
600               if (mapping[m].getSequence() == sequence[s])
601               {
602                 int pos = mapping[m].getSeqPos(tmp.at1.resNumber) - 1;
603                 if (pos > 0)
604                 {
605                   pos = sequence[s].findIndex(pos);
606                   tmp.startCol = sr.getResidueBoxColour(sequence[s], pos);
607                   if (showFeatures)
608                   {
609                     tmp.startCol = fr.findFeatureColour(tmp.startCol,
610                             sequence[s], pos);
611                   }
612                 }
613                 pos = mapping[m].getSeqPos(tmp.at2.resNumber) - 1;
614                 if (pos > 0)
615                 {
616                   pos = sequence[s].findIndex(pos);
617                   tmp.endCol = sr.getResidueBoxColour(sequence[s], pos);
618                   if (showFeatures)
619                   {
620                     tmp.endCol = fr.findFeatureColour(tmp.endCol,
621                             sequence[s], pos);
622                   }
623                 }
624
625               }
626             }
627           }
628         }
629       }
630     }
631   }
632
633   Zsort zsort;
634
635   public void drawScene(Graphics g)
636   {
637     if (zbuffer)
638     {
639       if (zsort == null)
640       {
641         zsort = new Zsort();
642       }
643
644       zsort.sort(visiblebonds);
645     }
646
647     Bond tmpBond = null;
648     for (int i = 0; i < visiblebonds.size(); i++)
649     {
650       tmpBond = visiblebonds.elementAt(i);
651
652       xstart = (int) (((tmpBond.start[0] - centre[0]) * scale) + (getSize().width / 2));
653       ystart = (int) (((centre[1] - tmpBond.start[1]) * scale) + (getSize().height / 2));
654
655       xend = (int) (((tmpBond.end[0] - centre[0]) * scale) + (getSize().width / 2));
656       yend = (int) (((centre[1] - tmpBond.end[1]) * scale) + (getSize().height / 2));
657
658       xmid = (xend + xstart) / 2;
659       ymid = (yend + ystart) / 2;
660
661       if (depthcue && !bymolecule)
662       {
663         if (tmpBond.start[2] < (centre[2] - (maxwidth / 6)))
664         {
665           g.setColor(tmpBond.startCol.darker().darker());
666           drawLine(g, xstart, ystart, xmid, ymid);
667
668           g.setColor(tmpBond.endCol.darker().darker());
669           drawLine(g, xmid, ymid, xend, yend);
670         }
671         else if (tmpBond.start[2] < (centre[2] + (maxwidth / 6)))
672         {
673           g.setColor(tmpBond.startCol.darker());
674           drawLine(g, xstart, ystart, xmid, ymid);
675
676           g.setColor(tmpBond.endCol.darker());
677           drawLine(g, xmid, ymid, xend, yend);
678         }
679         else
680         {
681           g.setColor(tmpBond.startCol);
682           drawLine(g, xstart, ystart, xmid, ymid);
683
684           g.setColor(tmpBond.endCol);
685           drawLine(g, xmid, ymid, xend, yend);
686         }
687
688       }
689       else if (depthcue && bymolecule)
690       {
691         if (tmpBond.start[2] < (centre[2] - (maxwidth / 6)))
692         {
693           g.setColor(Color.green.darker().darker());
694           drawLine(g, xstart, ystart, xend, yend);
695         }
696         else if (tmpBond.start[2] < (centre[2] + (maxwidth / 6)))
697         {
698           g.setColor(Color.green.darker());
699           drawLine(g, xstart, ystart, xend, yend);
700         }
701         else
702         {
703           g.setColor(Color.green);
704           drawLine(g, xstart, ystart, xend, yend);
705         }
706       }
707       else if (!depthcue && !bymolecule)
708       {
709         g.setColor(tmpBond.startCol);
710         drawLine(g, xstart, ystart, xmid, ymid);
711         g.setColor(tmpBond.endCol);
712         drawLine(g, xmid, ymid, xend, yend);
713       }
714       else
715       {
716         drawLine(g, xstart, ystart, xend, yend);
717       }
718
719       if (highlightBond1 != null && highlightBond1 == tmpBond)
720       {
721         g.setColor(Color.white);
722         drawLine(g, xmid, ymid, xend, yend);
723       }
724
725       if (highlightBond2 != null && highlightBond2 == tmpBond)
726       {
727         g.setColor(Color.white);
728         drawLine(g, xstart, ystart, xmid, ymid);
729       }
730
731     }
732   }
733
734   public void drawLine(Graphics g, int x1, int y1, int x2, int y2)
735   {
736     if (!wire)
737     {
738       if (((float) Math.abs(y2 - y1) / (float) Math.abs(x2 - x1)) < 0.5)
739       {
740         g.drawLine(x1, y1, x2, y2);
741         g.drawLine(x1 + 1, y1 + 1, x2 + 1, y2 + 1);
742         g.drawLine(x1, y1 - 1, x2, y2 - 1);
743       }
744       else
745       {
746         g.setColor(g.getColor().brighter());
747         g.drawLine(x1, y1, x2, y2);
748         g.drawLine(x1 + 1, y1, x2 + 1, y2);
749         g.drawLine(x1 - 1, y1, x2 - 1, y2);
750       }
751     }
752     else
753     {
754       g.drawLine(x1, y1, x2, y2);
755     }
756   }
757
758   public Dimension minimumsize()
759   {
760     return prefsize;
761   }
762
763   public Dimension preferredsize()
764   {
765     return prefsize;
766   }
767
768   public void doKeyPressed(KeyEvent evt)
769   {
770     if (evt.getKeyCode() == KeyEvent.VK_UP)
771     {
772       scale = (float) (scale * 1.1);
773       redrawneeded = true;
774       repaint();
775     }
776     else if (evt.getKeyCode() == KeyEvent.VK_DOWN)
777     {
778       scale = (float) (scale * 0.9);
779       redrawneeded = true;
780       repaint();
781     }
782   }
783
784   @Override
785   public void mousePressed(MouseEvent e)
786   {
787     pdbAction = true;
788     Atom fatom = findAtom(e.getX(), e.getY());
789     if (fatom != null)
790     {
791       fatom.isSelected = !fatom.isSelected;
792
793       redrawneeded = true;
794       repaint();
795       if (foundchain != -1)
796       {
797         PDBChain chain = pdb.getChains().elementAt(foundchain);
798         if (chain == mainchain)
799         {
800           if (fatom.alignmentMapping != -1)
801           {
802             if (highlightRes == null)
803             {
804               highlightRes = new Vector<String>();
805             }
806
807             final String atomString = Integer
808                     .toString(fatom.alignmentMapping);
809             if (highlightRes.contains(atomString))
810             {
811               highlightRes.removeElement(atomString);
812             }
813             else
814             {
815               highlightRes.addElement(atomString);
816             }
817           }
818         }
819       }
820
821     }
822     mx = e.getX();
823     my = e.getY();
824     omx = mx;
825     omy = my;
826     dragging = false;
827   }
828
829   @Override
830   public void mouseMoved(MouseEvent e)
831   {
832     pdbAction = true;
833     if (highlightBond1 != null)
834     {
835       highlightBond1.at2.isSelected = false;
836       highlightBond2.at1.isSelected = false;
837       highlightBond1 = null;
838       highlightBond2 = null;
839     }
840
841     Atom fatom = findAtom(e.getX(), e.getY());
842
843     PDBChain chain = null;
844     if (foundchain != -1)
845     {
846       chain = pdb.getChains().elementAt(foundchain);
847       if (chain == mainchain)
848       {
849         mouseOverStructure(fatom.resNumber, chain.id);
850       }
851     }
852
853     if (fatom != null)
854     {
855       toolx = e.getX();
856       tooly = e.getY();
857
858       appletToolTip = chain.id + ":" + fatom.resNumber + " "
859               + fatom.resName;
860       redrawneeded = true;
861       repaint();
862     }
863     else
864     {
865       mouseOverStructure(-1, chain != null ? chain.id : null);
866       appletToolTip = null;
867       redrawneeded = true;
868       repaint();
869     }
870   }
871
872   @Override
873   public void mouseClicked(MouseEvent e)
874   {
875   }
876
877   @Override
878   public void mouseEntered(MouseEvent e)
879   {
880   }
881
882   @Override
883   public void mouseExited(MouseEvent e)
884   {
885   }
886
887   @Override
888   public void mouseDragged(MouseEvent evt)
889   {
890     int x = evt.getX();
891     int y = evt.getY();
892     mx = x;
893     my = y;
894
895     MCMatrix objmat = new MCMatrix(3, 3);
896     objmat.setIdentity();
897
898     if ((evt.getModifiers() & Event.META_MASK) != 0)
899     {
900       objmat.rotatez(((mx - omx)));
901     }
902     else
903     {
904       objmat.rotatex(((omy - my)));
905       objmat.rotatey(((omx - mx)));
906     }
907
908     // Alter the bonds
909     for (PDBChain chain : pdb.getChains())
910     {
911       for (Bond tmpBond : chain.bonds)
912       {
913         // Translate the bond so the centre is 0,0,0
914         tmpBond.translate(-centre[0], -centre[1], -centre[2]);
915
916         // Now apply the rotation matrix
917         tmpBond.start = objmat.vectorMultiply(tmpBond.start);
918         tmpBond.end = objmat.vectorMultiply(tmpBond.end);
919
920         // Now translate back again
921         tmpBond.translate(centre[0], centre[1], centre[2]);
922       }
923     }
924
925     objmat = null;
926
927     omx = mx;
928     omy = my;
929
930     dragging = true;
931
932     redrawneeded = true;
933
934     repaint();
935   }
936
937   @Override
938   public void mouseReleased(MouseEvent evt)
939   {
940     dragging = false;
941     return;
942   }
943
944   void drawLabels(Graphics g)
945   {
946
947     for (PDBChain chain : pdb.getChains())
948     {
949       if (chain.isVisible)
950       {
951         for (Bond tmpBond : chain.bonds)
952         {
953           if (tmpBond.at1.isSelected)
954           {
955             labelAtom(g, tmpBond, 1);
956           }
957
958           if (tmpBond.at2.isSelected)
959           {
960             labelAtom(g, tmpBond, 2);
961           }
962         }
963       }
964     }
965   }
966
967   public void labelAtom(Graphics g, Bond b, int n)
968   {
969     g.setFont(font);
970
971     if (n == 1)
972     {
973       int xstart = (int) (((b.start[0] - centre[0]) * scale) + (getSize().width / 2));
974       int ystart = (int) (((centre[1] - b.start[1]) * scale) + (getSize().height / 2));
975
976       g.setColor(Color.red);
977       g.drawString(b.at1.resName + "-" + b.at1.resNumber, xstart, ystart);
978     }
979
980     if (n == 2)
981     {
982       int xstart = (int) (((b.end[0] - centre[0]) * scale) + (getSize().width / 2));
983       int ystart = (int) (((centre[1] - b.end[1]) * scale) + (getSize().height / 2));
984
985       g.setColor(Color.red);
986       g.drawString(b.at2.resName + "-" + b.at2.resNumber, xstart, ystart);
987     }
988   }
989
990   int foundchain = -1;
991
992   public Atom findAtom(int x, int y)
993   {
994     Atom fatom = null;
995
996     foundchain = -1;
997
998     for (int ii = 0; ii < pdb.getChains().size(); ii++)
999     {
1000       PDBChain chain = pdb.getChains().elementAt(ii);
1001       int truex;
1002       Bond tmpBond = null;
1003
1004       if (chain.isVisible)
1005       {
1006         Vector<Bond> bonds = pdb.getChains().elementAt(ii).bonds;
1007
1008         for (int i = 0; i < bonds.size(); i++)
1009         {
1010           tmpBond = bonds.elementAt(i);
1011
1012           truex = (int) (((tmpBond.start[0] - centre[0]) * scale) + (getSize().width / 2));
1013
1014           if (Math.abs(truex - x) <= 2)
1015           {
1016             int truey = (int) (((centre[1] - tmpBond.start[1]) * scale) + (getSize().height / 2));
1017
1018             if (Math.abs(truey - y) <= 2)
1019             {
1020               fatom = tmpBond.at1;
1021               foundchain = ii;
1022               break;
1023             }
1024           }
1025         }
1026
1027         // Still here? Maybe its the last bond
1028
1029         truex = (int) (((tmpBond.end[0] - centre[0]) * scale) + (getSize().width / 2));
1030
1031         if (Math.abs(truex - x) <= 2)
1032         {
1033           int truey = (int) (((tmpBond.end[1] - centre[1]) * scale) + (getSize().height / 2));
1034
1035           if (Math.abs(truey - y) <= 2)
1036           {
1037             fatom = tmpBond.at2;
1038             foundchain = ii;
1039             break;
1040           }
1041         }
1042
1043       }
1044
1045       if (fatom != null) // )&& chain.ds != null)
1046       {
1047         chain = pdb.getChains().elementAt(foundchain);
1048       }
1049     }
1050
1051     return fatom;
1052   }
1053
1054   @Override
1055   public void update(Graphics g)
1056   {
1057     paint(g);
1058   }
1059
1060   public void highlightRes(int ii)
1061   {
1062     if (!seqColoursReady)
1063     {
1064       return;
1065     }
1066
1067     if (highlightRes != null && highlightRes.contains((ii - 1) + ""))
1068     {
1069       return;
1070     }
1071
1072     int index = -1;
1073     Bond tmpBond;
1074     for (index = 0; index < mainchain.bonds.size(); index++)
1075     {
1076       tmpBond = mainchain.bonds.elementAt(index);
1077       if (tmpBond.at1.alignmentMapping == ii - 1)
1078       {
1079         if (highlightBond1 != null)
1080         {
1081           highlightBond1.at2.isSelected = false;
1082         }
1083
1084         if (highlightBond2 != null)
1085         {
1086           highlightBond2.at1.isSelected = false;
1087         }
1088
1089         highlightBond1 = null;
1090         highlightBond2 = null;
1091
1092         if (index > 0)
1093         {
1094           highlightBond1 = mainchain.bonds.elementAt(index - 1);
1095           highlightBond1.at2.isSelected = true;
1096         }
1097
1098         if (index != mainchain.bonds.size())
1099         {
1100           highlightBond2 = mainchain.bonds.elementAt(index);
1101           highlightBond2.at1.isSelected = true;
1102         }
1103
1104         break;
1105       }
1106     }
1107
1108     redrawneeded = true;
1109     repaint();
1110   }
1111
1112   public void setAllchainsVisible(boolean b)
1113   {
1114     for (int ii = 0; ii < pdb.getChains().size(); ii++)
1115     {
1116       PDBChain chain = pdb.getChains().elementAt(ii);
1117       chain.isVisible = b;
1118     }
1119     mainchain.isVisible = true;
1120     findCentre();
1121     setupBonds();
1122   }
1123
1124   // ////////////////////////////////
1125   // /StructureListener
1126   @Override
1127   public String[] getPdbFile()
1128   {
1129     return new String[] { pdbentry.getFile() };
1130   }
1131
1132   String lastMessage;
1133
1134   public void mouseOverStructure(int pdbResNum, String chain)
1135   {
1136     if (lastMessage == null || !lastMessage.equals(pdbResNum + chain))
1137     {
1138       ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile());
1139     }
1140
1141     lastMessage = pdbResNum + chain;
1142   }
1143
1144   StringBuffer resetLastRes = new StringBuffer();
1145
1146   StringBuffer eval = new StringBuffer();
1147
1148   /**
1149    * Highlight the specified atoms in the structure.
1150    * 
1151    * @param atoms
1152    */
1153   @Override
1154   public void highlightAtoms(List<AtomSpec> atoms)
1155   {
1156     if (!seqColoursReady)
1157     {
1158       return;
1159     }
1160     for (AtomSpec atom : atoms)
1161     {
1162       int atomIndex = atom.getAtomIndex();
1163
1164       if (highlightRes != null
1165               && highlightRes.contains((atomIndex - 1) + ""))
1166       {
1167         continue;
1168       }
1169
1170       highlightAtom(atomIndex);
1171     }
1172
1173     redrawneeded = true;
1174     repaint();
1175   }
1176
1177   /**
1178    * @param atomIndex
1179    */
1180   protected void highlightAtom(int atomIndex)
1181   {
1182     int index = -1;
1183     Bond tmpBond;
1184     for (index = 0; index < mainchain.bonds.size(); index++)
1185     {
1186       tmpBond = mainchain.bonds.elementAt(index);
1187       if (tmpBond.at1.atomIndex == atomIndex)
1188       {
1189         if (highlightBond1 != null)
1190         {
1191           highlightBond1.at2.isSelected = false;
1192         }
1193
1194         if (highlightBond2 != null)
1195         {
1196           highlightBond2.at1.isSelected = false;
1197         }
1198
1199         highlightBond1 = null;
1200         highlightBond2 = null;
1201
1202         if (index > 0)
1203         {
1204           highlightBond1 = mainchain.bonds.elementAt(index - 1);
1205           highlightBond1.at2.isSelected = true;
1206         }
1207
1208         if (index != mainchain.bonds.size())
1209         {
1210           highlightBond2 = mainchain.bonds.elementAt(index);
1211           highlightBond2.at1.isSelected = true;
1212         }
1213
1214         break;
1215       }
1216     }
1217   }
1218
1219   public Color getColour(int atomIndex, int pdbResNum, String chain,
1220           String pdbfile)
1221   {
1222     return Color.white;
1223     // if (!pdbfile.equals(pdbentry.getFile()))
1224     // return null;
1225
1226     // return new Color(viewer.getAtomArgb(atomIndex));
1227   }
1228
1229   @Override
1230   public void updateColours(Object source)
1231   {
1232     colourBySequence();
1233     redrawneeded = true;
1234     repaint();
1235   }
1236
1237   @Override
1238   public void releaseReferences(Object svl)
1239   {
1240     // TODO Auto-generated method stub
1241
1242   }
1243
1244   @Override
1245   public boolean isListeningFor(SequenceI seq)
1246   {
1247     if (sequence != null)
1248     {
1249       for (SequenceI s : sequence)
1250       {
1251         if (s == seq)
1252         {
1253           return true;
1254         }
1255       }
1256     }
1257     return false;
1258   }
1259 }