bc5ec3dcb997cd42d72635f56906c32e1af70101
[jalview.git] / src / jalview / gui / ScalePanel.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1)
3  * Copyright (C) 2014 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.gui;
22
23 import jalview.datamodel.ColumnSelection;
24 import jalview.datamodel.SequenceGroup;
25 import jalview.datamodel.SequenceI;
26 import jalview.util.MessageManager;
27
28 import java.awt.Color;
29 import java.awt.FontMetrics;
30 import java.awt.Graphics;
31 import java.awt.Graphics2D;
32 import java.awt.RenderingHints;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.MouseEvent;
36 import java.awt.event.MouseListener;
37 import java.awt.event.MouseMotionListener;
38
39 import javax.swing.JMenuItem;
40 import javax.swing.JPanel;
41 import javax.swing.JPopupMenu;
42 import javax.swing.SwingUtilities;
43 import javax.swing.ToolTipManager;
44
45 /**
46  * DOCUMENT ME!
47  * 
48  * @author $author$
49  * @version $Revision$
50  */
51 public class ScalePanel extends JPanel implements MouseMotionListener,
52         MouseListener
53 {
54   protected int offy = 4;
55
56   /** DOCUMENT ME!! */
57   public int width;
58
59   protected AlignViewport av;
60
61   AlignmentPanel ap;
62
63   boolean stretchingGroup = false;
64
65   int min; // used by mouseDragged to see if user
66
67   int max; // used by mouseDragged to see if user
68
69   boolean mouseDragging = false;
70
71   // wants to delete columns
72   public ScalePanel(AlignViewport av, AlignmentPanel ap)
73   {
74     this.av = av;
75     this.ap = ap;
76
77     addMouseListener(this);
78     addMouseMotionListener(this);
79   }
80
81   /**
82    * DOCUMENT ME!
83    * 
84    * @param evt
85    *          DOCUMENT ME!
86    */
87   public void mousePressed(MouseEvent evt)
88   {
89     int x = (evt.getX() / av.getCharWidth()) + av.getStartRes();
90     final int res;
91
92     if (av.hasHiddenColumns())
93     {
94       x = av.getColumnSelection().adjustForHiddenColumns(x);
95     }
96
97     if (x >= av.getAlignment().getWidth())
98     {
99       res = av.getAlignment().getWidth() - 1;
100     }
101     else
102     {
103       res = x;
104     }
105
106     min = res;
107     max = res;
108
109     if (SwingUtilities.isRightMouseButton(evt))
110     {
111       JPopupMenu pop = new JPopupMenu();
112       if (reveal != null)
113       {
114         JMenuItem item = new JMenuItem(
115                 MessageManager.getString("label.reveal"));
116         item.addActionListener(new ActionListener()
117         {
118           public void actionPerformed(ActionEvent e)
119           {
120             av.showColumn(reveal[0]);
121             reveal = null;
122             ap.paintAlignment(true);
123             if (ap.overviewPanel != null)
124             {
125               ap.overviewPanel.updateOverviewImage();
126             }
127           }
128         });
129         pop.add(item);
130
131         if (av.getColumnSelection().hasHiddenColumns())
132         {
133           item = new JMenuItem(
134                   MessageManager.getString("action.reveal_all"));
135           item.addActionListener(new ActionListener()
136           {
137             public void actionPerformed(ActionEvent e)
138             {
139               av.showAllHiddenColumns();
140               reveal = null;
141               ap.paintAlignment(true);
142               if (ap.overviewPanel != null)
143               {
144                 ap.overviewPanel.updateOverviewImage();
145               }
146             }
147           });
148           pop.add(item);
149         }
150         pop.show(this, evt.getX(), evt.getY());
151       }
152       else if (av.getColumnSelection().contains(res))
153       {
154         JMenuItem item = new JMenuItem(
155                 MessageManager.getString("label.hide_columns"));
156         item.addActionListener(new ActionListener()
157         {
158           public void actionPerformed(ActionEvent e)
159           {
160             av.hideColumns(res, res);
161             if (av.getSelectionGroup() != null
162                     && av.getSelectionGroup().getSize() == av
163                             .getAlignment().getHeight())
164             {
165               av.setSelectionGroup(null);
166             }
167
168             ap.paintAlignment(true);
169             if (ap.overviewPanel != null)
170             {
171               ap.overviewPanel.updateOverviewImage();
172             }
173           }
174         });
175         pop.add(item);
176         pop.show(this, evt.getX(), evt.getY());
177       }
178     }
179     else
180     // LEFT MOUSE TO SELECT
181     {
182       if (!evt.isControlDown() && !evt.isShiftDown())
183       {
184         av.getColumnSelection().clear();
185       }
186
187       av.getColumnSelection().addElement(res);
188       SequenceGroup sg = new SequenceGroup();
189       // try to be as quick as possible
190       SequenceI[] iVec = av.getAlignment().getSequencesArray();
191       for (int i = 0; i < iVec.length; i++)
192       {
193         sg.addSequence(iVec[i], false);
194         iVec[i] = null;
195       }
196       iVec = null;
197       sg.setStartRes(res);
198       sg.setEndRes(res);
199
200       if (evt.isShiftDown())
201       {
202         int min = Math.min(av.getColumnSelection().getMin(), res);
203         int max = Math.max(av.getColumnSelection().getMax(), res);
204         for (int i = min; i < max; i++)
205         {
206           av.getColumnSelection().addElement(i);
207         }
208         sg.setStartRes(min);
209         sg.setEndRes(max);
210       }
211       av.setSelectionGroup(sg);
212     }
213
214     ap.paintAlignment(false);
215     av.sendSelection();
216   }
217
218   /**
219    * DOCUMENT ME!
220    * 
221    * @param evt
222    *          DOCUMENT ME!
223    */
224   public void mouseReleased(MouseEvent evt)
225   {
226     mouseDragging = false;
227
228     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
229
230     if (av.hasHiddenColumns())
231     {
232       res = av.getColumnSelection().adjustForHiddenColumns(res);
233     }
234
235     if (res >= av.getAlignment().getWidth())
236     {
237       res = av.getAlignment().getWidth() - 1;
238     }
239
240     if (!stretchingGroup)
241     {
242       ap.paintAlignment(false);
243
244       return;
245     }
246
247     SequenceGroup sg = av.getSelectionGroup();
248
249     if (sg != null)
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   /**
266    * DOCUMENT ME!
267    * 
268    * @param evt
269    *          DOCUMENT ME!
270    */
271   public void mouseDragged(MouseEvent evt)
272   {
273     mouseDragging = true;
274
275     int res = (evt.getX() / av.getCharWidth()) + av.getStartRes();
276     if (res < 0)
277     {
278       res = 0;
279     }
280
281     if (av.hasHiddenColumns())
282     {
283       res = av.getColumnSelection().adjustForHiddenColumns(res);
284     }
285
286     if (res >= av.getAlignment().getWidth())
287     {
288       res = av.getAlignment().getWidth() - 1;
289     }
290
291     if (res < min)
292     {
293       min = res;
294     }
295
296     if (res > max)
297     {
298       max = res;
299     }
300
301     SequenceGroup sg = av.getSelectionGroup();
302
303     if (sg != null)
304     {
305       stretchingGroup = true;
306
307       if (!av.getColumnSelection().contains(res))
308       {
309         av.getColumnSelection().addElement(res);
310       }
311
312       if (res > sg.getStartRes())
313       {
314         sg.setEndRes(res);
315       }
316       if (res < sg.getStartRes())
317       {
318         sg.setStartRes(res);
319       }
320
321       int col;
322       for (int i = min; i <= max; i++)
323       {
324         col = i; // av.getColumnSelection().adjustForHiddenColumns(i);
325
326         if ((col < sg.getStartRes()) || (col > sg.getEndRes()))
327         {
328           av.getColumnSelection().removeElement(col);
329         }
330         else
331         {
332           av.getColumnSelection().addElement(col);
333         }
334       }
335
336       ap.paintAlignment(false);
337     }
338   }
339
340   public void mouseEntered(MouseEvent evt)
341   {
342     if (mouseDragging)
343     {
344       ap.getSeqPanel().scrollCanvas(null);
345     }
346   }
347
348   public void mouseExited(MouseEvent evt)
349   {
350     if (mouseDragging)
351     {
352       ap.getSeqPanel().scrollCanvas(evt);
353     }
354   }
355
356   public void mouseClicked(MouseEvent evt)
357   {
358   }
359
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     if (av.getColumnSelection().getHiddenColumns() != null)
373     {
374       for (int[] region : av.getColumnSelection().getHiddenColumns())
375       {
376         if (res + 1 == region[0] || res - 1 == region[1])
377         {
378           reveal = region;
379           ToolTipManager.sharedInstance().registerComponent(this);
380           this.setToolTipText(MessageManager
381                   .getString("label.reveal_hidden_columns"));
382           break;
383         }
384         else
385         {
386           this.setToolTipText(null);
387         }
388       }
389     }
390     repaint();
391   }
392
393   int[] reveal;
394
395   /**
396    * DOCUMENT ME!
397    * 
398    * @param g
399    *          DOCUMENT ME!
400    */
401   public void paintComponent(Graphics g)
402   {
403     drawScale(g, av.getStartRes(), av.getEndRes(), getWidth(), getHeight());
404   }
405
406   // scalewidth will normally be screenwidth,
407   public void drawScale(Graphics g, int startx, int endx, int width,
408           int height)
409   {
410     Graphics2D gg = (Graphics2D) g;
411     gg.setFont(av.getFont());
412
413     if (av.antiAlias)
414     {
415       gg.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
416               RenderingHints.VALUE_ANTIALIAS_ON);
417     }
418
419     // Fill in the background
420     gg.setColor(Color.white);
421     gg.fillRect(0, 0, width, height);
422     gg.setColor(Color.black);
423
424     // Fill the selected columns
425     ColumnSelection cs = av.getColumnSelection();
426     int s;
427     if (cs != null)
428     {
429       gg.setColor(new Color(220, 0, 0));
430
431       for (int i = 0; i < cs.size(); i++)
432       {
433         int sel = cs.columnAt(i);
434         if (av.hasHiddenColumns())
435         {
436           if (cs.isVisible(sel))
437           {
438             sel = cs.findColumnPosition(sel);
439           }
440           else
441           {
442             continue;
443           }
444         }
445
446         if ((sel >= startx) && (sel <= endx))
447         {
448           gg.fillRect((sel - startx) * av.charWidth, 0, av.charWidth,
449                   getHeight());
450         }
451       }
452     }
453     // Draw the scale numbers
454     gg.setColor(Color.black);
455
456     int scalestartx = (startx / 10) * 10;
457
458     FontMetrics fm = gg.getFontMetrics(av.getFont());
459     int y = av.charHeight - fm.getDescent();
460
461     if ((scalestartx % 10) == 0)
462     {
463       scalestartx += 5;
464     }
465
466     String string;
467     int maxX = 0;
468
469     for (int i = scalestartx; i < endx; i += 5)
470     {
471       if ((i % 10) == 0)
472       {
473         string = String.valueOf(av.getColumnSelection()
474                 .adjustForHiddenColumns(i));
475         if ((i - startx - 1) * av.charWidth > maxX)
476         {
477           gg.drawString(string, (i - startx - 1) * av.charWidth, y);
478           maxX = (i - startx + 1) * av.charWidth + fm.stringWidth(string);
479         }
480
481         gg.drawLine(
482                 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
483                 y + 2,
484                 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
485                 y + (fm.getDescent() * 2));
486
487       }
488       else
489       {
490         gg.drawLine(
491                 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
492                 y + fm.getDescent(),
493                 ((i - startx - 1) * av.charWidth) + (av.charWidth / 2),
494                 y + (fm.getDescent() * 2));
495       }
496     }
497
498     if (av.hasHiddenColumns())
499     {
500       gg.setColor(Color.blue);
501       int res;
502       if (av.getShowHiddenMarkers()
503               && av.getColumnSelection().getHiddenColumns() != null)
504       {
505         for (int i = 0; i < av.getColumnSelection().getHiddenColumns()
506                 .size(); i++)
507         {
508
509           res = av.getColumnSelection().findHiddenRegionPosition(i)
510                   - startx;
511
512           if (res < 0 || res > endx - scalestartx)
513           {
514             continue;
515           }
516
517           gg.fillPolygon(new int[]
518           { res * av.charWidth - av.charHeight / 4,
519               res * av.charWidth + av.charHeight / 4, res * av.charWidth },
520                   new int[]
521                   { y - av.charHeight / 2, y - av.charHeight / 2, y + 8 },
522                   3);
523
524         }
525       }
526
527       if (reveal != null && reveal[0] > startx && reveal[0] < endx)
528       {
529         gg.drawString(MessageManager.getString("label.reveal_columns"),
530                 reveal[0] * av.charWidth, 0);
531       }
532     }
533
534   }
535 }