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