/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* 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
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui.tools.components;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import com.rapidminer.gui.look.Colors;
import com.rapidminer.gui.tools.ExtendedJScrollPane;
import com.rapidminer.gui.tools.ListHoverHelper;
/**
* Container in which the user can switch the displayed children by clicking on an icon in a bar on
* the left hand side.
*
* Titles and icons for the elements representing the individual cards are taken from the GUI
* properties gui.cards.PANEL_KEY.CARD_KEY.title and gui.cards.PANEL_KEY.CARD_KEY.icon where
* PANEL_KEY is the key passed to {@link #ButtonBarCardPanel(String)} and CARD_KEY is the one passed
* to {@link #addCard(String, JComponent)}.
*
* @author Florian Ziegler, David Arnu, Nils Woehler
*
*/
public class ButtonBarCardPanel extends JPanel {
private static final long serialVersionUID = 1L;
private final DefaultListModel<Card> cardListModel = new DefaultListModel<>();
private JPanel content;
private JList<Card> navigation;
private CardLayout cardLayout;
private Map<String, Component> keyToComponentMap;
private boolean showCards = true;
private final Set<String> noCardKeys;
private ExtendedJScrollPane navigationScrollPane;
/**
* Constructor that creates a {@link ButtonBarCardPanel} which always shows the list of cards.
*/
public ButtonBarCardPanel() {
this(new HashSet<String>(), true);
}
/**
* Constructor that creates a {@link ButtonBarCardPanel} with cards shown.
*
* @param showCards
* if set to <code>false</code> the cards are not shown and the user cannot select
* the shown card manually
*/
public ButtonBarCardPanel(Set<String> noCardKeys, boolean showCards) {
this.noCardKeys = noCardKeys;
this.showCards = showCards;
navigation = new JList<Card>(cardListModel) {
private static final long serialVersionUID = -5414386397971825656L;
@Override
protected void paintComponent(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.LIGHT_GRAY);
g.drawLine(getWidth() - 1, 0, getWidth() - 1, getHeight());
super.paintComponent(g);
}
};
navigation.setOpaque(true);
DefaultListSelectionModel listSelectionModel = new DefaultListSelectionModel() {
private static final long serialVersionUID = 1L;
@Override
public void removeSelectionInterval(final int index0, final int index1) {
// deselecting is not allowed
return;
}
};
ListHoverHelper.install(navigation);
navigation.setSelectionModel(listSelectionModel);
navigation.setBounds(5, 5, navigation.getWidth(), navigation.getHeight());
navigation.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.LIGHT_GRAY));
navigation.setFixedCellHeight(100);
navigation.setBackground(Colors.PANEL_BACKGROUND);
navigation.setSelectionForeground(Color.BLACK);
navigation.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
navigation.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
// this prevents firing of multiple events for the same thing
if (e.getValueIsAdjusting()) {
return;
}
Card card = navigation.getSelectedValue();
if (card != null) {
cardLayout.show(content, card.getKey());
fireCardSelectedEvent(card.getKey());
}
}
});
navigation.setCellRenderer(new CardCellRenderer());
cardLayout = new CardLayout();
content = new JPanel(cardLayout);
setLayout(new BorderLayout(0, 25));
navigationScrollPane = new ExtendedJScrollPane(navigation);
navigationScrollPane.setBorder(BorderFactory.createEmptyBorder());
navigationScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
navigationScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
navigationScrollPane.setPreferredSize(new Dimension(105, 0));
// set component orientation to get scrollbar on left side
navigationScrollPane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
add(navigationScrollPane, BorderLayout.WEST);
add(content, BorderLayout.CENTER);
keyToComponentMap = new HashMap<>();
}
/**
* Adds a card to the {@link ButtonBarCardPanel}
*
* @param card
* the card referring to the component
* @param componentToAdd
* the component that is shown once the card is selected
*/
public void addCard(Card card, Component componentToAdd) {
JPanel borderPanel = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
borderPanel.add(componentToAdd, gbc);
content.add(borderPanel, card.getKey());
keyToComponentMap.put(card.getKey(), componentToAdd);
// in the below cases an unnecessary additional panel is suppressed
if (noCardKeys.contains(card.getKey()) || !showCards) {
navigationScrollPane.setPreferredSize(new Dimension(0, navigation.getHeight()));
}
cardListModel.addElement(card);
if (cardListModel.size() == 1) {
navigation.setSelectedIndex(0);
}
}
/**
*
* @return the component of this ButtonBarCardPanel that is currently shown
*/
public Component getShownComponent() {
return keyToComponentMap.get(navigation.getSelectedValue().getKey());
}
/**
* @return the currently selected card
*/
public Card getSelectedCard() {
return navigation.getSelectedValue();
}
/**
* @param index
* changes the selected card to the card at the specified index
*/
public void setSelectedCard(int index) {
navigation.setSelectedIndex(index);
}
/**
* @param key
* Sets the selections to a specific card identified by it's key. If the key is not
* present, nothings happens.
*/
public void selectCard(String key) {
for (int i = 0; i < navigation.getModel().getSize(); i++) {
if (navigation.getModel().getElementAt(i).getKey().equals(key)) {
navigation.setSelectedIndex(i);
}
}
}
public void addCardSelectionListener(CardSelectionListener l) {
listenerList.add(CardSelectionListener.class, l);
}
public void removeCardSelectionListener(CardSelectionListener l) {
listenerList.remove(CardSelectionListener.class, l);
}
private void fireCardSelectedEvent(String key) {
for (CardSelectionListener listener : getListeners(CardSelectionListener.class)) {
CardSelectionEvent e = new CardSelectionEvent(this, key);
listener.cardSelected(e);
}
}
}