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