~ubuntu-branches/ubuntu/intrepid/irssi/intrepid-updates

« back to all changes in this revision

Viewing changes to src/fe-common/core/chat-completion.c

  • Committer: Bazaar Package Importer
  • Author(s): David Pashley
  • Date: 2005-12-10 21:25:51 UTC
  • Revision ID: james.westby@ubuntu.com-20051210212551-5qwm108g7inyu2f2
Tags: upstream-0.8.10
ImportĀ upstreamĀ versionĀ 0.8.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 chat-completion.c : irssi
 
3
 
 
4
    Copyright (C) 1999-2000 Timo Sirainen
 
5
 
 
6
    This program is free software; you can redistribute it and/or modify
 
7
    it under the terms of the GNU General Public License as published by
 
8
    the Free Software Foundation; either version 2 of the License, or
 
9
    (at your option) any later version.
 
10
 
 
11
    This program is distributed in the hope that it will be useful,
 
12
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
    GNU General Public License for more details.
 
15
 
 
16
    You should have received a copy of the GNU General Public License
 
17
    along with this program; if not, write to the Free Software
 
18
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
*/
 
20
 
 
21
#include "module.h"
 
22
#include "signals.h"
 
23
#include "commands.h"
 
24
#include "misc.h"
 
25
#include "levels.h"
 
26
#include "lib-config/iconfig.h"
 
27
#include "settings.h"
 
28
 
 
29
#include "chatnets.h"
 
30
#include "servers.h"
 
31
#include "servers-setup.h"
 
32
#include "channels.h"
 
33
#include "channels-setup.h"
 
34
#include "queries.h"
 
35
#include "nicklist.h"
 
36
 
 
37
#include "completion.h"
 
38
#include "chat-completion.h"
 
39
#include "window-items.h"
 
40
 
 
41
static int keep_privates_count, keep_publics_count;
 
42
static int completion_lowercase;
 
43
static const char *completion_char, *cmdchars;
 
44
static GSList *global_lastmsgs;
 
45
static int completion_auto, completion_strict;
 
46
 
 
47
#define SERVER_LAST_MSG_ADD(server, nick) \
 
