Files
qt5-legatus-legacy/Lts/Plugins/PlanOverlays/Gui/plan_overlay_view.cpp
T
2026-08-13 18:33:10 +02:00

622 lines
21 KiB
C++
Executable File

/**
**
** @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 <LtsWidgets/lts_drag_icon.h>
#include <QtCore/QByteArray>
#include <QtGui/QGuiApplication>
#include <QtGui/QPainter>
#include <QtGui/QTransform>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QSlider>
#include <QtWidgets/QStyledItemDelegate>
#include <QtWidgets/QVBoxLayout>
#include <QtWidgets/QWidget>
namespace PlanOverlaysPlugin
{
class WidgetItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
private:
PlanOverlayView *m_planOverlayView;
mutable QMap<QByteArray, QWidget *> m_widgetMap;
mutable QMap<QByteArray, QSize> 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<QWidget *>(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<PlanOverlayModel::ColumnSection>(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<PlanOverlayModel::ColumnSection>(index.column()))
{
case PlanOverlayModel::ColumnSection::PlanName:
{
QLabel *laName = qobject_cast<QLabel *>(editor);
if (laName) laName->setText(modelData.toString());
break;
}
case PlanOverlayModel::ColumnSection::MinScale:
{
QPushButton *pbMinScale = qobject_cast<QPushButton *>(editor);
if (pbMinScale) pbMinScale->setText(modelData.toString());
break;
}
case PlanOverlayModel::ColumnSection::MaxScale:
{
QPushButton *pbMaxScale = qobject_cast<QPushButton *>(editor);
if (pbMaxScale) pbMaxScale->setText(modelData.toString());
break;
}
case PlanOverlayModel::ColumnSection::Opacity:
{
QSlider *slOpacity = qobject_cast<QSlider *>(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<PlanOverlayModel *>(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<PlanOverlayModel *>(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<QPushButton *>(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<QPushButton *>(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<LtsWidgets::LtsDragIcon *>(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<LtsWidgets::LtsDragIcon *>(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<LtsWidgets::LtsDragIcon *>(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<QSlider *>(sender());
if (nullptr == senderSlider) return;
int row = senderSlider->property("row").toInt();
PlanOverlayModel *poModel = dynamic_cast<PlanOverlayModel *>(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<LtsWidgets::LtsDragIcon *>(sender());
if (nullptr == senderButton) return;
int row = senderButton->property("row").toInt();
PlanOverlayModel *poModel = dynamic_cast<PlanOverlayModel *>(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<LtsWidgets::LtsDragIcon *>(sender());
if (nullptr == senderButton) return;
int row = senderButton->property("row").toInt();
PlanOverlayModel *poModel = dynamic_cast<PlanOverlayModel *>(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 <plan_overlay_view.moc>