diff --git a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java index 1fd48be8fa75..62719351d7f6 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/actionSystem/Presentation.java @@ -13,12 +13,15 @@ import com.intellij.openapi.util.text.Strings; import com.intellij.openapi.util.text.TextWithMnemonic; import com.intellij.util.BitUtil; import com.intellij.util.SmartFMap; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.FList; import org.jetbrains.annotations.*; import javax.swing.*; import java.awt.*; +import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.beans.PropertyChangeSupport; +import java.beans.PropertyChangeListenerProxy; import java.util.HashSet; import java.util.Objects; import java.util.Set; @@ -82,14 +85,14 @@ public final class Presentation implements Cloneable { private Icon hoveredIcon; private Icon selectedIcon; - private PropertyChangeSupport changeSupport; + private @NotNull FList myListeners = FList.emptyList(); private double myWeight = DEFAULT_WEIGHT; - private static final @NotNull NotNullLazyValue removeMnemonics = NotNullLazyValue.createValue(() -> { + private static final @NotNull NotNullLazyValue outRemoveMnemonics = NotNullLazyValue.createValue(() -> { return SystemInfoRt.isMac && DynamicBundle.LanguageBundleEP.EP_NAME.hasAnyExtensions(); }); - public static Presentation newTemplatePresentation() { + public static @NotNull Presentation newTemplatePresentation() { Presentation presentation = new Presentation(); presentation.myFlags = BitUtil.set(presentation.myFlags, IS_TEMPLATE, true); return presentation; @@ -108,18 +111,11 @@ public final class Presentation implements Cloneable { } public void addPropertyChangeListener(@NotNull PropertyChangeListener l) { - PropertyChangeSupport support = changeSupport; - if (support == null) { - changeSupport = support = new PropertyChangeSupport(this); - } - support.addPropertyChangeListener(l); + myListeners = myListeners.prepend(l); } public void removePropertyChangeListener(@NotNull PropertyChangeListener l) { - PropertyChangeSupport support = changeSupport; - if (support != null) { - support.removePropertyChangeListener(l); - } + myListeners = myListeners.without(l); } /** @@ -184,7 +180,7 @@ public final class Presentation implements Cloneable { TextWithMnemonic parsed = TextWithMnemonic.parse(s); UISettings uiSettings = UISettings.getInstanceOrNull(); boolean mnemonicsDisabled = uiSettings != null && uiSettings.getDisableMnemonicsInControls(); - return mnemonicsDisabled ? parsed.dropMnemonic(removeMnemonics.getValue()) : parsed; + return mnemonicsDisabled ? parsed.dropMnemonic(outRemoveMnemonics.getValue()) : parsed; }; } else { @@ -201,7 +197,7 @@ public final class Presentation implements Cloneable { * @param textWithMnemonicSupplier text with mnemonic to set */ public void setTextWithMnemonic(@NotNull Supplier textWithMnemonicSupplier) { - if (changeSupport == null) { + if (myListeners.isEmpty()) { this.textWithMnemonicSupplier = textWithMnemonicSupplier; return; } @@ -265,19 +261,23 @@ public final class Presentation implements Cloneable { } public void setDescription(@NotNull Supplier<@ActionDescription String> dynamicDescription) { + if (myListeners.isEmpty()) { + descriptionSupplier = dynamicDescription; + return; + } Supplier oldDescription = descriptionSupplier; descriptionSupplier = dynamicDescription; - if (changeSupport != null) { - fireObjectPropertyChange(PROP_DESCRIPTION, oldDescription.get(), descriptionSupplier.get()); - } + fireObjectPropertyChange(PROP_DESCRIPTION, oldDescription.get(), descriptionSupplier.get()); } public void setDescription(@ActionDescription String description) { + if (myListeners.isEmpty()) { + descriptionSupplier = () -> description; + return; + } Supplier oldDescriptionSupplier = descriptionSupplier; descriptionSupplier = () -> description; - if (changeSupport != null) { - fireObjectPropertyChange(PROP_DESCRIPTION, oldDescriptionSupplier.get(), description); - } + fireObjectPropertyChange(PROP_DESCRIPTION, oldDescriptionSupplier.get(), description); } public Icon getIcon() { @@ -304,28 +304,26 @@ public final class Presentation implements Cloneable { } public void setIcon(@Nullable Icon icon) { - if (changeSupport == null) { + if (myListeners.isEmpty()) { this.icon = icon == null ? null : () -> icon; return; } - Icon oldIcon = getIcon(); + Icon oldIcon = this.icon == null ? null : this.icon.get(); this.icon = () -> icon; - fireObjectPropertyChange(PROP_ICON, oldIcon, this.icon.get()); + fireObjectPropertyChange(PROP_ICON, oldIcon, icon); } public void setIconSupplier(@Nullable Supplier icon) { - Supplier oldIcon = this.icon; - this.icon = icon; - - PropertyChangeSupport support = changeSupport; - if (support != null) { - Icon icon1 = oldIcon == null ? null : oldIcon.get(); - Icon icon2 = icon == null ? null : icon.get(); - if (!Objects.equals(icon1, icon2)) { - support.firePropertyChange(PROP_ICON, icon1, icon2); - } + if (myListeners.isEmpty()) { + this.icon = icon; + return; } + + Icon oldIcon = this.icon == null ? null : this.icon.get(); + this.icon = icon; + Icon newIcon = icon == null ? null : icon.get(); + fireObjectPropertyChange(PROP_ICON, oldIcon, newIcon); } public Icon getDisabledIcon() { @@ -342,7 +340,7 @@ public final class Presentation implements Cloneable { return hoveredIcon; } - public void setHoveredIcon(final @Nullable Icon hoveredIcon) { + public void setHoveredIcon(@Nullable Icon hoveredIcon) { Icon old = this.hoveredIcon; this.hoveredIcon = hoveredIcon; fireObjectPropertyChange(PROP_HOVERED_ICON, old, this.hoveredIcon); @@ -352,7 +350,7 @@ public final class Presentation implements Cloneable { return selectedIcon; } - public void setSelectedIcon(Icon selectedIcon) { + public void setSelectedIcon(@Nullable Icon selectedIcon) { Icon old = this.selectedIcon; this.selectedIcon = selectedIcon; fireObjectPropertyChange(PROP_SELECTED_ICON, old, this.selectedIcon); @@ -505,17 +503,26 @@ public final class Presentation implements Cloneable { setVisible(enabled); } - private void fireBooleanPropertyChange(String propertyName, boolean oldValue, boolean newValue) { - PropertyChangeSupport support = changeSupport; - if (oldValue != newValue && support != null) { - support.firePropertyChange(propertyName, oldValue, newValue); - } + private void fireBooleanPropertyChange(@NotNull String propertyName, boolean oldValue, boolean newValue) { + if (myListeners.isEmpty() || oldValue == newValue) return; + PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + doFirePropertyChange(event, myListeners); } - private void fireObjectPropertyChange(String propertyName, Object oldValue, Object newValue) { - PropertyChangeSupport support = changeSupport; - if (support != null && !Objects.equals(oldValue, newValue)) { - support.firePropertyChange(propertyName, oldValue, newValue); + private void fireObjectPropertyChange(@NotNull String propertyName, Object oldValue, Object newValue) { + if (myListeners.isEmpty() || Objects.equals(oldValue, newValue)) return; + PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + doFirePropertyChange(event, myListeners); + } + + private static void doFirePropertyChange(@NotNull PropertyChangeEvent event, + @NotNull FList listeners) { + for (PropertyChangeListener listener : listeners.size() == 1 ? listeners : ContainerUtil.reverse(listeners)) { + if (listener instanceof PropertyChangeListenerProxy p && + !event.getPropertyName().equals(p.getPropertyName())) { + continue; + } + listener.propertyChange(event); } } @@ -530,7 +537,7 @@ public final class Presentation implements Cloneable { try { Presentation clone = (Presentation)super.clone(); clone.myFlags = BitUtil.set(clone.myFlags, IS_TEMPLATE, false); - clone.changeSupport = null; + clone.myListeners = FList.emptyList(); return clone; } catch (CloneNotSupportedException e) {