seals selection box if at bottom 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.RefreshPanels();\r
119     repaint();\r
120 \r
121   }\r
122 \r
123   public void doMousePressed(MouseEvent evt) {\r
124     ap.alignFrame.addHistoryItem("sequence edit");\r
125     int seq;\r
126     int res;\r
127 \r
128     int x = evt.getX();\r
129     int y = evt.getY();\r
130 \r
131     res = x/av.getCharWidth() + av.getStartRes();\r
132     seq = y/av.getCharHeight() + av.getStartSeq();\r
133 \r
134     if (seq < av.getAlignment().getHeight() &&\r
135         res < av.getAlignment().getSequenceAt(seq).getLength())\r
136     {\r
137       //char resstr = align.getSequenceAt(seq).getSequence().charAt(res);\r
138       // Find the residue's position in the sequence (res is the position\r
139       // in the alignment\r
140 \r
141       startseq = seq;\r
142 \r
143       if (startseq == (av.getAlignment().getHeight() - 1))\r
144         padseq = 1;\r
145       else\r
146         padseq = 1;\r
147 \r
148       startres = res;\r
149       lastres = res;\r
150 \r
151     }\r
152     else\r
153     {\r
154       startseq = -1;\r
155       startres = -1;\r
156       lastres = -1;\r
157     }\r
158 \r
159     return;\r
160   }\r
161 \r
162   public void doMouseMoved(MouseEvent evt)\r
163   {\r
164     int res=0, seq=0;\r
165     int x = evt.getX();\r
166     int y = evt.getY();\r
167     if(av.wrapAlignment)\r
168     {\r
169       y -= 2*av.charHeight;\r
170       int chunkHeight = (av.getAlignment().getHeight()+2)*av.charHeight;\r
171 \r
172 \r
173       res =   (int)((y/chunkHeight)*(getWidth()/av.charWidth)) +  x/av.getCharWidth() + av.getStartRes();\r
174 \r
175       y %= chunkHeight;\r
176       seq =     y / av.getCharHeight() + av.getStartSeq();\r
177 \r
178       //   chunkHeight =  (da.getHeight() + 2)*charHeight;\r
179       //  startx += chunkWidth;\r
180     }\r
181     else\r
182     {\r
183       res = x / av.getCharWidth()  + av.getStartRes();\r
184       seq = y / av.getCharHeight() + av.getStartSeq();\r
185     }\r
186 \r
187 \r
188     if(seq>=av.getAlignment().getHeight())\r
189       return;\r
190 \r
191     SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
192     if(res>sequence.getLength())\r
193       return;\r
194 \r
195     Object obj = ResidueProperties.aa2Triplet.get( sequence.getCharAt(res)+"" ) ;\r
196     String aa = "";\r
197     if(obj!=null)\r
198          aa = obj.toString();\r
199 \r
200     StringBuffer text = new StringBuffer("Sequence " +(seq+1)+" ID: "+sequence.getName());\r
201     if(aa!="")\r
202       text.append("  Residue: "+aa+" ("+  av.getAlignment().getSequenceAt(seq).findPosition(res)+")");\r
203 \r
204     ap.alignFrame.statusBar.setText(text.toString());\r
205 \r
206     // use aa to see if the mouse pointer is on a\r
207     if(  av.showSequenceFeatures)\r
208     {\r
209       Vector features = sequence.getSequenceFeatures();\r
210       Enumeration e = features.elements();\r
211       StringBuffer sbuffer = new StringBuffer();\r
212 \r
213 \r
214       while (e.hasMoreElements())\r
215       {\r
216         SequenceFeature sf = (SequenceFeature) e.nextElement();\r
217         if (sf.getStart() <= sequence.findPosition(res) &&\r
218             sf.getEnd() >= sequence.findPosition(res))\r
219         {\r
220           if(sbuffer.length()>0)\r
221             sbuffer.append("; ");\r
222           sbuffer.append(sf.getType() + " " + sf.getDescription());\r
223           if(sf.getStatus().length()>0)\r
224             sbuffer.append(" ("+sf.getStatus()+")");\r
225         }\r
226 \r
227       }\r
228 \r
229       ToolTipManager.sharedInstance().registerComponent(this);\r
230       this.setToolTipText(sbuffer.toString());\r
231     }\r
232 \r
233 \r
234   }\r
235 \r
236   public void doMouseDragged(MouseEvent evt) {\r
237     // If we're dragging we're editing\r
238 \r
239     if(lastres==-1)\r
240       return;\r
241 \r
242     int x = evt.getX();\r
243 \r
244     int res = x/av.getCharWidth() + av.getStartRes();\r
245     if (res < 0)\r
246       res = 0;\r
247 \r
248     if (res != lastres)\r
249     {\r
250         // Group editing\r
251         if (evt.isAltDown() || evt.isControlDown())\r
252         {\r
253           SequenceGroup sg = av.getSelectionGroup();\r
254           if(sg==null)\r
255             av.getAlignment().findGroup(startseq);\r
256           if (sg != null)\r
257           {\r
258             boolean deleteAllowed = false;\r
259             if (res < av.getAlignment().getWidth() && res < lastres)\r
260             {\r
261               /// Are we able to delete?\r
262               boolean allGaps = true;\r
263               for (int i = 0; i < sg.getSize(); i++)\r
264               {\r
265                 SequenceI s = sg.getSequenceAt(i);\r
266                 for (int j = lastres-1; j >= res && allGaps; j--)\r
267                 {\r
268                     if (!jalview.util.Comparison.isGap(s.getSequence().charAt(j)))\r
269                     {\r
270                       res = j + 1;\r
271                       allGaps = false;\r
272                     }\r
273                 }\r
274 \r
275                 if(!deleteAllowed && allGaps)\r
276                   deleteAllowed = true;\r
277               }\r
278             }\r
279 \r
280             // drag to right\r
281             if (res < av.getAlignment().getWidth() && res > lastres)\r
282                 sg.setEndRes(sg.getEndRes() + 1);\r
283 \r
284             // drag to left\r
285             else if (deleteAllowed && res < av.getAlignment().getWidth() &&\r
286                      res < lastres)\r
287                 sg.setEndRes(sg.getEndRes() - 1);\r
288 \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 (res < av.getAlignment().getWidth() && res > lastres)\r
299                 for (int j = lastres; j < res; j++)\r
300                   insertChar(j, k);\r
301 \r
302               // drag to left\r
303               else if (deleteAllowed && res < av.getAlignment().getWidth() && res < lastres)\r
304               {\r
305                 for (int j = res; j < lastres; j++)\r
306                 {\r
307                   deleteChar(j, k);\r
308                   startres = res;\r
309                 }\r
310               }\r
311             }\r
312           }\r
313         }\r
314         else /////Editing a single sequence///////////\r
315         {\r
316           if (res < av.getAlignment().getWidth() && res > lastres)\r
317           {\r
318             // dragging to the right\r
319             for (int j = lastres; j < res; j++)\r
320               insertChar(j, startseq);\r
321           }\r
322           else if (res < av.getAlignment().getWidth() && res < lastres)\r
323           {\r
324             // dragging to the left\r
325             for (int j = res; j < lastres; j++)\r
326             {\r
327               deleteChar(j, startseq);\r
328               startres = res;\r
329             }\r
330           }\r
331 \r
332         }\r
333 \r
334     }\r
335 \r
336     lastres = res;\r
337     repaint();\r
338   }\r
339 \r
340   public void drawChars(int seqstart, int seqend, int start) {\r
341     seqCanvas.drawPanel(seqCanvas.gg, start,av.getEndRes(),seqstart,seqend,av.getStartRes(),av.getStartSeq(),0);\r
342     repaint();\r
343   }\r
344 \r
345   public void insertChar(int j, int seq)\r
346   {\r
347     av.alignment.getSequenceAt(seq).insertCharAt(j, av.getGapCharacter());\r
348     seqEditOccurred=seq;\r
349   }\r
350 \r
351   public void deleteChar(int j, int seq)\r
352   {\r
353 \r
354     if ( jalview.util.Comparison.isGap( av.alignment.getSequenceAt(seq).getSequence().charAt(j)))\r
355         av.alignment.getSequenceAt(seq).deleteCharAt(j);\r
356 \r
357     av.alignment.getWidth();\r
358     repaint();\r
359     seqEditOccurred=seq;\r
360   }\r
361 \r
362 \r
363   void updateConservation(int i)\r
364   {\r
365     av.updateConservation();\r
366     av.updateConsensus();\r
367   }\r
368 \r
369 //////////////////////////////////////////\r
370 /////Everything below this is for defining the boundary of the rubberband\r
371 //////////////////////////////////////////\r
372   int oldSeq = -1;\r
373   public void doMousePressedDefineMode(MouseEvent evt)\r
374   {\r
375     int res = evt.getX()/av.getCharWidth() + av.getStartRes();\r
376     int seq = evt.getY()/av.getCharHeight() + av.getStartSeq();\r
377     oldSeq = seq;\r
378 \r
379     SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
380 \r
381     if(sequence==null || res>sequence.getLength())\r
382       return;\r
383 \r
384     stretchGroup = av.getSelectionGroup();\r
385 \r
386     if(stretchGroup == null)\r
387      {\r
388        stretchGroup = av.alignment.findGroup( sequence );\r
389        if(stretchGroup!=null && res>stretchGroup.getStartRes() && res<stretchGroup.getEndRes())\r
390          av.setSelectionGroup(stretchGroup);\r
391        else\r
392          stretchGroup = null;\r
393      }\r
394 \r
395     else if(!stretchGroup.sequences.contains(sequence)\r
396             || stretchGroup.getStartRes()>res\r
397             || stretchGroup.getEndRes()<res)\r
398      {\r
399        stretchGroup = null;\r
400 \r
401        SequenceGroup[] allGroups = av.alignment.findAllGroups( sequence );\r
402 \r
403        if (allGroups != null)\r
404          for (int i = 0; i < allGroups.length; i++)\r
405            if (allGroups[i].getStartRes() <= res &&\r
406                allGroups[i].getEndRes() >= res)\r
407            {\r
408              stretchGroup = allGroups[i];\r
409              av.setSelectionGroup(stretchGroup);\r
410              break;\r
411            }\r
412      }\r
413 \r
414     if(stretchGroup==null)\r
415     {\r
416       // define a new group here\r
417       SequenceGroup sg = new SequenceGroup();\r
418       sg.setStartRes(res);\r
419       sg.setEndRes(res);\r
420       sg.addSequence( sequence );\r
421       av.setSelectionGroup( sg );\r
422       stretchGroup = sg;\r
423 \r
424       if(av.getConservationSelected())\r
425         Desktop.setConservationSliderSource(ap, av.getGlobalColourScheme(), "Background");\r
426       else if(av.getGlobalColourScheme()!=null && av.getGlobalColourScheme().canThreshold())\r
427       {\r
428         ResidueColourScheme rcs = (ResidueColourScheme) av.getGlobalColourScheme();\r
429         int threshold = rcs.getThreshold();\r
430         if (threshold > 0)\r
431           Desktop.setPIDSliderSource(ap, av.getGlobalColourScheme(), "Background");\r
432       }\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 \r
479 \r
480        stretchGroup.cs = ccs;\r
481 \r
482 \r
483        Desktop.setConservationSliderSource(ap, stretchGroup.cs, stretchGroup.getName()) ;\r
484 \r
485        repaint();\r
486     }\r
487     else\r
488     {\r
489       if(stretchGroup.cs !=null && stretchGroup.cs.canThreshold())\r
490       {\r
491         ResidueColourScheme rcs =  (ResidueColourScheme) stretchGroup.cs;\r
492         int threshold = rcs.getThreshold();\r
493         if(threshold>0)\r
494           Desktop.setPIDSliderSource(ap, stretchGroup.cs, stretchGroup.getName());\r
495       }\r
496 \r
497     }\r
498 \r
499 \r
500     changeEndRes = false;\r
501     changeStartRes = false;\r
502     stretchGroup = null;\r
503     ap.idPanel.repaint();\r
504   }\r
505 \r
506 \r
507   boolean remove = false;\r
508   public void doMouseDraggedDefineMode(MouseEvent evt)\r
509   {\r
510     int res = evt.getX()/av.getCharWidth() + av.getStartRes();\r
511     int y = evt.getY()/av.getCharHeight() + av.getStartSeq();\r
512 \r
513     if(stretchGroup==null)\r
514       return;\r
515 \r
516     if(stretchGroup.getEndRes()==res)\r
517       // Edit end res position of selected group\r
518       changeEndRes = true;\r
519 \r
520     else if(stretchGroup.getStartRes()==res)\r
521       // Edit start res position of selected group\r
522       changeStartRes = true;\r
523 \r
524 \r
525     if(res<av.getStartRes())\r
526       res = av.getStartRes();\r
527     else if(res>av.getEndRes())\r
528       res = av.getEndRes();\r
529 \r
530     if(changeEndRes)\r
531     {\r
532       if(res>stretchGroup.getStartRes()-1)\r
533         stretchGroup.setEndRes( res );\r
534     }\r
535     else if(changeStartRes)\r
536     {\r
537       if(res<stretchGroup.getEndRes()+1)\r
538         stretchGroup.setStartRes( res );\r
539     }\r
540 \r
541     int dragDirection = 0;\r
542     if (y > oldSeq)\r
543       dragDirection = 1;\r
544     else if (y < oldSeq)\r
545       dragDirection = -1;\r
546 \r
547     while (y != oldSeq && oldSeq>0 && y<av.alignment.getHeight())\r
548     {\r
549       // This routine ensures we don't skip any sequences, as the\r
550       // selection is quite slow.\r
551       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
552 \r
553       oldSeq += dragDirection;\r
554       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
555 \r
556       if (stretchGroup.sequences.contains(nextSeq))\r
557       {\r
558         stretchGroup.deleteSequence(seq);\r
559         stretchGroup.deleteSequence(nextSeq);\r
560       }\r
561       else\r
562       {\r
563         stretchGroup.addSequence(seq);\r
564         stretchGroup.addSequence(nextSeq);\r
565       }\r
566     }\r
567     oldSeq = y;\r
568     mouseDragging = true;\r
569     if(scrollThread!=null)\r
570       scrollThread.setEvent(evt);\r
571 \r
572     repaint();\r
573   }\r
574 \r
575   public void doMouseEnteredDefineMode(MouseEvent e)\r
576   {\r
577     if (scrollThread != null)\r
578       scrollThread.running = false;\r
579   }\r
580 \r
581   public void doMouseExitedDefineMode(MouseEvent e)\r
582   {\r
583     if (av.getWrapAlignment())\r
584       return;\r
585 \r
586     if(mouseDragging)\r
587       scrollThread = new ScrollThread();\r
588 \r
589   }\r
590   // this class allows scrolling off the bottom of the visible alignment\r
591   class ScrollThread extends Thread\r
592   {\r
593     MouseEvent evt;\r
594     boolean running = false;\r
595     public ScrollThread()\r
596     {\r
597       start();\r
598     }\r
599 \r
600     public void setEvent(MouseEvent e)\r
601     {\r
602       evt = e;\r
603     }\r
604 \r
605     public void stopScrolling()\r
606     {\r
607       running = false;\r
608     }\r
609 \r
610     public void run()\r
611     {\r
612       running = true;\r
613       while (running)\r
614       {\r
615         if(evt!=null)\r
616         {\r
617 \r
618           if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)\r
619             running = ap.scrollUp(true);\r
620 \r
621           if (mouseDragging && evt.getY() >= getHeight() &&\r
622               av.alignment.getHeight() > av.getEndSeq())\r
623             running = ap.scrollUp(false);\r
624 \r
625           if (mouseDragging && evt.getX() < 0)\r
626             running = ap.scrollRight(true);\r
627 \r
628           else if (mouseDragging && evt.getX() >= getWidth())\r
629             running = ap.scrollRight(false);\r
630         }\r
631 \r
632         try\r
633         {\r
634           Thread.sleep(75);\r
635         }\r
636         catch (Exception ex)\r
637         {}\r
638       }\r
639     }\r
640 }\r
641 \r
642 \r
643 \r
644 }\r
645 \r
646 \r
647 \r
648 \r