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