Newer
Older
marking / src / main / java / ui / CriterionPanel.java
package ui;

import dao.MarkingProperties;
import java.awt.Dimension;
import java.awt.Font;
import model.CriterionEditor;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import model.Criterion;

/**
 *
 * @author Mark George <mark.george@otago.ac.nz>
 */
public class CriterionPanel extends JPanel implements CriterionEditor {

	private RadioGroup radioGroup = null;
	private final JTextArea txtComment;
	private final Criterion criterion;
	private final JButton clearBtn;
	private JTextField txtNumeric = null;

	private static Dimension selectedSize;

	@SuppressWarnings("Convert2Lambda")
	public CriterionPanel(Criterion criterion) {
		this.criterion = criterion;

		this.setLayout(new GridBagLayout());

		// label is left aligned and full width
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.gridx = 0;
		gbc.gridy = 0;
		gbc.gridwidth = 3;
		gbc.weightx = 1;
		gbc.fill = GridBagConstraints.HORIZONTAL;
		gbc.insets = new Insets(0, 0, 5, 0);

		this.add(new JLabel(criterion.getDescription()), gbc);

		// clear mark buttonleft aligned, but doesn't grow
		gbc = new GridBagConstraints();
		gbc.gridx = 0;
		gbc.gridy = 1;
		clearBtn = new JButton("X");

		this.add(clearBtn, gbc);

		// mark component is left aligned, but doesn't grow
		gbc = new GridBagConstraints();
		gbc.gridx = 1;
		gbc.gridy = 1;

		if (criterion.isScale()) {
			radioGroup = new RadioGroup(criterion.getMin(), criterion.getMax());
			this.add(radioGroup, gbc);
		} else {
			gbc.insets.left = 20;
			gbc.insets.right = 20;
			gbc.fill = GridBagConstraints.HORIZONTAL;
			txtNumeric = new JTextField();
			txtNumeric.setToolTipText("Out of " + criterion.getMax());
			this.add(txtNumeric, gbc);
		}

		// text area is right aligned, but grows in both directions
		gbc = new GridBagConstraints();
		gbc.gridx = 2;
		gbc.gridy = 1;
		gbc.weightx = 1;
		gbc.weighty = 1;
		gbc.fill = GridBagConstraints.BOTH;

		txtComment = new JTextArea();
		txtComment.setWrapStyleWord(true);
		txtComment.setLineWrap(true);

		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setViewportView(txtComment);
		scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

		// Dimensions are necessary to prevent the semi-famous "text area grows infinitely" bug.
		// Unfortunately this also means we can't let the layout manager figure out the correct
		// size dynamically...
		txtComment.setMinimumSize(new Dimension(10, 20));
		txtComment.setPreferredSize(new Dimension(10, 100));  // need to hardcode height which is annoying...
		scrollPane.setPreferredSize(new Dimension(10, 20));

		txtComment.addFocusListener(new FocusAdapter() {

			@Override
			public void focusGained(FocusEvent fe) {
				super.focusGained(fe);
				CriterionPanel selectedPanel = CriterionPanel.this;
				MarkingFrame frame = (MarkingFrame) SwingUtilities.getWindowAncestor(selectedPanel);
				if (frame.getExpandable()) {
					selectedPanel.setPreferredSize(new Dimension(10, 150));
					selectedPanel.invalidate();
					SwingUtilities.getWindowAncestor(selectedPanel).pack();
				}
			}

			@Override
			public void focusLost(FocusEvent fe) {
				super.focusLost(fe);
				CriterionPanel selectedPanel = CriterionPanel.this;
				MarkingFrame frame = (MarkingFrame) SwingUtilities.getWindowAncestor(selectedPanel);
				if (frame.getExpandable()) {
					selectedPanel.setPreferredSize(selectedSize);
					selectedPanel.invalidate();
					SwingUtilities.getWindowAncestor(selectedPanel).pack();
				}
			}

		});

		clearBtn.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent ae) {
				if (criterion.isScale()) {
					radioGroup.clearSelection();
				} else {
					txtNumeric.setText("");
				}
			}
		});

		Font font = txtComment.getFont();
		Font biggerFont = font.deriveFont((float)MarkingProperties.textAreaFontSize());
		txtComment.setFont(biggerFont);

		this.add(scrollPane, gbc);
	}

	@Override
	public String getComment() {
		return txtComment.getText().trim();
	}

	@Override
	// TODO: switch to BigDeciamal
	public Double getResult() {
		if (criterion.isScale()) {
			Integer result = radioGroup.getSelected();
			return result != null ? Double.valueOf(result) : null;
		} else {
			try {
				String val = txtNumeric.getText();
				if (val.isBlank()) {
					// mark not entered, so return null
					return null;
				}
				Double result = Double.valueOf(val);
				if (result < criterion.getMin() || result > criterion.getMax()) {
					JOptionPane.showMessageDialog(null, txtNumeric.getText() + " is not within the exected range.\n\nMinimum: " + criterion.getMin() + "\nMaximum: " + criterion.getMax(), "Invalid Number", JOptionPane.ERROR_MESSAGE);
					throw new RuntimeException("Number is not within expected range");
				}
				return Double.valueOf(txtNumeric.getText());
			} catch (NumberFormatException ex) {
				JOptionPane.showMessageDialog(null, txtNumeric.getText() + " is not a valid number", "Invalid Number", JOptionPane.ERROR_MESSAGE);
				throw new RuntimeException(ex.getLocalizedMessage());
			}

		}
	}

	@Override
	public boolean isComplete() {
		return getResult() != null;
	}

	@Override
	public void setComment(String comment) {
		txtComment.setText(comment);

		// scroll to top
		txtComment.setCaretPosition(0);
	}

	@Override
	public void setResult(Double result) {
		if (criterion.isScale()) {
			radioGroup.setSelected(result.intValue());
		} else {
			txtNumeric.setText(String.valueOf(result));
		}
	}

	public void setZero() {
		setResult(0.0);
	}

	public void setMax() {
		setResult(Double.valueOf(criterion.getMax()));
	}

	public Criterion getCriterion() {
		return criterion;
	}

}