/* Copyright 2012 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.taskproperties; import biz.ganttproject.core.calendar.GPCalendarCalc; import biz.ganttproject.core.option.BooleanOption; import biz.ganttproject.core.option.ChangeValueEvent; import biz.ganttproject.core.option.ChangeValueListener; import biz.ganttproject.core.option.DefaultBooleanOption; import biz.ganttproject.core.time.CalendarFactory; import biz.ganttproject.core.time.GanttCalendar; import com.google.common.collect.ImmutableList; import net.sourceforge.ganttproject.action.GPAction; import net.sourceforge.ganttproject.gui.UIFacade; import net.sourceforge.ganttproject.gui.UIUtil; import net.sourceforge.ganttproject.gui.UIUtil.DateValidator; import net.sourceforge.ganttproject.language.GanttLanguage; import net.sourceforge.ganttproject.task.Task; 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.FocusEvent; import java.awt.event.FocusListener; import java.util.Calendar; import java.util.Date; import java.util.List; /** * Encapsulates start date/end date/duration field. * * @author dbarashev (Dmitry Barashev) */ public class TaskScheduleDatesPanel { private static final GanttLanguage language = GanttLanguage.getInstance(); private JXDatePicker myStartDatePicker; private JXDatePicker myEndDatePicker; private JTextField durationField1; private GanttCalendar myStart; private GanttCalendar myEnd; private Task myUnpluggedClone; private static final BooleanOption ourStartDateLock = new DefaultBooleanOption("", false); private static final BooleanOption ourEndDateLock = new DefaultBooleanOption("", true); private static final BooleanOption ourDurationLock = new DefaultBooleanOption("", false); private static BooleanOption ourPrevLock = ourEndDateLock; private final UIFacade myUiFacade; private JXHyperlink myLockHyperlink; private boolean isMilestone; private JLabel myLockLabel; public TaskScheduleDatesPanel(UIFacade uiFacade) { myUiFacade = uiFacade; } public void setUnpluggedClone(Task unpluggedClone) { myUnpluggedClone = unpluggedClone; isMilestone = unpluggedClone.isMilestone(); DateValidator validator = new UIUtil.DateValidator() { @Override public Pair<Boolean, String> apply(Date value) { return DateValidator.Default.aroundProjectStart(myUnpluggedClone.getManager().getProjectStart()).apply(value); } }; UIUtil.setupDatePicker(myStartDatePicker, myUnpluggedClone.getStart().getTime(), validator, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Date date = ((JXDatePicker) e.getSource()).getDate(); if (date != null) { setStart(CalendarFactory.createGanttCalendar(date), true); } } }); UIUtil.setupDatePicker(myEndDatePicker, myUnpluggedClone.getEnd().getTime(), validator, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { GanttCalendar c = CalendarFactory.createGanttCalendar(((JXDatePicker) e.getSource()).getDate()); c.add(Calendar.DATE, 1); setEnd(c, true); } }); setStart(myUnpluggedClone.getStart(), false); setEnd(myUnpluggedClone.getEnd(), false); adjustLength(); } private static JComponent createLabel(String title, final BooleanOption isLocked, final JComponent controlledComponent, final GPAction lockAction) { final JPanel labelPanel = new JPanel(new BorderLayout()); JLabel result = new JLabel(title); isLocked.addChangeValueListener(new ChangeValueListener() { @Override public void changeValue(ChangeValueEvent event) { UIUtil.setEnabledTree(labelPanel, !isLocked.getValue()); UIUtil.setEnabledTree(controlledComponent, !isLocked.getValue()); lockAction.setEnabled(!isLocked.getValue()); lockAction.putValue(Action.SELECTED_KEY, isLocked.getValue()); } }); labelPanel.add(result, BorderLayout.WEST); UIUtil.setEnabledTree(labelPanel, !isLocked.getValue()); UIUtil.setEnabledTree(controlledComponent, !isLocked.getValue()); lockAction.setEnabled(!isLocked.getValue()); return labelPanel; } private void swapLocks(BooleanOption newLock) { newLock.setValue(true); ourPrevLock.setValue(false); ourPrevLock = newLock; } private GPAction createLockAction(String key, final BooleanOption lock) { return new GPAction(key) { { putValue(Action.SELECTED_KEY, lock.isChecked()); } @Override public void actionPerformed(ActionEvent e) { swapLocks(lock); } }; } protected void showPopup(List<Action> actions, JComponent owner, JComponent anchor) { myUiFacade.showPopupMenu(owner, actions, anchor.getLocation().x, anchor.getLocation().y); } public void insertInto(final JPanel propertiesPanel) { // Begin date myStartDatePicker = UIUtil.createDatePicker(); final GPAction startDateLockAction = createLockAction("option.taskProperties.main.scheduling.manual.value.start", ourStartDateLock); JComponent startDateLabel = createLabel(language.getText("dateOfBegining"), ourStartDateLock, myStartDatePicker, startDateLockAction); // End date myEndDatePicker = UIUtil.createDatePicker(); final GPAction endDateLockAction = createLockAction("option.taskProperties.main.scheduling.manual.value.end", ourEndDateLock); JComponent endDateLabel = createLabel(language.getText("dateOfEnd"), ourEndDateLock, myEndDatePicker, endDateLockAction); // Duration durationField1 = new JTextField(8); durationField1.setName("length"); durationField1.addFocusListener(new FocusListener() { @Override public void focusLost(FocusEvent e) { fireDurationChanged(); } @Override public void focusGained(FocusEvent e) { } }); final GPAction durationLockAction = createLockAction("option.taskProperties.main.scheduling.manual.value.duration", ourDurationLock); JComponent durationLabel = createLabel(language.getText("length"), ourDurationLock, durationField1, durationLockAction); myLockLabel = new JLabel(language.getText("option.taskProperties.main.scheduling.label")); propertiesPanel.add(myLockLabel); final Box box = Box.createHorizontalBox(); myLockHyperlink = new JXHyperlink(new GPAction("option.taskProperties.main.scheduling.manual.label") { @Override public void actionPerformed(ActionEvent e) { showPopup(ImmutableList.<Action>of(startDateLockAction, endDateLockAction, durationLockAction), box, myLockHyperlink); } }); box.add(myLockHyperlink); // box.add(Box.createHorizontalStrut(15)); // mySchedulingHyperlink = new JXHyperlink(new GPAction("option.taskProperties.main.scheduling.automated.label") { // @Override // public void actionPerformed(ActionEvent e) { // showPopup(createSchedulingMenu(), box, mySchedulingHyperlink); // } // }); // box.add(mySchedulingHyperlink); propertiesPanel.add(box); propertiesPanel.add(startDateLabel); propertiesPanel.add(myStartDatePicker); propertiesPanel.add(endDateLabel); propertiesPanel.add(myEndDatePicker); propertiesPanel.add(durationLabel); propertiesPanel.add(durationField1); } private GPCalendarCalc getCalendar() { return myUnpluggedClone.getManager().getCalendar(); } private void setStart(GanttCalendar start, boolean recalculateEnd) { myStart = start; myStartDatePicker.setDate(myStart.getTime()); if (!recalculateEnd) { return; } if (!isMilestone && ourDurationLock.isChecked()) { adjustLength(); } else { GanttCalendar endDate = isMilestone ? myStart : CalendarFactory.createGanttCalendar( getCalendar().shiftDate(myStart.getTime(), myUnpluggedClone.getDuration())); setEnd(endDate, false); } } private void setEnd(GanttCalendar end, boolean recalculateStart) { myEnd = end; myEndDatePicker.setDate((isMilestone ? end : end.getDisplayValue()).getTime()); if (!recalculateStart) { return; } if (!isMilestone && ourDurationLock.isChecked()) { adjustLength(); } else { setStart(CalendarFactory.createGanttCalendar( getCalendar().shiftDate(myEnd.getTime(), myUnpluggedClone.getDuration().reverse())), false); } } public int getLength() { try { return Integer.parseInt(durationField1.getText().trim()); } catch (NumberFormatException e) { return 0; } } private void fireDurationChanged() { String value = durationField1.getText(); try { int duration = Integer.parseInt(value); changeLength(duration); } catch (NumberFormatException e) { } } private void changeLength(int length) { if (length <= 0) { length = 1; } durationField1.setText(String.valueOf(length)); myUnpluggedClone.setDuration(myUnpluggedClone.getManager().createLength(length)); if (ourStartDateLock.isChecked()) { // Calculate the start date for the given length setStart(CalendarFactory.createGanttCalendar( getCalendar().shiftDate(myEnd.getTime(), myUnpluggedClone.getDuration().reverse())), false); } else { // Calculate the end date for the given length setEnd(CalendarFactory.createGanttCalendar( getCalendar().shiftDate(myStart.getTime(), myUnpluggedClone.getDuration())), false); } } private void adjustLength() { myUnpluggedClone.setStart(myStart); myUnpluggedClone.setEnd(myEnd); int length = myUnpluggedClone.getDuration().getLength(); if (length > 0) { durationField1.setText(String.valueOf(length)); } else { // Start is bigger than end Date, so set length to 1 and adjust the // non-locked field // TODO It would be nice if the user is notified of the illegal date he // selected changeLength(1); } } public GanttCalendar getEnd() { return myEnd; } public GanttCalendar getStart() { return myStart; } public void setMilestone(boolean isMilestone) { if (isMilestone) { ourStartDateLock.setValue(false); ourDurationLock.setValue(true); ourEndDateLock.setValue(true); myLockHyperlink.setEnabled(false); myLockLabel.setEnabled(false); } else { ourDurationLock.setValue(false); ourEndDateLock.setValue(false); myLockHyperlink.setEnabled(true); myLockLabel.setEnabled(true); ourPrevLock.setValue(true); } this.isMilestone = isMilestone; } public void setSupertask(boolean isSupertask) { if (isSupertask) { ourStartDateLock.setValue(true); ourDurationLock.setValue(true); ourEndDateLock.setValue(true); myLockHyperlink.setEnabled(false); myLockLabel.setEnabled(false); } else { ourDurationLock.setValue(false); ourStartDateLock.setValue(false); myLockHyperlink.setEnabled(true); ourPrevLock.setValue(true); myLockLabel.setEnabled(true); } } }