diff options
Diffstat (limited to 'src/gui/conversation.C')
-rw-r--r-- | src/gui/conversation.C | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/src/gui/conversation.C b/src/gui/conversation.C new file mode 100644 index 00000000..1938dbb7 --- /dev/null +++ b/src/gui/conversation.C @@ -0,0 +1,222 @@ +// Copyright (C) 2011, Gabriel Dos Reis. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// - Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// +// - Neither the name of OpenAxiom. nor the names of its contributors +// may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <string> +#include <sstream> +#include <iostream> + +#include <QScrollBar> +#include "conversation.h" +#include "debate.h" + +namespace OpenAxiom { + // -- Question -- + Question::Question(Exchange& e) : QLineEdit(&e) { } + + // -- Answer -- + Answer::Answer(Exchange& e) : QLabel(&e) { } + + + // -- Exchange -- + // Amount of pixel spacing between the query and reply areas. + const int spacing = 0; + + // Margin around query and reply areas. + const int margin = 2; + + QSize Exchange::sizeHint() const { + QSize sz = question()->frameSize(); + sz.rwidth() += 2 * margin; + sz.rheight() += answer()->frameSize().height() + spacing + 2 * margin; + return sz; + } + + QSize Exchange::minimumSizeHint() const { + QSize sz = question()->frameSize(); + sz.rwidth() += 2 * margin; + if (not answer()->isHidden()) + sz.rheight() += answer()->frameSize().height() + spacing; + sz.rheight() += 2 * margin; + return sz; + } + + // Return a monospace font + QFont + monospace_font() { + QFont f("Courier"); + f.setStyleHint(QFont::TypeWriter); + return f; + } + + // Measurement in pixel of the em unit in the given font metrics. + static QSize + em_metrics(const QFontMetrics& fm) { + return QSize(fm.width(QLatin1Char('m')), fm.height()); + } + + // Measurement in pixel of the em unit in the given font `f'. + QSize em_metrics(const QFont& f) { + return em_metrics(QFontMetrics(f)); + } + + // Dress the query area with initial properties. + static void + prepare_query_widget(QLineEdit* w) { + w->setFrame(false); + w->setFont(monospace_font()); + QSize em = em_metrics(w->fontMetrics()); + w->setGeometry(margin, margin, + question_columns * em.width(), em.height()); + } + + // Dress the reply aread with initial properties. + static void + prepare_reply_widget(QLabel* w, const QRect& below) { + w->setFont(monospace_font()); + w->setGeometry(below.x(), below.height() + spacing, + below.width(), 2 * w->fontMetrics().height()); + w->hide(); // nothing to show yet + } + + // -- Exchange -- + Exchange::Exchange(Conversation& conv, int n) + : QFrame(&conv), no(n), query(*this), reply(*this) { + // 1. Construct the query area. + prepare_query_widget(question()); + + // 2. Construct the response area. + prepare_reply_widget(answer(), question()->geometry()); + + // 3. Construct the whole frame + QSize qs = question()->frameSize(); + QSize rs = answer()->frameSize(); + resize(qs.width() + 2 * margin, qs.height() + rs.height() + 2 * margin); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + setLineWidth(1); + // 4. + connect(&query, SIGNAL(returnPressed()), + this, SLOT(reply_to_query())); + } + + static QString + parenthesize(int n) { + std::ostringstream os; + os << '(' << n << ')'; + return QString::fromStdString(os.str()); + } + + Conversation* Exchange::conversation() { + return &dynamic_cast<Conversation&>(*parentWidget()); + } + + Debate* Exchange::debate() { + return conversation()->debate(); + } + + void + Exchange::reply_to_query() { + QString input = question()->text().trimmed(); + if (input.isEmpty()) + return; + QString ans = parenthesize(number()) + " " + input; + answer()->show(); + answer()->setText(ans); + debate()->ensureWidgetVisible(answer()); + debate()->horizontalScrollBar()->setValue(0); + conversation()->next(this)->question() + ->setFocus(Qt::OtherFocusReason); + } + + // ------------------ + // -- Conversation -- + // ------------------- + + static void resize_if_necessary(Conversation& conv) { + QSize sz = conv.sizeHint(); + if (sz.height() > conv.height()) + conv.resize(conv.width(), sz.height()); + } + + static void debug_engine(Conversation& conv) { + std::cerr << "# conversations: " << conv.length() + << std::endl; + QSize sz = conv.sizeHint(); + std::cerr << "size: " << sz.width() << ", " << sz.height() + << std::endl; + } + + Conversation::Conversation(Debate& parent) + : QWidget(&parent), group(parent) { + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + QSize sz = new_topic()->frameSize(); + setMinimumSize(sz.width(), 10 * sz.height()); + } + + Conversation::~Conversation() { + for (int i = children.size() -1 ; i >= 0; --i) + delete children[i]; + } + + QSize Conversation::sizeHint() const { + QSize sz(0,0); + for (int i = 0; i < length(); ++i) { + QSize s = children[i]->sizeHint(); + if (s.width() > sz.width()) + sz.setWidth(s.width()); + sz.rheight() += s.height(); + } + return sz; + } + + Exchange* + Conversation::new_topic() { + QPoint loc(0,0); + if (not fresh()) { + Exchange* last = children.back(); + loc = last->geometry().bottomLeft(); + } + Exchange* w = new Exchange(*this, length() + 1); + w->setVisible(true); + w->move(loc); + children.push_back(w); + resize_if_necessary(*this); + debug_engine(*this); + return w; + } + + + Exchange* + Conversation::next(Exchange* w) { + if (w == 0 or w->number() == length()) + return new_topic(); + return children[w->number()]; + } +} |