File indexing completed on 2024-12-22 03:51:29

0001 /*
0002     Copyright (C) 2002-2005, Jason Katz-Brown <jasonkb@mit.edu>
0003     Copyright 2010 Stefan Majewsky <majewsky@gmx.net>
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     This program is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013     GNU General Public License for more details.
0014 
0015     You should have received a copy of the GNU General Public License
0016     along with this program; if not, write to the Free Software
0017     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018 */
0019 
0020 #include "newgame.h"
0021 #include "game.h"
0022 
0023 #include <QFileDialog>
0024 #include <QLabel>
0025 #include <QListWidget>
0026 #include <QPushButton>
0027 #include <QStandardPaths>
0028 #include <QVBoxLayout>
0029 
0030 #include <KLocalizedString>
0031 #include <KMessageBox>
0032 #include <KSeparator>
0033 #include <KSharedConfig>
0034 
0035 #include <KGameHighScoreDialog>
0036 
0037 NewGameDialog::NewGameDialog(bool enableCourses)
0038     : KPageDialog()
0039 {
0040     setWindowTitle(i18nc("@title:window", "New Game"));
0041     buttonBox()->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0042     setMinimumSize(640,310);
0043     connect(buttonBox(), &QDialogButtonBox::accepted, this, &NewGameDialog::slotOk);
0044 
0045     setFaceType(KPageDialog::Tree);
0046     this->enableCourses = enableCourses;
0047 
0048     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0049         // following use this group
0050         KConfigGroup configGroup(config->group(QStringLiteral("New Game Dialog Mode")));
0051 
0052     // lots o' colors :)
0053     startColors << Qt::blue << Qt::red << Qt::yellow << Qt::lightGray << Qt::cyan << Qt::darkBlue << Qt::magenta << Qt::darkGray << Qt::darkMagenta << Qt::darkYellow;
0054 
0055     playerPage = new QFrame();
0056         addPage(playerPage, i18n("Players"));
0057 
0058     QVBoxLayout *bigLayout = new QVBoxLayout(playerPage);
0059 
0060     addButton = new QPushButton(i18n("&New Player"), playerPage);
0061     bigLayout->addWidget(addButton);
0062 
0063     connect(addButton, &QPushButton::clicked, this, &NewGameDialog::addPlayer);
0064 
0065     scroller = new QScrollArea(playerPage);
0066     bigLayout->addWidget(scroller);
0067     playersWidget = new QWidget(playerPage);
0068     scroller->setWidget(playersWidget);
0069     new QVBoxLayout(playersWidget);
0070 
0071     QMap<QString, QString> entries = config->entryMap(QStringLiteral("New Game Dialog"));
0072     int i = 0;
0073     for (QMap<QString, QString>::Iterator it = entries.begin(); it != entries.end(); ++it)
0074     {
0075         if (i > startColors.count())
0076             return;
0077 
0078         addPlayer();
0079         editors.last()->setName(it.key().right(it.key().length() - 1));
0080         editors.last()->setColor(QColor(it.value()));
0081         ++i;
0082     }
0083 
0084     if (editors.isEmpty())
0085     {
0086         addPlayer();
0087         addPlayer();
0088     }
0089 
0090     enableButtons();
0091     KPageWidgetItem *pageItem =nullptr;
0092     if (enableCourses)
0093     {
0094         coursePage = new QFrame();
0095         pageItem = new KPageWidgetItem( coursePage, i18n("Course") );
0096         pageItem->setHeader(i18n("Choose Course to Play"));
0097         addPage(pageItem);
0098         QVBoxLayout *coursePageLayout = new QVBoxLayout(coursePage);
0099 
0100         QHBoxLayout *hlayout = new QHBoxLayout;
0101                 coursePageLayout->addLayout( hlayout );
0102 
0103 
0104         // find other courses
0105         externCourses = configGroup.readEntry("extra",QStringList());
0106 
0107         /// course loading
0108         QStringList files;
0109         const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("courses"), QStandardPaths::LocateDirectory);
0110         for (const QString& dir : dirs) {
0111             const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*"), QDir::Files);
0112             for (const QString& file : fileNames) {
0113                 files.append(dir + QLatin1Char('/') + file);
0114             }
0115         }
0116         const QStringList items = externCourses + files;
0117 
0118         QStringList nameList;
0119         const QString lastCourse(configGroup.readEntry("course", ""));
0120         int curItem = 0;
0121         i = 0;
0122         for (QStringList::const_iterator it = items.begin(); it != items.end(); ++it, ++i)
0123         {
0124             QString file = *it;
0125             CourseInfo curinfo;
0126             KolfGame::courseInfo(curinfo, file);
0127             info[file] = curinfo;
0128             names.append(file);
0129             nameList.append(curinfo.name);
0130 
0131             if (lastCourse == file)
0132                 curItem = i;
0133         }
0134 
0135         const QString newName(i18n("Create New"));
0136         info[QString()] = CourseInfo(newName, newName, i18n("You"), 0, 0);
0137         names.append(QString());
0138         nameList.append(newName);
0139 
0140         courseList = new QListWidget(coursePage);
0141         hlayout->addWidget(courseList);
0142         courseList->addItems(nameList);
0143         courseList->setCurrentRow(curItem);
0144         connect(courseList, &QListWidget::currentRowChanged, this, &NewGameDialog::courseSelected);
0145         connect(courseList, &QListWidget::itemSelectionChanged, this, &NewGameDialog::selectionChanged);
0146 
0147         QVBoxLayout *detailLayout = new QVBoxLayout;
0148                 hlayout->addLayout( detailLayout );
0149         name = new QLabel(coursePage);
0150         detailLayout->addWidget(name);
0151         author = new QLabel(coursePage);
0152         detailLayout->addWidget(author);
0153 
0154         QHBoxLayout *minorLayout = new QHBoxLayout;
0155                 detailLayout->addLayout( minorLayout );
0156         par = new QLabel(coursePage);
0157         minorLayout->addWidget(par);
0158         holes = new QLabel(coursePage);
0159         minorLayout->addWidget(holes);
0160 
0161         detailLayout->addStretch();
0162         QPushButton *scores = new QPushButton(i18n("Highscores"), coursePage);
0163         connect(scores, &QPushButton::clicked, this, &NewGameDialog::showHighscores);
0164         detailLayout->addWidget(scores);
0165 
0166         detailLayout->addStretch();
0167         detailLayout->addWidget(new KSeparator(coursePage));
0168 
0169         minorLayout = new QHBoxLayout;
0170                 detailLayout->addLayout( minorLayout );
0171 
0172         QPushButton *addCourseButton = new QPushButton(i18n("Add..."), coursePage);
0173         minorLayout->addWidget(addCourseButton);
0174         connect(addCourseButton, &QPushButton::clicked, this, &NewGameDialog::addCourse);
0175 
0176         remove = new QPushButton(i18n("Remove"), coursePage);
0177         minorLayout->addWidget(remove);
0178         connect(remove, &QPushButton::clicked, this, &NewGameDialog::removeCourse);
0179 
0180         courseSelected(curItem);
0181         selectionChanged();
0182     }
0183 
0184     // options page
0185     optionsPage = new QFrame();
0186     pageItem = new KPageWidgetItem( optionsPage, i18n("Options") );
0187     pageItem->setHeader(i18n("Game Options"));
0188     addPage(pageItem);
0189 
0190     QVBoxLayout *vlayout = new QVBoxLayout(optionsPage);
0191 
0192     mode = new QCheckBox(i18n("&Strict mode"), optionsPage);
0193     vlayout->addWidget(mode);
0194     mode->setChecked(configGroup.readEntry("competition", false));
0195 
0196     QLabel *desc = new QLabel(i18n("In strict mode, undo, editing, and switching holes is not allowed. This is generally for competition. Only in strict mode are highscores kept."), optionsPage);
0197     desc->setTextFormat(Qt::RichText);
0198     desc->setWordWrap(true);
0199     vlayout->addWidget(desc);
0200 }
0201 
0202 NewGameDialog::~NewGameDialog()
0203 {
0204     qDeleteAll(editors);
0205 }
0206 
0207 void NewGameDialog::slotOk()
0208 {
0209     KSharedConfig::Ptr config = KSharedConfig::openConfig();
0210     KConfigGroup configGroup(config->group(QStringLiteral("New Game Dialog Mode")));
0211 
0212     configGroup.writeEntry("competition", mode->isChecked());
0213     if (enableCourses)
0214     {
0215         configGroup.writeEntry("course", currentCourse);
0216         configGroup.writeEntry("extra", externCourses);
0217     }
0218 
0219     config->deleteGroup(QStringLiteral("New Game Dialog"));
0220 
0221     PlayerEditor *curEditor = nullptr;
0222     int i = 0;
0223     for (; i < editors.count(); ++i) {
0224         curEditor = editors.at(i);
0225         configGroup.writeEntry(QString::number(i) + curEditor->name(), curEditor->color().name());
0226     }
0227 
0228     config->sync();
0229 }
0230 
0231 void NewGameDialog::courseSelected(int index)
0232 {
0233     //BUG 274418: select first course if nothing selected (should not happen, but meh)
0234     if (index < 0)
0235         index = 0;
0236     currentCourse = names.at(index);
0237 
0238     CourseInfo &curinfo = info[currentCourse];
0239 
0240     name->setText(QStringLiteral("<strong>%1</strong>").arg(curinfo.name));
0241 
0242     author->setText(i18n("By %1", curinfo.author));
0243     par->setText(i18n("Par %1", curinfo.par));
0244     holes->setText(i18n("%1 Holes", curinfo.holes));
0245 }
0246 
0247 void NewGameDialog::showHighscores()
0248 {
0249     KGameHighScoreDialog *scoreDialog = new KGameHighScoreDialog(KGameHighScoreDialog::Name | KGameHighScoreDialog::Custom1 | KGameHighScoreDialog::Score, this);
0250     scoreDialog->addField(KGameHighScoreDialog::Custom1, i18n("Par"), QStringLiteral("Par"));
0251     scoreDialog->setConfigGroup(qMakePair(QByteArray(info[currentCourse].untranslatedName.toUtf8() + " Highscores"), i18n("High Scores for %1", info[currentCourse].name)));
0252     scoreDialog->setComment(i18n("High Scores for %1", info[currentCourse].name));
0253     scoreDialog->show();
0254 }
0255 
0256 void NewGameDialog::removeCourse()
0257 {
0258     QListWidgetItem* curItem = courseList->currentItem();
0259     if (!curItem)
0260         return;
0261 
0262     QString file = curItem->text();
0263     if (!externCourses.contains(file))
0264         return;
0265 
0266     names.removeAll(file);
0267     externCourses.removeAll(file);
0268     delete courseList->takeItem(courseList->currentRow());
0269 
0270     selectionChanged();
0271 }
0272 
0273 void NewGameDialog::selectionChanged()
0274 {
0275     QListWidgetItem* curItem = courseList->currentItem();
0276     remove->setEnabled(curItem && externCourses.contains(curItem->text()));
0277 }
0278 
0279 void NewGameDialog::addCourse()
0280 {
0281     QPointer<QFileDialog> fileLoadDialog = new QFileDialog(this);
0282     fileLoadDialog->setWindowTitle(i18nc("@title:window", "Pick Kolf Course"));
0283     fileLoadDialog->setMimeTypeFilters(QStringList(QStringLiteral("application/x-kourse")));
0284     fileLoadDialog->setAcceptMode(QFileDialog::AcceptOpen);
0285     fileLoadDialog->setFileMode(QFileDialog::ExistingFile);
0286     fileLoadDialog->exec();
0287         const QStringList files = fileLoadDialog->selectedFiles();
0288     delete fileLoadDialog;
0289 
0290     bool hasDuplicates = false;
0291 
0292     for (QStringList::const_iterator fileIt = files.begin(); fileIt != files.end(); ++fileIt)
0293     {
0294         if (names.contains(*fileIt) > 0)
0295         {
0296             hasDuplicates = true;
0297             continue;
0298         }
0299 
0300         CourseInfo curinfo;
0301         KolfGame::courseInfo(curinfo, *fileIt);
0302         info[*fileIt] = curinfo;
0303         names.prepend(*fileIt);
0304         externCourses.prepend(*fileIt);
0305 
0306         courseList->insertItem(0, new QListWidgetItem(curinfo.name));
0307     }
0308 
0309     if (hasDuplicates)
0310         KMessageBox::information(this, i18n("Chosen course is already on course list."));
0311 
0312     courseList->setCurrentItem(nullptr);
0313     courseSelected(0);
0314     selectionChanged();
0315 }
0316 
0317 void NewGameDialog::addPlayer()
0318 {
0319     if (editors.count() >= startColors.count())
0320         return;
0321 
0322 
0323     PlayerEditor *pe = new PlayerEditor(i18n("Player %1", editors.count() + 1), startColors.at(editors.count()), playersWidget);
0324     editors.append(pe);
0325     pe->show();
0326     playersWidget->layout()->addWidget(pe);
0327     connect(pe, &PlayerEditor::deleteEditor, this, &NewGameDialog::deleteEditor);
0328 
0329     enableButtons();
0330     playersWidget->setMinimumSize(playersWidget->sizeHint());
0331 }
0332 
0333 void NewGameDialog::deleteEditor(PlayerEditor *editor)
0334 {
0335     if (editors.count() < 2)
0336         return;
0337 
0338     editors.removeAll(editor);
0339     delete editor;
0340 
0341     enableButtons();
0342     playersWidget->setMinimumSize(playersWidget->sizeHint());
0343     playersWidget->resize(playersWidget->sizeHint());
0344 }
0345 
0346 void NewGameDialog::enableButtons()
0347 {
0348     addButton->setEnabled(!(editors.count() >= startColors.count()));
0349 }
0350 
0351 /////////////////////////
0352 
0353 PlayerEditor::PlayerEditor(const QString &startName, QColor startColor, QWidget *parent)
0354     : QWidget(parent)
0355 {
0356     QHBoxLayout *layout = new QHBoxLayout(this);
0357 
0358     editor = new KLineEdit(this);
0359     layout->addWidget(editor);
0360     editor->setFrame(false);
0361     editor->setText(startName);
0362     layout->addStretch();
0363     layout->addWidget(colorButton = new KColorButton(startColor, this));
0364     colorButton->hide();
0365     QPushButton *remove = new QPushButton(i18n("Remove"), this);
0366     layout->addWidget(remove);
0367     connect(remove, &QPushButton::clicked, this, &PlayerEditor::removeMe);
0368 }
0369 
0370 void PlayerEditor::removeMe()
0371 {
0372     Q_EMIT deleteEditor(this);
0373 }
0374 
0375 #include "moc_newgame.cpp"