48
        last_msg_add(&((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs, \
 
49
                     nick, TRUE, keep_privates_count)
 
50
 
 
51
#define CHANNEL_LAST_MSG_ADD(channel, nick, own) \
 
52
        last_msg_add(&((MODULE_CHANNEL_REC *) MODULE_DATA(channel))->lastmsgs, \
 
53
                     nick, own, keep_publics_count)
 
54
 
 
55
static LAST_MSG_REC *last_msg_find(GSList *list, const char *nick)
 
56
{
 
57
        while (list != NULL) {
 
58
                LAST_MSG_REC *rec = list->data;
 
59
 
 
60
                if (g_strcasecmp(rec->nick, nick) == 0)
 
61
                        return rec;
 
62
                list = list->next;
 
63
        }
 
64
 
 
65
        return NULL;
 
66
}
 
67
 
 
68
static void last_msg_dec_owns(GSList *list)
 
69
{
 
70
        LAST_MSG_REC *rec;
 
71
 
 
72
        while (list != NULL) {
 
73
                rec = list->data;
 
74
                if (rec->own) rec->own--;
 
75
 
 
76
                list = list->next;
 
77
        }
 
78
}
 
79
 
 
80
static void last_msg_destroy(GSList **list, LAST_MSG_REC *rec)
 
81
{
 
82
        *list = g_slist_remove(*list, rec);
 
83
 
 
84
        g_free(rec->nick);
 
85
        g_free(rec);
 
86
}
 
87
 
 
88
static void last_msg_add(GSList **list, const char *nick, int own, int max)
 
89
{
 
90
        LAST_MSG_REC *rec;
 
91
 
 
92
        if (max <= 0)
 
93
                return;
 
94
 
 
95
        rec = last_msg_find(*list, nick);
 
96
        if (rec != NULL) {
 
97
                /* msg already exists, update it */
 
98
                *list = g_slist_remove(*list, rec);
 
99
                if (own)
 
100
                        rec->own = max;
 
101
                else if (rec->own)
 
102
                        rec->own--;
 
103
        } else {
 
104
                rec = g_new(LAST_MSG_REC, 1);
 
105
                rec->nick = g_strdup(nick);
 
106
 
 
107
                while ((int)g_slist_length(*list) >= max) {
 
108
                        last_msg_destroy(list, g_slist_last(*list)->data);
 
109
                }
 
110
 
 
111
                rec->own = own ? max : 0;
 
112
        }
 
113
        rec->time = time(NULL);
 
114
 
 
115
        last_msg_dec_owns(*list);
 
116
 
 
117
        *list = g_slist_prepend(*list, rec);
 
118
}
 
119
 
 
120
void completion_last_message_add(const char *nick)
 
121
{
 
122
        g_return_if_fail(nick != NULL);
 
123
 
 
124
        last_msg_add(&global_lastmsgs, nick, TRUE, keep_privates_count);
 
125
}
 
126
 
 
127
void completion_last_message_remove(const char *nick)
 
128
{
 
129
        LAST_MSG_REC *rec;
 
130
 
 
131
        g_return_if_fail(nick != NULL);
 
132
 
 
133
        rec = last_msg_find(global_lastmsgs, nick);
 
134
        if (rec != NULL) last_msg_destroy(&global_lastmsgs, rec);
 
135
}
 
136
 
 
137
void completion_last_message_rename(const char *oldnick, const char *newnick)
 
138
{
 
139
        LAST_MSG_REC *rec;
 
140
 
 
141
        g_return_if_fail(oldnick != NULL);
 
142
        g_return_if_fail(newnick != NULL);
 
143
 
 
144
        rec = last_msg_find(global_lastmsgs, oldnick);
 
145
        if (rec != NULL) {
 
146
                g_free(rec->nick);
 
147
                rec->nick = g_strdup(newnick);
 
148
        }
 
149
}
 
150
 
 
151
static void sig_message_public(SERVER_REC *server, const char *msg,
 
152
                               const char *nick, const char *address,
 
153
                               const char *target)
 
154
{
 
155
        CHANNEL_REC *channel;
 
156
        int own;
 
157
 
 
158
        channel = channel_find(server, target);
 
159
        if (channel != NULL) {
 
160
                own = nick_match_msg(channel, msg, server->nick);
 
161
                CHANNEL_LAST_MSG_ADD(channel, nick, own);
 
162
        }
 
163
}
 
164
 
 
165
static void sig_message_join(SERVER_REC *server, const char *channel,
 
166
                             const char *nick, const char *address)
 
167
{
 
168
        CHANNEL_REC *chanrec;
 
169
 
 
170
        chanrec = channel_find(server, channel);
 
171
        if (chanrec != NULL)
 
172
                CHANNEL_LAST_MSG_ADD(chanrec, nick, FALSE);
 
173
}
 
174
 
 
175
static void sig_message_private(SERVER_REC *server, const char *msg,
 
176
                                const char *nick, const char *address)
 
177
{
 
178
        g_return_if_fail(server != NULL);
 
179
        g_return_if_fail(nick != NULL);
 
180
 
 
181
        SERVER_LAST_MSG_ADD(server, nick);
 
182
}
 
183
 
 
184
static void sig_message_own_public(SERVER_REC *server, const char *msg,
 
185
                                   const char *target, const char *origtarget)
 
186
{
 
187
        CHANNEL_REC *channel;
 
188
        NICK_REC *nick;
 
189
        char *p, *msgnick;
 
190
 
 
191
        g_return_if_fail(server != NULL);
 
192
        g_return_if_fail(msg != NULL);
 
193
        if (target == NULL) return;
 
194
 
 
195
        channel = channel_find(server, target);
 
196
        if (channel == NULL)
 
197
                return;
 
198
 
 
199
        /* channel msg - if first word in line is nick,
 
200
           add it to lastmsgs */
 
201
        p = strchr(msg, ' ');
 
202
        if (p != NULL && p != msg) {
 
203
                msgnick = g_strndup(msg, (int) (p-msg));
 
204
                nick = nicklist_find(channel, msgnick);
 
205
                if (nick == NULL && msgnick[1] != '\0') {
 
206
                        /* probably ':' or ',' or some other
 
207
                           char after nick, try without it */
 
208
                        msgnick[strlen(msgnick)-1] = '\0';
 
209
                        nick = nicklist_find(channel, msgnick);
 
210
                }
 
211
                g_free(msgnick);
 
212
                if (nick != NULL && nick != channel->ownnick)
 
213
                        CHANNEL_LAST_MSG_ADD(channel, nick->nick, TRUE);
 
214
        }
 
215
}
 
216
 
 
217
static void sig_message_own_private(SERVER_REC *server, const char *msg,
 
218
                                    const char *target, const char *origtarget)
 
219
{
 
220
        g_return_if_fail(server != NULL);
 
221
 
 
222
        if (target != NULL && query_find(server, target) == NULL)
 
223
                SERVER_LAST_MSG_ADD(server, target);
 
224
}
 
225
 
 
226
static void sig_nick_removed(CHANNEL_REC *channel, NICK_REC *nick)
 
227
{
 
228
        MODULE_CHANNEL_REC *mchannel;
 
229
        LAST_MSG_REC *rec;
 
230
 
 
231
        mchannel = MODULE_DATA(channel);
 
232
        rec = last_msg_find(mchannel->lastmsgs, nick->nick);
 
233
        if (rec != NULL) last_msg_destroy(&mchannel->lastmsgs, rec);
 
234
}
 
235
 
 
236
static void sig_nick_changed(CHANNEL_REC *channel, NICK_REC *nick,
 
237
                             const char *oldnick)
 
238
{
 
239
        MODULE_CHANNEL_REC *mchannel;
 
240
        LAST_MSG_REC *rec;
 
241
 
 
242
        mchannel = MODULE_DATA(channel);
 
243
        rec = last_msg_find(mchannel->lastmsgs, oldnick);
 
244
        if (rec != NULL) {
 
245
                g_free(rec->nick);
 
246
                rec->nick = g_strdup(nick->nick);
 
247
        }
 
248
}
 
249
 
 
250
static int last_msg_cmp(LAST_MSG_REC *m1, LAST_MSG_REC *m2)
 
251
{
 
252
        return m1->time < m2->time ? 1 : -1;
 
253
}
 
254
 
 
255
/* Complete /MSG from specified server, or from
 
256
   global_lastmsgs if server is NULL */
 
257
static void completion_msg_server(GSList **list, SERVER_REC *server,
 
258
                                  const char *nick, const char *prefix)
 
259
{
 
260
        LAST_MSG_REC *msg;
 
261
        GSList *tmp;
 
262
        int len;
 
263
 
 
264
        g_return_if_fail(nick != NULL);
 
265
 
 
266
        len = strlen(nick);
 
267
        tmp = server == NULL ? global_lastmsgs :
 
268
                ((MODULE_SERVER_REC *) MODULE_DATA(server))->lastmsgs;
 
269
        for (; tmp != NULL; tmp = tmp->next) {
 
270
                LAST_MSG_REC *rec = tmp->data;
 
271
 
 
272
                if (len != 0 && g_strncasecmp(rec->nick, nick, len) != 0)
 
273
                        continue;
 
274
 
 
275
                msg = g_new(LAST_MSG_REC, 1);
 
276
                msg->time = rec->time;
 
277
                msg->nick = prefix == NULL || *prefix == '\0' ?
 
278
                        g_strdup(rec->nick) :
 
279
                        g_strconcat(prefix, " ", rec->nick, NULL);
 
280
                *list = g_slist_insert_sorted(*list, msg,
 
281
                                              (GCompareFunc) last_msg_cmp);
 
282
        }
 
283
}
 
284
 
 
285
/* convert list of LAST_MSG_REC's to list of char* nicks. */
 
286
static GList *convert_msglist(GSList *msglist)
 
287
{
 
288
        GList *list;
 
289
 
 
290
        list = NULL;
 
291
        while (msglist != NULL) {
 
292
                LAST_MSG_REC *rec = msglist->data;
 
293
 
 
294
                list = g_list_append(list, rec->nick);
 
295
                msglist = g_slist_remove(msglist, rec);
 
296
                g_free(rec);
 
297
        }
 
298
 
 
299
        return list;
 
300
}
 
301
 
 
302
/* Complete /MSG - if `find_server' is NULL, complete nicks from all servers */
 
303
GList *completion_msg(SERVER_REC *win_server,
 
304
                             SERVER_REC *find_server,
 
305
                             const char *nick, const char *prefix)
 
306
{
 
307
        GSList *tmp, *list;
 
308
        char *newprefix;
 
309
 
 
310
        g_return_val_if_fail(nick != NULL, NULL);
 
311
        if (servers == NULL) return NULL;
 
312
 
 
313
        list = NULL;
 
314
        if (find_server != NULL) {
 
315
                completion_msg_server(&list, find_server, nick, prefix);
 
316
                return convert_msglist(list);
 
317
        }
 
318
 
 
319
        completion_msg_server(&list, NULL, nick, prefix);
 
320
        for (tmp = servers; tmp != NULL; tmp = tmp->next) {
 
321
                SERVER_REC *rec = tmp->data;
 
322
 
 
323
                if (servers->next == NULL && rec == win_server)
 
324
                        newprefix = g_strdup(prefix);
 
325
                else {
 
326
                        newprefix = prefix == NULL ?
 
327
                                g_strdup_printf("-%s", rec->tag) :
 
328
                                g_strdup_printf("%s -%s", prefix, rec->tag);
 
329
                }
 
330
 
 
331
                completion_msg_server(&list, rec, nick, newprefix);
 
332
                g_free_not_null(newprefix);
 
333
        }
 
334
 
 
335
        return convert_msglist(list);
 
336
}
 
337
 
 
338
static void complete_from_nicklist(GList **outlist, CHANNEL_REC *channel,
 
339
                                   const char *nick, const char *suffix)
 
340
{
 
341
        MODULE_CHANNEL_REC *mchannel;
 
342
        GSList *tmp;
 
343
        GList *ownlist;
 
344
        char *str;
 
345
        int len;
 
346
 
 
347
        /* go through the last x nicks who have said something in the channel.
 
348
           nicks of all the "own messages" are placed before others */
 
349
        ownlist = NULL;
 
350
        len = strlen(nick);
 
351
        mchannel = MODULE_DATA(channel);
 
352
        for (tmp = mchannel->lastmsgs; tmp != NULL; tmp = tmp->next) {
 
353
                LAST_MSG_REC *rec = tmp->data;
 
354
 
 
355
                if (g_strncasecmp(rec->nick, nick, len) == 0 &&
 
356
                    glist_find_icase_string(*outlist, rec->nick) == NULL) {
 
357
                        str = g_strconcat(rec->nick, suffix, NULL);
 
358
                        if (completion_lowercase) g_strdown(str);
 
359
                        if (rec->own)
 
360
                                ownlist = g_list_append(ownlist, str);
 
361
                        else
 
362
                                *outlist = g_list_append(*outlist, str);
 
363
                }
 
364
        }
 
365
 
 
366
        *outlist = g_list_concat(ownlist, *outlist);
 
367
}
 
368
 
 
369
static GList *completion_nicks_nonstrict(CHANNEL_REC *channel,
 
370
                                         const char *nick,
 
371
                                         const char *suffix)
 
372
{
 
373
        GSList *nicks, *tmp;
 
374
        GList *list;
 
375
        char *tnick, *str, *in, *out;
 
376
        int len, str_len, tmplen;
 
377
 
 
378
        g_return_val_if_fail(channel != NULL, NULL);
 
379
 
 
380
        list = NULL;
 
381
 
 
382
        /* get all nicks from current channel, strip non alnum chars,
 
383
           compare again and add to completion list on matching */
 
384
        len = strlen(nick);
 
385
        nicks = nicklist_getnicks(channel);
 
386
 
 
387
        str_len = 80; str = g_malloc(str_len+1);
 
388
        for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
 
389
                NICK_REC *rec = tmp->data;
 
390
 
 
391
                tmplen = strlen(rec->nick);
 
392
                if (tmplen > str_len) {
 
393
                        str_len = tmplen*2;
 
394
                        str = g_realloc(str, str_len+1);
 
395
                }
 
396
 
 
397
                /* remove non alnum chars from nick */
 
398
                in = rec->nick; out = str;
 
399
                while (*in != '\0') {
 
400
                        if (i_isalnum(*in))
 
401
                                *out++ = *in;
 
402
                        in++;
 
403
                }
 
404
                *out = '\0';
 
405
 
 
406
                /* add to list if 'cleaned' nick matches */
 
407
                if (g_strncasecmp(str, nick, len) == 0) {
 
408
                        tnick = g_strconcat(rec->nick, suffix, NULL);
 
409
                        if (completion_lowercase)
 
410
                                g_strdown(tnick);
 
411
 
 
412
                        if (glist_find_icase_string(list, tnick) == NULL)
 
413
                                list = g_list_append(list, tnick);
 
414
                        else
 
415
                                g_free(tnick);
 
416
                }
 
417
 
 
418
        }
 
419
        g_free(str);
 
420
        g_slist_free(nicks);
 
421
 
 
422
        return list;
 
423
}
 
424
 
 
425
static GList *completion_channel_nicks(CHANNEL_REC *channel, const char *nick,
 
426
                                       const char *suffix)
 
427
{
 
428
        GSList *nicks, *tmp;
 
429
        GList *list;
 
430
        char *str;
 
431
        int len;
 
432
 
 
433
        g_return_val_if_fail(channel != NULL, NULL);
 
434
        g_return_val_if_fail(nick != NULL, NULL);
 
435
        if (*nick == '\0') return NULL;
 
436
 
 
437
        if (suffix != NULL && *suffix == '\0')
 
438
                suffix = NULL;
 
439
 
 
440
        /* put first the nicks who have recently said something */
 
441
        list = NULL;
 
442
        complete_from_nicklist(&list, channel, nick, suffix);
 
443
 
 
444
        /* and add the rest of the nicks too */
 
445
        len = strlen(nick);
 
446
        nicks = nicklist_getnicks(channel);
 
447
        for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
 
448
                NICK_REC *rec = tmp->data;
 
449
 
 
450
                if (g_strncasecmp(rec->nick, nick, len) == 0 &&
 
451
                    rec != channel->ownnick) {
 
452
                        str = g_strconcat(rec->nick, suffix, NULL);
 
453
                        if (completion_lowercase)
 
454
                                g_strdown(str);
 
455
                        if (glist_find_icase_string(list, str) == NULL)
 
456
                                list = g_list_append(list, str);
 
457
                        else
 
458
                                g_free(str);
 
459
                }
 
460
        }
 
461
        g_slist_free(nicks);
 
462
 
 
463
        /* remove non alphanum chars from nick and search again in case
 
464
           list is still NULL ("foo<tab>" would match "_foo_" f.e.) */
 
465
        if (!completion_strict)
 
466
                list = g_list_concat(list, completion_nicks_nonstrict(channel, nick, suffix));
 
467
        return list;
 
468
}
 
