801615ccef8a3728690993e29776e09300e20a38
[jalview.git] / src / jalview / gui / ColourMenuHelper.java
1 package jalview.gui;
2
3 import jalview.bin.Cache;
4 import jalview.datamodel.AnnotatedCollectionI;
5 import jalview.schemes.ColourSchemeI;
6 import jalview.schemes.ColourSchemeLoader;
7 import jalview.schemes.ColourSchemes;
8 import jalview.schemes.ResidueColourScheme;
9 import jalview.schemes.UserColourScheme;
10 import jalview.util.MessageManager;
11
12 import java.awt.Component;
13 import java.awt.event.ActionEvent;
14 import java.awt.event.ActionListener;
15 import java.awt.event.MouseAdapter;
16 import java.awt.event.MouseEvent;
17
18 import javax.swing.ButtonGroup;
19 import javax.swing.JMenu;
20 import javax.swing.JRadioButtonMenuItem;
21
22 public class ColourMenuHelper
23 {
24   public interface ColourChangeListener
25   {
26     void changeColour_actionPerformed(String name);
27   }
28
29   /**
30    * Adds items to the colour menu, as mutually exclusive members of a button
31    * group. The callback handler is responsible for the action on selecting any
32    * of these options. The callback method receives the name of the selected
33    * colour, or "None" or "User Defined". This method returns the ButtonGroup to
34    * which items were added.
35    * <ul>
36    * <li>None</li>
37    * <li>Clustal</li>
38    * <li>...other 'built-in' colours</li>
39    * <li>...any user-defined colours</li>
40    * <li>User Defined..</li>
41    * </ul>
42    * 
43    * @param colourMenu
44    *          the menu to attach items to
45    * @param client
46    *          a callback to handle menu selection
47    * @param coll
48    *          the data the menu is being built for
49    * @param simpleOnly
50    *          if true, only simple per-residue colour schemes are included
51    */
52   public static ButtonGroup addMenuItems(final JMenu colourMenu,
53           final ColourChangeListener client, AnnotatedCollectionI coll,
54           boolean simpleOnly)
55   {
56     /*
57      * ButtonGroup groups those items whose 
58      * selection is mutually exclusive
59      */
60     ButtonGroup colours = new ButtonGroup();
61
62     if (!simpleOnly)
63     {
64       JRadioButtonMenuItem noColourmenuItem = new JRadioButtonMenuItem(
65               MessageManager.getString("label.none"));
66       noColourmenuItem.setName(ResidueColourScheme.NONE);
67       noColourmenuItem.addActionListener(new ActionListener()
68       {
69         @Override
70         public void actionPerformed(ActionEvent e)
71         {
72           client.changeColour_actionPerformed(ResidueColourScheme.NONE);
73         }
74       });
75       colourMenu.add(noColourmenuItem);
76       colours.add(noColourmenuItem);
77     }
78
79     /*
80      * scan registered colour schemes (built-in or user-defined
81      * and add them to the menu (in the order they were registered)
82      */
83     Iterable<ColourSchemeI> colourSchemes = ColourSchemes.getInstance()
84             .getColourSchemes();
85     for (ColourSchemeI scheme : colourSchemes)
86     {
87       if (simpleOnly && !scheme.isSimple())
88       {
89         continue;
90       }
91
92       /*
93        * button text is i18n'd but the name is the canonical name of
94        * the colour scheme (inspected in setColourSelected())
95        */
96       final String name = scheme.getSchemeName();
97       String label = MessageManager.getStringOrReturn("label.colourScheme_"
98               + name.toLowerCase().replace(" ", "_"), name);
99       final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(label);
100       radioItem.setName(name);
101       radioItem.setEnabled(scheme.isApplicableTo(coll));
102       if (scheme instanceof UserColourScheme)
103       {
104         /*
105          * user-defined colour scheme loaded on startup or during the
106          * Jalview session; right-click on this offers the option to
107          * remove it as a colour choice (unless currently selected)
108          */
109         radioItem.addMouseListener(new MouseAdapter()
110         {
111           @Override
112           public void mousePressed(MouseEvent evt)
113           {
114             if (evt.isPopupTrigger() && !radioItem.isSelected()) // Mac
115             {
116               offerRemoval();
117             }
118           }
119
120           @Override
121           public void mouseReleased(MouseEvent evt)
122           {
123             if (evt.isPopupTrigger() && !radioItem.isSelected()) // Windows
124             {
125               offerRemoval();
126             }
127           }
128
129           void offerRemoval()
130           {
131             ActionListener al = radioItem.getActionListeners()[0];
132             radioItem.removeActionListener(al);
133             int option = JvOptionPane.showInternalConfirmDialog(
134                     Desktop.desktop, MessageManager
135                             .getString("label.remove_from_default_list"),
136                     MessageManager
137                             .getString("label.remove_user_defined_colour"),
138                     JvOptionPane.YES_NO_OPTION);
139             if (option == JvOptionPane.YES_OPTION)
140             {
141               ColourSchemes.getInstance().removeColourScheme(
142                       radioItem.getName());
143               colourMenu.remove(radioItem);
144               updatePreferences();
145             }
146             else
147             {
148               radioItem.addActionListener(al);
149             }
150           }
151         });
152       }
153       radioItem.addActionListener(new ActionListener()
154       {
155         @Override
156         public void actionPerformed(ActionEvent evt)
157         {
158           client.changeColour_actionPerformed(name);
159         }
160       });
161       colourMenu.add(radioItem);
162       colours.add(radioItem);
163     }
164
165     /*
166      * only add the option to load/configure a user-defined colour
167      * to the AlignFrame colour menu
168      */
169     if (client instanceof AlignFrame)
170     {
171       final String label = MessageManager.getString("action.user_defined");
172       JRadioButtonMenuItem userDefinedColour = new JRadioButtonMenuItem(
173               label);
174       userDefinedColour.addActionListener(new ActionListener()
175       {
176         @Override
177         public void actionPerformed(ActionEvent e)
178         {
179           client.changeColour_actionPerformed(ResidueColourScheme.USER_DEFINED);
180         }
181       });
182       colourMenu.add(userDefinedColour);
183       colours.add(userDefinedColour);
184     }
185
186     return colours;
187   }
188
189   /**
190    * Marks as selected the colour menu item matching the given name, or the
191    * first item ('None') if no match is found
192    * 
193    * @param colourMenu
194    * @param colourName
195    */
196   public static void setColourSelected(JMenu colourMenu, String colourName)
197   {
198     if (colourName == null)
199     {
200       return;
201     }
202
203     JRadioButtonMenuItem none = null;
204
205     /*
206      * select the radio button whose name matches the colour name
207      * (not the button text, as it may be internationalised)
208      */
209     for (Component menuItem : colourMenu.getMenuComponents())
210     {
211       if (menuItem instanceof JRadioButtonMenuItem)
212       {
213         String buttonName = ((JRadioButtonMenuItem) menuItem).getName();
214         if (colourName.equals(buttonName))
215         {
216           ((JRadioButtonMenuItem) menuItem).setSelected(true);
217           return;
218         }
219         if (ResidueColourScheme.NONE.equals(buttonName))
220         {
221           none = (JRadioButtonMenuItem) menuItem;
222         }
223       }
224     }
225     if (none != null)
226     {
227       none.setSelected(true);
228     }
229   }
230
231   /**
232    * Marks as selected the colour menu item matching the given colour scheme, or
233    * the first item ('None') if no match is found
234    * 
235    * @param colourMenu
236    * @param cs
237    */
238   public static void setColourSelected(JMenu colourMenu, ColourSchemeI cs)
239   {
240     setColourSelected(colourMenu, cs == null ? ResidueColourScheme.NONE
241             : cs.getSchemeName());
242   }
243
244   /**
245    * Updates the USER_DEFINE_COLOURS preference to remove any de-registered
246    * colour scheme
247    */
248   static void updatePreferences()
249   {
250     StringBuilder coloursFound = new StringBuilder();
251     String[] files = Cache.getProperty("USER_DEFINED_COLOURS").split("\\|");
252
253     /*
254      * the property does not include the scheme name, it is in the file;
255      * so just load the colour schemes and discard any whose name is not
256      * registered
257      */
258     for (String file : files)
259     {
260       try
261       {
262         UserColourScheme ucs = ColourSchemeLoader.loadColourScheme(file);
263         if (ucs != null
264                 && ColourSchemes.getInstance().nameExists(ucs.getName()))
265         {
266           if (coloursFound.length() > 0)
267           {
268             coloursFound.append("|");
269           }
270           coloursFound.append(file);
271         }
272       } catch (Exception ex)
273       {
274         System.out.println("Error loading User ColourFile\n" + ex);
275       }
276     }
277
278     if (coloursFound.toString().length() > 1)
279     {
280       Cache.setProperty("USER_DEFINED_COLOURS", coloursFound.toString());
281     }
282     else
283     {
284       Cache.applicationProperties.remove("USER_DEFINED_COLOURS");
285     }
286   }
287 }