File indexing completed on 2024-04-14 03:49:04

0001 /*
0002     SPDX-FileCopyrightText: 2008 Frederik Gladhorn <frederik.gladhorn@kdemail.net>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "entryfilter.h"
0007 
0008 #include <QDebug>
0009 #include <QRandomGenerator>
0010 
0011 #include <KLocalizedString>
0012 #include <KMessageBox>
0013 
0014 #include <KEduVocDocument>
0015 #include <KEduVocExpression>
0016 #include <KEduVocWordtype>
0017 
0018 #include "documentsettings.h"
0019 #include "entryfilterdialog.h"
0020 #include "testentry.h"
0021 
0022 // using namespace Practice;
0023 
0024 // Blocking times for pregrade levels.
0025 //
0026 // It could be argued that these should be configurable but I am not
0027 // sure what that would bring us or the user. Definitely not improved
0028 // learning...
0029 //
0030 // FIXME: Find out what the optimal values are.
0031 int preGradeTimes[] = {
0032     0,
0033     3 * 60 + 30, // 1: 3.5 minutes
0034     7 * 60, // 2: 7 minutes
0035     15 * 60, // 3: 15 minutes
0036     1 * 3600, // 4: 1 hour
0037     2 * 3600, // 5: 2 hours
0038     4 * 3600, // 6: 4 hours
0039     8 * 3600, // 7: 8 hours
0040 };
0041 
0042 EntryFilter::EntryFilter(KEduVocDocument *doc, QObject *parent)
0043     : QObject(parent)
0044     , m_doc(doc)
0045     , m_numSets(0)
0046     , m_fromTranslation(Prefs::knownLanguage())
0047     , m_toTranslation(Prefs::learningLanguage())
0048 {
0049     if (Prefs::practiceMode() == Prefs::EnumPracticeMode::ConjugationPractice) {
0050         DocumentSettings documentSettings(m_doc->url().url() + QString::number(m_toTranslation));
0051         documentSettings.load();
0052         m_tenses = documentSettings.conjugationTenses();
0053         //         qDebug() << "Tenses" << m_tenses;
0054     }
0055 }
0056 /*
0057 static void debugEntry(const QString &comment, KEduVocExpression *vocexp,
0058                        KEduVocTranslation *from, KEduVocTranslation *to)
0059 {
0060     Q_UNUSED(vocexp);
0061 
0062     qDebug() << comment << "from" << from->text() << "to" << to->text();
0063 }
0064 */
0065 QList<TestEntry *> EntryFilter::entries(bool showDialog)
0066 {
0067     switch (Prefs::practiceDirection()) {
0068     case Prefs::EnumPracticeDirection::KnownToLearning:
0069         m_numSets = 1;
0070         m_fromTranslation = Prefs::knownLanguage();
0071         m_toTranslation = Prefs::learningLanguage();
0072         break;
0073     case Prefs::EnumPracticeDirection::LearningToKnown:
0074         m_numSets = 1;
0075         m_fromTranslation = Prefs::learningLanguage();
0076         m_toTranslation = Prefs::knownLanguage();
0077         break;
0078     case Prefs::EnumPracticeDirection::MixedDirectionsWordsOnly:
0079         m_numSets = 2;
0080         m_fromTranslation = Prefs::knownLanguage();
0081         m_toTranslation = Prefs::learningLanguage();
0082         break;
0083     case Prefs::EnumPracticeDirection::MixedDirectionsWithSound:
0084         // FIXME: Not yet supported. Use same settings as MixedModeWordsOnly
0085         m_numSets = 2;
0086         m_fromTranslation = Prefs::knownLanguage();
0087         m_toTranslation = Prefs::learningLanguage();
0088         break;
0089     default:
0090         // Use KnownToLearning as default.
0091         m_numSets = 1;
0092         m_fromTranslation = Prefs::knownLanguage();
0093         m_toTranslation = Prefs::learningLanguage();
0094         break;
0095     }
0096 
0097     for (int pass = 0; pass < m_numSets; ++pass) {
0098         // If we only do one pass, then from/to translation are already set.
0099         // But in mixed mode we need to set up pass 1 and later.
0100         if (pass == 1) {
0101             m_fromTranslation = Prefs::learningLanguage();
0102             m_toTranslation = Prefs::knownLanguage();
0103         }
0104 
0105         // qDebug() << "Filter for " << m_fromTranslation << " to " << m_toTranslation;
0106 
0107         collectEntries(pass);
0108         expireEntries(pass);
0109         setupFilteredEntries(pass);
0110     }
0111 
0112     static QString noEntriesError = i18n(
0113         "The vocabulary document contains no entries that can be used for the chosen type"
0114         " of practice.");
0115 
0116     // qDebug() << "Document contains " << m_entries[0].count() + m_entries[1].count() << " valid entries.";
0117     if (m_entries[0].count() + m_entries[1].count() == 0) {
0118         if (showDialog) {
0119             KMessageBox::error(nullptr, noEntriesError);
0120         }
0121         return QList<TestEntry *>();
0122     }
0123 
0124     updateCurrentSelection();
0125 
0126     bool ignoreBlocked = false;
0127     int numSelected = m_currentSelection[0].count() + m_currentSelection[1].count();
0128     if (numSelected == 0 && showDialog) {
0129         EntryFilterDialog dialog(*this); // this pointer isn't used as pointer to parent widget here!
0130                                          // It should provide access to the EntryFilter data.
0131         if (dialog.exec() == QDialog::Rejected) {
0132             return QList<TestEntry *>();
0133         }
0134         ignoreBlocked = dialog.ignoreBlocked();
0135     }
0136 
0137     // Finally, create the list of test entries from the selected
0138     // lists of EduVocTranslations.
0139     if (Prefs::practiceMode() == Prefs::EnumPracticeMode::ConjugationPractice) {
0140         QList<TestEntry *> ret = conjugationTestEntries(ignoreBlocked);
0141         if ((ret.count() == 0) && showDialog) {
0142             KMessageBox::error(nullptr, noEntriesError);
0143         }
0144         return ret;
0145     } else {
0146         // FIXME: Create entries already from the beginning so we
0147         //        don't have to work with kvtml translations first and
0148         //        then entries later.
0149         QList<TestEntry *> testEntries;
0150         for (int setNo = 0; setNo < m_numSets; ++setNo) {
0151             int from;
0152             int to;
0153 
0154             if (setNo == 0) {
0155                 switch (Prefs::practiceDirection()) {
0156                 case Prefs::EnumPracticeDirection::KnownToLearning:
0157                     from = Prefs::knownLanguage();
0158                     to = Prefs::learningLanguage();
0159                     break;
0160                 case Prefs::EnumPracticeDirection::LearningToKnown:
0161                     from = Prefs::learningLanguage();
0162                     to = Prefs::knownLanguage();
0163                     break;
0164                 case Prefs::EnumPracticeDirection::MixedDirectionsWordsOnly:
0165                 case Prefs::EnumPracticeDirection::MixedDirectionsWithSound:
0166                 default:
0167                     from = Prefs::knownLanguage();
0168                     to = Prefs::learningLanguage();
0169                     break;
0170                 }
0171             } else {
0172                 from = Prefs::learningLanguage();
0173                 to = Prefs::knownLanguage();
0174             }
0175 
0176             for (KEduVocExpression *entry : qAsConst(m_currentSelection[setNo])) {
0177                 // Set the from and to translation for the entry itself.
0178                 TestEntry *testEntry = new TestEntry(entry);
0179 
0180                 testEntry->setLanguageFrom(from);
0181                 testEntry->setLanguageTo(to);
0182 
0183                 randomizedInsert(testEntries, testEntry);
0184             }
0185         }
0186         return testEntries;
0187     }
0188 }
0189 
0190 void EntryFilter::expireEntries(int setNo)
0191 {
0192     if (Prefs::expire()) {
0193         int counter = 0;
0194         for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0195             int grade = entry->translation(m_toTranslation)->grade();
0196 
0197             const QDateTime &date = entry->translation(m_toTranslation)->practiceDate();
0198 
0199             const QDateTime &expireDate = QDateTime::currentDateTime().addSecs(-Prefs::expireItem(grade));
0200 
0201             if (date < expireDate && grade > 0) {
0202                 // decrease the grade
0203                 entry->translation(m_toTranslation)->decGrade();
0204 
0205                 // prevent from endless dropping
0206                 // use blockItem() time to prevent blocking after expiring
0207                 entry->translation(m_toTranslation)->setPracticeDate(QDateTime::currentDateTime().addSecs(-Prefs::blockItem(grade - 1)));
0208                 counter++;
0209             }
0210         }
0211         qDebug() << "Expired words dropped their confidence: " << counter;
0212     }
0213 }
0214 
0215 void EntryFilter::collectEntries(int setNo)
0216 {
0217     // Set up the lists/sets of filtered vocabulary
0218     QList<KEduVocExpression *> entries = m_doc->lesson()->entries(KEduVocLesson::Recursive);
0219     m_entries[setNo] = QSet<KEduVocExpression *>(entries.cbegin(), entries.cend());
0220     cleanupInvalid(setNo);
0221 
0222     // FIXME the filtering needs to be done for each word or the grammar modes get included with written or somesuch
0223 
0224     /* FIXME
0225     if (Prefs::genderPractice())
0226     {
0227         if (m_doc->identifier(m_toTranslation).article().isEmpty())
0228         {
0229             KMessageBox::error(0, i18n("The vocabulary document contains no articles for the current language. Please add some in the Edit->Grammar menu."));
0230             return QList<KEduVocExpression*>();
0231         }
0232     }
0233     */
0234 }
0235 
0236 void EntryFilter::setupFilteredEntries(int setNo)
0237 {
0238     lessonEntries(setNo);
0239     wordTypeEntries(setNo);
0240     blockedEntries(setNo);
0241     timesWrongEntries(setNo);
0242     timesPracticedEntries(setNo);
0243     minMaxGradeEntries(setNo);
0244 }
0245 
0246 void EntryFilter::lessonEntries(int setNo)
0247 {
0248     for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0249         if (entry->lesson()->inPractice()) {
0250             m_entriesLesson[setNo].insert(entry);
0251         }
0252     }
0253 }
0254 
0255 void EntryFilter::wordTypeEntries(int setNo)
0256 {
0257     if (Prefs::wordTypesInPracticeEnabled()) {
0258         for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0259             if (entry->translation(m_toTranslation)->wordType()) {
0260                 if (entry->translation(m_toTranslation)->wordType()->inPractice()) {
0261                     m_entriesWordType[setNo].insert(entry);
0262                 }
0263             }
0264         }
0265     } else {
0266         m_entriesWordType[setNo] = m_entries[setNo];
0267     }
0268 }
0269 
0270 void EntryFilter::blockedEntries(int setNo)
0271 {
0272     if (!Prefs::block()) {
0273         m_entriesNotBlocked[setNo] = m_entries[setNo];
0274         return;
0275     }
0276 
0277     switch (Prefs::practiceMode()) {
0278     case Prefs::EnumPracticeMode::ConjugationPractice:
0279         for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0280             if (!isConjugationBlocked(entry->translation(m_toTranslation))) {
0281                 m_entriesNotBlocked[setNo].insert(entry);
0282             }
0283         }
0284         break;
0285     case Prefs::EnumPracticeMode::GenderPractice:
0286         for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0287             KEduVocText article = entry->translation(m_toTranslation)->article();
0288             if (!isBlocked(&article)) {
0289                 m_entriesNotBlocked[setNo].insert(entry);
0290             }
0291         }
0292         break;
0293     case Prefs::EnumPracticeMode::ComparisonPractice:
0294         for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0295             KEduVocTranslation *translation = entry->translation(m_toTranslation);
0296             KEduVocText comparative = translation->comparativeForm();
0297             KEduVocText superlative = translation->superlativeForm();
0298             if (!isBlocked(&(comparative)) || !isBlocked(&(superlative))) {
0299                 m_entriesNotBlocked[setNo].insert(entry);
0300             }
0301         }
0302         break;
0303     default:
0304         for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0305             if (!isBlocked(entry->translation(m_toTranslation))) {
0306                 m_entriesNotBlocked[setNo].insert(entry);
0307                 // debugEntry("Not blocked:", entry,
0308                 //           entry->translation(m_fromTranslation),
0309                 //           entry->translation(m_toTranslation));
0310             } else {
0311                 // debugEntry("Blocked:", entry,
0312                 //           entry->translation(m_fromTranslation),
0313                 //           entry->translation(m_toTranslation));
0314             }
0315         }
0316         break;
0317     }
0318 }
0319 
0320 bool EntryFilter::isConjugationBlocked(KEduVocTranslation *translation) const
0321 {
0322     const QStringList conjugationTenses = translation->conjugationTenses();
0323     for (const QString &tense : conjugationTenses) {
0324         if (m_tenses.contains(tense)) {
0325             const QList<KEduVocWordFlags> pronouns = translation->getConjugation(tense).keys();
0326             for (const KEduVocWordFlags &pronoun : pronouns) {
0327                 KEduVocText grade = translation->getConjugation(tense).conjugation(pronoun);
0328                 if (!isBlocked(&(grade))) {
0329                     // just need to find any form that is not blocked for generating test entries
0330                     // exact filtering is done later in conjugationTestEntries
0331                     return false;
0332                 }
0333             }
0334         }
0335     }
0336     return true;
0337 }
0338 
0339 bool EntryFilter::isBlocked(const KEduVocText *const text) const
0340 {
0341     grade_t grade = text->grade();
0342     grade_t preGrade = text->preGrade();
0343 
0344     // Sanity checks.
0345     // Note that grade_t is unsigned so no need to check < 0.
0346     //
0347     // FIXME: This should be done when the prefs are first read.
0348     if (preGrade > KV_MAX_GRADE) {
0349         preGrade = KV_MAX_GRADE;
0350     }
0351     if (grade > KV_MAX_GRADE) {
0352         grade = KV_MAX_GRADE;
0353     }
0354 
0355     QDateTime now = QDateTime::currentDateTime();
0356 
0357     if ((grade == KV_NORM_GRADE && preGrade == KV_NORM_GRADE) || (grade > 0 && Prefs::blockItem(grade) == 0)) {
0358         // Always include untrained words and all words when blocking is off.
0359 
0360         // qDebug() << "Not blocked, test 1; word =" << text->text() << "grade =" << grade
0361         //         << "blockItem(grade) =" << Prefs::blockItem(grade);
0362 
0363         return false;
0364     } else if (grade == KV_NORM_GRADE) {
0365         // Test for pregrade blocking.
0366         QDateTime date = text->practiceDate();
0367         if (date.addSecs(preGradeTimes[preGrade]) < now) {
0368             // qDebug() << "Not blocked, test 2";
0369             return false;
0370         }
0371     } else {
0372         // Test for grade blocking.
0373 
0374         QDateTime date = text->practiceDate();
0375         if (date.addSecs(Prefs::blockItem(grade)) < now) {
0376             // qDebug() << "Not blocked, test 3";
0377             return false;
0378         }
0379     }
0380 
0381     return true;
0382 }
0383 
0384 void EntryFilter::timesWrongEntries(int setNo)
0385 {
0386     for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0387         if (entry->translation(m_toTranslation)->badCount() >= Prefs::practiceMinimumWrongCount()
0388             && entry->translation(m_toTranslation)->badCount() <= Prefs::practiceMaximumWrongCount()) {
0389             m_entriesTimesWrong[setNo].insert(entry);
0390         }
0391     }
0392 }
0393 
0394 void EntryFilter::timesPracticedEntries(int setNo)
0395 {
0396     for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0397         if (entry->translation(m_toTranslation)->practiceCount() >= Prefs::practiceMinimumTimesAsked()
0398             && entry->translation(m_toTranslation)->practiceCount() <= Prefs::practiceMaximumTimesAsked()) {
0399             m_entriesTimesPracticed[setNo].insert(entry);
0400         }
0401     }
0402 }
0403 
0404 void EntryFilter::minMaxGradeEntries(int setNo)
0405 {
0406     for (KEduVocExpression *entry : qAsConst(m_entries[setNo])) {
0407         int grade = entry->translation(m_toTranslation)->grade();
0408         if (grade >= Prefs::practiceMinimumGrade() && grade <= Prefs::practiceMaximumGrade()) {
0409             m_entriesMinMaxGrade[setNo].insert(entry);
0410         }
0411     }
0412 }
0413 /*
0414    if (m_testType == Prefs::EnumTestType::ArticleTest) {
0415    KMessageBox::information(0,
0416    i18n("You selected to practice the genders of nouns, but no appropriate nouns could be found. Use \"Edit Entry\" and select Noun as word type and the
0417 gender."), i18n("No valid word type found")); return;
0418 }
0419    if (m_testType == Prefs::EnumTestType::ComparisonTest) {
0420    KMessageBox::information(0,
0421    i18n("You selected to practice comparison forms, but no adjectives or adverbs containing comparison forms could be found. Use \"Edit Entry\" and select
0422 Adverb or Adjective as word type and enter the comparison forms."), i18n("No valid word type found")); return;
0423 }
0424    if (m_testType == Prefs::EnumTestType::ConjugationTest) {
0425    KMessageBox::information(0, i18n("You selected to practice conjugations, but no vocabulary containing conjugations in the tenses you selected could be found.
0426 Use \"Edit Entry\" and select Verb as word type and enter the conjugation forms."), i18n("No valid word type found")); return;
0427 }
0428 }
0429 
0430    if ( removeTestEntryList.count() == m_entries.count() ) {
0431    if ( KMessageBox::questionYesNo(0, i18n("<p>The units you selected for the practice contain no entries when the threshold settings are respected.</p><p>Hint:
0432 To configure the thresholds use the \"Threshold Page\" in the \"Configure Practice\" dialog.</p><p>Would you like to ignore the threshold setting?</p>"),
0433 i18n("No Entries with Current Threshold Settings") ) == KMessageBox::No ) { return;
0434 }
0435 
0436 */
0437 
0438 void EntryFilter::cleanupInvalid(int setNo)
0439 {
0440     Prefs::EnumPracticeMode::type mode = Prefs::practiceMode();
0441     bool wordTypeNeeded = (mode == Prefs::EnumPracticeMode::GenderPractice) || (mode == Prefs::EnumPracticeMode::ComparisonPractice)
0442         || (mode == Prefs::EnumPracticeMode::ConjugationPractice);
0443 
0444     QSet<KEduVocExpression *>::iterator i = m_entries[setNo].begin();
0445     while (i != m_entries[setNo].end()) {
0446         KEduVocTranslation *fromTranslation = (*i)->translation(m_fromTranslation);
0447         KEduVocTranslation *toTranslation = (*i)->translation(m_toTranslation);
0448 
0449         // Remove empty entries.
0450         bool keep = ((!fromTranslation->text().isEmpty() || (Prefs::allowImageInsteadOfWord() && !fromTranslation->imageUrl().isEmpty()))
0451                      && !toTranslation->text().isEmpty());
0452         if (!keep) {
0453             i = m_entries[setNo].erase(i);
0454             // debugEntry("Removing empty:", *i, fromTranslation, toTranslation);
0455             continue;
0456         }
0457 
0458         // For grammar stuff we need the word to have its word type set, else continue
0459         if (wordTypeNeeded && !(*i)->translation(m_toTranslation)->wordType()) {
0460             i = m_entries[setNo].erase(i);
0461             continue;
0462         }
0463 
0464         // Grammar modes need different things:
0465         switch (Prefs::practiceMode()) {
0466             // example sentences: need the example sentence to exist
0467         case Prefs::EnumPracticeMode::ExampleSentencesPractice:
0468             if ((*i)->translation(m_toTranslation)->example().simplified().isEmpty()) {
0469                 i = m_entries[setNo].erase(i);
0470                 continue;
0471             }
0472             break;
0473 
0474         case Prefs::EnumPracticeMode::GenderPractice:
0475             if (!((*i)->translation(m_toTranslation)->wordType()->wordType() & KEduVocWordFlag::Noun)) {
0476                 i = m_entries[setNo].erase(i);
0477                 continue;
0478             }
0479             break;
0480 
0481         case Prefs::EnumPracticeMode::ComparisonPractice:
0482             if (
0483                 // only adjective/adverb
0484                 (((*i)->translation(m_toTranslation)->wordType()->wordType() != KEduVocWordFlag::Adjective)
0485                  && ((*i)->translation(m_toTranslation)->wordType()->wordType() != KEduVocWordFlag::Adverb))
0486                 // at least one comparison forms is there
0487                 || ((*i)->translation(m_toTranslation)->comparativeForm().text().isEmpty()
0488                     || (*i)->translation(m_toTranslation)->superlativeForm().text().isEmpty())) {
0489                 i = m_entries[setNo].erase(i);
0490                 continue;
0491             }
0492             break;
0493 
0494         case Prefs::EnumPracticeMode::ConjugationPractice: {
0495             KEduVocTranslation *translation = (*i)->translation(m_toTranslation);
0496             bool erase = false;
0497 
0498             // Remove entries which are not verbs
0499             if (translation->wordType()->wordType() != KEduVocWordFlag::Verb) {
0500                 erase = true;
0501             }
0502 
0503             // Remove entries which don't have any of the tenses which are configured for practice
0504             QSet<QString> practice_tenses = QSet<QString>(m_tenses.begin(), m_tenses.end());
0505             QSet<QString> existing_tenses;
0506             const QStringList conjugationTenses = translation->conjugationTenses();
0507             for (const QString &tense : conjugationTenses) {
0508                 if (!translation->getConjugation(tense).isEmpty()) {
0509                     existing_tenses << tense;
0510                 }
0511             }
0512             if (!existing_tenses.intersects(practice_tenses))
0513                 erase = true;
0514 
0515             if (erase) {
0516                 i = m_entries[setNo].erase(i);
0517                 continue;
0518             }
0519             break;
0520         }
0521         default:
0522             break;
0523         } // switch
0524         ++i;
0525     } // while
0526     // qDebug() << "Invalid items removed. Remaining: " << m_entries[setNo].count();
0527 }
0528 
0529 QList<TestEntry *> EntryFilter::conjugationTestEntries(bool ignoreBlocked) const
0530 {
0531     //     qDebug() << "Filtering conjugation entries for tenses... " << m_tenses;
0532 
0533     // TODO CM make this configurable
0534     enum MODE { M_SEPARATE, M_COMPLETE };
0535     MODE mode = M_SEPARATE;
0536 
0537     QList<TestEntry *> testEntries;
0538     for (int i = 0; i < m_numSets; ++i) {
0539         for (KEduVocExpression *entry : qAsConst(m_currentSelection[i])) {
0540             const QStringList conjugationTenses = entry->translation(m_toTranslation)->conjugationTenses();
0541             for (const QString &tense : conjugationTenses) {
0542                 // Only include tenses which are both non-empty and which should be practiced
0543                 if (!m_tenses.contains(tense)) {
0544                     continue;
0545                 }
0546                 KEduVocConjugation conjugation = entry->translation(m_toTranslation)->getConjugation(tense);
0547                 if (conjugation.isEmpty()) {
0548                     continue;
0549                 }
0550 
0551                 bool blocked = true;
0552                 const QList<KEduVocWordFlags> pronouns = conjugation.keys();
0553                 for (const KEduVocWordFlags &pronoun : pronouns) {
0554                     KEduVocText *grade = &conjugation.conjugation(pronoun);
0555                     if (ignoreBlocked || !isBlocked(grade)) {
0556                         blocked = false;
0557 
0558                         if (mode == M_SEPARATE) {
0559                             TestEntry *testEntry = new TestEntry(entry);
0560                             testEntry->setConjugationTense(tense);
0561                             testEntry->setLanguageTo(m_toTranslation);
0562                             testEntry->setLanguageFrom(m_fromTranslation);
0563                             QList<KEduVocWordFlags> list;
0564                             list << pronoun;
0565                             testEntry->setConjugationPronouns(list);
0566                             randomizedInsert(testEntries, testEntry);
0567                         }
0568                     }
0569                 }
0570 
0571                 if (!blocked && mode == M_COMPLETE) {
0572                     TestEntry *testEntry = new TestEntry(entry);
0573                     testEntry->setConjugationTense(tense);
0574                     testEntry->setConjugationPronouns(pronouns);
0575                     testEntry->setLanguageTo(m_toTranslation);
0576                     testEntry->setLanguageFrom(m_fromTranslation);
0577                     randomizedInsert(testEntries, testEntry);
0578                 }
0579             }
0580         }
0581     }
0582     return testEntries;
0583 }
0584 
0585 void EntryFilter::updateCurrentSelection()
0586 {
0587     for (int i = 0; i < m_numSets; ++i) {
0588         m_currentSelection[i] = m_entries[i];
0589         m_currentSelection[i] = m_currentSelection[i].intersect(m_entriesLesson[i]);
0590         m_currentSelection[i] = m_currentSelection[i].intersect(m_entriesWordType[i]);
0591         m_currentSelection[i] = m_currentSelection[i].intersect(m_entriesNotBlocked[i]);
0592         m_currentSelection[i] = m_currentSelection[i].intersect(m_entriesTimesWrong[i]);
0593         m_currentSelection[i] = m_currentSelection[i].intersect(m_entriesTimesPracticed[i]);
0594         m_currentSelection[i] = m_currentSelection[i].intersect(m_entriesMinMaxGrade[i]);
0595     }
0596 }
0597 
0598 // ----------------------------------------------------------------
0599 //                             Utilities
0600 
0601 void EntryFilter::randomizedInsert(QList<TestEntry *> &list, TestEntry *entry)
0602 {
0603     list.insert(QRandomGenerator::global()->bounded(list.size() + 1), entry);
0604 }
0605 
0606 #include "moc_entryfilter.cpp"