/*
* Copyright 2004-2010 Information & Software Engineering Group (188/1)
* Institute of Software Technology and Interactive Systems
* Vienna University of Technology, Austria
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package at.tuwien.ifs.commons.gui.controls;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import org.jdesktop.swingx.JXCollapsiblePane;
import org.jdesktop.swingx.JXCollapsiblePane.Direction;
import org.jdesktop.swingx.JXTitledPanel;
import at.tuwien.ifs.somtoolbox.apps.viewer.CommonSOMViewerStateData;
import at.tuwien.ifs.somtoolbox.apps.viewer.SOMViewer;
/**
* <p>
* This component combines a {@link JXCollapsiblePane} with a clickable title bar to toggle between collapsed and
* expanded state.
* </p>
* <p>
* The methods {@link #add(Component)}, {@link #add(Component, Object)}, {@link #remove(Component)},
* {@link #remove(int)}, {@link #removeAll()} delegate to {@link JXCollapsiblePane}.
* </p>
* <p>
* Use {@link #setTitle(String)}, {@link #setIcon(Icon)}, {@link #setIconTitleGap(int)},
* {@link #setContentBackground(Color)}, {@link #setContentBorder(Border)}, {@link #setTitleBackground(Color)} and
* {@link #setTitleBorder(Border)} so set the appearence.
* </p>
* <p>
* Use {@link #setCollapsed(boolean)} to collapse/expand the contentPane.
* </p>
*
* @author Jakob Frank
* @version $Id: TitledCollapsiblePanel.java 3888 2010-11-02 17:42:53Z frank $
* @see JXCollapsiblePane
* @see JXTitledPanel
*/
public class TitledCollapsiblePanel extends JPanel {
private static final long serialVersionUID = 1L;
public enum Orientation {
TOP, LEFT, RIGHT, BOTTOM;
private String getBLConstraint() {
switch (this) {
case BOTTOM:
return BorderLayout.SOUTH;
case RIGHT:
return BorderLayout.EAST;
case LEFT:
return BorderLayout.WEST;
case TOP:
default:
return BorderLayout.NORTH;
}
}
private Direction getContentDirection() {
switch (this) {
case BOTTOM:
return Direction.DOWN;
case RIGHT:
return Direction.RIGHT;
case LEFT:
return Direction.LEFT;
case TOP:
default:
return Direction.UP;
}
}
}
private Orientation orientation;
private final TitleRow title;
public int getDecoratorPadding() {
return title.getDecoratorPadding();
}
public void setDecoratorPadding(int decoratorPadding) {
title.setDecoratorPadding(decoratorPadding);
}
private final JXCollapsiblePane contentCollapser;
private Container contentContainer;
public TitledCollapsiblePanel() {
this("");
}
public TitledCollapsiblePanel(String title) {
this.title = new TitleRow(title);
this.contentCollapser = new JXCollapsiblePane();
setContentPane(new JPanel());
this.setOrientation(Orientation.TOP);
init();
}
public TitledCollapsiblePanel(LayoutManager layout) {
this("", layout);
}
public TitledCollapsiblePanel(String title, CommonSOMViewerStateData state, LayoutManager layout) {
this(title, layout);
}
public TitledCollapsiblePanel(String title, LayoutManager layout) {
this(title, layout, false);
}
public TitledCollapsiblePanel(String title, boolean collapsed) {
this(title);
setCollapsed(collapsed);
}
public TitledCollapsiblePanel(String title, LayoutManager layout, boolean collapsed) {
this(title);
setLayout(layout);
setCollapsed(collapsed);
}
@Override
public Component add(Component comp) {
if (contentContainer != null) {
return contentContainer.add(comp);
} else {
return comp;
}
}
@Override
public void add(Component comp, Object constraints) {
if (contentContainer != null) {
contentContainer.add(comp, constraints);
}
}
@Override
public Component add(Component comp, int index) {
return contentContainer.add(comp, index);
}
@Override
public void add(Component comp, Object constraints, int index) {
contentContainer.add(comp, constraints, index);
}
@Override
public Component add(String name, Component comp) {
return contentContainer.add(name, comp);
}
public int getHorizontalTitleAlignment() {
return title.getHorizontalAlignment();
}
public int getHorizontalTitleTextPosition() {
return title.getHorizontalTextPosition();
}
public Icon getIcon() {
return title.getIcon();
}
public int getIconTitleGap() {
return title.getIconTextGap();
}
@Override
public LayoutManager getLayout() {
return contentContainer.getLayout();
}
public String getTitle() {
return title.getText();
}
private void init() {
super.setLayout(new BorderLayout());
initTitle();
initContent();
setBorder(BorderFactory.createLineBorder(Color.GRAY, 1));
super.add(title, BorderLayout.NORTH);
super.add(contentCollapser, BorderLayout.CENTER);
}
private void initContent() {
contentCollapser.setBorder(BorderFactory.createEmptyBorder());
contentCollapser.addPropertyChangeListener("collapsed", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
title.revalidate();
title.repaint();
TitledCollapsiblePanel.this.firePropertyChange(evt.getPropertyName(), evt.getOldValue(),
evt.getNewValue());
}
});
}
private void initTitle() {
title.setBackground(Color.lightGray);
title.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
title.setFocusPainted(false);
title.setHorizontalAlignment(SwingConstants.LEFT);
}
public void setContentBackground(Color bg) {
contentCollapser.setBackground(bg);
contentContainer.setBackground(bg);
}
public void setHorizontalTitleAlignment(int alignment) {
title.setHorizontalAlignment(alignment);
}
public void setHorizontalTitleTextPosition(int textPosition) {
title.setHorizontalTextPosition(textPosition);
}
public void setIcon(Icon defaultIcon) {
title.setIcon(defaultIcon);
}
public void setIconTitleGap(int iconTextGap) {
title.setIconTextGap(iconTextGap);
}
@Override
public void setLayout(LayoutManager mgr) {
if (contentContainer != null) {
contentContainer.setLayout(mgr);
}
}
public void setTitle(String title) {
this.title.setText(title);
}
public void setTitleBackground(Color bg) {
title.setBackground(bg);
}
public void setTitleBorder(Border border) {
title.setBorder(border);
}
public Border getTitleBorder() {
return title.getBorder();
}
public Color getTitleBackground() {
return title.getBackground();
}
/**
* The title decoration is the expand/collapse icon in the top right corner of the title.
*/
public void setShowTitleDecoration(boolean show) {
title.setShowDecorators(show);
}
/**
* see {@link #getShowTitleDecoration()}
*/
public boolean getShowTitleDecoration() {
return title.getShowDecorators();
}
public Color getContentBackground() {
return contentContainer.getBackground();
}
public Border getContentBorder() {
return contentCollapser.getBorder();
}
public Container getContentPane() {
return contentContainer;
}
public void setContentPane(Container contentPane) {
contentContainer = contentPane;
contentCollapser.removeAll();
contentCollapser.add(contentPane);
}
@Override
public void remove(Component comp) {
contentContainer.remove(comp);
}
@Override
public void remove(int index) {
contentContainer.remove(index);
}
@Override
public void removeAll() {
contentContainer.removeAll();
}
public void setContentBorder(Border border) {
contentCollapser.setBorder(border);
}
/**
* @see JXCollapsiblePane#isAnimated()
* @return true if the pane is animated, false otherwise
*/
public boolean isAnimated() {
return contentCollapser.isAnimated();
}
/**
* @see JXCollapsiblePane#isCollapsed()
* @return true if the pane is collapsed, false if expanded
*/
public boolean isCollapsed() {
return contentCollapser.isCollapsed();
}
/**
* @see JXCollapsiblePane#setAnimated(boolean)
*/
public void setAnimated(boolean animated) {
contentCollapser.setAnimated(animated);
}
/**
* @see JXCollapsiblePane#setCollapsed(boolean)
*/
public void setCollapsed(boolean val) {
if (val != isCollapsed()) {
contentCollapser.setCollapsed(val);
}
}
public void setOrientation(Orientation orientation) {
this.orientation = orientation;
super.removeAll();
super.add(title, orientation.getBLConstraint());
super.add(contentCollapser, BorderLayout.CENTER);
contentCollapser.setDirection(orientation.getContentDirection());
revalidate();
}
public Orientation getOrientation() {
return orientation;
}
private class TitleRow extends JButton {
private final Icon iconPlus;
private final Icon iconMinus;
private static final long serialVersionUID = 1L;
private int decoratorPadding = 1;
private boolean showDecorators = true;
private Icon getToggleIcon() {
if (isCollapsed()) {
return iconPlus;
} else {
return iconMinus;
}
}
public int getDecoratorPadding() {
return decoratorPadding;
}
public void setDecoratorPadding(int decoratorPadding) {
this.decoratorPadding = decoratorPadding;
}
public TitleRow(String title) {
this(title, true);
}
public TitleRow(String title, boolean fancy) {
super(title);
iconPlus = loadIcon(fancy, true);
iconMinus = loadIcon(fancy, false);
this.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
contentCollapser.setCollapsed(!contentCollapser.isCollapsed());
}
});
}
private Icon loadIcon(boolean fancy, boolean plus) {
if (!fancy) {
return new PlaceholderIcon(plus);
}
String fname = (plus ? "plus" : "minus") + ".png";
Icon tmp;
try {
tmp = new ImageIcon(ClassLoader.getSystemResource(SOMViewer.RESOURCE_PATH_ICONS + fname));
} catch (Exception e) {
tmp = new PlaceholderIcon(plus);
}
return tmp;
}
@Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
if (!getShowDecorators()) {
return;
}
Graphics myG = g.create();
myG.setColor(getForeground());
Icon icon = getToggleIcon();
icon.paintIcon(TitleRow.this, myG, TitleRow.this.getWidth() - TitleRow.this.getInsets().right
- decoratorPadding - icon.getIconWidth(), TitleRow.this.getInsets().top + decoratorPadding);
}
@Override
public final Dimension getMinimumSize() {
Dimension superMin = super.getMinimumSize();
superMin.height = Math.max(superMin.height, getToggleIcon().getIconHeight() + 2 * decoratorPadding
+ getInsets().top + getInsets().bottom);
superMin.width = Math.max(superMin.width, getToggleIcon().getIconWidth() + 2 * decoratorPadding
+ getInsets().left + getInsets().right);
return superMin;
}
@Override
public final Dimension getPreferredSize() {
Dimension superPref = super.getPreferredSize();
superPref.height = Math.max(superPref.height, getToggleIcon().getIconHeight() + 2 * decoratorPadding
+ getInsets().top + getInsets().bottom);
superPref.width = Math.max(superPref.width, getToggleIcon().getIconWidth() + 2 * decoratorPadding
+ getInsets().left + getInsets().right);
return superPref;
}
public void setShowDecorators(boolean showDecorators) {
this.showDecorators = showDecorators;
}
public boolean getShowDecorators() {
return showDecorators;
}
private class PlaceholderIcon implements Icon {
private final boolean isPlus;
private int size = 11;
private boolean showFrame = true;
public PlaceholderIcon(boolean isPlus) {
this(isPlus, 11);
}
public PlaceholderIcon(boolean isPlus, int size) {
this.isPlus = isPlus;
this.size = size;
}
@Override
public int getIconHeight() {
return size;
}
@Override
public int getIconWidth() {
return size;
}
@Override
public void paintIcon(Component c, Graphics g, int x, int y) {
Graphics myG = g.create();
myG.setColor(c.getForeground());
int frameGap = size / 5;
if (!showFrame) {
frameGap = 0;
} else {
myG.drawRect(x, y, size - 1, size - 1);
}
myG.drawLine(x + frameGap, y + size / 2, x + size - frameGap - 1, y + size / 2);
if (isPlus) {
myG.drawLine(x + size / 2, y + frameGap, x + size / 2, y + size - frameGap - 1);
}
}
}
}
// FIXME: Useless.
@Deprecated
public void pack() {
}
/**
* Do not override this method. Use {@link #setPreferredSize(Dimension)} if necessary!
*
* @see javax.swing.JComponent#getPreferredSize()
*/
@Override
public final Dimension getPreferredSize() {
return super.getPreferredSize();
}
}