/** * @RDE_HEADER@ * SituationMap - Layer Control Dialog * $Id$ * $HeadURL$ * $Author$ * Created by Patric Karwat, SLSE, 2016-06-01 */ #include "sitmap_layer_control.h" #include "ui_sitmap_layer_control.h" #include #include #include #include #include namespace SituationMap { // // class SpinBoxDelegate // class SpinBoxDelegate : public QStyledItemDelegate { Q_OBJECT public: using BaseClass = QStyledItemDelegate; explicit SpinBoxDelegate(LayerControlDialog *lcDialog) : BaseClass(lcDialog), m_layerControlDialog(lcDialog) { } ~SpinBoxDelegate() { if (m_layerControlDialog) m_layerControlDialog->m_ignoreLayerParameterChanged = false; } using QStyledItemDelegate::QStyledItemDelegate; void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (index.column() == LayersModel::Layer) { QStyledItemDelegate::paint(painter, option, index); return; } if (!displayValue(index)) return; int progress = index.data().toInt(); QStyleOptionProgressBar progressBarOption; progressBarOption.rect = option.rect; progressBarOption.minimum = minSpinboxValue(index); progressBarOption.maximum = 100; progressBarOption.progress = progress; if (index.column() == LayersModel::Opacity) progressBarOption.text = QString::number(progress) + QLatin1Char('%'); else progressBarOption.text = QString::number(progress); progressBarOption.textVisible = true; QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter); } QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { if (m_layerControlDialog) m_layerControlDialog->m_ignoreLayerParameterChanged = true; m_modelIdx = QModelIndex(); if (index.column() == 0) return QStyledItemDelegate::createEditor(parent, option, index); if (!displayValue(index)) return nullptr; QSlider *sl = new QSlider(Qt::Horizontal, parent); sl->setAutoFillBackground(true); sl->setMinimum(minSpinboxValue(index)); sl->setMaximum(100); sl->setToolTipDuration(0); connect(sl, &QSlider::valueChanged, this, [sl](int value) { sl->setToolTip(QString::number(value)); }); return sl; } void destroyEditor(QWidget *editor, const QModelIndex &index) const override { BaseClass::destroyEditor(editor, index); if (m_layerControlDialog) m_layerControlDialog->m_ignoreLayerParameterChanged = false; } void setEditorData(QWidget *editor, const QModelIndex &index) const override { QSlider *sl = index.column() == 0 ? nullptr : qobject_cast(editor); if (!sl) QStyledItemDelegate::setEditorData(editor, index); else { m_modelIdx = index; connect(sl, &QSlider::valueChanged, this, &SpinBoxDelegate::onValueChanged, Qt::UniqueConnection); sl->setValue(index.data(Qt::EditRole).toInt()); } } private Q_SLOTS: void onValueChanged(int value) { m_value = value; //wait until user stops movement if (!m_timer) { m_timer = new QTimer(this); m_timer->setSingleShot(true); m_timer->setInterval(500); // msec connect(m_timer, &QTimer::timeout, this, &SpinBoxDelegate::onValueChangedTimout); } m_timer->start(); } void onValueChangedTimout() { if (!m_layerControlDialog || !m_layerControlDialog->layersModel() || !m_modelIdx.isValid()) return; m_layerControlDialog->layersModel()->setData(m_modelIdx, m_value, Qt::EditRole); } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { QSlider *sl = index.column() == 0 ? nullptr : qobject_cast(editor); if (!sl) QStyledItemDelegate::setModelData(editor, model, index); else model->setData(index, sl->value(), Qt::EditRole); } void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QSlider *sl = index.column() == 0 ? nullptr : qobject_cast(editor); if (!sl) QStyledItemDelegate::updateEditorGeometry(editor, option, index); else sl->setGeometry(option.rect); } private: static bool displayValue(const QModelIndex &index) { if (index.column() == LayersModel::Opacity) return true; LayerType lt = static_cast(index.data(Qt::UserRole).toInt()); return (lt == LayerType::Basemap); } static int minSpinboxValue(const QModelIndex &idx) { return idx.column() == LayersModel::Opacity ? 0 : -100; } // Slider: to enable on-the-fly update : mutable QModelIndex m_modelIdx; QTimer *m_timer = nullptr; LayerControlDialog *m_layerControlDialog = nullptr; int m_value = 0; // : Slider: to enable on-the-fly update }; // SpinBoxDelegate // // class LayerControlDialog // LayerControlDialog::LayerControlDialog(QWidget *parent) : LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::CloseOnly) , m_session(nullptr) , m_situationMap(nullptr) , m_layersModel(nullptr) , m_ui(new Ui::LayerControlDialog) , m_logger(getLogger("LayerControlDialog")) { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; m_ui->setupUi(this); m_ui->tvLayer->setItemDelegate(new SpinBoxDelegate(this)); setOnlineHelpFilename(QStringLiteral("Situation-Layer_Control")); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } LayerControlDialog::~LayerControlDialog() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; delete m_layersModel; delete m_ui; TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void LayerControlDialog::retranslate() { translate(m_ui, tr("Layer Control")); } bool LayerControlDialog::init(LtsSession::LtsSession *session, SituationMap::Map *map) { TRACE_LOG(m_logger) << LOGFUNCTION << " START " << " map=" << map; if (!session || !map) return false; m_session = session; m_situationMap = map; if (m_situationMap) connect(map, &Map::beginDestroy, this, &LayerControlDialog::onMapDestroyed, Qt::UniqueConnection); // setup initial position of splitter: m_ui->splitter->setStretchFactor(0, 1); m_ui->splitter->setStretchFactor(1, 2); m_layersModel = new LayersModel(m_session, this); m_layersModel->initModel(m_situationMap); m_ui->tvLayer->setModel(m_layersModel); m_ui->tvLayer->resizeColumnsToContents(); m_ui->tvLayer->setColumnWidth(1, 100); // Column 1 -> Opacity .... m_ui->tvLayer->setColumnWidth(2, 100); m_ui->tvLayer->setColumnWidth(3, 100); m_ui->tvLayer->setColumnWidth(4, 100); connect(m_ui->pbUp, &QPushButton::clicked, this, &LayerControlDialog::onPushButtonUpClicked); connect(m_ui->pbDown, &QPushButton::clicked, this, &LayerControlDialog::onPushButtonDownClicked); connect(m_layersModel, &LayersModel::layerDataChanged, this, &LayerControlDialog::onLayerDataChanged); connect(m_layersModel, &LayersModel::isVisibleChanged, this, &LayerControlDialog::layerOnOff); //set values onZoomSliderInit(); m_ui->slRotation->setValue(static_cast(m_situationMap->rotation())); m_ui->lblRotation->setToolTip(QString::number(m_ui->slRotation->value())); onNavigateBelowGroundOfMapChanged(m_situationMap->navigationConstraint()); onBaseSurfaceOpacityOfMapChanged(m_situationMap->baseSurfaceOpacity()); const bool isMode2D = m_situationMap->mode() == Mode::Mode_2D; m_ui->slZoom->setEnabled(isMode2D); m_ui->tbResetZoom->setEnabled(isMode2D); m_ui->slRotation->setEnabled(isMode2D); m_ui->tbResetRotation->setEnabled(isMode2D); const bool isMode3D = m_situationMap->mode() == Mode::Mode_3D; m_ui->lbNavigateBelowGround->setEnabled(isMode3D); m_ui->cbNavigateBelowGround->setEnabled(isMode3D); m_ui->slBaseSurfaceOpacity->setEnabled(isMode3D); m_ui->tbBaseSurfaceOpacity->setEnabled(isMode3D); //connects if (m_situationMap->mode() == Mode::Mode_2D) { //rotation connect(m_ui->slRotation, &QSlider::valueChanged, this, &LayerControlDialog::onRotationSliderValueChanged); connect(m_situationMap, &Map::rotationChanged, this, &LayerControlDialog::onRotationOfMapChanged); //zoom // Attention: use identical signal at any other zoom/scale-Slider!!! connect(m_situationMap, &Map::extentChangedDelayed, this, &LayerControlDialog::onMapExtentChanged); connect(m_ui->slZoom, &QSlider::valueChanged, this, &LayerControlDialog::onZoomSliderValueChanged); } if (m_situationMap->mode() == Mode::Mode_3D) { // navigationConstraint connect(m_situationMap, &Map::navigationConstraintChanged, this, &LayerControlDialog::onNavigateBelowGroundOfMapChanged); connect(m_ui->cbNavigateBelowGround, &QCheckBox::stateChanged, this, &LayerControlDialog::onNavigateBelowGroundValueChanged); // baseSurfaceOpacity connect(m_situationMap, &Map::baseSurfaceOpacityChanged, this, &LayerControlDialog::onBaseSurfaceOpacityOfMapChanged); connect(m_ui->slBaseSurfaceOpacity, &QSlider::valueChanged, this, &LayerControlDialog::onBaseSurfaceOpacitySliderValueChanged); } //updatePanel(); connect(m_ui->tbResetZoom, &QToolButton::clicked, this, [this]() { m_ui->slZoom->setValue(100); }); connect(m_ui->tbResetRotation, &QToolButton::clicked, this, [this]() { m_ui->slRotation->setValue(0); m_ui->lblRotation->setToolTip(QString::number(m_ui->slRotation->value())); }); connect(m_ui->tbBaseSurfaceOpacity, &QToolButton::clicked, this, [this]() { m_ui->slBaseSurfaceOpacity->setValue(100); m_ui->slBaseSurfaceOpacity->setToolTip(QString::number(m_ui->slBaseSurfaceOpacity->value())); }); connect(m_situationMap, &Map::mapReady, this, &LayerControlDialog::onMapReady, Qt::ConnectionType::QueuedConnection); // if (this->physicalDpiX() <= LtsWidgets::HIGHDPI) // m_ui->horizontalLayout->addSpacerItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); connect(m_situationMap, &Map::layerParameterChanged, this, &LayerControlDialog::onLayerParamterChanged); // Load Filter if (FilterManagement::FilterChangeDialog::isAvailableAtApp(m_session->app())) { m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setText(tr("Load filter")); m_buttonBox->getButton(LtsWidgets::DialogButton::Load)->setVisible(true); connect(m_buttonBox->getButton(LtsWidgets::DialogButton::Load), &QPushButton::clicked, this, &LayerControlDialog::onLoadButtonClicked, Qt::UniqueConnection); } // show only if baseSurfaceOpacity is supported / arcgis >= 100.5.0. // and if item is enabled via ProjectSettings const NavigationConstraint navConstraint = m_situationMap->navigationConstraint(); LtsUtils::ProjectSettings *ps = LtsUtils::ProjectSettings::getInstance(); const bool enableNavigateBelowGround = ps->value(QStringLiteral("SituationMap"), QStringLiteral("enableNavigateBelowGround")).toBool(); const bool enableBaseSurfaceOpacity = ps->value(QStringLiteral("SituationMap"), QStringLiteral("enableBaseSurfaceOpacity")).toBool(); if (navConstraint == NavigationConstraint::Undefined || // < 105.5.0 !enableNavigateBelowGround) { m_ui->lbNavigateBelowGround->hide(); m_ui->cbNavigateBelowGround->hide(); } if (navConstraint == NavigationConstraint::Undefined || // < 105.5.0 !enableBaseSurfaceOpacity) { m_ui->lblBaseSurfaceOpacity->hide(); m_ui->slBaseSurfaceOpacity->hide(); m_ui->tbBaseSurfaceOpacity->hide(); } TRACE_LOG(m_logger) << LOGFUNCTION << " END " << " map=" << map; return true; } void LayerControlDialog::onPushButtonUpClicked() { int currentRow = m_ui->tvLayer->currentIndex().row(); if (currentRow - 1 < 0) return; if (m_layersModel->canBeMovedUp(m_ui->tvLayer->currentIndex())) { QModelIndex idx = m_layersModel->moveRow(m_ui->tvLayer->currentIndex(), currentRow - 1); m_ui->tvLayer->setCurrentIndex(idx); QStringList sl = m_layersModel->orderLayerNames(); m_situationMap->layerFactory().orderLayerTo(sl); } } void LayerControlDialog::onPushButtonDownClicked() { int currentRow = m_ui->tvLayer->currentIndex().row(); if (m_layersModel->canBeMovedDown(m_ui->tvLayer->currentIndex())) { QModelIndex idx = m_layersModel->moveRow(m_ui->tvLayer->currentIndex(), currentRow + 1); m_ui->tvLayer->setCurrentIndex(idx); QStringList sl = m_layersModel->orderLayerNames(); m_situationMap->layerFactory().orderLayerTo(sl); } } void LayerControlDialog::onLayerDataChanged(const QString &layerName, int newValue, int column) { LayerParameter lp = m_situationMap->layerFactory().layerParameter(layerName); switch (column) { case LayersModel::Opacity: lp.opacity = newValue; break; case LayersModel::Brightness: lp.brightness = newValue; break; case LayersModel::Gamma: lp.gamma = newValue; break; case LayersModel::Contrast: lp.contrast = newValue; break; default: break; } m_situationMap->layerFactory().setLayerParameter(layerName, lp); } void LayerControlDialog::onLoadButtonClicked() { FilterManagement::FilterChangeDialog *dialog = new FilterManagement::FilterChangeDialog(this); if (!dialog->init(m_session, FilterManagement::SymbolAppearanceSettings::Part::All, false, false, true)) { delete dialog; return; } dialog->setAttribute(Qt::WA_DeleteOnClose); QObject::connect(dialog, &FilterManagement::FilterChangeDialog::rejected, dialog, &FilterManagement::FilterChangeDialog::deleteLater); dialog->show(); } void LayerControlDialog::onMapReady() { TRACE_LOG(m_logger) << LOGFUNCTION << " START"; //set values onZoomSliderInit(); m_ui->slRotation->setValue(static_cast(m_situationMap->rotation())); m_ui->lblRotation->setToolTip(QString::number(m_ui->slRotation->value())); m_ui->slBaseSurfaceOpacity->setValue(static_cast(m_situationMap->baseSurfaceOpacity()*100.0f)); m_ui->slBaseSurfaceOpacity->setToolTip(QString::number(m_ui->slBaseSurfaceOpacity->value())); TRACE_LOG(m_logger) << LOGFUNCTION << " END"; } void LayerControlDialog::onMapDestroyed() { delete this; } void LayerControlDialog::onLayerParamterChanged() { DEBUG_LOG(m_logger) << LOGFUNCTION << " XXXXXXXXX "; if (!m_situationMap) return; if (m_ignoreLayerParameterChanged) { DEBUG_LOG(m_logger) << LOGFUNCTION << " skip due to m_ignoreLayerParameterChanged"; return; } if (m_layersModel) m_layersModel->initModel(m_situationMap); // zoom and rotation slider will be updated by theirself } //SLOTS void LayerControlDialog::onRotationSliderValueChanged() { if (m_situationMap->isReady()) m_situationMap->setRotation(m_ui->slRotation->value()); } void LayerControlDialog::onRotationOfMapChanged(double rotation) { if (m_situationMap->isReady() && !m_ui->slRotation->isSliderDown()) { const QSignalBlocker blocker(m_ui->slRotation); m_ui->slRotation->setValue(static_cast(rotation)); m_ui->lblRotation->setToolTip(QString::number(m_ui->slRotation->value())); } } void LayerControlDialog::onZoomSliderInit() { TRACE_LOG(m_logger) << LOGFUNCTION << "'" << " START"; if (!m_ui || !m_ui->slZoom) return; m_ui->slZoom->blockSignals(true); m_ui->slZoom->setMinimum(static_cast(sqrt(m_situationMap->maxScale()))); m_ui->slZoom->setMaximum(static_cast(sqrt(m_situationMap->minScale()))); if (m_ui->slZoom->minimum() == m_ui->slZoom->maximum()) { WARNING_LOG(m_logger) << LOGFUNCTION << " min/max value are identical" << " minimum=" << m_ui->slZoom->minimum() << " maximum=" << m_ui->slZoom->maximum() << " value=" << m_ui->slZoom->value(); m_ui->slZoom->setMinimum(static_cast(sqrt(500))); m_ui->slZoom->setMaximum(static_cast(sqrt(100000))); WARNING_LOG(m_logger) << LOGFUNCTION << " min/max value are identical" << " minimum=" << m_ui->slZoom->minimum() << " maximum=" << m_ui->slZoom->maximum() << " value=" << m_ui->slZoom->value(); } QString toolTipStr = QStringLiteral("min=%1 - max=%2").arg(m_ui->slZoom->minimum()).arg(m_ui->slZoom->maximum()); m_ui->slZoom->setToolTip(toolTipStr); onMapExtentChanged(); m_ui->slZoom->blockSignals(false); TRACE_LOG(m_logger) << LOGFUNCTION << "'" << " END"; } void LayerControlDialog::onZoomSliderValueChanged() { if (m_situationMap->isReady()) m_situationMap->setScale(m_ui->slZoom->value() * m_ui->slZoom->value()); } void LayerControlDialog::onMapExtentChanged() { if (m_ui->slZoom && m_ui->slZoom->isEnabled() && m_situationMap->isReady() && (!m_ui->slZoom->isSliderDown())) { const QSignalBlocker blocker(m_ui->slZoom); double newVal = sqrt(m_situationMap->scale()); m_ui->slZoom->setValue(static_cast(newVal)); m_ui->lblZoom->setToolTip(QString::number(m_situationMap->scale())); } } void LayerControlDialog::onNavigateBelowGroundValueChanged(int state) { if (!m_situationMap->isReady()) // required ? return; Qt::CheckState cs = static_cast(state); switch (cs) { case Qt::CheckState::PartiallyChecked: m_situationMap->setNavigationConstraint(NavigationConstraint::Undefined); break; case Qt::CheckState::Checked: m_situationMap->setNavigationConstraint(NavigationConstraint::None); break; case Qt::CheckState::Unchecked: m_situationMap->setNavigationConstraint(NavigationConstraint::StayAbove); break; } // switch } void LayerControlDialog::onNavigateBelowGroundOfMapChanged(NavigationConstraint newNavigationConstraint) { switch (newNavigationConstraint) { case NavigationConstraint::Undefined: m_ui->cbNavigateBelowGround->setCheckState(Qt::CheckState::PartiallyChecked); // should never happen break; case NavigationConstraint::None: m_ui->cbNavigateBelowGround->setCheckState(Qt::CheckState::Checked); break; case NavigationConstraint::StayAbove: m_ui->cbNavigateBelowGround->setCheckState(Qt::CheckState::Unchecked); break; } // switch } void LayerControlDialog::onBaseSurfaceOpacitySliderValueChanged() { if (m_situationMap->isReady()) // required ? m_situationMap->setBaseSurfaceOpacity(m_ui->slBaseSurfaceOpacity->value()/100.0f); } void LayerControlDialog::onBaseSurfaceOpacityOfMapChanged(float opacity) { if (m_situationMap->isReady() && !m_ui->slBaseSurfaceOpacity->isSliderDown()) { const QSignalBlocker blocker(m_ui->slBaseSurfaceOpacity); m_ui->slBaseSurfaceOpacity->setValue(static_cast(opacity*100)); m_ui->slBaseSurfaceOpacity->setToolTip(QString::number(m_ui->slBaseSurfaceOpacity->value())); } } void LayerControlDialog::layerOnOff(const QString &layerName, bool isVisible) { // get LayerParameter, update data and set: LayerParameter lp = m_situationMap->layerFactory().layerParameter(layerName); lp.visible = isVisible; m_situationMap->layerFactory().setLayerParameter(layerName, lp); } } //namespace SituationMap #include "sitmap_layer_control.moc"