package esmska.gui;
import esmska.data.CountryPrefix;
import esmska.data.Gateways;
import esmska.data.Gateway;
import esmska.data.Gateway.Feature;
import esmska.data.Gateways.Events;
import esmska.data.Tuple;
import esmska.data.event.ValuedEvent;
import esmska.data.event.ValuedListener;
import esmska.utils.L10N;
import java.awt.Component;
import java.awt.SystemColor;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.ResourceBundle;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.commons.lang.math.RandomUtils;
/** JComboBox showing available gateways.
*
* @author ripper
*/
public class GatewayComboBox extends JComboBox {
private static final ResourceBundle l10n = L10N.l10nBundle;
private static final String RES = "/esmska/resources/";
private static final GatewayComboBoxRenderer cellRenderer = new GatewayComboBoxRenderer();
private static final Gateways gateways = Gateways.getInstance();
private DefaultComboBoxModel model = new DefaultComboBoxModel();
/** used only for non-existing gateways */
private String gatewayName;
/** Current phone number filter */
private String filter;
public GatewayComboBox() {
updateGateways();
setModel(model);
setRenderer(cellRenderer);
if (model.getSize() > 0) {
setSelectedIndex(0);
}
//add listener to gateway updates
Gateways.getInstance().addValuedListener(new ValuedListener<Gateways.Events, Gateway>() {
@Override
public void eventOccured(ValuedEvent<Events, Gateway> e) {
switch (e.getEvent()) {
case ADDED_GATEWAY:
case ADDED_GATEWAYS:
case CLEARED_GATEWAYS:
case REMOVED_GATEWAY:
case REMOVED_GATEWAYS:
case FAVORITES_UPDATED:
case HIDDEN_UPDATED:
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
updateGateways();
}
});
}
}
});
//add listener to change tooltip depending on the chosen gateway
this.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
switch (e.getStateChange()) {
case ItemEvent.DESELECTED:
setToolTipText(null);
break;
case ItemEvent.SELECTED:
setToolTipText(cellRenderer.generateTooltip(getSelectedGateway()));
break;
}
}
});
}
/** Get selected gateway in list or null if none selected. */
public Gateway getSelectedGateway() {
return (Gateway) getSelectedItem();
}
/** Get name of the selected gateway.
* If unknown gateway selected (selection cleared), it returns previously inserted
* gateway name (may be null).
*/
public String getSelectedGatewayName() {
return (getSelectedGateway() != null ? getSelectedGateway().getName() : gatewayName);
}
/** Set currently selected gateway by it's name.
* Use null for clearing the selection. Non-existing gateway will also clear the selection.
*/
public void setSelectedGateway(String gatewayName) {
this.gatewayName = gatewayName;
Gateway gateway = gateways.get(gatewayName);
if (model.getIndexOf(gateway) < 0) {
setSelectedItem(null);
} else {
setSelectedItem(gateway);
}
}
/** Select gateway according to phone number or phone number prefix.
* Searches through available (displayed) gateways and selects the best
* suited on supporting this phone number. Clear selection if no
* such gateway is found or just non-recommended gateways are suggested.
*
* @param number phone number or it's prefix. The minimum length is two characters,
* for shorter input (or null) the method does nothing.
* @return boolean whether there were more than 1 options for the suggested gateway
* (and therefore some choice was done)
*/
public boolean selectSuggestedGateway(String number) {
Tuple<ArrayList<Gateway>, Boolean> tuple = gateways.suggestGateway(number);
ArrayList<Gateway> gws = tuple.get1();
boolean recommended = tuple.get2();
if (gws.isEmpty()) {
setSelectedGateway(null);
} else if (gws.contains(getSelectedGateway())) {
//suggested gateway already selected, do nothing
} else {
if (recommended) {
//recommended, select random one
setSelectedGateway(gws.get(RandomUtils.nextInt(gws.size())).getName());
} else {
//not recommended, leave selection empty and let user click
//on "Suggest" button if he wants
setSelectedGateway(null);
}
}
return gws.size() > 1;
}
/** If there are more than 1 suggested gateways for this phone number,
* this method will select the next one.
*/
public void selectNextSuggestedGateway(String number) {
ArrayList<Gateway> gws = gateways.suggestGateway(number).get1();
if (!gws.isEmpty()) {
int index = gws.indexOf(getSelectedGateway());
if (index >= 0) {
//traverse to next gateway
index = (index + 1) % gws.size();
} else {
index = RandomUtils.nextInt(gws.size());
}
setSelectedGateway(gws.get(index).getName());
} else {
setSelectedGateway(null);
}
}
/** Filter available gateways to display only those that are capable
* of sending defined phone number or phone number prefix.
*
* @param number phone number or its prefix; use null to clear filter
*/
public void setFilter(String number) {
this.filter = number;
updateGateways();
}
/** Update model when gateways are updated or when filter is changed */
private void updateGateways() {
String opName = getSelectedGatewayName();
model.removeAllElements();
for (Gateway gw : gateways.getVisible()) {
if (StringUtils.isEmpty(filter) || Gateways.isNumberSupported(gw, filter)) {
model.addElement(gw);
}
}
setSelectedGateway(opName);
}
/** Renderer for items in GatewayComboBox */
public static class GatewayComboBoxRenderer extends DefaultListCellRenderer {
private final ListCellRenderer lafRenderer = new JList().getCellRenderer();
private final URL keyringIconURI = getClass().getResource(RES + "keyring-16.png");
private final String pattern = l10n.getString("GatewayComboBox.gatewayTooltip");
private final String noReg = l10n.getString("GatewayComboBox.noRegistration");
private final String registration = MessageFormat.format("<img src=\"{0}\"> ", keyringIconURI) +
l10n.getString("GatewayComboBox.needRegistration");
private final String international = l10n.getString("GatewayComboBox.international");
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component c = lafRenderer.getListCellRendererComponent(list, value,
index, isSelected, cellHasFocus);
if (!(value instanceof Gateway)) {
return c;
}
JLabel label = (JLabel) c;
Gateway gateway = (Gateway)value;
adjustLabel(label, gateway);
return label;
}
/** Do all the adjustments to a JLabel needed to render this item properly */
public void adjustLabel(JLabel label, Gateway gateway) {
label.setText(gateway.getName());
label.setIcon(gateway.getIcon());
label.setToolTipText(generateTooltip(gateway));
if (gateway.isHidden()) {
label.setForeground(SystemColor.textInactiveText);
}
}
/** Generate tooltip with gateway info */
private String generateTooltip(Gateway gateway) {
if (gateway == null) {
return null;
}
String country = CountryPrefix.extractCountryCode(gateway.getName());
String local = MessageFormat.format(l10n.getString("GatewayComboBox.onlyCountry"), country);
String description = WordUtils.wrap(gateway.getDescription(), 50, "<br> ", false);
String tooltip = MessageFormat.format(pattern,
gateway.getName(), gateway.getWebsite(), description,
gateway.hasFeature(Feature.LOGIN_ONLY) ? registration : noReg,
Gateways.convertDelayToHumanString(gateway.getDelayBetweenMessages(), false),
country.equals(CountryPrefix.INTERNATIONAL_CODE) ? international : local,
gateway.getVersion());
return tooltip;
}
}
}