JAL-2446 optimised new method to find feature colour for overview
[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.getResidueColour(sequence[s], pos,
579                           finder);
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
586                           .getResidueColour(sequence[s], pos, finder);
587                 }
588
589               }
590             }
591           }
592         }
593       }
594     }
595   }
596
597   Zsort zsort;
598
599   public void drawScene(Graphics g)
600   {
601     if (zbuffer)
602     {
603       if (zsort == null)
604       {
605         zsort = new Zsort();
606       }
607
608       zsort.sort(visiblebonds);
609     }
610
611     Bond tmpBond = null;
612     for (int i = 0; i < visiblebonds.size(); i++)
613     {
614       tmpBond = visiblebonds.elementAt(i);
615
616       xstart = (int) (((tmpBond.start[0] - centre[0]) * scale) + (getWidth() / 2));
617       ystart = (int) (((centre[1] - tmpBond.start[1]) * scale) + (getHeight() / 2));
618
619       xend = (int) (((tmpBond.end[0] - centre[0]) * scale) + (getWidth() / 2));
620       yend = (int) (((centre[1] - tmpBond.end[1]) * scale) + (getHeight() / 2));
621
622       xmid = (xend + xstart) / 2;
623       ymid = (yend + ystart) / 2;
624       if (depthcue && !bymolecule)
625       {
626         if (tmpBond.start[2] < (centre[2] - (maxwidth / 6)))
627         {
628
629           g.setColor(tmpBond.startCol.darker().darker());
630           drawLine(g, xstart, ystart, xmid, ymid);
631           g.setColor(tmpBond.endCol.darker().darker());
632           drawLine(g, xmid, ymid, xend, yend);
633
634         }
635         else if (tmpBond.start[2] < (centre[2] + (maxwidth / 6)))
636         {
637           g.setColor(tmpBond.startCol.darker());
638           drawLine(g, xstart, ystart, xmid, ymid);
639
640           g.setColor(tmpBond.endCol.darker());
641           drawLine(g, xmid, ymid, xend, yend);
642         }
643         else
644         {
645           g.setColor(tmpBond.startCol);
646           drawLine(g, xstart, ystart, xmid, ymid);
647
648           g.setColor(tmpBond.endCol);
649           drawLine(g, xmid, ymid, xend, yend);
650         }
651       }
652       else if (depthcue && bymolecule)
653       {
654         if (tmpBond.start[2] < (centre[2] - (maxwidth / 6)))
655         {
656           g.setColor(Color.green.darker().darker());
657           drawLine(g, xstart, ystart, xend, yend);
658         }
659         else if (tmpBond.start[2] < (centre[2] + (maxwidth / 6)))
660         {
661           g.setColor(Color.green.darker());
662           drawLine(g, xstart, ystart, xend, yend);
663         }
664         else
665         {
666           g.setColor(Color.green);
667           drawLine(g, xstart, ystart, xend, yend);
668         }
669       }
670       else if (!depthcue && !bymolecule)
671       {
672         g.setColor(tmpBond.startCol);
673         drawLine(g, xstart, ystart, xmid, ymid);
674         g.setColor(tmpBond.endCol);
675         drawLine(g, xmid, ymid, xend, yend);
676       }
677       else
678       {
679         drawLine(g, xstart, ystart, xend, yend);
680       }
681
682       if (highlightBond1 != null && highlightBond1 == tmpBond)
683       {
684         g.setColor(tmpBond.endCol.brighter().brighter().brighter()
685                 .brighter());
686         drawLine(g, xmid, ymid, xend, yend);
687       }
688
689       if (highlightBond2 != null && highlightBond2 == tmpBond)
690       {
691         g.setColor(tmpBond.startCol.brighter().brighter().brighter()
692                 .brighter());
693         drawLine(g, xstart, ystart, xmid, ymid);
694       }
695
696     }
697
698   }
699
700   public void drawLine(Graphics g, int x1, int y1, int x2, int y2)
701   {
702     if (!wire)
703     {
704       if (((float) Math.abs(y2 - y1) / (float) Math.abs(x2 - x1)) < 0.5)
705       {
706         g.drawLine(x1, y1, x2, y2);
707         g.drawLine(x1 + 1, y1 + 1, x2 + 1, y2 + 1);
708         g.drawLine(x1, y1 - 1, x2, y2 - 1);
709       }
710       else
711       {
712         g.setColor(g.getColor().brighter());
713         g.drawLine(x1, y1, x2, y2);
714         g.drawLine(x1 + 1, y1, x2 + 1, y2);
715         g.drawLine(x1 - 1, y1, x2 - 1, y2);
716       }
717     }
718     else
719     {
720       g.drawLine(x1, y1, x2, y2);
721     }
722   }
723
724   public Dimension minimumsize()
725   {
726     return prefsize;
727   }
728
729   public Dimension preferredsize()
730   {
731     return prefsize;
732   }
733
734   public void keyPressed(KeyEvent evt)
735   {
736     if (evt.getKeyCode() == KeyEvent.VK_UP)
737     {
738       scale = (float) (scale * 1.1);
739       redrawneeded = true;
740       repaint();
741     }
742     else if (evt.getKeyCode() == KeyEvent.VK_DOWN)
743     {
744       scale = (float) (scale * 0.9);
745       redrawneeded = true;
746       repaint();
747     }
748   }
749
750   @Override
751   public void mousePressed(MouseEvent e)
752   {
753     pdbAction = true;
754     Atom fatom = findAtom(e.getX(), e.getY());
755     if (fatom != null)
756     {
757       fatom.isSelected = !fatom.isSelected;
758
759       redrawneeded = true;
760       repaint();
761       if (foundchain != -1)
762       {
763         PDBChain chain = pdb.getChains().elementAt(foundchain);
764         if (chain == mainchain)
765         {
766           if (fatom.alignmentMapping != -1)
767           {
768             if (highlightRes == null)
769             {
770               highlightRes = new Vector<String>();
771             }
772
773             final String atomString = Integer
774                     .toString(fatom.alignmentMapping);
775             if (highlightRes.contains(atomString))
776             {
777               highlightRes.remove(atomString);
778             }
779             else
780             {
781               highlightRes.add(atomString);
782             }
783           }
784         }
785       }
786
787     }
788     mx = e.getX();
789     my = e.getY();
790     omx = mx;
791     omy = my;
792     dragging = false;
793   }
794
795   @Override
796   public void mouseMoved(MouseEvent e)
797   {
798     pdbAction = true;
799     if (highlightBond1 != null)
800     {
801       highlightBond1.at2.isSelected = false;
802       highlightBond2.at1.isSelected = false;
803       highlightBond1 = null;
804       highlightBond2 = null;
805     }
806
807     Atom fatom = findAtom(e.getX(), e.getY());
808
809     PDBChain chain = null;
810     if (foundchain != -1)
811     {
812       chain = pdb.getChains().elementAt(foundchain);
813       if (chain == mainchain)
814       {
815         mouseOverStructure(fatom.resNumber, chain.id);
816       }
817     }
818
819     if (fatom != null)
820     {
821       this.setToolTipText(chain.id + ":" + fatom.resNumber + " "
822               + fatom.resName);
823     }
824     else
825     {
826       mouseOverStructure(-1, chain != null ? chain.id : null);
827       this.setToolTipText(null);
828     }
829   }
830
831   @Override
832   public void mouseClicked(MouseEvent e)
833   {
834   }
835
836   @Override
837   public void mouseEntered(MouseEvent e)
838   {
839   }
840
841   @Override
842   public void mouseExited(MouseEvent e)
843   {
844   }
845
846   @Override
847   public void mouseDragged(MouseEvent evt)
848   {
849     int x = evt.getX();
850     int y = evt.getY();
851     mx = x;
852     my = y;
853
854     MCMatrix objmat = new MCMatrix(3, 3);
855     objmat.setIdentity();
856
857     if ((evt.getModifiers() & Event.META_MASK) != 0)
858     {
859       objmat.rotatez(((mx - omx)));
860     }
861     else
862     {
863       objmat.rotatex(((my - omy)));
864       objmat.rotatey(((omx - mx)));
865     }
866
867     // Alter the bonds
868     for (PDBChain chain : pdb.getChains())
869     {
870       for (Bond tmpBond : chain.bonds)
871       {
872         // Translate the bond so the centre is 0,0,0
873         tmpBond.translate(-centre[0], -centre[1], -centre[2]);
874
875         // Now apply the rotation matrix
876         tmpBond.start = objmat.vectorMultiply(tmpBond.start);
877         tmpBond.end = objmat.vectorMultiply(tmpBond.end);
878
879         // Now translate back again
880         tmpBond.translate(centre[0], centre[1], centre[2]);
881       }
882     }
883
884     objmat = null;
885
886     omx = mx;
887     omy = my;
888
889     dragging = true;
890
891     redrawneeded = true;
892
893     repaint();
894   }
895
896   @Override
897   public void mouseReleased(MouseEvent evt)
898   {
899     dragging = false;
900     return;
901   }
902
903   void drawLabels(Graphics g)
904   {
905
906     for (PDBChain chain : pdb.getChains())
907     {
908       if (chain.isVisible)
909       {
910         for (Bond tmpBond : chain.bonds)
911         {
912           if (tmpBond.at1.isSelected)
913           {
914             labelAtom(g, tmpBond, 1);
915           }
916
917           if (tmpBond.at2.isSelected)
918           {
919             labelAtom(g, tmpBond, 2);
920           }
921         }
922       }
923     }
924   }
925
926   public void labelAtom(Graphics g, Bond b, int n)
927   {
928     g.setFont(font);
929     g.setColor(Color.red);
930     if (n == 1)
931     {
932       int xstart = (int) (((b.start[0] - centre[0]) * scale) + (getWidth() / 2));
933       int ystart = (int) (((centre[1] - b.start[1]) * scale) + (getHeight() / 2));
934
935       g.drawString(b.at1.resName + "-" + b.at1.resNumber, xstart, ystart);
936     }
937
938     if (n == 2)
939     {
940       int xstart = (int) (((b.end[0] - centre[0]) * scale) + (getWidth() / 2));
941       int ystart = (int) (((centre[1] - b.end[1]) * scale) + (getHeight() / 2));
942
943       g.drawString(b.at2.resName + "-" + b.at2.resNumber, xstart, ystart);
944     }
945   }
946
947   int foundchain = -1;
948
949   public Atom findAtom(int x, int y)
950   {
951     Atom fatom = null;
952
953     foundchain = -1;
954
955     for (int ii = 0; ii < pdb.getChains().size(); ii++)
956     {
957       PDBChain chain = pdb.getChains().elementAt(ii);
958       int truex;
959       Bond tmpBond = null;
960
961       if (chain.isVisible)
962       {
963         for (Bond bond : chain.bonds)
964         {
965           tmpBond = bond;
966
967           truex = (int) (((tmpBond.start[0] - centre[0]) * scale) + (getWidth() / 2));
968
969           if (Math.abs(truex - x) <= 2)
970           {
971             int truey = (int) (((centre[1] - tmpBond.start[1]) * scale) + (getHeight() / 2));
972
973             if (Math.abs(truey - y) <= 2)
974             {
975               fatom = tmpBond.at1;
976               foundchain = ii;
977               break;
978             }
979           }
980         }
981
982         // Still here? Maybe its the last bond
983
984         truex = (int) (((tmpBond.end[0] - centre[0]) * scale) + (getWidth() / 2));
985
986         if (Math.abs(truex - x) <= 2)
987         {
988           int truey = (int) (((tmpBond.end[1] - centre[1]) * scale) + (getHeight() / 2));
989
990           if (Math.abs(truey - y) <= 2)
991           {
992             fatom = tmpBond.at2;
993             foundchain = ii;
994             break;
995           }
996         }
997
998       }
999
1000       if (fatom != null) // )&& chain.ds != null)
1001       { // dead code? value of chain is either overwritten or discarded
1002         chain = pdb.getChains().elementAt(foundchain);
1003       }
1004     }
1005
1006     return fatom;
1007   }
1008
1009   Bond highlightBond1, highlightBond2;
1010
1011   public void highlightRes(int ii)
1012   {
1013     if (!seqColoursReady)
1014     {
1015       return;
1016     }
1017
1018     if (highlightRes != null && highlightRes.contains((ii - 1) + ""))
1019     {
1020       return;
1021     }
1022
1023     int index = -1;
1024     Bond tmpBond;
1025     for (index = 0; index < mainchain.bonds.size(); index++)
1026     {
1027       tmpBond = mainchain.bonds.elementAt(index);
1028       if (tmpBond.at1.alignmentMapping == ii - 1)
1029       {
1030         if (highlightBond1 != null)
1031         {
1032           highlightBond1.at2.isSelected = false;
1033         }
1034
1035         if (highlightBond2 != null)
1036         {
1037           highlightBond2.at1.isSelected = false;
1038         }
1039
1040         highlightBond1 = null;
1041         highlightBond2 = null;
1042
1043         if (index > 0)
1044         {
1045           highlightBond1 = mainchain.bonds.elementAt(index - 1);
1046           highlightBond1.at2.isSelected = true;
1047         }
1048
1049         if (index != mainchain.bonds.size())
1050         {
1051           highlightBond2 = mainchain.bonds.elementAt(index);
1052           highlightBond2.at1.isSelected = true;
1053         }
1054
1055         break;
1056       }
1057     }
1058
1059     redrawneeded = true;
1060     repaint();
1061   }
1062
1063   public void setAllchainsVisible(boolean b)
1064   {
1065     for (int ii = 0; ii < pdb.getChains().size(); ii++)
1066     {
1067       PDBChain chain = pdb.getChains().elementAt(ii);
1068       chain.isVisible = b;
1069     }
1070     mainchain.isVisible = true;
1071     findCentre();
1072     setupBonds();
1073   }
1074
1075   // ////////////////////////////////
1076   // /StructureListener
1077   @Override
1078   public String[] getPdbFile()
1079   {
1080     return new String[] { pdbentry.getFile() };
1081   }
1082
1083   String lastMessage;
1084
1085   public void mouseOverStructure(int pdbResNum, String chain)
1086   {
1087     if (lastMessage == null || !lastMessage.equals(pdbResNum + chain))
1088     {
1089       ssm.mouseOverStructure(pdbResNum, chain, pdbentry.getFile());
1090     }
1091
1092     lastMessage = pdbResNum + chain;
1093   }
1094
1095   StringBuffer resetLastRes = new StringBuffer();
1096
1097   StringBuffer eval = new StringBuffer();
1098
1099   /**
1100    * Highlight the specified atoms in the structure.
1101    * 
1102    * @param atoms
1103    */
1104   @Override
1105   public void highlightAtoms(List<AtomSpec> atoms)
1106   {
1107     if (!seqColoursReady)
1108     {
1109       return;
1110     }
1111
1112     for (AtomSpec atom : atoms)
1113     {
1114       int atomIndex = atom.getAtomIndex();
1115       if (highlightRes != null
1116               && highlightRes.contains((atomIndex - 1) + ""))
1117       {
1118         continue;
1119       }
1120
1121       highlightAtom(atomIndex);
1122     }
1123
1124     redrawneeded = true;
1125     repaint();
1126   }
1127
1128   /**
1129    * Highlight the atom at the specified index.
1130    * 
1131    * @param atomIndex
1132    */
1133   protected void highlightAtom(int atomIndex)
1134   {
1135     int index = -1;
1136     Bond tmpBond;
1137     for (index = 0; index < mainchain.bonds.size(); index++)
1138     {
1139       tmpBond = mainchain.bonds.elementAt(index);
1140       if (tmpBond.at1.atomIndex == atomIndex)
1141       {
1142         if (highlightBond1 != null)
1143         {
1144           highlightBond1.at2.isSelected = false;
1145         }
1146
1147         if (highlightBond2 != null)
1148         {
1149           highlightBond2.at1.isSelected = false;
1150         }
1151
1152         highlightBond1 = null;
1153         highlightBond2 = null;
1154
1155         if (index > 0)
1156         {
1157           highlightBond1 = mainchain.bonds.elementAt(index - 1);
1158           highlightBond1.at2.isSelected = true;
1159         }
1160
1161         if (index != mainchain.bonds.size())
1162         {
1163           highlightBond2 = mainchain.bonds.elementAt(index);
1164           highlightBond2.at1.isSelected = true;
1165         }
1166
1167         break;
1168       }
1169     }
1170   }
1171
1172   public Color getColour(int atomIndex, int pdbResNum, String chain,
1173           String pdbfile)
1174   {
1175     return Color.white;
1176     // if (!pdbfile.equals(pdbentry.getFile()))
1177     // return null;
1178
1179     // return new Color(viewer.getAtomArgb(atomIndex));
1180   }
1181
1182   @Override
1183   public void updateColours(Object source)
1184   {
1185     colourBySequence();
1186     redrawneeded = true;
1187     repaint();
1188   }
1189
1190   @Override
1191   public void releaseReferences(Object svl)
1192   {
1193     // TODO Auto-generated method stub
1194
1195   }
1196
1197   @Override
1198   public boolean isListeningFor(SequenceI seq)
1199   {
1200     if (sequence != null)
1201     {
1202       for (SequenceI s : sequence)
1203       {
1204         if (s == seq)
1205         {
1206           return true;
1207         }
1208       }
1209     }
1210     return false;
1211   }
1212 }