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