/*
* This file is part of the aidGer project.
*
* Copyright (C) 2010-2013 The aidGer Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.aidger.view;
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Paint;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import de.aidger.controller.actions.TaskPaneAction;
import de.aidger.controller.actions.TaskPaneAction.Task;
/**
* This class represents a single task pane. A task pane is a box with a title
* bar on the top and a content pane on the bottom which can holds links,
* buttons, text fields and more. Furthermore the content pane is collapsible.
*
* @author aidGer Team
*/
@SuppressWarnings("serial")
public class TaskPane extends JComponent {
/**
* The title bar of the task pane.
*/
private final TaskPaneTitleBar titleBar;
/**
* The collapsible content pane.
*/
private final JComponent contentPane;
/**
* The background color.
*/
private final Color bg = new Color(0xf2f2f2);
/**
* A flag whether the content pane is expanded.
*/
private boolean expanded = false;
/**
* The first color for the gradient painting.
*/
private final Color firstColor = new Color(0x33628c);
/**
* The second color for the gradient painting.
*/
private final Color secondColor = new Color(0xa7bbcd);
/**
* The container which holds this task pane.
*/
protected TaskPaneContainer container;
/**
* The animation speed.
*/
private final float speed = 16.0f;
/**
* The current alpha value for a fade out effect.
*/
private float alpha = 1.0f;
/**
* A flag whether the alpha value should be currently used.
*/
private boolean useAlpha;
/**
* The expanded height for the animation process.
*/
private int expandedHeight;
/**
* The saved height for the animation process.
*/
private int savedHeight;
/**
* The expanding flag for the animation process.
*/
private boolean expanding;
/**
* The collapsing flag for the animation process.
*/
private boolean collapsing;
/**
* A helper object for the animation process.
*/
private final CObj cObj = new CObj();
/**
* A helper object for the animation process.
*/
private final EObj eObj = new EObj();
/**
* The position on the task pane container.
*/
private int position;
/**
* Constructs the task pane.
*
* @param title
* the title text of the task pane
*/
public TaskPane(TaskPaneAction a) {
setLayout(new BorderLayout());
updateUI();
String title = (String) a.getValue(AbstractAction.NAME);
setBackground(bg);
contentPane = new ContentPane();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.PAGE_AXIS));
contentPane.setBorder(new EmptyBorder(5, 8, 5, 8));
contentPane.setOpaque(false);
// sets the border of the content pane properly
Border b = contentPane.getBorder();
if (b != null) {
contentPane.setBorder(new CompoundBorder(new TaskPaneBorder(), b));
} else {
contentPane.setBorder(new TaskPaneBorder());
}
titleBar = new TaskPaneTitleBar(title);
titleBar.setMargin(new Insets(3, 5, 3, 5));
a.setTaskPane(this);
titleBar.getToogleIcon().addMouseListener(a);
titleBar.getTitleLabel().addMouseListener(a);
super.addImpl(titleBar, BorderLayout.NORTH, -1);
super.addImpl(this.contentPane, null, -1);
finishExpand();
}
/**
* Constructs the task pane.
*
* @param a
* the task pane action
*/
public TaskPane(String title) {
this(new TaskPaneAction(title, Task.Void));
}
/**
* Adds a new component to the task pane.
*
* @param comp
* the component that will be added
*/
public void add(JComponent comp) {
contentPane.add(comp, BorderLayout.LINE_START);
contentPane.add(Box.createRigidArea(new Dimension(0, 8)));
}
/**
* Returns whether the content pane is expanded.
*
* @return a flag whether the content pane is expanded
*/
public boolean isExpanded() {
return expanded;
}
/**
* Sets the expanded state of the content pane.
*
* @param expanded
* a flag whether the content pane should be expanded
*/
public void setExpanded(boolean expanded) {
titleBar.setExpanded(expanded);
this.expanded = expanded;
if (!expanded) {
container.collapse(this);
} else {
container.expand(this);
}
}
/*
* (non-Javadoc)
*
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
@Override
protected void paintComponent(Graphics g) {
g.setColor(getBackground());
int w = getWidth();
int h = getHeight();
int tbh = titleBar.getHeight();
if (useAlpha) {
((Graphics2D) g).setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha));
}
g.fillRect(0, tbh, w - 1, h - tbh);
if (useAlpha) {
g.setPaintMode();
}
}
/**
* Turns on/off the animation effect for this task pane.
*
* @param animated
* A flag whether the task pane should be animated
*/
void setAnimated(boolean animated) {
if (!animated) {
setPreferredSize(null);
if (!expanded) {
contentPane.setVisible(false);
}
}
}
/**
* This class represents the border of the task pane.
*
* @author aidGer Team
*/
class TaskPaneBorder extends AbstractBorder {
/*
* (non-Javadoc)
*
* @see
* javax.swing.border.AbstractBorder#paintBorder(java.awt.Component,
* java.awt.Graphics, int, int, int, int)
*/
@Override
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
int w = width / 2;
g.setColor(firstColor);
g.drawLine(x, y, x, y + height);
g.drawLine(x, y + height - 1, x + w, y + height - 1);
Graphics2D g2d = (Graphics2D) g.create(w, 0, w, height);
Paint p = new GradientPaint(0, 0, firstColor, w, 0, secondColor);
g2d.setPaint(p);
g2d.drawLine(0, y + height - 1, w, y + height - 1);
g2d.drawLine(w - 1, y, w - 1, y + height - 1);
}
/*
* (non-Javadoc)
*
* @see
* javax.swing.border.AbstractBorder#getBorderInsets(java.awt.Component)
*/
@Override
public Insets getBorderInsets(Component c) {
return new Insets(0, 1, 1, 1);
}
/*
* (non-Javadoc)
*
* @see
* javax.swing.border.AbstractBorder#getBorderInsets(java.awt.Component,
* java.awt.Insets)
*/
@Override
public Insets getBorderInsets(Component c, Insets insets) {
if (insets != null) {
insets.top = 0;
insets.left = 1;
insets.bottom = 1;
insets.right = 1;
return insets;
} else {
return new Insets(0, 1, 1, 1);
}
}
}
/**
* Finishes the collapse process.
*/
protected void finishCollapse() {
useAlpha = false;
expanded = false;
contentPane.setVisible(false);
collapsing = false;
setPreferredSize(null);
contentPane.setSize(contentPane.getWidth(), savedHeight);
}
/**
* Prepares the collapse process.
*
* @return the count variable for internal use
*/
protected int prepareToCollapse() {
collapsing = true;
expandedHeight = 0;
savedHeight = contentPane.getHeight();
int h = getHeight() - titleBar.getHeight();
Dimension d = getPreferredSize();
useAlpha = true;
float k = (h / speed);
float step = 1.0f / (h / k);
int count = (int) (h / k);
alpha = 1.0f;
cObj.set(count, k, d, step);
return count;
}
/**
* Performs the whole collapse process.
*/
protected void doCollapse() {
for (int i = 0; i < cObj.count; i++) {
doCollapseStep();
container.doLayout();
doLayout();
Dimension d0 = container.getSize();
container.paintImmediately(0, 0, d0.width, d0.height);
}
}
/**
* Performs operations for each collapse step.
*/
protected void doCollapseStep() {
cObj.d.height -= cObj.k;
cObj.d.height = Math.max(cObj.d.height, titleBar.getHeight());
setPreferredSize(cObj.d);
alpha = Math.max(alpha - cObj.step, 0.0f);
// pause();
}
/**
* Finished the expand process.
*/
protected void finishExpand() {
useAlpha = false;
expanded = true;
expanding = false;
contentPane.setVisible(true);
// setPreferredSize(null);
}
/**
* Prepares a expand process.
*
* @return the count variable for internal use
*/
protected int prepareToExpand() {
expanding = true;
setPreferredSize(null);
contentPane.setVisible(true);
int h = titleBar.getHeight();
Dimension d0 = getPreferredSize();
d0.height = Math.max(d0.height, expandedHeight);
Dimension d = new Dimension(d0.width, h);
useAlpha = true;
float amount = d0.height - h;
int k = (int) (amount / speed);
int count = (int) (amount / k);
float step = 1.0f / (amount / k);
alpha = 0.0f;
eObj.set(h, count, k, d, d0, step);
return count;
}
/**
* Performs a whole expand process.
*/
protected void doExpand() {
for (int i = eObj.h; i > 0; i--) {
doExpandStep();
container.doLayout();
Dimension d1 = container.getSize();
container.paintImmediately(0, 0, d1.width, d1.height);
}
}
/**
* Performs operations for each expand step.
*/
protected void doExpandStep() {
eObj.d.height += eObj.k;
eObj.d.height = Math.min(eObj.d.height, eObj.d0.height);
setPreferredSize(eObj.d);
alpha = Math.min(alpha + eObj.step, 1.0f);
}
/**
* Retrieves the preferred height of the content pane.
*
* @return the preferred height of the content pane
*/
public int getPreferredHeight() {
Dimension d = contentPane.getPreferredSize();
int h = titleBar.getHeight();
return d.height + h;
}
/**
* Sets the expanded height for the animation process.
*
* @param expandedHeight
* the expanded height
*/
public void setExpandedHeight(int expandedHeight) {
this.expandedHeight = expandedHeight;
}
/**
* Sets the position on the container.
*
* @param position
* the position to set
*/
public void setPosition(int position) {
this.position = position;
}
/**
* Returns the position on the container.
*
* @return the position
*/
public int getPosition() {
return position;
}
/**
* Helper class for the animation effect. It holds some frequently used
* variables.
*
* @author aidGer Team
*/
private static class CObj {
int count;
float k;
Dimension d;
float step;
public void set(int count, float k, Dimension d, float step) {
this.count = count;
this.k = k;
this.d = d;
this.step = step;
}
}
/**
* Helper class for the animation effect. It holds some frequently used
* variables.
*
* @author aidGer Team
*/
private static class EObj {
int h;
@SuppressWarnings("unused")
int count;
float k;
Dimension d;
Dimension d0;
float step;
public void set(int h, int count, float k, Dimension d, Dimension d0,
float step) {
this.h = h;
this.count = count;
this.k = k;
this.d = d;
this.d0 = d0;
this.step = step;
}
}
/**
* This class represents the collapsible content pane of the task pane.
*
* @author aidGer Team
*/
class ContentPane extends JPanel {
/**
* Constructs a content pane. The constructor does nothing at the
* moment.
*/
public ContentPane() {
}
/*
* (non-Javadoc)
*
* @see javax.swing.JComponent#paintBorder(java.awt.Graphics)
*/
@Override
protected void paintBorder(Graphics g) {
Border border = getBorder();
int h = TaskPane.this.getHeight();
int ph = Math.max(TaskPane.this.getPreferredHeight(), h);
int y = h - ph;
if (!expanding || collapsing) {
y = 0;
}
if (border != null) {
int width = getWidth();
int height = getHeight() + y;
border.paintBorder(this, g, 0, 0, width, height);
}
}
/*
* (non-Javadoc)
*
* @see javax.swing.JComponent#paintChildren(java.awt.Graphics)
*/
@Override
protected void paintChildren(Graphics g) {
if (useAlpha) {
((Graphics2D) g).setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, alpha));
}
int ph = Math.max(TaskPane.this.getPreferredHeight(), TaskPane.this
.getHeight());
int h = TaskPane.this.getHeight();
int w = getWidth();
if ((expanding || collapsing) && ph != h) {
int y = h - ph;
Graphics2D g2D = (Graphics2D) g.create(0, y, w, ph);
g2D.setClip(0, -y, w, ph);
super.paintChildren(g2D);
g2D.dispose();
} else {
super.paintChildren(g);
}
if (useAlpha) {
g.setPaintMode();
}
}
}
}