#include "SpacedRepetitionWindow.h" #include "CardsStatusBar.h" #include "NumberFrame.h" #include "WarningPanel.h" #include "../dictionary/Dictionary.h" #include "../dictionary/Card.h" #include "../dictionary/CardPack.h" SpacedRepetitionWindow::SpacedRepetitionWindow( SpacedRepetitionModel* aModel, QWidget* aParent ): IStudyWindow( aModel, tr("Spaced repetition"), aParent ), exactAnswerLabel(NULL), exactAnswerEdit(NULL) { usesExactAnswer = m_model->getCardPack()->getUsesExactAnswer(); studyType = AppModel::SpacedRepetition; if(usesExactAnswer) createExactAnswerWidget(); createUI(); // Can't call from parent constructor setWindowIcon( QIcon(":/images/spaced-rep.png") ); dayCardsLimitShown = false; showNextCard(); } SpacedRepetitionWindow::~SpacedRepetitionWindow() { WriteSettings(); } void SpacedRepetitionWindow::createExactAnswerWidget() { exactAnswerEdit = new QLineEdit; exactAnswerLabel = new CardSideView; exactAnswerLabel->setMaximumHeight(40); exactAnswerLabel->setShowMode(CardSideView::AnsMode); exactAnswerLabel->setPack(m_model->getCardPack()); } QVBoxLayout* SpacedRepetitionWindow::createLowerPanel() { warningPanel = new WarningPanel; controlLt = new QVBoxLayout; controlLt->addWidget(warningPanel); controlLt->addLayout(createProgressLayout()); controlLt->addLayout(new QVBoxLayout); // Grades layout createGradeButtons(); return controlLt; } QBoxLayout* SpacedRepetitionWindow::createProgressLayout() { QVBoxLayout* lt = new QVBoxLayout; lt->setSpacing(3); lt->addLayout(createStatsLayout()); lt->addLayout(createProgressBarLayout()); return lt; } QBoxLayout* SpacedRepetitionWindow::createStatsLayout() { todayNewLabel = new NumberFrame; todayNewLabel->setToolTip(tr("Today learned new cards")); todayNewLabel->setColor("#c0d6ff"); learningCardsLabel = new NumberFrame; learningCardsLabel->setToolTip(tr("Scheduled learning reviews:\n" "new cards must be repeated today to learn")); learningCardsLabel->setColor(CardsStatusBar::Colors[1]); timeToNextLearningLabel = new QLabel; timeToNextLearningLabel->setToolTip(tr("Time left to the next learning review")); scheduledCardsLabel = new NumberFrame; scheduledCardsLabel->setToolTip(tr("Scheduled cards for today")); scheduledCardsLabel->setColor(CardsStatusBar::Colors[2]); scheduledNewLabel = new NumberFrame; scheduledNewLabel->setToolTip(tr("New scheduled cards for today:\n" "new cards that will be shown between the scheduled ones")); scheduledNewLabel->setColor("#bd8d71"); QHBoxLayout* lt = new QHBoxLayout; lt->setSpacing(5); lt->addWidget(todayNewLabel); lt->addStretch(); lt->addWidget(learningCardsLabel); lt->addWidget(timeToNextLearningLabel); lt->addWidget(scheduledCardsLabel); lt->addWidget(scheduledNewLabel); return lt; } QBoxLayout* SpacedRepetitionWindow::createProgressBarLayout() { progressLabel = new QLabel; progressLabel->setToolTip(getProgressBarTooltip()); coloredProgressBar = new CardsStatusBar; coloredProgressBar->setToolTip(progressLabel->toolTip()); QHBoxLayout* lt = new QHBoxLayout; lt->setSpacing(5); lt->addWidget(progressLabel); lt->addWidget(coloredProgressBar); return lt; } QString SpacedRepetitionWindow::getProgressBarTooltip() { QString boxSpace; for(int i = 0; i < 6; i++) boxSpace += " "; QString colorBoxPattern = "

" + boxSpace + "  "; QString pEnd = "

