/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* CreateMinimalWorkspacePanel.java
* Creation date: May 25, 2006.
* By: Joseph Wong
*/
package org.openquark.gems.client;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.ItemSelectable;
import java.awt.SystemColor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.EventListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.ListModel;
import org.openquark.cal.compiler.FieldName;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.services.CALWorkspace;
import org.openquark.cal.services.VaultElementInfo;
import org.openquark.util.Pair;
import org.openquark.util.UnsafeCast;
import org.openquark.util.model.EventListenerList;
import org.openquark.util.ui.LabelProvider;
/**
* This class implements a reusable panel for specifying the modules creating a minimal workspace.
*
* @author Joseph Wong
*/
class CreateMinimalWorkspacePanel extends JPanel {
private static final long serialVersionUID = -8843872729742066027L;
/**
* A JList that adds check-boxes to each row and maintains a set of "checked"
* values. Implements ItemSelectable so listeners can keep track of which
* values are selected according to the check boxes.
*
* @author Robin Salkeld
*/
public static class ListWithCheckBoxes extends JList implements ItemSelectable {
private static final long serialVersionUID = -3360677654265720512L;
/** The background color of the parent list */
private static final Color BACKGROUND = SystemColor.text;
/** The label provider used to render the cells */
private final LabelProvider labelProvider;
/** The set of listeners triggered when checkboxes are set or cleared */
private final EventListenerList checkboxListeners = new EventListenerList(ItemListener.class);
/** An internal flag for determining whether events should be blocked. */
private boolean blockEvents = false;
public ListWithCheckBoxes(LabelProvider labelProvider) {
super();
this.labelProvider = labelProvider;
setBackground(BACKGROUND);
setCellRenderer(new LabelWithCheckBoxCellRenderer());
addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
handleMousePressedEvent(e);
}
});
}
/**
* Returns a list of objects that are selected by the user.
* @return the checked values
*/
public Set<ModuleName> getCheckedValues() {
Set<ModuleName> retval = new HashSet<ModuleName>();
ListModel model = getModel();
for (int i = 0, size = model.getSize(); i < size; i++) {
LabelWithCheckBox checkBox = (LabelWithCheckBox) model.getElementAt(i);
if (checkBox.isSelected()) {
retval.add((ModuleName)checkBox.getObject());
}
}
return retval;
}
/**
* {@inheritDoc}
*/
public void addItemListener(ItemListener l) {
checkboxListeners.add(l);
}
/**
* {@inheritDoc}
*/
public Object[] getSelectedObjects() {
return getCheckedValues().toArray();
}
/**
* {@inheritDoc}
*/
public void removeItemListener(ItemListener l) {
checkboxListeners.remove(l);
}
/**
* Builds the valid elements list.
*/
public void buildList(Set<FieldName> selectedValues, List<ModuleName> allValues) {
DefaultListModel model = new DefaultListModel();
for (int i = 0, size = allValues.size(); i < size; i++) {
Object value = allValues.get(i);
LabelWithCheckBox checkBox = new LabelWithCheckBox(value, labelProvider);
checkBox.setBackground(BACKGROUND);
if (selectedValues.contains(value)) {
checkBox.setSelected(true);
}
model.add(i, checkBox);
}
setModel(model);
}
/**
* Selects all the elements in the list.
*/
public void selectAll() {
blockEvents = true;
DefaultListModel model = (DefaultListModel)getModel();
for (int i = 0, n = model.getSize(); i < n; i++) {
LabelWithCheckBox checkBox = (LabelWithCheckBox)model.get(i);
checkBox.setSelected(true);
}
blockEvents = false;
fireEvent(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, model, ItemEvent.SELECTED));
repaint();
}
/**
* Deselects all the elements in the list.
*/
public void deselectAll() {
blockEvents = true;
DefaultListModel model = (DefaultListModel)getModel();
for (int i = 0, n = model.getSize(); i < n; i++) {
LabelWithCheckBox checkBox = (LabelWithCheckBox)model.get(i);
checkBox.setSelected(false);
}
blockEvents = false;
fireEvent(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED, model, ItemEvent.DESELECTED));
repaint();
}
/**
* This event handler is called whenever the mouse button is pressed.
* @param event
*/
private void handleMousePressedEvent(MouseEvent event) {
int index = locationToIndex(event.getPoint());
if (index >= 0) {
LabelWithCheckBox checkBox =
(LabelWithCheckBox) getModel().getElementAt(index);
if (checkBox.isEnabled()) {
checkBox.setSelected(!checkBox.isSelected());
}
repaint();
}
}
/**
* Fires an item state change event to all relevant listeners.
*/
private void fireEvent(ItemEvent event) {
if (!blockEvents) {
for (Iterator<EventListener> iter = checkboxListeners.getListeners(); iter.hasNext();) {
ItemListener listener = (ItemListener)iter.next();
listener.itemStateChanged(event);
}
}
}
/**
* A simple panel that emulates a checkbox, with custom node type icons.
*/
private class LabelWithCheckBox extends JPanel {
private static final long serialVersionUID = 4609318443869473231L;
/** Stores a reference to the object */
private final Object object;
/** Do not display the text with this checkbox */
private JCheckBox checkBox;
/** Display the node type name and icon with this component */
private JLabel label;
/** The label provider used to render the object */
private final LabelProvider labelProvider;
public LabelWithCheckBox(Object object, LabelProvider labelProvider) {
super(new BorderLayout());
this.object = object;
this.labelProvider = labelProvider;
initializeUI();
}
/**
* Returns the object associated with this check box.
* @return Object
*/
public Object getObject() {
return object;
}
/**
* Sets the selection state of this check box and fires listeners as needed.
* @param selected
*/
public void setSelected(boolean selected) {
boolean wasSelected = checkBox.isSelected();
checkBox.setSelected(selected);
if (wasSelected != selected) {
int stateChange = (wasSelected ? ItemEvent.DESELECTED : ItemEvent.SELECTED);
ItemEvent event = new ItemEvent(ListWithCheckBoxes.this, ItemEvent.ITEM_STATE_CHANGED,
getObject(), stateChange);
fireEvent(event);
}
}
/**
* Returns the selection state of this check box.
* @return boolean
*/
public boolean isSelected() {
return checkBox.isSelected();
}
/**
* {@inheritDoc}
*/
@Override
public void setBackground(Color color) {
super.setBackground(color);
if (checkBox != null) {
checkBox.setBackground(color);
}
if (label != null) {
label.setBackground(color);
}
}
/**
* Initializes the UI and add comonents to the panel. This should be
* called once only.
*/
private void initializeUI() {
checkBox = new JCheckBox();
add(checkBox, BorderLayout.WEST);
label = new JLabel(labelProvider.getText(object));
Icon icon = labelProvider.getIcon(object, false);
if (icon != null) {
label.setIcon(icon);
}
add(label, BorderLayout.CENTER);
}
/**
* {@inheritDoc}
*/
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
label.setEnabled(enabled);
checkBox.setEnabled(enabled);
}
}
/**
* A simple <code>ListCellRenderer</code> that is able to render checkboxes
* in a JList component.
*/
private static class LabelWithCheckBoxCellRenderer implements ListCellRenderer {
/**
* {@inheritDoc}
*/
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
return (LabelWithCheckBox) value;
}
}
/**
* {@inheritDoc}
*/
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
DefaultListModel model = (DefaultListModel)getModel();
for (int i = 0, n = model.getSize(); i < n; i++) {
LabelWithCheckBox checkBox = (LabelWithCheckBox)model.get(i);
checkBox.setEnabled(enabled);
}
}
}
/** The list of checkboxes for the user to specify which modules should be the root set of the minimal workspace. */
private ListWithCheckBoxes fromList;
/** The list for displaying all the modules in the minimal workspace. */
private JList modulesInMinimalWorkspaceList;
/** The current workspace. */
private final CALWorkspace workspace;
/** The button for selecting all the modules in the list of checkboxes. */
private final JButton selectAllButton = new JButton(GemCutter.getResourceString("CreateMinimalWorkspaceSelectAll"));
/** The button for deselecting all the modules in the list of checkboxes. */
private final JButton deselectAllButton = new JButton(GemCutter.getResourceString("CreateMinimalWorkspaceDeselectAll"));
/**
* Constructs a CreateMinimalWorkspacePanel.
* @param workspace the current workspace.
*/
CreateMinimalWorkspacePanel(CALWorkspace workspace) {
super(new GridLayout(2, 0));
if (workspace == null) {
throw new NullPointerException();
}
this.workspace = workspace;
ModuleName[] moduleNames = workspace.getModuleNames();
Arrays.sort(moduleNames);
Dimension listDimension = new Dimension(130, 130);
JPanel fromListPanel = new JPanel(new BorderLayout());
{
fromList = new ListWithCheckBoxes(new LabelProvider() {
public Icon getIcon(Object item, boolean expanded) {
return null;
}
public String getText(Object item) {
return item.toString();
}
public String getTooltipText(Object item) {
return item.toString();
}
});
fromList.buildList(Collections.<FieldName>emptySet(), Arrays.asList(moduleNames));
fromList.addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent e) {
updateModulesInMinimalWorkspaceList();
}
});
JScrollPane fromListScrollPane = new JScrollPane(fromList);
fromListScrollPane.setMinimumSize(listDimension);
fromListScrollPane.setPreferredSize(listDimension);
fromListPanel.add(fromListScrollPane, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel(new BorderLayout());
{
JPanel innerButtonPanel = new JPanel();
{
innerButtonPanel.setLayout(new GridLayout(0, 1));
selectAllButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fromList.selectAll();
}
});
deselectAllButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
fromList.deselectAll();
}
});
innerButtonPanel.add(wrapWithBorder(selectAllButton, 0, 6, 3, 0));
innerButtonPanel.add(wrapWithBorder(deselectAllButton, 3, 6, 0, 0));
}
buttonPanel.add(innerButtonPanel, BorderLayout.NORTH);
}
fromListPanel.add(buttonPanel, BorderLayout.EAST);
fromListPanel.setBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(0, 0, 12, 0),
BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), GemCutter.getResourceString("CreateMinimalWorkspaceSelectModules"))));
}
this.add(fromListPanel);
JPanel toListPanel = new JPanel(new BorderLayout());
{
modulesInMinimalWorkspaceList = new JList();
modulesInMinimalWorkspaceList.setModel(new DefaultListModel());
modulesInMinimalWorkspaceList.setCellRenderer(new ListCellRenderer() {
private final JLabel label = new JLabel();
private final Font origFont = label.getFont();
private final Font boldFont = origFont.deriveFont(Font.BOLD);
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Pair<ModuleName, Boolean> p = UnsafeCast.<Pair<ModuleName, Boolean>>unsafeCast(value);
ModuleName moduleName = p.fst();
boolean isUserSelected = (p.snd()).booleanValue();
label.setText(moduleName.toSourceText());
if (!list.isEnabled()) {
label.setForeground(SystemColor.textInactiveText);
} else if (isUserSelected) {
label.setForeground(SystemColor.textText);
label.setFont(boldFont);
} else {
label.setForeground(SystemColor.textText);
label.setFont(origFont);
}
return label;
}
});
JScrollPane toListScrollPane = new JScrollPane(modulesInMinimalWorkspaceList);
toListScrollPane.setMinimumSize(listDimension);
toListScrollPane.setPreferredSize(listDimension);
toListPanel.add(toListScrollPane, BorderLayout.CENTER);
toListPanel.setBorder(
BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), GemCutter.getResourceString("CreateMinimalWorkspaceModulesInMinimalWorkspace")));
}
this.add(toListPanel);
}
/**
* Wraps the specified component with a panel with a empty border of the
* specified dimensions.
*
* @param component the component to be wrapped.
* @param top an integer specifying the width of the top, in pixels.
* @param left an integer specifying the width of the left side, in pixels.
* @param bottom an integer specifying the width of the right side, in pixels.
* @param right an integer specifying the width of the bottom, in pixels.
* @return a new panel containing the component.
*/
private JPanel wrapWithBorder(Component component, int top, int left, int bottom, int right) {
JPanel panel = new JPanel(new BorderLayout());
panel.add(component, BorderLayout.CENTER);
panel.setBorder(BorderFactory.createEmptyBorder(top, left, bottom, right));
return panel;
}
/**
* Updates the list of modules in the minimal workspace.
*/
private void updateModulesInMinimalWorkspaceList() {
DefaultListModel model = (DefaultListModel)modulesInMinimalWorkspaceList.getModel();
model.clear();
CALWorkspace.DependencyFinder depFinder = getDependencyFinder();
List <Pair<ModuleName, Boolean>> items = new ArrayList<Pair<ModuleName, Boolean>>();
SortedSet<ModuleName> rootSet = depFinder.getRootSet();
SortedSet<ModuleName> importedModulesSet = depFinder.getImportedModulesSet();
for (final ModuleName moduleName : rootSet) {
items.add(new Pair<ModuleName, Boolean>(moduleName, Boolean.TRUE));
}
for (final ModuleName moduleName : importedModulesSet) {
items.add(new Pair<ModuleName, Boolean>(moduleName, Boolean.FALSE));
}
Collections.sort(items, new Comparator<Pair<ModuleName, Boolean>>() {
public int compare(Pair<ModuleName, Boolean> a, Pair<ModuleName, Boolean> b) {
ModuleName aKey = a.fst();
ModuleName bKey = b.fst();
return aKey.compareTo(bKey);
}
});
for (int i = 0, n = items.size(); i < n; i++) {
model.addElement(items.get(i));
}
}
/**
* @return a dependency finder based on the root set specified in {@link #fromList}.
*/
CALWorkspace.DependencyFinder getDependencyFinder() {
return workspace.getDependencyFinder(fromList.getCheckedValues());
}
/**
* @return the content of the minimal workspace declaration.
*/
String getMinimalWorkspaceDeclaration() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
CALWorkspace.DependencyFinder depFinder = getDependencyFinder();
pw.println("// This CAL workspace declaration file is automatically generated at " + new Date());
pw.println();
pw.println("// imported modules");
for (final ModuleName moduleName : depFinder.getImportedModulesSet()) {
pw.println(getWorkspaceDeclarationLine(moduleName));
}
pw.println();
pw.println("// root modules");
for (final ModuleName moduleName : depFinder.getRootSet()) {
pw.println(getWorkspaceDeclarationLine(moduleName));
}
pw.flush();
return sw.toString();
}
/**
* Returns a line for a workspace declaration based on the given module name.
* @param moduleName
* @return a line for a workspace declaration.
*/
private String getWorkspaceDeclarationLine(ModuleName moduleName) {
VaultElementInfo vaultInfo = workspace.getVaultInfo(moduleName);
if (vaultInfo instanceof VaultElementInfo.Basic) {
return ((VaultElementInfo.Basic)vaultInfo).toDeclarationString();
} else {
return "StandardVault " + moduleName;
}
}
/**
* {@inheritDoc}
*/
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
fromList.setEnabled(enabled);
selectAllButton.setEnabled(enabled);
deselectAllButton.setEnabled(enabled);
modulesInMinimalWorkspaceList.setEnabled(enabled);
modulesInMinimalWorkspaceList.repaint(); // to force the repainting of this list with disabled labels
}
}