369dde67928ad52d6563b89d71672f4a4cf8a6b5
[jalview.git] / src / jalview / gui / UserDefinedColours.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.bin.Cache;
24 import jalview.io.JalviewFileChooser;
25 import jalview.io.JalviewFileView;
26 import jalview.jbgui.GUserDefinedColours;
27 import jalview.schemabinding.version2.Colour;
28 import jalview.schemabinding.version2.JalviewUserColours;
29 import jalview.schemes.ColourSchemeI;
30 import jalview.schemes.ColourSchemeLoader;
31 import jalview.schemes.ColourSchemes;
32 import jalview.schemes.ResidueProperties;
33 import jalview.schemes.UserColourScheme;
34 import jalview.util.ColorUtils;
35 import jalview.util.Format;
36 import jalview.util.MessageManager;
37
38 import java.awt.Color;
39 import java.awt.Font;
40 import java.awt.Insets;
41 import java.awt.event.MouseAdapter;
42 import java.awt.event.MouseEvent;
43 import java.io.File;
44 import java.io.FileOutputStream;
45 import java.io.OutputStreamWriter;
46 import java.io.PrintWriter;
47 import java.util.ArrayList;
48 import java.util.List;
49
50 import javax.swing.JButton;
51 import javax.swing.JInternalFrame;
52 import javax.swing.event.ChangeEvent;
53 import javax.swing.event.ChangeListener;
54
55 /**
56  * This panel allows the user to assign colours to Amino Acid residue codes, and
57  * save the colour scheme.
58  * 
59  * @author Andrew Waterhouse
60  * @author Mungo Carstairs
61  */
62 public class UserDefinedColours extends GUserDefinedColours implements
63         ChangeListener
64 {
65   private static final Font VERDANA_BOLD_10 = new Font("Verdana",
66           Font.BOLD, 10);
67
68   public static final String USER_DEFINED_COLOURS = "USER_DEFINED_COLOURS";
69
70   private static final String LAST_DIRECTORY = "LAST_DIRECTORY";
71
72   private static final int MY_FRAME_HEIGHT = 440;
73
74   private static final int MY_FRAME_WIDTH = 810;
75
76   private static final int MY_FRAME_WIDTH_CASE_SENSITIVE = 970;
77
78   AlignmentPanel ap;
79
80   ColourSchemeI oldColourScheme;
81
82   JInternalFrame frame;
83
84   List<JButton> upperCaseButtons;
85
86   List<JButton> lowerCaseButtons;
87
88   /**
89    * Creates and displays a new UserDefinedColours panel
90    * 
91    * @param alignPanel
92    */
93   public UserDefinedColours(AlignmentPanel alignPanel)
94   {
95     this();
96
97     lcaseColour.setEnabled(false);
98
99     this.ap = alignPanel;
100
101     oldColourScheme = alignPanel.av.getGlobalColourScheme();
102
103     if (oldColourScheme instanceof UserColourScheme)
104     {
105       schemeName.setText(oldColourScheme.getSchemeName());
106       if (((UserColourScheme) oldColourScheme).getLowerCaseColours() != null)
107       {
108         caseSensitive.setSelected(true);
109         lcaseColour.setEnabled(true);
110         resetButtonPanel(true);
111       }
112       else
113       {
114         resetButtonPanel(false);
115       }
116     }
117     else
118     {
119       resetButtonPanel(false);
120     }
121
122     showFrame();
123   }
124
125   UserDefinedColours()
126   {
127     super();
128     selectedButtons = new ArrayList<JButton>();
129   }
130
131   void showFrame()
132   {
133     colorChooser.getSelectionModel().addChangeListener(this);
134     frame = new JInternalFrame();
135     frame.setContentPane(this);
136     Desktop.addInternalFrame(frame,
137             MessageManager.getString("label.user_defined_colours"),
138             MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
139   }
140
141   /**
142    * Rebuilds the panel with coloured buttons for residues. If not case
143    * sensitive colours, show 3-letter amino acid code as button text. If case
144    * sensitive, just show the single letter code, in order to make space for the
145    * additional buttons.
146    * 
147    * @param isCaseSensitive
148    */
149   void resetButtonPanel(boolean isCaseSensitive)
150   {
151     buttonPanel.removeAll();
152
153     if (upperCaseButtons == null)
154     {
155       upperCaseButtons = new ArrayList<JButton>();
156     }
157
158     for (int i = 0; i < 20; i++)
159     {
160       String label = isCaseSensitive ? ResidueProperties.aa[i]
161               : ResidueProperties.aa2Triplet.get(ResidueProperties.aa[i])
162                       .toString();
163       JButton button = makeButton(label, ResidueProperties.aa[i],
164               upperCaseButtons, i);
165       buttonPanel.add(button);
166     }
167
168     buttonPanel.add(makeButton("B", "B", upperCaseButtons, 20));
169     buttonPanel.add(makeButton("Z", "Z", upperCaseButtons, 21));
170     buttonPanel.add(makeButton("X", "X", upperCaseButtons, 22));
171     buttonPanel.add(makeButton("Gap", "-", upperCaseButtons, 23));
172
173     if (!isCaseSensitive)
174     {
175       gridLayout.setRows(6);
176       gridLayout.setColumns(4);
177     }
178     else
179     {
180       gridLayout.setRows(7);
181       int cols = 7;
182       gridLayout.setColumns(cols + 1);
183
184       if (lowerCaseButtons == null)
185       {
186         lowerCaseButtons = new ArrayList<JButton>();
187       }
188
189       for (int i = 0; i < 20; i++)
190       {
191         int row = i / cols + 1;
192         int index = (row * cols) + i;
193         JButton button = makeButton(ResidueProperties.aa[i].toLowerCase(),
194                 ResidueProperties.aa[i].toLowerCase(), lowerCaseButtons, i);
195
196         buttonPanel.add(button, index);
197       }
198     }
199
200     if (isCaseSensitive)
201     {
202       buttonPanel.add(makeButton("b", "b", lowerCaseButtons, 20));
203       buttonPanel.add(makeButton("z", "z", lowerCaseButtons, 21));
204       buttonPanel.add(makeButton("x", "x", lowerCaseButtons, 22));
205     }
206
207     // JAL-1360 widen the frame dynamically to accommodate case-sensitive AA
208     // codes
209     if (this.frame != null)
210     {
211       int newWidth = isCaseSensitive ? MY_FRAME_WIDTH_CASE_SENSITIVE
212               : MY_FRAME_WIDTH;
213       this.frame.setSize(newWidth, this.frame.getHeight());
214     }
215
216     buttonPanel.validate();
217     validate();
218   }
219
220   /**
221    * ChangeListener handler for when a colour is picked in the colour chooser.
222    * The action is to apply the colour to all selected buttons as their
223    * background colour. Foreground colour (text) is set to a lighter shade in
224    * order to highlight which buttons are selected. If 'Lower Case Colour' is
225    * active, then the colour is applied to all lower case buttons (as well as
226    * the Lower Case Colour button itself).
227    * 
228    * @param evt
229    */
230   @Override
231   public void stateChanged(ChangeEvent evt)
232   {
233     JButton button = null;
234     final Color newColour = colorChooser.getColor();
235     if (lcaseColour.isSelected())
236     {
237       selectedButtons.clear();
238       for (int i = 0; i < lowerCaseButtons.size(); i++)
239       {
240         button = lowerCaseButtons.get(i);
241         button.setBackground(newColour);
242         button.setForeground(ColorUtils.brighterThan(button.getBackground()));
243       }
244     }
245     for (int i = 0; i < selectedButtons.size(); i++)
246     {
247       button = selectedButtons.get(i);
248       button.setBackground(newColour);
249       button.setForeground(ColorUtils.brighterThan(newColour));
250     }
251   }
252
253   /**
254    * Performs actions when a residue button is clicked. This manages the button
255    * selection set (highlighted by brighter foreground text).
256    * <p>
257    * On select button(s) with Ctrl/click or Shift/click: set button foreground
258    * text to brighter than background.
259    * <p>
260    * On unselect button(s) with Ctrl/click on selected, or click to release
261    * current selection: reset foreground text to darker than background.
262    * <p>
263    * Simple click: clear selection (resetting foreground to darker); set clicked
264    * button foreground to brighter
265    * <p>
266    * Finally, synchronize the colour chooser to the colour of the first button
267    * in the selected set.
268    * 
269    * @param e
270    */
271   public void colourButtonPressed(MouseEvent e)
272   {
273     JButton pressed = (JButton) e.getSource();
274
275     if (e.isShiftDown())
276     {
277       JButton start, end = (JButton) e.getSource();
278       if (selectedButtons.size() > 0)
279       {
280         start = selectedButtons.get(selectedButtons.size() - 1);
281       }
282       else
283       {
284         start = (JButton) e.getSource();
285       }
286
287       int startIndex = 0, endIndex = 0;
288       for (int b = 0; b < buttonPanel.getComponentCount(); b++)
289       {
290         if (buttonPanel.getComponent(b) == start)
291         {
292           startIndex = b;
293         }
294         if (buttonPanel.getComponent(b) == end)
295         {
296           endIndex = b;
297         }
298       }
299
300       if (startIndex > endIndex)
301       {
302         int temp = startIndex;
303         startIndex = endIndex;
304         endIndex = temp;
305       }
306
307       for (int b = startIndex; b <= endIndex; b++)
308       {
309         JButton button = (JButton) buttonPanel.getComponent(b);
310         if (!selectedButtons.contains(button))
311         {
312           button.setForeground(ColorUtils.brighterThan(button
313                   .getBackground()));
314           selectedButtons.add(button);
315         }
316       }
317     }
318     else if (!e.isControlDown())
319     {
320       for (int b = 0; b < selectedButtons.size(); b++)
321       {
322         JButton button = selectedButtons.get(b);
323         button.setForeground(ColorUtils.darkerThan(button.getBackground()));
324       }
325       selectedButtons.clear();
326       pressed.setForeground(ColorUtils.brighterThan(pressed.getBackground()));
327       selectedButtons.add(pressed);
328
329     }
330     else if (e.isControlDown())
331     {
332       if (selectedButtons.contains(pressed))
333       {
334         pressed.setForeground(ColorUtils.darkerThan(pressed.getBackground()));
335         selectedButtons.remove(pressed);
336       }
337       else
338       {
339         pressed.setForeground(ColorUtils.brighterThan(pressed
340                 .getBackground()));
341         selectedButtons.add(pressed);
342       }
343     }
344
345     if (selectedButtons.size() > 0)
346     {
347       colorChooser.setColor((selectedButtons.get(0)).getBackground());
348     }
349   }
350
351   /**
352    * A helper method to update or make a colour button, whose background colour
353    * is the associated colour, and text colour a darker shade of the same. If
354    * the button is already in the list, then its text and margins are updated,
355    * if not then it is created and added. This method supports toggling between
356    * case-sensitive and case-insensitive button panels. The case-sensitive
357    * version has abbreviated button text in order to fit in more buttons.
358    * 
359    * @param label
360    * @param residue
361    * @param the
362    *          list of buttons
363    * @param buttonIndex
364    *          the button's position in the list
365    */
366   JButton makeButton(String label, String residue, List<JButton> buttons,
367           int buttonIndex)
368   {
369     final JButton button;
370     Color col;
371
372     if (buttonIndex < buttons.size())
373     {
374       button = buttons.get(buttonIndex);
375       col = button.getBackground();
376     }
377     else
378     {
379       button = new JButton();
380       button.addMouseListener(new MouseAdapter()
381       {
382         @Override
383         public void mouseClicked(MouseEvent e)
384         {
385           colourButtonPressed(e);
386         }
387       });
388
389       buttons.add(button);
390
391       /*
392        * make initial button colour that of the current colour scheme,
393        * if it is a simple per-residue colouring, else white
394        */
395       col = Color.white;
396       if (oldColourScheme != null && oldColourScheme.isSimple())
397       {
398         col = oldColourScheme.findColour(residue.charAt(0), 0, null, null,
399                 0f);
400       }
401     }
402
403     if (caseSensitive.isSelected())
404     {
405       button.setMargin(new Insets(2, 2, 2, 2));
406     }
407     else
408     {
409       button.setMargin(new Insets(2, 14, 2, 14));
410     }
411
412     button.setOpaque(true); // required for the next line to have effect
413     button.setBackground(col);
414     button.setText(label);
415     button.setForeground(ColorUtils.darkerThan(col));
416     button.setFont(VERDANA_BOLD_10);
417
418     return button;
419   }
420
421   /**
422    * On 'OK', check that at least one colour has been assigned to a residue (and
423    * if not issue a warning), and apply the chosen colour scheme and close the
424    * panel.
425    */
426   @Override
427   protected void okButton_actionPerformed()
428   {
429     if (isNoSelectionMade())
430     {
431       JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
432               .getString("label.no_colour_selection_in_scheme"),
433               MessageManager.getString("label.no_colour_selection_warn"),
434               JvOptionPane.WARNING_MESSAGE);
435     }
436     else
437     {
438       applyButton_actionPerformed();
439
440       try
441       {
442         frame.setClosed(true);
443       } catch (Exception ex)
444       {
445       }
446     }
447   }
448
449   /**
450    * Returns true if the user has not made any colour selection (including if
451    * 'case-sensitive' selected and no lower-case colour chosen).
452    * 
453    * @return
454    */
455   protected boolean isNoSelectionMade()
456   {
457     final boolean noUpperCaseSelected = upperCaseButtons == null
458             || upperCaseButtons.isEmpty();
459     final boolean noLowerCaseSelected = caseSensitive.isSelected()
460             && (lowerCaseButtons == null || lowerCaseButtons.isEmpty());
461     final boolean noSelectionMade = noUpperCaseSelected
462             || noLowerCaseSelected;
463     return noSelectionMade;
464   }
465
466   /**
467    * Applies the current colour scheme to the alignment or sequence group
468    */
469   @Override
470   protected void applyButton_actionPerformed()
471   {
472     if (isNoSelectionMade())
473     {
474       JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
475               .getString("label.no_colour_selection_in_scheme"),
476               MessageManager.getString("label.no_colour_selection_warn"),
477               JvOptionPane.WARNING_MESSAGE);
478
479     }
480     UserColourScheme ucs = getSchemeFromButtons();
481
482     ap.alignFrame.changeColour(ucs);
483   }
484
485   /**
486    * Constructs an instance of UserColourScheme with the residue colours
487    * currently set on the buttons on the panel
488    * 
489    * @return
490    */
491   UserColourScheme getSchemeFromButtons()
492   {
493
494     Color[] newColours = new Color[24];
495
496     int length = upperCaseButtons.size();
497     if (length < 24)
498     {
499       int i = 0;
500       for (JButton btn : upperCaseButtons)
501       {
502         newColours[i] = btn.getBackground();
503         i++;
504       }
505     }
506     else
507     {
508       for (int i = 0; i < 24; i++)
509       {
510         JButton button = upperCaseButtons.get(i);
511         newColours[i] = button.getBackground();
512       }
513     }
514
515     UserColourScheme ucs = new UserColourScheme(newColours);
516     ucs.setName(schemeName.getText());
517
518     if (caseSensitive.isSelected())
519     {
520       newColours = new Color[23];
521       length = lowerCaseButtons.size();
522       if (length < 23)
523       {
524         int i = 0;
525         for (JButton btn : lowerCaseButtons)
526         {
527           newColours[i] = btn.getBackground();
528           i++;
529         }
530       }
531       else
532       {
533         for (int i = 0; i < 23; i++)
534         {
535           JButton button = lowerCaseButtons.get(i);
536           newColours[i] = button.getBackground();
537         }
538       }
539       ucs.setLowerCaseColours(newColours);
540     }
541
542     return ucs;
543   }
544
545   /**
546    * Action on clicking Load scheme button.
547    * <ul>
548    * <li>Open a file chooser to browse for files with extension .jc</li>
549    * <li>Load in the colour scheme and transfer it to this panel's buttons</li>
550    * <li>Register the loaded colour scheme</li>
551    * </ul>
552    */
553   @Override
554   protected void loadbutton_actionPerformed()
555   {
556     upperCaseButtons = new ArrayList<JButton>();
557     lowerCaseButtons = new ArrayList<JButton>();
558
559     JalviewFileChooser chooser = new JalviewFileChooser("jc",
560             "Jalview User Colours");
561     chooser.setFileView(new JalviewFileView());
562     chooser.setDialogTitle(MessageManager
563             .getString("label.load_colour_scheme"));
564     chooser.setToolTipText(MessageManager.getString("action.load"));
565
566     int value = chooser.showOpenDialog(this);
567
568     if (value != JalviewFileChooser.APPROVE_OPTION)
569     {
570       return;
571     }
572     File choice = chooser.getSelectedFile();
573     Cache.setProperty(LAST_DIRECTORY, choice.getParent());
574
575     UserColourScheme ucs = ColourSchemeLoader.loadColourScheme(choice
576             .getAbsolutePath());
577     Color[] colors = ucs.getColours();
578     schemeName.setText(ucs.getSchemeName());
579
580     if (ucs.getLowerCaseColours() != null)
581     {
582       caseSensitive.setSelected(true);
583       lcaseColour.setEnabled(true);
584       resetButtonPanel(true);
585       for (int i = 0; i < lowerCaseButtons.size(); i++)
586       {
587         JButton button = lowerCaseButtons.get(i);
588         button.setBackground(ucs.getLowerCaseColours()[i]);
589       }
590     }
591     else
592     {
593       caseSensitive.setSelected(false);
594       lcaseColour.setEnabled(false);
595       resetButtonPanel(false);
596     }
597
598     for (int i = 0; i < upperCaseButtons.size(); i++)
599     {
600       JButton button = upperCaseButtons.get(i);
601       button.setBackground(colors[i]);
602     }
603
604     addNewColourScheme(choice.getPath());
605   }
606
607   /**
608    * Loads the user-defined colour scheme from the first file listed in property
609    * "USER_DEFINED_COLOURS". If this fails, returns an all-white colour scheme.
610    * 
611    * @return
612    */
613   public static UserColourScheme loadDefaultColours()
614   {
615     UserColourScheme ret = null;
616
617     String colours = Cache.getProperty(USER_DEFINED_COLOURS);
618     if (colours != null)
619     {
620       if (colours.indexOf("|") > -1)
621       {
622         colours = colours.substring(0, colours.indexOf("|"));
623       }
624       ret = ColourSchemeLoader.loadColourScheme(colours);
625     }
626
627     if (ret == null)
628     {
629       ret = new UserColourScheme("white");
630     }
631
632     return ret;
633   }
634
635   /**
636    * Action on pressing the Save button.
637    * <ul>
638    * <li>Check a name has been entered</li>
639    * <li>Warn if the name already exists, remove any existing scheme of the same
640    * name if overwriting</li>
641    * <li>Do the standard file chooser thing to write with extension .jc</li>
642    * <li>If saving changes (possibly not yet applied) to the currently selected
643    * colour scheme, then apply the changes, as it is too late to back out now</li>
644    * <li>Don't apply the changes if the currently selected scheme is different,
645    * to allow a new scheme to be configured and saved but not applied</li>
646    * </ul>
647    */
648   @Override
649   protected void savebutton_actionPerformed()
650   {
651     String name = schemeName.getText().trim();
652     if (name.length() < 1)
653     {
654       JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
655               .getString("label.user_colour_scheme_must_have_name"),
656               MessageManager.getString("label.no_name_colour_scheme"),
657               JvOptionPane.WARNING_MESSAGE);
658       return;
659     }
660
661     if (ColourSchemes.getInstance().nameExists(name))
662     {
663       int reply = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
664               MessageManager.formatMessage(
665                       "label.colour_scheme_exists_overwrite", new Object[] {
666                           name, name }),
667               MessageManager.getString("label.duplicate_scheme_name"),
668               JvOptionPane.YES_NO_OPTION);
669       if (reply != JvOptionPane.YES_OPTION)
670       {
671         return;
672       }
673     }
674     JalviewFileChooser chooser = new JalviewFileChooser("jc",
675             "Jalview User Colours");
676
677     JalviewFileView fileView = new JalviewFileView();
678     chooser.setFileView(fileView);
679     chooser.setDialogTitle(MessageManager
680             .getString("label.save_colour_scheme"));
681     chooser.setToolTipText(MessageManager.getString("action.save"));
682
683     int value = chooser.showSaveDialog(this);
684
685     if (value == JalviewFileChooser.APPROVE_OPTION)
686     {
687       File file = chooser.getSelectedFile();
688       addNewColourScheme(file.getPath());
689       saveToFile(file);
690
691       /*
692        * changes saved - apply to alignment if we are changing 
693        * the currently selected colour scheme
694        */
695       if (oldColourScheme != null
696               && name.equals(oldColourScheme.getSchemeName()))
697       {
698         applyButton_actionPerformed();
699       }
700     }
701   }
702
703   /**
704    * Adds the current colour scheme to the Jalview properties file so it is
705    * loaded on next startup, and updates the Colour menu in the parent
706    * AlignFrame (if there is one). Note this action does not including applying
707    * the colour scheme.
708    * 
709    * @param filePath
710    */
711   protected void addNewColourScheme(String filePath)
712   {
713     /*
714      * update the delimited list of user defined colour files in
715      * Jalview property USER_DEFINED_COLOURS
716      */
717     String defaultColours = Cache
718             .getDefault(USER_DEFINED_COLOURS, filePath);
719     if (defaultColours.indexOf(filePath) == -1)
720     {
721       if (defaultColours.length() > 0)
722       {
723         defaultColours = defaultColours.concat("|");
724       }
725       defaultColours = defaultColours.concat(filePath);
726     }
727     Cache.setProperty(USER_DEFINED_COLOURS, defaultColours);
728
729     /*
730      * construct and register the colour scheme
731      */
732     UserColourScheme ucs = getSchemeFromButtons();
733     ColourSchemes.getInstance().registerColourScheme(ucs);
734
735     /*
736      * update the Colour menu items
737      */
738     if (ap != null)
739     {
740       ap.alignFrame.buildColourMenu();
741     }
742   }
743
744   /**
745    * Saves the colour scheme to file in XML format
746    * 
747    * @param path
748    */
749   protected void saveToFile(File toFile)
750   {
751     /*
752      * build a Java model of colour scheme as XML, and 
753      * marshal to file
754      */
755     JalviewUserColours ucs = new JalviewUserColours();
756     String name = schemeName.getText();
757     ucs.setSchemeName(name);
758     try
759     {
760       PrintWriter out = new PrintWriter(new OutputStreamWriter(
761               new FileOutputStream(toFile), "UTF-8"));
762
763       for (int i = 0; i < buttonPanel.getComponentCount(); i++)
764       {
765         JButton button = (JButton) buttonPanel.getComponent(i);
766         Colour col = new Colour();
767         col.setName(button.getText());
768         col.setRGB(Format.getHexString(button.getBackground()));
769         ucs.addColour(col);
770       }
771       ucs.marshal(out);
772       out.close();
773     } catch (Exception ex)
774     {
775       ex.printStackTrace();
776     }
777   }
778
779   /**
780    * On cancel, restores the colour scheme that was selected before the dialogue
781    * was opened
782    */
783   @Override
784   protected void cancelButton_actionPerformed()
785   {
786     ap.alignFrame.changeColour(oldColourScheme);
787     ap.paintAlignment(true);
788
789     try
790     {
791       frame.setClosed(true);
792     } catch (Exception ex)
793     {
794     }
795   }
796
797   /**
798    * Action on selecting or deselecting the Case Sensitive option. When
799    * selected, separate buttons are shown for lower case residues, and the panel
800    * is resized to accommodate them. Also, the checkbox for 'apply colour to all
801    * lower case' is enabled.
802    */
803   @Override
804   public void caseSensitive_actionPerformed()
805   {
806     boolean selected = caseSensitive.isSelected();
807     resetButtonPanel(selected);
808     lcaseColour.setEnabled(selected);
809   }
810 }