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