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