/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.gui;
import jalview.bin.Cache;
import jalview.datamodel.AnnotatedCollectionI;
import jalview.schemes.ColourSchemeI;
import jalview.schemes.ColourSchemeLoader;
import jalview.schemes.ColourSchemes;
import jalview.schemes.ResidueColourScheme;
import jalview.schemes.UserColourScheme;
import jalview.util.MessageManager;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.ButtonGroup;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
public class ColourMenuHelper
{
public interface ColourChangeListener
{
/**
* Change colour scheme to the selected scheme
*
* @param name
* the registered (unique) name of a colour scheme
*/
void changeColour_actionPerformed(String name);
}
/**
* Adds items to the colour menu, as mutually exclusive members of a button
* group. The callback handler is responsible for the action on selecting any
* of these options. The callback method receives the name of the selected
* colour, or "None" or "User Defined". This method returns the ButtonGroup to
* which items were added.
*
* - None
* - Clustal
* - ...other 'built-in' colours
* - ...any user-defined colours
* - User Defined..(only for AlignFrame menu)
*
*
* @param colourMenu
* the menu to attach items to
* @param client
* a callback to handle menu selection
* @param coll
* the data the menu is being built for
* @param simpleOnly
* if true, only simple per-residue colour schemes are included
*/
public static ButtonGroup addMenuItems(final JMenu colourMenu,
final ColourChangeListener client, AnnotatedCollectionI coll,
boolean simpleOnly)
{
/*
* ButtonGroup groups those items whose
* selection is mutually exclusive
*/
ButtonGroup colours = new ButtonGroup();
if (!simpleOnly)
{
JRadioButtonMenuItem noColourmenuItem = new JRadioButtonMenuItem(
MessageManager.getString("label.none"));
noColourmenuItem.setName(ResidueColourScheme.NONE);
noColourmenuItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
client.changeColour_actionPerformed(ResidueColourScheme.NONE);
}
});
colourMenu.add(noColourmenuItem);
colours.add(noColourmenuItem);
}
/*
* scan registered colour schemes (built-in or user-defined)
* and add them to the menu (in the order they were registered)
*/
Iterable colourSchemes = ColourSchemes.getInstance()
.getColourSchemes();
for (ColourSchemeI scheme : colourSchemes)
{
if (simpleOnly && !scheme.isSimple())
{
continue;
}
/*
* button text is i18n'd but the name is the canonical name of
* the colour scheme (inspected in setColourSelected())
*/
final String name = scheme.getSchemeName();
String label = MessageManager.getStringOrReturn(
"label.colourScheme_", name);
final JRadioButtonMenuItem radioItem = new JRadioButtonMenuItem(
label);
radioItem.setName(name);
radioItem.setEnabled(scheme.isApplicableTo(coll));
if (scheme instanceof UserColourScheme)
{
/*
* user-defined colour scheme loaded on startup or during the
* Jalview session; right-click on this offers the option to
* remove it as a colour choice (unless currently selected)
*/
radioItem.addMouseListener(new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent evt)
{
if (evt.isPopupTrigger() && !radioItem.isSelected()) // Mac
{
offerRemoval();
}
}
@Override
public void mouseReleased(MouseEvent evt)
{
if (evt.isPopupTrigger() && !radioItem.isSelected()) // Windows
{
offerRemoval();
}
}
void offerRemoval()
{
ActionListener al = radioItem.getActionListeners()[0];
radioItem.removeActionListener(al);
int option = JvOptionPane.showInternalConfirmDialog(
Desktop.getDesktopPane(),
MessageManager
.getString("label.remove_from_default_list"),
MessageManager
.getString("label.remove_user_defined_colour"),
JvOptionPane.YES_NO_OPTION);
if (option == JvOptionPane.YES_OPTION)
{
ColourSchemes.getInstance()
.removeColourScheme(radioItem.getName());
colourMenu.remove(radioItem);
updatePreferences();
}
else
{
radioItem.addActionListener(al);
}
}
});
}
radioItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent evt)
{
client.changeColour_actionPerformed(name);
}
});
colourMenu.add(radioItem);
colours.add(radioItem);
}
/*
* only add the option to load/configure a user-defined colour
* to the AlignFrame colour menu
*/
if (client instanceof AlignFrame)
{
final String label = MessageManager.getString("action.user_defined");
JRadioButtonMenuItem userDefinedColour = new JRadioButtonMenuItem(
label);
userDefinedColour.setName(ResidueColourScheme.USER_DEFINED_MENU);
userDefinedColour.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
client.changeColour_actionPerformed(
ResidueColourScheme.USER_DEFINED_MENU);
}
});
colourMenu.add(userDefinedColour);
colours.add(userDefinedColour);
}
return colours;
}
/**
* Marks as selected the colour menu item matching the given colour scheme, or
* the first item ('None') if no match is found. If the colour scheme is a
* user defined scheme, but not in the menu (this arises if a new scheme is
* defined and applied but not saved to file), then menu option "User
* Defined.." is selected.
*
* @param colourMenu
* @param cs
*/
public static void setColourSelected(JMenu colourMenu, ColourSchemeI cs)
{
String colourName = cs == null ? ResidueColourScheme.NONE
: cs.getSchemeName();
JRadioButtonMenuItem none = null;
JRadioButtonMenuItem userDefined = null;
/*
* select the radio button whose name matches the colour name
* (not the button text, as it may be internationalised)
*/
for (Component menuItem : colourMenu.getMenuComponents())
{
if (menuItem instanceof JRadioButtonMenuItem)
{
JRadioButtonMenuItem radioButton = (JRadioButtonMenuItem) menuItem;
String buttonName = radioButton.getName();
if (buttonName.equals(colourName))
{
radioButton.setSelected(true);
return;
}
if (ResidueColourScheme.NONE.equals(buttonName))
{
none = radioButton;
}
if (ResidueColourScheme.USER_DEFINED_MENU.equals(buttonName))
{
userDefined = radioButton;
}
}
}
/*
* no match by name; select User Defined.. if current scheme is a
* user defined one, else select None
*/
if (cs instanceof UserColourScheme && userDefined != null)
{
userDefined.setSelected(true);
}
else if (none != null)
{
none.setSelected(true);
}
}
/**
* Updates the USER_DEFINE_COLOURS preference to remove any de-registered
* colour scheme
*/
static void updatePreferences()
{
StringBuilder coloursFound = new StringBuilder();
String[] files = Cache.getProperty(Preferences.USER_DEFINED_COLOURS).split("\\|");
/*
* the property does not include the scheme name, it is in the file;
* so just load the colour schemes and discard any whose name is not
* registered
*/
for (String file : files)
{
try
{
UserColourScheme ucs = ColourSchemeLoader.loadColourScheme(file);
if (ucs != null
&& ColourSchemes.getInstance().nameExists(ucs.getName()))
{
if (coloursFound.length() > 0)
{
coloursFound.append("|");
}
coloursFound.append(file);
}
} catch (Exception ex)
{
System.out.println("Error loading User ColourFile\n" + ex);
}
}
if (coloursFound.toString().length() > 1)
{
Cache.setProperty(Preferences.USER_DEFINED_COLOURS, coloursFound.toString());
}
else
{
Cache.removePropertyNoSave(Preferences.USER_DEFINED_COLOURS);
}
}
}