File indexing completed on 2024-04-28 04:03:11

0001 /***************************************************************************
0002     File                 : enginesettings.cpp
0003     Project              : Knights
0004     Description          : Engine Settings
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2018 Alexander Semke (alexander.semke@web.de)
0007     SPDX-FileCopyrightText: 2009-2011 Miha Čančula (miha@noughmad.eu)
0008 
0009  ***************************************************************************/
0010 
0011 /***************************************************************************
0012  *                                                                         *
0013  *  SPDX-License-Identifier: GPL-2.0-or-later
0014  *                                                                         *
0015  *  This program is distributed in the hope that it will be useful,        *
0016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0018  *  GNU General Public License for more details.                           *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program; if not, write to the Free Software           *
0022  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0023  *   Boston, MA  02110-1301  USA                                           *
0024  *                                                                         *
0025  ***************************************************************************/
0026 
0027 #include "enginesettings.h"
0028 #include "ui_enginesettings.h"
0029 #include "settings.h"
0030 
0031 #include <KComboBox>
0032 
0033 #include <QLabel>
0034 #include <QStandardPaths>
0035 
0036 using namespace Knights;
0037 
0038 EngineConfiguration::EngineConfiguration(const QString& string) : iface(Invalid) {
0039     QStringList list = string.split(QLatin1Char(':'));
0040     if ( list.size() > 2 ) {
0041         name = list[0];
0042         commandLine = list[1];
0043         if ( list[2] == QLatin1String("xboard") )
0044             iface = XBoard;
0045         else if ( list[2] == QLatin1String("uci") )
0046             iface = Uci;
0047     }
0048 }
0049 
0050 const QString EngineConfiguration::toString() const {
0051     if ( iface == Invalid || commandLine.isEmpty() || name.isEmpty() )
0052         return QString();
0053 
0054     QLatin1Char sep(':');
0055     QString str = name + sep + commandLine + sep + ( iface == XBoard ? QLatin1String("xboard") : QLatin1String("uci") );
0056     return str;
0057 }
0058 
0059 EngineSettings::EngineSettings(QWidget* parent, Qt::WindowFlags f) : QWidget(parent, f), ui(new Ui::EngineSettings) {
0060     ui->setupUi(this);
0061 
0062     ui->autoDetectButton->setIcon( QIcon::fromTheme(QStringLiteral("tools-wizard")) );
0063     ui->addButton->setIcon( QIcon::fromTheme(QStringLiteral("list-add")) );
0064     ui->removeButton->setIcon( QIcon::fromTheme(QStringLiteral("list-remove")) );
0065 
0066     //add saved engines
0067     int row = 0;
0068     for ( const QString& s : Settings::engineConfigurations() ) {
0069         addClicked();
0070         EngineConfiguration c = EngineConfiguration ( s );
0071         ui->tableWidget->setItem ( row, NameColumn, new QTableWidgetItem ( c.name ) );
0072         ui->tableWidget->setItem ( row, CommandColumn, new QTableWidgetItem ( c.commandLine ) );
0073         qobject_cast<KComboBox*> ( ui->tableWidget->cellWidget ( row, ProtocolColumn ) )->setCurrentIndex ( (int)c.iface );
0074         checkInstalled(row, c.commandLine);
0075         ++row;
0076     }
0077 
0078     ui->tableWidget->resizeColumnsToContents();
0079 
0080     //connects
0081     connect(ui->autoDetectButton, &QPushButton::clicked, this, &EngineSettings::autoDetectEngines);
0082     connect(ui->addButton, &QPushButton::clicked, this, &EngineSettings::addClicked);
0083     connect(ui->removeButton, &QPushButton::clicked, this, &EngineSettings::removeClicked);
0084     connect(ui->tableWidget, &QTableWidget::itemChanged, this, &EngineSettings::tableItemChanged);
0085 }
0086 
0087 EngineSettings::~EngineSettings() {
0088     delete ui;
0089 }
0090 
0091 void EngineSettings::checkInstalled(int row, const QString& name) {
0092     const bool exists = !QStandardPaths::findExecutable(name).isEmpty();
0093     const char* iconName = exists ? "dialog-ok" : "dialog-error";
0094     QLabel* label = new QLabel(this);
0095     label->setPixmap ( QIcon::fromTheme(QLatin1String(iconName)).pixmap(32, 32) );
0096     ui->tableWidget->setCellWidget(row, InstalledColumn, label );
0097 }
0098 
0099 void EngineSettings::tableItemChanged(QTableWidgetItem* item) {
0100     //if the name of the executable was changed, check whether it's available
0101     if (item->column() == CommandColumn)
0102         checkInstalled(item->row(), item->text());
0103 }
0104 
0105 void EngineSettings::addClicked() {
0106     int n = ui->tableWidget->rowCount();
0107     ui->tableWidget->insertRow ( n );
0108     KComboBox* box = new KComboBox ( this );
0109     box->insertItems(0, {i18nc("Protocol name", "XBoard"), i18nc("Protocol name", "UCI")});
0110     ui->tableWidget->setCellWidget ( n, ProtocolColumn, box );
0111     ui->tableWidget->edit ( ui->tableWidget->model()->index ( n, NameColumn ) );
0112 }
0113 
0114 void EngineSettings::removeClicked() {
0115     if ( ui->tableWidget->selectionModel()->selectedRows().isEmpty() )
0116         return;
0117 
0118     int i = ui->tableWidget->selectionModel()->selectedRows().first().row();
0119     if (i != -1)
0120         ui->tableWidget->removeRow(i);
0121 }
0122 
0123 void EngineSettings::autoDetectEngines() {
0124     //descriptions for couple of known/popular open-source chess engines:
0125     //provide the name/Description of the engine, the name of the executable and the protocol,
0126     //keep the order within the three string lists in sync
0127 
0128     QStringList names;
0129     names << QStringLiteral("GNU Chess");
0130     names << QStringLiteral("Crafty");
0131     names << QStringLiteral("Stockfish");
0132     names << QStringLiteral("Stockfish v1.4");
0133     names << QStringLiteral("Stockfish v1.6");
0134     names << QStringLiteral("Stockfish v1.7");
0135     names << QStringLiteral("Stockfish v1.8");
0136     names << QStringLiteral("Stockfish v1.9");
0137     names << QStringLiteral("Stockfish v2.0");
0138     names << QStringLiteral("Sjeng");
0139     names << QStringLiteral("Phalanx");
0140     names << QStringLiteral("Fruit");
0141     names << QStringLiteral("Fruit v2.1");
0142     names << QStringLiteral("TogaII");
0143     names << QStringLiteral("TogaII v.1.4.2");
0144     names << QStringLiteral("Glaurung");
0145     names << QStringLiteral("HoiChess");
0146 
0147     QStringList commands;
0148     commands << QStringLiteral("gnuchess");
0149     commands << QStringLiteral("crafty");
0150     commands << QStringLiteral("stockfish");
0151     commands << QStringLiteral("stockfish14");
0152     commands << QStringLiteral("stockfish16");
0153     commands << QStringLiteral("stockfish17");
0154     commands << QStringLiteral("stockfish18");
0155     commands << QStringLiteral("stockfish19");
0156     commands << QStringLiteral("stockfish20");
0157     commands << QStringLiteral("sjeng");
0158     commands << QStringLiteral("phalanx");
0159     commands << QStringLiteral("fruit");
0160     commands << QStringLiteral("fruit21");
0161     commands << QStringLiteral("toga2");
0162     commands << QStringLiteral("togaII-142"); //openSUSE naming convention
0163     commands << QStringLiteral("glaurung");
0164     commands << QStringLiteral("hoichess");
0165 
0166     QList<EngineConfiguration::Interface> interfaces;
0167     interfaces << EngineConfiguration::XBoard; //gnuchess
0168     interfaces << EngineConfiguration::XBoard; //crafty
0169     interfaces << EngineConfiguration::Uci; //stockfish
0170     interfaces << EngineConfiguration::Uci; //stockfish14
0171     interfaces << EngineConfiguration::Uci; //stockfish16
0172     interfaces << EngineConfiguration::Uci; //stockfish17
0173     interfaces << EngineConfiguration::Uci; //stockfish18
0174     interfaces << EngineConfiguration::Uci; //stockfish19
0175     interfaces << EngineConfiguration::Uci; //stockfish20
0176     interfaces << EngineConfiguration::XBoard; //sjeng
0177     interfaces << EngineConfiguration::XBoard; //phalanx //TODO: check this
0178     interfaces << EngineConfiguration::Uci; //fruit
0179     interfaces << EngineConfiguration::Uci; //fruit21
0180     interfaces << EngineConfiguration::Uci; //toga2
0181     interfaces << EngineConfiguration::Uci; //togaII-142
0182     interfaces << EngineConfiguration::Uci; //glaurung
0183     interfaces << EngineConfiguration::XBoard; //hoichess
0184 
0185     //remove all available engines first
0186     ui->tableWidget->setRowCount(0);
0187 
0188     //check the presence of the known engines defined above and add the corresponding entries in the table widget
0189     for (int i = 0; i < commands.size(); ++i) {
0190         const QString& command = commands.at(i);
0191         const bool exists = !QStandardPaths::findExecutable(command).isEmpty();
0192         if (!exists)
0193             continue;
0194 
0195         const int row = ui->tableWidget->rowCount();
0196         ui->tableWidget->insertRow(row);
0197 
0198         //name
0199         QTableWidgetItem* item = new QTableWidgetItem(names.at(i));
0200         ui->tableWidget->setItem(row, NameColumn, item);
0201         item->setFlags(item->flags() & (~Qt::ItemIsEditable));
0202 
0203         //command
0204         item = new QTableWidgetItem(command);
0205         ui->tableWidget->setItem(row, CommandColumn, item);
0206         item->setFlags(item->flags() & (~Qt::ItemIsEditable));
0207 
0208         //interface
0209         const QString interface = (interfaces.at(i) == EngineConfiguration::XBoard) ? QStringLiteral("XBoard") : QStringLiteral("UCI");
0210         item = new QTableWidgetItem(interface);
0211         ui->tableWidget->setItem(row, ProtocolColumn, item);
0212         item->setFlags(item->flags() & (~Qt::ItemIsEditable));
0213 
0214         //icon
0215         QLabel* label = new QLabel(this);
0216         label->setPixmap( QIcon::fromTheme(QStringLiteral("dialog-ok")).pixmap(32, 32) );
0217         ui->tableWidget->setCellWidget(row, InstalledColumn, label);
0218     }
0219 }
0220 
0221 void EngineSettings::save() {
0222     QStringList out;
0223     for ( int i = 0; i < ui->tableWidget->rowCount(); ++i ) {
0224         EngineConfiguration c = EngineConfiguration ( QString() );
0225 
0226         //name
0227         QTableWidgetItem* item = ui->tableWidget->item (i, NameColumn);
0228         if (!item) continue;
0229         c.name = item->text();
0230 
0231         //command
0232         item = ui->tableWidget->item (i, CommandColumn);
0233         if (!item) continue;
0234         c.commandLine = item->text();
0235 
0236         //interface
0237         QWidget* w = ui->tableWidget->cellWidget(i, ProtocolColumn);
0238         const KComboBox* box = dynamic_cast<const KComboBox*>(w);
0239         if (box)
0240             c.iface = (EngineConfiguration::Interface)box->currentIndex();
0241         else if (ui->tableWidget->item(i, ProtocolColumn)->text() == QLatin1String("XBoard"))
0242             c.iface = EngineConfiguration::XBoard;
0243         else
0244             c.iface = EngineConfiguration::Uci;
0245 
0246         const QString str = c.toString();
0247         if(!str.isEmpty())
0248             out << c.toString();
0249     }
0250 
0251     Settings::setEngineConfigurations(out);
0252 }
0253 
0254 #include "moc_enginesettings.cpp"