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