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