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}