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