| /* |
| * Copyright 2000-2009 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.uiDesigner.propertyInspector.properties; |
| |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.uiDesigner.*; |
| import com.intellij.uiDesigner.core.SupportCode; |
| import com.intellij.uiDesigner.lw.IProperty; |
| import com.intellij.uiDesigner.lw.StringDescriptor; |
| import com.intellij.uiDesigner.propertyInspector.IntrospectedProperty; |
| import com.intellij.uiDesigner.propertyInspector.PropertyEditor; |
| import com.intellij.uiDesigner.propertyInspector.PropertyRenderer; |
| import com.intellij.uiDesigner.propertyInspector.editors.string.StringEditor; |
| import com.intellij.uiDesigner.propertyInspector.renderers.StringRenderer; |
| import com.intellij.uiDesigner.radComponents.RadComponent; |
| import com.intellij.uiDesigner.radComponents.RadRootContainer; |
| import com.intellij.uiDesigner.snapShooter.SnapshotContext; |
| import com.intellij.util.containers.HashMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.lang.reflect.Method; |
| import java.util.Locale; |
| |
| /** |
| * @author Anton Katilin |
| * @author Vladimir Kondratyev |
| */ |
| public final class IntroStringProperty extends IntrospectedProperty<StringDescriptor> { |
| private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.propertyInspector.IntroStringProperty"); |
| |
| /** |
| * value: HashMap<String, StringDescriptor> |
| */ |
| @NonNls |
| private static final String CLIENT_PROP_NAME_2_DESCRIPTOR = "name2descriptor"; |
| |
| private final StringRenderer myRenderer; |
| private StringEditor myEditor; |
| private final Project myProject; |
| |
| public IntroStringProperty(final String name, |
| final Method readMethod, |
| final Method writeMethod, |
| final Project project, |
| final boolean storeAsClient) { |
| super(name, readMethod, writeMethod, storeAsClient); |
| myProject = project; |
| myRenderer = new StringRenderer(); |
| } |
| |
| @NotNull |
| public PropertyRenderer<StringDescriptor> getRenderer() { |
| return myRenderer; |
| } |
| |
| public PropertyEditor<StringDescriptor> getEditor() { |
| if (myEditor == null) { |
| myEditor = new StringEditor(myProject, this); |
| } |
| return myEditor; |
| } |
| |
| /** |
| * @return per RadComponent map between string property name and its StringDescriptor value. |
| */ |
| @NotNull |
| private static HashMap<String, StringDescriptor> getName2Descriptor(final RadComponent component){ |
| //noinspection unchecked |
| HashMap<String, StringDescriptor> name2Descriptor = (HashMap<String, StringDescriptor>)component.getClientProperty(CLIENT_PROP_NAME_2_DESCRIPTOR); |
| if(name2Descriptor == null){ |
| name2Descriptor = new HashMap<String,StringDescriptor>(); |
| component.putClientProperty(CLIENT_PROP_NAME_2_DESCRIPTOR, name2Descriptor); |
| } |
| return name2Descriptor; |
| } |
| |
| /** |
| * Utility method which merge together text and mnemonic at some position |
| */ |
| private static String mergeTextAndMnemonic(String text, final int mnemonic, final int mnemonicIndex){ |
| if (text == null) { |
| text = ""; |
| } |
| final int index; |
| if( |
| mnemonicIndex >= 0 && |
| mnemonicIndex < text.length() && |
| Character.toUpperCase(text.charAt(mnemonicIndex)) == mnemonic |
| ){ |
| // Index really corresponds to the mnemonic |
| index = mnemonicIndex; |
| } |
| else{ |
| // Mnemonic exists but index is wrong |
| index = -1; |
| } |
| |
| final StringBuffer buffer = new StringBuffer(text); |
| if(index != -1){ |
| buffer.insert(index, '&'); |
| // Quote all '&' except inserted one |
| for(int i = buffer.length() - 1; i >= 0; i--){ |
| if(buffer.charAt(i) == '&' && i != index){ |
| buffer.insert(i, '&'); |
| } |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| /** |
| * It's good that method is overriden here. |
| * |
| * @return instance of {@link StringDescriptor} |
| */ |
| public StringDescriptor getValue(final RadComponent component) { |
| // 1. resource bundle |
| { |
| final StringDescriptor descriptor = getName2Descriptor(component).get(getName()); |
| if(descriptor != null){ |
| return descriptor; |
| } |
| } |
| |
| // 2. plain value |
| final JComponent delegee = component.getDelegee(); |
| return stringDescriptorFromValue(component, delegee); |
| } |
| |
| private StringDescriptor stringDescriptorFromValue(final RadComponent component, final JComponent delegee) { |
| final StringDescriptor result; |
| if(SwingProperties.TEXT.equals(getName()) && (delegee instanceof JLabel)){ |
| final JLabel label = (JLabel)delegee; |
| result = StringDescriptor.create( |
| mergeTextAndMnemonic(label.getText(), label.getDisplayedMnemonic(), label.getDisplayedMnemonicIndex()) |
| ); |
| } |
| else |
| if(SwingProperties.TEXT.equals(getName()) && (delegee instanceof AbstractButton)){ |
| final AbstractButton button = (AbstractButton)delegee; |
| result = StringDescriptor.create( |
| mergeTextAndMnemonic(button.getText(), button.getMnemonic(), button.getDisplayedMnemonicIndex()) |
| ); |
| } |
| else { |
| if (component != null) { |
| result = StringDescriptor.create((String) invokeGetter(component)); |
| } |
| else { |
| try { |
| result = StringDescriptor.create((String) myReadMethod.invoke(delegee, EMPTY_OBJECT_ARRAY)); |
| } |
| catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| } |
| } |
| |
| if (result != null) { |
| // in this branch, the StringDescriptor is always a plain string, so resolve() is not necessary |
| result.setResolvedValue(result.getValue()); |
| } |
| return result; |
| } |
| |
| protected void setValueImpl(final RadComponent component, final StringDescriptor value) throws Exception { |
| // 1. Put value into map |
| if(value == null || (value.getBundleName() == null && !value.isNoI18n())) { |
| getName2Descriptor(component).remove(getName()); |
| } |
| else{ |
| getName2Descriptor(component).put(getName(), value); |
| } |
| |
| // 2. Apply real string value to JComponent peer |
| final JComponent delegee = component.getDelegee(); |
| |
| Locale locale = (Locale)component.getClientProperty(RadComponent.CLIENT_PROP_LOAD_TIME_LOCALE); |
| if (locale == null) { |
| RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(component); |
| if (root != null) { |
| locale = root.getStringDescriptorLocale(); |
| } |
| } |
| final String resolvedValue = (value != null && value.getValue() != null) |
| ? value.getValue() |
| : StringDescriptorManager.getInstance(component.getModule()).resolve(value, locale); |
| |
| if (value != null) { |
| value.setResolvedValue(resolvedValue); |
| } |
| |
| if(SwingProperties.TEXT.equals(getName())) { |
| final SupportCode.TextWithMnemonic textWithMnemonic = SupportCode.parseText(resolvedValue); |
| if (delegee instanceof JLabel) { |
| final JLabel label = (JLabel)delegee; |
| label.setText(textWithMnemonic.myText); |
| if(textWithMnemonic.myMnemonicIndex != -1){ |
| label.setDisplayedMnemonic(textWithMnemonic.getMnemonicChar()); |
| label.setDisplayedMnemonicIndex(textWithMnemonic.myMnemonicIndex); |
| } |
| else{ |
| label.setDisplayedMnemonic(0); |
| } |
| } |
| else if (delegee instanceof AbstractButton) { |
| final AbstractButton button = (AbstractButton)delegee; |
| button.setText(textWithMnemonic.myText); |
| if(textWithMnemonic.myMnemonicIndex != -1){ |
| button.setMnemonic(textWithMnemonic.getMnemonicChar()); |
| button.setDisplayedMnemonicIndex(textWithMnemonic.myMnemonicIndex); |
| } |
| else{ |
| button.setMnemonic(0); |
| } |
| } |
| else { |
| invokeSetter(component, resolvedValue); |
| } |
| checkUpdateBindingFromText(component, value, textWithMnemonic); |
| } |
| else{ |
| invokeSetter(component, resolvedValue); |
| } |
| } |
| |
| private static void checkUpdateBindingFromText(final RadComponent component, final StringDescriptor value, final SupportCode.TextWithMnemonic textWithMnemonic) { |
| if (component.isLoadingProperties()) { |
| return; |
| } |
| // only generate binding from text if default locale is active (IDEADEV-9427) |
| if (value.getValue() == null) { |
| RadRootContainer root = (RadRootContainer) FormEditingUtil.getRoot(component); |
| Locale locale = root.getStringDescriptorLocale(); |
| if (locale != null && locale.getDisplayName().length() > 0) { |
| return; |
| } |
| } |
| |
| BindingProperty.checkCreateBindingFromText(component, textWithMnemonic.myText); |
| if (component.getDelegee() instanceof JLabel) { |
| for(IProperty prop: component.getModifiedProperties()) { |
| if (prop.getName().equals(SwingProperties.LABEL_FOR) && prop instanceof IntroComponentProperty) { |
| ((IntroComponentProperty) prop).updateLabelForBinding(component); |
| } |
| } |
| } |
| } |
| |
| public boolean refreshValue(RadComponent component) { |
| StringDescriptor descriptor = getValue(component); |
| if (descriptor.getValue() != null) return false; |
| String oldResolvedValue = descriptor.getResolvedValue(); |
| descriptor.setResolvedValue(null); |
| try { |
| setValueImpl(component, descriptor); |
| return !Comparing.equal(oldResolvedValue, descriptor.getResolvedValue()); |
| } |
| catch (Exception e) { |
| LOG.error(e); |
| return false; |
| } |
| } |
| |
| public void write(@NotNull final StringDescriptor value, final XmlWriter writer) { |
| writer.writeStringDescriptor(value, |
| UIFormXmlConstants.ATTRIBUTE_VALUE, |
| UIFormXmlConstants.ATTRIBUTE_RESOURCE_BUNDLE, |
| UIFormXmlConstants.ATTRIBUTE_KEY); |
| } |
| |
| @Override public void importSnapshotValue(final SnapshotContext context, final JComponent component, final RadComponent radComponent) { |
| try { |
| Object value = myReadMethod.invoke(component, EMPTY_OBJECT_ARRAY); |
| if (value != null) { |
| setValue(radComponent, stringDescriptorFromValue(null, component)); |
| } |
| } |
| catch (Exception e) { |
| // ignore |
| } |
| } |
| } |