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.data.UndoRedoHandler; 
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.progress.swing.PleaseWaitProgressMonitor;
017import org.openstreetmap.josm.gui.util.GuiHelper;
018
019/**
020 * This is the content panel inside the {@link MainFrame}. It displays the content the user is working with.
021 * <p>
022 * If there is no active layer, there is no content displayed. As soon as there are active layers, the {@link MapFrame} is displayed.
023 *
024 * @author Michael Zangl
025 * @since 10432
026 */
027public class MainPanel extends JPanel {
028    private MapFrame map;
029    // Needs to be lazy because we need to wait for preferences to set up.
030    private GettingStarted gettingStarted;
031    private final CopyOnWriteArrayList<MapFrameListener> mapFrameListeners = new CopyOnWriteArrayList<>();
032    private final transient MainLayerManager layerManager;
033
034    /**
035     * Create a new main panel
036     * @param layerManager The layer manager to use to display the content.
037     */
038    public MainPanel(MainLayerManager layerManager) {
039        super(new BorderLayout());
040        this.layerManager = layerManager;
041    }
042
043    /**
044     * Update the content of this {@link MainFrame} to either display the map or display the welcome screen.
045     * @param showMap If the map should be displayed.
046     */
047    protected synchronized void updateContent(boolean showMap) {
048        GuiHelper.assertCallFromEdt();
049        MapFrame old = map;
050        if (old != null && showMap) {
051            // no state change
052            return;
053        }
054
055        // remove old content
056        setVisible(false);
057        removeAll();
058        if (old != null) {
059            old.destroy();
060        }
061
062        // create new content
063        if (showMap) {
064            map = createNewMapFrame();
065        } else {
066            map = null;
067            MainApplication.map = map;
068            add(getGettingStarted(), BorderLayout.CENTER);
069            UndoRedoHandler.getInstance().clean();
070        }
071        setVisible(true);
072
073        if (old == null && !showMap) {
074            // listeners may not be able to handle this...
075            return;
076        }
077
078        // Notify map frame listeners, mostly plugins.
079        for (MapFrameListener listener : mapFrameListeners) {
080            listener.mapFrameInitialized(old, map);
081        }
082        if (map == null && PleaseWaitProgressMonitor.getCurrent() != null) {
083            PleaseWaitProgressMonitor.getCurrent().showForegroundDialog();
084        }
085    }
086
087    private MapFrame createNewMapFrame() {
088        MapFrame mapFrame = new MapFrame(null);
089        // Required by many components.
090        MainApplication.map = mapFrame;
091
092        mapFrame.fillPanel(this);
093
094        //TODO: Move this to some better place
095        List<Layer> layers = MainApplication.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 synchronized 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 synchronized 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(() -> updateContent(!layerManager.getLayers().isEmpty()));
166    }
167}