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