/*
Copyright 2003-2012 Dmitry Barashev, GanttProject Team
This file is part of GanttProject, an opensource project management tool.
GanttProject is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GanttProject is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GanttProject. If not, see <http://www.gnu.org/licenses/>.
*/
package net.sourceforge.ganttproject.gui.tableView;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import biz.ganttproject.core.option.ChangeValueEvent;
import biz.ganttproject.core.option.ChangeValueListener;
import biz.ganttproject.core.option.DefaultBooleanOption;
import biz.ganttproject.core.option.DefaultEnumerationOption;
import biz.ganttproject.core.option.DefaultStringOption;
import biz.ganttproject.core.option.GPOption;
import biz.ganttproject.core.option.GPOptionGroup;
import biz.ganttproject.core.table.ColumnList;
import biz.ganttproject.core.table.ColumnList.Column;
import com.google.common.base.Function;
import net.sourceforge.ganttproject.CustomPropertyClass;
import net.sourceforge.ganttproject.CustomPropertyDefinition;
import net.sourceforge.ganttproject.CustomPropertyManager;
import net.sourceforge.ganttproject.DefaultCustomPropertyDefinition;
import net.sourceforge.ganttproject.action.GPAction;
import net.sourceforge.ganttproject.gui.AbstractTableAndActionsComponent;
import net.sourceforge.ganttproject.gui.AbstractTableAndActionsComponent.SelectionListener;
import net.sourceforge.ganttproject.gui.EditableList;
import net.sourceforge.ganttproject.gui.ListAndFieldsPanel;
import net.sourceforge.ganttproject.gui.UIUtil;
import net.sourceforge.ganttproject.gui.options.OptionsPageBuilder;
import net.sourceforge.ganttproject.gui.options.model.CustomPropertyDefaultValueAdapter;
/**
* UI component inside "Custom Fields Manager" dialog which allows for showing/hiding
* table columns and for the management of custom fields.
*
* @author dbarashev (Dmitry Barashev)
*/
public class ColumnManagerPanel {
private IsVisibleOption myIsVisibleOption = new IsVisibleOption();
private NameOption myNameOption = new NameOption();
private DefaultValueOption myEnableDefaultValueOption = new DefaultValueOption();
private PropertyClassOption myType = new PropertyClassOption();
private GPOption<?> myDefaultValueOption;
private JPanel panelDefaultValue = null;
private CardLayout cardLayoutDefaultValue = null;
private final CustomPropertyManager myManager;
private final ColumnList myVisibleFields;
private JComponent myFields;
public ColumnManagerPanel(CustomPropertyManager columnManager, ColumnList visibleFields) {
myManager = columnManager;
myVisibleFields = visibleFields;
}
public void commitCustomPropertyEdit() {
myIsVisibleOption.commit();
myNameOption.commit();
myEnableDefaultValueOption.commit();
myType.commit();
}
public Component createComponent() {
List<CustomPropertyDefinition> emptyList = Collections.emptyList();
List<CustomPropertyDefinition> defs = new ArrayList<CustomPropertyDefinition>();
createDefaultFieldDefinitions(myVisibleFields, myManager.getDefinitions(), defs);
final Set<CustomPropertyDefinition> standardDefs = new HashSet<CustomPropertyDefinition>(defs);
defs.addAll(myManager.getDefinitions());
final EditableList<CustomPropertyDefinition> props = new EditableList<CustomPropertyDefinition>(defs, emptyList) {
@Override
protected boolean isEditable(CustomPropertyDefinition t) {
return ColumnManagerPanel.this.isEditable(t);
}
@Override
protected String getStringValue(CustomPropertyDefinition value) {
return value.getName();
}
@Override
protected CustomPropertyDefinition createPrototype(Object editValue) {
return new DefaultCustomPropertyDefinition(String.valueOf(editValue));
}
@Override
protected CustomPropertyDefinition createValue(CustomPropertyDefinition prototype) {
final CustomPropertyDefinition def = myManager.createDefinition(CustomPropertyClass.TEXT.getID(),
prototype.getName(), null);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myVisibleFields.add(def.getID(), -1, -1);
getTableComponent().requestFocus();
setSelectedDefinition(def);
}
});
return def;
}
@Override
protected void deleteValue(CustomPropertyDefinition value) {
myManager.deleteDefinition(value);
}
@Override
protected CustomPropertyDefinition updateValue(CustomPropertyDefinition newValue,
CustomPropertyDefinition curValue) {
curValue.setName(newValue.getName());
return curValue;
}
@Override
protected Component getTableCellRendererComponent(DefaultTableCellRenderer defaultRenderer,
CustomPropertyDefinition def, boolean isSelected, boolean hasFocus, int row) {
StringBuffer value = new StringBuffer("<html>");
Column column = myIsVisibleOption.findColumn(def);
if (column != null && !column.isVisible()) {
value.append("<font color=#999999>{0}</font>");
} else {
value.append("{0}");
}
value.append("</html>");
defaultRenderer.setText(MessageFormat.format(value.toString(), getStringValue(def)));
return defaultRenderer;
}
};
class ShowHideSelectionAction extends GPAction implements SelectionListener<CustomPropertyDefinition> {
private boolean isShow;
private List<CustomPropertyDefinition> mySelection;
public ShowHideSelectionAction(boolean isShow, String name) {
super(name);
this.isShow = isShow;
}
@Override
public void selectionChanged(List<CustomPropertyDefinition> selection) {
mySelection = selection;
}
@Override
public void actionPerformed(ActionEvent e) {
for (CustomPropertyDefinition def : mySelection) {
myIsVisibleOption.setVisible(def, isShow);
}
}
}
props.getTableAndActions().addAction(new ShowHideSelectionAction(true, "customPropertyDialog.showSelected"));
props.getTableAndActions().addAction(new ShowHideSelectionAction(false, "customPropertyDialog.hideSelected"));
myIsVisibleOption.setVisibleFields(myVisibleFields);
ChangeValueListener defaultValuePanelEnabler = new ChangeValueListener() {
@Override
public void changeValue(ChangeValueEvent event) {
setDefaultValuePanelEnabled(myEnableDefaultValueOption.isChecked());
}
};
myEnableDefaultValueOption.addChangeValueListener(defaultValuePanelEnabler);
myType.addChangeValueListener(defaultValuePanelEnabler);
myFields = getFieldsComponent();
ListAndFieldsPanel<CustomPropertyDefinition> listAndFields = new ListAndFieldsPanel<CustomPropertyDefinition>(
props, myFields);
Function<List<CustomPropertyDefinition>, Boolean> isDeleteEnabled = new Function<List<CustomPropertyDefinition>, Boolean>() {
@Override
public Boolean apply(List<CustomPropertyDefinition> input) {
for (CustomPropertyDefinition def : input) {
if (standardDefs.contains(def)) {
return false;
}
}
return !input.isEmpty();
}
};
props.getTableAndActions().getDeleteItemAction().putValue(AbstractTableAndActionsComponent.PROPERTY_IS_ENABLED_FUNCTION, isDeleteEnabled);
props.getTableAndActions().addSelectionListener(new SelectionListener<CustomPropertyDefinition>() {
@Override
public void selectionChanged(List<CustomPropertyDefinition> selection) {
if (selection.size() != 1) {
UIUtil.setEnabledTree(myFields, false);
} else {
commitCustomPropertyEdit();
CustomPropertyDefinition selectedElement = selection.get(0);
setSelectedDefinition(selectedElement);
}
}
});
return listAndFields.getComponent();
}
private void setSelectedDefinition(CustomPropertyDefinition selected) {
UIUtil.setEnabledTree(myFields, isEditable(selected));
myNameOption.reloadValue(selected);
myType.reloadValue(selected);
myEnableDefaultValueOption.reloadValue(selected);
}
/**
* This method returns default columns (such as name, Id, etc.) in {@code output} list.
* It does it by scanning the tableHeader where all columns live and filtering out
* the real custom columns.
*/
private void createDefaultFieldDefinitions(ColumnList tableHeader,
List<CustomPropertyDefinition> customFields, List<CustomPropertyDefinition> output) {
LinkedHashMap<String, Column> name2column = new LinkedHashMap<String, Column>();
for (int i = 0; i < tableHeader.getSize(); i++) {
Column column = tableHeader.getField(i);
name2column.put(column.getName(), column);
}
for (CustomPropertyDefinition def : customFields) {
name2column.remove(def.getName());
}
for (Column column : name2column.values()) {
output.add(new DefaultCustomPropertyDefinition(column.getName()));
}
}
protected boolean isEditable(CustomPropertyDefinition def) {
return myManager.getCustomPropertyDefinition(def.getID()) != null;
}
protected void setDefaultValuePanelEnabled(boolean enabled) {
UIUtil.setEnabledTree(panelDefaultValue, enabled);
}
private JComponent getFieldsComponent() {
OptionsPageBuilder builder = new OptionsPageBuilder();
{
cardLayoutDefaultValue = new CardLayout();
panelDefaultValue = new JPanel(cardLayoutDefaultValue);
myType.setUIControls(cardLayoutDefaultValue, panelDefaultValue);
}
Component optionsComponent = builder.createGroupComponent(null, myNameOption, myType);
Box result = Box.createVerticalBox();
result.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// result.setLayout(new BorderLayout());
result.add(optionsComponent);
result.add(Box.createVerticalStrut(10));
GPOptionGroup defaultValueGroup = new GPOptionGroup("customPropertyDialog.defaultValue",
new GPOption[] { myEnableDefaultValueOption });
defaultValueGroup.setTitled(false);
result.add(builder.createGroupComponent(defaultValueGroup, myEnableDefaultValueOption));
result.add(panelDefaultValue);
setDefaultValuePanelEnabled(false);
return result;
}
class IsVisibleOption extends DefaultBooleanOption {
private ColumnList myVisibleFields;
IsVisibleOption() {
super("customPropertyDialog.isVisible");
}
public void setVisibleFields(ColumnList visibleFields) {
myVisibleFields = visibleFields;
}
@Override
public void commit() {
if (isChanged()) {
super.commit();
}
}
void setVisible(CustomPropertyDefinition def, boolean isVisible) {
Column column = findColumn(def);
if (column != null) {
column.setVisible(isVisible);
}
}
Column findColumn(CustomPropertyDefinition def) {
for (int i = 0; i < myVisibleFields.getSize(); i++) {
Column nextColumn = myVisibleFields.getField(i);
if (nextColumn.getName().equals(def.getName())) {
return nextColumn;
}
}
return null;
}
}
/**
* Option class controlling changes of a custom field name
*/
static class NameOption extends DefaultStringOption {
CustomPropertyDefinition myDefinition;
NameOption() {
super("customPropertyDialog.name");
}
public void reloadValue(CustomPropertyDefinition selectedElement) {
myDefinition = selectedElement;
resetValue(myDefinition.getName(), true);
}
@Override
public void commit() {
if (isChanged()) {
super.commit();
myDefinition.setName(getValue());
}
}
}
/**
* Option class controlling changes of a custom field default value
*/
class DefaultValueOption extends DefaultBooleanOption {
CustomPropertyDefinition myDefinition;
DefaultValueOption() {
super("customPropertyDialog.defaultValue");
}
@Override
public void setValue(Boolean value) {
super.setValue(value);
UIUtil.setEnabledTree(panelDefaultValue, value);
}
@Override
public void commit() {
if (isChanged()) {
super.commit();
if (!getValue()) {
myDefinition.setDefaultValueAsString(null);
}
}
}
public void reloadValue(CustomPropertyDefinition selectedElement) {
myDefinition = selectedElement;
resetValue(myDefinition.getDefaultValue() != null, true);
}
}
/**
* Option class controlling changes of a custom field type
*/
class PropertyClassOption extends DefaultEnumerationOption<CustomPropertyClass> {
private CardLayout myCardLayout;
private JPanel myCardPanel;
// private Map<CustomPropertyClass, Component> myDefaultValueEditors =
// new HashMap<CustomPropertyClass, Component>();
private CustomPropertyDefinition myDefinition;
private CustomPropertyDefinition myDefinitionRO;
public PropertyClassOption() {
super("taskProperties.customColumn.type", CustomPropertyClass.values());
}
@Override
protected String objectToString(CustomPropertyClass value) {
return value.getDisplayName();
}
@Override
protected void resetValue(String value, boolean resetInitial, Object triggerId) {
CustomPropertyClass propertyClass = getCustomPropertyClass(value);
myDefinition.setPropertyClass(propertyClass);
// Component defaultValueEditor =
// myDefaultValueEditors.get(propertyClass);
// System.err.println("property class option setValue: class=" +
// propertyClass + " defaultValueEditor=" + defaultValueEditor);
// if (defaultValueEditor == null) {
myDefaultValueOption = CustomPropertyDefaultValueAdapter.createDefaultValueOption(propertyClass, myDefinition);
OptionsPageBuilder builder = new OptionsPageBuilder();
Component defaultValueEditor = builder.createOptionComponent(null, myDefaultValueOption);
JPanel defaultValuePanel = new JPanel(new BorderLayout());
defaultValuePanel.add(defaultValueEditor, BorderLayout.NORTH);
// myDefaultValueEditors.put(propertyClass, defaultValuePanel);
myCardPanel.add(defaultValuePanel, propertyClass.getDisplayName());
// }
myCardLayout.show(myCardPanel, value);
super.resetValue(value, resetInitial, triggerId);
}
@Override
public void commit() {
if (isChanged()) {
super.commit();
myDefinitionRO.setPropertyClass(getCustomPropertyClass(getValue()));
}
if (myEnableDefaultValueOption.getValue() && myDefaultValueOption != null && myDefaultValueOption.isChanged()) {
myDefaultValueOption.commit();
myDefinitionRO.setDefaultValueAsString(myDefinition.getDefaultValueAsString());
}
}
private CustomPropertyClass getCustomPropertyClass(String value) {
CustomPropertyClass newPropertyClass = null;
for (CustomPropertyClass propertyClass : CustomPropertyClass.values()) {
if (propertyClass.getDisplayName().equals(value)) {
newPropertyClass = propertyClass;
break;
}
}
assert newPropertyClass != null;
return newPropertyClass;
}
void setUIControls(CardLayout layout, JPanel panel) {
myCardLayout = layout;
myCardPanel = panel;
}
public void reloadValue(CustomPropertyDefinition def) {
myDefinitionRO = def;
myDefinition = new DefaultCustomPropertyDefinition(def.getName(), def.getID(), def);
resetValue(def.getPropertyClass().getDisplayName(), true);
}
}
}