15.1.445. tablet_qt/questionnairelib/qudiagnosticcode.cpp

/*
    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CamCOPS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/

// #define DEBUG_DX_SELECTOR_SPEED

#include "qudiagnosticcode.h"

#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWidget>

#include "common/cssconst.h"
#include "core/camcopsapp.h"
#include "diagnosis/diagnosticcodeset.h"
#include "lib/slowguiguard.h"
#include "lib/uifunc.h"
#include "questionnairelib/questionnaire.h"
#include "widgets/basewidget.h"
#include "widgets/clickablelabelwordwrapwide.h"
#include "widgets/diagnosticcodeselector.h"
#include "widgets/labelwordwrapwide.h"

QuDiagnosticCode::QuDiagnosticCode(
    DiagnosticCodeSetPtr codeset,
    FieldRefPtr fieldref_code,
    FieldRefPtr fieldref_description,
    QObject* parent
) :
    QuElement(parent),
    m_codeset(codeset),
    m_fieldref_code(fieldref_code),
    m_fieldref_description(fieldref_description),
    m_offer_null_button(true),
    m_missing_indicator(nullptr),
    m_label_code(nullptr),
    m_label_description(nullptr)
{
    Q_ASSERT(m_codeset);
    Q_ASSERT(m_fieldref_code);
    Q_ASSERT(m_fieldref_description);
    connect(
        m_fieldref_code.data(),
        &FieldRef::valueChanged,
        this,
        &QuDiagnosticCode::fieldValueChanged
    );
    connect(
        m_fieldref_code.data(),
        &FieldRef::mandatoryChanged,
        this,
        &QuDiagnosticCode::fieldValueChanged
    );
    // We don't track changes to the description; they're assumed to follow
    // code changes directly.
    // NOTE that this method violates the "DRY" principle but is for clinical
    // margin-of-safety reasons so that a record of what the user saw when they
    // picked the diagnosis is preserved with the code.
}

QuDiagnosticCode*
    QuDiagnosticCode::setOfferNullButton(const bool offer_null_button)
{
    m_offer_null_button = offer_null_button;
    return this;
}

void QuDiagnosticCode::setFromField()
{
    fieldValueChanged(m_fieldref_code.data());
}

QPointer<QWidget> QuDiagnosticCode::makeWidget(Questionnaire* questionnaire)
{
    m_questionnaire = questionnaire;
    const bool read_only = questionnaire->readOnly();

    m_missing_indicator
        = uifunc::iconWidget(uifunc::iconFilename(uiconst::ICON_WARNING));
    m_label_code = new LabelWordWrapWide();
    m_label_code->setObjectName(cssconst::DIAGNOSTIC_CODE);
    m_label_description = new LabelWordWrapWide();
    m_label_description->setObjectName(cssconst::DIAGNOSTIC_CODE);

    Qt::Alignment widget_align = Qt::AlignTop;

    auto textlayout = new HBoxLayout();
    textlayout->setContentsMargins(uiconst::NO_MARGINS);
    textlayout->addWidget(m_missing_indicator, 0, widget_align);
    textlayout->addWidget(m_label_code, 0, widget_align);
    textlayout->addWidget(m_label_description, 0, widget_align);
    textlayout->addStretch();

    auto button = new ClickableLabelWordWrapWide(tr("Set diagnosis"));
    button->setObjectName(cssconst::DIAGNOSTIC_CODE);
    // button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
    button->setEnabled(!read_only);
    if (!read_only) {
        connect(
            button,
            &ClickableLabelWordWrapWide::clicked,
            this,
            &QuDiagnosticCode::setButtonClicked
        );
    }

    auto buttonlayout = new HBoxLayout();
    buttonlayout->setContentsMargins(uiconst::NO_MARGINS);
    buttonlayout->addWidget(button, 0, widget_align);

    if (m_offer_null_button) {
        auto null_button = new ClickableLabelWordWrapWide(tr("Clear"));
        null_button->setObjectName(cssconst::DIAGNOSTIC_CODE);
        // null_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
        null_button->setEnabled(!read_only);
        if (!read_only) {
            connect(
                null_button,
                &ClickableLabelWordWrapWide::clicked,
                this,
                &QuDiagnosticCode::nullButtonClicked
            );
        }
        buttonlayout->addWidget(null_button, 0, widget_align);
    }
    buttonlayout->addStretch();

    auto toplayout = new VBoxLayout();
    toplayout->setContentsMargins(uiconst::NO_MARGINS);
    toplayout->addLayout(textlayout);
    toplayout->addLayout(buttonlayout);

    m_widget = new BaseWidget();
    // m_widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
    // ... no, keep as the default of expandingFixedHFWPolicy()?
    m_widget->setLayout(toplayout);

    setFromField();

    return m_widget;
}

void QuDiagnosticCode::setButtonClicked()
{
    if (!m_questionnaire) {
        qWarning() << Q_FUNC_INFO << "m_questionnaire missing";
        return;
    }
    SlowGuiGuard guard = m_questionnaire->app().getSlowGuiGuard();
    const QString code = m_fieldref_code->valueString();
    const QModelIndex selected = m_codeset->firstMatchCode(code);
    const QString stylesheet = m_questionnaire->getSubstitutedCss(
        uiconst::CSS_CAMCOPS_DIAGNOSTIC_CODE
    );
#ifdef DEBUG_DX_SELECTOR_SPEED
    qDebug() << Q_FUNC_INFO << "Making DiagnosticCodeSelector...";
#endif
    auto selector
        = new DiagnosticCodeSelector(stylesheet, m_codeset, selected);
#ifdef DEBUG_DX_SELECTOR_SPEED
    qDebug() << Q_FUNC_INFO << "... done";
#endif
    connect(
        selector,
        &DiagnosticCodeSelector::codeChanged,
        this,
        &QuDiagnosticCode::widgetChangedCode
    );
#ifdef DEBUG_DX_SELECTOR_SPEED
    qDebug() << Q_FUNC_INFO << "Opening DiagnosticCodeSelector widget...";
#endif
    m_questionnaire->openSubWidget(selector);
#ifdef DEBUG_DX_SELECTOR_SPEED
    qDebug() << Q_FUNC_INFO << "... done";
#endif
}

void QuDiagnosticCode::nullButtonClicked()
{
    const QVariant nullvalue;
    m_fieldref_description->setValue(nullvalue);  // BEFORE setting code, as:
    m_fieldref_code->setValue(nullvalue);  // ... will trigger valueChanged
    emit elementValueChanged();
}

FieldRefPtrList QuDiagnosticCode::fieldrefs() const
{
    return FieldRefPtrList{m_fieldref_code, m_fieldref_description};
}

void QuDiagnosticCode::widgetChangedCode(
    const QString& code, const QString& description
)
{
    m_fieldref_description->setValue(description);  // BEFORE setting code, as:
    m_fieldref_code->setValue(code);  // ... will trigger valueChanged
    emit elementValueChanged();
}

void QuDiagnosticCode::fieldValueChanged(const FieldRef* fieldref_code)
{
    if (m_missing_indicator) {
        m_missing_indicator->setVisible(fieldref_code->missingInput());
    }
    if (m_label_code) {
        m_label_code->setText(fieldref_code->valueString());
    }
    if (m_label_description) {
        m_label_description->setText(m_fieldref_description->valueString());
    }
    // m_widget->updateGeometry();
}