#include #include #include #include "widgetinfomodel.h" namespace { inline QString get_widget_dconf_base(int index) { return QString("widget%1_").arg(index); } WidgetInfo::WidgetPosition canonicalize_widget_pos(WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size) { switch (size) { case WidgetInfo::Size1Q: return pos; // Any position is valid case WidgetInfo::Size2QHorizontal: switch (pos) { case WidgetInfo::PosNW: case WidgetInfo::PosNE: return WidgetInfo::PosNW; case WidgetInfo::PosSW: case WidgetInfo::PosSE: return WidgetInfo::PosSW; } break; case WidgetInfo::Size2QVertical: switch (pos) { case WidgetInfo::PosNW: case WidgetInfo::PosSW: return WidgetInfo::PosNW; case WidgetInfo::PosNE: case WidgetInfo::PosSE: return WidgetInfo::PosNE; } break; case WidgetInfo::Size4Q: return WidgetInfo::PosNW; // 4Q widgets use entire screen } return pos; } QBitArray used_positions(WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size) { QBitArray used(4, false); switch (size) { case WidgetInfo::Size1Q: used[pos] = true; break; case WidgetInfo::Size2QHorizontal: switch (pos) { case WidgetInfo::PosNW: case WidgetInfo::PosNE: used[WidgetInfo::PosNW] = true; used[WidgetInfo::PosNE] = true; break; case WidgetInfo::PosSW: case WidgetInfo::PosSE: used[WidgetInfo::PosNW] = true; used[WidgetInfo::PosNE] = true; break; } break; case WidgetInfo::Size2QVertical: switch (pos) { case WidgetInfo::PosNW: case WidgetInfo::PosSW: used[WidgetInfo::PosNW] = true; used[WidgetInfo::PosSW] = true; break; case WidgetInfo::PosNE: case WidgetInfo::PosSE: used[WidgetInfo::PosNE] = true; used[WidgetInfo::PosSE] = true; break; } break; case WidgetInfo::Size4Q: used.fill(true); break; } return used; } } WidgetInfoModel::WidgetInfoModel(const QString &settingsPrefix, QObject *parent) : QAbstractListModel(parent), _settings(new MDConfGroup(this)) { _settings->setPath(settingsPrefix); connect(_settings, &MDConfGroup::valueChanged, this, &WidgetInfoModel::handleSettingChanged); reload(); } QHash WidgetInfoModel::roleNames() const { QHash roles; roles[UrlRole] = "url"; roles[InvertRole] = "invert"; roles[PageRole] = "page"; roles[SizeRole] = "size"; roles[PositionRole] = "position"; return roles; } int WidgetInfoModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return _widgets.size(); } QVariant WidgetInfoModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const int row = index.row(); if (row < 0 || row >= _widgets.size()) return QVariant(); switch (role) { case UrlRole: return QVariant::fromValue(_widgets[row].url()); case InvertRole: return QVariant::fromValue(_widgets[row].invert()); case PageRole: return QVariant::fromValue(_widgets[row].page()); case SizeRole: return QVariant::fromValue(_widgets[row].size()); case PositionRole: return QVariant::fromValue(_widgets[row].position()); default: qWarning() << "Unknown role:" << role; return QVariant(); } } QList WidgetInfoModel::toList() const { return _widgets.toList(); } bool WidgetInfoModel::widgetOverlaps(int page, WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size) const { QBitArray usedPos = used_positions(pos, size); for (int i = 0; i < _widgets.size(); i++) { if (_widgets[i].url().isEmpty()) continue; if (_widgets[i].page() != page) continue; QBitArray intersection = usedPos & used_positions(_widgets[i].position(), _widgets[i].size()); if (intersection.count(true) > 0) { return true; } } return false; } int WidgetInfoModel::addWidget(const QUrl &url, int page, WidgetInfo::WidgetPosition pos, WidgetInfo::WidgetSize size) { int slot = findEmptySlot(); if (slot == -1) { qWarning() << "No empty slots!"; // This shouldn't happen return -1; } pos = canonicalize_widget_pos(pos, size); qDebug() << "Adding widget" << url << page << pos << size; if (widgetOverlaps(page, pos, size)) { qWarning() << "Can't add the widget: it overlaps with an existing one!"; return -1; } const QString base = get_widget_dconf_base(slot); _settings->setValue(base + "invert", QVariant::fromValue(false)); _settings->setValue(base + "page", QVariant::fromValue(page)); _settings->setValue(base + "size", QVariant::fromValue(size)); _settings->setValue(base + "position", QVariant::fromValue(pos)); _settings->setValue(base + "url", QVariant::fromValue(url)); // MDConf watcher will update the model. return slot; } void WidgetInfoModel::removeWidget(int widgetId) { const QString base = get_widget_dconf_base(widgetId); _settings->setValue(base + "url", QVariant()); _settings->setValue(base + "invert", QVariant()); _settings->setValue(base + "page", QVariant()); _settings->setValue(base + "size", QVariant()); _settings->setValue(base + "position", QVariant()); } void WidgetInfoModel::reload() { beginResetModel(); _widgets.resize(16); for (int i = 0; i < _widgets.size(); i++) { WidgetInfo &info = _widgets[i]; const QString base = get_widget_dconf_base(i); info.setInvert(_settings->value(base + "invert").toBool()); info.setPage(_settings->value(base + "page").toInt()); info.setSize(static_cast(_settings->value(base + "size").toInt())); info.setPosition(static_cast(_settings->value(base + "position").toInt())); info.setUrl(_settings->value(base + "url").toUrl()); } endResetModel(); } int WidgetInfoModel::findEmptySlot() { for (int i = 0; i < _widgets.size(); i++) { if (_widgets[i].url().isEmpty()) { return i; } } return -1; } void WidgetInfoModel::handleSettingChanged(const QString &key) { static const QString widgetPrefix("widget"); if (key.startsWith(widgetPrefix)) { QStringList parts = key.split('_'); if (parts.size() == 2) { bool ok = false; int slot = parts[0].mid(widgetPrefix.length()).toInt(&ok); if (!ok || slot < 0 || slot >= _widgets.size()) { qWarning() << "Invalid widget number:" << key; } WidgetInfo &info = _widgets[slot]; const QString &role = parts[1]; QVector roles; if (role == "url") { info.setUrl(_settings->value(key).toUrl()); roles << UrlRole; } else if (role == "invert") { info.setInvert(_settings->value(key).toBool()); roles << InvertRole; } else if (role == "page") { info.setPage(_settings->value(key).toInt()); roles << PageRole; } else if (role == "size") { info.setSize(static_cast(_settings->value(key).toInt())); roles << SizeRole; } else if (role == "position") { info.setPosition(static_cast(_settings->value(key).toInt())); roles << PositionRole; } else { qWarning() << "Unknown widget key changed:" << key; return; } const QModelIndex index = createIndex(slot, 0); emit dataChanged(index, index, roles); } else { qWarning() << "Unknown widget key changed:" << key; } } }