/*
*
* Copyright 2014 http://Bither.net
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* /
*/
package net.bither.viewsystem.components;
import com.google.common.base.Preconditions;
import net.bither.BitherUI;
import net.bither.bitherj.BitherjSettings;
import net.bither.bitherj.BitherjSettings.MarketType;
import net.bither.languages.Languages;
import net.bither.languages.MessageKey;
import net.bither.preference.UserPreference;
import net.bither.utils.ExchangeUtil;
import net.bither.utils.MarketUtil;
import net.bither.viewsystem.base.AccessibilityDecorator;
import net.bither.viewsystem.themes.Themes;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
/**
* <p>Utility to provide the following to UI:</p>
* <ul>
* <li>Provision of localised combo boxes</li>
* </ul>
*
* @since 0.0.1
*/
public class ComboBoxes {
/**
* The "languages" combo box action command
*/
public static final String LANGUAGES_COMMAND = "languages";
/**
* The "show balance" combo box action command
*/
public static final String SHOW_BALANCE_COMMAND = "showBalance";
/**
* The "themes" combo box action command
*/
public static final String THEMES_COMMAND = "themes";
/**
* The "paymentRequests" combo box action command
*/
public static final String PAYMENT_REQUESTS_COMMAND = "paymentRequests";
/**
* The "alert sound" combo box action command
*/
public static final String ALERT_SOUND_COMMAND = "alertSound";
/**
* The "receive sound" combo box action command
*/
public static final String RECEIVE_SOUND_COMMAND = "receiveSound";
/**
* The "Bitcoin symbol" combo box action command
*/
public static final String BITCOIN_SYMBOL_COMMAND = "bitcoinSymbol";
/**
* The "local symbol" combo box action command
*/
public static final String LOCAL_SYMBOL_COMMAND = "localSymbol";
/**
* The "placement" combo box action command
*/
public static final String PLACEMENT_COMMAND = "placement";
/**
* The "grouping separator" combo box action command
*/
public static final String GROUPING_COMMAND = "grouping";
/**
* The "decimal separator" combo box action command
*/
public static final String DECIMAL_COMMAND = "decimal";
/**
* The "exchange rate provider" combo box action command
*/
public static final String EXCHANGE_RATE_PROVIDER_COMMAND = "exchange";
/**
* The "currency" combo box action command
*/
public static final String CURRENCY_COMMAND = "currency";
/**
* The "Tor" combo box action command
*/
public static final String TOR_COMMAND = "tor";
/**
* The "Trezor" combo box action command
*/
public static final String TREZOR_COMMAND = "trezor";
/**
* Utilities have no public constructor
*/
private ComboBoxes() {
}
/**
* @param items The items for the combo box model
* @return A new editable combo box with default styling (no listener since it will cause early event triggers during set up)
*/
public static <T> JComboBox<T> newComboBox(T[] items) {
JComboBox<T> comboBox = new JComboBox<T>(items);
// Required to match icon button heights
comboBox.setMinimumSize(new Dimension(25, BitherUI.NORMAL_ICON_SIZE + 14));
// Required to blend in with panel
comboBox.setBackground(Themes.currentTheme.detailPanelBackground());
// Ensure we use the correct component orientation
comboBox.applyComponentOrientation(Languages.currentComponentOrientation());
// Ensure that keyboard navigation does not trigger action events
comboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
// Increase border insets to create better visual clarity
comboBox.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0));
// Push out the standard scrollbar beyond the default
comboBox.setMaximumRowCount(10);
// Adjust the scrollbar UI for the popup menu
Object popupComponent = comboBox.getUI().getAccessibleChild(comboBox, 0);
if (popupComponent instanceof JPopupMenu) {
JPopupMenu popupMenu = (JPopupMenu) popupComponent;
for (Component component : popupMenu.getComponents()) {
if ((component instanceof JScrollPane)) {
JScrollPane scrollPane = (JScrollPane) component;
// Ensure we maintain the overall theme
ScrollBarUIDecorator.apply(scrollPane, true);
}
}
}
return comboBox;
}
/**
* @return A new read only combo box (no listeners attached)
*/
public static <T> JComboBox<T> newReadOnlyComboBox(T[] items) {
JComboBox<T> comboBox = newComboBox(items);
comboBox.setEditable(false);
// Apply theme
comboBox.setBackground(Themes.currentTheme.readOnlyComboBox());
return comboBox;
}
/**
* @param listener The action listener to alert when the selection is made
* @param selectYes True if the "yes" option [0] should be selected, otherwise "no" is selected [1]
* @return A new "yes/no" read only combo box
*/
public static JComboBox<String> newYesNoComboBox(ActionListener listener, boolean selectYes) {
JComboBox<String> comboBox = newReadOnlyComboBox(new String[]{
Languages.safeText(MessageKey.YES),
Languages.safeText(MessageKey.NO)
});
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.YES);
comboBox.setEditable(false);
comboBox.setSelectedIndex(selectYes ? 0 : 1);
// Apply theme
comboBox.setBackground(Themes.currentTheme.readOnlyComboBox());
// Set the listener at the end to avoid spurious events
comboBox.addActionListener(listener);
return comboBox;
}
/**
* @param listener The action listener to alert when the selection is made
* @param alertSound True if the "yes" option should be pre-selected
* @return A new "yes/no" combo box
*/
public static JComboBox<String> newAlertSoundYesNoComboBox(ActionListener listener, boolean alertSound) {
JComboBox<String> comboBox = newYesNoComboBox(listener, alertSound);
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.ALERT_SOUND, MessageKey.ALERT_SOUND_TOOLTIP);
comboBox.setActionCommand(ALERT_SOUND_COMMAND);
return comboBox;
}
/**
* @param listener The action listener to alert when the selection is made
* @param receiveSound True if the "yes" option should be pre-selected
* @return A new "yes/no" combo box
*/
public static JComboBox<String> newReceiveSoundYesNoComboBox(ActionListener listener, boolean receiveSound) {
JComboBox<String> comboBox = newYesNoComboBox(listener, receiveSound);
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.RECEIVE_SOUND, MessageKey.RECEIVE_SOUND_TOOLTIP);
comboBox.setActionCommand(RECEIVE_SOUND_COMMAND);
return comboBox;
}
/**
* @param listener The action listener to alert when the selection is made
* @param useTor True if the "yes" option should be pre-selected
* @return A new "yes/no" combo box
*/
public static JComboBox<String> newTorYesNoComboBox(ActionListener listener, boolean useTor) {
JComboBox<String> comboBox = newYesNoComboBox(listener, useTor);
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_TOR, MessageKey.SELECT_TOR_TOOLTIP);
comboBox.setActionCommand(TOR_COMMAND);
return comboBox;
}
/**
* @param listener The action listener to alert when the selection is made
* @param showBalance True if the "yes" option should be pre-selected
* @return A new "yes/no" combo box
*/
public static JComboBox<String> newShowBalanceYesNoComboBox(ActionListener listener, boolean showBalance) {
JComboBox<String> comboBox = newYesNoComboBox(listener, showBalance);
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.SHOW_BALANCE, MessageKey.SHOW_BALANCE_TOOLTIP);
comboBox.setActionCommand(SHOW_BALANCE_COMMAND);
return comboBox;
}
/**
* @param listener The action listener to alert when the selection is made
* @return A new "contact checkbox" combo box (all, none)
*/
public static JComboBox<String> newContactsCheckboxComboBox(ActionListener listener) {
String[] items = new String[]{
Languages.safeText(MessageKey.ALL),
Languages.safeText(MessageKey.NONE),
};
JComboBox<String> comboBox = newReadOnlyComboBox(items);
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.CONTACTS, MessageKey.CONTACTS_TOOLTIP);
// Add the listener at the end to avoid false events
comboBox.addActionListener(listener);
return comboBox;
}
/**
* @param listener The action listener to alert when the selection is made
* @return A new "history checkbox" combo box (all, none) - kept separate from contacts
*/
public static JComboBox<String> newHistoryCheckboxComboBox(ActionListener listener) {
JComboBox<String> comboBox = newContactsCheckboxComboBox(listener);
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.HISTORY, MessageKey.HISTORY_TOOLTIP);
return comboBox;
}
/**
* @param listener The action listener
* @return A new "exchange rate provider" combo box
*/
public static JComboBox<MarketUtil.MarketTypeMode> newExchangeRateProviderComboBox(ActionListener listener) {
Preconditions.checkNotNull(listener, "'listener' must be present");
// Get all the exchange names
MarketUtil.MarketTypeMode[] marketTypeModes = new MarketUtil.MarketTypeMode[MarketType.values().length];
for (int i = 0; i < MarketType.values().length; i++) {
marketTypeModes[i] = new MarketUtil.MarketTypeMode(MarketType.values()[i]);
}
JComboBox<MarketUtil.MarketTypeMode> comboBox = newReadOnlyComboBox(marketTypeModes);
comboBox.setMaximumRowCount(BitherUI.COMBOBOX_MAX_ROW_COUNT);
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.EXCHANGE_RATE_PROVIDER, MessageKey.EXCHANGE_RATE_PROVIDER_TOOLTIP);
// Determine the selected index
MarketType marketType = UserPreference.getInstance().getDefaultMarket();
comboBox.setSelectedIndex(marketType.ordinal());
// Add the listener at the end to avoid false events
comboBox.setActionCommand(EXCHANGE_RATE_PROVIDER_COMMAND);
comboBox.addActionListener(listener);
return comboBox;
}
public static JComboBox<String> newCurrencyCodeComboBox(ActionListener listener) {
final JComboBox<String> comboBox = newReadOnlyComboBox(ExchangeUtil.exchangeNames);
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_LOCAL_CURRENCY, MessageKey.SELECT_LOCAL_CURRENCY_TOOLTIP);
ExchangeUtil.Currency currency = UserPreference.getInstance().getDefaultCurrency();
comboBox.setSelectedIndex(currency.ordinal());
// Add the listener at the end to avoid false events
comboBox.setActionCommand(ComboBoxes.CURRENCY_COMMAND);
comboBox.addActionListener(listener);
return comboBox;
}
/**
* @param listener The action listener
* @return A new "seed size" combo box
*/
public static JComboBox<String> newSeedSizeComboBox(ActionListener listener) {
JComboBox<String> comboBox = newReadOnlyComboBox(new String[]{
"12",
"18",
"24"
});
// Ensure it is accessible
AccessibilityDecorator.apply(comboBox, MessageKey.SEED_SIZE, MessageKey.SEED_SIZE_TOOLTIP);
comboBox.setSelectedIndex(0);
// Add the listener at the end to avoid false events
comboBox.addActionListener(listener);
return comboBox;
}
/**
* @param comboBox The combo box to set the selection on
* @param items The items in the model
* @param item the item that should be matched using a case-sensitive "starts with" approach
*/
public static void selectFirstMatch(JComboBox<String> comboBox, String[] items, String item) {
// Avoid working with nulls
if (item == null) {
comboBox.setSelectedIndex(-1);
return;
}
// Determine the first matching separator
for (int i = 0; i < items.length; i++) {
Preconditions.checkNotNull(items[i], "'items[" + i + "]' must be present");
if (items[i].startsWith(item)) {
comboBox.setSelectedIndex(i);
break;
}
}
}
}