001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.gui.frame;
020
021import icy.action.IcyAbstractAction;
022import icy.common.MenuCallback;
023import icy.gui.util.ComponentUtil;
024import icy.gui.util.LookAndFeelUtil;
025import icy.resource.ResourceUtil;
026import icy.resource.icon.IcyIcon;
027import icy.system.SystemUtil;
028import icy.system.thread.ThreadUtil;
029
030import java.awt.HeadlessException;
031import java.awt.Rectangle;
032import java.awt.event.ActionEvent;
033import java.awt.event.KeyEvent;
034import java.awt.event.WindowAdapter;
035import java.awt.event.WindowEvent;
036import java.beans.PropertyChangeEvent;
037import java.beans.PropertyChangeListener;
038
039import javax.swing.JFrame;
040import javax.swing.JMenu;
041import javax.swing.JRootPane;
042
043import org.pushingpixels.substance.internal.utils.SubstanceTitlePane;
044
045/**
046 * @author Stephane
047 */
048public class IcyExternalFrame extends JFrame
049{
050    /**
051      * 
052      */
053    private static final long serialVersionUID = 9130936218505070807L;
054
055    private class CloseAction extends IcyAbstractAction
056    {
057        /**
058         * 
059         */
060        private static final long serialVersionUID = 4933605299188863452L;
061
062        public CloseAction()
063        {
064            super("Close", new IcyIcon(ResourceUtil.ICON_CLOSE, 20), "Close window", KeyEvent.VK_F4, SystemUtil
065                    .getMenuCtrlMask());
066        }
067
068        @Override
069        public boolean doAction(ActionEvent e)
070        {
071            close();
072            return true;
073        }
074    }
075
076    /**
077     * internals
078     */
079    private SubstanceTitlePane titlePane;
080    // private JMenuBar systemMenuBar;
081    MenuCallback systemMenuCallback;
082    private boolean titleBarVisible;
083    private boolean closeItemVisible;
084    private boolean initialized = false;
085
086    /**
087     * @param title
088     * @throws HeadlessException
089     */
090    public IcyExternalFrame(String title) throws HeadlessException
091    {
092        super(title);
093
094        getRootPane().addPropertyChangeListener("titlePane", new PropertyChangeListener()
095        {
096            @Override
097            public void propertyChange(PropertyChangeEvent evt)
098            {
099                // invoke later so the titlePane variable is up to date
100                ThreadUtil.invokeLater(new Runnable()
101                {
102                    @Override
103                    public void run()
104                    {
105                        updateTitlePane();
106                    }
107                });
108            }
109        });
110
111        addWindowListener(new WindowAdapter()
112        {
113            @Override
114            public void windowClosed(WindowEvent e)
115            {
116                // release the system menu callback as it can lead to some memory leak
117                // (cycling reference)
118                systemMenuCallback = null;
119            }
120        });
121        setIconImages(ResourceUtil.getIcyIconImages());
122        setVisible(false);
123
124        systemMenuCallback = null;
125        closeItemVisible = true;
126        updateTitlePane(LookAndFeelUtil.getTitlePane(this));
127
128        titleBarVisible = true;
129        initialized = true;
130    }
131
132    /**
133     * update internals informations linked to title pane with specified pane
134     */
135    protected void updateTitlePane(final SubstanceTitlePane pane)
136    {
137        // update pane save
138        if (pane != null)
139            titlePane = pane;
140        // update menu
141        // if (titlePane != null)
142        // systemMenuBar = titlePane.getMenuBar();
143        // refresh system menu whatever
144        updateSystemMenu();
145    }
146
147    /**
148     * update internals informations linked to title pane
149     */
150    protected void updateTitlePane()
151    {
152        if (initialized)
153        {
154            // title pane can have changed
155            updateTitlePane(LookAndFeelUtil.getTitlePane(this));
156
157            if (!titleBarVisible)
158                setTitleBarVisible(false);
159        }
160    }
161
162    /**
163     * Refresh system menu
164     */
165    public void updateSystemMenu()
166    {
167        if (titlePane != null)
168        {
169            final JMenu menu;
170
171            if (systemMenuCallback != null)
172                menu = systemMenuCallback.getMenu();
173            else
174                menu = getDefaultSystemMenu();
175
176            // ensure compatibility with heavyweight component
177            menu.getPopupMenu().setLightWeightPopupEnabled(false);
178
179            // rebuild menu
180            titlePane.setSystemMenu(menu);
181            // systemMenuBar.removeAll();
182            // systemMenuBar.add(menu);
183            // systemMenuBar.validate();
184        }
185    }
186
187    public void setTitleBarVisible(boolean value)
188    {
189        if (value)
190            getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
191        else
192            getRootPane().setWindowDecorationStyle(JRootPane.NONE);
193
194        validate();
195    }
196
197    /**
198     * close frame
199     */
200    public void close()
201    {
202        dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
203    }
204
205    /**
206     * Implement isMinimized method
207     */
208    public boolean isMinimized()
209    {
210        return ComponentUtil.isMinimized(this);
211    }
212
213    /**
214     * Implement isMaximized method
215     */
216    public boolean isMaximized()
217    {
218        return ComponentUtil.isMaximized(this);
219    }
220
221    /**
222     * Implement setMinimized method
223     */
224    public void setMinimized(final boolean value)
225    {
226        ComponentUtil.setMinimized(this, value);
227    }
228
229    /**
230     * Implement setMaximized method
231     */
232    public void setMaximized(final boolean value)
233    {
234        ComponentUtil.setMaximized(this, value);
235    }
236
237    /**
238     * @return the titleBarVisible
239     */
240    public boolean isTitleBarVisible()
241    {
242        return getRootPane().getWindowDecorationStyle() != JRootPane.NONE;
243    }
244
245    /**
246     * @return the closeItemVisible
247     */
248    public boolean isCloseItemVisible()
249    {
250        return closeItemVisible;
251    }
252
253    /**
254     * @param value
255     *        the closeItemVisible to set
256     */
257    public void setCloseItemVisible(boolean value)
258    {
259        if (closeItemVisible != value)
260        {
261            closeItemVisible = value;
262
263            ThreadUtil.invokeLater(new Runnable()
264            {
265                @Override
266                public void run()
267                {
268                    updateSystemMenu();
269                }
270            });
271        }
272    }
273
274    /**
275     * Return the default system menu
276     */
277    public JMenu getDefaultSystemMenu()
278    {
279        final JMenu result = new JMenu();
280
281        if (closeItemVisible)
282            result.add(new CloseAction());
283
284        return result;
285    }
286
287    /**
288     * @return the systemMenuCallback
289     */
290    public MenuCallback getSystemMenuCallback()
291    {
292        return systemMenuCallback;
293    }
294
295    /**
296     * @param value
297     *        the systemMenuCallback to set
298     */
299    public void setSystemMenuCallback(MenuCallback value)
300    {
301        if (systemMenuCallback != value)
302        {
303            systemMenuCallback = value;
304
305            ThreadUtil.invokeLater(new Runnable()
306            {
307                @Override
308                public void run()
309                {
310                    updateSystemMenu();
311                }
312            });
313        }
314    }
315
316    @Override
317    public void reshape(int x, int y, int width, int height)
318    {
319        final Rectangle r = new Rectangle(x, y, width, height);
320
321        // prevent to go completely off screen
322        ComponentUtil.fixPosition(this, r);
323
324        super.reshape(r.x, r.y, r.width, r.height);
325    }
326
327//    @Override
328//    public synchronized void setMaximizedBounds(Rectangle bounds)
329//    {
330//        Rectangle bnds = SystemUtil.getScreenBounds(ComponentUtil.getScreen(this), true);
331//
332//        if (bnds.isEmpty())
333//            bnds = bounds;
334//        // at least use the location from original bounds
335//        else if (bounds != null)
336//            bnds.setLocation(bounds.getLocation());
337//        else
338//            bnds.setLocation(0, 0);
339//
340//        super.setMaximizedBounds(bnds);
341//    }
342}