/* * Copyright 2000-2016 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.ApplicationNamesInfo; import com.intellij.openapi.components.PersistentStateComponent; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; 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", defaultStateAsResource = true, storages = @Storage("uiDesigner.xml")) 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 List<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<>(); myClassName2Item = new HashMap<>(); myGroups = new ArrayList<>(); if (project != null) { mySpecialGroup.setReadOnly(true); mySpecialGroup.addItem(ComponentItem.createAnyComponentItem(project)); } if (myLafManagerListener != null) { LafManager.getInstance().addLafManagerListener(myLafManagerListener); } } @Override public Element getState() { Element state = new Element("state"); writeGroups(state); return state; } @Override public void loadState(Element state) { myClass2Properties.clear(); myClassName2Item.clear(); myGroups.clear(); processGroups(state.getChildren(ELEMENT_GROUP)); // Ensure that all predefined items are loaded LOG.assertTrue(myPanelItem != null); if (!state.getAttributeValue(ATTRIBUTE_VERSION, "1").equals("2")) { upgradePalette(); } } /** * 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); } } private void upgradePalette() { // load new components from the predefined Palette2.xml try { //noinspection HardCodedStringLiteral Document document = new SAXBuilder().build(getClass().getResourceAsStream("/idea/Palette2.xml")); for (Element groupElement : document.getRootElement().getChildren(ELEMENT_GROUP)) { 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 (Element itemElement : groupElement.getChildren(ELEMENT_ITEM)) { 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); } } } } /** * @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 List<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 * @throws 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<>(); { 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(@NotNull List<Element> groupElements) { for (Element groupElement : groupElements) { GroupItem group = new GroupItem(LwXmlReader.getRequiredString(groupElement, ATTRIBUTE_NAME)); myGroups.add(group); for (Element itemElement : groupElement.getChildren(ELEMENT_ITEM)) { 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()); } private void writeGroups(@NotNull Element parentElement) { for (GroupItem group : myGroups) { Element groupElement = new Element(ELEMENT_GROUP); parentElement.addContent(groupElement); groupElement.setAttribute(ATTRIBUTE_NAME, group.getName()); for (ComponentItem aItemList : group.getItems()) { 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 arrays 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 could be hanged). if (myClass2Properties.containsKey(aClass)) { return myClass2Properties.get(aClass); } final ArrayList<IntrospectedProperty> result = new ArrayList<>(); 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 = 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 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]); } } @Override 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]); } } } } interface Listener { void groupsChanged(Palette palette); } }