more clustalx bugs fixed
[jalview.git] / src / jalview / gui / SeqPanel.java
1 package jalview.gui;\r
2 \r
3 import java.awt.*;\r
4 import java.awt.event.*;\r
5 import jalview.datamodel.*;\r
6 import javax.swing.*;\r
7 import java.util.*;\r
8 import jalview.schemes.*;\r
9 import jalview.analysis.Conservation;\r
10 \r
11 public class SeqPanel extends JPanel\r
12 {\r
13 \r
14   public    SeqCanvas         seqCanvas;\r
15   public    AlignmentPanel    ap;\r
16 \r
17   protected int lastres;\r
18   protected int startseq;\r
19   int startEdit=-1;\r
20   int endEdit=-1;\r
21 \r
22   protected AlignViewport av;\r
23 \r
24   // if character is inserted or deleted, we will need to recalculate the conservation\r
25   int seqEditOccurred = -1;\r
26 \r
27   ScrollThread scrollThread = null;\r
28   boolean mouseDragging = false;\r
29 \r
30   boolean editingSeqs = false;\r
31   boolean groupEditing = false;\r
32 \r
33 \r
34   public SeqPanel(AlignViewport avp, AlignmentPanel p) {\r
35     this.av         = avp;\r
36 \r
37     seqCanvas  = new SeqCanvas(avp);\r
38     setLayout(new BorderLayout());\r
39     add(seqCanvas, BorderLayout.CENTER);\r
40 \r
41     ap = p;\r
42 \r
43     addMouseMotionListener( new MouseMotionAdapter()\r
44     {\r
45       public void mouseMoved(MouseEvent evt)\r
46       {\r
47        if(av.getWrapAlignment())\r
48          return;\r
49         doMouseMoved(evt);    }\r
50 \r
51       public void mouseDragged(MouseEvent evt)\r
52       {\r
53         if(av.getWrapAlignment())\r
54          return;\r
55         if( editingSeqs )\r
56           doMouseDragged(evt);\r
57         else\r
58           doMouseDraggedDefineMode(evt);\r
59       }\r
60     });\r
61 \r
62     addMouseListener( new MouseAdapter()\r
63     {\r
64       public void mouseReleased(MouseEvent evt)\r
65       {\r
66         if(av.getWrapAlignment())\r
67          return;\r
68         if(editingSeqs)\r
69           doMouseReleased(evt);\r
70         else\r
71           doMouseReleasedDefineMode(evt);\r
72 \r
73       }\r
74       public void mousePressed(MouseEvent evt)\r
75       {\r
76         if(av.getWrapAlignment())\r
77          return;\r
78         if(evt.isShiftDown() || evt.isAltDown() || evt.isControlDown())\r
79         {\r
80           if(evt.isAltDown() || evt.isControlDown())\r
81             groupEditing = true;\r
82 \r
83           editingSeqs = true;\r
84           doMousePressed(evt);\r
85         }\r
86         else\r
87           doMousePressedDefineMode(evt);\r
88       }\r
89       public void mouseExited(MouseEvent evt)\r
90       {\r
91         if (av.getWrapAlignment() || editingSeqs)\r
92                 return;\r
93               doMouseExitedDefineMode(evt);\r
94 \r
95       }\r
96       public void mouseEntered(MouseEvent evt)\r
97       {\r
98         if (av.getWrapAlignment() || editingSeqs)\r
99           return;\r
100         doMouseEnteredDefineMode(evt);\r
101       }\r
102 \r
103     });\r
104     repaint();\r
105   }\r
106 \r
107 \r
108   public void doMouseReleased(MouseEvent evt) {\r
109 \r
110     if(seqEditOccurred>-1)\r
111       editOccurred(seqEditOccurred);\r
112 \r
113     startseq = -1;\r
114     lastres  = -1;\r
115     seqEditOccurred = -1;\r
116     editingSeqs  = false;\r
117     groupEditing = false;\r
118 \r
119     ap.repaint();\r
120   }\r
121 \r
122   public void doMousePressed(MouseEvent evt) {\r
123 \r
124     ap.alignFrame.addHistoryItem( new HistoryItem(\r
125         "Edit Sequence",av.alignment, HistoryItem.EDIT));\r
126 \r
127     int seq;\r
128     int res;\r
129 \r
130     int x = evt.getX();\r
131     int y = evt.getY();\r
132 \r
133     res = x/av.getCharWidth() + av.getStartRes();\r
134     seq = y/av.getCharHeight() + av.getStartSeq();\r
135 \r
136     if (seq < av.getAlignment().getHeight() &&\r
137         res < av.getAlignment().getSequenceAt(seq).getLength())\r
138     {\r
139       startseq = seq;\r
140       lastres = res;\r
141     }\r
142     else\r
143     {\r
144       startseq = -1;\r
145       lastres = -1;\r
146     }\r
147 \r
148     startEdit = lastres;\r
149     endEdit = lastres;\r
150 \r
151     return;\r
152   }\r
153 \r
154   public void doMouseMoved(MouseEvent evt)\r
155   {\r
156     int res=0, seq=0;\r
157     int x = evt.getX();\r
158     int y = evt.getY();\r
159     if(av.wrapAlignment)\r
160     {\r
161       y -= 2*av.charHeight;\r
162       int chunkHeight = (av.getAlignment().getHeight()+2)*av.charHeight;\r
163 \r
164 \r
165       res =   (int)((y/chunkHeight)*(getWidth()/av.charWidth)) +  x/av.getCharWidth() + av.getStartRes();\r
166 \r
167       y %= chunkHeight;\r
168       seq =     y / av.getCharHeight() + av.getStartSeq();\r
169 \r
170     }\r
171     else\r
172     {\r
173       res = x / av.getCharWidth()  + av.getStartRes();\r
174       seq = y / av.getCharHeight() + av.getStartSeq();\r
175     }\r
176 \r
177 \r
178     if(seq>=av.getAlignment().getHeight())\r
179       return;\r
180 \r
181     SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
182     if(res>sequence.getLength())\r
183       return;\r
184 \r
185     Object obj = ResidueProperties.aa2Triplet.get( sequence.getCharAt(res)+"" ) ;\r
186     String aa = "";\r
187     if(obj!=null)\r
188          aa = obj.toString();\r
189 \r
190     StringBuffer text = new StringBuffer("Sequence " +(seq+1)+" ID: "+sequence.getName());\r
191     if(aa!="")\r
192       text.append("  Residue: "+aa+" ("+  av.getAlignment().getSequenceAt(seq).findPosition(res)+")");\r
193 \r
194     ap.alignFrame.statusBar.setText(text.toString());\r
195 \r
196     // use aa to see if the mouse pointer is on a\r
197     if(  av.showSequenceFeatures)\r
198     {\r
199       Vector features = sequence.getSequenceFeatures();\r
200       Enumeration e = features.elements();\r
201       StringBuffer sbuffer = new StringBuffer();\r
202 \r
203 \r
204       while (e.hasMoreElements())\r
205       {\r
206         SequenceFeature sf = (SequenceFeature) e.nextElement();\r
207         if (sf.getStart() <= sequence.findPosition(res) &&\r
208             sf.getEnd() >= sequence.findPosition(res))\r
209         {\r
210           if(sbuffer.length()>0)\r
211             sbuffer.append("; ");\r
212           sbuffer.append(sf.getType() + " " + sf.getDescription());\r
213           if(sf.getStatus().length()>0)\r
214             sbuffer.append(" ("+sf.getStatus()+")");\r
215         }\r
216 \r
217       }\r
218 \r
219       ToolTipManager.sharedInstance().registerComponent(this);\r
220       this.setToolTipText(sbuffer.toString());\r
221     }\r
222 \r
223 \r
224   }\r
225 \r
226   public void doMouseDragged(MouseEvent evt) {\r
227 \r
228     // If we're dragging we're editing\r
229     int res = evt.getX() / av.getCharWidth() + av.getStartRes();\r
230     if (res < 0)\r
231       res = 0;\r
232 \r
233     if (lastres == -1 || lastres == res)\r
234       return;\r
235 \r
236     boolean dragRight = true;\r
237     if (res < av.getAlignment().getWidth() && res < lastres)\r
238       dragRight = false;\r
239 \r
240 \r
241     if (res != lastres)\r
242     {\r
243         // Group editing\r
244         if (groupEditing)\r
245         {\r
246           SequenceGroup sg = av.getSelectionGroup();\r
247           if(sg==null)\r
248           {\r
249             lastres=-1;\r
250             return;\r
251           }\r
252 \r
253             // drag to right\r
254             if(dragRight)\r
255                 sg.setEndRes(sg.getEndRes() + (res-lastres));\r
256 \r
257             // drag to left\r
258             else\r
259             {\r
260                /// Are we able to delete?\r
261                // ie are all columns blank?\r
262                boolean deleteAllowed = false;\r
263               for (int s = 0; s < sg.getSize(); s++)\r
264               {\r
265                 SequenceI seq = sg.getSequenceAt(s);\r
266                 for (int j=res; j<lastres; j++)\r
267                 {\r
268                   if(seq.getSequence().length()<=j)\r
269                     continue;\r
270 \r
271                   if(!jalview.util.Comparison.isGap(seq.getSequence().charAt(j)))\r
272                   {\r
273                     // Not a gap, block edit not valid\r
274                     res=j+1;\r
275                     deleteAllowed = false;\r
276                     continue;\r
277                   }\r
278                   deleteAllowed = true;\r
279                 }\r
280               }\r
281 \r
282               if(!deleteAllowed)\r
283               {\r
284                 lastres = -1;\r
285                 return;\r
286               }\r
287 \r
288               sg.setEndRes(sg.getEndRes() - (lastres-res));\r
289             }\r
290 \r
291 \r
292             for (int i = 0; i < sg.getSize(); i++)\r
293             {\r
294               SequenceI s = sg.getSequenceAt(i);\r
295               int k = av.alignment.findIndex(s);\r
296 \r
297               // drag to right\r
298               if (dragRight)\r
299                 for (int j = lastres; j < res; j++)\r
300                   insertChar(j, k);\r
301 \r
302               // drag to left\r
303               else\r
304               {\r
305                 for (int j = res; j < lastres; j++)\r
306                 {\r
307                   if(s.getLength()>j)\r
308                     deleteChar(res, k);\r
309                 }\r
310               }\r
311             }\r
312         }\r
313         else /////Editing a single sequence///////////\r
314         {\r
315           if (res < av.getAlignment().getWidth() && res > lastres)\r
316           {\r
317             // dragging to the right\r
318             for (int j = lastres; j < res; j++)\r
319               insertChar(j, startseq);\r
320           }\r
321           else if (res < av.getAlignment().getWidth() && res < lastres)\r
322           {\r
323             // dragging to the left\r
324             for (int j = lastres; j > res; j--)\r
325             {\r
326               if( jalview.util.Comparison.isGap(\r
327                 av.alignment.getSequenceAt(startseq).getSequence().charAt(res)))\r
328 \r
329               deleteChar(res, startseq);\r
330               else\r
331               {\r
332 \r
333                 break;\r
334               }\r
335             }\r
336           }\r
337 \r
338         }\r
339     }\r
340 \r
341     endEdit = res;\r
342     lastres = res;\r
343     repaint();\r
344   }\r
345 \r
346   public void drawChars(int seqstart, int seqend, int start) {\r
347     seqCanvas.drawPanel(seqCanvas.gg, start,av.getEndRes(),seqstart,seqend,av.getStartRes(),av.getStartSeq(),0);\r
348     repaint();\r
349   }\r
350 \r
351   public void insertChar(int j, int seq)\r
352   {\r
353     av.alignment.getSequenceAt(seq).insertCharAt(j, av.getGapCharacter());\r
354     seqEditOccurred=seq;\r
355   }\r
356 \r
357   public void deleteChar(int j, int seq)\r
358   {\r
359     av.alignment.getSequenceAt(seq).deleteCharAt(j);\r
360     seqEditOccurred=seq;\r
361 \r
362     av.alignment.getWidth();\r
363     repaint();\r
364   }\r
365 \r
366 \r
367   void editOccurred(int i)\r
368   {\r
369     if(endEdit==startEdit)\r
370     {\r
371       ap.alignFrame.historyList.pop();\r
372       ap.alignFrame.updateEditMenuBar();\r
373     }\r
374 \r
375     av.updateConservation();\r
376     av.updateConsensus();\r
377 \r
378     // Y O Y CLUSTALX\r
379     ColourSchemeI cs = av.getGlobalColourScheme();\r
380     if(cs instanceof ConservationColourScheme)\r
381     {\r
382       ConservationColourScheme ccs =  (ConservationColourScheme) cs;\r
383       if(ccs.cs instanceof ClustalxColourScheme)\r
384      {\r
385         Conservation c = new Conservation("All",\r
386                                           ResidueProperties.propHash, 3,\r
387                                           av.alignment.getSequences(), 0,\r
388                                           av.alignment.getWidth() - 1);\r
389         c.calculate();\r
390         c.verdict(false, av.ConsPercGaps);\r
391 \r
392         ClustalxColourScheme cxs = (ClustalxColourScheme)ccs.cs;\r
393         cxs.resetClustalX(av.alignment.getSequences(),  av.alignment.getWidth());\r
394         ccs = new ConservationColourScheme(c, cxs);\r
395         av.setGlobalColourScheme(ccs);\r
396      }\r
397     }\r
398 \r
399     if(cs instanceof ClustalxColourScheme)\r
400     {\r
401       ((ClustalxColourScheme)cs).resetClustalX(av.alignment.getSequences(),\r
402                                     av.alignment.getWidth());\r
403       av.setGlobalColourScheme(cs);\r
404     }\r
405 \r
406   }\r
407 \r
408 //////////////////////////////////////////\r
409 /////Everything below this is for defining the boundary of the rubberband\r
410 //////////////////////////////////////////\r
411   int oldSeq = -1;\r
412   public void doMousePressedDefineMode(MouseEvent evt)\r
413   {\r
414     int res = evt.getX()/av.getCharWidth() + av.getStartRes();\r
415     int seq = evt.getY()/av.getCharHeight() + av.getStartSeq();\r
416     oldSeq = seq;\r
417 \r
418     SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
419 \r
420     if(sequence==null || res>sequence.getLength())\r
421       return;\r
422 \r
423     stretchGroup = av.getSelectionGroup();\r
424 \r
425     if(stretchGroup == null)\r
426      {\r
427        stretchGroup = av.alignment.findGroup( sequence );\r
428        if(stretchGroup!=null && res>stretchGroup.getStartRes() && res<stretchGroup.getEndRes())\r
429          av.setSelectionGroup(stretchGroup);\r
430        else\r
431          stretchGroup = null;\r
432      }\r
433 \r
434     else if(!stretchGroup.sequences.contains(sequence)\r
435             || stretchGroup.getStartRes()>res\r
436             || stretchGroup.getEndRes()<res)\r
437      {\r
438        stretchGroup = null;\r
439 \r
440        SequenceGroup[] allGroups = av.alignment.findAllGroups( sequence );\r
441 \r
442        if (allGroups != null)\r
443          for (int i = 0; i < allGroups.length; i++)\r
444            if (allGroups[i].getStartRes() <= res &&\r
445                allGroups[i].getEndRes() >= res)\r
446            {\r
447              stretchGroup = allGroups[i];\r
448              av.setSelectionGroup(stretchGroup);\r
449              break;\r
450            }\r
451      }\r
452 \r
453     if(stretchGroup==null)\r
454     {\r
455       // define a new group here\r
456       SequenceGroup sg = new SequenceGroup();\r
457       sg.setStartRes(res);\r
458       sg.setEndRes(res);\r
459       sg.addSequence( sequence );\r
460       av.setSelectionGroup( sg );\r
461       stretchGroup = sg;\r
462 \r
463       if(av.getConservationSelected())\r
464         SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(), "Background");\r
465       if(av.getAbovePIDThreshold())\r
466         SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(), "Background");\r
467 \r
468     }\r
469     else if( javax.swing.SwingUtilities.isRightMouseButton(evt))\r
470     {\r
471         jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu( ap , null);\r
472         pop.show(this, evt.getX(), evt.getY());\r
473 \r
474     // edit the properties of existing group\r
475     }\r
476 \r
477     if(stretchGroup!=null && stretchGroup.getEndRes()==res)\r
478       // Edit end res position of selected group\r
479       changeEndRes = true;\r
480 \r
481    else if(stretchGroup!=null && stretchGroup.getStartRes()==res)\r
482       // Edit end res position of selected group\r
483       changeStartRes = true;\r
484 \r
485     stretchGroup.getWidth();\r
486 \r
487     repaint();\r
488 \r
489   }\r
490 \r
491   boolean changeEndSeq = false;\r
492   boolean changeStartSeq = false;\r
493   boolean changeEndRes = false;\r
494   boolean changeStartRes = false;\r
495   SequenceGroup stretchGroup = null;\r
496 \r
497   public void doMouseReleasedDefineMode(MouseEvent evt)\r
498   {\r
499     mouseDragging = false;\r
500 \r
501     if(stretchGroup==null)\r
502       return;\r
503 \r
504     if(stretchGroup.cs instanceof ClustalxColourScheme)\r
505     {\r
506       stretchGroup.cs = new ClustalxColourScheme(stretchGroup.sequences, av.alignment.getWidth());\r
507       repaint();\r
508     }\r
509 \r
510     else if(stretchGroup.cs instanceof ConservationColourScheme)\r
511     {\r
512        ConservationColourScheme ccs = (ConservationColourScheme)stretchGroup.cs;\r
513        stretchGroup.cs = ccs;\r
514        SliderPanel.setConservationSlider(ap, stretchGroup.cs, stretchGroup.getName()) ;\r
515 \r
516        repaint();\r
517     }\r
518     else\r
519         SliderPanel.setPIDSliderSource(ap, stretchGroup.cs, stretchGroup.getName());\r
520 \r
521 \r
522     changeEndRes = false;\r
523     changeStartRes = false;\r
524     stretchGroup = null;\r
525     ap.idPanel.repaint();\r
526   }\r
527 \r
528 \r
529   boolean remove = false;\r
530   public void doMouseDraggedDefineMode(MouseEvent evt)\r
531   {\r
532     int res = evt.getX()/av.getCharWidth() + av.getStartRes();\r
533     int y = evt.getY()/av.getCharHeight() + av.getStartSeq();\r
534 \r
535     if(stretchGroup==null)\r
536       return;\r
537 \r
538     if(res>av.alignment.getWidth())\r
539       res = av.alignment.getWidth()-1;\r
540 \r
541 \r
542     if(stretchGroup.getEndRes()==res)\r
543       // Edit end res position of selected group\r
544       changeEndRes = true;\r
545 \r
546     else if(stretchGroup.getStartRes()==res)\r
547       // Edit start res position of selected group\r
548       changeStartRes = true;\r
549 \r
550 \r
551     if(res<av.getStartRes())\r
552       res = av.getStartRes();\r
553     else if(res>av.getEndRes())\r
554       res = av.getEndRes();\r
555 \r
556     if(changeEndRes)\r
557     {\r
558       if(res>stretchGroup.getStartRes()-1)\r
559         stretchGroup.setEndRes( res );\r
560     }\r
561     else if(changeStartRes)\r
562     {\r
563       if(res<stretchGroup.getEndRes()+1)\r
564         stretchGroup.setStartRes( res );\r
565     }\r
566 \r
567     int dragDirection = 0;\r
568     if (y > oldSeq)\r
569       dragDirection = 1;\r
570     else if (y < oldSeq)\r
571       dragDirection = -1;\r
572 \r
573     while (y != oldSeq && oldSeq>0 && y<av.alignment.getHeight())\r
574     {\r
575       // This routine ensures we don't skip any sequences, as the\r
576       // selection is quite slow.\r
577       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
578 \r
579       oldSeq += dragDirection;\r
580       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
581 \r
582       if (stretchGroup.sequences.contains(nextSeq))\r
583       {\r
584         stretchGroup.deleteSequence(seq);\r
585         stretchGroup.deleteSequence(nextSeq);\r
586       }\r
587       else\r
588       {\r
589        if(seq!=null)\r
590         stretchGroup.addSequence(seq);\r
591         stretchGroup.addSequence(nextSeq);\r
592       }\r
593     }\r
594     oldSeq = y;\r
595     mouseDragging = true;\r
596     if(scrollThread!=null)\r
597       scrollThread.setEvent(evt);\r
598 \r
599     repaint();\r
600   }\r
601 \r
602   public void doMouseEnteredDefineMode(MouseEvent e)\r
603   {\r
604     if (scrollThread != null)\r
605       scrollThread.running = false;\r
606   }\r
607 \r
608   public void doMouseExitedDefineMode(MouseEvent e)\r
609   {\r
610     if (av.getWrapAlignment())\r
611       return;\r
612 \r
613     if(mouseDragging)\r
614       scrollThread = new ScrollThread();\r
615 \r
616   }\r
617   // this class allows scrolling off the bottom of the visible alignment\r
618   class ScrollThread extends Thread\r
619   {\r
620     MouseEvent evt;\r
621     boolean running = false;\r
622     public ScrollThread()\r
623     {\r
624       start();\r
625     }\r
626 \r
627     public void setEvent(MouseEvent e)\r
628     {\r
629       evt = e;\r
630     }\r
631 \r
632     public void stopScrolling()\r
633     {\r
634       running = false;\r
635     }\r
636 \r
637     public void run()\r
638     {\r
639       running = true;\r
640       while (running)\r
641       {\r
642         if(evt!=null)\r
643         {\r
644 \r
645           if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)\r
646             running = ap.scrollUp(true);\r
647 \r
648           if (mouseDragging && evt.getY() >= getHeight() &&\r
649               av.alignment.getHeight() > av.getEndSeq())\r
650             running = ap.scrollUp(false);\r
651 \r
652           if (mouseDragging && evt.getX() < 0)\r
653             running = ap.scrollRight(true);\r
654 \r
655           else if (mouseDragging && evt.getX() >= getWidth())\r
656             running = ap.scrollRight(false);\r
657         }\r
658 \r
659         try\r
660         {\r
661           Thread.sleep(75);\r
662         }\r
663         catch (Exception ex)\r
664         {}\r
665       }\r
666     }\r
667 }\r
668 \r
669 \r
670 \r
671 }\r
672 \r
673 \r
674 \r
675 \r