File indexing completed on 2024-04-21 03:42:03

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