36c50285690955068bb538db7dde8a6987348412
[jalviewjs.git] / unused / appletgui / AlignmentPanel.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)\r
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors\r
4  * \r
5  * This file is part of Jalview.\r
6  * \r
7  * Jalview is free software: you can redistribute it and/or\r
8  * modify it under the terms of the GNU General Public License \r
9  * as published by the Free Software Foundation, either version 3\r
10  * of the License, or (at your option) any later version.\r
11  *  \r
12  * Jalview is distributed in the hope that it will be useful, but \r
13  * WITHOUT ANY WARRANTY; without even the implied warranty \r
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
15  * PURPOSE.  See the GNU General Public License for more details.\r
16  * \r
17  * You should have received a copy of the GNU General Public License\r
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
19  * The Jalview Authors are detailed in the 'AUTHORS' file.\r
20  */\r
21 package jalview.appletgui;\r
22 \r
23 import jalview.analysis.AnnotationSorter;\r
24 import jalview.api.AlignViewportI;\r
25 import jalview.api.AlignmentViewPanel;\r
26 import jalview.bin.JalviewLite;\r
27 import jalview.datamodel.AlignmentI;\r
28 import jalview.datamodel.SearchResults;\r
29 import jalview.datamodel.SequenceI;\r
30 import jalview.structure.StructureSelectionManager;\r
31 \r
32 import java.awt.BorderLayout;\r
33 import java.awt.Color;\r
34 import java.awt.Dimension;\r
35 import java.awt.FontMetrics;\r
36 import java.awt.Graphics;\r
37 import java.awt.event.AdjustmentEvent;\r
38 import java.awt.event.AdjustmentListener;\r
39 import java.awt.event.ComponentAdapter;\r
40 import java.awt.event.ComponentEvent;\r
41 import java.util.List;\r
42 \r
43 import javax.swing.JPanel;\r
44 import javax.swing.JFrame;\r
45 import javax.swing.JScrollBar;\r
46 \r
47 public class AlignmentPanel extends JPanel implements AdjustmentListener,\r
48         AlignmentViewPanel\r
49 {\r
50 \r
51   public AlignViewport av;\r
52 \r
53   OverviewPanel overviewPanel;\r
54 \r
55   SeqPanel seqPanel;\r
56 \r
57   IdPanel idPanel;\r
58 \r
59   IdwidthAdjuster idwidthAdjuster;\r
60 \r
61   public AlignFrame alignFrame;\r
62 \r
63   ScalePanel scalePanel;\r
64 \r
65   AnnotationPanel annotationPanel;\r
66 \r
67   AnnotationLabels alabels;\r
68 \r
69   // this value is set false when selection area being dragged\r
70   boolean fastPaint = true;\r
71 \r
72   public void finalize()\r
73   {\r
74     alignFrame = null;\r
75     av = null;\r
76     seqPanel = null;\r
77     seqPanelHolder = null;\r
78     sequenceHolderPanel = null;\r
79     scalePanel = null;\r
80     scalePanelHolder = null;\r
81     annotationPanel = null;\r
82     annotationPanelHolder = null;\r
83     annotationSpaceFillerHolder = null;\r
84   }\r
85 \r
86   public AlignmentPanel(AlignFrame af, final AlignViewport av)\r
87   {\r
88     try\r
89     {\r
90       jbInit();\r
91     } catch (Exception e)\r
92     {\r
93       e.printStackTrace();\r
94     }\r
95 \r
96     alignFrame = af;\r
97     this.av = av;\r
98     seqPanel = new SeqPanel(av, this);\r
99     idPanel = new IdPanel(av, this);\r
100     scalePanel = new ScalePanel(av, this);\r
101     idwidthAdjuster = new IdwidthAdjuster(this);\r
102     annotationPanel = new AnnotationPanel(this);\r
103     annotationPanelHolder.add(annotationPanel, BorderLayout.CENTER);\r
104 \r
105     sequenceHolderPanel.add(annotationPanelHolder, BorderLayout.SOUTH);\r
106     alabels = new AnnotationLabels(this);\r
107 \r
108     setAnnotationVisible(av.isShowAnnotation());\r
109 \r
110     idPanelHolder.add(idPanel, BorderLayout.CENTER);\r
111     idSpaceFillerPanel1.add(idwidthAdjuster, BorderLayout.CENTER);\r
112     annotationSpaceFillerHolder.add(alabels, BorderLayout.CENTER);\r
113     scalePanelHolder.add(scalePanel, BorderLayout.CENTER);\r
114     seqPanelHolder.add(seqPanel, BorderLayout.CENTER);\r
115 \r
116     fontChanged();\r
117     setScrollValues(0, 0);\r
118 \r
119     apvscroll.addAdjustmentListener(this);\r
120     hscroll.addAdjustmentListener(this);\r
121     vscroll.addAdjustmentListener(this);\r
122 \r
123     addComponentListener(new ComponentAdapter()\r
124     {\r
125       public void componentResized(ComponentEvent evt)\r
126       {\r
127         setScrollValues(av.getStartRes(), av.getStartSeq());\r
128         if (getSize().height > 0\r
129                 && annotationPanelHolder.getSize().height > 0)\r
130         {\r
131           validateAnnotationDimensions(false);\r
132         }\r
133         repaint();\r
134       }\r
135 \r
136     });\r
137 \r
138     Dimension d = calculateIdWidth();\r
139     idPanel.idCanvas.setSize(d);\r
140 \r
141     hscrollFillerPanel.setSize(d.width, annotationPanel.getSize().height);\r
142 \r
143     idPanel.idCanvas.setSize(d.width, seqPanel.seqCanvas.getSize().height);\r
144     annotationSpaceFillerHolder.setSize(d.width,\r
145             annotationPanel.getSize().height);\r
146     alabels.setSize(d.width, annotationPanel.getSize().height);\r
147     final AlignmentPanel ap = this;\r
148     av.addPropertyChangeListener(new java.beans.PropertyChangeListener()\r
149     {\r
150       public void propertyChange(java.beans.PropertyChangeEvent evt)\r
151       {\r
152         if (evt.getPropertyName().equals("alignment"))\r
153         {\r
154           PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);\r
155           alignmentChanged();\r
156         }\r
157       }\r
158     });\r
159   }\r
160 \r
161   @Override\r
162   public AlignViewportI getAlignViewport()\r
163   {\r
164     return av;\r
165   }\r
166   public SequenceRenderer getSequenceRenderer()\r
167   {\r
168     return seqPanel.seqCanvas.sr;\r
169   }\r
170   @Override\r
171   public jalview.api.FeatureRenderer getFeatureRenderer()\r
172   {\r
173     return seqPanel.seqCanvas.fr;\r
174   }\r
175   @Override\r
176   public jalview.api.FeatureRenderer cloneFeatureRenderer()\r
177   {\r
178     FeatureRenderer nfr = new FeatureRenderer(av);\r
179     nfr.transferSettings(seqPanel.seqCanvas.fr);\r
180     return nfr;\r
181   }\r
182   public void alignmentChanged()\r
183   {\r
184     av.alignmentChanged(this);\r
185 \r
186     if (overviewPanel != null)\r
187     {\r
188       overviewPanel.updateOverviewImage();\r
189     }\r
190 \r
191     alignFrame.updateEditMenuBar();\r
192 \r
193     repaint();\r
194   }\r
195 \r
196   public void fontChanged()\r
197   {\r
198     // set idCanvas bufferedImage to null\r
199     // to prevent drawing old image\r
200     idPanel.idCanvas.image = null;\r
201     FontMetrics fm = getFontMetrics(av.getFont());\r
202 \r
203     scalePanel.setSize(new Dimension(10, av.getCharHeight()\r
204             + fm.getDescent()));\r
205     idwidthAdjuster.setSize(new Dimension(10, av.getCharHeight()\r
206             + fm.getDescent()));\r
207     av.updateSequenceIdColours();\r
208     annotationPanel.image = null;\r
209     int ap = annotationPanel.adjustPanelHeight(false);\r
210     Dimension d = calculateIdWidth();\r
211     d.setSize(d.width + 4, seqPanel.seqCanvas.getSize().height);\r
212     alabels.setSize(d.width + 4, ap);\r
213 \r
214     idPanel.idCanvas.setSize(d);\r
215     hscrollFillerPanel.setSize(d);\r
216 \r
217     validateAnnotationDimensions(false);\r
218     annotationPanel.repaint();\r
219     validate();\r
220     repaint();\r
221 \r
222     if (overviewPanel != null)\r
223     {\r
224       overviewPanel.updateOverviewImage();\r
225     }\r
226   }\r
227 \r
228   public void setIdWidth(int w, int h)\r
229   {\r
230     idPanel.idCanvas.setSize(w, h);\r
231     idPanelHolder.setSize(w, idPanelHolder.getSize().height);\r
232     annotationSpaceFillerHolder.setSize(w,\r
233             annotationSpaceFillerHolder.getSize().height);\r
234     alabels.setSize(w, alabels.getSize().height);\r
235     validate();\r
236   }\r
237 \r
238   Dimension calculateIdWidth()\r
239   {\r
240     if (av.nullFrame == null)\r
241     {\r
242       av.nullFrame = new JFrame();\r
243       av.nullFrame.addNotify();\r
244     }\r
245 \r
246     Graphics g = av.nullFrame.getGraphics();\r
247 \r
248     FontMetrics fm = g.getFontMetrics(av.font);\r
249     AlignmentI al = av.getAlignment();\r
250 \r
251     int i = 0;\r
252     int idWidth = 0;\r
253     String id;\r
254     while (i < al.getHeight() && al.getSequenceAt(i) != null)\r
255     {\r
256       SequenceI s = al.getSequenceAt(i);\r
257       id = s.getDisplayId(av.getShowJVSuffix());\r
258 \r
259       if (fm.stringWidth(id) > idWidth)\r
260       {\r
261         idWidth = fm.stringWidth(id);\r
262       }\r
263       i++;\r
264     }\r
265 \r
266     // Also check annotation label widths\r
267     i = 0;\r
268     if (al.getAlignmentAnnotation() != null)\r
269     {\r
270       fm = g.getFontMetrics(av.nullFrame.getFont());\r
271       while (i < al.getAlignmentAnnotation().length)\r
272       {\r
273         String label = al.getAlignmentAnnotation()[i].label;\r
274         if (fm.stringWidth(label) > idWidth)\r
275         {\r
276           idWidth = fm.stringWidth(label);\r
277         }\r
278         i++;\r
279       }\r
280     }\r
281 \r
282     return new Dimension(idWidth, idPanel.idCanvas.getSize().height);\r
283   }\r
284 \r
285   /**\r
286    * Highlight the given results on the alignment.\r
287    * \r
288    */\r
289   public void highlightSearchResults(SearchResults results)\r
290   {\r
291     scrollToPosition(results);\r
292     seqPanel.seqCanvas.highlightSearchResults(results);\r
293   }\r
294 \r
295   /**\r
296    * scroll the view to show the position of the highlighted region in results\r
297    * (if any) and redraw the overview\r
298    * \r
299    * @param results\r
300    * @return false if results were not found\r
301    */\r
302   public boolean scrollToPosition(SearchResults results)\r
303   {\r
304     return scrollToPosition(results, true);\r
305   }\r
306 \r
307   /**\r
308    * scroll the view to show the position of the highlighted region in results\r
309    * (if any)\r
310    * \r
311    * @param results\r
312    * @param redrawOverview\r
313    *          - when set, the overview will be recalculated (takes longer)\r
314    * @return false if results were not found\r
315    */\r
316   public boolean scrollToPosition(SearchResults results,\r
317           boolean redrawOverview)\r
318   {\r
319     return scrollToPosition(results, redrawOverview, false);\r
320   }\r
321 \r
322   /**\r
323    * scroll the view to show the position of the highlighted region in results\r
324    * (if any)\r
325    * \r
326    * @param results\r
327    * @param redrawOverview\r
328    *          - when set, the overview will be recalculated (takes longer)\r
329    * @return false if results were not found\r
330    */\r
331   public boolean scrollToPosition(SearchResults results,\r
332           boolean redrawOverview, boolean centre)\r
333   {\r
334     // do we need to scroll the panel?\r
335     if (results != null && results.getSize() > 0)\r
336     {\r
337       AlignmentI alignment = av.getAlignment();\r
338       int seqIndex = alignment.findIndex(results);\r
339       if (seqIndex == -1)\r
340       {\r
341         return false;\r
342       }\r
343       SequenceI seq = alignment.getSequenceAt(seqIndex);\r
344       int[] r = results.getResults(seq, 0, alignment.getWidth());\r
345       if (r == null)\r
346       {\r
347         if (JalviewLite.debug)\r
348         {// DEBUG\r
349           System.out\r
350                   .println("DEBUG: scroll didn't happen - results not within alignment : "\r
351                           + seq.getStart() + "," + seq.getEnd());\r
352         }\r
353         return false;\r
354       }\r
355       if (JalviewLite.debug)\r
356       {\r
357         // DEBUG\r
358         /*\r
359          * System.out.println("DEBUG: scroll: start=" + r[0] +\r
360          * " av.getStartRes()=" + av.getStartRes() + " end=" + r[1] +\r
361          * " seq.end=" + seq.getEnd() + " av.getEndRes()=" + av.getEndRes() +\r
362          * " hextent=" + hextent);\r
363          */\r
364       }\r
365       int start = r[0];\r
366       int end = r[1];\r
367 \r
368       /*\r
369        * To centre results, scroll to positions half the visible width\r
370        * left/right of the start/end positions\r
371        */\r
372       if (centre)\r
373       {\r
374         int offset = (av.getEndRes() - av.getStartRes() + 1) / 2 - 1;\r
375         start = Math.max(start - offset, 0);\r
376         end = Math.min(end + offset, seq.getEnd() - 1);\r
377       }\r
378 \r
379       if (start < 0)\r
380       {\r
381         return false;\r
382       }\r
383       if (end == seq.getEnd())\r
384       {\r
385         return false;\r
386       }\r
387       return scrollTo(start, end, seqIndex, false, redrawOverview);\r
388     }\r
389     return true;\r
390   }\r
391 \r
392   public boolean scrollTo(int ostart, int end, int seqIndex,\r
393           boolean scrollToNearest, boolean redrawOverview)\r
394   {\r
395     int startv, endv, starts, ends, width;\r
396 \r
397     int start = -1;\r
398     if (av.hasHiddenColumns())\r
399     {\r
400       start = av.getColumnSelection().findColumnPosition(ostart);\r
401       end = av.getColumnSelection().findColumnPosition(end);\r
402       if (start == end)\r
403       {\r
404         if (!scrollToNearest && !av.getColumnSelection().isVisible(ostart))\r
405         {\r
406           // don't scroll - position isn't visible\r
407           return false;\r
408         }\r
409       }\r
410     }\r
411     else\r
412     {\r
413       start = ostart;\r
414     }\r
415     if (!av.getWrapAlignment())\r
416     {\r
417       /*\r
418        * int spos=av.getStartRes(),sqpos=av.getStartSeq(); if ((startv =\r
419        * av.getStartRes()) >= start) { spos=start-1; // seqIn //\r
420        * setScrollValues(start - 1, seqIndex); } else if ((endv =\r
421        * av.getEndRes()) <= end) { // setScrollValues(spos=startv + 1 + end -\r
422        * endv, seqIndex); spos=startv + 1 + end - endv; } else if ((starts =\r
423        * av.getStartSeq()) > seqIndex) { setScrollValues(av.getStartRes(),\r
424        * seqIndex); } else if ((ends = av.getEndSeq()) <= seqIndex) {\r
425        * setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1); }\r
426        */\r
427 \r
428       // below is scrolling logic up to Jalview 2.8.2\r
429       // if ((av.getStartRes() > end)\r
430       // || (av.getEndRes() < start)\r
431       // || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex)))\r
432       // {\r
433       // if (start > av.getAlignment().getWidth() - hextent)\r
434       // {\r
435       // start = av.getAlignment().getWidth() - hextent;\r
436       // if (start < 0)\r
437       // {\r
438       // start = 0;\r
439       // }\r
440       //\r
441       // }\r
442       // if (seqIndex > av.getAlignment().getHeight() - vextent)\r
443       // {\r
444       // seqIndex = av.getAlignment().getHeight() - vextent;\r
445       // if (seqIndex < 0)\r
446       // {\r
447       // seqIndex = 0;\r
448       // }\r
449       // }\r
450       // setScrollValues(start, seqIndex);\r
451       // }\r
452       // logic copied from jalview.gui.AlignmentPanel:\r
453         if ((startv = av.getStartRes()) >= start)\r
454         {\r
455           /*\r
456            * Scroll left to make start of search results visible\r
457            */\r
458           setScrollValues(start - 1, seqIndex);\r
459         }\r
460         else if ((endv = av.getEndRes()) <= end)\r
461         {\r
462           /*\r
463            * Scroll right to make end of search results visible\r
464            */\r
465           setScrollValues(startv + 1 + end - endv, seqIndex);\r
466         }\r
467         else if ((starts = av.getStartSeq()) > seqIndex)\r
468         {\r
469           /*\r
470            * Scroll up to make start of search results visible\r
471            */\r
472           setScrollValues(av.getStartRes(), seqIndex);\r
473         }\r
474         else if ((ends = av.getEndSeq()) <= seqIndex)\r
475         {\r
476           /*\r
477            * Scroll down to make end of search results visible\r
478            */\r
479           setScrollValues(av.getStartRes(), starts + seqIndex - ends + 1);\r
480         }\r
481         /*\r
482          * Else results are already visible - no need to scroll\r
483          */\r
484     }\r
485     else\r
486     {\r
487       scrollToWrappedVisible(start);\r
488     }\r
489     if (redrawOverview && overviewPanel != null)\r
490     {\r
491       overviewPanel.setBoxPosition();\r
492     }\r
493     paintAlignment(redrawOverview);\r
494     return true;\r
495   }\r
496 \r
497   void scrollToWrappedVisible(int res)\r
498   {\r
499     int cwidth = seqPanel.seqCanvas\r
500             .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);\r
501     if (res <= av.getStartRes() || res >= (av.getStartRes() + cwidth))\r
502     {\r
503       vscroll.setValue(res / cwidth);\r
504       av.startRes = vscroll.getValue() * cwidth;\r
505     }\r
506   }\r
507 \r
508   public OverviewPanel getOverviewPanel()\r
509   {\r
510     return overviewPanel;\r
511   }\r
512 \r
513   public void setOverviewPanel(OverviewPanel op)\r
514   {\r
515     overviewPanel = op;\r
516   }\r
517 \r
518   public void setAnnotationVisible(boolean b)\r
519   {\r
520     if (!av.getWrapAlignment())\r
521     {\r
522       annotationSpaceFillerHolder.setVisible(b);\r
523       annotationPanelHolder.setVisible(b);\r
524     }\r
525     else\r
526     {\r
527       annotationSpaceFillerHolder.setVisible(false);\r
528       annotationPanelHolder.setVisible(false);\r
529     }\r
530     validate();\r
531     repaint();\r
532   }\r
533 \r
534   /**\r
535    * automatically adjust annotation panel height for new annotation whilst\r
536    * ensuring the alignment is still visible.\r
537    */\r
538   public void adjustAnnotationHeight()\r
539   {\r
540     // TODO: display vertical annotation scrollbar if necessary\r
541     // this is called after loading new annotation onto alignment\r
542     if (alignFrame.getSize().height == 0)\r
543     {\r
544       System.out\r
545               .println("adjustAnnotationHeight frame size zero NEEDS FIXING");\r
546     }\r
547     fontChanged();\r
548     validateAnnotationDimensions(true);\r
549     apvscroll.addNotify();\r
550     hscroll.addNotify();\r
551     validate();\r
552     paintAlignment(true);\r
553   }\r
554 \r
555   /**\r
556    * Calculate the annotation dimensions and refresh slider values accordingly.\r
557    * Need to do repaints/notifys afterwards.\r
558    */\r
559   protected void validateAnnotationDimensions(boolean adjustPanelHeight)\r
560   {\r
561     int rowHeight = av.getCharHeight();\r
562     int alignmentHeight = rowHeight * av.getAlignment().getHeight();\r
563     int annotationHeight = av.calcPanelHeight();\r
564 \r
565     int mheight = annotationHeight;\r
566     Dimension d = sequenceHolderPanel.getSize();\r
567 \r
568     int availableHeight = d.height - scalePanelHolder.getHeight();\r
569 \r
570     if (adjustPanelHeight)\r
571     {\r
572       /*\r
573        * If not enough vertical space, maximize annotation height while keeping\r
574        * at least two rows of alignment visible\r
575        */\r
576       if (annotationHeight + alignmentHeight > availableHeight)\r
577       {\r
578         annotationHeight = Math.min(annotationHeight, availableHeight - 2\r
579                 * rowHeight);\r
580       }\r
581     }\r
582     else\r
583     {\r
584       // maintain same window layout whilst updating sliders\r
585       annotationHeight = annotationPanelHolder.getSize().height;\r
586     }\r
587 \r
588     if (availableHeight - annotationHeight < 5)\r
589     {\r
590       annotationHeight = availableHeight;\r
591     }\r
592 \r
593     annotationPanel.setSize(new Dimension(d.width, annotationHeight));\r
594     annotationPanelHolder.setSize(new Dimension(d.width, annotationHeight));\r
595     // seqPanelHolder.setSize(d.width, seqandannot - height);\r
596     seqPanel.seqCanvas\r
597             .setSize(d.width, seqPanel.seqCanvas.getSize().height);\r
598 \r
599     Dimension e = idPanel.getSize();\r
600     alabels.setSize(new Dimension(e.width, annotationHeight));\r
601     annotationSpaceFillerHolder.setSize(new Dimension(e.width,\r
602             annotationHeight));\r
603 \r
604     int s = apvscroll.getValue();\r
605     if (s > mheight - annotationHeight)\r
606     {\r
607       s = 0;\r
608     }\r
609     apvscroll.setValues(s, annotationHeight, 0, mheight);\r
610     annotationPanel.setScrollOffset(apvscroll.getValue(), false);\r
611     alabels.setScrollOffset(apvscroll.getValue(), false);\r
612   }\r
613 \r
614   public void setWrapAlignment(boolean wrap)\r
615   {\r
616     av.startSeq = 0;\r
617     av.startRes = 0;\r
618     scalePanelHolder.setVisible(!wrap);\r
619 \r
620     hscroll.setVisible(!wrap);\r
621     idwidthAdjuster.setVisible(!wrap);\r
622 \r
623     if (wrap)\r
624     {\r
625       annotationPanelHolder.setVisible(false);\r
626       annotationSpaceFillerHolder.setVisible(false);\r
627     }\r
628     else if (av.isShowAnnotation())\r
629     {\r
630       annotationPanelHolder.setVisible(true);\r
631       annotationSpaceFillerHolder.setVisible(true);\r
632     }\r
633 \r
634     idSpaceFillerPanel1.setVisible(!wrap);\r
635 \r
636     fontChanged(); // This is so that the scalePanel is resized correctly\r
637 \r
638     validate();\r
639     sequenceHolderPanel.validate();\r
640     repaint();\r
641 \r
642   }\r
643 \r
644   int hextent = 0;\r
645 \r
646   int vextent = 0;\r
647 \r
648   // return value is true if the scroll is valid\r
649   public boolean scrollUp(boolean up)\r
650   {\r
651     if (up)\r
652     {\r
653       if (vscroll.getValue() < 1)\r
654       {\r
655         return false;\r
656       }\r
657       setScrollValues(hscroll.getValue(), vscroll.getValue() - 1);\r
658     }\r
659     else\r
660     {\r
661       if (vextent + vscroll.getValue() >= av.getAlignment().getHeight())\r
662       {\r
663         return false;\r
664       }\r
665       setScrollValues(hscroll.getValue(), vscroll.getValue() + 1);\r
666     }\r
667 \r
668     repaint();\r
669     return true;\r
670   }\r
671 \r
672   public boolean scrollRight(boolean right)\r
673   {\r
674     if (!right)\r
675     {\r
676       if (hscroll.getValue() < 1)\r
677       {\r
678         return false;\r
679       }\r
680       setScrollValues(hscroll.getValue() - 1, vscroll.getValue());\r
681     }\r
682     else\r
683     {\r
684       if (hextent + hscroll.getValue() >= av.getAlignment().getWidth())\r
685       {\r
686         return false;\r
687       }\r
688       setScrollValues(hscroll.getValue() + 1, vscroll.getValue());\r
689     }\r
690 \r
691     repaint();\r
692     return true;\r
693   }\r
694 \r
695   public void setScrollValues(int x, int y)\r
696   {\r
697     int width = av.getAlignment().getWidth();\r
698     int height = av.getAlignment().getHeight();\r
699 \r
700     if (av.hasHiddenColumns())\r
701     {\r
702       width = av.getColumnSelection().findColumnPosition(width);\r
703     }\r
704     if (x < 0)\r
705     {\r
706       x = 0;\r
707     }\r
708     ;\r
709 \r
710     hextent = seqPanel.seqCanvas.getSize().width / av.getCharWidth();\r
711     vextent = seqPanel.seqCanvas.getSize().height / av.getCharHeight();\r
712 \r
713     if (hextent > width)\r
714     {\r
715       hextent = width;\r
716     }\r
717 \r
718     if (vextent > height)\r
719     {\r
720       vextent = height;\r
721     }\r
722 \r
723     if ((hextent + x) > width)\r
724     {\r
725       System.err.println("hextent was " + hextent + " and x was " + x);\r
726 \r
727       x = width - hextent;\r
728     }\r
729 \r
730     if ((vextent + y) > height)\r
731     {\r
732       y = height - vextent;\r
733     }\r
734 \r
735     if (y < 0)\r
736     {\r
737       y = 0;\r
738     }\r
739 \r
740     if (x < 0)\r
741     {\r
742       System.err.println("x was " + x);\r
743       x = 0;\r
744     }\r
745 \r
746     av.setStartSeq(y);\r
747 \r
748     int endSeq = y + vextent;\r
749     if (endSeq > av.getAlignment().getHeight())\r
750     {\r
751       endSeq = av.getAlignment().getHeight();\r
752     }\r
753 \r
754     av.setEndSeq(endSeq);\r
755     av.setStartRes(x);\r
756     av.setEndRes((x + (seqPanel.seqCanvas.getSize().width / av\r
757             .getCharWidth())) - 1);\r
758 \r
759     hscroll.setValues(x, hextent, 0, width);\r
760     vscroll.setValues(y, vextent, 0, height);\r
761 \r
762     if (overviewPanel != null)\r
763     {\r
764       overviewPanel.setBoxPosition();\r
765     }\r
766     sendViewPosition();\r
767 \r
768   }\r
769 \r
770   public void adjustmentValueChanged(AdjustmentEvent evt)\r
771   {\r
772     int oldX = av.getStartRes();\r
773     int oldY = av.getStartSeq();\r
774 \r
775     if (evt == null || evt.getSource() == apvscroll)\r
776     {\r
777       annotationPanel.setScrollOffset(apvscroll.getValue(), false);\r
778       alabels.setScrollOffset(apvscroll.getValue(), false);\r
779       // annotationPanel.image=null;\r
780       // alabels.image=null;\r
781       // alabels.repaint();\r
782       // annotationPanel.repaint();\r
783     }\r
784     if (evt == null || evt.getSource() == hscroll)\r
785     {\r
786       int x = hscroll.getValue();\r
787       av.setStartRes(x);\r
788       av.setEndRes(x + seqPanel.seqCanvas.getSize().width\r
789               / av.getCharWidth() - 1);\r
790     }\r
791 \r
792     if (evt == null || evt.getSource() == vscroll)\r
793     {\r
794       int offy = vscroll.getValue();\r
795       if (av.getWrapAlignment())\r
796       {\r
797         int rowSize = seqPanel.seqCanvas\r
798                 .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);\r
799         av.setStartRes(vscroll.getValue() * rowSize);\r
800         av.setEndRes((vscroll.getValue() + 1) * rowSize);\r
801       }\r
802       else\r
803       {\r
804         av.setStartSeq(offy);\r
805         av.setEndSeq(offy + seqPanel.seqCanvas.getSize().height\r
806                 / av.getCharHeight());\r
807       }\r
808     }\r
809 \r
810     if (overviewPanel != null)\r
811     {\r
812       overviewPanel.setBoxPosition();\r
813     }\r
814 \r
815     int scrollX = av.startRes - oldX;\r
816     int scrollY = av.startSeq - oldY;\r
817 \r
818     if (av.getWrapAlignment() || !fastPaint || av.MAC)\r
819     {\r
820       repaint();\r
821     }\r
822     else\r
823     {\r
824       // Make sure we're not trying to draw a panel\r
825       // larger than the visible window\r
826       if (scrollX > av.endRes - av.startRes)\r
827       {\r
828         scrollX = av.endRes - av.startRes;\r
829       }\r
830       else if (scrollX < av.startRes - av.endRes)\r
831       {\r
832         scrollX = av.startRes - av.endRes;\r
833       }\r
834 \r
835       idPanel.idCanvas.fastPaint(scrollY);\r
836       seqPanel.seqCanvas.fastPaint(scrollX, scrollY);\r
837 \r
838       scalePanel.repaint();\r
839       if (av.isShowAnnotation())\r
840       {\r
841         annotationPanel.fastPaint(av.getStartRes() - oldX);\r
842       }\r
843     }\r
844     sendViewPosition();\r
845 \r
846     /*\r
847      * If there is one, scroll the (Protein/cDNA) complementary alignment to\r
848      * match, unless we are ourselves doing that.\r
849      */\r
850     if (isFollowingComplementScroll())\r
851     {\r
852       setFollowingComplementScroll(false);\r
853     }\r
854     else\r
855     {\r
856       AlignmentPanel ap = getComplementPanel();\r
857       av.scrollComplementaryAlignment(ap);\r
858     }\r
859 \r
860   }\r
861 \r
862   /**\r
863    * A helper method to return the AlignmentPanel in the other (complementary)\r
864    * half of a SplitFrame view. Returns null if not in a SplitFrame.\r
865    * \r
866    * @return\r
867    */\r
868   private AlignmentPanel getComplementPanel()\r
869   {\r
870     AlignmentPanel ap = null;\r
871     if (alignFrame != null)\r
872     {\r
873       SplitFrame sf = alignFrame.getSplitFrame();\r
874       if (sf != null)\r
875       {\r
876         AlignFrame other = sf.getComplement(alignFrame);\r
877         if (other != null)\r
878         {\r
879           ap = other.alignPanel;\r
880         }\r
881       }\r
882     }\r
883     return ap;\r
884   }\r
885 \r
886   /**\r
887    * Follow a scrolling change in the (cDNA/Protein) complementary alignment.\r
888    * The aim is to keep the two alignments 'lined up' on their centre columns.\r
889    * \r
890    * @param sr\r
891    *          holds mapped region(s) of this alignment that we are scrolling\r
892    *          'to'; may be modified for sequence offset by this method\r
893    * @param seqOffset\r
894    *          the number of visible sequences to show above the mapped region\r
895    */\r
896   protected void scrollToCentre(SearchResults sr, int seqOffset)\r
897   {\r
898     /*\r
899      * To avoid jumpy vertical scrolling (if some sequences are gapped or not\r
900      * mapped), we can make the scroll-to location a sequence above the one\r
901      * actually mapped.\r
902      */\r
903     SequenceI mappedTo = sr.getResultSequence(0);\r
904     List<SequenceI> seqs = av.getAlignment().getSequences();\r
905 \r
906     /*\r
907      * This is like AlignmentI.findIndex(seq) but here we are matching the\r
908      * dataset sequence not the aligned sequence\r
909      */\r
910     int sequenceIndex = 0;\r
911     boolean matched = false;\r
912     for (SequenceI seq : seqs)\r
913     {\r
914       if (mappedTo == seq.getDatasetSequence())\r
915       {\r
916         matched = true;\r
917         break;\r
918       }\r
919       sequenceIndex++;\r
920     }\r
921     if (!matched)\r
922     {\r
923       return; // failsafe, shouldn't happen\r
924     }\r
925     sequenceIndex = Math.max(0, sequenceIndex - seqOffset);\r
926     sr.getResults().get(0)\r
927             .setSequence(av.getAlignment().getSequenceAt(sequenceIndex));\r
928 \r
929     /*\r
930      * Scroll to position but centring the target residue. Also set a state flag\r
931      * to prevent adjustmentValueChanged performing this recursively.\r
932      */\r
933     setFollowingComplementScroll(true);\r
934     scrollToPosition(sr, true, true);\r
935   }\r
936 \r
937   private void sendViewPosition()\r
938   {\r
939     StructureSelectionManager.getStructureSelectionManager(av.applet)\r
940             .sendViewPosition(this, av.startRes, av.endRes, av.startSeq,\r
941                     av.endSeq);\r
942   }\r
943 \r
944   /**\r
945    * Repaint the alignment and annotations, and, optionally, any overview window\r
946    */\r
947   public void paintAlignment(boolean updateOverview)\r
948   {\r
949     final AnnotationSorter sorter = new AnnotationSorter(getAlignment(),\r
950             av.isShowAutocalculatedAbove());\r
951     sorter.sort(getAlignment().getAlignmentAnnotation(),\r
952             av.getSortAnnotationsBy());\r
953     repaint();\r
954 \r
955     if (updateOverview)\r
956     {\r
957       StructureSelectionManager.getStructureSelectionManager(av.applet)\r
958               .sequenceColoursChanged(this);\r
959 \r
960       if (overviewPanel != null)\r
961       {\r
962         overviewPanel.updateOverviewImage();\r
963       }\r
964     }\r
965   }\r
966 \r
967   public void update(Graphics g)\r
968   {\r
969     paint(g);\r
970   }\r
971 \r
972   public void paint(Graphics g)\r
973   {\r
974     invalidate();\r
975     Dimension d = idPanel.idCanvas.getSize();\r
976     final int canvasHeight = seqPanel.seqCanvas.getSize().height;\r
977     if (canvasHeight != d.height)\r
978     {\r
979       idPanel.idCanvas.setSize(d.width, canvasHeight);\r
980     }\r
981 \r
982     if (av.getWrapAlignment())\r
983     {\r
984       int maxwidth = av.getAlignment().getWidth();\r
985 \r
986       if (av.hasHiddenColumns())\r
987       {\r
988         maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;\r
989       }\r
990 \r
991       int canvasWidth = seqPanel.seqCanvas\r
992               .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);\r
993 \r
994       if (canvasWidth > 0)\r
995       {\r
996         int max = maxwidth / canvasWidth;\r
997         vscroll.setMaximum(1 + max);\r
998         vscroll.setUnitIncrement(1);\r
999         vscroll.setVisibleAmount(1);\r
1000       }\r
1001     }\r
1002     else\r
1003     {\r
1004       setScrollValues(av.getStartRes(), av.getStartSeq());\r
1005     }\r
1006 \r
1007     seqPanel.seqCanvas.repaint();\r
1008     idPanel.idCanvas.repaint();\r
1009     if (!av.getWrapAlignment())\r
1010     {\r
1011       if (av.isShowAnnotation())\r
1012       {\r
1013         alabels.repaint();\r
1014         annotationPanel.repaint();\r
1015       }\r
1016       scalePanel.repaint();\r
1017     }\r
1018 \r
1019   }\r
1020 \r
1021   protected JPanel sequenceHolderPanel = new JPanel();\r
1022 \r
1023   protected JScrollBar vscroll = new JScrollBar();\r
1024 \r
1025   protected JScrollBar hscroll = new JScrollBar();\r
1026 \r
1027   protected JPanel seqPanelHolder = new JPanel();\r
1028 \r
1029   protected JPanel scalePanelHolder = new JPanel();\r
1030 \r
1031   protected JPanel idPanelHolder = new JPanel();\r
1032 \r
1033   protected JPanel idSpaceFillerPanel1 = new JPanel();\r
1034 \r
1035   public JPanel annotationSpaceFillerHolder = new JPanel();\r
1036 \r
1037   protected JPanel hscrollFillerPanel = new JPanel();\r
1038 \r
1039   JPanel annotationPanelHolder = new JPanel();\r
1040 \r
1041   protected JScrollBar apvscroll = new JScrollBar();\r
1042 \r
1043   /*\r
1044    * Flag set while scrolling to follow complementary cDNA/protein scroll. When\r
1045    * true, suppresses invoking the same method recursively.\r
1046    */\r
1047   private boolean followingComplementScroll;\r
1048 \r
1049   private void jbInit() throws Exception\r
1050   {\r
1051     // idPanelHolder.setPreferredSize(new Dimension(70, 10));\r
1052     this.setLayout(new BorderLayout());\r
1053 \r
1054     // sequenceHolderPanel.setPreferredSize(new Dimension(150, 150));\r
1055     sequenceHolderPanel.setLayout(new BorderLayout());\r
1056     seqPanelHolder.setLayout(new BorderLayout());\r
1057     scalePanelHolder.setBackground(Color.white);\r
1058 \r
1059     // scalePanelHolder.setPreferredSize(new Dimension(10, 30));\r
1060     scalePanelHolder.setLayout(new BorderLayout());\r
1061     idPanelHolder.setLayout(new BorderLayout());\r
1062     idSpaceFillerPanel1.setBackground(Color.white);\r
1063 \r
1064     // idSpaceFillerPanel1.setPreferredSize(new Dimension(10, 30));\r
1065     idSpaceFillerPanel1.setLayout(new BorderLayout());\r
1066     annotationSpaceFillerHolder.setBackground(Color.white);\r
1067 \r
1068     // annotationSpaceFillerHolder.setPreferredSize(new Dimension(10, 80));\r
1069     annotationSpaceFillerHolder.setLayout(new BorderLayout());\r
1070     hscroll.setOrientation(JScrollBar.HORIZONTAL);\r
1071 \r
1072     JPanel hscrollHolder = new JPanel();\r
1073     hscrollHolder.setLayout(new BorderLayout());\r
1074     hscrollFillerPanel.setBackground(Color.white);\r
1075     apvscroll.setOrientation(JScrollBar.VERTICAL);\r
1076     apvscroll.setVisible(true);\r
1077     apvscroll.addAdjustmentListener(this);\r
1078 \r
1079     annotationPanelHolder.setBackground(Color.white);\r
1080     annotationPanelHolder.setLayout(new BorderLayout());\r
1081     annotationPanelHolder.add(apvscroll, BorderLayout.EAST);\r
1082     // hscrollFillerPanel.setPreferredSize(new Dimension(70, 10));\r
1083     hscrollHolder.setBackground(Color.white);\r
1084 \r
1085     // annotationScroller.setPreferredSize(new Dimension(10, 80));\r
1086     // this.setPreferredSize(new Dimension(220, 166));\r
1087     seqPanelHolder.setBackground(Color.white);\r
1088     idPanelHolder.setBackground(Color.white);\r
1089     sequenceHolderPanel.add(scalePanelHolder, BorderLayout.NORTH);\r
1090     sequenceHolderPanel.add(seqPanelHolder, BorderLayout.CENTER);\r
1091     seqPanelHolder.add(vscroll, BorderLayout.EAST);\r
1092 \r
1093     // Panel3.add(secondaryPanelHolder, BorderLayout.SOUTH);\r
1094     this.add(idPanelHolder, BorderLayout.WEST);\r
1095     idPanelHolder.add(idSpaceFillerPanel1, BorderLayout.NORTH);\r
1096     idPanelHolder.add(annotationSpaceFillerHolder, BorderLayout.SOUTH);\r
1097     this.add(hscrollHolder, BorderLayout.SOUTH);\r
1098     hscrollHolder.add(hscroll, BorderLayout.CENTER);\r
1099     hscrollHolder.add(hscrollFillerPanel, BorderLayout.WEST);\r
1100     this.add(sequenceHolderPanel, BorderLayout.CENTER);\r
1101   }\r
1102 \r
1103   /**\r
1104    * hides or shows dynamic annotation rows based on groups and av state flags\r
1105    */\r
1106   public void updateAnnotation()\r
1107   {\r
1108     updateAnnotation(false);\r
1109   }\r
1110 \r
1111   public void updateAnnotation(boolean applyGlobalSettings)\r
1112   {\r
1113     updateAnnotation(applyGlobalSettings, false);\r
1114   }\r
1115 \r
1116   public void updateAnnotation(boolean applyGlobalSettings,\r
1117           boolean preserveNewGroupSettings)\r
1118   {\r
1119     av.updateGroupAnnotationSettings(applyGlobalSettings,\r
1120             preserveNewGroupSettings);\r
1121     adjustAnnotationHeight();\r
1122   }\r
1123 \r
1124   @Override\r
1125   public AlignmentI getAlignment()\r
1126   {\r
1127     return av.getAlignment();\r
1128   }\r
1129 \r
1130   @Override\r
1131   public String getViewName()\r
1132   {\r
1133     return getName();\r
1134   }\r
1135 \r
1136   @Override\r
1137   public StructureSelectionManager getStructureSelectionManager()\r
1138   {\r
1139     return StructureSelectionManager\r
1140             .getStructureSelectionManager(av.applet);\r
1141   }\r
1142 \r
1143   @Override\r
1144   public void raiseOOMWarning(String string, OutOfMemoryError error)\r
1145   {\r
1146     // TODO: JAL-960\r
1147     System.err.println("Out of memory whilst '" + string + "'");\r
1148     error.printStackTrace();\r
1149   }\r
1150 \r
1151   /**\r
1152    * Set a flag to say we are scrolling to follow a (cDNA/protein) complement.\r
1153    * \r
1154    * @param b\r
1155    */\r
1156   protected void setFollowingComplementScroll(boolean b)\r
1157   {\r
1158     this.followingComplementScroll = b;\r
1159   }\r
1160 \r
1161   protected boolean isFollowingComplementScroll()\r
1162   {\r
1163     return this.followingComplementScroll;\r
1164   }\r
1165 \r
1166 }\r