diff options
author | Timo Sirainen <cras@irssi.org> | 2000-04-26 08:03:38 +0000 |
---|---|---|
committer | cras <cras@dbcabf3a-b0e7-0310-adc4-f8d773084564> | 2000-04-26 08:03:38 +0000 |
commit | c95034c6de1bf72536595e1e3431d8ec64b9880e (patch) | |
tree | e51aa4528257ed8aa9d53640649519f299aaf0c7 /src/fe-common/core | |
parent | d01b094151705d433bc43cae9eeb304e6f110a17 (diff) | |
download | irssi-c95034c6de1bf72536595e1e3431d8ec64b9880e.zip |
..adding new files..
git-svn-id: http://svn.irssi.org/repos/irssi/trunk@171 dbcabf3a-b0e7-0310-adc4-f8d773084564
Diffstat (limited to 'src/fe-common/core')
28 files changed, 4547 insertions, 0 deletions
diff --git a/src/fe-common/core/Makefile.am b/src/fe-common/core/Makefile.am new file mode 100644 index 00000000..025a9dda --- /dev/null +++ b/src/fe-common/core/Makefile.am @@ -0,0 +1,38 @@ +noinst_LTLIBRARIES = libfe_common_core.la + +INCLUDES = \ + $(GLIB_CFLAGS) \ + -I$(top_srcdir)/src -I$(top_srcdir)/src/core/ \ + -DHELPDIR=\""$(datadir)/irssi/help"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" + +libfe_common_core_la_SOURCES = \ + autorun.c \ + command-history.c \ + fe-common-core.c \ + fe-core-commands.c \ + fe-log.c \ + fe-server.c \ + fe-settings.c \ + hilight-text.c \ + keyboard.c \ + module-formats.c \ + nick-hilight.c \ + printtext.c \ + themes.c \ + translation.c \ + window-items.c \ + windows.c + +noinst_HEADERS = \ + command-history.h \ + fe-common-core.h \ + hilight-text.h \ + keyboard.h \ + module-formats.h \ + module.h \ + printtext.h \ + themes.h \ + translation.h \ + window-items.h \ + windows.h diff --git a/src/fe-common/core/autorun.c b/src/fe-common/core/autorun.c new file mode 100644 index 00000000..f1e5d88d --- /dev/null +++ b/src/fe-common/core/autorun.c @@ -0,0 +1,62 @@ +/* + autorun.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "line-split.h" +#include "special-vars.h" + +#include "windows.h" + +static void sig_autorun(void) +{ + char tmpbuf[1024], *str, *path; + LINEBUF_REC *buffer = NULL; + int f, ret, recvlen; + + /* open ~/.irssi/startup and run all commands in it */ + path = g_strdup_printf("%s/.irssi/startup", g_get_home_dir()); + f = open(path, O_RDONLY); + g_free(path); + if (f == -1) { + /* file not found */ + return; + } + + do { + recvlen = read(f, tmpbuf, sizeof(tmpbuf)); + + ret = line_split(tmpbuf, recvlen, &str, &buffer); + eval_special_string(str, "", active_win->active_server, active_win->active); + } while (ret > 0); + line_split_free(buffer); + + close(f); +} + +void autorun_init(void) +{ + signal_add_last("irssi init finished", (SIGNAL_FUNC) sig_autorun); +} + +void autorun_deinit(void) +{ + signal_remove("irssi init finished", (SIGNAL_FUNC) sig_autorun); +} diff --git a/src/fe-common/core/command-history.c b/src/fe-common/core/command-history.c new file mode 100644 index 00000000..01ae46d7 --- /dev/null +++ b/src/fe-common/core/command-history.c @@ -0,0 +1,182 @@ +/* + command-history.c : irssi + + Copyright (C) 1999 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "special-vars.h" +#include "settings.h" + +#include "windows.h" +#include "window-items.h" + +/* command history */ +static GList *cmdhist, *histpos; +static int histlines; +static int window_history; + +void command_history_add(WINDOW_REC *window, const char *text, int prepend) +{ + GList **pcmdhist, *link; + int *phistlines; + + g_return_if_fail(text != NULL); + + if (window_history) { + /* window specific command history */ + pcmdhist = &window->cmdhist; + phistlines = &window->histlines; + } else { + /* global command history */ + pcmdhist = &cmdhist; + phistlines = &histlines; + } + + if (settings_get_int("max_command_history") < 1 || *phistlines < settings_get_int("max_command_history")) + (*phistlines)++; + else { + link = *pcmdhist; + g_free(link->data); + *pcmdhist = g_list_remove_link(*pcmdhist, link); + g_list_free_1(link); + } + + if (prepend) + *pcmdhist = g_list_prepend(*pcmdhist, g_strdup(text)); + else + *pcmdhist = g_list_append(*pcmdhist, g_strdup(text)); +} + +const char *command_history_prev(WINDOW_REC *window, const char *text) +{ + GList *pos, **phistpos; + + phistpos = window_history ? &window->histpos : &histpos; + + pos = *phistpos; + if (*phistpos == NULL) + *phistpos = g_list_last(window_history ? window->cmdhist : cmdhist); + else + *phistpos = (*phistpos)->prev; + + if (*text != '\0' && + (pos == NULL || strcmp(pos->data, text) != 0)) { + /* save the old entry to history */ + command_history_add(window, text, FALSE); + } + + return *phistpos == NULL ? "" : (*phistpos)->data; +} + +const char *command_history_next(WINDOW_REC *window, const char *text) +{ + GList *pos, **phistpos; + + phistpos = window_history ? &window->histpos : &histpos; + + pos = *phistpos; + if (*phistpos == NULL) + *phistpos = window_history ? window->cmdhist : cmdhist; + else + *phistpos = (*phistpos)->next; + + if (*text != '\0' && + (pos == NULL || strcmp(pos->data, text) != 0)) { + /* save the old entry to history */ + command_history_add(window, text, TRUE); + } + return *phistpos == NULL ? "" : (*phistpos)->data; +} + +void command_history_clear_pos(WINDOW_REC *window) +{ + window->histpos = NULL; + histpos = NULL; +} + +static void sig_window_created(WINDOW_REC *window) +{ + window->histlines = 0; + window->cmdhist = NULL; + window->histpos = NULL; +} + +static void sig_window_destroyed(WINDOW_REC *window) +{ + g_list_foreach(window->cmdhist, (GFunc) g_free, NULL); + g_list_free(window->cmdhist); +} + +static char *special_history_func(const char *text, void *item, int *free_ret) +{ + WINDOW_REC *window; + GList *tmp; + char *findtext, *ret; + + window = item == NULL ? active_win : window_item_window(item); + + findtext = g_strdup_printf("*%s*", text); + ret = NULL; + + tmp = window_history ? window->cmdhist : cmdhist; + for (; tmp != NULL; tmp = tmp->next) { + const char *line = tmp->data; + + if (match_wildcards(findtext, line)) { + *free_ret = TRUE; + ret = g_strdup(line); + } + } + g_free(findtext); + + return ret; +} + +static void read_settings(void) +{ + window_history = settings_get_bool("toggle_window_history"); +} + +void command_history_init(void) +{ + settings_add_int("history", "max_textwidget_lines", 1000); + settings_add_int("history", "block_remove_lines", 20); + settings_add_int("history", "max_command_history", 100); + settings_add_bool("history", "toggle_window_history", FALSE); + + special_history_func_set(special_history_func); + + histlines = 0; + cmdhist = NULL; histpos = NULL; + read_settings(); + signal_add("window created", (SIGNAL_FUNC) sig_window_created); + signal_add("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void command_history_deinit(void) +{ + signal_remove("window created", (SIGNAL_FUNC) sig_window_created); + signal_remove("window destroyed", (SIGNAL_FUNC) sig_window_destroyed); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + + g_list_foreach(cmdhist, (GFunc) g_free, NULL); + g_list_free(cmdhist); +} diff --git a/src/fe-common/core/command-history.h b/src/fe-common/core/command-history.h new file mode 100644 index 00000000..8ed26757 --- /dev/null +++ b/src/fe-common/core/command-history.h @@ -0,0 +1,16 @@ +#ifndef __COMMAND_HISTORY_H +#define __COMMAND_HISTORY_H + +#include "windows.h" + +void command_history_init(void); +void command_history_deinit(void); + +void command_history_add(WINDOW_REC *window, const char *text, int prepend); + +const char *command_history_prev(WINDOW_REC *window, const char *text); +const char *command_history_next(WINDOW_REC *window, const char *text); + +void command_history_clear_pos(WINDOW_REC *window); + +#endif diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c new file mode 100644 index 00000000..cc76aea6 --- /dev/null +++ b/src/fe-common/core/fe-common-core.c @@ -0,0 +1,132 @@ +/* + fe-common-core.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "module.h" +#include "levels.h" +#include "settings.h" + +#include "hilight-text.h" +#include "command-history.h" +#include "keyboard.h" +#include "printtext.h" +#include "themes.h" +#include "translation.h" +#include "windows.h" +#include "window-items.h" + +#include <sys/signal.h> + +void autorun_init(void); +void autorun_deinit(void); + +void fe_core_log_init(void); +void fe_core_log_deinit(void); + +void fe_server_init(void); +void fe_server_deinit(void); + +void fe_settings_init(void); +void fe_settings_deinit(void); + +void nick_hilight_init(void); +void nick_hilight_deinit(void); + +void fe_core_commands_init(void); +void fe_core_commands_deinit(void); + +void fe_common_core_init(void) +{ + settings_add_bool("lookandfeel", "toggle_show_menubar", TRUE); + settings_add_bool("lookandfeel", "toggle_show_toolbar", FALSE); + settings_add_bool("lookandfeel", "toggle_show_statusbar", TRUE); + settings_add_bool("lookandfeel", "toggle_show_nicklist", TRUE); + settings_add_bool("lookandfeel", "toggle_show_timestamps", FALSE); + settings_add_bool("lookandfeel", "toggle_show_msgs_timestamps", FALSE); + settings_add_bool("lookandfeel", "toggle_hide_text_style", FALSE); + settings_add_bool("lookandfeel", "toggle_bell_beeps", FALSE); + settings_add_bool("lookandfeel", "toggle_actlist_moves", FALSE); + settings_add_bool("lookandfeel", "toggle_show_nickmode", TRUE); + settings_add_bool("lookandfeel", "toggle_show_topicbar", TRUE); + + settings_add_bool("lookandfeel", "toggle_use_status_window", FALSE); + settings_add_bool("lookandfeel", "toggle_use_msgs_window", TRUE); + settings_add_bool("lookandfeel", "toggle_autoraise_msgs_window", FALSE); + settings_add_bool("lookandfeel", "toggle_autocreate_query", TRUE); + settings_add_bool("lookandfeel", "toggle_notifylist_popups", FALSE); + settings_add_bool("lookandfeel", "toggle_use_tabbed_windows", TRUE); + settings_add_int("lookandfeel", "tab_orientation", 3); + settings_add_str("lookandfeel", "current_theme", "default"); + + autorun_init(); + nick_hilight_init(); + hilight_text_init(); + command_history_init(); + keyboard_init(); + printtext_init(); + fe_log_init(); + fe_server_init(); + fe_settings_init(); + themes_init(); + translation_init(); + windows_init(); + window_items_init(); + fe_core_commands_init(); +} + +void fe_common_core_deinit(void) +{ + autorun_deinit(); + nick_hilight_deinit(); + hilight_text_deinit(); + command_history_deinit(); + keyboard_deinit(); + printtext_deinit(); + fe_log_deinit(); + fe_server_deinit(); + fe_settings_deinit(); + themes_deinit(); + translation_deinit(); + windows_deinit(); + window_items_deinit(); + fe_core_commands_deinit(); +} + +void fe_common_core_finish_init(void) +{ + WINDOW_REC *window; + + signal(SIGPIPE, SIG_IGN); + + if (settings_get_bool("toggle_use_status_window")) { + window = window_create(NULL, TRUE); + window_set_name(window, "(status)"); + window_set_level(window, MSGLEVEL_ALL ^ (settings_get_bool("toggle_use_msgs_window") ? (MSGLEVEL_MSGS|MSGLEVEL_ACTIONS) : 0)); + } + + if (settings_get_bool("toggle_use_msgs_window")) { + window = window_create(NULL, TRUE); + window_set_name(window, "(msgs)"); + window_set_level(window, MSGLEVEL_MSGS|MSGLEVEL_ACTIONS); + } + + if (windows == NULL) { + /* we have to have at least one window.. */ + window = window_create(NULL, TRUE); + } +} diff --git a/src/fe-common/core/fe-common-core.h b/src/fe-common/core/fe-common-core.h new file mode 100644 index 00000000..1c12047b --- /dev/null +++ b/src/fe-common/core/fe-common-core.h @@ -0,0 +1,8 @@ +#ifndef __FE_COMMON_CORE_H +#define __FE_COMMON_CORE_H + +void fe_common_core_init(void); +void fe_common_core_deinit(void); +void fe_common_core_finish_init(void); + +#endif diff --git a/src/fe-common/core/fe-core-commands.c b/src/fe-common/core/fe-core-commands.c new file mode 100644 index 00000000..b036385d --- /dev/null +++ b/src/fe-common/core/fe-core-commands.c @@ -0,0 +1,266 @@ +/* + fe-core-commands.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "signals.h" +#include "commands.h" +#include "levels.h" +#include "line-split.h" +#include "irssi-version.h" + +#include "windows.h" + +static gchar *ret_texts[] = +{ + "Invalid parameter", + "Not enough parameters given", + "Not connected to IRC server yet", + "Not joined to any channels yet", + "Error: getsockname() failed", + "Error: listen() failed", + "Multiple matches found, be more specific", + "Nick not found", + "Not joined to such channel", + "Server not found", + "Channel not fully synchronized yet, try again after a while", + "Doing this is not a good idea. Add -YES if you really mean it", +}; + +static gint commands_compare(COMMAND_REC *rec, COMMAND_REC *rec2) +{ + if (rec->category == NULL && rec2->category != NULL) + return -1; + if (rec2->category == NULL && rec->category != NULL) + return 1; + + return strcmp(rec->cmd, rec2->cmd); +} + +static void help_category(GSList *cmdlist, gint items, gint max) +{ + COMMAND_REC *rec, *last; + GString *str; + GSList *tmp; + gint lines, cols, line, col, skip; + gchar *cmdbuf; + + str = g_string_new(NULL); + + cols = max > 65 ? 1 : (65 / max); + lines = items <= cols ? 1 : items / cols+1; + + last = NULL; cmdbuf = g_malloc(max+1); cmdbuf[max] = '\0'; + for (line = 0, col = 0, skip = 1, tmp = cmdlist; line < lines; last = rec, tmp = tmp->next) + { + rec = tmp->data; + + if (--skip == 0) + { + skip = lines; + memset(cmdbuf, ' ', max); + memcpy(cmdbuf, rec->cmd, strlen(rec->cmd)); + g_string_sprintfa(str, "%s ", cmdbuf); + cols++; + } + + if (col == cols || tmp->next == NULL) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, str->str); + g_string_truncate(str, 0); + col = 0; line++; + tmp = g_slist_nth(cmdlist, line-1); skip = 1; + } + } + if (str->len != 0) + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, str->str); + g_string_free(str, TRUE); + g_free(cmdbuf); +} + +static int show_help(COMMAND_REC *cmd) +{ + char tmpbuf[1024], *str, *path; + LINEBUF_REC *buffer = NULL; + int f, ret, recvlen; + + /* helpdir/command or helpdir/category/command */ + if (cmd->category == NULL) + path = g_strdup_printf("%s/%s", HELPDIR, cmd->cmd); + else + path = g_strdup_printf("%s/%s/%s", HELPDIR, cmd->category, cmd->cmd); + f = open(path, O_RDONLY); + g_free(path); + + if (f == -1) + return FALSE; + + /* just print to screen whatever is in the file */ + do + { + recvlen = read(f, tmpbuf, sizeof(tmpbuf)); + + ret = line_split(tmpbuf, recvlen, &str, &buffer); + printtext(NULL, NULL, MSGLEVEL_NEVER, str); + } + while (ret > 0); + line_split_free(buffer); + + close(f); + return TRUE; +} + +static void cmd_help(gchar *data) +{ + COMMAND_REC *rec, *last, *helpitem; + GSList *tmp, *cmdlist; + gint len, max, items, findlen; + gboolean header; + + g_return_if_fail(data != NULL); + + /* sort the commands list */ + commands = g_slist_sort(commands, (GCompareFunc) commands_compare); + + /* print command, sort by category */ + cmdlist = NULL; last = NULL; header = FALSE; helpitem = NULL; + max = items = 0; findlen = strlen(data); + for (tmp = commands; tmp != NULL; last = rec, tmp = tmp->next) + { + rec = tmp->data; + + if (last != NULL && rec->category != NULL && + (last->category == NULL || strcmp(rec->category, last->category) != 0)) + { + /* category changed */ + if (items > 0) + { + if (!header) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "Irssi commands:"); + header = TRUE; + } + if (last->category != NULL) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, ""); + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s:", last->category); + } + help_category(cmdlist, items, max); + } + + g_slist_free(cmdlist); cmdlist = NULL; + items = 0; max = 0; + } + + if (last != NULL && g_strcasecmp(rec->cmd, last->cmd) == 0) + continue; /* don't display same command twice */ + + if (strlen(rec->cmd) >= findlen && g_strncasecmp(rec->cmd, data, findlen) == 0) + { + if (rec->cmd[findlen] == '\0') + { + helpitem = rec; + break; + } + else if (strchr(rec->cmd+findlen+1, ' ') == NULL) + { + /* not a subcommand (and matches the query) */ + len = strlen(rec->cmd); + if (max < len) max = len; + items++; + cmdlist = g_slist_append(cmdlist, rec); + } + } + } + + if ((helpitem == NULL && items == 0) || (helpitem != NULL && !show_help(helpitem))) + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "No help for %s", data); + + if (items != 0) + { + /* display the last category */ + if (!header) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "Irssi commands:"); + header = TRUE; + } + + if (last->category != NULL) + { + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, ""); + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s:", last->category); + } + help_category(cmdlist, items, max); + g_slist_free(cmdlist); + } +} + +static void cmd_echo(const char *data, void *server, WI_ITEM_REC *item) +{ + g_return_if_fail(data != NULL); + + printtext(server, item == NULL ? NULL : item->name, MSGLEVEL_CRAP, "%s", data); +} + +static void cmd_version(char *data) +{ + g_return_if_fail(data != NULL); + + if (*data == '\0') + printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, "Client: "PACKAGE" " IRSSI_VERSION); +} + +static void cmd_unknown(const char *data, void *server, WI_ITEM_REC *item) +{ + char *cmd; + + cmd = g_strdup(data); g_strup(cmd); + printtext(server, item == NULL ? NULL : item->name, MSGLEVEL_CRAP, "Unknown command: %s", cmd); + g_free(cmd); + + signal_stop(); +} + +static void event_cmderror(gpointer error) +{ + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, ret_texts[GPOINTER_TO_INT(error)]); +} + +void fe_core_commands_init(void) +{ + command_bind("help", NULL, (SIGNAL_FUNC) cmd_help); + command_bind("echo", NULL, (SIGNAL_FUNC) cmd_echo); + command_bind("version", NULL, (SIGNAL_FUNC) cmd_version); + + signal_add("unknown command", (SIGNAL_FUNC) cmd_unknown); + signal_add("default command", (SIGNAL_FUNC) cmd_unknown); + signal_add("error command", (SIGNAL_FUNC) event_cmderror); +} + +void fe_core_commands_deinit(void) +{ + command_unbind("help", (SIGNAL_FUNC) cmd_help); + command_unbind("echo", (SIGNAL_FUNC) cmd_echo); + command_unbind("version", (SIGNAL_FUNC) cmd_version); + + signal_remove("unknown command", (SIGNAL_FUNC) cmd_unknown); + signal_remove("default command", (SIGNAL_FUNC) cmd_unknown); + signal_remove("error command", (SIGNAL_FUNC) event_cmderror); +} diff --git a/src/fe-common/core/fe-log.c b/src/fe-common/core/fe-log.c new file mode 100644 index 00000000..560e0f35 --- /dev/null +++ b/src/fe-common/core/fe-log.c @@ -0,0 +1,402 @@ +/* + fe-log.c : irssi + + Copyright (C) 1999 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "signals.h" +#include "commands.h" +#include "server.h" +#include "levels.h" +#include "misc.h" +#include "log.h" +#include "special-vars.h" +#include "settings.h" + +#include "windows.h" +#include "window-items.h" + +/* close autologs after 5 minutes of inactivity */ +#define AUTOLOG_INACTIVITY_CLOSE (60*5) + +#define LOG_DIR_CREATE_MODE 0770 + +static int autolog_level; +static int autoremove_tag; +static const char *autolog_path; + +static void cmd_log_open(const char *data) +{ + /* /LOG OPEN [-noopen] [-autoopen] [-channels <channels>] [-window] + [-rotate hour|day|week|month] <fname> [<levels>] */ + char *params, *args, *itemarg, *rotatearg, *fname, *levels; + char window[MAX_INT_STRLEN]; + LOG_REC *log; + int opened, level, rotate; + + args = "channels rotate"; + params = cmd_get_params(data, 5 | PARAM_FLAG_MULTIARGS | PARAM_FLAG_GETREST, + &args, &itemarg, &rotatearg, &fname, &levels); + if (*fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + rotate = LOG_ROTATE_NEVER; + if (stristr(args, "-rotate")) { + rotate = log_str2rotate(rotatearg); + if (rotate < 0) rotate = LOG_ROTATE_NEVER; + } + + level = level2bits(levels); + if (level == 0) level = MSGLEVEL_ALL; + + if (stristr(args, "-window")) { + /* log by window ref# */ + ltoa(window, active_win->refnum); + itemarg = window; + } + + log = log_create_rec(fname, level, itemarg); + if (log != NULL && log->handle == -1 && stristr(args, "-noopen") == NULL) { + /* start logging */ + opened = log_start_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + opened ? IRCTXT_LOG_OPENED : + IRCTXT_LOG_CREATE_FAILED, fname); + if (!opened) log_close(log); + } + if (log != NULL) { + if (stristr(args, "-autoopen")) + log->autoopen = TRUE; + log->rotate = rotate; + log_update(log); + } + + g_free(params); +} + +static void cmd_log_close(const char *data) +{ + LOG_REC *log; + + log = log_find(data); + if (log == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, IRCTXT_LOG_NOT_OPEN, data); + else { + log_close(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_LOG_CLOSED, data); + } +} + +static void cmd_log_start(const char *data) +{ + LOG_REC *log; + + log = log_find(data); + if (log != NULL) { + log_start_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_LOG_OPENED, data); + } +} + +static void cmd_log_stop(const char *data) +{ + LOG_REC *log; + + log = log_find(data); + if (log == NULL || log->handle == -1) + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, IRCTXT_LOG_NOT_OPEN, data); + else { + log_stop_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_LOG_CLOSED, data); + } +} + +static void cmd_log_list(void) +{ + GSList *tmp; + char *levelstr, *items, *rotate; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_LOG_LIST_HEADER); + for (tmp = logs; tmp != NULL; tmp = tmp->next) { + LOG_REC *rec = tmp->data; + + levelstr = bits2level(rec->level); + items = rec->items == NULL ? NULL : + g_strjoinv(",", rec->items); + rotate = rec->rotate == 0 ? NULL : + g_strdup_printf(" -rotate %s", log_rotate2str(rec->rotate)); + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_LOG_LIST, + rec->fname, items != NULL ? items : "", + levelstr, rotate != NULL ? rotate : "", + rec->autoopen ? " -autoopen" : ""); + + g_free_not_null(rotate); + g_free_not_null(items); + g_free(levelstr); + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_LOG_LIST_FOOTER); +} + +static void cmd_log(const char *data, SERVER_REC *server, void *item) +{ + command_runsub("log", data, server, item); +} + +static LOG_REC *log_find_item(const char *item) +{ + GSList *tmp; + + for (tmp = logs; tmp != NULL; tmp = tmp->next) { + LOG_REC *rec = tmp->data; + + if (rec->items != NULL && strarray_find(rec->items, item) != -1) + return rec; + } + + return NULL; +} + +static void cmd_window_log(const char *data) +{ + /* /WINDOW LOG ON|OFF|TOGGLE [<filename>] */ + LOG_REC *log; + char *params, *set, *fname, window[MAX_INT_STRLEN]; + int open_log, close_log; + + params = cmd_get_params(data, 2, &set, &fname); + + ltoa(window, active_win->refnum); + log = log_find_item(window); + + open_log = close_log = FALSE; + if (g_strcasecmp(set, "ON") == 0) + open_log = TRUE; + else if (g_strcasecmp(set, "OFF") == 0) { + close_log = TRUE; + } else if (g_strcasecmp(set, "TOGGLE") == 0) { + open_log = log == NULL; + close_log = log != NULL; + } else { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NOT_TOGGLE); + g_free(params); + return; + } + + if (open_log && log == NULL) { + /* irc.log.<windowname> or irc.log.Window<ref#> */ + fname = *fname != '\0' ? g_strdup(fname) : + g_strdup_printf("~/irc.log.%s%s", + active_win->name != NULL ? active_win->name : "Window", + active_win->name != NULL ? "" : window); + log = log_create_rec(fname, MSGLEVEL_ALL, window); + if (log != NULL) log_update(log); + g_free(fname); + } + + if (open_log && log != NULL) { + log_start_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_LOG_OPENED, log->fname); + } else if (close_log && log != NULL && log->handle != -1) { + log_stop_logging(log); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_LOG_CLOSED, log->fname); + } + + g_free(params); +} + +/* Create log file entry to window, but don't start logging */ +static void cmd_window_logfile(const char *data) +{ + LOG_REC *log; + char window[MAX_INT_STRLEN]; + + ltoa(window, active_win->refnum); + log = log_find_item(window); + + if (log != NULL) { + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_WINDOWLOG_FILE_LOGGING); + return; + } + + log = log_create_rec(data, MSGLEVEL_ALL, window); + if (log == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_WINDOWLOG_FILE, data); + else + log_update(log); +} + +static void autologs_close_all(void) +{ + GSList *tmp, *next; + + for (tmp = logs; tmp != NULL; tmp = next) { + LOG_REC *rec = tmp->data; + + next = tmp->next; + if (rec->temp) log_close(rec); + } +} + +static void autolog_log(void *server, const char *target) +{ + LOG_REC *log; + char *fname, *dir, *str; + + log = log_find_item(target); + if (log != NULL) return; + + fname = parse_special_string(autolog_path, server, NULL, target, NULL); + if (log_find(fname) == NULL) { + str = convert_home(fname); + dir = g_dirname(str); + g_free(str); + + mkdir(dir, LOG_DIR_CREATE_MODE); + g_free(dir); + + log = log_create_rec(fname, autolog_level, target); + if (log != NULL) { + log->temp = TRUE; + log_update(log); + log_start_logging(log); + } + } + g_free(fname); +} + +/* write to logs created with /WINDOW LOG */ +static void sig_printtext_stripped(void *server, const char *target, gpointer levelp, const char *text) +{ + char windownum[MAX_INT_STRLEN]; + WINDOW_REC *window; + LOG_REC *log; + int level; + + level = GPOINTER_TO_INT(levelp); + if ((autolog_level & level) && target != NULL && *target != '\0') + autolog_log(server, target); + + window = window_find_closest(server, target, level); + if (window != NULL) { + ltoa(windownum, window->refnum); + + log = log_find_item(windownum); + if (log != NULL) log_write_rec(log, text); + } +} + +static int sig_autoremove(void) +{ + GSList *tmp, *next; + time_t removetime; + + removetime = time(NULL)-AUTOLOG_INACTIVITY_CLOSE; + for (tmp = logs; tmp != NULL; tmp = next) { + LOG_REC *rec = tmp->data; + + next = tmp->next; + /* FIXME: here is a small kludge - We don't want autolog to + automatically close the logs with channels, only with + private messages. However, this is CORE module and we + don't know how to figure out if item is a channel or not, + so just assume that channels are everything that don't + start with alphanumeric character. */ + if (!rec->temp || rec->last > removetime || + rec->items == NULL || !isalnum(**rec->items)) + continue; + + log_close(rec); + } + return 1; +} + +static void sig_window_item_remove(WINDOW_REC *window, WI_ITEM_REC *item) +{ + GSList *tmp; + + for (tmp = logs; tmp != NULL; tmp = tmp->next) { + LOG_REC *rec = tmp->data; + + if (rec->temp && g_strcasecmp(rec->items[0], item->name) == 0) { + log_close(rec); + break; + } + } +} + +static void sig_log_locked(LOG_REC *log) +{ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_LOG_LOCKED, log->fname); +} + +static void read_settings(void) +{ + int old_autolog = autolog_level; + + autolog_path = settings_get_str("autolog_path"); + autolog_level = !settings_get_bool("autolog") ? 0 : + level2bits(settings_get_str("autolog_level")); + + if (old_autolog && !autolog_level) + autologs_close_all(); +} + +void fe_log_init(void) +{ + autoremove_tag = g_timeout_add(60000, (GSourceFunc) sig_autoremove, NULL); + + settings_add_str("log", "autolog_path", "~/irclogs/$tag/$0.log"); + settings_add_str("log", "autolog_level", "all"); + settings_add_bool("log", "autolog", FALSE); + + autolog_level = 0; + read_settings(); + + command_bind("log", NULL, (SIGNAL_FUNC) cmd_log); + command_bind("log open", NULL, (SIGNAL_FUNC) cmd_log_open); + command_bind("log close", NULL, (SIGNAL_FUNC) cmd_log_close); + command_bind("log start", NULL, (SIGNAL_FUNC) cmd_log_start); + command_bind("log stop", NULL, (SIGNAL_FUNC) cmd_log_stop); + command_bind("log ", NULL, (SIGNAL_FUNC) cmd_log_list); + command_bind("window log", NULL, (SIGNAL_FUNC) cmd_window_log); + command_bind("window logfile", NULL, (SIGNAL_FUNC) cmd_window_logfile); + signal_add_first("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped); + signal_add("window item remove", (SIGNAL_FUNC) sig_window_item_remove); + signal_add("log locked", (SIGNAL_FUNC) sig_log_locked); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); +} + +void fe_log_deinit(void) +{ + g_source_remove(autoremove_tag); + + command_unbind("log", (SIGNAL_FUNC) cmd_log); + command_unbind("log open", (SIGNAL_FUNC) cmd_log_open); + command_unbind("log close", (SIGNAL_FUNC) cmd_log_close); + command_unbind("log start", (SIGNAL_FUNC) cmd_log_start); + command_unbind("log stop", (SIGNAL_FUNC) cmd_log_stop); + command_unbind("log ", (SIGNAL_FUNC) cmd_log_list); + command_unbind("window log", (SIGNAL_FUNC) cmd_window_log); + command_unbind("window logfile", (SIGNAL_FUNC) cmd_window_logfile); + signal_remove("print text stripped", (SIGNAL_FUNC) sig_printtext_stripped); + signal_remove("window item remove", (SIGNAL_FUNC) sig_window_item_remove); + signal_remove("log locked", (SIGNAL_FUNC) sig_log_locked); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/src/fe-common/core/fe-server.c b/src/fe-common/core/fe-server.c new file mode 100644 index 00000000..960b94f9 --- /dev/null +++ b/src/fe-common/core/fe-server.c @@ -0,0 +1,96 @@ +/* + fe-server.c : irssi + + Copyright (C) 1999 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "signals.h" +#include "settings.h" +#include "network.h" + +#include "levels.h" +#include "server.h" + +static void sig_server_looking(SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_LOOKING_UP, server->connrec->address); +} + +static void sig_server_connecting(SERVER_REC *server, IPADDR *ip) +{ + char ipaddr[MAX_IP_LEN]; + + g_return_if_fail(server != NULL); + g_return_if_fail(ip != NULL); + + net_ip2host(ip, ipaddr); + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_CONNECTING, + server->connrec->address, ipaddr, server->connrec->port); +} + +static void sig_server_connected(SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + printformat(server, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_CONNECTION_ESTABLISHED, server->connrec->address); +} + +static void sig_connect_failed(SERVER_REC *server, gchar *msg) +{ + g_return_if_fail(server != NULL); + + if (msg == NULL) { + /* no message so this wasn't unexpected fail - send + connection_lost message instead */ + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_CONNECTION_LOST, server->connrec->address); + } else { + printformat(NULL, NULL, MSGLEVEL_CLIENTERROR, + IRCTXT_CANT_CONNECT, server->connrec->address, server->connrec->port, msg); + } +} + +static void sig_server_disconnected(SERVER_REC *server) +{ + g_return_if_fail(server != NULL); + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, + IRCTXT_CONNECTION_LOST, server->connrec->address); +} + +void fe_server_init(void) +{ + signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting); + signal_add("server connected", (SIGNAL_FUNC) sig_server_connected); + signal_add("server connect failed", (SIGNAL_FUNC) sig_connect_failed); + signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); +} + +void fe_server_deinit(void) +{ + signal_remove("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_remove("server connecting", (SIGNAL_FUNC) sig_server_connecting); + signal_remove("server connected", (SIGNAL_FUNC) sig_server_connected); + signal_remove("server connect failed", (SIGNAL_FUNC) sig_connect_failed); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); +} diff --git a/src/fe-common/core/fe-settings.c b/src/fe-common/core/fe-settings.c new file mode 100644 index 00000000..5e4b6df6 --- /dev/null +++ b/src/fe-common/core/fe-settings.c @@ -0,0 +1,215 @@ +/* + fe-settings.c : irssi + + Copyright (C) 1999 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "signals.h" +#include "commands.h" +#include "server.h" +#include "lib-config/iconfig.h" +#include "settings.h" + +#include "levels.h" + +static void set_print(SETTINGS_REC *rec) +{ + const char *value; + char value_int[MAX_INT_STRLEN]; + + switch (rec->type) { + case SETTING_TYPE_BOOLEAN: + value = settings_get_bool(rec->key) ? "ON" : "OFF"; + break; + case SETTING_TYPE_INT: + g_snprintf(value_int, sizeof(value_int), "%d", settings_get_int(rec->key)); + value = value_int; + break; + case SETTING_TYPE_STRING: + value = settings_get_str(rec->key); + break; + default: + value = ""; + } + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s = %s", rec->key, value); +} + +static void set_boolean(const char *key, const char *value) +{ + if (g_strcasecmp(value, "ON") == 0) + iconfig_set_bool("settings", key, TRUE); + else if (g_strcasecmp(value, "OFF") == 0) + iconfig_set_bool("settings", key, FALSE); + else if (g_strcasecmp(value, "TOGGLE") == 0) + iconfig_set_bool("settings", key, !settings_get_bool(key)); + else + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_NOT_TOGGLE); +} + +static void cmd_set(char *data) +{ + GSList *sets, *tmp; + char *params, *key, *value, *last_section; + int keylen, found; + + params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &key, &value); + + keylen = strlen(key); + last_section = ""; found = 0; + + sets = settings_get_sorted(); + for (tmp = sets; tmp != NULL; tmp = tmp->next) { + SETTINGS_REC *rec = tmp->data; + + if ((*value != '\0' && g_strcasecmp(rec->key, key) != 0) || + (*value == '\0' && keylen != 0 && g_strncasecmp(rec->key, key, keylen) != 0)) + continue; + + if (strcmp(last_section, rec->section) != 0) { + /* print section */ + printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%_[ %s ]", rec->section); + last_section = rec->section; + } + + if (*value != '\0') { + /* change the setting */ + switch (rec->type) { + case SETTING_TYPE_BOOLEAN: + set_boolean(key, value); + break; + case SETTING_TYPE_INT: + iconfig_set_int("settings", key, atoi(value)); + break; + case SETTING_TYPE_STRING: + iconfig_set_str("settings", key, value); + break; + } + signal_emit("setup changed", 0); + } + + set_print(rec); + found = TRUE; + } + g_slist_free(sets); + + if (!found) + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown setting %s", key); + + g_free(params); +} + +static void cmd_toggle(const char *data) +{ + char *params, *key, *value; + int type; + + params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &key, &value); + + type = settings_get_type(key); + if (type == -1) + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Unknown setting %_%s", key); + else if (type != SETTING_TYPE_BOOLEAN) + printtext(NULL, NULL, MSGLEVEL_CLIENTERROR, "Setting %_%s%_ isn't boolean, use /SET", key); + else { + set_boolean(key, *value != '\0' ? value : "TOGGLE"); + set_print(settings_get_record(key)); + } + + g_free(params); +} + +static void show_aliases(const char *alias) +{ + CONFIG_NODE *node; + GSList *tmp; + int aliaslen; + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_ALIASLIST_HEADER); + + node = iconfig_node_traverse("aliases", FALSE); + tmp = node == NULL ? NULL : node->value; + + aliaslen = strlen(alias); + for (; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *node = tmp->data; + + if (node->type != NODE_TYPE_KEY) + continue; + + if (aliaslen != 0 && g_strncasecmp(node->key, alias, aliaslen) != 0) + continue; + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_ALIASLIST_LINE, + node->key, node->value); + } + + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_ALIASLIST_FOOTER); +} + +static void alias_remove(const char *alias) +{ + if (iconfig_get_str("aliases", alias, NULL) == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_ALIAS_NOT_FOUND, alias); + else { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_ALIAS_REMOVED, alias); + iconfig_set_str("aliases", alias, NULL); + } +} + +static void cmd_alias(const char *data) +{ + char *params, *alias, *value; + + g_return_if_fail(data != NULL); + + params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &alias, &value); + if (*alias == '-') { + if (alias[1] != '\0') alias_remove(alias+1); + } else if (*alias == '\0' || *value == '\0') + show_aliases(alias); + else { + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_ALIAS_ADDED, alias); + iconfig_set_str("aliases", alias, value); + } + g_free(params); +} + +static void cmd_unalias(const char *data) +{ + g_return_if_fail(data != NULL); + if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); + + alias_remove(data); +} + +void fe_settings_init(void) +{ + command_bind("set", NULL, (SIGNAL_FUNC) cmd_set); + command_bind("toggle", NULL, (SIGNAL_FUNC) cmd_toggle); + command_bind("alias", NULL, (SIGNAL_FUNC) cmd_alias); + command_bind("unalias", NULL, (SIGNAL_FUNC) cmd_unalias); +} + +void fe_settings_deinit(void) +{ + command_unbind("set", (SIGNAL_FUNC) cmd_set); + command_unbind("toggle", (SIGNAL_FUNC) cmd_toggle); + command_unbind("alias", (SIGNAL_FUNC) cmd_alias); + command_unbind("unalias", (SIGNAL_FUNC) cmd_unalias); +} diff --git a/src/fe-common/core/hilight-text.c b/src/fe-common/core/hilight-text.c new file mode 100644 index 00000000..a96f7395 --- /dev/null +++ b/src/fe-common/core/hilight-text.c @@ -0,0 +1,354 @@ +/* + hilight-text.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "signals.h" +#include "commands.h" +#include "misc.h" +#include "lib-config/iconfig.h" +#include "settings.h" + +#include "levels.h" +#include "server.h" + +#include "hilight-text.h" + +#define DEFAULT_HILIGHT_CHECK_LEVEL \ + (MSGLEVEL_PUBLIC | MSGLEVEL_MSGS | MSGLEVEL_NOTICES | MSGLEVEL_ACTIONS) + +static int hilight_next; +GSList *hilights; + +static void hilight_add_config(HILIGHT_REC *rec) +{ + CONFIG_NODE *node; + + node = iconfig_node_traverse("(hilights", TRUE); + node = config_node_section(node, NULL, NODE_TYPE_BLOCK); + + config_node_set_str(node, "text", rec->text); + if (rec->level > 0) config_node_set_int(node, "level", rec->level); + if (rec->color) config_node_set_str(node, "color", rec->color); + if (rec->nickmask) config_node_set_bool(node, "nickmask", TRUE); + if (rec->fullword) config_node_set_bool(node, "fullword", TRUE); + if (rec->regexp) config_node_set_bool(node, "regexp", TRUE); + + if (rec->channels != NULL && *rec->channels != NULL) { + node = config_node_section(node, "channels", NODE_TYPE_LIST); + config_node_add_list(node, rec->channels); + } +} + +static void hilight_remove_config(HILIGHT_REC *rec) +{ + CONFIG_NODE *node; + + node = iconfig_node_traverse("hilights", FALSE); + if (node != NULL) config_node_list_remove(node, g_slist_index(hilights, rec)); +} + +static void hilight_destroy(HILIGHT_REC *rec) +{ + g_free(rec->text); + g_free_not_null(rec->color); + g_free(rec); +} + +static void hilights_destroy_all(void) +{ + g_slist_foreach(hilights, (GFunc) hilight_destroy, NULL); + g_slist_free(hilights); + hilights = NULL; +} + +static void hilight_remove(HILIGHT_REC *rec) +{ + hilight_remove_config(rec); + hilights = g_slist_remove(hilights, rec); + hilight_destroy(rec); +} + +static HILIGHT_REC *hilight_find(const char *text, char **channels) +{ + GSList *tmp; + char **chan; + + g_return_val_if_fail(text != NULL, NULL); + + for (tmp = hilights; tmp != NULL; tmp = tmp->next) { + HILIGHT_REC *rec = tmp->data; + + if (g_strcasecmp(rec->text, text) != 0) + continue; + + if ((channels == NULL && rec->channels == NULL)) + return rec; /* no channels - ok */ + + if (channels != NULL && strcmp(*channels, "*") == 0) + return rec; /* ignore channels */ + + if (channels == NULL || rec->channels == NULL) + continue; /* other doesn't have channels */ + + if (strarray_length(channels) != strarray_length(rec->channels)) + continue; /* different amount of channels */ + + /* check that channels match */ + for (chan = channels; *chan != NULL; chan++) { + if (strarray_find(rec->channels, *chan) == -1) + break; + } + + if (*chan == NULL) + return rec; /* channels ok */ + } + + return NULL; +} + +static void sig_print_text(SERVER_REC *server, const char *channel, gpointer level, const char *str) +{ + if (hilight_next) { + hilight_next = FALSE; + signal_stop(); + } +} + +static void sig_print_text_stripped(SERVER_REC *server, const char *channel, gpointer plevel, const char *str) +{ + GSList *tmp; + char *color, *newstr; + int len, level, best_match; + + g_return_if_fail(str != NULL); + + level = GPOINTER_TO_INT(plevel); + if (level & (MSGLEVEL_NOHILIGHT|MSGLEVEL_HILIGHT)) return; + + color = NULL; best_match = 0; + for (tmp = hilights; tmp != NULL; tmp = tmp->next) { + HILIGHT_REC *rec = tmp->data; + + if (rec->nickmask) + continue; + if ((level & (rec->level > 0 ? rec->level : DEFAULT_HILIGHT_CHECK_LEVEL)) == 0) + continue; + if (rec->channels != NULL && !strarray_find(rec->channels, channel)) + continue; + if (rec->regexp) { + if (!regexp_match(str, rec->text)) + continue; + } else if (rec->fullword) { + if (stristr_full(str, rec->text) == NULL) + continue; + } else { + if (stristr(str, rec->text) == NULL) + continue; + } + + len = strlen(rec->text); + if (best_match < len) { + best_match = len; + color = rec->color; + } + } + + if (best_match > 0) { + hilight_next = FALSE; + + if (color == NULL) color = "\00316"; + newstr = g_strconcat(isdigit(*color) ? "\003" : "", color, str, NULL); + signal_emit("print text", 4, server, channel, GINT_TO_POINTER(level | MSGLEVEL_HILIGHT), newstr); + g_free(newstr); + + hilight_next = TRUE; + } +} + +static void read_hilight_config(void) +{ + CONFIG_NODE *node; + HILIGHT_REC *rec; + GSList *tmp; + char *text, *color; + + hilights_destroy_all(); + + node = iconfig_node_traverse("hilights", FALSE); + if (node == NULL) return; + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (node->type != NODE_TYPE_BLOCK) + continue; + + text = config_node_get_str(node, "text", NULL); + if (text == NULL || *text == '\0') + continue; + + rec = g_new0(HILIGHT_REC, 1); + hilights = g_slist_append(hilights, rec); + + color = config_node_get_str(node, "color", NULL); + + rec->text = g_strdup(text); + rec->color = color == NULL || *color == '\0' ? NULL : + g_strdup(color); + rec->level = config_node_get_int(node, "level", 0); + rec->nickmask = config_node_get_bool(node, "nickmask", FALSE); + rec->fullword = config_node_get_bool(node, "fullword", FALSE); + rec->regexp = config_node_get_bool(node, "regexp", FALSE); + + node = config_node_section(node, "channels", -1); + if (node != NULL) rec->channels = config_node_get_list(node); + } +} + +static void hilight_print(int index, HILIGHT_REC *rec) +{ + char *chans, *levelstr; + + chans = rec->channels == NULL ? NULL : + g_strjoinv(",", rec->channels); + levelstr = rec->level == 0 ? NULL : + bits2level(rec->level); + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, + IRCTXT_HILIGHT_LINE, index, rec->text, + chans != NULL ? chans : "", + levelstr != NULL ? levelstr : "", + rec->nickmask ? " -nick" : "", + rec->fullword ? " -word" : "", + rec->regexp ? " -regexp" : ""); + g_free_not_null(chans); + g_free_not_null(levelstr); +} + +static void cmd_hilight_show(void) +{ + GSList *tmp; + int index; + + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_HILIGHT_HEADER); + index = 1; + for (tmp = hilights; tmp != NULL; tmp = tmp->next, index++) { + HILIGHT_REC *rec = tmp->data; + + hilight_print(index, rec); + } + printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_HILIGHT_FOOTER); +} + +static void cmd_hilight(const char *data) +{ + /* /HILIGHT [-nick | -regexp | -word] [-color <color>] [-level <level>] [-channels <channels>] <text> */ + char *params, *args, *colorarg, *levelarg, *chanarg, *text; + char **channels; + HILIGHT_REC *rec; + + g_return_if_fail(data != NULL); + + if (*data == '\0') { + cmd_hilight_show(); + return; + } + + args = "color level channels"; + params = cmd_get_params(data, 5 | PARAM_FLAG_MULTIARGS | PARAM_FLAG_GETREST, + &args, &colorarg, &levelarg, &chanarg, &text); + if (*text == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS); + + channels = *chanarg == '\0' ? NULL : + g_strsplit(replace_chars(chanarg, ',', ' '), " ", -1); + + rec = hilight_find(text, channels); + if (rec == NULL) { + rec = g_new0(HILIGHT_REC, 1); + + rec->text = g_strdup(text); + rec->channels = channels; + } else { + g_free_and_null(rec->color); + g_strfreev(channels); + + hilight_remove_config(rec); + hilights = g_slist_remove(hilights, rec); + } + + hilights = g_slist_append(hilights, rec); + rec->nickmask = stristr(args, "-nick") != NULL; + rec->fullword = stristr(args, "-word") != NULL; + rec->regexp = stristr(args, "-regexp") != NULL; + + rec->level = level2bits(replace_chars(levelarg, ',', ' ')); + if (*colorarg != '\0') rec->color = g_strdup(colorarg); + + hilight_print(g_slist_index(hilights, rec)+1, rec); + + hilight_add_config(rec); + g_free(params); +} + +static void cmd_dehilight(const char *data) +{ + HILIGHT_REC *rec; + GSList *tmp; + + if (is_numeric(data, ' ')) { + /* with index number */ + tmp = g_slist_nth(hilights, atol(data)-1); + rec = tmp == NULL ? NULL : tmp->data; + } else { + /* with mask */ + char *chans[2] = { "*", NULL }; + rec = hilight_find(data, chans); + } + + if (rec == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_HILIGHT_NOT_FOUND, data); + else + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_HILIGHT_REMOVED, rec->text); + hilight_remove(rec); +} + +void hilight_text_init(void) +{ + hilight_next = FALSE; + + read_hilight_config(); + + signal_add_first("print text", (SIGNAL_FUNC) sig_print_text); + signal_add_first("print text stripped", (SIGNAL_FUNC) sig_print_text_stripped); + signal_add("setup reread", (SIGNAL_FUNC) read_hilight_config); + command_bind("hilight", NULL, (SIGNAL_FUNC) cmd_hilight); + command_bind("dehilight", NULL, (SIGNAL_FUNC) cmd_dehilight); +} + +void hilight_text_deinit(void) +{ + hilights_destroy_all(); + + signal_remove("print text", (SIGNAL_FUNC) sig_print_text); + signal_remove("print text stripped", (SIGNAL_FUNC) sig_print_text_stripped); + signal_remove("setup reread", (SIGNAL_FUNC) read_hilight_config); + command_unbind("hilight", (SIGNAL_FUNC) cmd_hilight); + command_unbind("dehilight", (SIGNAL_FUNC) cmd_dehilight); +} diff --git a/src/fe-common/core/hilight-text.h b/src/fe-common/core/hilight-text.h new file mode 100644 index 00000000..6e047278 --- /dev/null +++ b/src/fe-common/core/hilight-text.h @@ -0,0 +1,22 @@ +#ifndef __HILIGHT_TEXT_H +#define __HILIGHT_TEXT_H + +typedef struct { + char *text; + + char **channels; /* if non-NULL, check the text only from these channels */ + int level; /* match only messages with this level, 0=default */ + char *color; /* if starts with number, \003 is automatically + inserted before it. */ + + int nickmask:1; /* `text 'is a nick mask - colorify the nick */ + int fullword:1; /* match `text' only for full words */ + int regexp:1; /* `text' is a regular expression */ +} HILIGHT_REC; + +extern GSList *hilights; + +void hilight_text_init(void); +void hilight_text_deinit(void); + +#endif diff --git a/src/fe-common/core/keyboard.c b/src/fe-common/core/keyboard.c new file mode 100644 index 00000000..6881d77a --- /dev/null +++ b/src/fe-common/core/keyboard.c @@ -0,0 +1,297 @@ +/* + keyboard.c : irssi + + Copyright (C) 1999 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "lib-config/iconfig.h" +#include "settings.h" + +#include "keyboard.h" +#include "windows.h" + +GSList *keyinfos; +static GHashTable *keys; + +KEYINFO_REC *key_info_find(gchar *id) +{ + GSList *tmp; + + for (tmp = keyinfos; tmp != NULL; tmp = tmp->next) + { + KEYINFO_REC *rec = tmp->data; + + if (g_strcasecmp(rec->id, id) == 0) + return rec; + } + + return NULL; +} + +/* Bind a key for function */ +void key_bind(gchar *id, gchar *data, gchar *description, gchar *key_default, SIGNAL_FUNC func) +{ + KEYINFO_REC *info; + KEY_REC *rec; + + g_return_if_fail(id != NULL); + g_return_if_fail(func != NULL); + + /* create key info record */ + info = key_info_find(id); + if (info == NULL) + { + g_return_if_fail(description != NULL); + info = g_new0(KEYINFO_REC, 1); + info->id = g_strdup(id); + info->description = g_strdup(description); + keyinfos = g_slist_append(keyinfos, info); + + /* add the signal */ + id = g_strconcat("key ", id, NULL); + signal_add(id, func); + g_free(id); + + signal_emit("keyinfo created", 1, info); + } + + if (key_default == NULL || *key_default == '\0') + { + /* just create a possible key command, don't bind it to any key yet */ + return; + } + + /* create/replace key record */ + rec = g_hash_table_lookup(keys, key_default); + if (rec != NULL) + { + if (rec->data != NULL) + g_free(rec->data); + } + else + { + rec = g_new0(KEY_REC, 1); + info->keys = g_slist_append(info->keys, rec); + rec->key = g_strdup(key_default); + g_hash_table_insert(keys, rec->key, rec); + } + rec->info = info; + rec->data = data == NULL ? NULL : g_strdup(data); +} + +static void keyinfo_remove(KEYINFO_REC *info) +{ + GSList *tmp; + + g_return_if_fail(info != NULL); + + keyinfos = g_slist_remove(keyinfos, info); + signal_emit("keyinfo destroyed", 1, info); + + /* destroy all keys */ + for (tmp = info->keys; tmp != NULL; tmp = tmp->next) + { + KEY_REC *rec = tmp->data; + + g_hash_table_remove(keys, rec->key); + if (rec->data != NULL) g_free(rec->data); + g_free(rec->key); + g_free(rec); + } + + /* destroy key info */ + g_slist_free(info->keys); + g_free(info->description); + g_free(info->id); + g_free(info); +} + +/* Unbind key */ +void key_unbind(gchar *id, SIGNAL_FUNC func) +{ + KEYINFO_REC *info; + + g_return_if_fail(id != NULL); + g_return_if_fail(func != NULL); + + /* remove keys */ + info = key_info_find(id); + if (info != NULL) + keyinfo_remove(info); + + /* remove signal */ + id = g_strconcat("key ", id, NULL); + signal_remove(id, func); + g_free(id); +} + +/* Configure new key */ +void key_configure_add(gchar *id, gchar *data, gchar *key) +{ + KEYINFO_REC *info; + KEY_REC *rec; + + g_return_if_fail(id != NULL); + g_return_if_fail(key != NULL && *key != '\0'); + + info = key_info_find(id); + if (info == NULL) + return; + + rec = g_new0(KEY_REC, 1); + info->keys = g_slist_append(info->keys, rec); + + rec->info = info; + rec->data = data == NULL ? NULL : g_strdup(data); + rec->key = g_strdup(key); + g_hash_table_insert(keys, rec->key, rec); +} + +/* Remove key */ +void key_configure_remove(gchar *key) +{ + KEY_REC *rec; + + g_return_if_fail(key != NULL); + + rec = g_hash_table_lookup(keys, key); + if (rec == NULL) return; + + rec->info->keys = g_slist_remove(rec->info->keys, rec); + g_hash_table_remove(keys, key); + + if (rec->data != NULL) g_free(rec->data); + g_free(rec->key); + g_free(rec); +} + +gboolean key_pressed(gchar *key, gpointer data) +{ + KEY_REC *rec; + gboolean ret; + gchar *str; + + g_return_val_if_fail(key != NULL, FALSE); + + rec = g_hash_table_lookup(keys, key); + if (rec == NULL) return FALSE; + + str = g_strconcat("key ", rec->info->id, NULL); + ret = signal_emit(str, 3, rec->data, data, rec->info); + g_free(str); + + return ret; +} + +void keyboard_save(void) +{ + CONFIG_NODE *keyboard, *node, *listnode; + GSList *tmp, *tmp2; + + /* remove old keyboard settings */ + config_node_set_str(NULL, "(keyboard", NULL); + keyboard = iconfig_node_traverse("(keyboard", TRUE); + + for (tmp = keyinfos; tmp != NULL; tmp = tmp->next) { + KEYINFO_REC *info = tmp->data; + + node = config_node_section(keyboard, info->id, TRUE); + for (tmp2 = info->keys; tmp2 != NULL; tmp2 = tmp2->next) { + KEY_REC *key = tmp2->data; + + listnode = config_node_section(node, NULL, NODE_TYPE_BLOCK); + if (key->data != NULL) + config_node_set_str(listnode, "data", key->data); + config_node_set_str(listnode, "key", key->key); + } + } +} + +static void sig_command(gchar *data) +{ + signal_emit("send command", 3, data, active_win->active_server, active_win->active); +} + +void read_keyinfo(KEYINFO_REC *info, CONFIG_NODE *node) +{ + GSList *tmp; + char *data, *key; + + g_return_if_fail(info != NULL); + g_return_if_fail(node != NULL); + g_return_if_fail(is_node_list(node)); + + /* remove all old keys */ + while (info->keys != NULL) + key_configure_remove(((KEY_REC *) info->keys->data)->key); + + /* add the new keys */ + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + data = config_node_get_str(node->value, "data", NULL); + key = config_node_get_str(node->value, "key", NULL); + + if (key != NULL) key_configure_add(info->id, data, key); + } +} + +static void read_keyboard_config(void) +{ + KEYINFO_REC *info; + CONFIG_NODE *node; + GSList *tmp; + + while (keyinfos != NULL) + keyinfo_remove(keyinfos->data); + if (keys != NULL) g_hash_table_destroy(keys); + + keys = g_hash_table_new((GHashFunc) g_str_hash, (GCompareFunc) g_str_equal); + + node = iconfig_node_traverse("keyboard", FALSE); + if (node == NULL) return; + + for (tmp = node->value; tmp != NULL; tmp = tmp->next) { + node = tmp->data; + + if (node->key == NULL || node->value == NULL) + continue; + + info = key_info_find(node->key); + if (info != NULL) read_keyinfo(info, node->value); + } +} + +void keyboard_init(void) +{ + keyinfos = NULL; keys = NULL; + key_bind("command", NULL, "Run any IRC command", NULL, (SIGNAL_FUNC) sig_command); + + read_keyboard_config(); + signal_add("setup reread", (SIGNAL_FUNC) read_keyboard_config); +} + +void keyboard_deinit(void) +{ + while (keyinfos != NULL) + keyinfo_remove(keyinfos->data); + g_hash_table_destroy(keys); + + signal_remove("setup reread", (SIGNAL_FUNC) read_keyboard_config); +} diff --git a/src/fe-common/core/keyboard.h b/src/fe-common/core/keyboard.h new file mode 100644 index 00000000..a6278adc --- /dev/null +++ b/src/fe-common/core/keyboard.h @@ -0,0 +1,40 @@ +#ifndef __KEYBOARD_H +#define __KEYBOARD_H + +#include "signals.h" + +typedef struct +{ + char *id; + char *description; + + GSList *keys; +} +KEYINFO_REC; + +typedef struct +{ + KEYINFO_REC *info; + + char *key; + void *data; +} +KEY_REC; + +extern GSList *keyinfos; + +void key_bind(gchar *id, gchar *data, gchar *description, gchar *key_default, SIGNAL_FUNC func); +void key_unbind(gchar *id, SIGNAL_FUNC func); + +void key_configure_add(gchar *id, gchar *data, gchar *key); +void key_configure_remove(gchar *key); + +KEYINFO_REC *key_info_find(gchar *id); +gboolean key_pressed(gchar *key, gpointer data); + +void keyboard_save(void); + +void keyboard_init(void); +void keyboard_deinit(void); + +#endif diff --git a/src/fe-common/core/module-formats.c b/src/fe-common/core/module-formats.c new file mode 100644 index 00000000..39546ea3 --- /dev/null +++ b/src/fe-common/core/module-formats.c @@ -0,0 +1,87 @@ +/* + module-formats.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "printtext.h" + +FORMAT_REC fecommon_core_formats[] = +{ + { MODULE_NAME, N_("Core"), 0 }, + + /* ---- */ + { NULL, N_("Windows"), 0 }, + + { "line_start", N_("%B-%W!%B-%n "), 0 }, + { "line_start_irssi", N_("%B-%W!%B- %WIrssi:%n "), 0 }, + { "timestamp", N_("[$[-2.0]3:$[-2.0]4] "), 6, { 1, 1, 1, 1, 1, 1 } }, + { "daychange", N_("Day changed to $[-2.0]1-$[-2.0]0 $2"), 3, { 1, 1, 1 } }, + { "talking_with", N_("You are now talking with %_$0%_"), 1, { 0 } }, + + /* ---- */ + { NULL, N_("Server"), 0 }, + + { "looking_up", N_("Looking up %_$0%_"), 1, { 0 } }, + { "connecting", N_("Connecting to %_$0%_ %K[%n$1%K]%n port %_$2%_"), 3, { 0, 0, 1 } }, + { "connection_established", N_("Connection to %_$0%_ established"), 1, { 0 } }, + { "cant_connect", N_("Unable to connect server %_$0%_ port %_$1%_ %K[%n$2%K]"), 3, { 0, 1, 0 } }, + { "connection_lost", N_("Connection lost to %_$0%_"), 1, { 0 } }, + { "server_changed", N_("Changed to %_$2%_ server %_$1%_"), 3, { 0, 0, 0 } }, + { "unknown_server_tag", N_("Unknown server tag %_$0%_"), 1, { 0 } }, + + /* ---- */ + { NULL, N_("Highlighting"), 0 }, + + { "hilight_header", N_("Highlights:"), 0 }, + { "hilight_line", N_("$[-4]0 $1 $2 $3$3$4$5"), 7, { 1, 0, 0, 0, 0, 0, 0 } }, + { "hilight_footer", "", 0 }, + { "hilight_not_found", N_("Highlight not found: $0"), 1, { 0 } }, + { "hilight_removed", N_("Highlight removed: $0"), 1, { 0 } }, + + /* ---- */ + { NULL, N_("Aliases"), 0 }, + + { "alias_added", N_("Alias $0 added"), 1, { 0 } }, + { "alias_removed", N_("Alias $0 removed"), 1, { 0 } }, + { "alias_not_found", N_("No such alias: $0"), 1, { 0 } }, + { "aliaslist_header", N_("Aliases:"), 0 }, + { "aliaslist_line", N_("$[10]0 $1"), 2, { 0, 0 } }, + { "aliaslist_footer", "", 0 }, + + /* ---- */ + { NULL, N_("Logging"), 0 }, + + { "log_opened", N_("Log file %W$0%n opened"), 1, { 0 } }, + { "log_closed", N_("Log file %W$0%n closed"), 1, { 0 } }, + { "log_create_failed", N_("Couldn't create log file %W$0"), 1, { 0 } }, + { "log_locked", N_("Log file %W$0%n is locked, probably by another running Irssi"), 1, { 0 } }, + { "log_not_open", N_("Log file %W$0%n not open"), 1, { 0 } }, + { "log_started", N_("Started logging to file %W$0"), 1, { 0 } }, + { "log_stopped", N_("Stopped logging to file %W$0"), 1, { 0 } }, + { "log_list_header", N_("Logs:"), 0 }, + { "log_list", N_("$0: $1 $2$3$4"), 5, { 0, 0, 0, 0, 0 } }, + { "log_list_footer", N_(""), 0 }, + { "windowlog_file", N_("Window LOGFILE set to $0"), 1, { 0 } }, + { "windowlog_file_logging", N_("Can't change window's logfile while log is on"), 0 }, + + /* ---- */ + { NULL, N_("Misc"), 0 }, + + { "not_toggle", N_("Value must be either ON, OFF or TOGGLE"), 0 } +}; diff --git a/src/fe-common/core/module-formats.h b/src/fe-common/core/module-formats.h new file mode 100644 index 00000000..cce8d48b --- /dev/null +++ b/src/fe-common/core/module-formats.h @@ -0,0 +1,62 @@ +#include "printtext.h" + +enum { + IRCTXT_MODULE_NAME, + + IRCTXT_FILL_1, + + IRCTXT_LINE_START, + IRCTXT_LINE_START_IRSSI, + IRCTXT_TIMESTAMP, + IRCTXT_DAYCHANGE, + IRCTXT_TALKING_WITH, + + IRCTXT_FILL_2, + + IRCTXT_LOOKING_UP, + IRCTXT_CONNECTING, + IRCTXT_CONNECTION_ESTABLISHED, + IRCTXT_CANT_CONNECT, + IRCTXT_CONNECTION_LOST, + IRCTXT_SERVER_CHANGED, + IRCTXT_UNKNOWN_SERVER_TAG, + + IRCTXT_FILL_3, + + IRCTXT_HILIGHT_HEADER, + IRCTXT_HILIGHT_LINE, + IRCTXT_HILIGHT_FOOTER, + IRCTXT_HILIGHT_NOT_FOUND, + IRCTXT_HILIGHT_REMOVED, + + IRCTXT_FILL_4, + + IRCTXT_ALIAS_ADDED, + IRCTXT_ALIAS_REMOVED, + IRCTXT_ALIAS_NOT_FOUND, + IRCTXT_ALIASLIST_HEADER, + IRCTXT_ALIASLIST_LINE, + IRCTXT_ALIASLIST_FOOTER, + + IRCTXT_FILL_5, + + IRCTXT_LOG_OPENED, + IRCTXT_LOG_CLOSED, + IRCTXT_LOG_CREATE_FAILED, + IRCTXT_LOG_LOCKED, + IRCTXT_LOG_NOT_OPEN, + IRCTXT_LOG_STARTED, + IRCTXT_LOG_STOPPED, + IRCTXT_LOG_LIST_HEADER, + IRCTXT_LOG_LIST, + IRCTXT_LOG_LIST_FOOTER, + IRCTXT_WINDOWLOG_FILE, + IRCTXT_WINDOWLOG_FILE_LOGGING, + + IRCTXT_FILL_6, + + IRCTXT_NOT_TOGGLE +}; + +extern FORMAT_REC fecommon_core_formats[]; +#define MODULE_FORMATS fecommon_core_formats diff --git a/src/fe-common/core/module.h b/src/fe-common/core/module.h new file mode 100644 index 00000000..4f6dbc87 --- /dev/null +++ b/src/fe-common/core/module.h @@ -0,0 +1,3 @@ +#include "common.h" + +#define MODULE_NAME "fe-common/core" diff --git a/src/fe-common/core/nick-hilight.c b/src/fe-common/core/nick-hilight.c new file mode 100644 index 00000000..8244ff50 --- /dev/null +++ b/src/fe-common/core/nick-hilight.c @@ -0,0 +1,115 @@ +/* + nick-hilight.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "levels.h" +#include "server.h" + +#include "windows.h" +#include "window-items.h" + +static void sig_hilight_text(SERVER_REC *server, const char *channel, gpointer levelptr, const char *msg) +{ + WINDOW_REC *window; + int level, oldlevel; + + level = GPOINTER_TO_INT(levelptr); + window = window_find_closest(server, channel, level); + if (window == active_win || (level & (MSGLEVEL_NEVER|MSGLEVEL_NO_ACT|MSGLEVEL_MSGS))) + return; + + oldlevel = window->new_data; + if (window->new_data < NEWDATA_TEXT) { + window->new_data = NEWDATA_TEXT; + signal_emit("window hilight", 1, window); + } + + signal_emit("window activity", 2, window, GINT_TO_POINTER(oldlevel)); +} + +static void sig_dehilight(WINDOW_REC *window, WI_ITEM_REC *item) +{ + g_return_if_fail(window != NULL); + + if (item != NULL && item->new_data != 0) { + item->new_data = 0; + signal_emit("window item hilight", 1, item); + } +} + +static void sig_dehilight_window(WINDOW_REC *window) +{ + int oldlevel; + + g_return_if_fail(window != NULL); + + if (window->new_data == 0) + return; + + if (window->new_data != 0) { + oldlevel = window->new_data; + window->new_data = 0; + signal_emit("window hilight", 2, window, GINT_TO_POINTER(oldlevel)); + } + signal_emit("window activity", 2, window, GINT_TO_POINTER(oldlevel)); + + g_slist_foreach(window->items, (GFunc) sig_dehilight, NULL); +} + +static void sig_hilight_window_item(WI_ITEM_REC *item) +{ + WINDOW_REC *window; + GSList *tmp; + int level, oldlevel; + + window = window_item_window(item); level = 0; + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + item = tmp->data; + + if (item->new_data > level) + level = item->new_data; + } + + oldlevel = window->new_data; + if (window->new_data < level || level == 0) { + window->new_data = level; + signal_emit("window hilight", 2, window, GINT_TO_POINTER(oldlevel)); + } + signal_emit("window activity", 2, window, GINT_TO_POINTER(oldlevel)); +} + +void nick_hilight_init(void) +{ + signal_add("print text", (SIGNAL_FUNC) sig_hilight_text); + signal_add("window item changed", (SIGNAL_FUNC) sig_dehilight); + signal_add("window changed", (SIGNAL_FUNC) sig_dehilight_window); + signal_add("window dehilight", (SIGNAL_FUNC) sig_dehilight_window); + signal_add("window item hilight", (SIGNAL_FUNC) sig_hilight_window_item); +} + +void nick_hilight_deinit(void) +{ + signal_remove("print text", (SIGNAL_FUNC) sig_hilight_text); + signal_remove("window item changed", (SIGNAL_FUNC) sig_dehilight); + signal_remove("window changed", (SIGNAL_FUNC) sig_dehilight_window); + signal_remove("window dehilight", (SIGNAL_FUNC) sig_dehilight_window); + signal_remove("window item hilight", (SIGNAL_FUNC) sig_hilight_window_item); +} diff --git a/src/fe-common/core/printtext.c b/src/fe-common/core/printtext.c new file mode 100644 index 00000000..b03658b1 --- /dev/null +++ b/src/fe-common/core/printtext.c @@ -0,0 +1,858 @@ +/* + printtext.c : irssi + + Copyright (C) 1999 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "modules.h" +#include "signals.h" +#include "commands.h" +#include "special-vars.h" +#include "settings.h" + +#include "levels.h" +#include "server.h" + +#include "translation.h" +#include "themes.h" +#include "windows.h" + +static gboolean toggle_show_timestamps, toggle_show_msgs_timestamps, toggle_hide_text_style; +static gint printtag; +static gchar ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + +static gint signal_gui_print_text; +static gint signal_print_text_stripped; +static gint signal_print_text; +static gint signal_print_text_finished; + +void printbeep(void) +{ + signal_emit_id(signal_gui_print_text, 6, active_win, NULL, NULL, + GINT_TO_POINTER(PRINTFLAG_BEEP), "", MSGLEVEL_NEVER); +} + +/* parse ANSI color string */ +static char *convert_ansi(char *str, int *fgcolor, int *bgcolor, int *flags) +{ + gchar *start; + gint fg, bg, fl, num; + + if (*str != '[') return str; + + start = str; + + fg = *fgcolor < 0 ? current_theme->default_color : *fgcolor; + bg = *bgcolor < 0 ? -1 : *bgcolor; + fl = *flags; + + str++; num = 0; + for (;; str++) + { + if (*str == '\0') return start; + + if (isdigit((gint) *str)) + { + num = num*10 + (*str-'0'); + continue; + } + + if (*str != ';' && *str != 'm') return start; + + switch (num) + { + case 0: + /* reset colors back to default */ + fg = current_theme->default_color; + bg = -1; + break; + case 1: + /* hilight */ + fg |= 8; + break; + case 5: + /* blink */ + bg = bg == -1 ? 8 : bg | 8; + break; + case 7: + /* reverse */ + fl |= PRINTFLAG_REVERSE; + break; + default: + if (num >= 30 && num <= 37) + fg = (fg & 0xf8) + ansitab[num-30]; + if (num >= 40 && num <= 47) + { + if (bg == -1) bg = 0; + bg = (bg & 0xf8) + ansitab[num-40]; + } + break; + } + num = 0; + + if (*str == 'm') + { + if (!toggle_hide_text_style) + { + *fgcolor = fg; + *bgcolor = bg == -1 ? -1 : bg; + *flags = fl; + } + str++; + break; + } + } + + return str; +} + +#define IN_COLOR_CODE 2 +#define IN_SECOND_CODE 4 +char *strip_codes(const char *input) +{ + const char *p; + gchar *str, *out; + gint loop_state; + + loop_state = 0; + out = str = g_strdup(input); + for (p = input; *p != '\0'; p++) /* Going through the string till the end k? */ + { + if (*p == '\003') + { + if (p[1] < 17 && p[1] > 0) + { + p++; + if (p[1] < 17 && p[1] > 0) p++; + continue; + } + loop_state = IN_COLOR_CODE; + continue; + } + + if (loop_state & IN_COLOR_CODE) + { + if (isdigit( (gint) *p )) continue; + if (*p != ',' || (loop_state & IN_SECOND_CODE)) + { + /* we're no longer in a color code */ + *out++ = *p; + loop_state &= ~IN_COLOR_CODE|IN_SECOND_CODE; + continue; + } + + /* we're in the second code */ + loop_state |= IN_SECOND_CODE; + continue; + + } + + /* we're not in a color code that means we should add the character */ + if (*p == 4 && p[1] != '\0' && p[2] != '\0') + { + p += 2; + continue; + } + + if (*p == 2 || *p == 22 || *p == 27 || *p == 31 || *p == 15) + continue; + *out++ = *p; + } + + *out = '\0'; + return str; +} + +static gboolean expand_styles(GString *out, char format, void *server, const char *channel, int level) +{ + static const char *backs = "01234567"; + static const char *fores = "krgybmcw"; + static const char *boldfores = "KRGYBMCW"; + gchar *p; + + /* p/P -> m/M */ + if (format == 'p') + format = 'm'; + else if (format == 'P') + format = 'M'; + + switch (format) + { + case 'U': + /* Underline on/off */ + g_string_append_c(out, 4); + g_string_append_c(out, -1); + g_string_append_c(out, 2); + break; + case '9': + case '_': + /* bold on/off */ + g_string_append_c(out, 4); + g_string_append_c(out, -1); + g_string_append_c(out, 1); + break; + case '8': + /* reverse */ + g_string_append_c(out, 4); + g_string_append_c(out, -1); + g_string_append_c(out, 3); + break; + case '%': + g_string_append_c(out, '%'); + break; + case ':': + /* Newline */ + printtext(server, channel, level, out->str); + g_string_truncate(out, 0); + break; + + case '|': + /* Indent here mark */ + g_string_append_c(out, 4); + g_string_append_c(out, -1); + g_string_append_c(out, 4); + break; + + case 'F': + /* flashing - ignore */ + break; + + case 'N': + /* don't put clear-color tag at the end of the output - ignore */ + break; + + case 'n': + /* default color */ + g_string_append_c(out, 4); + g_string_append_c(out, -1); + g_string_append_c(out, -1); + break; + + default: + /* check if it's a background color */ + p = strchr(backs, format); + if (p != NULL) + { + g_string_append_c(out, 4); + g_string_append_c(out, -2); + g_string_append_c(out, ansitab[(gint) (p-backs)]+1); + break; + } + + /* check if it's a foreground color */ + p = strchr(fores, format); + if (p != NULL) + { + g_string_append_c(out, 4); + g_string_append_c(out, ansitab[(gint) (p-fores)]+1); + g_string_append_c(out, -2); + break; + } + + /* check if it's a bold foreground color */ + p = strchr(boldfores, format); + if (p != NULL) + { + g_string_append_c(out, 4); + g_string_append_c(out, 8+ansitab[(gint) (p-boldfores)]+1); + g_string_append_c(out, -2); + break; + } + return FALSE; + } + + return TRUE; +} + +static void read_arglist(va_list va, FORMAT_REC *format, + char **arglist, int arglist_size, + char *buffer, int buffer_size) +{ + int num, len, bufpos; + + bufpos = 0; + for (num = 0; num < format->params && num < arglist_size; num++) { + switch (format->paramtypes[num]) { + case FORMAT_STRING: + arglist[num] = (char *) va_arg(va, char *); + if (arglist[num] == NULL) { + g_warning("output_format_text_args() : parameter %d is NULL", num); + arglist[num] = ""; + } + break; + case FORMAT_INT: { + int d = (int) va_arg(va, int); + + if (bufpos >= buffer_size) { + arglist[num] = ""; + break; + } + + arglist[num] = buffer+bufpos; + len = g_snprintf(buffer+bufpos, buffer_size-bufpos, + "%d", d); + bufpos += len+1; + break; + } + case FORMAT_LONG: { + long l = (long) va_arg(va, long); + + if (bufpos >= buffer_size) { + arglist[num] = ""; + break; + } + + arglist[num] = buffer+bufpos; + len = g_snprintf(buffer+bufpos, buffer_size-bufpos, + "%ld", l); + bufpos += len+1; + break; + } + case FORMAT_FLOAT: { + double f = (double) va_arg(va, double); + + if (bufpos >= buffer_size) { + arglist[num] = ""; + break; + } + + arglist[num] = buffer+bufpos; + len = g_snprintf(buffer+bufpos, buffer_size-bufpos, + "%0.2f", f); + bufpos += len+1; + break; + } + } + } +} + +static void output_format_text_args(GString *out, void *server, const char *channel, int level, FORMAT_REC *format, const char *text, va_list args) +{ + char *arglist[10]; + char buffer[200]; /* should be enough? (won't overflow even if it isn't) */ + + const char *str; + char code; + int need_free; + + str = current_theme != NULL && text != NULL ? text : format->def; + + /* read all optional arguments to arglist[] list + so they can be used in any order.. */ + read_arglist(args, format, + arglist, sizeof(arglist)/sizeof(void*), + buffer, sizeof(buffer)); + + code = 0; + while (*str != '\0') { + if (code == '%') { + /* color code */ + if (!expand_styles(out, *str, server, channel, level)) { + g_string_append_c(out, '%'); + g_string_append_c(out, '%'); + g_string_append_c(out, *str); + } + code = 0; + } else if (code == '$') { + /* argument */ + char *ret; + + ret = parse_special((char **) &str, active_win->active_server, active_win->active, arglist, &need_free, NULL); + if (ret != NULL) { + g_string_append(out, ret); + if (need_free) g_free(ret); + } + code = 0; + } else { + if (*str == '%' || *str == '$') + code = *str; + else + g_string_append_c(out, *str); + } + + str++; + } +} + +static void output_format_text(GString *out, void *server, const char *channel, int level, int formatnum, ...) +{ + MODULE_THEME_REC *theme; + va_list args; + + theme = g_hash_table_lookup(current_theme->modules, MODULE_FORMATS->tag); + + va_start(args, formatnum); + output_format_text_args(out, server, channel, level, + &MODULE_FORMATS[formatnum], + theme == NULL ? NULL : theme->format[formatnum], args); + va_end(args); +} + +static void add_timestamp(WINDOW_REC *window, GString *out, void *server, const char *channel, int level) +{ + time_t t; + struct tm *tm; + GString *tmp; + + if (!(level != MSGLEVEL_NEVER && (toggle_show_timestamps || (toggle_show_msgs_timestamps && (level & MSGLEVEL_MSGS) != 0)))) + return; + + t = time(NULL); + + if ((t - window->last_timestamp) < settings_get_int("timestamp_timeout")) { + window->last_timestamp = t; + return; + } + window->last_timestamp = t; + + tmp = g_string_new(NULL); + tm = localtime(&t); + output_format_text(tmp, server, channel, level, IRCTXT_TIMESTAMP, + tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); + + /* insert the timestamp right after \n */ + g_string_prepend(out, tmp->str); + g_string_free(tmp, TRUE); +} + +static void new_line_stuff(GString *out, void *server, const char *channel, int level) +{ + if ((level & (MSGLEVEL_CLIENTERROR|MSGLEVEL_CLIENTNOTICE)) != 0) + output_format_text(out, server, channel, level, IRCTXT_LINE_START_IRSSI); + else if ((level & (MSGLEVEL_MSGS|MSGLEVEL_PUBLIC|MSGLEVEL_NOTICES|MSGLEVEL_SNOTES|MSGLEVEL_CTCPS|MSGLEVEL_ACTIONS|MSGLEVEL_DCC|MSGLEVEL_CLIENTCRAP)) == 0 && level != MSGLEVEL_NEVER) + output_format_text(out, server, channel, level, IRCTXT_LINE_START); +} + +/* Write text to channel - convert color codes */ +void printtext(void *server, const char *channel, int level, const char *str, ...) +{ + va_list args; + GString *out; + gchar *tmpstr; + gint pros; + + g_return_if_fail(str != NULL); + + va_start(args, str); + + pros = 0; + out = g_string_new(NULL); + + new_line_stuff(out, server, channel, level); + for (; *str != '\0'; str++) + { + if (*str != '%') + { + g_string_append_c(out, *str); + continue; + } + + if (*++str == '\0') break; + switch (*str) + { + /* standard parameters */ + case 's': + { + gchar *s = (gchar *) va_arg(args, gchar *); + if (s && *s) g_string_append(out, s); + break; + } + case 'd': + { + gint d = (gint) va_arg(args, gint); + g_string_sprintfa(out, "%d", d); + break; + } + case 'f': + { + gdouble f = (gdouble) va_arg(args, gdouble); + g_string_sprintfa(out, "%0.2f", f); + break; + } + case 'u': + { + guint d = (guint) va_arg(args, guint); + g_string_sprintfa(out, "%u", d); + break; + } + case 'l': + { + gulong d = (gulong) va_arg(args, gulong); + if (*++str != 'd' && *str != 'u') + { + g_string_sprintfa(out, "%ld", d); + str--; + } + else + { + if (*str == 'd') + g_string_sprintfa(out, "%ld", d); + else + g_string_sprintfa(out, "%lu", d); + } + break; + } + default: + if (!expand_styles(out, *str, server, channel, level)) + { + g_string_append_c(out, '%'); + g_string_append_c(out, *str); + } + break; + } + } + va_end(args); + + /* send the plain text version for logging.. */ + tmpstr = strip_codes(out->str); + signal_emit_id(signal_print_text_stripped, 4, server, channel, GINT_TO_POINTER(level), tmpstr); + g_free(tmpstr); + + signal_emit_id(signal_print_text, 4, server, channel, GINT_TO_POINTER(level), out->str); + + g_string_free(out, TRUE); +} + +void printformat_format(FORMAT_REC *formats, void *server, const char *channel, int level, int formatnum, ...) +{ + MODULE_THEME_REC *theme; + GString *out; + va_list args; + + va_start(args, formatnum); + out = g_string_new(NULL); + + theme = g_hash_table_lookup(current_theme->modules, formats->tag); + + output_format_text_args(out, server, channel, level, + &formats[formatnum], + theme == NULL ? NULL : theme->format[formatnum], args); + if (out->len > 0) printtext(server, channel, level, "%s", out->str); + + g_string_free(out, TRUE); + va_end(args); +} + +static void newline(WINDOW_REC *window) +{ + window->lines++; + if (window->lines != 1) { + signal_emit_id(signal_gui_print_text, 6, window, + GINT_TO_POINTER(-1), GINT_TO_POINTER(-1), + GINT_TO_POINTER(0), "\n", GINT_TO_POINTER(-1)); + } +} + +static void sig_print_text(void *server, const char *target, gpointer level, const char *text) +{ + WINDOW_REC *window; + GString *out; + gchar *dup, *ptr, type, *str; + gint fgcolor, bgcolor; + gint flags; + + g_return_if_fail(text != NULL); + + window = window_find_closest(server, target, GPOINTER_TO_INT(level)); + g_return_if_fail(window != NULL); + + flags = 0; fgcolor = -1; bgcolor = -1; type = '\0'; + + newline(window); + + out = g_string_new(text); + if (server != NULL && servers != NULL && servers->next != NULL && + (window->active == NULL || window->active->server != server)) + { + /* connected to more than one server and active server isn't the + same where the message came or we're in status/msgs/empty window - + prefix with a [server tag] */ + gchar *str; + + str = g_strdup_printf("[%s] ", ((SERVER_REC *) server)->tag); + g_string_prepend(out, str); + g_free(str); + } + + add_timestamp(window, out, server, target, GPOINTER_TO_INT(level)); + + dup = str = out->str; + g_string_free(out, FALSE); + + while (*str != '\0') + { + for (ptr = str; *ptr != '\0'; ptr++) + { + if (*ptr == 2 || *ptr == 3 || *ptr == 4 || *ptr == 6 || *ptr == 7 || *ptr == 15 || *ptr == 22 || *ptr == 27 || *ptr == 31) + { + type = *ptr; + *ptr++ = '\0'; + break; + } + + *ptr = (gchar) translation_in[(gint) (guchar) *ptr]; + } + + if (type == 7) + { + /* bell */ + if (settings_get_bool("toggle_bell_beeps")) + flags |= PRINTFLAG_BEEP; + } + if (*str != '\0' || flags & PRINTFLAG_BEEP) + { + signal_emit_id(signal_gui_print_text, 6, window, + GINT_TO_POINTER(fgcolor), GINT_TO_POINTER(bgcolor), + GINT_TO_POINTER(flags), str, level); + flags &= ~(PRINTFLAG_BEEP|PRINTFLAG_INDENT); + } + if (*ptr == '\0') break; + + switch (type) + { + case 2: + /* bold */ + if (!toggle_hide_text_style) + flags ^= PRINTFLAG_BOLD; + break; + case 6: + /* blink */ + if (!toggle_hide_text_style) + flags ^= PRINTFLAG_BLINK; + break; + case 15: + /* remove all styling */ + flags &= PRINTFLAG_BEEP; + fgcolor = bgcolor = -1; + break; + case 22: + /* reverse */ + if (!toggle_hide_text_style) + flags ^= PRINTFLAG_REVERSE; + break; + case 31: + /* underline */ + if (!toggle_hide_text_style) + flags ^= PRINTFLAG_UNDERLINE; + case 27: + /* ansi color code */ + ptr = convert_ansi(ptr, &fgcolor, &bgcolor, &flags); + break; + case 4: + /* user specific colors */ + flags &= ~PRINTFLAG_MIRC_COLOR; + if ((signed char) *ptr == -1) + { + ptr++; + if ((signed char) *ptr == -1) + { + fgcolor = bgcolor = -1; + flags &= PRINTFLAG_INDENT; + } + else if (*ptr == 1) + flags ^= PRINTFLAG_BOLD; + else if (*ptr == 2) + flags ^= PRINTFLAG_UNDERLINE; + else if (*ptr == 3) + flags ^= PRINTFLAG_REVERSE; + else if (*ptr == 4) + flags |= PRINTFLAG_INDENT; + } + else + { + if ((signed char) *ptr != -2) + { + fgcolor = (guchar) *ptr-1; + if (fgcolor <= 7) + flags &= ~PRINTFLAG_BOLD; + else + { + /* bold */ + if (fgcolor != 8) fgcolor -= 8; + flags |= PRINTFLAG_BOLD; + } + } + ptr++; + if ((signed char) *ptr != -2) + bgcolor = (signed char) *ptr == -1 ? -1 : *ptr-1; + } + ptr++; + break; + case 3: + if (*ptr < 17) + { + /* mostly just for irssi's internal use.. */ + fgcolor = (*ptr++)-1; + if (*ptr == 0 || *ptr >= 17) + bgcolor = -1; + else + bgcolor = (*ptr++)-1; + if (fgcolor & 8) + flags |= PRINTFLAG_BOLD; + else + flags &= ~PRINTFLAG_BOLD; + break; + } + + /* MIRC color */ + if (toggle_hide_text_style) + { + /* don't show them. */ + if (isdigit((gint) *ptr)) + { + ptr++; + if (isdigit((gint) *ptr)) ptr++; + if (*ptr == ',') + { + ptr++; + if (isdigit((gint) *ptr)) + { + ptr++; + if (isdigit((gint) *ptr)) ptr++; + } + } + } + break; + } + + flags |= PRINTFLAG_MIRC_COLOR; + if (!isdigit((gint) *ptr) && *ptr != ',') + { + fgcolor = -1; + bgcolor = -1; + } + else + { + /* foreground color */ + if (*ptr != ',') + { + fgcolor = *ptr++-'0'; + if (isdigit((gint) *ptr)) + fgcolor = fgcolor*10 + (*ptr++-'0'); + } + if (*ptr == ',') + { + /* back color */ + bgcolor = 0; + if (!isdigit((gint) *++ptr)) + bgcolor = -1; + else + { + bgcolor = *ptr++-'0'; + if (isdigit((gint) *ptr)) + bgcolor = bgcolor*10 + (*ptr++-'0'); + } + } + } + break; + } + + str = ptr; + } + g_free(dup); + signal_emit_id(signal_print_text_finished, 1, window); +} + +static int sig_check_daychange(void) +{ + static gint lastday = -1; + GSList *tmp; + time_t t; + struct tm *tm; + + if (!toggle_show_timestamps) + { + /* display day change notice only when using timestamps */ + return TRUE; + } + + t = time(NULL); + tm = localtime(&t); + + if (lastday == -1) + { + /* First check, don't display. */ + lastday = tm->tm_mday; + return TRUE; + } + + if (tm->tm_mday == lastday) + return TRUE; + + /* day changed, print notice about it to every window */ + for (tmp = windows; tmp != NULL; tmp = tmp->next) + { + WINDOW_REC *win = tmp->data; + + printformat(win->active->server, win->active->name, MSGLEVEL_NEVER, + IRCTXT_DAYCHANGE, tm->tm_mday, tm->tm_mon+1, 1900+tm->tm_year); + } + lastday = tm->tm_mday; + return TRUE; +} + +static void sig_gui_dialog(const char *type, const char *text) +{ + char **lines, **tmp; + + if (g_strcasecmp(type, "warning") == 0) + type = _("%_Warning:%_ %s"); + else if (g_strcasecmp(type, "error") == 0) + type = _("%_Error:%_ %s"); + else + type = "%s"; + + lines = g_strsplit(text, "\n", -1); + for (tmp = lines; *tmp != NULL; tmp++) + printtext(NULL, NULL, MSGLEVEL_NEVER, type, *tmp); + g_strfreev(lines); +} + +static void read_settings(void) +{ + toggle_show_timestamps = settings_get_bool("toggle_show_timestamps"); + toggle_show_msgs_timestamps = settings_get_bool("toggle_show_msgs_timestamps"); + toggle_hide_text_style = settings_get_bool("toggle_hide_text_style"); +} + +void printtext_init(void) +{ + settings_add_int("misc", "timestamp_timeout", 0); + + signal_gui_print_text = module_get_uniq_id_str("signals", "gui print text"); + signal_print_text_stripped = module_get_uniq_id_str("signals", "print text stripped"); + signal_print_text = module_get_uniq_id_str("signals", "print text"); + signal_print_text_finished = module_get_uniq_id_str("signals", "print text finished"); + + read_settings(); + printtag = g_timeout_add(30000, (GSourceFunc) sig_check_daychange, NULL); + signal_add("print text", (SIGNAL_FUNC) sig_print_text); + signal_add("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + command_bind("beep", NULL, (SIGNAL_FUNC) printbeep); +} + +void printtext_deinit(void) +{ + g_source_remove(printtag); + signal_remove("print text", (SIGNAL_FUNC) sig_print_text); + signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); + command_unbind("beep", (SIGNAL_FUNC) printbeep); +} diff --git a/src/fe-common/core/printtext.h b/src/fe-common/core/printtext.h new file mode 100644 index 00000000..fc2b2c57 --- /dev/null +++ b/src/fe-common/core/printtext.h @@ -0,0 +1,61 @@ +#ifndef __PRINTTEXT_H +#define __PRINTTEXT_H + +enum { + FORMAT_STRING, + FORMAT_INT, + FORMAT_LONG, + FORMAT_FLOAT +}; + +typedef struct { + char *tag; + char *def; + + int params; + int paramtypes[10]; +} FORMAT_REC; + +#define PRINTFLAG_BOLD 0x01 +#define PRINTFLAG_REVERSE 0x02 +#define PRINTFLAG_UNDERLINE 0x04 +#define PRINTFLAG_BEEP 0x08 +#define PRINTFLAG_BLINK 0x10 +#define PRINTFLAG_MIRC_COLOR 0x20 +#define PRINTFLAG_INDENT 0x40 + +/* printformat(...) = printformat_format(module_formats, ...) + + Could this be any harder? :) With GNU C compiler and C99 compilers, + use #define. With others use either inline functions if they are + supported or static functions if they are not.. + */ +#ifdef __GNUC__ +/* GCC */ +# define printformat(server, channel, level, formatnum...) \ + printformat_format(MODULE_FORMATS, server, channel, level, ##formatnum) +#elif defined (_ISOC99_SOURCE) +/* C99 */ +# define printformat(server, channel, level, formatnum, ...) \ + printformat_format(MODULE_FORMATS, server, channel, level, formatnum, __VA_ARGS__) +#else +/* inline/static */ +#ifdef G_CAN_INLINE +inline +#else +static +#endif +void printformat(void *server, const char *channel, int level, int formatnum, ...) +{ + printformat_format(MODULE_FORMATS, server, channel, level, ##formatnum); +} +#endif +void printformat_format(FORMAT_REC *formats, void *server, const char *channel, int level, int formatnum, ...); + +void printtext(void *server, const char *channel, int level, const char *str, ...); +void printbeep(void); + +void printtext_init(void); +void printtext_deinit(void); + +#endif diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c new file mode 100644 index 00000000..0d9735ab --- /dev/null +++ b/src/fe-common/core/themes.c @@ -0,0 +1,278 @@ +/* + themes.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "misc.h" +#include "lib-config/iconfig.h" +#include "settings.h" + +#include "printtext.h" +#include "themes.h" + +GSList *themes; +THEME_REC *current_theme; + +THEME_REC *theme_create(const char *path, const char *name) +{ + THEME_REC *rec; + + g_return_val_if_fail(path != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + rec = g_new0(THEME_REC, 1); + rec->path = g_strdup(path); + rec->name = g_strdup(name); + rec->modules = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal); + signal_emit("theme created", 1, rec); + + return rec; +} + +static void theme_destroy_hash(const char *key, MODULE_THEME_REC *rec) +{ + int n, max; + + max = strarray_length(rec->formatlist); + for (n = 0; n < max; n++) + if (rec->format[n] != NULL) + g_free(rec->format[n]); + g_free(rec->format); + + g_strfreev(rec->formatlist); + g_free(rec->name); + g_free(rec); +} + +void theme_destroy(THEME_REC *rec) +{ + signal_emit("theme destroyed", 1, rec); + g_hash_table_foreach(rec->modules, (GHFunc) theme_destroy_hash, NULL); + g_hash_table_destroy(rec->modules); + + if (rec->bg_pixmap != NULL) g_free(rec->bg_pixmap); + if (rec->font != NULL) g_free(rec->font); + g_free(rec->path); + g_free(rec->name); + g_free(rec); +} + +static THEME_REC *theme_find(const char *name) +{ + GSList *tmp; + + for (tmp = themes; tmp != NULL; tmp = tmp->next) { + THEME_REC *rec = tmp->data; + + if (g_strcasecmp(rec->name, name) == 0) + return rec; + } + + return NULL; +} + +/* Add all *.theme files from directory to themes */ +static void find_themes(gchar *path) +{ + DIR *dirp; + struct dirent *dp; + char *fname, *name; + int len; + + dirp = opendir(path); + if (dirp == NULL) return; + + while ((dp = readdir(dirp)) != NULL) { + len = strlen(dp->d_name); + if (len <= 6 || strcmp(dp->d_name+len-6, ".theme") != 0) + continue; + + name = g_strndup(dp->d_name, strlen(dp->d_name)-6); + if (!theme_find(name)) { + fname = g_strdup_printf("%s/%s", path, dp->d_name); + themes = g_slist_append(themes, theme_create(fname, name)); + g_free(fname); + } + g_free(name); + } + closedir(dirp); +} + +/* Read module texts into theme */ +static void theme_read_module_texts(const char *hashkey, MODULE_THEME_REC *rec, CONFIG_REC *config) +{ + CONFIG_NODE *formats; + GSList *tmp; + char **flist; + int n; + + formats = config_node_traverse(config, "moduleformats", FALSE); + if (formats == NULL) return; + + for (tmp = formats->value; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *node = tmp->data; + + if (node->key == NULL || node->value == NULL) + continue; + + for (n = 0, flist = rec->formatlist; *flist != NULL; flist++, n++) { + if (g_strcasecmp(*flist, node->key) == 0) { + rec->format[n] = g_strdup(node->value); + break; + } + } + } +} + +static int theme_read(THEME_REC *theme, const char *path) +{ + MODULE_THEME_REC *mrec; + CONFIG_REC *config; + CONFIG_NODE *formats; + GSList *tmp; + char *value; + int errors; + + config = config_open(path, -1); + if (config == NULL) { + /* didn't exist or no access? */ + theme->default_color = 15; + return FALSE; + } + + errors = config_parse(config) == -1; + + /* default color */ + theme->default_color = config_get_int(config, NULL, "default_color", 15); + /* get font */ + value = config_get_str(config, NULL, "font", NULL); + theme->font = (value == NULL || *value == '\0') ? NULL : g_strdup(value); + + /* get background pixmap */ + value = config_get_str(config, NULL, "bg_pixmap", NULL); + theme->bg_pixmap = (value == NULL || *value == '\0') ? NULL : g_strdup(value); + /* get background pixmap properties */ + if (config_get_bool(config, NULL, "bg_scrollable", FALSE)) + theme->flags |= THEME_FLAG_BG_SCROLLABLE; + if (config_get_bool(config, NULL, "bg_scaled", TRUE)) + theme->flags |= THEME_FLAG_BG_SCALED; + if (config_get_bool(config, NULL, "bg_shaded", FALSE)) + theme->flags |= THEME_FLAG_BG_SHADED; + + /* Read modules that are defined in this theme. */ + formats = config_node_traverse(config, "modules", FALSE); + if (formats != NULL) { + for (tmp = formats->value; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *node = tmp->data; + + if (node->key == NULL || node->value == NULL) + continue; + + mrec = g_new0(MODULE_THEME_REC, 1); + mrec->name = g_strdup(node->key); + mrec->formatlist = g_strsplit(node->value, " ", -1); + mrec->format = g_new0(char*, strarray_length(mrec->formatlist)); + g_hash_table_insert(theme->modules, mrec->name, mrec); + } + } + + /* Read the texts inside the plugin */ + g_hash_table_foreach(theme->modules, (GHFunc) theme_read_module_texts, config); + + if (errors) { + /* errors fixed - save the theme */ + if (config_write(config, NULL, 0660) == -1) { + /* we probably tried to save to global directory + where we didn't have access.. try saving it to + home dir instead. */ + char *str; + + /* check that we really didn't try to save + it to home dir.. */ + str = g_strdup_printf("%s/.irssi/", g_get_home_dir()); + if (strncmp(path, str, strlen(str)) != 0) { + g_free(str); + str = g_strdup_printf("%s/.irssi/%s", g_get_home_dir(), g_basename(path)); + + config_write(config, str, 0660); + } + g_free(str); + } + } + config_close(config); + + return errors; +} + +static void sig_formats_error(void) +{ + signal_emit("gui dialog", 2, "warning", + "Your theme(s) had some old format strings, " + "these have been changed back to their default values."); + signal_remove("irssi init finished", (SIGNAL_FUNC) sig_formats_error); +} + +void themes_init(void) +{ + THEME_REC *rec; + GSList *tmp; + const char *value; + char *str; + int errors; + + /* first there's default theme.. */ + str = g_strdup_printf("%s/.irssi/default.theme", g_get_home_dir()); + current_theme = theme_create(str, "default"); + current_theme->default_color = 15; + themes = g_slist_append(NULL, current_theme); + g_free(str); + + /* read list of themes */ + str = g_strdup_printf("%s/.irssi", g_get_home_dir()); + find_themes(str); + g_free(str); + find_themes(SYSCONFDIR"/irssi"); + + /* read formats for all themes */ + errors = FALSE; + for (tmp = themes; tmp != NULL; tmp = tmp->next) { + rec = tmp->data; + + if (theme_read(rec, rec->path)) + errors = TRUE; + } + + if (errors) + signal_add("irssi init finished", (SIGNAL_FUNC) sig_formats_error); + + /* find the current theme to use */ + value = settings_get_str("current_theme"); + + rec = theme_find(value); + if (rec != NULL) current_theme = rec; +} + +void themes_deinit(void) +{ + /* free memory used by themes */ + g_slist_foreach(themes, (GFunc) theme_destroy, NULL); + g_slist_free(themes); + themes = NULL; +} diff --git a/src/fe-common/core/themes.h b/src/fe-common/core/themes.h new file mode 100644 index 00000000..7bd4a8f8 --- /dev/null +++ b/src/fe-common/core/themes.h @@ -0,0 +1,40 @@ +#ifndef __THEMES_H +#define __THEMES_H + +#define THEME_FLAG_BG_SCROLLABLE 0x0001 +#define THEME_FLAG_BG_SCALED 0x0002 +#define THEME_FLAG_BG_SHADED 0x0004 + +typedef struct +{ + char *name; + + char **formatlist; + char **format; +} +MODULE_THEME_REC; + +typedef struct { + char *path; + char *name; + + int default_color; + char *bg_pixmap; + char *font; + int flags; + + GHashTable *modules; + + gpointer gui_data; +} THEME_REC; + +extern GSList *themes; +extern THEME_REC *current_theme; + +THEME_REC *theme_create(const char *path, const char *name); +void theme_destroy(THEME_REC *rec); + +void themes_init(void); +void themes_deinit(void); + +#endif diff --git a/src/fe-common/core/translation.c b/src/fe-common/core/translation.c new file mode 100644 index 00000000..2713cc73 --- /dev/null +++ b/src/fe-common/core/translation.c @@ -0,0 +1,122 @@ +/* + translation.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "signals.h" +#include "line-split.h" +#include "misc.h" +#include "settings.h" + +unsigned char translation_in[256], translation_out[256]; + +void translation_reset(void) +{ + int n; + + for (n = 0; n < 256; n++) + translation_in[n] = (unsigned char) n; + for (n = 0; n < 256; n++) + translation_out[n] = (unsigned char) n; +} + +void translate_output(char *text) +{ + while (*text != '\0') { + *text = (char) translation_out[(int) (unsigned char) *text]; + text++; + } +} + +#define gethex(a) \ + (isdigit(a) ? ((a)-'0') : (toupper(a)-'A'+10)) + +void translation_parse_line(const char *str, int *pos) +{ + const char *ptr; + int value; + + for (ptr = str; *ptr != '\0'; ptr++) { + if (ptr[0] != '0' || ptr[1] != 'x') + break; + ptr += 2; + + value = (gethex(ptr[0]) << 4) + gethex(ptr[1]); + if (*pos < 256) + translation_in[*pos] = (unsigned char) value; + else + translation_out[*pos-256] = (unsigned char) value; + (*pos)++; + + ptr += 2; + if (*ptr != ',') break; + } +} + +int translation_read(const char *file) +{ + char tmpbuf[1024], *str, *path; + LINEBUF_REC *buffer; + int f, pos, ret, recvlen; + + g_return_val_if_fail(file != NULL, FALSE); + + path = convert_home(file); + f = open(file, O_RDONLY); + g_free(path); + + if (f == -1) return FALSE; + + pos = 0; buffer = NULL; + while (pos < 512) { + recvlen = read(f, tmpbuf, sizeof(tmpbuf)); + + ret = line_split(tmpbuf, recvlen, &str, &buffer); + if (ret <= 0) break; + + translation_parse_line(str, &pos); + } + line_split_free(buffer); + + close(f); + if (pos != 512) + translation_reset(); + return pos == 512; +} + +static void read_settings(void) +{ + translation_read(settings_get_str("translation")); +} + +void translation_init(void) +{ + translation_reset(); + + settings_add_str("misc", "translation", ""); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); + + read_settings(); +} + +void translation_deinit(void) +{ + read_settings(); + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); +} diff --git a/src/fe-common/core/translation.h b/src/fe-common/core/translation.h new file mode 100644 index 00000000..48b2c3dc --- /dev/null +++ b/src/fe-common/core/translation.h @@ -0,0 +1,12 @@ +#ifndef __TRANSLATION_H +#define __TRANSLATION_H + +extern unsigned char translation_in[256], translation_out[256]; + +int translation_read(const char *file); +void translate_output(char *text); + +void translation_init(void); +void translation_deinit(void); + +#endif diff --git a/src/fe-common/core/window-items.c b/src/fe-common/core/window-items.c new file mode 100644 index 00000000..ac4c30d5 --- /dev/null +++ b/src/fe-common/core/window-items.c @@ -0,0 +1,224 @@ +/* + window-items.c : irssi + + Copyright (C) 2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "modules.h" +#include "signals.h" +#include "server.h" +#include "settings.h" + +#include "levels.h" + +#include "printtext.h" +#include "windows.h" +#include "window-items.h" + +void window_add_item(WINDOW_REC *window, WI_ITEM_REC *item, int automatic) +{ + g_return_if_fail(window != NULL); + g_return_if_fail(item != NULL); + + MODULE_DATA_SET(item, window); + + if (window->items == NULL) { + window->active = item; + window->active_server = item->server; + } + + signal_emit("gui window item init", 1, item); + + if (!automatic || settings_get_bool("window_auto_change")) { + if (automatic) + signal_emit("window changed automatic", 1, window); + window_set_active(window); + } + + window->items = g_slist_append(window->items, item); + signal_emit("window item new", 2, window, item); + + if (!automatic || g_slist_length(window->items) == 1) { + window->active = NULL; + window_item_set_active(window, item); + } +} + +void window_remove_item(WINDOW_REC *window, WI_ITEM_REC *item) +{ + g_return_if_fail(window != NULL); + g_return_if_fail(item != NULL); + + if (g_slist_find(window->items, item) == NULL) + return; + + MODULE_DATA_SET(item, NULL); + window->items = g_slist_remove(window->items, item); + + if (window->active == item) { + window->active = window->items == NULL ? NULL : + window->items->data; + } + + signal_emit("window item remove", 2, window, item); +} + +WINDOW_REC *window_item_window(WI_ITEM_REC *item) +{ + g_return_val_if_fail(item != NULL, NULL); + + return MODULE_DATA(item); +} + +void window_item_set_active(WINDOW_REC *window, WI_ITEM_REC *item) +{ + g_return_if_fail(window != NULL); + + if (window->active != item) { + window->active = item; + if (item != NULL) window_change_server(window, window->active_server); + signal_emit("window item changed", 2, window, item); + } +} + +void window_item_change_server(WI_ITEM_REC *item, void *server) +{ + WINDOW_REC *window; + + g_return_if_fail(item != NULL); + + window = MODULE_DATA(item); + item->server = server; + + signal_emit("window item server changed", 2, window, item); + if (window->active == item) window_change_server(window, item->server); +} + +static WI_ITEM_REC *window_item_find_window(WINDOW_REC *window, void *server, const char *name) +{ + GSList *tmp; + + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *rec = tmp->data; + + if ((server == NULL || rec->server == server) && + g_strcasecmp(name, rec->name) == 0) return rec; + } + + return NULL; +} + +/* Find wanted window item by name. `server' can be NULL. */ +WI_ITEM_REC *window_item_find(void *server, const char *name) +{ + WI_ITEM_REC *item; + GSList *tmp; + + g_return_val_if_fail(name != NULL, NULL); + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + item = window_item_find_window(rec, server, name); + if (item != NULL) return item; + } + + return NULL; +} + +static int waiting_channels_get(WINDOW_REC *window, const char *tag) +{ + GSList *tmp; + + g_return_val_if_fail(window != NULL, FALSE); + g_return_val_if_fail(tag != NULL, FALSE); + + for (tmp = window->waiting_channels; tmp != NULL; tmp = tmp->next) { + if (g_strcasecmp(tmp->data, tag) == 0) { + g_free(tmp->data); + window->waiting_channels = g_slist_remove(window->waiting_channels, tmp->data); + return TRUE; + } + } + + return FALSE; +} + +void window_item_create(WI_ITEM_REC *item, int automatic) +{ + WINDOW_REC *window; + GSList *tmp; + char *str; + + g_return_if_fail(item != NULL); + + str = item->server == NULL ? NULL : + g_strdup_printf("%s %s", ((SERVER_REC *) item->server)->tag, item->name); + + window = NULL; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->items == NULL && rec->level == 0 && + (window == NULL || rec == active_win)) { + /* no items in this window, we should probably use it.. */ + window = rec; + } + + if (rec->waiting_channels != NULL && str != NULL) { + /* right name/server tag combination in + some waiting list? */ + if (waiting_channels_get(rec, str)) { + window = rec; + break; + } + } + } + g_free_not_null(str); + + if (window == NULL) { + /* create new window to use */ + window = window_create(item, automatic); + } else { + /* use existing window */ + window_add_item(window, item, automatic); + } +} + +static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item) +{ + g_return_if_fail(window != NULL); + + if (g_slist_length(window->items) > 1) { + /* default to printing "talking with ...", + you can override it it you wish */ + printformat(item->server, item->name, MSGLEVEL_CLIENTNOTICE, + IRCTXT_TALKING_WITH, item->name); + } +} + +void window_items_init(void) +{ + signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed); +} + +void window_items_deinit(void) +{ + signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed); +} diff --git a/src/fe-common/core/window-items.h b/src/fe-common/core/window-items.h new file mode 100644 index 00000000..c760ee36 --- /dev/null +++ b/src/fe-common/core/window-items.h @@ -0,0 +1,22 @@ +#ifndef __WINDOW_ITEMS_H +#define __WINDOW_ITEMS_H + +#include "windows.h" + +/* Add/remove window item from `window' */ +void window_add_item(WINDOW_REC *window, WI_ITEM_REC *item, int automatic); +void window_remove_item(WINDOW_REC *window, WI_ITEM_REC *item); +/* Find a window for `item' and call window_add_item(). */ +void window_item_create(WI_ITEM_REC *item, int automatic); + +WINDOW_REC *window_item_window(WI_ITEM_REC *item); +void window_item_set_active(WINDOW_REC *window, WI_ITEM_REC *item); +void window_item_change_server(WI_ITEM_REC *item, void *server); + +/* Find wanted window item by name. `server' can be NULL. */ +WI_ITEM_REC *window_item_find(void *server, const char *name); + +void window_items_init(void); +void window_items_deinit(void); + +#endif diff --git a/src/fe-common/core/windows.c b/src/fe-common/core/windows.c new file mode 100644 index 00000000..3a88833b --- /dev/null +++ b/src/fe-common/core/windows.c @@ -0,0 +1,466 @@ +/* + windows.c : irssi + + Copyright (C) 1999-2000 Timo Sirainen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "module.h" +#include "module-formats.h" +#include "modules.h" +#include "signals.h" +#include "commands.h" +#include "server.h" +#include "settings.h" + +#include "levels.h" + +#include "printtext.h" +#include "windows.h" +#include "window-items.h" + +GSList *windows; +WINDOW_REC *active_win; + +static int window_get_new_refnum(void) +{ + WINDOW_REC *win; + GSList *tmp; + int refnum; + + refnum = 1; + tmp = windows; + while (tmp != NULL) { + win = tmp->data; + + if (refnum != win->refnum) { + tmp = tmp->next; + continue; + } + + refnum++; + tmp = windows; + } + + return refnum; +} + +WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic) +{ + WINDOW_REC *rec; + + rec = g_new0(WINDOW_REC, 1); + rec->refnum = window_get_new_refnum(); + + windows = g_slist_append(windows, rec); + signal_emit("window created", 2, rec, GINT_TO_POINTER(automatic)); + + if (item != NULL) window_add_item(rec, item, automatic); + if (windows->next == NULL || !automatic || settings_get_bool("window_auto_change")) { + if (automatic && windows->next != NULL) + signal_emit("window changed automatic", 1, rec); + window_set_active(rec); + } + return rec; +} + +void window_destroy(WINDOW_REC *window) +{ + g_return_if_fail(window != NULL); + + if (window->destroying) return; + window->destroying = TRUE; + + while (window->items != NULL) + window_remove_item(window, window->items->data); + + windows = g_slist_remove(windows, window); + signal_emit("window destroyed", 1, window); + + g_slist_foreach(window->waiting_channels, (GFunc) g_free, NULL); + g_slist_free(window->waiting_channels); + + g_free_not_null(window->name); + g_free(window); +} + +void window_set_active_num(int number) +{ + GSList *win; + + win = g_slist_nth(windows, number); + if (win == NULL) return; + + active_win = win->data; + signal_emit("window changed", 1, active_win); +} + +void window_set_active(WINDOW_REC *window) +{ + int number; + + number = g_slist_index(windows, window); + if (number == -1) return; + + active_win = window; + signal_emit("window changed", 1, active_win); +} + +void window_change_server(WINDOW_REC *window, void *server) +{ + window->active_server = server; + signal_emit("window server changed", 2, window, server); +} + +void window_set_name(WINDOW_REC *window, const char *name) +{ + g_free_not_null(window->name); + window->name = g_strdup(name); + + signal_emit("window name changed", 1, window); +} + +void window_set_level(WINDOW_REC *window, int level) +{ + g_return_if_fail(window != NULL); + + window->level = level; + signal_emit("window level changed", 1, window); +} + +WINDOW_REC *window_find_level(void *server, int level) +{ + WINDOW_REC *match; + GSList *tmp; + + match = NULL; + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if ((server == NULL || rec->active_server == server) && + (rec->level & level)) { + if (server == NULL || rec->active_server == server) + return rec; + match = rec; + } + } + + return match; +} + +WINDOW_REC *window_find_closest(void *server, const char *name, int level) +{ + WINDOW_REC *window; + WI_ITEM_REC *item; + + /* match by name */ + item = name == NULL ? NULL : + window_item_find(server, name); + if (item != NULL) + return window_item_window(item); + + /* match by level */ + if (level != MSGLEVEL_HILIGHT) + level &= ~(MSGLEVEL_HILIGHT | MSGLEVEL_NOHILIGHT); + window = window_find_level(server, level); + if (window != NULL) return window; + + /* fallback to active */ + return active_win; +} + +static void cmd_window(const char *data, void *server, WI_ITEM_REC *item) +{ + command_runsub("window", data, server, item); +} + +static void cmd_window_new(const char *data, void *server, WI_ITEM_REC *item) +{ + WINDOW_REC *window; + int type; + + g_return_if_fail(data != NULL); + + type = (g_strcasecmp(data, "hide") == 0 || g_strcasecmp(data, "tab") == 0) ? 1 : + (g_strcasecmp(data, "split") == 0 ? 2 : 0); + signal_emit("gui window create override", 1, GINT_TO_POINTER(type)); + + window = window_create(NULL, FALSE); + window_change_server(window, server); +} + +static void cmd_window_close(const char *data) +{ + /* destroy window unless it's the last one */ + if (windows->next != NULL) + window_destroy(active_win); +} + +/* return the first window number with the highest activity */ +static int window_highest_activity(WINDOW_REC *window) +{ + WINDOW_REC *rec; + GSList *tmp; + int max_num, max_act, through; + + max_num = 0; max_act = 0; through = FALSE; + + tmp = g_slist_find(windows, window); + for (;; tmp = tmp->next) { + if (tmp == NULL) { + tmp = windows; + through = TRUE; + } + + if (through && tmp->data == window) + break; + + rec = tmp->data; + + if (rec->new_data && max_act < rec->new_data) { + max_act = rec->new_data; + max_num = g_slist_index(windows, rec)+1; + } + } + + return max_num; +} + +/* channel name - first try channel from same server */ +static int window_find_name(WINDOW_REC *window, const char *name) +{ + WI_ITEM_REC *item; + int num; + + item = window_item_find(window->active_server, name); + if (item == NULL && window->active_server != NULL) { + /* not found from the active server - any server? */ + item = window_item_find(NULL, name); + } + + if (item == NULL) { + char *chan; + + /* still nothing? maybe user just left the # in front of + channel, try again with it.. */ + chan = g_strdup_printf("#%s", name); + item = window_item_find(window->active_server, chan); + if (item == NULL) item = window_item_find(NULL, chan); + g_free(chan); + } + + if (item == NULL) + return 0; + + /* get the window number */ + window = MODULE_DATA(item); + if (window == NULL) return 0; + + num = g_slist_index(windows, window); + return num < 0 ? 0 : num+1; +} + +static void cmd_window_goto(const char *data) +{ + int num; + + g_return_if_fail(data != NULL); + + num = 0; + if (g_strcasecmp(data, "active") == 0) + num = window_highest_activity(active_win); + else if (isdigit(*data)) + num = atol(data); + else + num = window_find_name(active_win, data); + + if (num > 0) + window_set_active_num(num-1); +} + +static void cmd_window_next(const char *data) +{ + int num; + + num = g_slist_index(windows, active_win)+1; + if (num >= g_slist_length(windows)) num = 0; + window_set_active_num(num); +} + +static void cmd_window_prev(const char *data) +{ + int num; + + num = g_slist_index(windows, active_win)-1; + if (num < 0) num = g_slist_length(windows)-1; + window_set_active_num(num); +} + +static void cmd_window_level(const char *data) +{ + g_return_if_fail(data != NULL); + + window_set_level(active_win, combine_level(active_win->level, data)); + printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, "Window level is now %s", + bits2level(active_win->level)); +} + +static void cmd_window_server(const char *data) +{ + SERVER_REC *server; + + g_return_if_fail(data != NULL); + + server = server_find_tag(data); + if (server == NULL) + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_UNKNOWN_SERVER_TAG, data); + else if (active_win->active == NULL) { + window_change_server(active_win, server); + printformat(NULL, NULL, MSGLEVEL_CLIENTNOTICE, IRCTXT_SERVER_CHANGED, server->tag, server->connrec->address, + server->connrec->ircnet == NULL ? "" : server->connrec->ircnet); + } +} + +static void cmd_window_item_prev(const char *data, void *server, WI_ITEM_REC *item) +{ + WINDOW_REC *window; + WI_ITEM_REC *last; + GSList *tmp; + + window = item == NULL ? NULL : MODULE_DATA(item); + if (window == NULL) return; + + last = NULL; + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *rec = tmp->data; + + if (rec != item) + last = rec; + else { + /* current channel. did we find anything? + if not, go to the last channel */ + if (last != NULL) break; + } + } + + if (last != NULL) + window_item_set_active(window, last); +} + +static void cmd_window_item_next(const char *data, void *server, WI_ITEM_REC *item) +{ + WINDOW_REC *window; + WI_ITEM_REC *next; + GSList *tmp; + int gone; + + window = item == NULL ? NULL : MODULE_DATA(item); + if (window == NULL) return; + + next = NULL; gone = FALSE; + for (tmp = window->items; tmp != NULL; tmp = tmp->next) { + WI_ITEM_REC *rec = tmp->data; + + if (rec == item) + gone = TRUE; + else { + if (gone) { + /* found the next channel */ + next = rec; + break; + } + + if (next == NULL) + next = rec; /* fallback to first channel */ + } + } + + if (next != NULL) + window_item_set_active(window, next); +} + +static void cmd_window_name(const char *data) +{ + window_set_name(active_win, data); +} + +static void sig_server_looking(void *server) +{ + GSList *tmp; + + g_return_if_fail(server != NULL); + + /* try to keep some server assigned to windows.. */ + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->active_server == NULL) + window_change_server(rec, server); + } +} + +static void sig_server_disconnected(void *server) +{ + GSList *tmp; + + g_return_if_fail(server != NULL); + + for (tmp = windows; tmp != NULL; tmp = tmp->next) { + WINDOW_REC *rec = tmp->data; + + if (rec->active_server == server) + window_change_server(rec, NULL); + } +} + +void windows_init(void) +{ + active_win = NULL; + settings_add_bool("lookandfeel", "window_auto_change", FALSE); + + command_bind("window", NULL, (SIGNAL_FUNC) cmd_window); + command_bind("window new", NULL, (SIGNAL_FUNC) cmd_window_new); + command_bind("window close", NULL, (SIGNAL_FUNC) cmd_window_close); + command_bind("window server", NULL, (SIGNAL_FUNC) cmd_window_server); + command_bind("window goto", NULL, (SIGNAL_FUNC) cmd_window_goto); + command_bind("window prev", NULL, (SIGNAL_FUNC) cmd_window_prev); + command_bind("window next", NULL, (SIGNAL_FUNC) cmd_window_next); + command_bind("window level", NULL, (SIGNAL_FUNC) cmd_window_level); + command_bind("window item prev", NULL, (SIGNAL_FUNC) cmd_window_item_prev); + command_bind("window item next", NULL, (SIGNAL_FUNC) cmd_window_item_next); + command_bind("window name", NULL, (SIGNAL_FUNC) cmd_window_name); + signal_add("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_add("server connect failed", (SIGNAL_FUNC) sig_server_disconnected); +} + +void windows_deinit(void) +{ + command_unbind("window", (SIGNAL_FUNC) cmd_window); + command_unbind("window new", (SIGNAL_FUNC) cmd_window_new); + command_unbind("window close", (SIGNAL_FUNC) cmd_window_close); + command_unbind("window server", (SIGNAL_FUNC) cmd_window_server); + command_unbind("window goto", (SIGNAL_FUNC) cmd_window_goto); + command_unbind("window prev", (SIGNAL_FUNC) cmd_window_prev); + command_unbind("window next", (SIGNAL_FUNC) cmd_window_next); + command_unbind("window level", (SIGNAL_FUNC) cmd_window_level); + command_unbind("window item prev", (SIGNAL_FUNC) cmd_window_item_prev); + command_unbind("window item next", (SIGNAL_FUNC) cmd_window_item_next); + command_unbind("window name", (SIGNAL_FUNC) cmd_window_name); + signal_remove("server looking", (SIGNAL_FUNC) sig_server_looking); + signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected); + signal_remove("server connect failed", (SIGNAL_FUNC) sig_server_disconnected); +} diff --git a/src/fe-common/core/windows.h b/src/fe-common/core/windows.h new file mode 100644 index 00000000..f279e888 --- /dev/null +++ b/src/fe-common/core/windows.h @@ -0,0 +1,67 @@ +#ifndef __WINDOWS_H +#define __WINDOWS_H + +enum { + NEWDATA_TEXT = 1, + NEWDATA_MSG, + NEWDATA_MSG_FORYOU, + NEWDATA_CUSTOM +}; + +/* All window items *MUST* have these variables in same order + at the start of the structure - the server's type can of course be + replaced with the preferred record type. */ +typedef struct { + int type; + GHashTable *module_data; + + void *server; + char *name; + + int new_data; +} WI_ITEM_REC; + +typedef struct { + int refnum; + char *name; + + GSList *items; + WI_ITEM_REC *active; + void *active_server; + + GSList *waiting_channels; /* list of "<server tag> <channel>" */ + + int lines; + int destroying:1; + + /* window-specific command line history */ + GList *cmdhist, *histpos; + int histlines; + + int level; + int new_data; + time_t last_timestamp; /* When was last timestamp printed */ + + gpointer gui_data; +} WINDOW_REC; + +extern GSList *windows; +extern WINDOW_REC *active_win; + +WINDOW_REC *window_create(WI_ITEM_REC *item, int automatic); +void window_destroy(WINDOW_REC *window); + +void window_set_active_num(int number); +void window_set_active(WINDOW_REC *window); +void window_change_server(WINDOW_REC *window, void *server); + +void window_set_name(WINDOW_REC *window, const char *name); + +void window_set_level(WINDOW_REC *window, int level); +WINDOW_REC *window_find_level(void *server, int level); +WINDOW_REC *window_find_closest(void *server, const char *name, int level); + +void windows_init(void); +void windows_deinit(void); + +#endif |