4 Copyright (C) 1999-2000 Timo Sirainen
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.
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.
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include "module-formats.h"
30 #include "chat-protocols.h"
34 #include "channels-setup.h"
37 #include "fe-windows.h"
38 #include "fe-channels.h"
39 #include "window-items.h"
40 #include "printtext.h"
42 static void signal_channel_created(CHANNEL_REC *channel, void *automatic)
44 if (window_item_window(channel) == NULL) {
45 window_item_create((WI_ITEM_REC *) channel,
46 GPOINTER_TO_INT(automatic));
50 static void signal_channel_created_curwin(CHANNEL_REC *channel)
52 g_return_if_fail(channel != NULL);
54 window_item_add(active_win, (WI_ITEM_REC *) channel, FALSE);
57 static void signal_channel_destroyed(CHANNEL_REC *channel)
61 g_return_if_fail(channel != NULL);
63 window = window_item_window((WI_ITEM_REC *) channel);
67 window_item_destroy((WI_ITEM_REC *) channel);
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);
78 static void sig_disconnected(SERVER_REC *server)
83 g_return_if_fail(IS_SERVER(server));
85 for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
86 CHANNEL_REC *channel = tmp->data;
88 window = window_item_window((WI_ITEM_REC *) channel);
89 window_bind_add(window, server->tag, channel->name);
93 static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item)
95 g_return_if_fail(window != NULL);
96 if (item == NULL) return;
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);
106 static void sig_channel_joined(CHANNEL_REC *channel)
108 if (settings_get_bool("show_names_on_join") &&
109 !channel->session_rejoin)
110 fe_channels_nicklist(channel, CHANNEL_NICKLIST_FLAG_ALL);
113 /* SYNTAX: JOIN [-window] [-invite] [-<server tag>] <channels> [<keys>] */
114 static void cmd_join(const char *data, SERVER_REC *server)
117 CHANNEL_REC *channel;
124 if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS |
125 PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST,
126 "join", &optlist, &pdata))
129 invite = g_hash_table_lookup(optlist, "invite") != NULL;
130 samewindow = g_hash_table_lookup(optlist, "window") != NULL;
131 if (!invite && *pdata == '\0')
132 cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
135 server = cmd_options_get_server("join", optlist, server);
137 channel = channel_find(server, pdata);
138 if (channel != NULL) {
139 /* already joined to channel, set it active */
140 window = window_item_window(channel);
141 if (window != active_win)
142 window_set_active(window);
144 window_item_set_active(active_win, (WI_ITEM_REC *) channel);
147 if (server == NULL || !server->connected)
148 cmd_param_error(CMDERR_NOT_CONNECTED);
150 if (server->last_invite == NULL) {
151 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_INVITED);
153 cmd_params_free(free_arg);
156 pdata = server->last_invite;
159 signal_add("channel created",
160 (SIGNAL_FUNC) signal_channel_created_curwin);
161 server->channels_join(server, pdata, FALSE);
163 signal_remove("channel created",
164 (SIGNAL_FUNC) signal_channel_created_curwin);
166 cmd_params_free(free_arg);
169 static void cmd_channel_list_joined(void)
171 CHANNEL_REC *channel;
173 GSList *nicklist, *tmp, *ntmp;
175 if (channels == NULL) {
176 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_NOT_IN_CHANNELS);
180 /* print active channel */
181 channel = CHANNEL(active_win->active);
183 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
184 TXT_CURRENT_CHANNEL, channel->visible_name);
186 /* print list of all channels, their modes, server tags and nicks */
187 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANLIST_HEADER);
188 for (tmp = channels; tmp != NULL; tmp = tmp->next) {
191 nicklist = nicklist_getnicks(channel);
192 nicks = g_string_new(NULL);
193 for (ntmp = nicklist; ntmp != NULL; ntmp = ntmp->next) {
194 NICK_REC *rec = ntmp->data;
196 g_string_append_printf(nicks, "%s ", rec->nick);
199 if (nicks->len > 1) g_string_truncate(nicks, nicks->len-1);
200 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANLIST_LINE,
201 channel->visible_name, channel->mode,
202 channel->server->tag, nicks->str);
204 g_slist_free(nicklist);
205 g_string_free(nicks, TRUE);
209 /* SYNTAX: CHANNEL LIST */
210 static void cmd_channel_list(void)
215 str = g_string_new(NULL);
216 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_HEADER);
217 for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
218 CHANNEL_SETUP_REC *rec = tmp->data;
220 g_string_truncate(str, 0);
222 g_string_append(str, "autojoin, ");
223 if (rec->botmasks != NULL && *rec->botmasks != '\0')
224 g_string_append_printf(str, "bots: %s, ", rec->botmasks);
225 if (rec->autosendcmd != NULL && *rec->autosendcmd != '\0')
226 g_string_append_printf(str, "botcmd: %s, ", rec->autosendcmd);
228 if (str->len > 2) g_string_truncate(str, str->len-2);
229 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_LINE,
230 rec->name, rec->chatnet == NULL ? "" : rec->chatnet,
231 rec->password == NULL ? "" : rec->password, str->str);
233 g_string_free(str, TRUE);
234 printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_CHANSETUP_FOOTER);
237 static void cmd_channel(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
240 cmd_channel_list_joined();
241 else if (server != NULL && server_ischannel(server, data)) {
242 signal_emit("command join", 3, data, server, item);
244 command_runsub("channel", data, server, item);
248 /* SYNTAX: CHANNEL ADD [-auto | -noauto] [-bots <masks>] [-botcmd <command>]
249 <channel> <network> [<password>] */
250 static void cmd_channel_add(const char *data)
253 CHATNET_REC *chatnetrec;
254 CHANNEL_SETUP_REC *rec;
255 char *botarg, *botcmdarg, *chatnet, *channel, *password;
258 if (!cmd_get_params(data, &free_arg, 3 | PARAM_FLAG_OPTIONS,
259 "channel add", &optlist, &channel, &chatnet, &password))
262 if (*chatnet == '\0' || *channel == '\0')
263 cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
265 chatnetrec = chatnet_find(chatnet);
266 if (chatnetrec == NULL) {
267 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
268 TXT_UNKNOWN_CHATNET, chatnet);
269 cmd_params_free(free_arg);
273 botarg = g_hash_table_lookup(optlist, "bots");
274 botcmdarg = g_hash_table_lookup(optlist, "botcmd");
276 rec = channel_setup_find(channel, chatnet);
278 rec = CHAT_PROTOCOL(chatnetrec)->create_channel_setup();
279 rec->name = g_strdup(channel);
280 rec->chatnet = g_strdup(chatnet);
282 if (g_hash_table_lookup(optlist, "bots")) g_free_and_null(rec->botmasks);
283 if (g_hash_table_lookup(optlist, "botcmd")) g_free_and_null(rec->autosendcmd);
284 if (*password != '\0') g_free_and_null(rec->password);
286 if (g_hash_table_lookup(optlist, "auto")) rec->autojoin = TRUE;
287 if (g_hash_table_lookup(optlist, "noauto")) rec->autojoin = FALSE;
288 if (botarg != NULL && *botarg != '\0') rec->botmasks = g_strdup(botarg);
289 if (botcmdarg != NULL && *botcmdarg != '\0') rec->autosendcmd = g_strdup(botcmdarg);
290 if (*password != '\0' && strcmp(password, "-") != 0) rec->password = g_strdup(password);
292 signal_emit("channel add fill", 2, rec, optlist);
294 channel_setup_create(rec);
295 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE,
296 TXT_CHANSETUP_ADDED, channel, chatnet);
298 cmd_params_free(free_arg);
301 /* SYNTAX: CHANNEL REMOVE <channel> <network> */
302 static void cmd_channel_remove(const char *data)
304 CHANNEL_SETUP_REC *rec;
305 char *chatnet, *channel;
308 if (!cmd_get_params(data, &free_arg, 2, &channel, &chatnet))
310 if (*chatnet == '\0' || *channel == '\0')
311 cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
313 rec = channel_setup_find(channel, chatnet);
315 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CHANSETUP_NOT_FOUND, channel, chatnet);
317 printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, TXT_CHANSETUP_REMOVED, channel, chatnet);
318 channel_setup_remove(rec);
320 cmd_params_free(free_arg);
323 static int get_nick_length(void *data)
325 return strlen(((NICK_REC *) data)->nick);
328 static void display_sorted_nicks(CHANNEL_REC *channel, GSList *nicklist)
334 char *format, *stripped, *prefix_format;
335 char *linebuf, nickmode[2] = { 0, 0 };
336 int *columns, cols, rows, last_col_rows, col, row, max_width;
337 int item_extra, linebuf_size, formatnum;
339 window = window_find_closest(channel->server, channel->visible_name,
340 MSGLEVEL_CLIENTCRAP);
341 max_width = window->width;
343 /* get the length of item extra stuff ("[ ] ") */
344 format = format_get_text(MODULE_NAME, NULL,
345 channel->server, channel->visible_name,
346 TXT_NAMES_NICK, " ", "");
347 stripped = strip_codes(format);
348 item_extra = strlen(stripped);
352 if (settings_get_int("names_max_width") > 0 &&
353 settings_get_int("names_max_width") < max_width)
354 max_width = settings_get_int("names_max_width");
356 /* remove width of the timestamp from max_width */
357 format_create_dest(&dest, channel->server, channel->visible_name,
358 MSGLEVEL_CLIENTCRAP, NULL);
359 format = format_get_line_start(current_theme, &dest, time(NULL));
360 if (format != NULL) {
361 stripped = strip_codes(format);
362 max_width -= strlen(stripped);
367 /* remove width of the prefix from max_width */
368 prefix_format = format_get_text(MODULE_NAME, NULL,
369 channel->server, channel->visible_name,
371 channel->visible_name);
372 if (prefix_format != NULL) {
373 stripped = strip_codes(prefix_format);
374 max_width -= strlen(stripped);
378 if (max_width <= 0) {
379 /* we should always have at least some space .. if we
380 really don't, it won't show properly anyway. */
384 /* calculate columns */
385 cols = get_max_column_count(nicklist, get_nick_length, max_width,
386 settings_get_int("names_max_columns"),
387 item_extra, 3, &columns, &rows);
388 nicklist = columns_sort_list(nicklist, rows);
390 /* rows in last column */
391 last_col_rows = rows-(cols*rows-g_slist_length(nicklist));
392 if (last_col_rows == 0)
393 last_col_rows = rows;
395 str = g_string_new(prefix_format);
396 linebuf_size = max_width+1; linebuf = g_malloc(linebuf_size);
399 for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
400 NICK_REC *rec = tmp->data;
402 if (rec->prefixes[0])
403 nickmode[0] = rec->prefixes[0];
407 if (linebuf_size < columns[col]-item_extra+1) {
408 linebuf_size = (columns[col]-item_extra+1)*2;
409 linebuf = g_realloc(linebuf, linebuf_size);
411 memset(linebuf, ' ', columns[col]-item_extra);
412 linebuf[columns[col]-item_extra] = '\0';
413 memcpy(linebuf, rec->nick, strlen(rec->nick));
415 formatnum = rec->op ? TXT_NAMES_NICK_OP :
416 rec->halfop ? TXT_NAMES_NICK_HALFOP :
417 rec->voice ? TXT_NAMES_NICK_VOICE :
419 format = format_get_text(MODULE_NAME, NULL,
421 channel->visible_name,
422 formatnum, nickmode, linebuf);
423 g_string_append(str, format);
427 printtext(channel->server, channel->visible_name,
428 MSGLEVEL_CLIENTCRAP, "%s", str->str);
429 g_string_truncate(str, 0);
430 if (prefix_format != NULL)
431 g_string_assign(str, prefix_format);
434 if (row == last_col_rows)
439 if (str->len > strlen(prefix_format)) {
440 printtext(channel->server, channel->visible_name,
441 MSGLEVEL_CLIENTCRAP, "%s", str->str);
444 g_slist_free(nicklist);
445 g_string_free(str, TRUE);
446 g_free_not_null(columns);
447 g_free_not_null(prefix_format);
451 void fe_channels_nicklist(CHANNEL_REC *channel, int flags)
454 GSList *tmp, *nicklist, *sorted;
455 int nicks, normal, voices, halfops, ops;
456 const char *nick_flags;
458 nicks = normal = voices = halfops = ops = 0;
459 nicklist = nicklist_getnicks(channel);
461 nick_flags = channel->server->get_nick_flags(channel->server);
463 /* filter (for flags) and count ops, halfops, voices */
464 for (tmp = nicklist; tmp != NULL; tmp = tmp->next) {
470 if ((flags & CHANNEL_NICKLIST_FLAG_OPS) == 0)
472 } else if (nick->halfop) {
474 if ((flags & CHANNEL_NICKLIST_FLAG_HALFOPS) == 0)
476 } else if (nick->voice) {
478 if ((flags & CHANNEL_NICKLIST_FLAG_VOICES) == 0)
482 if ((flags & CHANNEL_NICKLIST_FLAG_NORMAL) == 0)
486 sorted = g_slist_prepend(sorted, nick);
488 g_slist_free(nicklist);
490 /* sort the nicklist */
491 sorted = g_slist_sort_with_data(sorted, (GCompareDataFunc) nicklist_compare, (void *)nick_flags);
493 /* display the nicks */
494 if ((flags & CHANNEL_NICKLIST_FLAG_COUNT) == 0) {
495 printformat(channel->server, channel->visible_name,
496 MSGLEVEL_CLIENTCRAP, TXT_NAMES,
497 channel->visible_name,
498 nicks, ops, halfops, voices, normal);
499 display_sorted_nicks(channel, sorted);
501 g_slist_free(sorted);
503 printformat(channel->server, channel->visible_name,
504 MSGLEVEL_CLIENTNOTICE, TXT_ENDOFNAMES,
505 channel->visible_name, nicks, ops, halfops, voices, normal);
508 /* SYNTAX: NAMES [-count | -ops -halfops -voices -normal] [<channels> | **] */
509 static void cmd_names(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
511 CHANNEL_REC *chanrec;
514 char *channel, **channels, **tmp;
518 g_return_if_fail(data != NULL);
519 if (!IS_SERVER(server) || !server->connected)
520 cmd_return_error(CMDERR_NOT_CONNECTED);
522 if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS,
523 "names", &optlist, &channel))
526 if (strcmp(channel, "*") == 0 || *channel == '\0') {
527 if (!IS_CHANNEL(item))
528 cmd_param_error(CMDERR_NOT_JOINED);
530 channel = CHANNEL(item)->name;
534 if (g_hash_table_lookup(optlist, "ops") != NULL)
535 flags |= CHANNEL_NICKLIST_FLAG_OPS;
536 if (g_hash_table_lookup(optlist, "halfops") != NULL)
537 flags |= CHANNEL_NICKLIST_FLAG_HALFOPS;
538 if (g_hash_table_lookup(optlist, "voices") != NULL)
539 flags |= CHANNEL_NICKLIST_FLAG_VOICES;
540 if (g_hash_table_lookup(optlist, "normal") != NULL)
541 flags |= CHANNEL_NICKLIST_FLAG_NORMAL;
542 if (g_hash_table_lookup(optlist, "count") != NULL)
543 flags |= CHANNEL_NICKLIST_FLAG_COUNT;
545 if (flags == 0) flags = CHANNEL_NICKLIST_FLAG_ALL;
547 unknowns = g_string_new(NULL);
549 channels = g_strsplit(channel, ",", -1);
550 for (tmp = channels; *tmp != NULL; tmp++) {
551 chanrec = channel_find(server, *tmp);
553 g_string_append_printf(unknowns, "%s,", *tmp);
555 fe_channels_nicklist(chanrec, flags);
559 g_strfreev(channels);
561 if (unknowns->len > 1)
562 g_string_truncate(unknowns, unknowns->len-1);
564 if (unknowns->len > 0 && strcmp(channel, unknowns->str) != 0)
565 signal_emit("command names", 3, unknowns->str, server, item);
566 g_string_free(unknowns, TRUE);
568 cmd_params_free(free_arg);
571 /* SYNTAX: CYCLE [<channel>] [<message>] */
572 static void cmd_cycle(const char *data, SERVER_REC *server, WI_ITEM_REC *item)
574 CHANNEL_REC *chanrec;
575 char *channame, *msg, *joindata;
578 g_return_if_fail(data != NULL);
579 if (!IS_SERVER(server) || !server->connected)
580 cmd_return_error(CMDERR_NOT_CONNECTED);
582 if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTCHAN,
583 item, &channame, &msg))
585 if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
587 chanrec = channel_find(server, channame);
588 if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND);
590 joindata = chanrec->get_join_data(chanrec);
591 window_bind_add(window_item_window(chanrec),
592 chanrec->server->tag, chanrec->name);
594 /* FIXME: kludgy kludgy... */
595 signal_emit("command part", 3, data, server, item);
597 if (g_slist_find(channels, chanrec) != NULL) {
598 chanrec->left = TRUE;
599 channel_destroy(chanrec);
602 server->channels_join(server, joindata, FALSE);
605 cmd_params_free(free_arg);
608 void fe_channels_init(void)
610 settings_add_bool("lookandfeel", "autoclose_windows", TRUE);
611 settings_add_bool("lookandfeel", "show_names_on_join", TRUE);
612 settings_add_int("lookandfeel", "names_max_columns", 6);
613 settings_add_int("lookandfeel", "names_max_width", 0);
615 signal_add("channel created", (SIGNAL_FUNC) signal_channel_created);
616 signal_add("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
617 signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
618 signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
619 signal_add_last("channel joined", (SIGNAL_FUNC) sig_channel_joined);
621 command_bind("join", NULL, (SIGNAL_FUNC) cmd_join);
622 command_bind("channel", NULL, (SIGNAL_FUNC) cmd_channel);
623 command_bind("channel add", NULL, (SIGNAL_FUNC) cmd_channel_add);
624 command_bind("channel remove", NULL, (SIGNAL_FUNC) cmd_channel_remove);
625 command_bind("channel list", NULL, (SIGNAL_FUNC) cmd_channel_list);
626 command_bind("names", NULL, (SIGNAL_FUNC) cmd_names);
627 command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
629 command_set_options("channel add", "auto noauto -bots -botcmd");
630 command_set_options("names", "count ops halfops voices normal");
631 command_set_options("join", "invite window");
634 void fe_channels_deinit(void)
636 signal_remove("channel created", (SIGNAL_FUNC) signal_channel_created);
637 signal_remove("channel destroyed", (SIGNAL_FUNC) signal_channel_destroyed);
638 signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
639 signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
640 signal_remove("channel joined", (SIGNAL_FUNC) sig_channel_joined);
642 command_unbind("join", (SIGNAL_FUNC) cmd_join);
643 command_unbind("channel", (SIGNAL_FUNC) cmd_channel);
644 command_unbind("channel add", (SIGNAL_FUNC) cmd_channel_add);
645 command_unbind("channel remove", (SIGNAL_FUNC) cmd_channel_remove);
646 command_unbind("channel list", (SIGNAL_FUNC) cmd_channel_list);
647 command_unbind("names", (SIGNAL_FUNC) cmd_names);
648 command_unbind("cycle", (SIGNAL_FUNC) cmd_cycle);