javadoc
[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    * highlight regions associated with a position (indexpos) in seq
295    * @param seq the sequeence that the mouse over occured on
296    * @param indexpos the absolute position being mouseovered in seq (0 to seq.length())
297    * @param index the sequence position (if -1, seq.findPosition is called to resolve the residue number)
298    */
299   public void mouseOverSequence(SequenceI seq, int indexpos, int index)
300   {
301     boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
302     SearchResults results = null;
303     if (index==-1)
304       index=seq.findPosition(indexpos);
305     StructureListener sl;
306     int atomNo = 0;
307     for (int i = 0; i < listeners.size(); i++)
308     {
309       if (listeners.elementAt(i) instanceof StructureListener)
310       {
311         sl = (StructureListener) listeners.elementAt(i);
312
313         for (int j = 0; j < mappings.length; j++)
314         {
315           if (mappings[j].sequence == seq || mappings[j].sequence==seq.getDatasetSequence())
316           {
317             atomNo = mappings[j].getAtomNum(index);
318
319             if (atomNo > 0)
320             {
321               sl.highlightAtom(atomNo, mappings[j].getPDBResNum(index),
322                       mappings[j].pdbchain, mappings[j].pdbfile);
323             }
324           }
325         }
326       }
327       else
328       {
329         if (relaySeqMappings && hasSequenceListeners
330                 && listeners.elementAt(i) instanceof SequenceListener)
331         {
332           // DEBUG 
333           //System.err.println("relay Seq " + seq.getDisplayId(false) + " " +
334           //  index);
335
336           if (results == null)
337           {
338             results = new SearchResults();
339             if (index >= seq.getStart() && index <= seq.getEnd())
340             {
341               // construct highlighted sequence list
342
343               if (seqmappings!=null)
344               {
345                 Enumeration e = seqmappings.elements();
346                 while (e.hasMoreElements())
347
348                 {
349                   ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
350                           seq, index, results);
351                 }
352               }
353               // hasSequenceListeners = results.getSize() > 0;
354               if (handlingVamsasMo)
355               {
356                 // maybe have to resolve seq to a dataset seqeunce...
357                 // add in additional direct sequence and/or dataset sequence
358                 // highlighting
359                 results.addResult(seq, index, index);
360               }
361             }
362           }
363           if (hasSequenceListeners)
364           {
365             ((SequenceListener) listeners.elementAt(i))
366                     .highlightSequence(results);
367           }
368         }
369         else if (listeners.elementAt(i) instanceof VamsasListener
370                 && !handlingVamsasMo)
371         {
372           // DEBUG 
373           //System.err.println("Vamsas from Seq " + seq.getDisplayId(false) + " " +
374           // index);
375           // pass the mouse over and absolute position onto the VamsasListener(s)
376           ((VamsasListener) listeners.elementAt(i)).mouseOver(seq, indexpos);
377         }
378       }
379     }
380   }
381
382   /**
383    * true if a mouse over event from an external (ie Vamsas) source is being
384    * handled
385    */
386   boolean handlingVamsasMo = false;
387   long lastmsg=0;
388   /**
389    * as mouseOverSequence but only route event to SequenceListeners
390    * 
391    * @param sequenceI
392    * @param position in an alignment sequence
393    */
394   public void mouseOverVamsasSequence(SequenceI sequenceI, int position)
395   {
396     handlingVamsasMo = true;
397     long msg = sequenceI.hashCode()*(1+position);
398     if (lastmsg!=msg)
399     {
400       lastmsg = msg;
401       mouseOverSequence(sequenceI, position, -1);
402     }
403       handlingVamsasMo = false;
404   }
405
406   public Annotation[] colourSequenceFromStructure(SequenceI seq,
407           String pdbid)
408   {
409     return null;
410     // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
411     // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
412     /*
413      * Annotation [] annotations = new Annotation[seq.getLength()];
414      * 
415      * StructureListener sl; int atomNo = 0; for (int i = 0; i <
416      * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
417      * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
418      * 
419      * for (int j = 0; j < mappings.length; j++) {
420      * 
421      * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid) &&
422      * mappings[j].pdbfile.equals(sl.getPdbFile())) { System.out.println(pdbid+"
423      * "+mappings[j].getPdbId() +" "+mappings[j].pdbfile);
424      * 
425      * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
426      * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
427      * 
428      * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
429      * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
430      * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
431      * mappings[j].pdbfile); }
432      * 
433      * annotations[index] = new Annotation("X",null,' ',0,col); } return
434      * annotations; } } } }
435      * 
436      * return annotations;
437      */
438   }
439
440   public void structureSelectionChanged()
441   {
442   }
443
444   public void sequenceSelectionChanged()
445   {
446   }
447
448   public void sequenceColoursChanged(Object source)
449   {
450     StructureListener sl;
451     for (int i = 0; i < listeners.size(); i++)
452     {
453       if (listeners.elementAt(i) instanceof StructureListener)
454       {
455         sl = (StructureListener) listeners.elementAt(i);
456         sl.updateColours(source);
457       }
458     }
459   }
460
461   public StructureMapping[] getMapping(String pdbfile)
462   {
463     Vector tmp = new Vector();
464     for (int i = 0; i < mappings.length; i++)
465     {
466       if (mappings[i].pdbfile.equals(pdbfile))
467       {
468         tmp.addElement(mappings[i]);
469       }
470     }
471
472     StructureMapping[] ret = new StructureMapping[tmp.size()];
473     for (int i = 0; i < tmp.size(); i++)
474     {
475       ret[i] = (StructureMapping) tmp.elementAt(i);
476     }
477
478     return ret;
479   }
480
481   public String printMapping(String pdbfile)
482   {
483     StringBuffer sb = new StringBuffer();
484     for (int i = 0; i < mappings.length; i++)
485     {
486       if (mappings[i].pdbfile.equals(pdbfile))
487       {
488         sb.append(mappings[i].mappingDetails);
489       }
490     }
491
492     return sb.toString();
493   }
494
495   private int[] seqmappingrefs = null; // refcount for seqmappings elements
496
497   private synchronized void modifySeqMappingList(boolean add,
498           AlignedCodonFrame[] codonFrames)
499   {
500     if (!add && (seqmappings == null || seqmappings.size() == 0))
501       return;
502     if (seqmappings == null)
503       seqmappings = new Vector();
504     if (codonFrames != null && codonFrames.length > 0)
505     {
506       for (int cf = 0; cf < codonFrames.length; cf++)
507       {
508         if (seqmappings.contains(codonFrames[cf]))
509         {
510           if (add)
511           {
512             seqmappingrefs[seqmappings.indexOf(codonFrames[cf])]++;
513           }
514           else
515           {
516             if (--seqmappingrefs[seqmappings.indexOf(codonFrames[cf])] <= 0)
517             {
518               int pos = seqmappings.indexOf(codonFrames[cf]);
519               int[] nr = new int[seqmappingrefs.length - 1];
520               if (pos > 0)
521               {
522                 System.arraycopy(seqmappingrefs, 0, nr, 0, pos);
523               }
524               if (pos < seqmappingrefs.length - 1)
525               {
526                 System.arraycopy(seqmappingrefs, pos + 1, nr, 0,
527                         seqmappingrefs.length - pos - 2);
528               }
529             }
530           }
531         }
532         else
533         {
534           if (add)
535           {
536             seqmappings.addElement(codonFrames[cf]);
537
538             int[] nsr = new int[(seqmappingrefs == null) ? 1
539                     : seqmappingrefs.length + 1];
540             if (seqmappingrefs != null && seqmappingrefs.length > 0)
541               System.arraycopy(seqmappingrefs, 0, nsr, 0,
542                       seqmappingrefs.length);
543             nsr[(seqmappingrefs == null) ? 0 : seqmappingrefs.length] = 1;
544             seqmappingrefs = nsr;
545           }
546         }
547       }
548     }
549   }
550
551   public void removeMappings(AlignedCodonFrame[] codonFrames)
552   {
553     modifySeqMappingList(false, codonFrames);
554   }
555
556   public void addMappings(AlignedCodonFrame[] codonFrames)
557   {
558     modifySeqMappingList(true, codonFrames);
559   }
560 }