/*
* TestChooserFrame.java
*
* Created on 03 September 2006, 21:21
*/
package uk.co.bytemark.vm.enigma.inquisition.gui.quizchooser;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidClassException;
import java.io.ObjectInputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.Collection;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JOptionPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.TreePath;
import uk.co.bytemark.vm.enigma.inquisition.gui.editor.QuestionEditor;
import uk.co.bytemark.vm.enigma.inquisition.gui.images.Icons;
import uk.co.bytemark.vm.enigma.inquisition.gui.misc.LookAndFeelMenu;
import uk.co.bytemark.vm.enigma.inquisition.gui.misc.QuizConfig;
import uk.co.bytemark.vm.enigma.inquisition.gui.quiz.QuizFrame;
import uk.co.bytemark.vm.enigma.inquisition.gui.quiz.ResizeListener;
import uk.co.bytemark.vm.enigma.inquisition.gui.screens.AboutDialog;
import uk.co.bytemark.vm.enigma.inquisition.gui.screens.AbstractQuestionSetSelectorFrame;
import uk.co.bytemark.vm.enigma.inquisition.misc.Constants;
import uk.co.bytemark.vm.enigma.inquisition.questions.DragAndDropQuestion;
import uk.co.bytemark.vm.enigma.inquisition.questions.MultipleChoiceQuestion;
import uk.co.bytemark.vm.enigma.inquisition.questions.Question;
import uk.co.bytemark.vm.enigma.inquisition.questions.QuestionSet;
import uk.co.bytemark.vm.enigma.inquisition.questions.QuestionSetManager;
import uk.co.bytemark.vm.enigma.inquisition.quiz.QuizState;
public class QuestionSetSelectorFrame extends AbstractQuestionSetSelectorFrame {
private static final Logger LOGGER = Logger.getLogger(QuestionSetSelectorFrame.class.getName());
private final ReturnCallback callback = new ReturnCallback() {
public void doReturn() {
setVisible(true);
beginQuizButton.setEnabled(true);
}
};
public QuestionSetSelectorFrame(Collection<QuestionSet> initialQuestionSets) {
createComponents();
if (Icons.FAVICON.isAvailable())
setIconImage(Icons.FAVICON.getImage());
relayoutContinuallyOnWindowResize();
addComponentListener(new ResizeListener());
timeSlider.setValue(0);
disableEditor();
setEnabledStateOfQuestionSetInfoComponents(false);
initialiseQuestionSetTree(initialQuestionSets);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
private void initialiseQuestionSetTree(Collection<QuestionSet> initialQuestionSets) {
final QuestionSetTree tree = getQuestionSetTree();
tree.initialise(initialQuestionSets);
MouseListener ml = new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
boolean doubleClicked = e.getClickCount() == 2;
if (doubleClicked && tree.isAQuestionSetSelected()) {
beginQuiz();
}
}
};
tree.addMouseListener(ml);
}
private void disableEditor() {
// toolsMenu.setVisible(false);
// editorMenuItem.setVisible(false);
editQuizButton.setVisible(false); // TODO: Remove this
}
private void relayoutContinuallyOnWindowResize() {
Toolkit.getDefaultToolkit().setDynamicLayout(true);
}
@Override
protected JTree createQuestionSetTree() {
return new QuestionSetTree();
}
@Override
protected void addQuizFromWebMenuItemActionPerformed() {
addQuizFromWebButtonActionPerformed();
}
@Override
protected void aboutMenuItemActionPerformed() {
JDialog aboutDialog = new AboutDialog(this, Constants.getAboutText());
aboutDialog.setLocationRelativeTo(this);
aboutDialog.setVisible(true);
}
@Override
protected void editTestButtonClicked() {
editorMenuItemSelected();
}
@Override
protected void editorMenuItemSelected() {
// QuestionSet set = getQuestionSetTree().getSelectedQuestionSet();
// setVisible(false);
// if (set != null)
// new TestEditor(set, callback).setVisible(true);
// else
// new TestEditor(callback).setVisible(true);
QuestionEditor questionEditor = new QuestionEditor();
questionEditor.pack();
questionEditor.setLocationRelativeTo(this);
questionEditor.setVisible(true);
}
private QuestionSetTree getQuestionSetTree() {
return (QuestionSetTree) questionSetTree;
}
@Override
protected void addTestFromFileMenuItemActionPerformed() {
addQuizFromFileButtonActionPerformed();
}
@Override
protected void helpMenuActionPerformed() {
// TODO add your handling code here:
}
@Override
protected void addQuizFromWebButtonActionPerformed() {
WebPageFileChooserDialog dialog = new WebPageFileChooserDialog(this, Constants.REPOSITORY_URL);
dialog.setVisible(true);
}
@Override
protected void addQuizFromFileButtonActionPerformed() {
JFileChooser fileChooser = new JFileChooser();
fileChooser.setDialogTitle("Open Questions File");
SuffixFileFilter.setSoleFileFilter(fileChooser, Constants.QUESTION_SET_FILE_FILTER);
if (fileChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
return;
String fileName = fileChooser.getSelectedFile().getAbsolutePath();
LOGGER.info("Importing question set from file " + fileName);
InputStream stream = null;
try {
QuestionSet questionSet;
stream = new FileInputStream(fileName);
questionSet = QuestionSetManager.getQuestionSetFromInputStream(stream);
addQuestionSet(questionSet);
} catch (ParseException e) {
LOGGER.log(Level.INFO, "Could not parse file " + fileName, e);
JOptionPane.showMessageDialog(this, "Could not parse " + fileName, "Could not parse file",
JOptionPane.ERROR_MESSAGE);
} catch (IOException e) {
LOGGER.log(Level.INFO, "Could not open file " + fileName, e);
JOptionPane.showMessageDialog(this, "Could not open " + fileName, "Could not open file",
JOptionPane.ERROR_MESSAGE);
} finally {
try {
if (stream != null)
stream.close();
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Could not close stream", e);
}
}
}
private void addQuestionSet(QuestionSet questionSet) {
TreePath path = getQuestionSetTree().addQuestionSetToTree(questionSet);
getQuestionSetTree().setSelectionPath(path);
}
@Override
protected void beginQuizButtonActionPerformed() {
beginQuiz();
}
void beginQuiz() {
beginQuizButton.setEnabled(false); // Make sure it can't be clicked again before we're done
// TODO: (Why do we need this?)
QuizConfig quizConfig = getQuizConfig();
QuestionSet questionSet = getQuestionSetTree().getSelectedQuestionSet();
QuizState quizState = new QuizState(questionSet, quizConfig);
beginQuiz(quizState);
}
private void beginQuiz(QuizState quizState) {
QuizFrame quizFrame = new QuizFrame(quizState, callback);
quizFrame.setLocationRelativeTo(this);
this.setVisible(false);
quizFrame.setVisible(true);
}
private QuizConfig getQuizConfig() {
boolean shuffleQuestionOrder = shuffleCheckBox.isSelected();
boolean stateNumberOfOptionsNeeded = stateOptionsNumberCheckBox.isSelected();
boolean useTimer = useTimerCheckBox.isSelected();
int time = 60 * getTimeSliderTime();
if (useTimer)
return QuizConfig.createWithTimer(shuffleQuestionOrder, stateNumberOfOptionsNeeded, time);
else
return QuizConfig.createWithoutTimer(shuffleQuestionOrder, stateNumberOfOptionsNeeded);
}
@Override
protected void exitMenuItemActionPerformed() {
LOGGER.info("Closing QuestionSetSelectorFrame");
dispose();
}
void addWebExam(String urlString) {
LOGGER.info("Importing exam from web: " + urlString);
URL url;
try {
url = new URL(urlString);
} catch (MalformedURLException e) {
LOGGER.log(Level.WARNING, "Invalid URL when opening web exam: " + urlString, e);
return;
}
try {
QuestionSet questionSet = QuestionSetManager.getQuestionSetFromInputStream(url.openStream());
addQuestionSet(questionSet);
} catch (ParseException e) {
JOptionPane.showMessageDialog(this, "Could not parse file from URL: " + url, "Could not open file",
JOptionPane.ERROR_MESSAGE);
LOGGER.log(Level.WARNING, "Could not parse file at " + url, e);
} catch (IOException e) {
JOptionPane.showMessageDialog(this, "Could not open file from URL: " + url, "Could not open file",
JOptionPane.ERROR_MESSAGE);
LOGGER.log(Level.WARNING, "Could not open file at " + url, e);
}
}
@Override
protected JMenu createLAFMenu() {
return new LookAndFeelMenu(this);
}
@Override
protected void questionSetTreeValueChanged() {
if (getQuestionSetTree().isAQuestionSetSelected()) {
setEnabledStateOfQuestionSetInfoComponents(true);
QuestionSet questionSet = getQuestionSetTree().getSelectedQuestionSet();
int numberOfDragAndDrop = questionSet.numberOfType(DragAndDropQuestion.class);
int numberOfMultipleChoice = questionSet.numberOfType(MultipleChoiceQuestion.class);
int numberOfQuestions = questionSet.numberOfQuestions();
String numberOfQuestionsInfo = numberOfQuestions + " (" + numberOfMultipleChoice + " multiple choice, "
+ numberOfDragAndDrop + " drag and drop)";
numberOfQuestionsText.setText(numberOfQuestionsInfo);
descriptionText.setText(questionSet.getDescription());
descriptionText.setCaretPosition(0);
int recommendedTime = questionSet.getRecommendedTimeForAllQuestions();
setTimeSliderValue(recommendedTime);
} else {
setEnabledStateOfQuestionSetInfoComponents(false);
numberOfQuestionsText.setText("");
descriptionText.setText("");
}
}
private void setEnabledStateOfQuestionSetInfoComponents(boolean enabled) {
numberOfQuestionsText.setEnabled(enabled);
descriptionText.setEnabled(enabled);
beginQuizButton.setEnabled(enabled);
editQuizButton.setEnabled(enabled);
jLabel1.setEnabled(enabled); // TODO: Get these renamed
jLabel3.setEnabled(enabled);
exportQuestionSetAsHtmlMenuItem.setEnabled(enabled);
// Options panel
timeSlider.setEnabled(enabled);
timeTextLabel.setEnabled(enabled);
shuffleCheckBox.setEnabled(enabled);
stateOptionsNumberCheckBox.setEnabled(enabled);
useTimerCheckBox.setEnabled(enabled);
}
@Override
protected void useTimerCheckBoxActionPerformed() {
boolean useTimer = useTimerCheckBox.isSelected();
timeSlider.setEnabled(useTimer);
timeTextLabel.setEnabled(useTimer);
}
// TODO: Refactor into a TimeSlider JComponent
@Override
protected void timeSliderStateChanged() {
int totalMinutes = getTimeSliderTime();
String timeText = getTimeText(totalMinutes);
timeTextLabel.setText(timeText);
}
private String getTimeText(int totalMinutes) {
int hours = totalMinutes / 60;
int extraMinutes = totalMinutes - hours * 60;
String minuteS = (extraMinutes == 1) ? "" : "s";
String hourS = (hours == 1) ? "" : "s";
String timeText;
if (hours == 0)
timeText = String.format("%d minute" + minuteS, extraMinutes);
else if (extraMinutes == 0)
timeText = String.format("%d hour" + hourS, hours);
else
timeText = String.format("%d hour" + hourS + " and %d minute" + minuteS, hours, extraMinutes);
return timeText;
}
private void setTimeSliderValue(int recommendedTime) {
int sliderPosition = (recommendedTime / 60) / 2 - 1;
timeSlider.setValue(sliderPosition);
}
private int getTimeSliderTime() {
return timeSlider.getValue() * 2 + 2;
}
public static void main(String[] args) {
QuestionSet mockQuestionSet = new QuestionSet("Mock Question Set", "Mock", 3, "mock", Collections
.<Question> emptyList());
final QuestionSetSelectorFrame quizSelectorFrame = new QuestionSetSelectorFrame(Collections
.singletonList(mockQuestionSet));
SwingUtilities.invokeLater(new Runnable() {
public void run() {
quizSelectorFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
quizSelectorFrame.setVisible(true);
}
});
}
@Override
protected void resumeQuizMenuItemActionPerformed() {
JFileChooser fileChooser = new JFileChooser();
SuffixFileFilter.setSoleFileFilter(fileChooser, Constants.QUIZ_SUFFIX_FILE_FILTER);
fileChooser.setDialogTitle("Resume Quiz");
if (fileChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION)
return;
String quizStateFileName = fileChooser.getSelectedFile().getAbsolutePath();
FileInputStream fileInputStream;
try {
fileInputStream = new FileInputStream(quizStateFileName);
ObjectInputStream ois = new ObjectInputStream(fileInputStream);
QuizState quizState = (QuizState) ois.readObject();
beginQuiz(quizState);
} catch (InvalidClassException e) {
LOGGER.log(Level.FINER, "Could not resume quiz at " + quizStateFileName, e);
String message = "Could not resume quiz '" + quizStateFileName
+ "': the quiz was saved with a different version of Inquisition.";
showCouldNotResumeQuizDialog(message);
} catch (Exception e) {
LOGGER.log(Level.FINER, "Could not resume quiz at " + quizStateFileName, e);
String message = "Could not resume quiz '" + quizStateFileName + "' due to error '" + e.getMessage() + "'";
showCouldNotResumeQuizDialog(message);
}
}
private void showCouldNotResumeQuizDialog(String message) {
JOptionPane pane = new MaxWidthOptionPane(100, message, JOptionPane.ERROR_MESSAGE);
JDialog dialog = pane.createDialog(this, "Could Not Resume Quiz");
dialog.setVisible(true);
}
@Override
protected void exportQuestionSetAsHtml() {
QuestionSet questionSet = getQuestionSetTree().getSelectedQuestionSet();
JDialog dialog = new RenderQuestionAsHtmlDialog(this, questionSet);
dialog.setLocationRelativeTo(this);
dialog.setVisible(true);
}
}