patch to fix occasional arrayoutofbounds exception when working with hidden columns...
[jalview.git] / src / jalview / appletgui / AlignmentPanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.appletgui;
20
21 import java.awt.*;
22 import java.awt.event.*;
23
24 import jalview.datamodel.*;
25
26 public class AlignmentPanel extends Panel implements AdjustmentListener
27 {
28
29   public AlignViewport av;
30
31   OverviewPanel overviewPanel;
32
33   SeqPanel seqPanel;
34
35   IdPanel idPanel;
36
37   IdwidthAdjuster idwidthAdjuster;
38
39   public AlignFrame alignFrame;
40
41   ScalePanel scalePanel;
42
43   AnnotationPanel annotationPanel;
44
45   AnnotationLabels alabels;
46
47   // this value is set false when selection area being dragged
48   boolean fastPaint = true;
49
50   public AlignmentPanel(AlignFrame af, final AlignViewport av)
51   {
52     try
53     {
54       jbInit();
55     } catch (Exception e)
56     {
57       e.printStackTrace();
58     }
59
60     alignFrame = af;
61     this.av = av;
62     seqPanel = new SeqPanel(av, this);
63     idPanel = new IdPanel(av, this);
64     scalePanel = new ScalePanel(av, this);
65     idwidthAdjuster = new IdwidthAdjuster(this);
66     annotationPanel = new AnnotationPanel(this);
67
68     sequenceHolderPanel.add(annotationPanel, BorderLayout.SOUTH);
69
70     alabels = new AnnotationLabels(this);
71
72     setAnnotationVisible(av.showAnnotation);
73
74     idPanelHolder.add(idPanel, BorderLayout.CENTER);
75     idSpaceFillerPanel1.add(idwidthAdjuster, BorderLayout.CENTER);
76     annotationSpaceFillerHolder.add(alabels, BorderLayout.CENTER);
77     scalePanelHolder.add(scalePanel, BorderLayout.CENTER);
78     seqPanelHolder.add(seqPanel, BorderLayout.CENTER);
79
80     fontChanged();
81     setScrollValues(0, 0);
82
83     hscroll.addAdjustmentListener(this);
84     vscroll.addAdjustmentListener(this);
85
86     addComponentListener(new ComponentAdapter()
87     {
88       public void componentResized(ComponentEvent evt)
89       {
90         setScrollValues(av.getStartRes(), av.getStartSeq());
91         repaint();
92       }
93     });
94
95     Dimension d = calculateIdWidth();
96     idPanel.idCanvas.setSize(d);
97
98     hscrollFillerPanel.setSize(d.width, annotationPanel.getSize().height);
99
100     idPanel.idCanvas.setSize(d.width, seqPanel.seqCanvas.getSize().height);
101     annotationSpaceFillerHolder.setSize(d.width,
102             annotationPanel.getSize().height);
103     alabels.setSize(d.width, annotationPanel.getSize().height);
104
105     final AlignmentPanel ap = this;
106     av.addPropertyChangeListener(new java.beans.PropertyChangeListener()
107     {
108       public void propertyChange(java.beans.PropertyChangeEvent evt)
109       {
110         if (evt.getPropertyName().equals("alignment"))
111         {
112           PaintRefresher.Refresh(ap, av.getSequenceSetId(), true, true);
113           alignmentChanged();
114         }
115       }
116     });
117
118   }
119
120   public SequenceRenderer getSequenceRenderer()
121   {
122     return seqPanel.seqCanvas.sr;
123   }
124
125   public FeatureRenderer getFeatureRenderer()
126   {
127     return seqPanel.seqCanvas.fr;
128   }
129
130   public void alignmentChanged()
131   {
132     av.alignmentChanged(this);
133
134     if (overviewPanel != null)
135     {
136       overviewPanel.updateOverviewImage();
137     }
138
139     alignFrame.updateEditMenuBar();
140
141     repaint();
142   }
143
144   public void fontChanged()
145   {
146     // set idCanvas bufferedImage to null
147     // to prevent drawing old image
148     idPanel.idCanvas.image = null;
149     FontMetrics fm = getFontMetrics(av.getFont());
150
151     scalePanel.setSize(new Dimension(10, av.charHeight + fm.getDescent()));
152     idwidthAdjuster.setSize(new Dimension(10, av.charHeight
153             + fm.getDescent()));
154     av.updateSequenceIdColours();
155     annotationPanel.image = null;
156     int ap = annotationPanel.adjustPanelHeight();
157     annotationPanel.repaint();
158     Dimension d = calculateIdWidth();
159     d.setSize(d.width + 4, seqPanel.seqCanvas.getSize().height);
160     alabels.setSize(d.width + 4, ap);
161     idPanel.idCanvas.setSize(d);
162     hscrollFillerPanel.setSize(d);
163
164     validate();
165     repaint();
166
167     if (overviewPanel != null)
168     {
169       overviewPanel.updateOverviewImage();
170     }
171   }
172
173   public void setIdWidth(int w, int h)
174   {
175     idPanel.idCanvas.setSize(w, h);
176     idPanelHolder.setSize(w, idPanelHolder.getSize().height);
177     alabels.setSize(w, alabels.getSize().height);
178     validate();
179   }
180
181   Dimension calculateIdWidth()
182   {
183     if (av.nullFrame == null)
184     {
185       av.nullFrame = new Frame();
186       av.nullFrame.addNotify();
187     }
188
189     Graphics g = av.nullFrame.getGraphics();
190
191     FontMetrics fm = g.getFontMetrics(av.font);
192     AlignmentI al = av.getAlignment();
193
194     int i = 0;
195     int idWidth = 0;
196     String id;
197     while (i < al.getHeight() && al.getSequenceAt(i) != null)
198     {
199       SequenceI s = al.getSequenceAt(i);
200       id = s.getDisplayId(av.getShowJVSuffix());
201
202       if (fm.stringWidth(id) > idWidth)
203       {
204         idWidth = fm.stringWidth(id);
205       }
206       i++;
207     }
208
209     // Also check annotation label widths
210     i = 0;
211     if (al.getAlignmentAnnotation() != null)
212     {
213       fm = g.getFontMetrics(av.nullFrame.getFont());
214       while (i < al.getAlignmentAnnotation().length)
215       {
216         String label = al.getAlignmentAnnotation()[i].label;
217         if (fm.stringWidth(label) > idWidth)
218         {
219           idWidth = fm.stringWidth(label);
220         }
221         i++;
222       }
223     }
224
225     return new Dimension(idWidth, idPanel.idCanvas.getSize().height);
226   }
227
228   public void highlightSearchResults(SearchResults results)
229   {
230     seqPanel.seqCanvas.highlightSearchResults(results);
231   }
232   /**
233    * scroll the view to show the position of the highlighted
234    * region in results (if any)
235    * @param results
236    */
237   public void scrollToPosition(SearchResults results)
238   {
239     // do we need to scroll the panel?
240     if (results != null && results.getSize()>0)
241     {
242       int seqIndex = av.alignment.findIndex(results);
243       if (seqIndex==-1)
244       {
245         return;
246       }
247       SequenceI seq = av.alignment.getSequenceAt(seqIndex);
248       int [] r = results.getResults(seq, seq.getStart(), seq.getEnd());
249       if (r==null)
250       {
251         return;
252       }
253       int start = r[0];
254       int end = r[1];
255       if (start<0)
256       {
257         return;
258       }
259       if (end==seq.getEnd())
260       {
261         return;
262       }
263       if (!av.wrapAlignment)
264       {
265         if ((av.getStartRes() > end)
266                 || (av.getEndRes() < start)
267                 || ((av.getStartSeq() > seqIndex) || (av.getEndSeq() < seqIndex)))
268         {
269           if (start > av.alignment.getWidth() - hextent)
270           {
271             start = av.alignment.getWidth() - hextent;
272             if (start < 0)
273             {
274               start = 0;
275             }
276           }
277           if (seqIndex > av.alignment.getHeight() - vextent)
278           {
279             seqIndex = av.alignment.getHeight() - vextent;
280             if (seqIndex < 0)
281             {
282               seqIndex = 0;
283             }
284           }
285           setScrollValues(start, seqIndex);
286         }
287       }
288       else
289       {
290         scrollToWrappedVisible(start);
291       }
292     }
293
294     repaint();
295   }
296
297   void scrollToWrappedVisible(int res)
298   {
299     int cwidth = seqPanel.seqCanvas
300             .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);
301     if (res <= av.getStartRes() || res >= (av.getStartRes() + cwidth))
302     {
303       vscroll.setValue(res / cwidth);
304       av.startRes = vscroll.getValue() * cwidth;
305     }
306   }
307
308   public OverviewPanel getOverviewPanel()
309   {
310     return overviewPanel;
311   }
312
313   public void setOverviewPanel(OverviewPanel op)
314   {
315     overviewPanel = op;
316   }
317
318   public void setAnnotationVisible(boolean b)
319   {
320     if (!av.wrapAlignment)
321     {
322       annotationSpaceFillerHolder.setVisible(b);
323       annotationPanel.setVisible(b);
324     }
325     validate();
326     repaint();
327   }
328
329   public void setWrapAlignment(boolean wrap)
330   {
331     av.startSeq = 0;
332     av.startRes = 0;
333     scalePanelHolder.setVisible(!wrap);
334
335     hscroll.setVisible(!wrap);
336     idwidthAdjuster.setVisible(!wrap);
337
338     if (wrap)
339     {
340       annotationPanel.setVisible(false);
341       annotationSpaceFillerHolder.setVisible(false);
342     }
343     else if (av.showAnnotation)
344     {
345       annotationPanel.setVisible(true);
346       annotationSpaceFillerHolder.setVisible(true);
347     }
348
349     idSpaceFillerPanel1.setVisible(!wrap);
350
351     fontChanged(); // This is so that the scalePanel is resized correctly
352
353     validate();
354     repaint();
355
356   }
357
358   int hextent = 0;
359
360   int vextent = 0;
361
362   // return value is true if the scroll is valid
363   public boolean scrollUp(boolean up)
364   {
365     if (up)
366     {
367       if (vscroll.getValue() < 1)
368       {
369         return false;
370       }
371       setScrollValues(hscroll.getValue(), vscroll.getValue() - 1);
372     }
373     else
374     {
375       if (vextent + vscroll.getValue() >= av.getAlignment().getHeight())
376       {
377         return false;
378       }
379       setScrollValues(hscroll.getValue(), vscroll.getValue() + 1);
380     }
381
382     repaint();
383     return true;
384   }
385
386   public boolean scrollRight(boolean right)
387   {
388     if (!right)
389     {
390       if (hscroll.getValue() < 1)
391       {
392         return false;
393       }
394       setScrollValues(hscroll.getValue() - 1, vscroll.getValue());
395     }
396     else
397     {
398       if (hextent + hscroll.getValue() >= av.getAlignment().getWidth())
399       {
400         return false;
401       }
402       setScrollValues(hscroll.getValue() + 1, vscroll.getValue());
403     }
404
405     repaint();
406     return true;
407   }
408
409   public void setScrollValues(int x, int y)
410   {
411     int width = av.alignment.getWidth();
412     int height = av.alignment.getHeight();
413
414     if (av.hasHiddenColumns)
415     {
416       width = av.getColumnSelection().findColumnPosition(width);
417     }
418
419     av.setStartRes(x);
420     av
421             .setEndRes((x + (seqPanel.seqCanvas.getSize().width / av.charWidth)) - 1);
422
423     hextent = seqPanel.seqCanvas.getSize().width / av.charWidth;
424     vextent = seqPanel.seqCanvas.getSize().height / av.charHeight;
425
426     if (hextent > width)
427     {
428       hextent = width;
429     }
430
431     if (vextent > height)
432     {
433       vextent = height;
434     }
435
436     if ((hextent + x) > width)
437     {
438       x = width - hextent;
439     }
440
441     if ((vextent + y) > height)
442     {
443       y = height - vextent;
444     }
445
446     if (y < 0)
447     {
448       y = 0;
449     }
450
451     if (x < 0)
452     {
453       x = 0;
454     }
455
456     av.setStartSeq(y);
457
458     int endSeq = y + vextent;
459     if (endSeq > av.alignment.getHeight())
460     {
461       endSeq = av.alignment.getHeight();
462     }
463
464     av.setEndSeq(endSeq);
465     hscroll.setValues(x, hextent, 0, width);
466     vscroll.setValues(y, vextent, 0, height);
467
468     if (overviewPanel != null)
469     {
470       overviewPanel.setBoxPosition();
471     }
472
473   }
474
475   public void adjustmentValueChanged(AdjustmentEvent evt)
476   {
477     int oldX = av.getStartRes();
478     int oldY = av.getStartSeq();
479
480     if (evt == null || evt.getSource() == hscroll)
481     {
482       int x = hscroll.getValue();
483       av.setStartRes(x);
484       av.setEndRes(x + seqPanel.seqCanvas.getSize().width
485               / av.getCharWidth() - 1);
486     }
487
488     if (evt == null || evt.getSource() == vscroll)
489     {
490       int offy = vscroll.getValue();
491       if (av.getWrapAlignment())
492       {
493         int rowSize = seqPanel.seqCanvas
494                 .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);
495         av.setStartRes(vscroll.getValue() * rowSize);
496         av.setEndRes((vscroll.getValue() + 1) * rowSize);
497       }
498       else
499       {
500         av.setStartSeq(offy);
501         av.setEndSeq(offy + seqPanel.seqCanvas.getSize().height
502                 / av.getCharHeight());
503       }
504     }
505
506     if (overviewPanel != null)
507     {
508       overviewPanel.setBoxPosition();
509     }
510
511     int scrollX = av.startRes - oldX;
512     int scrollY = av.startSeq - oldY;
513
514     if (av.getWrapAlignment() || !fastPaint || av.MAC)
515     {
516       repaint();
517     }
518     else
519     {
520       // Make sure we're not trying to draw a panel
521       // larger than the visible window
522       if (scrollX > av.endRes - av.startRes)
523       {
524         scrollX = av.endRes - av.startRes;
525       }
526       else if (scrollX < av.startRes - av.endRes)
527       {
528         scrollX = av.startRes - av.endRes;
529       }
530
531       idPanel.idCanvas.fastPaint(scrollY);
532       seqPanel.seqCanvas.fastPaint(scrollX, scrollY);
533
534       scalePanel.repaint();
535       if (av.getShowAnnotation())
536       {
537         annotationPanel.fastPaint(av.getStartRes() - oldX);
538       }
539     }
540
541   }
542
543   public void paintAlignment(boolean updateOverview)
544   {
545     repaint();
546
547     if (updateOverview)
548     {
549       jalview.structure.StructureSelectionManager
550               .getStructureSelectionManager().sequenceColoursChanged(this);
551
552       if (overviewPanel != null)
553       {
554         overviewPanel.updateOverviewImage();
555       }
556     }
557   }
558
559   public void update(Graphics g)
560   {
561     paint(g);
562   }
563
564   public void paint(Graphics g)
565   {
566     invalidate();
567     Dimension d = idPanel.idCanvas.getSize();
568     idPanel.idCanvas.setSize(d.width, seqPanel.seqCanvas.getSize().height);
569     annotationSpaceFillerHolder.setSize(d.width,
570             annotationPanel.getSize().height);
571
572     alabels.setSize(d.width, annotationPanel.getSize().height);
573
574     if (av.getWrapAlignment())
575     {
576       int maxwidth = av.alignment.getWidth();
577
578       if (av.hasHiddenColumns)
579       {
580         maxwidth = av.getColumnSelection().findColumnPosition(maxwidth) - 1;
581       }
582
583       int canvasWidth = seqPanel.seqCanvas
584               .getWrappedCanvasWidth(seqPanel.seqCanvas.getSize().width);
585
586       if (canvasWidth > 0)
587       {
588         int max = maxwidth / canvasWidth;
589         vscroll.setMaximum(max);
590         vscroll.setUnitIncrement(1);
591         vscroll.setVisibleAmount(1);
592       }
593     }
594     else
595     {
596       setScrollValues(av.getStartRes(), av.getStartSeq());
597     }
598
599     alabels.repaint();
600
601     seqPanel.seqCanvas.repaint();
602     scalePanel.repaint();
603     annotationPanel.repaint();
604     idPanel.idCanvas.repaint();
605   }
606
607   protected Panel sequenceHolderPanel = new Panel();
608
609   protected Scrollbar vscroll = new Scrollbar();
610
611   protected Scrollbar hscroll = new Scrollbar();
612
613   protected Panel seqPanelHolder = new Panel();
614
615   BorderLayout borderLayout1 = new BorderLayout();
616
617   BorderLayout borderLayout3 = new BorderLayout();
618
619   protected Panel scalePanelHolder = new Panel();
620
621   protected Panel idPanelHolder = new Panel();
622
623   BorderLayout borderLayout5 = new BorderLayout();
624
625   protected Panel idSpaceFillerPanel1 = new Panel();
626
627   public Panel annotationSpaceFillerHolder = new Panel();
628
629   BorderLayout borderLayout6 = new BorderLayout();
630
631   BorderLayout borderLayout7 = new BorderLayout();
632
633   Panel hscrollHolder = new Panel();
634
635   BorderLayout borderLayout10 = new BorderLayout();
636
637   protected Panel hscrollFillerPanel = new Panel();
638
639   BorderLayout borderLayout11 = new BorderLayout();
640
641   BorderLayout borderLayout4 = new BorderLayout();
642
643   BorderLayout borderLayout2 = new BorderLayout();
644
645   private void jbInit() throws Exception
646   {
647     // idPanelHolder.setPreferredSize(new Dimension(70, 10));
648     this.setLayout(borderLayout7);
649
650     // sequenceHolderPanel.setPreferredSize(new Dimension(150, 150));
651     sequenceHolderPanel.setLayout(borderLayout3);
652     seqPanelHolder.setLayout(borderLayout1);
653     scalePanelHolder.setBackground(Color.white);
654
655     // scalePanelHolder.setPreferredSize(new Dimension(10, 30));
656     scalePanelHolder.setLayout(borderLayout6);
657     idPanelHolder.setLayout(borderLayout5);
658     idSpaceFillerPanel1.setBackground(Color.white);
659
660     // idSpaceFillerPanel1.setPreferredSize(new Dimension(10, 30));
661     idSpaceFillerPanel1.setLayout(borderLayout11);
662     annotationSpaceFillerHolder.setBackground(Color.white);
663
664     // annotationSpaceFillerHolder.setPreferredSize(new Dimension(10, 80));
665     annotationSpaceFillerHolder.setLayout(borderLayout4);
666     hscroll.setOrientation(Scrollbar.HORIZONTAL);
667     hscrollHolder.setLayout(borderLayout10);
668     hscrollFillerPanel.setBackground(Color.white);
669
670     // hscrollFillerPanel.setPreferredSize(new Dimension(70, 10));
671     hscrollHolder.setBackground(Color.white);
672
673     // annotationScroller.setPreferredSize(new Dimension(10, 80));
674     // this.setPreferredSize(new Dimension(220, 166));
675     seqPanelHolder.setBackground(Color.white);
676     idPanelHolder.setBackground(Color.white);
677     sequenceHolderPanel.add(scalePanelHolder, BorderLayout.NORTH);
678     sequenceHolderPanel.add(seqPanelHolder, BorderLayout.CENTER);
679     seqPanelHolder.add(vscroll, BorderLayout.EAST);
680
681     // Panel3.add(secondaryPanelHolder, BorderLayout.SOUTH);
682     this.add(idPanelHolder, BorderLayout.WEST);
683     idPanelHolder.add(idSpaceFillerPanel1, BorderLayout.NORTH);
684     idPanelHolder.add(annotationSpaceFillerHolder, BorderLayout.SOUTH);
685     this.add(hscrollHolder, BorderLayout.SOUTH);
686     hscrollHolder.add(hscroll, BorderLayout.CENTER);
687     hscrollHolder.add(hscrollFillerPanel, BorderLayout.WEST);
688     this.add(sequenceHolderPanel, BorderLayout.CENTER);
689   }
690
691 }