Reduced jar size by 20%
[jalview.git] / src / jalview / appletgui / SeqPanel.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4  *\r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  *\r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  *\r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18  */\r
19 \r
20 package jalview.appletgui;\r
21 \r
22 import java.util.*;\r
23 \r
24 import java.awt.*;\r
25 import java.awt.event.*;\r
26 \r
27 import jalview.datamodel.*;\r
28 import jalview.schemes.*;\r
29 \r
30 public class SeqPanel\r
31     extends Panel implements MouseMotionListener, MouseListener\r
32 {\r
33 \r
34   public SeqCanvas seqCanvas;\r
35   public AlignmentPanel ap;\r
36 \r
37   protected int lastres;\r
38   protected int startseq;\r
39   int startEdit = -1;\r
40   int endEdit = -1;\r
41 \r
42   protected AlignViewport av;\r
43 \r
44   // if character is inserted or deleted, we will need to recalculate the conservation\r
45   int seqEditOccurred = -1;\r
46 \r
47   ScrollThread scrollThread = null;\r
48   boolean mouseDragging = false;\r
49 \r
50   boolean editingSeqs = false;\r
51   boolean groupEditing = false;\r
52 \r
53   public SeqPanel(AlignViewport avp, AlignmentPanel p)\r
54   {\r
55     this.av = avp;\r
56 \r
57     seqCanvas = new SeqCanvas(avp);\r
58     setLayout(new BorderLayout());\r
59     add(seqCanvas);\r
60 \r
61     ap = p;\r
62 \r
63     seqCanvas.addMouseMotionListener(this);\r
64     seqCanvas.addMouseListener(this);\r
65 \r
66     seqCanvas.repaint();\r
67   }\r
68 \r
69 \r
70      public void mousePressed(MouseEvent evt)\r
71      {\r
72        if (evt.isShiftDown() || evt.isAltDown() || evt.isControlDown())\r
73        {\r
74          if (evt.isAltDown() || evt.isControlDown())\r
75          {\r
76            groupEditing = true;\r
77          }\r
78 \r
79          editingSeqs = true;\r
80          doMousePressed(evt);\r
81        }\r
82        else\r
83        {\r
84          doMousePressedDefineMode(evt);\r
85        }\r
86      }\r
87 \r
88      public void mouseClicked(MouseEvent evt){}\r
89 \r
90 \r
91   public void mouseReleased(MouseEvent evt)\r
92   {\r
93     if (!editingSeqs)\r
94     {\r
95       doMouseReleasedDefineMode(evt);\r
96       return;\r
97     }\r
98 \r
99 \r
100     if (seqEditOccurred > -1)\r
101     {\r
102       editOccurred(seqEditOccurred);\r
103     }\r
104 \r
105     startseq = -1;\r
106     lastres = -1;\r
107     seqEditOccurred = -1;\r
108     editingSeqs = false;\r
109     groupEditing = false;\r
110     ap.repaint();\r
111   }\r
112 \r
113   int startWrapBlock=-1;\r
114   int wrappedBlock=-1;\r
115   int findRes(MouseEvent evt)\r
116  {\r
117    int res = 0;\r
118    int x = evt.getX();\r
119 \r
120   if (av.wrapAlignment)\r
121   {\r
122 \r
123     int hgap = av.charHeight;\r
124     if (av.scaleAboveWrapped)\r
125       hgap += av.charHeight;\r
126 \r
127     int cHeight = av.getAlignment().getHeight() * av.charHeight\r
128         + hgap + seqCanvas.getAnnotationHeight();\r
129 \r
130       int y = evt.getY();\r
131       y -= hgap;\r
132       x -= seqCanvas.LABEL_WEST;\r
133 \r
134 \r
135       int cwidth = seqCanvas.getWrappedCanvasWidth(getSize().width);\r
136 \r
137       wrappedBlock = y / cHeight;\r
138       wrappedBlock += av.getStartRes() / cwidth;\r
139 \r
140       res = wrappedBlock * cwidth + x / av.getCharWidth();\r
141 \r
142   }\r
143   else\r
144   {\r
145       res = (x / av.getCharWidth()) + av.getStartRes();\r
146   }\r
147 \r
148   return res;\r
149 \r
150  }\r
151 \r
152  int findSeq(MouseEvent evt)\r
153  {\r
154 \r
155    int seq = 0;\r
156    int y = evt.getY();\r
157 \r
158    if (av.wrapAlignment)\r
159    {\r
160      int hgap = av.charHeight;\r
161      if (av.scaleAboveWrapped)\r
162        hgap += av.charHeight;\r
163 \r
164      int cHeight = av.getAlignment().getHeight() * av.charHeight\r
165          + hgap + seqCanvas.getAnnotationHeight();\r
166 \r
167        y -= hgap;\r
168 \r
169      seq = ( (y % cHeight) / av.getCharHeight());\r
170    }\r
171    else\r
172    {\r
173      seq = (y / av.getCharHeight()) + av.getStartSeq();\r
174    }\r
175 \r
176    return seq;\r
177  }\r
178 \r
179 \r
180   public void doMousePressed(MouseEvent evt)\r
181   {\r
182     ap.alignFrame.addHistoryItem(new HistoryItem(\r
183         "Edit Sequence", av.alignment, HistoryItem.EDIT));\r
184 \r
185     int seq = findSeq(evt);\r
186     int res = findRes(evt);\r
187 \r
188     if (seq < av.getAlignment().getHeight() &&\r
189         res < av.getAlignment().getSequenceAt(seq).getLength())\r
190     {\r
191       //char resstr = align.getSequenceAt(seq).getSequence().charAt(res);\r
192       // Find the residue's position in the sequence (res is the position\r
193       // in the alignment\r
194 \r
195       startseq = seq;\r
196       lastres = res;\r
197     }\r
198     else\r
199     {\r
200       startseq = -1;\r
201       lastres = -1;\r
202     }\r
203 \r
204     return;\r
205   }\r
206 \r
207   public void mouseMoved(MouseEvent evt)\r
208   {\r
209     int res = findRes(evt);\r
210     int seq = findSeq(evt);\r
211 \r
212     if (seq >= av.getAlignment().getHeight() || seq<0 || res<0)\r
213     {\r
214       return;\r
215     }\r
216 \r
217     SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
218     if (res > sequence.getLength())\r
219     {\r
220       return;\r
221     }\r
222 \r
223     StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " +\r
224             sequence.getName());\r
225 \r
226     Object obj = null;\r
227     if (av.alignment.isNucleotide())\r
228     {\r
229       obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) +\r
230           "");\r
231       if(obj!=null)\r
232         text.append(" Nucleotide: ");\r
233     }\r
234     else\r
235     {\r
236       obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");\r
237       if(obj!=null)\r
238         text.append("  Residue: ");\r
239     }\r
240 \r
241     if (obj != null)\r
242     {\r
243 \r
244       if (obj != "")\r
245       {\r
246         text.append(obj + " (" +\r
247                     av.getAlignment().getSequenceAt(seq).findPosition(res) + ")");\r
248       }\r
249     }\r
250 \r
251     if(seqCanvas.pdbCanvas!=null && sequence==seqCanvas.pdbCanvas.sequence)\r
252     {\r
253       seqCanvas.pdbCanvas.highlightRes(sequence.findPosition(res));\r
254     }\r
255 \r
256 \r
257     // use aa to see if the mouse pointer is on a\r
258     if (av.showSequenceFeatures && sequence.getSequenceFeatures()!=null)\r
259     {\r
260       Vector features = sequence.getSequenceFeatures();\r
261       Enumeration e = features.elements();\r
262       boolean first = true;\r
263       while (e.hasMoreElements())\r
264       {\r
265         SequenceFeature sf = (SequenceFeature) e.nextElement();\r
266         if (sf.getBegin() <= sequence.findPosition(res) &&\r
267             sf.getEnd() >= sequence.findPosition(res))\r
268         {\r
269           if(first)\r
270           {\r
271             text.append(" Sequence Feature: ");\r
272             first = false;\r
273           }\r
274 \r
275           text.append(sf.getDescription());\r
276           if (sf.getStatus().length() > 0)\r
277           {\r
278             text.append(" (" + sf.getStatus() + ")");\r
279           }\r
280           text.append("; ");\r
281         }\r
282 \r
283       }\r
284     }\r
285 \r
286      ap.alignFrame.statusBar.setText(text.toString());\r
287 \r
288   }\r
289 \r
290   public void mouseDragged(MouseEvent evt)\r
291   {\r
292     if (!editingSeqs)\r
293     {\r
294       doMouseDraggedDefineMode(evt);\r
295       return;\r
296     }\r
297 \r
298     // If we're dragging we're editing\r
299     int res = findRes(evt);\r
300     if (res < 0)\r
301     {\r
302       res = 0;\r
303     }\r
304 \r
305     if (lastres == -1 || lastres == res)\r
306     {\r
307       return;\r
308     }\r
309 \r
310     boolean dragRight = true;\r
311     if (res < av.getAlignment().getWidth() && res < lastres)\r
312     {\r
313       dragRight = false;\r
314     }\r
315 \r
316     if (res != lastres)\r
317     {\r
318       // Group editing\r
319       if (groupEditing)\r
320       {\r
321         SequenceGroup sg = av.getSelectionGroup();\r
322         if (sg == null)\r
323         {\r
324           lastres = -1;\r
325           return;\r
326         }\r
327 \r
328         // drag to right\r
329         if (dragRight)\r
330         {\r
331           sg.setEndRes(sg.getEndRes() + (res - lastres));\r
332         }\r
333 \r
334         // drag to left\r
335         else\r
336         {\r
337           /// Are we able to delete?\r
338           // ie are all columns blank?\r
339           boolean deleteAllowed = false;\r
340           for (int s = 0; s < sg.getSize(); s++)\r
341           {\r
342             SequenceI seq = sg.getSequenceAt(s);\r
343             for (int j = res; j < lastres; j++)\r
344             {\r
345               if (seq.getSequence().length() <= j)\r
346               {\r
347                 continue;\r
348               }\r
349 \r
350               if (!jalview.util.Comparison.isGap(seq.getSequence().charAt(j)))\r
351               {\r
352                 // Not a gap, block edit not valid\r
353                 res = j + 1;\r
354                 deleteAllowed = false;\r
355                 continue;\r
356               }\r
357               deleteAllowed = true;\r
358             }\r
359           }\r
360 \r
361           if (!deleteAllowed)\r
362           {\r
363             lastres = -1;\r
364             return;\r
365           }\r
366 \r
367           sg.setEndRes(sg.getEndRes() - (lastres - res));\r
368         }\r
369 \r
370         for (int i = 0; i < sg.getSize(); i++)\r
371         {\r
372           SequenceI s = sg.getSequenceAt(i);\r
373           int k = av.alignment.findIndex(s);\r
374 \r
375           // drag to right\r
376           if (dragRight)\r
377           {\r
378             for (int j = lastres; j < res; j++)\r
379             {\r
380               insertChar(j, k);\r
381             }\r
382           }\r
383 \r
384           // drag to left\r
385           else\r
386           {\r
387             for (int j = res; j < lastres; j++)\r
388             {\r
389               if (s.getLength() > j)\r
390               {\r
391                 deleteChar(res, k);\r
392               }\r
393             }\r
394           }\r
395         }\r
396       }\r
397       else /////Editing a single sequence///////////\r
398       {\r
399         if (res < av.getAlignment().getWidth() && res > lastres)\r
400         {\r
401           // dragging to the right\r
402           for (int j = lastres; j < res; j++)\r
403           {\r
404             insertChar(j, startseq);\r
405           }\r
406         }\r
407         else if (res < av.getAlignment().getWidth() && res < lastres)\r
408         {\r
409           // dragging to the left\r
410           for (int j = lastres; j > res; j--)\r
411           {\r
412             if (jalview.util.Comparison.isGap(\r
413                 av.alignment.getSequenceAt(startseq).getSequence().charAt(res)))\r
414             {\r
415 \r
416               deleteChar(res, startseq);\r
417             }\r
418             else\r
419             {\r
420 \r
421               break;\r
422             }\r
423           }\r
424         }\r
425 \r
426       }\r
427     }\r
428 \r
429     endEdit = res;\r
430     lastres = res;\r
431     seqCanvas.repaint();\r
432   }\r
433 \r
434   public void drawChars(int seqstart, int seqend, int start)\r
435   {\r
436     seqCanvas.drawPanel(seqCanvas.gg, start, av.getEndRes(), seqstart, seqend,\r
437                         av.getStartRes(), av.getStartSeq(), 0);\r
438     seqCanvas.repaint();\r
439   }\r
440 \r
441   public void insertChar(int j, int seq)\r
442   {\r
443     av.alignment.getSequenceAt(seq).insertCharAt(j, av.getGapCharacter());\r
444     seqEditOccurred = seq;\r
445   }\r
446 \r
447   public void deleteChar(int j, int seq)\r
448   {\r
449 \r
450     av.alignment.getSequenceAt(seq).deleteCharAt(j);\r
451     seqEditOccurred = seq;\r
452     av.alignment.getWidth();\r
453     repaint();\r
454   }\r
455 \r
456   void editOccurred(int i)\r
457   {\r
458     if (endEdit == startEdit)\r
459     {\r
460         ap.alignFrame.historyList.pop();\r
461         ap.alignFrame.updateEditMenuBar();\r
462     }\r
463 \r
464     av.firePropertyChange("alignment", null,av.getAlignment().getSequences());\r
465   }\r
466 \r
467 //////////////////////////////////////////\r
468 /////Everything below this is for defining the boundary of the rubberband\r
469 //////////////////////////////////////////\r
470   int oldSeq = -1;\r
471   public void doMousePressedDefineMode(MouseEvent evt)\r
472   {\r
473     int res = findRes(evt);\r
474     int seq = findSeq(evt);\r
475     oldSeq = seq;\r
476     startWrapBlock=wrappedBlock;\r
477 \r
478     if(seq==-1)\r
479       return;\r
480 \r
481     SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
482 \r
483     if (sequence == null || res > sequence.getLength())\r
484     {\r
485       return;\r
486     }\r
487 \r
488     stretchGroup = av.getSelectionGroup();\r
489 \r
490     if (stretchGroup == null)\r
491     {\r
492       stretchGroup = av.alignment.findGroup(sequence);\r
493       if (stretchGroup != null && res > stretchGroup.getStartRes() &&\r
494           res < stretchGroup.getEndRes())\r
495       {\r
496         av.setSelectionGroup(stretchGroup);\r
497       }\r
498       else\r
499       {\r
500         stretchGroup = null;\r
501       }\r
502     }\r
503 \r
504     else if (!stretchGroup.sequences.contains(sequence)\r
505              || stretchGroup.getStartRes() > res\r
506              || stretchGroup.getEndRes() < res)\r
507     {\r
508       stretchGroup = null;\r
509 \r
510       SequenceGroup[] allGroups = av.alignment.findAllGroups(sequence);\r
511 \r
512       if (allGroups != null)\r
513       {\r
514         for (int i = 0; i < allGroups.length; i++)\r
515         {\r
516           if (allGroups[i].getStartRes() <= res &&\r
517               allGroups[i].getEndRes() >= res)\r
518           {\r
519             stretchGroup = allGroups[i];\r
520             av.setSelectionGroup(stretchGroup);\r
521             break;\r
522           }\r
523         }\r
524       }\r
525     }\r
526 \r
527     if (stretchGroup == null)\r
528     {\r
529       // define a new group here\r
530       SequenceGroup sg = new SequenceGroup();\r
531       sg.setStartRes(res);\r
532       sg.setEndRes(res);\r
533       sg.addSequence(sequence, false);\r
534       av.setSelectionGroup(sg);\r
535       stretchGroup = sg;\r
536 \r
537       if (av.getConservationSelected())\r
538       {\r
539         SliderPanel.setConservationSlider(ap, av.getGlobalColourScheme(),\r
540                                           "Background");\r
541       }\r
542       if (av.getAbovePIDThreshold())\r
543       {\r
544         SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
545                                        "Background");\r
546       }\r
547 \r
548     }\r
549 \r
550     // DETECT RIGHT MOUSE BUTTON IN AWT\r
551     else if ( (evt.getModifiers() & InputEvent.BUTTON3_MASK) ==\r
552              InputEvent.BUTTON3_MASK)\r
553     {\r
554       APopupMenu popup = new APopupMenu(ap, null, null);\r
555       this.add(popup);\r
556       popup.show(this, evt.getX(), evt.getY());\r
557     }\r
558 \r
559     if (stretchGroup != null && stretchGroup.getEndRes() == res)\r
560     {\r
561       // Edit end res position of selected group\r
562       changeEndRes = true;\r
563     }\r
564 \r
565     else if (stretchGroup != null && stretchGroup.getStartRes() == res)\r
566     {\r
567       // Edit end res position of selected group\r
568       changeStartRes = true;\r
569     }\r
570 \r
571   }\r
572 \r
573   boolean changeEndSeq = false;\r
574   boolean changeStartSeq = false;\r
575   boolean changeEndRes = false;\r
576   boolean changeStartRes = false;\r
577   SequenceGroup stretchGroup = null;\r
578 \r
579   public void doMouseReleasedDefineMode(MouseEvent evt)\r
580   {\r
581     if(mouseDragging)\r
582      {\r
583        stretchGroup.recalcConservation();\r
584        mouseDragging = false;\r
585      }\r
586 \r
587     if (stretchGroup == null)\r
588     {\r
589       return;\r
590     }\r
591 \r
592     if(stretchGroup.cs!=null)\r
593     {\r
594       if (stretchGroup.cs.conservationApplied())\r
595       {\r
596         SliderPanel.setConservationSlider(ap, stretchGroup.cs,\r
597                                           stretchGroup.getName());\r
598       }\r
599       else\r
600       {\r
601         SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,\r
602                                        stretchGroup.getName());\r
603       }\r
604     }\r
605     changeEndRes = false;\r
606     changeStartRes = false;\r
607     stretchGroup = null;\r
608     seqCanvas.repaint();\r
609     ap.repaint();\r
610   }\r
611 \r
612   boolean remove = false;\r
613   public void doMouseDraggedDefineMode(MouseEvent evt)\r
614   {\r
615     int res = findRes(evt);\r
616     int y = findSeq(evt);\r
617 \r
618     if(wrappedBlock!=startWrapBlock)\r
619         return;\r
620 \r
621     if(y>=av.alignment.getHeight())\r
622       y = av.alignment.getHeight()-1;\r
623 \r
624     if (stretchGroup == null)\r
625     {\r
626       return;\r
627     }\r
628 \r
629     if (res > av.alignment.getWidth())\r
630     {\r
631       res = av.alignment.getWidth() - 1;\r
632     }\r
633 \r
634     if (stretchGroup.getEndRes() == res)\r
635     {\r
636       // Edit end res position of selected group\r
637       changeEndRes = true;\r
638     }\r
639 \r
640     else if (stretchGroup.getStartRes() == res)\r
641     {\r
642       // Edit start res position of selected group\r
643       changeStartRes = true;\r
644     }\r
645 \r
646     if (res < av.getStartRes())\r
647     {\r
648       res = av.getStartRes();\r
649     }\r
650     else if (res > av.getEndRes() && !av.getWrapAlignment())\r
651     {\r
652       res = av.getEndRes();\r
653     }\r
654 \r
655     if (changeEndRes)\r
656     {\r
657       if (res > stretchGroup.getStartRes() - 1)\r
658       {\r
659         stretchGroup.setEndRes(res);\r
660       }\r
661     }\r
662     else if (changeStartRes)\r
663     {\r
664       if (res < stretchGroup.getEndRes() + 1)\r
665       {\r
666         stretchGroup.setStartRes(res);\r
667       }\r
668     }\r
669 \r
670     int dragDirection = 0;\r
671     if (y > oldSeq)\r
672     {\r
673       dragDirection = 1;\r
674     }\r
675     else if (y < oldSeq)\r
676     {\r
677       dragDirection = -1;\r
678     }\r
679 \r
680     while (y != oldSeq && oldSeq > 0 && y < av.alignment.getHeight())\r
681     {\r
682       // This routine ensures we don't skip any sequences, as the\r
683       // selection is quite slow.\r
684       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
685 \r
686       oldSeq += dragDirection;\r
687       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
688 \r
689       if (stretchGroup.sequences.contains(nextSeq))\r
690       {\r
691         stretchGroup.deleteSequence(seq, false);\r
692       }\r
693       else\r
694       {\r
695         if (seq != null)\r
696         {\r
697           stretchGroup.addSequence(seq, false);\r
698         }\r
699         stretchGroup.addSequence(nextSeq, false);\r
700       }\r
701     }\r
702     oldSeq = y;\r
703     mouseDragging = true;\r
704     if (scrollThread != null)\r
705     {\r
706       scrollThread.setEvent(evt);\r
707     }\r
708 \r
709     seqCanvas.repaint();\r
710   }\r
711 \r
712   public void mouseEntered(MouseEvent e)\r
713   {\r
714     if (editingSeqs && scrollThread != null)\r
715     {\r
716       scrollThread.running = false;\r
717     }\r
718   }\r
719 \r
720   public void mouseExited(MouseEvent e)\r
721   {\r
722     if (mouseDragging)\r
723     {\r
724       scrollThread = new ScrollThread();\r
725     }\r
726   }\r
727 \r
728   // this class allows scrolling off the bottom of the visible alignment\r
729   class ScrollThread\r
730       extends Thread\r
731   {\r
732     MouseEvent evt;\r
733     boolean running = false;\r
734     public ScrollThread()\r
735     {\r
736       start();\r
737     }\r
738 \r
739     public void setEvent(MouseEvent e)\r
740     {\r
741       evt = e;\r
742     }\r
743 \r
744     public void stopScrolling()\r
745     {\r
746       running = false;\r
747     }\r
748 \r
749     public void run()\r
750     {\r
751       running = true;\r
752       while (running)\r
753       {\r
754         if (evt != null)\r
755         {\r
756 \r
757           if (mouseDragging && evt.getY() < 0 && av.getStartSeq() > 0)\r
758           {\r
759             running = ap.scrollUp(true);\r
760           }\r
761 \r
762           if (mouseDragging && evt.getY() >= getSize().height &&\r
763               av.alignment.getHeight() > av.getEndSeq())\r
764           {\r
765             running = ap.scrollUp(false);\r
766           }\r
767 \r
768           if (mouseDragging && evt.getX() < 0)\r
769           {\r
770             running = ap.scrollRight(true);\r
771           }\r
772 \r
773           else if (mouseDragging && evt.getX() >= getSize().width)\r
774           {\r
775             running = ap.scrollRight(false);\r
776           }\r
777         }\r
778 \r
779         try\r
780         {\r
781           Thread.sleep(75);\r
782         }\r
783         catch (Exception ex)\r
784         {}\r
785       }\r
786     }\r
787   }\r
788 \r
789 }\r