/** ** ** @RDE_HEADER@ ** plan_overlay_view.h ** $Id$ ** $HeadURL$ ** $Author$ ** ** ----------------------------------------------------------------------------------------------- ** ** Hardware-Environment: Intel-PC ** Software-Environment: Windows 7, Qt 5.7. ArcGIS 100 ** ** Language ........: C++ 11 ** ** ----------------------------------------------------------------------------------------------- ** ** Author ...........: Jörg Turla, SPD G, Tel. 3132 ** ** ----------------------------------------------------------------------------------------------- ** ** Notes .........: ** ** 11.07.2017, JT : First edition for use with Qt5 ** ** ----------------------------------------------------------------------------------------------- */ #include "plan_overlay_view.h" #include "plan_overlay_model.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace PlanOverlaysPlugin { class WidgetItemDelegate : public QStyledItemDelegate { Q_OBJECT private: PlanOverlayView *m_planOverlayView; mutable QMap m_widgetMap; mutable QMap m_sizeHints; QByteArray buildKey(const QModelIndex &index) const { return QByteArray::number(index.row()) + ',' + QByteArray::number(index.column()); } // QByteArray buildKey(const QModelIndex & index) const void removeKey(const QByteArray &indexKey, QObject *qobj) const { QWidget *widget = qobject_cast(qobj); if (nullptr == widget) return; if (!m_widgetMap.contains(indexKey)) return; if (widget != m_widgetMap[indexKey]) return; // qCritical() << "remove key " << indexKey << " --> " << widget; m_sizeHints.remove(indexKey); m_widgetMap.remove(indexKey); } // removeKey public: explicit WidgetItemDelegate(PlanOverlayView *planOverlayView) : QStyledItemDelegate(planOverlayView), m_planOverlayView(planOverlayView) {;} ~WidgetItemDelegate() = default; void destroyEditor(QWidget *editor, const QModelIndex &index) const override { if (!index.isValid()) return; if (m_planOverlayView->model()->rowCount() <= index.row()) return; // do not destroy will be needed again const QByteArray &indexKey = buildKey(index); removeKey(indexKey, editor); if (editor) editor-> deleteLater(); // qCritical() << "Destroy widget " << indexKey << " --> " << editor; } // void destroyEditor(QWidget* editor, const QModelIndex & index) const QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { const QByteArray &indexKey = buildKey(index); const QVariant &modelData = index.model()->data(index, Qt::DisplayRole); if (m_widgetMap.contains(indexKey)) { QWidget *w = m_widgetMap[indexKey]; removeKey(indexKey, w); if (w) w->deleteLater(); } const QString &backgroundStyle = QStringLiteral("background-color: %1; color: %2; border:none;").arg(QGuiApplication::palette().color(QPalette::ColorRole::Base).name(), m_planOverlayView->palette().text().color().name()); const QString &backgroundDisabledStyle = QStringLiteral("background-color: %1; color: %2; border:none;").arg(QGuiApplication::palette().color(QPalette::ColorGroup::Disabled, QPalette::ColorRole::Base).name(), m_planOverlayView->palette().text().color().name()); const QString &buttonStyleSheet = QStringLiteral("QPushButton:!disabled {%1}\nQPushButton:disabled {%2}").arg(backgroundStyle, backgroundDisabledStyle); switch (static_cast(index.column())) { case PlanOverlayModel::ColumnSection::OrderButtons: { if (index.model()->rowCount() > 1) { QWidget *orderButtons = new QWidget(parent); QVBoxLayout *orderLayout = new QVBoxLayout(orderButtons); QPixmap icon(QStringLiteral("icon:media-vcr-normal.svg")); // better use up and down buttons LtsWidgets::LtsDragIcon *up = nullptr; if (index.row() > 0) { up = new LtsWidgets::LtsDragIcon(orderButtons); //up->setIcon(QIcon(QStringLiteral("icon:overlay_up.svg"))); // looks not very nice --> use simple arrow up->setIcon(QIcon(icon.transformed(QTransform().rotate(-90).scale(0.5, 0.5)))); up->setText(QString()); up->setIconSize(LtsWidgets::ButtonSize::eDefault); up->setButtonStyle(LtsWidgets::ButtonStyle::eIconOnly); up->setProperty("row", index.row()); up->setToolTip(tr("Move layer up in display order")); connect(up, &LtsWidgets::LtsDragIcon::clicked, m_planOverlayView, &PlanOverlayView::onChangeLayerOrderUp); } LtsWidgets::LtsDragIcon *down = nullptr; if ((index.row() + 1) < index.model()->rowCount()) { down = new LtsWidgets::LtsDragIcon(orderButtons); //down->setIcon(QIcon(QStringLiteral("icon:overlay_down.svg"))); // looks not very nice --> use simple arrow down->setIcon(QIcon(icon.transformed(QTransform().rotate(90).scale(0.5, 0.5)))); down->setText(QString()); down->setIconSize(LtsWidgets::ButtonSize::eDefault); down->setButtonStyle(LtsWidgets::ButtonStyle::eIconOnly); down->setProperty("row", index.row()); down->setToolTip(tr("Move layer down in display order")); connect(down, &LtsWidgets::LtsDragIcon::clicked, m_planOverlayView, &PlanOverlayView::onChangeLayerOrderDown); } orderLayout->addStretch(); orderLayout->addSpacing(5); if (up) orderLayout->addWidget(up); if (down) orderLayout->addWidget(down); orderLayout->addSpacing(5); orderLayout->addStretch(); orderButtons->setAutoFillBackground(true); connect(orderButtons, &QObject::destroyed, orderButtons, [this, indexKey](QObject * qobj) { removeKey(indexKey, qobj); }); m_sizeHints[indexKey] = orderButtons->sizeHint(); m_widgetMap[indexKey] = orderButtons; // qCritical() << "setup " << indexKey << " --> " << orderButtons; return orderButtons; } break; }; case PlanOverlayModel::ColumnSection::PlanName: { QLabel *laName = new QLabel(modelData.toString(), parent); laName->setAlignment(Qt::AlignCenter); laName->setStyleSheet(QStringLiteral("QLabel:!disabled {%1}\nQLabel:disabled {%2}").arg(backgroundStyle, backgroundDisabledStyle)); m_sizeHints[indexKey] = laName->sizeHint(); m_widgetMap[indexKey] = laName; return laName; }; case PlanOverlayModel::ColumnSection::MinScale: { QPushButton *pbScaleMin = new QPushButton(parent); pbScaleMin->setStyleSheet(buttonStyleSheet); pbScaleMin->setPalette(m_planOverlayView->palette()); pbScaleMin->setAutoFillBackground(true); pbScaleMin->setProperty("row", index.row()); pbScaleMin->setText(modelData.toString()); pbScaleMin->setToolTip(tr("Get minimum scale from current map settings to limit zoom out")); connect(pbScaleMin, &QPushButton::clicked, m_planOverlayView, &PlanOverlayView::onSetMinScale); connect(pbScaleMin, &QObject::destroyed, pbScaleMin, [this, indexKey](QObject * qobj) { removeKey(indexKey, qobj); }); m_sizeHints[indexKey] = pbScaleMin->sizeHint(); m_widgetMap[indexKey] = pbScaleMin; // qCritical() << "setup " << indexKey << " --> " << pbScaleMin; return pbScaleMin; }; case PlanOverlayModel::ColumnSection::MaxScale: { QPushButton *pbScaleMax = new QPushButton(parent); pbScaleMax->setStyleSheet(buttonStyleSheet); pbScaleMax->setAutoFillBackground(true); pbScaleMax->setProperty("row", index.row()); pbScaleMax->setText(modelData.toString()); pbScaleMax->setToolTip(tr("Get maximum scale from current map settings to limit zoom in")); connect(pbScaleMax, &QPushButton::clicked, m_planOverlayView, &PlanOverlayView::onSetMaxScale); connect(pbScaleMax, &QObject::destroyed, pbScaleMax, [this, indexKey](QObject * qobj) { removeKey(indexKey, qobj); }); m_sizeHints[indexKey] = pbScaleMax->sizeHint(); m_widgetMap[indexKey] = pbScaleMax; // qCritical() << "setup " << indexKey << " --> " << pbScaleMax; return pbScaleMax; }; case PlanOverlayModel::ColumnSection::Opacity: { QSlider *slOpacity = new QSlider(parent); slOpacity->setAutoFillBackground(true); slOpacity->setOrientation(Qt::Horizontal); slOpacity->setRange(0, 100); slOpacity->setValue(int(modelData.toDouble() * 100)); slOpacity->setProperty("row", index.row()); slOpacity->setToolTip(tr("Adjust overlays opacity")); connect(slOpacity, &QSlider::sliderMoved, m_planOverlayView, &PlanOverlayView::onChangeLayerOpacity); connect(slOpacity, &QObject::destroyed, slOpacity, [this, indexKey](QObject * qobj) { removeKey(indexKey, qobj); }); m_sizeHints[indexKey] = slOpacity->sizeHint(); m_widgetMap[indexKey] = slOpacity; return slOpacity; }; case PlanOverlayModel::ColumnSection::CustomSection: { QWidget *containerWidget = new QWidget(parent); QVBoxLayout *layout = new QVBoxLayout(containerWidget); QHBoxLayout *layoutBtnRow = new QHBoxLayout(); LtsWidgets::LtsDragIcon *pbReload = new LtsWidgets::LtsDragIcon(containerWidget); pbReload->setIcon(QPixmap(QStringLiteral("icon:overlay_reload_plan.svg"))); pbReload->setText(QString()); pbReload->setIconSize(LtsWidgets::ButtonSize::eDefault); pbReload->setButtonStyle(LtsWidgets::ButtonStyle::eIconOnly); pbReload->setProperty("row", index.row()); pbReload->setAutoFillBackground(true); pbReload->setToolTip(tr("Reload plan")); LtsWidgets::LtsDragIcon *pbUnload = new LtsWidgets::LtsDragIcon(containerWidget); pbUnload->setIcon(QPixmap(QStringLiteral("icon:overlay_unload_plan.svg"))); pbUnload->setText(QString()); pbUnload->setIconSize(LtsWidgets::ButtonSize::eDefault); pbUnload->setButtonStyle(LtsWidgets::ButtonStyle::eIconOnly); pbUnload->setProperty("row", index.row()); pbUnload->setAutoFillBackground(true); pbUnload->setToolTip(tr("Unload plan and remove overlayer")); layoutBtnRow->addWidget(pbReload); layoutBtnRow->addWidget(pbUnload); layout->addStretch(2); layout->addLayout(layoutBtnRow); layout->addStretch(2); containerWidget->setAutoFillBackground(true); connect(pbReload, &LtsWidgets::LtsDragIcon::clicked, m_planOverlayView, &PlanOverlayView::onReloadOverlayById); connect(pbUnload, &LtsWidgets::LtsDragIcon::clicked, m_planOverlayView, &PlanOverlayView::onUnloadOverlay); m_sizeHints[indexKey] = containerWidget->sizeHint(); m_widgetMap[indexKey] = containerWidget; return containerWidget; }; case PlanOverlayModel::ColumnSection::ExerciseId: case PlanOverlayModel::ColumnSection::PhaseId: break; } // switch const QString &labelText = m_planOverlayView->model()->data(index, Qt::ItemDataRole::DisplayRole).toString(); #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) int labelWidth = option.fontMetrics.width(labelText); #else int labelWidth = option.fontMetrics.horizontalAdvance(labelText); #endif labelWidth = (option.rect.width() > labelWidth) ? option.rect.width() : labelWidth + labelWidth / 5; m_sizeHints[indexKey] = QSize(labelWidth, option.rect.height()); return nullptr; } // QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { const QByteArray &indexKey = buildKey(index); if (m_widgetMap.contains(indexKey)) { QWidget *w = m_widgetMap[indexKey]; if (w) { w->setGeometry(option.rect); painter->drawPixmap(option.rect.x(), option.rect.y(), w->grab()); return; } } QStyledItemDelegate::paint(painter, option, index); } // void paint( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { Q_UNUSED(option); const QByteArray &indexKey = buildKey(index); return (m_sizeHints.contains(indexKey)) ? m_sizeHints[indexKey] : QSize(); } // QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const void setEditorData(QWidget *editor, const QModelIndex &index) const override { const QVariant &modelData = index.model()->data(index, Qt::ItemDataRole::DisplayRole); switch (static_cast(index.column())) { case PlanOverlayModel::ColumnSection::PlanName: { QLabel *laName = qobject_cast(editor); if (laName) laName->setText(modelData.toString()); break; } case PlanOverlayModel::ColumnSection::MinScale: { QPushButton *pbMinScale = qobject_cast(editor); if (pbMinScale) pbMinScale->setText(modelData.toString()); break; } case PlanOverlayModel::ColumnSection::MaxScale: { QPushButton *pbMaxScale = qobject_cast(editor); if (pbMaxScale) pbMaxScale->setText(modelData.toString()); break; } case PlanOverlayModel::ColumnSection::Opacity: { QSlider *slOpacity = qobject_cast(editor); if (slOpacity) slOpacity->setValue(int(modelData.toDouble() * 100)); break; } case PlanOverlayModel::ColumnSection::ExerciseId: case PlanOverlayModel::ColumnSection::PhaseId: case PlanOverlayModel::ColumnSection::OrderButtons: case PlanOverlayModel::ColumnSection::CustomSection: break; } } // void setEditorData(QWidget * editor, const QModelIndex & index) const void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { Q_UNUSED(editor); Q_UNUSED(model); Q_UNUSED(index); } // void setModelData(QWidget * editor, QAbstractItemModel * model; const QModelIndex & index) const }; // WidgetItemDelegate PlanOverlayView::PlanOverlayView(PlanOverlayModel *model, QWidget *parent) : QTableView(parent) { setModel(model); connect(model, &PlanOverlayModel::dropPlanAccepted, this, [this]() { setCurrentIndex(QModelIndex()); PlanOverlayView::clearSelection(); }); connect (model, &PlanOverlayModel::layoutChanged, this, &PlanOverlayView::onLayoutChanged); setSelectionMode(SelectionMode::SingleSelection); // selection have to be enabled to get drag working setSelectionBehavior(SelectionBehavior::SelectRows); setItemDelegate(new WidgetItemDelegate(this)); setEditTriggers(QAbstractItemView::AllEditTriggers); setDragEnabled(true); setAcceptDrops(true); setDragDropMode(QAbstractItemView::DragDrop); setDragDropOverwriteMode(false); setDropIndicatorShown(true); setSortingEnabled(false); setShowGrid(false); horizontalHeader()->setSectionResizeMode(int(PlanOverlayModel::ColumnSection::PlanName), QHeaderView::ResizeMode::Stretch); resizeColumnsToContents(); resizeRowsToContents(); horizontalHeader()->reset(); //allways persistent editor onLayoutChanged(); } // PlanOverlayView::PlanOverlayView() PlanOverlayView::~PlanOverlayView() { } // PlanOverlayView::~PlanOverlayView() void PlanOverlayView::onLayoutChanged() { clearSelection(); if (model() != nullptr) { for (int row = 0; row < model()->rowCount(); ++row) { for (int col = 0; col < model()->columnCount(); ++col) openPersistentEditor(model()->index(row, col, QModelIndex())); } } resizeRowsToContents(); resizeColumnsToContents(); horizontalHeader()->setSectionResizeMode(int(PlanOverlayModel::ColumnSection::PlanName), QHeaderView::ResizeMode::Stretch); } // void PlanOverlayView::updateGeometries() int PlanOverlayView::getPlanKeyIdOfRow(int row) const { PlanOverlayModel *poModel = dynamic_cast(model()); if (nullptr == poModel) return -1; if (row < 0) return -1; if (row >= poModel->rowCount(QModelIndex())) return -1; const PlanOverlayModel::PlanItem &planItem = poModel->planItemAtRow(row); return (planItem.isValid()) ? planItem.m_keyId : -1; } // int PlanOverlayView::getPlanKeyIdOfRow(int row) const QString PlanOverlayView::getPlanNameOfRow(int row) const { PlanOverlayModel *poModel = dynamic_cast(model()); if (nullptr == poModel) return QString(); if (row < 0) return QString(); if (row >= poModel->rowCount(QModelIndex())) return QString(); const PlanOverlayModel::PlanItem &planItem = poModel->planItemAtRow(row); return (planItem.isValid()) ? planItem.m_fileName : QString(); } // int PlanOverlayView::getPlanNameOfRow(int row) const void PlanOverlayView::onSetMinScale() { QPushButton *senderButton = qobject_cast(sender()); if (nullptr == senderButton) return; int row = senderButton->property("row").toInt(); int keyId = getPlanKeyIdOfRow(row); if (keyId < 0) return; emit requestMinScale(keyId, model()->data(model()->index(row, int(PlanOverlayModel::ColumnSection::PlanName), QModelIndex()), Qt::ItemDataRole::DisplayRole).toString()); } // void PlanOverlayView::onSetMinScale() void PlanOverlayView::onSetMaxScale() { QPushButton *senderButton = qobject_cast(sender()); if (nullptr == senderButton) return; int row = senderButton->property("row").toInt(); int keyId = getPlanKeyIdOfRow(row); if (keyId < 0) return; emit requestMaxScale(keyId, model()->data(model()->index(row, int(PlanOverlayModel::ColumnSection::PlanName), QModelIndex()), Qt::ItemDataRole::DisplayRole).toString()); } // void PlanOverlayView::onSetMaxScale() void PlanOverlayView::onReloadOverlayById() { LtsWidgets::LtsDragIcon *senderButton = qobject_cast(sender()); if (nullptr == senderButton) return; int row = senderButton->property("row").toInt(); int keyId = getPlanKeyIdOfRow(row); if (keyId < 0) return; emit requestReloadPlanById(keyId); } // void PlanOverlayView::onReloadOverlayById() void PlanOverlayView::onReloadOverlayByName() { LtsWidgets::LtsDragIcon *senderButton = qobject_cast(sender()); if (nullptr == senderButton) return; int row = senderButton->property("row").toInt(); QString planName = getPlanNameOfRow(row); if (planName.isEmpty()) return; emit requestReloadPlanByName(planName); } // void PlanOverlayView::onReloadOverlayById() void PlanOverlayView::onUnloadOverlay() { LtsWidgets::LtsDragIcon *senderButton = qobject_cast(sender()); if (nullptr == senderButton) return; int keyId = getPlanKeyIdOfRow(senderButton->property("row").toInt()); if (keyId >= 0) emit requestUnloadPlan(keyId); } // void PlanOverlayView::onUnloadOverlay() void PlanOverlayView::onChangeLayerOpacity(int sliderValue) { QSlider *senderSlider = qobject_cast(sender()); if (nullptr == senderSlider) return; int row = senderSlider->property("row").toInt(); PlanOverlayModel *poModel = dynamic_cast(model()); if (nullptr == poModel) return; if (row < 0) return; if (row >= poModel->rowCount(QModelIndex())) return; poModel->setOpacity(row, double(sliderValue) / 100.0); } // void PlanOverlayView::onChangeLayerOpacity(int sliderValue) void PlanOverlayView::onChangeLayerOrderUp() { LtsWidgets::LtsDragIcon *senderButton = qobject_cast(sender()); if (nullptr == senderButton) return; int row = senderButton->property("row").toInt(); PlanOverlayModel *poModel = dynamic_cast(model()); if (nullptr == poModel) return; if (row < 1) return; if (row >= poModel->rowCount(QModelIndex())) return; poModel->swapRow(row, row - 1); } // void PlanOverlayView::onChangeLayerOrderUp() void PlanOverlayView::onChangeLayerOrderDown() { LtsWidgets::LtsDragIcon *senderButton = qobject_cast(sender()); if (nullptr == senderButton) return; int row = senderButton->property("row").toInt(); PlanOverlayModel *poModel = dynamic_cast(model()); if (nullptr == poModel) return; if (row < 0) return; if (row + 1 >= poModel->rowCount(QModelIndex())) return; poModel->swapRow(row, row + 1); } // void PlanOverlayView::onChangeLayerOrderDown() } // namespace PlanOverlaysPlugin #include