~elementary-apps/cable/pastebin

« back to all changes in this revision

Viewing changes to src/Widgets/Room.vala

  • Committer: eduardgotwig at gmail
  • Author(s): Julien Spautz
  • Date: 2013-08-12 16:44:37 UTC
  • mfrom: (79.1.37 model-view)
  • Revision ID: eduardgotwig@gmail.com-20130812164437-wlojp66wpptckn96
Merged  lp:~julien-spautz/cable/model-view 

Show diffs side-by-side

added added

removed removed

Lines of Context:
10
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
12
    Lesser General Public License for more details.
13
 
 
 
13
 
14
14
    You should have received a copy of the GNU Lesser General
15
15
    Public License along with this library; if not, write to the
16
16
    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
17
    Boston, MA 02110-1301 USA.
18
18
***/
19
19
 
20
 
public enum Cable.Widgets.UserType {
 
20
public enum Cable.UserType {
21
21
    OPERATOR,
22
22
    VOICED,
23
23
    REGULAR
24
24
}
25
25
 
26
 
public enum Cable.Widgets.MessageType {
27
 
    NORMAL,
28
 
    ENTER,
29
 
    LEAVE,
30
 
    SUGGESTION,
31
 
    NICK,
32
 
    AWAY
 
26
public enum Cable.State {
 
27
    CHAT,
 
28
    JOINING,
 
29
    NOT_JOINED,
 
30
    NO_SUCH;
33
31
}
34
32
 
35
33
/**
36
34
 * This class represents a chat room, i.e, the topic, the message display,
37
35
 * a list of users and is associated to a channel.
38
36
 * This class is always accessed through its related channel.
39
 
 * TODO : Use Gtk.Grid instead of Gtk.Box, Gtk.Box will be deprecated in the future.
40
37
 */
41
 
public class Cable.Widgets.Room : Gtk.Box {
 
38
public class Cable.Widgets.Room : Gtk.Grid {
42
39
 
43
40
    Granite.Widgets.SourceList.ExpandableItem operators;
44
41
    Granite.Widgets.SourceList.ExpandableItem voiced;
45
42
    Granite.Widgets.SourceList.ExpandableItem regular;
46
43
 
47
44
    Granite.Widgets.SourceList users;
48
 
    Gtk.Entry entry;
49
 
    ChatDisplay chat;
50
 
    Gtk.ScrolledWindow left_scrolled;
51
 
 
52
 
    Gtk.Toolbar misc_bar;
 
45
    public ChatDisplay chat;
 
46
    Granite.Widgets.EmbeddedAlert not_joined_alert;
 
47
    Granite.Widgets.EmbeddedAlert no_such_alert;
 
48
    Granite.Widgets.EmbeddedAlert loading_screen;
 
49
    Granite.Widgets.ThinPaned paned;
 
50
 
 
51
    internal Gtk.Entry text_entry;
 
52
    Utils.AutoScrolled left_scrolled;
 
53
 
53
54
    Gtk.Box user_box;
54
 
    
55
 
    Gee.HashMap<string, string> color_map;
56
 
 
57
 
    private int unity_badge_count = 0;
58
 
 
59
 
    public Gtk.InfoBar topic;
60
 
    public Gtk.Label topic_name;
61
 
    private Gtk.Button identity_button;
 
55
 
 
56
    public Widgets.Topic topic_bar;
 
57
    Gtk.Action action_join;
 
58
    public Gtk.Label pseudo_label;
62
59
 
63
60
    public signal void send_message (string message);
64
 
 
65
 
    public signal bool kick_user (string nick_name);
66
 
    public signal bool ban_user (string nick_name);
67
 
    public signal void ignore (string nick_name, bool ignore = true);
68
 
 
69
 
    public Server server { get; private set; }
70
 
    public string channel;
71
 
    
72
 
