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