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