[polari] mainWindow: Delegate input processing to a new IrcParser class
- From: Florian Müllner <fmuellner src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [polari] mainWindow: Delegate input processing to a new IrcParser class
- Date: Thu, 8 Aug 2013 13:07:11 +0000 (UTC)
commit 8653c2206fab338e62d746d56d8eeccb0bf1a518
Author: Florian Müllner <fmuellner gnome org>
Date: Thu Aug 8 11:11:46 2013 +0200
mainWindow: Delegate input processing to a new IrcParser class
The new class is responsible to parse and handle IRC commands, or
just send the entered message in the normal case.
src/Makefile.am | 1 +
src/appNotifications.js | 49 ++++++++++
src/chatroomManager.js | 7 ++
src/ircParser.js | 228 +++++++++++++++++++++++++++++++++++++++++++++++
src/mainWindow.js | 6 +-
5 files changed, 289 insertions(+), 2 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 262a744..ca275d2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -32,6 +32,7 @@ dist_js_DATA = \
chatroomManager.js \
chatView.js \
connections.js \
+ ircParser.js \
main.js \
mainWindow.js \
roomList.js \
diff --git a/src/appNotifications.js b/src/appNotifications.js
index ea8c30b..7ca106b 100644
--- a/src/appNotifications.js
+++ b/src/appNotifications.js
@@ -42,6 +42,55 @@ const CommandOutputNotification = new Lang.Class({
}
});
+const SimpleOutput = new Lang.Class({
+ Name: 'SimpleOutput',
+ Extends: CommandOutputNotification,
+
+ _init: function(text) {
+ this.parent();
+
+ let label = new Gtk.Label({ label: text,
+ wrap: true,
+ wrap_mode: Pango.WrapMode.WORD,
+ vexpand: true,
+ visible: true });
+ this.widget.add(label);
+ this.widget.show_all();
+ }
+});
+
+const GridOutput = new Lang.Class({
+ Name: 'GridOutput',
+ Extends: CommandOutputNotification,
+
+ _init: function(header, items) {
+ this.parent();
+
+ let numItems = items.length;
+ let numCols = Math.min(numItems, 4);
+ let numRows = Number.toInteger(numItems / numCols) + numItems % numCols;
+
+ let grid = new Gtk.Grid({ column_homogeneous: true,
+ row_spacing: 6,
+ column_spacing: 18 });
+ grid.attach(new Gtk.Label({ label: header }), 0, 0, numCols, 1);
+
+ let row = 1;
+ for (let i = 0; i < numRows; i++) {
+ for (let j = 0; j < numCols; j++) {
+ let item = items[i + j * numRows];
+ if (!item)
+ continue;
+ let w = new Gtk.Label({ label: item });
+ grid.attach(w, j, row, 1, 1);
+ }
+ row++;
+ }
+ this.widget.add(grid);
+ this.widget.show_all();
+ }
+});
+
const NotificationQueue = new Lang.Class({
Name: 'NotificationQueue',
diff --git a/src/chatroomManager.js b/src/chatroomManager.js
index 6297b04..190ed6a 100644
--- a/src/chatroomManager.js
+++ b/src/chatroomManager.js
@@ -43,6 +43,13 @@ const _ChatroomManager = new Lang.Class({
return this._activeRoom;
},
+ getRoomByName: function(name) {
+ for (let id in this._rooms)
+ if (this._rooms[id].channel.identifier == name)
+ return this._rooms[id];
+ return null;
+ },
+
get roomCount() {
return Object.keys(this._rooms).length;
}
diff --git a/src/ircParser.js b/src/ircParser.js
new file mode 100644
index 0000000..df54850
--- /dev/null
+++ b/src/ircParser.js
@@ -0,0 +1,228 @@
+const Gdk = imports.gi.Gdk;
+const Gtk = imports.gi.Gtk;
+const Pango = imports.gi.Pango;
+const Tp = imports.gi.TelepathyGLib;
+
+const AppNotifications = imports.appNotifications;
+const ChatroomManager = imports.chatroomManager;
+const Lang = imports.lang;
+const Signals = imports.signals;
+
+const _ = imports.gettext.gettext;
+
+const knownCommands = {
+ /* commands that would be nice to support: */
+ /*
+ AWAY: _("/AWAY [<message>] - sets or unsets away message"),
+ INVITE: _("/INVITE <nick> [<channel>] - invites <nick> to <channel>, or the current one"),
+ KICK: _("/KICK <nick> - kicks <nick> from current channel"),
+ LIST: _("/LIST [<channel>] - lists stats on <channel>, or all channels on the server"),
+ MODE: "/MODE <mode> <nick|channel> - ",
+ NOTICE: _("/NOTICE <nick|channel> <message> - sends notice to <nick|channel>"),
+ OP: _("/OP <nick> - gives channel operator status to <nick>"),
+ QUERY: _("</QUERY <nick> - opens a private conversation with <nick>"),
+ TOPIC: _("/TOPIC <topic> - sets the topic to <topic>, or shows the current one"),
+ WHOIS: _("/WHOIS <nick> - requests information on <nick>"),
+ */
+
+ HELP: _("/HELP [<command>] - displays help for <command>, or a list of available commands"),
+ JOIN: _("/JOIN <channel> - joins <channel>"),
+ ME: _("/ME <action> - sends <action> to the current channel"),
+ NAMES: _("/NAMES - lists users on the current channel"),
+ NICK: _("/NICK <nickname> - sets your nick to <nickname>"),
+ PART: _("/PART [<channel>] [<reason>] - leaves <channel>, by default the current one"),
+ QUIT: _("</QUIT [<reason>] - disconnects from the current server"),
+ SAY: _("</SAY <text> - sends <text> to the current room/contact"),
+};
+const UNKNOWN_COMMAND_MESSAGE =
+ _("Unknown command - try /HELP for a list of available commands");
+
+const IrcParser = new Lang.Class({
+ Name: 'IrcParser',
+
+ _init: function() {
+ this._app = Gio.Application.get_default();
+ this._roomManager = ChatroomManager.getDefault();
+
+ this._roomManager.connect('active-changed', Lang.bind(this,
+ function(manager, room) {
+ this._room = room;
+ }));
+ this._room = null;
+ },
+
+ _createFeedbackLabel: function(text) {
+ return new AppNotifications.SimpleOutput(text);
+ },
+
+ _createFeedbackUsage: function(cmd) {
+ return this._createFeedbackLabel(_("Usage: %s").format(knownCommands[cmd]));
+ },
+
+ _createFeedbackGrid: function(header, items) {
+ return new AppNotifications.GridOutput(header, items);
+ },
+
+ process: function(text) {
+ if (!this._room || !this._room.channel || !text.length)
+ return;
+
+ if (text[0] != '/') {
+ let type = Tp.ChannelTextMessageType.NORMAL;
+ let message = Tp.ClientMessage.new_text(type, text);
+ this._sendMessage(message);
+ return;
+ }
+
+ let stripCommand = function(text) {
+ return text.substr(text.indexOf(' ')).trimLeft();
+ }
+
+ let argv = text.substr(1).split(/ +/);
+ let cmd = argv.shift().toUpperCase();
+ let output = null;
+ switch (cmd) {
+ case 'HELP': {
+ let command = argv.shift();
+ if (command)
+ command = command.toUpperCase();
+
+ let help;
+ if (command && knownCommands[command])
+ output = this._createFeedbackUsage(command);
+ else if (command)
+ output = this._createFeedbackLabel(UNKNOWN_COMMAND_MESSAGE);
+ else
+ output = this._createFeedbackGrid(_("Known commands:"),
+ Object.keys(knownCommands));
+
+ break;
+ }
+ case 'JOIN': {
+ let room = argv.shift();
+ if (!room) {
+ output = this._createFeedbackUsage(cmd);
+ break;
+ }
+ if (argv.length)
+ log('Excess arguments to JOIN command: ' + argv);
+
+ let time = Gdk.CURRENT_TIME;
+ let account = this._room.channel.connection.get_account();
+ let req = Tp.AccountChannelRequest.new_text(account, time);
+
+ let preferredHandler = Tp.CLIENT_BUS_NAME_BASE + 'Polari';
+ req.set_target_id(Tp.HandleType.ROOM, room);
+ req.set_delegate_to_preferred_handler(true);
+ req.ensure_channel_async(preferredHandler, null, Lang.bind(this,
+ function(req, res) {
+ try {
+ req.ensure_channel_finish(res);
+ } catch(e) {
+ logError(e, 'Failed to join channel');
+ }
+ }));
+ break;
+ }
+ case 'ME': {
+ if (!argv.length) {
+ output = this._createFeedbackUsage(cmd);
+ break;
+ }
+ let action = stripCommand(text);
+ let type = Tp.ChannelTextMessageType.ACTION;
+ let message = Tp.ClientMessage.new_text(type, action);
+ this._sendMessage(message);
+ break;
+ }
+ case 'NAMES': {
+ break;
+ }
+ case 'NICK': {
+ let nick = argv.shift();
+ if (!nick) {
+ output = this._createFeedbackUsage(cmd);
+ break;
+ }
+ if (argv.length)
+ log('Excess arguments to NICK command: ' + argv);
+
+ let account = this._room.channel.connection.get_account();
+ account.set_nickname_async(nick, Lang.bind(this,
+ function(a, res) {
+ try {
+ a.set_nickname_finish(res);
+ } catch(e) {
+ logError(e, 'Failed to update nick');
+ }
+ }));
+ break;
+ }
+ case 'PART': {
+ let room = null;;
+ let name = argv[0];
+ if (name)
+ room = this._roomManager.getRoomByName(name);
+ if (room)
+ argv.shift(); // first arg was a room name
+ else
+ room = this._room;
+ let reason = Tp.ChannelGroupChangeReason.NONE;
+ let message = argv.join(' ') || '';
+ room.channel.leave_async(reason, message, Lang.bind(this,
+ function(c, res) {
+ try {
+ c.leave_finish(res);
+ } catch(e) {
+ logError(e, 'Failed to leave channel');
+ }
+ }));
+ break;
+ }
+ case 'QUIT': {
+ let account = this._room.channel.connection.get_account();
+ let presence = Tp.ConnectionPresenceType.OFFLINE;
+ let message = stripCommand(text);
+ account.request_presence_async(presence, '', message,
+ Lang.bind(this, function(a, res) {
+ try {
+ a.request_presence_finish(res);
+ } catch(e) {
+ logError(e, 'Failed to disconnect');
+ }
+ }));
+ break;
+ }
+ case 'SAY': {
+ if (!argv.length) {
+ output = this._createFeedbackUsage(cmd);
+ break;
+ }
+ let raw = stripCommand(text);
+ let type = Tp.ChannelTextMessageType.NORMAL;
+ let message = Tp.ClientMessage.new_text(type, raw);
+ this._sendMessage(message);
+ break;
+ }
+ default:
+ output = this._createFeedbackLabel(UNKNOWN_COMMAND_MESSAGE);
+ break;
+ }
+
+ if (output)
+ this._app.commandOutputQueue.addNotification(output);
+ },
+
+ _sendMessage: function(message) {
+ this._room.channel.send_message_async(message, 0, Lang.bind(this,
+ function(c, res) {
+ try {
+ c.send_message_finish(res);
+ } catch(e) {
+ // TODO: propagate to user
+ logError(e, 'Failed to send message')
+ }
+ }));
+ }
+});
+Signals.addSignalMethods(IrcParser.prototype);
diff --git a/src/mainWindow.js b/src/mainWindow.js
index c1ec257..ddf9489 100644
--- a/src/mainWindow.js
+++ b/src/mainWindow.js
@@ -4,6 +4,7 @@ const Gtk = imports.gi.Gtk;
const AppNotifications = imports.appNotifications;
const ChatroomManager = imports.chatroomManager;
const ChatView = imports.chatView;
+const IrcParser = imports.ircParser;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const RoomList = imports.roomList;
@@ -30,6 +31,8 @@ const MainWindow = new Lang.Class({
overlay.add_overlay(app.notificationQueue.widget);
overlay.add_overlay(app.commandOutputQueue.widget);
+ this._ircParser = new IrcParser.IrcParser();
+
this._roomManager = new ChatroomManager.getDefault();
this._roomManager.connect('room-added',
Lang.bind(this, this._roomAdded));
@@ -73,8 +76,7 @@ const MainWindow = new Lang.Class({
this._sendButton.connect('clicked', Lang.bind(this,
function() {
- if (this._entry.text)
- this._room.send(this._entry.text);
+ this._ircParser.process(this._entry.text);
this._entry.text = '';
}));
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]