File indexing completed on 2022-09-27 12:25:41

0001 /*
0002     SPDX-FileCopyrightText: 2001-2008 Anne-Marie Mahfouf <annma@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "klettres.h"
0008 
0009 //Qt includes
0010 #include <QAction>
0011 #include <QBitmap>
0012 #include <QFile>
0013 #include <QLabel>
0014 #include <QPainter>
0015 #include <QStatusBar>
0016 #include <QIcon>
0017 #include <QTextStream>
0018 #include <QMenuBar>
0019 #include <QDomDocument>
0020 #include <QWidget>
0021 #include <QStandardPaths>
0022 #include <QPointer>
0023 
0024 //KDE includes
0025 #include <KActionCollection>
0026 #include <KConfigDialog>
0027 #include <KLocalizedString>
0028 #include <KMessageBox>
0029 #include <KSelectAction>
0030 #include <KStandardAction>
0031 #include <KToolBar>
0032 #include <KToggleAction>
0033 
0034 #include <KNS3/QtQuickDialogWrapper>
0035 //Project includes
0036 #include "ui_fontsdlg.h"
0037 #include "timer.h"
0038 #include "prefs.h"
0039 #include "langutils.h"
0040 #include "kltheme.h"
0041 
0042 class fontsdlg : public QWidget, public Ui::fontsdlg
0043 {
0044     public:
0045         fontsdlg( QWidget * parent ) : QWidget(parent)
0046         {
0047             setupUi(this);
0048         }
0049 };
0050 
0051 const int ID_KIDB      = 100;
0052 const int ID_GROWNB    = 101;
0053 const int ID_MENUBARB  = 102;
0054 
0055 KLettres::KLettres()
0056         : KXmlGuiWindow( nullptr )
0057 {
0058     setObjectName( QStringLiteral("KLettres") );
0059     mNewStuff = nullptr;
0060     m_view = new KLettresView(this);
0061     setMinimumSize( QSize( 800, 600 ) );
0062     //Tell the KXmlGuiWindow that this is indeed the main widget
0063     setCentralWidget(m_view);
0064     //Populate Languages menu with m_languageNames
0065     m_languageNames = LangUtils::getLanguagesNames(LangUtils::getLanguages());
0066     //MainWindow GUI: menus, tolbars and statusbar
0067     setupActions();
0068     setupStatusbar();
0069     setupToolbars();
0070     //Load Settings
0071     loadSettings();
0072     //Setup current language sounds
0073     soundFactory = new SoundFactory(this, QStringLiteral("sounds"));
0074     //Start game
0075     m_view->game();
0076 }
0077 
0078 KLettres::~KLettres()
0079 {
0080 }
0081 
0082 bool KLettres::loadLayout(QDomDocument &layoutDocument)
0083 {
0084     QFile layoutFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0085                         QStringLiteral("klettres/")+Prefs::language()+QStringLiteral("/sounds.xml")));
0086 
0087     //if xml file is not found, program exits
0088     if (!layoutFile.exists()) {
0089         qWarning() << "sounds.xml file not found in $KDEDIR/share/apps/klettres/" << Prefs::language() ;
0090         QString mString=i18n("The file sounds.xml was not found in\n"
0091                              "$KDEDIR/share/apps/klettres/\n\n"
0092                              "Please install this file and start KLettres again.\n\n");
0093         KMessageBox::information( this, mString,i18n("KLettres - Error") );
0094         qApp->quit();//exit(1);
0095     }
0096 
0097     if (!layoutFile.open(QIODevice::ReadOnly)) {
0098         return false;
0099     }
0100 
0101     //Check if document is well-formed
0102     if (!layoutDocument.setContent(&layoutFile)) {
0103         layoutFile.close();
0104         return false;
0105     }
0106 
0107     layoutFile.close();
0108 
0109     return true;
0110 }
0111 
0112 void KLettres::setupActions()
0113 {
0114     QAction *m_newAction = actionCollection()->addAction(QStringLiteral("play_new"));
0115     m_newAction->setText(i18n("New Sound"));
0116     actionCollection()->setDefaultShortcut(m_newAction,QKeySequence(Qt::CTRL | Qt::Key_N));
0117     m_newAction->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); // ### better icon for this?
0118     connect(m_newAction, &QAction::triggered, m_view, &KLettresView::game);
0119     m_newAction->setToolTip(i18n("Play a new sound"));
0120     m_newAction->setWhatsThis(i18n("You can play a new sound by clicking this button or using the File menu, New Sound."));
0121 
0122     QAction *m_stuffAction = actionCollection()->addAction(QStringLiteral("downloadnewstuff"));
0123     m_stuffAction->setText(i18n("Get Alphabet in New Language..."));
0124     m_stuffAction->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")));
0125     connect(m_stuffAction, &QAction::triggered, this, &KLettres::slotDownloadNewStuff);
0126 
0127     QAction *m_playAgainAction = actionCollection()->addAction(QStringLiteral("play_again"));
0128     m_playAgainAction->setText(i18n("Replay Sound"));
0129     actionCollection()->setDefaultShortcut(m_playAgainAction,QKeySequence(Qt::Key_F5));
0130     m_playAgainAction->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
0131     m_playAgainAction->setToolTip(i18n("Play the same sound again"));
0132     connect(m_playAgainAction, &QAction::triggered, m_view, &KLettresView::slotPlayAgain);
0133     m_playAgainAction->setWhatsThis(i18n("You can replay the same sound again by clicking this button or using the File menu, Replay Sound."));
0134     KStandardAction::quit(qApp, SLOT(quit()), actionCollection());
0135 
0136     m_menubarAction = KStandardAction::showMenubar(this, SLOT(slotMenubar()), actionCollection());
0137 
0138     m_levelAction = actionCollection()->add<KSelectAction>(QStringLiteral("levels"));
0139     m_levelAction->setText(i18nc("@label:listbox which difficulty level to use", "L&evel"));
0140     m_levelAction->setToolTip(i18n("Select the level"));
0141     m_levelAction->setWhatsThis(i18n("You can select the level: level 1 displays a letter and you hear it; level 2 does not display the letter, you only hear it; level 3 displays a syllable and you hear it; level 4 does not display the syllable, you only hear it."));
0142 
0143     m_languageAction = actionCollection()->add<KSelectAction>(QStringLiteral("languages"));
0144     m_languageAction->setText(i18nc("@label:listbox", "&Language"));
0145     m_languageAction->setItems(m_languageNames);
0146 
0147     m_levelsNames.append(i18ncp("@item:inlistbox choose level", "Level %1" , "Level %1", 1));
0148     m_levelsNames.append(i18ncp("@item:inlistbox choose level", "Level %1" , "Level %1", 2));
0149     m_levelsNames.append(i18ncp("@item:inlistbox choose level", "Level %1" , "Level %1", 3));
0150     m_levelsNames.append(i18ncp("@item:inlistbox choose level", "Level %1" , "Level %1", 4));
0151     m_levelAction->setItems(m_levelsNames);
0152     m_levelAction->setCurrentItem(Prefs::level()-1);
0153 
0154     m_themeAction = actionCollection()->add<KSelectAction>(QStringLiteral("looks"));
0155     m_themeAction->setText(i18n("Themes"));
0156     m_themeAction->setItems(KLThemeFactory::instance()->themeList());
0157     m_themeAction->setCurrentItem(Prefs::theme());
0158     m_themeAction->setToolTip(i18n("Select the theme"));
0159     m_themeAction->setWhatsThis(i18n("Here you can change the theme for KLettres. A theme consists in the background picture and the font color for the letter displayed."));
0160 
0161     m_kidAction = actionCollection()->add<KToggleAction>(QStringLiteral("mode_kid"));
0162     m_kidAction->setText(i18n("Mode Kid"));
0163     actionCollection()->setDefaultShortcut(m_kidAction,QKeySequence(Qt::CTRL | Qt::Key_K));
0164     m_kidAction->setIcon(QIcon::fromTheme(QStringLiteral("klettres_kids")));
0165     connect(m_kidAction, &KToggleAction::triggered, this, &KLettres::slotModeKid);
0166     m_kidAction->setWhatsThis(i18n("If you are in the Grown-up mode, clicking on this button will set up the Kid mode. The Kid mode has no menubar and the font is bigger in the statusbar."));
0167 
0168     m_grownupAction = actionCollection()->add<KToggleAction>(QStringLiteral("mode_grownup"));
0169     m_grownupAction->setText(i18n("Mode Grown-up"));
0170     actionCollection()->setDefaultShortcut(m_grownupAction,QKeySequence(Qt::CTRL | Qt::Key_G));
0171     m_grownupAction->setIcon(QIcon::fromTheme(QStringLiteral("klettres_grownup")));
0172     connect(m_grownupAction, &KToggleAction::triggered, this, &KLettres::slotModeGrownup);
0173     m_grownupAction->setWhatsThis(i18n("The Grownup mode is the normal mode where you can see the menubar."));
0174 
0175     connect(m_levelAction, &KSelectAction::indexTriggered, this, &KLettres::slotChangeLevel);
0176     connect(m_languageAction, &KSelectAction::indexTriggered, this, &KLettres::slotChangeLanguage);
0177     connect(m_themeAction, &KSelectAction::indexTriggered, this, &KLettres::slotChangeTheme);
0178 
0179     KStandardAction::preferences(this, SLOT(optionsPreferences()), actionCollection());
0180 
0181     setupGUI();
0182 }
0183 
0184 void KLettres::setupStatusbar()
0185 {
0186     QStatusBar *st=statusBar();
0187     m_langLabel = new QLabel(st);
0188     m_levLabel = new QLabel(st);
0189     st->addWidget(m_langLabel);
0190 //    st->insertFixedItem("", 1);//add a space
0191     st->addWidget(m_levLabel);
0192     statusBar();
0193 }
0194 
0195 void KLettres::setupToolbars()
0196 {
0197     // Toolbar for special characters
0198     specialCharToolbar = toolBar(QStringLiteral("specialCharToolbar"));
0199     addToolBar ( Qt::BottomToolBarArea, specialCharToolbar);
0200 }
0201 
0202 void KLettres::optionsPreferences()
0203 {
0204     if(KConfigDialog::showDialog(QStringLiteral("settings"))) {
0205         return;
0206     }
0207 
0208     KConfigDialog *dialog = new KConfigDialog(this, QStringLiteral("settings"), Prefs::self());
0209     dialog->addPage(new fontsdlg(nullptr), i18n("Font"), QStringLiteral("preferences-desktop-font"), i18n("Font Settings"));
0210     //fontsdlg is the page name, mFont is the widget name, Font Settings is the page display string
0211     //fonts is the icon
0212     Timer *m_timer = new Timer();
0213     dialog->addPage(m_timer, i18n("Timer"), QStringLiteral("chronometer"), i18n("Timer Settings"));
0214     connect(dialog, &KConfigDialog::settingsChanged, this, &KLettres::slotUpdateSettings);
0215     dialog->setAttribute( Qt::WA_DeleteOnClose );
0216     // dialog->setHelp(QString(), "klettres");
0217     dialog->show();
0218 }
0219 
0220 void KLettres::loadSettings()
0221 {
0222     if (LangUtils::getLanguages().indexOf(Prefs::language()) < 0)  {
0223         Prefs::setLanguage(QStringLiteral("en"));
0224     }
0225     QString langString = LangUtils::getLanguagesNames(LangUtils::getLanguages())[LangUtils::getLanguages().indexOf(Prefs::language())];
0226     m_languageAction->setCurrentItem(LangUtils::getLanguages().indexOf(Prefs::language()));
0227     langString.remove(QLatin1Char('&'));
0228     m_langLabel->setText(langString);
0229     loadLangToolBar();
0230     // load default level
0231     m_levLabel->setText(i18nc("@info:status the current level chosen", "(Level %1)", Prefs::level()));
0232 
0233     m_view->setTheme(KLThemeFactory::instance()->buildTheme(Prefs::theme()));
0234 
0235     if (Prefs::mode() == Prefs::EnumMode::grownup) {
0236         slotModeGrownup();
0237     } else {
0238         slotModeKid();
0239     }
0240 
0241     m_menubarAction->setChecked(Prefs::menuBarBool());
0242     slotMenubar();
0243 }
0244 
0245 void KLettres::slotDownloadNewStuff()
0246 {
0247     KNS3::QtQuickDialogWrapper *dialog = new KNS3::QtQuickDialogWrapper(QStringLiteral("klettres.knsrc"), this);
0248     dialog->open();
0249     connect(dialog, &KNS3::QtQuickDialogWrapper::closed, this, [this, dialog] {
0250         const QList<KNSCore::EntryInternal> entries = dialog->changedEntries();
0251         if (!entries.isEmpty()) {
0252             //look for languages dirs installed
0253             QStringList languages = LangUtils::getLanguages();
0254             m_languageNames = LangUtils::getLanguagesNames(languages);
0255 
0256             //refresh Languages menu
0257             m_languageAction->setItems(m_languageNames);
0258             slotChangeLanguage(languages.indexOf(Prefs::language()));
0259             m_languageAction->setCurrentItem(languages.indexOf(Prefs::language()));
0260         }
0261         dialog->deleteLater();
0262     });
0263 
0264 }
0265 
0266 void KLettres::slotMenubar()
0267 {
0268     menuBar()->setVisible(m_menubarAction->isChecked());
0269     Prefs::setMenuBarBool(m_menubarAction->isChecked());
0270     Prefs::self()->save();
0271 }
0272 
0273 void KLettres::slotUpdateSettings()
0274 {
0275     m_view->m_timer = Prefs::kidTimer();
0276     m_view->m_timer = Prefs::grownTimer();
0277     //apply the font
0278     m_view->setFont(Prefs::font());
0279 }
0280 
0281 void KLettres::slotChangeLevel(int newLevel)
0282 {
0283     Prefs::setLevel(newLevel+1);
0284     Prefs::self()->save();
0285     updateLevMenu(newLevel);
0286     //TODO is that necessary? Change level effectively by reloading sounds
0287 
0288     //this is duplicated in changeLanguage()
0289     soundFactory->change(Prefs::language());
0290     //update game effectively
0291     m_view->randomInt = 0;
0292     m_view->game();
0293 }
0294 
0295 void KLettres::updateLevMenu(int id)
0296 {
0297     //m_levelCombo->setCurrentItem(id);
0298     m_levelAction->setCurrentItem(id);
0299     m_levLabel->setText(i18nc("@info:status the current level chosen", "(Level %1)", Prefs::level()));
0300 }
0301 
0302 void KLettres::slotChangeLanguage(int newIndex)
0303 {
0304     // Write new language ISO in config
0305     QString newLanguage = LangUtils::getLanguages()[newIndex];
0306     Prefs::setLanguage(newLanguage);
0307     Prefs::self()->save();
0308     // Update the StatusBar
0309     QString langString = LangUtils::getLanguagesNames(LangUtils::getLanguages())[newIndex];
0310     langString.remove(QLatin1Char('&'));
0311     m_langLabel->setText(langString);
0312     loadLangToolBar();
0313     // Change language effectively
0314     bool ok = loadLayout(soundFactory->m_layoutsDocument);
0315 
0316     if (ok) {
0317         soundFactory->change(Prefs::language());
0318     }
0319 
0320     m_view->randomInt = 0;
0321     m_view->game();
0322 }
0323 
0324 void KLettres::slotChangeTheme(int index)
0325 {
0326     Prefs::setTheme(index);
0327     Prefs::self()->save();
0328     m_view->setTheme(KLThemeFactory::instance()->buildTheme(index));
0329 }
0330 
0331 void KLettres::slotModeGrownup()
0332 {
0333     QPalette pal;
0334     pal.setColor( QPalette::Window, Qt::white);
0335     statusBar()->setPalette( pal );
0336     QFont f_lab( QStringLiteral("Serif") , 10);  //font for statusBar
0337     m_levLabel->setFont(f_lab);
0338     m_langLabel->setFont(f_lab);
0339     m_menubarAction->setChecked(true);
0340     m_grownupAction->setChecked(true);
0341     m_kidAction->setChecked(false);
0342     m_grownupAction->setToolTip(i18n("Grown-up mode is currently active"));
0343     m_kidAction->setToolTip(i18n("Switch to Kid mode"));
0344     menuBar()->show();
0345     m_view->m_timer = Prefs::grownTimer();
0346     Prefs::setMode(Prefs::EnumMode::grownup);
0347     Prefs::self()->save();
0348 }
0349 
0350 void KLettres::slotModeKid()
0351 {
0352     QPalette pal;
0353     pal.setColor( QPalette::Window, Qt::white);
0354     statusBar()->setPalette( pal );
0355     QFont f_lab( QStringLiteral("Serif") , 12);  //font for statusBar
0356     f_lab.setBold(true);
0357     m_levLabel->setFont(f_lab);
0358     m_langLabel->setFont(f_lab);
0359     m_menubarAction->setChecked(false);
0360     m_kidAction->setChecked(true);
0361     m_kidAction->setToolTip(i18n("Kid mode is currently active"));
0362     m_grownupAction->setToolTip(i18n("Switch to Grown-up mode"));
0363     m_grownupAction->setChecked(false);
0364     menuBar()->hide();
0365     m_view->m_timer = Prefs::kidTimer();
0366     Prefs::setMode(Prefs::EnumMode::kid);
0367     Prefs::self()->save();
0368 }
0369 
0370 void KLettres::loadLangToolBar()
0371 {
0372     QString lang = Prefs::language();
0373 
0374     specialCharToolbar->clear();
0375 
0376     if (LangUtils::hasSpecialChars(lang)) {//Dutch, English, French and Italian have no special characters
0377         allData.clear();
0378         QString myString=QStringLiteral("klettres/%1.txt").arg(lang);
0379         QFile myFile;
0380         myFile.setFileName(QStandardPaths::locate(QStandardPaths::GenericDataLocation, myString));
0381 
0382         if (!myFile.exists()) {
0383             QString mString=i18n("File $KDEDIR/share/apps/klettres/%1.txt not found;\n"
0384                                     "please check your installation.", lang);
0385             KMessageBox::error( this, mString,
0386                                     i18n("Error") );
0387             qApp->quit();
0388         }
0389 
0390         //we open the file and store info into the stream...
0391         QFile openFileStream(myFile.fileName());
0392         openFileStream.open(QIODevice::ReadOnly);
0393         QTextStream readFileStr(&openFileStream);
0394 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0395         readFileStr.setCodec("UTF-8");
0396 #endif
0397         //allData contains all the words from the file
0398         allData = readFileStr.readAll().split(QLatin1Char('\n'));
0399         openFileStream.close();
0400 
0401         for (int i=0; i<(int) allData.count(); ++i) {
0402             if (!allData[i].isEmpty()) {
0403                 QAction *act = specialCharToolbar->addAction(allData.at(i));
0404                 act->setIcon(charIcon(allData.at(i).at(0)));
0405                 // used to carry the id
0406                 act->setData(i);
0407                 connect(act, &QAction::triggered, this, &KLettres::slotPasteChar);
0408             }
0409         }
0410 
0411         specialCharToolbar->show();
0412         update();
0413     } else {
0414       specialCharToolbar->hide();
0415     }
0416 }
0417 
0418 void KLettres::slotPasteChar()
0419 {
0420     QAction *act = qobject_cast<QAction*>(sender());
0421     if (!act) {
0422         return;
0423     }
0424 
0425     bool ok = true;
0426     int id = act->data().toInt(&ok);
0427 
0428     if (!ok || id < 0 || id >= allData.count()) {
0429         return;
0430     }
0431 
0432     m_view->m_letterEdit->insert(allData.at(id));
0433 }
0434 
0435 QIcon KLettres::charIcon(QChar c)
0436 {
0437     ///Create a name and path for the icon
0438     //
0439     // FIXME: This code used KStandardDirs::locateLocal("icon", ...) before
0440     //        and I am not sure if GenericCacheLocation is what we are aiming
0441     //        for.  So best would be if somebody experienced in QStandardPaths
0442     //        could confirm or change it.
0443 
0444     QString s = QStandardPaths::locate(QStandardPaths::GenericCacheLocation,
0445                                        QStringLiteral("char") + QString::number(c.unicode()) + QStringLiteral(".png"));
0446 
0447     QRect r(4, 4, 120, 120);
0448 
0449     ///A font to draw the character with
0450     QFont font;
0451     font.setFamily( QStringLiteral("Arial") );
0452     font.setPixelSize(120);
0453     font.setWeight(QFont::Normal);
0454 
0455     ///Create the pixmap
0456     QPixmap pm(128, 128);
0457     pm.fill(Qt::white);
0458     QPainter p(&pm);
0459     p.setFont(font);
0460     p.setPen(Qt::black);
0461     p.drawText(r, Qt::AlignCenter, (QString) c);
0462 
0463     ///Create transparency mask
0464     QBitmap bm(128, 128);
0465     bm.fill(Qt::color0);
0466     QPainter b(&bm);
0467     b.setFont(font);
0468     b.setPen(Qt::color1);
0469     b.drawText(r, Qt::AlignCenter, (QString) c);
0470 
0471     ///Mask the pixmap
0472     pm.setMask(bm);
0473 
0474     ///Save the icon to disk
0475     pm.save(s, "PNG");
0476 
0477     return QIcon(pm);
0478 }
0479 
0480