File indexing completed on 2024-04-21 03:51:11
0001 /* 0002 SPDX-FileCopyrightText: 2015 Inge Wallin <inge@lysator.liu.se> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 // Own 0007 #include "utils.h" 0008 0009 // Qt 0010 #include <QPainter> 0011 #include <QPainterPath> 0012 #include <QRect> 0013 0014 // KEduVocDocument library 0015 #include <KEduVocDocument> 0016 #include <KEduVocExpression> 0017 0018 // Parley 0019 #include "prefs.h" 0020 0021 // ---------------------------------------------------------------- 0022 // class WordCount 0023 0024 WordCount::WordCount() 0025 { 0026 clear(); 0027 } 0028 0029 void WordCount::clear() 0030 { 0031 for (int i = 0; i <= KV_MAX_GRADE; ++i) { 0032 grades[i] = 0; 0033 pregrades[i] = 0; 0034 } 0035 invalid = 0; 0036 0037 initialWords = 0; 0038 totalWords = 0; 0039 } 0040 0041 int WordCount::percentageCompleted() const 0042 { 0043 // To calculate the percentage done we add: 0044 // * 1..KV_MAX_GRADE points for words in the initial phase (grade = 0, pregrade > 0) 0045 // * KV_MAX_GRADE * (1..KV_MAX_GRADE) points for words in the long-term phase (grade>0) 0046 // So the maximum number of points is KV_MAX_GRADE^2 per word. 0047 // 0048 // In the final calculation, we exclude all invalid words from the percentage. 0049 int points = 0; 0050 for (int i = 0; i < KV_MAX_GRADE + 1; ++i) { 0051 points += pregrades[i] * i; 0052 points += grades[i] * KV_MAX_GRADE * i; 0053 } 0054 0055 if (totalWords - invalid == 0) { 0056 // Don't divide by 0. 0057 return 0; 0058 } else { 0059 return 100 * points / ((totalWords - invalid) * KV_MAX_GRADE * KV_MAX_GRADE); 0060 } 0061 } 0062 0063 void WordCount::fillFromContainer(KEduVocContainer &container, int translationIndex, KEduVocContainer::EnumEntriesRecursive recursive) 0064 { 0065 clear(); 0066 0067 const QList<KEduVocExpression *> entries = container.entries(recursive); 0068 for (KEduVocExpression *entry : entries) { 0069 KEduVocTranslation &translation(*entry->translation(translationIndex)); 0070 evaluateWord(translation, translation.text()); 0071 } 0072 } 0073 0074 void WordCount::fillFromContainerForPracticeMode(KEduVocContainer &container, 0075 int translationIndex, 0076 const QStringList &activeConjugationTenses, 0077 KEduVocContainer::EnumEntriesRecursive recursive) 0078 { 0079 KEduVocWordFlags wordTypeToProcess(KEduVocWordFlag::NoInformation); 0080 switch (Prefs::practiceMode()) { 0081 case Prefs::EnumPracticeMode::GenderPractice: 0082 wordTypeToProcess = KEduVocWordFlag::Noun; 0083 break; 0084 case Prefs::EnumPracticeMode::ConjugationPractice: 0085 wordTypeToProcess = KEduVocWordFlag::Verb; 0086 break; 0087 case Prefs::EnumPracticeMode::ComparisonPractice: 0088 wordTypeToProcess = KEduVocWordFlag::Adjective | KEduVocWordFlag::Adverb; 0089 break; 0090 default: 0091 fillFromContainer(container, translationIndex, recursive); 0092 return; 0093 } 0094 0095 clear(); 0096 0097 const QList<KEduVocExpression *> entries = container.entries(recursive); 0098 for (KEduVocExpression *entry : entries) { 0099 KEduVocTranslation &translation(*entry->translation(translationIndex)); 0100 if (isValidForProcessing(translation, wordTypeToProcess)) { 0101 switch (wordTypeToProcess) { 0102 case KEduVocWordFlag::Noun: { 0103 KEduVocText article = translation.article(); 0104 evaluateWord(article, translation.text()); 0105 } break; 0106 case KEduVocWordFlag::Verb: { 0107 QStringList conjugationTenses = translation.conjugationTenses(); 0108 for (const QString &activeTense : qAsConst(activeConjugationTenses)) { 0109 if (conjugationTenses.contains(activeTense)) { 0110 KEduVocConjugation conj = translation.getConjugation(activeTense); 0111 const QList<KEduVocWordFlags> keys = conj.keys(); 0112 for (KEduVocWordFlags key : keys) { 0113 KEduVocText person = conj.conjugation(key); 0114 evaluateWord(person, person.text()); 0115 } 0116 } 0117 } 0118 } break; 0119 case KEduVocWordFlag::Adjective | KEduVocWordFlag::Adverb: { 0120 KEduVocText comparative = translation.comparativeForm(); 0121 evaluateWord(comparative, comparative.text()); 0122 KEduVocText superlative = translation.superlativeForm(); 0123 evaluateWord(superlative, superlative.text()); 0124 } break; 0125 } 0126 } 0127 } 0128 } 0129 0130 bool WordCount::isValidForProcessing(KEduVocTranslation &trans, KEduVocWordFlags wordType) const 0131 { 0132 return !trans.isEmpty() && (trans.wordType() != nullptr) && ((trans.wordType()->wordType() & wordType) != 0); 0133 } 0134 0135 void WordCount::evaluateWord(const KEduVocText &item, const QString &text) 0136 { 0137 ++totalWords; 0138 if (text.isEmpty()) { 0139 ++invalid; 0140 } else if (item.preGrade() > 0) { 0141 // Initial phase (we assume correctness, i.e. if pregrade>0 then grade = 0) 0142 ++initialWords; 0143 ++pregrades[item.preGrade()]; 0144 } else { 0145 // Long term or unpracticed 0146 ++grades[item.grade()]; 0147 } 0148 } 0149 0150 // ---------------------------------------------------------------- 0151 // class confidenceColors 0152 0153 ConfidenceColors::ConfidenceColors(ColorScheme colorScheme) 0154 { 0155 initColors(colorScheme); 0156 } 0157 0158 void ConfidenceColors::initColors(ColorScheme colorScheme) 0159 { 0160 switch (colorScheme) { 0161 case MultiColorScheme: 0162 default: // Not default at the last line. Hope this works... 0163 0164 longTermColors[0] = QColor(25, 38, 41); 0165 // longTermColors[1] = QColor(Qt::yellow); 0166 longTermColors[1] = QColor(25, 38, 41, 64); 0167 longTermColors[2] = QColor(237, 21, 21); 0168 longTermColors[3] = QColor(246, 116, 0); 0169 longTermColors[4] = QColor(201, 206, 59); 0170 longTermColors[5] = QColor(28, 220, 154); 0171 longTermColors[6] = QColor(17, 209, 22); 0172 longTermColors[7] = QColor(61, 174, 253); 0173 0174 initialTermColor = QColor(25, 38, 41, 64); // Find something else 0175 0176 invalidColor = QColor(Qt::red); 0177 break; 0178 0179 case ProgressiveColorScheme: { 0180 static const int AlphaMax = 255; 0181 static const int AlphaStep = ((AlphaMax - 10) / KV_MAX_GRADE); 0182 0183 QColor color; 0184 0185 // Confidence 1..max 0186 for (int grade = 1; grade <= KV_MAX_GRADE; ++grade) { 0187 color = Prefs::gradeColor(); 0188 color.setAlpha(AlphaMax - (KV_MAX_GRADE - grade) * AlphaStep); 0189 0190 longTermColors[grade] = color; 0191 } 0192 0193 // Unpracticed (confidence 0) 0194 color = QColor("#FFFFFF"); 0195 color.setAlpha(AlphaMax); 0196 longTermColors[0] = color; 0197 0198 // Use one color for all initial phase values 0199 color = Prefs::preGradeColor(); 0200 color.setAlpha(AlphaMax); 0201 initialTermColor = color; 0202 0203 // Invalid 0204 invalidColor = Prefs::invalidUnitColor(); 0205 0206 break; 0207 } 0208 } 0209 0210 // These two are placeholders for the wordcloud background color. 0211 frontEndColors[0] = QColor(255, 221, 217); 0212 frontEndColors[1] = QColor(238, 232, 213); 0213 } 0214 0215 // ---------------------------------------------------------------- 0216 // Various utility functions 0217 0218 void paintColorBar(QPainter &painter, const QRect &rect, const WordCount &wordCount, const ConfidenceColors &colors) 0219 { 0220 // The outline of the total bar. 0221 QRectF roundedRect(rect); 0222 roundedRect.adjust(1.0, 1.0, -1.0, -1.0); 0223 0224 // Set a rounded clipping region to paint the bar segments in 0225 QPainterPath clippingPath; 0226 clippingPath.addRoundedRect(roundedRect, 2.0, 2.0); 0227 painter.setClipPath(clippingPath); 0228 0229 qreal xPosition = 0.0; 0230 0231 // >0: grade, 0: initial, -1: untrained, -2: invalid 0232 for (int i = KV_MAX_GRADE; i >= -2; --i) { 0233 qreal fraction; 0234 QColor color; 0235 0236 // Get the fraction and the color 0237 if (i > 0) { 0238 // long term 0239 fraction = qreal(wordCount.grades[i]) / qreal(wordCount.totalWords); 0240 color = colors.longTermColors[i]; 0241 } else if (i == 0) { 0242 // initial term 0243 fraction = qreal(wordCount.initialWords) / qreal(wordCount.totalWords); 0244 color = colors.initialTermColor; 0245 } else if (i == -1) { 0246 // untrained (stored in longterm[0]) 0247 fraction = qreal(wordCount.grades[0]) / qreal(wordCount.totalWords); 0248 color = colors.longTermColors[0]; 0249 } else { 0250 fraction = qreal(wordCount.invalid) / qreal(wordCount.totalWords); 0251 color = colors.invalidColor; 0252 } 0253 0254 // Create a rect from the current fraction 0255 qreal barElementWidth = fraction * roundedRect.width(); 0256 QRectF barElement(roundedRect.x() + xPosition, roundedRect.y(), barElementWidth, roundedRect.height()); 0257 xPosition += barElementWidth; 0258 0259 // Paint! 0260 painter.setBrush(QBrush(color)); 0261 painter.drawRect(barElement); 0262 } 0263 0264 // Draw the outline 0265 painter.setClipping(false); 0266 painter.setBrush(Qt::NoBrush); 0267 painter.drawRoundedRect(roundedRect, 2.0, 2.0); 0268 }