15.1.123. tablet_qt/dialogs/logbox.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/>.
*/

#include "logbox.h"
#include <QApplication>
#include <QDebug>
#include <QHBoxLayout>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include "common/textconst.h"
#include "lib/uifunc.h"
#include "lib/widgetfunc.h"

const QSize MIN_SIZE(600, 600);

LogBox::LogBox(QWidget* parent,
               const QString& title,
               const bool offer_cancel,
               const bool offer_ok_at_end,
               const int maximum_block_count,
               const bool scroll_to_end_on_insert,
               const bool word_wrap) :
    QDialog(parent),
    m_use_wait_cursor(true),
    m_editor(nullptr),
    m_ok(nullptr),
    m_cancel(nullptr),
    m_ack_fail(nullptr),
    m_wait_cursor_on(false),
    m_scroll_to_end_on_insert(scroll_to_end_on_insert)
{
    // qDebug() << Q_FUNC_INFO;
    setWindowTitle(title);
    setMinimumSize(MIN_SIZE);

    auto mainlayout = new QVBoxLayout();
    setLayout(mainlayout);

    m_editor = new QPlainTextEdit();
    // QPlainTextEdit better than QTextEdit because it supports
    // maximumBlockCount while still allowing HTML (via appendHtml,
    // not insertHtml).
    m_editor->setReadOnly(true);
    m_editor->setTextInteractionFlags(Qt::NoTextInteraction);
    m_editor->setLineWrapMode(word_wrap ? QPlainTextEdit::WidgetWidth
                                        : QPlainTextEdit::NoWrap);
    m_editor->setMaximumBlockCount(maximum_block_count);
    mainlayout->addWidget(m_editor);
    uifunc::applyScrollGestures(m_editor->viewport());
    // ... https://stackoverflow.com/questions/23675845/qt-conflict-between-qscroller-and-qscrollbar

    auto buttonlayout = new QHBoxLayout();
    auto copybutton = new QPushButton(TextConst::copy());
    buttonlayout->addWidget(copybutton);
    connect(copybutton, &QPushButton::clicked, this, &LogBox::copyClicked);

    if (offer_cancel) {
        m_cancel = new QPushButton(TextConst::cancel());
        buttonlayout->addWidget(m_cancel);
        connect(m_cancel, &QPushButton::clicked, this, &LogBox::reject);
    }

    buttonlayout->addStretch();
    // Don't have cancel button on the right (user might hit it thinking
    // it's the OK button, based on shared location).

    if (offer_ok_at_end) {
        m_ok = new QPushButton(TextConst::ok());
        buttonlayout->addWidget(m_ok);
        connect(m_ok, &QPushButton::clicked, this, &LogBox::okClicked);
        m_ok->hide();
    }

    m_ack_fail = new QPushButton(tr("Acknowledge failure"));
    buttonlayout->addWidget(m_ack_fail);
    connect(m_ack_fail, &QPushButton::clicked, this, &LogBox::okClicked);
    m_ack_fail->hide();

    mainlayout->addLayout(buttonlayout);
}


LogBox::~LogBox()
{
    if (m_wait_cursor_on) {
        QApplication::restoreOverrideCursor();
    }
}


void LogBox::useWaitCursor(bool use_wait_cursor)
{
    m_use_wait_cursor = use_wait_cursor;
}


void LogBox::open()
{
    // qDebug() << Q_FUNC_INFO;
    if (m_use_wait_cursor) {
        QApplication::setOverrideCursor(Qt::WaitCursor);
        m_wait_cursor_on = true;
    }
    QDialog::open();
}


void LogBox::statusMessage(const QString& msg, const bool as_html)
{
    if (!m_editor) {
        return;
    }
    if (as_html) {
        m_editor->appendHtml(msg);
    } else {
        m_editor->appendPlainText(msg);
    }
    if (m_scroll_to_end_on_insert) {
        widgetfunc::scrollToEnd(m_editor.data());
    }
}


void LogBox::finish(const bool success)
{
    // qDebug() << Q_FUNC_INFO;
    // If we're waiting for the user to press OK (so they can look at the log,
    // enable the OK button and await the accepted() signal via that button).
    // Otherwise, accept() now. Either way, restore the cursor.
    QApplication::restoreOverrideCursor();
    m_wait_cursor_on = false;
    if (m_cancel) {
        m_cancel->hide();
    }
    if (success && m_ok) {
        m_ok->show();  // and await the accepted() signal via the button
    } else if (!success) {
        m_ack_fail->show();  // and await the accepted() signal via the button
    } else {
        // success, but caller didn't want an OK button
        accept();  // will emit accepted()
    }
}


void LogBox::okClicked()
{
    // qDebug() << Q_FUNC_INFO;
    accept();
    hide();  // hide explicitly, as we may be called using open() not exec()
}


void LogBox::copyClicked()
{
    m_editor->selectAll();
    m_editor->copy();
    m_editor->moveCursor(QTextCursor::End);
    widgetfunc::scrollToEnd(m_editor.data());
}