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