/* * Copyright 2010 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.pas.swingui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingConstants; import org.optaplanner.examples.common.swingui.SolutionPanel; import org.optaplanner.examples.common.swingui.components.LabeledComboBoxRenderer; import org.optaplanner.examples.common.swingui.timetable.TimeTablePanel; import org.optaplanner.examples.pas.domain.AdmissionPart; import org.optaplanner.examples.pas.domain.Bed; import org.optaplanner.examples.pas.domain.BedDesignation; import org.optaplanner.examples.pas.domain.Department; import org.optaplanner.examples.pas.domain.Gender; import org.optaplanner.examples.pas.domain.GenderLimitation; import org.optaplanner.examples.pas.domain.Night; import org.optaplanner.examples.pas.domain.Patient; import org.optaplanner.examples.pas.domain.PatientAdmissionSchedule; import org.optaplanner.examples.pas.domain.RequiredPatientEquipment; import org.optaplanner.examples.pas.domain.Room; import org.optaplanner.examples.pas.domain.RoomEquipment; import org.optaplanner.swing.impl.SwingUtils; import org.optaplanner.swing.impl.TangoColorFactory; import static org.optaplanner.examples.common.swingui.timetable.TimeTablePanel.HeaderColumnKey.*; import static org.optaplanner.examples.common.swingui.timetable.TimeTablePanel.HeaderRowKey.*; public class PatientAdmissionSchedulePanel extends SolutionPanel<PatientAdmissionSchedule> { public static final String LOGO_PATH = "/org/optaplanner/examples/pas/swingui/pasLogo.png"; private final ImageIcon anyGenderIcon; private final ImageIcon maleIcon; private final ImageIcon femaleIcon; private final ImageIcon sameGenderIcon; private TimeTablePanel<Night, Bed> timeTablePanel; private TangoColorFactory equipmentTangoColorFactory; public PatientAdmissionSchedulePanel() { anyGenderIcon = new ImageIcon(getClass().getResource("anyGender.png")); maleIcon = new ImageIcon(getClass().getResource("male.png")); femaleIcon = new ImageIcon(getClass().getResource("female.png")); sameGenderIcon = new ImageIcon(getClass().getResource("sameGender.png")); setLayout(new BorderLayout()); timeTablePanel = new TimeTablePanel<>(); add(timeTablePanel, BorderLayout.CENTER); } @Override public void resetPanel(PatientAdmissionSchedule patientAdmissionSchedule) { timeTablePanel.reset(); equipmentTangoColorFactory = new TangoColorFactory(); defineGrid(patientAdmissionSchedule); fillCells(patientAdmissionSchedule); repaint(); // Hack to force a repaint of TimeTableLayout during "refresh screen while solving" } private void defineGrid(PatientAdmissionSchedule patientAdmissionSchedule) { JButton footprint = SwingUtils.makeSmallButton(new JButton("Patient9999")); int footprintWidth = footprint.getPreferredSize().width; timeTablePanel.defineColumnHeaderByKey(HEADER_COLUMN_GROUP2); // Department Header timeTablePanel.defineColumnHeaderByKey(HEADER_COLUMN_GROUP1); // Room Header timeTablePanel.defineColumnHeaderByKey(HEADER_COLUMN); // Bed Header for (Night night : patientAdmissionSchedule.getNightList()) { timeTablePanel.defineColumnHeader(night, footprintWidth); } timeTablePanel.defineRowHeaderByKey(HEADER_ROW); // Night header timeTablePanel.defineRowHeader(null); // Unassigned bed for (Bed bed : patientAdmissionSchedule.getBedList()) { timeTablePanel.defineRowHeader(bed); } } private void fillCells(PatientAdmissionSchedule patientAdmissionSchedule) { timeTablePanel.addCornerHeader(HEADER_COLUMN_GROUP2, HEADER_ROW, createHeaderPanel(new JLabel("Department"))); timeTablePanel.addCornerHeader(HEADER_COLUMN_GROUP1, HEADER_ROW, createHeaderPanel(new JLabel("Room"))); timeTablePanel.addCornerHeader(HEADER_COLUMN, HEADER_ROW, createHeaderPanel(new JLabel("Bed"))); fillNightCells(patientAdmissionSchedule); fillBedCells(patientAdmissionSchedule); fillBedDesignationCells(patientAdmissionSchedule); } private void fillNightCells(PatientAdmissionSchedule patientAdmissionSchedule) { for (Night night : patientAdmissionSchedule.getNightList()) { timeTablePanel.addColumnHeader(night, HEADER_ROW, createHeaderPanel(new JLabel(night.getLabel(), SwingConstants.CENTER))); } } private void fillBedCells(PatientAdmissionSchedule patientAdmissionSchedule) { timeTablePanel.addRowHeader(HEADER_COLUMN_GROUP2, null, HEADER_COLUMN, null, createHeaderPanel(new JLabel("Unassigned"))); for (Department department : patientAdmissionSchedule.getDepartmentList()) { List<Room> roomList = department.getRoomList(); List<Bed> firstRoomBedList = roomList.get(0).getBedList(); List<Bed> lastRoomBedList = roomList.get(roomList.size() - 1).getBedList(); timeTablePanel.addRowHeader(HEADER_COLUMN_GROUP2, firstRoomBedList.get(0), HEADER_COLUMN_GROUP2, lastRoomBedList.get(lastRoomBedList.size() - 1), createHeaderPanel(new JLabel(department.getLabel()))); for (Room room : roomList) { List<Bed> bedList = room.getBedList(); JLabel roomLabel = new JLabel(room.getLabel(), new PatientOrRoomIcon(room), SwingConstants.RIGHT); timeTablePanel.addRowHeader(HEADER_COLUMN_GROUP1, bedList.get(0), HEADER_COLUMN_GROUP1, bedList.get(bedList.size() - 1), createHeaderPanel(roomLabel)); for (Bed bed : bedList) { timeTablePanel.addRowHeader(HEADER_COLUMN, bed, createHeaderPanel(new JLabel(bed.getLabelInRoom(), SwingConstants.RIGHT))); } } } } private void fillBedDesignationCells(PatientAdmissionSchedule patientAdmissionSchedule) { for (BedDesignation bedDesignation : patientAdmissionSchedule.getBedDesignationList()) { JButton button = SwingUtils.makeSmallButton(new JButton(new BedDesignationAction(bedDesignation))); AdmissionPart admissionPart = bedDesignation.getAdmissionPart(); timeTablePanel.addCell(admissionPart.getFirstNight(), bedDesignation.getBed(), admissionPart.getLastNight(), bedDesignation.getBed(), button); } } private JPanel createHeaderPanel(JLabel label) { JPanel headerPanel = new JPanel(new BorderLayout()); headerPanel.add(label, BorderLayout.NORTH); headerPanel.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(TangoColorFactory.ALUMINIUM_5), BorderFactory.createEmptyBorder(2, 2, 2, 2))); return headerPanel; } private class BedDesignationAction extends AbstractAction { private final BedDesignation bedDesignation; public BedDesignationAction(BedDesignation bedDesignation) { super(bedDesignation.getAdmissionPart().getPatient().getName(), new PatientOrRoomIcon(bedDesignation.getAdmissionPart().getPatient())); this.bedDesignation = bedDesignation; Patient patient = bedDesignation.getPatient(); // Tooltip putValue(SHORT_DESCRIPTION, "<html>Patient: " + patient.getName() + "<br/>" + "Gender: " + patient.getGender().getCode() + " (see icon)<br/>" + "Age: " + patient.getAge() + "<br/>" + "Preferred maximum room capacity: " + patient.getPreferredMaximumRoomCapacity() + "<br/>" + "Requires " + patient.getRequiredPatientEquipmentList().size() + " equipments (shown as rectangles)<br/>" + "Prefers " + patient.getPreferredPatientEquipmentList().size() + " equipments" + "</html>"); } @Override public void actionPerformed(ActionEvent e) { JPanel listFieldsPanel = new JPanel(new GridLayout(2, 1)); List<Bed> bedList = getSolution().getBedList(); // Add 1 to array size to add null, which makes the entity unassigned JComboBox bedListField = new JComboBox( bedList.toArray(new Object[bedList.size() + 1])); LabeledComboBoxRenderer.applyToComboBox(bedListField); bedListField.setSelectedItem(bedDesignation.getBed()); listFieldsPanel.add(bedListField); int result = JOptionPane.showConfirmDialog(PatientAdmissionSchedulePanel.this.getRootPane(), listFieldsPanel, "Select bed for " + bedDesignation.getAdmissionPart().getPatient().getName(), JOptionPane.OK_CANCEL_OPTION); if (result == JOptionPane.OK_OPTION) { Bed toBed = (Bed) bedListField.getSelectedItem(); solutionBusiness.doChangeMove(bedDesignation, "bed", toBed); solverAndPersistenceFrame.resetScreen(); } } } private class PatientOrRoomIcon implements Icon { private static final int EQUIPMENT_ICON_WIDTH = 8; private final ImageIcon genderIcon; private final List<Color> equipmentColorList; private PatientOrRoomIcon(Patient patient) { genderIcon = determinePatientGenderIcon(patient.getGender()); List<RequiredPatientEquipment> equipmentList = patient.getRequiredPatientEquipmentList(); equipmentColorList = new ArrayList<>(equipmentList.size()); for (RequiredPatientEquipment equipment : equipmentList) { equipmentColorList.add(equipmentTangoColorFactory.pickColor(equipment.getEquipment())); } } private PatientOrRoomIcon(Room room) { genderIcon = determineRoomGenderIcon(room.getGenderLimitation()); List<RoomEquipment> equipmentList = room.getRoomEquipmentList(); equipmentColorList = new ArrayList<>(equipmentList.size()); for (RoomEquipment equipment : equipmentList) { equipmentColorList.add(equipmentTangoColorFactory.pickColor(equipment.getEquipment())); } } @Override public int getIconWidth() { return genderIcon.getIconWidth() + equipmentColorList.size() * EQUIPMENT_ICON_WIDTH; } @Override public int getIconHeight() { return genderIcon.getIconHeight(); } @Override public void paintIcon(Component c, Graphics g, int x, int y) { genderIcon.paintIcon(c, g, x, y); int innerX = x + genderIcon.getIconWidth(); int equipmentIconHeight = genderIcon.getIconHeight(); for (int i = 0; i < equipmentColorList.size(); i++) { g.setColor(equipmentColorList.get(i)); g.fillRect(innerX + 1, y + 1, EQUIPMENT_ICON_WIDTH - 2, equipmentIconHeight - 2); g.setColor(TangoColorFactory.ALUMINIUM_5); g.drawRect(innerX + 1, y + 1, EQUIPMENT_ICON_WIDTH - 2, equipmentIconHeight - 2); innerX += EQUIPMENT_ICON_WIDTH; } } } private ImageIcon determineRoomGenderIcon(GenderLimitation genderLimitation) { switch (genderLimitation) { case ANY_GENDER: return anyGenderIcon; case MALE_ONLY: return maleIcon; case FEMALE_ONLY: return femaleIcon; case SAME_GENDER: return sameGenderIcon; default: throw new IllegalStateException("The genderLimitation (" + genderLimitation + ") is not implemented."); } } private ImageIcon determinePatientGenderIcon(Gender gender) { switch (gender) { case MALE: return maleIcon; case FEMALE: return femaleIcon; default: throw new IllegalStateException("The gender (" + gender + ") is not implemented."); } } }