469
 
 
470
/* append all strings in list2 to list1 that already aren't there and
 
471
   free list2 */
 
472
static GList *completion_joinlist(GList *list1, GList *list2)
 
473
{
 
474
        GList *old;
 
475
 
 
476
        old = list2;
 
477
        while (list2 != NULL) {
 
478
                if (!glist_find_icase_string(list1, list2->data))
 
479
                        list1 = g_list_append(list1, list2->data);
 
480
                else
 
481
                        g_free(list2->data);
 
482
 
 
483
                list2 = list2->next;
 
484
        }
 
485
 
 
486
        g_list_free(old);
 
487
        return list1;
 
488
}
 
489
 
 
490
GList *completion_get_servertags(const char *word)
 
491
{
 
492
        GList *list;
 
493
        GSList *tmp;
 
494
        int len;
 
495
 
 
496
        g_return_val_if_fail(word != NULL, NULL);
 
497
 
 
498
        len = strlen(word);
 
499
        list = NULL;
 
500
 
 
501
        for (tmp = servers; tmp != NULL; tmp = tmp->next) {
 
502
                SERVER_REC *rec = tmp->data;
 
503
 
 
504
                if (g_strncasecmp(rec->tag, word, len) == 0) {
 
505
                        if (rec == active_win->active_server)
 
506
                                list = g_list_prepend(list, g_strdup(rec->tag));
 
507
                        else
 
508
                                list = g_list_append(list, g_strdup(rec->tag));
 
509
                }
 
510
 
 
511
        }
 
512
 
 
513
        return list;
 
514
}
 
