/*
* Copyright 2011 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.optaplanner.examples.nurserostering.swingui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import org.optaplanner.examples.common.swingui.components.LabeledComboBoxRenderer;
import org.optaplanner.examples.nurserostering.domain.Employee;
import org.optaplanner.examples.nurserostering.domain.Shift;
import org.optaplanner.examples.nurserostering.domain.ShiftAssignment;
import org.optaplanner.examples.nurserostering.domain.ShiftDate;
import org.optaplanner.examples.nurserostering.domain.ShiftType;
import org.optaplanner.examples.nurserostering.domain.WeekendDefinition;
import org.optaplanner.swing.impl.SwingUtils;
import org.optaplanner.swing.impl.TangoColorFactory;
public class EmployeePanel extends JPanel {
public static final int WEST_HEADER_WIDTH = 160;
public static final int EAST_HEADER_WIDTH = 130;
private final NurseRosteringPanel nurseRosteringPanel;
private List<ShiftDate> shiftDateList;
private List<Shift> shiftList;
private Employee employee;
private JButton deleteButton;
private JPanel shiftDateListPanel = null;
private Map<ShiftDate, JPanel> shiftDatePanelMap;
private Map<Shift, JPanel> shiftPanelMap;
private JLabel numberOfShiftAssignmentsLabel;
private Map<ShiftAssignment, JButton> shiftAssignmentButtonMap = new HashMap<>();
public EmployeePanel(NurseRosteringPanel nurseRosteringPanel, List<ShiftDate> shiftDateList, List<Shift> shiftList,
Employee employee) {
super(new BorderLayout());
this.nurseRosteringPanel = nurseRosteringPanel;
this.shiftDateList = shiftDateList;
this.shiftList = shiftList;
this.employee = employee;
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(1, 2, 1, 2),
BorderFactory.createLineBorder(Color.BLACK)),
BorderFactory.createEmptyBorder(1, 1, 1, 1)));
createUI();
}
public Employee getEmployee() {
return employee;
}
private String getEmployeeLabel() {
return employee == null ? "Unassigned" : employee.getLabel();
}
public void setShiftDateListAndShiftList(List<ShiftDate> shiftDateList, List<Shift> shiftList) {
this.shiftDateList = shiftDateList;
this.shiftList = shiftList;
resetShiftListPanel();
}
private void createUI() {
JPanel labelAndDeletePanel = new JPanel(new BorderLayout(5, 0));
if (employee != null) {
labelAndDeletePanel.add(new JLabel(nurseRosteringPanel.getEmployeeIcon()), BorderLayout.WEST);
}
JLabel employeeLabel = new JLabel(getEmployeeLabel());
employeeLabel.setEnabled(false);
labelAndDeletePanel.add(employeeLabel, BorderLayout.CENTER);
if (employee != null) {
JPanel deletePanel = new JPanel(new BorderLayout());
deleteButton = SwingUtils.makeSmallButton(new JButton(nurseRosteringPanel.getDeleteEmployeeIcon()));
deleteButton.setToolTipText("Delete");
deleteButton.addActionListener(e -> nurseRosteringPanel.deleteEmployee(employee));
deletePanel.add(deleteButton, BorderLayout.NORTH);
labelAndDeletePanel.add(deletePanel, BorderLayout.EAST);
}
labelAndDeletePanel.setPreferredSize(new Dimension(WEST_HEADER_WIDTH,
(int) labelAndDeletePanel.getPreferredSize().getHeight()));
add(labelAndDeletePanel, BorderLayout.WEST);
resetShiftListPanel();
numberOfShiftAssignmentsLabel = new JLabel("0 assignments", JLabel.RIGHT);
numberOfShiftAssignmentsLabel.setPreferredSize(new Dimension(EAST_HEADER_WIDTH, 20));
numberOfShiftAssignmentsLabel.setEnabled(false);
add(numberOfShiftAssignmentsLabel, BorderLayout.EAST);
}
public void resetShiftListPanel() {
if (shiftDateListPanel != null) {
remove(shiftDateListPanel);
}
WeekendDefinition weekendDefinition = (employee == null) ? WeekendDefinition.SATURDAY_SUNDAY
: employee.getContract().getWeekendDefinition();
shiftDateListPanel = new JPanel(new GridLayout(1, 0));
shiftDatePanelMap = new LinkedHashMap<>(shiftDateList.size());
for (ShiftDate shiftDate : shiftDateList) {
JPanel shiftDatePanel = new JPanel(new GridLayout(1, 0));
Color backgroundColor = weekendDefinition.isWeekend(shiftDate.getDayOfWeek())
? TangoColorFactory.ALUMINIUM_2 : shiftDatePanel.getBackground();
if (employee != null) {
if (employee.getDayOffRequestMap().containsKey(shiftDate)) {
backgroundColor = TangoColorFactory.ALUMINIUM_4;
} else if (employee.getDayOnRequestMap().containsKey(shiftDate)) {
backgroundColor = TangoColorFactory.SCARLET_1;
}
}
shiftDatePanel.setBackground(backgroundColor);
boolean inPlanningWindow = nurseRosteringPanel.getSolution().getNurseRosterParametrization()
.isInPlanningWindow(shiftDate);
shiftDatePanel.setEnabled(inPlanningWindow);
shiftDatePanel.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createLineBorder(inPlanningWindow ? TangoColorFactory.ALUMINIUM_6 : TangoColorFactory.ALUMINIUM_3),
BorderFactory.createEmptyBorder(2, 2, 2, 2)));
shiftDatePanelMap.put(shiftDate, shiftDatePanel);
if (employee == null) {
// TODO HACK should be in NurseRosterPanel.createHeaderPanel
JPanel wrappingShiftDatePanel = new JPanel(new BorderLayout());
JLabel shiftDateLabel = new JLabel(shiftDate.getLabel(), JLabel.CENTER);
shiftDateLabel.setEnabled(shiftDatePanel.isEnabled());
wrappingShiftDatePanel.add(shiftDateLabel, BorderLayout.NORTH);
wrappingShiftDatePanel.add(shiftDatePanel, BorderLayout.CENTER);
shiftDateListPanel.add(wrappingShiftDatePanel);
} else {
shiftDateListPanel.add(shiftDatePanel);
}
}
shiftPanelMap = new LinkedHashMap<>(shiftList.size());
for (Shift shift : shiftList) {
JPanel shiftDatePanel = shiftDatePanelMap.get(shift.getShiftDate());
JPanel shiftPanel = new JPanel();
shiftPanel.setEnabled(shiftDatePanel.isEnabled());
shiftPanel.setLayout(new BoxLayout(shiftPanel, BoxLayout.Y_AXIS));
Color backgroundColor = shiftDatePanel.getBackground();
if (employee != null) {
if (employee.getShiftOffRequestMap().containsKey(shift)) {
backgroundColor = TangoColorFactory.ALUMINIUM_4;
} else if (employee.getShiftOnRequestMap().containsKey(shift)) {
backgroundColor = TangoColorFactory.SCARLET_1;
}
}
shiftPanel.setBackground(backgroundColor);
shiftPanel.setToolTipText("<html>Date: " + shift.getShiftDate().getLabel() + "<br/>"
+ "Employee: " + (employee == null ? "unassigned" : employee.getLabel())
+ "</html>");
shiftPanelMap.put(shift, shiftPanel);
shiftDatePanel.add(shiftPanel);
}
add(shiftDateListPanel, BorderLayout.CENTER);
}
public void addShiftAssignment(ShiftAssignment shiftAssignment) {
Shift shift = shiftAssignment.getShift();
JPanel shiftPanel = shiftPanelMap.get(shift);
JButton shiftAssignmentButton = SwingUtils.makeSmallButton(new JButton(new ShiftAssignmentAction(shiftAssignment)));
shiftAssignmentButton.setEnabled(shiftPanel.isEnabled());
if (employee != null) {
if (employee.getDayOffRequestMap().containsKey(shift.getShiftDate())
|| employee.getShiftOffRequestMap().containsKey(shift)) {
shiftAssignmentButton.setForeground(TangoColorFactory.SCARLET_1);
}
}
Color color = nurseRosteringPanel.determinePlanningEntityColor(shiftAssignment, shift.getShiftType());
shiftAssignmentButton.setBackground(color);
String toolTip = nurseRosteringPanel.determinePlanningEntityTooltip(shiftAssignment);
shiftAssignmentButton.setToolTipText(toolTip);
shiftPanel.add(shiftAssignmentButton);
shiftPanel.repaint();
shiftAssignmentButtonMap.put(shiftAssignment, shiftAssignmentButton);
}
public void clearShiftAssignments() {
for (JPanel shiftPanel : shiftPanelMap.values()) {
shiftPanel.removeAll();
shiftPanel.repaint();
}
shiftAssignmentButtonMap.clear();
}
public void update() {
numberOfShiftAssignmentsLabel.setText(shiftAssignmentButtonMap.size() + " assignments");
}
private class ShiftAssignmentAction extends AbstractAction {
private ShiftAssignment shiftAssignment;
public ShiftAssignmentAction(ShiftAssignment shiftAssignment) {
super(shiftAssignment.getShift().getShiftType().getCode());
this.shiftAssignment = shiftAssignment;
Shift shift = shiftAssignment.getShift();
ShiftType shiftType = shift.getShiftType();
// Tooltip
putValue(SHORT_DESCRIPTION, "<html>Date: " + shift.getShiftDate().getLabel() + "<br/>"
+ "Shift type: " + shiftType.getLabel() + " (from " + shiftType.getStartTimeString()
+ " to " + shiftType.getEndTimeString() + ")<br/>"
+ "Employee: " + (employee == null ? "unassigned" : employee.getLabel())
+ "</html>");
}
@Override
public void actionPerformed(ActionEvent e) {
List<Employee> employeeList = nurseRosteringPanel.getSolution().getEmployeeList();
// Add 1 to array size to add null, which makes the entity unassigned
JComboBox employeeListField = new JComboBox(
employeeList.toArray(new Object[employeeList.size() + 1]));
LabeledComboBoxRenderer.applyToComboBox(employeeListField);
employeeListField.setSelectedItem(shiftAssignment.getEmployee());
int result = JOptionPane.showConfirmDialog(EmployeePanel.this.getRootPane(), employeeListField,
"Select employee", JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
Employee toEmployee = (Employee) employeeListField.getSelectedItem();
nurseRosteringPanel.moveShiftAssignmentToEmployee(shiftAssignment, toEmployee);
}
}
}
}