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