/* * Copyright (c) 2006, 2015, Oracle. All rights reserved. * * This software is the proprietary information of Oracle Corporation. * Use is subject to license terms. */ package org.eclipse.persistence.tools.workbench.uitools.swing; import java.awt.Cursor; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; import javax.swing.Icon; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JToggleButton; import javax.swing.SwingUtilities; import org.eclipse.persistence.tools.workbench.uitools.ComponentVisibilityEnabler; import org.eclipse.persistence.tools.workbench.uitools.app.PropertyValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.SimplePropertyValueModel; import org.eclipse.persistence.tools.workbench.uitools.app.ValueModel; import org.eclipse.persistence.tools.workbench.utility.events.ChangeSupport; /** * This pane can be used to group some widgets together and allow the user to * expand or collapse in order to show or hide those widgets, respectively. * * @version 11.0.0 * @since 11.0.0 * @author Pascal Filion */ @SuppressWarnings("nls") public final class ExpandablePane extends JPanel { private Icon buttonCollapsedDisabledImage; private Icon buttonCollapsedFocusImage; private Icon buttonCollapsedImage; private Icon buttonCollapsedPressedImage; private String buttonCollapsedText; private Icon buttonExpandedDisabledImage; private Icon buttonExpandedFocusImage; private Icon buttonExpandedImage; private Icon buttonExpandedPressedImage; private String buttonExpandedText; private ChangeSupport changeSupport; private JToggleButton expandButton; private PropertyValueModel expandedHolder; /** * This constant is used to tweak the painting of any components. If its * boolean value associated with this property is true, then the component * should paint as selected. */ String FAKE_FOCUS = "fakeFocus"; /** * Creates a new <code>ExpandablePanel</code>. */ public ExpandablePane(String buttonExpandedText, String buttonCollapsedText, Icon buttonExpandedImage, Icon buttonCollapsedImage, Icon buttonExpandedDisabledImage, Icon buttonCollapsedDisabledImage, Icon buttonExpandedFocusImage, Icon buttonCollapsedFocusImage, Icon buttonExpandedPressedImage, Icon buttonCollapsedPressedImage, JComponent internalPane, boolean expanded) { super(new GridBagLayout()); initialize(buttonExpandedText, buttonCollapsedText, buttonExpandedImage, buttonCollapsedImage, buttonExpandedDisabledImage, buttonCollapsedDisabledImage, buttonExpandedFocusImage, buttonCollapsedFocusImage, buttonExpandedPressedImage, buttonCollapsedPressedImage, internalPane, expanded); } /** * Creates a new <code>ExpandablePanel</code>. */ public ExpandablePane(String buttonExpandedText, String buttonCollapsedText, Icon buttonExpandedImage, Icon buttonCollapsedImage, JComponent internalPane, boolean expanded) { this(buttonExpandedText, buttonCollapsedText, buttonExpandedImage, buttonCollapsedImage, null, null, null, null, null, null, internalPane, true); } /** * Creates a new <code>ExpandablePanel</code>. The internal pane will be * shown by default. * * @param buttonExpandedText The text of the toggle button when the button is * selected * @param buttonCollapsedText The text of the toggle button when the button * is unselected * @param internalPane The pane that will be shown when the button is set as * expanded or hidden when the button is set as collapsed */ public ExpandablePane(String buttonExpandedText, String buttonCollapsedText, JComponent internalPane) { this(buttonExpandedText, buttonCollapsedText, null, null, null, null, null, null, null, null, internalPane, true); } /** * Creates a new <code>ExpandablePanel</code>. * * @param buttonExpandedText The text of the toggle button when the button is * selected * @param buttonCollapsedText The text of the toggle button when the button * is unselected * @param internalPane The pane that will be shown when the button is set as * expanded or hidden when the button is set as collapsed * @param expanded <code>true</code> if the internal pane should be shown or * <code>false</code> to hide it */ public ExpandablePane(String buttonExpandedText, String buttonCollapsedText, JComponent internalPane, boolean expanded) { this(buttonExpandedText, buttonCollapsedText, null, null, internalPane, expanded); } /** * Registers the given listener to be notified when the internal pane is * either shown or hidden. * * @param listener The <code>PropertyChangeListener</code> receiving the new * state of the expansion, which is <code>Boolean</code> value */ public void addExpansionPropertyChangeListener(PropertyChangeListener listener) { changeSupport.addPropertyChangeListener(ValueModel.VALUE, listener); } private ActionListener buildActionListener() { return new ActionListener() { public void actionPerformed(ActionEvent e) { expandedHolder.setValue(!((Boolean)expandedHolder.getValue()).booleanValue()); updateExpandButtonText(); updatePressedIcon(); changeSupport.firePropertyChanged(ValueModel.VALUE, !((Boolean)expandedHolder.getValue()).booleanValue(), ((Boolean)expandedHolder.getValue()).booleanValue()); }}; } private void initialize(String buttonExpandedText, String buttonCollapsedText, Icon buttonExpandedImage, Icon buttonCollapsedImage, Icon buttonExpandedDisabledImage, Icon buttonCollapsedDisabledImage, Icon buttonExpandedFocusImage, Icon buttonCollapsedFocusImage, Icon buttonExpandedPressedImage, Icon buttonCollapsedPressedImage, JComponent internalPane, boolean expanded) { if (internalPane == null) { throw new IllegalStateException("The internal panel cannot be null"); } this.buttonExpandedText = buttonExpandedText; this.buttonCollapsedText = buttonCollapsedText; this.buttonExpandedImage = buttonExpandedImage; this.buttonCollapsedImage = buttonCollapsedImage; this.buttonExpandedDisabledImage = buttonExpandedDisabledImage; this.buttonCollapsedDisabledImage = buttonCollapsedDisabledImage; this.buttonExpandedFocusImage = buttonExpandedFocusImage; this.buttonCollapsedFocusImage = buttonCollapsedFocusImage; this.buttonExpandedPressedImage = buttonExpandedPressedImage; this.buttonCollapsedPressedImage = buttonCollapsedPressedImage; changeSupport = new ChangeSupport(this); expandedHolder = new SimplePropertyValueModel(expanded); initializeLayout(internalPane); new ComponentVisibilityEnabler(expandedHolder, internalPane); setExpanded(expanded); setOpaque(false); } /** * Initializes the layout of this pane. * * @param expanded <code>true</code> if the internal pane should be shown or * <code>false</code> to hide it * @param internalPane The pane that will be shown when the button is set as * expanded or hidden when the button is set as collapsed */ private void initializeLayout(JComponent internalPane) { GridBagConstraints constraints = new GridBagConstraints(); boolean expanded = isExpanded(); // Expandable button expandButton = new ToggleButton(); expandButton.setBorderPainted(false); expandButton.setContentAreaFilled(false); expandButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); expandButton.setSelected(expanded); expandButton.addActionListener(buildActionListener()); expandButton.putClientProperty(FAKE_FOCUS, false); Insets margin = expandButton.getInsets(); margin.left = margin.right = 4; expandButton.setMargin(margin); constraints.gridx = 0; constraints.gridy = 0; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; constraints.weighty = 0; constraints.fill = GridBagConstraints.NONE; constraints.anchor = GridBagConstraints.LINE_START; constraints.insets = new Insets(0, 0, 0, 0); add(expandButton, constraints); updateExpandButtonIcon(); updateExpandButtonText(); // Internal pane constraints.gridx = 0; constraints.gridy = 1; constraints.gridwidth = 1; constraints.gridheight = 1; constraints.weightx = 1; constraints.weighty = 0; constraints.fill = GridBagConstraints.BOTH; constraints.anchor = GridBagConstraints.CENTER; constraints.insets = new Insets(5, 0, 0, 0); add(internalPane, constraints); } /** * Determines if the internal pane is shown. * * @return <code>true</code> if the toggle button is selected and the * internal pane is visible or <code>false</code> if the internal pane is * hidden */ public boolean isExpanded() { return ((Boolean)expandedHolder.getValue()).booleanValue(); } /** * Removes the given listener from being notified when the internal pane is * either shown or hidden. * * @param listener The <code>PropertyChangeListener</code> to be deregistered */ public void removeExpansionPropertyChangeListener(PropertyChangeListener listener) { changeSupport.removePropertyChangeListener(ValueModel.VALUE, listener); } /* * (non-Javadoc) */ @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); expandButton.setEnabled(enabled); } /** * Sets whether the internal pane should be shown or hidden and the toggle * button to be selected or unselected. * * @param expanded <code>true</code> to select the toggle button and for the * internal pane to be visible or <code>false</code> unselect the toggle * button and to hide the internal pane */ public void setExpanded(boolean expanded) { expandButton.setSelected(expanded); } private void updateExpandButtonIcon() { expandButton.setIcon(buttonExpandedImage); expandButton.setSelectedIcon(buttonCollapsedImage); expandButton.setPressedIcon(buttonExpandedPressedImage); expandButton.setDisabledIcon(buttonExpandedDisabledImage); expandButton.setDisabledSelectedIcon(buttonCollapsedDisabledImage); expandButton.setRolloverIcon(buttonExpandedFocusImage); expandButton.setRolloverSelectedIcon(buttonCollapsedFocusImage); } private void updateExpandButtonText() { expandButton.setText(isExpanded() ? buttonCollapsedText : buttonExpandedText); } private void updatePressedIcon() { if (expandButton.isSelected()) { expandButton.setPressedIcon(buttonCollapsedPressedImage); } else { expandButton.setPressedIcon(buttonExpandedPressedImage); } } /** * This extension of the Swing's toggle button simply updates the cursor. */ private static class ToggleButton extends JToggleButton { private final Rectangle iconRect; private final Rectangle textRect; private final Rectangle viewRect; /** * Creates a new <code>ToggleButton</code>. */ ToggleButton() { super(); viewRect = new Rectangle(); textRect = new Rectangle(); iconRect = new Rectangle(); } /* * (non-Javadoc) */ @Override public void paint(Graphics g) { super.paint(g); if (isEnabled()) { paintUnderline(g); } } private void paintUnderline(Graphics graphics) { Insets insets = getInsets(); viewRect.x = insets.left; viewRect.y = insets.top; viewRect.width = getWidth() - (insets.right + viewRect.x); viewRect.height = getHeight() - (insets.bottom + viewRect.y); textRect.x = textRect.y = textRect.width = textRect.height = 0; iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0; SwingUtilities.layoutCompoundLabel ( this, getFontMetrics(getFont()), getText(), getIcon(), getVerticalAlignment(), getHorizontalAlignment(), getVerticalTextPosition(), getHorizontalTextPosition(), viewRect, iconRect, textRect, getText() == null ? 0 : getIconTextGap() ); graphics.setColor(getForeground()); graphics.drawLine(textRect.x, textRect.y + textRect.height - 2, textRect.x + textRect.width, textRect.y + textRect.height - 2); } /* * (non-Javadoc) */ @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); if (enabled) { setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); } else { setCursor(Cursor.getDefaultCursor()); } } } }