3a707de53b16f72dd0293febcba007d6bf17462f
[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     // Y O Y CLUSTALX\r
374     ColourSchemeI cs = av.getGlobalColourScheme();\r
375     if(cs instanceof ConservationColourScheme)\r
376     {\r
377       ConservationColourScheme ccs =  (ConservationColourScheme) cs;\r
378       if(ccs.cs instanceof ClustalxColourScheme)\r
379      {\r
380         jalview.analysis.Conservation c = new jalview.analysis.Conservation\r
381             ("All",\r
382             ResidueProperties.propHash, 3,\r
383             av.alignment.getSequences(), 0,\r
384             av.alignment.getWidth() - 1);\r
385         c.calculate();\r
386         c.verdict(false, av.ConsPercGaps);\r
387 \r
388         ClustalxColourScheme cxs = (ClustalxColourScheme)ccs.cs;\r
389         cxs.resetClustalX(av.alignment.getSequences(),  av.alignment.getWidth());\r
390         ccs = new ConservationColourScheme(c, cxs);\r
391         av.setGlobalColourScheme(ccs);\r
392      }\r
393     }\r
394 \r
395     if(cs instanceof ClustalxColourScheme)\r
396     {\r
397       ((ClustalxColourScheme)cs).resetClustalX(av.alignment.getSequences(),\r
398                                     av.alignment.getWidth());\r
399       av.setGlobalColourScheme(cs);\r
400     }\r
401 \r
402 \r
403   }\r
404 \r
405 //////////////////////////////////////////\r
406 /////Everything below this is for defining the boundary of the rubberband\r
407 //////////////////////////////////////////\r
408   int oldSeq = -1;\r
409   public void doMousePressedDefineMode(MouseEvent evt)\r
410   {\r
411     int res = evt.getX()/av.getCharWidth() + av.getStartRes();\r
412     int seq = evt.getY()/av.getCharHeight() + av.getStartSeq();\r
413     oldSeq = seq;\r
414 \r
415     SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
416 \r
417     if(sequence==null || res>sequence.getLength())\r
418       return;\r
419 \r
420     stretchGroup = av.getSelectionGroup();\r
421 \r
422     if(stretchGroup == null)\r
423      {\r
424        stretchGroup = av.alignment.findGroup( sequence );\r
425        if(stretchGroup!=null && res>stretchGroup.getStartRes() && res<stretchGroup.getEndRes())\r
426          av.setSelectionGroup(stretchGroup);\r
427        else\r
428          stretchGroup = null;\r
429      }\r
430 \r
431     else if(!stretchGroup.sequences.contains(sequence)\r
432             || stretchGroup.getStartRes()>res\r
433             || stretchGroup.getEndRes()<res)\r
434      {\r
435        stretchGroup = null;\r
436 \r
437        SequenceGroup[] allGroups = av.alignment.findAllGroups( sequence );\r
438 \r
439        if (allGroups != null)\r
440          for (int i = 0; i < allGroups.length; i++)\r
441            if (allGroups[i].getStartRes() <= res &&\r
442                allGroups[i].getEndRes() >= res)\r
443            {\r
444              stretchGroup = allGroups[i];\r
445              av.setSelectionGroup(stretchGroup);\r
446              break;\r
447            }\r
448      }\r
449 \r
450     if(stretchGroup==null)\r
451     {\r
452       // define a new group here\r
453       SequenceGroup sg = new SequenceGroup();\r
454       sg.setStartRes(res);\r
455       sg.setEndRes(res);\r
456       sg.addSequence( sequence );\r
457       av.setSelectionGroup( sg );\r
458       stretchGroup = sg;\r
459 \r
460       if(av.getConservationSelected())\r
461         SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(), "Background");\r
462       if(av.getAbovePIDThreshold())\r
463         SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(), "Background");\r
464 \r
465     }\r
466 \r
467     // DETECT RIGHT MOUSE BUTTON IN AWT\r
468     else if( ( evt.getModifiers() & InputEvent .BUTTON3_MASK ) == InputEvent.BUTTON3_MASK )\r
469     {\r
470       APopupMenu popup = new APopupMenu(ap, null);\r
471       this.add(popup);\r
472       popup.show(this, evt.getX(), evt.getY());\r
473     }\r
474 \r
475 \r
476     if(stretchGroup!=null && stretchGroup.getEndRes()==res)\r
477       // Edit end res position of selected group\r
478       changeEndRes = true;\r
479 \r
480    else if(stretchGroup!=null && stretchGroup.getStartRes()==res)\r
481       // Edit end res position of selected group\r
482       changeStartRes = true;\r
483 \r
484   }\r
485 \r
486   boolean changeEndSeq = false;\r
487   boolean changeStartSeq = false;\r
488   boolean changeEndRes = false;\r
489   boolean changeStartRes = false;\r
490   SequenceGroup stretchGroup = null;\r
491 \r
492   public void doMouseReleasedDefineMode(MouseEvent evt)\r
493   {\r
494     mouseDragging = false;\r
495 \r
496     if(stretchGroup==null)\r
497       return;\r
498 \r
499     if(stretchGroup.cs instanceof ClustalxColourScheme)\r
500     {\r
501       stretchGroup.cs = new ClustalxColourScheme(stretchGroup.sequences, av.alignment.getWidth());\r
502       seqCanvas.repaint();\r
503     }\r
504 \r
505     else if(stretchGroup.cs instanceof ConservationColourScheme)\r
506     {\r
507        ConservationColourScheme ccs = (ConservationColourScheme)stretchGroup.cs;\r
508        stretchGroup.cs = ccs;\r
509        SliderPanel.setConservationSlider(ap, stretchGroup.cs, stretchGroup.getName()) ;\r
510 \r
511        seqCanvas.repaint();\r
512     }\r
513     else\r
514     {\r
515       if(stretchGroup.cs !=null)\r
516       {\r
517         SliderPanel.setPIDSliderSource(ap, stretchGroup.cs, stretchGroup.getName());\r
518       }\r
519 \r
520     }\r
521 \r
522 \r
523     changeEndRes = false;\r
524     changeStartRes = false;\r
525     stretchGroup = null;\r
526     ap.repaint();\r
527   }\r
528 \r
529 \r
530   boolean remove = false;\r
531   public void doMouseDraggedDefineMode(MouseEvent evt)\r
532   {\r
533     int res = evt.getX()/av.getCharWidth() + av.getStartRes();\r
534     int y = evt.getY()/av.getCharHeight() + av.getStartSeq();\r
535 \r
536     if(stretchGroup==null)\r
537       return;\r
538 \r
539     if(res>av.alignment.getWidth())\r
540       res = av.alignment.getWidth()-1;\r
541 \r
542 \r
543     if(stretchGroup.getEndRes()==res)\r
544       // Edit end res position of selected group\r
545       changeEndRes = true;\r
546 \r
547     else if(stretchGroup.getStartRes()==res)\r
548       // Edit start res position of selected group\r
549       changeStartRes = true;\r
550 \r
551 \r
552     if(res<av.getStartRes())\r
553       res = av.getStartRes();\r
554     else if(res>av.getEndRes())\r
555       res = av.getEndRes();\r
556 \r
557     if(changeEndRes)\r
558     {\r
559       if(res>stretchGroup.getStartRes()-1)\r
560         stretchGroup.setEndRes( res );\r
561     }\r
562     else if(changeStartRes)\r
563     {\r
564       if(res<stretchGroup.getEndRes()+1)\r
565         stretchGroup.setStartRes( res );\r
566     }\r
567 \r
568     int dragDirection = 0;\r
569     if (y > oldSeq)\r
570       dragDirection = 1;\r
571     else if (y < oldSeq)\r
572       dragDirection = -1;\r
573 \r
574     while (y != oldSeq && oldSeq>0 && y<av.alignment.getHeight())\r
575     {\r
576       // This routine ensures we don't skip any sequences, as the\r
577       // selection is quite slow.\r
578       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
579 \r
580       oldSeq += dragDirection;\r
581       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
582 \r
583       if (stretchGroup.sequences.contains(nextSeq))\r
584       {\r
585         stretchGroup.deleteSequence(seq);\r
586         stretchGroup.deleteSequence(nextSeq);\r
587       }\r
588       else\r
589       {\r
590        if(seq!=null)\r
591         stretchGroup.addSequence(seq);\r
592         stretchGroup.addSequence(nextSeq);\r
593       }\r
594     }\r
595     oldSeq = y;\r
596     mouseDragging = true;\r
597     if(scrollThread!=null)\r
598       scrollThread.setEvent(evt);\r
599 \r
600     seqCanvas.repaint();\r
601   }\r
602 \r
603   public void doMouseEnteredDefineMode(MouseEvent e)\r
604   {\r
605     if (scrollThread != null)\r
606       scrollThread.running = false;\r
607   }\r
608 \r
609   public void doMouseExitedDefineMode(MouseEvent e)\r
610   {\r
611     if (av.getWrapAlignment())\r
612       return;\r
613 \r
614     if(mouseDragging)\r
615       scrollThread = new ScrollThread();\r
616 \r
617   }\r
618   // this class allows scrolling off the bottom of the visible alignment\r
619   class ScrollThread extends Thread\r
620   {\r
621     MouseEvent evt;\r
622     boolean running = false;\r
623     public ScrollThread()\r
624     {\r
625       start();\r
626     }\r
627 \r
628     public void setEvent(MouseEvent e)\r
629     {\r
630       evt = e;\r
631     }\r
632 \r
633     public void stopScrolling()\r
634     {\r
635       running = false;\r
636     }\r
637 \r
638     public void run()\r
639     {\r
640       running = true;\r
641       while (running)\r
642       {\r
643         if(evt!=null)\r
644         {\r
645 \r
646           if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)\r
647             running = ap.scrollUp(true);\r
648 \r
649           if (mouseDragging && evt.getY() >= getSize().height &&\r
650               av.alignment.getHeight() > av.getEndSeq())\r
651             running = ap.scrollUp(false);\r
652 \r
653           if (mouseDragging && evt.getX() < 0)\r
654             running = ap.scrollRight(true);\r
655 \r
656           else if (mouseDragging && evt.getX() >= getSize().width)\r
657             running = ap.scrollRight(false);\r
658         }\r
659 \r
660         try\r
661         {\r
662           Thread.sleep(75);\r
663         }\r
664         catch (Exception ex)\r
665         {}\r
666       }\r
667     }\r
668 }\r
669 \r
670 \r
671 \r
672 }\r
673 \r
674 \r
675 \r
676 \r