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