File indexing completed on 2024-04-28 04:05:20
0001 /* 0002 SPDX-FileCopyrightText: 2015 Jakob Gruber <jakob.gruber@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "selectboardwindow.h" 0008 0009 #include <KLocalizedString> 0010 #include <QAbstractTableModel> 0011 #include <QDialogButtonBox> 0012 #include <QPushButton> 0013 #include <QVBoxLayout> 0014 #include <QtAlgorithms> 0015 #include <assert.h> 0016 0017 #include "src/logic/elapsedtime.h" 0018 #include "src/logic/levelloader.h" 0019 0020 static QString diffString(const int difficulty); 0021 0022 class LevelTableModel : public QAbstractTableModel 0023 { 0024 public: 0025 LevelTableModel(QList<QSharedPointer<Level> > &levels, QObject * parent = nullptr); 0026 0027 enum Columns { 0028 Name, 0029 LevelSet, 0030 Difficulty, 0031 Size, 0032 Solved, 0033 ColumnCount /* not a real column */ 0034 }; 0035 0036 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 0037 int columnCount(const QModelIndex &parent = QModelIndex()) const override; 0038 QVariant data(const QModelIndex &index, int role) const override; 0039 QVariant headerData(int section, Qt::Orientation orientation, int role) const override; 0040 void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; 0041 0042 private: 0043 QList<QSharedPointer<Level> > &m_levels; 0044 }; 0045 0046 LevelTableModel::LevelTableModel(QList<QSharedPointer<Level> > &levels, QObject *parent) : 0047 QAbstractTableModel(parent), m_levels(levels) 0048 { 0049 0050 } 0051 0052 int LevelTableModel::rowCount(const QModelIndex &parent) const { 0053 Q_UNUSED(parent); 0054 return m_levels.size(); 0055 } 0056 0057 int LevelTableModel::columnCount(const QModelIndex &parent) const { 0058 Q_UNUSED(parent); 0059 return ColumnCount; 0060 } 0061 0062 QVariant LevelTableModel::data(const QModelIndex &index, int role) const { 0063 if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::TextAlignmentRole)) { 0064 return QVariant(); 0065 } 0066 0067 if (role == Qt::TextAlignmentRole) { 0068 return Qt::AlignCenter; 0069 } 0070 0071 QSharedPointer<Level> level= m_levels[index.row()]; 0072 switch (index.column()) { 0073 case Name: return level->visibleName(); 0074 case LevelSet: return level->levelset(); 0075 case Difficulty: return diffString(level->difficulty()); 0076 case Size: return QStringLiteral("%1x%2").arg(level->width()).arg(level->height()); 0077 case Solved: return (level->solved()) ? Time(level->solvedTime()).toString() : QStringLiteral("-"); 0078 default: assert(0); 0079 } 0080 0081 return QVariant(); 0082 } 0083 0084 QVariant LevelTableModel::headerData(int section, Qt::Orientation orientation, int role) const { 0085 if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { 0086 switch (section) { 0087 case Name: return QVariant(ki18n("Level").toString()); 0088 case LevelSet: return QVariant(ki18n("Level Set").toString()); 0089 case Difficulty: return QVariant(ki18n("Difficulty").toString()); 0090 case Size: return QVariant(ki18n("Size").toString()); 0091 case Solved: return QVariant(ki18nc("board solved", "Solved").toString()); 0092 default: assert(0); 0093 } 0094 } 0095 return QAbstractTableModel::headerData(section, orientation, role); 0096 } 0097 0098 typedef bool (*LevelComparator)(const QSharedPointer<Level> &, 0099 const QSharedPointer<Level> &); 0100 0101 static bool levelLessThan(const QSharedPointer<Level> &lhs, 0102 const QSharedPointer<Level> &rhs) { 0103 return lhs->name() < rhs->name(); 0104 } 0105 0106 static bool levelGreaterThan(const QSharedPointer<Level> &lhs, 0107 const QSharedPointer<Level> &rhs) { 0108 return levelLessThan(rhs, lhs); 0109 } 0110 0111 static bool diffLessThan(const QSharedPointer<Level> &lhs, 0112 const QSharedPointer<Level> &rhs) { 0113 return lhs->difficulty() < rhs->difficulty(); 0114 } 0115 0116 static bool diffGreaterThan(const QSharedPointer<Level> &lhs, 0117 const QSharedPointer<Level> &rhs) { 0118 return diffLessThan(rhs, lhs); 0119 } 0120 0121 static bool solvedLessThan(const QSharedPointer<Level> &lhs, 0122 const QSharedPointer<Level> &rhs) { 0123 if (!lhs->solved() && !rhs->solved()) { 0124 return false; 0125 } else if (!lhs->solved() || !rhs->solved()) { 0126 return lhs->solved(); 0127 } else { 0128 return (lhs->solvedTime() < rhs->solvedTime()); 0129 } 0130 } 0131 0132 static bool solvedGreaterThan(const QSharedPointer<Level> &lhs, 0133 const QSharedPointer<Level> &rhs) { 0134 return solvedLessThan(rhs, lhs); 0135 } 0136 0137 void LevelTableModel::sort(int column, Qt::SortOrder order) { 0138 LevelComparator cmp = NULL; 0139 switch (column) { 0140 case Name: cmp = (order == Qt::AscendingOrder) ? levelLessThan : levelGreaterThan; break; 0141 case Difficulty: cmp = (order == Qt::AscendingOrder) ? diffLessThan : diffGreaterThan; break; 0142 case Solved: cmp = (order == Qt::AscendingOrder) ? solvedLessThan : solvedGreaterThan; break; 0143 default: assert(0); 0144 } 0145 0146 std::stable_sort(m_levels.begin(), m_levels.end(), cmp); 0147 Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, columnCount() -1)); 0148 } 0149 0150 SelectBoardWindow::SelectBoardWindow(QWidget *parent) 0151 : QDialog(parent) 0152 { 0153 setModal(true); 0154 setWindowTitle(i18nc("@title:window", "Level Selection")); 0155 QVBoxLayout *mainLayout = new QVBoxLayout; 0156 setLayout(mainLayout); 0157 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); 0158 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); 0159 okButton->setDefault(true); 0160 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 0161 connect(buttonBox, &QDialogButtonBox::accepted, this, &SelectBoardWindow::accept); 0162 connect(buttonBox, &QDialogButtonBox::rejected, this, &SelectBoardWindow::reject); 0163 mainLayout->addWidget(buttonBox); 0164 0165 QWidget *mainWidget = new QWidget(this); 0166 ui = new Ui::LevelSelectUi; 0167 ui->setupUi(mainWidget); 0168 mainLayout->addWidget(mainWidget); 0169 mainLayout->addWidget(buttonBox); 0170 0171 m_levels = LevelLoader::load(); 0172 m_model = QSharedPointer<LevelTableModel>(new LevelTableModel(m_levels)); 0173 0174 ui->tableView->setUpdatesEnabled(false); 0175 ui->tableView->setModel(m_model.data()); 0176 0177 /* Hide all columns except Level, Difficulty and Solved. */ 0178 0179 for (int i = 0; i < m_model->columnCount(); i++) { 0180 ui->tableView->hideColumn(i); 0181 } 0182 ui->tableView->showColumn(LevelTableModel::Name); 0183 ui->tableView->showColumn(LevelTableModel::Difficulty); 0184 ui->tableView->showColumn(LevelTableModel::Solved); 0185 0186 ui->tableView->sortByColumn(LevelTableModel::Name, Qt::AscendingOrder); 0187 ui->tableView->sortByColumn(LevelTableModel::Solved, Qt::DescendingOrder); 0188 ui->tableView->sortByColumn(LevelTableModel::Difficulty, Qt::AscendingOrder); 0189 0190 ui->tableView->resizeColumnsToContents(); 0191 ui->tableView->resizeRowsToContents(); 0192 0193 if (m_levels.empty()) { 0194 okButton->setEnabled(false); 0195 } else { 0196 resetSelection(); 0197 connect(ui->tableView->selectionModel(), &QItemSelectionModel::currentRowChanged, 0198 this, &SelectBoardWindow::selectedLevelChanged); 0199 connect(m_model.data(), &QAbstractItemModel::dataChanged, 0200 this, &SelectBoardWindow::levelDataChanged); 0201 updateDetails(m_levels[0]); 0202 } 0203 0204 ui->tableView->setUpdatesEnabled(true); 0205 } 0206 0207 void SelectBoardWindow::showEvent(QShowEvent *event) { 0208 updateDetails(selectedBoard()); 0209 QDialog::showEvent(event); 0210 } 0211 0212 void SelectBoardWindow::resizeEvent(QResizeEvent *event) { 0213 updateDetails(selectedBoard()); 0214 QDialog::resizeEvent(event); 0215 } 0216 0217 SelectBoardWindow::~SelectBoardWindow() { 0218 delete ui; 0219 } 0220 0221 void SelectBoardWindow::selectedLevelChanged(const QModelIndex ¤t, const QModelIndex &previous) { 0222 Q_UNUSED(previous); 0223 updateDetails(m_levels[current.row()]); 0224 } 0225 0226 void SelectBoardWindow::levelDataChanged(const QModelIndex &topLeft, const 0227 QModelIndex &bottomRight) { 0228 Q_UNUSED(topLeft); 0229 Q_UNUSED(bottomRight); 0230 resetSelection(); 0231 updateDetails(selectedBoard()); 0232 } 0233 0234 static QString diffString(const int difficulty) { 0235 if (difficulty < 0) { 0236 return diffString(0); 0237 } else if (difficulty > 7) { 0238 return diffString(7); 0239 } 0240 0241 switch (difficulty) { 0242 case 0: return ki18n("Ridiculously Easy").toString(); 0243 case 1: return ki18n("Very Easy").toString(); 0244 case 2: return ki18n("Easy").toString(); 0245 case 3: return ki18n("Medium").toString(); 0246 case 4: return ki18n("Hard").toString(); 0247 case 5: return ki18n("Very Hard").toString(); 0248 case 6: return ki18n("Extremely Hard").toString(); 0249 case 7: return ki18n("Impossible").toString(); 0250 default: throw OutOfBoundsException(); 0251 } 0252 } 0253 0254 void SelectBoardWindow::updateDetails(QSharedPointer<Level> level) { 0255 ui->labelName->setText(i18n("Name: %1", level->visibleName())); 0256 ui->labelAuthor->setText(i18n("Author: %1", level->author())); 0257 ui->labelSize->setText(i18n("Size: %1x%2", level->width(), level->height())); 0258 ui->labelDifficulty->setText(i18n("Difficulty: %1", diffString(level->difficulty()))); 0259 if (level->solved()) { 0260 ui->labelSolved->setText(i18nc("board solve time", "Solved: %1", 0261 Time(level->solvedTime()).toString())); 0262 QPixmap scaled = level->preview().scaled(ui->labelImage->size(), 0263 Qt::KeepAspectRatio, 0264 Qt::FastTransformation); 0265 ui->labelImage->setPixmap(scaled); 0266 } else { 0267 ui->labelSolved->setText(i18nc("board not solved yet", "Solved: -")); 0268 ui->labelImage->setText(QStringLiteral("?")); 0269 } 0270 } 0271 0272 void SelectBoardWindow::resetSelection() { 0273 QModelIndex index = m_model->index(0, 0); 0274 QItemSelectionModel::SelectionFlags flags = 0275 QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows; 0276 ui->tableView->selectionModel()->select(index, flags); 0277 } 0278 0279 QSharedPointer<Level> SelectBoardWindow::selectedBoard() const { 0280 int index = ui->tableView->selectionModel()->selectedIndexes().at(0).row(); 0281 return m_levels[index]; 0282 } 0283 0284 #include "moc_selectboardwindow.cpp"