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}