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