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.actions.mapmode.MapMode;
011import org.openstreetmap.josm.gui.layer.Layer;
012import org.openstreetmap.josm.gui.layer.MainLayerManager;
013import org.openstreetmap.josm.gui.layer.MainLayerManager.LayerAvailabilityEvent;
014import org.openstreetmap.josm.gui.layer.MainLayerManager.LayerAvailabilityListener;
015import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
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 synchronized 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            MainApplication.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            listener.mapFrameInitialized(old, map);
079        }
080        if (map == null && PleaseWaitProgressMonitor.getCurrent() != null) {
081            PleaseWaitProgressMonitor.getCurrent().showForegroundDialog();
082        }
083    }
084
085    private MapFrame createNewMapFrame() {
086        MapFrame mapFrame = new MapFrame(null);
087        // Required by many components.
088        MainApplication.map = mapFrame;
089
090        mapFrame.fillPanel(this);
091
092        //TODO: Move this to some better place
093        List<Layer> layers = MainApplication.getLayerManager().getLayers();
094        if (!layers.isEmpty()) {
095            mapFrame.selectMapMode((MapMode) mapFrame.getDefaultButtonAction(), layers.get(0));
096        }
097        mapFrame.initializeDialogsPane();
098        mapFrame.setVisible(true);
099        return mapFrame;
100    }
101
102    /**
103     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes.
104     * <p>
105     * It will fire an initial mapFrameInitialized event
106     * when the MapFrame is present. Otherwise will only fire when the MapFrame is created
107     * or destroyed.
108     * @param listener The MapFrameListener
109     * @return {@code true} if the listeners collection changed as a result of the call.
110     */
111    public synchronized boolean addAndFireMapFrameListener(MapFrameListener listener) {
112        boolean changed = addMapFrameListener(listener);
113        if (changed && map != null) {
114            listener.mapFrameInitialized(null, map);
115        }
116        return changed;
117    }
118
119    /**
120     * Registers a new {@code MapFrameListener} that will be notified of MapFrame changes
121     * @param listener The MapFrameListener
122     * @return {@code true} if the listeners collection changed as a result of the call
123     */
124    public boolean addMapFrameListener(MapFrameListener listener) {
125        return listener != null && mapFrameListeners.add(listener);
126    }
127
128    /**
129     * Unregisters the given {@code MapFrameListener} from MapFrame changes
130     * @param listener The MapFrameListener
131     * @return {@code true} if the listeners collection changed as a result of the call
132     */
133    public boolean removeMapFrameListener(MapFrameListener listener) {
134        return listener != null && mapFrameListeners.remove(listener);
135    }
136
137    /**
138     * Gets the {@link GettingStarted} panel.
139     * @return The panel.
140     */
141    public synchronized GettingStarted getGettingStarted() {
142        if (gettingStarted == null) {
143            gettingStarted = new GettingStarted();
144        }
145        return gettingStarted;
146    }
147
148    /**
149     * Re-adds the layer listeners. Never call this in production, only needed for testing.
150     */
151    public void reAddListeners() {
152        layerManager.addLayerAvailabilityListener(new LayerAvailabilityListener() {
153            @Override
154            public void beforeFirstLayerAdded(LayerAvailabilityEvent e) {
155                updateContent(true);
156            }
157
158            @Override
159            public void afterLastLayerRemoved(LayerAvailabilityEvent e) {
160                updateContent(false);
161            }
162        });
163        GuiHelper.runInEDTAndWait(() -> updateContent(!layerManager.getLayers().isEmpty()));
164    }
165}