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