515
 
 
516
GList *completion_get_channels(SERVER_REC *server, const char *word)
 
517
{
 
518
        GList *list;
 
519
        GSList *tmp;
 
520
        int len;
 
521
 
 
522
        g_return_val_if_fail(word != NULL, NULL);
 
523
 
 
524
        len = strlen(word);
 
525
        list = NULL;
 
526
 
 
527
        /* first get the joined channels */
 
528
        tmp = server == NULL ? NULL : server->channels;
 
529
        for (; tmp != NULL; tmp = tmp->next) {
 
530
                CHANNEL_REC *rec = tmp->data;
 
531
 
 
532
                if (g_strncasecmp(rec->visible_name, word, len) == 0)
 
533
                        list = g_list_append(list, g_strdup(rec->visible_name));
 
534
                else if (g_strncasecmp(rec->name, word, len) == 0)
 
535
                        list = g_list_append(list, g_strdup(rec->name));
 
536
        }
 
537
 
 
538
        /* get channels from setup */
 
539
        for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
 
540
                CHANNEL_SETUP_REC *rec = tmp->data;
 
541
 
 
542
                if (g_strncasecmp(rec->name, word, len) == 0 &&
 
543
                    glist_find_icase_string(list, rec->name) == NULL)
 
544
                        list = g_list_append(list, g_strdup(rec->name));
 
545
 
 
546
        }
 
547
 
 
548
        return list;
 
549
}
 
550
 
 
551
GList *completion_get_aliases(const char *word)
 
552
{
 
553
        CONFIG_NODE *node;
 
554
        GList *list;
 
555
        GSList *tmp;
 
556
        int len;
 
557
 
 
558
        g_return_val_if_fail(word != NULL, NULL);
 
559
 
 
560
        len = strlen(word);
 
561
        list = NULL;
 
562
 
 
563
        /* get the list of all aliases */
 
564
        node = iconfig_node_traverse("aliases", FALSE);
 
565
        tmp = node == NULL ? NULL : config_node_first(node->value);
 
566
        for (; tmp != NULL; tmp = config_node_next(tmp)) {
 
567
                node = tmp->data;
 
568
 
 
569
                if (node->type != NODE_TYPE_KEY)
 
570
                        continue;
 
571
 
 
572
                if (len != 0 && g_strncasecmp(node->key, word, len) != 0)
 
573
                        continue;
 
574
 
 
575
                list = g_list_append(list, g_strdup(node->key));
 
576
        }
 
577
        
 
578
        return list;
 
579
}
 
580
 
 
581
static void complete_window_nicks(GList **list, WINDOW_REC *window,
 
582
                                  const char *word, const char *linestart)
 
583
{
 
584
        CHANNEL_REC *channel;
 
585
        GList *tmplist;
 
586
        GSList *tmp;
 
587
        const char *nicksuffix;
 
588
 
 
589
        nicksuffix = *linestart != '\0' ? NULL : completion_char;
 
590
 
 
591
        channel = CHANNEL(window->active);
 
592
 
 
593
        /* first the active channel */
 
594
        if (channel != NULL) {
 
595
                tmplist = completion_channel_nicks(channel, word, nicksuffix);
 
596
                *list = completion_joinlist(*list, tmplist);
 
597
        }
 
598
 
 
599
        if (nicksuffix != NULL) {
 
600
                /* completing nick at the start of line - probably answering
 
601
                   to some other nick, don't even try to complete from
 
602
                   non-active channels */
 
603
                return;
 
604
        }
 
605
 
 
606
        /* then the rest */
 
607
        for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
 
608
                channel = CHANNEL(tmp->data);
 
609
                if (channel != NULL && tmp->data != window->active) {
 
610
                        tmplist = completion_channel_nicks(channel, word,
 
611
                                                           nicksuffix);
 
612
                        *list = completion_joinlist(*list, tmplist);
 
613
                }
 
614
        }
 
