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.gui.component.button.IcyButton;
022import icy.resource.ResourceUtil;
023import icy.resource.icon.IcyIcon;
024
025import java.awt.Color;
026import java.awt.Component;
027import java.awt.event.ActionEvent;
028import java.awt.event.ActionListener;
029import java.util.EventListener;
030
031import javax.swing.BorderFactory;
032import javax.swing.Box;
033import javax.swing.BoxLayout;
034import javax.swing.Icon;
035import javax.swing.JLabel;
036import javax.swing.JPanel;
037import javax.swing.JTabbedPane;
038import javax.swing.SwingConstants;
039
040/**
041 * JTabbedPane with close button (configurable) in tab.
042 * 
043 * @author Stephane
044 */
045public class CloseableTabbedPane extends JTabbedPane
046{
047    /**
048     * 
049     */
050    private static final long serialVersionUID = 2105647353561738725L;
051
052    public static interface CloseableTabbedPaneListener extends EventListener
053    {
054        /**
055         * Called when the tab close button has been clicked.<br>
056         * This callback is used to do special actions before tab close.<br>
057         * You can prevent the close operation by returning false.
058         * 
059         * @return true if the tab can be closed, false otherwise.
060         */
061        public boolean tabClosing(int index, String title);
062
063        /**
064         * Called when the tab has been closed.
065         */
066        public void tabClosed(int index, String title);
067    }
068
069    private class CloseableTabComponent extends JPanel
070    {
071        /**
072         * 
073         */
074        private static final long serialVersionUID = 6828288788693627610L;
075
076        final private IcyButton closeButton;
077        final private JLabel label;
078        final private Component sep;
079
080        public CloseableTabComponent(String title, Icon icon)
081        {
082            super();
083
084            setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
085            setBorder(BorderFactory.createEmptyBorder());
086            setOpaque(false);
087
088            label = new JLabel(title, icon, SwingConstants.CENTER);
089            label.setOpaque(false);
090
091            sep = Box.createHorizontalStrut(6);
092
093            closeButton = new IcyButton(new IcyIcon(ResourceUtil.ICON_DELETE, 12));
094            closeButton.setFlat(true);
095            closeButton.setContentAreaFilled(false);
096            closeButton.setToolTipText("close");
097
098            closeButton.addActionListener(new ActionListener()
099            {
100                @Override
101                public void actionPerformed(ActionEvent actionevent)
102                {
103                    final int index = indexOfTabComponent(CloseableTabComponent.this);
104
105                    if (index != -1)
106                    {
107                        if (CloseableTabbedPane.this.fireTabClosing(index, getTitle()))
108                        {
109                            CloseableTabbedPane.this.removeTabAt(index);
110                            CloseableTabbedPane.this.fireTabClosed(index, getTitle());
111                        }
112                    }
113                }
114            });
115
116            add(label);
117            add(sep);
118            add(closeButton);
119
120            validate();
121        }
122
123        public boolean isClosable()
124        {
125            return closeButton.isVisible();
126        }
127
128        public void setClosable(boolean value)
129        {
130            sep.setVisible(value);
131            closeButton.setVisible(value);
132        }
133
134        public String getTitle()
135        {
136            return label.getText();
137        }
138
139        public void setTitle(String title)
140        {
141            label.setText(title);
142        }
143
144        public void setIcon(Icon icon)
145        {
146            label.setIcon(icon);
147        }
148
149        public void setDisabledIcon(Icon disabledIcon)
150        {
151            label.setDisabledIcon(disabledIcon);
152        }
153
154        public void setBackgroundAll(Color background)
155        {
156            label.setBackground(background);
157            closeButton.setBackground(background);
158        }
159
160        public void setForegroundAll(Color foreground)
161        {
162            label.setForeground(foreground);
163            closeButton.setForeground(foreground);
164        }
165    }
166
167    /**
168     * {@link JTabbedPane}
169     */
170    public CloseableTabbedPane()
171    {
172        super();
173    }
174
175    /**
176     * {@link JTabbedPane}
177     */
178    public CloseableTabbedPane(int tabPlacement)
179    {
180        super(tabPlacement);
181    }
182
183    /**
184     * {@link JTabbedPane}
185     */
186    public CloseableTabbedPane(int tabPlacement, int tabLayoutPolicy)
187    {
188        super(tabPlacement, tabLayoutPolicy);
189    }
190
191    /**
192     * Returns the 'closable' state of tab component at <code>index</code>.
193     * 
194     * @param index
195     *        the tab index where the check state is queried
196     * @return true if tab component at <code>index</code> can be closed (close button visible).<br>
197     *         Returns false otherwise
198     * @exception IndexOutOfBoundsException
199     *            if index is out of range (index < 0 || index >= tab count)
200     * @see #setTabClosable(int, boolean)
201     */
202    public boolean isTabClosable(int index)
203    {
204        return ((CloseableTabComponent) getTabComponentAt(index)).isClosable();
205    }
206
207    /**
208     * Set the 'closable' state of tab component at <code>index</code>.
209     * 
210     * @param index
211     *        the tab index we want to set the 'closable' state
212     * @param value
213     *        true if the tab should be 'closable' (close button visible), false otherwise.
214     * @exception IndexOutOfBoundsException
215     *            if index is out of range (index < 0 || index >= tab count)
216     * @see #isTabClosable(int)
217     */
218    public void setTabClosable(int index, boolean value)
219    {
220        ((CloseableTabComponent) getTabComponentAt(index)).setClosable(value);
221    }
222
223    @Override
224    public void setIconAt(int index, Icon icon)
225    {
226        super.setIconAt(index, icon);
227
228        final CloseableTabComponent comp = (CloseableTabComponent) getTabComponentAt(index);
229
230        if (comp != null)
231            comp.setIcon(icon);
232    }
233
234    @Override
235    public void setDisabledIconAt(int index, Icon disabledIcon)
236    {
237        super.setDisabledIconAt(index, disabledIcon);
238
239        final CloseableTabComponent comp = (CloseableTabComponent) getTabComponentAt(index);
240
241        if (comp != null)
242            comp.setDisabledIcon(disabledIcon);
243    }
244
245    @Override
246    public void setBackgroundAt(int index, Color background)
247    {
248        super.setBackgroundAt(index, background);
249
250        final CloseableTabComponent comp = (CloseableTabComponent) getTabComponentAt(index);
251
252        if (comp != null)
253            comp.setBackgroundAll(background);
254    }
255
256    @Override
257    public void setForegroundAt(int index, Color foreground)
258    {
259        super.setForegroundAt(index, foreground);
260
261        final CloseableTabComponent comp = (CloseableTabComponent) getTabComponentAt(index);
262
263        if (comp != null)
264            comp.setForegroundAll(foreground);
265    }
266
267    @Override
268    public void setTitleAt(int index, String title)
269    {
270        super.setTitleAt(index, title);
271
272        final CloseableTabComponent comp = (CloseableTabComponent) getTabComponentAt(index);
273
274        if (comp != null)
275            comp.setTitle(title);
276    }
277
278    @Override
279    public void insertTab(String title, Icon icon, Component component, String tip, int index)
280    {
281        super.insertTab(title, icon, component, tip, index);
282
283        setTabComponentAt(index, new CloseableTabComponent(title, icon));
284    }
285
286    protected void fireTabClosed(int index, String text)
287    {
288        for (CloseableTabbedPaneListener l : listenerList.getListeners(CloseableTabbedPaneListener.class))
289            l.tabClosed(index, text);
290
291    }
292
293    protected boolean fireTabClosing(int index, String text)
294    {
295        for (CloseableTabbedPaneListener l : listenerList.getListeners(CloseableTabbedPaneListener.class))
296            if (!l.tabClosing(index, text))
297                return false;
298
299        return true;
300    }
301
302    public void addCloseableTabbedPaneListener(CloseableTabbedPaneListener l)
303    {
304        listenerList.add(CloseableTabbedPaneListener.class, l);
305    }
306
307    public void removeCloseableTabbedPaneListener(CloseableTabbedPaneListener l)
308    {
309        listenerList.remove(CloseableTabbedPaneListener.class, l);
310    }
311}