1 /*
2 SPDX-FileCopyrightText: 2009-2010 Frederik Gladhorn <gladhorn@kde.org>
3 SPDX-FileCopyrightText: 2009 Daniel Laidig <d.laidig@gmx.de>
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "practicestatemachine.h"
8
9 #include "parleydocument.h"
10
11 #include "comparisonbackendmode.h"
12 #include "conjugationbackendmode.h"
13 #include "examplesentencebackendmode.h"
14 #include "flashcardbackendmode.h"
15 #include "genderbackendmode.h"
16 #include "multiplechoicebackendmode.h"
17 #include "prefs.h"
18 #include "writtenbackendmode.h"
19
20 using namespace Practice;
21
PracticeStateMachine(AbstractFrontend * frontend,ParleyDocument * doc,SessionManagerBase * sessionManager,QObject * parent)22 PracticeStateMachine::PracticeStateMachine(AbstractFrontend *frontend, ParleyDocument *doc, SessionManagerBase *sessionManager, QObject *parent)
23 : QObject(parent)
24 , m_frontend(frontend)
25 , m_document(doc)
26 , m_sessionManager(sessionManager)
27 {
28 createPracticeMode();
29
30 // To allow to skip an an entry
31 connect(m_frontend, &AbstractFrontend::skipAction, this, &PracticeStateMachine::nextEntry);
32 connect(m_frontend, &AbstractFrontend::stopPractice, this, &PracticeStateMachine::slotPracticeFinished);
33 connect(m_frontend, &AbstractFrontend::hintAction, m_mode, &AbstractBackendMode::hintAction);
34
35 connect(m_frontend, &AbstractFrontend::continueAction, this, &PracticeStateMachine::continueAction);
36
37 connect(m_mode, &AbstractBackendMode::answerRight, this, &PracticeStateMachine::answerRight);
38 connect(m_mode, &AbstractBackendMode::answerWrongRetry, this, &PracticeStateMachine::answerWrongRetry);
39 connect(m_mode, &AbstractBackendMode::answerWrongShowSolution, this, &PracticeStateMachine::answerWrongShowSolution);
40 connect(m_mode, &AbstractBackendMode::showSolution, this, &PracticeStateMachine::showSolution);
41 }
42
createPracticeMode()43 void PracticeStateMachine::createPracticeMode()
44 {
45 switch (Prefs::practiceMode()) {
46 case Prefs::EnumPracticeMode::FlashCardsPractice:
47 qDebug() << "Create Flash Card Practice backend";
48 m_frontend->setMode(AbstractFrontend::FlashCard);
49 m_mode = new FlashCardBackendMode(m_frontend, this);
50 break;
51 case Prefs::EnumPracticeMode::MultipleChoicePractice:
52 qDebug() << "Create MultipleChoice Practice backend";
53 m_frontend->setMode(AbstractFrontend::MultipleChoice);
54 m_mode = new MultipleChoiceBackendMode(m_frontend, this, m_sessionManager);
55 break;
56 case Prefs::EnumPracticeMode::MixedLettersPractice:
57 qDebug() << "Create Mixed Letters Practice backend";
58 m_frontend->setMode(AbstractFrontend::MixedLetters);
59 m_mode = new WrittenBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
60 break;
61 case Prefs::EnumPracticeMode::WrittenPractice:
62 qDebug() << "Create Written Practice backend";
63 m_frontend->setMode(AbstractFrontend::Written);
64 m_mode = new WrittenBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
65 break;
66 case Prefs::EnumPracticeMode::ExampleSentencesPractice:
67 qDebug() << "Create Written Practice backend";
68 m_frontend->setMode(AbstractFrontend::ExampleSentence);
69 m_mode = new ExampleSentenceBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
70 break;
71 case Prefs::EnumPracticeMode::GenderPractice:
72 m_frontend->setMode(AbstractFrontend::MultipleChoice);
73 m_mode = new GenderBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
74 break;
75 case Prefs::EnumPracticeMode::ConjugationPractice:
76 m_frontend->setMode(AbstractFrontend::Conjugation);
77 m_mode = new ConjugationBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
78 break;
79 case Prefs::EnumPracticeMode::ComparisonPractice:
80 m_frontend->setMode(AbstractFrontend::Comparison);
81 m_mode = new ComparisonBackendMode(m_frontend, this, m_sessionManager, m_document->document().get());
82 break;
83
84 default:
85 Q_ASSERT("Implement selected practice mode" == 0);
86 }
87 }
88
start()89 void Practice::PracticeStateMachine::start()
90 {
91 qDebug() << "Start practice";
92 m_sessionManager->practiceStarted();
93 nextEntry();
94 }
95
nextEntry()96 void PracticeStateMachine::nextEntry()
97 {
98 m_state = NotAnswered;
99 m_current = m_sessionManager->nextTrainingEntry();
100
101 // qDebug() << "GETTING ENTRY - " << m_current;
102
103 // after going through all words, or at the start of practice
104 if (m_current == 0) {
105 slotPracticeFinished();
106 return;
107 }
108 if (!m_mode->setTestEntry(m_current)) {
109 // this is just a fall back, if an invalid entry slipped through
110 currentEntryFinished();
111 nextEntry();
112 }
113 updateFrontend();
114 }
115
slotPracticeFinished()116 void PracticeStateMachine::slotPracticeFinished()
117 {
118 qDebug() << "Stop practice";
119 m_sessionManager->practiceFinished();
120 emit practiceFinished();
121 }
122
currentEntryFinished()123 void PracticeStateMachine::currentEntryFinished()
124 {
125 m_sessionManager->removeCurrentEntryFromPractice();
126 }
127
continueAction()128 void PracticeStateMachine::continueAction()
129 {
130 // qDebug() << "continue" << m_state;
131 switch (m_state) {
132 // on continue, we check the answer, if in NotAnsweredState or AnswerWasWrongState
133 case NotAnswered:
134 case AnswerWasWrong:
135 m_mode->checkAnswer();
136 break;
137
138 case SolutionShown:
139 gradeEntryAndContinue();
140 break;
141 }
142 }
143
answerRight()144 void PracticeStateMachine::answerRight()
145 {
146 // qDebug() << "ans right";
147
148 m_frontend->setFeedbackState(AbstractFrontend::AnswerCorrect);
149 if (m_state == NotAnswered) {
150 m_frontend->setResultState(AbstractFrontend::AnswerCorrect);
151 } else {
152 m_frontend->setResultState(AbstractFrontend::AnswerWrong);
153 }
154
155 m_state = SolutionShown;
156 m_frontend->showSolution();
157 }
158
answerWrongRetry()159 void PracticeStateMachine::answerWrongRetry()
160 {
161 // qDebug() << "wrong retr";
162 m_frontend->setFeedbackState(AbstractFrontend::AnswerWrong);
163 m_state = AnswerWasWrong;
164 }
165
answerWrongShowSolution()166 void PracticeStateMachine::answerWrongShowSolution()
167 {
168 // qDebug() << "wrong sol";
169 m_frontend->setFeedbackState(AbstractFrontend::AnswerWrong);
170 // User gave an empty answer or the same answer for a second time so we want to drop out.
171 m_frontend->setResultState(AbstractFrontend::AnswerWrong);
172 m_state = SolutionShown;
173 m_frontend->showSolution();
174 }
175
showSolution()176 void PracticeStateMachine::showSolution()
177 {
178 // qDebug() << "show solution";
179 m_state = SolutionShown;
180 m_frontend->showSolution();
181 }
182
updateFrontend()183 void PracticeStateMachine::updateFrontend()
184 {
185 m_frontend->setFeedbackState(AbstractFrontend::QuestionState);
186 m_frontend->setResultState(AbstractFrontend::QuestionState);
187 m_frontend->setLessonName(m_current->entry()->lesson()->name());
188 grade_t grade = m_mode->currentGradeForEntry();
189 m_frontend->showGrade(m_mode->currentPreGradeForEntry(), grade);
190
191 // show the word that is currently practiced in the progress bar
192 m_frontend->setFinishedWordsTotalWords(m_sessionManager->allEntryCount() - m_sessionManager->activeEntryCount(), m_sessionManager->allEntryCount());
193
194 // Set fonts
195 m_frontend->setQuestionFont((m_current->languageFrom() == Prefs::learningLanguage()) ? m_frontend->learningLangFont() : m_frontend->knownLangFont());
196 m_frontend->setSolutionFont((m_current->languageTo() == Prefs::learningLanguage()) ? m_frontend->learningLangFont() : m_frontend->knownLangFont());
197
198 grade_t goodGrade = qMax(grade, grade_t(KV_LEV1_GRADE)); // if the word hasn't been practiced yet, use grade 1 as a base
199
200 if (m_current->statisticBadCount() == 0) {
201 goodGrade = qMax(KV_LEV2_GRADE, qMin(grade + 1, KV_MAX_GRADE));
202 }
203
204 m_frontend->setBoxes(grade, goodGrade, KV_LEV1_GRADE);
205
206 QUrl imgFrom = m_current->entry()->translation(m_current->languageFrom())->imageUrl();
207 QUrl imgTo = m_current->entry()->translation(m_current->languageTo())->imageUrl();
208 if (imgFrom.isEmpty()) {
209 imgFrom = imgTo;
210 }
211 if (imgTo.isEmpty()) {
212 imgTo = imgFrom;
213 }
214 if (Prefs::flashcardsFrontImage()) {
215 m_frontend->setQuestionImage(imgFrom);
216 } else {
217 m_frontend->setQuestionImage(QUrl());
218 }
219 if (Prefs::flashcardsBackImage()) {
220 m_frontend->setSolutionImage(imgTo);
221 } else {
222 m_frontend->setSolutionImage(QUrl());
223 }
224 m_frontend->showQuestion();
225 }
226
gradeEntryAndContinue()227 void PracticeStateMachine::gradeEntryAndContinue()
228 {
229 grade_t currentPreGrade = m_mode->currentPreGradeForEntry();
230 grade_t currentGrade = m_mode->currentGradeForEntry();
231
232 if (m_frontend->resultState() == AbstractFrontend::AnswerCorrect) {
233 m_current->updateStatisticsRightAnswer(currentPreGrade, currentGrade);
234 } else {
235 m_current->updateStatisticsWrongAnswer(currentPreGrade, currentGrade);
236 }
237
238 if (m_current->shouldChangeGrades()) {
239 m_mode->updateGrades();
240 if (m_frontend->resultState() == AbstractFrontend::AnswerCorrect) {
241 currentEntryFinished();
242 }
243 }
244 nextEntry();
245 }
246