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