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