| /* |
| * 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 com.intellij.android.designer.propertyTable.editors; |
| |
| import com.android.ide.common.resources.ResourceUrl; |
| import com.android.resources.ResourceType; |
| import com.intellij.android.designer.model.RadModelBuilder; |
| import com.intellij.android.designer.model.RadViewComponent; |
| import com.intellij.android.designer.propertyTable.renderers.ResourceRenderer; |
| import com.intellij.designer.model.PropertiesContainer; |
| import com.intellij.designer.model.PropertyContext; |
| import com.intellij.designer.model.RadComponent; |
| import com.intellij.designer.model.RadPropertyContext; |
| import com.intellij.designer.propertyTable.InplaceContext; |
| import com.intellij.designer.propertyTable.PropertyEditor; |
| import com.intellij.designer.propertyTable.editors.ComboEditor; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptor; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.ui.ComboBox; |
| import com.intellij.openapi.ui.ComponentWithBrowseButton; |
| import com.intellij.openapi.ui.TextFieldWithBrowseButton; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.ui.ComboboxWithBrowseButton; |
| import com.intellij.ui.DocumentAdapter; |
| import com.intellij.util.ArrayUtil; |
| import org.jetbrains.android.dom.attrs.AttributeFormat; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.DefaultComboBoxModel; |
| import javax.swing.JCheckBox; |
| import javax.swing.JComboBox; |
| import javax.swing.JComponent; |
| import javax.swing.JTextField; |
| import javax.swing.KeyStroke; |
| import javax.swing.SwingUtilities; |
| import javax.swing.border.Border; |
| import javax.swing.event.DocumentEvent; |
| import javax.swing.plaf.basic.ComboPopup; |
| import java.awt.Dimension; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.event.FocusAdapter; |
| import java.awt.event.FocusEvent; |
| import java.awt.event.ItemEvent; |
| import java.awt.event.ItemListener; |
| import java.awt.event.KeyEvent; |
| import java.util.EnumSet; |
| import java.util.Set; |
| |
| import static com.android.SdkConstants.*; |
| |
| /** |
| * @author Alexander Lobas |
| */ |
| public class ResourceEditor extends PropertyEditor { |
| private final ResourceType[] myTypes; |
| protected ComponentWithBrowseButton myEditor; |
| protected RadComponent myRootComponent; |
| protected RadComponent myComponent; |
| private JCheckBox myCheckBox; |
| private final Border myCheckBoxBorder = new JTextField().getBorder(); |
| private boolean myIgnoreCheckBoxValue; |
| private String myBooleanResourceValue; |
| private final boolean myIsDimension; |
| private final boolean myIsString; |
| |
| public ResourceEditor(Set<AttributeFormat> formats, String[] values) { |
| this(convertTypes(formats), formats, values); |
| } |
| |
| public ResourceEditor(@Nullable ResourceType[] types, Set<AttributeFormat> formats, @Nullable String[] values) { |
| myTypes = types; |
| myIsDimension = formats.contains(AttributeFormat.Dimension); |
| myIsString = formats.contains(AttributeFormat.String); |
| |
| if (formats.contains(AttributeFormat.Boolean)) { |
| myCheckBox = new JCheckBox(); |
| myEditor = new ComponentWithBrowseButton<JCheckBox>(myCheckBox, null) { |
| @Override |
| public Dimension getPreferredSize() { |
| return getComponentPreferredSize(); |
| } |
| }; |
| myCheckBox.addItemListener(new ItemListener() { |
| @Override |
| public void itemStateChanged(ItemEvent e) { |
| if (!myIgnoreCheckBoxValue) { |
| myBooleanResourceValue = null; |
| fireValueCommitted(false, true); |
| } |
| } |
| }); |
| } |
| else if (formats.contains(AttributeFormat.Enum)) { |
| ComboboxWithBrowseButton editor = new ComboboxWithBrowseButton(SystemInfo.isWindows ? new MyComboBox() : new JComboBox()) { |
| @Override |
| public Dimension getPreferredSize() { |
| return getComponentPreferredSize(); |
| } |
| }; |
| |
| final JComboBox comboBox = editor.getComboBox(); |
| DefaultComboBoxModel model = new DefaultComboBoxModel(values); |
| model.insertElementAt(StringsComboEditor.UNSET, 0); |
| comboBox.setModel(model); |
| comboBox.setEditable(true); |
| ComboEditor.installListeners(comboBox, new ComboEditor.ComboEditorListener(this) { |
| @Override |
| protected void onValueChosen() { |
| if (comboBox.getSelectedItem() == StringsComboEditor.UNSET) { |
| comboBox.setSelectedItem(null); |
| } |
| super.onValueChosen(); |
| } |
| }); |
| myEditor = editor; |
| comboBox.setSelectedIndex(0); |
| } |
| else { |
| myEditor = new TextFieldWithBrowseButton() { |
| @Override |
| protected void installPathCompletion(FileChooserDescriptor fileChooserDescriptor, |
| @Nullable Disposable parent) { |
| } |
| |
| @Override |
| public Dimension getPreferredSize() { |
| return getComponentPreferredSize(); |
| } |
| }; |
| myEditor.registerKeyboardAction(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| } |
| }, KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
| |
| JTextField textField = getComboText(); |
| textField.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| fireValueCommitted(false, true); |
| } |
| }); |
| textField.getDocument().addDocumentListener( |
| new DocumentAdapter() { |
| @Override |
| protected void textChanged(final DocumentEvent e) { |
| preferredSizeChanged(); |
| } |
| } |
| ); |
| selectTextOnFocusGain(textField); |
| } |
| |
| if (myCheckBox == null) { |
| myEditor.registerKeyboardAction(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| } |
| }, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); |
| } |
| |
| myEditor.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| showDialog(); |
| } |
| }); |
| myEditor.addFocusListener(new FocusAdapter() { |
| @Override |
| public void focusGained(FocusEvent e) { |
| myEditor.getChildComponent().requestFocus(); |
| } |
| }); |
| myEditor.getButton().setSize(22); |
| myEditor.getButton().setAttachedComponent(null); |
| } |
| |
| public static void selectTextOnFocusGain(JTextField textField) { |
| textField.addFocusListener(new FocusAdapter() { |
| @Override |
| public void focusGained(FocusEvent focusEvent) { |
| Object source = focusEvent.getSource(); |
| if (source instanceof JTextField) { |
| ((JTextField)source).selectAll(); |
| } |
| } |
| }); |
| } |
| |
| private Dimension getComponentPreferredSize() { |
| Dimension size1 = myEditor.getChildComponent().getPreferredSize(); |
| Dimension size2 = myEditor.getButton().getPreferredSize(); |
| return new Dimension(Math.max(size1.width, 25) + 5 + size2.width, size1.height); |
| } |
| |
| public static ResourceType[] convertTypes(Set<AttributeFormat> formats) { |
| Set<ResourceType> types = EnumSet.noneOf(ResourceType.class); |
| for (AttributeFormat format : formats) { |
| switch (format) { |
| case Boolean: |
| types.add(ResourceType.BOOL); |
| break; |
| case Color: |
| types.add(ResourceType.COLOR); |
| types.add(ResourceType.DRAWABLE); |
| types.add(ResourceType.MIPMAP); |
| break; |
| case Dimension: |
| types.add(ResourceType.DIMEN); |
| break; |
| case Integer: |
| types.add(ResourceType.INTEGER); |
| break; |
| case Fraction: |
| types.add(ResourceType.FRACTION); |
| break; |
| case String: |
| types.add(ResourceType.STRING); |
| break; |
| case Reference: |
| types.add(ResourceType.COLOR); |
| types.add(ResourceType.DRAWABLE); |
| types.add(ResourceType.MIPMAP); |
| types.add(ResourceType.STRING); |
| types.add(ResourceType.ID); |
| types.add(ResourceType.STYLE); |
| break; |
| } |
| } |
| |
| return types.toArray(new ResourceType[types.size()]); |
| } |
| |
| @NotNull |
| @Override |
| public JComponent getComponent(@Nullable PropertiesContainer container, |
| @Nullable PropertyContext context, |
| Object object, |
| @Nullable InplaceContext inplaceContext) { |
| myComponent = (RadComponent)container; |
| myRootComponent = context instanceof RadPropertyContext ? ((RadPropertyContext)context).getRootComponent() : null; |
| |
| String value = (String)object; |
| JTextField text = getComboText(); |
| |
| if (text == null) { |
| if (StringUtil.isEmpty(value) || value.equals("true") || value.equals("false")) { |
| myBooleanResourceValue = null; |
| } |
| else { |
| myBooleanResourceValue = value; |
| } |
| |
| try { |
| myIgnoreCheckBoxValue = true; |
| myCheckBox.setSelected(Boolean.parseBoolean(value)); |
| } |
| finally { |
| myIgnoreCheckBoxValue = false; |
| } |
| |
| if (inplaceContext == null) { |
| myEditor.setBorder(null); |
| myCheckBox.setText(null); |
| } |
| else { |
| myEditor.setBorder(myCheckBoxBorder); |
| myCheckBox.setText(myBooleanResourceValue); |
| } |
| } |
| else { |
| text.setText(value); |
| if (inplaceContext != null) { |
| text.setColumns(0); |
| if (inplaceContext.isStartChar()) { |
| text.setText(inplaceContext.getText(text.getText())); |
| } |
| } |
| } |
| return myEditor; |
| } |
| |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| JTextField text = getComboText(); |
| return text == null ? myCheckBox : text; |
| } |
| |
| @Override |
| public Object getValue() { |
| JTextField text = getComboText(); |
| if (text == null) { |
| return myBooleanResourceValue == null ? Boolean.toString(myCheckBox.isSelected()) : myBooleanResourceValue; |
| } |
| String value = text.getText(); |
| if (value == StringsComboEditor.UNSET || StringUtil.isEmpty(value)) { |
| return null; |
| } |
| if (myIsDimension && |
| !value.startsWith(PREFIX_RESOURCE_REF) && |
| !value.endsWith(UNIT_DIP) && |
| !value.equalsIgnoreCase(VALUE_WRAP_CONTENT) && |
| !value.equalsIgnoreCase(VALUE_FILL_PARENT) && |
| !value.equalsIgnoreCase(VALUE_MATCH_PARENT)) { |
| if (value.length() <= 2) { |
| return value + UNIT_DP; |
| } |
| int index = value.length() - 2; |
| String dimension = value.substring(index); |
| if (ArrayUtil.indexOf(ResourceRenderer.DIMENSIONS, dimension) == -1) { |
| return value + UNIT_DP; |
| } |
| } |
| |
| // If it looks like a reference, don't escape it. |
| if (myIsString && |
| (value.startsWith(PREFIX_RESOURCE_REF) || value.startsWith(PREFIX_THEME_REF)) && |
| ResourceUrl.parse(value) == null) { |
| return "\\" + value; |
| } |
| return value; |
| } |
| |
| @Override |
| public void updateUI() { |
| SwingUtilities.updateComponentTreeUI(myEditor); |
| } |
| |
| protected void showDialog() { |
| Module module = RadModelBuilder.getModule(myRootComponent); |
| if (module == null) { |
| if (myBooleanResourceValue != null) { |
| fireEditingCancelled(); |
| } |
| return; |
| } |
| ResourceDialog dialog = new ResourceDialog(module, myTypes, (String)getValue(), (RadViewComponent)myComponent); |
| if (dialog.showAndGet()) { |
| setValue(dialog.getResourceName()); |
| } |
| else { |
| if (myBooleanResourceValue != null) { |
| fireEditingCancelled(); |
| } |
| } |
| } |
| |
| protected final void setValue(String value) { |
| JTextField text = getComboText(); |
| if (text == null) { |
| myBooleanResourceValue = value; |
| fireValueCommitted(false, true); |
| } |
| else { |
| text.setText(value); |
| fireValueCommitted(true, true); |
| } |
| } |
| |
| private JTextField getComboText() { |
| JComponent component = myEditor.getChildComponent(); |
| if (component instanceof JTextField) { |
| return (JTextField)component; |
| } |
| if (component instanceof JComboBox) { |
| JComboBox combo = (JComboBox)component; |
| return (JTextField)combo.getEditor().getEditorComponent(); |
| } |
| return null; |
| } |
| |
| private static final class MyComboBox extends ComboBox { |
| public MyComboBox() { |
| ((JTextField)getEditor().getEditorComponent()).addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| if (isPopupVisible()) { |
| ComboPopup popup = getPopup(); |
| if (popup != null) { |
| setSelectedItem(popup.getList().getSelectedValue()); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |