diff options
Diffstat (limited to 'src/libcommuni/examples')
30 files changed, 1962 insertions, 0 deletions
diff --git a/src/libcommuni/examples/bot/bot.pro b/src/libcommuni/examples/bot/bot.pro new file mode 100644 index 0000000..1f4f434 --- /dev/null +++ b/src/libcommuni/examples/bot/bot.pro @@ -0,0 +1,17 @@ +###################################################################### +# Communi +###################################################################### + +TEMPLATE = app +TARGET = bot +DEPENDPATH += . +INCLUDEPATH += . +CONFIG += console +CONFIG -= app_bundle +QT = core network + +# Input +HEADERS += ircbot.h +SOURCES += ircbot.cpp main.cpp + +include(../examples.pri) diff --git a/src/libcommuni/examples/bot/ircbot.cpp b/src/libcommuni/examples/bot/ircbot.cpp new file mode 100644 index 0000000..1891e62 --- /dev/null +++ b/src/libcommuni/examples/bot/ircbot.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#include "ircbot.h" +#include <IrcMessage> +#include <IrcCommand> +#include <QCoreApplication> +#include <QTimer> + +IrcBot::IrcBot(QObject* parent) : IrcConnection(parent) +{ +//! [messages] + connect(this, SIGNAL(privateMessageReceived(IrcPrivateMessage*)), this, SLOT(processMessage(IrcPrivateMessage*))); +//! [messages] + +//! [commands] + parser.addCommand(IrcCommand::CtcpAction, "ACT [target] <message...>"); + parser.addCommand(IrcCommand::Custom, "HELP (<command...>)"); + parser.addCommand(IrcCommand::Nick, "NICK <nick>"); + parser.addCommand(IrcCommand::Join, "JOIN <#channel> (<key>)"); + parser.addCommand(IrcCommand::Part, "PART (<#channel>) (<message...>)"); + parser.addCommand(IrcCommand::Quit, "QUIT (<message...>)"); + parser.addCommand(IrcCommand::Message, "SAY [target] <message...>"); +//! [commands] + + bufferModel.setConnection(this); +//! [channels] + connect(&bufferModel, SIGNAL(channelsChanged(QStringList)), &parser, SLOT(setChannels(QStringList))); +//! [channels] +} + +void IrcBot::join(QString channel) +{ + sendCommand(IrcCommand::createJoin(channel)); +} + +//![receive] +void IrcBot::processMessage(IrcPrivateMessage* message) +{ + if (message->isPrivate()) { + // private message: reply to the message sender + // => triggers: "!<cmd> <params>" and "<cmd> <params>" + parser.setTarget(message->nick()); + parser.setTriggers(QStringList() << "!" << ""); + } else { + // channel message: reply to the target channel + // => triggers: "!<cmd> <params>" and "bot: <cmd> <params>" + parser.setTarget(message->target()); + parser.setTriggers(QStringList() << "!" << nickName().append(":")); + } + + IrcCommand* cmd = parser.parse(message->content()); + if (cmd) { + if (cmd->type() == IrcCommand::Custom && cmd->parameters().value(0) == "HELP") { + help(cmd->parameters().mid(1)); + } else { + sendCommand(cmd); + + if (cmd->type() == IrcCommand::Quit) { + connect(this, SIGNAL(disconnected()), qApp, SLOT(quit())); + QTimer::singleShot(1000, qApp, SLOT(quit())); + } + } + } +} +//![receive] + +void IrcBot::help(QStringList commands) +{ + if (commands.isEmpty()) + commands = parser.commands(); + + QString target = parser.target(); + foreach (const QString& command, commands) { + QString syntax = parser.syntax(command); + sendCommand(IrcCommand::createMessage(target, syntax)); + } +} diff --git a/src/libcommuni/examples/bot/ircbot.h b/src/libcommuni/examples/bot/ircbot.h new file mode 100644 index 0000000..1a88a01 --- /dev/null +++ b/src/libcommuni/examples/bot/ircbot.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#ifndef IRCBOT_H +#define IRCBOT_H + +#include <IrcConnection> +#include <IrcBufferModel> +#include <IrcCommandParser> + +class IrcBot : public IrcConnection +{ + Q_OBJECT + +public: + IrcBot(QObject* parent = 0); + +public slots: + void join(QString channel); + +private slots: + void processMessage(IrcPrivateMessage* message); + +private: + void help(QStringList commands); + + IrcCommandParser parser; + IrcBufferModel bufferModel; +}; + +#endif // IRCBOT_H diff --git a/src/libcommuni/examples/bot/main.cpp b/src/libcommuni/examples/bot/main.cpp new file mode 100644 index 0000000..526f776 --- /dev/null +++ b/src/libcommuni/examples/bot/main.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#include <QtCore> +#include "ircbot.h" + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + + // enable debug output + qputenv("IRC_DEBUG", "1"); + qsrand(QTime::currentTime().msec()); + + IrcBot bot; + bot.setHost("irc.freenode.net"); + bot.setUserName("communi"); + bot.setNickName("Bot" + QString::number(qrand() % 9999)); + bot.setRealName("Communi " + Irc::version() + " example bot"); + + bool joined = false; + QStringList args = app.arguments(); + for (int i = 0; i < args.count(); ++i) { + QString arg = args.at(i); + if (arg == "-help") { + qDebug() << "Usage:" << argv[0] << "(-host <host>) (-port <port>) (-ssl) (-nick <nick>) (-user <user>) (-password <password>) (-channel <channel>)"; + return 0; + } else if (arg == "-port") { + bool ok = false; + int port = args.value(++i).toInt(&ok); + if (ok) + bot.setPort(port); + } else if (arg == "-ssl") { + bot.setSecure(true); + } else if (arg == "-host") { + bot.setHost(args.value(++i)); + } else if (arg == "-user") { + bot.setUserName(args.value(++i)); + } else if (arg == "-password") { + bot.setPassword(args.value(++i)); + } else if (arg == "-nick") { + bot.setNickName(args.value(++i)); + } else if (arg == "-channel") { + bot.join(args.value(++i)); + joined = true; + } + } + if (!joined) + bot.join("#communi"); + + bot.open(); + return app.exec(); +} diff --git a/src/libcommuni/examples/client/client.pro b/src/libcommuni/examples/client/client.pro new file mode 100644 index 0000000..5e91b12 --- /dev/null +++ b/src/libcommuni/examples/client/client.pro @@ -0,0 +1,16 @@ +###################################################################### +# Communi +###################################################################### + +TEMPLATE = app +TARGET = client +DEPENDPATH += . +INCLUDEPATH += . +QT = core network gui +greaterThan(QT_MAJOR_VERSION, 4):QT += widgets + +# Input +HEADERS += ircclient.h ircmessageformatter.h +SOURCES += ircclient.cpp ircmessageformatter.cpp main.cpp + +include(../examples.pri) diff --git a/src/libcommuni/examples/client/ircclient.cpp b/src/libcommuni/examples/client/ircclient.cpp new file mode 100644 index 0000000..c33043b --- /dev/null +++ b/src/libcommuni/examples/client/ircclient.cpp @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#include "ircclient.h" +#include "ircmessageformatter.h" + +#include <QTextDocument> +#include <QTextCursor> +#include <QVBoxLayout> +#include <QScrollBar> +#include <QLineEdit> +#include <QShortcut> +#include <QListView> +#include <QTextEdit> +#include <QTime> + +#include <Irc> +#include <IrcUser> +#include <IrcBuffer> +#include <IrcCommand> +#include <IrcMessage> +#include <IrcUserModel> +#include <IrcCompleter> +#include <IrcConnection> +#include <IrcBufferModel> +#include <IrcCommandParser> + +static const char* CHANNEL = "#communi"; +static const char* SERVER = "irc.freenode.net"; + +IrcClient::IrcClient(QWidget* parent) : QSplitter(parent) +{ + createParser(); + createConnection(); + createCompleter(); + createUserList(); + createLayout(); + createBufferList(); + + // queue a command to automatically join the channel when connected + connection->sendCommand(IrcCommand::createJoin(CHANNEL)); + connection->open(); + + textEdit->append(IrcMessageFormatter::formatMessage(tr("! Welcome to the Communi %1 example client.").arg(IRC_VERSION_STR))); + textEdit->append(IrcMessageFormatter::formatMessage(tr("! This example connects %1 and joins %2.").arg(SERVER, CHANNEL))); + textEdit->append(IrcMessageFormatter::formatMessage(tr("! PS. Available commands: JOIN, ME, NICK, PART"))); +} + +IrcClient::~IrcClient() +{ + if (connection->isActive()) { + connection->quit(connection->realName()); + connection->close(); + } +} + +void IrcClient::onConnected() +{ + textEdit->append(IrcMessageFormatter::formatMessage("! Connected to %1.").arg(SERVER)); + textEdit->append(IrcMessageFormatter::formatMessage("! Joining %1...").arg(CHANNEL)); +} + +void IrcClient::onConnecting() +{ + textEdit->append(IrcMessageFormatter::formatMessage("! Connecting %1...").arg(SERVER)); +} + +void IrcClient::onDisconnected() +{ + textEdit->append(IrcMessageFormatter::formatMessage("! Disconnected from %1.").arg(SERVER)); +} + +void IrcClient::onTextEdited() +{ + // clear the possible error indication + lineEdit->setStyleSheet(QString()); +} + +void IrcClient::onTextEntered() +{ + QString input = lineEdit->text(); + IrcCommand* command = parser->parse(input); + if (command) { + connection->sendCommand(command); + + // echo own messages (servers do not send our own messages back) + if (command->type() == IrcCommand::Message || command->type() == IrcCommand::CtcpAction) { + IrcMessage* msg = command->toMessage(connection->nickName(), connection); + receiveMessage(msg); + delete msg; + } + + lineEdit->clear(); + } else if (input.length() > 1) { + QString error; + QString command = lineEdit->text().mid(1).split(" ", QString::SkipEmptyParts).value(0).toUpper(); + if (parser->commands().contains(command)) + error = tr("[ERROR] Syntax: %1").arg(parser->syntax(command).replace("<", "<").replace(">", ">")); + else + error = tr("[ERROR] Unknown command: %1").arg(command); + textEdit->append(IrcMessageFormatter::formatMessage(error)); + lineEdit->setStyleSheet("background: salmon"); + } +} + +void IrcClient::onCompletion() +{ + completer->complete(lineEdit->text(), lineEdit->cursorPosition()); +} + +void IrcClient::onCompleted(const QString& text, int cursor) +{ + lineEdit->setText(text); + lineEdit->setCursorPosition(cursor); +} + +void IrcClient::onBufferAdded(IrcBuffer* buffer) +{ + // joined a buffer - start listening to buffer specific messages + connect(buffer, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(receiveMessage(IrcMessage*))); + + // create a document for storing the buffer specific messages + QTextDocument* document = new QTextDocument(buffer); + documents.insert(buffer, document); + + // create a sorted model for buffer users + IrcUserModel* userModel = new IrcUserModel(buffer); + userModel->setSortMethod(Irc::SortByTitle); + userModels.insert(buffer, userModel); + + // activate the new buffer + int idx = bufferModel->buffers().indexOf(buffer); + if (idx != -1) + bufferList->setCurrentIndex(bufferModel->index(idx)); +} + +void IrcClient::onBufferRemoved(IrcBuffer* buffer) +{ + // the buffer specific models and documents are no longer needed + delete userModels.take(buffer); + delete documents.take(buffer); +} + +void IrcClient::onBufferActivated(const QModelIndex& index) +{ + IrcBuffer* buffer = index.data(Irc::BufferRole).value<IrcBuffer*>(); + + // document, user list and nick completion for the current buffer + textEdit->setDocument(documents.value(buffer)); + textEdit->verticalScrollBar()->triggerAction(QScrollBar::SliderToMaximum); + userList->setModel(userModels.value(buffer)); + completer->setBuffer(buffer); + + // keep the command parser aware of the context + if (buffer) + parser->setTarget(buffer->title()); +} + +void IrcClient::onUserActivated(const QModelIndex& index) +{ + IrcUser* user = index.data(Irc::UserRole).value<IrcUser*>(); + + if (user) { + IrcBuffer* buffer = bufferModel->add(user->name()); + + // activate the new query + int idx = bufferModel->buffers().indexOf(buffer); + if (idx != -1) + bufferList->setCurrentIndex(bufferModel->index(idx)); + } +} + +static void appendHtml(QTextDocument* document, const QString& html) +{ + QTextCursor cursor(document); + cursor.beginEditBlock(); + cursor.movePosition(QTextCursor::End); + if (!document->isEmpty()) + cursor.insertBlock(); + cursor.insertHtml(html); + cursor.endEditBlock(); +} + +void IrcClient::receiveMessage(IrcMessage* message) +{ + IrcBuffer* buffer = qobject_cast<IrcBuffer*>(sender()); + if (!buffer) + buffer = bufferList->currentIndex().data(Irc::BufferRole).value<IrcBuffer*>(); + + QTextDocument* document = documents.value(buffer); + if (document) { + QString html = IrcMessageFormatter::formatMessage(message); + if (!html.isEmpty()) { + if (document == textEdit->document()) + textEdit->append(html); + else + appendHtml(document, html); + } + } +} + +void IrcClient::createLayout() +{ + setWindowTitle(tr("Communi %1 example client").arg(IRC_VERSION_STR)); + + // a read-only text editor for showing the messages + textEdit = new QTextEdit(this); + textEdit->setReadOnly(true); + + // a line editor for entering commands + lineEdit = new QLineEdit(this); + lineEdit->setAttribute(Qt::WA_MacShowFocusRect, false); + textEdit->setFocusProxy(lineEdit); + connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(onTextEntered())); + connect(lineEdit, SIGNAL(textEdited(QString)), this, SLOT(onTextEdited())); + + // the rest is just setting up the UI layout... + QSplitter* splitter = new QSplitter(this); + splitter->setHandleWidth(1); + splitter->addWidget(textEdit); + splitter->addWidget(userList); + splitter->setStretchFactor(0, 5); + splitter->setStretchFactor(1, 1); + + QWidget* container = new QWidget(this); + QVBoxLayout* layout = new QVBoxLayout(container); + layout->setSpacing(0); + layout->setMargin(0); + layout->addWidget(splitter); + layout->addWidget(lineEdit); + + addWidget(container); + + setHandleWidth(1); +} + +void IrcClient::createCompleter() +{ + // nick name completion + completer = new IrcCompleter(this); + completer->setParser(parser); + connect(completer, SIGNAL(completed(QString,int)), this, SLOT(onCompleted(QString,int))); + + QShortcut* shortcut = new QShortcut(Qt::Key_Tab, this); + connect(shortcut, SIGNAL(activated()), this, SLOT(onCompletion())); +} + +void IrcClient::createParser() +{ + // create a command parser and teach it some commands. notice also + // that we must keep the command parser aware of the context in + // createUi() and onBufferActivated() + + parser = new IrcCommandParser(this); + parser->setTolerant(true); + parser->setTriggers(QStringList("/")); + parser->addCommand(IrcCommand::Join, "JOIN <#channel> (<key>)"); + parser->addCommand(IrcCommand::CtcpAction, "ME [target] <message...>"); + parser->addCommand(IrcCommand::Mode, "MODE (<channel/user>) (<mode>) (<arg>)"); + parser->addCommand(IrcCommand::Nick, "NICK <nick>"); + parser->addCommand(IrcCommand::Part, "PART (<#channel>) (<message...>)"); +} + +void IrcClient::createUserList() +{ + // a list of channel users + userList = new QListView(this); + userList->setFocusPolicy(Qt::NoFocus); + + // open a private query when double clicking a user + connect(userList, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onUserActivated(QModelIndex))); +} + +void IrcClient::createBufferList() +{ + bufferModel = new IrcBufferModel(connection); + connect(bufferModel, SIGNAL(added(IrcBuffer*)), this, SLOT(onBufferAdded(IrcBuffer*))); + connect(bufferModel, SIGNAL(removed(IrcBuffer*)), this, SLOT(onBufferRemoved(IrcBuffer*))); + + bufferList = new QListView(this); + bufferList->setFocusPolicy(Qt::NoFocus); + bufferList->setModel(bufferModel); + + // keep the command parser aware of the context + connect(bufferModel, SIGNAL(channelsChanged(QStringList)), parser, SLOT(setChannels(QStringList))); + + // keep track of the current buffer, see also onBufferActivated() + connect(bufferList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(onBufferActivated(QModelIndex))); + + // create a server buffer for non-targeted messages... + IrcBuffer* serverBuffer = bufferModel->add(connection->host()); + + // ...and connect it to IrcBufferModel::messageIgnored() + connect(bufferModel, SIGNAL(messageIgnored(IrcMessage*)), serverBuffer, SLOT(receiveMessage(IrcMessage*))); + + insertWidget(0, bufferList); + + setStretchFactor(0, 1); + setStretchFactor(1, 3); +} + +void IrcClient::createConnection() +{ + connection = new IrcConnection(this); + connect(connection, SIGNAL(connected()), this, SLOT(onConnected())); + connect(connection, SIGNAL(connecting()), this, SLOT(onConnecting())); + connect(connection, SIGNAL(disconnected()), this, SLOT(onDisconnected())); + + qsrand(QTime::currentTime().msec()); + + connection->setHost(SERVER); + connection->setUserName("communi"); + connection->setNickName(tr("Client%1").arg(qrand() % 9999)); + connection->setRealName(tr("Communi %1 example client").arg(IRC_VERSION_STR)); +} diff --git a/src/libcommuni/examples/client/ircclient.h b/src/libcommuni/examples/client/ircclient.h new file mode 100644 index 0000000..9d6ee3c --- /dev/null +++ b/src/libcommuni/examples/client/ircclient.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#ifndef IRCCLIENT_H +#define IRCCLIENT_H + +#include <QSplitter> +#include <QHash> + +class IrcBuffer; +class IrcMessage; +class IrcUserModel; +class IrcCompleter; +class IrcConnection; +class IrcBufferModel; +class IrcCommandParser; + +QT_FORWARD_DECLARE_CLASS(QLineEdit) +QT_FORWARD_DECLARE_CLASS(QListView) +QT_FORWARD_DECLARE_CLASS(QTextEdit) +QT_FORWARD_DECLARE_CLASS(QModelIndex) +QT_FORWARD_DECLARE_CLASS(QTextDocument) + +class IrcClient : public QSplitter +{ + Q_OBJECT + +public: + IrcClient(QWidget* parent = 0); + ~IrcClient(); + +private slots: + void onConnected(); + void onConnecting(); + void onDisconnected(); + + void onTextEdited(); + void onTextEntered(); + + void onCompletion(); + void onCompleted(const QString& text, int cursor); + + void onBufferAdded(IrcBuffer* buffer); + void onBufferRemoved(IrcBuffer* buffer); + + void onBufferActivated(const QModelIndex& index); + void onUserActivated(const QModelIndex& index); + + void receiveMessage(IrcMessage* message); + +private: + void createLayout(); + void createCompleter(); + void createParser(); + void createUserList(); + void createBufferList(); + void createConnection(); + + QLineEdit* lineEdit; + QTextEdit* textEdit; + QListView* userList; + QListView* bufferList; + + IrcCompleter* completer; + IrcCommandParser* parser; + IrcConnection* connection; + IrcBufferModel* bufferModel; + QHash<IrcBuffer*, IrcUserModel*> userModels; + QHash<IrcBuffer*, QTextDocument*> documents; +}; + +#endif // IRCCLIENT_H diff --git a/src/libcommuni/examples/client/ircmessageformatter.cpp b/src/libcommuni/examples/client/ircmessageformatter.cpp new file mode 100644 index 0000000..6537664 --- /dev/null +++ b/src/libcommuni/examples/client/ircmessageformatter.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#include "ircmessageformatter.h" + +#include <IrcTextFormat> +#include <IrcConnection> +#include <QTime> +#include <Irc> + +QString IrcMessageFormatter::formatMessage(IrcMessage* message) +{ + QString formatted; + switch (message->type()) { + case IrcMessage::Join: + formatted = formatJoinMessage(static_cast<IrcJoinMessage*>(message)); + break; + case IrcMessage::Mode: + formatted = formatModeMessage(static_cast<IrcModeMessage*>(message)); + break; + case IrcMessage::Names: + formatted = formatNamesMessage(static_cast<IrcNamesMessage*>(message)); + break; + case IrcMessage::Nick: + formatted = formatNickMessage(static_cast<IrcNickMessage*>(message)); + break; + case IrcMessage::Part: + formatted = formatPartMessage(static_cast<IrcPartMessage*>(message)); + break; + case IrcMessage::Private: + formatted = formatPrivateMessage(static_cast<IrcPrivateMessage*>(message)); + break; + case IrcMessage::Quit: + formatted = formatQuitMessage(static_cast<IrcQuitMessage*>(message)); + break; + default: + break; + } + return formatMessage(formatted); +} + +QString IrcMessageFormatter::formatMessage(const QString& message) +{ + if (!message.isEmpty()) { + QString formatted = QObject::tr("[%1] %2").arg(QTime::currentTime().toString(), message); + if (message.startsWith("!")) + formatted = QObject::tr("<font color='gray'>%1</font>").arg(formatted); + else if (message.startsWith("*")) + formatted = QObject::tr("<font color='maroon'>%1</font>").arg(formatted); + else if (message.startsWith("[")) + formatted = QObject::tr("<font color='indianred'>%1</font>").arg(formatted); + return formatted; + } + return QString(); +} + +QString IrcMessageFormatter::formatJoinMessage(IrcJoinMessage* message) +{ + if (message->flags() & IrcMessage::Own) + return QObject::tr("! You have joined %1 as %2").arg(message->channel(), message->nick()); + else + return QObject::tr("! %1 has joined %2").arg(message->nick(), message->channel()); +} + +QString IrcMessageFormatter::formatModeMessage(IrcModeMessage* message) +{ + QString args = message->arguments().join(" "); + if (message->isReply()) + return QObject::tr("! %1 mode is %2 %3").arg(message->target(), message->mode(), args); + else + return QObject::tr("! %1 sets mode %2 %3 %4").arg(message->nick(), message->target(), message->mode(), args); +} + +QString IrcMessageFormatter::formatNamesMessage(IrcNamesMessage* message) +{ + return QObject::tr("! %1 has %2 users").arg(message->channel()).arg(message->names().count()); +} + +QString IrcMessageFormatter::formatNickMessage(IrcNickMessage* message) +{ + return QObject::tr("! %1 has changed nick to %2").arg(message->oldNick(), message->newNick()); +} + +QString IrcMessageFormatter::formatPartMessage(IrcPartMessage* message) +{ + if (message->reason().isEmpty()) + return QObject::tr("! %1 has left %2").arg(message->nick(), message->channel()); + else + return QObject::tr("! %1 has left %2 (%3)").arg(message->nick(), message->channel(), message->reason()); +} + +QString IrcMessageFormatter::formatPrivateMessage(IrcPrivateMessage* message) +{ + const QString content = IrcTextFormat().toHtml(message->content()); + if (message->isAction()) + return QObject::tr("* %1 %2").arg(message->nick(), content); + else + return QObject::tr("<%1> %2").arg(message->nick(),content); +} + +QString IrcMessageFormatter::formatQuitMessage(IrcQuitMessage* message) +{ + if (message->reason().isEmpty()) + return QObject::tr("! %1 has quit").arg(message->nick()); + else + return QObject::tr("! %1 has quit (%2)").arg(message->nick(), message->reason()); +} diff --git a/src/libcommuni/examples/client/ircmessageformatter.h b/src/libcommuni/examples/client/ircmessageformatter.h new file mode 100644 index 0000000..f91be50 --- /dev/null +++ b/src/libcommuni/examples/client/ircmessageformatter.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#ifndef IRCMESSAGEFORMATTER_H +#define IRCMESSAGEFORMATTER_H + +#include <IrcMessage> + +class IrcMessageFormatter +{ +public: + static QString formatMessage(IrcMessage* message); + static QString formatMessage(const QString& message); + +private: + static QString formatJoinMessage(IrcJoinMessage* message); + static QString formatModeMessage(IrcModeMessage* message); + static QString formatNamesMessage(IrcNamesMessage* message); + static QString formatNickMessage(IrcNickMessage* message); + static QString formatPartMessage(IrcPartMessage* message); + static QString formatPrivateMessage(IrcPrivateMessage* message); + static QString formatQuitMessage(IrcQuitMessage* message); +}; + +#endif // IRCMESSAGEFORMATTER_H diff --git a/src/libcommuni/examples/client/main.cpp b/src/libcommuni/examples/client/main.cpp new file mode 100644 index 0000000..951ee91 --- /dev/null +++ b/src/libcommuni/examples/client/main.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#include <QApplication> +#include "ircclient.h" + +int main(int argc, char* argv[]) +{ + QApplication app(argc, argv); + IrcClient client; + client.resize(800, 480); + client.show(); + return app.exec(); +} diff --git a/src/libcommuni/examples/examples.pri b/src/libcommuni/examples/examples.pri new file mode 100644 index 0000000..bc9b138 --- /dev/null +++ b/src/libcommuni/examples/examples.pri @@ -0,0 +1,24 @@ +###################################################################### +# Communi +###################################################################### + +!verbose:CONFIG += silent + +CONFIG(debug, debug|release) { + OBJECTS_DIR = debug + MOC_DIR = debug + RCC_DIR = debug + UI_DIR = debug +} else { + OBJECTS_DIR = release + MOC_DIR = release + RCC_DIR = release + UI_DIR = release +} + +developer { + include(../src/src.pri) +} else { + IRC_MODULES = IrcCore IrcModel IrcUtil + include(../src/module_deps.pri) +} diff --git a/src/libcommuni/examples/examples.pro b/src/libcommuni/examples/examples.pro new file mode 100644 index 0000000..48d9906 --- /dev/null +++ b/src/libcommuni/examples/examples.pro @@ -0,0 +1,13 @@ +###################################################################### +# Communi +###################################################################### + +TEMPLATE = subdirs +SUBDIRS += bot client minimal + +!lessThan(QT_MAJOR_VERSION, 5):!lessThan(QT_MINOR_VERSION, 1) { + qtHaveModule(qml):SUBDIRS += qmlbot +} +!lessThan(QT_MAJOR_VERSION, 5):!lessThan(QT_MINOR_VERSION, 2) { + qtHaveModule(quick):SUBDIRS += quick +} diff --git a/src/libcommuni/examples/minimal/main.cpp b/src/libcommuni/examples/minimal/main.cpp new file mode 100644 index 0000000..790280e --- /dev/null +++ b/src/libcommuni/examples/minimal/main.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#include <QtCore> +#include <IrcConnection> +#include <IrcCommand> +#include <Irc> + +#ifndef IRC_DOXYGEN +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + + // enable debug output + //qputenv("IRC_DEBUG", "1"); + qsrand(QTime::currentTime().msec()); + +//! [minimal] + IrcConnection connection("irc.rbx.fr.euirc.net"); + connection.setUserName("communi"); + connection.setNickName(QString("Minimal%1").arg(qrand() % 9999)); + connection.setRealName(QString("Communi %1 minimal example").arg(Irc::version())); + connection.sendCommand(IrcCommand::createJoin("#clonk-tod")); + connection.sendCommand(IrcCommand::createMessage("#clonk-tod", "Hi, kthxbye!")); + connection.sendCommand(IrcCommand::createQuit()); + connection.open(); +//! [minimal] + + QObject::connect(&connection, SIGNAL(disconnected()), &app, SLOT(quit())); + return app.exec(); +} +#endif diff --git a/src/libcommuni/examples/minimal/minimal.pro b/src/libcommuni/examples/minimal/minimal.pro new file mode 100644 index 0000000..5dd8584 --- /dev/null +++ b/src/libcommuni/examples/minimal/minimal.pro @@ -0,0 +1,16 @@ +###################################################################### +# Communi +###################################################################### + +TEMPLATE = app +TARGET = minimal +DEPENDPATH += . +INCLUDEPATH += . +CONFIG += console +CONFIG -= app_bundle +QT = core network + +# Input +SOURCES += main.cpp + +include(../examples.pri) diff --git a/src/libcommuni/examples/qmlbot/main.cpp b/src/libcommuni/examples/qmlbot/main.cpp new file mode 100644 index 0000000..d74e909 --- /dev/null +++ b/src/libcommuni/examples/qmlbot/main.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#include <QCoreApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char* argv[]) +{ + QCoreApplication app(argc, argv); + + // enable debug output + qputenv("IRC_DEBUG", "1"); + + QQmlApplicationEngine engine(QUrl("qrc:/main.qml")); + return app.exec(); +} diff --git a/src/libcommuni/examples/qmlbot/qml/main.qml b/src/libcommuni/examples/qmlbot/qml/main.qml new file mode 100644 index 0000000..2dad458 --- /dev/null +++ b/src/libcommuni/examples/qmlbot/qml/main.qml @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQml 2.0 +import Communi 3.0 + +QtObject { + id: bot + + property string channel: "#communi" + + property Irc irc: Irc { id: irc } + property IrcCommand command: IrcCommand { id: command } + + property IrcBufferModel model: IrcBufferModel { + id: model + +//! [connection] + connection: IrcConnection { + id: connection + + host: "irc.freenode.net" + userName: "communi" + nickName: "QmlBot" + Math.round(Math.random() * 9999) + realName: qsTr("Communi %1 QML bot example").arg(irc.version()) + + Component.onCompleted: { + // queue a command to automatically join a channel when connected + sendCommand(command.createJoin(channel)) + open() + } +//! [connection] + +//! [receive] + onMessageReceived: { + if (message.type === IrcMessage.Private) { + // - in private, reply to the message sender + // - on channel, reply to the target channel + parser.target = message.private ? message.nick : message.target + + var command = parser.parse(message.content) + if (command) { + // send the command to the IRC server + sendCommand(command) + if (command.type === IrcCommand.Quit) { + // close the connection & quit the app when a !quit command was received + close() + Qt.quit() + } + } + } + } +//! [receive] + } + } + +//! [parser] + property IrcCommandParser parser: IrcCommandParser { + id: parser + + // keep the parser aware of the context + channels: model.channels + + // - on channel, respond to: "!<command> <params>" and "bot: <command> <params>" + // - in query, respond to: "!<command> <params>" and "<command> <params>" + triggers: connection.network.isChannel(target) ? ["!", connection.nickName + ":"] : ["!", ""] + + Component.onCompleted: { + // teach the bot some commands + parser.addCommand(IrcCommand.Nick, "NICK <nick>"); + parser.addCommand(IrcCommand.Join, "JOIN <#channel> (<key>)"); + parser.addCommand(IrcCommand.Part, "PART (<#channel>) (<message...>)"); + parser.addCommand(IrcCommand.Quit, "QUIT (<message...>)"); + parser.addCommand(IrcCommand.Message, "SAY [target] <message...>"); + } + } +//! [parser] +} diff --git a/src/libcommuni/examples/qmlbot/qmlbot.pro b/src/libcommuni/examples/qmlbot/qmlbot.pro new file mode 100644 index 0000000..95c2961 --- /dev/null +++ b/src/libcommuni/examples/qmlbot/qmlbot.pro @@ -0,0 +1,22 @@ +###################################################################### +# Communi +###################################################################### + +TEMPLATE = app +TARGET = qmlbot +DEPENDPATH += . +INCLUDEPATH += . +CONFIG += console +CONFIG -= app_bundle +QT = core network qml + +lessThan(QT_MAJOR_VERSION, 5)|lessThan(QT_MINOR_VERSION, 1) { + error(The QML2 based example requires Qt 5.1 or newer but Qt $$[QT_VERSION] was detected.) +} + +# Input +SOURCES += main.cpp +RESOURCES += qmlbot.qrc +OTHER_FILES += qml/main.qml + +include(../examples.pri) diff --git a/src/libcommuni/examples/qmlbot/qmlbot.qrc b/src/libcommuni/examples/qmlbot/qmlbot.qrc new file mode 100644 index 0000000..5f94096 --- /dev/null +++ b/src/libcommuni/examples/qmlbot/qmlbot.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file alias="main.qml">qml/main.qml</file> + </qresource> +</RCC> diff --git a/src/libcommuni/examples/quick/main.cpp b/src/libcommuni/examples/quick/main.cpp new file mode 100644 index 0000000..c503b98 --- /dev/null +++ b/src/libcommuni/examples/quick/main.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +#include <QGuiApplication> +#include <QQmlApplicationEngine> + +int main(int argc, char* argv[]) +{ + QGuiApplication app(argc, argv); + app.setOrganizationName("Commmuni"); + app.setApplicationName("QtQuick Example"); + QQmlApplicationEngine engine(QUrl("qrc:/main.qml")); + return app.exec(); +} diff --git a/src/libcommuni/examples/quick/qml/BufferListView.qml b/src/libcommuni/examples/quick/qml/BufferListView.qml new file mode 100644 index 0000000..3d277b6 --- /dev/null +++ b/src/libcommuni/examples/quick/qml/BufferListView.qml @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import Communi 3.0 + +Rectangle { + id: background + + property alias bufferModel: listView.model + property IrcBuffer currentBuffer + + signal closed(IrcBuffer buffer) + + color: "#edf3fe" + + Rectangle { + id: frame + anchors.fill: parent + anchors.topMargin: -1 + color: "transparent" + border.color: "#aaa" + } + + Menu { + id: menu + MenuItem { + text: qsTr("Close") + shortcut: qsTr("Ctrl+W") + enabled: !!currentBuffer + onTriggered: closed(currentBuffer) + } + } + + ScrollView { + id: scrollView + + anchors.fill: parent + anchors.topMargin: -1 + + ListView { + id: listView + + delegate: Rectangle { + property bool first: index === 0 + property bool current: model.buffer === currentBuffer + anchors.left: parent ? parent.left : undefined + anchors.right: parent ? parent.right : undefined + anchors.margins: 1 + height: Math.max(21, label.implicitHeight + 5) + color: first ? "#ddd" : current ? "#b5d5ff" : "transparent" + Rectangle { + visible: first + width: parent.width + height: 1 + anchors.bottom: parent.bottom + anchors.fill: parent + anchors.leftMargin: -1 + anchors.rightMargin: -1 + color: "transparent" + border.color: "#aaa" + } + Label { + id: label + text: model.title + font.bold: first + anchors.margins: 2 + anchors.leftMargin: 6 + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + onPressed: { + currentBuffer = model.buffer + if (mouse.button === Qt.RightButton) + menu.popup() + } + } + } + } + } +} diff --git a/src/libcommuni/examples/quick/qml/ChatPage.qml b/src/libcommuni/examples/quick/qml/ChatPage.qml new file mode 100644 index 0000000..474b950 --- /dev/null +++ b/src/libcommuni/examples/quick/qml/ChatPage.qml @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 +import Communi 3.0 + +Item { + id: page + + property IrcBuffer serverBuffer + property alias bufferModel: bufferListView.bufferModel + property alias currentBuffer: bufferListView.currentBuffer + property IrcChannel currentChannel: currentBuffer ? currentBuffer.toChannel() : null + + Connections { + target: bufferModel + onAdded: currentBuffer = buffer + onAboutToBeRemoved: { + var idx = bufferModel.indexOf(buffer) + currentBuffer = bufferModel.get(idx + 1) || bufferModel.get(Math.max(0, idx - 1)) + } + } + + SplitView { + anchors.fill: parent + + handleDelegate: Item { } + + BufferListView { + id: bufferListView + width: page.width / 6 + onClosed: { + if (buffer === serverBuffer) { + bufferModel.quit() + } else { + if (buffer.channel) + buffer.part(qsTr("Communi %1 QtQuick example").arg(irc.version())) + bufferModel.remove(buffer) + } + } + } + + Column { + Layout.fillWidth: true + + TopicLabel { + id: topicLabel + width: parent.width + visible: currentChannel + channel: currentChannel + } + + SplitView { + width: parent.width + height: parent.height - (currentChannel ? topicLabel.height : 0) - textEntry.height + + handleDelegate: Item { } + + Item { + id: stack + + width: 1; height: 1 + Layout.fillWidth: true + Layout.fillHeight: true + + Repeater { + anchors.fill: parent + model: bufferModel + delegate: TextBrowser { + anchors.fill: parent + buffer: model.buffer + visible: buffer == currentBuffer + } + } + } + + UserListView { + width: page.width / 6 + visible: currentChannel + channel: currentChannel + onQueried: currentBuffer = currentBuffer.model.add(user.name) + } + } + + TextEntry { + id: textEntry + width: parent.width + buffer: currentBuffer + enabled: currentBuffer + onMessageSent: currentBuffer.receiveMessage(message) + + Connections { + target: page + onCurrentBufferChanged: { + if (page.visible && currentBuffer) + textEntry.forceActiveFocus() + } + } + } + } + } +} diff --git a/src/libcommuni/examples/quick/qml/ConnectPage.qml b/src/libcommuni/examples/quick/qml/ConnectPage.qml new file mode 100644 index 0000000..8a7c296 --- /dev/null +++ b/src/libcommuni/examples/quick/qml/ConnectPage.qml @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import QtQuick.Layouts 1.0 +import QtQuick.Controls 1.0 +import Qt.labs.settings 1.0 +import Communi 3.0 + +Item { + id: page + + property string host: hostField.text || hostField.placeholderText + property int port: portField.value + property bool secure: secureField.checked + property bool sasl: saslField.checked + property string nickName: nickNameField.text || nickNameField.placeholderText + property string realName: realNameField.text || realNameField.placeholderText + property string userName: userNameField.text || userNameField.placeholderText + property string password: passwordField.text + property string channel: channelField.text || channelField.placeholderText + + Settings { + id: settings + property alias host: hostField.text + property alias port: portField.value + property alias secure: secureField.checked + property alias sasl: saslField.checked + property alias nickName: nickNameField.text + property alias realName: realNameField.text + property alias userName: userNameField.text + property alias password: passwordField.text + property alias channel: channelField.text + } + + signal accepted() + signal rejected() + + implicitWidth: grid.implicitWidth + row.implicitWidth + implicitHeight: grid.implicitHeight + row.implicitHeight + 48 + + GridLayout { + id: grid + + width: page.width * 2 / 3 + anchors.centerIn: parent + anchors.verticalCenterOffset: -row.height + 12 + + columns: 2 + rowSpacing: 12 + columnSpacing: 12 + + Label { text: qsTr("Host:") } + TextField { + id: hostField + focus: true + Layout.fillWidth: true + placeholderText: "irc.freenode.net" + } + + Label { text: qsTr("Port:") } + RowLayout { + SpinBox { + id: portField + value: 6667 + minimumValue: 1024 + maximumValue: 65535 + Layout.fillWidth: true + } + CheckBox { + id: secureField + text: qsTr("SSL") + } + CheckBox { + id: saslField + text: qsTr("SASL") + } + } + + Label { text: qsTr("Nick name:") } + TextField { + id: nickNameField + Layout.fillWidth: true + placeholderText: "Quick" + Math.round(Math.random() * 9999) + } + + Label { text: qsTr("Real name:") } + TextField { + id: realNameField + Layout.fillWidth: true + placeholderText: qsTr("Communi %1 QtQuick example").arg(irc.version()) + } + + Label { text: qsTr("User name:") } + TextField { + id: userNameField + Layout.fillWidth: true + placeholderText: "communi" + } + + Label { text: qsTr("Password:") } + TextField { + id: passwordField + echoMode: TextInput.Password + Layout.fillWidth: true + } + + Label { text: qsTr("Channel:") } + TextField { + id: channelField + Layout.fillWidth: true + placeholderText: "#communi" + } + } + + Keys.onReturnPressed: { + if (okButton.enabled) + page.accepted() + } + + Keys.onEnterPressed: { + if (okButton.enabled) + page.accepted() + } + + Keys.onEscapePressed: { + if (cancelButton.enabled) + page.rejected() + } + + RowLayout { + id: row + + anchors.margins: 12 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + + Item { Layout.fillWidth: true } + + Button { + id: okButton + text: qsTr("Ok") + enabled: page.visible + onClicked: page.accepted() + } + + Button { + id: cancelButton + text: qsTr("Cancel") + onClicked: page.rejected() + } + } +} diff --git a/src/libcommuni/examples/quick/qml/MessageFormatter.qml b/src/libcommuni/examples/quick/qml/MessageFormatter.qml new file mode 100644 index 0000000..754ca1c --- /dev/null +++ b/src/libcommuni/examples/quick/qml/MessageFormatter.qml @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import Communi 3.0 + +QtObject { + id: root + + property IrcTextFormat textFormat: IrcTextFormat { + id: textFormat + + palette.gray: "#606060" + palette.lightGray: "#808080" + + // http://ethanschoonover.com/solarized + palette.blue: "#268bd2" + palette.green: "#859900" + palette.red: "#dc322f" + palette.brown: "#cb4b16" + palette.purple: "#6c71c4" + palette.orange: "#cb4b16" + palette.yellow: "#b58900" + palette.lightGreen: "#859900" + palette.cyan: "#2aa198" + palette.lightCyan: "#2aa198" + palette.lightBlue: "#268bd2" + palette.pink: "#6c71c4" + } + + function formatMessage(message) { + var formatted + switch (message.type) { + case IrcMessage.Invite: formatted = formatInviteMessage(message); break + case IrcMessage.Join: formatted = formatJoinMessage(message); break + case IrcMessage.Kick: formatted = formatKickMessage(message); break + case IrcMessage.Mode: formatted = formatModeMessage(message); break + case IrcMessage.Names: formatted = formatNamesMessage(message); break + case IrcMessage.Nick: formatted = formatNickMessage(message); break + case IrcMessage.Notice: formatted = formatNoticeMessage(message); break + case IrcMessage.Numeric: formatted = formatNumericMessage(message); break + case IrcMessage.Part: formatted = formatPartMessage(message); break + case IrcMessage.Private: formatted = formatPrivateMessage(message); break + case IrcMessage.Quit: formatted = formatQuitMessage(message); break + case IrcMessage.Topic: formatted = formatTopicMessage(message); break + } + return formatText(formatted, message.timeStamp) + } + + function formatText(text, timeStamp) { + if (text) { + switch (text[0]) { + case '!': text = qsTr("<font color='gray'>%1</font>").arg(text); break; + case '[': text = qsTr("<font color='brown'>%1</font>").arg(text); break; + case '*': text = qsTr("<font color='darkmagenta'>%1</font>").arg(text); break; + case '?': text = qsTr("<font color='brown'>%1</font>").arg(text); break; + default: break; + } + + return qsTr("<font color='gray'>[%1]</font> %2").arg(Qt.formatTime(timeStamp, "hh:mm:ss")).arg(text) + } + } + + function formatInviteMessage(message) { + var sender = formatName(message.nick) + return qsTr("! %1 invited to %3").arg(sender).arg(message.channel) + } + + function formatJoinMessage(message) { + var sender = formatName(message.nick) + return qsTr("! %1 joined %2").arg(sender).arg(message.channel) + } + + function formatKickMessage(message) { + var sender = formatName(message.nick) + var user = formatName(message.user) + if (message.reason.length) + return qsTr("! %1 kicked %2 (%3)").arg(sender).arg(user).arg(message.reason) + return qsTr("! %1 kicked %2").arg(sender).arg(user) + } + + function formatModeMessage(message) { + var sender = formatName(message.nick) + if (message.reply) + return qsTr("! %1 mode is %2 %3").arg(message.target).arg(message.mode).arg(message.argument) + return qsTr("! %1 sets mode %2 %3").arg(sender).arg(message.mode).arg(message.argument) + } + + function formatNamesMessage(message) { + return qsTr("! %1 has %2 users").arg(message.channel).arg(message.names.length) + } + + function formatNickMessage(message) { + var sender = formatName(message.nick) + var nick = formatName(message.newNick) + return qsTr("! %1 changed nick to %2").arg(sender).arg(nick) + } + + function formatNoticeMessage(message) { + var sender = formatName(message.nick) + var content = formatHtml(message.content) + return qsTr("[%1] %2").arg(sender).arg(content) + } + + function formatNumericMessage(message) { + switch (message.code) { + case Irc.RPL_TOPIC: + case Irc.RPL_TOPICWHOTIME: + case Irc.RPL_CHANNEL_URL: + case Irc.RPL_NAMREPLY: + case Irc.RPL_ENDOFNAMES: + return // ignore + default: + return qsTr("[%1] %2").arg(message.code).arg(message.parameters.slice(1).join(" ")) + } + } + + function formatPartMessage(message) { + var sender = formatName(message.nick) + if (message.reason.length) + return qsTr("! %1 parted %2 (%3)").arg(sender).arg(message.channel).arg(formatHtml(message.reason)) + return qsTr("! %1 parted %2").arg(sender).arg(message.channel) + } + + function formatPrivateMessage(message) { + var sender = formatName(message.nick) + var content = formatHtml(message.content) + if (message.action) + return qsTr("* %1 %2").arg(sender).arg(content) + if (message.request) + return qsTr("! %1 requested %2").arg(sender).arg(content.split(" ")[0].toLowerCase()) + return qsTr("<%1> %2").arg(sender).arg(content) + } + + function formatQuitMessage(message) { + var sender = formatName(message.nick) + if (message.reason.length) + return qsTr("! %1 has quit (%2)").arg(sender).arg(formatHtml(message.reason)) + return qsTr("! %1 has quit").arg(sender) + } + + function formatTopicMessage(message) { + var sender = formatName(message.nick) + var topic = formatHtml(message.topic) + var channel = message.channel + if (message.reply) + return qsTr("! %1 topic is \"%2\"").arg(channel).arg(topic) + return qsTr("! %1 sets topic \"%2\" on %3").arg(sender).arg(topic).arg(channel) + } + + function formatHtml(message) { + return textFormat.toHtml(message) + } + + function formatName(name) { + var color = hslToRgb((hashCode(name) % 359)/359, 0.5, 0.25) + var r = ("0" + Math.round(Math.abs(color[0])).toString(16)).substr(-2) + var g = ("0" + Math.round(Math.abs(color[1])).toString(16)).substr(-2) + var b = ("0" + Math.round(Math.abs(color[2])).toString(16)).substr(-2) + return qsTr("<b><font color='#%1'>%2</font></b>").arg(r+g+b).arg(name); + } + + function hashCode(str) { + var hash = 0; + if (str.length == 0) return hash; + for (var i = 0; i < str.length; i++) { + var chr = str.charCodeAt(i); + hash = ((hash<<5)-hash)+chr; + hash = hash & hash; // Convert to 32bit integer + } + return hash; + } + + /** + * Converts an HSL color value to RGB. Conversion formula + * adapted from http://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + * + * @param Number h The hue + * @param Number s The saturation + * @param Number l The lightness + * @return Array The RGB representation + */ + function hslToRgb(h, s, l){ + var r, g, b; + + function hue2rgb(p, q, t){ + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6) return p + (q - p) * 6 * t; + if(t < 1/2) return q; + if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + if(s == 0){ + r = g = b = l; // achromatic + }else{ + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + + return [r * 255, g * 255, b * 255]; + } +} diff --git a/src/libcommuni/examples/quick/qml/TextBrowser.qml b/src/libcommuni/examples/quick/qml/TextBrowser.qml new file mode 100644 index 0000000..fb013bc --- /dev/null +++ b/src/libcommuni/examples/quick/qml/TextBrowser.qml @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import Communi 3.0 + +Item { + id: background + + property IrcBuffer buffer + + implicitWidth: textArea.implicitWidth + implicitHeight: textArea.implicitHeight + + MessageFormatter { + id: formatter + } + + Connections { + target: buffer + onMessageReceived: { + var line = formatter.formatMessage(message) + if (line) + textArea.append(line) + } + } + + TextArea { + id: textArea + + anchors.fill: parent + anchors.topMargin: -1 + anchors.leftMargin: -1 + anchors.bottomMargin: -1 + + readOnly: true + textFormat: Qt.RichText + frameVisible: false + } +} diff --git a/src/libcommuni/examples/quick/qml/TextEntry.qml b/src/libcommuni/examples/quick/qml/TextEntry.qml new file mode 100644 index 0000000..4201e86 --- /dev/null +++ b/src/libcommuni/examples/quick/qml/TextEntry.qml @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import QtQuick.Controls.Styles 1.0 +import Communi 3.1 + +TextField { + id: textField + + property alias buffer: completer.buffer + + signal messageSent(IrcMessage message) + + focus: true + placeholderText: qsTr("...") + + Keys.onTabPressed: completer.complete(text, cursorPosition) + + IrcCompleter { + id: completer + + onCompleted: { + textField.text = text + textField.cursorPosition = cursor + } + + parser: IrcCommandParser { + id: parser + + tolerant: true + triggers: ["/"] + channels: buffer ? buffer.model.channels : [] + target: buffer ? buffer.title : "" + + Component.onCompleted: { + parser.addCommand(IrcCommand.Join, "JOIN <#channel> (<key>)") + parser.addCommand(IrcCommand.CtcpAction, "ME [target] <message...>") + parser.addCommand(IrcCommand.Nick, "NICK <nick>") + parser.addCommand(IrcCommand.Part, "PART (<#channel>) (<message...>)") + } + } + } + + style: TextFieldStyle { + background: Rectangle { + color: palette.base + Rectangle { + color: "transparent" + anchors.fill: parent + anchors.leftMargin: -1 + border.color: "#aaa" + } + } + } + + onAccepted: { + var cmd = parser.parse(text) + if (cmd) { + buffer.connection.sendCommand(cmd) + if (cmd.type === IrcCommand.Message + || cmd.type === IrcCommand.CtcpAction + || cmd.type === IrcCommand.Notice) { + var msg = cmd.toMessage(buffer.connection.nickName, buffer.connection) + textField.messageSent(msg) + } + textField.text = "" + } + } +} diff --git a/src/libcommuni/examples/quick/qml/TopicLabel.qml b/src/libcommuni/examples/quick/qml/TopicLabel.qml new file mode 100644 index 0000000..4009304 --- /dev/null +++ b/src/libcommuni/examples/quick/qml/TopicLabel.qml @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import Communi 3.0 + +Rectangle { + id: background + + property IrcChannel channel + + color: "#ddd" + height: Math.max(20, label.implicitHeight + 4) + + IrcTextFormat { + id: textFormat + } + + Label { + id: label + + anchors.margins: 2 + anchors.leftMargin: 4 + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + wrapMode: Text.Wrap + text: channel && channel.topic ? textFormat.toHtml(channel.topic) : "-" + } + + Rectangle { + id: frame + + color: "transparent" + anchors.fill: parent + anchors.topMargin: -1 + anchors.leftMargin: -1 + border.color: "#aaa" + } +} diff --git a/src/libcommuni/examples/quick/qml/UserListView.qml b/src/libcommuni/examples/quick/qml/UserListView.qml new file mode 100644 index 0000000..6daf06b --- /dev/null +++ b/src/libcommuni/examples/quick/qml/UserListView.qml @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import Communi 3.0 + +Rectangle { + id: background + + property IrcChannel channel + + signal queried(IrcUser user) + + color: "#edf3fe" + + Rectangle { + id: frame + anchors.fill: parent + color: "transparent" + border.color: "#aaa" + anchors.topMargin: -1 + anchors.leftMargin: -1 + anchors.bottomMargin: -1 + } + + ScrollView { + id: scrollView + + anchors.fill: parent + anchors.topMargin: -1 + anchors.bottomMargin: -1 + + ListView { + id: listView + + model: IrcUserModel { + id: userModel + sortMethod: Irc.SortByTitle + channel: background.channel + onChannelChanged: listView.currentIndex = -1 + } + + delegate: Rectangle { + width: parent.width + height: Math.max(20, label.implicitHeight + 4) + color: ListView.isCurrentItem ? "#b5d5ff" : "transparent" + Label { + id: label + text: model.title + anchors.margins: 2 + anchors.leftMargin: 6 + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + MouseArea { + anchors.fill: parent + onPressed: listView.currentIndex = index + onDoubleClicked: queried(model.user) + } + } + } + } +} diff --git a/src/libcommuni/examples/quick/qml/main.qml b/src/libcommuni/examples/quick/qml/main.qml new file mode 100644 index 0000000..8c431ad --- /dev/null +++ b/src/libcommuni/examples/quick/qml/main.qml @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008-2014 The Communi Project + * + * This example is free, and not covered by the BSD license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. + */ + +import QtQuick 2.1 +import QtQuick.Controls 1.0 +import Communi 3.0 + +ApplicationWindow { + id: window + + visible: true + title: qsTr("Communi %1 QtQuick example").arg(irc.version()) + + width: 800 + height: 480 + + minimumWidth: connectPage.implicitWidth + minimumHeight: connectPage.implicitHeight + + color: Qt.darker(palette.base, 1.06) + + SystemPalette { id: palette } + + Irc { id: irc } + IrcCommand { id: cmd } + + ConnectPage { + id: connectPage + anchors.fill: parent + visible: !connection.active + onAccepted: { + chatPage.currentBuffer = serverBuffer + connection.sendCommand(cmd.createJoin(channel)) + connection.open() + } + onRejected: Qt.quit() + } + + ChatPage { + id: chatPage + anchors.fill: parent + visible: connection.active + bufferModel: IrcBufferModel { + id: bufferModel + sortMethod: Irc.SortByTitle + connection: IrcConnection { + id: connection + host: connectPage.host + port: connectPage.port + secure: connectPage.secure + saslMechanism: connectPage.sasl ? supportedSaslMechanisms[0] : "" + nickName: connectPage.nickName + realName: connectPage.realName + userName: connectPage.userName + password: connectPage.password + } + onMessageIgnored: serverBuffer.receiveMessage(message) + function quit() { + bufferModel.clear() + connection.quit(qsTr("Communi %1 QtQuick example").arg(irc.version())) + connection.close() + } + } + serverBuffer: IrcBuffer { + id: serverBuffer + sticky: true + persistent: true + name: connection.displayName + Component.onCompleted: bufferModel.add(serverBuffer) + } + } + + Component.onDestruction: bufferModel.quit() +} diff --git a/src/libcommuni/examples/quick/quick.pro b/src/libcommuni/examples/quick/quick.pro new file mode 100644 index 0000000..10690af --- /dev/null +++ b/src/libcommuni/examples/quick/quick.pro @@ -0,0 +1,28 @@ +###################################################################### +# Communi +###################################################################### + +TEMPLATE = app +TARGET = quick +DEPENDPATH += . +INCLUDEPATH += . +QT += network quick + +lessThan(QT_MAJOR_VERSION, 5)|lessThan(QT_MINOR_VERSION, 2) { + error(The Qt Quick 2 based example requires Qt 5.2 or newer but Qt $$[QT_VERSION] was detected.) +} + +# Input +SOURCES += main.cpp +RESOURCES += quick.qrc +OTHER_FILES += qml/main.qml +OTHER_FILES += qml/BufferListView.qml +OTHER_FILES += qml/ChatPage.qml +OTHER_FILES += qml/ConnectPage.qml +OTHER_FILES += qml/MessageFormatter.qml +OTHER_FILES += qml/TextBrowser.qml +OTHER_FILES += qml/TextEntry.qml +OTHER_FILES += qml/TopicLabel.qml +OTHER_FILES += qml/UserListView.qml + +include(../examples.pri) diff --git a/src/libcommuni/examples/quick/quick.qrc b/src/libcommuni/examples/quick/quick.qrc new file mode 100644 index 0000000..aba83b9 --- /dev/null +++ b/src/libcommuni/examples/quick/quick.qrc @@ -0,0 +1,13 @@ +<RCC> + <qresource prefix="/"> + <file alias="main.qml">qml/main.qml</file> + <file alias="BufferListView.qml">qml/BufferListView.qml</file> + <file alias="ChatPage.qml">qml/ChatPage.qml</file> + <file alias="ConnectPage.qml">qml/ConnectPage.qml</file> + <file alias="MessageFormatter.qml">qml/MessageFormatter.qml</file> + <file alias="TextBrowser.qml">qml/TextBrowser.qml</file> + <file alias="TextEntry.qml">qml/TextEntry.qml</file> + <file alias="TopicLabel.qml">qml/TopicLabel.qml</file> + <file alias="UserListView.qml">qml/UserListView.qml</file> + </qresource> +</RCC> |
