File indexing completed on 2024-12-08 04:31:13

0001 /***************************************************************************
0002  *   Copyright (C) 2009 Matthias Fuchs <mat69@gmx.net>                     *
0003  *                                                                         *
0004  *   This program is free software; you can redistribute it and/or modify  *
0005  *   it under the terms of the GNU General Public License as published by  *
0006  *   the Free Software Foundation; either version 2 of the License, or     *
0007  *   (at your option) any later version.                                   *
0008  *                                                                         *
0009  *   This program is distributed in the hope that it will be useful,       *
0010  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
0011  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
0012  *   GNU General Public License for more details.                          *
0013  *                                                                         *
0014  *   You should have received a copy of the GNU General Public License     *
0015  *   along with this program; if not, write to the                         *
0016  *   Free Software Foundation, Inc.,                                       *
0017  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
0018  ***************************************************************************/
0019 
0020 #include "mirrormodel.h"
0021 
0022 #include <QComboBox>
0023 #include <QSpinBox>
0024 #include <QStandardPaths>
0025 
0026 #include <KCountry>
0027 #include <KLineEdit>
0028 #include <KLocalizedString>
0029 
0030 MirrorDelegate::MirrorDelegate(QObject *parent)
0031     : QStyledItemDelegate(parent)
0032     , m_countrySort(nullptr)
0033 {
0034 }
0035 
0036 MirrorDelegate::MirrorDelegate(QSortFilterProxyModel *countrySort, QObject *parent)
0037     : QStyledItemDelegate(parent)
0038     , m_countrySort(countrySort)
0039 {
0040 }
0041 
0042 QWidget *MirrorDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
0043 {
0044     Q_UNUSED(option)
0045 
0046     if (index.isValid()) {
0047         if (index.column() == MirrorItem::Url) {
0048             auto *line = new KLineEdit(parent);
0049 
0050             return line;
0051         } else if (index.column() == MirrorItem::Connections) {
0052             auto *numConnections = new QSpinBox(parent);
0053             numConnections->setRange(0, 20);
0054 
0055             return numConnections;
0056         } else if (index.column() == MirrorItem::Priority) {
0057             auto *priority = new QSpinBox(parent);
0058             priority->setRange(0, 999999);
0059 
0060             return priority;
0061         } else if (index.column() == MirrorItem::Country) {
0062             if (m_countrySort) {
0063                 auto *countrySort = new QComboBox(parent);
0064                 countrySort->setModel(m_countrySort);
0065 
0066                 return countrySort;
0067             }
0068         }
0069     }
0070 
0071     return nullptr;
0072 }
0073 
0074 void MirrorDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
0075 {
0076     if (index.isValid() && editor) {
0077         if (index.column() == MirrorItem::Url) {
0078             auto *line = static_cast<KLineEdit *>(editor);
0079             const QUrl url = index.data(Qt::EditRole).toUrl();
0080             line->setUrl(url);
0081         } else if (index.column() == MirrorItem::Connections) {
0082             auto *numConnections = static_cast<QSpinBox *>(editor);
0083             const int num = index.data(Qt::EditRole).toInt();
0084             numConnections->setValue(num);
0085         } else if (index.column() == MirrorItem::Priority) {
0086             auto *priority = static_cast<QSpinBox *>(editor);
0087             const int num = index.data(Qt::EditRole).toInt();
0088             priority->setValue(num);
0089         } else if (index.column() == MirrorItem::Country) {
0090             auto *countrySort = static_cast<QComboBox *>(editor);
0091             const QString countryCode = index.data(Qt::EditRole).toString();
0092             const int indexCountrySort = countrySort->findData(countryCode);
0093             countrySort->setCurrentIndex(indexCountrySort);
0094         }
0095     }
0096 }
0097 
0098 void MirrorDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
0099 {
0100     if (index.isValid() && editor && model) {
0101         if (index.column() == MirrorItem::Url) {
0102             auto *line = static_cast<KLineEdit *>(editor);
0103             if (!line->text().isEmpty()) {
0104                 model->setData(index, line->text());
0105             }
0106         } else if (index.column() == MirrorItem::Connections) {
0107             auto *numConnections = static_cast<QSpinBox *>(editor);
0108             model->setData(index, numConnections->value());
0109         } else if (index.column() == MirrorItem::Priority) {
0110             auto *priority = static_cast<QSpinBox *>(editor);
0111             model->setData(index, priority->value());
0112         } else if (index.column() == MirrorItem::Country) {
0113             auto *countrySort = static_cast<QComboBox *>(editor);
0114             const QString countryCode = countrySort->itemData(countrySort->currentIndex()).toString();
0115             model->setData(index, countryCode);
0116         }
0117     }
0118 }
0119 
0120 void MirrorDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
0121 {
0122     Q_UNUSED(index)
0123     editor->setGeometry(option.rect);
0124 }
0125 
0126 QSize MirrorDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0127 {
0128     // make the sizeHint a little bit nicer to have more beautiful editors
0129     QSize hint;
0130     hint.setWidth(QStyledItemDelegate::sizeHint(option, index).width());
0131     hint.setHeight(option.fontMetrics.height() + 7);
0132     return hint;
0133 }
0134 
0135 MirrorProxyModel::MirrorProxyModel(QObject *parent)
0136     : QSortFilterProxyModel(parent)
0137 {
0138 }
0139 
0140 bool MirrorProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
0141 {
0142     if (left.column() == MirrorItem::Used) {
0143         const int leftData = sourceModel()->data(left, Qt::CheckStateRole).toInt();
0144         const int rightData = sourceModel()->data(right, Qt::CheckStateRole).toInt();
0145         return leftData < rightData;
0146     } else if (left.column() == MirrorItem::Priority) {
0147         const int leftData = sourceModel()->data(left, Qt::UserRole).toInt();
0148         const int rightData = sourceModel()->data(right, Qt::UserRole).toInt();
0149         return (!leftData ? true : (leftData > rightData) && rightData); // 0 is always smallest, otherwise larger is smaller
0150     }
0151 
0152     return QSortFilterProxyModel::lessThan(left, right);
0153 }
0154 
0155 MirrorItem::MirrorItem()
0156     : m_checked(Qt::Unchecked)
0157     , m_numConnections(0)
0158     , m_priority(0)
0159 {
0160 }
0161 
0162 QVariant MirrorItem::data(int column, int role) const
0163 {
0164     if (column == MirrorItem::Used) {
0165         if (role == Qt::CheckStateRole) {
0166             return m_checked;
0167         }
0168     } else if (column == MirrorItem::Url) {
0169         if (role == Qt::DisplayRole) {
0170             return m_url.toString();
0171         } else if ((role == Qt::UserRole) || (role == Qt::EditRole)) {
0172             return QVariant(m_url);
0173         }
0174     } else if (column == MirrorItem::Connections) {
0175         if (role == Qt::DisplayRole) {
0176             if (m_numConnections) {
0177                 return m_numConnections;
0178             } else {
0179                 return i18n("not specified");
0180             }
0181         } else if ((role == Qt::EditRole) || (role == Qt::UserRole)) {
0182             return m_numConnections;
0183         }
0184     } else if (column == MirrorItem::Priority) {
0185         if (role == Qt::DisplayRole) {
0186             if (m_priority) {
0187                 return m_priority;
0188             } else {
0189                 return i18n("not specified");
0190             }
0191         } else if ((role == Qt::EditRole) || (role == Qt::UserRole)) {
0192             return m_priority;
0193         }
0194     } else if (column == MirrorItem::Country) {
0195         if (role == Qt::DisplayRole) {
0196             return m_countryName;
0197         } else if ((role == Qt::UserRole) || (role == Qt::EditRole)) {
0198             return m_countryCode;
0199         }
0200     }
0201 
0202     return QVariant();
0203 }
0204 
0205 Qt::ItemFlags MirrorItem::flags(int column) const
0206 {
0207     Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
0208     if (column == MirrorItem::Used) {
0209         flags |= Qt::ItemIsUserCheckable;
0210     } else if (column == MirrorItem::Url) {
0211         flags |= Qt::ItemIsEditable;
0212     } else if (column == MirrorItem::Connections) {
0213         flags |= Qt::ItemIsEditable;
0214     } else if (column == MirrorItem::Priority) {
0215         flags |= Qt::ItemIsEditable;
0216     } else if (column == MirrorItem::Country) {
0217         flags |= Qt::ItemIsEditable;
0218     }
0219 
0220     return flags;
0221 }
0222 
0223 bool MirrorItem::setData(int column, const QVariant &value, int role)
0224 {
0225     if ((column == MirrorItem::Used) && (role == Qt::CheckStateRole)) {
0226         m_checked = static_cast<Qt::CheckState>(value.toInt());
0227         return true;
0228     } else if ((column == MirrorItem::Url) && (role == Qt::EditRole)) {
0229         QUrl url;
0230         if (value.type() == QVariant::Url) {
0231             url = QUrl(value.toUrl());
0232         } else if (value.type() == QVariant::String) {
0233             url = QUrl(value.toString());
0234         }
0235 
0236         if (!url.isEmpty()) {
0237             m_url = url;
0238             return true;
0239         }
0240     } else if ((column == MirrorItem::Connections) && (role == Qt::EditRole)) {
0241         m_numConnections = value.toInt();
0242         return true;
0243     } else if ((column == MirrorItem::Priority) && (role == Qt::EditRole)) {
0244         m_priority = value.toInt();
0245         return true;
0246     } else if ((column == MirrorItem::Country) && (role == Qt::EditRole)) {
0247         m_countryCode = value.toString();
0248         m_countryName = KCountry::fromAlpha2(m_countryCode).name();
0249         return true;
0250     }
0251 
0252     return false;
0253 }
0254 
0255 MirrorModel::MirrorModel(QObject *parent)
0256     : QAbstractTableModel(parent)
0257 {
0258 }
0259 
0260 MirrorModel::~MirrorModel()
0261 {
0262     qDeleteAll(m_data);
0263 }
0264 
0265 int MirrorModel::rowCount(const QModelIndex &index) const
0266 {
0267     if (!index.isValid()) {
0268         return m_data.count();
0269     } else {
0270         return 0;
0271     }
0272 }
0273 
0274 int MirrorModel::columnCount(const QModelIndex &index) const
0275 {
0276     if (index.isValid()) {
0277         return 0;
0278     }
0279 
0280     return 5;
0281 }
0282 
0283 QVariant MirrorModel::headerData(int section, Qt::Orientation orientation, int role) const
0284 {
0285     if (orientation == Qt::Vertical) {
0286         return QVariant();
0287     }
0288 
0289     if ((section == MirrorItem::Url) && (role == Qt::DisplayRole)) {
0290         return i18nc("Mirror as in server, in url", "Mirror");
0291     } else if (section == MirrorItem::Priority) {
0292         if (role == Qt::DisplayRole) {
0293             return i18nc("The priority of the mirror", "Priority");
0294         } else if (role == Qt::DecorationRole) {
0295             return QIcon::fromTheme("games-highscores");
0296         }
0297     } else if ((section == MirrorItem::Connections) && (role == Qt::DisplayRole)) {
0298         return i18nc("Number of parallel connections to the mirror", "Connections");
0299     } else if ((section == MirrorItem::Country) && (role == Qt::DisplayRole)) {
0300         return i18nc("Location = country", "Location");
0301     }
0302 
0303     return QVariant();
0304 }
0305 
0306 QVariant MirrorModel::data(const QModelIndex &index, int role) const
0307 {
0308     if (!index.isValid()) {
0309         return QVariant();
0310     }
0311 
0312     return m_data.at(index.row())->data(index.column(), role);
0313 }
0314 
0315 Qt::ItemFlags MirrorModel::flags(const QModelIndex &index) const
0316 {
0317     if (!index.isValid()) {
0318         return Qt::NoItemFlags;
0319     }
0320 
0321     return m_data.at(index.row())->flags(index.column());
0322 }
0323 
0324 bool MirrorModel::setData(const QModelIndex &index, const QVariant &value, int role)
0325 {
0326     if (!index.isValid()) {
0327         return false;
0328     }
0329 
0330     const bool changed = m_data.at(index.row())->setData(index.column(), value, role);
0331     if (changed) {
0332         Q_EMIT dataChanged(index, index);
0333     }
0334     return changed;
0335 }
0336 
0337 bool MirrorModel::removeRows(int row, int count, const QModelIndex &parent)
0338 {
0339     if (parent.isValid() || (row < 0) || (count < 1) || (row + count > rowCount())) {
0340         return false;
0341     }
0342 
0343     beginRemoveRows(parent, row, row + count - 1);
0344     while (count) {
0345         MirrorItem *item = m_data[row];
0346         m_data.removeAt(row);
0347         delete item;
0348         --count;
0349     }
0350     endRemoveRows();
0351 
0352     return true;
0353 }
0354 
0355 void MirrorModel::addMirror(const QUrl &url, int numConnections, int priority, const QString &countryCode)
0356 {
0357     if (!url.isValid()) {
0358         return;
0359     }
0360 
0361     for (int i = 0; i < rowCount(); ++i) {
0362         // exists already, so remove the row
0363         if (QUrl(m_data.at(i)->data(MirrorItem::Url).toString()) == url) {
0364             removeRow(i);
0365             break;
0366         }
0367     }
0368 
0369     int index = rowCount();
0370     Q_EMIT beginInsertRows(QModelIndex(), index, index);
0371 
0372     auto *item = new MirrorItem;
0373     m_data.append(item);
0374     item->setData(MirrorItem::Used, Qt::Checked, Qt::CheckStateRole); // every newly added mirror is set to checked automatically
0375     item->setData(MirrorItem::Url, QVariant(url));
0376     item->setData(MirrorItem::Connections, numConnections);
0377     item->setData(MirrorItem::Priority, priority);
0378     item->setData(MirrorItem::Country, countryCode);
0379 
0380     Q_EMIT endInsertRows();
0381 }
0382 
0383 void MirrorModel::setMirrors(const QHash<QUrl, QPair<bool, int>> &mirrors)
0384 {
0385     beginResetModel();
0386     removeRows(0, rowCount());
0387 
0388     QHash<QUrl, QPair<bool, int>>::const_iterator it;
0389     QHash<QUrl, QPair<bool, int>>::const_iterator itEnd = mirrors.constEnd();
0390     for (it = mirrors.constBegin(); it != itEnd; ++it) {
0391         auto *item = new MirrorItem;
0392         item->setData(MirrorItem::Url, QVariant(it.key()));
0393         Qt::CheckState state = (*it).first ? Qt::Checked : Qt::Unchecked;
0394         item->setData(MirrorItem::Used, state, Qt::CheckStateRole);
0395         item->setData(MirrorItem::Connections, (*it).second);
0396         m_data.append(item);
0397     }
0398 
0399     endResetModel();
0400 }
0401 
0402 QHash<QUrl, QPair<bool, int>> MirrorModel::availableMirrors() const
0403 {
0404     QHash<QUrl, QPair<bool, int>> mirrors;
0405     foreach (MirrorItem *item, m_data) {
0406         bool used = (item->data(MirrorItem::Used, Qt::CheckStateRole).toInt() == Qt::Checked) ? true : false;
0407         const QUrl url = QUrl(item->data(MirrorItem::Url).toString());
0408         mirrors[url] = QPair<bool, int>(used, item->data(MirrorItem::Connections, Qt::UserRole).toInt());
0409     }
0410     return mirrors;
0411 }
0412 
0413 #include "moc_mirrormodel.cpp"