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 WindowAdapter() { 092 @Override 093 public void windowClosing(final WindowEvent evt) { 094 Main.exitJosm(true, 0, null); 095 } 096 }); 097 setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); 098 099 // This listener is never removed, since the main frame exists forever. 100 Main.getLayerManager().addActiveLayerChangeListener(e -> refreshTitle()); 101 Main.getLayerManager().addLayerChangeListener(new ManageLayerListeners(), true); 102 103 refreshTitle(); 104 105 getContentPane().add(Main.panel, BorderLayout.CENTER); 106 menu.initialize(); 107 } 108 109 /** 110 * Stores the current state of the main frame. 111 */ 112 public void storeState() { 113 if (geometry != null) { 114 geometry.remember("gui.geometry"); 115 } 116 Main.pref.put("gui.maximized", (windowState & JFrame.MAXIMIZED_BOTH) != 0); 117 } 118 119 /** 120 * Gets the main menu used for this window. 121 * @return The main menu. 122 */ 123 public MainMenu getMenu() { 124 if (menu == null) { 125 throw new IllegalStateException("Not initialized."); 126 } 127 return menu; 128 } 129 130 /** 131 * Sets this frame to be maximized. 132 * @param maximized <code>true</code> if the window should be maximized. 133 */ 134 public void setMaximized(boolean maximized) { 135 if (maximized) { 136 if (Toolkit.getDefaultToolkit().isFrameStateSupported(JFrame.MAXIMIZED_BOTH)) { 137 windowState = JFrame.MAXIMIZED_BOTH; 138 setExtendedState(windowState); 139 } else { 140 Main.debug("Main window: maximizing not supported"); 141 } 142 } else { 143 throw new UnsupportedOperationException("Unimplemented."); 144 } 145 } 146 147 /** 148 * Update the title of the window to reflect the current content. 149 */ 150 public void refreshTitle() { 151 OsmDataLayer editLayer = Main.getLayerManager().getEditLayer(); 152 boolean dirty = editLayer != null && (editLayer.requiresSaveToFile() 153 || (editLayer.requiresUploadToServer() && !editLayer.isUploadDiscouraged())); 154 setTitle((dirty ? "* " : "") + tr("Java OpenStreetMap Editor")); 155 getRootPane().putClientProperty("Window.documentModified", dirty); 156 } 157 158 private void onLayerChange(OsmDataLayer layer) { 159 if (layer == Main.getLayerManager().getEditLayer()) { 160 refreshTitle(); 161 } 162 } 163 164 /** 165 * Manages the layer listeners, adds them to every layer. 166 */ 167 private final class ManageLayerListeners implements LayerChangeListener { 168 @Override 169 public void layerAdded(LayerAddEvent e) { 170 if (e.getAddedLayer() instanceof OsmDataLayer) { 171 OsmDataLayer osmDataLayer = (OsmDataLayer) e.getAddedLayer(); 172 osmDataLayer.addLayerStateChangeListener(updateTitleOnLayerStateChange); 173 } 174 e.getAddedLayer().addPropertyChangeListener(updateTitleOnSaveChange); 175 } 176 177 @Override 178 public void layerRemoving(LayerRemoveEvent e) { 179 if (e.getRemovedLayer() instanceof OsmDataLayer) { 180 OsmDataLayer osmDataLayer = (OsmDataLayer) e.getRemovedLayer(); 181 osmDataLayer.removeLayerStateChangeListener(updateTitleOnLayerStateChange); 182 } 183 e.getRemovedLayer().removePropertyChangeListener(updateTitleOnSaveChange); 184 } 185 186 @Override 187 public void layerOrderChanged(LayerOrderChangeEvent e) { 188 // not used 189 } 190 } 191 192 private class WindowPositionSizeListener extends WindowAdapter implements ComponentListener { 193 @Override 194 public void windowStateChanged(WindowEvent e) { 195 windowState = e.getNewState(); 196 } 197 198 @Override 199 public void componentHidden(ComponentEvent e) { 200 // Do nothing 201 } 202 203 @Override 204 public void componentMoved(ComponentEvent e) { 205 handleComponentEvent(e); 206 } 207 208 @Override 209 public void componentResized(ComponentEvent e) { 210 handleComponentEvent(e); 211 } 212 213 @Override 214 public void componentShown(ComponentEvent e) { 215 // Do nothing 216 } 217 218 private void handleComponentEvent(ComponentEvent e) { 219 Component c = e.getComponent(); 220 if (c instanceof JFrame && c.isVisible()) { 221 if (windowState == JFrame.NORMAL) { 222 geometry = new WindowGeometry((JFrame) c); 223 } else { 224 geometry.fixScreen((JFrame) c); 225 } 226 } 227 } 228 } 229 230}