jog pixels for OSX
[jalview.git] / src / jalview / gui / FeatureColourChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 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.util.*;
22
23 import java.awt.*;
24 import java.awt.event.*;
25 import javax.swing.*;
26 import javax.swing.border.LineBorder;
27 import javax.swing.event.*;
28
29 import jalview.datamodel.*;
30 import jalview.schemes.*;
31 import java.awt.Dimension;
32
33 public class FeatureColourChooser extends JPanel 
34 {
35   JDialog frame;
36   
37
38 //  FeatureSettings fs;
39   FeatureRenderer fr;
40   
41   
42   private GraduatedColor cs;
43   private Object oldcs;
44   /**
45    * 
46    * @return the last colour setting selected by user - either oldcs (which may be a java.awt.Color) or the new GraduatedColor
47    */
48   public Object getLastColour() {
49     if (cs==null)
50     {
51       return oldcs;
52     }
53     return cs;
54   }
55   Hashtable oldgroupColours;
56   
57   AlignmentPanel ap;
58   
59
60   boolean adjusting = false;
61
62   private float min;
63
64   private float max;
65   String type = null;
66   public FeatureColourChooser(FeatureRenderer frender, String type)
67   {
68     this(frender,false,type);
69   }
70     public FeatureColourChooser(FeatureRenderer frender, boolean block, String type)
71     {
72     this.fr = frender;
73     this.type = type;
74     ap = fr.ap;
75     frame = new JDialog(Desktop.instance,true);
76     frame.setTitle("Graduated Feature Colour for "+type);
77     Rectangle deskr = Desktop.instance.getBounds();
78     frame.setBounds(new Rectangle((int) (deskr.getCenterX()-240),(int) (deskr.getCenterY()-92),480,185));
79     frame.setContentPane(this);
80     //frame.setLayer(JLayeredPane.PALETTE_LAYER);
81     //Desktop.addInternalFrame(frame, "Graduated Feature Colour for "+type, 480, 145);
82
83     slider.addChangeListener(new ChangeListener()
84     {
85       public void stateChanged(ChangeEvent evt)
86       {
87         if (!adjusting)
88         {
89           thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
90           valueChanged();
91         }
92       }
93     });
94     slider.addMouseListener(new MouseAdapter()
95     {
96       public void mouseReleased(MouseEvent evt)
97       {
98         if (ap!=null) { ap.paintAlignment(true); };
99       }
100     });
101
102     float mm[] = ((float[][]) fr.minmax.get(type))[0];
103     min = mm[0];
104     max = mm[1];
105     oldcs = fr.featureColours.get(type);
106     if (oldcs instanceof GraduatedColor)
107     {
108       if (((GraduatedColor)oldcs).isAutoScale())
109       {
110         // update the scale
111         cs = new GraduatedColor((GraduatedColor) oldcs, min, max);
112       } else {
113         cs = new GraduatedColor((GraduatedColor) oldcs);
114       }
115     } else {
116       // promote original color to a graduated color
117       Color bl = Color.black;
118       if (oldcs instanceof Color)
119       {
120         bl = (Color) oldcs;
121       }
122       // original colour becomes the maximum colour
123       cs = new GraduatedColor(Color.white,bl,mm[0],mm[1]);
124       cs.setColourByLabel(false);
125     }
126     minColour.setBackground(oldminColour=cs.getMinColor());
127     maxColour.setBackground(oldmaxColour=cs.getMaxColor());
128     adjusting = true;
129     
130     try
131     {
132       jbInit();
133     } catch (Exception ex)
134     {
135     }
136     // update the gui from threshold state
137     thresholdIsMin.setSelected(!cs.isAutoScale());
138     colourByLabel.setSelected(cs.isColourByLabel());
139     if (cs.getThreshType()!=AnnotationColourGradient.NO_THRESHOLD)
140     {
141       // initialise threshold slider and selector
142       threshold.setSelectedIndex(cs.getThreshType()==AnnotationColourGradient.ABOVE_THRESHOLD ? 1 : 2);
143             slider.setEnabled(true);
144       thresholdValue.setEnabled(true);
145       threshline = new jalview.datamodel.GraphLine(
146                         (max - min) / 2f,
147                         "Threshold", Color.black);
148       
149     } 
150
151     adjusting = false;
152
153     changeColour();
154     if (!block)
155     {
156       new Thread(new Runnable() {
157
158       public void run()
159       {
160         frame.show();
161       }
162       
163     }).start();
164     } else {
165       frame.show();
166     }
167   }
168
169   public FeatureColourChooser()
170   {
171     try
172     {
173       jbInit();
174     } catch (Exception ex)
175     {
176       ex.printStackTrace();
177     }
178   }
179
180   private void jbInit() throws Exception
181   {
182     
183     minColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
184     minColour.setBorder(BorderFactory.createLineBorder(Color.black));
185     minColour.setPreferredSize(new Dimension(40, 20));
186     minColour.setToolTipText("Minimum Colour");
187     minColour.addMouseListener(new MouseAdapter()
188     {
189       public void mousePressed(MouseEvent e)
190       {
191         if (minColour.isEnabled())
192         {
193           minColour_actionPerformed();
194         }
195       }
196     });
197     maxColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
198     maxColour.setBorder(BorderFactory.createLineBorder(Color.black));
199     maxColour.setPreferredSize(new Dimension(40, 20));
200     maxColour.setToolTipText("Maximum Colour");
201     maxColour.addMouseListener(new MouseAdapter()
202     {
203       public void mousePressed(MouseEvent e)
204       {
205         if (maxColour.isEnabled())
206         {
207           maxColour_actionPerformed();
208         }
209       }
210     });
211     maxColour.setBorder(new LineBorder(Color.black));
212     minText.setText("Min:");
213     minText.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
214     maxText.setText("Max:");
215     maxText.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
216     ok.setOpaque(false);
217     ok.setText("OK");
218     ok.addActionListener(new ActionListener()
219     {
220       public void actionPerformed(ActionEvent e)
221       {
222         ok_actionPerformed(e);
223       }
224     });
225     cancel.setOpaque(false);
226     cancel.setText("Cancel");
227     cancel.addActionListener(new ActionListener()
228     {
229       public void actionPerformed(ActionEvent e)
230       {
231         cancel_actionPerformed(e);
232       }
233     });
234     this.setLayout(borderLayout1);
235     jPanel2.setLayout(flowLayout1);
236     jPanel1.setBackground(Color.white);
237     jPanel2.setBackground(Color.white);
238     threshold.addActionListener(new ActionListener()
239     {
240       public void actionPerformed(ActionEvent e)
241       {
242         threshold_actionPerformed(e);
243       }
244     });
245     threshold.setToolTipText("Threshold the feature display by score.");
246     threshold.addItem("No Threshold"); // index 0
247     threshold.addItem("Above Threshold"); // index 1
248     threshold.addItem("Below Threshold"); // index 2
249     jPanel3.setLayout(flowLayout2);
250     thresholdValue.addActionListener(new ActionListener()
251     {
252       public void actionPerformed(ActionEvent e)
253       {
254         thresholdValue_actionPerformed(e);
255       }
256     });
257     slider.setPaintLabels(false);
258     slider.setPaintTicks(true);
259     slider.setBackground(Color.white);
260     slider.setEnabled(false);
261     slider.setOpaque(false);
262     slider.setPreferredSize(new Dimension(100, 32));
263     slider.setToolTipText("Adjust threshold");
264     thresholdValue.setEnabled(false);
265     thresholdValue.setColumns(7);
266     jPanel3.setBackground(Color.white);
267     thresholdIsMin.setBackground(Color.white);
268     thresholdIsMin.setText("Threshold is Min/Max");
269     thresholdIsMin.setToolTipText("Toggle between absolute and relative display threshold.");
270     thresholdIsMin.addActionListener(new ActionListener()
271     {
272       public void actionPerformed(ActionEvent actionEvent)
273       {
274         thresholdIsMin_actionPerformed(actionEvent);
275       }
276     });
277     colourByLabel.setBackground(Color.white);
278     colourByLabel.setText("Colour by Label");
279     colourByLabel.setToolTipText("Display features of the same type with a different label using a different colour. (e.g. domain features)");
280     colourByLabel.addActionListener(new ActionListener()
281     {
282       public void actionPerformed(ActionEvent actionEvent)
283       {
284         colourByLabel_actionPerformed(actionEvent);
285       }
286     });
287     colourPanel.setBackground(Color.white);
288     jPanel1.add(ok);
289     jPanel1.add(cancel);
290     jPanel2.add(colourByLabel,java.awt.BorderLayout.WEST);
291     jPanel2.add(colourPanel,java.awt.BorderLayout.EAST);
292     colourPanel.add(minText);
293     colourPanel.add(minColour);
294     colourPanel.add(maxText);
295     colourPanel.add(maxColour);
296     this.add(jPanel3, java.awt.BorderLayout.CENTER);
297     jPanel3.add(threshold);
298     jPanel3.add(slider);
299     jPanel3.add(thresholdValue);
300     jPanel3.add(thresholdIsMin);
301     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
302     this.add(jPanel2, java.awt.BorderLayout.NORTH);
303   }
304
305
306   JLabel minText = new JLabel();
307   JLabel maxText = new JLabel();
308   JPanel minColour = new JPanel();
309
310   JPanel maxColour = new JPanel();
311
312   JButton ok = new JButton();
313
314   JButton cancel = new JButton();
315   JPanel colourPanel = new JPanel();
316   JPanel jPanel1 = new JPanel();
317
318   JPanel jPanel2 = new JPanel();
319
320   BorderLayout borderLayout1 = new BorderLayout();
321
322   JComboBox threshold = new JComboBox();
323
324   FlowLayout flowLayout1 = new FlowLayout();
325
326   JPanel jPanel3 = new JPanel();
327
328   FlowLayout flowLayout2 = new FlowLayout();
329
330   JSlider slider = new JSlider();
331
332   JTextField thresholdValue = new JTextField(20);
333   // TODO implement GUI for tolower flag
334   // JCheckBox toLower = new JCheckBox();
335
336   JCheckBox thresholdIsMin = new JCheckBox();
337   JCheckBox colourByLabel = new JCheckBox();
338
339   private GraphLine threshline;
340
341
342   private Color oldmaxColour;
343
344
345   private Color oldminColour;
346
347   public void minColour_actionPerformed()
348   {
349     Color col = JColorChooser.showDialog(this,
350             "Select Colour for Minimum Value", minColour.getBackground());
351     if (col != null)
352     {
353       minColour.setBackground(col);
354       minColour.setForeground(col);
355     }
356     minColour.repaint();
357     changeColour();
358   }
359
360   public void maxColour_actionPerformed()
361   {
362     Color col = JColorChooser.showDialog(this,
363             "Select Colour for Maximum Value", maxColour.getBackground());
364     if (col != null)
365     {
366       maxColour.setBackground(col);
367       maxColour.setForeground(col);
368     }
369     maxColour.repaint();
370     changeColour();
371   }
372
373   void changeColour()
374   {
375     // Check if combobox is still adjusting
376     if (adjusting)
377     {
378       return;
379     }
380
381
382     int aboveThreshold = AnnotationColourGradient.NO_THRESHOLD;
383     if (threshold.getSelectedItem().equals("Above Threshold"))
384     {
385       aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
386     }
387     else if (threshold.getSelectedItem().equals("Below Threshold"))
388     {
389       aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
390     } 
391
392     slider.setEnabled(true);
393     thresholdValue.setEnabled(true);
394     
395     GraduatedColor acg;
396     if (cs.isColourByLabel())
397       {
398         acg = new GraduatedColor(oldminColour, oldmaxColour, min, max);
399       } else {
400         acg = new GraduatedColor(oldminColour=minColour.getBackground(), oldmaxColour=maxColour.getBackground(), min, max);
401         
402       }
403
404     if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
405     {
406       slider.setEnabled(false);
407       thresholdValue.setEnabled(false);
408       thresholdValue.setText("");
409       thresholdIsMin.setEnabled(false);
410     }
411     else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
412             && threshline == null)
413     {
414       // todo visual indication of feature threshold
415       threshline = new jalview.datamodel.GraphLine(
416                       (max - min) / 2f,
417                       "Threshold", Color.black);
418     }
419
420     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
421     {
422       adjusting = true;
423       acg.setThresh(threshline.value);
424
425       float range = max * 1000f
426               - min * 1000f;
427
428       slider.setMinimum((int) (min * 1000));
429       slider.setMaximum((int) (max * 1000));
430       slider.setValue((int) (threshline.value * 1000));
431       thresholdValue.setText(threshline.value + "");
432       slider.setMajorTickSpacing((int) (range / 10f));
433       slider.setEnabled(true);
434       thresholdValue.setEnabled(true);
435       thresholdIsMin.setEnabled(!colourByLabel.isSelected());
436       adjusting = false;
437     }
438
439     acg.setThreshType(aboveThreshold);
440     if (thresholdIsMin.isSelected() && aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
441     {
442       acg.setAutoScaled(false);
443       if (aboveThreshold==AnnotationColourGradient.ABOVE_THRESHOLD)
444       { 
445         acg = new GraduatedColor(acg, threshline.value, max);
446       } else { 
447         acg = new GraduatedColor(acg, min,threshline.value);
448       }
449     } else {
450       acg.setAutoScaled(true);
451     }
452     acg.setColourByLabel(colourByLabel.isSelected());
453     if (acg.isColourByLabel())
454     {
455       maxColour.setEnabled(false);
456       minColour.setEnabled(false);
457       maxColour.setBackground(this.getBackground());
458       maxColour.setForeground(this.getBackground());
459       minColour.setBackground(this.getBackground());
460       minColour.setForeground(this.getBackground());
461       
462     } else {
463       maxColour.setEnabled(true);
464       minColour.setEnabled(true);
465       maxColour.setBackground(oldmaxColour);
466       minColour.setBackground(oldminColour);
467       maxColour.setForeground(oldmaxColour);
468       minColour.setForeground(oldminColour);
469     }
470     fr.featureColours.put(type,acg);
471     cs = acg;
472     ap.paintAlignment(false);
473   }
474   private void raiseClosed() {
475     if (this.colourEditor!=null)
476     {
477       colourEditor.actionPerformed(new ActionEvent(this, 0, "CLOSED"));
478     }
479   }
480   public void ok_actionPerformed(ActionEvent e)
481   {
482     changeColour();
483     try
484     {
485       frame.dispose();
486       raiseClosed();
487     } catch (Exception ex)
488     {
489     }
490   }
491
492   public void cancel_actionPerformed(ActionEvent e)
493   {
494     reset();
495     try
496     {
497       frame.dispose();
498 //      frame.setClosed(true);
499       raiseClosed();
500     } catch (Exception ex)
501     {
502     }
503   }
504
505   void reset()
506   {
507     fr.featureColours.put(type, oldcs);
508     ap.paintAlignment(false);
509     cs = null;
510   }
511
512   public void thresholdCheck_actionPerformed(ActionEvent e)
513   {
514     changeColour();
515   }
516
517   public void annotations_actionPerformed(ActionEvent e)
518   {
519     changeColour();
520   }
521
522   public void threshold_actionPerformed(ActionEvent e)
523   {
524     changeColour();
525   }
526
527   public void thresholdValue_actionPerformed(ActionEvent e)
528   {
529     try
530     {
531       float f = Float.parseFloat(thresholdValue.getText());
532       slider.setValue((int) (f * 1000));
533       threshline.value = f;
534     } catch (NumberFormatException ex)
535     {
536     }
537   }
538
539   public void valueChanged()
540   {
541     threshline.value = (float) slider.getValue() / 1000f;
542     cs.setThresh(threshline.value);
543     changeColour();
544     ap.paintAlignment(false);
545   }
546
547   public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
548   {
549     changeColour();
550   }
551   public void colourByLabel_actionPerformed(ActionEvent actionEvent)
552   {
553     changeColour();
554   }
555   ActionListener colourEditor=null;
556   public void addActionListener(ActionListener graduatedColorEditor)
557   {
558     if (colourEditor!=null)
559     {
560       System.err.println("IMPLEMENTATION ISSUE: overwriting action listener for FeatureColourChooser");
561     }
562     colourEditor = graduatedColorEditor;
563   }
564
565 }