Warning, file /education/khangman/src/khangman.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /*************************************************************************** 0002 * Copyright 2001-2009 Anne-Marie Mahfouf <annma@kde.org> * 0003 * Copyright 2014 Rahul Chowdhury <rahul.chowdhury@kdemail.net> * 0004 * * 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, write to the * 0018 * Free Software Foundation, Inc., * 0019 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * 0020 ***************************************************************************/ 0021 0022 0023 #include "khangman.h" 0024 0025 #include <QApplication> 0026 #include <QStandardPaths> 0027 #include <QQuickWidget> 0028 #include <QQmlContext> 0029 #include <QQmlEngine> 0030 0031 #include <KLocalizedContext> 0032 #include <KLocalizedString> 0033 #include <KMessageBox> 0034 #include <KRandom> 0035 #include <knewstuff_version.h> 0036 #if KNEWSTUFF_VERSION < QT_VERSION_CHECK(5, 240, 0) 0037 #include <KNS3/QtQuickDialogWrapper> 0038 #else 0039 #include <KNSWidgets/QtQuickDialogWrapper> 0040 #endif 0041 #include <KHelpMenu> 0042 0043 #include <KEduVocDocument> 0044 #include <keduvocexpression.h> 0045 #include <sharedkvtmlfiles.h> 0046 0047 #include "langutils.h" 0048 #include "prefs.h" 0049 0050 KHangMan::KHangMan() 0051 : QMainWindow(), 0052 m_currentCategory(0), 0053 m_currentLanguage(0), 0054 m_winCount(0), 0055 m_lossCount(0), 0056 m_randomInt(0), 0057 m_scoreMultiplyingFactor(1), 0058 m_netScore(0), 0059 m_helpMenu(new KHelpMenu(NULL)) 0060 { 0061 setObjectName(QStringLiteral("KHangMan")); 0062 0063 m_view = new QQuickWidget(this); 0064 m_view->rootContext()->setContextProperty(QStringLiteral("khangman"), this); 0065 0066 // prepare i18n 0067 auto context = new KLocalizedContext(this); 0068 m_view->engine()->rootContext()->setContextObject(context); 0069 0070 KConfigGroup windowConfig = config(QStringLiteral("Window")); 0071 if (windowConfig.hasKey("geometry")) { 0072 setGeometry(windowConfig.readEntry<QRect>("geometry", QRect())); 0073 setWindowState(Qt::WindowState(windowConfig.readEntry("windowState").toInt())); 0074 } 0075 0076 setMinimumSize(800, 600); 0077 0078 m_view->setResizeMode(QQuickWidget::SizeRootObjectToView); 0079 0080 setCentralWidget(m_view); 0081 scanLanguages(); 0082 0083 //find the themes 0084 m_themeFactory.addTheme(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("khangman/themes/standardthemes.xml"))); 0085 } 0086 0087 KHangMan::~KHangMan() 0088 { 0089 KConfigGroup windowConfig = config(QStringLiteral("Window")); 0090 windowConfig.writeEntry("geometry", geometry()); 0091 windowConfig.writeEntry("windowState", int(windowState())); 0092 0093 delete m_view; 0094 m_view = NULL; 0095 } 0096 0097 void KHangMan::showAboutKHangMan() 0098 { 0099 m_helpMenu->aboutApplication(); 0100 } 0101 0102 void KHangMan::showAboutKDE() 0103 { 0104 m_helpMenu->aboutKDE(); 0105 } 0106 0107 void KHangMan::showHandbook() 0108 { 0109 m_helpMenu->appHelpActivated(); 0110 } 0111 0112 KConfigGroup KHangMan::config(const QString &group) 0113 { 0114 return KConfigGroup(KSharedConfig::openConfig(qApp->applicationName() + QStringLiteral("rc")), group); 0115 } 0116 0117 // ---------------------------------------------------------------- 0118 // Slots 0119 void KHangMan::setCurrentCategory(int index) 0120 { 0121 QMap<QString, QString>::const_iterator currentLevel = m_titleLevels.constBegin() + index; 0122 Prefs::setCurrentLevel(index); 0123 Prefs::setLevelFile(currentLevel.value()); 0124 Prefs::self()->save(); 0125 m_currentCategory = index; 0126 Q_EMIT currentCategoryChanged(); 0127 } 0128 0129 void KHangMan::setCurrentLanguage(int index) 0130 { 0131 if (index >= 0 && index < m_languages.size()) { 0132 Prefs::setSelectedLanguage(m_languages[index]); 0133 m_currentLanguage = index; 0134 Prefs::self()->save(); 0135 loadLevels(); 0136 Q_EMIT currentLanguageChanged(); 0137 } 0138 } 0139 0140 void KHangMan::setCurrentTheme(int index) 0141 { 0142 const KHMTheme *theme = m_themeFactory.getTheme(index); 0143 Prefs::setTheme(theme->name()); 0144 Prefs::self()->save(); 0145 Q_EMIT currentThemeChanged(); 0146 } 0147 0148 void KHangMan::readFile() 0149 { 0150 // Check if the data files are installed in the correct dir. 0151 QFile myFile; 0152 myFile.setFileName(Prefs::levelFile()); 0153 0154 if (!myFile.exists()) { 0155 QString mString = i18n("File $KDEDIR/share/apps/kvtml/%1/%2 not found.\n" 0156 "Please check your installation.", 0157 Prefs::selectedLanguage(), 0158 Prefs::levelFile()); 0159 KMessageBox::error (this, mString, i18n("Error")); 0160 } 0161 0162 // Detects if file is a kvtml file so that it's a hint enable file 0163 slotSetWordsSequence(); 0164 } 0165 0166 void KHangMan::nextWord() 0167 { 0168 // If there are no words, there's nothing we can do, trying will crash 0169 if (m_randomList.isEmpty()) { 0170 return; 0171 } 0172 0173 // Pick words from m_randomList sequentially since it is already shuffled. 0174 int whichWord = m_randomInt++ % m_randomList.count(); 0175 m_originalWord = m_randomList[whichWord].first; 0176 m_originalWord = m_originalWord.toUpper(); 0177 m_hint = m_randomList[whichWord].second; 0178 0179 Q_EMIT currentHintChanged(); 0180 0181 m_currentWord.clear(); 0182 0183 // Find dashes, spaces, middot and apostrophe 0184 const QString search = QStringLiteral("- ยท'"); 0185 for (const QChar &c : m_originalWord) { 0186 if (search.contains(c)) { 0187 m_currentWord.append(c); 0188 } else { 0189 m_currentWord.append(QLatin1Char('_')); 0190 } 0191 } 0192 0193 Q_EMIT currentWordChanged(); 0194 } 0195 0196 void KHangMan::slotSetWordsSequence() 0197 { 0198 //Current level file 0199 KEduVocDocument doc(this); 0200 0201 ///@todo open returns KEduVocDocument::ErrorCode 0202 doc.open(QUrl::fromLocalFile(Prefs::levelFile()), KEduVocDocument::FileIgnoreLock); 0203 0204 //how many words in the file 0205 QList<KEduVocExpression *> words = doc.lesson()->entries(KEduVocLesson::Recursive); 0206 0207 //get the words+hints 0208 m_randomList.clear(); 0209 for (KEduVocExpression* entry : words) { 0210 QString hint = entry->translation(0)->comment(); 0211 if (hint.isEmpty() && doc.identifierCount() > 0) { 0212 // if there is no comment or it's empty, use the first translation if there is one 0213 hint = entry->translation(1)->text(); 0214 } 0215 QString text = entry->translation(0)->text(); 0216 if (!text.isEmpty()) { 0217 m_randomList.append(qMakePair(text, hint)); 0218 } 0219 } 0220 //shuffle the list 0221 KRandom::shuffle(m_randomList); 0222 } 0223 0224 QString KHangMan::stripAccents(const QString & original) 0225 { 0226 QString noAccents; 0227 QString decomposed = original.normalized(QString::NormalizationForm_D); 0228 for (int i = 0; i < decomposed.length(); ++i) { 0229 if ( decomposed[i].category() != QChar::Mark_NonSpacing ) { 0230 noAccents.append(decomposed[i]); 0231 } 0232 } 0233 return noAccents; 0234 } 0235 0236 void KHangMan::replaceLetters(const QString& charString) 0237 { 0238 QChar ch = charString.at(0); 0239 bool oneLetter = Prefs::oneLetter(); 0240 0241 for (int i = 0; i < m_originalWord.size(); ++i) { 0242 if (m_originalWord.at(i) == ch) { 0243 m_currentWord[i] = ch; 0244 0245 if (oneLetter) 0246 break; 0247 } 0248 } 0249 Q_EMIT currentWordChanged(); 0250 } 0251 0252 bool KHangMan::isResolved() const 0253 { 0254 return m_currentWord == m_originalWord; 0255 } 0256 0257 void KHangMan::revealCurrentWord() 0258 { 0259 m_currentWord = m_originalWord; 0260 Q_EMIT currentWordChanged(); 0261 } 0262 0263 void KHangMan::calculateNetScore() 0264 { 0265 m_netScore = ( m_winCount - m_lossCount ) * m_scoreMultiplyingFactor; 0266 Q_EMIT netScoreChanged(); 0267 } 0268 0269 bool KHangMan::containsChar(const QString &sChar) 0270 { 0271 return m_originalWord.contains(sChar) || stripAccents(m_originalWord).contains(sChar); 0272 } 0273 0274 int KHangMan::resolveTime() 0275 { 0276 return Prefs::resolveTime(); 0277 } 0278 0279 void KHangMan::setResolveTime(int resolveTime) 0280 { 0281 Prefs::setResolveTime(resolveTime); 0282 Q_EMIT resolveTimeChanged(); 0283 } 0284 0285 bool KHangMan::soundEnabled() 0286 { 0287 return Prefs::sound(); 0288 } 0289 0290 void KHangMan::setSoundEnabled(bool sound) 0291 { 0292 Prefs::setSound(sound); 0293 Q_EMIT soundEnabledChanged(); 0294 } 0295 0296 QStringList KHangMan::languages() 0297 { 0298 return m_languageNames; 0299 } 0300 0301 int KHangMan::winCount() const 0302 { 0303 return m_winCount; 0304 } 0305 0306 void KHangMan::setWinCount(int count) 0307 { 0308 m_winCount = count; 0309 calculateNetScore(); 0310 Q_EMIT winCountChanged(); 0311 } 0312 0313 int KHangMan::lossCount() const 0314 { 0315 return m_lossCount; 0316 } 0317 0318 void KHangMan::setLossCount(int count) 0319 { 0320 m_lossCount = count; 0321 calculateNetScore(); 0322 Q_EMIT lossCountChanged(); 0323 } 0324 0325 int KHangMan::scoreMultiplyingFactor() const 0326 { 0327 return m_scoreMultiplyingFactor; 0328 } 0329 0330 void KHangMan::setScoreMultiplyingFactor( int factor ) 0331 { 0332 m_scoreMultiplyingFactor = factor; 0333 calculateNetScore(); 0334 Q_EMIT scoreMultiplyingFactorChanged(); 0335 } 0336 0337 int KHangMan::netScore() const 0338 { 0339 return m_netScore; 0340 } 0341 0342 int KHangMan::currentLanguage() 0343 { 0344 return m_currentLanguage; 0345 } 0346 0347 QStringList KHangMan::themes() 0348 { 0349 return m_themeFactory.themeList(); 0350 } 0351 0352 int KHangMan::currentTheme() 0353 { 0354 QStringList themes = m_themeFactory.getNames(); 0355 return themes.indexOf(Prefs::theme()); 0356 } 0357 0358 QString KHangMan::backgroundUrl() 0359 { 0360 const KHMTheme *theme = m_themeFactory.getTheme(currentTheme()); 0361 if (theme) { 0362 return QStandardPaths::locate(QStandardPaths::AppLocalDataLocation, QStringLiteral("themes/") + theme->svgFileName()); 0363 } 0364 return QString(); 0365 } 0366 0367 QColor KHangMan::currentThemeLetterColor() 0368 { 0369 const KHMTheme *theme = m_themeFactory.getTheme(currentTheme()); 0370 if (theme) { 0371 return theme->letterColor(); 0372 } 0373 0374 // Default to white letters 0375 return QColor("white"); 0376 } 0377 0378 QStringList KHangMan::categories() 0379 { 0380 return m_titleLevels.keys(); 0381 } 0382 0383 int KHangMan::currentCategory() 0384 { 0385 return m_currentCategory; 0386 } 0387 0388 QStringList KHangMan::currentWord() const 0389 { 0390 QStringList currentWordLetters; 0391 0392 for (const QChar& currentWordLetter : std::as_const(m_currentWord)) 0393 { 0394 currentWordLetters.append(currentWordLetter); 0395 } 0396 0397 return currentWordLetters; 0398 } 0399 0400 QString KHangMan::getCurrentHint() const 0401 { 0402 return m_hint; 0403 } 0404 0405 QQmlEngine* KHangMan::getEngine() 0406 { 0407 return m_view->engine(); 0408 } 0409 0410 QStringList KHangMan::alphabet() const 0411 { 0412 QStringList letterList; 0413 for (char c = 'A'; c <= 'Z'; ++c) { 0414 letterList.append(QChar(c)); 0415 } 0416 0417 letterList.append(m_specialCharacters); 0418 0419 return letterList; 0420 } 0421 0422 // ---------------------------------------------------------------- 0423 0424 void KHangMan::scanLanguages() 0425 { 0426 m_languageNames.clear(); 0427 0428 //the program scans in khangman/data/ to see what languages data is found 0429 m_languages = SharedKvtmlFiles::languages(); 0430 if (m_languages.isEmpty()) { 0431 qApp->closeAllWindows(); 0432 } 0433 //find duplicated entries in KDEDIR and KDEHOME 0434 0435 // Write the present languages in config so they cannot be downloaded. 0436 // FIXME: use pre-seeding here 0437 KConfigGroup cg( KSharedConfig::openConfig() ,"KNewStuff2"); 0438 for (int i=0; i<m_languages.count(); ++i) { 0439 //QString tmp = cg.readEntry(m_languages[i]); 0440 // if (!tmp) 0441 cg.writeEntry(m_languages[i], QDate::currentDate().toString(Qt::ISODate)); 0442 } 0443 cg.config()->sync(); 0444 0445 for (int i = 0; i < m_languages.size(); ++i) { 0446 QLocale locale(m_languages[i]); 0447 QString languageName = locale.nativeLanguageName(); 0448 if (languageName.isEmpty()) { 0449 languageName = i18nc("@item:inlistbox no language for that locale","None"); 0450 } 0451 m_languageNames.append(languageName); 0452 0453 if (m_languages[i] == Prefs::selectedLanguage()) 0454 m_currentLanguage = i; 0455 } 0456 0457 Q_EMIT languagesChanged(); 0458 } 0459 0460 void KHangMan::show() 0461 { 0462 loadLevels(); 0463 // kvtml files have been found 0464 0465 if (m_themeFactory.getQty() == 0) { // themes not present 0466 KMessageBox::error(this, i18n("No theme files found."), i18n("Error")); 0467 exit(EXIT_FAILURE); 0468 } 0469 0470 QMainWindow::show(); 0471 // add the qml view as the main widget 0472 QString location = QStandardPaths::locate(QStandardPaths::AppLocalDataLocation, QStringLiteral("qml/main.qml")); 0473 QUrl url = QUrl::fromLocalFile(location); 0474 m_view->setSource(url); 0475 } 0476 0477 void KHangMan::loadLevels() 0478 { 0479 //build the Level combobox menu dynamically depending of the data for each language 0480 m_titleLevels.clear(); 0481 QStringList levelFilenames = SharedKvtmlFiles::fileNames(Prefs::selectedLanguage()); 0482 0483 if (levelFilenames.isEmpty()) { 0484 Prefs::setSelectedLanguage(QStringLiteral("en")); 0485 Prefs::self()->save(); 0486 levelFilenames = SharedKvtmlFiles::fileNames(Prefs::selectedLanguage()); 0487 if (levelFilenames.isEmpty()) { 0488 KMessageBox::error(this, i18n("No kvtml files found."), i18n("Error")); 0489 exit(EXIT_FAILURE); 0490 } 0491 } 0492 0493 const QStringList titles = SharedKvtmlFiles::titles(Prefs::selectedLanguage()); 0494 Q_ASSERT(levelFilenames.count() == titles.count()); 0495 for(int i = 0; i < levelFilenames.count(); ++i) { 0496 m_titleLevels.insert(titles.at(i), levelFilenames.at(i)); 0497 } 0498 Q_EMIT categoriesChanged(); 0499 0500 int level = Prefs::currentLevel(); 0501 if (level >= m_titleLevels.count() || !levelFilenames.contains(Prefs::levelFile())) { 0502 level = 0; 0503 } 0504 setCurrentCategory(level); 0505 0506 loadLanguageSpecialCharacters(); 0507 } 0508 0509 void KHangMan::slotDownloadNewStuff() 0510 { 0511 #if KNEWSTUFF_VERSION < QT_VERSION_CHECK(5, 240, 0) 0512 KNS3::QtQuickDialogWrapper *dialog = new KNS3::QtQuickDialogWrapper(QStringLiteral("khangman.knsrc"), this); 0513 #else 0514 KNSWidgets::QtQuickDialogWrapper *dialog = new KNSWidgets::QtQuickDialogWrapper(QStringLiteral("khangman.knsrc"), this); 0515 #endif 0516 dialog->open(); 0517 0518 #if KNEWSTUFF_VERSION < QT_VERSION_CHECK(5, 240, 0) 0519 connect(dialog, &KNS3::QtQuickDialogWrapper::closed, this, [this, dialog] { 0520 #else 0521 connect(dialog, &KNSWidgets::QtQuickDialogWrapper::closed, this, [this, dialog] { 0522 #endif 0523 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0524 const QList<KNSCore::EntryInternal> entries = dialog->changedEntries(); 0525 #else 0526 const QList<KNSCore::Entry> entries = dialog->changedEntries(); 0527 #endif 0528 if ( !entries.isEmpty() ){ 0529 SharedKvtmlFiles::sortDownloadedFiles(); 0530 //look for languages dirs installed 0531 scanLanguages(); 0532 //refresh Languages menu 0533 setCurrentLanguage(m_languages.indexOf(Prefs::selectedLanguage())); 0534 } 0535 0536 dialog->deleteLater(); 0537 }); 0538 } 0539 0540 void KHangMan::loadLanguageSpecialCharacters() 0541 { 0542 QString lang = Prefs::selectedLanguage(); 0543 if (lang.isEmpty()) 0544 return; 0545 0546 m_specialCharacters.clear(); 0547 if (!LangUtils::hasSpecialChars(lang)) { 0548 return; 0549 } 0550 0551 QString langFileName=QStringLiteral("khangman/%1.txt").arg(lang); 0552 QFile langFile; 0553 langFile.setFileName(QStandardPaths::locate(QStandardPaths::GenericDataLocation, langFileName)); 0554 0555 // Let's look in local KDEHOME dir then KNS installs each .txt 0556 // in kvtml/<lang> as it installs everything at the same place 0557 if (!langFile.exists()) { 0558 langFileName = QStringLiteral("apps/kvtml/%1/%1.txt").arg(lang); 0559 langFile.setFileName(QStandardPaths::locate(QStandardPaths::GenericDataLocation, langFileName)); 0560 if (!langFile.exists()) { 0561 return; 0562 } 0563 } 0564 0565 update(); 0566 0567 // We open the file and store info into the stream... 0568 QFile openFileStream(langFile.fileName()); 0569 openFileStream.open(QIODevice::ReadOnly); 0570 QTextStream readFileStr(&openFileStream); 0571 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0572 readFileStr.setCodec("UTF-8"); 0573 #endif 0574 0575 // m_specialCharacters contains all the words from the file 0576 // FIXME: Better name 0577 m_specialCharacters = readFileStr.readAll().split(QLatin1Char('\n')); 0578 openFileStream.close(); 0579 } 0580 0581 0582 // kate: space-indent on; tab-width 4; indent-width 4; mixed-indent off; replace-tabs on; 0583 // vim: set et sw=4 ts=4 cino=l1,cs,U1: 0584