001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui;
003
004import java.awt.BorderLayout;
005import java.util.List;
006import java.util.concurrent.CopyOnWriteArrayList;
007
008import javax.swing.JPanel;
009
010import org.openstreetmap.josm.Main;
011import org.openstreetmap.josm.actions.mapmode.MapMode;
012import org.openstreetmap.josm.gui.layer.Layer;
013import org.openstreetmap.josm.gui.layer.MainLayerManager;
014import org.openstreetmap.josm.gui.layer.MainLayerManager.LayerAvailabilityEvent;
015import org.openstreetmap.josm.gui.layer.MainLayerManager.LayerAvailabilityListener;
016import org.openstreetmap.josm.gui.util.GuiHelper;
017
018/**
019 * This is the content panel inside the {@link MainFrame}. It displays the content the user is working with.
020 * <p>
021 * If there is no active layer, there is no content displayed. As soon as there are active layers, the {@link MapFrame} is displayed.
022 *
023 * @author Michael Zangl
024 * @since 10432
025 */
026public class MainPanel extends JPanel {
027    private MapFrame map;
028    // Needs to be lazy because we need to wait for preferences to set up.
029    private GettingStarted gettingStarted;
030    private final CopyOnWriteArrayList<MapFrameListener> mapFrameListeners = new CopyOnWriteArrayList<>();
031    private final transient MainLayerManager layerManager;
032
033    /**
034     * Create a new main panel
035     * @param layerManager The layer manager to use to display the content.
036     */
037    public MainPanel(MainLayerManager layerManager) {
038        super(new BorderLayout());
039        this.layerManager = layerManager;
040    }
041
042    /**
043     * Update the content of this {@link MainFrame} to either display the map or display the welcome screen.
044     * @param showMap If the map should be displayed.
045     */
046    protected void updateContent(boolean showMap) {
047        GuiHelper.assertCallFromEdt();
048        MapFrame old = map;
049        if (old != null && showMap) {
050            // no state change
051            return;
052        }
053
054        // remove old content
055        setVisible(false);
056        removeAll();
057        if (old != null) {
058            old.destroy();
059        }
060
061        // create new content
062        if (showMap) {
063            map = createNewMapFrame();
064        } else {
065            map = null;
066            Main.map = map;
067            add(getGettingStarted(), BorderLayout.CENTER);
068        }
069        setVisible(true);
070
071        if (old == null && !showMap) {
072            // listeners may not be able to handle this...
073            return;
074        }
075
076        // Notify map frame listeners, mostly plugins.
077        for (MapFrameListener listener : mapFrameListeners) {
078            MapView.fireDeprecatedListenerOnAdd = true;
079            listener.mapFrameInitialized(old, map);
080            MapView.fireDeprecatedListenerOnAdd = false;
081        }
082        if (map == null && Main.currentProgressMonitor != null) {
083            Main.currentProgressMonitor.showForegroundDialog();
084        }
085    }
086
087    private MapFrame createNewMapFrame() {
088        MapFrame mapFrame = new MapFrame(null, null);
089        // Required by many components.
090        Main.map = mapFrame;
091
092        mapFrame.fillPanel(this);
093
094        //TODO: Move this to some better place
095        List<Layer> layers = Main.getLayerManager().getLayers();
096        if (!layers.isEmpty()) {
097            mapFrame.selectMapMode((MapMode) mapFrame.getDefaultButtonAction(), layers.get(0));
098        }
099        mapFrame.initializeDialogsPane();
100        mapFrame.setVisible(true);
101        return mapFrame;
102    }
103
104    /**
105     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
106     * <p>
107     * It will fire an initial mapFrameInitialized event
108     * when the MapFrame is present. Otherwise will only fire when the MapFrame is created
109     * or destroyed.
110     * @param listener The MapFrameListener
111     * @return {@code true} if the listeners collection changed as a result of the call.
112     */
113    public boolean addAndFireMapFrameListener(MapFrameListener listener) {
114        boolean changed = addMapFrameListener(listener);
115        if (changed && map != null) {
116            listener.mapFrameInitialized(null, map);
117        }
118        return changed;
119    }
120
121    /**
122     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
123     * @param listener The MapFrameListener
124     * @return {@code true} if the listeners collection changed as a result of the call
125     */
126    public boolean addMapFrameListener(MapFrameListener listener) {
127        return listener != null && mapFrameListeners.add(listener);
128    }
129
130    /**
131     * Unregisters the given {@code MapFrameListener} from MapFrame changes
132     * @param listener The MapFrameListener
133     * @return {@code true} if the listeners collection changed as a result of the call
134     */
135    public boolean removeMapFrameListener(MapFrameListener listener) {
136        return listener != null && mapFrameListeners.remove(listener);
137    }
138
139    /**
140     * Gets the {@link GettingStarted} panel.
141     * @return The panel.
142     */
143    public GettingStarted getGettingStarted() {
144        if (gettingStarted == null) {
145            gettingStarted = new GettingStarted();
146        }
147        return gettingStarted;
148    }
149
150    /**
151     * Re-adds the layer listeners. Never call this in production, only needed for testing.
152     */
153    public void reAddListeners() {
154        layerManager.addLayerAvailabilityListener(new LayerAvailabilityListener() {
155            @Override
156            public void beforeFirstLayerAdded(LayerAvailabilityEvent e) {
157                updateContent(true);
158            }
159
160            @Override
161            public void afterLastLayerRemoved(LayerAvailabilityEvent e) {
162                updateContent(false);
163            }
164        });
165        GuiHelper.runInEDTAndWait(new Runnable() {
166            @Override
167            public void run() {
168                updateContent(!layerManager.getLayers().isEmpty());
169            }
170        });
171    }
172}