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