enable mouseover between DNA coding regions and translated peptide location and broad...
[jalview.git] / src / jalview / structure / StructureSelectionManager.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.structure;
20
21 import java.io.*;
22 import java.util.*;
23
24 import MCview.*;
25 import jalview.analysis.*;
26 import jalview.datamodel.*;
27
28 public class StructureSelectionManager
29 {
30   static StructureSelectionManager instance;
31
32   StructureMapping[] mappings;
33
34   Hashtable mappingData = new Hashtable();
35
36   public static StructureSelectionManager getStructureSelectionManager()
37   {
38     if (instance == null)
39     {
40       instance = new StructureSelectionManager();
41     }
42
43     return instance;
44   }
45
46   /**
47    * flag controlling whether SeqMappings are relayed from received sequence
48    * mouse over events to other sequences
49    */
50   boolean relaySeqMappings = true;
51
52   /**
53    * Enable or disable relay of seqMapping events to other sequences. You might
54    * want to do this if there are many sequence mappings and the host computer
55    * is slow
56    * 
57    * @param relay
58    */
59   public void setRelaySeqMappings(boolean relay)
60   {
61     relaySeqMappings = relay;
62   }
63
64   /**
65    * get the state of the relay seqMappings flag.
66    * 
67    * @return true if sequence mouse overs are being relayed to other mapped
68    *         sequences
69    */
70   public boolean isRelaySeqMappingsEnabled()
71   {
72     return relaySeqMappings;
73   }
74
75   Vector listeners = new Vector();
76
77   public void addStructureViewerListener(Object svl)
78   {
79     if (!listeners.contains(svl))
80     {
81       listeners.addElement(svl);
82     }
83   }
84
85   public String alreadyMappedToFile(String pdbid)
86   {
87     if (mappings != null)
88     {
89       for (int i = 0; i < mappings.length; i++)
90       {
91         if (mappings[i].getPdbId().equals(pdbid))
92         {
93           return mappings[i].pdbfile;
94         }
95       }
96     }
97     return null;
98   }
99
100   /*
101    * There will be better ways of doing this in the future, for now we'll use
102    * the tried and tested MCview pdb mapping
103    */
104   synchronized public MCview.PDBfile setMapping(SequenceI[] sequence,
105           String[] targetChains, String pdbFile, String protocol)
106   {
107     MCview.PDBfile pdb = null;
108     try
109     {
110       pdb = new MCview.PDBfile(pdbFile, protocol);
111     } catch (Exception ex)
112     {
113       ex.printStackTrace();
114       return null;
115     }
116
117     String targetChain;
118     for (int s = 0; s < sequence.length; s++)
119     {
120       if (targetChains != null && targetChains[s] != null)
121         targetChain = targetChains[s];
122       else if (sequence[s].getName().indexOf("|") > -1)
123       {
124         targetChain = sequence[s].getName().substring(
125                 sequence[s].getName().lastIndexOf("|") + 1);
126       }
127       else
128         targetChain = "";
129
130       int max = -10;
131       AlignSeq maxAlignseq = null;
132       String maxChainId = " ";
133       PDBChain maxChain = null;
134
135       for (int i = 0; i < pdb.chains.size(); i++)
136       {
137         AlignSeq as = new AlignSeq(sequence[s], ((PDBChain) pdb.chains
138                 .elementAt(i)).sequence, AlignSeq.PEP);
139         as.calcScoreMatrix();
140         as.traceAlignment();
141         PDBChain chain = ((PDBChain) pdb.chains.elementAt(i));
142
143         if (as.maxscore > max
144                 || (as.maxscore == max && chain.id.equals(targetChain)))
145         {
146           maxChain = chain;
147           max = as.maxscore;
148           maxAlignseq = as;
149           maxChainId = chain.id;
150         }
151       }
152
153       final StringBuffer mappingDetails = new StringBuffer();
154       mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
155               + maxChain.sequence.getSequenceAsString());
156       mappingDetails.append("\nNo of residues = "
157               + maxChain.residues.size() + "\n\n");
158       PrintStream ps = new PrintStream(System.out)
159       {
160         public void print(String x)
161         {
162           mappingDetails.append(x);
163         }
164
165         public void println()
166         {
167           mappingDetails.append("\n");
168         }
169       };
170
171       maxAlignseq.printAlignment(ps);
172
173       mappingDetails.append("\nPDB start/end " + maxAlignseq.seq2start
174               + " " + maxAlignseq.seq2end);
175       mappingDetails.append("\nSEQ start/end "
176               + (maxAlignseq.seq1start + sequence[s].getStart() - 1) + " "
177               + (maxAlignseq.seq1end + sequence[s].getEnd() - 1));
178
179       maxChain.makeExactMapping(maxAlignseq, sequence[s]);
180
181       maxChain.transferRESNUMFeatures(sequence[s], null);
182
183       int[][] mapping = new int[sequence[s].getEnd() + 2][2];
184       int resNum = -10000;
185       int index = 0;
186
187       do
188       {
189         Atom tmp = (Atom) maxChain.atoms.elementAt(index);
190         if (resNum != tmp.resNumber && tmp.alignmentMapping != -1)
191         {
192           resNum = tmp.resNumber;
193           mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber;
194           mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex;
195         }
196
197         index++;
198       } while (index < maxChain.atoms.size());
199
200       if (mappings == null)
201       {
202         mappings = new StructureMapping[1];
203       }
204       else
205       {
206         StructureMapping[] tmp = new StructureMapping[mappings.length + 1];
207         System.arraycopy(mappings, 0, tmp, 0, mappings.length);
208         mappings = tmp;
209       }
210
211       if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
212         pdbFile = "INLINE" + pdb.id;
213
214       mappings[mappings.length - 1] = new StructureMapping(sequence[s],
215               pdbFile, pdb.id, maxChainId, mapping, mappingDetails
216                       .toString());
217       maxChain.transferResidueAnnotation(mappings[mappings.length - 1]);
218     }
219     // ///////
220
221     return pdb;
222   }
223
224   public void removeStructureViewerListener(Object svl, String pdbfile)
225   {
226     listeners.removeElement(svl);
227
228     boolean removeMapping = true;
229
230     StructureListener sl;
231     for (int i = 0; i < listeners.size(); i++)
232     {
233       if (listeners.elementAt(i) instanceof StructureListener)
234       {
235         sl = (StructureListener) listeners.elementAt(i);
236         if (sl.getPdbFile().equals(pdbfile))
237         {
238           removeMapping = false;
239           break;
240         }
241       }
242     }
243
244     if (removeMapping && mappings != null)
245     {
246       Vector tmp = new Vector();
247       for (int i = 0; i < mappings.length; i++)
248       {
249         if (!mappings[i].pdbfile.equals(pdbfile))
250         {
251           tmp.addElement(mappings[i]);
252         }
253       }
254
255       mappings = new StructureMapping[tmp.size()];
256       tmp.copyInto(mappings);
257     }
258   }
259
260   public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
261   {
262     SearchResults results = null;
263     for (int i = 0; i < listeners.size(); i++)
264     {
265       if (listeners.elementAt(i) instanceof SequenceListener)
266       {
267         if (results == null)
268         {
269           results = new SearchResults();
270
271           for (int j = 0; j < mappings.length; j++)
272           {
273             if (mappings[j].pdbfile.equals(pdbfile)
274                     && mappings[j].pdbchain.equals(chain))
275             {
276               results.addResult(mappings[j].sequence, mappings[j]
277                       .getSeqPos(pdbResNum), mappings[j]
278                       .getSeqPos(pdbResNum));
279             }
280           }
281         }
282         if (results.getSize() > 0)
283         {
284           ((SequenceListener) listeners.elementAt(i))
285                   .highlightSequence(results);
286         }
287
288       }
289     }
290   }
291
292   Vector seqmappings = null; // should be a simpler list of mapped seuqence
293
294   // pairs
295
296   public void mouseOverSequence(SequenceI seq, int index)
297   {
298     boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
299     SearchResults results = null;
300     StructureListener sl;
301     int atomNo = 0;
302     for (int i = 0; i < listeners.size(); i++)
303     {
304       if (listeners.elementAt(i) instanceof StructureListener)
305       {
306         sl = (StructureListener) listeners.elementAt(i);
307
308         for (int j = 0; j < mappings.length; j++)
309         {
310           if (mappings[j].sequence == seq)
311           {
312             atomNo = mappings[j].getAtomNum(index);
313
314             if (atomNo > 0)
315             {
316               sl.highlightAtom(atomNo, mappings[j].getPDBResNum(index),
317                       mappings[j].pdbchain, mappings[j].pdbfile);
318             }
319           }
320         }
321       }
322       else
323       {
324         if (relaySeqMappings && hasSequenceListeners
325                 && listeners.elementAt(i) instanceof SequenceListener)
326         {
327           // DEBUG 
328           //System.err.println("relay Seq " + seq.getDisplayId(false) + " " +
329           //  index);
330
331           if (results == null)
332           {
333             results = new SearchResults();
334             if (index >= seq.getStart() && index <= seq.getEnd())
335             {
336               // construct highlighted sequence list
337
338               if (seqmappings!=null)
339               {
340                 Enumeration e = seqmappings.elements();
341                 while (e.hasMoreElements())
342
343                 {
344                   ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
345                           seq, index, results);
346                 }
347               }
348               // hasSequenceListeners = results.getSize() > 0;
349               if (handlingVamsasMo)
350               {
351                 // add in additional direct sequence and/or dataset sequence
352                 // highlighting
353                 results.addResult(seq, index, index);
354               }
355             }
356           }
357           if (hasSequenceListeners)
358           {
359             ((SequenceListener) listeners.elementAt(i))
360                     .highlightSequence(results);
361           }
362         }
363         else if (listeners.elementAt(i) instanceof VamsasListener
364                 && !handlingVamsasMo)
365         {
366           // DEBUG 
367           //System.err.println("Vamsas from Seq " + seq.getDisplayId(false) + " " +
368           // index);
369           // pass the mouse over onto the VamsasListener(s)
370           ((VamsasListener) listeners.elementAt(i)).mouseOver(seq, index);
371         }
372       }
373     }
374   }
375
376   /**
377    * true if a mouse over event from an external (ie Vamsas) source is being
378    * handled
379    */
380   boolean handlingVamsasMo = false;
381
382   /**
383    * as mouseOverSequence but only route event to SequenceListeners
384    * 
385    * @param sequenceI
386    * @param position
387    */
388   public void mouseOverVamsasSequence(SequenceI sequenceI, int position)
389   {
390     handlingVamsasMo = true;
391     mouseOverSequence(sequenceI, position);
392     handlingVamsasMo = false;
393   }
394
395   public Annotation[] colourSequenceFromStructure(SequenceI seq,
396           String pdbid)
397   {
398     return null;
399     // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
400     // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
401     /*
402      * Annotation [] annotations = new Annotation[seq.getLength()];
403      * 
404      * StructureListener sl; int atomNo = 0; for (int i = 0; i <
405      * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
406      * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
407      * 
408      * for (int j = 0; j < mappings.length; j++) {
409      * 
410      * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid) &&
411      * mappings[j].pdbfile.equals(sl.getPdbFile())) { System.out.println(pdbid+"
412      * "+mappings[j].getPdbId() +" "+mappings[j].pdbfile);
413      * 
414      * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
415      * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
416      * 
417      * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
418      * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
419      * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
420      * mappings[j].pdbfile); }
421      * 
422      * annotations[index] = new Annotation("X",null,' ',0,col); } return
423      * annotations; } } } }
424      * 
425      * return annotations;
426      */
427   }
428
429   public void structureSelectionChanged()
430   {
431   }
432
433   public void sequenceSelectionChanged()
434   {
435   }
436
437   public void sequenceColoursChanged(Object source)
438   {
439     StructureListener sl;
440     for (int i = 0; i < listeners.size(); i++)
441     {
442       if (listeners.elementAt(i) instanceof StructureListener)
443       {
444         sl = (StructureListener) listeners.elementAt(i);
445         sl.updateColours(source);
446       }
447     }
448   }
449
450   public StructureMapping[] getMapping(String pdbfile)
451   {
452     Vector tmp = new Vector();
453     for (int i = 0; i < mappings.length; i++)
454     {
455       if (mappings[i].pdbfile.equals(pdbfile))
456       {
457         tmp.addElement(mappings[i]);
458       }
459     }
460
461     StructureMapping[] ret = new StructureMapping[tmp.size()];
462     for (int i = 0; i < tmp.size(); i++)
463     {
464       ret[i] = (StructureMapping) tmp.elementAt(i);
465     }
466
467     return ret;
468   }
469
470   public String printMapping(String pdbfile)
471   {
472     StringBuffer sb = new StringBuffer();
473     for (int i = 0; i < mappings.length; i++)
474     {
475       if (mappings[i].pdbfile.equals(pdbfile))
476       {
477         sb.append(mappings[i].mappingDetails);
478       }
479     }
480
481     return sb.toString();
482   }
483
484   private int[] seqmappingrefs = null; // refcount for seqmappings elements
485
486   private synchronized void modifySeqMappingList(boolean add,
487           AlignedCodonFrame[] codonFrames)
488   {
489     if (!add && (seqmappings == null || seqmappings.size() == 0))
490       return;
491     if (seqmappings == null)
492       seqmappings = new Vector();
493     if (codonFrames != null && codonFrames.length > 0)
494     {
495       for (int cf = 0; cf < codonFrames.length; cf++)
496       {
497         if (seqmappings.contains(codonFrames[cf]))
498         {
499           if (add)
500           {
501             seqmappingrefs[seqmappings.indexOf(codonFrames[cf])]++;
502           }
503           else
504           {
505             if (--seqmappingrefs[seqmappings.indexOf(codonFrames[cf])] <= 0)
506             {
507               int pos = seqmappings.indexOf(codonFrames[cf]);
508               int[] nr = new int[seqmappingrefs.length - 1];
509               if (pos > 0)
510               {
511                 System.arraycopy(seqmappingrefs, 0, nr, 0, pos);
512               }
513               if (pos < seqmappingrefs.length - 1)
514               {
515                 System.arraycopy(seqmappingrefs, pos + 1, nr, 0,
516                         seqmappingrefs.length - pos - 2);
517               }
518             }
519           }
520         }
521         else
522         {
523           if (add)
524           {
525             seqmappings.addElement(codonFrames[cf]);
526
527             int[] nsr = new int[(seqmappingrefs == null) ? 1
528                     : seqmappingrefs.length + 1];
529             if (seqmappingrefs != null && seqmappingrefs.length > 0)
530               System.arraycopy(seqmappingrefs, 0, nsr, 0,
531                       seqmappingrefs.length);
532             nsr[(seqmappingrefs == null) ? 0 : seqmappingrefs.length] = 1;
533             seqmappingrefs = nsr;
534           }
535         }
536       }
537     }
538   }
539
540   public void removeMappings(AlignedCodonFrame[] codonFrames)
541   {
542     modifySeqMappingList(false, codonFrames);
543   }
544
545   public void addMappings(AlignedCodonFrame[] codonFrames)
546   {
547     modifySeqMappingList(true, codonFrames);
548   }
549 }