| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * 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 com.android.tools.idea.wizard.template; |
| |
| import com.android.tools.idea.templates.Parameter; |
| import com.android.tools.idea.templates.Template; |
| import com.android.tools.idea.templates.TemplateMetadata; |
| import com.android.tools.idea.ui.ComboBoxItemWithApiTag; |
| import com.android.tools.idea.wizard.dynamic.AndroidStudioWizardStep; |
| import com.android.tools.idea.wizard.dynamic.DynamicWizardStepWithHeaderAndDescription; |
| import com.android.tools.idea.templates.StringEvaluator; |
| import com.android.utils.Pair; |
| import com.android.utils.XmlUtils; |
| import com.google.common.collect.BiMap; |
| import com.google.common.collect.HashBiMap; |
| import com.google.common.collect.Maps; |
| import com.intellij.ide.util.projectWizard.ModuleWizardStep; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.TextFieldWithBrowseButton; |
| import com.intellij.ui.ColorPanel; |
| import com.intellij.ui.ColorUtil; |
| import com.intellij.ui.JBColor; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| import javax.swing.*; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.event.DocumentListener; |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.FocusEvent; |
| import java.awt.event.FocusListener; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Queue; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.ConcurrentLinkedQueue; |
| |
| import static com.android.SdkConstants.ATTR_ID; |
| import static com.android.tools.idea.templates.Template.ATTR_DEFAULT; |
| import static com.android.tools.idea.templates.TemplateMetadata.*; |
| |
| /** |
| * TemplateWizardStep is the base class for step pages in Freemarker template-based wizards. |
| * |
| * Deprecated. Use {@link DynamicWizardStepWithHeaderAndDescription} instead. |
| */ |
| @Deprecated |
| public abstract class TemplateWizardStep extends ModuleWizardStep |
| implements ActionListener, FocusListener, DocumentListener, ChangeListener, AndroidStudioWizardStep { |
| private static final Logger LOG = Logger.getInstance("#" + TemplateWizardStep.class.getName()); |
| |
| protected static final String LAST_USED_CLASS_PREFIX_KEY = "LAST_USED_CLASS_PREFIX"; |
| |
| protected final TemplateWizardState myTemplateState; |
| protected final BiMap<String, JComponent> myParamFields = HashBiMap.create(); |
| protected final Map<String, JLabel> myParamFieldLabels = Maps.newHashMap(); |
| protected final Map<JRadioButton, Pair<String, Object>> myRadioButtonValues = Maps.newHashMap(); |
| protected final Map<Parameter, ComboBoxItemWithApiTag> myComboBoxValues = Maps.newHashMap(); |
| protected final Project myProject; |
| protected final Module myModule; |
| private final Icon mySidePanelIcon; |
| protected boolean myIgnoreUpdates = false; |
| protected boolean myFirstUpdate = true; |
| protected boolean myIsValid = true; |
| protected boolean myVisible = true; |
| protected final UpdateListener myUpdateListener; |
| private final StringEvaluator myStringEvaluator = new StringEvaluator(); |
| |
| // Ids for params that have been edited in this round of updating/validation |
| protected Queue<String> myIdsWithNewValues = new ConcurrentLinkedQueue<String>(); |
| |
| public interface UpdateListener { |
| public void update(); |
| } |
| |
| public static final UpdateListener NONE = new UpdateListener() { |
| @Override |
| public void update() {} |
| }; |
| |
| public TemplateWizardStep(@NotNull TemplateWizardState state, @Nullable Project project, @Nullable Module module, |
| @Nullable Icon sidePanelIcon, @Nullable UpdateListener updateListener) { |
| myTemplateState = state; |
| myProject = project; |
| myModule = module; |
| mySidePanelIcon = sidePanelIcon; |
| myUpdateListener = updateListener; |
| } |
| |
| @Override |
| public void _init() { |
| super._init(); |
| update(); |
| } |
| |
| /** Override this to return a {@link JLabel} that holds the description of the UI element that has focus. */ |
| @NotNull protected abstract JLabel getDescription(); |
| |
| /** Wraps the given string in <html> and </html> tags and sets it into the description label. */ |
| protected void setDescriptionHtml(@Nullable String s) { |
| if (s == null) { |
| s = ""; |
| } |
| if (!s.startsWith("<html>")) { |
| s = "<html>" + s + "</html>"; |
| s.replaceAll("\n", "<br>"); |
| } |
| JLabel label = getDescription(); |
| if (label != null) { |
| label.setText(s); |
| growLabelIfNecessary(label); |
| } |
| } |
| |
| /** Override this to return a {@link JLabel} that displays a validation error message. */ |
| @NotNull protected abstract JLabel getError(); |
| |
| /** Wraps the given string in <html> and </html> tags and sets it into the error label. */ |
| public void setErrorHtml(@Nullable String s) { |
| if (s == null) { |
| s = ""; |
| } |
| if (!s.startsWith("<html>")) { |
| s = "<html><font color='#" + ColorUtil.toHex(JBColor.RED) + "'>" + XmlUtils.toXmlTextValue(s) + "</font></html>"; |
| s.replaceAll("\n", "<br>"); |
| } |
| JLabel label = getError(); |
| if (label != null) { |
| label.setText(s); |
| growLabelIfNecessary(label); |
| } |
| } |
| |
| /** |
| * Increases the given label's vertical size if necessary to accommodate the amount of text it currently displays. If you are using |
| * IntelliJ's {@link GridLayoutManager}, then the minimum, maximum, and preferred sizes for the label component must be unspecified in |
| * the layout, or the layout manager will use those and override the component-level sizes that this method adjusts. |
| */ |
| protected void growLabelIfNecessary(JLabel label) { |
| Dimension newSize = label.getMinimumSize(); |
| label.setPreferredSize(null); |
| label.validate(); |
| Dimension pd = label.getPreferredSize(); |
| int currentWidth = label.getSize().width; |
| int preferredHeight = 0; |
| if (currentWidth != 0 && pd.width != 0 && pd.height != 0) { |
| preferredHeight = pd.height * (int) ((float)pd.width / (float)currentWidth); |
| if (currentWidth % pd.width != 0) { |
| preferredHeight += pd.height; |
| } |
| } |
| newSize.height = Math.max(newSize.height, preferredHeight); |
| label.setMinimumSize(newSize); |
| label.setPreferredSize(newSize); |
| if (getComponent() != null) { |
| getComponent().revalidate(); |
| } |
| } |
| |
| /** Override this to provide help text for a parameter that does not have its own help text in the template. */ |
| @Nullable |
| protected String getHelpText(@NotNull String param) { |
| return null; |
| } |
| |
| /** Returns true if the data in this wizard step is correct and the user is permitted to move to the next step. */ |
| @Override |
| public boolean isValid() { |
| return myIsValid; |
| } |
| |
| public void setVisible(boolean visible) { |
| myVisible = visible; |
| } |
| |
| @Override |
| public boolean isStepVisible() { |
| return myVisible; |
| } |
| |
| /** |
| * Called by update to write the new values of the parameters being edited into the template model. |
| */ |
| public void updateParams() { |
| if (!myVisible) { |
| return; |
| } |
| |
| Template.convertApisToInt(myTemplateState.getParameters()); |
| |
| Component focusedComponent = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); |
| setDescriptionHtml(""); |
| setErrorHtml(""); |
| for (String paramName : myParamFields.keySet()) { |
| if (myTemplateState.myHidden.contains(paramName)) { |
| continue; |
| } |
| Parameter param = myTemplateState.hasTemplate() ? myTemplateState.getTemplateMetadata().getParameter(paramName) : null; |
| Object oldValue = myTemplateState.get(paramName); |
| JComponent component = myParamFields.get(paramName); |
| if (component == focusedComponent || component.isAncestorOf(focusedComponent)) { |
| String help = param != null && param.help != null && param.help.length() > 0 ? param.help : getHelpText(paramName); |
| setDescriptionHtml(help); |
| } |
| Object newValue = getComponentValue(param, component); |
| |
| if (newValue != null && !newValue.equals(oldValue)) { |
| myTemplateState.put(paramName, newValue); |
| if (oldValue != null) { |
| myTemplateState.myModified.add(paramName); |
| } |
| if (!myIdsWithNewValues.contains(paramName)) { |
| myIdsWithNewValues.add(paramName); |
| } |
| } |
| } |
| for (Map.Entry<JRadioButton, Pair<String, Object>> entry : myRadioButtonValues.entrySet()) { |
| if (entry.getKey().isSelected()) { |
| Pair<String, Object> value = entry.getValue(); |
| myTemplateState.put(value.getFirst(), value.getSecond()); |
| } |
| } |
| } |
| |
| /** |
| * Called by update() to write derived values to the template model. |
| */ |
| protected void deriveValues() { |
| TemplateMetadata metadata = myTemplateState.getTemplateMetadata(); |
| if (metadata == null) { |
| return; |
| } |
| |
| for (String changedParamId : myIdsWithNewValues) { |
| for (String paramName : myParamFields.keySet()) { |
| Parameter param = myTemplateState.hasTemplate() ? metadata.getParameter(paramName) : null; |
| |
| // If this parameter is null or doesn't have anything to update (both visibility and suggestion are null or empty), skip it. |
| if (param == null || |
| ((param.suggest == null || param.suggest.isEmpty()) && (param.visibility == null || param.visibility.isEmpty()))) { |
| continue; |
| } |
| |
| // If this parameter has dynamic visibility, calculate it and process accordingly |
| if (param.visibility != null && param.visibility.contains(changedParamId)) { |
| updateVisibility(param); |
| } |
| |
| // Don't process hidden fields |
| if (myTemplateState.myHidden.contains(paramName)) { |
| continue; |
| } |
| |
| // If this parameter has a suggestion depending on the changed parameter, calculate it, record the new value |
| // and add it for consideration so that things dependent on it can be updated |
| if (param.suggest != null && param.suggest.contains(changedParamId)) { |
| final String updated = myStringEvaluator.evaluate(param.suggest, myTemplateState.getParameters()); |
| if (updated != null && !updated.equals(myTemplateState.get(param.id))) { |
| myIdsWithNewValues.add(param.id); |
| |
| updateDerivedValue(param.id, (JTextField)myParamFields.get(param.id), new Callable<String>() { |
| @Override |
| public String call() throws Exception { |
| return updated; |
| } |
| }); |
| } |
| } |
| } |
| } |
| } |
| |
| protected void updateVisibility(Parameter param) { |
| if (param.visibility != null && !param.visibility.isEmpty()) { |
| boolean visible = myStringEvaluator.evaluateBooleanExpression(param.visibility, myTemplateState.getParameters(), true); |
| if (visible) { |
| myTemplateState.myHidden.remove(param.id); |
| // Update the newly shown parameter if necessary |
| if (param.suggest != null) { |
| final String updated = myStringEvaluator.evaluate(param.suggest, myTemplateState.getParameters()); |
| if (updated != null && !updated.equals(myTemplateState.get(param.id))) { |
| updateDerivedValue(param.id, (JTextField)myParamFields.get(param.id), new Callable<String>() { |
| @Override |
| public String call() throws Exception { |
| return updated; |
| } |
| }); |
| } |
| } |
| } |
| else { |
| myTemplateState.myHidden.add(param.id); |
| } |
| myParamFields.get(param.id).setVisible(visible); |
| if (myParamFieldLabels.containsKey(param.id)) { |
| myParamFieldLabels.get(param.id).setVisible(visible); |
| } |
| } |
| } |
| |
| @Override |
| public boolean validate() { |
| Template.convertApisToInt(myTemplateState.getParameters()); |
| if (!myVisible) { |
| return true; |
| } |
| Integer minApi = (Integer)myTemplateState.get(ATTR_MIN_API_LEVEL); |
| Integer buildApi = (Integer)myTemplateState.get(ATTR_BUILD_API); |
| |
| for (String paramName : myParamFields.keySet()) { |
| if (myTemplateState.myHidden.contains(paramName)) { |
| continue; |
| } |
| Parameter param = myTemplateState.hasTemplate() ? myTemplateState.getTemplateMetadata().getParameter(paramName) : null; |
| if (param != null) { |
| String error = param.validate(myProject, myModule, myTemplateState.getSourceProvider(), |
| (String)myTemplateState.get(ATTR_PACKAGE_NAME), myTemplateState.get(paramName)); |
| if (error != null) { |
| setErrorHtml(error); |
| return false; |
| } |
| |
| // Check to see that the selection's constraints are met if this is a combo box |
| if (myComboBoxValues.containsKey(param)) { |
| ComboBoxItemWithApiTag selectedItem = myComboBoxValues.get(param); |
| |
| if (selectedItem == null) { |
| return false; |
| } |
| if (minApi != null && selectedItem.minApi > minApi) { |
| setErrorHtml(String.format("The \"%s\" option for %s requires a minimum API level of %d", selectedItem.label, param.name, |
| selectedItem.minApi)); |
| return false; |
| } |
| if (buildApi != null && selectedItem.minBuildApi > buildApi) { |
| setErrorHtml(String.format("The \"%s\" option for %s requires a minimum API level of %d", selectedItem.label, param.name, |
| selectedItem.minBuildApi)); |
| return false; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| public void refreshUiFromParameters() { |
| if (myTemplateState.myTemplate == null) { |
| return; |
| } |
| |
| for (Parameter param : myTemplateState.myTemplate.getMetadata().getParameters()) { |
| if (param.initial != null && !myTemplateState.myModified.contains(param.id)) { |
| myTemplateState.myParameters.remove(param.id); |
| } |
| } |
| myTemplateState.setParameterDefaults(); |
| Template.convertApisToInt(myTemplateState.getParameters()); |
| boolean oldIgnoreUpdates = myIgnoreUpdates; |
| try { |
| myIgnoreUpdates = true; |
| for (String paramName : myParamFields.keySet()) { |
| if (myTemplateState.myHidden.contains(paramName)) { |
| continue; |
| } |
| JComponent component = myParamFields.get(paramName); |
| Object value = myTemplateState.get(paramName); |
| if (value == null) { |
| continue; |
| } |
| if (component instanceof JCheckBox) { |
| ((JCheckBox)component).setSelected(Boolean.parseBoolean(value.toString())); |
| } |
| else if (component instanceof JComboBox) { |
| for (int i = 0; i < ((JComboBox)component).getItemCount(); i++) { |
| if (((ComboBoxItemWithApiTag)((JComboBox)component).getItemAt(i)).id.equals(value)) { |
| ((JComboBox)component).setSelectedIndex(i); |
| break; |
| } |
| } |
| } |
| else if (component instanceof JTextField) { |
| ((JTextField)component).setText(value.toString()); |
| } else if (component instanceof TextFieldWithBrowseButton) { |
| ((TextFieldWithBrowseButton)component).setText(value.toString()); |
| } else if (component instanceof JSlider) { |
| ((JSlider)component).setValue(Integer.parseInt(value.toString())); |
| } else if (component instanceof JSpinner) { |
| ((JSpinner)component).setValue(Integer.parseInt(value.toString())); |
| } else if (component instanceof ColorPanel) { |
| ((ColorPanel)component).setSelectedColor((Color)value); |
| } |
| } |
| } finally { |
| myIgnoreUpdates = oldIgnoreUpdates; |
| } |
| } |
| |
| /** |
| * Retrieve a value from the given JComponent |
| */ |
| @Nullable |
| protected Object getComponentValue(Parameter param, JComponent component) { |
| Object newValue = null; |
| if (component instanceof JCheckBox) { |
| newValue = ((JCheckBox)component).isSelected(); |
| } |
| else if (component instanceof JComboBox) { |
| ComboBoxItemWithApiTag selectedItem = (ComboBoxItemWithApiTag)((JComboBox)component).getSelectedItem(); |
| myComboBoxValues.put(param, selectedItem); |
| |
| if (selectedItem != null) { |
| newValue = selectedItem.id; |
| } |
| } |
| else if (component instanceof JTextField) { |
| newValue = ((JTextField)component).getText(); |
| } else if (component instanceof TextFieldWithBrowseButton) { |
| newValue = ((TextFieldWithBrowseButton)component).getText(); |
| } else if (component instanceof JSlider) { |
| newValue = ((JSlider)component).getValue(); |
| } else if (component instanceof JSpinner) { |
| newValue = ((JSpinner)component).getValue(); |
| } else if (component instanceof ColorPanel) { |
| newValue = ((ColorPanel)component).getSelectedColor(); |
| } |
| return newValue; |
| } |
| |
| /** |
| * Takes a {@link JComboBox} instance and a {@Parameter} that represents an enumerated type and |
| * populates the combo box with all possible values of the enumerated type. |
| */ |
| protected static void populateComboBox(@NotNull JComboBox comboBox, @NotNull Parameter parameter) { |
| List<Element> options = parameter.getOptions(); |
| assert !options.isEmpty(); |
| for (int i = 0, n = options.size(); i < n; i++) { |
| Element option = options.get(i); |
| String optionId = option.getAttribute(ATTR_ID); |
| assert optionId != null && !optionId.isEmpty() : ATTR_ID; |
| NodeList childNodes = option.getChildNodes(); |
| assert childNodes.getLength() == 1 && childNodes.item(0).getNodeType() == Node.TEXT_NODE; |
| String optionLabel = childNodes.item(0).getNodeValue().trim(); |
| |
| int minSdk = 1; |
| try { minSdk = Integer.parseInt(option.getAttribute(TemplateMetadata.ATTR_MIN_API)); } catch (Exception e) { } |
| int minBuildApi = 1; |
| try { minBuildApi = Integer.parseInt(option.getAttribute(TemplateMetadata.ATTR_MIN_BUILD_API)); } catch (Exception e) { } |
| comboBox.addItem(new ComboBoxItemWithApiTag(optionId, optionLabel, minSdk, minBuildApi)); |
| String isDefault = option.getAttribute(ATTR_DEFAULT); |
| if (isDefault != null && !isDefault.isEmpty() && Boolean.valueOf(isDefault)) { |
| comboBox.setSelectedIndex(comboBox.getItemCount() - 1); |
| } |
| } |
| } |
| |
| /** |
| * Takes a {@link JComboBox} instance and a {@Enum} and |
| * populates the combo box with all possible values of the enumerated type. |
| */ |
| protected static <E extends Enum<E>> void populateComboBox(@NotNull JComboBox comboBox, @NotNull Class<E> enumClass) { |
| for (Enum<E> e : enumClass.getEnumConstants()) { |
| comboBox.addItem(new ComboBoxItemWithApiTag(e.name(), e.toString(), 1, 1)); |
| } |
| } |
| |
| /** |
| * Takes a {@link JComboBox} instance and an array and |
| * populates the combo box with the values in the array. |
| * Similar to the {@link DefaultComboBoxModel}, but uses our ComboBoxItemWithApiTag. |
| */ |
| protected static void populateComboBox(@NotNull JComboBox comboBox, @NotNull Object[] array) { |
| for (int i = 0; i < array.length; ++i) { |
| comboBox.addItem(new ComboBoxItemWithApiTag(i, array[i].toString(), 1, 1)); |
| } |
| } |
| |
| /** |
| * Connects the given {@link JLabel} to the given parameter and sets a listener to pick up changes to visibility that need to |
| * trigger UI updates. |
| */ |
| protected void registerLabel(@NotNull String paramName, @NotNull JLabel label) { |
| myParamFieldLabels.put(paramName, label); |
| } |
| |
| /** |
| * Connects the given {@link JCheckBox} to the given parameter and sets a listener to pick up changes that need to trigger validation |
| * and UI updates. |
| */ |
| protected void register(@NotNull String paramName, @NotNull JCheckBox checkBox) { |
| myParamFields.put(paramName, (JComponent)checkBox); |
| Object value = myTemplateState.get(paramName); |
| if (value != null) { |
| checkBox.setSelected(Boolean.parseBoolean(value.toString())); |
| } else { |
| myTemplateState.put(paramName, false); |
| } |
| checkBox.addFocusListener(this); |
| checkBox.addActionListener(this); |
| } |
| |
| /** |
| * Connects the given {@link JComboBox} to the given parameter and sets a listener to pick up changes that need to trigger validation |
| * and UI updates. |
| */ |
| protected void register(@NotNull String paramName, @NotNull JComboBox comboBox) { |
| myParamFields.put(paramName, comboBox); |
| Object value = myTemplateState.get(paramName); |
| if (value != null) { |
| for (int i = 0; i < comboBox.getItemCount(); i++) { |
| Object item = comboBox.getItemAt(i); |
| if (!(item instanceof ComboBoxItemWithApiTag)) { |
| continue; |
| } |
| if (((ComboBoxItemWithApiTag)item).id.equals(value)) { |
| comboBox.setSelectedIndex(i); |
| break; |
| } |
| } |
| } |
| comboBox.addFocusListener(this); |
| comboBox.addActionListener(this); |
| } |
| |
| /** |
| * Connects the given {@link JTextField} to the given parameter and sets a listener to pick up changes that need to trigger validation |
| * and UI updates. |
| */ |
| protected void register(@NotNull String paramName, @NotNull JTextField textField) { |
| String value = (String)myTemplateState.get(paramName); |
| if (value != null) { |
| textField.setText(value); |
| } else { |
| myTemplateState.put(paramName, ""); |
| } |
| myParamFields.put(paramName, textField); |
| textField.addFocusListener(this); |
| textField.getDocument().addDocumentListener(this); |
| } |
| |
| protected void register(@NotNull String paramName, @NotNull JRadioButton radioButton, @Nullable Object value) { |
| Object currentValue = myTemplateState.get(paramName); |
| radioButton.setSelected(currentValue != null && currentValue.equals(value)); |
| if (value != null) { |
| myRadioButtonValues.put(radioButton, Pair.of(paramName, value)); |
| } |
| radioButton.addFocusListener(this); |
| radioButton.addActionListener(this); |
| } |
| |
| protected void register(@NotNull String paramName, @NotNull JSlider paddingSlider) { |
| Integer value = (Integer)myTemplateState.get(paramName); |
| if (value != null) { |
| paddingSlider.setValue(value); |
| } else { |
| myTemplateState.put(paramName, paddingSlider.getValue()); |
| } |
| myParamFields.put(paramName, (JComponent)paddingSlider); |
| paddingSlider.addFocusListener(this); |
| paddingSlider.addChangeListener(this); |
| } |
| |
| protected void register(@NotNull String paramName, @NotNull JSpinner spinner) { |
| Integer value = (Integer)myTemplateState.get(paramName); |
| if (value != null) { |
| spinner.setValue(value); |
| } else { |
| myTemplateState.put(paramName, spinner.getValue()); |
| } |
| myParamFields.put(paramName, (JComponent)spinner); |
| spinner.addFocusListener(this); |
| spinner.addChangeListener(this); |
| } |
| |
| protected void register(@NotNull String paramName, @NotNull final TextFieldWithBrowseButton field) { |
| String value = (String)myTemplateState.get(paramName); |
| if (value != null) { |
| field.setText(value); |
| } else { |
| myTemplateState.put(paramName, field.getText()); |
| } |
| myParamFields.put(paramName, (JComponent)field); |
| field.addFocusListener(this); |
| field.getTextField().getDocument().addDocumentListener(this); |
| field.getTextField().addFocusListener(this); |
| } |
| |
| protected void register(@NotNull String paramName, @NotNull ColorPanel colorPanel) { |
| Color value = (Color)myTemplateState.get(paramName); |
| if (value != null) { |
| colorPanel.setSelectedColor(value); |
| } else { |
| myTemplateState.put(paramName, colorPanel.getSelectedColor()); |
| } |
| myParamFields.put(paramName, (JComponent)colorPanel); |
| colorPanel.addFocusListener(this); |
| colorPanel.addActionListener(this); |
| } |
| |
| /** Revalidates the UI and asks the parent wizard to update its state to reflect changes. */ |
| protected void update() { |
| if (myIgnoreUpdates) { |
| return; |
| } |
| |
| myIgnoreUpdates = true; |
| |
| // Create a list of parameters that have been modified this round |
| myIdsWithNewValues = new ConcurrentLinkedQueue<String>(); |
| |
| // First we load the updated values into our model |
| updateParams(); |
| |
| // The first time update() is called, refresh the "suggest" attrs by ensuring *all* parameters are set. |
| if (myFirstUpdate) { |
| myIdsWithNewValues = new ConcurrentLinkedQueue<String>(myTemplateState.getParameters().keySet()); |
| } |
| |
| // Then we calculate any values that we need to |
| deriveValues(); |
| // Finally we make sure these new values are valid |
| myIsValid = validate(); |
| if (myUpdateListener != null) { |
| myUpdateListener.update(); |
| } |
| |
| myFirstUpdate = false; |
| myIgnoreUpdates = false; |
| } |
| |
| protected boolean updateDerivedValue(@NotNull String attrName, @NotNull JTextField textField, @NotNull Callable<String> valueDeriver) { |
| boolean updated = false; |
| try { |
| myIgnoreUpdates = true; |
| if (!myTemplateState.myModified.contains(attrName)) { |
| String s = valueDeriver.call(); |
| if (s != null && !s.equals(myTemplateState.get(attrName))) { |
| myTemplateState.put(attrName, s); |
| textField.setText(s); |
| myTemplateState.myModified.remove(attrName); |
| updated = true; |
| } |
| } |
| } |
| catch (Exception e) { |
| } |
| finally { |
| myIgnoreUpdates = false; |
| } |
| return updated; |
| } |
| |
| @Override |
| public JComponent getComponent() { |
| return null; |
| } |
| |
| @Override |
| public Icon getIcon() { |
| return mySidePanelIcon; |
| } |
| |
| @Override |
| public void updateDataModel() { |
| } |
| |
| @Override |
| public void actionPerformed(@NotNull ActionEvent actionEvent) { |
| update(); |
| } |
| |
| @Override |
| public void focusGained(@NotNull FocusEvent focusEvent) { |
| update(); |
| } |
| |
| @Override |
| public void focusLost(@NotNull FocusEvent focusEvent) { |
| update(); |
| } |
| |
| @Override |
| public void insertUpdate(@NotNull DocumentEvent documentEvent) { |
| update(); |
| } |
| |
| @Override |
| public void removeUpdate(@NotNull DocumentEvent documentEvent) { |
| update(); |
| } |
| |
| @Override |
| public void changedUpdate(@NotNull DocumentEvent documentEvent) { |
| update(); |
| } |
| |
| @Override |
| public void stateChanged(@NotNull ChangeEvent e) { |
| update(); |
| } |
| |
| protected void hide(JComponent... components) { |
| for (JComponent component : components) { |
| component.setVisible(false); |
| } |
| } |
| |
| protected void show(JComponent... components) { |
| for (JComponent component : components) { |
| component.setVisible(true); |
| } |
| } |
| } |