package uk.co.bytemark.vm.enigma.inquisition.gui.editor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JDialog;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableModel;
import uk.co.bytemark.vm.enigma.inquisition.gui.screens.editor.AbstractMultipleChoiceQuestionEditorPanel;
import uk.co.bytemark.vm.enigma.inquisition.questions.AbstractQuestion;
import uk.co.bytemark.vm.enigma.inquisition.questions.MultipleChoiceQuestion;
import uk.co.bytemark.vm.enigma.inquisition.questions.MultipleChoiceRenderingHelper;
import uk.co.bytemark.vm.enigma.inquisition.questions.Option;
import uk.co.bytemark.vm.enigma.inquisition.questions.QuestionRenderingHelper;
public class MultipleChoiceQuestionEditorPanel extends AbstractMultipleChoiceQuestionEditorPanel implements
IQuestionEditorPanel {
private final DefaultTableModel tableModel;
private final DefaultListSelectionModel selectionModel;
private QuestionValidityListener questionValidityListener;
public MultipleChoiceQuestionEditorPanel(MultipleChoiceQuestion question) {
questionTextArea.setText(question.getQuestionText());
questionTextArea.setCaretPosition(0);
explanationTextArea.setText(question.getExplanationText());
explanationTextArea.setCaretPosition(0);
shufflableCheckBox.setSelected(question.isShufflable());
singleOptionModeCheckBox.setSelected(question.isSingleOptionMode());
tableModel = makeOptionsTableModel(question);
optionsTable.setModel(tableModel);
optionsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
optionsTable.setRowSelectionAllowed(true);
selectionModel = (DefaultListSelectionModel) optionsTable.getSelectionModel();
deleteButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
int deleteIndex = selectionModel.getMinSelectionIndex();
tableModel.removeRow(deleteIndex);
selectNearestQuestion(deleteIndex);
updateOkButtonEnabledState();
}
});
addButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
tableModel.addRow(new Object[] { "option", false });
updateOkButtonEnabledState();
}
});
moveUpButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
moveSelectedItem(-1);
}
});
moveDownButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
moveSelectedItem(1);
}
});
selectionModel.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
int index = selectionModel.getMinSelectionIndex();
if (index >= 0) {
deleteButton.setEnabled(true);
moveUpButton.setEnabled(index != 0);
moveDownButton.setEnabled(index != optionsTable.getRowCount() - 1);
} else {
deleteButton.setEnabled(false);
moveUpButton.setEnabled(false);
moveDownButton.setEnabled(false);
}
}
});
previewQuestionButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String rawQuestionText = questionTextArea.getText();
String questionTextHtml = QuestionRenderingHelper.syntaxHighlightForJava(AbstractQuestion
.removeCopyToExplanationBlocks(rawQuestionText));
launchPreviewDialog(questionTextHtml);
}
});
previewExplanationButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String rawQuestionText = questionTextArea.getText();
String rawExplanationText = explanationTextArea.getText();
String explanationTextHtml = MultipleChoiceRenderingHelper.getExplanationText(rawQuestionText,
rawExplanationText, getOptions());
launchPreviewDialog(explanationTextHtml);
}
});
singleOptionModeCheckBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateOkButtonEnabledState();
}
});
tableModel.addTableModelListener(new TableModelListener() {
public void tableChanged(TableModelEvent e) {
updateOkButtonEnabledState();
}
});
}
private void updateOkButtonEnabledState() {
List<Option> options = getOptions();
int correctCount = 0;
for (Option option : options)
if (option.isCorrect())
correctCount++;
boolean singleOptionMode = singleOptionModeCheckBox.isSelected();
boolean validQuestion = false;
if (correctCount == 0)
validQuestion = false;
else if (correctCount == 1)
validQuestion = true;
else if (correctCount > 1)
validQuestion = !singleOptionMode;
questionValidityListener.questionValid(validQuestion);
}
private void selectNearestQuestion(int index) {
if (tableModel.getRowCount() > 0) {
int newIndex = Math.max(0, index - 1);
selectionModel.setSelectionInterval(newIndex, newIndex);
}
}
private JDialog getParentDialog() { // TODO: Panel shouldn't really know where it's placed
return (JDialog) SwingUtilities.getAncestorOfClass(JDialog.class, this);
}
private void moveSelectedItem(int offset) {
int index = selectionModel.getMinSelectionIndex();
tableModel.moveRow(index, index, index + offset);
selectionModel.setSelectionInterval(index + offset, index + offset);
}
private DefaultTableModel makeOptionsTableModel(MultipleChoiceQuestion question) {
List<Option> options = question.getOptions();
Object[][] tableRows = new Object[options.size()][];
for (int i = 0; i < options.size(); i++) {
Option option = options.get(i);
Object[] row = new Object[] { option.getOptionText(), option.isCorrect() };
tableRows[i] = row;
}
DefaultTableModel model = new DefaultTableModel(tableRows, new String[] { "Option label", "True or false" }) {
Class<?>[] types = new Class<?>[] { String.class, Boolean.class };
@Override
public Class<?> getColumnClass(int columnIndex) {
return types[columnIndex];
}
};
return model;
}
public List<Option> getOptions() {
MultipleChoiceQuestion.Builder questionBuilder = new MultipleChoiceQuestion.Builder();
for (int row = 0; row < tableModel.getRowCount(); row++) {
String optionText = (String) tableModel.getValueAt(row, 0);
Boolean outputFromModel = (Boolean) tableModel.getValueAt(row, 1);
boolean optionCorrect = outputFromModel == null ? false : outputFromModel.booleanValue();
questionBuilder.option(optionText, optionCorrect);
}
return questionBuilder.getOptions();
}
public MultipleChoiceQuestion getQuestion() {
MultipleChoiceQuestion.Builder questionBuilder = new MultipleChoiceQuestion.Builder();
questionBuilder.options(getOptions());
questionBuilder.questionText(questionTextArea.getText()).explanationText(explanationTextArea.getText());
questionBuilder.shufflable(shufflableCheckBox.isSelected()).singleOptionMode(
singleOptionModeCheckBox.isSelected());
return questionBuilder.build();
}
private void launchPreviewDialog(String html) {
PreviewHtmlDialog dialog = new PreviewHtmlDialog(getParentDialog(), html);
dialog.setSize(PreviewHtmlDialog.PREVIEW_HTML_DIALOG_DEFAULT_DIMENSION);
dialog.setLocationRelativeTo(getParentDialog());
dialog.setVisible(true);
}
public void setQuestionValidityListener(QuestionValidityListener listener) {
this.questionValidityListener = listener;
updateOkButtonEnabledState();
}
}