001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.actions; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.awt.event.ActionEvent; 008 009import org.openstreetmap.josm.Main; 010import org.openstreetmap.josm.data.preferences.BooleanProperty; 011import org.openstreetmap.josm.tools.ListenerList; 012 013/** 014 * This action toggles the Expert mode. 015 * @since 4840 016 */ 017public class ExpertToggleAction extends ToggleAction { 018 019 /** 020 * This listener is notified whenever the expert mode setting changed. 021 */ 022 @FunctionalInterface 023 public interface ExpertModeChangeListener { 024 /** 025 * The expert mode changed. 026 * @param isExpert <code>true</code> if expert mode was enabled, false otherwise. 027 */ 028 void expertChanged(boolean isExpert); 029 } 030 031 // TODO: Switch to checked list. We can do this as soon as we do not see any more warnings. 032 private static final ListenerList<ExpertModeChangeListener> listeners = ListenerList.createUnchecked(); 033 private static final ListenerList<Component> visibilityToggleListeners = ListenerList.createUnchecked(); 034 035 private static final BooleanProperty PREF_EXPERT = new BooleanProperty("expert", false); 036 037 private static final ExpertToggleAction INSTANCE = new ExpertToggleAction(); 038 039 private static synchronized void fireExpertModeChanged(boolean isExpert) { 040 listeners.fireEvent(listener -> listener.expertChanged(isExpert)); 041 visibilityToggleListeners.fireEvent(c -> c.setVisible(isExpert)); 042 } 043 044 /** 045 * Register a expert mode change listener 046 * 047 * @param listener the listener. Ignored if null. 048 */ 049 public static void addExpertModeChangeListener(ExpertModeChangeListener listener) { 050 addExpertModeChangeListener(listener, false); 051 } 052 053 public static synchronized void addExpertModeChangeListener(ExpertModeChangeListener listener, boolean fireWhenAdding) { 054 if (listener == null) return; 055 listeners.addWeakListener(listener); 056 if (fireWhenAdding) { 057 listener.expertChanged(isExpert()); 058 } 059 } 060 061 /** 062 * Removes a expert mode change listener 063 * 064 * @param listener the listener. Ignored if null. 065 */ 066 public static synchronized void removeExpertModeChangeListener(ExpertModeChangeListener listener) { 067 if (listener == null) return; 068 listeners.removeListener(listener); 069 } 070 071 /** 072 * Marks a component to be only visible when expert mode is enabled. The visibility of the component is changed automatically. 073 * @param c The component. 074 */ 075 public static synchronized void addVisibilitySwitcher(Component c) { 076 if (c == null) return; 077 visibilityToggleListeners.addWeakListener(c); 078 c.setVisible(isExpert()); 079 } 080 081 /** 082 * Stops tracking visibility changes for the given component. 083 * @param c The component. 084 * @see #addVisibilitySwitcher(Component) 085 */ 086 public static synchronized void removeVisibilitySwitcher(Component c) { 087 if (c == null) return; 088 visibilityToggleListeners.removeListener(c); 089 } 090 091 /** 092 * Constructs a new {@code ExpertToggleAction}. 093 */ 094 public ExpertToggleAction() { 095 super(tr("Expert Mode"), 096 "expert", 097 tr("Enable/disable expert mode"), 098 null, 099 false /* register toolbar */ 100 ); 101 putValue("toolbar", "expertmode"); 102 if (Main.toolbar != null) { 103 Main.toolbar.register(this); 104 } 105 setSelected(PREF_EXPERT.get()); 106 notifySelectedState(); 107 } 108 109 @Override 110 protected final void notifySelectedState() { 111 super.notifySelectedState(); 112 PREF_EXPERT.put(isSelected()); 113 fireExpertModeChanged(isSelected()); 114 } 115 116 /** 117 * Forces the expert mode state to the given state. 118 * @param isExpert if expert mode should be used. 119 * @since 11224 120 */ 121 public void setExpert(boolean isExpert) { 122 if (isSelected() != isExpert) { 123 setSelected(isExpert); 124 notifySelectedState(); 125 } 126 } 127 128 @Override 129 public void actionPerformed(ActionEvent e) { 130 toggleSelectedState(e); 131 notifySelectedState(); 132 } 133 134 /** 135 * Replies the unique instance of this action. 136 * @return The unique instance of this action 137 */ 138 public static ExpertToggleAction getInstance() { 139 return INSTANCE; 140 } 141 142 /** 143 * Determines if expert mode is enabled. 144 * @return {@code true} if expert mode is enabled, {@code false} otherwise. 145 */ 146 public static boolean isExpert() { 147 return INSTANCE.isSelected(); 148 } 149}