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.component;
020
021import icy.common.listener.weak.WeakListener;
022import icy.gui.frame.IcyFrame;
023import icy.gui.frame.IcyFrameAdapter;
024import icy.gui.frame.IcyFrameEvent;
025import icy.gui.util.WindowPositionSaver;
026import icy.main.Icy;
027import icy.util.StringUtil;
028
029import java.awt.BorderLayout;
030import java.awt.Container;
031import java.awt.Dimension;
032import java.awt.HeadlessException;
033import java.awt.Point;
034import java.util.EventListener;
035
036import javax.swing.JPanel;
037import javax.swing.WindowConstants;
038
039/**
040 * Externalizable panel component.<br>
041 * Basically this is a JPanel you can externalize and display in an IcyFrame.<br>
042 * 
043 * @author Stephane
044 */
045public class ExternalizablePanel extends JPanel
046{
047    public static class WeakStateListener extends WeakListener<StateListener> implements StateListener
048    {
049        public WeakStateListener(StateListener listener)
050        {
051            super(listener);
052        }
053
054        @Override
055        public void removeListener(Object source)
056        {
057            if (source != null)
058                ((ExternalizablePanel) source).removeStateListener(this);
059        }
060
061        @Override
062        public void stateChanged(ExternalizablePanel source, boolean externalized)
063        {
064            final StateListener listener = getListener(source);
065
066            if (listener != null)
067                listener.stateChanged(source, externalized);
068        }
069    }
070
071    public static interface StateListener extends EventListener
072    {
073        public void stateChanged(ExternalizablePanel source, boolean externalized);
074    }
075
076    public class Frame extends IcyFrame
077    {
078        public Frame(String title) throws HeadlessException
079        {
080            super(title, true, true, true, true);
081
082            setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
083
084            addFrameListener(new IcyFrameAdapter()
085            {
086                @Override
087                public void icyFrameClosing(IcyFrameEvent e)
088                {
089                    super.icyFrameClosing(e);
090
091                    // ignore the event when frame is manually closed or application is exiting
092                    if (!(closed || Icy.isExiting()))
093                    {
094                        if (ExternalizablePanel.this.isExternalized())
095                            ExternalizablePanel.this.internalizeInternal();
096                    }
097                }
098            });
099
100            setLayout(new BorderLayout());
101            setSize(400, 400);
102        }
103    }
104
105    /**
106     * 
107     */
108    private static final long serialVersionUID = -7690543443681714719L;
109
110    /**
111     * extern frame
112     */
113    protected final Frame frame;
114    /**
115     * parent component
116     */
117    private Container parent;
118
119    /**
120     * internals
121     */
122    private boolean internalizationAutorized;
123    private boolean externalizationAutorized;
124    boolean closed;
125
126    // we need to keep reference on it as the object only use weak reference
127    final WindowPositionSaver positionSaver;
128
129    /**
130     * Create a new externalizable panel.
131     * 
132     * @param title
133     *        title for the associated frame.
134     * @param key
135     *        save key, used for WindowPositionSaver.<br>
136     *        Set to null or empty string disable parameter saving.
137     * @param defLoc
138     *        the default location for the frame (externalized state)
139     * @param defDim
140     *        the default dimension for the frame (externalized state)
141     */
142    public ExternalizablePanel(String title, String key, Point defLoc, Dimension defDim)
143    {
144        super();
145
146        frame = new Frame(title);
147        parent = null;
148        internalizationAutorized = true;
149        externalizationAutorized = true;
150        closed = false;
151
152        // use window position saver with default parameters
153        if (!StringUtil.isEmpty(key))
154            positionSaver = new WindowPositionSaver(this, "frame/" + key, defLoc, defDim);
155        else
156            positionSaver = null;
157    }
158
159    /**
160     * Create a new externalizable panel.
161     * 
162     * @param title
163     *        title for the associated frame.
164     * @param key
165     *        save key, used for WindowPositionSaver.<br>
166     *        Set to null or empty string disable parameter saving.
167     */
168    public ExternalizablePanel(String title, String key)
169    {
170        // default location and dimension for extern frame
171        this(title, key, new Point(200, 200), new Dimension(400, 300));
172    }
173
174    public ExternalizablePanel(String title)
175    {
176        this(title, null);
177    }
178
179    public ExternalizablePanel()
180    {
181        this("", null);
182    }
183
184    @Override
185    public void addNotify()
186    {
187        super.addNotify();
188
189        // set parent on first attachment
190        if (parent == null)
191        {
192            final Container p = getParent();
193
194            if ((p != frame.getInternalFrame().getContentPane()) && (p != frame.getExternalFrame().getContentPane()))
195                parent = p;
196        }
197    }
198
199    /**
200     * Close the panel (close and release associated frames and resources).
201     */
202    public void close()
203    {
204        closed = true;
205        frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
206        frame.close();
207    }
208
209    /**
210     * Manual parent set
211     */
212    public void setParent(Container value)
213    {
214        parent = value;
215    }
216
217    /**
218     * @return the internalizationAutorized
219     */
220    public boolean isInternalizationAutorized()
221    {
222        return internalizationAutorized;
223    }
224
225    /**
226     * @param internalizationAutorized
227     *        the internalizationAutorized to set
228     */
229    public void setInternalizationAutorized(boolean internalizationAutorized)
230    {
231        this.internalizationAutorized = internalizationAutorized;
232    }
233
234    /**
235     * @return the externalizationAutorized
236     */
237    public boolean isExternalizationAutorized()
238    {
239        return externalizationAutorized;
240    }
241
242    /**
243     * @param externalizationAutorized
244     *        the externalizationAutorized to set
245     */
246    public void setExternalizationAutorized(boolean externalizationAutorized)
247    {
248        this.externalizationAutorized = externalizationAutorized;
249    }
250
251    /**
252     * Externalize panel in an independent frame
253     */
254    public void externalize()
255    {
256        if (isInternalized())
257            externalizeInternal();
258    }
259
260    /**
261     * Internalize panel (remove from independent frame)
262     */
263    public void internalize()
264    {
265        if (isExternalized())
266            internalizeInternal();
267    }
268
269    /**
270     * Externalize panel (internal method)
271     */
272    void externalizeInternal()
273    {
274        if (!externalizationAutorized)
275            return;
276
277        // externalize
278        if (parent != null)
279        {
280            parent.remove(this);
281            parent.validate();
282        }
283
284        frame.add(this, BorderLayout.CENTER);
285        frame.validate();
286        frame.addToDesktopPane();
287        frame.setVisible(true);
288
289        // notify
290        fireStateChange(true);
291    }
292
293    /**
294     * Internalize panel (internal method)
295     */
296    void internalizeInternal()
297    {
298        if (!internalizationAutorized)
299            return;
300
301        // internalize
302        frame.setVisible(false);
303        frame.remove(this);
304        frame.validate();
305        frame.removeFromMainDesktopPane();
306
307        if (parent != null)
308        {
309            parent.add(this);
310            parent.validate();
311        }
312
313        // notify
314        fireStateChange(false);
315    }
316
317    /**
318     * Switch from internalized <--> externalized state and vice versa
319     */
320    public void switchState()
321    {
322        if (isExternalized())
323            internalizeInternal();
324        else
325            externalizeInternal();
326    }
327
328    public boolean isInternalized()
329    {
330        return !frame.isVisible();
331    }
332
333    public boolean isExternalized()
334    {
335        return frame.isVisible();
336    }
337
338    /**
339     * @return the frame
340     */
341    public IcyFrame getFrame()
342    {
343        return frame;
344    }
345
346    // /**
347    // * Implement getMinimumSize method for external frame only
348    // */
349    // public Dimension getMinimumSizeExternal()
350    // {
351    // return frame.getMinimumSize();
352    // }
353    //
354    // /**
355    // * Implement getMaximumSize method for external frame only
356    // */
357    // public Dimension getMaximumSizeExternal()
358    // {
359    // return frame.getMaximumSize();
360    // }
361    //
362    // /**
363    // * Implement getPreferredSize method for external frame only
364    // */
365    // public Dimension getPreferredSizeExternal()
366    // {
367    // return frame.getPreferredSize();
368    // }
369    //
370    // /**
371    // * Implement getSize method for external frame only
372    // */
373    // public Dimension getSizeExternal()
374    // {
375    // return frame.getSize();
376    // }
377    //
378    // /**
379    // * Implement getHeight method for external frame only
380    // */
381    // public int getHeightExternal()
382    // {
383    // return frame.getHeight();
384    // }
385    //
386    // /**
387    // * Implement getWidth method for external frame only
388    // */
389    // public int getWidthExternal()
390    // {
391    // return frame.getWidth();
392    // }
393    //
394    // /**
395    // * Implement getLocation method for external frame only
396    // */
397    // public Point getLocationExternal()
398    // {
399    // return frame.getLocation();
400    // }
401    //
402    // /**
403    // * Implement getBounds method for external frame only
404    // */
405    // public Rectangle getBoundsExternal()
406    // {
407    // return frame.getBounds();
408    // }
409    //
410    // /**
411    // * Implement setLocation method for external frame only
412    // */
413    // public void setLocationExternal(final Point p)
414    // {
415    // frame.setLocation(p);
416    // }
417    //
418    // /**
419    // * Implement setLocation method for external frame only
420    // */
421    // public void setLocationExternal(final int x, final int y)
422    // {
423    // frame.setLocation(x, y);
424    // }
425    //
426    // /**
427    // * Implement setSize method for external frame only
428    // */
429    // public void setSizeExternal(final Dimension d)
430    // {
431    // frame.setSize(d);
432    // }
433    //
434    // /**
435    // * Implement setSize method for external frame only
436    // */
437    // public void setSizeExternal(final int width, final int height)
438    // {
439    // frame.setSize(width, height);
440    // }
441    //
442    // /**
443    // * Implement setPreferredSize method for external frame only
444    // */
445    // public void setPreferredSizeExternal(final Dimension d)
446    // {
447    // frame.setPreferredSize(d);
448    // }
449    //
450    // /**
451    // * Implement setMinimumSize method for external frame only
452    // */
453    // public void setMinimumSizeExternal(final Dimension d)
454    // {
455    // frame.setMinimumSize(d);
456    // }
457    //
458    // /**
459    // * Implement setMaximumSize method for external frame only
460    // */
461    // public void setMaximumSizeExternal(final Dimension d)
462    // {
463    // frame.setMaximumSize(d);
464    // }
465
466    /**
467     * Fire state change event
468     */
469    private void fireStateChange(boolean externalized)
470    {
471        for (StateListener l : listenerList.getListeners(StateListener.class))
472            l.stateChanged(this, externalized);
473    }
474
475    /**
476     * Implement addFrameListener method
477     */
478    public void addStateListener(StateListener l)
479    {
480        listenerList.add(StateListener.class, l);
481    }
482
483    /**
484     * Implement removeFrameListener method
485     */
486    public void removeStateListener(StateListener l)
487    {
488        listenerList.remove(StateListener.class, l);
489    }
490}