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}