2fe27b381bf84bffe35aac5fb94700b32ef764fa
[jalview.git] / src / jalview / gui / 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 package jalview.gui;\r
20 \r
21 import jalview.datamodel.*;\r
22 \r
23 import jalview.schemes.*;\r
24 \r
25 import java.awt.*;\r
26 import java.awt.event.*;\r
27 \r
28 import javax.swing.*;\r
29 \r
30 \r
31 /**\r
32  * DOCUMENT ME!\r
33  *\r
34  * @author $author$\r
35  * @version $Revision$\r
36  */\r
37 public class SeqPanel extends JPanel implements MouseListener,\r
38     MouseMotionListener, MouseWheelListener\r
39 \r
40 {\r
41     /** DOCUMENT ME!! */\r
42     public SeqCanvas seqCanvas;\r
43 \r
44     /** DOCUMENT ME!! */\r
45     public AlignmentPanel ap;\r
46     protected int lastres;\r
47     protected int startseq;\r
48     int startEdit = -1;\r
49     int endEdit = -1;\r
50     protected AlignViewport av;\r
51 \r
52     // if character is inserted or deleted, we will need to recalculate the conservation\r
53     boolean seqEditOccurred = false;\r
54     ScrollThread scrollThread = null;\r
55     boolean mouseDragging = false;\r
56     boolean editingSeqs = false;\r
57     boolean groupEditing = false;\r
58 \r
59     //////////////////////////////////////////\r
60     /////Everything below this is for defining the boundary of the rubberband\r
61     //////////////////////////////////////////\r
62     int oldSeq = -1;\r
63     boolean changeEndSeq = false;\r
64     boolean changeStartSeq = false;\r
65     boolean changeEndRes = false;\r
66     boolean changeStartRes = false;\r
67     SequenceGroup stretchGroup = null;\r
68     boolean remove = false;\r
69 \r
70     boolean mouseWheelPressed = false;\r
71 \r
72     /**\r
73      * Creates a new SeqPanel object.\r
74      *\r
75      * @param avp DOCUMENT ME!\r
76      * @param p DOCUMENT ME!\r
77      */\r
78     public SeqPanel(AlignViewport avp, AlignmentPanel p)\r
79     {\r
80         ToolTipManager.sharedInstance().registerComponent(this);\r
81         ToolTipManager.sharedInstance().setInitialDelay(0);\r
82         ToolTipManager.sharedInstance().setDismissDelay(10000);\r
83         this.av = avp;\r
84         setBackground(Color.white);\r
85 \r
86         seqCanvas = new SeqCanvas(avp);\r
87         setLayout(new BorderLayout());\r
88         add(seqCanvas, BorderLayout.CENTER);\r
89 \r
90         ap = p;\r
91 \r
92         if(!av.isDataset())\r
93         {\r
94           addMouseMotionListener(this);\r
95           addMouseListener(this);\r
96           addMouseWheelListener(this);\r
97         }\r
98     }\r
99 \r
100     int startWrapBlock=-1;\r
101     int wrappedBlock=-1;\r
102     int findRes(MouseEvent evt)\r
103    {\r
104      int res = 0;\r
105      int x = evt.getX();\r
106 \r
107     if (av.wrapAlignment)\r
108     {\r
109 \r
110       int hgap = av.charHeight;\r
111       if (av.scaleAboveWrapped)\r
112         hgap += av.charHeight;\r
113 \r
114       int cHeight = av.getAlignment().getHeight() * av.charHeight\r
115           + hgap + seqCanvas.getAnnotationHeight();\r
116 \r
117         int y = evt.getY();\r
118         y -= hgap;\r
119         x -= seqCanvas.LABEL_WEST;\r
120 \r
121 \r
122         int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());\r
123 \r
124         wrappedBlock = y / cHeight;\r
125         wrappedBlock += av.getStartRes() / cwidth;\r
126 \r
127         res = wrappedBlock * cwidth + x / av.getCharWidth();\r
128 \r
129     }\r
130     else\r
131     {\r
132         res = (x / av.getCharWidth()) + av.getStartRes();\r
133     }\r
134 \r
135     if(av.hasHiddenColumns)\r
136           res = av.getColumnSelection().adjustForHiddenColumns(res);\r
137 \r
138     return res;\r
139 \r
140    }\r
141 \r
142    int findSeq(MouseEvent evt)\r
143    {\r
144 \r
145      int seq = 0;\r
146      int y = evt.getY();\r
147 \r
148      if (av.wrapAlignment)\r
149      {\r
150        int hgap = av.charHeight;\r
151        if (av.scaleAboveWrapped)\r
152          hgap += av.charHeight;\r
153 \r
154        int cHeight = av.getAlignment().getHeight() * av.charHeight\r
155            + hgap + seqCanvas.getAnnotationHeight();\r
156 \r
157          y -= hgap;\r
158 \r
159        seq = ( (y % cHeight) / av.getCharHeight());\r
160      }\r
161      else\r
162      {\r
163        seq = (y / av.getCharHeight()) + av.getStartSeq();\r
164      }\r
165 \r
166      return seq;\r
167    }\r
168 \r
169    void endEditing()\r
170    {\r
171      startseq = -1;\r
172      lastres = -1;\r
173      seqEditOccurred = false;\r
174      editingSeqs = false;\r
175      groupEditing = false;\r
176     }\r
177 \r
178     /**\r
179      * DOCUMENT ME!\r
180      *\r
181      * @param evt DOCUMENT ME!\r
182      */\r
183     public void mouseReleased(MouseEvent evt)\r
184     {\r
185       mouseDragging = false;\r
186 \r
187       if (!editingSeqs)\r
188       {\r
189          doMouseReleasedDefineMode(evt);\r
190          return;\r
191       }\r
192 \r
193         if (seqEditOccurred)\r
194         {\r
195             editOccurred();\r
196         }\r
197 \r
198         endEditing();\r
199         ap.repaint();\r
200     }\r
201 \r
202 \r
203 \r
204     /**\r
205      * DOCUMENT ME!\r
206      *\r
207      * @param evt DOCUMENT ME!\r
208      */\r
209     public void mousePressed(MouseEvent evt)\r
210     {\r
211       if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))\r
212       {\r
213         mouseWheelPressed = true;\r
214         return;\r
215       }\r
216 \r
217       if (evt.isShiftDown() || evt.isAltDown() ||\r
218           evt.isControlDown())\r
219       {\r
220         if (evt.isAltDown() || evt.isControlDown())\r
221         {\r
222           groupEditing = true;\r
223         }\r
224         editingSeqs = true;\r
225       }\r
226       else\r
227       {\r
228         doMousePressedDefineMode(evt);\r
229         return;\r
230       }\r
231 \r
232 \r
233       ap.alignFrame.addHistoryItem(new HistoryItem("Edit Sequence",\r
234                                                    av.alignment, HistoryItem.EDIT));\r
235 \r
236       int seq = findSeq(evt);\r
237       int res = findRes(evt);\r
238 \r
239       if(seq<0 || res<0)\r
240         return;\r
241 \r
242         if ((seq < av.getAlignment().getHeight()) &&\r
243                 (res < av.getAlignment().getSequenceAt(seq).getLength()))\r
244         {\r
245             startseq = seq;\r
246             lastres = res;\r
247         }\r
248         else\r
249         {\r
250             startseq = -1;\r
251             lastres = -1;\r
252         }\r
253 \r
254         startEdit = lastres;\r
255         endEdit = lastres;\r
256 \r
257         return;\r
258     }\r
259 \r
260     /**\r
261      * DOCUMENT ME!\r
262      *\r
263      * @param evt DOCUMENT ME!\r
264      */\r
265     public void mouseMoved(MouseEvent evt)\r
266     {\r
267       if (editingSeqs)\r
268       {\r
269        // This is because MacOSX creates a mouseMoved\r
270        // If control is down, other platforms will not.\r
271        mouseDragged(evt);\r
272       }\r
273 \r
274       int res = findRes(evt);\r
275       int seq = findSeq(evt);\r
276 \r
277       if(res<0 || seq<0 || seq >= av.getAlignment().getHeight())\r
278             return;\r
279 \r
280       SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
281 \r
282       if (res > sequence.getLength())\r
283       {\r
284         return;\r
285       }\r
286 \r
287       if(seqCanvas.pdbCanvas!=null && sequence==seqCanvas.pdbCanvas.sequence)\r
288       {\r
289         seqCanvas.pdbCanvas.highlightRes(sequence.findPosition(res));\r
290       }\r
291 \r
292 \r
293         StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " +\r
294                 sequence.getName());\r
295 \r
296         Object obj = null;\r
297         if (av.alignment.isNucleotide())\r
298         {\r
299           obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) +\r
300               "");\r
301           if(obj!=null)\r
302             text.append(" Nucleotide: ");\r
303         }\r
304         else\r
305         {\r
306           obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");\r
307           if(obj!=null)\r
308             text.append("  Residue: ");\r
309         }\r
310 \r
311         if (obj != null)\r
312         {\r
313 \r
314           if (obj != "")\r
315           {\r
316             text.append( obj + " (" +\r
317                         av.getAlignment().getSequenceAt(seq).findPosition(res) + ")");\r
318           }\r
319         }\r
320 \r
321         ap.alignFrame.statusBar.setText(text.toString());\r
322 \r
323         // use aa to see if the mouse pointer is on a\r
324         if (av.showSequenceFeatures)\r
325         {\r
326             SequenceFeature [] features = sequence.getDatasetSequence().getSequenceFeatures();\r
327             if(features!=null)\r
328             {\r
329               StringBuffer sbuffer = new StringBuffer("<html>");\r
330 \r
331               for (int i = 0; i < features.length; i++)\r
332               {\r
333 \r
334                 if ( (features[i].getBegin() <= sequence.findPosition(res)) &&\r
335                     (features[i].getEnd() >= sequence.findPosition(res)))\r
336                 {\r
337                   if(!av.featuresDisplayed.containsKey(features[i].getType()))\r
338                   continue;\r
339 \r
340 \r
341                   if (features[i].getType().equals("disulfide bond"))\r
342                   {\r
343                     if (features[i].getBegin() == sequence.findPosition(res)\r
344                         || features[i].getEnd() == sequence.findPosition(res))\r
345                     {\r
346                       if (sbuffer.length() > 6)\r
347                         sbuffer.append("<br>");\r
348                       sbuffer.append("disulfide bond " + features[i].getBegin() + ":" +\r
349                                      features[i].getEnd());\r
350                     }\r
351                   }\r
352                   else\r
353                   {\r
354                     if (sbuffer.length() > 6)\r
355                       sbuffer.append("<br>");\r
356                     if(features[i].featureGroup!=null)\r
357                       sbuffer.append(features[i].featureGroup+";");\r
358 \r
359                     sbuffer.append(features[i].getType());\r
360 \r
361                     if (features[i].getDescription() != null\r
362                         && !features[i].description.equals(features[i].getType()))\r
363                       sbuffer.append("; " + features[i].getDescription());\r
364 \r
365                     if (features[i].getStatus() != null && features[i].getStatus().length()>0)\r
366                     {\r
367                       sbuffer.append("; (" + features[i].getStatus() + ")");\r
368                     }\r
369                   }\r
370                 }\r
371 \r
372               }\r
373 \r
374               sbuffer.append("</html>");\r
375               if(sbuffer.length()==13) // <html></html>\r
376                 setToolTipText("");\r
377               else\r
378                setToolTipText(sbuffer.toString());\r
379             }\r
380             else\r
381               setToolTipText("");\r
382         }\r
383     }\r
384 \r
385     /**\r
386      * DOCUMENT ME!\r
387      *\r
388      * @param evt DOCUMENT ME!\r
389      */\r
390     public void mouseDragged(MouseEvent evt)\r
391     {\r
392       if (!editingSeqs)\r
393       {\r
394         doMouseDraggedDefineMode(evt);\r
395         return;\r
396       }\r
397 \r
398         int res = findRes(evt);\r
399 \r
400         if (res < 0)\r
401         {\r
402             res = 0;\r
403         }\r
404 \r
405         if ((lastres == -1) || (lastres == res))\r
406         {\r
407             return;\r
408         }\r
409 \r
410         boolean dragRight = true;\r
411 \r
412         if ((res < av.getAlignment().getWidth()) && (res < lastres))\r
413         {\r
414             dragRight = false;\r
415         }\r
416         else if(av.hasHiddenColumns)\r
417         {\r
418           //Stop editing if the user has dragged beyond hiddenBoundary\r
419           int lastCol = av.getColumnSelection().getHiddenRegionBoundary(lastres);\r
420           if( lastCol < res)\r
421           {\r
422             if(lastres!=lastCol)\r
423             {\r
424               endEditing();\r
425               return;\r
426             }\r
427           }\r
428         }\r
429 \r
430         if(!groupEditing && av.hasHiddenRows)\r
431         {\r
432           if(av.alignment.getSequenceAt(startseq).getHiddenSequences()!=null)\r
433           {\r
434             groupEditing = true;\r
435           }\r
436         }\r
437 \r
438 \r
439         if (res != lastres)\r
440         {\r
441             SequenceI seq;\r
442             // Group editing\r
443             if (groupEditing)\r
444             {\r
445                 SequenceGroup sg = av.getSelectionGroup();\r
446 \r
447                 if (av.hasHiddenRows)\r
448                 {\r
449                   //sg might be null as the user may only see 1 sequence\r
450                   if(sg==null)\r
451                   {\r
452                     sg = new SequenceGroup();\r
453                     sg.addSequence(av.alignment.getSequenceAt(startseq), false);\r
454                   }\r
455 \r
456                   SequenceGroup tmp = new SequenceGroup();\r
457 \r
458                   //Do any of the sequences have hidden associates?\r
459                   for (int s = 0; s < sg.getSize(); s++)\r
460                   {\r
461                     seq = sg.getSequenceAt(s);\r
462                     tmp.addSequence(seq, false);\r
463                     if (seq.getHiddenSequences()!=null)\r
464                     {\r
465                       for(int h=0; h<seq.getHiddenSequences().getSize(); h++)\r
466                         tmp.addSequence(seq.getHiddenSequences().getSequenceAt(h),\r
467                                         false);\r
468                     }\r
469                   }\r
470 \r
471                   sg = tmp;\r
472                 }\r
473 \r
474 \r
475                 if (sg == null)\r
476                 {\r
477                     endEditing();\r
478                     return;\r
479                 }\r
480 \r
481                 int blankColumn = -1;\r
482 \r
483                 // drag to right\r
484                 if (dragRight)\r
485                 {\r
486 \r
487                   // Is it valid??\r
488                   // Find the next gap before the end\r
489                   // of the visible region boundary\r
490                   if (av.hasHiddenColumns)\r
491                   {\r
492                     if(res-lastres > 1)\r
493                     {\r
494                       res = lastres+1;\r
495                     }\r
496 \r
497 \r
498                     int lastCol = av.getColumnSelection().\r
499                           getHiddenRegionBoundary(res);\r
500 \r
501                     if(lastCol!=res)\r
502                     {\r
503                       for (blankColumn = lastCol;\r
504                            blankColumn > lastres;\r
505                            blankColumn--)\r
506                       {\r
507                         boolean blank = true;\r
508                         for (int s = 0; s < sg.getSize(); s++)\r
509                         {\r
510                           seq = sg.getSequenceAt(s);\r
511 \r
512                           if (seq.getSequence().length() <= blankColumn)\r
513                           {\r
514                             continue;\r
515                           }\r
516 \r
517                           if (!jalview.util.Comparison.isGap(\r
518                               seq.getSequence().charAt(blankColumn)))\r
519                           {\r
520                             blank = false;\r
521                             continue;\r
522                           }\r
523                         }\r
524                         if (blank)\r
525                           break;\r
526                       }\r
527 \r
528 \r
529                       if (blankColumn <= lastres)\r
530                       {\r
531                         endEditing();\r
532                         return;\r
533                       }\r
534                     }\r
535                     else\r
536                       blankColumn = -1;\r
537 \r
538                   }\r
539 \r
540                   sg.setEndRes(sg.getEndRes() + (res - lastres));\r
541                 }\r
542 \r
543                 // drag to left\r
544                 else\r
545                 {\r
546                     /// Are we able to delete?\r
547                     // ie are all columns blank?\r
548 \r
549                     for (int s = 0; s < sg.getSize(); s++)\r
550                     {\r
551                         seq = sg.getSequenceAt(s);\r
552 \r
553                         for (int j = res; j < lastres; j++)\r
554                         {\r
555                             if (seq.getSequence().length() <= j)\r
556                             {\r
557                                 continue;\r
558                             }\r
559 \r
560                             if (!jalview.util.Comparison.isGap(\r
561                                         seq.getSequence().charAt(j)))\r
562                             {\r
563                                 // Not a gap, block edit not valid\r
564                                 endEditing();\r
565                                 return;\r
566                             }\r
567                         }\r
568                     }\r
569 \r
570                     if(res<sg.getStartRes())\r
571                     {\r
572                       sg.setStartRes(sg.getStartRes() - 1);\r
573                     }\r
574                     sg.setEndRes(sg.getEndRes() - (lastres - res));\r
575                 }\r
576 \r
577                 for (int i = 0; i < sg.getSize(); i++)\r
578                 {\r
579                     seq = sg.getSequenceAt(i);\r
580 \r
581                     // drag to right\r
582                     if (dragRight)\r
583                     {\r
584                         for (int j = lastres; j < res; j++)\r
585                         {\r
586                             insertChar(j, seq, blankColumn);\r
587                         }\r
588                     }\r
589 \r
590                     // drag to left\r
591                     else\r
592                     {\r
593                         for (int j = res; j < lastres; j++)\r
594                         {\r
595                             if (seq.getLength()-1 > res)\r
596                             {\r
597                                 deleteChar(res, seq);\r
598                             }\r
599                         }\r
600                     }\r
601                 }\r
602             }\r
603             else /////Editing a single sequence///////////\r
604             {\r
605                 seq = av.alignment.getSequenceAt(startseq);\r
606                 if ((res < av.getAlignment().getWidth()) && (res > lastres))\r
607                 {\r
608                     // dragging to the right\r
609                     for (int j = lastres; j < res; j++)\r
610                     {\r
611                         insertChar(j, seq, j);\r
612                     }\r
613                 }\r
614                 else if ((res < av.getAlignment().getWidth()) &&\r
615                         (res < lastres))\r
616                 {\r
617                     // dragging to the left\r
618                     for (int j = lastres; j > res; j--)\r
619                     {\r
620                         if (jalview.util.Comparison.isGap(\r
621                                     av.alignment.getSequenceAt(startseq)\r
622                                                     .getSequence().charAt(res)))\r
623                         {\r
624                             deleteChar(res, seq);\r
625                         }\r
626                         else\r
627                         {\r
628                             break;\r
629                         }\r
630                     }\r
631                 }\r
632             }\r
633         }\r
634 \r
635         mouseDragging = true;\r
636         if(scrollThread!=null)\r
637           scrollThread.setEvent(evt);\r
638 \r
639         endEdit = res;\r
640         lastres = res;\r
641         seqCanvas.repaint();\r
642     }\r
643 \r
644     /**\r
645      * DOCUMENT ME!\r
646      *\r
647      * @param e DOCUMENT ME!\r
648      */\r
649     public void mouseEntered(MouseEvent e)\r
650     {\r
651         if(oldSeq < 0)\r
652           oldSeq = 0;\r
653 \r
654         if (scrollThread != null)\r
655         {\r
656             scrollThread.running = false;\r
657             scrollThread = null;\r
658         }\r
659     }\r
660 \r
661     /**\r
662      * DOCUMENT ME!\r
663      *\r
664      * @param e DOCUMENT ME!\r
665      */\r
666     public void mouseExited(MouseEvent e)\r
667     {\r
668         if (av.getWrapAlignment())\r
669         {\r
670             return;\r
671         }\r
672 \r
673         if (mouseDragging)\r
674         {\r
675             scrollThread = new ScrollThread();\r
676         }\r
677     }\r
678 \r
679     public void mouseClicked(MouseEvent evt)\r
680     {}\r
681 \r
682     public void mouseWheelMoved(MouseWheelEvent e)\r
683     {\r
684       if (mouseWheelPressed)\r
685       {\r
686         Font font = av.getFont();\r
687         int fontSize = font.getSize();\r
688         if (e.getWheelRotation() > 0 && fontSize < 51)\r
689           fontSize++;\r
690         else if (fontSize > 1)\r
691           fontSize--;\r
692 \r
693         av.setFont(new Font(font.getName(), font.getStyle(), fontSize));\r
694         ap.fontChanged();\r
695       }\r
696       else\r
697       {\r
698         if (e.getWheelRotation() > 0)\r
699           ap.scrollUp(false);\r
700         else\r
701           ap.scrollUp(true);\r
702       }\r
703 \r
704     }\r
705 \r
706 \r
707     /**\r
708      * DOCUMENT ME!\r
709      *\r
710      * @param j DOCUMENT ME!\r
711      * @param seq DOCUMENT ME!\r
712      */\r
713     void insertChar(int j, SequenceI seq, int blankColumn)\r
714     {\r
715         if(av.hasHiddenColumns)\r
716         {\r
717           //Find the next gap before the end of the visible region boundary\r
718           int lastCol = av.getColumnSelection().getHiddenRegionBoundary(j);\r
719           if(lastCol != j)\r
720           {\r
721             if (!groupEditing || lastCol <= j)\r
722             {\r
723               blankColumn = lastCol;\r
724               //If lastCol > j, theres a boundary after the gap insertion\r
725               if (lastCol > j)\r
726               {\r
727                 for (blankColumn = lastCol; blankColumn > j; blankColumn--)\r
728                 {\r
729                   if (jalview.util.Comparison.isGap(seq.getCharAt(blankColumn)))\r
730                   {\r
731                     //Theres a space, so break and insert the gap\r
732                     break;\r
733                   }\r
734                 }\r
735 \r
736                 if (blankColumn <= j)\r
737                 {\r
738                   endEditing();\r
739                   return;\r
740                 }\r
741               }\r
742 \r
743             }\r
744 \r
745             // Editing with hidden regions only!!\r
746             seq.deleteCharAt(blankColumn);\r
747           }\r
748       }\r
749 \r
750         seq.insertCharAt(j, av.getGapCharacter());\r
751         seqEditOccurred = true;\r
752     }\r
753 \r
754     /**\r
755      * DOCUMENT ME!\r
756      *\r
757      * @param j DOCUMENT ME!\r
758      * @param seq DOCUMENT ME!\r
759      */\r
760     public void deleteChar(int j, SequenceI seq)\r
761     {\r
762       if (av.hasHiddenColumns)\r
763       {\r
764         //Find the next gap before the end of the visible region boundary\r
765         int lastCol = av.getColumnSelection().getHiddenRegionBoundary(j);\r
766 \r
767         //If lastCol > j, theres a boundary after the gap insertion\r
768         if (lastCol > j)\r
769         {\r
770           seq.insertCharAt(lastCol, av.getGapCharacter());\r
771         }\r
772       }\r
773 \r
774         seq.deleteCharAt(j);\r
775         seqEditOccurred = true;\r
776 \r
777         seqCanvas.repaint();\r
778     }\r
779 \r
780     /**\r
781      * DOCUMENT ME!\r
782      *\r
783      * @param i DOCUMENT ME!\r
784      */\r
785     void editOccurred()\r
786     {\r
787       if (endEdit == startEdit)\r
788       {\r
789         ap.alignFrame.historyList.pop();\r
790         ap.alignFrame.updateEditMenuBar();\r
791       }\r
792 \r
793       av.firePropertyChange("alignment", null,av.getAlignment().getSequences());\r
794 \r
795     }\r
796 \r
797     /**\r
798      * DOCUMENT ME!\r
799      *\r
800      * @param evt DOCUMENT ME!\r
801      */\r
802     public void doMousePressedDefineMode(MouseEvent evt)\r
803     {\r
804       int res = findRes(evt);\r
805       int seq = findSeq(evt);\r
806       oldSeq = seq;\r
807 \r
808       startWrapBlock=wrappedBlock;\r
809 \r
810       if(av.wrapAlignment && seq>av.alignment.getHeight())\r
811       {\r
812           JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
813               "Cannot edit annotations in wrapped view.",\r
814               "Wrapped view - no edit",\r
815               JOptionPane.WARNING_MESSAGE);\r
816         return;\r
817       }\r
818 \r
819       if(seq<0 || res<0)\r
820             return;\r
821 \r
822 \r
823         SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
824 \r
825         if ((sequence == null) || (res > sequence.getLength()))\r
826         {\r
827             return;\r
828         }\r
829 \r
830         stretchGroup = av.getSelectionGroup();\r
831 \r
832         if (stretchGroup == null)\r
833         {\r
834             stretchGroup = av.alignment.findGroup(sequence);\r
835 \r
836             if ((stretchGroup != null) && (res > stretchGroup.getStartRes()) &&\r
837                     (res < stretchGroup.getEndRes()))\r
838             {\r
839                 av.setSelectionGroup(stretchGroup);\r
840             }\r
841             else\r
842             {\r
843                 stretchGroup = null;\r
844             }\r
845         }\r
846         else if (!stretchGroup.sequences.contains(sequence) ||\r
847                 (stretchGroup.getStartRes() > res) ||\r
848                 (stretchGroup.getEndRes() < res))\r
849         {\r
850             stretchGroup = null;\r
851 \r
852             SequenceGroup[] allGroups = av.alignment.findAllGroups(sequence);\r
853 \r
854             if (allGroups != null)\r
855             {\r
856                 for (int i = 0; i < allGroups.length; i++)\r
857                 {\r
858                     if ((allGroups[i].getStartRes() <= res) &&\r
859                             (allGroups[i].getEndRes() >= res))\r
860                     {\r
861                         stretchGroup = allGroups[i];\r
862                         av.setSelectionGroup(stretchGroup);\r
863 \r
864                         break;\r
865                     }\r
866                 }\r
867             }\r
868         }\r
869 \r
870         if (stretchGroup == null)\r
871         {\r
872             // define a new group here\r
873             SequenceGroup sg = new SequenceGroup();\r
874             sg.setStartRes(res);\r
875             sg.setEndRes(res);\r
876             sg.addSequence(sequence, false);\r
877             av.setSelectionGroup(sg);\r
878             stretchGroup = sg;\r
879 \r
880             if (av.getConservationSelected())\r
881             {\r
882                 SliderPanel.setConservationSlider(ap,\r
883                     av.getGlobalColourScheme(), "Background");\r
884             }\r
885 \r
886             if (av.getAbovePIDThreshold())\r
887             {\r
888                 SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
889                     "Background");\r
890             }\r
891         }\r
892         else if (javax.swing.SwingUtilities.isRightMouseButton(evt))\r
893         {\r
894             jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null);\r
895             pop.show(this, evt.getX(), evt.getY());\r
896 \r
897             // edit the properties of existing group\r
898         }\r
899 \r
900         if ((stretchGroup != null) && (stretchGroup.getEndRes() == res))\r
901         {\r
902             // Edit end res position of selected group\r
903             changeEndRes = true;\r
904         }\r
905         else if ((stretchGroup != null) && (stretchGroup.getStartRes() == res))\r
906         {\r
907             // Edit end res position of selected group\r
908             changeStartRes = true;\r
909         }\r
910 \r
911         stretchGroup.getWidth();\r
912 \r
913         seqCanvas.repaint();\r
914     }\r
915 \r
916     /**\r
917      * DOCUMENT ME!\r
918      *\r
919      * @param evt DOCUMENT ME!\r
920      */\r
921     public void doMouseReleasedDefineMode(MouseEvent evt)\r
922     {\r
923         if (stretchGroup == null)\r
924         {\r
925             return;\r
926         }\r
927 \r
928 \r
929         if(stretchGroup.cs!=null)\r
930         {\r
931           if (stretchGroup.cs instanceof ClustalxColourScheme)\r
932           {\r
933             ( (ClustalxColourScheme) stretchGroup.cs).resetClustalX(stretchGroup.\r
934                 sequences,\r
935                 stretchGroup.getWidth());\r
936           }\r
937 \r
938           if (stretchGroup.cs.conservationApplied())\r
939           {\r
940             SliderPanel.setConservationSlider(ap, stretchGroup.cs,\r
941                                               stretchGroup.getName());\r
942             stretchGroup.recalcConservation();\r
943           }\r
944           else\r
945           {\r
946             SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,\r
947                                            stretchGroup.getName());\r
948           }\r
949         }\r
950         changeEndRes = false;\r
951         changeStartRes = false;\r
952         stretchGroup = null;\r
953         PaintRefresher.Refresh(av.alignment);\r
954     }\r
955 \r
956     /**\r
957      * DOCUMENT ME!\r
958      *\r
959      * @param evt DOCUMENT ME!\r
960      */\r
961     public void doMouseDraggedDefineMode(MouseEvent evt)\r
962     {\r
963       int res = findRes(evt);\r
964       int y = findSeq(evt);\r
965 \r
966       if(wrappedBlock!=startWrapBlock)\r
967         return;\r
968 \r
969        if (stretchGroup == null)\r
970        {\r
971             return;\r
972        }\r
973 \r
974 \r
975         if(y > av.alignment.getHeight())\r
976         {\r
977           y = av.alignment.getHeight() -1;\r
978         }\r
979 \r
980         if (stretchGroup.getEndRes() == res)\r
981         {\r
982             // Edit end res position of selected group\r
983             changeEndRes = true;\r
984         }\r
985         else if (stretchGroup.getStartRes() == res)\r
986         {\r
987             // Edit start res position of selected group\r
988             changeStartRes = true;\r
989         }\r
990 \r
991         if (res < av.getStartRes())\r
992         {\r
993             res = av.getStartRes();\r
994         }\r
995 \r
996         if (changeEndRes)\r
997         {\r
998             if (res > (stretchGroup.getStartRes() - 1))\r
999             {\r
1000                 stretchGroup.setEndRes(res);\r
1001             }\r
1002         }\r
1003         else if (changeStartRes)\r
1004         {\r
1005             if (res < (stretchGroup.getEndRes() + 1))\r
1006             {\r
1007                 stretchGroup.setStartRes(res);\r
1008             }\r
1009         }\r
1010 \r
1011         int dragDirection = 0;\r
1012 \r
1013         if (y > oldSeq)\r
1014         {\r
1015             dragDirection = 1;\r
1016         }\r
1017         else if (y < oldSeq)\r
1018         {\r
1019             dragDirection = -1;\r
1020         }\r
1021 \r
1022 \r
1023         while ((y != oldSeq) && (oldSeq > -1) && (y < av.alignment.getHeight()))\r
1024         {\r
1025             // This routine ensures we don't skip any sequences, as the\r
1026             // selection is quite slow.\r
1027             Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1028 \r
1029             oldSeq += dragDirection;\r
1030 \r
1031             if(oldSeq<0)\r
1032               break;\r
1033 \r
1034             Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1035 \r
1036             if (stretchGroup.sequences.contains(nextSeq))\r
1037             {\r
1038                 stretchGroup.deleteSequence(seq, false);\r
1039             }\r
1040             else\r
1041             {\r
1042                 if (seq != null)\r
1043                 {\r
1044                     stretchGroup.addSequence(seq, false);\r
1045                 }\r
1046 \r
1047                 stretchGroup.addSequence(nextSeq, false);\r
1048             }\r
1049         }\r
1050 \r
1051         if(oldSeq < 0)\r
1052           oldSeq = -1;\r
1053 \r
1054         mouseDragging = true;\r
1055 \r
1056         if (scrollThread != null)\r
1057         {\r
1058             scrollThread.setEvent(evt);\r
1059         }\r
1060 \r
1061         seqCanvas.repaint();\r
1062     }\r
1063 \r
1064 \r
1065 \r
1066     // this class allows scrolling off the bottom of the visible alignment\r
1067     class ScrollThread extends Thread\r
1068     {\r
1069         MouseEvent evt;\r
1070         boolean running = false;\r
1071 \r
1072         public ScrollThread()\r
1073         {\r
1074             start();\r
1075         }\r
1076 \r
1077         public void setEvent(MouseEvent e)\r
1078         {\r
1079             evt = e;\r
1080         }\r
1081 \r
1082         public void stopScrolling()\r
1083         {\r
1084             running = false;\r
1085         }\r
1086 \r
1087         public void run()\r
1088         {\r
1089             running = true;\r
1090 \r
1091             while (running)\r
1092             {\r
1093                 if (evt != null)\r
1094                 {\r
1095                     if (mouseDragging && (evt.getY() < 0) &&\r
1096                             (av.getStartSeq() > 0))\r
1097                     {\r
1098                         running = ap.scrollUp(true);\r
1099                     }\r
1100 \r
1101                     if (mouseDragging && (evt.getY() >= getHeight()) &&\r
1102                             (av.alignment.getHeight() > av.getEndSeq()))\r
1103                     {\r
1104                         running = ap.scrollUp(false);\r
1105                     }\r
1106 \r
1107                     if (mouseDragging && (evt.getX() < 0))\r
1108                     {\r
1109                         running = ap.scrollRight(true);\r
1110                     }\r
1111                     else if (mouseDragging && (evt.getX() >= getWidth()))\r
1112                     {\r
1113                         running = ap.scrollRight(false);\r
1114                     }\r
1115                 }\r
1116 \r
1117                 try\r
1118                 {\r
1119                     Thread.sleep(20);\r
1120                 }\r
1121                 catch (Exception ex)\r
1122                 {\r
1123                 }\r
1124             }\r
1125         }\r
1126     }\r
1127 }\r