/* * Copyright 2016 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.taskassigning.swingui; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; 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.JViewport; import javax.swing.Scrollable; import javax.swing.SwingConstants; import org.optaplanner.examples.common.swingui.SolutionPanel; import org.optaplanner.examples.common.swingui.components.LabeledComboBoxRenderer; import org.optaplanner.examples.taskassigning.domain.Employee; import org.optaplanner.examples.taskassigning.domain.Skill; import org.optaplanner.examples.taskassigning.domain.Task; import org.optaplanner.examples.taskassigning.domain.TaskAssigningSolution; import org.optaplanner.examples.taskassigning.domain.TaskOrEmployee; import org.optaplanner.swing.impl.SwingUtils; import org.optaplanner.swing.impl.TangoColorFactory; public class TaskOverviewPanel extends JPanel implements Scrollable { public static final int HEADER_ROW_HEIGHT = 50; public static final int HEADER_COLUMN_WIDTH = 150; public static final int ROW_HEIGHT = 50; public static final int TIME_COLUMN_WIDTH = 60; private final TaskAssigningPanel taskAssigningPanel; private final ImageIcon[] affinityIcons; private final ImageIcon[] priorityIcons; private TangoColorFactory skillColorFactory; private int consumedDuration = 0; public TaskOverviewPanel(TaskAssigningPanel taskAssigningPanel) { this.taskAssigningPanel = taskAssigningPanel; affinityIcons = new ImageIcon[] { new ImageIcon(getClass().getResource("affinityNone.png")), new ImageIcon(getClass().getResource("affinityLow.png")), new ImageIcon(getClass().getResource("affinityMedium.png")), new ImageIcon(getClass().getResource("affinityHigh.png")) }; priorityIcons = new ImageIcon[] { new ImageIcon(getClass().getResource("priorityMinor.png")), new ImageIcon(getClass().getResource("priorityMajor.png")), new ImageIcon(getClass().getResource("priorityCritical.png")) }; setLayout(null); setMinimumSize(new Dimension(HEADER_COLUMN_WIDTH * 2, ROW_HEIGHT * 8)); } public void resetPanel(TaskAssigningSolution taskAssigningSolution) { removeAll(); skillColorFactory = new TangoColorFactory(); List<Employee> employeeList = taskAssigningSolution.getEmployeeList(); Map<Employee, Integer> employeeIndexMap = new HashMap<>(employeeList.size()); int employeeIndex = 0; for (Employee employee : employeeList) { JLabel employeeLabel = new JLabel(employee.getLabel(), new TaskOrEmployeeIcon(employee), SwingConstants.LEFT); employeeLabel.setOpaque(true); employeeLabel.setToolTipText(employee.getToolText()); employeeLabel.setLocation(0, HEADER_ROW_HEIGHT + employeeIndex * ROW_HEIGHT); employeeLabel.setSize(HEADER_COLUMN_WIDTH, ROW_HEIGHT); employeeLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); add(employeeLabel); employeeIndexMap.put(employee, employeeIndex); employeeIndex++; } int panelWidth = HEADER_COLUMN_WIDTH; int unassignedIndex = employeeList.size(); for (Task task : taskAssigningSolution.getTaskList()) { JButton taskButton = createTaskButton(task); int x; int y; if (task.getEmployee() != null) { x = HEADER_COLUMN_WIDTH + task.getStartTime(); y = HEADER_ROW_HEIGHT + employeeIndexMap.get(task.getEmployee()) * ROW_HEIGHT; } else { x = HEADER_COLUMN_WIDTH + task.getReadyTime(); y = HEADER_ROW_HEIGHT + unassignedIndex * ROW_HEIGHT; unassignedIndex++; } if (x + taskButton.getWidth() > panelWidth) { panelWidth = x + taskButton.getWidth(); } taskButton.setLocation(x, y); add(taskButton); } for (int x = HEADER_COLUMN_WIDTH; x < panelWidth; x += TIME_COLUMN_WIDTH) { // Use 10 hours per day int minutes = (x - HEADER_COLUMN_WIDTH) % (10 * 60); // Start at 8:00 int hours = 8 + (minutes / 60); minutes %= 60; JLabel timeLabel = new JLabel((hours < 10 ? "0" : "") + hours + ":" + (minutes < 10 ? "0" : "") + minutes); timeLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); timeLabel.setLocation(x, 0); timeLabel.setSize(TIME_COLUMN_WIDTH, ROW_HEIGHT); add(timeLabel); } if ((panelWidth - HEADER_COLUMN_WIDTH) % TIME_COLUMN_WIDTH != 0) { panelWidth = panelWidth - ((panelWidth - HEADER_COLUMN_WIDTH) % TIME_COLUMN_WIDTH) + TIME_COLUMN_WIDTH; } Dimension size = new Dimension(panelWidth, HEADER_ROW_HEIGHT + unassignedIndex * ROW_HEIGHT); setSize(size); setPreferredSize(size); repaint(); } public void setConsumedDuration(int consumedDuration) { this.consumedDuration = consumedDuration; repaint(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); g.setColor(TangoColorFactory.ALUMINIUM_2); int lineX = HEADER_COLUMN_WIDTH + consumedDuration; g.fillRect(HEADER_COLUMN_WIDTH, 0, lineX, getHeight()); g.setColor(Color.WHITE); g.fillRect(lineX, 0, getWidth(), getHeight()); } private JButton createTaskButton(Task task) { JButton taskButton = SwingUtils.makeSmallButton(new JButton(new TaskAction(task))); taskButton.setBackground(task.isLocked() ? TangoColorFactory.ALUMINIUM_3 : TangoColorFactory.ALUMINIUM_1); taskButton.setHorizontalTextPosition(SwingConstants.CENTER); taskButton.setVerticalTextPosition(SwingConstants.TOP); taskButton.setSize(task.getDuration(), ROW_HEIGHT); return taskButton; } private class TaskAction extends AbstractAction { private final Task task; public TaskAction(Task task) { super(task.getCode(), new TaskOrEmployeeIcon(task)); this.task = task; // Tooltip putValue(SHORT_DESCRIPTION, task.getToolText()); } @Override public void actionPerformed(ActionEvent e) { JPanel listFieldsPanel = new JPanel(new GridLayout(2, 1)); List<TaskOrEmployee> taskOrEmployeeList = new ArrayList<>(); taskOrEmployeeList.addAll(taskAssigningPanel.getSolution().getEmployeeList()); taskOrEmployeeList.addAll(taskAssigningPanel.getSolution().getTaskList()); // Add 1 to array size to add null, which makes the entity unassigned JComboBox TaskOrEmployeeListField = new JComboBox( taskOrEmployeeList.toArray(new Object[taskOrEmployeeList.size() + 1])); LabeledComboBoxRenderer.applyToComboBox(TaskOrEmployeeListField); TaskOrEmployeeListField.setSelectedItem(task.getPreviousTaskOrEmployee()); listFieldsPanel.add(TaskOrEmployeeListField); int result = JOptionPane.showConfirmDialog(TaskOverviewPanel.this.getRootPane(), listFieldsPanel, "Select previous task or employee for " + task.getLabel(), JOptionPane.OK_CANCEL_OPTION); if (result == JOptionPane.OK_OPTION) { TaskOrEmployee toTaskOrEmployee = (TaskOrEmployee) TaskOrEmployeeListField.getSelectedItem(); taskAssigningPanel.getSolutionBusiness().doChangeMove(task, "previousTaskOrEmployee", toTaskOrEmployee); taskAssigningPanel.getSolverAndPersistenceFrame().resetScreen(); } } } @Override public Dimension getPreferredScrollableViewportSize() { return SolutionPanel.PREFERRED_SCROLLABLE_VIEWPORT_SIZE; } @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 20; } @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return 20; } @Override public boolean getScrollableTracksViewportWidth() { if (getParent() instanceof JViewport) { return (((JViewport) getParent()).getWidth() > getPreferredSize().width); } return false; } @Override public boolean getScrollableTracksViewportHeight() { if (getParent() instanceof JViewport) { return (((JViewport) getParent()).getHeight() > getPreferredSize().height); } return false; } private class TaskOrEmployeeIcon implements Icon { private static final int SKILL_ICON_WIDTH = 8; private static final int SKILL_ICON_HEIGHT = 16; private final ImageIcon priorityIcon; private final List<Color> skillColorList; private final ImageIcon affinityIcon; private TaskOrEmployeeIcon(Task task) { priorityIcon = priorityIcons[task.getPriority().ordinal()]; List<Skill> skillList = task.getTaskType().getRequiredSkillList(); skillColorList = new ArrayList<>(skillList.size()); for (Skill skill : skillList) { skillColorList.add(skillColorFactory.pickColor(skill)); } affinityIcon = affinityIcons[task.getAffinity().ordinal()]; } private TaskOrEmployeeIcon(Employee employee) { priorityIcon = null; Set<Skill> skillSet = employee.getSkillSet(); skillColorList = new ArrayList<>(skillSet.size()); for (Skill skill : skillSet) { skillColorList.add(skillColorFactory.pickColor(skill)); } affinityIcon = null; } @Override public int getIconWidth() { int width = 0; if (priorityIcon != null) { width += priorityIcon.getIconWidth(); } width += skillColorList.size() * SKILL_ICON_WIDTH; if (affinityIcon != null) { width += affinityIcon.getIconWidth(); } return width; } @Override public int getIconHeight() { int height = SKILL_ICON_HEIGHT; if (priorityIcon != null && priorityIcon.getIconHeight() > height) { height = priorityIcon.getIconHeight(); } if (affinityIcon != null && affinityIcon.getIconHeight() > height) { height = affinityIcon.getIconHeight(); } return height; } @Override public void paintIcon(Component c, Graphics g, int x, int y) { int innerX = x; if (priorityIcon != null) { priorityIcon.paintIcon(c, g, innerX, y); innerX += priorityIcon.getIconWidth(); } for (Color skillColor : skillColorList) { g.setColor(skillColor); g.fillRect(innerX + 1, y + 1, SKILL_ICON_WIDTH - 2, SKILL_ICON_HEIGHT - 2); g.setColor(TangoColorFactory.ALUMINIUM_5); g.drawRect(innerX + 1, y + 1, SKILL_ICON_WIDTH - 2, SKILL_ICON_HEIGHT - 2); innerX += SKILL_ICON_WIDTH; } if (affinityIcon != null) { affinityIcon.paintIcon(c, g, innerX, y); innerX += affinityIcon.getIconWidth(); } } } }