/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.framework.ui.view;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Window;
import java.util.prefs.Preferences;
import javax.accessibility.Accessible;
import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.ComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SpinnerDateModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.Border;
import javax.swing.text.Document;
import org.eclipse.persistence.tools.workbench.framework.Application;
import org.eclipse.persistence.tools.workbench.framework.NodeManager;
import org.eclipse.persistence.tools.workbench.framework.app.NavigatorSelectionModel;
import org.eclipse.persistence.tools.workbench.framework.context.ApplicationContext;
import org.eclipse.persistence.tools.workbench.framework.context.DefaultWorkbenchContextHolder;
import org.eclipse.persistence.tools.workbench.framework.context.PreferencesContext;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContext;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContextHolder;
import org.eclipse.persistence.tools.workbench.framework.help.HelpManager;
import org.eclipse.persistence.tools.workbench.framework.resources.ResourceRepository;
import org.eclipse.persistence.tools.workbench.framework.uitools.AccessibleTitledPanel;
import org.eclipse.persistence.tools.workbench.framework.uitools.ComponentAligner;
import org.eclipse.persistence.tools.workbench.framework.uitools.Pane;
import org.eclipse.persistence.tools.workbench.framework.uitools.Spacer;
import org.eclipse.persistence.tools.workbench.framework.uitools.SwingComponentFactory;
/**
* AbstractPanel holds a context and provides
* convenience methods for the NodeManager, ResourceRepository,
* HelpManager, etc.
*
* This abstract panel contains convenience methods for building
* buttons, labels, check boxes, and radio buttons with their text, mnemonic,
* and mnemonic index. To build some components you must pass in the
* associated model. This class ensures that the mnemonic and model are
* set in the proper order to avoid a Swing bug.
*
* See #AbstractPropertiesPage and #AbstractSubjectPanel before subclassing #AbstractPanel.
* Note: There is no initializeLayout method in this abstract class
*/
public abstract class AbstractPanel extends AccessibleTitledPanel {
/**
* This <code>ComponentAligner</code> is responsible to properly align left all
* the components added to the group.
*/
private ComponentAligner alignLeftGroup;
/**
* This <code>ComponentAligner</code> is responsible to properly align right
* all the components added to the group.
*/
private ComponentAligner alignRightGroup;
/**
* The workbench context holder provides a level of indirection that allows us
* to swap in a different workbench context when the panel is installed in a
* particular window. When the panel is first being built the workbench context
* will be empty except for its application context (i.e. the current window,
* the navigator selection model, and anything else tied to the window the
* panel is displayed in will be unavailable).
*/
private WorkbenchContextHolder workbenchContextHolder;
/**
* some panels only need an app context; accessing the workbench
* context will trigger a runtime exception
*/
protected AbstractPanel(ApplicationContext context) {
this(new GridBagLayout(), context);
}
/**
* some panels only need an app context; accessing the workbench
* context will trigger a runtime exception
*/
protected AbstractPanel(LayoutManager layoutManager, ApplicationContext context) {
this(layoutManager, new DefaultWorkbenchContextHolder(context));
}
/**
* the workbench context must be accessed indirectly so it can be
* switched whenever the panel is displayed in a different window
*/
protected AbstractPanel(WorkbenchContextHolder contextHolder) {
this(new GridBagLayout(), contextHolder);
}
/**
* the workbench context must be accessed indirectly so it can be
* switched whenever the panel is displayed in a different window
*/
protected AbstractPanel(LayoutManager layoutManager, WorkbenchContextHolder contextHolder) {
super(layoutManager);
this.workbenchContextHolder = contextHolder;
}
/**
* Adds the given group for left alignment. Its preferred width will be used
* along with the width of all the other components in order to get the
* widest component and use its width as the width for all the components.
*
* @param component The group containing component to be aligned with the
* component contained in this panel's group
*/
protected final void addAlignLeft(ComponentAligner group)
{
getAlignLeftGroup().add(group);
}
/**
* Adds the given component for left alignment. Its preferred width will be
* used along with the width of all the other components in order to get the
* widest component and use its width as the width for all the components.
*
* @param component The component to be properly aligned
*/
protected final void addAlignLeft(JComponent component)
{
getAlignLeftGroup().add(component);
}
/**
* Adds the given group for left alignment. Its preferred width will be used
* along with the width of all the other components in order to get the
* widest component and use its width as the width for all the components.
*
* @param component The group containing component to be aligned with the
* component contained in this panel's group
*/
protected final void addAlignRight(ComponentAligner group)
{
getAlignRightGroup().add(group);
}
/**
* Adds the given component for left alignment. Its preferred width will be
* used along with the width of all the other components in order to get the
* widest component and use its width as the width for all the components.
*
* @param component The component to be properly aligned
*/
protected final void addAlignRight(JComponent component)
{
getAlignRightGroup().add(component);
}
protected final void addHelpTopicId(Component component, String topicId) {
helpManager().addTopicID(component, topicId);
}
/**
* Adds the given component for left alignment. Its preferred width will be
* used along with the width of all the other components in order to get the
* widest component and use its width as the width for all the components.
*
* @param panel The panel to be properly aligned with this panel
*/
protected final void addPaneForAlignLeft(AbstractPanel page)
{
getAlignLeftGroup().add(page.getAlignLeftGroup());
}
/**
* Adds the given component for left alignment. Its preferred width will be
* used along with the width of all the other components in order to get the
* widest component and use its width as the width for all the components.
*
* @param panel The panel to be properly aligned with this panel
*/
protected final void addPaneForAlignment(AbstractPanel panel)
{
addPaneForAlignLeft(panel);
addPaneForAlignRight(panel);
}
/**
* Adds the given component for left alignment. Its preferred width will be
* used along with the width of all the other components in order to get the
* widest component and use its width as the width for all the components.
*
* @param panel The panel to be properly aligned with this panel
*/
protected final void addPaneForAlignRight(AbstractPanel page)
{
getAlignRightGroup().add(page.getAlignRightGroup());
}
/**
* Creates a new <code>JButton</code> and sets the text,
* mnemonic and mnemonic index using the given resource key.
*/
protected final JButton buildButton(String key)
{
return SwingComponentFactory.buildButton(key, resourceRepository());
}
/**
* Creates a new <code>JButton</code> and sets the text, mnemonic and
* mnemonic index using the given resource key.
* It also uses the given labeler to augment the accessible context of the
* button.
*/
protected final JButton buildBrowseButton(String key, Accessible labeler) {
return SwingComponentFactory.buildBrowseButton(key, this.resourceRepository(), labeler);
}
/**
* Creates a new <code>JCheckBox</code> and sets the text, mnemonic, and
* mnemonic index using the given resource key.
* Must pass in a buttonModel because of a bug in Swing.
* The mnemonic will only work properly if set after the model is set.
*/
protected final JCheckBox buildCheckBox(String key, ButtonModel buttonModel)
{
return SwingComponentFactory.buildCheckBox(key, buttonModel, resourceRepository());
}
/**
* Creates a new <code>JLabel</code> and sets at the text,
* mnemonic and mnemonic index using the given resource key.
*/
protected final JLabel buildLabel(String key)
{
return SwingComponentFactory.buildLabel(key, resourceRepository());
}
/**
* Creates a container that has a label and a combo box.
*
* @param key The key used to retrieve the localized string
* @param model The model of the combo box
* @return A new component containing a label and a combo box
*/
protected final JComponent buildLabeledComboBox(String key, ComboBoxModel model)
{
return buildLabeledComboBoxImp(key, model, null, false);
}
/**
* Creates a container that has a label and a combo box.
*
* @param key The key used to retrieve the localized string
* @param model The model of the combo box
* @param renderer The <code>ListCellRenderer</code> used to decorate all the
* items in the model
* @return A new component containing a label and a combo box
*/
protected final JComponent buildLabeledComboBox(String key, ComboBoxModel model, ListCellRenderer renderer)
{
return buildLabeledComboBoxImp(key, model, renderer, false);
}
/**
* Creates a container that has a label and a combo box.
*
* @param key The key used to retrieve the localized string
* @param model The model of the combo box
* @param renderer The <code>ListCellRenderer</code> used to decorate all the
* items in the model
* @return A new component containing a label and a combo box
*/
private JComponent buildLabeledComboBoxImp(String key, ComboBoxModel model, ListCellRenderer renderer, boolean editable)
{
JComboBox comboBox = new JComboBox(model);
comboBox.setEditable(editable);
if (renderer != null)
comboBox.setRenderer(renderer);
return buildLabeledComponent(key, comboBox);
}
/**
* Creates a container where a label and the given component are laid out
*
* @param key The key used to retrieve the localized string
* @param component The component that is labeled
* @return A new component containing a label and a text field
*/
protected final JComponent buildLabeledComponent(String key, JComponent component)
{
return buildLabeledComponent(key, component, new Spacer());
}
/**
* Creates a container that has a label and the given chooser.
*
* @param key The key used to retrieve the localized string
* @param component The component that is labeled
* @param rightComponent A component to be added to the right of the labeled
* component, usually a browse button would be passed, <code>null</code> can
* also be passed so that no filler will be added by default
* @return A new component containing a label and the chooser
*/
protected JComponent buildLabeledComponent(String key, JComponent component, JComponent rightComponent)
{
GridBagConstraints constraints = new GridBagConstraints();
Pane pane = new Pane(new GridBagLayout());
// Left component
JLabel label = buildLabel(key);
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.LINE_START;
constraints.insets = new Insets(0, 0, 0, 0);
pane.add(label, constraints);
addAlignLeft(label);
// Center component
constraints.gridx = 1;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 1;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.anchor = GridBagConstraints.CENTER;
constraints.insets = new Insets(0, 5, 0, 0);
pane.add(component, constraints);
component.setName(key);
label.setLabelFor(component);
// Right component
if (rightComponent != null)
{
constraints.gridx = 2;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.LINE_START;
constraints.insets = new Insets(0, 5, 0, 0);
pane.add(rightComponent, constraints);
rightComponent.setName(key);
addAlignRight(rightComponent);
if (rightComponent instanceof AbstractButton)
{
SwingComponentFactory.updateButtonAccessibleName(label, (AbstractButton) rightComponent);
}
}
return pane;
}
/**
* Creates a container that has a label and a combo box.
*
* @param key The key used to retrieve the localized string
* @param model The model of the combo box
* @return A new component containing a label and a combo box
*/
protected final JComponent buildLabeledEditableComboBox(String key, ComboBoxModel model)
{
return buildLabeledComboBoxImp(key, model, null, true);
}
/**
* Creates a container that has a label and a combo box.
*
* @param key The key used to retrieve the localized string
* @param model The model of the combo box
* @param renderer The <code>ListCellRenderer</code> used to decorate all the
* items in the model
* @return A new component containing a label and a combo box
*/
protected final JComponent buildLabeledEditableComboBox(String key, ComboBoxModel model, ListCellRenderer renderer)
{
return buildLabeledComboBoxImp(key, model, renderer, true);
}
/**
* Creates a container that has a label and a spinner.
*
* @param key The key used to retrieve the localized string
* @param model The spinner's model, which only handles number
* @return A new component containing a label and a spin button
*/
protected final JComponent buildLabeledSpinnerNumber(String key, SpinnerNumberModel model)
{
return buildLabeledSpinnerNumber(key, model, 4);
}
/**
* Creates a container that has a label and a spinner.
*
* @param key The key used to retrieve the localized string
* @param model The spinner's model, which only handles number
* @param columns The number of columns the spinner's editor should have, the
* default value is 4, even though on screen the width is greater than 4, a
* column count of 3 makes it too narrow
* @return A new component containing a label and a spin button
*/
protected final JComponent buildLabeledSpinnerNumber(String key, SpinnerNumberModel model, int columns)
{
Pane pane = new Pane(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
// Left component
JLabel label = buildLabel(key);
pane.add(label, constraints);
addAlignLeft(label);
// Center component
JSpinner spinner = SwingComponentFactory.buildSpinnerNumber(model, columns);
spinner.setName(key);
constraints.weightx = 1;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.LINE_START;
constraints.insets = new Insets(0, 5, 0, 0);
pane.add(spinner, constraints);
label.setLabelFor(spinner);
// Right component
Spacer spacer = new Spacer();
constraints.weightx = 0;
constraints.fill = GridBagConstraints.NONE;
pane.add(spacer, constraints);
addAlignRight(spacer);
return pane;
}
/**
* Creates a container that has a label and a text field.
*
* @param key The key used to retrieve the localized string
* @param document The document of the text field
* @return A new component containing a label and a text field
*/
protected final JComponent buildLabeledTextField(String key, Document document)
{
return buildLabeledTextField(key, document, new Spacer());
}
/**
* Creates a container that has a label and a text field.
*
* @param key The key used to retrieve the localized string
* @param document The document of the text field
* @param rightComponent A component to be added to the right of the labeled
* component, usually a browse button would be passed
* @return A new component containing a label and a text field
*/
protected final JComponent buildLabeledTextField(String key, Document document, JComponent rightComponent) {
JTextField textField = new JTextField(document, null, 1);
return buildLabeledComponent(key, textField, rightComponent);
}
/**
* Creates a new <code>JRadioButton</code> and sets the text, mnemonic, and
* mnemonic index using the given resource key.
* Must pass in a buttonModel because of a bug in Swing.
* The mnemonic will only work properly if set after the model is set.
*/
protected final JRadioButton buildRadioButton(String key, ButtonModel buttonModel) {
return SwingComponentFactory.buildRadioButton(key, buttonModel, resourceRepository());
}
/**
* Creates a new titled border with the text retrieved from the resource
* repository using the given key.
*
* @param key The key used to retrieve the localized string
* @return A new <code>TitledBorder</code>
*/
protected final Border buildTitledBorder(String key) {
return SwingComponentFactory.buildTitledBorder(resourceRepository(), key);
}
/**
* Returns the standard 2-pixel, empty border
* for placing around panels etc.
*/
protected final Border buildStandardEmptyBorder() {
return SwingComponentFactory.buildStandardEmptyBorder();
}
/**
* Returns the <code>ComponentAligner</code> that is responsible to align all
* the components that are on the left side of a pane, usually those
* components are labels.
*
* @return The <code>ComponentAligner</code> that makes sure all components
* with it have the same width
*/
public ComponentAligner getAlignLeftGroup() {
if (this.alignLeftGroup == null) {
this.alignLeftGroup = new ComponentAligner();
}
return this.alignLeftGroup;
}
/**
* Returns the <code>ComponentAligner</code> that is responsible to align all
* the components that are on the right side of a pane, usually those
* components are browse buttons.
*
* @return The <code>ComponentAligner</code> that makes sure all components
* with it have the same width
*/
public ComponentAligner getAlignRightGroup() {
if (this.alignRightGroup == null) {
this.alignRightGroup = new ComponentAligner();
}
return this.alignRightGroup;
}
/**
* Build a spinner that handles Dates.
*/
protected final JSpinner buildSpinnerDate(SpinnerDateModel model) {
JSpinner spinner = new JSpinner();
spinner.setModel(model);
return spinner;
}
public final ApplicationContext getApplicationContext() {
return this.getWorkbenchContext().getApplicationContext();
}
public final Application application() {
return this.getApplicationContext().getApplication();
}
public final Preferences preferences() {
return this.getApplicationContext().getPreferences();
}
public final ResourceRepository resourceRepository() {
return this.getApplicationContext().getResourceRepository();
}
public final HelpManager helpManager() {
return this.getApplicationContext().getHelpManager();
}
public final WorkbenchContextHolder getWorkbenchContextHolder() {
return this.workbenchContextHolder;
}
public final WorkbenchContext getWorkbenchContext() {
return this.workbenchContextHolder.getWorkbenchContext();
}
public final NavigatorSelectionModel navigatorSelectionModel() {
return this.getWorkbenchContext().getNavigatorSelectionModel();
}
/**
* Return the current window. This will be needed to open dialogs.
*/
public final Window currentWindow() {
return this.getWorkbenchContext().getCurrentWindow();
}
public final NodeManager nodeManager() {
return this.getApplicationContext().getNodeManager();
}
public final PreferencesContext getPreferencesContext() {
return (PreferencesContext) this.getApplicationContext();
}
}