15.1.488. tablet_qt/questionnairelib/qumcqgrid.h

/*
    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/>.
*/

#pragma once
#include "layouts/layouts.h"
#include "questionnairelib/namevalueoptions.h"
#include "questionnairelib/mcqgridsubtitle.h"
#include "questionnairelib/quelement.h"
#include "questionnairelib/questionwithonefield.h"

class BooleanWidget;
class QuMcqGridSignaller;


class QuMcqGrid : public QuElement
{
    // Offers a grid of multiple-choice questions, where several questions
    // share the same possible responses. For example:
    //
    //      How much do you like it?                     <- TITLE
    //                   Not at all  A bit  Lots         <- OPTIONS
    //      Fruit                                        <- SUBTITLE
    //      1. Banana        O         O     O
    //      Jewels                                       <- SUBTITLE
    //      2. Diamond       O         O     O
    //      3. Ruby          O         O     O
    //
    //      ^
    //      |
    //      QUESTIONS


    Q_OBJECT
    friend class QuMcqGridSignaller;

public:
    // Constructor
    QuMcqGrid(const QVector<QuestionWithOneField>& question_field_pairs,
              const NameValueOptions& options, QObject* parent = nullptr);

    // Destructor
    virtual ~QuMcqGrid() override;

    // Set widths:
    // - question_width: relative width of question column
    // - option_widths: relative widths of option columns
    // This is what Qt calls "stretch". Columns with a higher stretch factor
    // take more of the available space.
    QuMcqGrid* setWidth(int question_width, const QVector<int>& option_widths);

    QuMcqGrid* setMinimumWidthInPixels(int question_width, const QVector<int>& option_widths);

    // Sets the title
    QuMcqGrid* setTitle(const QString& title);

    // Sets the subtitles.
    // - You can have multiple subtitle rows.
    // - The "options" display may be repeated on subtitle rows. See
    //   McqGridSubtitle.
    QuMcqGrid* setSubtitles(const QVector<McqGridSubtitle>& subtitles);

    // Ask widgets to expand horizontally?
    QuMcqGrid* setExpand(bool expand);

    // Apply a stripy background to the grid?
    QuMcqGrid* setStripy(bool stripy);

    // Show the title (as the first row)? Default is true.
    QuMcqGrid* showTitle(bool show_title);

    // Show the questions in bold? Default is true.
    QuMcqGrid* setQuestionsBold(bool bold);

    // Without changing the displayed options, sets alternative hidden
    // name/value options for specific questions. Typically used for questions
    // that appear the same (e.g. Always - Sometimes - Never) but are sometimes
    // scored ascending and sometimes scored descending.
    // The NameValueOptions passed must have the same length as the main one
    // passed to the constructor.
    QuMcqGrid* setAlternateNameValueOptions(
            const QVector<int>& question_indexes,
            const NameValueOptions& options);

protected:
    // Set the widget state from the fields' data.
    void setFromFields();

    virtual QPointer<QWidget> makeWidget(Questionnaire* questionnaire) override;
    virtual FieldRefPtrList fieldrefs() const override;

    // Returns the column number containing the specified (zero-based)
    // option/value index.
    int colnum(int value_index) const;

    // Internal function to add options to a grid.
    void addOptions(GridLayout* grid, int row);

protected slots:
    // "One of the response widgets was clicked/touched."
    void clicked(int question_index, int value_index);

    // "A field's value, or a field's mandatory status, has changed."
    void fieldValueOrMandatoryChanged(int question_index,
                                      const FieldRef* fieldref);

protected:
    QVector<QuestionWithOneField> m_question_field_pairs;  // Question/field mapping
    NameValueOptions m_options;  // Name/value pairs for options
    int m_question_width;  // relative width for question column
    QVector<int> m_option_widths;  // relative widths for option columns
    int m_question_min_width_px;  // minimum width in pixels for question
    QVector<int> m_option_min_widths_px;  // minimum width in pixels for option columns
    QString m_title;  // title text
    QVector<McqGridSubtitle> m_subtitles;  // subtitle info
    bool m_expand;  // expand our widgets horizontally?
    bool m_stripy;  // apply a stripy background?
    bool m_show_title;  // show the title?
    bool m_questions_bold;  // show questions in bold?
    QVector<QVector<QPointer<BooleanWidget>>> m_widgets;  // our response widgets
    QVector<QuMcqGridSignaller*> m_signallers;
        // ... objects to signal us when field data/mandatory status changes
    QMap<int, NameValueOptions> m_alternate_options;
};