"; QStringList legendLines = { tr("Reviewed cards"), tr("Learning reviews"), tr("Scheduled cards"), tr("New cards for today")}; QString legend; for(int i = 0; i < 4; i++) legend += colorBoxPattern.arg(CardsStatusBar::Colors[i]) + legendLines[i] + pEnd; return tr("Progress of reviews scheduled for today:") + legend; } void SpacedRepetitionWindow::createGradeButtons() { cardGradedSM = new QSignalMapper(this); connect( cardGradedSM, SIGNAL(mapped(int)), qobject_cast(m_model), SLOT(scheduleCard(int)) ); gradeBtns[0] = createGradeButton(0, "question.png", tr("Unknown"), tr("Completely forgotten card, couldn't recall the answer.")); gradeBtns[1] = createGradeButton(1, "red-stop.png", tr("Incorrect"), tr("The answer is incorrect.")); gradeBtns[2] = createGradeButton(2, "blue-triangle-down.png", tr("Difficult"), tr("It's difficult to recall the answer. The last interval was too long.")); gradeBtns[3] = createGradeButton(3, "green-tick.png", tr("Good"), tr("The answer is recalled in couple of seconds. The last interval was good enough.")); gradeBtns[4] = createGradeButton(4, "green-triangle-up.png", tr("Easy"), tr("The card is too easy, and recalled without any effort. The last interval was too short.")); goodBtn = gradeBtns[StudyRecord::Good - 1]; } QPushButton* SpacedRepetitionWindow::createGradeButton(int i, const QString& iconName, const QString& label, const QString& toolTip) { QPushButton* btn = new QPushButton; btn->setIcon(QIcon(":/images/" + iconName)); btn->setText(QString("%1 %2").arg(i + 1).arg(label)); btn->setToolTip("

