remove excessive painting
[jalview.git] / src / jalview / gui / SeqPanel.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer\r
3  * Copyright (C) 2007 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 java.util.*;\r
22 \r
23 import java.awt.*;\r
24 import java.awt.event.*;\r
25 import javax.swing.*;\r
26 \r
27 import jalview.commands.*;\r
28 import jalview.datamodel.*;\r
29 import jalview.schemes.*;\r
30 import jalview.structure.*;\r
31 \r
32 /**\r
33  * DOCUMENT ME!\r
34  *\r
35  * @author $author$\r
36  * @version $Revision$\r
37  */\r
38 public class SeqPanel\r
39     extends JPanel implements MouseListener,\r
40     MouseMotionListener, MouseWheelListener, SequenceListener\r
41 \r
42 {\r
43   /** DOCUMENT ME!! */\r
44   public SeqCanvas seqCanvas;\r
45 \r
46   /** DOCUMENT ME!! */\r
47   public AlignmentPanel ap;\r
48   protected int lastres;\r
49   protected int startseq;\r
50   protected AlignViewport av;\r
51 \r
52   ScrollThread scrollThread = null;\r
53   boolean mouseDragging = false;\r
54   boolean editingSeqs = false;\r
55   boolean groupEditing = false;\r
56 \r
57   //////////////////////////////////////////\r
58   /////Everything below this is for defining the boundary of the rubberband\r
59   //////////////////////////////////////////\r
60   int oldSeq = -1;\r
61   boolean changeEndSeq = false;\r
62   boolean changeStartSeq = false;\r
63   boolean changeEndRes = false;\r
64   boolean changeStartRes = false;\r
65   SequenceGroup stretchGroup = null;\r
66   boolean remove = false;\r
67 \r
68   Point lastMousePress;\r
69   boolean mouseWheelPressed = false;\r
70   StringBuffer keyboardNo1;\r
71   StringBuffer keyboardNo2;\r
72 \r
73   java.net.URL linkImageURL;\r
74 \r
75   StringBuffer tooltipText = new StringBuffer("<html>");\r
76   String tmpString;\r
77 \r
78   EditCommand editCommand;\r
79 \r
80   StructureSelectionManager ssm;\r
81 \r
82 \r
83   /**\r
84    * Creates a new SeqPanel object.\r
85    *\r
86    * @param avp DOCUMENT ME!\r
87    * @param p DOCUMENT ME!\r
88    */\r
89   public SeqPanel(AlignViewport av, AlignmentPanel ap)\r
90   {\r
91     linkImageURL = getClass().getResource("/images/link.gif");\r
92     ToolTipManager.sharedInstance().registerComponent(this);\r
93     ToolTipManager.sharedInstance().setInitialDelay(0);\r
94     ToolTipManager.sharedInstance().setDismissDelay(10000);\r
95     this.av = av;\r
96     setBackground(Color.white);\r
97 \r
98     seqCanvas = new SeqCanvas(ap);\r
99     setLayout(new BorderLayout());\r
100     add(seqCanvas, BorderLayout.CENTER);\r
101 \r
102     this.ap = ap;\r
103 \r
104     if (!av.isDataset())\r
105     {\r
106       addMouseMotionListener(this);\r
107       addMouseListener(this);\r
108       addMouseWheelListener(this);\r
109       ssm = StructureSelectionManager.getStructureSelectionManager();\r
110       ssm.addStructureViewerListener(this);\r
111     }\r
112   }\r
113 \r
114   int startWrapBlock = -1;\r
115   int wrappedBlock = -1;\r
116   int findRes(MouseEvent evt)\r
117   {\r
118     int res = 0;\r
119     int x = evt.getX();\r
120 \r
121     if (av.wrapAlignment)\r
122     {\r
123 \r
124       int hgap = av.charHeight;\r
125       if (av.scaleAboveWrapped)\r
126       {\r
127         hgap += av.charHeight;\r
128       }\r
129 \r
130       int cHeight = av.getAlignment().getHeight() * av.charHeight\r
131           + hgap + seqCanvas.getAnnotationHeight();\r
132 \r
133       int y = evt.getY();\r
134       y -= hgap;\r
135       x -= seqCanvas.LABEL_WEST;\r
136 \r
137       int cwidth = seqCanvas.getWrappedCanvasWidth(this.getWidth());\r
138       if (cwidth < 1)\r
139       {\r
140         return 0;\r
141       }\r
142 \r
143       wrappedBlock = y / cHeight;\r
144       wrappedBlock += av.getStartRes() / cwidth;\r
145 \r
146       res = wrappedBlock * cwidth + x / av.getCharWidth();\r
147 \r
148     }\r
149     else\r
150     {\r
151       res = (x / av.getCharWidth()) + av.getStartRes();\r
152     }\r
153 \r
154     if (av.hasHiddenColumns)\r
155     {\r
156       res = av.getColumnSelection().adjustForHiddenColumns(res);\r
157     }\r
158 \r
159     return res;\r
160 \r
161   }\r
162 \r
163   int findSeq(MouseEvent evt)\r
164   {\r
165     int seq = 0;\r
166     int y = evt.getY();\r
167 \r
168     if (av.wrapAlignment)\r
169     {\r
170       int hgap = av.charHeight;\r
171       if (av.scaleAboveWrapped)\r
172       {\r
173         hgap += av.charHeight;\r
174       }\r
175 \r
176       int cHeight = av.getAlignment().getHeight() * av.charHeight\r
177           + hgap + seqCanvas.getAnnotationHeight();\r
178 \r
179       y -= hgap;\r
180 \r
181       seq = Math.min( (y % cHeight) / av.getCharHeight(),\r
182                      av.alignment.getHeight() - 1);\r
183     }\r
184     else\r
185     {\r
186       seq = Math.min( (y / av.getCharHeight()) + av.getStartSeq(),\r
187                      av.alignment.getHeight() - 1);\r
188     }\r
189 \r
190     return seq;\r
191   }\r
192 \r
193   SequenceFeature[] findFeaturesAtRes(SequenceI sequence, int res)\r
194   {\r
195     Vector tmp = new Vector();\r
196     SequenceFeature[] features = sequence.getSequenceFeatures();\r
197     if (features != null)\r
198     {\r
199       for (int i = 0; i < features.length; i++)\r
200       {\r
201         if (av.featuresDisplayed == null\r
202             || !av.featuresDisplayed.containsKey(features[i].getType()))\r
203         {\r
204           continue;\r
205         }\r
206 \r
207         if (features[i].featureGroup != null\r
208            && seqCanvas.fr.featureGroups!=null\r
209             && seqCanvas.fr.featureGroups.containsKey(features[i].featureGroup)\r
210             && !((Boolean)seqCanvas.fr.featureGroups.get(features[i].featureGroup)).booleanValue())\r
211           continue;\r
212 \r
213 \r
214         if ( (features[i].getBegin() <= res) &&\r
215             (features[i].getEnd() >= res))\r
216         {\r
217           tmp.addElement(features[i]);\r
218         }\r
219       }\r
220     }\r
221 \r
222     features = new SequenceFeature[tmp.size()];\r
223     tmp.copyInto(features);\r
224 \r
225     return features;\r
226   }\r
227 \r
228 \r
229   void endEditing()\r
230   {\r
231     if (editCommand != null && editCommand.getSize() > 0)\r
232     {\r
233       ap.alignFrame.addHistoryItem(editCommand);\r
234       av.firePropertyChange("alignment", null,\r
235                             av.getAlignment().getSequences());\r
236     }\r
237 \r
238     startseq = -1;\r
239     lastres = -1;\r
240     editingSeqs = false;\r
241     groupEditing = false;\r
242     keyboardNo1 = null;\r
243     keyboardNo2 = null;\r
244     editCommand = null;\r
245   }\r
246 \r
247   void setCursorRow()\r
248   {\r
249     seqCanvas.cursorY = getKeyboardNo1() - 1;\r
250     scrollToVisible();\r
251   }\r
252 \r
253   void setCursorColumn()\r
254   {\r
255     seqCanvas.cursorX = getKeyboardNo1() - 1;\r
256     scrollToVisible();\r
257   }\r
258 \r
259   void setCursorRowAndColumn()\r
260   {\r
261     if (keyboardNo2 == null)\r
262     {\r
263       keyboardNo2 = new StringBuffer();\r
264     }\r
265     else\r
266     {\r
267       seqCanvas.cursorX = getKeyboardNo1() - 1;\r
268       seqCanvas.cursorY = getKeyboardNo2() - 1;\r
269       scrollToVisible();\r
270     }\r
271   }\r
272 \r
273   void setCursorPosition()\r
274   {\r
275     SequenceI sequence =\r
276         (Sequence) av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
277 \r
278     seqCanvas.cursorX = sequence.findIndex(\r
279         getKeyboardNo1() - 1\r
280         );\r
281     scrollToVisible();\r
282   }\r
283 \r
284   void moveCursor(int dx, int dy)\r
285   {\r
286     seqCanvas.cursorX += dx;\r
287     seqCanvas.cursorY += dy;\r
288     if (av.hasHiddenColumns && !av.colSel.isVisible(seqCanvas.cursorX))\r
289     {\r
290       int original = seqCanvas.cursorX - dx;\r
291       int maxWidth = av.alignment.getWidth();\r
292 \r
293       while (!av.colSel.isVisible(seqCanvas.cursorX)\r
294              && seqCanvas.cursorX < maxWidth\r
295              && seqCanvas.cursorX > 0)\r
296       {\r
297         seqCanvas.cursorX += dx;\r
298       }\r
299 \r
300       if (seqCanvas.cursorX >= maxWidth\r
301           || !av.colSel.isVisible(seqCanvas.cursorX))\r
302       {\r
303         seqCanvas.cursorX = original;\r
304       }\r
305     }\r
306 \r
307     scrollToVisible();\r
308   }\r
309 \r
310   void scrollToVisible()\r
311   {\r
312     if (seqCanvas.cursorX < 0)\r
313     {\r
314       seqCanvas.cursorX = 0;\r
315     }\r
316     else if (seqCanvas.cursorX > av.alignment.getWidth() - 1)\r
317     {\r
318       seqCanvas.cursorX = av.alignment.getWidth() - 1;\r
319     }\r
320 \r
321     if (seqCanvas.cursorY < 0)\r
322     {\r
323       seqCanvas.cursorY = 0;\r
324     }\r
325     else if (seqCanvas.cursorY > av.alignment.getHeight() - 1)\r
326     {\r
327       seqCanvas.cursorY = av.alignment.getHeight() - 1;\r
328     }\r
329 \r
330     endEditing();\r
331     if (av.wrapAlignment)\r
332     {\r
333       ap.scrollToWrappedVisible(seqCanvas.cursorX);\r
334     }\r
335     else\r
336     {\r
337       while (seqCanvas.cursorY < av.startSeq)\r
338       {\r
339         ap.scrollUp(true);\r
340       }\r
341       while (seqCanvas.cursorY + 1 > av.endSeq)\r
342       {\r
343         ap.scrollUp(false);\r
344       }\r
345       if (!av.wrapAlignment)\r
346       {\r
347         while (seqCanvas.cursorX < av.colSel.adjustForHiddenColumns(av.startRes))\r
348         {\r
349           if (!ap.scrollRight(false))\r
350           {\r
351             break;\r
352           }\r
353         }\r
354         while (seqCanvas.cursorX > av.colSel.adjustForHiddenColumns(av.endRes))\r
355         {\r
356           if (!ap.scrollRight(true))\r
357           {\r
358             break;\r
359           }\r
360         }\r
361       }\r
362     }\r
363     setStatusMessage(av.alignment.getSequenceAt(seqCanvas.cursorY),\r
364                      seqCanvas.cursorX, seqCanvas.cursorY);\r
365 \r
366     seqCanvas.repaint();\r
367   }\r
368 \r
369   void setSelectionAreaAtCursor(boolean topLeft)\r
370   {\r
371     SequenceI sequence =\r
372         (Sequence) av.getAlignment().getSequenceAt(seqCanvas.cursorY);\r
373 \r
374     if (av.getSelectionGroup() != null)\r
375     {\r
376       SequenceGroup sg = av.selectionGroup;\r
377       //Find the top and bottom of this group\r
378       int min = av.alignment.getHeight(), max = 0;\r
379       for (int i = 0; i < sg.getSize(); i++)\r
380       {\r
381         int index = av.alignment.findIndex(sg.getSequenceAt(i));\r
382         if (index > max)\r
383         {\r
384           max = index;\r
385         }\r
386         if (index < min)\r
387         {\r
388           min = index;\r
389         }\r
390       }\r
391 \r
392       max++;\r
393 \r
394       if (topLeft)\r
395       {\r
396         sg.setStartRes(seqCanvas.cursorX);\r
397         if (sg.getEndRes() < seqCanvas.cursorX)\r
398         {\r
399           sg.setEndRes(seqCanvas.cursorX);\r
400         }\r
401 \r
402         min = seqCanvas.cursorY;\r
403       }\r
404       else\r
405       {\r
406         sg.setEndRes(seqCanvas.cursorX);\r
407         if (sg.getStartRes() > seqCanvas.cursorX)\r
408         {\r
409           sg.setStartRes(seqCanvas.cursorX);\r
410         }\r
411 \r
412         max = seqCanvas.cursorY + 1;\r
413       }\r
414 \r
415       if (min > max)\r
416       {\r
417         // Only the user can do this\r
418         av.setSelectionGroup(null);\r
419       }\r
420       else\r
421       {\r
422         // Now add any sequences between min and max\r
423         sg.getSequences(null).clear();\r
424         for (int i = min; i < max; i++)\r
425         {\r
426           sg.addSequence(av.alignment.getSequenceAt(i), false);\r
427         }\r
428       }\r
429     }\r
430 \r
431     if (av.getSelectionGroup() == null)\r
432     {\r
433       SequenceGroup sg = new SequenceGroup();\r
434       sg.setStartRes(seqCanvas.cursorX);\r
435       sg.setEndRes(seqCanvas.cursorX);\r
436       sg.addSequence(sequence, false);\r
437       av.setSelectionGroup(sg);\r
438     }\r
439 \r
440     ap.paintAlignment(false);\r
441   }\r
442 \r
443   void insertGapAtCursor(boolean group)\r
444   {\r
445     groupEditing = group;\r
446     startseq = seqCanvas.cursorY;\r
447     lastres = seqCanvas.cursorX;\r
448     editSequence(true, seqCanvas.cursorX + getKeyboardNo1());\r
449     endEditing();\r
450   }\r
451 \r
452   void deleteGapAtCursor(boolean group)\r
453   {\r
454     groupEditing = group;\r
455     startseq = seqCanvas.cursorY;\r
456     lastres = seqCanvas.cursorX + getKeyboardNo1();\r
457     editSequence(false, seqCanvas.cursorX);\r
458     endEditing();\r
459   }\r
460 \r
461   void numberPressed(char value)\r
462   {\r
463     if (keyboardNo1 == null)\r
464     {\r
465       keyboardNo1 = new StringBuffer();\r
466     }\r
467 \r
468     if (keyboardNo2 != null)\r
469     {\r
470       keyboardNo2.append(value);\r
471     }\r
472     else\r
473     {\r
474       keyboardNo1.append(value);\r
475     }\r
476   }\r
477 \r
478   int getKeyboardNo1()\r
479   {\r
480     if (keyboardNo1 == null)\r
481       return 1;\r
482     else\r
483     {\r
484       int value = Integer.parseInt(keyboardNo1.toString());\r
485       keyboardNo1 = null;\r
486       return value;\r
487     }\r
488   }\r
489 \r
490   int getKeyboardNo2()\r
491   {\r
492     if (keyboardNo2 == null)\r
493       return 1;\r
494     else\r
495     {\r
496       int value = Integer.parseInt(keyboardNo2.toString());\r
497       keyboardNo2 = null;\r
498       return value;\r
499     }\r
500   }\r
501 \r
502   /**\r
503    * DOCUMENT ME!\r
504    *\r
505    * @param evt DOCUMENT ME!\r
506    */\r
507   public void mouseReleased(MouseEvent evt)\r
508   {\r
509     mouseDragging = false;\r
510     mouseWheelPressed = false;\r
511 \r
512     if (!editingSeqs)\r
513     {\r
514       doMouseReleasedDefineMode(evt);\r
515       return;\r
516     }\r
517 \r
518     endEditing();\r
519   }\r
520 \r
521   /**\r
522    * DOCUMENT ME!\r
523    *\r
524    * @param evt DOCUMENT ME!\r
525    */\r
526   public void mousePressed(MouseEvent evt)\r
527   {\r
528     lastMousePress = evt.getPoint();\r
529 \r
530     if (javax.swing.SwingUtilities.isMiddleMouseButton(evt))\r
531     {\r
532       mouseWheelPressed = true;\r
533       return;\r
534     }\r
535 \r
536     if (evt.isShiftDown() || evt.isAltDown() ||\r
537         evt.isControlDown())\r
538     {\r
539       if (evt.isAltDown() || evt.isControlDown())\r
540       {\r
541         groupEditing = true;\r
542       }\r
543       editingSeqs = true;\r
544     }\r
545     else\r
546     {\r
547       doMousePressedDefineMode(evt);\r
548       return;\r
549     }\r
550 \r
551     int seq = findSeq(evt);\r
552     int res = findRes(evt);\r
553 \r
554     if (seq < 0 || res < 0)\r
555     {\r
556       return;\r
557     }\r
558 \r
559     if ( (seq < av.getAlignment().getHeight()) &&\r
560         (res < av.getAlignment().getSequenceAt(seq).getLength()))\r
561     {\r
562       startseq = seq;\r
563       lastres = res;\r
564     }\r
565     else\r
566     {\r
567       startseq = -1;\r
568       lastres = -1;\r
569     }\r
570 \r
571     return;\r
572   }\r
573 \r
574   String lastMessage;\r
575   public void mouseOverSequence(SequenceI sequence, int index)\r
576   {\r
577     String tmp = sequence.hashCode()+index+"";\r
578     if (lastMessage == null || !lastMessage.equals(tmp))\r
579       ssm.mouseOverSequence(sequence, index);\r
580 \r
581     lastMessage = tmp;\r
582   }\r
583 \r
584 \r
585   public void highlightSequence(jalview.datamodel.SequenceI seq, int index)\r
586   {\r
587     if(av.alignment.findIndex(seq)>-1)\r
588     {\r
589       SearchResults highlight = new SearchResults();\r
590       highlight.addResult(seq,index,index);\r
591       seqCanvas.highlightSearchResults(highlight);\r
592     }\r
593   }\r
594 \r
595   public void updateColours(SequenceI seq, int index)\r
596   {\r
597     System.out.println("update the seqPanel colours");\r
598     //repaint();\r
599   }\r
600 \r
601   /**\r
602    * DOCUMENT ME!\r
603    *\r
604    * @param evt DOCUMENT ME!\r
605    */\r
606   public void mouseMoved(MouseEvent evt)\r
607   {\r
608     if (editingSeqs)\r
609     {\r
610       // This is because MacOSX creates a mouseMoved\r
611       // If control is down, other platforms will not.\r
612       mouseDragged(evt);\r
613     }\r
614 \r
615     int res = findRes(evt);\r
616     int seq = findSeq(evt);\r
617 \r
618     if (res < 0 || seq < 0 || seq >= av.getAlignment().getHeight())\r
619     {\r
620       return;\r
621     }\r
622 \r
623     SequenceI sequence = av.getAlignment().getSequenceAt(seq);\r
624 \r
625     if (res > sequence.getLength())\r
626     {\r
627       return;\r
628     }\r
629 \r
630     if (ssm != null)\r
631       mouseOverSequence(sequence, sequence.findPosition(res));\r
632 \r
633     setStatusMessage(sequence, res, seq);\r
634 \r
635     tooltipText.setLength(6); // Cuts the buffer back to <html>\r
636 \r
637     SequenceGroup[] groups = av.alignment.findAllGroups(sequence);\r
638     if (groups != null)\r
639     {\r
640       for (int g = 0; g < groups.length; g++)\r
641       {\r
642         if (groups[g].getStartRes() <= res && groups[g].getEndRes() >= res)\r
643         {\r
644           if (tooltipText.length() > 6)\r
645           {\r
646             tooltipText.append("<br>");\r
647           }\r
648 \r
649           if (!groups[g].getName().startsWith("JTreeGroup") &&\r
650               !groups[g].getName().startsWith("JGroup"))\r
651           {\r
652             tooltipText.append(groups[g].getName());\r
653           }\r
654 \r
655           if (groups[g].getDescription() != null)\r
656           {\r
657             tooltipText.append(": " + groups[g].getDescription());\r
658           }\r
659         }\r
660       }\r
661     }\r
662 \r
663     // use aa to see if the mouse pointer is on a\r
664     if (av.showSequenceFeatures)\r
665     {\r
666       SequenceFeature[] features = findFeaturesAtRes(\r
667           sequence.getDatasetSequence(), sequence.findPosition(res));\r
668 \r
669       if (features != null)\r
670       {\r
671         for (int i = 0; i < features.length; i++)\r
672         {\r
673           if (features[i].getType().equals("disulfide bond"))\r
674           {\r
675             if (features[i].getBegin() == sequence.findPosition(res)\r
676                 || features[i].getEnd() == sequence.findPosition(res))\r
677             {\r
678               if (tooltipText.length() > 6)\r
679               {\r
680                 tooltipText.append("<br>");\r
681               }\r
682               tooltipText.append("disulfide bond " + features[i].getBegin() +\r
683                                  ":" +\r
684                                  features[i].getEnd());\r
685               if (features[i].links != null)\r
686               {\r
687                 tooltipText.append(" <img src=\"" + linkImageURL + "\">");\r
688               }\r
689             }\r
690           }\r
691           else\r
692           {\r
693             if (tooltipText.length() > 6)\r
694             {\r
695               tooltipText.append("<br>");\r
696             }\r
697 \r
698             tooltipText.append(features[i].getType() + " " +\r
699                                features[i].begin);\r
700             if (features[i].begin != features[i].end)\r
701             {\r
702               tooltipText.append(" " + features[i].end);\r
703             }\r
704 \r
705             if (features[i].getDescription() != null\r
706                 && !features[i].description.equals(features[i].getType()))\r
707             {\r
708               tmpString = features[i].getDescription();\r
709               int startTag = tmpString.toUpperCase().indexOf("<HTML>");\r
710               if (startTag > -1)\r
711               {\r
712                 tmpString = tmpString.substring(startTag + 6);\r
713               }\r
714               int endTag = tmpString.toUpperCase().indexOf("</BODY>");\r
715               if (endTag > -1)\r
716               {\r
717                 tmpString = tmpString.substring(0, endTag);\r
718               }\r
719               endTag = tmpString.toUpperCase().indexOf("</HTML>");\r
720               if (endTag > -1)\r
721               {\r
722                 tmpString = tmpString.substring(0, endTag);\r
723               }\r
724 \r
725               if (startTag > -1)\r
726               {\r
727                 tooltipText.append("; " + tmpString);\r
728               }\r
729               else\r
730               {\r
731                 if (tmpString.indexOf("<") > -1 || tmpString.indexOf(">") > -1)\r
732                 {\r
733                   //The description does not specify html is to\r
734                   //be used, so we must remove < > symbols\r
735                   tmpString = tmpString.replaceAll("<", "&lt;");\r
736                   tmpString = tmpString.replaceAll(">", "&gt;");\r
737 \r
738                   tooltipText.append("; ");\r
739                   tooltipText.append(tmpString);\r
740 \r
741                 }\r
742                 else\r
743                 {\r
744                   tooltipText.append("; " + tmpString);\r
745                 }\r
746               }\r
747             }\r
748             if (features[i].getValue("status") != null)\r
749             {\r
750               String status = features[i].getValue("status").toString();\r
751               if (status.length() > 0)\r
752               {\r
753                 tooltipText.append("; (" + features[i].getValue("status") + ")");\r
754               }\r
755             }\r
756 \r
757             if (features[i].links != null)\r
758             {\r
759               tooltipText.append(" <img src=\"" + linkImageURL + "\">");\r
760             }\r
761 \r
762           }\r
763         }\r
764       }\r
765     }\r
766 \r
767     if (tooltipText.length() == 6) // <html></html>\r
768     {\r
769       setToolTipText(null);\r
770     }\r
771     else\r
772     {\r
773       tooltipText.append("</html>");\r
774       setToolTipText(tooltipText.toString());\r
775     }\r
776     // else\r
777     //  setToolTipText(null);\r
778   }\r
779 \r
780   void setStatusMessage(SequenceI sequence, int res, int seq)\r
781   {\r
782     StringBuffer text = new StringBuffer("Sequence " + (seq + 1) + " ID: " +\r
783                                          sequence.getName());\r
784 \r
785     Object obj = null;\r
786     if (av.alignment.isNucleotide())\r
787     {\r
788       obj = ResidueProperties.nucleotideName.get(sequence.getCharAt(res) +\r
789                                                  "");\r
790       if (obj != null)\r
791       {\r
792         text.append(" Nucleotide: ");\r
793       }\r
794     }\r
795     else\r
796     {\r
797       obj = ResidueProperties.aa2Triplet.get(sequence.getCharAt(res) + "");\r
798       if (obj != null)\r
799       {\r
800         text.append("  Residue: ");\r
801       }\r
802     }\r
803 \r
804     if (obj != null)\r
805     {\r
806 \r
807       if (obj != "")\r
808       {\r
809         text.append(obj + " (" + sequence.findPosition(res) +\r
810                     ")");\r
811       }\r
812     }\r
813     ap.alignFrame.statusBar.setText(text.toString());\r
814 \r
815   }\r
816 \r
817   /**\r
818    * DOCUMENT ME!\r
819    *\r
820    * @param evt DOCUMENT ME!\r
821    */\r
822   public void mouseDragged(MouseEvent evt)\r
823   {\r
824     if (mouseWheelPressed)\r
825     {\r
826       int oldWidth = av.charWidth;\r
827 \r
828       //Which is bigger, left-right or up-down?\r
829       if (Math.abs(evt.getY() - lastMousePress.getY())\r
830           > Math.abs(evt.getX() - lastMousePress.getX()))\r
831       {\r
832         int fontSize = av.font.getSize();\r
833 \r
834         if (evt.getY() < lastMousePress.getY())\r
835         {\r
836           fontSize--;\r
837         }\r
838         else if (evt.getY() > lastMousePress.getY())\r
839         {\r
840           fontSize++;\r
841         }\r
842 \r
843         if (fontSize < 1)\r
844         {\r
845           fontSize = 1;\r
846         }\r
847 \r
848         av.setFont(new Font(av.font.getName(), av.font.getStyle(), fontSize));\r
849         av.charWidth = oldWidth;\r
850         ap.fontChanged();\r
851       }\r
852       else\r
853       {\r
854         if (evt.getX() < lastMousePress.getX() && av.charWidth > 1)\r
855         {\r
856           av.charWidth--;\r
857         }\r
858         else if (evt.getX() > lastMousePress.getX())\r
859         {\r
860           av.charWidth++;\r
861         }\r
862 \r
863         ap.paintAlignment(false);\r
864       }\r
865 \r
866       FontMetrics fm = getFontMetrics(av.getFont());\r
867       av.validCharWidth = fm.charWidth('M') <= av.charWidth;\r
868 \r
869       lastMousePress = evt.getPoint();\r
870 \r
871       return;\r
872     }\r
873 \r
874     if (!editingSeqs)\r
875     {\r
876       doMouseDraggedDefineMode(evt);\r
877       return;\r
878     }\r
879 \r
880     int res = findRes(evt);\r
881 \r
882     if (res < 0)\r
883     {\r
884       res = 0;\r
885     }\r
886 \r
887     if ( (lastres == -1) || (lastres == res))\r
888     {\r
889       return;\r
890     }\r
891 \r
892     if ( (res < av.getAlignment().getWidth()) && (res < lastres))\r
893     {\r
894       // dragLeft, delete gap\r
895       editSequence(false, res);\r
896     }\r
897     else\r
898     {\r
899       editSequence(true, res);\r
900     }\r
901 \r
902     mouseDragging = true;\r
903     if (scrollThread != null)\r
904     {\r
905       scrollThread.setEvent(evt);\r
906     }\r
907   }\r
908 \r
909 \r
910 \r
911   synchronized void editSequence(boolean insertGap, int startres)\r
912   {\r
913     int fixedLeft = -1;\r
914     int fixedRight = -1;\r
915     boolean fixedColumns = false;\r
916     SequenceGroup sg = av.getSelectionGroup();\r
917 \r
918     SequenceI seq = av.alignment.getSequenceAt(startseq);\r
919 \r
920     //No group, but the sequence may represent a group\r
921     if (!groupEditing && av.hasHiddenRows)\r
922     {\r
923       if (av.hiddenRepSequences != null\r
924           && av.hiddenRepSequences.containsKey(seq))\r
925       {\r
926         sg = (SequenceGroup) av.hiddenRepSequences.get(seq);\r
927         groupEditing = true;\r
928       }\r
929     }\r
930 \r
931     StringBuffer message = new StringBuffer();\r
932     if (groupEditing)\r
933     {\r
934       message.append("Edit group:");\r
935       if (editCommand == null)\r
936       {\r
937         editCommand = new EditCommand("Edit Group");\r
938       }\r
939     }\r
940     else\r
941     {\r
942       message.append("Edit sequence: " + seq.getName());\r
943       String label = seq.getName();\r
944       if (label.length() > 10)\r
945       {\r
946         label = label.substring(0, 10);\r
947       }\r
948       if (editCommand == null)\r
949       {\r
950         editCommand = new EditCommand("Edit " + label);\r
951       }\r
952     }\r
953 \r
954     if (insertGap)\r
955     {\r
956       message.append(" insert ");\r
957     }\r
958     else\r
959     {\r
960       message.append(" delete ");\r
961     }\r
962 \r
963     message.append(Math.abs(startres - lastres) + " gaps.");\r
964     ap.alignFrame.statusBar.setText(message.toString());\r
965 \r
966     //Are we editing within a selection group?\r
967     if (groupEditing\r
968         || (sg != null && sg.getSequences(av.hiddenRepSequences).contains(seq)))\r
969     {\r
970       fixedColumns = true;\r
971 \r
972       //sg might be null as the user may only see 1 sequence,\r
973       //but the sequence represents a group\r
974       if (sg == null)\r
975       {\r
976         if (av.hiddenRepSequences == null\r
977             || !av.hiddenRepSequences.containsKey(seq))\r
978         {\r
979           endEditing();\r
980           return;\r
981         }\r
982         sg = (SequenceGroup) av.hiddenRepSequences.get(seq);\r
983       }\r
984 \r
985       fixedLeft = sg.getStartRes();\r
986       fixedRight = sg.getEndRes();\r
987 \r
988       if ( (startres < fixedLeft && lastres >= fixedLeft)\r
989           || (startres >= fixedLeft && lastres < fixedLeft)\r
990           || (startres > fixedRight && lastres <= fixedRight)\r
991           || (startres <= fixedRight && lastres > fixedRight))\r
992       {\r
993         endEditing();\r
994         return;\r
995       }\r
996 \r
997       if (fixedLeft > startres)\r
998       {\r
999         fixedRight = fixedLeft - 1;\r
1000         fixedLeft = 0;\r
1001       }\r
1002       else if (fixedRight < startres)\r
1003       {\r
1004         fixedLeft = fixedRight;\r
1005         fixedRight = -1;\r
1006       }\r
1007     }\r
1008 \r
1009     if (av.hasHiddenColumns)\r
1010     {\r
1011       fixedColumns = true;\r
1012       int y1 = av.getColumnSelection().getHiddenBoundaryLeft(startres);\r
1013       int y2 = av.getColumnSelection().getHiddenBoundaryRight(startres);\r
1014 \r
1015       if ( (insertGap && startres > y1 && lastres < y1)\r
1016           || (!insertGap && startres < y2 && lastres > y2))\r
1017       {\r
1018         endEditing();\r
1019         return;\r
1020       }\r
1021 \r
1022       //System.out.print(y1+" "+y2+" "+fixedLeft+" "+fixedRight+"~~");\r
1023       //Selection spans a hidden region\r
1024       if (fixedLeft < y1 && (fixedRight > y2 || fixedRight == -1))\r
1025       {\r
1026         if (startres >= y2)\r
1027         {\r
1028           fixedLeft = y2;\r
1029         }\r
1030         else\r
1031         {\r
1032           fixedRight = y2 - 1;\r
1033         }\r
1034       }\r
1035     }\r
1036 \r
1037     if (groupEditing)\r
1038     {\r
1039       Vector vseqs = sg.getSequences(av.hiddenRepSequences);\r
1040       int g, groupSize = vseqs.size();\r
1041       SequenceI[] groupSeqs = new SequenceI[groupSize];\r
1042       for (g = 0; g < groupSeqs.length; g++)\r
1043       {\r
1044         groupSeqs[g] = (SequenceI) vseqs.elementAt(g);\r
1045       }\r
1046 \r
1047       // drag to right\r
1048       if (insertGap)\r
1049       {\r
1050         //If the user has selected the whole sequence, and is dragging to\r
1051         // the right, we can still extend the alignment and selectionGroup\r
1052         if (sg.getStartRes() == 0\r
1053             && sg.getEndRes() == fixedRight\r
1054             && sg.getEndRes() == av.alignment.getWidth() - 1\r
1055             )\r
1056         {\r
1057           sg.setEndRes(av.alignment.getWidth() + startres - lastres);\r
1058           fixedRight = sg.getEndRes();\r
1059         }\r
1060 \r
1061         // Is it valid with fixed columns??\r
1062         // Find the next gap before the end\r
1063         // of the visible region boundary\r
1064         boolean blank = false;\r
1065         for (fixedRight = fixedRight;\r
1066              fixedRight > lastres;\r
1067              fixedRight--)\r
1068         {\r
1069           blank = true;\r
1070 \r
1071           for (g = 0; g < groupSize; g++)\r
1072           {\r
1073             for (int j = 0; j < startres - lastres; j++)\r
1074             {\r
1075               if (!jalview.util.Comparison.isGap(\r
1076                   groupSeqs[g].getCharAt(fixedRight - j)))\r
1077               {\r
1078                 blank = false;\r
1079                 break;\r
1080               }\r
1081             }\r
1082           }\r
1083           if (blank)\r
1084           {\r
1085             break;\r
1086           }\r
1087         }\r
1088 \r
1089         if (!blank)\r
1090         {\r
1091           if (sg.getSize() == av.alignment.getHeight())\r
1092           {\r
1093             if ( (av.hasHiddenColumns\r
1094                   &&\r
1095                   startres < av.getColumnSelection().getHiddenBoundaryRight(startres)))\r
1096             {\r
1097               endEditing();\r
1098               return;\r
1099             }\r
1100 \r
1101             int alWidth = av.alignment.getWidth();\r
1102             if (av.hasHiddenRows)\r
1103             {\r
1104               int hwidth = av.alignment.getHiddenSequences().getWidth();\r
1105               if (hwidth > alWidth)\r
1106               {\r
1107                 alWidth = hwidth;\r
1108               }\r
1109             }\r
1110             //We can still insert gaps if the selectionGroup\r
1111             //contains all the sequences\r
1112             sg.setEndRes(sg.getEndRes() + startres - lastres);\r
1113             fixedRight = alWidth + startres - lastres;\r
1114           }\r
1115           else\r
1116           {\r
1117             endEditing();\r
1118             return;\r
1119           }\r
1120         }\r
1121       }\r
1122 \r
1123       // drag to left\r
1124       else if (!insertGap)\r
1125       {\r
1126         /// Are we able to delete?\r
1127         // ie are all columns blank?\r
1128 \r
1129         for (g = 0; g < groupSize; g++)\r
1130         {\r
1131           for (int j = startres; j < lastres; j++)\r
1132           {\r
1133             if (groupSeqs[g].getLength() <= j)\r
1134             {\r
1135               continue;\r
1136             }\r
1137 \r
1138             if (!jalview.util.Comparison.isGap(\r
1139                 groupSeqs[g].getCharAt(j)))\r
1140             {\r
1141               // Not a gap, block edit not valid\r
1142               endEditing();\r
1143               return;\r
1144             }\r
1145           }\r
1146         }\r
1147       }\r
1148 \r
1149       if (insertGap)\r
1150       {\r
1151         // dragging to the right\r
1152         if (fixedColumns && fixedRight != -1)\r
1153         {\r
1154           for (int j = lastres; j < startres; j++)\r
1155           {\r
1156             insertChar(j, groupSeqs, fixedRight);\r
1157           }\r
1158         }\r
1159         else\r
1160         {\r
1161           editCommand.appendEdit(EditCommand.INSERT_GAP,\r
1162                                  groupSeqs,\r
1163                                  startres, startres - lastres,\r
1164                                  av.alignment,\r
1165                                  true);\r
1166         }\r
1167       }\r
1168       else\r
1169       {\r
1170         // dragging to the left\r
1171         if (fixedColumns && fixedRight != -1)\r
1172         {\r
1173           for (int j = lastres; j > startres; j--)\r
1174           {\r
1175             deleteChar(startres, groupSeqs, fixedRight);\r
1176           }\r
1177         }\r
1178         else\r
1179         {\r
1180           editCommand.appendEdit(EditCommand.DELETE_GAP,\r
1181                                  groupSeqs,\r
1182                                  startres, lastres - startres,\r
1183                                  av.alignment,\r
1184                                  true);\r
1185         }\r
1186 \r
1187       }\r
1188     }\r
1189     else /////Editing a single sequence///////////\r
1190     {\r
1191       if (insertGap)\r
1192       {\r
1193         // dragging to the right\r
1194         if (fixedColumns && fixedRight != -1)\r
1195         {\r
1196           for (int j = lastres; j < startres; j++)\r
1197           {\r
1198             insertChar(j, new SequenceI[]\r
1199                        {seq}, fixedRight);\r
1200           }\r
1201         }\r
1202         else\r
1203         {\r
1204           editCommand.appendEdit(EditCommand.INSERT_GAP,\r
1205                                  new SequenceI[]\r
1206                                  {seq},\r
1207                                  lastres, startres - lastres,\r
1208                                  av.alignment,\r
1209                                  true);\r
1210         }\r
1211       }\r
1212       else\r
1213       {\r
1214         // dragging to the left\r
1215         if (fixedColumns && fixedRight != -1)\r
1216         {\r
1217           for (int j = lastres; j > startres; j--)\r
1218           {\r
1219             if (!jalview.util.Comparison.isGap(seq.getCharAt(startres)))\r
1220             {\r
1221               endEditing();\r
1222               break;\r
1223             }\r
1224             deleteChar(startres, new SequenceI[]\r
1225                        {seq}, fixedRight);\r
1226           }\r
1227         }\r
1228         else\r
1229         {\r
1230           //could be a keyboard edit trying to delete none gaps\r
1231           int max = 0;\r
1232           for (int m = startres; m < lastres; m++)\r
1233           {\r
1234             if (!jalview.util.Comparison.isGap(seq.getCharAt(m)))\r
1235             {\r
1236               break;\r
1237             }\r
1238             max++;\r
1239           }\r
1240 \r
1241           if (max > 0)\r
1242           {\r
1243             editCommand.appendEdit(EditCommand.DELETE_GAP,\r
1244                                    new SequenceI[]\r
1245                                    {seq},\r
1246                                    startres, max,\r
1247                                    av.alignment,\r
1248                                    true);\r
1249           }\r
1250         }\r
1251       }\r
1252     }\r
1253 \r
1254     lastres = startres;\r
1255     seqCanvas.repaint();\r
1256   }\r
1257 \r
1258   void insertChar(int j, SequenceI[] seq, int fixedColumn)\r
1259   {\r
1260     int blankColumn = fixedColumn;\r
1261     for (int s = 0; s < seq.length; s++)\r
1262     {\r
1263       //Find the next gap before the end of the visible region boundary\r
1264       //If lastCol > j, theres a boundary after the gap insertion\r
1265 \r
1266       for (blankColumn = fixedColumn; blankColumn > j; blankColumn--)\r
1267       {\r
1268         if (jalview.util.Comparison.isGap(seq[s].getCharAt(blankColumn)))\r
1269         {\r
1270           //Theres a space, so break and insert the gap\r
1271           break;\r
1272         }\r
1273       }\r
1274 \r
1275       if (blankColumn <= j)\r
1276       {\r
1277         blankColumn = fixedColumn;\r
1278         endEditing();\r
1279         return;\r
1280       }\r
1281     }\r
1282 \r
1283     editCommand.appendEdit(EditCommand.DELETE_GAP,\r
1284                            seq,\r
1285                            blankColumn, 1, av.alignment, true);\r
1286 \r
1287     editCommand.appendEdit(EditCommand.INSERT_GAP,\r
1288                            seq,\r
1289                            j, 1, av.alignment,\r
1290                            true);\r
1291 \r
1292   }\r
1293 \r
1294   void deleteChar(int j, SequenceI[] seq, int fixedColumn)\r
1295   {\r
1296 \r
1297     editCommand.appendEdit(EditCommand.DELETE_GAP,\r
1298                            seq,\r
1299                            j, 1, av.alignment, true);\r
1300 \r
1301     editCommand.appendEdit(EditCommand.INSERT_GAP,\r
1302                            seq,\r
1303                            fixedColumn, 1, av.alignment, true);\r
1304   }\r
1305 \r
1306   /**\r
1307    * DOCUMENT ME!\r
1308    *\r
1309    * @param e DOCUMENT ME!\r
1310    */\r
1311   public void mouseEntered(MouseEvent e)\r
1312   {\r
1313     if (oldSeq < 0)\r
1314     {\r
1315       oldSeq = 0;\r
1316     }\r
1317 \r
1318     if (scrollThread != null)\r
1319     {\r
1320       scrollThread.running = false;\r
1321       scrollThread = null;\r
1322     }\r
1323   }\r
1324 \r
1325   /**\r
1326    * DOCUMENT ME!\r
1327    *\r
1328    * @param e DOCUMENT ME!\r
1329    */\r
1330   public void mouseExited(MouseEvent e)\r
1331   {\r
1332     if (av.getWrapAlignment())\r
1333     {\r
1334       return;\r
1335     }\r
1336 \r
1337     if (mouseDragging)\r
1338     {\r
1339       scrollThread = new ScrollThread();\r
1340     }\r
1341   }\r
1342 \r
1343   public void mouseClicked(MouseEvent evt)\r
1344   {\r
1345     SequenceI sequence = av.alignment.getSequenceAt(findSeq(evt));\r
1346     if (evt.getClickCount() > 1)\r
1347     {\r
1348       if (av.getSelectionGroup().getSize() == 1\r
1349           && av.getSelectionGroup().getEndRes()\r
1350           - av.getSelectionGroup().getStartRes() < 2)\r
1351       {\r
1352         av.setSelectionGroup(null);\r
1353       }\r
1354 \r
1355       SequenceFeature[] features = findFeaturesAtRes(\r
1356           sequence.getDatasetSequence(),\r
1357           sequence.findPosition(findRes(evt))\r
1358           );\r
1359 \r
1360       if (features != null && features.length > 0)\r
1361       {\r
1362         SearchResults highlight = new SearchResults();\r
1363         highlight.addResult(sequence,\r
1364                             features[0].getBegin(),\r
1365                             features[0].getEnd());\r
1366         seqCanvas.highlightSearchResults(highlight);\r
1367       }\r
1368       if (features != null && features.length > 0)\r
1369       {\r
1370         seqCanvas.getFeatureRenderer().amendFeatures(\r
1371             new SequenceI[]\r
1372             {sequence}, features, false, ap);\r
1373 \r
1374         seqCanvas.highlightSearchResults(null);\r
1375       }\r
1376     }\r
1377   }\r
1378 \r
1379   public void mouseWheelMoved(MouseWheelEvent e)\r
1380   {\r
1381     e.consume();\r
1382       if (e.getWheelRotation() > 0)\r
1383       {\r
1384         ap.scrollUp(false);\r
1385       }\r
1386       else\r
1387       {\r
1388         ap.scrollUp(true);\r
1389       }\r
1390   }\r
1391 \r
1392   /**\r
1393    * DOCUMENT ME!\r
1394    *\r
1395    * @param evt DOCUMENT ME!\r
1396    */\r
1397   public void doMousePressedDefineMode(MouseEvent evt)\r
1398   {\r
1399     int res = findRes(evt);\r
1400     int seq = findSeq(evt);\r
1401     oldSeq = seq;\r
1402 \r
1403     startWrapBlock = wrappedBlock;\r
1404 \r
1405     if (av.wrapAlignment && seq > av.alignment.getHeight())\r
1406     {\r
1407       JOptionPane.showInternalMessageDialog(Desktop.desktop,\r
1408                                             "Cannot edit annotations in wrapped view.",\r
1409                                             "Wrapped view - no edit",\r
1410                                             JOptionPane.WARNING_MESSAGE);\r
1411       return;\r
1412     }\r
1413 \r
1414     if (seq < 0 || res < 0)\r
1415     {\r
1416       return;\r
1417     }\r
1418 \r
1419     SequenceI sequence = (Sequence) av.getAlignment().getSequenceAt(seq);\r
1420 \r
1421     if ( (sequence == null) || (res > sequence.getLength()))\r
1422     {\r
1423       return;\r
1424     }\r
1425 \r
1426     stretchGroup = av.getSelectionGroup();\r
1427 \r
1428     if (stretchGroup == null)\r
1429     {\r
1430       stretchGroup = av.alignment.findGroup(sequence);\r
1431 \r
1432       if ( (stretchGroup != null) && (res > stretchGroup.getStartRes()) &&\r
1433           (res < stretchGroup.getEndRes()))\r
1434       {\r
1435         av.setSelectionGroup(stretchGroup);\r
1436       }\r
1437       else\r
1438       {\r
1439         stretchGroup = null;\r
1440       }\r
1441     }\r
1442     else if (!stretchGroup.getSequences(null).contains(sequence) ||\r
1443              (stretchGroup.getStartRes() > res) ||\r
1444              (stretchGroup.getEndRes() < res))\r
1445     {\r
1446       stretchGroup = null;\r
1447 \r
1448       SequenceGroup[] allGroups = av.alignment.findAllGroups(sequence);\r
1449 \r
1450       if (allGroups != null)\r
1451       {\r
1452         for (int i = 0; i < allGroups.length; i++)\r
1453         {\r
1454           if ( (allGroups[i].getStartRes() <= res) &&\r
1455               (allGroups[i].getEndRes() >= res))\r
1456           {\r
1457             stretchGroup = allGroups[i];\r
1458             break;\r
1459           }\r
1460         }\r
1461       }\r
1462 \r
1463       av.setSelectionGroup(stretchGroup);\r
1464 \r
1465     }\r
1466 \r
1467     if (javax.swing.SwingUtilities.isRightMouseButton(evt))\r
1468     {\r
1469       SequenceFeature [] allFeatures = findFeaturesAtRes(sequence.getDatasetSequence(),\r
1470                                                sequence.findPosition(res));\r
1471       Vector links = new Vector();\r
1472       for (int i = 0; i < allFeatures.length; i++)\r
1473       {\r
1474         if (allFeatures[i].links != null)\r
1475         {\r
1476           for (int j = 0; j < allFeatures[i].links.size(); j++)\r
1477           {\r
1478             links.addElement(allFeatures[i].links.elementAt(j));\r
1479           }\r
1480         }\r
1481       }\r
1482 \r
1483       jalview.gui.PopupMenu pop = new jalview.gui.PopupMenu(ap, null, links);\r
1484       pop.show(this, evt.getX(), evt.getY());\r
1485       return;\r
1486     }\r
1487 \r
1488     if (av.cursorMode)\r
1489     {\r
1490       seqCanvas.cursorX = findRes(evt);\r
1491       seqCanvas.cursorY = findSeq(evt);\r
1492       seqCanvas.repaint();\r
1493       return;\r
1494     }\r
1495 \r
1496     if (stretchGroup == null)\r
1497     {\r
1498       //Only if left mouse button do we want to change group sizes\r
1499 \r
1500       // define a new group here\r
1501       SequenceGroup sg = new SequenceGroup();\r
1502       sg.setStartRes(res);\r
1503       sg.setEndRes(res);\r
1504       sg.addSequence(sequence, false);\r
1505       av.setSelectionGroup(sg);\r
1506       stretchGroup = sg;\r
1507 \r
1508       if (av.getConservationSelected())\r
1509       {\r
1510         SliderPanel.setConservationSlider(ap,\r
1511                                           av.getGlobalColourScheme(),\r
1512                                           "Background");\r
1513       }\r
1514 \r
1515       if (av.getAbovePIDThreshold())\r
1516       {\r
1517         SliderPanel.setPIDSliderSource(ap, av.getGlobalColourScheme(),\r
1518                                        "Background");\r
1519       }\r
1520       if ( (stretchGroup != null) && (stretchGroup.getEndRes() == res))\r
1521       {\r
1522         // Edit end res position of selected group\r
1523         changeEndRes = true;\r
1524       }\r
1525       else if ( (stretchGroup != null) &&\r
1526                (stretchGroup.getStartRes() == res))\r
1527       {\r
1528         // Edit end res position of selected group\r
1529         changeStartRes = true;\r
1530       }\r
1531       stretchGroup.getWidth();\r
1532     }\r
1533 \r
1534     seqCanvas.repaint();\r
1535   }\r
1536 \r
1537   /**\r
1538    * DOCUMENT ME!\r
1539    *\r
1540    * @param evt DOCUMENT ME!\r
1541    */\r
1542   public void doMouseReleasedDefineMode(MouseEvent evt)\r
1543   {\r
1544     if (stretchGroup == null)\r
1545     {\r
1546       return;\r
1547     }\r
1548 \r
1549     if (stretchGroup.cs != null)\r
1550     {\r
1551       if (stretchGroup.cs instanceof ClustalxColourScheme)\r
1552       {\r
1553         ( (ClustalxColourScheme) stretchGroup.cs).resetClustalX(\r
1554             stretchGroup.getSequences(av.hiddenRepSequences),\r
1555             stretchGroup.getWidth());\r
1556       }\r
1557 \r
1558       if (stretchGroup.cs instanceof Blosum62ColourScheme\r
1559           || stretchGroup.cs instanceof PIDColourScheme\r
1560           || stretchGroup.cs.conservationApplied()\r
1561           || stretchGroup.cs.getThreshold() > 0)\r
1562       {\r
1563         stretchGroup.recalcConservation();\r
1564       }\r
1565 \r
1566       if (stretchGroup.cs.conservationApplied())\r
1567       {\r
1568         SliderPanel.setConservationSlider(ap, stretchGroup.cs,\r
1569                                           stretchGroup.getName());\r
1570       }\r
1571       else\r
1572       {\r
1573         SliderPanel.setPIDSliderSource(ap, stretchGroup.cs,\r
1574                                        stretchGroup.getName());\r
1575       }\r
1576       PaintRefresher.Refresh(this, av.getSequenceSetId());\r
1577       ap.paintAlignment(true);\r
1578     }\r
1579 \r
1580     changeEndRes = false;\r
1581     changeStartRes = false;\r
1582     stretchGroup = null;\r
1583 \r
1584   }\r
1585 \r
1586   /**\r
1587    * DOCUMENT ME!\r
1588    *\r
1589    * @param evt DOCUMENT ME!\r
1590    */\r
1591   public void doMouseDraggedDefineMode(MouseEvent evt)\r
1592   {\r
1593     int res = findRes(evt);\r
1594     int y = findSeq(evt);\r
1595 \r
1596     if (wrappedBlock != startWrapBlock)\r
1597     {\r
1598       return;\r
1599     }\r
1600 \r
1601     if (stretchGroup == null)\r
1602     {\r
1603       return;\r
1604     }\r
1605 \r
1606     if (res >= av.alignment.getWidth())\r
1607     {\r
1608       res = av.alignment.getWidth() - 1;\r
1609     }\r
1610 \r
1611     if (stretchGroup.getEndRes() == res)\r
1612     {\r
1613       // Edit end res position of selected group\r
1614       changeEndRes = true;\r
1615     }\r
1616     else if (stretchGroup.getStartRes() == res)\r
1617     {\r
1618       // Edit start res position of selected group\r
1619       changeStartRes = true;\r
1620     }\r
1621 \r
1622     if (res < av.getStartRes())\r
1623     {\r
1624       res = av.getStartRes();\r
1625     }\r
1626 \r
1627     if (changeEndRes)\r
1628     {\r
1629       if (res > (stretchGroup.getStartRes() - 1))\r
1630       {\r
1631         stretchGroup.setEndRes(res);\r
1632       }\r
1633     }\r
1634     else if (changeStartRes)\r
1635     {\r
1636       if (res < (stretchGroup.getEndRes() + 1))\r
1637       {\r
1638         stretchGroup.setStartRes(res);\r
1639       }\r
1640     }\r
1641 \r
1642     int dragDirection = 0;\r
1643 \r
1644     if (y > oldSeq)\r
1645     {\r
1646       dragDirection = 1;\r
1647     }\r
1648     else if (y < oldSeq)\r
1649     {\r
1650       dragDirection = -1;\r
1651     }\r
1652 \r
1653     while ( (y != oldSeq) && (oldSeq > -1) && (y < av.alignment.getHeight()))\r
1654     {\r
1655       // This routine ensures we don't skip any sequences, as the\r
1656       // selection is quite slow.\r
1657       Sequence seq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1658 \r
1659       oldSeq += dragDirection;\r
1660 \r
1661       if (oldSeq < 0)\r
1662       {\r
1663         break;\r
1664       }\r
1665 \r
1666       Sequence nextSeq = (Sequence) av.getAlignment().getSequenceAt(oldSeq);\r
1667 \r
1668       if (stretchGroup.getSequences(null).contains(nextSeq))\r
1669       {\r
1670         stretchGroup.deleteSequence(seq, false);\r
1671       }\r
1672       else\r
1673       {\r
1674         if (seq != null)\r
1675         {\r
1676           stretchGroup.addSequence(seq, false);\r
1677         }\r
1678 \r
1679         stretchGroup.addSequence(nextSeq, false);\r
1680       }\r
1681     }\r
1682 \r
1683     if (oldSeq < 0)\r
1684     {\r
1685       oldSeq = -1;\r
1686     }\r
1687 \r
1688     mouseDragging = true;\r
1689 \r
1690     if (scrollThread != null)\r
1691     {\r
1692       scrollThread.setEvent(evt);\r
1693     }\r
1694 \r
1695     seqCanvas.repaint();\r
1696   }\r
1697 \r
1698   void scrollCanvas(MouseEvent evt)\r
1699   {\r
1700     if (evt == null)\r
1701     {\r
1702       if (scrollThread != null)\r
1703       {\r
1704         scrollThread.running = false;\r
1705         scrollThread = null;\r
1706       }\r
1707       mouseDragging = false;\r
1708     }\r
1709     else\r
1710     {\r
1711       if (scrollThread == null)\r
1712       {\r
1713         scrollThread = new ScrollThread();\r
1714       }\r
1715 \r
1716       mouseDragging = true;\r
1717       scrollThread.setEvent(evt);\r
1718     }\r
1719 \r
1720   }\r
1721 \r
1722   // this class allows scrolling off the bottom of the visible alignment\r
1723   class ScrollThread\r
1724       extends Thread\r
1725   {\r
1726     MouseEvent evt;\r
1727     boolean running = false;\r
1728 \r
1729     public ScrollThread()\r
1730     {\r
1731       start();\r
1732     }\r
1733 \r
1734     public void setEvent(MouseEvent e)\r
1735     {\r
1736       evt = e;\r
1737     }\r
1738 \r
1739     public void stopScrolling()\r
1740     {\r
1741       running = false;\r
1742     }\r
1743 \r
1744     public void run()\r
1745     {\r
1746       running = true;\r
1747 \r
1748       while (running)\r
1749       {\r
1750         if (evt != null)\r
1751         {\r
1752           if (mouseDragging && (evt.getY() < 0) &&\r
1753               (av.getStartSeq() > 0))\r
1754           {\r
1755             running = ap.scrollUp(true);\r
1756           }\r
1757 \r
1758           if (mouseDragging && (evt.getY() >= getHeight()) &&\r
1759               (av.alignment.getHeight() > av.getEndSeq()))\r
1760           {\r
1761             running = ap.scrollUp(false);\r
1762           }\r
1763 \r
1764           if (mouseDragging && (evt.getX() < 0))\r
1765           {\r
1766             running = ap.scrollRight(false);\r
1767           }\r
1768           else if (mouseDragging && (evt.getX() >= getWidth()))\r
1769           {\r
1770             running = ap.scrollRight(true);\r
1771           }\r
1772         }\r
1773 \r
1774         try\r
1775         {\r
1776           Thread.sleep(20);\r
1777         }\r
1778         catch (Exception ex)\r
1779         {\r
1780         }\r
1781       }\r
1782     }\r
1783   }\r
1784 }\r