blob: abe744594ca7ad1f77a14a9bbbefd9720c058c92 [file] [log] [blame]
/*
* 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.uiDesigner.palette;
import com.intellij.ide.ui.LafManager;
import com.intellij.ide.ui.LafManagerListener;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
import com.intellij.openapi.components.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Condition;
import com.intellij.uiDesigner.Properties;
import com.intellij.uiDesigner.SwingProperties;
import com.intellij.uiDesigner.UIDesignerBundle;
import com.intellij.uiDesigner.core.GridConstraints;
import com.intellij.uiDesigner.lw.LwXmlReader;
import com.intellij.uiDesigner.lw.StringDescriptor;
import com.intellij.uiDesigner.propertyInspector.IntrospectedProperty;
import com.intellij.uiDesigner.propertyInspector.Property;
import com.intellij.uiDesigner.propertyInspector.PropertyEditor;
import com.intellij.uiDesigner.propertyInspector.PropertyRenderer;
import com.intellij.uiDesigner.propertyInspector.editors.IntEnumEditor;
import com.intellij.uiDesigner.propertyInspector.properties.*;
import com.intellij.uiDesigner.propertyInspector.renderers.IntEnumRenderer;
import com.intellij.uiDesigner.radComponents.RadComponent;
import com.intellij.util.containers.ContainerUtil;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author Anton Katilin
* @author Vladimir Kondratyev
*/
@State(
name = "Palette2",
storages = {
@Storage(file = StoragePathMacros.PROJECT_FILE),
@Storage(file = StoragePathMacros.PROJECT_CONFIG_DIR + "/uiDesigner.xml", scheme = StorageScheme.DIRECTORY_BASED)
}
)
public final class Palette implements Disposable, PersistentStateComponent<Element> {
private static final Logger LOG = Logger.getInstance("#com.intellij.uiDesigner.palette.Palette");
private final MyLafManagerListener myLafManagerListener;
private final Map<Class, IntrospectedProperty[]> myClass2Properties;
private final Map<String, ComponentItem> myClassName2Item;
/*All groups in the palette*/
private final ArrayList<GroupItem> myGroups;
/*Listeners, etc*/
private final List<Listener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private final Project myProject;
private final GroupItem mySpecialGroup = new GroupItem(true);
/**
* Predefined item for javax.swing.JPanel
*/
private ComponentItem myPanelItem;
@NonNls private static final String ATTRIBUTE_VSIZE_POLICY = "vsize-policy";
@NonNls private static final String ATTRIBUTE_HSIZE_POLICY = "hsize-policy";
@NonNls private static final String ATTRIBUTE_ANCHOR = "anchor";
@NonNls private static final String ATTRIBUTE_FILL = "fill";
@NonNls private static final String ELEMENT_MINIMUM_SIZE = "minimum-size";
@NonNls private static final String ATTRIBUTE_WIDTH = "width";
@NonNls private static final String ATTRIBUTE_HEIGHT = "height";
@NonNls private static final String ELEMENT_PREFERRED_SIZE = "preferred-size";
@NonNls private static final String ELEMENT_MAXIMUM_SIZE = "maximum-size";
@NonNls private static final String ATTRIBUTE_CLASS = "class";
@NonNls private static final String ATTRIBUTE_ICON = "icon";
@NonNls private static final String ATTRIBUTE_TOOLTIP_TEXT = "tooltip-text";
@NonNls private static final String ELEMENT_DEFAULT_CONSTRAINTS = "default-constraints";
@NonNls private static final String ELEMENT_INITIAL_VALUES = "initial-values";
@NonNls private static final String ELEMENT_PROPERTY = "property";
@NonNls private static final String ATTRIBUTE_NAME = "name";
@NonNls private static final String ATTRIBUTE_VALUE = "value";
@NonNls private static final String ATTRIBUTE_REMOVABLE = "removable";
@NonNls private static final String ELEMENT_ITEM = "item";
@NonNls private static final String ELEMENT_GROUP = "group";
@NonNls private static final String ATTRIBUTE_VERSION = "version";
@NonNls private static final String ATTRIBUTE_SINCE_VERSION = "since-version";
@NonNls private static final String ATTRIBUTE_AUTO_CREATE_BINDING = "auto-create-binding";
@NonNls private static final String ATTRIBUTE_CAN_ATTACH_LABEL = "can-attach-label";
@NonNls private static final String ATTRIBUTE_IS_CONTAINER = "is-container";
public static Palette getInstance(@NotNull final Project project) {
return ServiceManager.getService(project, Palette.class);
}
/** Invoked by reflection */
public Palette(Project project) {
myProject = project;
myLafManagerListener = project == null ? null : new MyLafManagerListener();
myClass2Properties = new HashMap<Class, IntrospectedProperty[]>();
myClassName2Item = new HashMap<String, ComponentItem>();
myGroups = new ArrayList<GroupItem>();
if (project != null) {
mySpecialGroup.setReadOnly(true);
mySpecialGroup.addItem(ComponentItem.createAnyComponentItem(project));
}
if (myLafManagerListener != null) {
LafManager.getInstance().addLafManagerListener(myLafManagerListener);
}
}
public Element getState() {
final Element e = new Element("state");
writeExternal(e);
return e;
}
public void loadState(Element state) {
readExternal(state);
}
/**Adds specified listener.*/
public void addListener(@NotNull final Listener l){
LOG.assertTrue(!myListeners.contains(l));
myListeners.add(l);
}
/**Removes specified listener.*/
public void removeListener(@NotNull final Listener l){
LOG.assertTrue(myListeners.contains(l));
myListeners.remove(l);
}
void fireGroupsChanged() {
for(Listener listener : myListeners) {
listener.groupsChanged(this);
}
}
@Override
public void dispose() {
if (myLafManagerListener != null) {
LafManager.getInstance().removeLafManagerListener(myLafManagerListener);
}
}
public void readExternal(@NotNull final Element element) {
/*
ApplicationManager.getApplication().assertIsDispatchThread();
*/
// It seems that IDEA inokes readExternal twice: first time for node in defaults XML
// the second time for node in project file. Stupidity... :(
myClass2Properties.clear();
myClassName2Item.clear();
myGroups.clear();
// Parse XML
final List groupElements = element.getChildren(ELEMENT_GROUP);
processGroups(groupElements);
// Ensure that all predefined items are loaded
LOG.assertTrue(myPanelItem != null);
if (!element.getAttributeValue(ATTRIBUTE_VERSION, "1").equals("2")) {
upgradePalette();
}
}
private void upgradePalette() {
// load new components from the predefined Palette2.xml
try {
//noinspection HardCodedStringLiteral
final Document document = new SAXBuilder().build(getClass().getResourceAsStream("/idea/Palette2.xml"));
for(Object o: document.getRootElement().getChildren(ELEMENT_GROUP)) {
Element groupElement = (Element) o;
for(GroupItem group: myGroups) {
if (group.getName().equals(groupElement.getAttributeValue(ATTRIBUTE_NAME))) {
upgradeGroup(group, groupElement);
break;
}
}
}
}
catch (Exception e) {
LOG.error(e);
}
}
private void upgradeGroup(final GroupItem group, final Element groupElement) {
for(Object o: groupElement.getChildren(ELEMENT_ITEM)) {
Element itemElement = (Element) o;
if (itemElement.getAttributeValue(ATTRIBUTE_SINCE_VERSION, "").equals("2")) {
processItemElement(itemElement, group, true);
}
final String className = LwXmlReader.getRequiredString(itemElement, ATTRIBUTE_CLASS);
final ComponentItem item = getItem(className);
if (item != null) {
if (LwXmlReader.getOptionalBoolean(itemElement, ATTRIBUTE_AUTO_CREATE_BINDING, false)) {
item.setAutoCreateBinding(true);
}
if (LwXmlReader.getOptionalBoolean(itemElement, ATTRIBUTE_CAN_ATTACH_LABEL, false)) {
item.setCanAttachLabel(true);
}
}
}
}
public void writeExternal(@NotNull final Element element) {
ApplicationManager.getApplication().assertIsDispatchThread();
writeGroups(element);
//element.setAttribute(ATTRIBUTE_VERSION, "2");
}
/**
* @return a predefined palette item which corresponds to the JPanel.
*/
@NotNull
public ComponentItem getPanelItem(){
return myPanelItem;
}
/**
* @return <code>ComponentItem</code> for the UI bean with the specified <code>componentClassName</code>.
* The method returns <code>null</code> if palette has no information about the specified
* class.
*/
@Nullable
public ComponentItem getItem(@NotNull final String componentClassName) {
return myClassName2Item.get(componentClassName);
}
/**
* @return read-only list of all groups in the palette.
* <em>DO NOT MODIFY OR CACHE THIS LIST</em>.
*/
public ArrayList<GroupItem> getGroups(){
return myGroups;
}
public GroupItem[] getToolWindowGroups() {
GroupItem[] groups = new GroupItem[myGroups.size()+1];
for(int i=0; i<myGroups.size(); i++) {
groups [i] = myGroups.get(i);
}
groups [myGroups.size()] = mySpecialGroup;
return groups;
}
/**
* @param groups list of new groups.
*/
public void setGroups(@NotNull final ArrayList<GroupItem> groups){
myGroups.clear();
myGroups.addAll(groups);
fireGroupsChanged();
}
/**
* Adds specified <code>item</code> to the palette.
* @param item item to be added
* @exception java.lang.IllegalArgumentException if an item for the same class
* is already exists in the palette
*/
public void addItem(@NotNull final GroupItem group, @NotNull final ComponentItem item) {
// class -> item
final String componentClassName = item.getClassName();
if (getItem(componentClassName) != null) {
Messages.showMessageDialog(
UIDesignerBundle.message("error.item.already.added", componentClassName),
ApplicationNamesInfo.getInstance().getFullProductName(),
Messages.getErrorIcon()
);
return;
}
myClassName2Item.put(componentClassName, item);
// group -> items
group.addItem(item);
// Process special predefined item for JPanel
if("javax.swing.JPanel".equals(item.getClassName())){
myPanelItem = item;
}
}
public void replaceItem(GroupItem group, ComponentItem oldItem, ComponentItem newItem) {
group.replaceItem(oldItem, newItem);
myClassName2Item.put(oldItem.getClassName(), newItem);
}
public void removeItem(final GroupItem group, final ComponentItem selectedItem) {
group.removeItem(selectedItem);
myClassName2Item.remove(selectedItem.getClassName());
}
public GroupItem findGroup(final ComponentItem componentItem) {
for(GroupItem group: myGroups) {
if (group.contains(componentItem)) {
return group;
}
}
return null;
}
/**
* Helper method.
*/
private static GridConstraints processDefaultConstraintsElement(@NotNull final Element element){
final GridConstraints constraints = new GridConstraints();
// grid related attributes
constraints.setVSizePolicy(LwXmlReader.getRequiredInt(element, ATTRIBUTE_VSIZE_POLICY));
constraints.setHSizePolicy(LwXmlReader.getRequiredInt(element, ATTRIBUTE_HSIZE_POLICY));
constraints.setAnchor(LwXmlReader.getRequiredInt(element, ATTRIBUTE_ANCHOR));
constraints.setFill(LwXmlReader.getRequiredInt(element, ATTRIBUTE_FILL));
// minimum size
final Element minSizeElement = element.getChild(ELEMENT_MINIMUM_SIZE);
if (minSizeElement != null) {
constraints.myMinimumSize.width = LwXmlReader.getRequiredInt(minSizeElement, ATTRIBUTE_WIDTH);
constraints.myMinimumSize.height = LwXmlReader.getRequiredInt(minSizeElement, ATTRIBUTE_HEIGHT);
}
// preferred size
final Element prefSizeElement = element.getChild(ELEMENT_PREFERRED_SIZE);
if (prefSizeElement != null){
constraints.myPreferredSize.width = LwXmlReader.getRequiredInt(prefSizeElement, ATTRIBUTE_WIDTH);
constraints.myPreferredSize.height = LwXmlReader.getRequiredInt(prefSizeElement, ATTRIBUTE_HEIGHT);
}
// maximum size
final Element maxSizeElement = element.getChild(ELEMENT_MAXIMUM_SIZE);
if (maxSizeElement != null) {
constraints.myMaximumSize.width = LwXmlReader.getRequiredInt(maxSizeElement, ATTRIBUTE_WIDTH);
constraints.myMaximumSize.height = LwXmlReader.getRequiredInt(maxSizeElement, ATTRIBUTE_HEIGHT);
}
return constraints;
}
private void processItemElement(@NotNull final Element itemElement, @NotNull final GroupItem group, final boolean skipExisting){
// Class name. It's OK if class does not exist.
final String className = LwXmlReader.getRequiredString(itemElement, ATTRIBUTE_CLASS);
if (skipExisting && getItem(className) != null) {
return;
}
// Icon (optional)
final String iconPath = LwXmlReader.getString(itemElement, ATTRIBUTE_ICON);
// Tooltip text (optional)
final String toolTipText = LwXmlReader.getString(itemElement, ATTRIBUTE_TOOLTIP_TEXT); // can be null
boolean autoCreateBinding = LwXmlReader.getOptionalBoolean(itemElement, ATTRIBUTE_AUTO_CREATE_BINDING, false);
boolean canAttachLabel = LwXmlReader.getOptionalBoolean(itemElement, ATTRIBUTE_CAN_ATTACH_LABEL, false);
boolean isContainer = LwXmlReader.getOptionalBoolean(itemElement, ATTRIBUTE_IS_CONTAINER, false);
// Default constraint
final GridConstraints constraints;
final Element defaultConstraints = itemElement.getChild(ELEMENT_DEFAULT_CONSTRAINTS);
if (defaultConstraints != null) {
constraints = processDefaultConstraintsElement(defaultConstraints);
}
else {
constraints = new GridConstraints();
}
final HashMap<String, StringDescriptor> propertyName2initialValue = new HashMap<String, StringDescriptor>();
{
final Element initialValues = itemElement.getChild(ELEMENT_INITIAL_VALUES);
if (initialValues != null){
for(final Object o : initialValues.getChildren(ELEMENT_PROPERTY)) {
final Element e = (Element)o;
final String name = LwXmlReader.getRequiredString(e, ATTRIBUTE_NAME);
// TODO[all] currently all initial values are strings
final StringDescriptor value = StringDescriptor.create(LwXmlReader.getRequiredString(e, ATTRIBUTE_VALUE));
propertyName2initialValue.put(name, value);
}
}
}
final boolean removable = LwXmlReader.getOptionalBoolean(itemElement, ATTRIBUTE_REMOVABLE, true);
final ComponentItem item = new ComponentItem(
myProject,
className,
iconPath,
toolTipText,
constraints,
propertyName2initialValue,
removable,
autoCreateBinding,
canAttachLabel
);
item.setIsContainer(isContainer);
addItem(group, item);
}
/**
* Reads PaletteElements from
*/
private void processGroups(final List groupElements){
for(final Object groupElement1 : groupElements) {
final Element groupElement = (Element)groupElement1;
final String groupName = LwXmlReader.getRequiredString(groupElement, ATTRIBUTE_NAME);
final GroupItem group = new GroupItem(groupName);
myGroups.add(group);
for (final Object o : groupElement.getChildren(ELEMENT_ITEM)) {
final Element itemElement = (Element)o;
try {
processItemElement(itemElement, group, false);
}
catch (Exception ex) {
LOG.error(ex);
}
}
}
}
/** Helper method */
private static void writeDefaultConstraintsElement(@NotNull final Element itemElement, @NotNull final GridConstraints c){
LOG.assertTrue(ELEMENT_ITEM.equals(itemElement.getName()));
final Element element = new Element(ELEMENT_DEFAULT_CONSTRAINTS);
itemElement.addContent(element);
// grid related attributes
{
element.setAttribute(ATTRIBUTE_VSIZE_POLICY, Integer.toString(c.getVSizePolicy()));
element.setAttribute(ATTRIBUTE_HSIZE_POLICY, Integer.toString(c.getHSizePolicy()));
element.setAttribute(ATTRIBUTE_ANCHOR, Integer.toString(c.getAnchor()));
element.setAttribute(ATTRIBUTE_FILL, Integer.toString(c.getFill()));
}
// minimum size
{
if (c.myMinimumSize.width != -1 || c.myMinimumSize.height != -1) {
final Element _element = new Element(ELEMENT_MINIMUM_SIZE);
element.addContent(_element);
_element.setAttribute(ATTRIBUTE_WIDTH, Integer.toString(c.myMinimumSize.width));
_element.setAttribute(ATTRIBUTE_HEIGHT, Integer.toString(c.myMinimumSize.height));
}
}
// preferred size
{
if (c.myPreferredSize.width != -1 || c.myPreferredSize.height != -1) {
final Element _element = new Element(ELEMENT_PREFERRED_SIZE);
element.addContent(_element);
_element.setAttribute(ATTRIBUTE_WIDTH, Integer.toString(c.myPreferredSize.width));
_element.setAttribute(ATTRIBUTE_HEIGHT, Integer.toString(c.myPreferredSize.height));
}
}
// maximum size
{
if (c.myMaximumSize.width != -1 || c.myMaximumSize.height != -1) {
final Element _element = new Element(ELEMENT_MAXIMUM_SIZE);
element.addContent(_element);
_element.setAttribute(ATTRIBUTE_WIDTH, Integer.toString(c.myMaximumSize.width));
_element.setAttribute(ATTRIBUTE_HEIGHT, Integer.toString(c.myMaximumSize.height));
}
}
}
/** Helper method */
private static void writeInitialValuesElement(
@NotNull final Element itemElement,
@NotNull final HashMap<String, StringDescriptor> name2value
){
LOG.assertTrue(ELEMENT_ITEM.equals(itemElement.getName()));
if(name2value.size() == 0){ // do not append 'initial-values' subtag
return;
}
final Element initialValuesElement = new Element(ELEMENT_INITIAL_VALUES);
itemElement.addContent(initialValuesElement);
for (final Map.Entry<String, StringDescriptor> entry : name2value.entrySet()) {
final Element propertyElement = new Element(ELEMENT_PROPERTY);
initialValuesElement.addContent(propertyElement);
propertyElement.setAttribute(ATTRIBUTE_NAME, entry.getKey());
propertyElement.setAttribute(ATTRIBUTE_VALUE, entry.getValue().getValue()/*descriptor is always trivial*/);
}
}
/** Helper method */
private static void writeComponentItem(@NotNull final Element groupElement, @NotNull final ComponentItem item){
LOG.assertTrue(ELEMENT_GROUP.equals(groupElement.getName()));
final Element itemElement = new Element(ELEMENT_ITEM);
groupElement.addContent(itemElement);
// Class
itemElement.setAttribute(ATTRIBUTE_CLASS, item.getClassName());
// Tooltip text (if any)
if(item.myToolTipText != null){
itemElement.setAttribute(ATTRIBUTE_TOOLTIP_TEXT, item.myToolTipText);
}
// Icon (if any)
final String iconPath = item.getIconPath();
if(iconPath != null){
itemElement.setAttribute(ATTRIBUTE_ICON, iconPath);
}
// Removable
itemElement.setAttribute(ATTRIBUTE_REMOVABLE, Boolean.toString(item.isRemovable()));
itemElement.setAttribute(ATTRIBUTE_AUTO_CREATE_BINDING, Boolean.toString(item.isAutoCreateBinding()));
itemElement.setAttribute(ATTRIBUTE_CAN_ATTACH_LABEL, Boolean.toString(item.isCanAttachLabel()));
if (item.isContainer()) {
itemElement.setAttribute(ATTRIBUTE_IS_CONTAINER, Boolean.toString(item.isContainer()));
}
// Default constraints
writeDefaultConstraintsElement(itemElement, item.getDefaultConstraints());
// Initial values (if any)
writeInitialValuesElement(itemElement, item.getInitialValues());
}
/**
* @param parentElement element to which all "group" elements will be appended
*/
private void writeGroups(@NotNull final Element parentElement){
for (final GroupItem group : myGroups) {
final Element groupElement = new Element(ELEMENT_GROUP);
parentElement.addContent(groupElement);
groupElement.setAttribute(ATTRIBUTE_NAME, group.getName());
final ComponentItem[] itemList = group.getItems();
for (ComponentItem aItemList : itemList) {
writeComponentItem(groupElement, aItemList);
}
}
}
/**
* Helper method
*/
private static IntroIntProperty createIntEnumProperty(
final String name,
final Method readMethod,
final Method writeMethod,
final IntEnumEditor.Pair[] pairs
){
return new IntroIntProperty(
name,
readMethod,
writeMethod,
new IntEnumRenderer(pairs),
new IntEnumEditor(pairs), false);
}
@NotNull
public IntrospectedProperty[] getIntrospectedProperties(@NotNull final RadComponent component) {
return getIntrospectedProperties(component.getComponentClass(), component.getDelegee().getClass());
}
/**
* @return arrys of all properties that can be introspected from the
* specified class. Only properties with getter and setter methods are
* returned.
*/
@NotNull
public IntrospectedProperty[] getIntrospectedProperties(@NotNull final Class aClass, @NotNull final Class delegeeClass) {
// Try the cache first
// TODO[vova, anton] update cache after class reloading (its properties caould be hanged).
if (myClass2Properties.containsKey(aClass)) {
return myClass2Properties.get(aClass);
}
final ArrayList<IntrospectedProperty> result = new ArrayList<IntrospectedProperty>();
try {
final BeanInfo beanInfo = Introspector.getBeanInfo(aClass);
final PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
for (final PropertyDescriptor descriptor : descriptors) {
Method readMethod = descriptor.getReadMethod();
Method writeMethod = descriptor.getWriteMethod();
Class propertyType = descriptor.getPropertyType();
if (writeMethod == null || readMethod == null || propertyType == null) {
continue;
}
boolean storeAsClient = false;
try {
delegeeClass.getMethod(readMethod.getName(), readMethod.getParameterTypes());
delegeeClass.getMethod(writeMethod.getName(), writeMethod.getParameterTypes());
}
catch (NoSuchMethodException e) {
storeAsClient = true;
}
@NonNls final String name = descriptor.getName();
final IntrospectedProperty property;
final Properties properties = (myProject == null) ? new Properties() : Properties.getInstance();
if (int.class.equals(propertyType)) { // int
IntEnumEditor.Pair[] enumPairs = properties.getEnumPairs(aClass, name);
if (enumPairs != null) {
property = createIntEnumProperty(name, readMethod, writeMethod, enumPairs);
}
else if (JLabel.class.isAssignableFrom(aClass)) { // special handling for javax.swing.JLabel
if (JLabel.class.isAssignableFrom(aClass) && ("displayedMnemonic".equals(name) || "displayedMnemonicIndex".equals(name)))
{ // skip JLabel#displayedMnemonic and JLabel#displayedMnemonicIndex
continue;
}
else {
property = new IntroIntProperty(name, readMethod, writeMethod, storeAsClient);
}
}
else if (AbstractButton.class.isAssignableFrom(aClass)) { // special handling AbstractButton subclasses
if ("mnemonic".equals(name) || "displayedMnemonicIndex".equals(name)) { // AbstractButton#mnemonic
continue;
}
else {
property = new IntroIntProperty(name, readMethod, writeMethod, storeAsClient);
}
}
else if (JTabbedPane.class.isAssignableFrom(aClass)) {
if (SwingProperties.SELECTED_INDEX.equals(name)) {
continue;
}
property = new IntroIntProperty(name, readMethod, writeMethod, storeAsClient);
}
else {
property = new IntroIntProperty(name, readMethod, writeMethod, storeAsClient);
}
}
else if (boolean.class.equals(propertyType)) { // boolean
property = new IntroBooleanProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (double.class.equals(propertyType)) {
property = new IntroPrimitiveTypeProperty(name, readMethod, writeMethod, storeAsClient, Double.class);
}
else if (float.class.equals(propertyType)) {
property = new IntroPrimitiveTypeProperty(name, readMethod, writeMethod, storeAsClient, Float.class);
}
else if (long.class.equals(propertyType)) {
property = new IntroPrimitiveTypeProperty(name, readMethod, writeMethod, storeAsClient, Long.class);
}
else if (byte.class.equals(propertyType)) {
property = new IntroPrimitiveTypeProperty(name, readMethod, writeMethod, storeAsClient, Byte.class);
}
else if (short.class.equals(propertyType)) {
property = new IntroPrimitiveTypeProperty(name, readMethod, writeMethod, storeAsClient, Short.class);
}
else if (char.class.equals(propertyType)) { // java.lang.String
property = new IntroCharProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (String.class.equals(propertyType)) { // java.lang.String
property = new IntroStringProperty(name, readMethod, writeMethod, myProject, storeAsClient);
}
else if (Insets.class.equals(propertyType)) { // java.awt.Insets
property = new IntroInsetsProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (Dimension.class.equals(propertyType)) { // java.awt.Dimension
property = new IntroDimensionProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (Rectangle.class.equals(propertyType)) { // java.awt.Rectangle
property = new IntroRectangleProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (Component.class.isAssignableFrom(propertyType)) {
if (JSplitPane.class.isAssignableFrom(aClass) && (name.equals("leftComponent") || name.equals("rightComponent") ||
name.equals("topComponent") || name.equals("bottomComponent"))) {
// these properties are set through layout
continue;
}
if (JTabbedPane.class.isAssignableFrom(aClass) && name.equals(SwingProperties.SELECTED_COMPONENT)) {
// can't set selectedComponent because of set property / add child sequence
continue;
}
if (JMenuBar.class.isAssignableFrom(propertyType) || JPopupMenu.class.isAssignableFrom(propertyType)) {
// no menu editing yet
continue;
}
Condition<RadComponent> filter = null;
if (name.equals(SwingProperties.LABEL_FOR)) {
filter = new Condition<RadComponent>() {
public boolean value(final RadComponent t) {
ComponentItem item = getItem(t.getComponentClassName());
return item != null && item.isCanAttachLabel();
}
};
}
property = new IntroComponentProperty(name, readMethod, writeMethod, propertyType, filter, storeAsClient);
}
else if (Color.class.equals(propertyType)) {
property = new IntroColorProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (Font.class.equals(propertyType)) {
property = new IntroFontProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (Icon.class.equals(propertyType)) {
property = new IntroIconProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (ListModel.class.isAssignableFrom(propertyType)) {
property = new IntroListModelProperty(name, readMethod, writeMethod, storeAsClient);
}
else if (Enum.class.isAssignableFrom(propertyType)) {
property = new IntroEnumProperty(name, readMethod, writeMethod, storeAsClient, propertyType);
}
else {
// other types are not supported (yet?)
continue;
}
result.add(property);
}
}
catch (IntrospectionException e) {
throw new RuntimeException(e);
}
final IntrospectedProperty[] properties = result.toArray(new IntrospectedProperty[result.size()]);
myClass2Properties.put(aClass, properties);
return properties;
}
/**
* @return introspected property with the given <code>name</code> of the
* specified <code>class</code>. The method returns <code>null</code> if there is no
* property with the such name.
*/
@Nullable
public IntrospectedProperty getIntrospectedProperty(@NotNull final RadComponent component, @NotNull final String name){
final IntrospectedProperty[] properties = getIntrospectedProperties(component);
for (final IntrospectedProperty property: properties) {
if (name.equals(property.getName())) {
return property;
}
}
return null;
}
/**
* @return "inplace" property for the component with the specified class.
* <b>DO NOT USE THIS METHOD DIRECTLY</b>. Use {@link com.intellij.uiDesigner.radComponents.RadComponent#getInplaceProperty(int, int) }
* instead.
*/
@Nullable
public IntrospectedProperty getInplaceProperty(@NotNull final RadComponent component) {
final String inplaceProperty = Properties.getInstance().getInplaceProperty(component.getComponentClass());
final IntrospectedProperty[] properties = getIntrospectedProperties(component);
for (int i = properties.length - 1; i >= 0; i--) {
final IntrospectedProperty property = properties[i];
if(property.getName().equals(inplaceProperty)){
return property;
}
}
return null;
}
public static boolean isRemovable(@NotNull final GroupItem group){
final ComponentItem[] items = group.getItems();
for(int i = items.length - 1; i >=0; i--){
if(!items [i].isRemovable()){
return false;
}
}
return true;
}
/**
* Updates UI of editors and renderers of all introspected properties
*/
private final class MyLafManagerListener implements LafManagerListener{
private void updateUI(final Property property){
final PropertyRenderer renderer = property.getRenderer();
renderer.updateUI();
final PropertyEditor editor = property.getEditor();
if(editor != null){
editor.updateUI();
}
final Property[] children = property.getChildren(null);
for (int i = children.length - 1; i >= 0; i--) {
updateUI(children[i]);
}
}
public void lookAndFeelChanged(final LafManager source) {
for (final IntrospectedProperty[] properties : myClass2Properties.values()) {
LOG.assertTrue(properties != null);
for (int j = properties.length - 1; j >= 0; j--) {
updateUI(properties[j]);
}
}
}
}
static interface Listener{
void groupsChanged(Palette palette);
}
}