    public Room (string channel, Server server) {
 
61
    // public signal void request_complete ();
 
62
    public signal void rejoin ();
 
63
 
 
64
    public string channel { get; set; }
 
65
    
 
66
    public string nick {
 
67
        get { return pseudo_label.label; }
 
68
        set { pseudo_label.label = value; }
 
69
    }
 
70
    
 
71
    public string entry {
 
72
        get { return text_entry.text; }
 
73
        set { text_entry.text = value; }
 
74
    }
 
75
 
 
76
    State _state;
 
77
    public State state {
 
78
        get { return _state; }
 
79
        set {
 
80
            _state = value;
 
81
 
 
82
            var widget = get_child_at (0, 0);
 
83
            if (widget != null)
 
84
                remove (widget);
 
85
 
 
86
            switch (_state) {
 
87
                case State.CHAT:
 
88
                    attach (paned, 0, 0, 1, 1);
 
89
                    break;
 
90
                case State.JOINING:
 
91
                    attach (loading_screen, 0, 0, 1, 1);
 
92
                    loading_screen.working = true;
 
93
                    break;
 
94
                case State.NOT_JOINED:
 
95
                    attach (not_joined_alert, 0, 0, 1, 1);
 
96
                    break;
 
97
                case State.NO_SUCH:
 
98
                    attach (no_such_alert, 0, 0, 1, 1);
 
99
                    break;
 
100
                default:
 
101
                    assert_not_reached ();
 
102
            }
 
103
 
 
104
            show_all ();
 
105
        }
 
106
    }
 
107
    
 
108
    private Gee.Set <string> nicks {
 
109
        owned get {
 
110
            var _nicks = new Gee.HashSet <string> ();
 
111
            Granite.Widgets.SourceList.ExpandableItem[] lists = {voiced, operators, regular};
 
112
            foreach (var list in lists)
 
113
                foreach (var child in list.children)
 
114
                    _nicks.add (child.name);
 
115
            return _nicks as Gee.Set;
 
116
        }
 
117
    }
 
118
 
 
119
    public Room (string channel, string nick="") {
73
120
        this.channel = channel;
74
 
        this.server = server;
75
 
        
76
 
        color_map = new Gee.HashMap<string, string> (null, null);
77
 
        
78
 
        this.orientation = Gtk.Orientation.VERTICAL;
79
 
        
80
 
        var paned = new Granite.Widgets.ThinPaned ();
 
121
 
 
122
        init_layout ();
 
123
        connect_callbacks ();
 
124
    }
 
125
 
 
126
    void init_layout () {
 
127
        paned = new Granite.Widgets.ThinPaned ();
81
128
        var box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
82
 
        
83
 
        /* Create basic SourceList with items */
 
129
 
84
130
        operators = new Granite.Widgets.SourceList.ExpandableItem (_("Operators"));
85
131
        operators.expanded = true;
86
 
        
87
 
        regular = new Granite.Widgets.SourceList.ExpandableItem ("Regular");
 
132
 
 
133
        regular = new Granite.Widgets.SourceList.ExpandableItem (_("Regular"));
88
134
        regular.expanded = true;
89
 
        
90
 
        voiced = new Granite.Widgets.SourceList.ExpandableItem ("Voiced");
 
135
 
 
136
        voiced = new Granite.Widgets.SourceList.ExpandableItem (_("Voiced"));
91
137
        voiced.expanded = true;
92
 
        
 
138
 
93
139
        users = new Granite.Widgets.SourceList ();
94
140
        users.hscrollbar_policy = Gtk.PolicyType.NEVER;
95
141
        users.width_request = 150;
96
142
        users.set_sort_func ((a,b) => {return a.name.collate (b.name);});
97
 
        var css = new Gtk.CssProvider ();
98
 
        try {
99
 
            css.load_from_data ("*{
100
 
                background-color: #fff;
101
 
            }", -1);
102
 
        } catch (Error e) {
103
 
            critical (e.message);
104
 
        }
105
 
        users.get_style_context ().add_provider (css, 40000);
106
 
        
 
143
 
107
144
        users.root.add (operators);
108
145
        users.root.add (voiced);
109
146
        users.root.add (regular);
110
 
        
 
147
 
111
148
        user_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
112
149
        user_box.pack_start (users);
113
 
        
114
150
        paned.pack1 (box, true, false);
115
151
        paned.pack2 (user_box, false, false);
116
 
        
117
 
        /* Create inline toolbar */
118
 
        misc_bar = new Gtk.Toolbar ();
119
 
        misc_bar.set_icon_size (Gtk.IconSize.SMALL_TOOLBAR);
120
 
        
121
 
        var misc_bar_style = misc_bar.get_style_context ();
122
 
        misc_bar_style.add_class(Gtk.STYLE_CLASS_INLINE_TOOLBAR);
123
 
 
124
 
        var aligner = new Gtk.SeparatorToolItem ();
125
 
        aligner.set_draw (false);
126
 
        aligner.set_expand (true);
127
 
        
128
 
        var about_button = main_actions.get_action ("About").create_tool_item() as Gtk.ToolButton;   
129
 
        var settings_button = main_actions.get_action ("Preferences").create_tool_item() as Gtk.ToolButton;
130
 
        
131
 
        misc_bar.add (aligner);
132
 
        misc_bar.add (settings_button);
133
 
        misc_bar.add (about_button);
134
 
        
135
 
        /* Create the Topic InfoBar */
136
 
        var topic = new Gtk.InfoBar ();
137
 
        topic_name = new Gtk.Label (_("Loading Topic…"));
138
 
        topic_name.use_markup = true;
139
 
        topic_name.wrap = true;
140
 
        topic_name.halign = Gtk.Align.FILL;
141
 
        topic_name.valign = Gtk.Align.FILL;
142
 
        var content = topic.get_content_area () as Gtk.Container;
143
 
        content.add (topic_name);
144
 
        
145
 
        entry = new Gtk.Entry ();
146
 
        entry.hexpand = true;
147
 
        
148
 
        left_scrolled = new Gtk.ScrolledWindow (null, null);
 
152
 
 
153
        text_entry = new Gtk.Entry ();
 
154
        text_entry.hexpand = true;
 
155
 
 
156
        left_scrolled = new Utils.AutoScrolled ();
149
157
        left_scrolled.hscrollbar_policy = Gtk.PolicyType.NEVER;
150
 
        
 
158
 
151
159
        chat = new ChatDisplay ();
152
160
 
153
 
        var pseudo_label = new Gtk.Label (server.identity.nick_name);
 
161
        topic_bar = new Widgets.Topic ();
 
162
 
 
163
        pseudo_label = new Gtk.Label ("");
154
164
        pseudo_label.set_alignment (1, (float)0.5);
155
165
        pseudo_label.width_request = 80;
156
 
        
 
166
 
157
167
        var entry_eventbox = new Gtk.EventBox ();
158
168
        entry_eventbox.get_style_context ().add_class (Granite.StyleClass.CONTENT_VIEW);
159
169
        var entry_grid = new Gtk.Grid ();
160
170
        entry_grid.margin = 12;
161
171
        entry_grid.column_spacing = 12;
162
172
        entry_eventbox.add (entry_grid);
163
 
        
 
173
 
164
174
        entry_grid.attach (pseudo_label, 0, 0, 1, 1);
165
 
        entry_grid.attach (entry, 1, 0, 1, 1);
 
175
        entry_grid.attach (text_entry, 1, 0, 1, 1);
166
176
        left_scrolled.add_with_viewport(chat);
167
177
 
168
 
        box.pack_start (topic, false);
 
178
        box.pack_start (topic_bar, false);
169
179
        box.pack_start (left_scrolled);
170
180
        box.pack_start (entry_eventbox, false);
171
181
 
172
 
        this.pack_start (paned);
173
 
 
174
 
        entry.focus_in_event.connect (() => {
175
 
            for (int n = 0; n < unity_badge_count; n++)
176
 
                Cable.Services.unity.remove_job ();
177
 
            unity_badge_count = 0;
178
 
            return false;
179
 
        });
180
 
 
181
 
        entry.activate.connect (() => {
182
 
            send_message (entry.text);
183
 
            entry.text = "";
184
 
        });
185
 
        
186
 
        string text = "";
187
 
        entry.key_press_event.connect ((ev) => {
 
182
        loading_screen = new Granite.Widgets.EmbeddedAlert ();
 
183
        loading_screen.primary_text = _("Joining channel %s").printf(channel);
 
184
        loading_screen.working = true;
 
185
 
 
186
        action_join = new Gtk.Action ("join", "Join Channel", "Join this channel", null);
 
187
 
 
188
        not_joined_alert = new Granite.Widgets.EmbeddedAlert ();
 
189
        not_joined_alert.primary_text = _("Channel is not joined");
 
190
        not_joined_alert.secondary_text = _("Join the channel to receive and send messages.");
 
191
        not_joined_alert.message_type = Gtk.MessageType.WARNING;
 
192
        not_joined_alert.actions = {action_join};
 
193
 
 
194
        no_such_alert = new Granite.Widgets.EmbeddedAlert ();
 
195
        no_such_alert.primary_text = _("Cannot join channel");
 
196
        no_such_alert.secondary_text = _("An error has occurred, this channel does not exist.");
 
197
        no_such_alert.message_type = Gtk.MessageType.WARNING;
 
198
        no_such_alert.show_icon=  true;
 
199
 
 
200
        this.show_all ();
 
201
    }
 
202
 
 
203
    void connect_callbacks () {
 
204
        text_entry.key_press_event.connect ((ev) => {
188
205
            if (ev.keyval == Gdk.Key.Tab) {
189
 
                if (entry.text == null || entry.text == "")
 
206
                var text = text_entry.text;
 
207
 
 
208
                if (text == null || text == "")
190
209
                    return false;
191
 
                
192
 
                var txt = entry.text.split (" ");
193
 
                var word = txt[txt.length-1];
194
 
                
 
210
 
 
211
                var words = text.split (" ");
 
212
                var word =  words[words.length-1];
 
213
 
195
214
                if (word == null || word == "")
196
215
                    return false;
197
 
                
198
 
                var collection = new Gee.LinkedList<Granite.Widgets.SourceList.Item> ();
199
 
                collection.add_all (operators.children);
200
 
                collection.add_all (voiced.children);
201
 
                collection.add_all (regular.children);
202
 
                var user_array = collection.to_array ();
203
 
                
204
 
                string[]? completion = null;
205
 
                for (int n = 0; n < user_array.length; n++)
206
 
                    if (user_array[n].name != null)
207
 
                        if (word.down () == user_array[n].name[0:word.length].down ())
208
 
                            completion += user_array[n].name; 
 
216
 
 
217
                string[] completion = null;
 
218
                foreach (var nick in nicks)
 
219
                    if (word.down () == nick[0:word.length].down ())
 
220
                        completion += nick;
209
221
 
210
222
                if (completion == null)
211
223
                    return true;
212
 
                
 
224
 
213
225
                if (completion.length > 1) {
214
 
                    string msg = "";
215
 
                    string sep = " - ";
216
 
                    for (int i = 0; i < completion.length; i++) {
217
 
                        if (i != (completion.length -1))
218
 
                            msg += (completion[i] + sep);
219
 
                        else
220
 
                            msg += completion[i];
221
 
                    }
222
 
                    this.message ("", msg, MessageType.SUGGESTION);
223
 
                    return true;
224
 
                }
225
 
                else {
226
 
                    txt = entry.text.split (" ");
227
 
                    word = txt[txt.length-1];
228
 
                    entry.buffer.delete_text (entry.cursor_position - word.length, word.length);
229
 
                    entry.insert_at_cursor (completion[0] + ": ");
230
 
                    return true;
231
 
                }
232
 
 
233
 
                for (int n = 0; n < user_array.length; n++)
234
 
                    if (user_array[n].name != null)
235
 
                        if (word.casefold () == user_array[n].name[0:word.length].casefold ()) {
236
 
                            entry.delete_from_cursor (Gtk.DeleteType.WORDS, 1);
237
 
                            entry.insert_at_cursor (user_array[n].name + ": ");
238
 
                            return true;
239
 
                        }
240
 
                return true;
 
226
                    chat.message_suggestions (completion);
 
227
                    return true;
 
228
                } else {
 
229
                    text_entry.buffer.delete_text (text_entry.cursor_position - word.length, word.length);
 
230
                    text_entry.insert_at_cursor (completion[0] + ": ");
 
231
                    return true;
 
232
                }
241
233
            }
242
234
            return false;
243
 
        });        
244
 
 
245
 
        /* Load nickname's color from settings */
246
 
        string[] colors = Settings.settings.get_strv ("colors");
247
 
        foreach (var color in colors) {
248
 
            var array = color.split(":", 4);
249
 
            if (array.length == 4) {
250
 
                
251
 
                if (array[0] == server.network.address && array[1] == channel) {
252
 
                    color_map.set (array[2], array[3]);
253
 
                }
 
235
        });
 
236
    
 
237
        text_entry.activate.connect (() => {
 
238
            if (text_entry.text != "")
 
239
                send_message (text_entry.text); // signal
 
240
        });
 
241
 
 
242
        action_join.activate.connect (() => {
 
243
            rejoin (); // signal
 
244
        });
 
245
    }
 
246
 
 
247
    public bool has_user (string nick) {
 
248
        Granite.Widgets.SourceList.ExpandableItem[] lists = {voiced, operators, regular};
 
249
        foreach (var list in lists)
 
250
            foreach (var child in list.children)
 
251
                if (child.name == nick)
 
252
                    return true;
 
253
        return false;
 
254
    }
 
255
 
 
256
    public void list_users (string[] nicks, string[] prefixes)
 
257
        requires (nicks.length == prefixes.length) {
 
258
 
 
259
        for (int i = 0; i < nicks.length; i++) {
 
260
            switch (prefixes[i]) {
 
261
                case "@":
 
262
                    add_user (nicks[i], UserType.OPERATOR, true);
 
263
                    break;
 
264
                case "+":
 
265
                    add_user (nicks[i], UserType.VOICED, true);
 
266
                    break;
 
267
                default:
 
268
                    add_user (nicks[i], UserType.REGULAR, true);
 
269
                    break;
254
270
            }
255
271
        }
256
 
 
257
 
    }
258
 
 
259
 
    public void rename_user (string old_name, string new_name, UserType type, bool silent = false) {
260
 
        remove_user (old_name, true);
261
 
        add_user (new_name, type, true);
 
272
}
 
273
 
 
274
    public void rename_user (string? old_name, string new_name, UserType type, bool silent = false)
 
275
        requires (has_user (old_name))
 
276
        requires (!has_user (new_name))
 
277
        ensures (!has_user (old_name))
 
278
        ensures (has_user (new_name)) {
262
279
        
263
 
        if (!silent)
264
 
            message (new_name, "", MessageType.NICK);
 
280
        if (old_name == null) {
 
281
            add_user (new_name, type, true);
 
282
            return;
 
283
        }
 
284
 
 
285
        if (remove_user (old_name, "", true)) {
 
286
            add_user (new_name, type, true);
 
287
            if (!silent)
 
288
                chat.message_nick (old_name, new_name);
 
289
        }
265
290
    }
266
291
 
267
 
    public void add_user (string name, UserType type, bool silent = false) {
 
292
    public void add_user (string name, UserType type, bool silent = false)
 
293
        requires (!has_user (name))
 
294
        ensures (has_user (name)) {
 
295
 
268
296
        var user = new User (name);
269
 
        if (name == server.identity.nick_name)
270
 
            user.is_me = true;
271
 
        user.color_changed.connect (user_color_changed);
272
297
        user.selectable = false;
273
 
        if (color_map.has_key (name)) {
274
 
            user.set_color_from_string (color_map.get (name));
 
298
            
 
299
        switch (type) {
 
300
            case UserType.OPERATOR:
 
301
                operators.add (user);
 
302
                break;
 
303
            case UserType.VOICED:
 
304
                voiced.add (user);
 
305
                break;
 
306
            default:
 
307
                regular.add (user);
 
308
                break;
275
309
        }
276
310
 
277
 
        if (type == UserType.OPERATOR)
278
 
            operators.add (user);
279
 
        else if (type == UserType.VOICED)
280
 
            voiced.add (user);
281
 
        else
282
 
            regular.add (user);
283
 
 
284
311
        if (!silent)
285
 
            message (name, "", MessageType.ENTER);
 
312
            chat.message_join (name);
286
313
 
287
314
        regular.name = _("Regular") + " (" + regular.n_children.to_string () + ")";
288
315
        operators.name = _("Operators") + " (" + operators.n_children.to_string () + ")";
289
316
        voiced.name = _("Voiced") + " (" + voiced.n_children.to_string () + ")";
290
317
    }
291
318
 
292
 
    public void remove_user (string name, bool silent = false) {
 
319
    public bool remove_user (string name, string message, bool silent = false)
 
320
        requires (has_user (name))
 
321
        ensures (!has_user (name)) {
 
322
        
293
323
        Granite.Widgets.SourceList.ExpandableItem[] lists = {voiced, operators, regular};
294
324
        foreach (var list in lists) {
295
325
            foreach (var child in list.children) {
296
326
                if (child.name == name) {
297
327
                    list.remove (child);
298
 
                    
 
328
 
299
329
                    if (!silent)
300
 
                        message (child.name, "", MessageType.LEAVE);
301
 
                    
 
330
                        chat.message_part (child.name, message);
 
331
 
302
332
                    regular.name = _("Regular") + " (" + regular.n_children.to_string () + ")";
303
333
                    operators.name = _("Operators") + " (" + operators.n_children.to_string () + ")";
304
334
                    voiced.name = _("Voiced") + " (" + voiced.n_children.to_string () + ")";
305
 
                    
306
 
                    return;
307
 
                }
308
 
            }
309
 
        }
310
 
        
311
 
    }
312
 
    
313
 
    private void user_color_changed (string user, string color) {
314
 
        if (!color_map.has_key (user)) {
315
 
            color_map.set (user, color);
316
 
        } else {
317
 
            color_map.unset (user, null);
318
 
            color_map.set (user, color);
319
 
        }
320
 
        string[] colors = Settings.settings.get_strv ("colors");
321
 
        var colors_modified = new string[colors.length+1];
322
 
        bool value_exists = false;
323
 
        for (int i = 0; i<colors.length; i++) {
324
 
            colors_modified[i] = colors[i];
325
 
            var array = colors[i].split(":", 4);
326
 
            if (array.length == 4) {
327
 
                if (array[0] == server.network.address && array[1] == channel && array[2]==user) {
328
 
                    colors_modified[i] = server.network.address + ":" + channel + ":" + user + ":" + color;
329
 
                    value_exists = true;
330
 
                    break;
331
 
                }
332
 
            }
333
 
        }
334
 
        if (!value_exists) {
335
 
            colors_modified[colors.length] = server.network.address + ":" + channel + ":" + user + ":" + color;
336
 
        }
337
 
        Settings.settings.set_strv ("colors", colors_modified);
338
 
    }
339
 
 
340
 
    public void message (owned string from, string message, MessageType type = MessageType.NORMAL) {
341
 
        bool do_scroll = left_scrolled.vadjustment.value + 10 > left_scrolled.vadjustment.upper;
342
 
 
343
 
        switch (type) {
344
 
            case MessageType.NORMAL:
345
 
                var new_message = message;
346
 
                foreach (var entry in color_map.entries) {
347
 
                    new_message = new_message.replace (entry.key, "<span color='"+entry.value+"'>"+entry.key+"</span>");
348
 
                }
349
 
                if (message.contains (server.identity.nick_name)) {
350
 
                    chat.add_message (@"<span color='#DA4D45'><b>$from</b></span>", @"<span color='#DA4D45'><b>$new_message</b></span>");
351
 
                    // Send a notification...
352
 
                    var notify = new Notify.Notification (from, new_message, "internet-chat");
353
 
                    try {
354
 
                        notify.show ();
355
 
                    } catch (Error e) { warning (e.message); }
356
 
                    // libunity badge
357
 
                    if (!entry.has_focus) {
358
 
                        Cable.Services.unity.append_job ();
359
 
                        unity_badge_count++;
360
 
                    }
361
 
                
362
 
                } else {
363
 
                    if (color_map.has_key (from)) {
364
 
                        chat.add_message ("<span color='"+color_map.get (from)+@"'>$from</span>", new_message);
365
 
                    } else {
366
 
                        chat.add_message (from, new_message);
367
 
                    }
368
 
                }
369
 
                break;
370
 
            case MessageType.ENTER:
371
 
                chat.add_message ("", @"<span color='#9a9'>"+_("%s joined").printf (from)+"</span>");
372
 
                break;
373
 
            case MessageType.LEAVE:
374
 
                chat.add_message ("", @"<span color='#a99'>"+_("%s left").printf (from)+"</span>");
375
 
                break;
376
 
            case MessageType.AWAY:
377
 
                chat.add_message ("", @"<span color='#999'>"+_("%s is away").printf (from)+"</span>");
378
 
                break;
379
 
            case MessageType.NICK:
380
 
                chat.add_message ("", @"<span color='#999'>$from</span>");
381
 
                break;
382
 
            case MessageType.SUGGESTION:
383
 
                chat.add_message ("", @"<span color='#9a9'>"+ ("%s").printf (message) + "</span>");
384
 
                break;
385
 
        }
386
 
 
387
 
        Timeout.add (1, () => {
388
 
            if (do_scroll)
389
 
                left_scrolled.vadjustment.value = left_scrolled.vadjustment.upper;
390
 
                return false;
391
 
        });
 
335
 
 
336
                    return true;
 
337
                }
 
338
            }
 
339
        }
 
340
        return false;
392
341
    }
393
342
}
394
343