JAL-2094 new classes ColorI, Colour added
[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       rightMouseButtonPressed(evt, res);
93     }
94     else
95     {
96       leftMouseButtonPressed(evt, res);
97     }
98   }
99
100   /**
101    * Handles left mouse button pressed (selection / clear selections)
102    * 
103    * @param evt
104    * @param res
105    */
106   protected void leftMouseButtonPressed(MouseEvent evt, final int res)
107   {
108     if (!evt.isControlDown() && !evt.isShiftDown())
109     {
110       av.getColumnSelection().clear();
111     }
112
113     av.getColumnSelection().addElement(res);
114     SequenceGroup sg = new SequenceGroup();
115     for (int i = 0; i < av.getAlignment().getSequences().size(); i++)
116     {
117       sg.addSequence(av.getAlignment().getSequenceAt(i), false);
118     }
119
120     sg.setStartRes(res);
121     sg.setEndRes(res);
122     av.setSelectionGroup(sg);
123
124     if (evt.isShiftDown())
125     {
126       int min = Math.min(av.getColumnSelection().getMin(), res);
127       int max = Math.max(av.getColumnSelection().getMax(), res);
128       for (int i = min; i < max; i++)
129       {
130         av.getColumnSelection().addElement(i);
131       }
132       sg.setStartRes(min);
133       sg.setEndRes(max);
134     }
135     ap.paintAlignment(true);
136     av.sendSelection();
137   }
138
139   /**
140    * Handles right mouse button press. If pressed in a selected column, opens
141    * context menu for 'Hide Columns'. If pressed on a hidden columns marker,
142    * opens context menu for 'Reveal / Reveal All'. Else does nothing.
143    * 
144    * @param evt
145    * @param res
146    */
147   protected void rightMouseButtonPressed(MouseEvent evt, final int res)
148   {
149     PopupMenu pop = new PopupMenu();
150     if (reveal != null)
151     {
152       MenuItem item = new MenuItem(
153               MessageManager.getString("label.reveal"));
154       item.addActionListener(new ActionListener()
155       {
156         @Override
157         public void actionPerformed(ActionEvent e)
158         {
159           av.showColumn(reveal[0]);
160           reveal = null;
161           ap.paintAlignment(true);
162           if (ap.overviewPanel != null)
163           {
164             ap.overviewPanel.updateOverviewImage();
165           }
166           av.sendSelection();
167         }
168       });
169       pop.add(item);
170
171       if (av.getColumnSelection().hasManyHiddenColumns())
172       {
173         item = new MenuItem(MessageManager.getString("action.reveal_all"));
174         item.addActionListener(new ActionListener()
175         {
176           @Override
177           public void actionPerformed(ActionEvent e)
178           {
179             av.showAllHiddenColumns();
180             reveal = null;
181             ap.paintAlignment(true);
182             if (ap.overviewPanel != null)
183             {
184               ap.overviewPanel.updateOverviewImage();
185             }
186             av.sendSelection();
187           }
188         });
189         pop.add(item);
190       }
191       this.add(pop);
192       pop.show(this, evt.getX(), evt.getY());
193     }
194     else if (av.getColumnSelection().contains(res))
195     {
196       MenuItem item = new MenuItem(
197               MessageManager.getString("label.hide_columns"));
198       item.addActionListener(new ActionListener()
199       {
200         @Override
201         public void actionPerformed(ActionEvent e)
202         {
203           av.hideColumns(res, res);
204           if (av.getSelectionGroup() != null
205                   && av.getSelectionGroup().getSize() == av
206                           .getAlignment().getHeight())
207           {
208             av.setSelectionGroup(null);
209           }
210
211           ap.paintAlignment(true);
212           if (ap.overviewPanel != null)
213           {
214             ap.overviewPanel.updateOverviewImage();
215           }
216           av.sendSelection();
217         }
218       });
219       pop.add(item);
220       this.add(pop);
221       pop.show(this, evt.getX(), evt.getY());
222     }
223   }
224
225   @Override
226   public void mouseReleased(MouseEvent evt)
227   {
228     mouseDragging = false;
229
230     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
231
232     if (res > av.getAlignment().getWidth())
233     {
234       res = av.getAlignment().getWidth() - 1;
235     }
236
237     if (av.hasHiddenColumns())
238     {
239       res = av.getColumnSelection().adjustForHiddenColumns(res);
240     }
241
242     if (!stretchingGroup)
243     {
244       ap.paintAlignment(false);
245
246       return;
247     }
248
249     SequenceGroup sg = av.getSelectionGroup();
250
251     if (res > sg.getStartRes())
252     {
253       sg.setEndRes(res);
254     }
255     else if (res < sg.getStartRes())
256     {
257       sg.setStartRes(res);
258     }
259
260     stretchingGroup = false;
261     ap.paintAlignment(false);
262     av.sendSelection();
263   }
264
265   @Override
266   public void mouseDragged(MouseEvent evt)
267   {
268     mouseDragging = true;
269
270     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
271     if (res < 0)
272     {
273       res = 0;
274     }
275
276     if (av.hasHiddenColumns())
277     {
278       res = av.getColumnSelection().adjustForHiddenColumns(res);
279     }
280
281     if (res > av.getAlignment().getWidth())
282     {
283       res = av.getAlignment().getWidth() - 1;
284     }
285
286     if (res < min)
287     {
288       min = res;
289     }
290
291     if (res > max)
292     {
293       max = res;
294     }
295
296     SequenceGroup sg = av.getSelectionGroup();
297
298     if (sg != null)
299     {
300       stretchingGroup = true;
301
302       if (!av.getColumnSelection().contains(res))
303       {
304         av.getColumnSelection().addElement(res);
305       }
306
307       if (res > sg.getStartRes())
308       {
309         sg.setEndRes(res);
310       }
311       if (res < sg.getStartRes())
312       {
313         sg.setStartRes(res);
314       }
315
316       int col;
317       for (int i = min; i <= max; i++)
318       {
319         col = av.getColumnSelection().adjustForHiddenColumns(i);
320
321         if ((col < sg.getStartRes()) || (col > sg.getEndRes()))
322         {
323           av.getColumnSelection().removeElement(col);
324         }
325         else
326         {
327           av.getColumnSelection().addElement(col);
328         }
329       }
330
331       ap.paintAlignment(false);
332     }
333   }
334
335   @Override
336   public void mouseEntered(MouseEvent evt)
337   {
338     if (mouseDragging)
339     {
340       ap.seqPanel.scrollCanvas(null);
341     }
342   }
343
344   @Override
345   public void mouseExited(MouseEvent evt)
346   {
347     if (mouseDragging)
348     {
349       ap.seqPanel.scrollCanvas(evt);
350     }
351   }
352
353   @Override
354   public void mouseClicked(MouseEvent evt)
355   {
356
357   }
358
359   @Override
360   public void mouseMoved(MouseEvent evt)
361   {
362     if (!av.hasHiddenColumns())
363     {
364       return;
365     }
366
367     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
368
369     res = av.getColumnSelection().adjustForHiddenColumns(res);
370
371     reveal = null;
372     for (int[] region : av.getColumnSelection().getHiddenColumns())
373     {
374       if (res + 1 == region[0] || res - 1 == region[1])
375       {
376         reveal = region;
377         break;
378       }
379     }
380
381     repaint();
382   }
383
384   @Override
385   public void update(Graphics g)
386   {
387     paint(g);
388   }
389
390   @Override
391   public void paint(Graphics g)
392   {
393     drawScale(g, av.getStartRes(), av.getEndRes(), getSize().width,
394             getSize().height);
395   }
396
397   // scalewidth will normally be screenwidth,
398   public void drawScale(Graphics gg, int startx, int endx, int width,
399           int height)
400   {
401     gg.setFont(av.getFont());
402     // Fill in the background
403     gg.setColor(Color.white);
404     gg.fillRect(0, 0, width, height);
405     gg.setColor(Color.black);
406
407     // Fill the selected columns
408     ColumnSelection cs = av.getColumnSelection();
409     gg.setColor(new Color(220, 0, 0));
410     int avcharWidth = av.getCharWidth(), avcharHeight = av.getCharHeight();
411     for (int sel : cs.getSelected())
412     {
413       // TODO: JAL-2001 - provide a fast method to list visible selected in a
414       // given range
415       if (av.hasHiddenColumns())
416       {
417         sel = av.getColumnSelection().findColumnPosition(sel);
418       }
419
420       if ((sel >= startx) && (sel <= endx))
421       {
422         gg.fillRect((sel - startx) * avcharWidth, 0, avcharWidth,
423                 getSize().height);
424       }
425     }
426
427     // Draw the scale numbers
428     gg.setColor(Color.black);
429
430     int scalestartx = (startx / 10) * 10;
431     int widthx = 1 + endx - startx;
432
433     FontMetrics fm = gg.getFontMetrics(av.getFont());
434     int y = avcharHeight - fm.getDescent();
435
436     if ((scalestartx % 10) == 0)
437     {
438       scalestartx += 5;
439     }
440
441     String string;
442     int maxX = 0;
443
444     for (int i = scalestartx; i < endx; i += 5)
445     {
446       if ((i % 10) == 0)
447       {
448         string = String.valueOf(av.getColumnSelection()
449                 .adjustForHiddenColumns(i));
450         if ((i - startx - 1) * avcharWidth > maxX)
451         {
452           gg.drawString(string, (i - startx - 1) * avcharWidth, y);
453           maxX = (i - startx + 1) * avcharWidth + fm.stringWidth(string);
454         }
455
456         gg.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2),
457                 y + 2,
458                 ((i - startx - 1) * avcharWidth) + (avcharWidth / 2), y
459                         + (fm.getDescent() * 2));
460
461       }
462       else
463       {
464         gg.drawLine(((i - startx - 1) * avcharWidth) + (avcharWidth / 2), y
465                 + fm.getDescent(), ((i - startx - 1) * avcharWidth)
466                 + (avcharWidth / 2), y + (fm.getDescent() * 2));
467       }
468     }
469
470     if (av.hasHiddenColumns())
471     {
472       gg.setColor(Color.blue);
473       int res;
474       if (av.getShowHiddenMarkers())
475       {
476         for (int i = 0; i < av.getColumnSelection().getHiddenColumns()
477                 .size(); i++)
478         {
479
480           res = av.getColumnSelection().findHiddenRegionPosition(i)
481                   - startx;
482
483           if (res < 0 || res > widthx)
484           {
485             continue;
486           }
487
488           gg.fillPolygon(new int[] { res * avcharWidth - avcharHeight / 4,
489               res * avcharWidth + avcharHeight / 4, res * avcharWidth },
490                   new int[] { y - avcharHeight / 2, y - avcharHeight / 2,
491                       y + 8 }, 3);
492
493         }
494       }
495
496       if (reveal != null && reveal[0] > startx && reveal[0] < endx)
497       {
498         gg.drawString(MessageManager.getString("label.reveal_columns"),
499                 reveal[0] * avcharWidth, 0);
500       }
501     }
502
503   }
504
505 }