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