Added SILC Thread Queue API
[runtime.git] / apps / irssi / src / fe-common / core / fe-channels.c
1 /*
2  fe-channels.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 "module-formats.h"
23 #include "modules.h"
24 #include "signals.h"
25 #include "commands.h"
26 #include "levels.h"
27 #include "misc.h"
28 #include "settings.h"
29
30 #include "chat-protocols.h"
31 #include "chatnets.h"
32 #include "servers.h"
33 #include "channels.h"
34 #include "channels-setup.h"
35 #include "nicklist.h"
36
37 #include "fe-windows.h"
38 #include "fe-channels.h"
39 #include "window-items.h"
40 #include "printtext.h"
41
42 static void signal_channel_created(CHANNEL_REC *channel, void *automatic)
43 {
44         if (window_item_window(channel) == NULL) {
45                 window_item_create((WI_ITEM_REC *) channel,
46                                    GPOINTER_TO_INT(automatic));
47         }
48 }
49
50 static void signal_channel_created_curwin(CHANNEL_REC *channel)
51 {
52         g_return_if_fail(channel != NULL);
53
54         window_item_add(active_win, (WI_ITEM_REC *) channel, FALSE);
55 }
56
57 static void signal_channel_destroyed(CHANNEL_REC *channel)
58 {
59         WINDOW_REC *window;
60
61         g_return_if_fail(channel != NULL);
62
63         window = window_item_window((WI_ITEM_REC *) channel);
64         if (window == NULL)
65                 return;
66
67         window_item_destroy((WI_ITEM_REC *) channel);
68
69         if (channel->joined && !channel->left &&
70             !channel->server->disconnected) {
71                 /* kicked out from channel */
72                 window_bind_add(window, channel->server->tag,
73                                 channel->visible_name);
74         } else if (!channel->joined || channel->left)
75                 window_auto_destroy(window);
76 }
77
78 static void sig_disconnected(SERVER_REC *server)
79 {
80         WINDOW_REC *window;
81         GSList *tmp;
82
83         g_return_if_fail(IS_SERVER(server));
84
85         for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
86                 CHANNEL_REC *channel = tmp->data;
87
88                 window = window_item_window((WI_ITEM_REC *) channel);
89                 window_bind_add(window, server->tag, channel->name);
90         }
91 }
92
93 static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item)
94 {
95         g_return_if_fail(window != NULL);
96         if (item == NULL) return;
97
98         if (g_slist_length(window->items) > 1 && IS_CHANNEL(item)) {
99                 printformat(item->server, item->visible_name,
100                             MSGLEVEL_CLIENTNOTICE,
101                             TXT_TALKING_IN, item->visible_name);
102                 signal_stop();
103         }
104 }
105
106 static void sig_channel_joined(CHANNEL_REC *channel)
107 {
108         if (settings_get_bool("show_names_on_join") &&
109             !channel->session_rejoin)
110                 fe_channels_nicklist(channel, CHANNEL_NICKLIST_FLAG_ALL);
111 }
112
113 static void cmd_wjoin_pre(const char *data, SERVER_REC *server)
114 {
115         GHashTable *optlist;
116         char *nick;
117         void *free_arg;
118
119         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
120                             PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
121                             "join", &optlist, &nick))
122                 return;
123
124         /* kludge for /join -invite -window if there is no invite */
125         if (g_hash_table_lookup(optlist, "invite") &&
126             server != NULL && server->last_invite == NULL) {
127                 cmd_params_free(free_arg);
128                 return;
129         }                    
130         if (g_hash_table_lookup(optlist, "window") != NULL) {
131                 signal_add("channel created",
132                            (SIGNAL_FUNC) signal_channel_created_curwin);
133         }
134         cmd_params_free(free_arg);
135 }
136
137 static void cmd_join(const char *data, SERVER_REC *server)
138 {
139         WINDOW_REC *window;
140         CHANNEL_REC *channel;
141         GHashTable *optlist;
142         char *channelname;
143         void *free_arg;
144
145         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
146                             PARAM_FLAG_UNKNOWN_OPTIONS,
147                             "join", &optlist, &channelname))
148                 return;
149
150         /* -<server tag> */
151         server = cmd_options_get_server("join", optlist, server);
152         
153         channel = channel_find(server, channelname);
154         if (channel != NULL) {
155                 /* already joined to channel, set it active */
156                 window = window_item_window(channel);
157                 if (window != active_win)
158                         window_set_active(window);
159
160                 window_item_set_active(active_win, (WI_ITEM_REC *) channel);
161         }
162         cmd_params_free(free_arg);
163 }
164
165 static void cmd_wjoin_post(const char *data)
166 {
167         GHashTable *optlist;
168         char *nick;
169         void *free_arg;
170
171         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
172                             PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
173                             "join", &optlist, &nick))
174                 return;
175
176         if (g_hash_table_lookup(optlist, "window") != NULL) {
177                 signal_remove("channel created",
178                            (SIGNAL_FUNC) signal_channel_created_curwin);
179         }
180         cmd_params_free(free_arg);
181 }
182
183 static void cmd_channel_list_joined(void)
184 {
185         CHANNEL_REC *channel;
186         GString *nicks;
187         GSList *nicklist, *tmp, *ntmp;
188
189         if (channels == NULL) {
190                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_IN_CHANNELS);
191                 return;
192         }
193
194         /* print active channel */
195         channel = CHANNEL(active_win->active);
196         if (channel != NULL)
197                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
198                             TXT_CURRENT_CHANNEL, channel->visible_name);
199
200         /* print list of all channels, their modes, server tags and nicks */
201         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANLIST_HEADER);
202         for (tmp = channels; tmp != NULL; tmp = tmp->next) {
203                 channel = tmp->data;
204
205                 nicklist = nicklist_getnicks(channel);
206                 nicks = g_string_new(NULL);
207                 for (ntmp = nicklist; ntmp != NULL; ntmp = ntmp->next) {
208                         NICK_REC *rec = ntmp->data;
209
210                         g_string_sprintfa(nicks, "%s ", rec->nick);
211                 }
212
213                 if (nicks->len > 1) g_string_truncate(nicks, nicks->len-1);
214                 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANLIST_LINE,
215                             channel->visible_name, channel->mode,
216                             channel->server->tag, nicks->str);
217
218                 g_slist_free(nicklist);
219                 g_string_free(nicks, TRUE);
220         }
221 }
222
223 /* SYNTAX: CHANNEL LIST */
224 static void cmd_channel_list(void)
225 {
226         GString *str;
227         GSList *tmp;
228
229         str = g_string_new(NULL);
230         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_HEADER);
231         for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
232                 CHANNEL_SETUP_REC *rec = tmp->data;
233
234                 g_string_truncate(str, 0);
235                 if (rec->autojoin)
236                         g_string_append(str, "autojoin, ");
237                 if (rec->botmasks != NULL && *rec->botmasks != '\0')
238                         g_string_sprintfa(str, "bots: %s, ", rec->botmasks);
239                 if (rec->autosendcmd != NULL && *rec->autosendcmd != '\0')
240                         g_string_sprintfa(str, "botcmd: %s, ", rec->autosendcmd);
241
242                 if (str->len > 2) g_string_truncate(str, str->len-2);
243                 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_LINE,
244                             rec->name, rec->chatnet == NULL ? "" : rec->chatnet,
245                             rec->password == NULL ? "" : rec->password, str->str);
246         }
247         g_string_free(str, TRUE);
248         printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_FOOTER);
249 }
250
251 static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
252 {
253         if (*data == '\0')
254                 cmd_channel_list_joined();
255         else if (server != NULL && server_ischannel(server, data)) {
256                 signal_emit("command join", 3, data, server, item);
257         } else {
258                 command_runsub("channel", data, server, item);
259         }
260 }
261
262 /* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
263                        <channel> <network> [<password>] */
264 static void cmd_channel_add(const char *data)
265 {
266         GHashTable *optlist;
267         CHATNET_REC *chatnetrec;
268         CHANNEL_SETUP_REC *rec;
269         char *botarg, *botcmdarg, *chatnet, *channel, *password;
270         void *free_arg;
271
272         if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
273                             "channel add", &optlist, &channel, &chatnet, &password))
274                 return;
275
276         if (*chatnet == '\0' || *channel == '\0')
277                 cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
278
279         chatnetrec = chatnet_find(chatnet);
280         if (chatnetrec == NULL) {
281                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
282                             TXT_UNKNOWN_CHATNET, chatnet);
283                 cmd_params_free(free_arg);
284                 return;
285         }
286
287         botarg = g_hash_table_lookup(optlist, "bots");
288         botcmdarg = g_hash_table_lookup(optlist, "botcmd");
289
290         rec = channel_setup_find(channel, chatnet);
291         if (rec == NULL) {
292                 rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup();
293                 rec->name = g_strdup(channel);
294                 rec->chatnet = g_strdup(chatnet);
295         } else {
296                 if (g_hash_table_lookup(optlist, "bots")) g_free_and_null(rec->botmasks);
297                 if (g_hash_table_lookup(optlist, "botcmd")) g_free_and_null(rec->autosendcmd);
298                 if (*password != '\0') g_free_and_null(rec->password);
299         }
300         if (g_hash_table_lookup(optlist, "auto")) rec->autojoin = TRUE;
301         if (g_hash_table_lookup(optlist, "noauto")) rec->autojoin = FALSE;
302         if (botarg != NULL && *botarg != '\0') rec->botmasks = g_strdup(botarg);
303         if (botcmdarg != NULL && *botcmdarg != '\0') rec->autosendcmd = g_strdup(botcmdarg);
304         if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
305
306         signal_emit("channel add fill", 2, rec, optlist);
307
308         channel_setup_create(rec);
309         printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
310                     TXT_CHANSETUP_ADDED, channel, chatnet);
311
312         cmd_params_free(free_arg);
313 }
314
315 /* SYNTAX: CHANNEL REMOVE <channel> <network> */
316 static void cmd_channel_remove(const char *data)
317 {
318         CHANNEL_SETUP_REC *rec;
319         char *chatnet, *channel;
320         void *free_arg;
321
322         if (!cmd_get_params(data, &free_arg, 2, &channel, &chatnet))
323                 return;
324         if (*chatnet == '\0' || *channel == '\0')
325                 cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
326
327         rec = channel_setup_find(channel, chatnet);
328         if (rec == NULL)
329                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CHANSETUP_NOT_FOUND, channel, chatnet);
330         else {
331                 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CHANSETUP_REMOVED, channel, chatnet);
332                 channel_setup_remove(rec);
333         }
334         cmd_params_free(free_arg);
335 }
336
337 static int get_nick_length(void *data)
338 {
339         return strlen(((NICK_REC *) data)->nick);
340 }
341
342 static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
343 {
344         WINDOW_REC *window;
345         TEXT_DEST_REC dest;
346         GString *str;
347         GSList *tmp;
348         char *format, *stripped, *prefix_format;
349         char *linebuf, nickmode[2] = { 0, 0 };
350         int *columns, cols, rows, last_col_rows, col, row, max_width;
351         int item_extra, linebuf_size, formatnum;
352
353         window = window_find_closest(channel->server, channel->visible_name,
354                                      MSGLEVEL_CLIENTCRAP);
355         max_width = window->width;
356
357         /* get the length of item extra stuff ("[ ] ") */
358         format = format_get_text(MODULE_NAME, NULL,
359                                  channel->server, channel->visible_name,
360                                  TXT_NAMES_NICK, " ", "");
361         stripped = strip_codes(format);
362         item_extra = strlen(stripped);
363         g_free(stripped);
364         g_free(format);
365
366         if (settings_get_int("names_max_width") > 0 &&
367             settings_get_int("names_max_width") < max_width)
368                 max_width = settings_get_int("names_max_width");
369
370         /* remove width of the timestamp from max_width */
371         format_create_dest(&dest, channel->server, channel->visible_name,
372                            MSGLEVEL_CLIENTCRAP, NULL);
373         format = format_get_line_start(current_theme, &dest, time(NULL));
374         if (format != NULL) {
375                 stripped = strip_codes(format);
376                 max_width -= strlen(stripped);
377                 g_free(stripped);
378                 g_free(format);
379         }
380
381         /* remove width of the prefix from max_width */
382         prefix_format = format_get_text(MODULE_NAME, NULL,
383                                         channel->server, channel->visible_name,
384                                         TXT_NAMES_PREFIX,
385                                         channel->visible_name);
386         if (prefix_format != NULL) {
387                 stripped = strip_codes(prefix_format);
388                 max_width -= strlen(stripped);
389                 g_free(stripped);
390         }
391
392         if (max_width <= 0) {
393                 /* we should always have at least some space .. if we
394                    really don't, it won't show properly anyway. */
395                 max_width = 10;
396         }
397
398         /* calculate columns */
399         cols = get_max_column_count(nicklist, get_nick_length, max_width,
400                                     settings_get_int("names_max_columns"),
401                                     item_extra, 3, &columns, &rows);
402         nicklist = columns_sort_list(nicklist, rows);
403
404         /* rows in last column */
405         last_col_rows = rows-(cols*rows-g_slist_length(nicklist));
406         if (last_col_rows == 0)
407                 last_col_rows = rows;
408
409         str = g_string_new(prefix_format);
410         linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
411
412         col = 0; row = 0;
413         for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
414                 NICK_REC *rec = tmp->data;
415
416                 if (rec->other)
417                         nickmode[0] = rec->other;
418                 else if (rec->op)
419                         nickmode[0] = '@';
420                 else if (rec->halfop)
421                         nickmode[0] = '%';
422                 else if (rec->voice)
423                         nickmode[0] = '+';
424                 else
425                         nickmode[0] = ' ';
426                 
427                 if (linebuf_size < columns[col]-item_extra+1) {
428                         linebuf_size = (columns[col]-item_extra+1)*2;
429                         linebuf = g_realloc(linebuf, linebuf_size);
430                 }
431                 memset(linebuf, ' ', columns[col]-item_extra);
432                 linebuf[columns[col]-item_extra] = '\0';
433                 memcpy(linebuf, rec->nick, strlen(rec->nick));
434
435                 formatnum = rec->op ? TXT_NAMES_NICK_OP :
436                         rec->halfop ? TXT_NAMES_NICK_HALFOP :
437                         rec->voice ? TXT_NAMES_NICK_VOICE :
438                         TXT_NAMES_NICK;
439                 format = format_get_text(MODULE_NAME, NULL,
440                                          channel->server,
441                                          channel->visible_name,
442                                          formatnum, nickmode, linebuf);
443                 g_string_append(str, format);
444                 g_free(format);
445
446                 if (++col == cols) {
447                         printtext(channel->server, channel->visible_name,
448                                   MSGLEVEL_CLIENTCRAP, "%s", str->str);
449                         g_string_truncate(str, 0);
450                         if (prefix_format != NULL)
451                                 g_string_assign(str, prefix_format);
452                         col = 0; row++;
453
454                         if (row == last_col_rows)
455                                 cols--;
456                 }
457         }
458
459         if (str->len > strlen(prefix_format)) {
460                 printtext(channel->server, channel->visible_name,
461                           MSGLEVEL_CLIENTCRAP, "%s", str->str);
462         }
463
464         g_slist_free(nicklist);
465         g_string_free(str, TRUE);
466         g_free_not_null(columns);
467         g_free_not_null(prefix_format);
468         g_free(linebuf);
469 }
470
471 void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
472 {
473         NICK_REC *nick;
474         GSList *tmp, *nicklist, *sorted;
475         int nicks, normal, voices, halfops, ops;
476         const char *nick_flags;
477
478         nicks = normal = voices = halfops = ops = 0;
479         nicklist = nicklist_getnicks(channel);
480         sorted = NULL;
481         nick_flags = channel->server->get_nick_flags(channel->server);
482
483         /* filter (for flags) and count ops, halfops, voices */
484         for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
485                 nick = tmp->data;
486
487                 nicks++;
488                 if (nick->op) {
489                         ops++;
490                         if ((flags & CHANNEL_NICKLIST_FLAG_OPS) == 0)
491                                 continue;
492                 } else if (nick->halfop) {
493                         halfops++;
494                         if ((flags & CHANNEL_NICKLIST_FLAG_HALFOPS) == 0)
495                                 continue;
496                 } else if (nick->voice) {
497                         voices++;
498                         if ((flags & CHANNEL_NICKLIST_FLAG_VOICES) == 0)
499                                 continue;
500                 } else {
501                         normal++;
502                         if ((flags & CHANNEL_NICKLIST_FLAG_NORMAL) == 0)
503                                 continue;
504                 }
505
506                 sorted = g_slist_prepend(sorted, nick);
507         }
508         g_slist_free(nicklist);
509
510         /* sort the nicklist */
511 #if GLIB_MAJOR_VERSION < 2
512         /* glib1 doesn't have g_slist_sort_with_data, so non-standard prefixes won't be sorted correctly */
513         sorted = g_slist_sort(sorted, (GCompareFunc)nicklist_compare_glib1);
514 #else
515         sorted = g_slist_sort_with_data(sorted, (GCompareDataFunc) nicklist_compare, (void *)nick_flags);
516 #endif
517
518         /* display the nicks */
519         if ((flags & CHANNEL_NICKLIST_FLAG_COUNT) == 0) {
520                 printformat(channel->server, channel->visible_name,
521                             MSGLEVEL_CLIENTCRAP, TXT_NAMES,
522                             channel->visible_name,
523                             nicks, ops, halfops, voices, normal);
524                 display_sorted_nicks(channel, sorted);
525         }
526         g_slist_free(sorted);
527
528         printformat(channel->server, channel->visible_name,
529                     MSGLEVEL_CLIENTNOTICE, TXT_ENDOFNAMES,
530                     channel->visible_name, nicks, ops, halfops, voices, normal);
531 }
532
533 /* SYNTAX: NAMES [-count | -ops -halfops -voices -normal] [<channels> | **] */
534 static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
535 {
536         CHANNEL_REC *chanrec;
537         GHashTable *optlist;
538         GString *unknowns;
539         char *channel, **channels, **tmp;
540         int flags;
541         void *free_arg;
542
543         g_return_if_fail(data != NULL);
544         if (!IS_SERVER(server) || !server->connected)
545                 cmd_return_error(CMDERR_NOT_CONNECTED);
546
547         if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
548                             "names", &optlist, &channel))
549                 return;
550
551         if (strcmp(channel, "*") == 0 || *channel == '\0') {
552                 if (!IS_CHANNEL(item))
553                         cmd_param_error(CMDERR_NOT_JOINED);
554
555                 channel = CHANNEL(item)->name;
556         }
557
558         flags = 0;
559         if (g_hash_table_lookup(optlist, "ops") != NULL)
560                 flags |= CHANNEL_NICKLIST_FLAG_OPS;
561         if (g_hash_table_lookup(optlist, "halfops") != NULL)
562                 flags |= CHANNEL_NICKLIST_FLAG_HALFOPS;
563         if (g_hash_table_lookup(optlist, "voices") != NULL)
564                 flags |= CHANNEL_NICKLIST_FLAG_VOICES;
565         if (g_hash_table_lookup(optlist, "normal") != NULL)
566                 flags |= CHANNEL_NICKLIST_FLAG_NORMAL;
567         if (g_hash_table_lookup(optlist, "count") != NULL)
568                 flags |= CHANNEL_NICKLIST_FLAG_COUNT;
569
570         if (flags == 0) flags = CHANNEL_NICKLIST_FLAG_ALL;
571
572         unknowns = g_string_new(NULL);
573
574         channels = g_strsplit(channel, ",", -1);
575         for (tmp = channels; *tmp != NULL; tmp++) {
576                 chanrec = channel_find(server, *tmp);
577                 if (chanrec == NULL)
578                         g_string_sprintfa(unknowns, "%s,", *tmp);
579                 else {
580                         fe_channels_nicklist(chanrec, flags);
581                         signal_stop();
582                 }
583         }
584         g_strfreev(channels);
585
586         if (unknowns->len > 1)
587                 g_string_truncate(unknowns, unknowns->len-1);
588
589         if (unknowns->len > 0 && strcmp(channel, unknowns->str) != 0)
590                 signal_emit("command names", 3, unknowns->str, server, item);
591         g_string_free(unknowns, TRUE);
592
593         cmd_params_free(free_arg);
594 }
595
596 /* SYNTAX: CYCLE [<channel>] [<message>] */
597 static void cmd_cycle(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
598 {
599         CHANNEL_REC *chanrec;
600         char *channame, *msg, *joindata;
601         void *free_arg;
602
603         g_return_if_fail(data != NULL);
604         if (!IS_SERVER(server) || !server->connected)
605                 cmd_return_error(CMDERR_NOT_CONNECTED);
606
607         if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN,
608                             item, &channame, &msg))
609                 return;
610         if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
611
612         chanrec = channel_find(server, channame);
613         if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND);
614
615         joindata = chanrec->get_join_data(chanrec);
616         window_bind_add(window_item_window(chanrec),
617                         chanrec->server->tag, chanrec->name);
618
619         /* FIXME: kludgy kludgy... */
620         signal_emit("command part", 3, data, server, item);
621
622         if (g_slist_find(channels, chanrec) != NULL) {
623                 chanrec->left = TRUE;
624                 channel_destroy(chanrec);
625         }
626
627         server->channels_join(server, joindata, FALSE);
628         g_free(joindata);
629
630         cmd_params_free(free_arg);
631 }
632
633 void fe_channels_init(void)
634 {
635         settings_add_bool("lookandfeel", "autoclose_windows", TRUE);
636         settings_add_bool("lookandfeel", "show_names_on_join", TRUE);
637         settings_add_int("lookandfeel", "names_max_columns", 6);
638         settings_add_int("lookandfeel", "names_max_width", 0);
639
640         signal_add("channel created", (SIGNAL_FUNC) signal_channel_created);
641         signal_add("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
642         signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
643         signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
644         signal_add_last("channel joined", (SIGNAL_FUNC) sig_channel_joined);
645
646         command_bind_first("join", NULL, (SIGNAL_FUNC) cmd_wjoin_pre);
647         command_bind("join", NULL, (SIGNAL_FUNC) cmd_join);
648         command_bind_last("join", NULL, (SIGNAL_FUNC) cmd_wjoin_post);
649         command_bind("channel", NULL, (SIGNAL_FUNC) cmd_channel);
650         command_bind("channel add", NULL, (SIGNAL_FUNC) cmd_channel_add);
651         command_bind("channel remove", NULL, (SIGNAL_FUNC) cmd_channel_remove);
652         command_bind("channel list", NULL, (SIGNAL_FUNC) cmd_channel_list);
653         command_bind("names", NULL, (SIGNAL_FUNC) cmd_names);
654         command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
655
656         command_set_options("channel add", "auto noauto -bots -botcmd");
657         command_set_options("names", "count ops halfops voices normal");
658         command_set_options("join", "window");
659 }
660
661 void fe_channels_deinit(void)
662 {
663         signal_remove("channel created", (SIGNAL_FUNC) signal_channel_created);
664         signal_remove("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
665         signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
666         signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
667         signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined);
668
669         command_unbind("join", (SIGNAL_FUNC) cmd_wjoin_pre);
670         command_unbind("join", (SIGNAL_FUNC) cmd_join);
671         command_unbind("join", (SIGNAL_FUNC) cmd_wjoin_post);
672         command_unbind("channel", (SIGNAL_FUNC) cmd_channel);
673         command_unbind("channel add", (SIGNAL_FUNC) cmd_channel_add);
674         command_unbind("channel remove", (SIGNAL_FUNC) cmd_channel_remove);
675         command_unbind("channel list", (SIGNAL_FUNC) cmd_channel_list);
676         command_unbind("names", (SIGNAL_FUNC) cmd_names);
677         command_unbind("cycle", (SIGNAL_FUNC) cmd_cycle);
678 }