bug adding null sequence to selection fixed
[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     }\r
179     else\r
180     {\r
181       res = x / av.getCharWidth()  + av.getStartRes();\r
182       seq = y / av.getCharHeight() + av.getStartSeq();\r
183     }\r
184 \r
185 \r
186     if(seq>=av.getAlignment().getHeight())\r
187       return;\r
188 \r
189     SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
190     if(res>sequence.getLength())\r
191       return;\r
192 \r
193     Object obj = ResidueProperties.aa2Triplet.get( sequence.getCharAt(res)+"" ) ;\r
194     String aa = "";\r
195     if(obj!=null)\r
196          aa = obj.toString();\r
197 \r
198     StringBuffer text = new StringBuffer("Sequence " +(seq+1)+" ID: "+sequence.getName());\r
199     if(aa!="")\r
200       text.append("  Residue: "+aa+" ("+  av.getAlignment().getSequenceAt(seq).findPosition(res)+")");\r
201 \r
202     ap.alignFrame.statusBar.setText(text.toString());\r
203 \r
204     // use aa to see if the mouse pointer is on a\r
205     if(  av.showSequenceFeatures)\r
206     {\r
207       Vector features = sequence.getSequenceFeatures();\r
208       Enumeration e = features.elements();\r
209       StringBuffer sbuffer = new StringBuffer();\r
210 \r
211 \r
212       while (e.hasMoreElements())\r
213       {\r
214         SequenceFeature sf = (SequenceFeature) e.nextElement();\r
215         if (sf.getStart() <= sequence.findPosition(res) &&\r
216             sf.getEnd() >= sequence.findPosition(res))\r
217         {\r
218           if(sbuffer.length()>0)\r
219             sbuffer.append("; ");\r
220           sbuffer.append(sf.getType() + " " + sf.getDescription());\r
221           if(sf.getStatus().length()>0)\r
222             sbuffer.append(" ("+sf.getStatus()+")");\r
223         }\r
224 \r
225       }\r
226 \r
227       ToolTipManager.sharedInstance().registerComponent(this);\r
228       this.setToolTipText(sbuffer.toString());\r
229     }\r
230 \r
231 \r
232   }\r
233 \r
234   public void doMouseDragged(MouseEvent evt) {\r
235     // If we're dragging we're editing\r
236 \r
237     if(lastres==-1)\r
238       return;\r
239 \r
240     int x = evt.getX();\r
241 \r
242     int res = x/av.getCharWidth() + av.getStartRes();\r
243     if (res < 0)\r
244       res = 0;\r
245 \r
246     if (res != lastres)\r
247     {\r
248         // Group editing\r
249         if (evt.isAltDown() || evt.isControlDown())\r
250         {\r
251           SequenceGroup sg = av.getSelectionGroup();\r
252           if(sg==null)\r
253             av.getAlignment().findGroup(startseq);\r
254           if (sg != null)\r
255           {\r
256             boolean deleteAllowed = false;\r
257             if (res < av.getAlignment().getWidth() && res < lastres)\r
258             {\r
259               /// Are we able to delete?\r
260               boolean allGaps = true;\r
261               for (int i = 0; i < sg.getSize(); i++)\r
262               {\r
263                 SequenceI s = sg.getSequenceAt(i);\r
264                 for (int j = lastres-1; j >= res && allGaps; j--)\r
265                 {\r
266                     if (!jalview.util.Comparison.isGap(s.getSequence().charAt(j)))\r
267                     {\r
268                       res = j + 1;\r
269                       allGaps = false;\r
270                     }\r
271                 }\r
272 \r
273                 if(!deleteAllowed && allGaps)\r
274                   deleteAllowed = true;\r
275               }\r
276             }\r
277 \r
278             // drag to right\r
279             if (res < av.getAlignment().getWidth() && res > lastres)\r
280                 sg.setEndRes(sg.getEndRes() + 1);\r
281 \r
282             // drag to left\r
283             else if (deleteAllowed && res < av.getAlignment().getWidth() &&\r
284                      res < lastres)\r
285                 sg.setEndRes(sg.getEndRes() - 1);\r
286 \r
287 \r
288 \r
289 \r
290             for (int i = 0; i < sg.getSize(); i++)\r
291             {\r
292               SequenceI s = sg.getSequenceAt(i);\r
293               int k = av.alignment.findIndex(s);\r
294 \r
295               // drag to right\r
296               if (res < av.getAlignment().getWidth() && res > lastres)\r
297                 for (int j = lastres; j < res; j++)\r
298                   insertChar(j, k);\r
299 \r
300               // drag to left\r
301               else if (deleteAllowed && res < av.getAlignment().getWidth() && res < lastres)\r
302               {\r
303                 for (int j = res; j < lastres; j++)\r
304                 {\r
305                   deleteChar(j, k);\r
306                   startres = res;\r
307                 }\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 = res; j < lastres; j++)\r
324             {\r
325               deleteChar(j, startseq);\r
326               startres = res;\r
327             }\r
328           }\r
329 \r
330         }\r
331 \r
332     }\r
333 \r
334     lastres = res;\r
335     repaint();\r
336   }\r
337 \r
338   public void drawChars(int seqstart, int seqend, int start) {\r
339     seqCanvas.drawPanel(seqCanvas.gg, start,av.getEndRes(),seqstart,seqend,av.getStartRes(),av.getStartSeq(),0);\r
340     repaint();\r
341   }\r
342 \r
343   public void insertChar(int j, int seq)\r
344   {\r
345     av.alignment.getSequenceAt(seq).insertCharAt(j, av.getGapCharacter());\r
346     seqEditOccurred=seq;\r
347   }\r
348 \r
349   public void deleteChar(int j, int seq)\r
350   {\r
351 \r
352     if ( jalview.util.Comparison.isGap( av.alignment.getSequenceAt(seq).getSequence().charAt(j)))\r
353         av.alignment.getSequenceAt(seq).deleteCharAt(j);\r
354 \r
355     av.alignment.getWidth();\r
356     repaint();\r
357     seqEditOccurred=seq;\r
358   }\r
359 \r
360 \r
361   void updateConservation(int i)\r
362   {\r
363     av.updateConservation();\r
364     av.updateConsensus();\r
365   }\r
366 \r
367 //////////////////////////////////////////\r
368 /////Everything below this is for defining the boundary of the rubberband\r
369 //////////////////////////////////////////\r
370   int oldSeq = -1;\r
371   public void doMousePressedDefineMode(MouseEvent evt)\r
372   {\r
373     int res = evt.getX()/av.getCharWidth() + av.getStartRes();\r
374     int seq = evt.getY()/av.getCharHeight() + av.getStartSeq();\r
375     oldSeq = seq;\r
376 \r
377     SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
378 \r
379     if(sequence==null || res>sequence.getLength())\r
380       return;\r
381 \r
382     stretchGroup = av.getSelectionGroup();\r
383 \r
384     if(stretchGroup == null)\r
385      {\r
386        stretchGroup = av.alignment.findGroup( sequence );\r
387        if(stretchGroup!=null && res>stretchGroup.getStartRes() && res<stretchGroup.getEndRes())\r
388          av.setSelectionGroup(stretchGroup);\r
389        else\r
390          stretchGroup = null;\r
391      }\r
392 \r
393     else if(!stretchGroup.sequences.contains(sequence)\r
394             || stretchGroup.getStartRes()>res\r
395             || stretchGroup.getEndRes()<res)\r
396      {\r
397        stretchGroup = null;\r
398 \r
399        SequenceGroup[] allGroups = av.alignment.findAllGroups( sequence );\r
400 \r
401        if (allGroups != null)\r
402          for (int i = 0; i < allGroups.length; i++)\r
403            if (allGroups[i].getStartRes() <= res &&\r
404                allGroups[i].getEndRes() >= res)\r
405            {\r
406              stretchGroup = allGroups[i];\r
407              av.setSelectionGroup(stretchGroup);\r
408              break;\r
409            }\r
410      }\r
411 \r
412     if(stretchGroup==null)\r
413     {\r
414       // define a new group here\r
415       SequenceGroup sg = new SequenceGroup();\r
416       sg.setStartRes(res);\r
417       sg.setEndRes(res);\r
418       sg.addSequence( sequence );\r
419       av.setSelectionGroup( sg );\r
420       stretchGroup = sg;\r
421 \r
422       if(av.getConservationSelected())\r
423         Desktop.setConservationSliderSource(ap, av.getGlobalColourScheme(), "Background");\r
424       else if(av.getGlobalColourScheme()!=null && av.getGlobalColourScheme().canThreshold())\r
425       {\r
426         ResidueColourScheme rcs = (ResidueColourScheme) av.getGlobalColourScheme();\r
427         int threshold = rcs.getThreshold();\r
428         if (threshold > 0)\r
429           Desktop.setPIDSliderSource(ap, av.getGlobalColourScheme(), "Background");\r
430       }\r
431 \r
432     }\r
433     else if( javax.swing.SwingUtilities.isRightMouseButton(evt))\r
434     {\r
435         jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu( ap , null);\r
436         pop.show(this, evt.getX(), evt.getY());\r
437 \r
438     // edit the properties of existing group\r
439     }\r
440 \r
441     if(stretchGroup!=null && stretchGroup.getEndRes()==res)\r
442       // Edit end res position of selected group\r
443       changeEndRes = true;\r
444 \r
445    else if(stretchGroup!=null && stretchGroup.getStartRes()==res)\r
446       // Edit end res position of selected group\r
447       changeStartRes = true;\r
448 \r
449 \r
450     repaint();\r
451 \r
452   }\r
453 \r
454   boolean changeEndSeq = false;\r
455   boolean changeStartSeq = false;\r
456   boolean changeEndRes = false;\r
457   boolean changeStartRes = false;\r
458   SequenceGroup stretchGroup = null;\r
459 \r
460   public void doMouseReleasedDefineMode(MouseEvent evt)\r
461   {\r
462     mouseDragging = false;\r
463 \r
464     if(stretchGroup==null)\r
465       return;\r
466 \r
467     if(stretchGroup.cs instanceof ClustalxColourScheme)\r
468     {\r
469       stretchGroup.cs = new ClustalxColourScheme(stretchGroup.sequences, av.alignment.getWidth());\r
470       repaint();\r
471     }\r
472 \r
473     else if(stretchGroup.cs instanceof ConservationColourScheme)\r
474     {\r
475       ConservationColourScheme ccs = (ConservationColourScheme)stretchGroup.cs;\r
476 \r
477 \r
478        stretchGroup.cs = ccs;\r
479 \r
480 \r
481        Desktop.setConservationSliderSource(ap, stretchGroup.cs, stretchGroup.getName()) ;\r
482 \r
483        repaint();\r
484     }\r
485     else\r
486     {\r
487       if(stretchGroup.cs !=null && stretchGroup.cs.canThreshold())\r
488       {\r
489         ResidueColourScheme rcs =  (ResidueColourScheme) stretchGroup.cs;\r
490         int threshold = rcs.getThreshold();\r
491         if(threshold>0)\r
492           Desktop.setPIDSliderSource(ap, stretchGroup.cs, stretchGroup.getName());\r
493       }\r
494 \r
495     }\r
496 \r
497 \r
498     changeEndRes = false;\r
499     changeStartRes = false;\r
500     stretchGroup = null;\r
501     ap.idPanel.repaint();\r
502   }\r
503 \r
504 \r
505   boolean remove = false;\r
506   public void doMouseDraggedDefineMode(MouseEvent evt)\r
507   {\r
508     int res = evt.getX()/av.getCharWidth() + av.getStartRes();\r
509     int y = evt.getY()/av.getCharHeight() + av.getStartSeq();\r
510 \r
511     if(stretchGroup==null)\r
512       return;\r
513 \r
514     if(res>av.alignment.getWidth()-2)\r
515       res = av.alignment.getWidth()-2;\r
516 \r
517 \r
518     if(stretchGroup.getEndRes()==res)\r
519       // Edit end res position of selected group\r
520       changeEndRes = true;\r
521 \r
522     else if(stretchGroup.getStartRes()==res)\r
523       // Edit start res position of selected group\r
524       changeStartRes = true;\r
525 \r
526 \r
527     if(res<av.getStartRes())\r
528       res = av.getStartRes();\r
529     else if(res>av.getEndRes())\r
530       res = av.getEndRes();\r
531 \r
532     if(changeEndRes)\r
533     {\r
534       if(res>stretchGroup.getStartRes()-1)\r
535         stretchGroup.setEndRes( res );\r
536     }\r
537     else if(changeStartRes)\r
538     {\r
539       if(res<stretchGroup.getEndRes()+1)\r
540         stretchGroup.setStartRes( res );\r
541     }\r
542 \r
543     int dragDirection = 0;\r
544     if (y > oldSeq)\r
545       dragDirection = 1;\r
546     else if (y < oldSeq)\r
547       dragDirection = -1;\r
548 \r
549     while (y != oldSeq && oldSeq>0 && y<av.alignment.getHeight())\r
550     {\r
551       // This routine ensures we don't skip any sequences, as the\r
552       // selection is quite slow.\r
553       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
554 \r
555       oldSeq += dragDirection;\r
556       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
557 \r
558       if (stretchGroup.sequences.contains(nextSeq))\r
559       {\r
560         stretchGroup.deleteSequence(seq);\r
561         stretchGroup.deleteSequence(nextSeq);\r
562       }\r
563       else\r
564       {\r
565        if(seq!=null)\r
566         stretchGroup.addSequence(seq);\r
567         stretchGroup.addSequence(nextSeq);\r
568       }\r
569     }\r
570     oldSeq = y;\r
571     mouseDragging = true;\r
572     if(scrollThread!=null)\r
573       scrollThread.setEvent(evt);\r
574 \r
575     repaint();\r
576   }\r
577 \r
578   public void doMouseEnteredDefineMode(MouseEvent e)\r
579   {\r
580     if (scrollThread != null)\r
581       scrollThread.running = false;\r
582   }\r
583 \r
584   public void doMouseExitedDefineMode(MouseEvent e)\r
585   {\r
586     if (av.getWrapAlignment())\r
587       return;\r
588 \r
589     if(mouseDragging)\r
590       scrollThread = new ScrollThread();\r
591 \r
592   }\r
593   // this class allows scrolling off the bottom of the visible alignment\r
594   class ScrollThread extends Thread\r
595   {\r
596     MouseEvent evt;\r
597     boolean running = false;\r
598     public ScrollThread()\r
599     {\r
600       start();\r
601     }\r
602 \r
603     public void setEvent(MouseEvent e)\r
604     {\r
605       evt = e;\r
606     }\r
607 \r
608     public void stopScrolling()\r
609     {\r
610       running = false;\r
611     }\r
612 \r
613     public void run()\r
614     {\r
615       running = true;\r
616       while (running)\r
617       {\r
618         if(evt!=null)\r
619         {\r
620 \r
621           if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)\r
622             running = ap.scrollUp(true);\r
623 \r
624           if (mouseDragging && evt.getY() >= getHeight() &&\r
625               av.alignment.getHeight() > av.getEndSeq())\r
626             running = ap.scrollUp(false);\r
627 \r
628           if (mouseDragging && evt.getX() < 0)\r
629             running = ap.scrollRight(true);\r
630 \r
631           else if (mouseDragging && evt.getX() >= getWidth())\r
632             running = ap.scrollRight(false);\r
633         }\r
634 \r
635         try\r
636         {\r
637           Thread.sleep(75);\r
638         }\r
639         catch (Exception ex)\r
640         {}\r
641       }\r
642     }\r
643 }\r
644 \r
645 \r
646 \r
647 }\r
648 \r
649 \r
650 \r
651 \r