blob: 40e691c0d27f3b6c396ad640330fd67c18b63d97 [file] [log] [blame]
/*
* 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
}
}
}