615
}
 
616
 
 
617
static void sig_complete_word(GList **list, WINDOW_REC *window,
 
618
                              const char *word, const char *linestart,
 
619
                              int *want_space)
 
620
{
 
621
        SERVER_REC *server;
 
622
        CHANNEL_REC *channel;
 
623
        QUERY_REC *query;
 
624
        char *prefix;
 
625
 
 
626
        g_return_if_fail(list != NULL);
 
627
        g_return_if_fail(window != NULL);
 
628
        g_return_if_fail(word != NULL);
 
629
        g_return_if_fail(linestart != NULL);
 
630
 
 
631
        server = window->active_server;
 
632
        if (server == NULL && servers != NULL)
 
633
                server = servers->data;
 
634
 
 
635
        if (server != NULL && server_ischannel(server, word)) {
 
636
                /* probably completing a channel name */
 
637
                *list = completion_get_channels(window->active_server, word);
 
638
                return;
 
639
        }
 
640
 
 
641
        server = window->active_server;
 
642
        if (server == NULL || !server->connected)
 
643
                return;
 
644
 
 
645
        if (*linestart == '\0' && *word == '\0') {
 
646
                /* pressed TAB at the start of line - add /MSG */
 
647
                prefix = g_strdup_printf("%cmsg", *cmdchars);
 
648
                *list = completion_msg(server, NULL, "", prefix);
 
649
                if (*list == NULL)
 
650
                        *list = g_list_append(*list, g_strdup(prefix));
 
651
                g_free(prefix);
 
652
 
 
653
                signal_stop();
 
654
                return;
 
655
        }
 
656
 
 
657
        channel = CHANNEL(window->active);
 
658
        query = QUERY(window->active);
 
659
        if (channel == NULL && query != NULL &&
 
660
            g_strncasecmp(word, query->name, strlen(word)) == 0) {
 
661
                /* completion in query */
 
662
                *list = g_list_append(*list, g_strdup(query->name));
 
663
        } else if (channel != NULL) {
 
664
                /* nick completion .. we could also be completing a nick
 
665
                   after /MSG from nicks in channel */
 
666
                complete_window_nicks(list, window, word, linestart);
 
667
        } else if (window->level & MSGLEVEL_MSGS) {
 
668
                /* msgs window, complete /MSG nicks */
 
669
                *list = g_list_concat(completion_msg(server, NULL, word, NULL), *list);
 
670
        }
 
671
 
 
672
        if (*list != NULL) signal_stop();
 
673
}
 
674
 
 
675
static SERVER_REC *line_get_server(const char *line)
 
676
{
 
677
        SERVER_REC *server;
 
678
        char *tag, *ptr;
 
679
 
 
680
        g_return_val_if_fail(line != NULL, NULL);
 
681
        if (*line != '-') return NULL;
 
682
 
 
683
        /* -option found - should be server tag */
 
684
        tag = g_strdup(line+1);
 
685
        ptr = strchr(tag, ' ');
 
686
        if (ptr != NULL) *ptr = '\0';
 
687
 
 
688
        server = server_find_tag(tag);
 
689
 
 
690
        g_free(tag);
 
691
        return server;
 
692
}
 
693
 
 
694
static void sig_complete_msg(GList **list, WINDOW_REC *window,
 
695
                             const char *word, const char *line,
 
696
                             int *want_space)
 
697
{
 
698
        SERVER_REC *server, *msgserver;
 
699
 
 
700
        g_return_if_fail(list != NULL);
 
701
        g_return_if_fail(word != NULL);
 
702
        g_return_if_fail(line != NULL);
 
703
 
 
704
        server = window->active_server;
 
705
        if (server == NULL || !server->connected)
 
706
                return;
 
707
 
 
708
        msgserver = line_get_server(line);
 
709
        *list = completion_msg(server, msgserver, word, NULL);
 
710
        if (*list != NULL) signal_stop();
 
711
}
 
712
 
 
713
static void sig_erase_complete_msg(WINDOW_REC *window, const char *word,
 
714
                                   const char *line)
 
715
{
 
716
        SERVER_REC *server;
 
717
        MODULE_SERVER_REC *mserver;
 
718
        GSList *tmp;
 
719
 
 
720
        server = line_get_server(line);
 
721
        if (server == NULL){
 
722
                server = window->active_server;
 
723
                if (server == NULL)
 
724
                        return;
 
725
        }
 
726
 
 
727
        if (*word == '\0')
 
728
                return;
 
729
 
 
730
        /* check from global list */
 
731
        completion_last_message_remove(word);
 
732
 
 
733
        /* check from server specific list */
 
734
        if (server != NULL) {
 
735
                mserver = MODULE_DATA(server);
 
736
                for (tmp = mserver->lastmsgs; tmp != NULL; tmp = tmp->next) {
 
737
                        LAST_MSG_REC *rec = tmp->data;
 
738
 
 
739
                        if (g_strcasecmp(rec->nick, word) == 0) {
 
740
                                last_msg_destroy(&mserver->lastmsgs, rec);
 
741
                                break;
 
742
                        }
 
743
                }
 
744
 
 
745
        }
 
746
}
 
747
 
 
748
GList *completion_get_chatnets(const char *word)
 
749
{
 
750
        GList *list;
 
751
        GSList *tmp;
 
752
        int len;
 
753
 
 
754
        g_return_val_if_fail(word != NULL, NULL);
 
755
 
 
756
        len = strlen(word);
 
757
        list = NULL;
 
758
 
 
759
        for (tmp = chatnets; tmp != NULL; tmp = tmp->next) {
 
760
                CHATNET_REC *rec = tmp->data;
 
761
 
 
762
                if (g_strncasecmp(rec->name, word, len) == 0)
 
763
                        list = g_list_append(list, g_strdup(rec->name));
 
764
        }
 
765
 
 
766
        return list;
 
767
}
 
768
 
 
769
GList *completion_get_servers(const char *word)
 
