diff options
-rw-r--r-- | src/gui/conversation.C | 170 | ||||
-rw-r--r-- | src/gui/conversation.h | 62 | ||||
-rw-r--r-- | src/gui/debate.C | 8 |
3 files changed, 193 insertions, 47 deletions
diff --git a/src/gui/conversation.C b/src/gui/conversation.C index 22d9dea7..0c5ec778 100644 --- a/src/gui/conversation.C +++ b/src/gui/conversation.C @@ -39,32 +39,67 @@ #include "debate.h" namespace OpenAxiom { - static void debug_size(const char* s, const QSize& sz) { - std::cerr << s << " == " - << sz.width() << ", " << sz.height() << std::endl; - } - // Measurement in pixel of the em unit in the given font `f'. static QSize em_metrics(const QWidget* w) { const QFontMetrics fm = w->fontMetrics(); return QSize(fm.width(QLatin1Char('m')), fm.height()); } - // -- Question -- - Question::Question(Exchange& e) : Base(&e), parent(&e) { } + // -------------------- + // -- OutputTextArea -- + // -------------------- + OutputTextArea::OutputTextArea(QWidget* p) + : Base(p) { + setFont(p->font()); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); + setLineWidth(1); + } - void Question::enterEvent(QEvent* e) { - Base::enterEvent(e); - setFocus(Qt::OtherFocusReason); + // Concatenate two paragraphs. + static QString + accumulate_paragaphs(const QString& before, const QString& after) { + if (before.isNull() or before.isEmpty()) + return after; + return before + "\n" + after; + } + + void OutputTextArea::add_paragraph(const QString& s) { + setText(accumulate_paragaphs(text(), s)); + adjustSize(); + const int w = parentWidget()->width() - 2 * frameWidth(); + if (w > width()) + resize(w, height()); + show(); + updateGeometry(); + } + + void OutputTextArea::add_text(const QString& s) { + setText(text() + s); + adjustSize(); + const int w = parentWidget()->width(); + if (w < width()) + resize(w, height()); + show(); + updateGeometry(); } + // -------------- + // -- Question -- + // -------------- + Question::Question(Exchange& e) : Base(&e), parent(&e) { + setBackgroundRole(QPalette::AlternateBase); + } + + // ------------ // -- Answer -- + // ------------ Answer::Answer(Exchange& e) : Base(&e), parent(&e) { - setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding); - setFrameStyle(StyledPanel | Plain); + setFrameStyle(StyledPanel | Sunken); } + // -------------- // -- Exchange -- + // -------------- // Amount of pixel spacing between the query and reply areas. const int spacing = 0; @@ -75,8 +110,9 @@ namespace OpenAxiom { return f; } - static int margin(const Exchange*e) { - return 2 + e->frameWidth(); + // Return a resonable margin for this frame. + static int margin(const QFrame* f) { + return 2 + f->frameWidth(); } // The layout within an exchange is as follows: @@ -117,12 +153,13 @@ namespace OpenAxiom { static void finish_exchange_make_up(Conversation& conv, Exchange* e) { e->setAutoFillBackground(true); - e->setBackgroundRole(e->question()->backgroundRole()); + //e->setBackgroundRole(e->question()->backgroundRole()); e->move(conv.bottom_left()); } Exchange::Exchange(Conversation& conv, int n) - : QFrame(&conv), parent(&conv), no(n), query(*this), reply(*this) { + : QFrame(&conv), parent(&conv), no(n), + query(*this), reply(*this) { setLineWidth(1); setFont(conv.font()); prepare_query_widget(conv, this); @@ -132,13 +169,6 @@ namespace OpenAxiom { this, SLOT(reply_to_query())); } - static QString - parenthesize(int n) { - std::ostringstream os; - os << '(' << n << ')'; - return QString::fromStdString(os.str()); - } - static void ensure_visibility(Debate* debate, Exchange* e) { const int y = e->y() + e->height(); QScrollBar* vbar = debate->verticalScrollBar(); @@ -156,13 +186,8 @@ namespace OpenAxiom { QString input = question()->text().trimmed(); if (input.isEmpty()) return; - QString ans = parenthesize(number()) + " " + input; - answer()->show(); - answer()->setText(ans); - adjustSize(); - update(); - updateGeometry(); - ensure_visibility(conversation()->debate(), conversation()->next(this)); + topic()->oracle()->write(input.toAscii()); + topic()->oracle()->write("\n"); } void Exchange::resizeEvent(QResizeEvent* e) { @@ -174,6 +199,14 @@ namespace OpenAxiom { } } + // ------------ + // -- Banner -- + // ------------ + Banner::Banner(Conversation* conv) : Base(conv) { + setFrameStyle(StyledPanel | Raised); + setBackgroundRole(QPalette::Base); + } + // ------------------ // -- Conversation -- // ------------------- @@ -190,21 +223,32 @@ namespace OpenAxiom { // Set a minimum preferred widget size, so no layout manager // messes with it. Indicate we can make use of more space. - Conversation::Conversation(Debate& parent) : group(parent) { + Conversation::Conversation(Debate& parent) + : group(parent), greatings(this), cur_ex(), cur_out(&greatings) { + setBackgroundRole(QPalette::Base); setFont(monospace_font()); // setMinimumSize(minimum_preferred_size(this)); // setSizePolicy(QSizePolicy::MinimumExpanding, // QSizePolicy::MinimumExpanding); + oracle()->setProcessChannelMode(QProcess::MergedChannels); + connect(oracle(), SIGNAL(readyReadStandardOutput()), + this, SLOT(read_reply())); + // connect(oracle(), SIGNAL(readyReadStandardError()), + // this, SLOT(read_reply())); + // connect(oracle(), SIGNAL(started()), + // this, SLOT(read_reply())); } Conversation::~Conversation() { for (int i = children.size() -1 ; i >= 0; --i) delete children[i]; + if (oracle()->state() == QProcess::Running) + oracle()->terminate(); } QPoint Conversation::bottom_left() const { if (length() == 0) - return QPoint(0, 0); + return greatings.geometry().bottomLeft(); return children.back()->geometry().bottomLeft(); } @@ -221,8 +265,8 @@ namespace OpenAxiom { const int n = length(); if (n == 0) return round_up_height(minimum_preferred_size(this), view_height); - QSize sz = children.front()->size(); - for (int i = 1; i < n; ++i) + QSize sz = greatings.size(); + for (int i = 0; i < n; ++i) sz.rheight() += children[i]->height(); return round_up_height(sz, view_height); } @@ -233,6 +277,7 @@ namespace OpenAxiom { const QSize sz = size(); if (e->oldSize() == sz) return; + greatings.resize(sz.width(), greatings.height()); for (int i = 0; i < length(); ++i) { Exchange* e = children[i]; e->resize(sz.width(), e->height()); @@ -242,7 +287,7 @@ namespace OpenAxiom { void Conversation::paintEvent(QPaintEvent* e) { QWidget::paintEvent(e); if (length() == 0) - new_topic(); + greatings.update(); } Exchange* @@ -252,13 +297,64 @@ namespace OpenAxiom { children.push_back(w); adjustSize(); updateGeometry(); - return w; + cur_out = w->answer(); + return cur_ex = w; } Exchange* Conversation::next(Exchange* w) { if (w == 0 or w->number() == length()) return new_topic(); - return children[w->number()]; + return cur_ex = children[w->number()]; + } + + struct OracleOutput { + QString result; + QString prompt; + }; + + static bool + empty_string(const QString& s) { + return s.isNull() or s.isEmpty(); + } + + static OracleOutput + read_output(QProcess& proc) { + OracleOutput output; + QStringList strs = QString::fromLocal8Bit(proc.readAll()).split('\n'); + QStringList new_list; + QRegExp rx("\\(\\d+\\)\\s->"); + while (not strs.isEmpty()) { + QString s = strs.takeFirst(); + if (empty_string(s)) + continue; + if (rx.indexIn(s) != -1) { + output.prompt = s; + break; + } + new_list.append(s); + } + output.result =new_list.join("\n"); + return output; + } + + void + Conversation::read_reply() { + OracleOutput output = read_output(proc); + if (empty_string(output.result)) + return; + std::cerr << output.result.toStdString() << std::endl; + cur_out->add_paragraph(output.result); + if (length() == 0) { + if (not empty_string(output.prompt)) + ensure_visibility(debate(), new_topic()); + } + else { + exchange()->adjustSize(); + exchange()->update(); + exchange()->updateGeometry(); + if (not empty_string(output.prompt)) + ensure_visibility(debate(), next(exchange())); + } } } diff --git a/src/gui/conversation.h b/src/gui/conversation.h index bed717cd..374dda16 100644 --- a/src/gui/conversation.h +++ b/src/gui/conversation.h @@ -40,6 +40,7 @@ #include <QEvent> #include <QResizeEvent> #include <QPaintEvent> +#include <QProcess> namespace OpenAxiom { // A conversation is a set of exchanges. An exchange is a question @@ -51,23 +52,41 @@ namespace OpenAxiom { class Question; class Answer; - // -- A question is just a one-liner query. + // -------------------- + // -- OutputTextArea -- + // -------------------- + // An output text area is a widget where we output text. + // The texts are accumulated, as opposed to overwritten. + class OutputTextArea : public QLabel { + typedef QLabel Base; + public: + explicit OutputTextArea(QWidget*); + // Add a new paragraph to existing texts. Paragraghs are + // separated by the newline character. + void add_paragraph(const QString&); + // Add accumulate new text. + void add_text(const QString&); + }; + + // --------------- + // -- Question -- + // --------------- + // A question is just a one-liner query area. class Question : public QLineEdit { typedef QLineEdit Base; public: explicit Question(Exchange&); Exchange* exchange() const { return parent; } - protected: - // Automatically grab focus when mouse moves into this widget - void enterEvent(QEvent*); - private: Exchange* const parent; }; - class Answer : public QLabel { - typedef QLabel Base; + // ------------ + // -- Answer -- + // ------------ + class Answer : public OutputTextArea { + typedef OutputTextArea Base; public: explicit Answer(Exchange&); Exchange* exchange() const { return parent; } @@ -75,14 +94,17 @@ namespace OpenAxiom { private: Exchange* const parent; }; - + + // -------------- + // -- Exchange -- + // -------------- class Exchange : public QFrame { Q_OBJECT; public: Exchange(Conversation&, int); // Return the parent widget of this conversation topic - Conversation* conversation() const { return parent; } + Conversation* topic() const { return parent; } // The widget holding the query area Question* question() { return &query; } @@ -111,6 +133,13 @@ namespace OpenAxiom { void reply_to_query(); }; + // Conversation banner, welcome greatings. + class Banner : public OutputTextArea { + typedef OutputTextArea Base; + public: + explicit Banner(Conversation*); + }; + // -- Set of conversations that make up a session. // -- We remember and number each topic so that we // -- can go back in the conversation set and reevaluate @@ -137,11 +166,17 @@ namespace OpenAxiom { // Start a new conversation topic. Exchange* new_topic(); - + // Override QWidegt::sizeHint. Return the cumulative sizes // of all conversations so far. QSize sizeHint() const; + // Return a pointer to the oracle in this conversation. + QProcess* oracle() { return &proc; } + + // Return a pointer to the current exchange, if any. + Exchange* exchange() { return cur_ex; } + // Return the parent engine widget. Debate* debate() const { return const_cast<Debate*>(&group); } @@ -153,10 +188,17 @@ namespace OpenAxiom { void resizeEvent(QResizeEvent*); void paintEvent(QPaintEvent*); + private slots: + void read_reply(); + private: typedef std::vector<Exchange*> Children; Debate& group; + Banner greatings; Children children; + QProcess proc; + Exchange* cur_ex; + OutputTextArea* cur_out; }; } diff --git a/src/gui/debate.C b/src/gui/debate.C index 50c6891a..ba58c522 100644 --- a/src/gui/debate.C +++ b/src/gui/debate.C @@ -35,6 +35,13 @@ namespace OpenAxiom { + static void + start_interpreter(Conversation* conv) { + QStringList args; + args << "--no-server" << "--role=slave"; + conv->oracle()->start("open-axiom",args); + } + Debate::Debate(QWidget* parent) : QScrollArea(parent), conv(*this) { setWidget(&conv); @@ -44,6 +51,7 @@ namespace OpenAxiom { setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); adjustSize(); + start_interpreter(exchanges()); } Debate::~Debate() { } |