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.PropertyChangeListener; 017import java.util.LinkedList; 018import java.util.List; 019 020import javax.swing.JFrame; 021import javax.swing.JPanel; 022 023import org.openstreetmap.josm.Main; 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.tools.ImageProvider; 031import org.openstreetmap.josm.tools.WindowGeometry; 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 MainMenu menu; 052 053 /** 054 * Create a new main window. 055 */ 056 public MainFrame() { 057 this(new JPanel(), new MainPanel(Main.getLayerManager()), new WindowGeometry(new Rectangle(10, 10, 500, 500))); 058 } 059 060 /** 061 * Create a new main window. The parameters will be removed in the future. 062 * @param contentPanePrivate The content 063 * @param mainPanel The main panel. 064 * @param geometry The inital geometry to use. 065 */ 066 public MainFrame(Container contentPanePrivate, MainPanel mainPanel, WindowGeometry geometry) { 067 super(); 068 this.geometry = geometry; 069 setContentPane(contentPanePrivate); 070 } 071 072 /** 073 * Initializes the content of the window and get the current status panel. 074 */ 075 public void initialize() { 076 menu = new MainMenu(); 077 addComponentListener(new WindowPositionSizeListener()); 078 addWindowStateListener(new WindowPositionSizeListener()); 079 080 setJMenuBar(menu); 081 geometry.applySafe(this); 082 List<Image> l = new LinkedList<>(); 083 l.add(ImageProvider.get("logo_16x16x32").getImage()); 084 l.add(ImageProvider.get("logo_16x16x8").getImage()); 085 l.add(ImageProvider.get("logo_32x32x32").getImage()); 086 l.add(ImageProvider.get("logo_32x32x8").getImage()); 087 l.add(ImageProvider.get("logo_48x48x32").getImage()); 088 l.add(ImageProvider.get("logo_48x48x8").getImage()); 089 l.add(ImageProvider.get("logo").getImage()); 090 setIconImages(l); 091 addWindowListener(new ExitWindowAdapter()); 092 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 093 094 // This listener is never removed, since the main frame exists forever. 095 Main.getLayerManager().addActiveLayerChangeListener(e -> refreshTitle()); 096 Main.getLayerManager().addLayerChangeListener(new ManageLayerListeners(), true); 097 098 refreshTitle(); 099 100 getContentPane().add(Main.panel, BorderLayout.CENTER); 101 menu.initialize(); 102 } 103 104 /** 105 * Stores the current state of the main frame. 106 */ 107 public void storeState() { 108 if (geometry != null) { 109 geometry.remember("gui.geometry"); 110 } 111 Main.pref.put("gui.maximized", (windowState & JFrame.MAXIMIZED_BOTH) != 0); 112 } 113 114 /** 115 * Gets the main menu used for this window. 116 * @return The main menu. 117 */ 118 public MainMenu getMenu() { 119 if (menu == null) { 120 throw new IllegalStateException("Not initialized."); 121 } 122 return menu; 123 } 124 125 /** 126 * Sets this frame to be maximized. 127 * @param maximized <code>true</code> if the window should be maximized. 128 */ 129 public void setMaximized(boolean maximized) { 130 if (maximized) { 131 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) { 132 windowState = JFrame.MAXIMIZED_BOTH; 133 setExtendedState(windowState); 134 } else { 135 Main.debug("Main window: maximizing not supported"); 136 } 137 } else { 138 throw new UnsupportedOperationException("Unimplemented."); 139 } 140 } 141 142 /** 143 * Update the title of the window to reflect the current content. 144 */ 145 public void refreshTitle() { 146 OsmDataLayer editLayer = Main.getLayerManager().getEditLayer(); 147 boolean dirty = editLayer != null && (editLayer.requiresSaveToFile() 148 || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged())); 149 setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor")); 150 getRootPane().putClientProperty("Window.documentModified", dirty); 151 } 152 153 private void onLayerChange(OsmDataLayer layer) { 154 if (layer == Main.getLayerManager().getEditLayer()) { 155 refreshTitle(); 156 } 157 } 158 159 static final class ExitWindowAdapter extends WindowAdapter { 160 @Override 161 public void windowClosing(final WindowEvent evt) { 162 Main.exitJosm(true, 0, null); 163 } 164 } 165 166 /** 167 * Manages the layer listeners, adds them to every layer. 168 */ 169 private final class ManageLayerListeners implements LayerChangeListener { 170 @Override 171 public void layerAdded(LayerAddEvent e) { 172 if (e.getAddedLayer() instanceof OsmDataLayer) { 173 OsmDataLayer osmDataLayer = (OsmDataLayer) e.getAddedLayer(); 174 osmDataLayer.addLayerStateChangeListener(updateTitleOnLayerStateChange); 175 } 176 e.getAddedLayer().addPropertyChangeListener(updateTitleOnSaveChange); 177 } 178 179 @Override 180 public void layerRemoving(LayerRemoveEvent e) { 181 if (e.getRemovedLayer() instanceof OsmDataLayer) { 182 OsmDataLayer osmDataLayer = (OsmDataLayer) e.getRemovedLayer(); 183 osmDataLayer.removeLayerStateChangeListener(updateTitleOnLayerStateChange); 184 } 185 e.getRemovedLayer().removePropertyChangeListener(updateTitleOnSaveChange); 186 } 187 188 @Override 189 public void layerOrderChanged(LayerOrderChangeEvent e) { 190 // not used 191 } 192 } 193 194 private class WindowPositionSizeListener extends WindowAdapter implements ComponentListener { 195 @Override 196 public void windowStateChanged(WindowEvent e) { 197 windowState = e.getNewState(); 198 } 199 200 @Override 201 public void componentHidden(ComponentEvent e) { 202 // Do nothing 203 } 204 205 @Override 206 public void componentMoved(ComponentEvent e) { 207 handleComponentEvent(e); 208 } 209 210 @Override 211 public void componentResized(ComponentEvent e) { 212 handleComponentEvent(e); 213 } 214 215 @Override 216 public void componentShown(ComponentEvent e) { 217 // Do nothing 218 } 219 220 private void handleComponentEvent(ComponentEvent e) { 221 Component c = e.getComponent(); 222 if (c instanceof JFrame && c.isVisible()) { 223 if (windowState == JFrame.NORMAL) { 224 geometry = new WindowGeometry((JFrame) c); 225 } else { 226 geometry.fixScreen((JFrame) c); 227 } 228 } 229 } 230 } 231 232}