diff options
author | dos-reis <gdr@axiomatics.org> | 2011-05-23 07:47:04 +0000 |
---|---|---|
committer | dos-reis <gdr@axiomatics.org> | 2011-05-23 07:47:04 +0000 |
commit | f89c5d0438e969615c4bb9e59192cad996109665 (patch) | |
tree | 2194b79bc16642ebd10d049d237fc00118f9b837 | |
parent | f87a31a02a7b0b84ed877880d9ae22253ce89671 (diff) | |
download | open-axiom-f89c5d0438e969615c4bb9e59192cad996109665.tar.gz |
Add new files
-rw-r--r-- | src/gui/conversation.C | 222 | ||||
-rw-r--r-- | src/gui/conversation.h | 152 | ||||
-rw-r--r-- | src/gui/debate.C | 44 | ||||
-rw-r--r-- | src/gui/debate.h | 56 | ||||
-rw-r--r-- | src/gui/gui.pro.in | 4 | ||||
-rw-r--r-- | src/gui/main-window.C | 9 | ||||
-rw-r--r-- | src/gui/main-window.h | 4 | ||||
-rw-r--r-- | src/gui/main.C | 4 |
8 files changed, 489 insertions, 6 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()]; + } +} diff --git a/src/gui/conversation.h b/src/gui/conversation.h new file mode 100644 index 00000000..d949e9d6 --- /dev/null +++ b/src/gui/conversation.h @@ -0,0 +1,152 @@ +// 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. + +#ifndef OPENAXIOM_CONVERSATION_INCLUDED +#define OPENAXIOM_CONVERSATION_INCLUDED + +#include <QFrame> +#include <QLineEdit> +#include <QLabel> +#include <QFont> +#include <vector> + +namespace OpenAxiom { + // A conversation is a set of exchanges. An exchange is a question + // followed by an answer. A conversation that takes place in a + // a certain frame is a debate. + class Debate; + class Conversation; + class Exchange; + class Question; + class Answer; + + // -- A question is just a one-liner query. + class Question : public QLineEdit { + public: + explicit Question(Exchange&); + }; + + class Answer : public QLabel { + public: + explicit Answer(Exchange&); + }; + + // -- Elemental conversation widget + // -- A basic interaction consists of a query, a reply, and the + // -- the type of the reply. + class Exchange : public QFrame { + Q_OBJECT; + public: + Exchange(Conversation&, int); + + // Return the parent widget of this conversation topic + Conversation* conversation(); + Debate* debate(); + + // The widget holding the query area + Question* question() { return &query; } + const Question* question() const { return &query; } + + // The widget holding the reply area. + Answer* answer() { return &reply; } + const Answer* answer() const { return &reply; } + + // Conversion number + int number() const { return no; } + + // Reimplement positiion management. + QSize sizeHint() const; + QSize minimumSizeHint() const; + + protected: + // void resizeEvent(QResizeEvent*); + + private: + const int no; + Question query; + Answer reply; + + private slots: + void reply_to_query(); + }; + + // -- 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 + // -- queries. + class Conversation : public QWidget { + Q_OBJECT; + public: + explicit Conversation(Debate&); + ~Conversation(); + + // Holds if this conversation just started. + bool fresh() const { return children.empty(); } + + // Number of exchanges in this conversation + int length() const { return children.size(); } + + // Return the `i'-th conversation in this set, if any. + Exchange* operator[](int) const; + + // Start a new conversation topic. + Exchange* new_topic(); + + // Override QWidegt::sizeHint. Return the cumulative sizes + // of all conversations so far. + QSize sizeHint() const; + + // Return the parent engine widget. + Debate* debate() { return &group; } + + public slots: + // Return the topic following a given topic in this set of conversations + Exchange* next(Exchange*); + + private: + typedef std::vector<Exchange*> Children; + Debate& group; + Children children; + }; + + // Default number of characters per question line. + const int question_columns = 80; + + QFont monospace_font(); + QSize em_metrics(const QFont&); +} + +#endif // OPENAXIOM_CONVERSATION_INCLUDED + + +// Local Variables: +// mode: c++ +// End: diff --git a/src/gui/debate.C b/src/gui/debate.C new file mode 100644 index 00000000..a4f289c1 --- /dev/null +++ b/src/gui/debate.C @@ -0,0 +1,44 @@ +// 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 <QScrollBar> +#include "debate.h" + +namespace OpenAxiom { + + Debate::Debate(QWidget* parent) + : QScrollArea(parent), conv(*this) { + setViewportMargins(0, 0, 0, 0); + setWidget(&conv); + } + + Debate::~Debate() { } +} diff --git a/src/gui/debate.h b/src/gui/debate.h new file mode 100644 index 00000000..86016909 --- /dev/null +++ b/src/gui/debate.h @@ -0,0 +1,56 @@ +// 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. + +#ifndef OPENAXIOM_DEBATE_INCLUDED +#define OPENAXIOM_DEBATE_INCLUDED + +#include <QWidget> +#include <QFontMetrics> +#include <QScrollArea> +#include "conversation.h" + +namespace OpenAxiom { + + class Debate : public QScrollArea { + public: + explicit Debate(QWidget*); + ~Debate(); + + private: + Conversation conv; + }; +} + +#endif // OPENAXIOM_DEBATE_INCLUDED + +// Local Variables: +// mode: c++ +// End: diff --git a/src/gui/gui.pro.in b/src/gui/gui.pro.in index 6ea0d2b7..8159a926 100644 --- a/src/gui/gui.pro.in +++ b/src/gui/gui.pro.in @@ -12,12 +12,12 @@ TARGET = gui VPATH += @srcdir@ ## Our headers -HEADERS += main-window.h +HEADERS += conversation.h main-window.h debate.h INCLUDEPATH += @srcdir@ DEPENDPATH += @srcdir@ ## Source files -SOURCES += main-window.C main.C +SOURCES += conversation.C main-window.C debate.C main.C ## Additional support libraries LIBS += $$OA_LIB diff --git a/src/gui/main-window.C b/src/gui/main-window.C index 9409fde8..6e0e16a7 100644 --- a/src/gui/main-window.C +++ b/src/gui/main-window.C @@ -31,18 +31,23 @@ #include <QMenuBar> #include <QAction> +#include <QScrollArea> +#include "engine-widget.h" #include "main-window.h" namespace OpenAxiom { - MainWindow::MainWindow() { + + MainWindow::MainWindow() : tabs(this) { + setCentralWidget(&tabs); + tabs.addTab(new Debate(&tabs), "Main Frame"); QMenu* file = menuBar()->addMenu(tr("&File")); QAction* action = new QAction(tr("Quit"), this); file->addAction(action); action->setShortcut(tr("Ctrl+Q")); connect(action, SIGNAL(triggered()), this, SLOT(close())); } - + MainWindow::~MainWindow() { } } diff --git a/src/gui/main-window.h b/src/gui/main-window.h index fb891504..af9d3d87 100644 --- a/src/gui/main-window.h +++ b/src/gui/main-window.h @@ -33,6 +33,7 @@ #define OPENAXIOM_GUI_MAIN_WINDOW_INCLUDED #include <QMainWindow> +#include <QTabWidget> namespace OpenAxiom { // -- Main application window -- @@ -41,6 +42,9 @@ namespace OpenAxiom { public: MainWindow(); ~MainWindow(); + + private: + QTabWidget tabs; }; } diff --git a/src/gui/main.C b/src/gui/main.C index 46a412df..3f05c12e 100644 --- a/src/gui/main.C +++ b/src/gui/main.C @@ -36,7 +36,7 @@ int main(int argc, char* argv[]) { using namespace OpenAxiom; QApplication app(argc, argv); - MainWindow mainWin; - mainWin.show(); + MainWindow main_win; + main_win.show(); return app.exec(); } |