/** * */ package javax.swing.origamist; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Polygon; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; /** * A panel with a button that allows hiding that panel. * * @author Martin Pecka */ public class JHideablePanel extends JPanel { /** */ private static final long serialVersionUID = -6265414666873979043L; /** The button that hides/shows the panel's content. */ protected JButton hideButton; /** The content of the panel. */ protected JPanel content; /** The arrow to show when the content is shown. */ protected Icon shownIcon; /** The arrow to show when the content is hidden. */ protected Icon hiddenIcon; /** The alignment of the hide button. */ protected String buttonAlignment; /** * Create the panel that has a button to show/hide its contents. * * @param buttonAlignment Alignment of the button. The direction of the arrow for shown/hidden state is computed * from this value. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. */ public JHideablePanel(String buttonAlignment) { this(buttonAlignment, getDefaultArrowAlignmentFromButtonAlignment(buttonAlignment)); } /** * Create the panel that has a button to show/hide its contents. * * @param buttonAlignment Alignment of the button. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @param arrowDirection Direction of the arrow when the content is shown. The direction of the arrow when the * content is hidden will be the opposite of this one. One of * <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. */ public JHideablePanel(String buttonAlignment, String arrowDirection) { this(buttonAlignment, arrowDirection, getOppositeAlignment(arrowDirection)); } /** * Create the panel that has a button to show/hide its contents. * * @param buttonAlignment Alignment of the button. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @param shownArrowDirection Direction of the arrow when the content is shown. One of * <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @param hiddenArrowDirection Direction of the arrow when the content is hidden. One of * <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. */ public JHideablePanel(String buttonAlignment, String shownArrowDirection, String hiddenArrowDirection) { this(getDefaultArrowButton(), buttonAlignment, shownArrowDirection, hiddenArrowDirection); } /** * Create the panel that has a button to show/hide its contents. * * @param arrowButton The button that serves to show/hide the content. * @param buttonAlignment Alignment of the button. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @param shownArrowDirection Direction of the arrow when the content is shown. One of * <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @param hiddenArrowDirection Direction of the arrow when the content is hidden. One of * <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. */ public JHideablePanel(JButton arrowButton, String buttonAlignment, String shownArrowDirection, String hiddenArrowDirection) { this(new JPanel(), arrowButton, buttonAlignment, shownArrowDirection, hiddenArrowDirection); } /** * Create the panel that has a button to show/hide its contents. * * @param content The component containing the content of the panel. * @param arrowButton The button that serves to show/hide the content. * @param buttonAlignment Alignment of the button. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @param shownArrowDirection Direction of the arrow when the content is shown. One of * <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @param hiddenArrowDirection Direction of the arrow when the content is hidden. One of * <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. */ public JHideablePanel(JPanel content, JButton arrowButton, final String buttonAlignment, String shownArrowDirection, String hiddenArrowDirection) { this.buttonAlignment = buttonAlignment; shownIcon = getIconForDirection(shownArrowDirection); hiddenIcon = getIconForDirection(hiddenArrowDirection); this.content = content; this.hideButton = arrowButton; hideButton.setIcon(shownIcon); hideButton.setText(null); hideButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (JHideablePanel.this.content.isVisible()) { hidePanel(); } else { showPanel(); } } }); setLayout(new BorderLayout()); addHideButtonToLayout(); add(content, BorderLayout.CENTER); } /** * Add the hide button to the layout according to current buttonAlignment. */ protected void addHideButtonToLayout() { add(hideButton, buttonAlignment); } /** * @return Alignment of the button. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. */ public String getButtonAlignment() { return buttonAlignment; } /** * Set the new alignment of the hide button. * <p> * This call relayouts the component if needed. * * @param buttonAlignment Alignment of the button. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. */ public void setButtonAlignment(String buttonAlignment) { String oldAlignment = this.buttonAlignment; this.buttonAlignment = buttonAlignment; if (!oldAlignment.equals(buttonAlignment)) { remove(hideButton); shownIcon = getIconForDirection(getOppositeAlignment(buttonAlignment)); hiddenIcon = getIconForDirection(buttonAlignment); if (content.isVisible()) { hideButton.setIcon(shownIcon); } else { hideButton.setIcon(hiddenIcon); } addHideButtonToLayout(); } } /** * Show the content. */ public void showPanel() { content.setVisible(true); hideButton.setIcon(shownIcon); } /** * Hide the content. */ public void hidePanel() { content.setVisible(false); hideButton.setIcon(hiddenIcon); } /** * @return the content */ public JPanel getContent() { return content; } /** * @param content the content to set */ public void setContent(JPanel content) { this.content = content; } /** * @return The hideButton. */ public JButton getHideButton() { return hideButton; } /** * @return The default button to be used as the arrow button. */ protected static JButton getDefaultArrowButton() { // Just setting the icon will result in odd behavior with JButton. The icon isn't shown in the center, but in // the top left corner. That is the reason why paintComponent is overridden here. JButton but = new JButton() { /** */ private static final long serialVersionUID = 1L; @Override protected void paintComponent(Graphics g) { Icon icon = getIcon(); setIcon(null); super.paintComponent(g); setIcon(icon); Graphics2D g2 = (Graphics2D) g.create(); g2.translate((getWidth() - icon.getIconWidth()) / 2, (getHeight() - icon.getIconHeight()) / 2); icon.paintIcon(this, g2, 0, 0); } }; but.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.GRAY, 1), BorderFactory.createEmptyBorder(1, 1, 1, 1))); return but; } /** * Set all sizes (max,min,default) of the component. * * @param comp The component to set size of. * @param dimension The new dimension of the component. */ protected static void setSize(JComponent comp, Dimension dimension) { comp.setSize(dimension); comp.setMaximumSize(dimension); comp.setMinimumSize(dimension); } /** * Return the alignment opposite to the one given. * * @param alignment The alignment to be reversed. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @return The alignment opposite to the one given. */ protected static String getOppositeAlignment(String alignment) { if (BorderLayout.NORTH.equals(alignment)) { return BorderLayout.SOUTH; } else if (BorderLayout.SOUTH.equals(alignment)) { return BorderLayout.NORTH; } else if (BorderLayout.EAST.equals(alignment)) { return BorderLayout.WEST; } else if (BorderLayout.WEST.equals(alignment)) { return BorderLayout.EAST; } else { return BorderLayout.SOUTH; } } /** * Return the alignment of an arrow computed from the alignment of the button. * * @param buttonAlignment The alignment of the button. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @return The alignment of an arrow computed from the alignment of the button. */ protected static String getDefaultArrowAlignmentFromButtonAlignment(String buttonAlignment) { return getOppositeAlignment(buttonAlignment); } /** * Return the icon of an arrow pointing to the given direction. * * @param direction The direction the arrow points to. One of <code>BorderLayout.(NORTH|SOUTH|EAST|WEST)</code>. * @return The icon of an arrow pointing to the given direction. */ protected static Icon getIconForDirection(String direction) { if (BorderLayout.NORTH.equals(direction)) { return new NorthIcon(); } else if (BorderLayout.SOUTH.equals(direction)) { return new SouthIcon(); } else if (BorderLayout.EAST.equals(direction)) { return new EastIcon(); } else if (BorderLayout.WEST.equals(direction)) { return new WestIcon(); } else { return new SouthIcon(); } } /** * An arrow icon. * * @author Martin Pecka */ protected static class Arrow implements Icon { /** Color of the arrow. */ protected Color arrowColor = Color.black; /** The points the arrow consists of. */ protected Polygon points; /** Cached width of the arrow. */ protected int width; /** Cached height of the arrow. */ protected int height; /** * Creates a triangle arrow. * * @param x The first vertex of the triangle. * @param y The second vertex of the triangle. * @param z The third vertex of the triangle. */ public Arrow(Point x, Point y, Point z) { points = new Polygon(); points.addPoint(x.x, x.y); points.addPoint(y.x, y.y); points.addPoint(z.x, z.y); updateSize(); } /** * Creates the arrow from the given polygon. * * @param polygon The polygon to draw by this icon. */ public Arrow(Polygon polygon) { points = polygon; updateSize(); } /** * Recomputes the size of the icon from the polygon. */ protected void updateSize() { Rectangle size = points.getBounds(); width = (int) size.getWidth(); height = (int) size.getHeight(); } @Override public void paintIcon(Component c, Graphics g, int x, int y) { g.setColor(arrowColor); ((Graphics2D) g).getRenderingHints().add( new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)); g.drawPolygon(points); g.fillPolygon(points); } @Override public int getIconWidth() { return width; } @Override public int getIconHeight() { return height; } } /** * An arrow with its head pointing to the south. * * @author Martin Pecka */ protected static class SouthIcon extends Arrow { public SouthIcon() { super(new Point(0, 0), new Point(10, 0), new Point(5, 7)); } } /** * An arrow with its head pointing to the north. * * @author Martin Pecka */ protected static class NorthIcon extends Arrow { public NorthIcon() { super(new Point(0, 7), new Point(10, 7), new Point(5, 0)); } } /** * An arrow with its head pointing to the west. * * @author Martin Pecka */ protected static class WestIcon extends Arrow { public WestIcon() { super(new Point(7, 0), new Point(7, 10), new Point(0, 5)); } } /** * An arrow with its head pointing to the east. * * @author Martin Pecka */ protected static class EastIcon extends Arrow { public EastIcon() { super(new Point(0, 0), new Point(0, 10), new Point(7, 5)); } } }