770
{
 
771
        GList *list;
 
772
        GSList *tmp;
 
773
        int len;
 
774
 
 
775
        g_return_val_if_fail(word != NULL, NULL);
 
776
 
 
777
        len = strlen(word);
 
778
        list = NULL;
 
779
 
 
780
        for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
 
781
                SERVER_SETUP_REC *rec = tmp->data;
 
782
 
 
783
                if (g_strncasecmp(rec->address, word, len) == 0) 
 
784
                        list = g_list_append(list, g_strdup(rec->address));
 
785
        }
 
786
 
 
787
        return list;
 
788
}
 
789
 
 
790
GList *completion_get_targets(const char *word)
 
791
{
 
792
        CONFIG_NODE *node;
 
793
        GList *list;
 
794
        GSList *tmp;
 
795
        int len;
 
796
 
 
797
        g_return_val_if_fail(word != NULL, NULL);
 
798
 
 
799
        len = strlen(word);
 
800
        list = NULL;
 
801
 
 
802
        /* get the list of all conversion targets */
 
803
        node = iconfig_node_traverse("conversions", FALSE);
 
804
        tmp = node == NULL ? NULL : config_node_first(node->value);
 
805
        for (; tmp != NULL; tmp = config_node_next(tmp)) {
 
806
                node = tmp->data;
 
807
 
 
808
                if (node->type != NODE_TYPE_KEY)
 
809
                        continue;
 
810
 
 
811
                if (len != 0 && g_strncasecmp(node->key, word, len) != 0)
 
812
                        continue;
 
813
 
 
814
                list = g_list_append(list, g_strdup(node->key));
 
815
        }
 
816
        
 
817
        return list;
 
818
}
 
819
 
 
820
static void sig_complete_connect(GList **list, WINDOW_REC *window,
 
821
                                 const char *word, const char *line, 
 
822
                                 int *want_space)
 
823
{
 
824
        g_return_if_fail(list != NULL);
 
825
        g_return_if_fail(word != NULL);
 
826
 
 
827
        *list = completion_get_chatnets(word);
 
828
        *list = g_list_concat(*list, completion_get_servers(word));
 
829
        if (*list != NULL) signal_stop();
 
830
}
 
831
 
 
832
static void sig_complete_tag(GList **list, WINDOW_REC *window,
 
833
                             const char *word, const char *line,
 
834
                             int *want_space)
 
835
{
 
836
        g_return_if_fail(list != NULL);
 
837
        g_return_if_fail(word != NULL);
 
838
 
 
839
        *list = completion_get_servertags(word);
 
840
        if (*list != NULL) signal_stop();
 
841
}
 
842
 
 
843
static void sig_complete_topic(GList **list, WINDOW_REC *window,
 
844
                               const char *word, const char *line,
 
845
                               int *want_space)
 
846
{
 
847
        const char *topic;
 
848
 
 
849
        g_return_if_fail(list != NULL);
 
850
        g_return_if_fail(word != NULL);
 
851
 
 
852
        if (*word == '\0' && IS_CHANNEL(window->active)) {
 
853
                topic = CHANNEL(window->active)->topic;
 
854
                if (topic != NULL) {
 
855
                        *list = g_list_append(NULL, g_strdup(topic));
 
856
                        signal_stop();
 
857
                }
 
858
        }
 
859
}
 
860
 
 
861
static void sig_complete_away(GList **list, WINDOW_REC *window,
 
862
                               const char *word, const char *line,
 
863
                               int *want_space)
 
864
{
 
865
        const char *reason;
 
866
 
 
867
        g_return_if_fail(list != NULL);
 
868
        g_return_if_fail(word != NULL);
 
869
 
 
870
        *want_space = FALSE;
 
871
 
 
872
        if (*word == '\0' && window->active_server != NULL) {
 
873
                reason = SERVER(window->active_server)->away_reason;
 
874
                if (reason != NULL) {
 
875
                        *list = g_list_append(NULL, g_strdup(reason));
 
876
                        signal_stop();
 
877
                }
 
878
        }
 
879
}
 
880
 
 
881
static void sig_complete_unalias(GList **list, WINDOW_REC *window,
 
882
                                const char *word, const char *line,
 
883
                                int *want_space)
 
884
{
 
885
        g_return_if_fail(list != NULL);
 
886
        g_return_if_fail(word != NULL);
 
887
 
 
888
        *list = completion_get_aliases(word);
 
889
        if (*list != NULL) signal_stop();
 
890
}
 
891
 
 
892
static void sig_complete_alias(GList **list, WINDOW_REC *window,
 
893
                                const char *word, const char *line,
 
894
                                int *want_space)
 
895
{
 
896
        const char *definition;
 
897
        
 
898
        g_return_if_fail(list != NULL);
 
899
        g_return_if_fail(word != NULL);
 
900
        g_return_if_fail(line != NULL);
 
901
 
 
902
        if (*line != '\0') {
 
903
                if ((definition = alias_find(line)) != NULL) {
 
904
                        *list = g_list_append(NULL, g_strdup(definition));
 
905
                        signal_stop();
 
906
                }
 
907
        } else {        
 
908
                *list = completion_get_aliases(word);
 
909
                if (*list != NULL) signal_stop();
 
910
        }
 
911
}
 
912
 
 
913
static void sig_complete_channel(GList **list, WINDOW_REC *window,
 
914
                                 const char *word, const char *line,
 
915
                                 int *want_space)
 
916
{
 
917
        g_return_if_fail(list != NULL);
 
918
        g_return_if_fail(word != NULL);
 
919
 
 
920
        *list = completion_get_channels(NULL, word);
 
921
        if (*list != NULL) signal_stop();
 
922
}
 
923
 
 
924
static void sig_complete_server(GList **list, WINDOW_REC *window,
 
925
                                const char *word, const char *line,
 
926
                                int *want_space)
 
927
{
 
928
        g_return_if_fail(list != NULL);
 
929
        g_return_if_fail(word != NULL);
 
930
 
 
931
        *list = completion_get_servers(word);
 
932
        if (*list != NULL) signal_stop();
 
933
}
 
934
 
 
935
static void sig_complete_target(GList **list, WINDOW_REC *window,
 
936
                                const char *word, const char *line,
 
937
                                int *want_space)
 
