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.LookAndFeelUtil;
024import icy.resource.ResourceUtil;
025import icy.resource.icon.IcyIcon;
026import icy.system.IcyExceptionHandler;
027import icy.system.SystemUtil;
028import icy.system.thread.ThreadUtil;
029
030import java.awt.event.ActionEvent;
031import java.awt.event.KeyEvent;
032import java.beans.PropertyChangeEvent;
033import java.beans.PropertyChangeListener;
034import java.beans.PropertyVetoException;
035
036import javax.swing.JInternalFrame;
037import javax.swing.JMenu;
038import javax.swing.event.InternalFrameAdapter;
039import javax.swing.event.InternalFrameEvent;
040
041import org.pushingpixels.substance.internal.utils.SubstanceInternalFrameTitlePane;
042
043/**
044 * @author Stephane
045 */
046public class IcyInternalFrame extends JInternalFrame
047{
048    /**
049     * 
050     */
051    private static final long serialVersionUID = -5445569637723054083L;
052
053    private class CloseAction extends IcyAbstractAction
054    {
055        /**
056         * 
057         */
058        private static final long serialVersionUID = 4933605299188863452L;
059
060        public CloseAction()
061        {
062            super("Close", new IcyIcon(ResourceUtil.ICON_CLOSE, 20), "Close window", KeyEvent.VK_F4, SystemUtil
063                    .getMenuCtrlMask());
064        }
065
066        @Override
067        public boolean doAction(ActionEvent e)
068        {
069            close(false);
070            return true;
071        }
072    }
073
074    /**
075     * internals
076     */
077    SubstanceInternalFrameTitlePane titlePane = null;
078    // JMenu systemMenu;
079    MenuCallback systemMenuCallback;
080    private boolean titleBarVisible;
081    private boolean closeItemVisible;
082    private boolean initialized = false;
083
084    /**
085     * @param title
086     * @param resizable
087     * @param closable
088     * @param maximizable
089     * @param iconifiable
090     */
091    public IcyInternalFrame(String title, boolean resizable, boolean closable, boolean maximizable, boolean iconifiable)
092    {
093        super(title, resizable, closable, maximizable, iconifiable);
094
095        addPropertyChangeListener("titlePane", new PropertyChangeListener()
096        {
097            @Override
098            public void propertyChange(PropertyChangeEvent evt)
099            {
100                // invoke later so the titlePane variable is up to date
101                ThreadUtil.invokeLater(new Runnable()
102                {
103                    @Override
104                    public void run()
105                    {
106                        updateTitlePane();
107                    }
108                });
109            }
110        });
111
112        addInternalFrameListener(new InternalFrameAdapter()
113        {
114            @Override
115            public void internalFrameClosed(InternalFrameEvent e)
116            {
117                // release the system menu callback as it can lead to some memory leak
118                // (cycling reference)
119                systemMenuCallback = null;
120            }
121        });
122
123        setFrameIcon(ResourceUtil.ICON_ICY_16);
124        setVisible(false);
125
126        systemMenuCallback = null;
127        closeItemVisible = closable;
128        updateTitlePane(LookAndFeelUtil.getTitlePane(this));
129
130        titleBarVisible = true;
131        initialized = true;
132    }
133
134    /**
135     * update internals informations linked to title pane with specified pane
136     */
137    protected void updateTitlePane(final SubstanceInternalFrameTitlePane pane)
138    {
139        // update pane save
140        if (pane != null)
141            titlePane = pane;
142        // update menu
143        // if (titlePane != null)
144        // systemMenuBar = titlePane.getMenuBar();
145        // refresh system menu whatever
146        updateSystemMenu();
147    }
148
149    /**
150     * update internals informations linked to title pane and title pane state
151     */
152    protected void updateTitlePane()
153    {
154        if (initialized)
155        {
156            // title pane can have changed
157            updateTitlePane(LookAndFeelUtil.getTitlePane(this));
158
159            if (!titleBarVisible)
160                setTitleBarVisible(false);
161        }
162    }
163
164    /**
165     * Refresh system menu
166     */
167    public void updateSystemMenu()
168    {
169        if ((titlePane != null) && !isClosed())
170        {
171            final JMenu menu;
172
173            if (systemMenuCallback != null)
174                menu = systemMenuCallback.getMenu();
175            else
176                menu = getDefaultSystemMenu();
177
178            // ensure compatibility with heavyweight component
179            menu.getPopupMenu().setLightWeightPopupEnabled(false);
180
181            // rebuild menu
182            titlePane.setSystemMenu(menu);
183            // systemMenuBar.removeAll();
184            // systemMenuBar.add(menu);
185            // systemMenuBar.validate();
186        }
187    }
188
189    /**
190     * Close the frame.
191     * 
192     * @param force
193     *        if <code>true</code> the frame is close even if {@link #isClosable()} return <code>false</code>
194     */
195    public void close(boolean force)
196    {
197        if (force || isClosable())
198            doDefaultCloseAction();
199    }
200
201    /**
202     * Implement isMinimized method
203     */
204    public boolean isMinimized()
205    {
206        return isIcon();
207    }
208
209    /**
210     * Implement isMaximized method
211     */
212    public boolean isMaximized()
213    {
214        return isMaximum();
215    }
216
217    /**
218     * Implement setMinimized method
219     */
220    public void setMinimized(final boolean value)
221    {
222        // only relevant if state changed
223        if (isMinimized() ^ value)
224        {
225            try
226            {
227                setIcon(value);
228            }
229            catch (PropertyVetoException e)
230            {
231                IcyExceptionHandler.showErrorMessage(e, true);
232            }
233        }
234    }
235
236    /**
237     * Implement setMaximized method
238     */
239    public void setMaximized(final boolean value)
240    {
241        // have to check that else we obtain a null pointer exception
242        if (getParent() == null)
243            return;
244
245        // only relevant if state changed
246        if (isMaximized() ^ value)
247        {
248            try
249            {
250                // have to check for parent non null
251                setMaximum(value);
252            }
253            catch (PropertyVetoException e)
254            {
255                IcyExceptionHandler.showErrorMessage(e, true);
256            }
257        }
258    }
259
260    /**
261     * @return the titleBarVisible
262     */
263    public boolean isTitleBarVisible()
264    {
265        return titleBarVisible;
266    }
267
268    /**
269     * @return the closeItemVisible
270     */
271    public boolean isCloseItemVisible()
272    {
273        return closeItemVisible;
274    }
275
276    /**
277     * @param value
278     *        the closeItemVisible to set
279     */
280    public void setCloseItemVisible(boolean value)
281    {
282        if (closeItemVisible != value)
283        {
284            closeItemVisible = value;
285
286            ThreadUtil.invokeLater(new Runnable()
287            {
288                @Override
289                public void run()
290                {
291                    updateSystemMenu();
292                }
293            });
294        }
295    }
296
297    @Override
298    public void setClosable(boolean b)
299    {
300        super.setClosable(b);
301
302        if (!b)
303            setCloseItemVisible(false);
304    }
305
306    /**
307     * @param value
308     *        the titleBarVisible to set
309     */
310    public void setTitleBarVisible(boolean value)
311    {
312        if (value)
313            LookAndFeelUtil.setTitlePane(this, titlePane);
314        else
315            LookAndFeelUtil.setTitlePane(this, null);
316
317        revalidate();
318
319        titleBarVisible = value;
320    }
321
322    /**
323     * Return the default system menu
324     */
325    public JMenu getDefaultSystemMenu()
326    {
327        final JMenu result = new JMenu();
328
329        if (closeItemVisible)
330            result.add(new CloseAction());
331
332        return result;
333    }
334
335    /**
336     * @return the systemMenuCallback
337     */
338    public MenuCallback getSystemMenuCallback()
339    {
340        return systemMenuCallback;
341    }
342
343    /**
344     * @param value
345     *        the systemMenuCallback to set
346     */
347    public void setSystemMenuCallback(MenuCallback value)
348    {
349        if (systemMenuCallback != value)
350        {
351            systemMenuCallback = value;
352
353            ThreadUtil.invokeLater(new Runnable()
354            {
355                @Override
356                public void run()
357                {
358                    updateSystemMenu();
359                }
360            });
361        }
362    }
363}