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