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