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