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}