File indexing completed on 2024-09-29 03:29:23
0001 /* 0002 SPDX-FileCopyrightText: 2008-2010 Peter Hedlund <peter.hedlund@kdemail.net> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "kwqquizmodel.h" 0007 0008 #include "blankanswer.h" 0009 #include "kwqsortfiltermodel.h" 0010 #include "kwqcardmodel.h" 0011 0012 #include <QRandomGenerator> 0013 0014 #include <KRandom> 0015 0016 using namespace Qt::Literals::StringLiterals; 0017 0018 KWQQuizModel::KWQQuizModel(QObject *parent) : QSortFilterProxyModel(parent) 0019 { 0020 m_sourceModel = nullptr; 0021 m_currentQuestion = 0; 0022 setSortCaseSensitivity(Qt::CaseInsensitive); 0023 setFilterCaseSensitivity(Qt::CaseInsensitive); 0024 setFilterKeyColumn(-1); 0025 m_list.clear(); 0026 m_errorList.clear(); 0027 m_quizList.clear(); 0028 } 0029 0030 0031 void KWQQuizModel::setFilterModel(KWQSortFilterModel * sourceModel) 0032 { 0033 m_sourceModel = sourceModel; 0034 QSortFilterProxyModel::setSourceModel(sourceModel); 0035 } 0036 0037 KWQSortFilterModel * KWQQuizModel::sourceModel() const 0038 { 0039 return m_sourceModel; 0040 } 0041 0042 bool KWQQuizModel::lessThan(const QModelIndex & left, const QModelIndex & right) const 0043 { 0044 return m_list[left.row()] < m_list[right.row()]; 0045 } 0046 0047 bool KWQQuizModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const 0048 { 0049 QString text; 0050 text.clear(); 0051 for (int i = 0 ; i < 2; i++) 0052 text.append(sourceModel()->data(sourceModel()->index(sourceRow, i, sourceParent), Qt::DisplayRole).toString()); 0053 0054 return !text.isEmpty(); 0055 } 0056 0057 0058 int KWQQuizModel::column(int i) 0059 { 0060 int result = 0; 0061 if (isOdd(m_quizMode)) 0062 result = 1; 0063 if ((m_quizMode == 5) && qAbs(i) == i) 0064 result = 0; 0065 return result; 0066 } 0067 0068 bool KWQQuizModel::atEnd() 0069 { 0070 return m_currentQuestion >= questionCount(); 0071 } 0072 0073 void KWQQuizModel::toNext() 0074 { 0075 ++m_currentQuestion; 0076 } 0077 0078 bool KWQQuizModel::hasErrors() 0079 { 0080 if (m_quizType == Prefs::EnumStartSession::MultipleChoice) 0081 return m_errorList.count() > 2; //this is a hack until better multiple choice is written 0082 else 0083 return !m_errorList.isEmpty(); 0084 } 0085 0086 bool KWQQuizModel::init() 0087 { 0088 if (m_sourceModel == nullptr) 0089 return false; 0090 0091 invalidate(); 0092 0093 // check if enoufile:///home/carl/kde6/src/log/2023-09-26-07/kwordquiz/build.loggh in list 0094 bool result = false; 0095 switch (m_quizType) { 0096 case Prefs::EnumStartSession::Editor: 0097 // 0098 break; 0099 case Prefs::EnumStartSession::Flashcard: 0100 result = (rowCount(QModelIndex()) > 0); 0101 break; 0102 case Prefs::EnumStartSession::QA: 0103 result = (rowCount(QModelIndex()) > 0); 0104 break; 0105 case Prefs::EnumStartSession::MultipleChoice: 0106 result = (rowCount(QModelIndex()) > 2); 0107 break; 0108 } 0109 0110 if (!result) 0111 return false; 0112 0113 m_quizList.clear(); 0114 for (int i = 0; i < rowCount(QModelIndex()); ++i) { 0115 m_quizList.append(i); 0116 if (m_quizMode == 5) 0117 m_quizList.append(-i); 0118 } 0119 0120 activateBaseList(); 0121 return true; 0122 } 0123 0124 void KWQQuizModel::activateBaseList() 0125 { 0126 m_list.clear(); 0127 m_errorList.clear(); 0128 foreach(int l, m_quizList) 0129 m_list.append(l); 0130 0131 if (m_quizMode > 2) { 0132 KRandom::shuffle(m_list, QRandomGenerator::global()); 0133 }; 0134 0135 m_questionCount = m_list.count(); 0136 m_currentQuestion = 0; 0137 } 0138 0139 bool KWQQuizModel::checkAnswer(const QString & a) 0140 { 0141 bool result = false; 0142 QString ans = a; 0143 0144 int row = m_list.at(m_currentQuestion); 0145 0146 QString tTemp = data(index(qAbs(row), column(row), QModelIndex()), Qt::DisplayRole).toString(); 0147 0148 tTemp = tTemp.simplified(); 0149 ans = ans.simplified(); 0150 0151 if (m_quizType == Prefs::EnumStartSession::QA) { 0152 if (!m_correctBlank.isEmpty()) { 0153 QStringList la, ls; 0154 if (ans.indexOf(QLatin1Char(';')) > 0) 0155 ls = ans.split(QLatin1Char(';'), Qt::SkipEmptyParts); 0156 else 0157 ls.append(ans); 0158 0159 if (m_correctBlank.indexOf(QLatin1Char(';')) > 0) 0160 la = m_correctBlank.split(QLatin1Char(';'), Qt::SkipEmptyParts); 0161 else 0162 la.append(m_correctBlank); 0163 0164 result = (ls.count() == la.count()); 0165 if (result) { 0166 for (int counter = 0; counter < la.count(); counter++) { 0167 result = (ls[counter].simplified() == la[counter].simplified()); 0168 if (!result) 0169 break; 0170 } 0171 } 0172 } else if (Prefs::caseSensitiveAnswers()) { 0173 result = (ans == tTemp); 0174 } else { 0175 result = ans.compare(tTemp, Qt::CaseInsensitive) == 0; 0176 } 0177 } else { 0178 if (m_quizType == Prefs::EnumStartSession::MultipleChoice) { 0179 if (Prefs::enableBlanks()) { 0180 tTemp.remove(QLatin1Char('[')); 0181 tTemp.remove(QLatin1Char(']')); 0182 } 0183 result = (ans == tTemp); 0184 } else { 0185 result = (ans == tTemp); 0186 } 0187 } 0188 ///@todo currently the Leitner stuff crashes KWQ 0189 //QString tmpLeitner( m_doc->entry(li.oneOp())->leitnerBox() ); 0190 0191 if (!result) { 0192 //m_doc->entry(li.oneOp())->setLeitnerBox(m_doc->leitnerSystem()->wrongBox( tmpLeitner )); 0193 m_errorList.append(row); 0194 } 0195 //else 0196 //m_doc->entry(li.oneOp())->setLeitnerBox(m_doc->leitnerSystem()->correctBox( tmpLeitner )); 0197 0198 return result; 0199 } 0200 0201 0202 QStringList KWQQuizModel::multiOptions() 0203 { 0204 QStringList ls; 0205 int a = -1; 0206 int b = -1; 0207 0208 do 0209 a = QRandomGenerator::global()->bounded(m_questionCount); 0210 while(a == m_currentQuestion); 0211 0212 do 0213 b = QRandomGenerator::global()->bounded(m_questionCount); 0214 while(b == m_currentQuestion || b == a); 0215 0216 int row = m_list.at(m_currentQuestion); 0217 int col = column(row); 0218 if (col == 0) 0219 col = 0; 0220 else 0221 col = 1; 0222 ls.append(data(index(qAbs(row), col, QModelIndex()), Qt::DisplayRole).toString()); 0223 row = m_list.at(a); 0224 ls.append(data(index(qAbs(row), col, QModelIndex()), Qt::DisplayRole).toString()); 0225 row = m_list.at(b); 0226 ls.append(data(index(qAbs(row), col, QModelIndex()), Qt::DisplayRole).toString()); 0227 0228 if (Prefs::enableBlanks()) { 0229 for (int i = 0; i < ls.count(); i++) { 0230 ls[i].remove(QLatin1Char('[')); 0231 ls[i].remove(QLatin1Char(']')); 0232 } 0233 } 0234 0235 KRandom::shuffle(ls, QRandomGenerator::global()); 0236 0237 return ls; 0238 } 0239 0240 0241 QString KWQQuizModel::quizIcon(QuizIcon ico) 0242 { 0243 QString s = QStringLiteral("question"); 0244 int col = column(m_list.at(m_currentQuestion)); 0245 if (ico == IconLeftCol) { 0246 if (col == 0) 0247 s = QStringLiteral("answer"); 0248 } 0249 0250 if (ico == IconRightCol) { 0251 if (col != 0) 0252 s = QStringLiteral("answer"); 0253 } 0254 return s; 0255 } 0256 0257 QString KWQQuizModel::yourAnswer(const QString &givenAnswer) const 0258 { 0259 return BlankAnswer::yourAnswerResult(givenAnswer, m_answerBlank); 0260 } 0261 0262 0263 QString KWQQuizModel::hint() 0264 { 0265 if (!m_correctBlank.isEmpty()) 0266 return m_correctBlank; 0267 else 0268 return answer(); 0269 } 0270 0271 0272 QString KWQQuizModel::question() 0273 { 0274 int row = m_list.at(m_currentQuestion); 0275 int col = column(row); 0276 if (col == 0) 0277 col = 1; 0278 else 0279 col = 0; 0280 0281 QString s = data(index(qAbs(row), col, QModelIndex()), Qt::DisplayRole).toString(); 0282 0283 if (Prefs::enableBlanks()) { 0284 s.remove('['); 0285 s.remove(']'); 0286 } 0287 0288 if (m_quizType != Prefs::EnumStartSession::Flashcard && m_currentQuestion > 0) 0289 Q_EMIT checkingAnswer(m_list.at(m_currentQuestion - 1)); 0290 else 0291 Q_EMIT checkingAnswer(row); 0292 0293 return s; 0294 } 0295 0296 QString KWQQuizModel::blankAnswer() 0297 { 0298 m_correctBlank.clear(); 0299 m_answerBlank.clear(); 0300 0301 if (m_quizType == Prefs::EnumStartSession::QA && Prefs::enableBlanks()) { 0302 int row = m_list.at(m_currentQuestion); 0303 const QString input = data(index(qAbs(row), column(row), QModelIndex()), Qt::DisplayRole).toString(); 0304 0305 const BlankAnswer::BlankResult result = BlankAnswer::blankAnswer(input); 0306 0307 m_answerBlank = result.blankedAnswer; 0308 m_correctBlank = result.correctAnswer; 0309 } 0310 return m_answerBlank; 0311 } 0312 0313 QString KWQQuizModel::answer() 0314 { 0315 int row = m_list.at(m_currentQuestion); 0316 QString s = data(index(qAbs(row), column(row), QModelIndex()), Qt::DisplayRole).toString(); 0317 0318 if (m_quizType == Prefs::EnumStartSession::QA) { 0319 if (Prefs::enableBlanks()) { 0320 s.replace(QLatin1Char('['), u"<u>"_s); 0321 s.replace(QLatin1Char(']'), u"</u>"_s); 0322 s.prepend(u"<qt>"_s); 0323 s.append(u"</qt>"_s); 0324 } 0325 } 0326 else 0327 { 0328 if (Prefs::enableBlanks()) { 0329 s.remove(QLatin1Char('[')); 0330 s.remove(QLatin1Char(']')); 0331 } 0332 } 0333 return s; 0334 } 0335 0336 QString KWQQuizModel::kbAnswer() 0337 { 0338 int col = column(m_list.at(m_currentQuestion)); 0339 QString result; 0340 0341 if (col == 0) 0342 return headerData(0, Qt::Horizontal, KWQTableModel::KeyboardLayoutRole).toString(); 0343 if (col == 1) 0344 return headerData(1, Qt::Horizontal, KWQTableModel::KeyboardLayoutRole).toString(); 0345 0346 return result; 0347 } 0348 0349 0350 void KWQQuizModel::removeLastError() 0351 { 0352 m_errorList.removeLast(); 0353 } 0354 0355 0356 int KWQQuizModel::questionCount() 0357 { 0358 return m_questionCount; 0359 } 0360 0361 0362 void KWQQuizModel::finish() 0363 { 0364 Q_EMIT checkingAnswer(-1); 0365 } 0366 0367 0368 void KWQQuizModel::setQuizType(Prefs::EnumStartSession::type qt) 0369 { 0370 m_quizType = qt; 0371 } 0372 0373 0374 void KWQQuizModel::setQuizMode(int qm) 0375 { 0376 m_quizMode = qm; 0377 } 0378 0379 0380 void KWQQuizModel::activateErrorList() 0381 { 0382 m_list.clear(); 0383 foreach(int i, m_errorList) 0384 m_list.append(i); 0385 0386 m_errorList.clear(); 0387 m_questionCount = m_list.count(); 0388 m_currentQuestion = 0; 0389 } 0390 0391 inline bool KWQQuizModel::isOdd(int value) const { return value & 0x1; } 0392 0393 #include "moc_kwqquizmodel.cpp"