From cd3c4fec887bab4c956ce88a6aef7e17c172c3f3 Mon Sep 17 00:00:00 2001 From: Drew Fisher Date: Mon, 4 Oct 2010 22:57:48 -0700 Subject: [PATCH 1/1] Near-complete implementation of actions, a shell menu, and basic network connections. A number of things are still stubbed out while we work out network protocol specifics. --- action.cpp | 92 +++++++++++++++++++++++++++++++++++++++++++ action.h | 83 ++++++++++++++++++++++++++++++++++++++ connectionmanager.cpp | 49 +++++++++++++++++++++++ connectionmanager.h | 35 ++++++++++++++++ connectwidget.cpp | 49 +++++++++++++++++++++++ connectwidget.h | 33 ++++++++++++++++ main.cpp | 9 +++++ mainwindow.cpp | 89 +++++++++++++++++++++++++++++++++++++++++ mainwindow.h | 37 +++++++++++++++++ shareboard.pro | 14 +++++++ 10 files changed, 490 insertions(+) create mode 100644 action.cpp create mode 100644 action.h create mode 100644 connectionmanager.cpp create mode 100644 connectionmanager.h create mode 100644 connectwidget.cpp create mode 100644 connectwidget.h create mode 100644 main.cpp create mode 100644 mainwindow.cpp create mode 100644 mainwindow.h create mode 100644 shareboard.pro diff --git a/action.cpp b/action.cpp new file mode 100644 index 0000000..fe82a21 --- /dev/null +++ b/action.cpp @@ -0,0 +1,92 @@ +#include "action.h" + +Action::Action() { + type = INVALID; + id = 0; + approved = false; +} + +Action::Action(Type t) { + type = t; + id = 0; + approved = false; +} + +Action::~Action() { +} + +// +UserJoinAction::UserJoinAction() : Action(Action::UserJoin) { +} +UserJoinAction::~UserJoinAction() { +} + +// +UserPartAction::UserPartAction() : Action(Action::UserPart) { +} +UserPartAction::~UserPartAction() { +} + +// +MouseMoveAction::MouseMoveAction() : Action(Action::MouseMove) { +} +MouseMoveAction::~MouseMoveAction() { +} + +// +DrawLineAction::DrawLineAction() : Action(Action::DrawLine) { + color = Qt::black; + width = 1; +} +DrawLineAction::~DrawLineAction() { +} + +QTextStream& operator<<(QTextStream& out, const Action& action) { + out << "0 " << action.id << " 0 " << static_cast(action.type) ; // mesgID, userID, timestamp, mesgType + switch(action.type) { + case Action::INVALID: break; + case Action::UserJoin: + { + const UserJoinAction* a = static_cast(&action); + out << " " << a->username; + } break; + case Action::UserPart: + break; // UserPart doesn't have any special data + case Action::MouseMove: + { + const MouseMoveAction* a = static_cast(&action); + out << " " << a->pos.x() << " " << a->pos.y(); + } break; + case Action::DrawLine: + { + const DrawLineAction* a = static_cast(&action); + const QColor& c = a->color; + out << " " << c.red() << " " << c.green() << " " << c.blue() << " " << a->width; + foreach(QPointF p, a->points) { + out << " " << p.x() << " " << p.y(); + } + } break; + case Action::AddImage: + { + const AddImageAction* a = static_cast(&action); + // TODO: unbackburner this + //out << " " << a->topLeft << a->image; + } break; + case Action::UserChat: + { + const UserChatAction* a = static_cast(&action); + out << " " << a->text; + } break; + } + out << "\n"; + return out; +} + +/*QTextStream& operator>>(QTextStream& in, Action& action){ + quint16 k; + in >> k >> action.id >> action.approved >> action.when; + action.type = static_cast(k); + return in; +}*/ + + diff --git a/action.h b/action.h new file mode 100644 index 0000000..3f2baf2 --- /dev/null +++ b/action.h @@ -0,0 +1,83 @@ +#ifndef __ACTION_H__ +#define __ACTION_H__ + +#include +#include +#include +#include +#include +#include +#include + +class Action { +public: + enum Type { + INVALID, + UserJoin, + UserPart, + MouseMove, + DrawLine, + AddImage, + UserChat + }; + + Action(); + Action(Type t); + virtual ~Action(); + + Type type; + // UUID uuid; // Unique id for this + int id; // Which user does this refer to? + bool approved; // Has the server approved this event yet? + QDateTime when; // Time at which the server made this event official + +}; + +class UserJoinAction : public Action { +public: + UserJoinAction(); + ~UserJoinAction(); + QString username; // What's their username? +}; + +class UserPartAction : public Action { +public: + UserPartAction(); + ~UserPartAction(); +}; + +class MouseMoveAction : public Action { +public: + MouseMoveAction(); + ~MouseMoveAction(); + QPointF pos; +}; + +class DrawLineAction : public Action { +public: + DrawLineAction(); + ~DrawLineAction(); + QColor color; + int width; + QVector points; + +}; + +class AddImageAction : public Action { +public: + AddImageAction(); + ~AddImageAction(); + QPointF topLeft; + QImage image; +}; + +class UserChatAction : public Action { +public: + UserChatAction(); + ~UserChatAction(); + QString text; +}; + +QTextStream& operator<<(QTextStream& out, const Action& action); + +#endif //__ACTION_H__ diff --git a/connectionmanager.cpp b/connectionmanager.cpp new file mode 100644 index 0000000..3ad8525 --- /dev/null +++ b/connectionmanager.cpp @@ -0,0 +1,49 @@ +#include "connectionmanager.h" +#include +#include +#include +#include "action.h" + +ConnectionManager::ConnectionManager(QObject* parent) : QObject(parent) { + sock = new QTcpSocket(); + textStream = new QTextStream(sock); + QObject::connect(sock, SIGNAL(connected()), this, SLOT(onConnect())); + QObject::connect(sock, SIGNAL(readyRead()), this, SLOT(haveData())); +} + +ConnectionManager::~ConnectionManager() { + if (sock) delete sock; +} + +void ConnectionManager::joinServer(QString _username, QString host) { + username = _username; + quint16 port = 4260; + sock->connectToHost(host, port); + qDebug() << "Connecting to " << host << "as " << _username; +} + +void ConnectionManager::sendAction(Action* action) { + switch(action->type) { // Handle serializing all actions + default: + break; + } + +} +void ConnectionManager::haveData() { + data.append(sock->readAll()); + // Check if we have a full action waiting, if so, dispatch + // Event structure: + // uint32 num_bytes (we don't support things larger than 4GB, kthx + // uint16 type + // char approved + // QDateTime when + // [rest of details] + qDebug() << data.size() << " bytes read so far"; +} + +void ConnectionManager::onConnect() { + qDebug() << "connection established"; + emit connected(); + (*textStream) << "0 0 0 2 " << username << endl; + qDebug() << "sent JoinAction"; +} diff --git a/connectionmanager.h b/connectionmanager.h new file mode 100644 index 0000000..d9bc4e5 --- /dev/null +++ b/connectionmanager.h @@ -0,0 +1,35 @@ +#ifndef __CONNECTIONMANAGER_H__ +#define __CONNECTIONMANAGER_H__ + +#include +#include +#include +#include + +class Action; +class QTcpSocket; + +class ConnectionManager : public QObject{ + Q_OBJECT + public: + ConnectionManager(QObject* parent = 0); + ~ConnectionManager(); + signals: + //void readData(); + //void actionRecieved(Action* act); // act is freed by recipient of signal + void connected(); + public slots: + void joinServer(QString _username, QString host); + void sendAction(Action* action); + private: + QString username; + QByteArray data; + QTcpSocket* sock; + QTextStream* textStream; + private slots: + void haveData(); + void onConnect(); +}; + +#endif // __CONNECTIONMANAGER_H__ + diff --git a/connectwidget.cpp b/connectwidget.cpp new file mode 100644 index 0000000..9933701 --- /dev/null +++ b/connectwidget.cpp @@ -0,0 +1,49 @@ +#include "connectwidget.h" + +#include +#include +#include +#include +#include + + +ConnectWidget::ConnectWidget(QWidget* parent) : QWidget(parent) { + layout = new QVBoxLayout(); + grid = new QGridLayout(); + userEdit = new QLineEdit("guest", this); + hostEdit = new QLineEdit("kraken.zarvox.org", this); + userLabel = new QLabel("Pick a username:", this); + userLabel->setAlignment(Qt::AlignRight); + hostLabel = new QLabel("Server to connect to:", this); + hostLabel->setAlignment(Qt::AlignRight); + goButton = new QPushButton("Connect!"); + + grid->addWidget(userLabel, 0,0); + grid->addWidget(userEdit, 0,1); + grid->addWidget(hostLabel, 1,0); + grid->addWidget(hostEdit, 1,1); + layout->addStretch(); + layout->addLayout(grid); + layout->addWidget(goButton); + layout->addStretch(); + + setLayout(layout); + QObject::connect(goButton, SIGNAL(clicked()), this, SLOT(connect())); +} + +ConnectWidget::~ConnectWidget() { +} + +void ConnectWidget::connect() { + QString username = userEdit->text().replace(" ", "_"); + QString host = hostEdit->text(); + emit connectToServer(username, host); +} + +QString ConnectWidget::user() { + return userEdit->text(); +} + +QString ConnectWidget::host() { + return hostEdit->text(); +} diff --git a/connectwidget.h b/connectwidget.h new file mode 100644 index 0000000..39dca00 --- /dev/null +++ b/connectwidget.h @@ -0,0 +1,33 @@ +#ifndef __CONNECTWIDGET_H__ +#define __CONNECTWIDGET_H__ + +#include + +class QGridLayout; +class QLabel; +class QLineEdit; +class QPushButton; +class QVBoxLayout; + +class ConnectWidget : public QWidget { + Q_OBJECT + public: + ConnectWidget(QWidget* parent=0); + ~ConnectWidget(); + QString user(); + QString host(); + public slots: + void connect(); + signals: + void connectToServer(QString username, QString host); + private: + QVBoxLayout* layout; + QGridLayout* grid; + QLineEdit* userEdit; + QLineEdit* hostEdit; + QLabel* userLabel; + QLabel* hostLabel; + QPushButton* goButton; +}; + +#endif // __CONNECTWIDGET_H__ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..5f29b1c --- /dev/null +++ b/main.cpp @@ -0,0 +1,9 @@ +#include +#include "mainwindow.h" + +int main(int argc, char** argv) { + QApplication* app = new QApplication(argc, argv); + MainWindow* win = new MainWindow(); + win->show(); + return app->exec(); +} diff --git a/mainwindow.cpp b/mainwindow.cpp new file mode 100644 index 0000000..b45c8cd --- /dev/null +++ b/mainwindow.cpp @@ -0,0 +1,89 @@ +#include "mainwindow.h" +#include +#include +#include +#include +#include +#include +#include + +#include "connectwidget.h" +#include "connectionmanager.h" + +MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { + createActions(); + createMenus(); + + prompt = new ConnectWidget(); + setCentralWidget(prompt); + + connMan = new ConnectionManager(this); + QObject::connect(prompt, SIGNAL(connectToServer(QString, QString)), connMan, SLOT(joinServer(QString, QString))); + + statusBar()->showMessage("Ready"); +} + +MainWindow::~MainWindow() { + +} + +void MainWindow::load() { + QString fileName = QFileDialog::getOpenFileName(this, "Load saved board:", ".", "Shareboards (*.board)"); + if(fileName.isEmpty()) { + statusBar()->showMessage("Cancelled loading board."); + return; + } + QFile file(fileName); + QDataStream in(&file); + // Load all history from that file + statusBar()->showMessage(QString("Successfully loaded ").append(fileName)); +} + +void MainWindow::save() { + QString fileName = QFileDialog::getSaveFileName(this, "Save board history as:", ".", "Shareboards (*.board)"); + if(fileName.isEmpty()) { + statusBar()->showMessage("Cancelled saving board."); + return; + } + if(!fileName.endsWith(".board")) + fileName = fileName.append(".board"); + QFile file(fileName); + QDataStream out(&file); + // Output all history to that file + statusBar()->showMessage(QString("Successfully saved ").append(fileName)); +} + +void MainWindow::quit() { + // If we want, we can add some annoying dialog that says "Quit now?" and throws the user off + qApp->quit(); +} + +void MainWindow::switchToBoard() { + // Here, we set the central widget to be the as-of-yet unimplemented Shareboard widget +} + +void MainWindow::switchToConnect() { + setCentralWidget(prompt); +} + +void MainWindow::createActions() { + //joinAction = new QAction("Joi&n a board online", this); + loadPastAction = new QAction("&Open a board", this); + saveAction = new QAction("&Save board", this); + quitAction = new QAction("&Quit", this); + QObject::connect(loadPastAction, SIGNAL(triggered()), this, SLOT(load())); + QObject::connect(saveAction, SIGNAL(triggered()), this, SLOT(save())); + QObject::connect(quitAction, SIGNAL(triggered()), this, SLOT(quit())); +} + +void MainWindow::createMenus() { + + fileMenu = menuBar()->addMenu("&File"); + //fileMenu->addAction(joinAction); + fileMenu->addAction(loadPastAction); + fileMenu->addAction(saveAction); + fileMenu->addSeparator(); + fileMenu->addAction(quitAction); + +} + diff --git a/mainwindow.h b/mainwindow.h new file mode 100644 index 0000000..44cda15 --- /dev/null +++ b/mainwindow.h @@ -0,0 +1,37 @@ +#ifndef __MAINWINDOW_H__ +#define __MAINWINDOW_H__ + +#include + +class QMenu; +class QAction; +class ConnectWidget; +class ConnectionManager; + +class MainWindow : public QMainWindow { + Q_OBJECT +public: + MainWindow(QWidget* parent = 0); + ~MainWindow(); +public slots: +// void join(); + void load(); + void save(); + void quit(); + void switchToBoard(); + void switchToConnect(); +private: + void createActions(); + void createMenus(); + QMenu* fileMenu; +// QAction* joinAction; // Removed in favor of central widget handling this. + QAction* loadPastAction; + QAction* saveAction; + QAction* quitAction; + ConnectionManager* connMan; +// HistoryManager* historyManager; +// Shareboard* board; + ConnectWidget* prompt; +}; + +#endif // __MAINWINDOW_H__ diff --git a/shareboard.pro b/shareboard.pro new file mode 100644 index 0000000..2c4c6bf --- /dev/null +++ b/shareboard.pro @@ -0,0 +1,14 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Thu Sep 30 22:59:13 2010 +###################################################################### + +TEMPLATE = app +TARGET = shareboard +CONFIG += qt +QT += network +DEPENDPATH += . +INCLUDEPATH += . + +# Input +HEADERS += mainwindow.h action.h connectwidget.h connectionmanager.h +SOURCES += main.cpp mainwindow.cpp action.cpp connectwidget.cpp connectionmanager.cpp -- 2.39.2