/* * Copyright 2000-2012 JetBrains s.r.o. * * 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 org.jetbrains.android.uipreview; import com.android.ide.common.resources.LocaleManager; import com.android.ide.common.resources.configuration.*; import com.android.resources.*; import com.android.tools.idea.rendering.FlagManager; import com.google.common.collect.Maps; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ui.VerticalFlowLayout; import com.intellij.openapi.util.Comparing; import com.intellij.ui.*; import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBList; import com.intellij.ui.components.JBScrollPane; import com.intellij.ui.speedSearch.ListWithFilter; import com.intellij.util.containers.HashMap; import com.intellij.util.ui.AbstractLayoutManager; import com.intellij.util.ui.UIUtil; import icons.AndroidIcons; import org.jetbrains.android.util.AndroidBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @author Eugene.Kudelevsky */ public abstract class DeviceConfiguratorPanel extends JPanel { private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.uipreview.DeviceConfiguratorPanel"); private JBList myAvailableQualifiersList; private JButton myAddQualifierButton; private JButton myRemoveQualifierButton; private JPanel myQualifierOptionsPanel; private final Map<String, MyQualifierEditor> myEditors = new HashMap<String, MyQualifierEditor>(); private final FolderConfiguration myAvailableQualifiersConfig = new FolderConfiguration(); private final FolderConfiguration myChosenQualifiersConfig = new FolderConfiguration(); private FolderConfiguration myActualQualifiersConfig = new FolderConfiguration(); private JBList myChosenQualifiersList; private final DocumentListener myUpdatingDocumentListener = new DocumentAdapter() { @Override protected void textChanged(DocumentEvent e) { applyEditors(); } }; public final ListSelectionListener myUpdatingListListener = new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent listSelectionEvent) { applyEditors(); } }; @SuppressWarnings("unchecked") public DeviceConfiguratorPanel() { super(new BorderLayout()); createUIComponents(); createDefaultConfig(myAvailableQualifiersConfig); myChosenQualifiersConfig.reset(); for (ResourceQualifier qualifier : myAvailableQualifiersConfig.getQualifiers()) { final String name = qualifier.getShortName(); if (qualifier instanceof CountryCodeQualifier) { myEditors.put(name, new MyCountryCodeEditor()); } else if (qualifier instanceof NetworkCodeQualifier) { myEditors.put(name, new MyNetworkCodeEditor()); } else if (qualifier instanceof KeyboardStateQualifier) { myEditors.put(name, new MyKeyboardStateEditor()); } else if (qualifier instanceof NavigationMethodQualifier) { myEditors.put(name, new MyNavigationMethodEditor()); } else if (qualifier instanceof NavigationStateQualifier) { myEditors.put(name, new MyNavigationStateEditor()); } else if (qualifier instanceof DensityQualifier) { myEditors.put(name, new MyDensityEditor()); } else if (qualifier instanceof ScreenDimensionQualifier) { myEditors.put(name, new MyScreenDimensionEditor()); } else if (qualifier instanceof ScreenOrientationQualifier) { myEditors.put(name, new MyScreenOrientationEditor()); } else if (qualifier instanceof ScreenRatioQualifier) { myEditors.put(name, new MyScreenRatioEditor()); } else if (qualifier instanceof ScreenSizeQualifier) { myEditors.put(name, new MyScreenSizeEditor()); } else if (qualifier instanceof TextInputMethodQualifier) { myEditors.put(name, new MyTextInputMethodEditor()); } else if (qualifier instanceof TouchScreenQualifier) { myEditors.put(name, new MyTouchScreenEditor()); } else if (qualifier instanceof VersionQualifier) { myEditors.put(name, new MyVersionEditor()); } else if (qualifier instanceof NightModeQualifier) { myEditors.put(name, new MyNightModeEditor()); } else if (qualifier instanceof UiModeQualifier) { myEditors.put(name, new MyUiModeEditor()); } else if (qualifier instanceof LanguageQualifier) { myEditors.put(name, new MyLanguageEditor()); } else if (qualifier instanceof RegionQualifier) { myEditors.put(name, new MyRegionEditor()); } else if (qualifier instanceof SmallestScreenWidthQualifier) { myEditors.put(name, new MySmallestScreenWidthEditor()); } else if (qualifier instanceof ScreenWidthQualifier) { myEditors.put(name, new MyScreenWidthEditor()); } else if (qualifier instanceof ScreenHeightQualifier) { myEditors.put(name, new MyScreenHeightEditor()); } else if (qualifier instanceof LayoutDirectionQualifier) { myEditors.put(name, new MyLayoutDirectionEditor()); } else { LOG.info("Missing editor for qualifier " + qualifier); } } for (String name : myEditors.keySet()) { final MyQualifierEditor editor = myEditors.get(name); myQualifierOptionsPanel.add(editor.getComponent(), name); } myAvailableQualifiersList.setCellRenderer(new ColoredListCellRenderer() { @Override protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { if (value instanceof ResourceQualifier) { ResourceQualifier qualifier = (ResourceQualifier)value; append(qualifier.getShortName()); setIcon(getResourceIcon(qualifier)); } } }); myChosenQualifiersList.setCellRenderer(new ColoredListCellRenderer() { @Override protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) { if (value instanceof ResourceQualifier) { final ResourceQualifier qualifier = getActualQualifier((ResourceQualifier)value); final String shortDisplayValue = qualifier.getShortDisplayValue(); if (shortDisplayValue != null && !shortDisplayValue.isEmpty()) { append(shortDisplayValue); } else { append(qualifier.getShortName() + " (?)"); } setIcon(getResourceIcon(qualifier)); } } }); myAddQualifierButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final ResourceQualifier selectedQualifier = (ResourceQualifier)myAvailableQualifiersList.getSelectedValue(); if (selectedQualifier != null) { final int index = myAvailableQualifiersList.getSelectedIndex(); myAvailableQualifiersConfig.removeQualifier(selectedQualifier); myChosenQualifiersConfig.addQualifier(selectedQualifier); updateLists(); applyEditors(); if (index >= 0) { myAvailableQualifiersList.setSelectedIndex(Math.min(index, myAvailableQualifiersList.getItemsCount() - 1)); } myChosenQualifiersList.setSelectedValue(selectedQualifier, true); } } }); myRemoveQualifierButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final ResourceQualifier selectedQualifier = (ResourceQualifier)myChosenQualifiersList.getSelectedValue(); if (selectedQualifier != null) { final int index = myChosenQualifiersList.getSelectedIndex(); myChosenQualifiersConfig.removeQualifier(selectedQualifier); myAvailableQualifiersConfig.addQualifier(selectedQualifier); updateLists(); applyEditors(); if (index >= 0) { myChosenQualifiersList.setSelectedIndex(Math.min(index, myChosenQualifiersList.getItemsCount() - 1)); } } } }); myAvailableQualifiersList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { updateButtons(); } }); myChosenQualifiersList.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { updateButtons(); updateQualifierEditor(); } }); } private static final Map<String, Icon> ourIcons = Maps.newHashMapWithExpectedSize(25); static { ourIcons.put(UiModeQualifier.NAME, AndroidIcons.Configs.Dock); ourIcons.put(NightModeQualifier.NAME, AndroidIcons.Configs.Night); ourIcons.put(ScreenDimensionQualifier.NAME, AndroidIcons.Configs.Dimension); ourIcons.put(DensityQualifier.NAME, AndroidIcons.Configs.Dpi); ourIcons.put(ScreenHeightQualifier.NAME, AndroidIcons.Configs.Height); ourIcons.put(KeyboardStateQualifier.NAME, AndroidIcons.Configs.Keyboard); ourIcons.put(LanguageQualifier.NAME, AndroidIcons.Configs.Language); ourIcons.put(CountryCodeQualifier.NAME, AndroidIcons.Configs.Mcc); ourIcons.put(NetworkCodeQualifier.NAME, AndroidIcons.Configs.Mnc); ourIcons.put(NavigationStateQualifier.NAME, AndroidIcons.Configs.Navpad); ourIcons.put(NavigationMethodQualifier.NAME, AndroidIcons.Configs.NavpadMethod); ourIcons.put(ScreenOrientationQualifier.NAME, AndroidIcons.Configs.Orientation); ourIcons.put(ScreenRatioQualifier.NAME, AndroidIcons.Configs.Ratio); ourIcons.put(RegionQualifier.NAME, AndroidIcons.Configs.Region); ourIcons.put(ScreenSizeQualifier.NAME, AndroidIcons.Configs.Size); ourIcons.put(SmallestScreenWidthQualifier.NAME, AndroidIcons.Configs.SmallestWidth); ourIcons.put(ScreenWidthQualifier.NAME, AndroidIcons.Configs.Width); ourIcons.put(TextInputMethodQualifier.NAME, AndroidIcons.Configs.TextInput); ourIcons.put(TouchScreenQualifier.NAME, AndroidIcons.Configs.Touch); ourIcons.put(LayoutDirectionQualifier.NAME, AndroidIcons.Configs.LayoutDirection); // TODO: Get dedicated icon for the API version ourIcons.put(VersionQualifier.NAME, AndroidIcons.Targets); // TODO: Get icon for layout direction qualifier, and add it in here! } @Nullable private static Icon getResourceIcon(ResourceQualifier qualifier) { return ourIcons.get(qualifier.getName()); } public void init(@NotNull FolderConfiguration config) { myChosenQualifiersConfig.set(config); myAvailableQualifiersConfig.substract(config); for (ResourceQualifier qualifier : config.getQualifiers()) { final MyQualifierEditor editor = myEditors.get(qualifier.getShortName()); if (editor != null) { editor.reset(qualifier); } } } protected void createDefaultConfig(FolderConfiguration config) { config.createDefault(); } public abstract void applyEditors(); public void updateAll() { updateLists(); updateButtons(); updateQualifierEditor(); applyEditors(); } public void doApplyEditors() throws InvalidOptionValueException { try { final FolderConfiguration newConfig = new FolderConfiguration(); for (ResourceQualifier qualifier : myChosenQualifiersConfig.getQualifiers()) { final MyQualifierEditor editor = myEditors.get(qualifier.getShortName()); if (editor != null) { newConfig.addQualifier(editor.apply()); } } myActualQualifiersConfig = newConfig; } finally { myAvailableQualifiersList.repaint(); myChosenQualifiersList.repaint(); } } public DocumentListener getUpdatingDocumentListener() { return myUpdatingDocumentListener; } private ResourceQualifier getActualQualifier(ResourceQualifier qualifier) { for (ResourceQualifier qualifier1 : myActualQualifiersConfig.getQualifiers()) { if (Comparing.equal(qualifier1.getShortName(), qualifier.getShortName())) { return qualifier1; } } return qualifier; } private void updateQualifierEditor() { final ResourceQualifier selectedQualifier = (ResourceQualifier)myChosenQualifiersList.getSelectedValue(); if (selectedQualifier != null && myEditors.containsKey(selectedQualifier.getShortName())) { final CardLayout layout = (CardLayout)myQualifierOptionsPanel.getLayout(); layout.show(myQualifierOptionsPanel, selectedQualifier.getShortName()); myQualifierOptionsPanel.setVisible(true); } else { myQualifierOptionsPanel.setVisible(false); } } private void updateButtons() { myAddQualifierButton.setEnabled(myAvailableQualifiersList.getSelectedIndex() >= 0); myRemoveQualifierButton.setEnabled(myChosenQualifiersList.getSelectedIndex() >= 0); } private void updateLists() { Object qualifier = myAvailableQualifiersList.getSelectedValue(); final ResourceQualifier[] availableQualifiers = filterUnsupportedQualifiers(myAvailableQualifiersConfig.getQualifiers()); myAvailableQualifiersList.setModel(new CollectionListModel(availableQualifiers)); myAvailableQualifiersList.setSelectedValue(qualifier, true); if (myAvailableQualifiersList.getSelectedValue() == null && myAvailableQualifiersList.getItemsCount() > 0) { myAvailableQualifiersList.setSelectedIndex(0); } qualifier = myChosenQualifiersList.getSelectedValue(); final ResourceQualifier[] chosenQualifiers = filterUnsupportedQualifiers(myChosenQualifiersConfig.getQualifiers()); myChosenQualifiersList.setModel(new CollectionListModel(chosenQualifiers)); myChosenQualifiersList.setSelectedValue(qualifier, true); if (myChosenQualifiersList.getSelectedValue() == null && myChosenQualifiersList.getItemsCount() > 0) { myChosenQualifiersList.setSelectedIndex(0); } } private ResourceQualifier[] filterUnsupportedQualifiers(ResourceQualifier[] qualifiers) { final List<ResourceQualifier> result = new ArrayList<ResourceQualifier>(); for (ResourceQualifier qualifier : qualifiers) { if (myEditors.containsKey(qualifier.getShortName())) { result.add(qualifier); } } return result.toArray(new ResourceQualifier[result.size()]); } public FolderConfiguration getConfiguration() { return myActualQualifiersConfig; } private void createUIComponents() { myQualifierOptionsPanel = new JPanel(new CardLayout()); final JPanel leftPanel = new JPanel(new BorderLayout(5, 5)); myAvailableQualifiersList = new JBList(); myAvailableQualifiersList.setMinimumSize(new Dimension(10, 10)); JBLabel label = new JBLabel(AndroidBundle.message("android.layout.preview.edit.configuration.available.qualifiers.label")); label.setLabelFor(myAvailableQualifiersList); leftPanel.add(label, BorderLayout.NORTH); leftPanel.add(new JBScrollPane(myAvailableQualifiersList, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.CENTER); final JPanel rightPanel = new JPanel(new BorderLayout(5, 5)); myChosenQualifiersList = new JBList(); myChosenQualifiersList.setMinimumSize(new Dimension(10, 10)); label = new JBLabel(AndroidBundle.message("android.layout.preview.edit.configuration.choosen.qualifiers.label")); label.setLabelFor(myChosenQualifiersList); rightPanel.add(label, BorderLayout.NORTH); rightPanel.add(new JBScrollPane(myChosenQualifiersList, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER), BorderLayout.CENTER); final JPanel buttonsPanel = new JPanel(new VerticalFlowLayout(VerticalFlowLayout.MIDDLE, 0, 0, true, false)); myAddQualifierButton = new JButton(">>"); buttonsPanel.add(myAddQualifierButton); myRemoveQualifierButton = new JButton("<<"); buttonsPanel.add(myRemoveQualifierButton); final int gap = 5; final JPanel listsPanel = new JPanel(new AbstractLayoutManager() { @SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext") @Override public Dimension preferredLayoutSize(Container target) { synchronized (target.getTreeLock()) { final Dimension leftPref = leftPanel.getPreferredSize(); final Dimension rightPref = rightPanel.getPreferredSize(); final Dimension middlePref = buttonsPanel.getPreferredSize(); final Insets insets = target.getInsets(); final int width = leftPref.width + middlePref.width + rightPref.width + insets.left + insets.right + gap * 2; final int height = Math .max(leftPref.height, Math.max(rightPref.height, middlePref.height)) + insets.top + insets.bottom; return new Dimension(width, height); } } @SuppressWarnings("NonPrivateFieldAccessedInSynchronizedContext") @Override public void layoutContainer(Container target) { synchronized (target.getTreeLock()) { final Insets insets = target.getInsets(); int top = insets.top; int bottom = target.getHeight() - insets.bottom; int left = insets.left; int right = target.getWidth() - insets.right; final int middleWidth = buttonsPanel.getPreferredSize().width + gap * 2; final int listWidth = (right - left - middleWidth) / 2; final int height = bottom - top; leftPanel.setSize(listWidth, height); rightPanel.setSize(listWidth, height); buttonsPanel.setSize(middleWidth, height); leftPanel.setBounds(left, top, listWidth, height); rightPanel.setBounds(right - listWidth, top, listWidth, height); buttonsPanel.setBounds(left + listWidth + gap, top, middleWidth - gap * 2, height); } } }); listsPanel.add(leftPanel); listsPanel.add(buttonsPanel); listsPanel.add(rightPanel); add(listsPanel, BorderLayout.CENTER); add(myQualifierOptionsPanel, BorderLayout.EAST); } public JBList getAvailableQualifiersList() { return myAvailableQualifiersList; } private abstract static class MyQualifierEditor<T extends ResourceQualifier> { abstract JComponent getComponent(); abstract void reset(@NotNull T qualifier); @NotNull abstract T apply() throws InvalidOptionValueException; } private class MyCountryCodeEditor extends MyQualifierEditor<CountryCodeQualifier> { private final JTextField myTextField = new JTextField(3); @Override JComponent getComponent() { final JPanel panel = new JPanel(new VerticalFlowLayout()); final JBLabel label = new JBLabel("<html><body>Mobile country code<br>(3 digits):</body></html>"); label.setLabelFor(myTextField); myTextField.setColumns(3); myTextField.getDocument().addDocumentListener(myUpdatingDocumentListener); panel.add(label); panel.add(myTextField); return panel; } @Override void reset(@NotNull CountryCodeQualifier qualifier) { myTextField.setText(Integer.toString(qualifier.getCode())); } @NotNull @Override CountryCodeQualifier apply() throws InvalidOptionValueException { if (myTextField.getText().length() != 3) { throw new InvalidOptionValueException("Country code must contain 3 digits"); } try { final int code = Integer.parseInt(myTextField.getText()); if (code < 100 || code > 999) { throw new InvalidOptionValueException("Incorrect country code"); } return new CountryCodeQualifier(code); } catch (NumberFormatException e) { throw new InvalidOptionValueException("Country code must be a number"); } } } private class MyNetworkCodeEditor extends MyQualifierEditor<NetworkCodeQualifier> { private final JTextField myTextField = new JTextField(3); @Override JComponent getComponent() { final JPanel panel = new JPanel(new VerticalFlowLayout()); final JBLabel label = new JBLabel("<html><body>Mobile network code<br>(1-3 digits):</body></html>"); panel.add(label); label.setLabelFor(myTextField); myTextField.getDocument().addDocumentListener(myUpdatingDocumentListener); panel.add(myTextField); return panel; } @Override void reset(@NotNull NetworkCodeQualifier qualifier) { myTextField.setText(Integer.toString(qualifier.getCode())); } @NotNull @Override NetworkCodeQualifier apply() throws InvalidOptionValueException { try { final int code = Integer.parseInt(myTextField.getText()); if (code <= 0 || code >= 1000) { throw new InvalidOptionValueException("Incorrect network code"); } return new NetworkCodeQualifier(code); } catch (NumberFormatException e) { throw new InvalidOptionValueException("Network code must be a number"); } } } private abstract class MyEnumBasedEditor<T extends ResourceQualifier, U extends Enum<U>> extends MyQualifierEditor<T> { private final JComboBox myComboBox = new JComboBox(); private final Class<U> myEnumClass; protected MyEnumBasedEditor(@NotNull Class<U> enumClass) { myEnumClass = enumClass; } @Override JComponent getComponent() { myComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { applyEditors(); } }); myComboBox.setRenderer(new ListCellRendererWrapper() { @Override public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) { if (value instanceof ResourceEnum) { setText(((ResourceEnum)value).getShortDisplayValue()); } } }); final JPanel panel = new JPanel(new VerticalFlowLayout()); final JBLabel label = new JBLabel(getCaption()); label.setLabelFor(myComboBox); myComboBox.setModel(new EnumComboBoxModel<U>(myEnumClass)); panel.add(label); panel.add(myComboBox); return panel; } @NotNull protected abstract String getCaption(); @Override void reset(@NotNull T qualifier) { final U value = getValue(qualifier); if (value != null) { myComboBox.setSelectedItem(value); } else if (myComboBox.getItemCount() > 0) { myComboBox.setSelectedIndex(0); } } protected abstract U getValue(@NotNull T qualifier); @NotNull protected abstract T getQualifier(@NotNull U value); @NotNull protected abstract String getErrorMessage(); @NotNull @Override T apply() throws InvalidOptionValueException { final U selectedItem = (U)myComboBox.getSelectedItem(); if (selectedItem == null) { throw new InvalidOptionValueException(getErrorMessage()); } return getQualifier(selectedItem); } } private class MyScreenSizeEditor extends MyEnumBasedEditor<ScreenSizeQualifier, ScreenSize> { private MyScreenSizeEditor() { super(ScreenSize.class); } @NotNull @Override protected String getCaption() { return "Screen size:"; } @NotNull @Override protected ScreenSize getValue(@NotNull ScreenSizeQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected ScreenSizeQualifier getQualifier(@NotNull ScreenSize value) { return new ScreenSizeQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify screen size"; } } private class MyScreenOrientationEditor extends MyEnumBasedEditor<ScreenOrientationQualifier, ScreenOrientation> { private MyScreenOrientationEditor() { super(ScreenOrientation.class); } @NotNull @Override protected String getCaption() { return "Screen orientation:"; } @NotNull @Override protected ScreenOrientation getValue(@NotNull ScreenOrientationQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected ScreenOrientationQualifier getQualifier(@NotNull ScreenOrientation value) { return new ScreenOrientationQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify screen orientation"; } } private class MyScreenRatioEditor extends MyEnumBasedEditor<ScreenRatioQualifier, ScreenRatio> { private MyScreenRatioEditor() { super(ScreenRatio.class); } @NotNull @Override protected String getCaption() { return "Screen ratio:"; } @NotNull @Override protected ScreenRatio getValue(@NotNull ScreenRatioQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected ScreenRatioQualifier getQualifier(@NotNull ScreenRatio value) { return new ScreenRatioQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify screen ratio"; } } private class MyDensityEditor extends MyEnumBasedEditor<DensityQualifier, Density> { private MyDensityEditor() { super(Density.class); } @NotNull @Override protected String getCaption() { return "Density:"; } @NotNull @Override protected Density getValue(@NotNull DensityQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected DensityQualifier getQualifier(@NotNull Density value) { return new DensityQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify density"; } } private class MyTouchScreenEditor extends MyEnumBasedEditor<TouchScreenQualifier, TouchScreen> { private MyTouchScreenEditor() { super(TouchScreen.class); } @NotNull @Override protected String getCaption() { return "Touch screen:"; } @NotNull @Override protected TouchScreen getValue(@NotNull TouchScreenQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected TouchScreenQualifier getQualifier(@NotNull TouchScreen value) { return new TouchScreenQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify touch screen"; } } private class MyKeyboardStateEditor extends MyEnumBasedEditor<KeyboardStateQualifier, KeyboardState> { private MyKeyboardStateEditor() { super(KeyboardState.class); } @NotNull @Override protected String getCaption() { return "Keyboard state:"; } @NotNull @Override protected KeyboardState getValue(@NotNull KeyboardStateQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected KeyboardStateQualifier getQualifier(@NotNull KeyboardState value) { return new KeyboardStateQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify keyboard state"; } } private class MyTextInputMethodEditor extends MyEnumBasedEditor<TextInputMethodQualifier, Keyboard> { private MyTextInputMethodEditor() { super(Keyboard.class); } @NotNull @Override protected String getCaption() { return "Text input method:"; } @NotNull @Override protected Keyboard getValue(@NotNull TextInputMethodQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected TextInputMethodQualifier getQualifier(@NotNull Keyboard value) { return new TextInputMethodQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify text input method"; } } private class MyNavigationStateEditor extends MyEnumBasedEditor<NavigationStateQualifier, NavigationState> { private MyNavigationStateEditor() { super(NavigationState.class); } @NotNull @Override protected String getCaption() { return "Navigation state:"; } @NotNull @Override protected NavigationState getValue(@NotNull NavigationStateQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected NavigationStateQualifier getQualifier(@NotNull NavigationState value) { return new NavigationStateQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify navigation state"; } } private class MyNavigationMethodEditor extends MyEnumBasedEditor<NavigationMethodQualifier, Navigation> { private MyNavigationMethodEditor() { super(Navigation.class); } @NotNull @Override protected String getCaption() { return "Navigation method:"; } @NotNull @Override protected Navigation getValue(@NotNull NavigationMethodQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected NavigationMethodQualifier getQualifier(@NotNull Navigation value) { return new NavigationMethodQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify navigation method"; } } private class MyLayoutDirectionEditor extends MyEnumBasedEditor<LayoutDirectionQualifier, LayoutDirection> { private MyLayoutDirectionEditor() { super(LayoutDirection.class); } @NotNull @Override protected String getCaption() { return "Layout direction:"; } @NotNull @Override protected LayoutDirection getValue(@NotNull LayoutDirectionQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected LayoutDirectionQualifier getQualifier(@NotNull LayoutDirection value) { return new LayoutDirectionQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify layout direction"; } } private class MyScreenDimensionEditor extends MyQualifierEditor<ScreenDimensionQualifier> { private final JTextField mySizeField1 = new JTextField(); private final JTextField mySizeField2 = new JTextField(); @Override JComponent getComponent() { final JPanel panel = new JPanel(new VerticalFlowLayout()); final JBLabel label = new JBLabel("Screen dimension in dp:"); label.setLabelFor(mySizeField1); panel.add(label); panel.add(mySizeField1); panel.add(mySizeField2); mySizeField1.getDocument().addDocumentListener(myUpdatingDocumentListener); mySizeField2.getDocument().addDocumentListener(myUpdatingDocumentListener); return panel; } @Override void reset(@NotNull ScreenDimensionQualifier qualifier) { final int value1 = qualifier.getValue1(); if (value1 >= 0) { mySizeField1.setText(Integer.toString(value1)); } final int value2 = qualifier.getValue2(); if (value2 >= 0) { mySizeField2.setText(Integer.toString(value2)); } } @NotNull @Override ScreenDimensionQualifier apply() throws InvalidOptionValueException { try { final int size1 = Integer.parseInt(mySizeField1.getText()); final int size2 = Integer.parseInt(mySizeField2.getText()); if (size1 <= 0 || size2 <= 0) { throw new InvalidOptionValueException("Incorrect screen dimension"); } return new ScreenDimensionQualifier(size1, size2); } catch (NumberFormatException e) { LOG.debug(e); throw new InvalidOptionValueException("Incorrect screen dimension"); } } } private class MyVersionEditor extends MyQualifierEditor<VersionQualifier> { private final JTextField myTextField = new JTextField(3); @Override JComponent getComponent() { final JPanel panel = new JPanel(new VerticalFlowLayout()); final JBLabel label = new JBLabel("Platform API level:"); panel.add(label); label.setLabelFor(myTextField); myTextField.getDocument().addDocumentListener(myUpdatingDocumentListener); panel.add(myTextField); return panel; } @Override void reset(@NotNull VersionQualifier qualifier) { myTextField.setText(Integer.toString(qualifier.getVersion())); } @NotNull @Override VersionQualifier apply() throws InvalidOptionValueException { try { final int apiLevel = Integer.parseInt(myTextField.getText().trim()); if (apiLevel < 0) { throw new InvalidOptionValueException("Incorrect API level"); } return new VersionQualifier(apiLevel); } catch (NumberFormatException e) { throw new InvalidOptionValueException("Incorrect API level"); } } } private class MyNightModeEditor extends MyEnumBasedEditor<NightModeQualifier, NightMode> { protected MyNightModeEditor() { super(NightMode.class); } @NotNull @Override protected String getCaption() { return "Night mode:"; } @Override protected NightMode getValue(@NotNull NightModeQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected NightModeQualifier getQualifier(@NotNull NightMode value) { return new NightModeQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify night mode"; } } private class MyUiModeEditor extends MyEnumBasedEditor<UiModeQualifier, UiMode> { private MyUiModeEditor() { super(UiMode.class); } @NotNull @Override protected String getCaption() { return "UI mode:"; } @Override protected UiMode getValue(@NotNull UiModeQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected UiModeQualifier getQualifier(@NotNull UiMode value) { return new UiModeQualifier(value); } @NotNull @Override protected String getErrorMessage() { return "Specify UI mode"; } } // See com.intellij.codeInsight.lookup.impl.Advertiser -- the class used to // render tips at the bottom of code completion dialog private static Font adFont() { Font font = UIUtil.getLabelFont(); return font.deriveFont((float)(font.getSize() - 2)); } private class MyLanguageEditor extends MyQualifierEditor<LanguageQualifier> { private final JBList myList = new JBList(); @Override JComponent getComponent() { BorderLayout layout = new BorderLayout(5, 5); // Matches constants in createUIComponents final JPanel panel = new JPanel(layout); panel.setBorder(new EmptyBorder(0, 20, 0, 0)); JBLabel header = new JBLabel("Language:"); header.setLabelFor(myList); panel.add(header, BorderLayout.NORTH); SortedListModel<String> model = new SortedListModel<String>(String.CASE_INSENSITIVE_ORDER); model.addAll(LocaleManager.getLanguageCodes()); myList.setModel(model); myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); myList.setCellRenderer(FlagManager.get().getLanguageCodeCellRenderer()); JBScrollPane scroll = new JBScrollPane(myList); JComponent list = ListWithFilter.wrap(myList, scroll, FlagManager.getLanguageNameMapper()); panel.add(list, BorderLayout.CENTER); JLabel tipLabel = new JBLabel("Tip: Type in list to filter"); tipLabel.setHorizontalAlignment(SwingConstants.RIGHT); tipLabel.setFont(adFont()); panel.add(tipLabel, BorderLayout.SOUTH); myList.addListSelectionListener(myUpdatingListListener); return panel; } @Override void reset(@NotNull LanguageQualifier qualifier) { if (qualifier.isValid() && !qualifier.hasFakeValue()) { String value = qualifier.getValue(); ListModel model = myList.getModel(); for (int i = 0, n = model.getSize(); i < n; i++) { if (value.equals(model.getElementAt(i))) { myList.setSelectedIndex(i); break; } } } else { myList.setSelectedIndex(-1); } } @NotNull @Override LanguageQualifier apply() throws InvalidOptionValueException { Object selectedValue = myList.getSelectedValue(); if (selectedValue == null) { throw new InvalidOptionValueException("Select a language"); } return new LanguageQualifier((String) selectedValue); } } private class MyRegionEditor extends MyQualifierEditor<RegionQualifier> { private final JBList myList = new JBList(); @Override JComponent getComponent() { BorderLayout layout = new BorderLayout(5, 5); // Matches constants in createUIComponents final JPanel panel = new JPanel(layout); panel.setBorder(new EmptyBorder(0, 20, 0, 0)); JBLabel header = new JBLabel("Region:"); header.setLabelFor(myList); panel.add(header, BorderLayout.NORTH); SortedListModel<String> model = new SortedListModel<String>(String.CASE_INSENSITIVE_ORDER); model.addAll(LocaleManager.getRegionCodes()); myList.setModel(model); myList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); myList.setCellRenderer(FlagManager.get().getRegionCodeCellRenderer()); JBScrollPane scroll = new JBScrollPane(myList); JComponent list = ListWithFilter.wrap(myList, scroll, FlagManager.getRegionNameMapper()); panel.add(list, BorderLayout.CENTER); JLabel tipLabel = new JBLabel("Tip: Type in list to filter"); tipLabel.setHorizontalAlignment(SwingConstants.RIGHT); tipLabel.setFont(adFont()); panel.add(tipLabel, BorderLayout.SOUTH); myList.addListSelectionListener(myUpdatingListListener); return panel; } @Override void reset(@NotNull RegionQualifier qualifier) { if (qualifier.isValid() && !qualifier.hasFakeValue()) { String value = qualifier.getValue(); ListModel model = myList.getModel(); for (int i = 0, n = model.getSize(); i < n; i++) { if (value.equals(model.getElementAt(i))) { myList.setSelectedIndex(i); break; } } } else { myList.setSelectedIndex(-1); } } @NotNull @Override RegionQualifier apply() throws InvalidOptionValueException { Object selectedValue = myList.getSelectedValue(); if (selectedValue == null) { throw new InvalidOptionValueException("Select a region"); } return new RegionQualifier((String) selectedValue); } } private abstract class MySizeEditorBase<T extends ResourceQualifier> extends MyQualifierEditor<T> { private final JTextField myTextField = new JTextField(3); private String myLabelText; protected MySizeEditorBase(String labelText) { myLabelText = labelText; } @Override JComponent getComponent() { final JPanel panel = new JPanel(new VerticalFlowLayout()); final JBLabel label = new JBLabel(myLabelText); panel.add(label); label.setLabelFor(myTextField); myTextField.getDocument().addDocumentListener(myUpdatingDocumentListener); panel.add(myTextField); return panel; } @Override void reset(@NotNull T qualifier) { myTextField.setText(Integer.toString(getValue(qualifier))); } protected abstract int getValue(@NotNull T qualifier); @NotNull protected abstract T createQualifier(int value); protected abstract String getErrorMessage(); @NotNull @Override T apply() throws InvalidOptionValueException { try { final int value = Integer.parseInt(myTextField.getText().trim()); if (value < 0) { throw new InvalidOptionValueException(getErrorMessage()); } return createQualifier(value); } catch (NumberFormatException e) { throw new InvalidOptionValueException(getErrorMessage()); } } } private class MySmallestScreenWidthEditor extends MySizeEditorBase<SmallestScreenWidthQualifier> { private MySmallestScreenWidthEditor() { super("Smallest screen width:"); } @Override protected int getValue(@NotNull SmallestScreenWidthQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected SmallestScreenWidthQualifier createQualifier(int value) { return new SmallestScreenWidthQualifier(value); } @Override protected String getErrorMessage() { return "Incorrect smallest screen width"; } } private class MyScreenWidthEditor extends MySizeEditorBase<ScreenWidthQualifier> { private MyScreenWidthEditor() { super("Screen width:"); } @Override protected int getValue(@NotNull ScreenWidthQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected ScreenWidthQualifier createQualifier(int value) { return new ScreenWidthQualifier(value); } @Override protected String getErrorMessage() { return "Incorrect screen width"; } } private class MyScreenHeightEditor extends MySizeEditorBase<ScreenHeightQualifier> { private MyScreenHeightEditor() { super("Screen height:"); } @Override protected int getValue(@NotNull ScreenHeightQualifier qualifier) { return qualifier.getValue(); } @NotNull @Override protected ScreenHeightQualifier createQualifier(int value) { return new ScreenHeightQualifier(value); } @Override protected String getErrorMessage() { return "Incorrect screen height"; } } }