blob: 81b615f077f1aa3c35ed621f54e7879e9384f616 [file] [log] [blame]
/*
* Copyright 2000-2014 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 com.intellij.util.xml.ui;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.JBColor;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.xml.*;
import com.intellij.util.xml.highlighting.DomElementAnnotationsManager;
import com.intellij.util.xml.highlighting.DomElementProblemDescriptor;
import com.intellij.util.xml.highlighting.DomElementsProblemsHolder;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.*;
import java.util.List;
/**
* @author peter
*/
public class ComboControl extends BaseModifiableControl<JComboBox, String> {
private static final Pair<String, Icon> EMPTY = new ComboBoxItem(" ", null);
private final Factory<List<Pair<String, Icon>>> myDataFactory;
private boolean myNullable;
private final Map<String, Icon> myIcons = new HashMap<String, Icon>();
private final ItemListener myCommitListener = new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
setModified();
commit();
}
};
public ComboControl(final GenericDomValue genericDomValue, final Factory<List<Pair<String, Icon>>> dataFactory) {
this(new DomStringWrapper(genericDomValue), dataFactory);
}
public ComboControl(final DomWrapper<String> domWrapper, final Factory<List<Pair<String, Icon>>> dataFactory) {
super(domWrapper);
myDataFactory = dataFactory;
reset();
}
public ComboControl(final DomWrapper<String> domWrapper, final Class<? extends Enum> aClass) {
super(domWrapper);
myDataFactory = createEnumFactory(aClass);
reset();
}
public final boolean isNullable() {
return myNullable;
}
public final void setNullable(final boolean nullable) {
myNullable = nullable;
}
public ComboControl(final GenericDomValue<?> reference) {
this(reference, createResolvingFunction(reference));
}
public static Factory<List<Pair<String, Icon>>> createResolvingFunction(final GenericDomValue<?> reference) {
return new Factory<List<Pair<String, Icon>>>() {
@Override
public List<Pair<String, Icon>> create() {
final Converter converter = reference.getConverter();
if (converter instanceof ResolvingConverter) {
final AbstractConvertContext context = new AbstractConvertContext() {
@Override
@NotNull
public DomElement getInvocationElement() {
return reference;
}
};
final ResolvingConverter resolvingConverter = (ResolvingConverter)converter;
final Collection<Object> variants = resolvingConverter.getVariants(context);
final List<Pair<String, Icon>> all =
new ArrayList<Pair<String, Icon>>(ContainerUtil.map(variants, new Function<Object, Pair<String, Icon>>() {
@Override
public Pair<String, Icon> fun(final Object s) {
return Pair.create(ElementPresentationManager.getElementName(s), ElementPresentationManager.getIcon(s));
}
}));
all.addAll(ContainerUtil.map(resolvingConverter.getAdditionalVariants(context), new Function() {
@Override
public Object fun(final Object s) {
return new Pair(s, null);
}
}));
return all;
}
return Collections.emptyList();
}
};
}
public static Factory<List<Pair<String, Icon>>> createPresentationFunction(final Factory<Collection<?>> variantFactory) {
return new Factory<List<Pair<String, Icon>>>() {
@Override
public List<Pair<String, Icon>> create() {
return ContainerUtil.map(variantFactory.create(), new Function<Object, Pair<String, Icon>>() {
@Override
public Pair<String, Icon> fun(final Object s) {
return Pair.create(ElementPresentationManager.getElementName(s), ElementPresentationManager.getIcon(s));
}
});
}
};
}
static Factory<List<Pair<String, Icon>>> createEnumFactory(final Class<? extends Enum> aClass) {
return new Factory<List<Pair<String, Icon>>>() {
@Override
public List<Pair<String, Icon>> create() {
return ContainerUtil.map2List(aClass.getEnumConstants(), new Function<Enum, Pair<String, Icon>>() {
@Override
public Pair<String, Icon> fun(final Enum s) {
return Pair.create(NamedEnumUtil.getEnumValueByElement(s), ElementPresentationManager.getIcon(s));
}
});
}
};
}
public static <T extends Enum> JComboBox createEnumComboBox(final Class<T> type) {
return tuneUpComboBox(new JComboBox(), createEnumFactory(type));
}
private static JComboBox tuneUpComboBox(final JComboBox comboBox, Factory<List<Pair<String, Icon>>> dataFactory) {
final List<Pair<String, Icon>> list = dataFactory.create();
final Set<String> standardValues = new HashSet<String>();
for (final Pair<String, Icon> pair : list) {
comboBox.addItem(new ComboBoxItem(pair));
standardValues.add(pair.first);
}
return initComboBox(comboBox, new Condition<String>() {
@Override
public boolean value(final String object) {
return standardValues.contains(object);
}
});
}
private static class ComboBoxItem extends Pair<String,Icon> {
public ComboBoxItem(String first, Icon second) {
super(first, second);
}
public ComboBoxItem(Pair<String,Icon> pair) {
super(pair.first, pair.second);
}
public String toString() {
return StringUtil.notNullize(first);
}
}
static JComboBox initComboBox(final JComboBox comboBox, final Condition<String> validity) {
comboBox.setEditable(false);
comboBox.setPrototypeDisplayValue(new ComboBoxItem("A", null));
comboBox.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
final Pair<String, Icon> pair = (Pair<String, Icon>)value;
final String text = pair == null ? null : pair.first;
setText(text);
final Dimension dimension = getPreferredSize();
if (!validity.value(text)) {
setFont(getFont().deriveFont(Font.ITALIC));
setForeground(JBColor.RED);
}
setIcon(pair == null ? null : pair.second);
setPreferredSize(new Dimension(-1, dimension.height));
return this;
}
});
return comboBox;
}
@Override
protected JComboBox createMainComponent(final JComboBox boundedComponent) {
return initComboBox(boundedComponent == null ? new JComboBox() : boundedComponent, new Condition<String>() {
@Override
public boolean value(final String object) {
return isValidValue(object);
}
});
}
public boolean isValidValue(final String object) {
return myNullable && object == EMPTY.first || myIcons.containsKey(object);
}
private boolean dataChanged(List<Pair<String, Icon>> newData) {
final JComboBox comboBox = getComponent();
final int size = comboBox.getItemCount();
final List<Pair<String, Icon>> oldData = new ArrayList<Pair<String, Icon>>(size);
for (int i = 0; i < size; i++) {
oldData.add((Pair<String, Icon>)comboBox.getItemAt(i));
}
if (myNullable) {
final LinkedList<Pair<String, Icon>> list = new LinkedList<Pair<String, Icon>>(newData);
list.addFirst(EMPTY);
newData = list;
}
return !newData.equals(oldData);
}
@Override
protected boolean isCommitted() {
return getComponent().isPopupVisible() || super.isCommitted();
}
@Override
protected void doReset() {
final List<Pair<String, Icon>> data = myDataFactory.create();
final JComboBox comboBox = getComponent();
comboBox.removeItemListener(myCommitListener);
try {
if (!dataChanged(data)) {
super.doReset();
return;
}
final String oldValue = getValue();
myIcons.clear();
comboBox.removeAllItems();
if (myNullable) {
comboBox.addItem(EMPTY);
}
for (final Pair<String, Icon> s : data) {
comboBox.addItem(new ComboBoxItem(s));
myIcons.put(s.first, s.second);
}
setValue(oldValue);
super.doReset();
}
finally {
comboBox.addItemListener(myCommitListener);
}
}
@Override
@Nullable
protected final String getValue() {
final Pair<String, Icon> pair = (Pair<String, Icon>)getComponent().getSelectedItem();
return pair == null || pair == EMPTY ? null : pair.first;
}
@Override
protected final void setValue(final String value) {
final JComboBox component = getComponent();
if (!isValidValue(value)) {
component.setEditable(true);
}
component.setSelectedItem(new ComboBoxItem(value, myIcons.get(value)));
component.setEditable(false);
}
@Override
protected void updateComponent() {
final DomElement domElement = getDomElement();
if (domElement == null || !domElement.isValid()) return;
final JComboBox comboBox = getComponent();
final Project project = getProject();
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (!project.isOpen()) return;
if (!getDomWrapper().isValid()) return;
final DomElement domElement = getDomElement();
if (domElement == null || !domElement.isValid()) return;
final DomElementAnnotationsManager manager = DomElementAnnotationsManager.getInstance(project);
final DomElementsProblemsHolder holder = manager.getCachedProblemHolder(domElement);
final List<DomElementProblemDescriptor> errorProblems = holder.getProblems(domElement);
final List<DomElementProblemDescriptor> warningProblems = holder.getProblems(domElement, true, HighlightSeverity.WARNING);
Color background = getDefaultBackground();
comboBox.setToolTipText(null);
if (errorProblems.size() > 0) {
background = getErrorBackground();
comboBox.setToolTipText(TooltipUtils.getTooltipText(errorProblems));
}
else if (warningProblems.size() > 0) {
background = getWarningBackground();
comboBox.setToolTipText(TooltipUtils.getTooltipText(warningProblems));
}
final Pair<String, Icon> pair = (Pair<String, Icon>)comboBox.getSelectedItem();
final String s = pair == null ? null : pair.first;
background = s != null && s.trim().length() > 0 ? getDefaultBackground() : background;
comboBox.setBackground(background);
comboBox.getEditor().getEditorComponent().setBackground(background);
}
});
}
}