package org.multibit.hd.ui.views.components; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import org.bitcoinj.core.NetworkParameters; import org.multibit.hd.core.blockexplorer.BlockExplorer; import org.multibit.hd.core.blockexplorer.BlockExplorers; import org.multibit.hd.core.config.BitcoinConfiguration; import org.multibit.hd.core.config.Configurations; import org.multibit.hd.core.dto.BackupSummary; import org.multibit.hd.core.dto.MBHDPaymentRequestData; import org.multibit.hd.core.dto.Recipient; import org.multibit.hd.core.dto.WalletSummary; import org.multibit.hd.core.dto.comparators.BackupSummaryDescendingComparator; import org.multibit.hd.core.exchanges.ExchangeKey; import org.multibit.hd.core.services.ContactService; import org.multibit.hd.core.utils.BitcoinSymbol; import org.multibit.hd.core.utils.CurrencyUtils; import org.multibit.hd.ui.MultiBitUI; import org.multibit.hd.ui.languages.LanguageKey; import org.multibit.hd.ui.languages.Languages; import org.multibit.hd.ui.languages.MessageKey; import org.multibit.hd.ui.views.components.auto_complete.AutoCompleteDecorator; import org.multibit.hd.ui.views.components.auto_complete.AutoCompleteFilter; import org.multibit.hd.ui.views.components.auto_complete.AutoCompleteFilters; import org.multibit.hd.ui.views.components.renderers.*; import org.multibit.hd.ui.views.components.select_recipient.RecipientComboBoxEditor; import org.multibit.hd.ui.views.components.select_recipient.RecipientListCellRenderer; import org.multibit.hd.ui.views.themes.ThemeKey; import org.multibit.hd.ui.views.themes.Themes; import javax.swing.*; import java.awt.*; import java.awt.event.ActionListener; import java.util.Collection; import java.util.List; import java.util.Locale; /** * <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 "show Atom feed alert" combo box action command */ public static final String SHOW_ATOM_FEED_ALERT_COMMAND = "showAtomFeedAlert"; /** * The "block explorer" combo box action command */ public static final String BLOCK_EXPLORER_COMMAND = "blockExplorer"; /** * 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 "hardware" combo box action command */ public static final String HARDWARE_COMMAND = "hardware"; /** * The "Show restore Beta 7 wallets"action command */ public static final String SHOW_RESTORE_BETA7_WALLETS_COMMAND = "show-restore-beta7-wallets"; /** * 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<>(items); // Use a list cell renderer to ensure Bitcoin symbols are correctly presented ListCellRenderer<T> renderer = new ThemeAwareListCellRenderer<>(); comboBox.setRenderer(renderer); // Required to match icon button heights comboBox.setMinimumSize(new Dimension(25, MultiBitUI.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 blockExplorerId The id of the blockExplorer to select by default 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> newBlockExplorerComboBox(ActionListener listener, String blockExplorerId) { Collection<BlockExplorer> allBlockExplorers = BlockExplorers.getAll(); int selectedIndex = -1; int index = 0; List<String> comboBoxValues = Lists.newArrayList(); for (BlockExplorer blockExplorer : allBlockExplorers) { comboBoxValues.add(blockExplorer.getName()); if (blockExplorer.getId().equals(blockExplorerId)) { selectedIndex = index; } index++; } JComboBox<String> comboBox = newReadOnlyComboBox(comboBoxValues.toArray(new String[comboBoxValues.size()])); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.BLOCK_EXPLORER); comboBox.setEditable(false); if (selectedIndex > -1) { comboBox.setSelectedIndex(selectedIndex); } // Apply theme comboBox.setBackground(Themes.currentTheme.readOnlyComboBox()); // Set the listener at the end to avoid spurious events comboBox.addActionListener(listener); comboBox.setActionCommand(BLOCK_EXPLORER_COMMAND); 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> newHardwareYesNoComboBox(ActionListener listener, boolean useTor) { JComboBox<String> comboBox = newYesNoComboBox(listener, useTor); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_HARDWARE_WALLET, MessageKey.SELECT_HARDWARE_WALLET_TOOLTIP); comboBox.setActionCommand(HARDWARE_COMMAND); return comboBox; } /** * @param listener The action listener to alert when the selection is made * @param showRestoreBeta7Wallets True if the "yes" option should be pre-selected * * @return A new "yes/no" combo box */ public static JComboBox<String> newShowRestoreBeta7WalletsYesNoComboBox(ActionListener listener, boolean showRestoreBeta7Wallets) { JComboBox<String> comboBox = newYesNoComboBox(listener, showRestoreBeta7Wallets); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_SHOW_RESTORE_BETA7_WALLETS, MessageKey.SELECT_SHOW_RESTORE_BETA7_WALLETSTOOLTIP); comboBox.setActionCommand(SHOW_RESTORE_BETA7_WALLETS_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 * @param showBalance True if the "yes" option should be pre-selected * * @return A new "yes/no" combo box */ public static JComboBox<String> newShowAtomFeedAlertYesNoComboBox(ActionListener listener, boolean showBalance) { JComboBox<String> comboBox = newYesNoComboBox(listener, showBalance); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SHOW_ATOM_FEED_ALERT, MessageKey.SHOW_ATOM_FEED_ALERT_TOOLTIP); comboBox.setActionCommand(SHOW_ATOM_FEED_ALERT_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 * @param locale The locale to use for initial selection * * @return A new "language" combo box containing all supported languages and variants */ public static JComboBox<String> newLanguagesComboBox(ActionListener listener, Locale locale) { // Populate the combo box and declare a suitable renderer JComboBox<String> comboBox = newReadOnlyComboBox(LanguageKey.localisedNames()); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_LANGUAGE, MessageKey.SELECT_LANGUAGE_TOOLTIP); comboBox.setRenderer(new LanguageListCellRenderer()); comboBox.setMaximumRowCount(MultiBitUI.COMBOBOX_MAX_ROW_COUNT); // Can use the ordinal due to the declaration ordering comboBox.setSelectedIndex(LanguageKey.fromLocale(locale).ordinal()); // Add the listener at the end to avoid false events comboBox.setActionCommand(LANGUAGES_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * @param listener The action listener to alert when the selection is made * * @return A new "themes" combo box containing all supported languages and variants */ public static JComboBox<String> newThemesComboBox(ActionListener listener) { // Get the current themes // Populate the combo box and declare a suitable renderer JComboBox<String> comboBox = newReadOnlyComboBox(ThemeKey.localisedNames()); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_THEME, MessageKey.SELECT_THEME_TOOLTIP); // Can use the ordinal due to the declaration ordering comboBox.setSelectedIndex(ThemeKey.fromTheme(Themes.currentTheme).ordinal()); // Add the listener at the end to avoid false events comboBox.setActionCommand(THEMES_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * @param listener The action listener to alert when the selection is made * @param MBHDPaymentRequestDataList The list of paymentRequestData to put in the combo box * * @return A new "payment requests" combo box containing all supported languages and variants */ public static JComboBox<MBHDPaymentRequestData> newPaymentRequestsComboBox(ActionListener listener, List<MBHDPaymentRequestData> MBHDPaymentRequestDataList) { // Populate the combo box and declare a suitable renderer JComboBox<MBHDPaymentRequestData> comboBox = newReadOnlyComboBox(MBHDPaymentRequestDataList.toArray(new MBHDPaymentRequestData[MBHDPaymentRequestDataList.size()])); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.CHOOSE_PAYMENT_REQUEST, MessageKey.CHOOSE_PAYMENT_REQUEST_TOOLTIP); // Can use the ordinal due to the declaration ordering if (MBHDPaymentRequestDataList.size() > 0) { comboBox.setSelectedIndex(0); } // Add the listener at the end to avoid false events comboBox.setActionCommand(PAYMENT_REQUESTS_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * @param listener The action listener to alert when the selection is made * @param bitcoinConfiguration The Bitcoin configuration to use * * @return A new "decimal" combo box */ public static JComboBox<String> newDecimalComboBox(ActionListener listener, BitcoinConfiguration bitcoinConfiguration) { String[] decimalSeparators = Languages.getCurrencySeparators(false); JComboBox<String> comboBox = newReadOnlyComboBox(decimalSeparators); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_DECIMAL_SEPARATOR, MessageKey.SELECT_DECIMAL_SEPARATOR_TOOLTIP); // Determine the first matching separator String decimal = bitcoinConfiguration.getDecimalSeparator(); selectFirstMatch(comboBox, decimalSeparators, decimal); // Add the listener at the end to avoid false events comboBox.setActionCommand(DECIMAL_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * @param listener The action listener to alert when the selection is made * @param bitcoinConfiguration The Bitcoin configuration to use * * @return A new "decimal" combo box */ public static JComboBox<String> newGroupingComboBox(ActionListener listener, BitcoinConfiguration bitcoinConfiguration) { String[] groupingSeparators = Languages.getCurrencySeparators(true); JComboBox<String> comboBox = newReadOnlyComboBox(groupingSeparators); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_GROUPING_SEPARATOR, MessageKey.SELECT_GROUPING_SEPARATOR_TOOLTIP); // Determine the first matching separator String grouping = bitcoinConfiguration.getGroupingSeparator(); selectFirstMatch(comboBox, groupingSeparators, grouping); // Add the listener at the end to avoid false events comboBox.setActionCommand(GROUPING_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * <p>Provide a choice between the local currency symbol, or its 3-letter code</p> * * @param listener The action listener to alert when the selection is made * @param bitcoinConfiguration The Bitcoin configuration to use * * @return A new "local symbol" combo box (e.g. ["$", "USD"] or ["£","GBP"] etc) */ public static JComboBox<String> newLocalSymbolComboBox(ActionListener listener, BitcoinConfiguration bitcoinConfiguration) { String[] localSymbols = new String[]{ CurrencyUtils.symbolFor(bitcoinConfiguration.getLocalCurrencyCode()), bitcoinConfiguration.getLocalCurrencyCode() }; JComboBox<String> comboBox = newReadOnlyComboBox(localSymbols); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_LOCAL_SYMBOL, MessageKey.SELECT_LOCAL_SYMBOL_TOOLTIP); selectFirstMatch(comboBox, localSymbols, bitcoinConfiguration.getLocalCurrencySymbol()); // Ensure we have no ugly scrollbar comboBox.setMaximumRowCount(localSymbols.length); // Add the listener at the end to avoid false events comboBox.setActionCommand(LOCAL_SYMBOL_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * @param listener The action listener to alert when the selection is made * @param bitcoinConfiguration The Bitcoin configuration to use * * @return A new "Bitcoin symbol" combo box (e.g. "mB", "XBT" etc) */ public static JComboBox<BitcoinSymbol> newBitcoinSymbolComboBox(ActionListener listener, BitcoinConfiguration bitcoinConfiguration) { // Order of insertion is important here JComboBox<BitcoinSymbol> comboBox = newReadOnlyComboBox(BitcoinSymbol.values()); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_BITCOIN_SYMBOL, MessageKey.SELECT_BITCOIN_SYMBOL_TOOLTIP); // Increase default font size comboBox.setFont(comboBox.getFont().deriveFont(MultiBitUI.COMBO_BOX_TEXT_FONT_SIZE)); // Ensure we have no ugly scrollbar comboBox.setMaximumRowCount(BitcoinSymbol.values().length); // Use a list cell renderer to ensure Bitcoin symbols are correctly presented ListCellRenderer<BitcoinSymbol> renderer = new BitcoinSymbolListCellRenderer(); comboBox.setRenderer(renderer); // Ensure we start with the given symbol selected BitcoinSymbol bitcoinSymbol = BitcoinSymbol.of(bitcoinConfiguration.getBitcoinSymbol()); comboBox.setSelectedIndex(bitcoinSymbol.ordinal()); // Add the listener at the end to avoid false events comboBox.setActionCommand(BITCOIN_SYMBOL_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * @param listener The action listener to alert when the selection is made * @param bitcoinConfiguration The Bitcoin configuration to use * * @return A new "placement" combo box (e.g. "Leading", "Trailing" etc) */ public static JComboBox<String> newPlacementComboBox(ActionListener listener, BitcoinConfiguration bitcoinConfiguration) { // Order of insertion is important here String[] positions = new String[]{ Languages.safeText(MessageKey.LEADING), Languages.safeText(MessageKey.TRAILING), }; JComboBox<String> comboBox = newReadOnlyComboBox(positions); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_PLACEMENT, MessageKey.SELECT_PLACEMENT_TOOLTIP); if (bitcoinConfiguration.isCurrencySymbolLeading()) { comboBox.setSelectedIndex(0); } else { comboBox.setSelectedIndex(1); } // Add the listener at the end to avoid false events comboBox.setActionCommand(PLACEMENT_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * @param contactService The contact service * @param networkParameters The Bitcoin network parameters * * @return A new "recipient" combo box with auto-complete functionality */ public static JComboBox<Recipient> newRecipientComboBox(ContactService contactService, NetworkParameters networkParameters) { Preconditions.checkNotNull(contactService, "'contactService' must be present"); Preconditions.checkNotNull(networkParameters, "'networkParameters' must be present"); AutoCompleteFilter<Recipient> filter = AutoCompleteFilters.newRecipientFilter(contactService); JComboBox<Recipient> comboBox = newComboBox(filter.create()); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.RECIPIENT, MessageKey.RECIPIENT_TOOLTIP); // Remove border comboBox.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); // Ensure we start with nothing selected (must come before editor is set) comboBox.setSelectedIndex(-1); // Allow editing comboBox.setEditable(true); // Use a recipient editor to force use of the name field comboBox.setEditor(new RecipientComboBoxEditor(contactService, networkParameters)); // Use a recipient list cell renderer to ensure recipient is set on selection JTextField textField = (JTextField) comboBox.getEditor().getEditorComponent(); ListCellRenderer<Recipient> renderer = new RecipientListCellRenderer(textField); AccessibilityDecorator.apply(textField, MessageKey.RECIPIENT, MessageKey.RECIPIENT_TOOLTIP); comboBox.setRenderer(renderer); AutoCompleteDecorator.apply(comboBox, filter); return comboBox; } /** * @param listener The action listener * @param backupSummaries The backup summary entries * * @return A new "backup summary" combo box */ public static JComboBox<BackupSummary> newBackupSummaryComboBox(ActionListener listener, List<BackupSummary> backupSummaries) { Preconditions.checkNotNull(listener, "'listener' must be present"); Preconditions.checkNotNull(backupSummaries, "'backupSummaries' must be present"); // Convert the backup summaries to an array BackupSummary[] backupSummaryArray = new BackupSummary[backupSummaries.size()]; JComboBox<BackupSummary> comboBox = newReadOnlyComboBox(backupSummaryArray); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_BACKUP_NOTE_1, MessageKey.SELECT_BACKUP_NOTE_1_TOOLTIP); // Use a backup summary list cell renderer to ensure the correct fields are displayed ListCellRenderer<BackupSummary> renderer = new BackupSummaryListCellRenderer(); comboBox.setRenderer(renderer); // Use a sorted model for maximum compatibility across platforms comboBox.setModel(new SortedComboBoxModel<>(new BackupSummaryDescendingComparator())); // Ensure we start with nothing selected comboBox.setSelectedIndex(-1); // Add the listener at the end to avoid false events comboBox.addActionListener(listener); return comboBox; } /** * @param listener The action listener * @param wallets The wallet data entries * * @return A new "select wallet" combo box */ public static JComboBox<WalletSummary> newSelectWalletComboBox(ActionListener listener, List<WalletSummary> wallets) { Preconditions.checkNotNull(listener, "'listener' must be present"); Preconditions.checkNotNull(wallets, "'wallets' must be present"); // Convert the wallet data entries to an array WalletSummary[] walletSummaryArray = new WalletSummary[wallets.size()]; JComboBox<WalletSummary> comboBox = newReadOnlyComboBox(walletSummaryArray); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_WALLET, MessageKey.SELECT_WALLET_TOOLTIP); // Use a wallet list cell renderer to ensure the correct fields are displayed ListCellRenderer<WalletSummary> renderer = new WalletSummaryListCellRenderer(); comboBox.setRenderer(renderer); // Ensure we start with nothing selected comboBox.setSelectedIndex(-1); // Add the listener at the end to avoid false events comboBox.addActionListener(listener); return comboBox; } /** * @param listener The action listener * * @return A new "exchange rate provider" combo box */ public static JComboBox<String> newExchangeRateProviderComboBox(ActionListener listener, BitcoinConfiguration bitcoinConfiguration) { Preconditions.checkNotNull(listener, "'listener' must be present"); // Get all the exchange names String[] allExchangeNames = ExchangeKey.allExchangeNames(); allExchangeNames[0] = Languages.safeText(MessageKey.NONE); JComboBox<String> comboBox = newReadOnlyComboBox(allExchangeNames); comboBox.setMaximumRowCount(MultiBitUI.COMBOBOX_MAX_ROW_COUNT); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.EXCHANGE_RATE_PROVIDER, MessageKey.EXCHANGE_RATE_PROVIDER_TOOLTIP); // Determine the selected index ExchangeKey exchangeKey = ExchangeKey.valueOf(bitcoinConfiguration.getCurrentExchange()); comboBox.setSelectedIndex(exchangeKey.ordinal()); // Add the listener at the end to avoid false events comboBox.setActionCommand(EXCHANGE_RATE_PROVIDER_COMMAND); comboBox.addActionListener(listener); return comboBox; } /** * <p>Creates a combo that is populated asynchronously from the currency and exchange provided</p> * * @param listener The action listener * * @return A new "currency code" combo box */ public static JComboBox<String> newCurrencyCodeComboBox(ActionListener listener) { final JComboBox<String> comboBox = newReadOnlyComboBox(new String[]{}); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_LOCAL_CURRENCY, MessageKey.SELECT_LOCAL_CURRENCY_TOOLTIP); // 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 listener The action listener * * @return A new "restore wallet type" combo box */ public static JComboBox<String> newRestoreWalletTypeComboBox(ActionListener listener) { // There is an extra wallet type according to the configuration String[] walletTypes; if (Configurations.currentConfiguration != null && Configurations.currentConfiguration.isShowRestoreBeta7Wallets()) { walletTypes = new String[]{ Languages.safeText(MessageKey.SELECT_WALLET_TYPE_BIP32), Languages.safeText(MessageKey.SELECT_WALLET_TYPE_BETA7), Languages.safeText(MessageKey.SELECT_WALLET_TYPE_BIP44) }; } else { walletTypes = new String[]{ Languages.safeText(MessageKey.SELECT_WALLET_TYPE_BIP32), Languages.safeText(MessageKey.SELECT_WALLET_TYPE_BIP44) }; } JComboBox<String> comboBox = newReadOnlyComboBox(walletTypes); // Ensure it is accessible AccessibilityDecorator.apply(comboBox, MessageKey.SELECT_WALLET_TYPE, MessageKey.SELECT_WALLET_TYPE_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; } } } }