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