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