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