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.data.preferences.BooleanProperty; 010import org.openstreetmap.josm.gui.MainApplication; 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 /** 054 * Register a expert mode change listener, and optionnally fires it. 055 * @param listener the listener. Ignored if null. 056 * @param fireWhenAdding if true, the listener will be fired immediately after added 057 */ 058 public static synchronized void addExpertModeChangeListener(ExpertModeChangeListener listener, boolean fireWhenAdding) { 059 if (listener == null) return; 060 listeners.addWeakListener(listener); 061 if (fireWhenAdding) { 062 listener.expertChanged(isExpert()); 063 } 064 } 065 066 /** 067 * Removes a expert mode change listener 068 * 069 * @param listener the listener. Ignored if null. 070 */ 071 public static synchronized void removeExpertModeChangeListener(ExpertModeChangeListener listener) { 072 if (listener == null) return; 073 listeners.removeListener(listener); 074 } 075 076 /** 077 * Marks a component to be only visible when expert mode is enabled. The visibility of the component is changed automatically. 078 * @param c The component. 079 */ 080 public static synchronized void addVisibilitySwitcher(Component c) { 081 if (c == null) return; 082 visibilityToggleListeners.addWeakListener(c); 083 c.setVisible(isExpert()); 084 } 085 086 /** 087 * Stops tracking visibility changes for the given component. 088 * @param c The component. 089 * @see #addVisibilitySwitcher(Component) 090 */ 091 public static synchronized void removeVisibilitySwitcher(Component c) { 092 if (c == null) return; 093 visibilityToggleListeners.removeListener(c); 094 } 095 096 /** 097 * Determines if the given component tracks visibility changes. 098 * @param c The component. 099 * @return {@code true} if the given component tracks visibility changes 100 * @since 15649 101 */ 102 public static synchronized boolean hasVisibilitySwitcher(Component c) { 103 if (c == null) return false; 104 return visibilityToggleListeners.containsListener(c); 105 } 106 107 /** 108 * Constructs a new {@code ExpertToggleAction}. 109 */ 110 public ExpertToggleAction() { 111 super(tr("Expert Mode"), 112 "expert", 113 tr("Enable/disable expert mode"), 114 null, 115 false /* register toolbar */ 116 ); 117 putValue("toolbar", "expertmode"); 118 if (MainApplication.getToolbar() != null) { 119 MainApplication.getToolbar().register(this); 120 } 121 setSelected(PREF_EXPERT.get()); 122 notifySelectedState(); 123 } 124 125 @Override 126 protected final void notifySelectedState() { 127 super.notifySelectedState(); 128 PREF_EXPERT.put(isSelected()); 129 fireExpertModeChanged(isSelected()); 130 } 131 132 /** 133 * Forces the expert mode state to the given state. 134 * @param isExpert if expert mode should be used. 135 * @since 11224 136 */ 137 public void setExpert(boolean isExpert) { 138 if (isSelected() != isExpert) { 139 setSelected(isExpert); 140 notifySelectedState(); 141 } 142 } 143 144 @Override 145 public void actionPerformed(ActionEvent e) { 146 toggleSelectedState(e); 147 notifySelectedState(); 148 } 149 150 /** 151 * Replies the unique instance of this action. 152 * @return The unique instance of this action 153 */ 154 public static ExpertToggleAction getInstance() { 155 return INSTANCE; 156 } 157 158 /** 159 * Determines if expert mode is enabled. 160 * @return {@code true} if expert mode is enabled, {@code false} otherwise. 161 */ 162 public static boolean isExpert() { 163 return INSTANCE.isSelected(); 164 } 165}