JAL-1354 replacement of bare strings in GUI dialogs with i18n bundle lookup via jalvi...
[jalview.git] / src / jalview / gui / AnnotationColourChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, 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
25 import javax.swing.*;
26 import javax.swing.event.*;
27
28 import net.miginfocom.swing.MigLayout;
29
30 import jalview.bin.Cache;
31 import jalview.datamodel.*;
32 import jalview.schemes.*;
33 import jalview.util.MessageManager;
34
35 import java.awt.Dimension;
36
37 public class AnnotationColourChooser extends JPanel
38 {
39   JInternalFrame frame;
40
41   AlignViewport av;
42
43   AlignmentPanel ap;
44
45   ColourSchemeI oldcs;
46
47   Hashtable oldgroupColours;
48
49   jalview.datamodel.AlignmentAnnotation currentAnnotation;
50
51   boolean adjusting = false;
52   /**
53    * enabled if the user is dragging the slider - try to keep updates to a minimun
54    */
55   boolean sliderDragging = false;
56
57   public AnnotationColourChooser(AlignViewport av, final AlignmentPanel ap)
58   {
59     oldcs = av.getGlobalColourScheme();
60     if (av.getAlignment().getGroups() != null)
61     {
62       oldgroupColours = new Hashtable();
63       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
64       {
65         if (sg.cs != null)
66         {
67           oldgroupColours.put(sg, sg.cs);
68         }
69       }
70     }
71     this.av = av;
72     this.ap = ap;
73     frame = new JInternalFrame();
74     frame.setContentPane(this);
75     frame.setLayer(JLayeredPane.PALETTE_LAYER);
76     Desktop.addInternalFrame(frame, "Colour by Annotation", 520, 215);
77
78     slider.addChangeListener(new ChangeListener()
79     {
80       public void stateChanged(ChangeEvent evt)
81       {
82         if (!adjusting)
83         {
84           thresholdValue.setText(((float) slider.getValue() / 1000f) + "");
85           valueChanged(!sliderDragging);
86         }
87       }
88     });
89     slider.addMouseListener(new MouseAdapter()
90     {
91       @Override
92       public void mousePressed(MouseEvent e)
93       {
94         sliderDragging=true;
95         super.mousePressed(e);
96       }
97
98       @Override
99       public void mouseDragged(MouseEvent e)
100       {
101         sliderDragging=true;
102         super.mouseDragged(e);
103       }
104       public void mouseReleased(MouseEvent evt)
105       {
106         if (sliderDragging)
107         {
108           sliderDragging=false;
109           valueChanged(true);
110         }
111         ap.paintAlignment(true);
112       }
113     });
114
115     if (av.getAlignment().getAlignmentAnnotation() == null)
116     {
117       return;
118     }
119
120     // Always get default shading from preferences.
121     setDefaultMinMax();
122
123     if (oldcs instanceof AnnotationColourGradient)
124     {
125       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
126       currentColours.setSelected(acg.predefinedColours);
127       if (!acg.predefinedColours)
128       {
129         minColour.setBackground(acg.getMinColour());
130         maxColour.setBackground(acg.getMaxColour());
131       }
132       seqAssociated.setSelected(acg.isSeqAssociated());
133     }
134     adjusting = true;
135     annotations = new JComboBox(
136             getAnnotationItems(seqAssociated.isSelected()));
137
138     threshold.addItem("No Threshold");
139     threshold.addItem("Above Threshold");
140     threshold.addItem("Below Threshold");
141
142     if (oldcs instanceof AnnotationColourGradient)
143     {
144       AnnotationColourGradient acg = (AnnotationColourGradient) oldcs;
145       annotations.setSelectedItem(acg.getAnnotation());
146       switch (acg.getAboveThreshold())
147       {
148       case AnnotationColourGradient.NO_THRESHOLD:
149         threshold.setSelectedItem("No Threshold");
150         break;
151       case AnnotationColourGradient.ABOVE_THRESHOLD:
152         threshold.setSelectedItem("Above Threshold");
153         break;
154       case AnnotationColourGradient.BELOW_THRESHOLD:
155         threshold.setSelectedItem("Below Threshold");
156         break;
157       default:
158         throw new Error(
159                 "Implementation error: don't know about threshold setting for current AnnotationColourGradient.");
160       }
161       thresholdIsMin.setSelected(acg.thresholdIsMinMax);
162       thresholdValue.setText("" + acg.getAnnotationThreshold());
163     }
164
165     try
166     {
167       jbInit();
168     } catch (Exception ex)
169     {
170     }
171
172     adjusting = false;
173
174     changeColour();
175     frame.invalidate();
176     frame.pack();
177
178   }
179
180   private Vector<String> getAnnotationItems(boolean isSeqAssociated)
181   {
182     Vector<String> list = new Vector<String>();
183     int index = 1;
184     int[] anmap = new int[av.getAlignment().getAlignmentAnnotation().length];
185     boolean enableSeqAss = false;
186     for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
187     {
188       if (av.getAlignment().getAlignmentAnnotation()[i].sequenceRef == null)
189       {
190         if (isSeqAssociated)
191         {
192           continue;
193         }
194       }
195       else
196       {
197         enableSeqAss = true;
198       }
199       String label = av.getAlignment().getAlignmentAnnotation()[i].label;
200       if (!list.contains(label))
201       {
202         anmap[list.size()] = i;
203         list.addElement(label);
204
205       }
206       else
207       {
208         if (!isSeqAssociated)
209         {
210           anmap[list.size()] = i;
211           list.addElement(label + "_" + (index++));
212         }
213       }
214     }
215     seqAssociated.setEnabled(enableSeqAss);
216     annmap = new int[list.size()];
217     System.arraycopy(anmap, 0, annmap, 0, annmap.length);
218     return list;
219   }
220   private void setDefaultMinMax()
221   {
222     minColour.setBackground(Cache.getDefaultColour("ANNOTATIONCOLOUR_MIN",
223             Color.orange));
224     maxColour.setBackground(Cache.getDefaultColour("ANNOTATIONCOLOUR_MAX",
225             Color.red));
226   }
227
228   public AnnotationColourChooser()
229   {
230     try
231     {
232       jbInit();
233     } catch (Exception ex)
234     {
235       ex.printStackTrace();
236     }
237   }
238
239   private void jbInit() throws Exception
240   {
241     minColour.setFont(JvSwingUtils.getLabelFont());
242     minColour.setBorder(BorderFactory.createEtchedBorder());
243     minColour.setPreferredSize(new Dimension(40, 20));
244     minColour.setToolTipText("Minimum Colour");
245     minColour.addMouseListener(new MouseAdapter()
246     {
247       public void mousePressed(MouseEvent e)
248       {
249         if (minColour.isEnabled())
250         {
251           minColour_actionPerformed();
252         }
253       }
254     });
255     maxColour.setFont(JvSwingUtils.getLabelFont());
256     maxColour.setBorder(BorderFactory.createEtchedBorder());
257     maxColour.setPreferredSize(new Dimension(40, 20));
258     maxColour.setToolTipText("Maximum Colour");
259     maxColour.addMouseListener(new MouseAdapter()
260     {
261       public void mousePressed(MouseEvent e)
262       {
263         if (maxColour.isEnabled())
264         {
265           maxColour_actionPerformed();
266         }
267       }
268     });
269     ok.setOpaque(false);
270     ok.setText(MessageManager.getString("action.ok"));
271     ok.addActionListener(new ActionListener()
272     {
273       public void actionPerformed(ActionEvent e)
274       {
275         ok_actionPerformed(e);
276       }
277     });
278     cancel.setOpaque(false);
279     cancel.setText(MessageManager.getString("action.cancel"));
280     cancel.addActionListener(new ActionListener()
281     {
282       public void actionPerformed(ActionEvent e)
283       {
284         cancel_actionPerformed(e);
285       }
286     });
287     defColours.setOpaque(false);
288     defColours.setText(MessageManager.getString("action.set_defaults"));
289     defColours
290             .setToolTipText("Reset min and max colours to defaults from user preferences.");
291     defColours.addActionListener(new ActionListener()
292     {
293
294       @Override
295       public void actionPerformed(ActionEvent arg0)
296       {
297         resetColours_actionPerformed(arg0);
298       }
299     });
300
301     annotations.addActionListener(new ActionListener()
302     {
303       public void actionPerformed(ActionEvent e)
304       {
305         annotations_actionPerformed(e);
306       }
307     });
308     threshold.addActionListener(new ActionListener()
309     {
310       public void actionPerformed(ActionEvent e)
311       {
312         threshold_actionPerformed(e);
313       }
314     });
315     thresholdValue.addActionListener(new ActionListener()
316     {
317       public void actionPerformed(ActionEvent e)
318       {
319         thresholdValue_actionPerformed(e);
320       }
321     });
322     slider.setPaintLabels(false);
323     slider.setPaintTicks(true);
324     slider.setBackground(Color.white);
325     slider.setEnabled(false);
326     slider.setOpaque(false);
327     slider.setPreferredSize(new Dimension(100, 32));
328     thresholdValue.setEnabled(false);
329     thresholdValue.setColumns(7);
330     currentColours.setFont(JvSwingUtils.getLabelFont());
331     currentColours.setOpaque(false);
332     currentColours.setText(MessageManager.getString("label.use_original_colours"));
333     currentColours.addActionListener(new ActionListener()
334     {
335       public void actionPerformed(ActionEvent e)
336       {
337         currentColours_actionPerformed(e);
338       }
339     });
340     thresholdIsMin.setBackground(Color.white);
341     thresholdIsMin.setFont(JvSwingUtils.getLabelFont());
342     thresholdIsMin.setText(MessageManager.getString("label.threshold_minmax"));
343     thresholdIsMin.addActionListener(new ActionListener()
344     {
345       public void actionPerformed(ActionEvent actionEvent)
346       {
347         thresholdIsMin_actionPerformed(actionEvent);
348       }
349     });
350     seqAssociated.setBackground(Color.white);
351     seqAssociated.setFont(JvSwingUtils.getLabelFont());
352     seqAssociated.setText(MessageManager.getString("label.per_sequence_only"));
353     seqAssociated.addActionListener(new ActionListener()
354     {
355
356       @Override
357       public void actionPerformed(ActionEvent arg0)
358       {
359         seqAssociated_actionPerformed(arg0);
360       }
361     });
362
363     this.setLayout(borderLayout1);
364     jPanel2.setLayout(new MigLayout("", "[left][center][right]", "[][][]"));
365     jPanel1.setBackground(Color.white);
366     jPanel2.setBackground(Color.white);
367
368     jPanel1.add(ok);
369     jPanel1.add(cancel);
370     jPanel2.add(annotations, "grow, wrap");
371     jPanel2.add(seqAssociated);
372     jPanel2.add(currentColours);
373     JPanel colpanel = new JPanel(new FlowLayout());
374     colpanel.setBackground(Color.white);
375     colpanel.add(minColour);
376     colpanel.add(maxColour);
377     jPanel2.add(colpanel, "wrap");
378     jPanel2.add(threshold);
379     jPanel2.add(defColours, "skip 1, wrap");
380     jPanel2.add(thresholdIsMin);
381     jPanel2.add(slider, "grow");
382     jPanel2.add(thresholdValue, "grow");
383     this.add(jPanel1, java.awt.BorderLayout.SOUTH);
384     this.add(jPanel2, java.awt.BorderLayout.CENTER);
385     this.validate();
386   }
387
388   protected void seqAssociated_actionPerformed(ActionEvent arg0)
389   {
390     adjusting = true;
391     String cursel = (String) annotations.getSelectedItem();
392     boolean isvalid = false, isseqs = seqAssociated.isSelected();
393     this.annotations.removeAllItems();
394     for (String anitem : getAnnotationItems(seqAssociated.isSelected()))
395     {
396       if (anitem.equals(cursel) || (isseqs && cursel.startsWith(anitem)))
397       {
398         isvalid = true;
399         cursel = anitem;
400       }
401       this.annotations.addItem(anitem);
402     }
403     adjusting = false;
404     if (isvalid)
405     {
406       this.annotations.setSelectedItem(cursel);
407     }
408     else
409     {
410       if (annotations.getItemCount() > 0)
411       {
412         annotations.setSelectedIndex(0);
413       }
414     }
415   }
416
417   protected void resetColours_actionPerformed(ActionEvent arg0)
418   {
419     setDefaultMinMax();
420     changeColour();
421   }
422
423   JComboBox annotations;
424
425   int[] annmap;
426
427   JPanel minColour = new JPanel();
428
429   JPanel maxColour = new JPanel();
430
431   JButton defColours = new JButton();
432
433   JButton ok = new JButton();
434
435   JButton cancel = new JButton();
436
437   JPanel jPanel1 = new JPanel();
438
439   JPanel jPanel2 = new JPanel();
440
441   BorderLayout borderLayout1 = new BorderLayout();
442
443   JComboBox threshold = new JComboBox();
444
445   JSlider slider = new JSlider();
446
447   JTextField thresholdValue = new JTextField(20);
448
449   JCheckBox currentColours = new JCheckBox();
450
451   JCheckBox thresholdIsMin = new JCheckBox();
452
453   JCheckBox seqAssociated = new JCheckBox();
454
455   public void minColour_actionPerformed()
456   {
457     Color col = JColorChooser.showDialog(this,
458             "Select Colour for Minimum Value", minColour.getBackground());
459     if (col != null)
460     {
461       minColour.setBackground(col);
462     }
463     minColour.repaint();
464     changeColour();
465   }
466
467   public void maxColour_actionPerformed()
468   {
469     Color col = JColorChooser.showDialog(this,
470             "Select Colour for Maximum Value", maxColour.getBackground());
471     if (col != null)
472     {
473       maxColour.setBackground(col);
474     }
475     maxColour.repaint();
476     changeColour();
477   }
478
479   void changeColour()
480   {
481     // Check if combobox is still adjusting
482     if (adjusting)
483     {
484       return;
485     }
486
487     currentAnnotation = av.getAlignment().getAlignmentAnnotation()[annmap[annotations
488             .getSelectedIndex()]];
489
490     int aboveThreshold = -1;
491     if (threshold.getSelectedItem().equals("Above Threshold"))
492     {
493       aboveThreshold = AnnotationColourGradient.ABOVE_THRESHOLD;
494     }
495     else if (threshold.getSelectedItem().equals("Below Threshold"))
496     {
497       aboveThreshold = AnnotationColourGradient.BELOW_THRESHOLD;
498     }
499
500     slider.setEnabled(true);
501     thresholdValue.setEnabled(true);
502     thresholdIsMin.setEnabled(true);
503
504     if (aboveThreshold == AnnotationColourGradient.NO_THRESHOLD)
505     {
506       slider.setEnabled(false);
507       thresholdValue.setEnabled(false);
508       thresholdValue.setText("");
509       thresholdIsMin.setEnabled(false);
510     }
511     else if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD
512             && currentAnnotation.threshold == null)
513     {
514       currentAnnotation
515               .setThreshold(new jalview.datamodel.GraphLine(
516                       (currentAnnotation.graphMax - currentAnnotation.graphMin) / 2f,
517                       "Threshold", Color.black));
518     }
519
520     if (aboveThreshold != AnnotationColourGradient.NO_THRESHOLD)
521     {
522       adjusting = true;
523       float range = currentAnnotation.graphMax * 1000
524               - currentAnnotation.graphMin * 1000;
525
526       slider.setMinimum((int) (currentAnnotation.graphMin * 1000));
527       slider.setMaximum((int) (currentAnnotation.graphMax * 1000));
528       slider.setValue((int) (currentAnnotation.threshold.value * 1000));
529       thresholdValue.setText(currentAnnotation.threshold.value + "");
530       slider.setMajorTickSpacing((int) (range / 10f));
531       slider.setEnabled(true);
532       thresholdValue.setEnabled(true);
533       adjusting = false;
534     }
535
536     AnnotationColourGradient acg = null;
537     if (currentColours.isSelected())
538     {
539       acg = new AnnotationColourGradient(currentAnnotation,
540               av.getGlobalColourScheme(), aboveThreshold);
541     }
542     else
543     {
544       acg = new AnnotationColourGradient(currentAnnotation,
545               minColour.getBackground(), maxColour.getBackground(),
546               aboveThreshold);
547     }
548     acg.setSeqAssociated(seqAssociated.isSelected());
549
550     if (currentAnnotation.graphMin == 0f
551             && currentAnnotation.graphMax == 0f)
552     {
553       acg.predefinedColours = true;
554     }
555
556     acg.thresholdIsMinMax = thresholdIsMin.isSelected();
557
558     av.setGlobalColourScheme(acg);
559
560     if (av.getAlignment().getGroups() != null)
561     {
562
563       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
564       {
565         if (sg.cs == null)
566         {
567           continue;
568         }
569
570         if (currentColours.isSelected())
571         {
572           sg.cs = new AnnotationColourGradient(currentAnnotation, sg.cs,
573                   aboveThreshold);
574           ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
575                   .isSelected());
576
577         }
578         else
579         {
580           sg.cs = new AnnotationColourGradient(currentAnnotation,
581                   minColour.getBackground(), maxColour.getBackground(),
582                   aboveThreshold);
583           ((AnnotationColourGradient) sg.cs).setSeqAssociated(seqAssociated
584                   .isSelected());
585         }
586
587       }
588     }
589     ap.alignmentChanged();
590     // ensure all associated views (overviews, structures, etc) are notified of
591     // updated colours.
592     ap.paintAlignment(true);
593   }
594
595   public void ok_actionPerformed(ActionEvent e)
596   {
597     changeColour();
598     try
599     {
600       frame.setClosed(true);
601     } catch (Exception ex)
602     {
603     }
604   }
605
606   public void cancel_actionPerformed(ActionEvent e)
607   {
608     reset();
609     // ensure all original colouring is propagated to listeners.
610     ap.paintAlignment(true);
611     try
612     {
613       frame.setClosed(true);
614     } catch (Exception ex)
615     {
616     }
617   }
618
619   void reset()
620   {
621     av.setGlobalColourScheme(oldcs);
622     if (av.getAlignment().getGroups() != null)
623     {
624
625       for (SequenceGroup sg : ap.av.getAlignment().getGroups())
626       {
627         sg.cs = (ColourSchemeI) oldgroupColours.get(sg);
628       }
629     }
630   }
631
632   public void thresholdCheck_actionPerformed(ActionEvent e)
633   {
634     changeColour();
635   }
636
637   public void annotations_actionPerformed(ActionEvent e)
638   {
639     changeColour();
640   }
641
642   public void threshold_actionPerformed(ActionEvent e)
643   {
644     changeColour();
645   }
646
647   public void thresholdValue_actionPerformed(ActionEvent e)
648   {
649     try
650     {
651       float f = Float.parseFloat(thresholdValue.getText());
652       slider.setValue((int) (f * 1000));
653     } catch (NumberFormatException ex)
654     {
655     }
656   }
657
658   public void valueChanged(boolean updateAllAnnotation)
659   {
660     if (currentColours.isSelected()
661             && !(av.getGlobalColourScheme() instanceof AnnotationColourGradient))
662     {
663       changeColour();
664     }
665     currentAnnotation.threshold.value = (float) slider.getValue() / 1000f;
666     propagateSeqAssociatedThreshold(updateAllAnnotation);
667     ap.paintAlignment(false);
668   }
669
670   private void propagateSeqAssociatedThreshold(boolean allAnnotation)
671   {
672     if (currentAnnotation.sequenceRef == null
673             || currentAnnotation.threshold == null)
674     {
675       return;
676     }
677     // TODO: JAL-1327 only update visible annotation thresholds if allAnnotation is false, since we only need to provide a quick visual indicator
678
679     float thr = currentAnnotation.threshold.value;
680     for (int i = 0; i < av.getAlignment().getAlignmentAnnotation().length; i++)
681     {
682       AlignmentAnnotation aa = av.getAlignment().getAlignmentAnnotation()[i];
683       if (aa.label.equals(currentAnnotation.label))
684       {
685         aa.threshold.value = thr;
686       }
687     }
688   }
689
690   public void currentColours_actionPerformed(ActionEvent e)
691   {
692     if (currentColours.isSelected())
693     {
694       reset();
695     }
696
697     maxColour.setEnabled(!currentColours.isSelected());
698     minColour.setEnabled(!currentColours.isSelected());
699
700     changeColour();
701   }
702
703   public void thresholdIsMin_actionPerformed(ActionEvent actionEvent)
704   {
705     changeColour();
706   }
707
708 }