/*******************************************************************************
* 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.chooser;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JTextField;
import org.eclipse.persistence.tools.workbench.framework.context.WorkbenchContextHolder;
import org.eclipse.persistence.tools.workbench.framework.ui.view.AbstractPanel;
import org.eclipse.persistence.tools.workbench.framework.uitools.SwingComponentFactory;
import org.eclipse.persistence.tools.workbench.uitools.app.PropertyValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.TransformationPropertyValueModel;
import org.eclipse.persistence.tools.workbench.uitools.app.swing.DocumentAdapter;
/**
* This panel looks like a ListChooser, but the button launches a
* class chooser dialog. The list of classes presented to the user
* is derived from the specified class description repository.
*
* Since this panel simply selects a type, it will *not* refresh
* the chosen type. This should be OK in most situations.
* If you need to use the selected class to generate a list
* of methods, or some such, you will need to listen for the
* selection to change and check whether the selection is
* a "stub". If it is, and you need a list of methods, you will
* need to refresh the selected type and notify the user of
* any problems. Alternatively, you could provide the user with
* a manual way to refresh the list of methods.
*/
public class ClassChooserPanel extends AbstractPanel {
/** The current selection (taken from the "class description" repository). */
private PropertyValueModel selectionHolder;
/** The repository passed to the class chooser dialog. */
private ClassDescriptionRepositoryFactory classDescriptionRepositoryFactory;
/** An adapter for the repository and selection. */
ClassDescriptionAdapter classDescriptionAdapter;
/** Whether the user is allowed to clear the selection - default is false. */
private boolean allowNullSelection;
/** Allows for custom button label text and mnemonic */
private String buttonKey;
// ********** constructor/initialization **********
/**
* the default allows primitives to be included in the pick list
*/
public ClassChooserPanel(
PropertyValueModel selectionHolder,
ClassDescriptionRepositoryFactory classDescriptionRepositoryFactory,
ClassDescriptionAdapter classDescriptionAdapter,
WorkbenchContextHolder contextHolder
) {
this(selectionHolder, classDescriptionRepositoryFactory, classDescriptionAdapter, null, contextHolder);
}
/**
* the default allows primitives to be included in the pick list
*/
public ClassChooserPanel(
PropertyValueModel selectionHolder,
ClassDescriptionRepositoryFactory classDescriptionRepositoryFactory,
ClassDescriptionAdapter classDescriptionAdapter,
JLabel label,
WorkbenchContextHolder contextHolder
) {
this(selectionHolder, classDescriptionRepositoryFactory, classDescriptionAdapter, label, contextHolder, "CLASS_CHOOSER_BROWSE_BUTTON");
}
/**
* the default allows primitives to be included in the pick list
* allows custom button key primarily for differentiation of mnemonic, key must exist in FrameworkResourceBundle
*/
public ClassChooserPanel(
PropertyValueModel selectionHolder,
ClassDescriptionRepositoryFactory classDescriptionRepositoryFactory,
ClassDescriptionAdapter classDescriptionAdapter,
JLabel label,
WorkbenchContextHolder contextHolder,
String buttonKey
) {
super(contextHolder);
this.selectionHolder = selectionHolder;
this.classDescriptionRepositoryFactory = classDescriptionRepositoryFactory;
this.classDescriptionAdapter = classDescriptionAdapter;
this.allowNullSelection = false;
this.buttonKey = buttonKey;
this.initializeLayout(label);
}
private void initializeLayout(JLabel label) {
GridBagConstraints constraints = new GridBagConstraints();
// the (read-only) text field that displays the current selection
JTextField textField = new JTextField(new DocumentAdapter(this.buildClassNameHolder()), null, 1);
textField.setEditable(false);
constraints.gridx = 0;
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, 0, 0, 0);
this.add(textField, constraints);
if (label != null) {
textField.putClientProperty("labeledBy", label);
}
// the button that brings up the class chooser dialog
JButton button = buildButton(this.buttonKey);
button.addActionListener(this.buildActionListener());
if (label != null) {
label.setLabelFor(button);
SwingComponentFactory.updateButtonAccessibleName(label, button);
}
constraints.gridx = 1;
constraints.gridy = 0;
constraints.gridwidth = 1;
constraints.gridheight = 1;
constraints.weightx = 0;
constraints.weighty = 0;
constraints.fill = GridBagConstraints.NONE;
constraints.anchor = GridBagConstraints.CENTER;
constraints.insets = new Insets(0, 5, 0, 0);
this.add(button, constraints);
this.addAlignRight(button);
}
private PropertyValueModel buildClassNameHolder() {
return new TransformationPropertyValueModel(this.selectionHolder) {
protected Object transform(Object value) {
return (value == null) ? "" : ClassChooserPanel.this.classDescriptionAdapter.className(value).replace('$', '.');
}
protected Object reverseTransform(Object value) {
throw new UnsupportedOperationException();
}
};
}
/**
* this listener opens up the class chooser dialog
* when the button is pressed
*/
private ActionListener buildActionListener() {
return new ActionListener() {
public void actionPerformed(ActionEvent e) {
ClassChooserPanel.this.promptUserToSelectClass();
}
};
}
// ********** queries **********
private ClassDescriptionRepository buildRepository() {
return this.classDescriptionRepositoryFactory.createClassDescriptionRepository();
}
private Object getSelection() {
return this.selectionHolder.getValue();
}
// ********** behavior **********
/**
* Do NOT automatically refresh the types when opening the class chooser dialog.
* Allow the user to control when the list of available types is refreshed, with the "Refresh" button.
* @see MWClassRepository#refreshExternalClassDescriptions()
*/
void promptUserToSelectClass() {
ClassChooserDialog dialog = this.buildDialog();
dialog.setAllowNullSelection(this.allowNullSelection);
dialog.setInitialSelection(this.getSelection());
dialog.show();
if (dialog.wasConfirmed()) {
this.selectionHolder.setValue(dialog.selection());
}
// try to force all the objects generated by the dialog to be garbage-collected
dialog = null;
ClassChooserDialog.gc();
}
private ClassChooserDialog buildDialog() {
return ClassChooserDialog.createDialog(
this.buildRepository(),
this.classDescriptionAdapter,
this.getWorkbenchContext()
);
}
// ********** public API **********
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
for (int i = this.getComponentCount(); i-- > 0; ) {
this.getComponent(i).setEnabled(enabled);
}
}
/**
* Set whether the user is allowed to clear the selection.
* The default is false.
*/
public void setAllowNullSelection(boolean allowNullSelection) {
this.allowNullSelection = allowNullSelection;
}
}