" + toolTip); btn->setShortcut( QString::number(i + 1) ); btn->setMinimumWidth(GradeButtonMinWidth); #if defined(Q_OS_WIN) QFont font = btn->font(); font.setPointSize(12); font.setFamily("Calibri"); btn->setFont(font); #endif cardGradedSM->setMapping(btn, i + 1); connect(btn, SIGNAL(clicked()), cardGradedSM, SLOT(map())); return btn; } void SpacedRepetitionWindow::processState() { setEnabledGradeButtons(); switch(state) { case StateAnswerHidden: displayQuestion(); break; case StateAnswerVisible: displayAnswer(); break; case StateNoCards: updateCardsProgress(); displayNoRemainedCards(); break; } } void SpacedRepetitionWindow::setEnabledGradeButtons() { for(int i = 0; i < StudyRecord::GradesNum; i++) gradeBtns[i]->setEnabled(state == StateAnswerVisible); } void SpacedRepetitionWindow::displayQuestion() { questionLabel->setQuestion( m_model->getCurCard()->getQuestion() ); answerStackedLt->setCurrentIndex( AnsButtonPage ); updateCardsProgress(); layoutGradeButtons(); focusAnswerWidget(); m_answerTime.start(); processIsNewCard(); } void SpacedRepetitionWindow::layoutGradeButtons() { QHBoxLayout* higherLt = new QHBoxLayout; higherLt->addStretch(); putGradeButtonsIntoLayouts(higherLt); higherLt->addStretch(); replaceGradesLayout(higherLt); setGoodButtonText(); } void SpacedRepetitionWindow::putGradeButtonsIntoLayouts(QBoxLayout* higherLt) { QList visibleGrades = static_cast(m_model) ->getAvailableGrades(); for(int i = 0; i < StudyRecord::GradesNum; i++) { if(!visibleGrades.contains(i + 1)) { gradeBtns[i]->setParent(NULL); continue; } higherLt->addWidget(gradeBtns[i]); } } void SpacedRepetitionWindow::replaceGradesLayout(QBoxLayout* higherLt) { const int GradesLtIndex = 2; delete controlLt->takeAt(GradesLtIndex); controlLt->insertLayout(GradesLtIndex, higherLt); } void SpacedRepetitionWindow::setGoodButtonText() { QList visibleGrades = static_cast(m_model) ->getAvailableGrades(); QString goodLabel = visibleGrades.size() == StudyRecord::GradesNum? tr("Good") : tr("OK"); goodBtn->setText(QString("%1 %2").arg(StudyRecord::Good).arg(goodLabel)); goodBtn->setShortcut(QString::number(StudyRecord::Good)); } void SpacedRepetitionWindow::focusAnswerWidget() { if(usesExactAnswer) { exactAnswerEdit->clear(); exactAnswerEdit->setFocus(); } else answerBtn->setFocus(); } void SpacedRepetitionWindow::processIsNewCard() { questionLabel->showNewIcon(static_cast(m_model)->isNew()); } void SpacedRepetitionWindow::displayAnswer() { static_cast(m_model)->setRecallTime(m_answerTime.elapsed()); Card* card = m_model->getCurCard(); QStringList correctAnswers = card->getAnswers(); answerStackedLt->setCurrentIndex(AnsLabelPage); if(usesExactAnswer) showExactAnswer(correctAnswers); answerLabel->setQstAnsr(card->getQuestion(), correctAnswers); goodBtn->setFocus(); } void SpacedRepetitionWindow::showExactAnswer(const QStringList& correctAnswers) { if(static_cast(m_model)->isNew()) { exactAnswerLabel->hide(); return; } else exactAnswerLabel->show(); bool isCorrect = correctAnswers.first() == exactAnswerEdit->text().trimmed(); QString beginning(""; QString answer = beginning + exactAnswerEdit->text() + ""; exactAnswerLabel->setQstAnsr("", {answer}); } void SpacedRepetitionWindow::updateCardsProgress() { CardPack* pack = m_model->getCardPack(); int todayReviewed = pack->getTodayReviewedCardsNum(); int todayNewReviewed = pack->getTodayNewCardsNum(); int activeRepeating = pack->getActiveRepeatingCardsNum(); int learningReviews = pack->getLearningReviewsNum(); int timeToNextLearning = pack->getTimeToNextLearning(); int scheduledNew = static_cast(m_model)-> estimatedNewReviewedCardsToday(); int allTodaysCards = todayReviewed + learningReviews + activeRepeating + scheduledNew; todayNewLabel->setVisible(todayNewReviewed > 0); todayNewLabel->setValue(todayNewReviewed); learningCardsLabel->setValue(learningReviews); int minsToNextLearning = timeToNextLearning / 60; timeToNextLearningLabel->setVisible(minsToNextLearning > 0); timeToNextLearningLabel->setText(tr("(%1 min)"). arg(minsToNextLearning)); scheduledCardsLabel->setValue(activeRepeating); scheduledNewLabel->setValue(scheduledNew); progressLabel->setText(QString("%1/%2"). arg(todayReviewed).arg(allTodaysCards)); coloredProgressBar->setValues({todayReviewed, learningReviews, activeRepeating, scheduledNew}); showLimitWarnings(); } void SpacedRepetitionWindow::showLimitWarnings() { if(warningPanel->isVisible()) return; CardPack* pack = m_model->getCardPack(); if(dayLimitReached(pack->dictionary()->countTodaysAllCards())) { warningPanel->setText(tr("Day cards limit is reached: %1 cards.\n" "It is recommended to stop studying this dictionary.") .arg(StudySettings::inst()->cardsDayLimit)); warningPanel->show(); dayCardsLimitShown = true; } } bool SpacedRepetitionWindow::dayLimitReached(int todayReviewed) { return todayReviewed >= StudySettings::inst()->cardsDayLimit && !dayCardsLimitShown; } void SpacedRepetitionWindow::displayNoRemainedCards() { centralStackedLt->setCurrentIndex(MessagePage); messageLabel->setText( QString("

") + tr("All cards are reviewed") + "
" + "

" + tr("You can go to the next pack or dictionary, or open the Word drill.") + "

"); } void SpacedRepetitionWindow::ReadSettings() { QSettings settings; move( settings.value("spacedrep-pos", QPoint(PosX, PosY)).toPoint() ); resize( settings.value("spacedrep-size", QSize(Width, Height)).toSize() ); } void SpacedRepetitionWindow::WriteSettings() { QSettings settings; settings.setValue("spacedrep-pos", pos()); settings.setValue("spacedrep-size", size()); } QWidget* SpacedRepetitionWindow::getAnswerEdit() { return exactAnswerEdit; } QWidget* SpacedRepetitionWindow::getUserAnswerLabel() { return exactAnswerLabel; }