Merge lp:~julien-spautz/cable/pm into lp:cable

Proposed by Julien Spautz
Status: Merged
Merged at revision: 139
Proposed branch: lp:~julien-spautz/cable/pm
Merge into: lp:cable
Diff against target: 1358 lines (+509/-388)
16 files modified
CMakeLists.txt (+2/-0)
src/Controller/Channel.vala (+18/-17)
src/Controller/PrivateChat.vala (+85/-0)
src/Controller/Server.vala (+32/-12)
src/Controller/Window.vala (+28/-38)
src/Utils/HostMask.vala (+31/-0)
src/View/Channel.vala (+22/-22)
src/View/Server.vala (+21/-54)
src/View/Window.vala (+2/-2)
src/Widgets/Channel.vala (+68/-61)
src/Widgets/ChatDisplay.vala (+1/-24)
src/Widgets/Room.vala (+38/-118)
src/Widgets/Server.vala (+26/-35)
src/Widgets/User.vala (+8/-0)
src/Widgets/UserList.vala (+122/-0)
src/Widgets/Window.vala (+5/-5)
To merge this branch: bzr merge lp:~julien-spautz/cable/pm
Reviewer Review Type Date Requested Status
Julien Spautz Approve
Review via email: mp+186069@code.launchpad.net

Description of the change

This branch implements basic private chat features.

A chat will be opened when you receive a /msg or when you double click on a name in the user list.

To post a comment you must log in.
Revision history for this message
Julien Spautz (julien-spautz) wrote :

Now open for review.

Revision history for this message
982c80311320c1b (alexander-wilms) wrote :

Compiles and works as expected

Revision history for this message
Daniele "OpenNingia" Simonetti (oppifjellet) wrote :

Compiles and works with some quirks:

* You can have a private chat with yourself that is quite confusing.
* IRC commands ( e.g. /join ) don't work in the private chat window.

lp:~julien-spautz/cable/pm updated
146. By Julien Spautz

don't allow talking to yourself (you weirdo)

Revision history for this message
Julien Spautz (julien-spautz) wrote :

Private chats with yourself have been fixed.

IRC commands will need some more code shifting, but I don't want to unnecessarily bloat the diff, so that's for another merge.

This branch should allow basic private chat functionality including opening chats by double clicking a name and when receiving a /msg.

