All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datacleaner.widgets.tabs.CloseableTabbedPane Maven / Gradle / Ivy

/**
 * DataCleaner (community edition)
 * Copyright (C) 2014 Free Software Foundation, Inc.
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */
package org.datacleaner.widgets.tabs;

import java.awt.Color;
import java.awt.Component;
import java.awt.Rectangle;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeListener;

import org.datacleaner.panels.DCBannerPanel;
import org.datacleaner.util.WidgetUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This is a slightly rewritten/modified version of swingutil's
 * ClosableTabbedPane
 */
public final class CloseableTabbedPane extends JTabbedPane {

    public static final Color COLOR_BACKGROUND = WidgetUtils.COLOR_DEFAULT_BACKGROUND;
    public static final Color COLOR_FOREGROUND_SELECTED = WidgetUtils.BG_COLOR_DARKEST;
    public static final Color COLOR_FOREGROUND = WidgetUtils.BG_COLOR_LESS_DARK;
    private static final long serialVersionUID = -411551524171347329L;
    private static Logger _logger = LoggerFactory.getLogger(CloseableTabbedPane.class);
    private final List _closeListeners = new LinkedList<>();
    private final List _unclosables = new LinkedList<>();
    private final List _separators = new LinkedList<>();
    private final Map _doubleClickActionListeners = new HashMap<>();
    private final Map _rightClickActionListeners = new HashMap<>();

    /**
     * Create a tabbed pane using defaults
     */
    public CloseableTabbedPane() {
        this(true);
    }

    /**
     * Create a tabbed pane
     *
     * @param addBorder
     *            add a small border around the tabbed pane?
     */
    public CloseableTabbedPane(final boolean addBorder) {
        super(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
        setUI(new CloseableTabbedPaneUI(this));
        setForeground(COLOR_FOREGROUND);
        setBackground(COLOR_BACKGROUND);
        setOpaque(true);
        if (addBorder) {
            setBorder(WidgetUtils.BORDER_WIDE_DEFAULT);
        }
    }

    /**
     * Tyically, the last tab open is not closable. So, call this method with a
     * tab number and that tab will not contain a close icon. If you monitor
     * with a {@link TabCloseListener}, you can use {@link #getTabCount()} and
     * when it is 1, you can call {@link #setUncloseableTab(int)}(0) to make the
     * last tab unclosable.
     *
     * @return the tabbed pane itself
     */
    public CloseableTabbedPane setUnclosableTab(final int val) {
        if (!_unclosables.contains(val)) {
            _unclosables.add(val);
        }
        return this;
    }

    public void addSeparator() {
        synchronized (this) {
            final int tabCountBefore = getTabCount();
            addTab("SEPARATOR", new JLabel());
            _separators.add(tabCountBefore);
        }
    }

    /**
     * Use this method to reverse the actions of {@link #setUnclosableTab(int)}
     */
    public void setClosableTab(final int val) {
        // cast to Object to ensure the RIGHT remove(...) method is invoked in
        // _unclosables
        final Object value = Integer.valueOf(val);
        _unclosables.remove(value);
    }

    @SuppressWarnings("unchecked")
    public  List getTabsOfClass(final Class clazz) {
        final List list = new ArrayList<>();
        final Component[] components = getComponents();
        for (Component component : components) {
            if (component instanceof JScrollPane) {
                component = ((JScrollPane) component).getViewport().getComponent(0);
            }
            if (clazz.isAssignableFrom(component.getClass())) {
                list.add((E) component);
            }
        }
        return list;
    }

    /**
     * Add a tab close listener. On close events, the listener is responsible
     * for deleting the tab or otherwise reacting to the event.
     */
    public void addTabCloseListener(final TabCloseListener lis) {
        _closeListeners.add(lis);
    }

    /** Remove a tab close listener */
    public void removeTabCloseListener(final TabCloseListener lis) {
        _closeListeners.remove(lis);
    }

    public void closeTab(final int tabIndex) {
        final Component component = getComponent(tabIndex);
        remove(tabIndex);

        int selectedIndex = getSelectedIndex();
        while (_separators.contains(selectedIndex) && selectedIndex > 0) {
            // make sure the separator tabs are not "selected"
            selectedIndex = selectedIndex - 1;
            setSelectedIndex(selectedIndex);
        }

        if (!_closeListeners.isEmpty()) {
            final TabCloseEvent ev = new TabCloseEvent(this, tabIndex, component);
            for (final TabCloseListener l : _closeListeners) {
                try {
                    l.tabClosed(ev);
                } catch (final Exception ex) {
                    _logger.error(ex.toString(), ex);
                }
            }
        }
    }

    @Override
    public void remove(final Component component) {
        final int index = indexOfComponent(component);
        remove(index);
    }

    @Override
    public void remove(final int removedIndex) {
        if (removedIndex < 0) {
            return;
        }
        super.remove(removedIndex);
        _rightClickActionListeners.remove(removedIndex);
        _doubleClickActionListeners.remove(removedIndex);

        // move all right click listeners for tabs above this index down
        {
            final Set keySet = new TreeSet<>(Collections.reverseOrder());
            keySet.addAll(_rightClickActionListeners.keySet());
            for (final Integer key : keySet) {
                final int curIndex = key.intValue();
                if (curIndex > removedIndex) {
                    final ActionListener actionListener = _rightClickActionListeners.get(curIndex);
                    _rightClickActionListeners.remove(curIndex);
                    _rightClickActionListeners.put(curIndex - 1, actionListener);
                }
            }
        }

        // move all double click listeners for tabs above this index down
        {
            final Set keySet = new TreeSet<>(Collections.reverseOrder());
            keySet.addAll(_doubleClickActionListeners.keySet());
            for (final Integer key : keySet) {
                final int curIndex = key.intValue();
                if (curIndex > removedIndex) {
                    final ActionListener actionListener = _doubleClickActionListeners.get(curIndex);
                    _doubleClickActionListeners.remove(curIndex);
                    _doubleClickActionListeners.put(curIndex - 1, actionListener);
                }
            }
        }

        // moved all the uncloseable tabs for tabs above this index down
        {
            for (final ListIterator it = _unclosables.listIterator(); it.hasNext(); ) {
                final Integer tabIndex = it.next();
                final int curIndex = tabIndex.intValue();
                if (curIndex == removedIndex) {
                    it.remove();
                } else if (curIndex > removedIndex) {
                    it.set(curIndex - 1);
                }
            }
        }
    }

    @Override
    public void setSelectedIndex(int index) {
        if (getSeparators().contains(index)) {
            index--;
            setSelectedIndex(index);
        } else {
            super.setSelectedIndex(index);
        }
    }

    public void closeAllTabs() {
        for (int i = getTabCount() - 1; i >= 0; i--) {
            closeTab(i);
        }
        removeAll();
    }

    protected List getUnclosables() {
        return _unclosables;
    }

    protected List getSeparators() {
        return _separators;
    }

    public void setRightClickActionListener(final int index, final ActionListener actionListener) {
        _rightClickActionListeners.put(index, actionListener);
    }

    public ActionListener getRightClickActionListener(final int index) {
        return _rightClickActionListeners.get(index);
    }

    public void setDoubleClickActionListener(final int index, final ActionListener actionListener) {
        _doubleClickActionListeners.put(index, actionListener);
    }

    public ActionListener getDoubleClickActionListener(final int index) {
        return _doubleClickActionListeners.get(index);
    }

    @Override
    public Color getForegroundAt(final int index) {
        if (getSelectedIndex() == index) {
            return COLOR_FOREGROUND_SELECTED;
        }
        return super.getForegroundAt(index);
    }

    @Override
    public void updateUI() {
        repaint();
    }

    public boolean isCloseable(final int index) {
        return !isUncloseable(index);
    }

    public boolean isUncloseable(final int index) {
        return _unclosables.contains(index);
    }

    public Rectangle getTabBounds(final int tabIndex) {
        return getUI().getTabBounds(this, tabIndex);
    }

    public void bindTabTitleToBanner(final DCBannerPanel bannerPanel) {
        final ChangeListener changeListener = e -> {
            final int selectedIndex = getSelectedIndex();
            if (selectedIndex == -1) {
                return;
            }
            final String title = getTitleAt(selectedIndex);
            bannerPanel.setTitle2(title);
            bannerPanel.updateUI();
        };
        addChangeListener(changeListener);

        // trigger an initial update
        changeListener.stateChanged(null);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy