9456986e141d6a4ba6f7a99c042a817c70eb224c
[jalview.git] / src / jalview / appletgui / AnnotationColourChooser.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.appletgui;
22
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.SequenceGroup;
25 import jalview.schemes.AnnotationColourGradient;
26 import jalview.schemes.ColourSchemeI;
27 import jalview.util.MessageManager;
28
29 import java.awt.BorderLayout;
30 import java.awt.Button;
31 import java.awt.Checkbox;
32 import java.awt.Choice;
33 import java.awt.Color;
34 import java.awt.Dimension;
35 import java.awt.FlowLayout;
36 import java.awt.Font;
37 import java.awt.Frame;
38 import java.awt.Panel;
39 import java.awt.Scrollbar;
40 import java.awt.TextField;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.awt.event.AdjustmentEvent;
44 import java.awt.event.AdjustmentListener;
45 import java.awt.event.ItemEvent;
46 import java.awt.event.ItemListener;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.MouseListener;
49 import java.util.HashMap;
50 import java.util.Map;
51 import java.util.Vector;
52
53 public class AnnotationColourChooser extends Panel implements
54         ActionListener, AdjustmentListener, ItemListener, MouseListener
55 {
56   Frame frame;
57
58   AlignViewport av;
59
60   AlignmentPanel ap;
61
62   ColourSchemeI oldcs;
63
64   Map<SequenceGroup, ColourSchemeI> oldgroupColours;
65
66   /*
67    * map from annotation to its menu item display label
68    * - so we know which item to pre-select on restore
69    */
70   private Map<AlignmentAnnotation, String> annotationLabels;
71
72   AlignmentAnnotation currentAnnotation;
73
74   boolean adjusting = false;
75
76   public AnnotationColourChooser(AlignViewport av, AlignmentPanel ap)
77   {
78     try
79     {
80       jbInit();
81     } catch (Exception ex)
82     {
83     }
84
85     oldcs = av.getGlobalColourScheme();
86     if (av.getAlignment().getGroups() != null)
87     {
88       oldgroupColours = new HashMap<>();
89       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
90       {
91         oldgroupColours.put(sg, sg.getColourScheme());
92       }
93     }
94     this.av = av;
95     this.ap = ap;
96
97     slider.addAdjustmentListener(this);
98     slider.addMouseListener(this);
99
100     AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
101     if (anns == null)
102     {
103       return;
104     }
105
106     setDefaultMinMax();
107
108     adjusting = true;
109     if (oldcs instanceof AnnotationColourGradient)
110     {
111       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
112       currentColours.setState(
113               acg.isPredefinedColours() || acg.getBaseColour() != null);
114       if (!acg.isPredefinedColours() && acg.getBaseColour() == null)
115       {
116         minColour.setBackground(acg.getMinColour());
117         maxColour.setBackground(acg.getMaxColour());
118       }
119       // seqAssociated.setState(acg.isSeqAssociated());
120     }
121
122     Vector<String> list = getAnnotationItems();
123
124     for (int i = 0; i < list.size(); i++)
125     {
126       annotations.addItem(list.elementAt(i).toString());
127     }
128
129     threshold.addItem(MessageManager
130             .getString("label.threshold_feature_no_threshold"));
131     threshold.addItem(MessageManager
132             .getString("label.threshold_feature_above_threshold"));
133     threshold.addItem(MessageManager
134             .getString("label.threshold_feature_below_threshold"));
135
136     if (oldcs instanceof AnnotationColourGradient)
137     {
138       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
139       String label = annotationLabels.get(acg.getAnnotation());
140       annotations.select(label);
141       switch (acg.getAboveThreshold())
142       {
143       case AnnotationColourGradient.NO_THRESHOLD:
144         threshold.select(0);
145         break;
146       case AnnotationColourGradient.ABOVE_THRESHOLD:
147         threshold.select(1);
148         break;
149       case AnnotationColourGradient.BELOW_THRESHOLD:
150         threshold.select(1);
151         break;
152       default:
153         throw new Error(MessageManager.getString(
154                 "error.implementation_error_dont_know_threshold_annotationcolourgradient"));
155       }
156       thresholdIsMin.setState(acg.isThresholdIsMinMax());
157       thresholdValue.setText("" + acg.getAnnotationThreshold());
158     }
159
160     adjusting = false;
161
162     changeColour();
163
164     frame = new Frame();
165     frame.add(this);
166     jalview.bin.JalviewLite.addFrame(frame,
167             MessageManager.getString("label.colour_by_annotation"), 560,
168             175);
169     validate();
170   }
171
172   /**
173    * Builds and returns a list of menu items (display text) for choice of
174    * annotation. Also builds a map between annotations and their display labels.
175    * 
176    * @return
177    */
178   protected Vector<String> getAnnotationItems()
179   {
180     // TODO remove duplication with gui.AnnotationRowFilter
181     // TODO add 'per sequence only' option / parameter
182
183     annotationLabels = new HashMap<>();
184     Vector<String> list = new Vector<>();
185     AlignmentAnnotation[] anns = av.getAlignment().getAlignmentAnnotation();
186     if (anns == null)
187     {
188       return list;
189     }
190     int index = 1;
191     for (int i = 0; i < anns.length; i++)
192     {
193       String label = anns[i].label;
194       if (anns[i].sequenceRef != null)
195       {
196         /*
197          * be helpful and include sequence id in label for
198          * sequence-associated annotation (JAL-2236)
199          */
200         label = label + "_" + anns[i].sequenceRef.getName();
201       }
202       if (!list.contains(label))
203       {
204         list.addElement(label);
205         annotationLabels.put(anns[i], label);
206       }
207       else
208       {
209         label = label + "_" + (index++);
210         list.addElement(label);
211         annotationLabels.put(anns[i], label);
212       }
213     }
214     return list;
215   }
216
217   private void setDefaultMinMax()
218   {
219     minColour.setBackground(av.applet.getDefaultColourParameter(
220             "ANNOTATIONCOLOUR_MIN", Color.orange));
221     maxColour.setBackground(av.applet
222             .getDefaultColourParameter("ANNOTATIONCOLOUR_MAX", Color.red));
223
224   }
225
226   public AnnotationColourChooser()
227   {
228     try
229     {
230       jbInit();
231     } catch (Exception ex)
232     {
233       ex.printStackTrace();
234     }
235   }
236
237   private void jbInit() throws Exception
238   {
239     minColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
240     minColour.setLabel(MessageManager.getString("label.min_colour"));
241     minColour.addActionListener(this);
242
243     maxColour.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
244     maxColour.setLabel(MessageManager.getString("label.max_colour"));
245     maxColour.addActionListener(this);
246
247     thresholdIsMin.addItemListener(this);
248     ok.setLabel(MessageManager.getString("action.ok"));
249     ok.addActionListener(this);
250
251     cancel.setLabel(MessageManager.getString("action.cancel"));
252     cancel.addActionListener(this);
253
254     defColours.setLabel(MessageManager.getString("action.set_defaults"));
255     defColours.addActionListener(this);
256
257     annotations.addItemListener(this);
258
259     thresholdValue.addActionListener(this);
260     slider.setBackground(Color.white);
261     slider.setPreferredSize(new Dimension(193, 21));
262     slider.setEnabled(false);
263     thresholdValue.setPreferredSize(new Dimension(79, 22));
264     thresholdValue.setEnabled(false);
265     thresholdValue.setColumns(5);
266     currentColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
267     currentColours.setLabel(
268             MessageManager.getString("label.use_original_colours"));
269     currentColours.addItemListener(this);
270
271     thresholdIsMin.setBackground(Color.white);
272     thresholdIsMin
273             .setLabel(MessageManager.getString("label.threshold_minmax"));
274
275     this.setLayout(borderLayout1);
276
277     jPanel1.setBackground(Color.white);
278
279     jPanel2.setLayout(new FlowLayout());
280     jPanel2.setBackground(Color.white);
281     threshold.addItemListener(this);
282     jPanel3.setLayout(new FlowLayout());
283     jPanel3.setBackground(Color.white);
284     Panel jPanel4 = new Panel();
285     jPanel4.setLayout(new BorderLayout());
286     jPanel4.setBackground(Color.white);
287
288     jPanel1.add(ok);
289     jPanel1.add(cancel);
290
291     jPanel2.add(annotations);
292     jPanel2.add(currentColours);
293     jPanel2.add(minColour);
294     jPanel2.add(maxColour);
295
296     jPanel4.add(thresholdIsMin, BorderLayout.WEST);
297     jPanel4.add(slider, BorderLayout.CENTER);
298     jPanel4.add(thresholdValue, BorderLayout.EAST);
299
300     Panel jPanel34 = new Panel();
301     jPanel34.setLayout(new BorderLayout());
302     jPanel34.setBackground(Color.white);
303     jPanel34.add(jPanel2, BorderLayout.NORTH);
304     jPanel34.add(threshold, BorderLayout.WEST);
305     jPanel3.add(defColours);
306     jPanel34.add(jPanel3, BorderLayout.EAST);
307     jPanel34.add(jPanel4, BorderLayout.SOUTH);
308
309     this.add(jPanel34, java.awt.BorderLayout.CENTER);
310     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
311
312   }
313
314   Choice annotations = new Choice();
315
316   Button minColour = new Button();
317
318   Button maxColour = new Button();
319
320   Button ok = new Button();
321
322   Button cancel = new Button();
323
324   Button defColours = new Button();
325
326   Panel jPanel1 = new Panel();
327
328   Panel jPanel2 = new Panel();
329
330   Choice threshold = new Choice();
331
332   FlowLayout flowLayout1 = new FlowLayout();
333
334   Panel jPanel3 = new Panel();
335
336   Scrollbar slider = new Scrollbar(Scrollbar.HORIZONTAL);
337
338   TextField thresholdValue = new TextField(20);
339
340   Checkbox currentColours = new Checkbox();
341
342   BorderLayout borderLayout1 = new BorderLayout();
343
344   Checkbox thresholdIsMin = new Checkbox();
345
346   @Override
347   public void actionPerformed(ActionEvent evt)
348   {
349     if (evt.getSource() == thresholdValue)
350     {
351       try
352       {
353         float f = Float.valueOf(thresholdValue.getText()).floatValue();
354         slider.setValue((int) (f * 1000));
355         adjustmentValueChanged(null);
356       } catch (NumberFormatException ex)
357       {
358       }
359     }
360     else if (evt.getSource() == minColour)
361     {
362       minColour_actionPerformed(null);
363     }
364     else if (evt.getSource() == maxColour)
365     {
366       maxColour_actionPerformed(null);
367     }
368     else if (evt.getSource() == defColours)
369     {
370       defColour_actionPerformed();
371     }
372     else if (evt.getSource() == ok)
373     {
374       frame.setVisible(false);
375     }
376     else if (evt.getSource() == cancel)
377     {
378       reset();
379       ap.paintAlignment(true, true);
380       frame.setVisible(false);
381     }
382
383     else
384     {
385       changeColour();
386     }
387   }
388
389   @Override
390   public void itemStateChanged(ItemEvent evt)
391   {
392     if (evt.getSource() == currentColours)
393     {
394       if (currentColours.getState())
395       {
396         reset();
397       }
398
399       maxColour.setEnabled(!currentColours.getState());
400       minColour.setEnabled(!currentColours.getState());
401
402     }
403
404     changeColour();
405   }
406
407   @Override
408   public void adjustmentValueChanged(AdjustmentEvent evt)
409   {
410     if (!adjusting)
411     {
412       thresholdValue.setText((slider.getValue() / 1000f) + "");
413       if (currentColours.getState() && !(av
414               .getGlobalColourScheme() instanceof AnnotationColourGradient))
415       {
416         changeColour();
417       }
418
419       currentAnnotation.threshold.value = slider.getValue() / 1000f;
420       ap.paintAlignment(false, false);
421     }
422   }
423
424   public void minColour_actionPerformed(Color newCol)
425   {
426     if (newCol != null)
427     {
428       minColour.setBackground(newCol);
429       minColour.repaint();
430       changeColour();
431     }
432     else
433     {
434       new UserDefinedColours(this, "Min Colour", minColour.getBackground());
435     }
436
437   }
438
439   public void maxColour_actionPerformed(Color newCol)
440   {
441     if (newCol != null)
442     {
443       maxColour.setBackground(newCol);
444       maxColour.repaint();
445       changeColour();
446     }
447     else
448     {
449       new UserDefinedColours(this, "Max Colour", maxColour.getBackground());
450     }
451   }
452
453   public void defColour_actionPerformed()
454   {
455     setDefaultMinMax();
456     minColour.repaint();
457     maxColour.repaint();
458     changeColour();
459   }
460
461   void changeColour()
462   {
463     // Check if combobox is still adjusting
464     if (adjusting)
465     {
466       return;
467     }
468
469     currentAnnotation = av.getAlignment()
470             .getAlignmentAnnotation()[annotations.getSelectedIndex()];
471
472     int aboveThreshold = -1;
473     if (threshold.getSelectedIndex() == 1)
474     {
475       aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
476     }
477     else if (threshold.getSelectedIndex() == 2)
478     {
479       aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
480     }
481
482     slider.setEnabled(true);
483     thresholdValue.setEnabled(true);
484     thresholdIsMin.setEnabled(true);
485
486     if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
487     {
488       slider.setEnabled(false);
489       thresholdValue.setEnabled(false);
490       thresholdIsMin.setEnabled(false);
491       thresholdValue.setText("");
492     }
493     else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
494             && currentAnnotation.threshold == null)
495     {
496       currentAnnotation.setThreshold(new jalview.datamodel.GraphLine(
497               (currentAnnotation.graphMax - currentAnnotation.graphMin)
498                       / 2f,
499               "Threshold", Color.black));
500     }
501
502     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
503     {
504       adjusting = true;
505
506       slider.setMinimum((int) (currentAnnotation.graphMin * 1000));
507       slider.setMaximum((int) (currentAnnotation.graphMax * 1000));
508       slider.setValue((int) (currentAnnotation.threshold.value * 1000));
509       thresholdValue.setText(currentAnnotation.threshold.value + "");
510       slider.setEnabled(true);
511       thresholdValue.setEnabled(true);
512       adjusting = false;
513     }
514
515     AnnotationColourGradient acg = null;
516     if (currentColours.getState())
517     {
518     }
519     else
520     {
521       acg = new AnnotationColourGradient(currentAnnotation,
522               minColour.getBackground(), maxColour.getBackground(),
523               aboveThreshold);
524     }
525
526     if (currentAnnotation.graphMin == 0f
527             && currentAnnotation.graphMax == 0f)
528     {
529       acg.setPredefinedColours(true);
530     }
531
532     acg.setThresholdIsMinMax(thresholdIsMin.getState());
533
534     av.setGlobalColourScheme(acg);
535
536     // TODO: per group colour propagation not always desired
537     if (av.getAlignment().getGroups() != null)
538     {
539       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
540       {
541         if (sg.getColourScheme() == null)
542         {
543           continue;
544         }
545
546         if (currentColours.getState())
547         {
548           sg.setColourScheme(new AnnotationColourGradient(currentAnnotation,
549                   sg.getColourScheme(), aboveThreshold));
550         }
551         else
552         {
553           sg.setColourScheme(new AnnotationColourGradient(currentAnnotation,
554                   minColour.getBackground(), maxColour.getBackground(),
555                   aboveThreshold));
556         }
557       }
558     }
559
560     // update colours in linked windows
561     ap.alignmentChanged();
562     ap.paintAlignment(true, true);
563   }
564
565   void reset()
566   {
567     av.setGlobalColourScheme(oldcs);
568     if (av.getAlignment().getGroups() != null)
569     {
570       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
571       {
572         sg.setColourScheme(oldgroupColours.get(sg));
573       }
574     }
575     ap.paintAlignment(true, true);
576   }
577
578   @Override
579   public void mouseClicked(MouseEvent evt)
580   {
581   }
582
583   @Override
584   public void mousePressed(MouseEvent evt)
585   {
586   }
587
588   @Override
589   public void mouseReleased(MouseEvent evt)
590   {
591     ap.paintAlignment(true, true);
592   }
593
594   @Override
595   public void mouseEntered(MouseEvent evt)
596   {
597   }
598
599   @Override
600   public void mouseExited(MouseEvent evt)
601   {
602   }
603
604 }