Revision history for this message
Julien Spautz (julien-spautz) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2013-09-14 18:12:05 +0000
3+++ CMakeLists.txt 2013-09-22 16:58:59 +0000
4@@ -69,6 +69,7 @@
5 src/Controller/Window.vala
6 src/Controller/Server.vala
7 src/Controller/Channel.vala
8+ src/Controller/PrivateChat.vala
9
10 src/View/Server.vala
11 src/View/Channel.vala
12@@ -89,6 +90,7 @@
13 src/Widgets/Room.vala
14 src/Widgets/ChatDisplay.vala
15 src/Widgets/User.vala
16+ src/Widgets/UserList.vala
17 src/Widgets/TopicBar.vala
18
19 src/Dialogs/JoinChannel.vala
20
21=== modified file 'src/Controller/Channel.vala'
22--- src/Controller/Channel.vala 2013-09-14 12:55:26 +0000
23+++ src/Controller/Channel.vala 2013-09-22 16:58:59 +0000
24@@ -17,24 +17,22 @@
25 Boston, MA 02110-1301 USA.
26 ***/
27
28-internal class Cable.Controller.Channel : GLib.Object {
29-
30- internal weak Server server;
31- internal View.Channel view;
32- internal Irc.Channel backend;
33-
34- internal string name; //TODO id
35-
36- internal Channel (Server server, string name) {
37- debug (@"+ CONTROLLER CHANNEL $name");
38-
39+public class Cable.Controller.Channel : GLib.Object {
40+
41+ public weak Server server;
42+ public View.Channel view;
43+ public Irc.Channel backend;
44+
45+ public string name; //TODO id
46+
47+ public Channel (Server server, string name) {
48 this.server = server;
49 this.name = name;
50
51- if (server.view.has_channel (name) == false)
52+ if (server.view.has_chat (name) == false)
53 server.view.add_channel (name, name);
54
55- view = server.view.get_channel_by_id (name);
56+ view = server.view.get_chat (name) as View.Channel;
57 view.nick = server.nick;
58 backend = new Irc.Channel (name, server.backend);
59
60@@ -44,8 +42,6 @@
61 load_state ();
62 }
63
64- ~Channel () { debug (@"- CONTROLLER CHANNEL $name"); }
65-
66 void load_state () {
67 /*if (backend.joined) {
68 view.room.state = Widgets.Room.State.CHAT;
69@@ -102,12 +98,17 @@
70 return false;
71 });
72
73+ view.private_chat_requested.connect ((name) => {
74+ server.open_new_chat_with (name);
75+ if (server.private_chats.has_key (name))
76+ server.window.view.current_chat = server.private_chats [name].view;
77+ });
78 }
79
80 // IRC signals
81 void connect_backend_signals () {
82 backend.event_message.connect ((from, message) => {
83- if (server.window.view.current_channel != view)
84+ if (server.window.view.current_chat != view)
85 view.unread_messages++;
86 });
87
88@@ -207,7 +208,7 @@
89 }
90 var channel = new Channel (server, channel_with_hash);
91 server.channels[channel_with_hash] = channel;
92- server.window.view.current_channel = channel.view;
93+ server.window.view.current_chat = channel.view;
94 break;
95
96 case "part":
97
98=== added file 'src/Controller/PrivateChat.vala'
99--- src/Controller/PrivateChat.vala 1970-01-01 00:00:00 +0000
100+++ src/Controller/PrivateChat.vala 2013-09-22 16:58:59 +0000
101@@ -0,0 +1,85 @@
102+/***
103+ Copyright (C) 2013 Cable Developers
104+
105+ This program or library is free software; you can redistribute it
106+ and/or modify it under the terms of the GNU Lesser General Public
107+ License as published by the Free Software Foundation; either
108+ version 3 of the License, or (at your option) any later version.
109+
110+ This library is distributed in the hope that it will be useful,
111+ but WITHOUT ANY WARRANTY; without even the implied warranty of
112+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
113+ Lesser General Public License for more details.
114+
115+ You should have received a copy of the GNU Lesser General
116+ Public License along with this library; if not, write to the
117+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
118+ Boston, MA 02110-1301 USA.
119+***/
120+
121+public class Cable.Controller.PrivateChat : GLib.Object {
122+
123+ public weak Server server;
124+ public View.PrivateChat view;
125+ string id;
126+
127+ public PrivateChat (Server server, string id, string? message = null) {
128+ this.id = id;
129+ this.server = server;
130+
131+ server.view.add_private_chat (id, id);
132+ view = server.view.get_chat (id) as View.PrivateChat;
133+ view.nick = server.nick;
134+
135+ if (message != null)
136+ on_backend_message (id, server.nick, message);
137+
138+ connect_signals ();
139+ }
140+
141+ void connect_signals () {
142+ server.backend.event_message.connect (on_backend_message);
143+ view.send_message.connect (on_frontend_message);
144+ server.window.view.selected.connect (on_chat_selected);
145+ }
146+
147+ void on_backend_message (string from, string target, string message) {
148+ if (is_not_me (target))
149+ return;
150+
151+ if (is_not_correspondent (from))
152+ return;
153+
154+ var nick_from = from.split ("!")[0];
155+
156+ view.message (nick_from, message);
157+
158+ if (view.has_focus == false) {
159+ Services.Notify.send (nick_from, message);
160+ Services.Launcher.jobs++;
161+ Services.Launcher.urgent = true;
162+ }
163+
164+ if (server.window.view.current_chat != view)
165+ view.unread_messages++;
166+ }
167+
168+ void on_frontend_message (string message) {
169+ server.backend.send_message (id.split("!")[0], message);
170+ view.message (server.nick, message);
171+ view.entry = "";
172+ }
173+
174+ void on_chat_selected (View.Chat chat) {
175+ if (chat == view)
176+ view.unread_messages = 0;
177+ }
178+
179+ bool is_not_me (string nick) {
180+ return nick != server.nick;
181+ }
182+
183+ bool is_not_correspondent (string from) {
184+ return from.split ("!")[0] != id;
185+ }
186+}
187
188=== modified file 'src/Controller/Server.vala'
189--- src/Controller/Server.vala 2013-08-25 18:02:19 +0000
190+++ src/Controller/Server.vala 2013-09-22 16:58:59 +0000
191@@ -17,22 +17,25 @@
192 Boston, MA 02110-1301 USA.
193 ***/
194
195-internal class Cable.Controller.Server : GLib.Object {
196+public class Cable.Controller.Server : GLib.Object {
197
198- internal weak Window window;
199- internal View.Server view;
200- internal Irc.Server backend;
201+ public weak Window window;
202+ public View.Server view;
203+ public Irc.Server backend;
204
205- internal string nick; // FIXME not reliable
206- internal string address; // FIXME not reliable
207- internal string name;
208+ public string nick; // FIXME not reliable
209+ public string address; // FIXME not reliable
210+ public string name;
211
212- internal Gee.HashMap <string, Channel> channels {
213+ public Gee.HashMap <string, Channel> channels {
214 get; set; default = new Gee.HashMap <string, Channel> ();
215 }
216+
217+ public Gee.HashMap <string, PrivateChat> private_chats {
218+ get; set; default = new Gee.HashMap <string, PrivateChat> ();
219+ }
220
221- internal Server (string address, string nick, Window window) {
222- debug (@"+ CONTROLLER SERVER $address");
223+ public Server (string address, string nick, Window window) {
224 this.backend = Irc.Server.get_or_create (address, nick);
225
226 this.name = backend.name;
227@@ -49,8 +52,6 @@
228 connect_backend_signals ();
229 }
230
231- ~Server () { debug (@"- CONTROLLER SERVER $address"); }
232-
233 void connect_frontend_signals () {
234 view.toggle_away.connect (() => {
235 if (backend.away) {
236@@ -85,5 +86,24 @@
237 foreach (var channel in channels.values)
238 channel.view.remove_user (nick, message);
239 });
240+
241+ backend.event_message.connect ((from, target, message) => {
242+ if (target != nick)
243+ return;
244+
245+ var from_nick = from.split ("!")[0];
246+
247+ open_new_chat_with (from_nick, message);
248+ });
249+ }
250+
251+ public void open_new_chat_with (string from_nick, string message = "") {
252+ if (from_nick == backend.nick)
253+ return;
254+
255+ if (!private_chats.has_key (from_nick)) {
256+ var chat = new PrivateChat (this, from_nick, message);
257+ private_chats [from_nick] = chat;
258+ }
259 }
260 }
261
262=== modified file 'src/Controller/Window.vala'
263--- src/Controller/Window.vala 2013-09-12 20:26:19 +0000
264+++ src/Controller/Window.vala 2013-09-22 16:58:59 +0000
265@@ -17,16 +17,16 @@
266 Boston, MA 02110-1301 USA.
267 ***/
268
269-internal class Cable.Controller.Window : GLib.Object {
270-
271- internal View.Window view { get; set; }
272-
273- internal Gee.HashMap <string, Server> servers {
274+public class Cable.Controller.Window : GLib.Object {
275+
276+ public View.Window view { get; set; }
277+
278+ public Gee.HashMap <string, Server> servers {
279 get; set;
280 default = new Gee.HashMap <string, Server> ();
281 }
282
283- internal Window (View.Window window) {
284+ public Window (View.Window window) {
285 GLib.Object (view: window);
286
287 connect_frontend_signals ();
288@@ -61,11 +61,11 @@
289 // backend events that affect the window's state
290 void connect_backend_signals () {
291 Irc.client.event_nick.connect ((time, server, from, nick) => {
292- var current_channel = view.current_channel;
293+ var current_chat = view.current_chat;
294 var current_server = view.current_server;
295
296 if (server == current_server.id)
297- view.title = string.join (" — ", current_channel.name, nick);
298+ view.title = string.join (" — ", current_chat.name, nick);
299 });
300 }
301
302@@ -84,13 +84,26 @@
303 });
304
305 view.remove_channel.connect (() => {
306- var channel_id = view.current_channel.id;
307+ var channel_id = view.current_chat.id;
308 var server_id = view.current_server.id;
309
310- if (view.current_channel != null) {
311+ if (view.current_chat == null) {
312+ warning ("No channel selected, cannot leave.");
313+ } else if (view.current_chat is View.Channel) {
314 part_channel (servers[server_id].channels[channel_id]);
315- } else {
316- warning ("No channel selected, cannot leave.");
317+ } else if (view.current_chat is View.PrivateChat) {
318+ var channel = servers[server_id].private_chats[channel_id];
319+ var server = channel.server;
320+ server.view.remove_chat (channel.view.id);
321+ server.private_chats.unset (channel.view.id);
322+
323+ if (server.channels.size == 0)
324+ servers.unset (server.view.id);
325+
326+ if (servers.size == 0) {
327+ view.title = App.TITLE;
328+ view.welcome_shown = true;
329+ }
330 }
331 });
332
333@@ -130,7 +143,7 @@
334 servers[server.backend.name] = server;
335 }
336
337- if (server.view.has_channel (channel_name)) {
338+ if (server.view.has_chat (channel_name)) {
339 warning ("Channel is already joined");
340 return;
341 }
342@@ -138,7 +151,7 @@
343 var channel = new Channel (server, channel_name);
344 server.channels[channel_name] = channel;
345
346- view.current_channel = channel.view;
347+ view.current_chat = channel.view;
348 view.welcome_shown = false;
349 }
350
351@@ -146,7 +159,7 @@
352 var server = channel.server;
353
354 channel.backend.send_part ();
355- server.view.remove_channel (channel.view.id);
356+ server.view.remove_chat (channel.view.id);
357 server.channels.unset (channel.view.id);
358
359 if (server.channels.size == 0)
360@@ -157,27 +170,4 @@
361 view.welcome_shown = true;
362 }
363 }
364-
365- /*void leave_channel (Settings.Network network, Settings.Identity identity, string channel_name) {
366- var server_backend = Irc.Server.search (network.address, identity.nick_name);
367-
368- var server_view = servers[server_backend.name].view;
369-
370- if (server_view == null) {
371- warning ("Cannot leave channel: server does not exist (%s - %s)", network.address, identity.nick_name);
372- return;
373- }
374- if (!server_view.has_channel (channel_name)) {
375- warning ("Cannot leave channel: channel not joined (%s)", channel_name);
376- return;
377- }
378-
379- server_view.remove_channel (channel_name);
380- server_backend.send_part (channel_name);
381-
382- if (view.n_servers == 0) {
383- view.title = App.TITLE;
384- view.welcome_shown = true;
385- }
386- }*/
387 }
388
389=== added file 'src/Utils/HostMask.vala'
390--- src/Utils/HostMask.vala 1970-01-01 00:00:00 +0000
391+++ src/Utils/HostMask.vala 2013-09-22 16:58:59 +0000
392@@ -0,0 +1,31 @@
393+// parses a hostmask in form of <nick>!<user>@<host>
394+/*public class Cable.Utils.HostMask : GLib.Object { // TODO use this
395+
396+ string nick { get; private set; }
397+ string user { get; private set; }
398+ string host { get; private set; }
399+
400+ public HostMask (string hostmask)
401+ requires (is_well_formed (hostmask)) {
402+
403+ var first_split = hostmask.split ("!", 2);
404+ var user_and_host = first_split[1];
405+ var second_split = user_and_host.split ("@", 2);
406+
407+ this.nick = first_split[0];
408+ this.user = second_split[0];
409+ this.host = second_split[1];
410+ }
411+
412+ bool is_well_formed (string hostmask) {
413+ if ("!" in hostmask == false)
414+ return false;
415+
416+ var remainder = hostmask.split ("!", 2)[-1];
417+
418+ if ("@" in remainder == false);
419+ return false;
420+
421+ return true;
422+ }
423+}*/
424\ No newline at end of file
425
426=== modified file 'src/View/Channel.vala'
427--- src/View/Channel.vala 2013-08-16 15:31:17 +0000
428+++ src/View/Channel.vala 2013-09-22 16:58:59 +0000
429@@ -17,42 +17,42 @@
430 Boston, MA 02110-1301 USA.
431 ***/
432
433-public interface Cable.View.Channel : GLib.Object {
434+public interface Cable.View.Chat : GLib.Object {
435
436 public abstract int unread_messages { get; set; }
437-
438 public abstract string nick { get; set; }
439-
440 public abstract string name { get; set; }
441-
442- public abstract State state { get; set; }
443-
444- public abstract string topic { get; set; }
445-
446 public abstract string id { get; set; }
447-
448 public abstract string entry { get; set; }
449-
450 public abstract bool has_focus { get; }
451+
452+ public signal void send_message (string message);
453+ public signal bool focused ();
454+
455+ public abstract void rename_user (string? old_nick, string new_nick, UserType type, bool silent = false);
456+ public abstract void set_away_status (string nick, bool is_away);
457+ public abstract void message (owned string from, string message, MessageType type = MessageType.NORMAL);
458+}
459+
460+public interface Cable.View.Channel : GLib.Object, View.Chat {
461+
462+ public abstract State state { get; set; }
463+ public abstract string topic { get; set; }
464
465 public signal void join ();
466-
467 public signal void part ();
468-
469 public signal void send_message (string message);
470-
471 public signal bool focused ();
472+ public signal void private_chat_requested (string name);
473
474 public abstract bool has_user (string nick);
475-
476 public abstract void list_users (string[] nicks, string[] prefixes);
477-
478 public abstract void add_user (string nick, UserType type, bool silent = false);
479-
480- public abstract bool remove_user (string nick, string message, bool silent = false);
481-
482- public abstract void rename_user (string? old_nick, string new_nick, UserType type, bool silent = false);
483- public abstract void set_away_status (string nick, bool is_away);
484-
485- public abstract void message (owned string from, string message, MessageType type = MessageType.NORMAL);
486+ public abstract void remove_user (string nick, string message, bool silent = false);
487+}
488+
489+public interface Cable.View.PrivateChat : GLib.Object, View.Chat {
490+
491+ public signal void send_message (string message);
492+ public signal bool focused ();
493 }
494
495=== modified file 'src/View/Server.vala'
496--- src/View/Server.vala 2013-08-16 15:31:17 +0000
497+++ src/View/Server.vala 2013-09-22 16:58:59 +0000
498@@ -19,70 +19,37 @@
499
500 public interface Cable.View.Server : GLib.Object {
501
502- // TODO parent-tier reference (i.e. window tier)
503-
504- /**
505- * Change the away status of this server. Set-only.
506- */
507 public abstract bool away { set; }
508-
509 public abstract string name { get; set; }
510-
511 public abstract string id { get; set; }
512
513- /**
514- * List of all channels of this server.
515- */
516- public abstract Gee.ArrayList <Channel> channels { owned get; }
517-
518- public abstract signal void toggle_away ();
519-
520- public abstract signal void part_all ();
521-
522- /**
523- * Create and add a new channel to this server.
524- * @param channel_name Name of the new channel.
525- */
526- public abstract void add_channel (string channel_name, string channel_id);
527-
528- /**
529- * Remove a channel from this server.
530- * @param channel_name Name of the channel to be removed.
531- */
532- public abstract void remove_channel (string channel_id);
533-
534- /**
535- * Names of all owned channels.
536- */
537- public string[] channel_ids {
538+ public abstract Gee.ArrayList <Chat> chats { owned get; }
539+
540+ public string[] chat_ids {
541 owned get {
542 string[] result = {};
543- foreach (var channel in channels)
544- result += channel.id;
545+ foreach (var chat in chats)
546+ result += chat.id;
547 return result;
548 }
549 }
550
551- /**
552- * Check whether this server has a given channel.
553- * @param channel_name Name of the channel you are looking for.
554- * @return True if the channel exists, else false.
555- */
556- public bool has_channel (string channel_id) {
557- return channel_id in channel_ids;
558- }
559+ public abstract signal void toggle_away ();
560+ public abstract signal void part_all ();
561+
562+ public abstract void add_channel (string chat_name, string chat_id);
563+ public abstract void add_private_chat (string chat_name, string chat_id);
564+ public abstract void remove_chat (string chat_id);
565
566- /**
567- * Retrieve a channel by its name.
568- *
569- * @param channel_name Name of the channel you are looking for.
570- * @return The channel or null if not found.
571- */
572- public Channel? get_channel_by_id (string channel_id) {
573- foreach (var channel in channels)
574- if (channel.id == channel_id)
575- return channel;
576-
577- return (Channel) null;
578+ public bool has_chat (string chat_id) {
579+ return chat_id in chat_ids;
580+ }
581+
582+ public Chat? get_chat (string chat_id) {
583+ foreach (var chat in chats)
584+ if (chat.id == chat_id)
585+ return chat;
586+
587+ return (Chat) null;
588 }
589 }
590
591=== modified file 'src/View/Window.vala'
592--- src/View/Window.vala 2013-08-16 15:31:17 +0000
593+++ src/View/Window.vala 2013-09-22 16:58:59 +0000
594@@ -46,7 +46,7 @@
595 /**
596 * Get the currently selected channel.
597 */
598- public abstract Channel? current_channel { get; set; }
599+ public abstract Chat? current_chat { get; set; }
600
601 /**
602 * Amount of opened servers.
603@@ -65,7 +65,7 @@
604
605 public signal void add_channel ();
606
607- public signal void selected (Channel channel);
608+ public signal void selected (Chat chat);
609
610 public signal void show_preferences ();
611
612
613=== modified file 'src/Widgets/Channel.vala'
614--- src/Widgets/Channel.vala 2013-08-12 22:09:44 +0000
615+++ src/Widgets/Channel.vala 2013-09-22 16:58:59 +0000
616@@ -17,62 +17,84 @@
617 Boston, MA 02110-1301 USA.
618 ***/
619
620-/**
621- * An implementation of the View.Channel interface.
622- */
623-public class Cable.Widgets.Channel : Granite.Widgets.SourceList.Item, View.Channel {
624-
625- Gtk.Menu menu;
626- Gtk.MenuItem leave_menu_item;
627- internal Room room;
628-
629- /**
630- * Amount of unread messages. Displayed in a badge.
631- */
632+public class Cable.Widgets.Chat : Granite.Widgets.SourceList.Item {
633+
634+ public Room room;
635+
636+ public string nick { get; set; }
637+ public string id { get; set; }
638+ public string entry { get; set; }
639+
640 public int unread_messages {
641 get { return int.parse (badge); }
642 set { badge = (value <= 0) ? "" : value.to_string (); }
643 }
644-
645+
646+ public bool has_focus {
647+ get { return room.text_entry.has_focus; }
648+ }
649+
650+ public void rename_user (string? old_name, string new_name, UserType type, bool silent = false) {
651+ room.rename_user (old_name, new_name, type, silent);
652+ }
653+
654+ public void set_away_status (string nick, bool away) {
655+ room.chat.message_away (nick, away);
656+ }
657+
658+ public void message (owned string from, string message, MessageType type = MessageType.NORMAL) {
659+ room.chat.add_message (from, message, type);
660+ }
661+}
662+
663+public class Cable.Widgets.PrivateChat : Widgets.Chat, View.Chat, View.PrivateChat {
664+ public PrivateChat (string name, string id) {
665+ GLib.Object (name: name, id: id);
666+ this.room = new Room (name);
667+
668+ room.send_message.connect ((msg) => send_message (msg));
669+ room.text_entry.grab_focus.connect (() => { focused (); });
670+
671+ // bind properties
672+ bind_property ("nick", room, "nick", GLib.BindingFlags.BIDIRECTIONAL);
673+ bind_property ("name", room, "name", GLib.BindingFlags.BIDIRECTIONAL);
674+ bind_property ("entry", room, "entry", GLib.BindingFlags.BIDIRECTIONAL);
675+ }
676+}
677+
678+public class Cable.Widgets.Channel : Widgets.Chat, View.Chat, View.Channel {
679+
680+ Gtk.Menu menu;
681+ Gtk.MenuItem leave_menu_item;
682+
683 public State state { get; set; }
684-
685- public string nick { get; set; }
686-
687 public string topic { get; set; }
688
689- public string id { get; set; }
690-
691- public string entry { get; set; }
692-
693- public bool has_focus {
694- get { return room.text_entry.has_focus; }
695- }
696-
697- /*
698- signal void autocomplete (int pos)
699- */
700-
701- /**
702- * Create a new channel item for the source list.
703- * @param channel_name Displayed name and id.
704- */
705- internal Channel (string name, string id) {
706- debug (@"+ VIEW CHANNEL $name");
707-
708+ public Channel (string name, string id) {
709 GLib.Object (name: name, id: id);
710 this.room = new Room (name);
711-
712+
713 this.menu = new Gtk.Menu ();
714 this.leave_menu_item = new Gtk.MenuItem.with_label (_("Leave Channel"));
715 menu.append (leave_menu_item);
716 menu.show_all ();
717-
718+
719 // setup signals
720- leave_menu_item.activate.connect (() => part ());
721- room.send_message.connect ((msg) => send_message (msg));
722- room.rejoin.connect (() => join ());
723- room.text_entry.grab_focus.connect (() => { focused (); });
724+ leave_menu_item.activate.connect (()
725+ => part ());
726+
727+ room.send_message.connect ((msg)
728+ => send_message (msg));
729+
730+ room.rejoin.connect (()
731+ => join ());
732+
733+ room.text_entry.grab_focus.connect (()
734+ => { focused (); });
735
736+ room.user_list.private_chat_requested.connect ((name)
737+ => private_chat_requested (name));
738+
739 // bind properties
740 bind_property ("state", room, "state", GLib.BindingFlags.BIDIRECTIONAL);
741 bind_property ("nick", room, "nick", GLib.BindingFlags.BIDIRECTIONAL);
742@@ -80,17 +102,13 @@
743 bind_property ("topic", room.topic_bar, "topic", GLib.BindingFlags.BIDIRECTIONAL);
744 bind_property ("entry", room, "entry", GLib.BindingFlags.BIDIRECTIONAL);
745 }
746-
747- ~Channel () {
748- debug (@"- VIEW CHANNEL $name");
749- }
750
751 public override Gtk.Menu? get_context_menu () {
752 if (menu != null && menu.get_attach_widget () != null)
753 menu.detach ();
754 return menu;
755 }
756-
757+
758 public bool has_user (string nick) {
759 return room.has_user (nick);
760 }
761@@ -103,19 +121,8 @@
762 room.add_user (name, type, silent);
763 }
764
765- public bool remove_user (string name, string message, bool silent = false) {
766- return room.remove_user (name, message, silent);
767- }
768-
769- public void rename_user (string? old_name, string new_name, UserType type, bool silent = false) {
770- room.rename_user (old_name, new_name, type, silent);
771- }
772-
773- public void set_away_status (string nick, bool away) {
774- room.chat.message_away (nick, away);
775- }
776-
777- public void message (owned string from, string message, MessageType type = MessageType.NORMAL) {
778- room.chat.add_message (from, message, type);
779- }
780+ public void remove_user (string name, string message, bool silent = false) {
781+ room.remove_user (name, message, silent);
782+ }
783+
784 }
785
786=== modified file 'src/Widgets/ChatDisplay.vala'
787--- src/Widgets/ChatDisplay.vala 2013-08-25 21:01:06 +0000
788+++ src/Widgets/ChatDisplay.vala 2013-09-22 16:58:59 +0000
789@@ -39,9 +39,7 @@
790 case LEAVE:
791 return "#a99";
792 case SUGGESTION:
793- return "#999";
794 case NICK:
795- return "#999";
796 case AWAY:
797 return "#999";
798 case PING:
799@@ -152,25 +150,4 @@
800
801 return color_map[nick];
802 }
803-}
804-
805-
806-
807-
808-
809-
810-
811-
812-
813-
814-
815-
816-
817-
818-
819-
820-
821-
822-
823-
824-
825+}
826\ No newline at end of file
827
828=== modified file 'src/Widgets/Room.vala'
829--- src/Widgets/Room.vala 2013-09-12 15:16:56 +0000
830+++ src/Widgets/Room.vala 2013-09-22 16:58:59 +0000
831@@ -37,25 +37,20 @@
832 */
833 public class Cable.Widgets.Room : Gtk.Grid {
834
835- Granite.Widgets.SourceList.ExpandableItem operators;
836- Granite.Widgets.SourceList.ExpandableItem voiced;
837- Granite.Widgets.SourceList.ExpandableItem regular;
838-
839- Granite.Widgets.SourceList users;
840+ public UserList user_list;
841 public ChatDisplay chat;
842+ public Gtk.Entry text_entry;
843+ public Widgets.Topic topic_bar;
844+ public Gtk.Label pseudo_label;
845+
846 Granite.Widgets.EmbeddedAlert not_joined_alert;
847 Granite.Widgets.EmbeddedAlert no_such_alert;
848 Granite.Widgets.EmbeddedAlert loading_screen;
849 Granite.Widgets.ThinPaned paned;
850
851- internal Gtk.Entry text_entry;
852 Utils.AutoScrolled left_scrolled;
853
854- Gtk.Box user_box;
855-
856- public Widgets.Topic topic_bar;
857 Gtk.Action action_join;
858- public Gtk.Label pseudo_label;
859
860 public signal void send_message (string message);
861 // public signal void request_complete ();
862@@ -107,18 +102,7 @@
863 }
864 }
865
866- private Gee.Set <string> nicks {
867- owned get {
868- var _nicks = new Gee.HashSet <string> ();
869- Granite.Widgets.SourceList.ExpandableItem[] lists = {voiced, operators, regular};
870- foreach (var list in lists)
871- foreach (var child in list.children)
872- _nicks.add (child.name);
873- return _nicks as Gee.Set;
874- }
875- }
876-
877- public Room (string channel, string nick="") {
878+ public Room (string channel, string nick = "") {
879 this.channel = channel;
880
881 history = new Utils.MessageHistory ();
882@@ -128,31 +112,13 @@
883 }
884
885 void init_layout () {
886+ user_list = new UserList ();
887+
888 paned = new Granite.Widgets.ThinPaned ();
889 var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
890
891- operators = new Granite.Widgets.SourceList.ExpandableItem (_("Operators"));
892- operators.expanded = true;
893-
894- regular = new Granite.Widgets.SourceList.ExpandableItem (_("Regular"));
895- regular.expanded = true;
896-
897- voiced = new Granite.Widgets.SourceList.ExpandableItem (_("Voiced"));
898- voiced.expanded = true;
899-
900- users = new Granite.Widgets.SourceList ();
901- users.hscrollbar_policy = Gtk.PolicyType.NEVER;
902- users.width_request = 150;
903- users.set_sort_func ((a,b) => {return a.name.collate (b.name);});
904-
905- users.root.add (operators);
906- users.root.add (voiced);
907- users.root.add (regular);
908-
909- user_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
910- user_box.pack_start (users);
911 paned.pack1 (box, true, false);
912- paned.pack2 (user_box, false, false);
913+ paned.pack2 (user_list, false, false);
914
915 text_entry = new Gtk.Entry ();
916 text_entry.hexpand = true;
917@@ -165,7 +131,7 @@
918 topic_bar = new Widgets.Topic ();
919
920 pseudo_label = new Gtk.Label ("");
921- pseudo_label.set_alignment (1, (float)0.5);
922+ pseudo_label.set_alignment (1, (float) 0.5);
923 pseudo_label.width_request = 80;
924
925 var entry_eventbox = new Gtk.EventBox ();
926@@ -177,14 +143,14 @@
927
928 entry_grid.attach (pseudo_label, 0, 0, 1, 1);
929 entry_grid.attach (text_entry, 1, 0, 1, 1);
930- left_scrolled.add_with_viewport(chat);
931+ left_scrolled.add_with_viewport (chat);
932
933 box.pack_start (topic_bar, false);
934 box.pack_start (left_scrolled);
935 box.pack_start (entry_eventbox, false);
936
937 loading_screen = new Granite.Widgets.EmbeddedAlert ();
938- loading_screen.primary_text = _("Joining channel %s").printf(channel);
939+ loading_screen.primary_text = _("Joining channel %s").printf (channel);
940 loading_screen.working = true;
941
942 action_join = new Gtk.Action ("join", "Join Channel", "Join this channel", null);
943@@ -201,6 +167,8 @@
944 no_such_alert.message_type = Gtk.MessageType.WARNING;
945 no_such_alert.show_icon= true;
946
947+ state = State.CHAT;
948+
949 this.show_all ();
950 }
951
952@@ -219,7 +187,7 @@
953 return false;
954
955 string[] completion = null;
956- foreach (var nick in nicks)
957+ foreach (var nick in user_list.nicks)
958 if (word.down () == nick[0:word.length].down ())
959 completion += nick;
960
961@@ -232,7 +200,7 @@
962 } else {
963 text_entry.buffer.delete_text (text_entry.cursor_position - word.length, word.length);
964
965- if( text_entry.text != "" ){
966+ if (text_entry.text != "") {
967 text_entry.insert_at_cursor (completion[0] + " ");
968 } else {
969 text_entry.insert_at_cursor (completion[0] + ": ");
970@@ -264,100 +232,52 @@
971 });
972 }
973
974- public bool has_user (string nick) {
975- Granite.Widgets.SourceList.ExpandableItem[] lists = {voiced, operators, regular};
976- foreach (var list in lists)
977- foreach (var child in list.children)
978- if (child.name == nick)
979- return true;
980- return false;
981- }
982-
983 public void list_users (string[] nicks, string[] prefixes)
984 requires (nicks.length == prefixes.length) {
985
986 for (int i = 0; i < nicks.length; i++) {
987 switch (prefixes[i]) {
988 case "@":
989- add_user (nicks[i], UserType.OPERATOR, true);
990+ user_list.add_user (nicks[i], UserType.OPERATOR);
991 break;
992 case "+":
993- add_user (nicks[i], UserType.VOICED, true);
994+ user_list.add_user (nicks[i], UserType.VOICED);
995 break;
996 default:
997- add_user (nicks[i], UserType.REGULAR, true);
998+ user_list.add_user (nicks[i], UserType.REGULAR);
999 break;
1000 }
1001 }
1002-}
1003-
1004- public void rename_user (string? old_name, string new_name, UserType type, bool silent = false)
1005- requires (has_user (old_name))
1006- requires (!has_user (new_name))
1007- ensures (!has_user (old_name))
1008- ensures (has_user (new_name)) {
1009+ }
1010+
1011+ public bool has_user (string name) {
1012+ return user_list.has_user (name);
1013+ }
1014+
1015+ public void rename_user (string? old_name, string new_name,
1016+ UserType type, bool silent = false) {
1017
1018 if (old_name == null) {
1019 add_user (new_name, type, true);
1020 return;
1021 }
1022
1023- if (remove_user (old_name, "", true)) {
1024- add_user (new_name, type, true);
1025- if (!silent)
1026- chat.message_nick (old_name, new_name);
1027- }
1028+ remove_user (old_name, "", true);
1029+ add_user (new_name, type, true);
1030+
1031+ if (!silent)
1032+ chat.message_nick (old_name, new_name);
1033 }
1034
1035- public void add_user (string name, UserType type, bool silent = false)
1036- requires (!has_user (name))
1037- ensures (has_user (name)) {
1038-
1039- var user = new User (name);
1040- user.selectable = false;
1041-
1042- switch (type) {
1043- case UserType.OPERATOR:
1044- operators.add (user);
1045- break;
1046- case UserType.VOICED:
1047- voiced.add (user);
1048- break;
1049- default:
1050- regular.add (user);
1051- break;
1052- }
1053-
1054+ public void add_user (string name, UserType type, bool silent = false) {
1055+ user_list.add_user (name, type);
1056 if (!silent)
1057 chat.message_join (name);
1058-
1059- regular.name = _("Regular") + " (" + regular.n_children.to_string () + ")";
1060- operators.name = _("Operators") + " (" + operators.n_children.to_string () + ")";
1061- voiced.name = _("Voiced") + " (" + voiced.n_children.to_string () + ")";
1062 }
1063
1064- public bool remove_user (string name, string message, bool silent = false)
1065- requires (has_user (name))
1066- ensures (!has_user (name)) {
1067-
1068- Granite.Widgets.SourceList.ExpandableItem[] lists = {voiced, operators, regular};
1069- foreach (var list in lists) {
1070- foreach (var child in list.children) {
1071- if (child.name == name) {
1072- list.remove (child);
1073-
1074- if (!silent)
1075- chat.message_part (child.name, message);
1076-
1077- regular.name = _("Regular") + " (" + regular.n_children.to_string () + ")";
1078- operators.name = _("Operators") + " (" + operators.n_children.to_string () + ")";
1079- voiced.name = _("Voiced") + " (" + voiced.n_children.to_string () + ")";
1080-
1081- return true;
1082- }
1083- }
1084- }
1085- return false;
1086+ public void remove_user (string name, string message, bool silent = false) {
1087+ user_list.remove_user (name);
1088+ if (!silent)
1089+ chat.message_part (name, message);
1090 }
1091 }
1092-
1093
1094=== modified file 'src/Widgets/Server.vala'
1095--- src/Widgets/Server.vala 2013-08-12 21:04:54 +0000
1096+++ src/Widgets/Server.vala 2013-09-22 16:58:59 +0000
1097@@ -23,15 +23,12 @@
1098 */
1099 public class Cable.Widgets.Server : Granite.Widgets.SourceList.ExpandableItem, View.Server {
1100
1101- /**
1102- * List of all channels.
1103- */
1104- public Gee.ArrayList <View.Channel> channels {
1105+ public Gee.ArrayList <View.Chat> chats {
1106 owned get {
1107- var _channels = new Gee.ArrayList <View.Channel> ();
1108+ var _chats = new Gee.ArrayList <View.Chat> ();
1109 foreach (var child in children)
1110- _channels.add (child as View.Channel);
1111- return _channels;
1112+ _chats.add (child as View.Chat);
1113+ return _chats;
1114 }
1115 }
1116
1117@@ -49,15 +46,10 @@
1118 Gtk.MenuItem part_menu_item;
1119
1120 internal Server (string name, string id) {
1121- debug (@"+ VIEW SERVER $name");
1122 GLib.Object (name: name, id: id);
1123 setup_contextmenu ();
1124 }
1125
1126- ~Server () {
1127- debug (@"- VIEW SERVER $name");
1128- }
1129-
1130 void setup_contextmenu ()
1131 requires (menu == null)
1132 ensures (menu != null) {
1133@@ -82,28 +74,27 @@
1134 return menu;
1135 }
1136
1137- /**
1138- * Create and add a new channel to this server.
1139- * @param channel_name Name of the new channel.
1140- */
1141- public void add_channel (string channel_name, string channel_id)
1142- requires (!has_channel (channel_name))
1143- ensures (has_channel (channel_name)) {
1144-
1145- var channel = new Channel (channel_name, channel_id);
1146- add (channel);
1147- }
1148-
1149- /**
1150- * Remove a channel from this server.
1151- * @param channel_name Name of the channel to be removed.
1152- */
1153- public void remove_channel (string channel_name)
1154- requires (has_channel (channel_name))
1155- ensures (!has_channel (channel_name)) {
1156-
1157- var channel = get_channel_by_id (channel_name) as Widgets.Channel;
1158- return_if_fail (channel != null);
1159- remove (channel);
1160+ public void add_channel (string chat_name, string chat_id)
1161+ requires (!has_chat (chat_name))
1162+ ensures (has_chat (chat_name)) {
1163+
1164+ var chat = new Channel (chat_name, chat_id);
1165+ add (chat);
1166+ }
1167+
1168+ public void add_private_chat (string chat_name, string chat_id)
1169+ requires (!has_chat (chat_name))
1170+ ensures (has_chat (chat_name)) {
1171+
1172+ var chat = new PrivateChat (chat_name, chat_id);
1173+ add (chat);
1174+ }
1175+
1176+ public void remove_chat (string chat_name)
1177+ requires (has_chat (chat_name))
1178+ ensures (!has_chat (chat_name)) {
1179+
1180+ var chat = get_chat (chat_name) as Widgets.Chat;
1181+ remove (chat);
1182 }
1183 }
1184
1185=== modified file 'src/Widgets/User.vala'
1186--- src/Widgets/User.vala 2013-07-26 13:16:58 +0000
1187+++ src/Widgets/User.vala 2013-09-22 16:58:59 +0000
1188@@ -21,8 +21,16 @@
1189 /*Gtk.MenuItem kick_item;
1190 Gtk.MenuItem ban_item;
1191 Gtk.MenuItem ignore_item;*/
1192+
1193 public class Cable.Widgets.User : Granite.Widgets.SourceList.Item {
1194+
1195+ public signal void private_chat_requested ();
1196+
1197 public User (string name) {
1198 base (name);
1199+
1200+ activated.connect (() => {
1201+ private_chat_requested (); // signal
1202+ });
1203 }
1204 }
1205
1206=== added file 'src/Widgets/UserList.vala'
1207--- src/Widgets/UserList.vala 1970-01-01 00:00:00 +0000
1208+++ src/Widgets/UserList.vala 2013-09-22 16:58:59 +0000
1209@@ -0,0 +1,122 @@
1210+/***
1211+ Copyright (C) 2013 Cable Developers
1212+
1213+ This program or library is free software; you can redistribute it
1214+ and/or modify it under the terms of the GNU Lesser General Public
1215+ License as published by the Free Software Foundation; either
1216+ version 3 of the License, or (at your option) any later version.
1217+
1218+ This library is distributed in the hope that it will be useful,
1219+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1220+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1221+ Lesser General Public License for more details.
1222+
1223+ You should have received a copy of the GNU Lesser General
1224+ Public License along with this library; if not, write to the
1225+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
1226+ Boston, MA 02110-1301 USA.
1227+***/
1228+
1229+public class Cable.Widgets.UserList : Granite.Widgets.SourceList {
1230+
1231+ Granite.Widgets.SourceList.ExpandableItem operators;
1232+ Granite.Widgets.SourceList.ExpandableItem voiced;
1233+ Granite.Widgets.SourceList.ExpandableItem regular;
1234+
1235+ delegate void ForeachFunction (User user);
1236+
1237+ public Gee.Set <string> nicks {
1238+ owned get {
1239+ var _nicks = new Gee.HashSet <string> ();
1240+ @foreach ((child) => _nicks.add (child.name));
1241+ return _nicks as Gee.Set;
1242+ }
1243+ }
1244+
1245+ public signal void private_chat_requested (string user_name);
1246+
1247+ public UserList () {
1248+ operators = new Granite.Widgets.SourceList.ExpandableItem (_("Operators"));
1249+ voiced = new Granite.Widgets.SourceList.ExpandableItem (_("Voiced"));
1250+ regular = new Granite.Widgets.SourceList.ExpandableItem (_("Regular"));
1251+
1252+ regular.expanded = true;
1253+ operators.expanded = true;
1254+ voiced.expanded = true;
1255+
1256+ hscrollbar_policy = Gtk.PolicyType.NEVER;
1257+ width_request = 150;
1258+ set_sort_func ((a,b) => {
1259+ return a.name.collate (b.name);
1260+ });
1261+
1262+ root.add (operators);
1263+ root.add (voiced);
1264+ root.add (regular);
1265+
1266+ no_show_all = true;
1267+ visible = false;
1268+ }
1269+
1270+ public bool has_user (string name) {
1271+ return name in nicks;
1272+ }
1273+
1274+ public void add_user (string name, UserType user_type = UserType.REGULAR) {
1275+ var user = new User (name);
1276+ user.selectable = false;
1277+
1278+ switch (user_type) {
1279+ case UserType.OPERATOR:
1280+ operators.add (user);
1281+ break;
1282+ case UserType.VOICED:
1283+ voiced.add (user);
1284+ break;
1285+ default:
1286+ regular.add (user);
1287+ break;
1288+ }
1289+
1290+ user.private_chat_requested.connect (() => {
1291+ private_chat_requested (user.name);
1292+ });
1293+
1294+ update_user_counts ();
1295+
1296+ if (visible == false) {
1297+ no_show_all = false;
1298+ visible = true;
1299+ }
1300+ }
1301+
1302+ public void remove_user (string name) {
1303+ @foreach ((child) => {
1304+ if (child.name != name)
1305+ return;
1306+ child.parent.remove (child);
1307+ update_user_counts ();
1308+ });
1309+
1310+ if (nicks.size == 0) {
1311+ no_show_all = true;
1312+ visible = false;
1313+ }
1314+ }
1315+
1316+ void update_user_counts () {
1317+ regular.name = _("Regular") + @" ($(regular.n_children.to_string ()))";
1318+ operators.name = _("Operators") + @" ($(operators.n_children.to_string ()))";
1319+ voiced.name = _("Voiced") + @" ($(voiced.n_children.to_string ()))";
1320+ }
1321+
1322+ new void @foreach (ForeachFunction func) {
1323+ Granite.Widgets.SourceList.ExpandableItem[] lists = {
1324+ voiced, operators, regular
1325+ };
1326+
1327+ foreach (var list in lists)
1328+ foreach (var child in list.children)
1329+ func (child as User);
1330+ }
1331+}
1332
1333=== modified file 'src/Widgets/Window.vala'
1334--- src/Widgets/Window.vala 2013-08-20 14:28:43 +0000
1335+++ src/Widgets/Window.vala 2013-09-22 16:58:59 +0000
1336@@ -112,9 +112,9 @@
1337 /**
1338 * Get the currently selected channel.
1339 */
1340- public View.Channel? current_channel {
1341- get { return server_list.selected as View.Channel; }
1342- set { server_list.selected = value as Widgets.Channel; }
1343+ public View.Chat? current_chat {
1344+ get { return server_list.selected as View.Chat; }
1345+ set { server_list.selected = value as Widgets.Chat; }
1346 }
1347
1348 //public signal void edit_identity ();
1349@@ -126,8 +126,8 @@
1350
1351 server_list.item_selected.connect ((item) => {
1352 if (item != null) {
1353- display_widget = (current_channel as Widgets.Channel).room;
1354- selected (current_channel);
1355+ display_widget = (current_chat as Widgets.Chat).room;
1356+ selected (current_chat);
1357 }
1358 });
1359

Subscribers

People subscribed via source and target branches

to all changes: