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}