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