JAL-1989 more unit tests and encapsulation for ColumnSelection
[jalview.git] / src / jalview / appletgui / ScalePanel.java
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.datamodel.ColumnSelection;
24 import jalview.datamodel.SequenceGroup;
25 import jalview.util.MessageManager;
26
27 import java.awt.Color;
28 import java.awt.FontMetrics;
29 import java.awt.Graphics;
30 import java.awt.MenuItem;
31 import java.awt.Panel;
32 import java.awt.PopupMenu;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.InputEvent;
36 import java.awt.event.MouseEvent;
37 import java.awt.event.MouseListener;
38 import java.awt.event.MouseMotionListener;
39
40 public class ScalePanel extends Panel implements MouseMotionListener,
41         MouseListener
42 {
43
44   protected int offy = 4;
45
46   public int width;
47
48   protected AlignViewport av;
49
50   AlignmentPanel ap;
51
52   boolean stretchingGroup = false;
53
54   int min; // used by mouseDragged to see if user
55
56   int max; // used by mouseDragged to see if user
57
58   boolean mouseDragging = false;
59
60   int[] reveal;
61
62   public ScalePanel(AlignViewport av, AlignmentPanel ap)
63   {
64     setLayout(null);
65     this.av = av;
66     this.ap = ap;
67
68     addMouseListener(this);
69     addMouseMotionListener(this);
70
71   }
72
73   @Override
74   public void mousePressed(MouseEvent evt)
75   {
76     int x = (evt.getX() / av.getCharWidth()) + av.getStartRes();
77     final int res;
78
79     if (av.hasHiddenColumns())
80     {
81       res = av.getColumnSelection().adjustForHiddenColumns(x);
82     }
83     else
84     {
85       res = x;
86     }
87
88     min = res;
89     max = res;
90     if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK)
91     {
92       PopupMenu pop = new PopupMenu();
93       if (reveal != null)
94       {
95         MenuItem item = new MenuItem(
96                 MessageManager.getString("label.reveal"));
97         item.addActionListener(new ActionListener()
98         {
99           @Override
100           public void actionPerformed(ActionEvent e)
101           {
102             av.showColumn(reveal[0]);
103             reveal = null;
104             ap.paintAlignment(true);
105             if (ap.overviewPanel != null)
106             {
107               ap.overviewPanel.updateOverviewImage();
108             }
109           }
110         });
111         pop.add(item);
112
113         if (av.getColumnSelection().hasManyHiddenColumns())
114         {
115           item = new MenuItem(MessageManager.getString("action.reveal_all"));
116           item.addActionListener(new ActionListener()
117           {
118             @Override
119             public void actionPerformed(ActionEvent e)
120             {
121               av.showAllHiddenColumns();
122               reveal = null;
123               ap.paintAlignment(true);
124               if (ap.overviewPanel != null)
125               {
126                 ap.overviewPanel.updateOverviewImage();
127               }
128             }
129           });
130           pop.add(item);
131         }
132         this.add(pop);
133         pop.show(this, evt.getX(), evt.getY());
134       }
135       else if (av.getColumnSelection().contains(res))
136       {
137         MenuItem item = new MenuItem(
138                 MessageManager.getString("label.hide_columns"));
139         item.addActionListener(new ActionListener()
140         {
141           @Override
142           public void actionPerformed(ActionEvent e)
143           {
144             av.hideColumns(res, res);
145             if (av.getSelectionGroup() != null
146                     && av.getSelectionGroup().getSize() == av
147                             .getAlignment().getHeight())
148             {
149               av.setSelectionGroup(null);
150             }
151
152             ap.paintAlignment(true);
153             if (ap.overviewPanel != null)
154             {
155               ap.overviewPanel.updateOverviewImage();
156             }
157           }
158         });
159         pop.add(item);
160         this.add(pop);
161         pop.show(this, evt.getX(), evt.getY());
162       }
163     }
164     else
165     // LEFT MOUSE TO SELECT
166     {
167       if (!evt.isControlDown() && !evt.isShiftDown())
168       {
169         av.getColumnSelection().clear();
170       }
171
172       av.getColumnSelection().addElement(res);
173       SequenceGroup sg = new SequenceGroup();
174       for (int i = 0; i < av.getAlignment().getSequences().size(); i++)
175       {
176         sg.addSequence(av.getAlignment().getSequenceAt(i), false);
177       }
178
179       sg.setStartRes(res);
180       sg.setEndRes(res);
181       av.setSelectionGroup(sg);
182
183       if (evt.isShiftDown())
184       {
185         int min = Math.min(av.getColumnSelection().getMin(), res);
186         int max = Math.max(av.getColumnSelection().getMax(), res);
187         for (int i = min; i < max; i++)
188         {
189           av.getColumnSelection().addElement(i);
190         }
191         sg.setStartRes(min);
192         sg.setEndRes(max);
193       }
194     }
195
196     ap.paintAlignment(true);
197     av.sendSelection();
198   }
199
200   @Override
201   public void mouseReleased(MouseEvent evt)
202   {
203     mouseDragging = false;
204
205     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
206
207     if (res > av.getAlignment().getWidth())
208     {
209       res = av.getAlignment().getWidth() - 1;
210     }
211
212     if (av.hasHiddenColumns())
213     {
214       res = av.getColumnSelection().adjustForHiddenColumns(res);
215     }
216
217     if (!stretchingGroup)
218     {
219       ap.paintAlignment(false);
220
221       return;
222     }
223
224     SequenceGroup sg = av.getSelectionGroup();
225
226     if (res > sg.getStartRes())
227     {
228       sg.setEndRes(res);
229     }
230     else if (res < sg.getStartRes())
231     {
232       sg.setStartRes(res);
233     }
234
235     stretchingGroup = false;
236     ap.paintAlignment(false);
237     av.sendSelection();
238   }
239
240   @Override
241   public void mouseDragged(MouseEvent evt)
242   {
243     mouseDragging = true;
244
245     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
246     if (res < 0)
247     {
248       res = 0;
249     }
250
251     if (av.hasHiddenColumns())
252     {
253       res = av.getColumnSelection().adjustForHiddenColumns(res);
254     }
255
256     if (res > av.getAlignment().getWidth())
257     {
258       res = av.getAlignment().getWidth() - 1;
259     }
260
261     if (res < min)
262     {
263       min = res;
264     }
265
266     if (res > max)
267     {
268       max = res;
269     }
270
271     SequenceGroup sg = av.getSelectionGroup();
272
273     if (sg != null)
274     {
275       stretchingGroup = true;
276
277       if (!av.getColumnSelection().contains(res))
278       {
279         av.getColumnSelection().addElement(res);
280       }
281
282       if (res > sg.getStartRes())
283       {
284         sg.setEndRes(res);
285       }
286       if (res < sg.getStartRes())
287       {
288         sg.setStartRes(res);
289       }
290
291       int col;
292       for (int i = min; i <= max; i++)
293       {
294         col = av.getColumnSelection().adjustForHiddenColumns(i);
295
296         if ((col < sg.getStartRes()) || (col > sg.getEndRes()))
297         {
298           av.getColumnSelection().removeElement(col);
299         }
300         else
301         {
302           av.getColumnSelection().addElement(col);
303         }
304       }
305
306       ap.paintAlignment(false);
307     }
308   }
309
310   @Override
311   public void mouseEntered(MouseEvent evt)
312   {
313     if (mouseDragging)
314     {
315       ap.seqPanel.scrollCanvas(null);
316     }
317   }
318
319   @Override
320   public void mouseExited(MouseEvent evt)
321   {
322     if (mouseDragging)
323     {
324       ap.seqPanel.scrollCanvas(evt);
325     }
326   }
327
328   @Override
329   public void mouseClicked(MouseEvent evt)
330   {
331
332   }
333
334   @Override
335   public void mouseMoved(MouseEvent evt)
336   {
337     if (!av.hasHiddenColumns())
338     {
339       return;
340     }
341
342     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
343
344     res = av.getColumnSelection().adjustForHiddenColumns(res);
345
346     reveal = null;
347     for (int[] region : av.getColumnSelection().getHiddenColumns())
348     {
349       if (res + 1 == region[0] || res - 1 == region[1])
350       {
351         reveal = region;
352         break;
353       }
354     }
355
356     repaint();
357   }
358
359   @Override
360   public void update(Graphics g)
361   {
362     paint(g);
363   }
364
365   @Override
366   public void paint(Graphics g)
367   {
368     drawScale(g, av.getStartRes(), av.getEndRes(), getSize().width,
369             getSize().height);
370   }
371
372   // scalewidth will normally be screenwidth,
373   public void drawScale(Graphics gg, int startx, int endx, int width,
374           int height)
375   {
376     gg.setFont(av.getFont());
377     // Fill in the background
378     gg.setColor(Color.white);
379     gg.fillRect(0, 0, width, height);
380     gg.setColor(Color.black);
381
382     // Fill the selected columns
383     ColumnSelection cs = av.getColumnSelection();
384     gg.setColor(new Color(220, 0, 0));
385     int avcharWidth = av.getCharWidth(), avcharHeight = av.getCharHeight();
386     for (int sel : cs.getSelected())
387     {
388       if (av.hasHiddenColumns())
389       {
390         sel = av.getColumnSelection().findColumnPosition(sel);
391       }
392
393       if ((sel >= startx) && (sel <= endx))
394       {
395         gg.fillRect((sel - startx) * avcharWidth, 0, avcharWidth,
396                 getSize().height);
397       }
398     }
399
400     // Draw the scale numbers
401     gg.setColor(Color.black);
402
403     int scalestartx = (startx / 10) * 10;
404
405     FontMetrics fm = gg.getFontMetrics(av.getFont());
406     int y = avcharHeight - fm.getDescent();
407
408     if ((scalestartx % 10) == 0)
409     {
410       scalestartx += 5;
411     }
412
413     String string;
414     int maxX = 0;
415
416     for (int i = scalestartx; i < endx; i += 5)
417     {
418       if ((i % 10) == 0)
419       {
420         string = String.valueOf(av.getColumnSelection()
421                 .adjustForHiddenColumns(i));
422         if ((i - startx - 1) * avcharWidth > maxX)
423         {
424           gg.drawString(string, (i - startx - 1) * avcharWidth, y);
425           maxX = (i - startx + 1) * avcharWidth + fm.stringWidth(string);
426         }
427
428         gg.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
429                 y + 2,
430                 ((i - startx - 1) * avcharWidth) + (avcharWidth / 2), y
431                         + (fm.getDescent() * 2));
432
433       }
434       else
435       {
436         gg.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2), y
437                 + fm.getDescent(), ((i - startx - 1) * avcharWidth)
438                 + (avcharWidth / 2), y + (fm.getDescent() * 2));
439       }
440     }
441
442     if (av.hasHiddenColumns())
443     {
444       gg.setColor(Color.blue);
445       int res;
446       if (av.getShowHiddenMarkers())
447       {
448         for (int i = 0; i < av.getColumnSelection().getHiddenColumns()
449                 .size(); i++)
450         {
451
452           res = av.getColumnSelection().findHiddenRegionPosition(i)
453                   - startx;
454
455           if (res < 0 || res > endx - scalestartx)
456           {
457             continue;
458           }
459
460           gg.fillPolygon(new int[] { res * avcharWidth - avcharHeight / 4,
461               res * avcharWidth + avcharHeight / 4, res * avcharWidth },
462                   new int[] { y - avcharHeight / 2, y - avcharHeight / 2,
463                       y + 8 }, 3);
464
465         }
466       }
467
468       if (reveal != null && reveal[0] > startx && reveal[0] < endx)
469       {
470         gg.drawString(MessageManager.getString("label.reveal_columns"),
471                 reveal[0] * avcharWidth, 0);
472       }
473     }
474
475   }
476
477 }