JAL-2401 Lower Case Colour toggleable, help text added
[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.datamodel.SequenceGroup;
25 import jalview.io.JalviewFileChooser;
26 import jalview.io.JalviewFileView;
27 import jalview.jbgui.GUserDefinedColours;
28 import jalview.schemes.ColourSchemeI;
29 import jalview.schemes.ResidueProperties;
30 import jalview.schemes.UserColourScheme;
31 import jalview.util.ColorUtils;
32 import jalview.util.MessageManager;
33
34 import java.awt.Color;
35 import java.awt.Font;
36 import java.awt.event.ActionEvent;
37 import java.awt.event.MouseEvent;
38 import java.io.File;
39 import java.io.FileInputStream;
40 import java.io.FileOutputStream;
41 import java.io.InputStreamReader;
42 import java.io.OutputStreamWriter;
43 import java.io.PrintWriter;
44 import java.util.ArrayList;
45 import java.util.Hashtable;
46 import java.util.StringTokenizer;
47
48 import javax.swing.JButton;
49 import javax.swing.JInternalFrame;
50 import javax.swing.event.ChangeEvent;
51 import javax.swing.event.ChangeListener;
52
53 /**
54  * This panel allows the user to assign colours to Amino Acid residue codes, and
55  * save the colour scheme.
56  * 
57  * @author Andrew Waterhouse
58  * @author Mungo Carstairs
59  */
60 public class UserDefinedColours extends GUserDefinedColours implements
61         ChangeListener
62 {
63   private static final int MY_FRAME_HEIGHT = 420;
64
65   private static final int MY_FRAME_WIDTH = 810;
66
67   private static final int MY_FRAME_WIDTH_CASE_SENSITIVE = 970;
68
69   AlignmentPanel ap;
70
71   SequenceGroup seqGroup;
72
73   ColourSchemeI oldColourScheme;
74
75   JInternalFrame frame;
76
77   JalviewStructureDisplayI jmol;
78
79   ArrayList<JButton> upperCaseButtons;
80
81   ArrayList<JButton> lowerCaseButtons;
82
83   /**
84    * Creates a new UserDefinedColours object.
85    * 
86    * @param ap
87    *          DOCUMENT ME!
88    * @param sg
89    *          DOCUMENT ME!
90    */
91   public UserDefinedColours(AlignmentPanel ap, SequenceGroup sg)
92   {
93     super();
94
95     lcaseColour.setEnabled(false);
96
97     this.ap = ap;
98     seqGroup = sg;
99
100     if (seqGroup != null)
101     {
102       oldColourScheme = seqGroup.cs;
103     }
104     else
105     {
106       oldColourScheme = ap.av.getGlobalColourScheme();
107     }
108
109     if (oldColourScheme instanceof UserColourScheme)
110     {
111       schemeName.setText(((UserColourScheme) oldColourScheme).getName());
112       if (((UserColourScheme) oldColourScheme).getLowerCaseColours() != null)
113       {
114         caseSensitive.setSelected(true);
115         lcaseColour.setEnabled(true);
116         resetButtonPanel(true);
117       }
118       else
119       {
120         resetButtonPanel(false);
121       }
122     }
123     else
124     {
125       resetButtonPanel(false);
126     }
127
128     showFrame();
129   }
130
131   public UserDefinedColours(JalviewStructureDisplayI jmol,
132           ColourSchemeI oldcs)
133   {
134     super();
135     this.jmol = jmol;
136
137     colorChooser.getSelectionModel().addChangeListener(this);
138
139     oldColourScheme = oldcs;
140
141     if (oldColourScheme instanceof UserColourScheme)
142     {
143       schemeName.setText(((UserColourScheme) oldColourScheme).getName());
144     }
145
146     resetButtonPanel(false);
147
148     showFrame();
149
150   }
151
152   void showFrame()
153   {
154     colorChooser.getSelectionModel().addChangeListener(this);
155     frame = new JInternalFrame();
156     frame.setContentPane(this);
157     Desktop.addInternalFrame(frame,
158             MessageManager.getString("label.user_defined_colours"),
159             MY_FRAME_WIDTH, MY_FRAME_HEIGHT, true);
160
161     if (seqGroup != null)
162     {
163       frame.setTitle(frame.getTitle() + " (" + seqGroup.getName() + ")");
164     }
165   }
166
167   void resetButtonPanel(boolean caseSensitive)
168   {
169     buttonPanel.removeAll();
170
171     if (upperCaseButtons == null)
172     {
173       upperCaseButtons = new ArrayList<JButton>();
174     }
175
176     JButton button;
177     String label;
178     for (int i = 0; i < 20; i++)
179     {
180       if (caseSensitive)
181       {
182         label = ResidueProperties.aa[i];
183       }
184       else
185       {
186         label = ResidueProperties.aa2Triplet.get(ResidueProperties.aa[i])
187                 .toString();
188       }
189
190       button = makeButton(label, ResidueProperties.aa[i], upperCaseButtons,
191               i);
192
193       buttonPanel.add(button);
194     }
195
196     buttonPanel.add(makeButton("B", "B", upperCaseButtons, 20));
197     buttonPanel.add(makeButton("Z", "Z", upperCaseButtons, 21));
198     buttonPanel.add(makeButton("X", "X", upperCaseButtons, 22));
199     buttonPanel.add(makeButton("Gap", "-", upperCaseButtons, 23));
200
201     if (!caseSensitive)
202     {
203       gridLayout.setRows(6);
204       gridLayout.setColumns(4);
205     }
206     else
207     {
208       gridLayout.setRows(7);
209       int cols = 7;
210       gridLayout.setColumns(cols + 1);
211
212       if (lowerCaseButtons == null)
213       {
214         lowerCaseButtons = new ArrayList<JButton>();
215       }
216
217       for (int i = 0; i < 20; i++)
218       {
219         int row = i / cols + 1;
220         int index = (row * cols) + i;
221         button = makeButton(ResidueProperties.aa[i].toLowerCase(),
222                 ResidueProperties.aa[i].toLowerCase(), lowerCaseButtons, i);
223
224         buttonPanel.add(button, index);
225       }
226     }
227
228     if (caseSensitive)
229     {
230       buttonPanel.add(makeButton("b", "b", lowerCaseButtons, 20));
231       buttonPanel.add(makeButton("z", "z", lowerCaseButtons, 21));
232       buttonPanel.add(makeButton("x", "x", lowerCaseButtons, 22));
233     }
234
235     // JAL-1360 widen the frame dynamically to accommodate case-sensitive AA
236     // codes
237     if (this.frame != null)
238     {
239       int newWidth = caseSensitive ? MY_FRAME_WIDTH_CASE_SENSITIVE
240               : MY_FRAME_WIDTH;
241       this.frame.setSize(newWidth, this.frame.getHeight());
242     }
243
244     buttonPanel.validate();
245     validate();
246   }
247
248   /**
249    * ChangeListener handler for when a colour is picked in the colour chooser.
250    * The action is to apply the colour to all selected buttons as their
251    * background colour. Foreground colour (text) is set to a lighter shade in
252    * order to highlight which buttons are selected. If 'Lower Case Colour' is
253    * active, then the colour is applied to all lower case buttons (as well as
254    * the Lower Case Colour button itself).
255    * 
256    * @param evt
257    */
258   @Override
259   public void stateChanged(ChangeEvent evt)
260   {
261     JButton button = null;
262     final Color newColour = colorChooser.getColor();
263     for (int i = 0; i < selectedButtons.size(); i++)
264     {
265       button = selectedButtons.get(i);
266       button.setBackground(newColour);
267       button.setForeground(ColorUtils.brighterThan(newColour));
268     }
269     if (button == lcaseColour)
270     {
271       button.setForeground(Color.black);
272       for (int i = 0; i < lowerCaseButtons.size(); i++)
273       {
274         button = lowerCaseButtons.get(i);
275         button.setBackground(newColour);
276         button.setForeground(ColorUtils.brighterThan(button.getBackground()));
277       }
278     }
279   }
280
281   /**
282    * Performs actions when a residue button is clicked. This manages the button
283    * selection set (highlighted by brighter foreground text).
284    * <p>
285    * On select button(s) with Ctrl/click or Shift/click: set button foreground
286    * text to brighter than background.
287    * <p>
288    * On unselect button(s) with Ctrl/click on selected, or click to release
289    * current selection: reset foreground text to darker than background.
290    * <p>
291    * Simple click: clear selection (resetting foreground to darker); set clicked
292    * button foreground to brighter
293    * <p>
294    * Finally, synchronize the colour chooser to the colour of the first button
295    * in the selected set.
296    * 
297    * @param e
298    */
299   public void colourButtonPressed(MouseEvent e)
300   {
301     JButton pressed = (JButton) e.getSource();
302
303     if (e.isShiftDown())
304     {
305       JButton start, end = (JButton) e.getSource();
306       if (selectedButtons.size() > 0)
307       {
308         start = selectedButtons.get(selectedButtons.size() - 1);
309       }
310       else
311       {
312         start = (JButton) e.getSource();
313       }
314
315       int startIndex = 0, endIndex = 0;
316       for (int b = 0; b < buttonPanel.getComponentCount(); b++)
317       {
318         if (buttonPanel.getComponent(b) == start)
319         {
320           startIndex = b;
321         }
322         if (buttonPanel.getComponent(b) == end)
323         {
324           endIndex = b;
325         }
326       }
327
328       if (startIndex > endIndex)
329       {
330         int temp = startIndex;
331         startIndex = endIndex;
332         endIndex = temp;
333       }
334
335       for (int b = startIndex; b <= endIndex; b++)
336       {
337         JButton button = (JButton) buttonPanel.getComponent(b);
338         if (!selectedButtons.contains(button))
339         {
340           button.setForeground(ColorUtils.brighterThan(button
341                   .getBackground()));
342           selectedButtons.add(button);
343         }
344       }
345     }
346     else if (!e.isControlDown())
347     {
348       for (int b = 0; b < selectedButtons.size(); b++)
349       {
350         JButton button = selectedButtons.get(b);
351         button.setForeground(ColorUtils.darkerThan(button.getBackground()));
352       }
353       selectedButtons.clear();
354       pressed.setForeground(ColorUtils.brighterThan(pressed.getBackground()));
355       selectedButtons.add(pressed);
356
357     }
358     else if (e.isControlDown())
359     {
360       if (selectedButtons.contains(pressed))
361       {
362         pressed.setForeground(ColorUtils.darkerThan(pressed.getBackground()));
363         selectedButtons.remove(pressed);
364       }
365       else
366       {
367         pressed.setForeground(ColorUtils.brighterThan(pressed
368                 .getBackground()));
369         selectedButtons.add(pressed);
370       }
371     }
372
373     if (selectedButtons.size() > 0)
374     {
375       colorChooser.setColor((selectedButtons.get(0)).getBackground());
376     }
377   }
378
379   /**
380    * DOCUMENT ME!
381    * 
382    * @param label
383    *          DOCUMENT ME!
384    * @param aa
385    *          DOCUMENT ME!
386    */
387   JButton makeButton(String label, String aa,
388           ArrayList<JButton> caseSensitiveButtons, int buttonIndex)
389   {
390     final JButton button;
391     Color col;
392
393     if (buttonIndex < caseSensitiveButtons.size())
394     {
395       button = caseSensitiveButtons.get(buttonIndex);
396       col = button.getBackground();
397     }
398     else
399     {
400       button = new JButton();
401       button.addMouseListener(new java.awt.event.MouseAdapter()
402       {
403         @Override
404         public void mouseClicked(MouseEvent e)
405         {
406           colourButtonPressed(e);
407         }
408       });
409
410       caseSensitiveButtons.add(button);
411
412       col = Color.white;
413       if (oldColourScheme != null)
414       {
415         try
416         {
417           col = oldColourScheme.findColour(aa.charAt(0), -1, null);
418         } catch (Exception ex)
419         {
420         }
421       }
422     }
423
424     if (caseSensitive.isSelected())
425     {
426       button.setMargin(new java.awt.Insets(2, 2, 2, 2));
427     }
428     else
429     {
430       button.setMargin(new java.awt.Insets(2, 14, 2, 14));
431     }
432
433     button.setOpaque(true); // required for the next line to have effect
434     button.setBackground(col);
435     button.setText(label);
436     button.setForeground(ColorUtils.darkerThan(col));
437     button.setFont(new java.awt.Font("Verdana", Font.BOLD, 10));
438
439     return button;
440   }
441
442   /**
443    * DOCUMENT ME!
444    * 
445    * @param e
446    *          DOCUMENT ME!
447    */
448   @Override
449   protected void okButton_actionPerformed(ActionEvent e)
450   {
451     if (isNoSelectionMade())
452     {
453       JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
454               .getString("label.no_colour_selection_in_scheme"),
455               MessageManager.getString("label.no_colour_selection_warn"),
456               JvOptionPane.WARNING_MESSAGE);
457     }
458     else
459     {
460       applyButton_actionPerformed(null);
461
462       try
463       {
464         frame.setClosed(true);
465       } catch (Exception ex)
466       {
467       }
468     }
469   }
470
471   /**
472    * Returns true if the user has not made any colour selection (including if
473    * 'case-sensitive' selected and no lower-case colour chosen).
474    * 
475    * @return
476    */
477   protected boolean isNoSelectionMade()
478   {
479     final boolean noUpperCaseSelected = upperCaseButtons == null
480             || upperCaseButtons.isEmpty();
481     final boolean noLowerCaseSelected = caseSensitive.isSelected()
482             && (lowerCaseButtons == null || lowerCaseButtons.isEmpty());
483     final boolean noSelectionMade = noUpperCaseSelected
484             || noLowerCaseSelected;
485     return noSelectionMade;
486   }
487
488   /**
489    * DOCUMENT ME!
490    * 
491    * @param e
492    *          DOCUMENT ME!
493    */
494   @Override
495   protected void applyButton_actionPerformed(ActionEvent e)
496   {
497     if (isNoSelectionMade())
498     {
499       JvOptionPane.showMessageDialog(Desktop.desktop, MessageManager
500               .getString("label.no_colour_selection_in_scheme"),
501               MessageManager.getString("label.no_colour_selection_warn"),
502               JvOptionPane.WARNING_MESSAGE);
503
504     }
505     UserColourScheme ucs = getSchemeFromButtons();
506     ucs.setName(schemeName.getText());
507
508     if (seqGroup != null)
509     {
510       seqGroup.cs = ucs;
511       ap.paintAlignment(true);
512     }
513     else if (ap != null)
514     {
515       ap.alignFrame.changeColour(ucs);
516     }
517     else if (jmol != null)
518     {
519       jmol.setJalviewColourScheme(ucs);
520     }
521   }
522
523   UserColourScheme getSchemeFromButtons()
524   {
525
526     Color[] newColours = new Color[24];
527
528     int length = upperCaseButtons.size();
529     if (length < 24)
530     {
531       int i = 0;
532       for (JButton btn : upperCaseButtons)
533       {
534         newColours[i] = btn.getBackground();
535         i++;
536       }
537     }
538     else
539     {
540       for (int i = 0; i < 24; i++)
541       {
542         JButton button = upperCaseButtons.get(i);
543         newColours[i] = button.getBackground();
544       }
545     }
546
547     UserColourScheme ucs = new UserColourScheme(newColours);
548
549     if (caseSensitive.isSelected())
550     {
551       newColours = new Color[23];
552       length = lowerCaseButtons.size();
553       if (length < 23)
554       {
555         int i = 0;
556         for (JButton btn : lowerCaseButtons)
557         {
558           newColours[i] = btn.getBackground();
559           i++;
560         }
561       }
562       else
563       {
564         for (int i = 0; i < 23; i++)
565         {
566           JButton button = lowerCaseButtons.get(i);
567           newColours[i] = button.getBackground();
568         }
569       }
570       ucs.setLowerCaseColours(newColours);
571     }
572
573     if (ap != null)
574     {
575       ucs.setThreshold(0, ap.av.isIgnoreGapsConsensus());
576     }
577
578     return ucs;
579   }
580
581   /**
582    * DOCUMENT ME!
583    * 
584    * @param e
585    *          DOCUMENT ME!
586    */
587   @Override
588   protected void loadbutton_actionPerformed(ActionEvent e)
589   {
590     upperCaseButtons = new ArrayList<JButton>();
591     lowerCaseButtons = new ArrayList<JButton>();
592
593     JalviewFileChooser chooser = new JalviewFileChooser("jc",
594             "Jalview User Colours");
595     chooser.setFileView(new JalviewFileView());
596     chooser.setDialogTitle(MessageManager
597             .getString("label.load_colour_scheme"));
598     chooser.setToolTipText(MessageManager.getString("action.load"));
599
600     int value = chooser.showOpenDialog(this);
601
602     if (value == JalviewFileChooser.APPROVE_OPTION)
603     {
604       File choice = chooser.getSelectedFile();
605       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
606       String defaultColours = jalview.bin.Cache.getDefault(
607               "USER_DEFINED_COLOURS", choice.getPath());
608       if (defaultColours.indexOf(choice.getPath()) == -1)
609       {
610         defaultColours = defaultColours.concat("|")
611                 .concat(choice.getPath());
612       }
613
614       jalview.bin.Cache.setProperty("USER_DEFINED_COLOURS", defaultColours);
615
616       UserColourScheme ucs = loadColours(choice.getAbsolutePath());
617       Color[] colors = ucs.getColours();
618       schemeName.setText(ucs.getName());
619
620       if (ucs.getLowerCaseColours() != null)
621       {
622         caseSensitive.setSelected(true);
623         lcaseColour.setEnabled(true);
624         resetButtonPanel(true);
625         for (int i = 0; i < lowerCaseButtons.size(); i++)
626         {
627           JButton button = lowerCaseButtons.get(i);
628           button.setBackground(ucs.getLowerCaseColours()[i]);
629         }
630
631       }
632       else
633       {
634         caseSensitive.setSelected(false);
635         lcaseColour.setEnabled(false);
636         resetButtonPanel(false);
637       }
638
639       for (int i = 0; i < upperCaseButtons.size(); i++)
640       {
641         JButton button = upperCaseButtons.get(i);
642         button.setBackground(colors[i]);
643       }
644
645     }
646   }
647
648   /**
649    * DOCUMENT ME!
650    * 
651    * @return DOCUMENT ME!
652    */
653   public static UserColourScheme loadDefaultColours()
654   {
655     UserColourScheme ret = null;
656
657     String colours = jalview.bin.Cache.getProperty("USER_DEFINED_COLOURS");
658     if (colours != null)
659     {
660       if (colours.indexOf("|") > -1)
661       {
662         colours = colours.substring(0, colours.indexOf("|"));
663       }
664
665       ret = loadColours(colours);
666     }
667
668     if (ret == null)
669     {
670       Color[] newColours = new Color[24];
671       for (int i = 0; i < 24; i++)
672       {
673         newColours[i] = Color.white;
674       }
675       ret = new UserColourScheme(newColours);
676     }
677
678     return ret;
679   }
680
681   /**
682    * DOCUMENT ME!
683    * 
684    * @param file
685    *          DOCUMENT ME!
686    * 
687    * @return DOCUMENT ME!
688    */
689   static UserColourScheme loadColours(String file)
690   {
691     UserColourScheme ucs = null;
692     Color[] newColours = null;
693     try
694     {
695       InputStreamReader in = new InputStreamReader(
696               new FileInputStream(file), "UTF-8");
697
698       jalview.schemabinding.version2.JalviewUserColours jucs = new jalview.schemabinding.version2.JalviewUserColours();
699
700       org.exolab.castor.xml.Unmarshaller unmar = new org.exolab.castor.xml.Unmarshaller(
701               jucs);
702       jucs = (jalview.schemabinding.version2.JalviewUserColours) unmar
703               .unmarshal(in);
704
705       newColours = new Color[24];
706
707       Color[] lowerCase = null;
708       boolean caseSensitive = false;
709
710       String name;
711       int index;
712       for (int i = 0; i < jucs.getColourCount(); i++)
713       {
714         name = jucs.getColour(i).getName();
715         if (ResidueProperties.aa3Hash.containsKey(name))
716         {
717           index = ResidueProperties.aa3Hash.get(name).intValue();
718         }
719         else
720         {
721           index = ResidueProperties.aaIndex[name.charAt(0)];
722         }
723         if (index == -1)
724         {
725           continue;
726         }
727
728         if (name.toLowerCase().equals(name))
729         {
730           if (lowerCase == null)
731           {
732             lowerCase = new Color[23];
733           }
734           caseSensitive = true;
735           lowerCase[index] = new Color(Integer.parseInt(jucs.getColour(i)
736                   .getRGB(), 16));
737         }
738         else
739         {
740           newColours[index] = new Color(Integer.parseInt(jucs.getColour(i)
741                   .getRGB(), 16));
742         }
743       }
744
745       if (newColours != null)
746       {
747         ucs = new UserColourScheme(newColours);
748         ucs.setName(jucs.getSchemeName());
749         if (caseSensitive)
750         {
751           ucs.setLowerCaseColours(lowerCase);
752         }
753       }
754
755     } catch (Exception ex)
756     {
757       // Could be Archive Jalview format
758       try
759       {
760         InputStreamReader in = new InputStreamReader(new FileInputStream(
761                 file), "UTF-8");
762
763         jalview.binding.JalviewUserColours jucs = new jalview.binding.JalviewUserColours();
764
765         jucs = jucs.unmarshal(in);
766
767         newColours = new Color[jucs.getColourCount()];
768
769         for (int i = 0; i < 24; i++)
770         {
771           newColours[i] = new Color(Integer.parseInt(jucs.getColour(i)
772                   .getRGB(), 16));
773         }
774         if (newColours != null)
775         {
776           ucs = new UserColourScheme(newColours);
777           ucs.setName(jucs.getSchemeName());
778         }
779       } catch (Exception ex2)
780       {
781         ex2.printStackTrace();
782       }
783
784       if (newColours == null)
785       {
786         System.out.println("Error loading User ColourFile\n" + ex);
787       }
788     }
789
790     return ucs;
791   }
792
793   /**
794    * DOCUMENT ME!
795    * 
796    * @param e
797    *          DOCUMENT ME!
798    */
799   @Override
800   protected void savebutton_actionPerformed(ActionEvent e)
801   {
802     if (schemeName.getText().trim().length() < 1)
803     {
804       JvOptionPane.showInternalMessageDialog(Desktop.desktop, MessageManager
805               .getString("label.user_colour_scheme_must_have_name"),
806               MessageManager.getString("label.no_name_colour_scheme"),
807               JvOptionPane.WARNING_MESSAGE);
808       return;
809     }
810
811     if (userColourSchemes != null
812             && userColourSchemes.containsKey(schemeName.getText()))
813     {
814       int reply = JvOptionPane.showInternalConfirmDialog(Desktop.desktop,
815               MessageManager.formatMessage(
816                       "label.colour_scheme_exists_overwrite", new Object[] {
817                           schemeName.getText(), schemeName.getText() }),
818               MessageManager.getString("label.duplicate_scheme_name"),
819               JvOptionPane.YES_NO_OPTION);
820       if (reply != JvOptionPane.YES_OPTION)
821       {
822         return;
823       }
824
825       userColourSchemes.remove(schemeName.getText());
826     }
827     JalviewFileChooser chooser = new JalviewFileChooser("jc",
828             "Jalview User Colours");
829
830     chooser.setFileView(new JalviewFileView());
831     chooser.setDialogTitle(MessageManager
832             .getString("label.save_colour_scheme"));
833     chooser.setToolTipText(MessageManager.getString("action.save"));
834
835     int value = chooser.showSaveDialog(this);
836
837     if (value == JalviewFileChooser.APPROVE_OPTION)
838     {
839       String choice = chooser.getSelectedFile().getPath();
840       String defaultColours = jalview.bin.Cache.getDefault(
841               "USER_DEFINED_COLOURS", choice);
842       if (defaultColours.indexOf(choice) == -1)
843       {
844         if (defaultColours.length() > 0)
845         {
846           defaultColours = defaultColours.concat("|");
847         }
848         defaultColours = defaultColours.concat(choice);
849       }
850
851       userColourSchemes.put(schemeName.getText(), getSchemeFromButtons());
852
853       ap.alignFrame.updateUserColourMenu();
854
855       jalview.bin.Cache.setProperty("USER_DEFINED_COLOURS", defaultColours);
856
857       jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
858
859       ucs.setSchemeName(schemeName.getText());
860       try
861       {
862         PrintWriter out = new PrintWriter(new OutputStreamWriter(
863                 new FileOutputStream(choice), "UTF-8"));
864
865         for (int i = 0; i < buttonPanel.getComponentCount(); i++)
866         {
867           JButton button = (JButton) buttonPanel.getComponent(i);
868           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
869           col.setName(button.getText());
870           col.setRGB(jalview.util.Format.getHexString(button
871                   .getBackground()));
872           ucs.addColour(col);
873         }
874
875         ucs.marshal(out);
876         out.close();
877       } catch (Exception ex)
878       {
879         ex.printStackTrace();
880       }
881     }
882   }
883
884   /**
885    * DOCUMENT ME!
886    * 
887    * @param e
888    *          DOCUMENT ME!
889    */
890   @Override
891   protected void cancelButton_actionPerformed(ActionEvent e)
892   {
893     if (ap != null)
894     {
895       if (seqGroup != null)
896       {
897         seqGroup.cs = oldColourScheme;
898       }
899       else if (ap != null)
900       {
901         ap.av.setGlobalColourScheme(oldColourScheme);
902       }
903       ap.paintAlignment(true);
904     }
905
906     if (jmol != null)
907     {
908       jmol.setJalviewColourScheme(oldColourScheme);
909     }
910
911     try
912     {
913       frame.setClosed(true);
914     } catch (Exception ex)
915     {
916     }
917   }
918
919   static Hashtable userColourSchemes;
920
921   public static Hashtable getUserColourSchemes()
922   {
923     return userColourSchemes;
924   }
925
926   public static void initUserColourSchemes(String files)
927   {
928     userColourSchemes = new Hashtable();
929
930     if (files == null || files.length() == 0)
931     {
932       return;
933     }
934
935     // In case colours can't be loaded, we'll remove them
936     // from the default list here.
937     StringBuffer coloursFound = new StringBuffer();
938     StringTokenizer st = new StringTokenizer(files, "|");
939     while (st.hasMoreElements())
940     {
941       String file = st.nextToken();
942       try
943       {
944         UserColourScheme ucs = loadColours(file);
945         if (ucs != null)
946         {
947           if (coloursFound.length() > 0)
948           {
949             coloursFound.append("|");
950           }
951           coloursFound.append(file);
952           userColourSchemes.put(ucs.getName(), ucs);
953         }
954       } catch (Exception ex)
955       {
956         System.out.println("Error loading User ColourFile\n" + ex);
957       }
958     }
959     if (!files.equals(coloursFound.toString()))
960     {
961       if (coloursFound.toString().length() > 1)
962       {
963         jalview.bin.Cache.setProperty("USER_DEFINED_COLOURS",
964                 coloursFound.toString());
965       }
966       else
967       {
968         jalview.bin.Cache.applicationProperties
969                 .remove("USER_DEFINED_COLOURS");
970       }
971     }
972   }
973
974   public static void removeColourFromDefaults(String target)
975   {
976     // The only way to find colours by name is to load them in
977     // In case colours can't be loaded, we'll remove them
978     // from the default list here.
979
980     userColourSchemes = new Hashtable();
981
982     StringBuffer coloursFound = new StringBuffer();
983     StringTokenizer st = new StringTokenizer(
984             jalview.bin.Cache.getProperty("USER_DEFINED_COLOURS"), "|");
985
986     while (st.hasMoreElements())
987     {
988       String file = st.nextToken();
989       try
990       {
991         UserColourScheme ucs = loadColours(file);
992         if (ucs != null && !ucs.getName().equals(target))
993         {
994           if (coloursFound.length() > 0)
995           {
996             coloursFound.append("|");
997           }
998           coloursFound.append(file);
999           userColourSchemes.put(ucs.getName(), ucs);
1000         }
1001       } catch (Exception ex)
1002       {
1003         System.out.println("Error loading User ColourFile\n" + ex);
1004       }
1005     }
1006
1007     if (coloursFound.toString().length() > 1)
1008     {
1009       jalview.bin.Cache.setProperty("USER_DEFINED_COLOURS",
1010               coloursFound.toString());
1011     }
1012     else
1013     {
1014       jalview.bin.Cache.applicationProperties
1015               .remove("USER_DEFINED_COLOURS");
1016     }
1017
1018   }
1019
1020   @Override
1021   public void caseSensitive_actionPerformed(ActionEvent e)
1022   {
1023     boolean selected = caseSensitive.isSelected();
1024     resetButtonPanel(selected);
1025     lcaseColour.setEnabled(selected);
1026     lcaseColour.setForeground(Color.GRAY);
1027   }
1028
1029   /**
1030    * Action on clicking 'Lower case colour', which results in changing colour of
1031    * all lower-case buttons when a colour is picked. A second click of the
1032    * button turns off this behaviour.
1033    */
1034   @Override
1035   public void lcaseColour_actionPerformed(ActionEvent e)
1036   {
1037     boolean enable = !selectedButtons.contains(lcaseColour);
1038     selectedButtons.clear();
1039     if (enable)
1040     {
1041       selectedButtons.add(lcaseColour);
1042       lcaseColour.setForeground(lowerCaseButtons.get(0).getForeground());
1043       lcaseColour.setForeground(Color.black);
1044     }
1045     else
1046     {
1047       lcaseColour.setBackground(Color.white);
1048       lcaseColour.setForeground(Color.gray);
1049     }
1050   }
1051 }