938
{
 
939
        const char *definition;
 
940
        
 
941
        g_return_if_fail(list != NULL);
 
942
        g_return_if_fail(word != NULL);
 
943
        g_return_if_fail(line != NULL);
 
944
 
 
945
        if (*line != '\0') {
 
946
                if ((definition = iconfig_get_str("conversions", line ,NULL)) != NULL) {
 
947
                        *list = g_list_append(NULL, g_strdup(definition));
 
948
                        signal_stop();
 
949
                }
 
950
        } else {        
 
951
                *list = completion_get_targets(word);
 
952
                if (*list != NULL) signal_stop();
 
953
        }
 
954
}
 
955
 
 
956
/* expand \n, \t and \\ */
 
957
static char *expand_escapes(const char *line, SERVER_REC *server,
 
958
                            WI_ITEM_REC *item)
 
959
{
 
960
        char *ptr, *ret;
 
961
        int chr;
 
962
 
 
963
        ret = ptr = g_malloc(strlen(line)+1);
 
964
        for (; *line != '\0'; line++) {
 
965
                if (*line != '\\') {
 
966
                        *ptr++ = *line;
 
967
                        continue;
 
968
                }
 
969
 
 
970
                line++;
 
971
                if (*line == '\0') {
 
972
                        *ptr++ = '\\';
 
973
                        break;
 
974
                }
 
975
 
 
976
                chr = expand_escape(&line);
 
977
                if (chr == '\r' || chr == '\n') {
 
978
                        /* newline .. we need to send another "send text"
 
979
                           event to handle it (or actually the text before
 
980
                           the newline..) */
 
981
                        if (ret != ptr) {
 
982
                                *ptr = '\0';
 
983
                                signal_emit("send text", 3, ret, server, item);
 
984
                                ptr = ret;
 
985
                        }
 
986
                } else if (chr != -1) {
 
987
                        /* escaping went ok */
 
988
                        *ptr++ = chr;
 
989
                } else {
 
990
                        /* unknown escape, add it as-is */
 
991
                        *ptr++ = '\\';
 
992
                        *ptr++ = *line;
 
993
                }
 
994
        }
 
995
 
 
996
        *ptr = '\0';
 
997
        return ret;
 
998
}
 
999
 
 
1000
static char *auto_complete(CHANNEL_REC *channel, const char *line)
 
1001
{
 
1002
        GList *comp;
 
1003
        const char *p;
 
1004
        char *nick, *ret;
 
1005
 
 
1006
        p = strstr(line, completion_char);
 
1007
        if (p == NULL)
 
1008
                return NULL;
 
1009
 
 
1010
        nick = g_strndup(line, (int) (p-line));
 
1011
 
 
1012
        ret = NULL;
 
1013
        if (nicklist_find(channel, nick) == NULL) {
 
1014
                /* not an exact match, use the first possible completion */
 
1015
                comp = completion_channel_nicks(channel, nick, NULL);
 
1016
                if (comp != NULL) {
 
1017
                        ret = g_strconcat(comp->data, p, NULL);
 
1018
                        g_list_foreach(comp, (GFunc) g_free, NULL);
 
1019
                        g_list_free(comp);
 
1020
                }
 
1021
        }
 
1022
 
 
1023
        g_free(nick);
 
1024
 
 
1025
        return ret;
 
1026
}
 
