001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.BorderLayout; 007import java.awt.Component; 008import java.awt.Image; 009import java.awt.Rectangle; 010import java.awt.Toolkit; 011import java.awt.event.ComponentEvent; 012import java.awt.event.ComponentListener; 013import java.awt.event.WindowAdapter; 014import java.awt.event.WindowEvent; 015import java.beans.PropertyChangeListener; 016import java.util.LinkedList; 017import java.util.List; 018 019import javax.swing.JFrame; 020import javax.swing.JPanel; 021 022import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent; 023import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener; 024import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent; 025import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent; 026import org.openstreetmap.josm.gui.layer.OsmDataLayer; 027import org.openstreetmap.josm.gui.layer.OsmDataLayer.LayerStateChangeListener; 028import org.openstreetmap.josm.gui.util.WindowGeometry; 029import org.openstreetmap.josm.spi.preferences.Config; 030import org.openstreetmap.josm.tools.ImageProvider; 031import org.openstreetmap.josm.tools.Logging; 032 033/** 034 * This is the JOSM main window. It updates it's title. 035 * @author Michael Zangl 036 * @since 10340 037 */ 038public class MainFrame extends JFrame { 039 private final transient LayerStateChangeListener updateTitleOnLayerStateChange = (layer, newValue) -> onLayerChange(layer); 040 041 private final transient PropertyChangeListener updateTitleOnSaveChange = evt -> { 042 if (evt.getPropertyName().equals(OsmDataLayer.REQUIRES_SAVE_TO_DISK_PROP) 043 || evt.getPropertyName().equals(OsmDataLayer.REQUIRES_UPLOAD_TO_SERVER_PROP)) { 044 OsmDataLayer layer = (OsmDataLayer) evt.getSource(); 045 onLayerChange(layer); 046 } 047 }; 048 049 protected transient WindowGeometry geometry; 050 protected int windowState = JFrame.NORMAL; 051 private final MainPanel panel; 052 private MainMenu menu; 053 054 /** 055 * Create a new main window. 056 */ 057 public MainFrame() { 058 this(new WindowGeometry(new Rectangle(10, 10, 500, 500))); 059 } 060 061 /** 062 * Create a new main window. The parameter will be removed in the future. 063 * @param geometry The inital geometry to use. 064 * @since 12127 065 */ 066 public MainFrame(WindowGeometry geometry) { 067 super(); 068 this.geometry = geometry; 069 this.panel = new MainPanel(MainApplication.getLayerManager()); 070 setContentPane(new JPanel(new BorderLayout())); 071 } 072 073 /** 074 * Initializes the content of the window and get the current status panel. 075 */ 076 public void initialize() { 077 menu = new MainMenu(); 078 addComponentListener(new WindowPositionSizeListener()); 079 addWindowStateListener(new WindowPositionSizeListener()); 080 081 setJMenuBar(menu); 082 geometry.applySafe(this); 083 List<Image> l = new LinkedList<>(); 084 l.add(ImageProvider.get("logo_16x16x32").getImage()); 085 l.add(ImageProvider.get("logo_16x16x8").getImage()); 086 l.add(ImageProvider.get("logo_32x32x32").getImage()); 087 l.add(ImageProvider.get("logo_32x32x8").getImage()); 088 l.add(ImageProvider.get("logo_48x48x32").getImage()); 089 l.add(ImageProvider.get("logo_48x48x8").getImage()); 090 l.add(ImageProvider.get("logo").getImage()); 091 setIconImages(l); 092 addWindowListener(new ExitWindowAdapter()); 093 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 094 095 // This listener is never removed, since the main frame exists forever. 096 MainApplication.getLayerManager().addActiveLayerChangeListener(e -> refreshTitle()); 097 MainApplication.getLayerManager().addAndFireLayerChangeListener(new ManageLayerListeners()); 098 099 refreshTitle(); 100 101 getContentPane().add(panel, BorderLayout.CENTER); 102 menu.initialize(); 103 } 104 105 /** 106 * Stores the current state of the main frame. 107 */ 108 public void storeState() { 109 if (geometry != null) { 110 geometry.remember("gui.geometry"); 111 } 112 Config.getPref().putBoolean("gui.maximized", (windowState & JFrame.MAXIMIZED_BOTH) != 0); 113 } 114 115 /** 116 * Gets the main menu used for this window. 117 * @return The main menu. 118 * @throws IllegalStateException if the main frame has not been initialized yet 119 * @see #initialize 120 */ 121 public MainMenu getMenu() { 122 if (menu == null) { 123 throw new IllegalStateException("Not initialized."); 124 } 125 return menu; 126 } 127 128 /** 129 * Gets the main panel. 130 * @return The main panel. 131 * @since 12125 132 */ 133 public MainPanel getPanel() { 134 return panel; 135 } 136 137 /** 138 * Sets this frame to be maximized. 139 * @param maximized <code>true</code> if the window should be maximized. 140 */ 141 public void setMaximized(boolean maximized) { 142 if (maximized) { 143 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) { 144 windowState = JFrame.MAXIMIZED_BOTH; 145 setExtendedState(windowState); 146 } else { 147 Logging.debug("Main window: maximizing not supported"); 148 } 149 } else { 150 throw new UnsupportedOperationException("Unimplemented."); 151 } 152 } 153 154 /** 155 * Update the title of the window to reflect the current content. 156 */ 157 public void refreshTitle() { 158 OsmDataLayer editLayer = MainApplication.getLayerManager().getEditLayer(); 159 boolean dirty = editLayer != null && (editLayer.requiresSaveToFile() 160 || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged())); 161 setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor")); 162 getRootPane().putClientProperty("Window.documentModified", dirty); 163 } 164 165 private void onLayerChange(OsmDataLayer layer) { 166 if (layer == MainApplication.getLayerManager().getEditLayer()) { 167 refreshTitle(); 168 } 169 } 170 171 static final class ExitWindowAdapter extends WindowAdapter { 172 @Override 173 public void windowClosing(final WindowEvent evt) { 174 MainApplication.exitJosm(true, 0, null); 175 } 176 } 177 178 /** 179 * Manages the layer listeners, adds them to every layer. 180 */ 181 private final class ManageLayerListeners implements LayerChangeListener { 182 @Override 183 public void layerAdded(LayerAddEvent e) { 184 if (e.getAddedLayer() instanceof OsmDataLayer) { 185 OsmDataLayer osmDataLayer = (OsmDataLayer) e.getAddedLayer(); 186 osmDataLayer.addLayerStateChangeListener(updateTitleOnLayerStateChange); 187 } 188 e.getAddedLayer().addPropertyChangeListener(updateTitleOnSaveChange); 189 } 190 191 @Override 192 public void layerRemoving(LayerRemoveEvent e) { 193 if (e.getRemovedLayer() instanceof OsmDataLayer) { 194 OsmDataLayer osmDataLayer = (OsmDataLayer) e.getRemovedLayer(); 195 osmDataLayer.removeLayerStateChangeListener(updateTitleOnLayerStateChange); 196 } 197 e.getRemovedLayer().removePropertyChangeListener(updateTitleOnSaveChange); 198 } 199 200 @Override 201 public void layerOrderChanged(LayerOrderChangeEvent e) { 202 // not used 203 } 204 } 205 206 private class WindowPositionSizeListener extends WindowAdapter implements ComponentListener { 207 @Override 208 public void windowStateChanged(WindowEvent e) { 209 windowState = e.getNewState(); 210 } 211 212 @Override 213 public void componentHidden(ComponentEvent e) { 214 // Do nothing 215 } 216 217 @Override 218 public void componentMoved(ComponentEvent e) { 219 handleComponentEvent(e); 220 } 221 222 @Override 223 public void componentResized(ComponentEvent e) { 224 handleComponentEvent(e); 225 } 226 227 @Override 228 public void componentShown(ComponentEvent e) { 229 // Do nothing 230 } 231 232 private void handleComponentEvent(ComponentEvent e) { 233 Component c = e.getComponent(); 234 if (c instanceof JFrame && c.isVisible()) { 235 if (windowState == JFrame.NORMAL) { 236 geometry = new WindowGeometry((JFrame) c); 237 } else { 238 geometry.fixScreen((JFrame) c); 239 } 240 } 241 } 242 } 243 244}