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