/*
GanttProject is an opensource project management tool.
Copyright (C) 2011 GanttProject team
This program 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.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package net.sourceforge.ganttproject.gui;
import biz.ganttproject.core.chart.render.ShapeConstants;
import biz.ganttproject.core.chart.render.ShapePaint;
import biz.ganttproject.core.option.ColorOption;
import biz.ganttproject.core.option.DefaultColorOption;
import biz.ganttproject.core.time.CalendarFactory;
import biz.ganttproject.core.time.GanttCalendar;
import com.google.common.base.Objects;
import net.sourceforge.ganttproject.GanttProject;
import net.sourceforge.ganttproject.GanttTask;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.action.GPAction;
import net.sourceforge.ganttproject.gui.UIUtil.DateValidator;
import net.sourceforge.ganttproject.gui.options.OptionsPageBuilder;
import net.sourceforge.ganttproject.gui.options.SpringUtilities;
import net.sourceforge.ganttproject.gui.taskproperties.CustomColumnsPanel;
import net.sourceforge.ganttproject.gui.taskproperties.TaskAllocationsPanel;
import net.sourceforge.ganttproject.gui.taskproperties.TaskDependenciesPanel;
import net.sourceforge.ganttproject.gui.taskproperties.TaskScheduleDatesPanel;
import net.sourceforge.ganttproject.language.GanttLanguage;
import net.sourceforge.ganttproject.resource.HumanResourceManager;
import net.sourceforge.ganttproject.roles.RoleManager;
import net.sourceforge.ganttproject.shape.JPaintCombo;
import net.sourceforge.ganttproject.task.Task;
import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade;
import net.sourceforge.ganttproject.task.TaskManager;
import net.sourceforge.ganttproject.task.TaskMutator;
import net.sourceforge.ganttproject.util.BrowserControl;
import net.sourceforge.ganttproject.util.collect.Pair;
import org.jdesktop.swingx.JXDatePicker;
import org.jdesktop.swingx.JXHyperlink;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
/**
* Real panel for editing task properties
*/
public class GanttTaskPropertiesBean extends JPanel {
private ColorOption myTaskColorOption = new DefaultColorOption("");
private final GPAction mySetDefaultColorAction = new GPAction("defaultColor") {
@Override
public void actionPerformed(ActionEvent e) {
myTaskColorOption.setValue(myUIfacade.getGanttChart().getTaskDefaultColorOption().getValue());
}
};
private JXDatePicker myEarliestBeginDatePicker;
private GanttTask[] selectedTasks;
private static final GanttLanguage language = GanttLanguage.getInstance();
private GanttCalendar myThird;
private JTabbedPane tabbedPane; // TabbedPane that includes the following four
// items
private JPanel generalPanel;
private JComponent predecessorsPanel;
private JPanel resourcesPanel;
private JPanel notesPanel;
private JTextField nameField1;
private JTextField tfWebLink;
private JButton bWebLink;
private JSpinner percentCompleteSlider;
private JComboBox priorityComboBox;
private JCheckBox myEarliestBeginEnabled;
private JCheckBox mileStoneCheckBox1;
private JCheckBox projectTaskCheckBox1;
/** Shape chooser combo Box */
private JPaintCombo shapeComboBox;
private JScrollPane scrollPaneNotes;
private JTextArea noteAreaNotes;
private JPanel secondRowPanelNotes;
private String originalName;
private String originalWebLink;
private boolean originalIsMilestone;
private GanttCalendar originalStartDate;
private GanttCalendar originalEndDate;
private GanttCalendar originalEarliestBeginDate;
private int originalEarliestBeginEnabled;
private boolean originalIsProjectTask;
private String originalNotes;
private int originalCompletionPercentage;
private Task.Priority originalPriority;
private ShapePaint originalShape;
private final TaskScheduleDatesPanel myTaskScheduleDates;
private CustomColumnsPanel myCustomColumnPanel = null;
private TaskDependenciesPanel myDependenciesPanel;
private TaskAllocationsPanel myAllocationsPanel;
private final HumanResourceManager myHumanResourceManager;
private final RoleManager myRoleManager;
private Task myUnpluggedClone;
private final TaskManager myTaskManager;
private final IGanttProject myProject;
private final UIFacade myUIfacade;
private JCheckBox myShowInTimeline;
private AbstractAction myOnEarliestBeginToggle;
public GanttTaskPropertiesBean(GanttTask[] selectedTasks, IGanttProject project, UIFacade uifacade) {
myTaskScheduleDates = new TaskScheduleDatesPanel(uifacade);
this.selectedTasks = selectedTasks;
storeOriginalValues(selectedTasks[0]);
myHumanResourceManager = project.getHumanResourceManager();
myRoleManager = project.getRoleManager();
myTaskManager = project.getTaskManager();
myProject = project;
myUIfacade = uifacade;
init();
setSelectedTaskProperties();
}
private static void addEmptyRow(JPanel form) {
form.add(Box.createRigidArea(new Dimension(1, 10)));
form.add(Box.createRigidArea(new Dimension(1, 10)));
}
/** Construct the general panel */
private void constructGeneralPanel() {
final JPanel propertiesPanel = new JPanel(new SpringLayout());
propertiesPanel.add(new JLabel(language.getText("name")));
nameField1 = new JTextField(20);
nameField1.setName("name_of_task");
propertiesPanel.add(nameField1);
Pair<String, JCheckBox> checkBox = constructCheckBox();
if (checkBox != null) {
propertiesPanel.add(new JLabel(checkBox.first()));
propertiesPanel.add(checkBox.second());
}
addEmptyRow(propertiesPanel);
myTaskScheduleDates.insertInto(propertiesPanel);
constructEarliestBegin(propertiesPanel);
addEmptyRow(propertiesPanel);
propertiesPanel.add(new JLabel(language.getText("priority")));
priorityComboBox = new JComboBox();
for (Task.Priority p : Task.Priority.values()) {
priorityComboBox.addItem(language.getText(p.getI18nKey()));
}
priorityComboBox.setEditable(false);
propertiesPanel.add(priorityComboBox);
propertiesPanel.add(new JLabel(language.getText("advancement")));
SpinnerNumberModel spinnerModel = new SpinnerNumberModel(0, 0, 100, 1);
percentCompleteSlider = new JSpinner(spinnerModel);
propertiesPanel.add(percentCompleteSlider);
addEmptyRow(propertiesPanel);
propertiesPanel.add(new JLabel(language.getText("option.taskProperties.main.showInTimeline.label")));
myShowInTimeline = new JCheckBox();
propertiesPanel.add(myShowInTimeline);
propertiesPanel.add(new JLabel(language.getText("shape")));
shapeComboBox = new JPaintCombo(ShapeConstants.PATTERN_LIST);
propertiesPanel.add(shapeComboBox);
OptionsPageBuilder builder = new OptionsPageBuilder(GanttTaskPropertiesBean.this, OptionsPageBuilder.TWO_COLUMN_LAYOUT);
builder.setUiFacade(myUIfacade);
JPanel colorBox = new JPanel(new BorderLayout(5, 0));
colorBox.add(builder.createColorComponent(myTaskColorOption).getJComponent(), BorderLayout.WEST);
//colorBox.add(Box.createHorizontalStrut(5));
colorBox.add(new JXHyperlink(mySetDefaultColorAction), BorderLayout.CENTER);
//colorBox.add(Box.createHorizontalGlue());
//colorBox.add(Box.createHorizontalGlue());
//colorBox.add(Box.createHorizontalGlue());
propertiesPanel.add(new JLabel(language.getText("colors")));
propertiesPanel.add(colorBox);
Box weblinkBox = Box.createHorizontalBox();
tfWebLink = new JTextField(20);
weblinkBox.add(tfWebLink);
weblinkBox.add(Box.createHorizontalStrut(2));
bWebLink = new TestGanttRolloverButton(new ImageIcon(getClass().getResource("/icons/web_16.gif")));
bWebLink.setToolTipText(GanttProject.getToolTip(language.getText("openWebLink")));
weblinkBox.add(bWebLink);
bWebLink.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// link to open the web link
if (!BrowserControl.displayURL(tfWebLink.getText())) {
GanttDialogInfo gdi = new GanttDialogInfo(null, GanttDialogInfo.ERROR, GanttDialogInfo.YES_OPTION,
language.getText("msg4"), language.getText("error"));
gdi.setVisible(true);
}
}
});
propertiesPanel.add(new JLabel(language.getText("webLink")));
propertiesPanel.add(weblinkBox);
SpringUtilities.makeCompactGrid(propertiesPanel, propertiesPanel.getComponentCount() / 2, 2, 1, 1, 5, 5);
JPanel propertiesWrapper = new JPanel(new BorderLayout());
propertiesWrapper.add(propertiesPanel, BorderLayout.NORTH);
generalPanel = new JPanel(new SpringLayout());
//generalPanel.add(new JLayer<JPanel>(propertiesPanel, layerUi));
generalPanel.add(propertiesWrapper);
generalPanel.add(notesPanel);
SpringUtilities.makeCompactGrid(generalPanel, 1, 2, 1, 1, 10, 5);
}
private void constructEarliestBegin(Container propertiesPanel) {
final JXHyperlink copyFromBeginDate = new JXHyperlink(new GPAction("option.taskProperties.main.earliestBegin.copyBeginDate") {
@Override
public void actionPerformed(ActionEvent e) {
setThird(myTaskScheduleDates.getStart());
}
@Override
protected String getLocalizedName() {
String fallbackLabel = String.format("%s %s", language.getText("copy"), language.getText("generic.startDate.label"));
return Objects.firstNonNull(super.getLocalizedName(), fallbackLabel);
}
});
myEarliestBeginDatePicker = UIUtil.createDatePicker();
Box valueBox = Box.createHorizontalBox();
myOnEarliestBeginToggle = new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
myEarliestBeginDatePicker.setEnabled(myEarliestBeginEnabled.isSelected());
copyFromBeginDate.setEnabled(myEarliestBeginEnabled.isSelected());
}
};
myEarliestBeginEnabled = new JCheckBox(myOnEarliestBeginToggle);
valueBox.add(myEarliestBeginEnabled);
valueBox.add(Box.createHorizontalStrut(10));
valueBox.add(myEarliestBeginDatePicker);
valueBox.add(Box.createHorizontalStrut(5));
valueBox.add(copyFromBeginDate);
propertiesPanel.add(new JLabel(language.getText("earliestBegin")));
propertiesPanel.add(valueBox);
}
private void constructCustomColumnPanel() {
myCustomColumnPanel = new CustomColumnsPanel(myProject.getTaskCustomColumnManager(), myUIfacade,
selectedTasks[0].getCustomValues(), myUIfacade.getTaskTree().getVisibleFields());
}
/** Construct the predecessors tabbed pane */
private void constructPredecessorsPanel() {
myDependenciesPanel = new TaskDependenciesPanel();
myDependenciesPanel.init(selectedTasks[0]);
predecessorsPanel = myDependenciesPanel.getComponent();
}
/** Construct the resources panel */
private void constructResourcesPanel() {
myAllocationsPanel = new TaskAllocationsPanel(selectedTasks[0], myHumanResourceManager, myRoleManager);
resourcesPanel = myAllocationsPanel.getComponent();
}
/** Construct the notes panel */
private void constructNotesPanel() {
secondRowPanelNotes = new JPanel(new BorderLayout());
UIUtil.createTitle(secondRowPanelNotes, language.getText("notesTask"));
noteAreaNotes = new JTextArea(8, 40);
noteAreaNotes.setLineWrap(true);
noteAreaNotes.setWrapStyleWord(true);
noteAreaNotes.setBackground(new Color(1.0f, 1.0f, 1.0f));
scrollPaneNotes = new JScrollPane(noteAreaNotes);
secondRowPanelNotes.add(scrollPaneNotes, BorderLayout.CENTER);
notesPanel = secondRowPanelNotes;
}
/** Initialize the widgets */
private void init() {
constructNotesPanel();
tabbedPane = new JTabbedPane() {
@Override
public void addTab(String title, Icon icon, Component component) {
super.addTab(title, icon, UIUtil.contentPaneBorder((JComponent)component));
}
};
constructGeneralPanel();
tabbedPane.addTab(language.getText("general"), new ImageIcon(getClass().getResource("/icons/properties_16.gif")),
generalPanel);
constructPredecessorsPanel();
tabbedPane.addTab(language.getText("predecessors"), new ImageIcon(getClass().getResource("/icons/relashion.gif")),
predecessorsPanel);
constructResourcesPanel();
tabbedPane.addTab(language.getCorrectedLabel("human"), new ImageIcon(getClass().getResource("/icons/res_16.gif")),
resourcesPanel);
setLayout(new BorderLayout());
add(tabbedPane, BorderLayout.CENTER);
constructCustomColumnPanel();
tabbedPane.addTab(language.getText("customColumns"), new ImageIcon(getClass().getResource("/icons/custom.gif")),
myCustomColumnPanel.getComponent());
tabbedPane.addFocusListener(new FocusAdapter() {
private boolean isFirstFocusGain = true;
@Override
public void focusGained(FocusEvent e) {
super.focusGained(e);
if (isFirstFocusGain) {
nameField1.requestFocus();
isFirstFocusGain = false;
}
}
});
tabbedPane.setBorder(BorderFactory.createEmptyBorder(2, 0, 5, 0));
}
/** Apply the modified properties to the selected Tasks */
public void applySettings() {
for (int i = 0; i < selectedTasks.length; i++) {
// TODO The originalXXX values should not be used,
// but the original values should be read from each processed task to
// determine whether the value has been changed
TaskMutator mutator = selectedTasks[i].createMutator();
if (originalName == null || !originalName.equals(getTaskName())) {
mutator.setName(getTaskName());
}
if (originalWebLink == null || !originalWebLink.equals(getWebLink())) {
mutator.setWebLink(getWebLink());
}
if (mileStoneCheckBox1 != null) {
if (originalIsMilestone != isMilestone()) {
mutator.setMilestone(isMilestone());
}
} else if (projectTaskCheckBox1 != null) {
if (originalIsProjectTask != isProjectTask()) {
mutator.setProjectTask(isProjectTask());
}
}
if (!originalStartDate.equals(getStart())) {
mutator.setStart(getStart());
}
if (!originalEndDate.equals(getEnd())) {
mutator.setEnd(getEnd());
}
if (originalEarliestBeginDate == null && getThird() != null || originalEarliestBeginDate != null && getThird() == null
|| originalEarliestBeginDate != null && !originalEarliestBeginDate.equals(getThird())
|| originalEarliestBeginEnabled != getThirdDateConstraint()) {
mutator.setThird(getThird(), getThirdDateConstraint());
}
if (getLength() > 0) {
mutator.setDuration(selectedTasks[i].getManager().createLength(getLength()));
}
if (!Objects.equal(originalNotes, getNotes())) {
mutator.setNotes(getNotes());
}
if (originalCompletionPercentage != getPercentComplete()) {
mutator.setCompletionPercentage(getPercentComplete());
}
if (this.originalPriority != getPriority()) {
mutator.setPriority(getPriority());
}
mutator.setColor(myTaskColorOption.getValue());
if (this.originalShape == null && shapeComboBox.getSelectedIndex() != 0 || originalShape != null
&& !this.originalShape.equals(shapeComboBox.getSelectedPaint())) {
mutator.setShape(new ShapePaint((ShapePaint) shapeComboBox.getSelectedPaint(), Color.white,
myTaskColorOption.getValue()));
}
mutator.commit();
myDependenciesPanel.commit();
myAllocationsPanel.commit();
myCustomColumnPanel.commit();
if (!myShowInTimeline.isSelected()) {
myUIfacade.getCurrentTaskView().getTimelineTasks().remove(selectedTasks[i]);
} else {
myUIfacade.getCurrentTaskView().getTimelineTasks().add(selectedTasks[i]);
}
}
}
private void setSelectedTaskProperties() {
myUnpluggedClone = selectedTasks[0].unpluggedClone();
nameField1.setText(originalName);
setName(selectedTasks[0].toString());
percentCompleteSlider.setValue(new Integer(originalCompletionPercentage));
priorityComboBox.setSelectedIndex(originalPriority.ordinal());
myTaskScheduleDates.setUnpluggedClone(myUnpluggedClone);
DateValidator validator = UIUtil.DateValidator.Default.aroundProjectStart(myProject.getTaskManager().getProjectStart());
ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setThird(CalendarFactory.createGanttCalendar(((JXDatePicker) e.getSource()).getDate()));
}
};
UIUtil.setupDatePicker(myEarliestBeginDatePicker, originalEarliestBeginDate == null ? null : originalEarliestBeginDate.getTime(), validator, listener);
myThird = originalEarliestBeginDate;
myEarliestBeginEnabled.setSelected(originalEarliestBeginEnabled == 1);
myOnEarliestBeginToggle.actionPerformed(null);
if (mileStoneCheckBox1 != null) {
mileStoneCheckBox1.setSelected(originalIsMilestone);
} else if (projectTaskCheckBox1 != null) {
projectTaskCheckBox1.setSelected(originalIsProjectTask);
}
myTaskScheduleDates.setMilestone(isMilestone());
boolean isSupertask = myUnpluggedClone.getManager().getTaskHierarchy().hasNestedTasks(selectedTasks[0]);
myTaskScheduleDates.setSupertask(isSupertask);
tfWebLink.setText(originalWebLink);
if (selectedTasks[0].shapeDefined()) {
for (int j = 0; j < ShapeConstants.PATTERN_LIST.length; j++) {
if (originalShape.equals(ShapeConstants.PATTERN_LIST[j])) {
shapeComboBox.setSelectedIndex(j);
break;
}
}
}
noteAreaNotes.setText(originalNotes);
myTaskColorOption.setValue(selectedTasks[0].getColor());
myShowInTimeline.setSelected(myUIfacade.getCurrentTaskView().getTimelineTasks().contains(selectedTasks[0]));
}
private boolean isMilestone() {
if (mileStoneCheckBox1 == null) {
return false;
}
return mileStoneCheckBox1.isSelected();
}
private boolean isProjectTask() {
return projectTaskCheckBox1.isSelected();
}
private int getThirdDateConstraint() {
return myEarliestBeginEnabled.isSelected() ? 1 : 0;
}
private String getNotes() {
return noteAreaNotes.getText();
}
private String getTaskName() {
String text = nameField1.getText();
return text == null ? "" : text.trim();
}
private String getWebLink() {
String text = tfWebLink.getText();
return text == null ? "" : text.trim();
}
private int getPercentComplete() {
return ((Integer) percentCompleteSlider.getValue()).hashCode();
}
private Task.Priority getPriority() {
return Task.Priority.getPriority(priorityComboBox.getSelectedIndex());
}
private GanttCalendar getStart() {
return myTaskScheduleDates.getStart();
}
private GanttCalendar getEnd() {
return myTaskScheduleDates.getEnd();
}
private int getLength() {
return myTaskScheduleDates.getLength();
}
private GanttCalendar getThird() {
return myThird;
}
private void setThird(GanttCalendar third) {
myThird = third;
myEarliestBeginDatePicker.setDate(myThird.getTime());
}
private void storeOriginalValues(GanttTask task) {
originalName = task.getName();
originalWebLink = task.getWebLink();
originalIsMilestone = task.isLegacyMilestone();
originalStartDate = task.getStart();
originalEndDate = task.getEnd();
originalNotes = task.getNotes();
originalCompletionPercentage = task.getCompletionPercentage();
originalPriority = task.getPriority();
originalShape = task.getShape();
originalEarliestBeginDate = task.getThird();
originalEarliestBeginEnabled = task.getThirdDateConstraint();
originalIsProjectTask = task.isProjectTask();
}
private boolean canBeProjectTask(Task testedTask, TaskContainmentHierarchyFacade taskHierarchy) {
Task[] nestedTasks = taskHierarchy.getNestedTasks(testedTask);
if (nestedTasks.length == 0) {
return false;
}
for (Task parent = taskHierarchy.getContainer(testedTask); parent != null; parent = taskHierarchy.getContainer(parent)) {
if (parent.isProjectTask()) {
return false;
}
}
for (Task nestedTask : nestedTasks) {
if (isProjectTaskOrContainsProjectTask(nestedTask)) {
return false;
}
}
return true;
}
private boolean isProjectTaskOrContainsProjectTask(Task task) {
if (task.isProjectTask()) {
return true;
}
for (Task nestedTask : task.getNestedTasks()) {
if (isProjectTaskOrContainsProjectTask(nestedTask)) {
return true;
}
}
return false;
}
/**
* Creates a milestone, a project task or no checkbox depending on the
* selected task
*
* @return the created checkbox or null
*/
private Pair<String, JCheckBox> constructCheckBox() {
boolean canBeProjectTask = true;
boolean canBeMilestone = true;
TaskContainmentHierarchyFacade taskHierarchy = myTaskManager.getTaskHierarchy();
for (Task task : selectedTasks) {
canBeMilestone &= !taskHierarchy.hasNestedTasks(task);
canBeProjectTask &= canBeProjectTask(task, taskHierarchy);
}
assert false == (canBeProjectTask && canBeMilestone);
final Pair<String, JCheckBox> result;
if (canBeProjectTask) {
projectTaskCheckBox1 = new JCheckBox();
result = Pair.create(language.getText("projectTask"), projectTaskCheckBox1);
} else if (canBeMilestone) {
mileStoneCheckBox1 = new JCheckBox(new AbstractAction() {
@Override
public void actionPerformed(ActionEvent arg0) {
myTaskScheduleDates.setMilestone(isMilestone());
}
});
result = Pair.create(language.getText("meetingPoint"), mileStoneCheckBox1);
} else {
result = null;
}
return result;
}
}