1027
 
 
1028
static void event_text(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
 
1029
{
 
1030
        char *line, *str, *target;
 
1031
 
 
1032
        g_return_if_fail(data != NULL);
 
1033
 
 
1034
        if (item == NULL)
 
1035
                return;
 
1036
 
 
1037
        if (*data == '\0') {
 
1038
                /* empty line, forget it. */
 
1039
                signal_stop();
 
1040
                return;
 
1041
        }
 
1042
 
 
1043
        line = settings_get_bool("expand_escapes") ?
 
1044
                expand_escapes(data, server, item) : g_strdup(data);
 
1045
 
 
1046
        /* check for automatic nick completion */
 
1047
        if (completion_auto && IS_CHANNEL(item)) {
 
1048
                str = auto_complete(CHANNEL(item), line);
 
1049
                if (str != NULL) {
 
1050
                        g_free(line);
 
1051
                        line = str;
 
1052
                }
 
1053
        }
 
1054
 
 
1055
        /* the nick is quoted in case it contains '-' character. also
 
1056
           spaces should work too now :) The nick is also escaped in case
 
1057
           it contains '\' characters */
 
1058
        target = escape_string(window_item_get_target(item));
 
1059
        str = g_strdup_printf(IS_CHANNEL(item) ? "-channel \"%s\" %s" :
 
1060
                              IS_QUERY(item) ? "-nick \"%s\" %s" : "%s %s",
 
1061
                              target, line);
 
1062
        g_free(target);
 
1063
 
 
1064
        signal_emit("command msg", 3, str, server, item);
 
1065
 
 
1066
        g_free(str);
 
1067
        g_free(line);
 
1068
 
 
1069
        signal_stop();
 
1070
}
 
1071
 
 
1072
static void sig_server_disconnected(SERVER_REC *server)
 
1073
{
 
1074
        MODULE_SERVER_REC *mserver;
 
1075
 
 
1076
        g_return_if_fail(server != NULL);
 
1077
 
 
1078
        mserver = MODULE_DATA(server);
 
1079
        while (mserver->lastmsgs)
 
1080
                last_msg_destroy(&mserver->lastmsgs, mserver->lastmsgs->data);
 
1081
}
 
1082
 
 
1083
static void sig_channel_destroyed(CHANNEL_REC *channel)
 
1084
{
 
1085
        MODULE_CHANNEL_REC *mchannel;
 
1086
 
 
1087
        g_return_if_fail(channel != NULL);
 
1088
 
 
1089
        mchannel = MODULE_DATA(channel);
 
1090
        while (mchannel->lastmsgs != NULL) {
 
1091
                last_msg_destroy(&mchannel->lastmsgs,
 
1092
                                 mchannel->lastmsgs->data);
 
1093
        }
 
1094
}
 
1095
 
 
1096
static void read_settings(void)
 
1097
{
 
1098
        keep_privates_count = settings_get_int("completion_keep_privates");
 
1099
        keep_publics_count = settings_get_int("completion_keep_publics");
 
1100
        completion_lowercase = settings_get_bool("completion_nicks_lowercase");
 
1101
        completion_char = settings_get_str("completion_char");
 
1102
        cmdchars = settings_get_str("cmdchars");
 
1103
        completion_auto = settings_get_bool("completion_auto");
 
1104
        completion_strict = settings_get_bool("completion_strict");
 
1105
 
 
1106
        if (*completion_char == '\0') {
 
1107
                /* this would break.. */
 
1108
                completion_auto = FALSE;
 
1109
        }
 
1110
}
 
1111
 
 
1112
void chat_completion_init(void)
 
1113
{
 
1114
        settings_add_str("completion", "completion_char", ":");
 
1115
        settings_add_bool("completion", "completion_auto", FALSE);
 
1116
        settings_add_int("completion", "completion_keep_publics", 50);
 
1117
        settings_add_int("completion", "completion_keep_privates", 10);
 
1118
        settings_add_bool("completion", "completion_nicks_lowercase", FALSE);
 
1119
        settings_add_bool("completion", "completion_strict", FALSE);
 
1120
 
 
1121
        settings_add_bool("lookandfeel", "expand_escapes", FALSE);
 
1122
 
 
1123
        read_settings();
 
1124
        signal_add("complete word", (SIGNAL_FUNC) sig_complete_word);
 
1125
        signal_add("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
 
1126
        signal_add("complete command query", (SIGNAL_FUNC) sig_complete_msg);
 
1127
        signal_add("complete command action", (SIGNAL_FUNC) sig_complete_msg);
 
1128
        signal_add("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg);
 
1129
        signal_add("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg);
 
1130
        signal_add("complete erase command action", (SIGNAL_FUNC) sig_erase_complete_msg);
 
1131
        signal_add("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
 
1132
        signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect);
 
1133
        signal_add("complete command disconnect", (SIGNAL_FUNC) sig_complete_tag);
 
1134
        signal_add("complete command reconnect", (SIGNAL_FUNC) sig_complete_tag);
 
1135
        signal_add("complete command topic", (SIGNAL_FUNC) sig_complete_topic);
 
1136
        signal_add("complete command away", (SIGNAL_FUNC) sig_complete_away);
 
1137
        signal_add("complete command unalias", (SIGNAL_FUNC) sig_complete_unalias);
 
1138
        signal_add("complete command alias", (SIGNAL_FUNC) sig_complete_alias);
 
1139
        signal_add("complete command window item move", (SIGNAL_FUNC) sig_complete_channel);
 
1140
        signal_add("complete command server add", (SIGNAL_FUNC) sig_complete_server);
 
1141
        signal_add("complete command server remove", (SIGNAL_FUNC) sig_complete_server);
 
1142
        signal_add("complete command recode remove", (SIGNAL_FUNC) sig_complete_target);
 
1143
        signal_add("message public", (SIGNAL_FUNC) sig_message_public);
 
1144
        signal_add("message join", (SIGNAL_FUNC) sig_message_join);
 
1145
        signal_add("message private", (SIGNAL_FUNC) sig_message_private);
 
1146
        signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public);
 
1147
        signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
 
1148
        signal_add("nicklist remove", (SIGNAL_FUNC) sig_nick_removed);
 
1149
        signal_add("nicklist changed", (SIGNAL_FUNC) sig_nick_changed);
 
1150
        signal_add("send text", (SIGNAL_FUNC) event_text);
 
1151
        signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
 
1152
        signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
 
1153
        signal_add("setup changed", (SIGNAL_FUNC) read_settings);
 
1154
}
 
1155
 
 
1156
void chat_completion_deinit(void)
 
1157
{
 
1158
        while (global_lastmsgs != NULL)
 
1159
                last_msg_destroy(&global_lastmsgs, global_lastmsgs->data);
 
1160
 
 
1161
        signal_remove("complete word", (SIGNAL_FUNC) sig_complete_word);
 
1162
        signal_remove("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
 
1163
        signal_remove("complete command query", (SIGNAL_FUNC) sig_complete_msg);
 
1164
        signal_remove("complete command action", (SIGNAL_FUNC) sig_complete_msg);
 
1165
        signal_remove("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg);
 
1166
        signal_remove("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg);
 
1167
        signal_remove("complete erase command action", (SIGNAL_FUNC) sig_erase_complete_msg);
 
1168
        signal_remove("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
 
1169
        signal_remove("complete command server", (SIGNAL_FUNC) sig_complete_connect);
 
1170
        signal_remove("complete command disconnect", (SIGNAL_FUNC) sig_complete_tag);
 
1171
        signal_remove("complete command reconnect", (SIGNAL_FUNC) sig_complete_tag);
 
1172
        signal_remove("complete command topic", (SIGNAL_FUNC) sig_complete_topic);
 
1173
        signal_remove("complete command away", (SIGNAL_FUNC) sig_complete_away);
 
1174
        signal_remove("complete command unalias", (SIGNAL_FUNC) sig_complete_unalias);
 
1175
        signal_remove("complete command alias", (SIGNAL_FUNC) sig_complete_alias);
 
1176
        signal_remove("complete command window item move", (SIGNAL_FUNC) sig_complete_channel);
 
1177
        signal_remove("complete command server add", (SIGNAL_FUNC) sig_complete_server);
 
1178
        signal_remove("complete command server remove", (SIGNAL_FUNC) sig_complete_server);
 
1179
        signal_remove("complete command recode remove", (SIGNAL_FUNC) sig_complete_target);
 
1180
        signal_remove("message public", (SIGNAL_FUNC) sig_message_public);
 
1181
        signal_remove("message join", (SIGNAL_FUNC) sig_message_join);
 
1182
        signal_remove("message private", (SIGNAL_FUNC) sig_message_private);
 
1183
        signal_remove("message own_public", (SIGNAL_FUNC) sig_message_own_public);
 
1184
        signal_remove("message own_private", (SIGNAL_FUNC) sig_message_own_private);
 
1185
        signal_remove("nicklist remove", (SIGNAL_FUNC) sig_nick_removed);
 
1186
        signal_remove("nicklist changed", (SIGNAL_FUNC) sig_nick_changed);
 
1187
        signal_remove("send text", (SIGNAL_FUNC) event_text);
 
1188
        signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
 
1189
        signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
 
1190
        signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
 
1191
}