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