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