File indexing completed on 2024-04-21 03:41:52

0001 /***************************************************************************
0002  *   Copyright (C) 2004-2007 by Albert Astals Cid                          *
0003  *   aacid@kde.org                                                         *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "kgeography.h"
0012 
0013 #include <KConfigDialog>
0014 #include <KMessageBox>
0015 #include <KStandardAction>
0016 #include <KToggleAction>
0017 #include <KToolBar>
0018 #include <KActionCollection>
0019 #include <KLocalizedString>
0020 
0021 #include <QMenuBar>
0022 #include <QFile>
0023 #include <QLabel>
0024 #include <QLayout>
0025 #include <QSize>
0026 #include <QTimer>
0027 #include <QPushButton>
0028 
0029 #include "answersdialog.h"
0030 #include "capitaldivisionasker.h"
0031 #include "divisioncapitalasker.h"
0032 #include "divisionflagasker.h"
0033 #include "flagdivisionasker.h"
0034 #include "settings.h"
0035 #include "mapasker.h"
0036 #include "mapchooser.h"
0037 #include "mapparser.h"
0038 #include "map.h"
0039 #include "placeasker.h"
0040 #include "integerinputdialog.h"
0041 #include "ui_kgeographyoptions.h"
0042 
0043 kgeography::kgeography() : KXmlGuiWindow(), p_firstShow(true), p_mustShowResultsDialog(false)
0044 {
0045     p_map = nullptr;
0046     p_askWidget = nullptr;
0047 
0048     p_bigWidget = new QWidget(this);
0049     QHBoxLayout *bigWidgetLayout = new QHBoxLayout(p_bigWidget);
0050     bigWidgetLayout -> setSpacing(0);
0051     bigWidgetLayout -> setContentsMargins(0, 0, 0, 0);
0052 
0053     QWidget *leftWidget = new QWidget(p_bigWidget);
0054     QVBoxLayout *leftWidgetLayout = new QVBoxLayout(leftWidget);
0055 
0056     p_currentMap = new QLabel(leftWidget);
0057     p_currentMap -> setAlignment(Qt::AlignCenter);
0058     p_consult = new QPushButton(i18n("E&xplore Map"), leftWidget);
0059     p_consult->setWhatsThis(i18n("In this section left click on any part of the map to learn about the divisions" ));
0060     QLabel * testYourselfLabel = new QLabel( i18n("Test Yourself:"), this );
0061     testYourselfLabel->setAlignment( Qt::AlignHCenter );
0062     p_askMap = new QPushButton(i18n("&Location of Regions"), leftWidget);
0063     p_askMap->setWhatsThis(i18n("In this challenge you are given a division name on the left under the menu and you must find it on the map and click on it"));
0064     p_askPlaceMap = new QPushButton(i18n("&Place Regions in the Map"), leftWidget);
0065     p_askPlaceMap->setWhatsThis(i18n("In this challenge you are given the shape of a region and you must place it on the map"));
0066     p_askCapitalDivisions = new QPushButton(i18n("&Regions by Capital"), leftWidget);
0067     p_askCapitalDivisions->setWhatsThis(i18n("In this quiz you have to guess the division name given its capital"));
0068     p_askDivisionCapitals = new QPushButton(i18n("&Capitals of Regions"), leftWidget);
0069     p_askDivisionCapitals->setWhatsThis(i18n("In this quiz you have to guess the capital of a given division name"));
0070     p_askFlagDivisions = new QPushButton(i18n("Re&gions by Flag"), leftWidget);
0071     p_askFlagDivisions->setWhatsThis(i18n("In this quiz you have to guess the division name given its flag"));
0072     p_askDivisionFlags = new QPushButton(i18n("&Flags of Regions"), leftWidget);
0073     p_askDivisionFlags->setWhatsThis(i18n("In this quiz you have to guess the flag of a division given its name"));
0074     p_underLeftWidget = new QWidget(leftWidget);
0075     // where the number of answers will be shown
0076     m_underLeftWidgetLayout = new QVBoxLayout(p_underLeftWidget);
0077     m_underLeftWidgetLayout->setContentsMargins(0, 0, 0, 0);
0078 
0079     leftWidgetLayout -> addWidget(p_currentMap);
0080     leftWidgetLayout -> addWidget(p_consult);
0081     leftWidgetLayout -> addSpacing(10);
0082     leftWidgetLayout -> addWidget(testYourselfLabel);
0083     leftWidgetLayout -> addWidget(p_askMap);
0084     leftWidgetLayout -> addWidget(p_askPlaceMap);
0085     leftWidgetLayout -> addWidget(p_askCapitalDivisions);
0086     leftWidgetLayout -> addWidget(p_askDivisionCapitals);
0087     leftWidgetLayout -> addWidget(p_askFlagDivisions);
0088     leftWidgetLayout -> addWidget(p_askDivisionFlags);
0089     leftWidgetLayout -> addWidget(p_underLeftWidget);
0090 
0091     // Make p_underLeftWidget as thin as possible, and always be shown in the same place
0092     QWidget* p_underUnderLeftWidget = new QWidget(leftWidget);
0093     QVBoxLayout *underUnderLeftWidgetLayout = new QVBoxLayout(p_underUnderLeftWidget);
0094     underUnderLeftWidgetLayout -> addStretch(1);
0095     leftWidgetLayout -> addWidget(p_underUnderLeftWidget);
0096 
0097     bigWidgetLayout -> addWidget(leftWidget);
0098     setCentralWidget(p_bigWidget);
0099 
0100     connect(p_consult, &QPushButton::clicked, this, &kgeography::consult);
0101     connect(p_askMap, &QPushButton::clicked, this, &kgeography::askMap);
0102     connect(p_askPlaceMap, &QPushButton::clicked, this, &kgeography::askPlaceMap);
0103     connect(p_askCapitalDivisions, &QPushButton::clicked, this, &kgeography::askCapitalDivisions);
0104     connect(p_askDivisionCapitals, &QPushButton::clicked, this, &kgeography::askDivisionCapitals);
0105     connect(p_askFlagDivisions, &QPushButton::clicked, this, &kgeography::askFlagDivisions);
0106     connect(p_askDivisionFlags, &QPushButton::clicked, this, &kgeography::askDivisionFlags);
0107 
0108     QAction *a = KStandardAction::open(this, SLOT(openMap()), actionCollection());
0109     a -> setText(i18n("&Open Map..."));
0110 
0111     a = KStandardAction::quit(this, SLOT(close()), actionCollection());
0112     
0113     p_zoom =  actionCollection()->add<KToggleAction>( QStringLiteral("zoom_select") );
0114     p_zoom -> setText(i18n("&Zoom") );
0115     p_zoom -> setIcon( QIcon::fromTheme(QStringLiteral("zoom-in")) );
0116     p_zoom -> setEnabled(false);
0117 
0118     p_zoomOriginal = actionCollection()->addAction( QStringLiteral("zoom_original") );
0119     p_zoomOriginal -> setText( i18n("&Original Size") );
0120     p_zoomOriginal -> setIcon( QIcon::fromTheme(QStringLiteral("zoom-original")) );
0121     p_zoomOriginal -> setEnabled(false);
0122 
0123     p_zoomAutomatic = actionCollection()->add<KToggleAction>( QStringLiteral("zoom_automatic") );
0124     p_zoomAutomatic -> setText(  i18n("&Automatic Zoom") );
0125     p_zoomAutomatic -> setIcon( QIcon::fromTheme(QStringLiteral("zoom-fit-best")) );
0126     p_zoomAutomatic -> setEnabled(false);
0127 
0128     p_move = actionCollection()->add<KToggleAction>( QStringLiteral("move") );
0129     p_move->setText( i18n("&Move") );
0130     p_move->setIcon( QIcon::fromTheme(QStringLiteral("transform-move")) );
0131     p_move -> setEnabled(false);
0132 
0133     a = actionCollection()->addAction( QStringLiteral("disclaimer") );
0134     a->setText( i18n("Disclaimer") );
0135     connect(a, &QAction::triggered, this, &kgeography::disclaimer);
0136     
0137     p_showAuthor = actionCollection()->addAction( QStringLiteral("author") );
0138     p_showAuthor->setText( i18n("Map author") );
0139     p_showAuthor->setEnabled(false);
0140     connect(p_showAuthor, &QAction::triggered, this, &kgeography::showMapAuthor);
0141 
0142     KStandardAction::preferences(this, SLOT(showPreferencesDialog()), actionCollection());
0143 
0144     setupGUI(Keys | ToolBar | Save | Create);
0145 
0146     show();
0147 }
0148 
0149 kgeography::~kgeography()
0150 {
0151     delete p_askWidget;
0152     delete p_map;
0153 }
0154 
0155 void kgeography::showPreferencesDialog()
0156 {
0157     if ( KConfigDialog::showDialog( QStringLiteral("settings") ) )
0158         return; 
0159 
0160     // KConfigDialog didn't find an instance of this dialog, so lets
0161     // create it : 
0162     KConfigDialog* dialog = new KConfigDialog(this, QStringLiteral("settings"),
0163                                               kgeographySettings::self()); 
0164 //  FIXME: Waiting for the answer from Kévin about what to do with it
0165 //  dialog->setHelp("configuration", "kgeography");
0166     QWidget *w = new QWidget();
0167     Ui::Options confWdg;
0168     confWdg.setupUi(w);
0169 
0170     dialog->addPage(w, i18n("General"), QStringLiteral("kgeography") ); 
0171 
0172     // User edited the configuration - update your local copies of the 
0173     // configuration data 
0174     connect(dialog, &KConfigDialog::settingsChanged, this, &kgeography::updateConfiguration);
0175 
0176     dialog->show();
0177 }
0178 
0179 void kgeography::updateConfiguration()
0180 {
0181     boxAsker *aBoxAsker = dynamic_cast<boxAsker*>(p_askWidget);
0182     if ( aBoxAsker != nullptr ) {
0183         aBoxAsker->updateLayout();
0184     }
0185 }
0186 
0187 void kgeography::showEvent(QShowEvent *)
0188 {
0189     if (p_firstShow)
0190     {
0191         QString file = kgeographySettings::self() -> lastMap();
0192 
0193         if (QFile::exists(file))
0194         {
0195             mapReader reader;
0196             KGmap *kgmap = reader.parseMap(file);
0197             if (kgmap)
0198             {
0199                 setMap(kgmap);
0200             }
0201             else
0202             {
0203                 KMessageBox::error(this, i18n("Could not open last used map. Error parsing %1: %2", file, reader.getError()));
0204                 openMap();
0205             }
0206         }
0207         else openMap();
0208 
0209         if (!p_map)
0210         {
0211             p_currentMap -> setText(i18n("There is no current map"));
0212             p_consult -> setEnabled(false);
0213             p_askMap -> setEnabled(false);
0214             p_askPlaceMap -> setEnabled(false);
0215             p_askFlagDivisions -> setEnabled(false);
0216             p_askDivisionFlags -> setEnabled(false);
0217             p_askCapitalDivisions -> setEnabled(false);
0218             p_askDivisionCapitals -> setEnabled(false);
0219         }
0220         // if anyone can explain why with the slot works and now without
0221         // i'll be glad to learn
0222         QTimer::singleShot(0, this, &kgeography::resizeMainWindow);
0223 //      resizeMainWindow();
0224 
0225         p_firstShow = false;
0226     }
0227 }
0228 
0229 void kgeography::openMap()
0230 {
0231     mapChooser mp(this);
0232     if (mp.exec() == mapChooser::Accepted)
0233     {
0234         showResultsDialog();
0235         setMap(mp.getMap());
0236         // if anyone can explain why with the slot works and now without
0237         // i'll be glad to learn
0238         QTimer::singleShot(0, this, &kgeography::resizeMainWindow);
0239 //      resizeMainWindow();
0240     }
0241 }
0242 
0243 void kgeography::consult()
0244 {
0245     // No need to create another map viewer if we are already in it
0246     mapAsker *ma = dynamic_cast<mapAsker*>(p_askWidget);
0247     if (ma && !ma -> isAsker()) return;
0248 
0249     showResultsDialog();
0250     removeOldAskWidget();
0251     p_askWidget = new mapAsker(p_bigWidget, p_map, p_underLeftWidget, false);
0252     p_zoom -> setEnabled(true);
0253     p_zoomOriginal -> setEnabled(true);
0254     p_zoomAutomatic -> setEnabled(true);
0255     putAskWidget();
0256 }
0257 
0258 int kgeography::askQuestionNumber(int upto, bool *rOK)
0259 {
0260     int i;
0261     bool ok;
0262     i = IntegerInputDialog::GetInteger(this, i18n("Number of Questions"),
0263                        i18n("How many questions do you want? (1 to %1)", upto),
0264                        1, upto, upto, &ok);
0265     if ( rOK != nullptr ) *rOK = ok;
0266     return i;
0267 }
0268 
0269 void kgeography::askCapitalDivisions()
0270 {
0271     int i;
0272     bool ok;
0273     showResultsDialog();
0274     i = askQuestionNumber(p_map -> count(division::eCapital), &ok);
0275     if (ok)
0276     {
0277         removeOldAskWidget();
0278         p_askWidget = new capitalDivisionAsker(p_bigWidget, p_map, p_underLeftWidget, i);
0279         putAskWidget();
0280         p_mustShowResultsDialog = true;
0281     }
0282     else consult();
0283 }
0284 
0285 void kgeography::askDivisionCapitals()
0286 {
0287     int i;
0288     bool ok;
0289     showResultsDialog();
0290     i = askQuestionNumber(p_map -> count(division::eCapital), &ok);
0291     if (ok)
0292     {
0293         removeOldAskWidget();
0294         p_askWidget = new divisionCapitalAsker(p_bigWidget, p_map, p_underLeftWidget, i);
0295         putAskWidget();
0296         p_mustShowResultsDialog = true;
0297     }
0298     else consult();
0299 }
0300 
0301 void kgeography::askMap()
0302 {
0303     int i;
0304     bool ok;
0305     showResultsDialog();
0306     i = askQuestionNumber(p_map -> count(division::eClick), &ok);
0307     if (ok)
0308     {
0309         removeOldAskWidget();
0310         p_askWidget = new mapAsker(p_bigWidget, p_map, p_underLeftWidget, true, i);
0311         p_zoom -> setEnabled(true);
0312         p_zoomOriginal -> setEnabled(true);
0313         p_zoomAutomatic -> setEnabled(true);
0314         putAskWidget();
0315         p_mustShowResultsDialog = true;
0316     }
0317     else consult();
0318 }
0319 
0320 void kgeography::askPlaceMap()
0321 {
0322     int i;
0323     bool ok;
0324     showResultsDialog();
0325     i = askQuestionNumber(p_map -> count(division::eClick), &ok);
0326     if (ok)
0327     {
0328         removeOldAskWidget();
0329         p_askWidget = new placeAsker(p_bigWidget, p_map, p_underLeftWidget, i);
0330         p_zoom -> setEnabled(true);
0331         p_zoomOriginal -> setEnabled(true);
0332         p_zoomAutomatic -> setEnabled(true);
0333         putAskWidget();
0334         p_mustShowResultsDialog = true;
0335     }
0336     else consult();
0337 }
0338 
0339 void kgeography::askFlagDivisions()
0340 {
0341     int i;
0342     bool ok;
0343     showResultsDialog();
0344     i = askQuestionNumber(p_map -> count(division::eFlag), &ok);
0345     if (ok)
0346     {
0347         removeOldAskWidget();
0348         p_askWidget = new flagDivisionAsker(p_bigWidget, p_map, p_underLeftWidget, i);
0349         putAskWidget();
0350         p_mustShowResultsDialog = true;
0351     }
0352     else consult();
0353 }
0354 
0355 void kgeography::askDivisionFlags()
0356 {
0357     int i;
0358     bool ok;
0359     showResultsDialog();
0360     i = askQuestionNumber(p_map -> count(division::eFlag), &ok);
0361     if (ok)
0362     {
0363         removeOldAskWidget();
0364         p_askWidget = new divisionFlagAsker(p_bigWidget, p_map, p_underLeftWidget, i);
0365         putAskWidget();
0366         p_mustShowResultsDialog = true;
0367     }
0368     else consult();
0369 }
0370 
0371 void kgeography::removeOldAskWidget()
0372 {
0373     // next lines because setZoom(true) (may have) put cursor at application level
0374     if ( p_askWidget != nullptr )
0375         p_askWidget->setZoom(false);
0376     delete p_askWidget;
0377     p_askWidget = nullptr;
0378     p_zoom -> setEnabled(false);
0379     p_zoomOriginal -> setEnabled(false);
0380     p_zoomAutomatic -> setEnabled(false);
0381     p_move -> setEnabled(false);
0382     p_zoom -> setChecked(false);
0383     p_move -> setChecked(false);
0384 }
0385 
0386 QSize kgeography::getPreferredSize()
0387 {
0388     int ySize = menuBar() -> size().height() + toolBar() -> size().height() + ((mapAsker*) p_askWidget)->mapSize().height();
0389     int xSize = p_underLeftWidget -> parentWidget()-> size().width() + ((mapAsker*) p_askWidget)->mapSize().width();
0390     return QSize(xSize + 10, ySize + 10);
0391 }
0392 
0393 void kgeography::putAskWidget()
0394 {
0395     static_cast<QBoxLayout*>(p_bigWidget -> layout()) -> addWidget(p_askWidget, 1);
0396     p_askWidget -> setAutomaticZoom(p_zoomAutomatic -> isChecked());
0397     p_askWidget -> show();
0398     connect(p_askWidget, &askWidget::setZoomActionChecked, p_zoom, &KToggleAction::setChecked);
0399     connect(p_zoom, &KToggleAction::toggled, p_askWidget, &askWidget::setZoom);
0400     connect(p_zoomOriginal, &QAction::triggered, p_askWidget, &askWidget::setOriginalZoom);
0401     connect(p_zoomOriginal, &QAction::triggered, p_zoomAutomatic, &QAction::setChecked);
0402     connect(p_zoomAutomatic, &KToggleAction::toggled, this, &kgeography::setAutomaticZoom);
0403     connect(p_askWidget, &askWidget::setMoveActionEnabled, this, &kgeography::setMoveActionEnabled);
0404     connect(p_askWidget, &askWidget::setMoveActionChecked, p_move, &KToggleAction::setChecked);
0405     connect(p_move, &KToggleAction::toggled, p_askWidget, &askWidget::setMovement);
0406     connect(p_askWidget, &askWidget::questionsEnded, this, &kgeography::questionsEnded);
0407 }
0408 
0409 void kgeography::setMap(KGmap *m)
0410 {
0411     removeOldAskWidget();
0412     kgeographySettings *set = kgeographySettings::self();
0413     set -> setLastMap(m -> getFile());
0414     set -> save();
0415     delete p_map;
0416     p_map = m;
0417     p_showAuthor->setEnabled(m);
0418 
0419     QString mapName = p_map -> getName();
0420     QString divisionType = p_map->getDivisionsString();
0421 
0422     p_askMap->setText(i18n("&Location of %1", divisionType));
0423     p_askPlaceMap->setText(i18n("&Place %1 in the Map", divisionType));
0424     p_askCapitalDivisions->setText(p_map->getCapitalToDivisionTitle());
0425     p_askDivisionCapitals->setText(p_map->getDivisionToCapitalTitle());
0426     p_askFlagDivisions->setText(i18n("%1 by Flag", divisionType));
0427     p_askDivisionFlags->setText(i18n("&Flags of %1", divisionType));
0428 
0429     p_currentMap -> setText(i18n("<qt>Current map:<br /><b>%1</b></qt>", mapName));
0430     p_consult -> setEnabled(true);
0431     p_askMap -> setEnabled(m -> count(division::eClick) > 0);
0432     p_askPlaceMap -> setEnabled(m -> count(division::eClick) > 0);
0433     // as we always want to let user choose among 4 choices, verify we have the ammunition
0434     p_askFlagDivisions -> setEnabled(m -> count(division::eFlag) > 3);
0435     p_askDivisionFlags -> setEnabled(m -> count(division::eFlag) > 3);
0436     p_askCapitalDivisions -> setEnabled(m -> count(division::eCapital) > 3);
0437     p_askDivisionCapitals -> setEnabled(m -> count(division::eCapital) > 3);
0438     consult();
0439 
0440     // set a maximum width for the under left widget, enabling the labels wordwrap and
0441     // avoiding the move of the map in case of large division names.
0442     int width = qMax(p_askMap->sizeHint().width(), p_askPlaceMap->sizeHint().width());
0443     width = qMax(width, p_askCapitalDivisions->sizeHint().width());
0444     width = qMax(width, p_askDivisionCapitals->sizeHint().width());
0445     width = qMax(width, p_askFlagDivisions->sizeHint().width());
0446     width = qMax(width, p_askDivisionFlags->sizeHint().width());
0447     p_underLeftWidget->setMaximumWidth(width);
0448 }
0449 
0450 void kgeography::disclaimer()
0451 {
0452     KMessageBox::information(this, i18n("Maps, flags, translations, etc. are as accurate as their respective authors could achieve,"
0453                         " but KGeography should not be taken as an authoritative source."), i18n("Disclaimer"));
0454 }
0455 
0456 void kgeography::showMapAuthor()
0457 {
0458     if (p_map)
0459     {
0460         KMessageBox::information(this, i18n("This map has been created by %1.", p_map->getAuthor()), i18n("Map Author"));
0461     }
0462 }
0463 
0464 void kgeography::resizeMainWindow()
0465 {
0466     if (p_askWidget && width() ) resize(getPreferredSize().expandedTo(size()));
0467 }
0468 
0469 void kgeography::questionsEnded()
0470 {
0471     showResultsDialog();
0472     consult();
0473 }
0474 
0475 void kgeography::setAutomaticZoom(bool b)
0476 {
0477     p_zoom->setChecked(false);
0478     p_askWidget->setAutomaticZoom(b);
0479 }
0480 
0481 void kgeography::setMoveActionEnabled(bool b)
0482 {
0483     p_move->setEnabled(b);
0484     if (b && p_zoomAutomatic->isChecked())
0485     {
0486         // we don't want the unchecking to bring us to the original zoom state
0487         p_zoomAutomatic->blockSignals(true);
0488         p_zoomAutomatic->setChecked(false);
0489         p_zoomAutomatic->blockSignals(false);
0490     }
0491 }
0492 
0493 void kgeography::showResultsDialog()
0494 {
0495     if (p_mustShowResultsDialog)
0496     {
0497         p_mustShowResultsDialog = false;
0498         int ca = p_askWidget -> correctAnswers();
0499         QString q = p_askWidget -> getQuestionHook();
0500         QVector<userAnswer> ua = p_askWidget -> userAnswers();
0501 
0502         answersDialog *ad = new answersDialog(this, ua, q, ca);
0503         ad -> exec();
0504         delete ad;
0505     }
0506 }
0507 
0508 #include "moc_kgeography.cpp"