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