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}