4 Copyright (C) 1999-2000 Timo Sirainen
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "module-formats.h"
24 #include "special-vars.h"
29 #include "fe-windows.h"
32 #include "translation.h"
34 static const char *format_backs = "04261537";
35 static const char *format_fores = "kbgcrmyw";
36 static const char *format_boldfores = "KBGCRMYW";
38 static int signal_gui_print_text;
39 static int hide_text_style, hide_server_tags;
41 static int timestamps, msgs_timestamps;
42 static int timestamp_timeout;
44 int format_find_tag(const char *module, const char *tag)
49 formats = g_hash_table_lookup(default_formats, module);
53 for (n = 0; formats[n].def != NULL; n++) {
54 if (formats[n].tag != NULL &&
55 g_strcasecmp(formats[n].tag, tag) == 0)
62 int format_expand_styles(GString *out, char format)
68 /* Underline on/off */
69 g_string_append_c(out, 4);
70 g_string_append_c(out, FORMAT_STYLE_UNDERLINE);
75 g_string_append_c(out, 4);
76 g_string_append_c(out, FORMAT_STYLE_BOLD);
80 g_string_append_c(out, 4);
81 g_string_append_c(out, FORMAT_STYLE_REVERSE);
84 g_string_append_c(out, '%');
88 g_string_append_c(out, '\n');
92 g_string_append_c(out, 4);
93 g_string_append_c(out, FORMAT_STYLE_INDENT);
97 g_string_append_c(out, 4);
98 g_string_append_c(out, FORMAT_STYLE_BLINK);
103 g_string_append_c(out, 4);
104 g_string_append_c(out, FORMAT_STYLE_DEFAULTS);
107 /* check if it's a background color */
108 p = strchr(format_backs, format);
110 g_string_append_c(out, 4);
111 g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
112 g_string_append_c(out, (char) ((int) (p-format_backs)+'0'));
116 /* check if it's a foreground color */
117 if (format == 'p') format = 'm';
118 p = strchr(format_fores, format);
120 g_string_append_c(out, 4);
121 g_string_append_c(out, (char) ((int) (p-format_fores)+'0'));
122 g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
126 /* check if it's a bold foreground color */
127 if (format == 'P') format = 'M';
128 p = strchr(format_boldfores, format);
130 g_string_append_c(out, 4);
131 g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0'));
132 g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
142 void format_read_arglist(va_list va, FORMAT_REC *format,
143 char **arglist, int arglist_size,
144 char *buffer, int buffer_size)
146 int num, len, bufpos;
148 g_return_if_fail(format->params < arglist_size);
151 arglist[format->params] = NULL;
152 for (num = 0; num < format->params; num++) {
153 switch (format->paramtypes[num]) {
155 arglist[num] = (char *) va_arg(va, char *);
156 if (arglist[num] == NULL) {
157 g_warning("format_read_arglist() : parameter %d is NULL", num);
162 int d = (int) va_arg(va, int);
164 if (bufpos >= buffer_size) {
169 arglist[num] = buffer+bufpos;
170 len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
176 long l = (long) va_arg(va, long);
178 if (bufpos >= buffer_size) {
183 arglist[num] = buffer+bufpos;
184 len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
190 double f = (double) va_arg(va, double);
192 if (bufpos >= buffer_size) {
197 arglist[num] = buffer+bufpos;
198 len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
207 void format_create_dest(TEXT_DEST_REC *dest,
208 void *server, const char *target,
209 int level, WINDOW_REC *window)
211 dest->server = server;
212 dest->target = target;
214 dest->window = window != NULL ? window :
215 window_find_closest(server, target, level);
217 dest->hilight_priority = 0;
218 dest->hilight_color = NULL;
221 /* Return length of text part in string (ie. without % codes) */
222 int format_get_length(const char *str)
227 g_return_val_if_fail(str != NULL, 0);
229 tmp = g_string_new(NULL);
231 while (*str != '\0') {
232 if (*str == '%' && str[1] != '\0') {
234 if (*str != '%' && format_expand_styles(tmp, *str)) {
239 /* %% or unknown %code, written as-is */
248 g_string_free(tmp, TRUE);
252 /* Return how many characters in `str' must be skipped before `len'
253 characters of text is skipped. Like strip_real_length(), except this
255 int format_real_length(const char *str, int len)
260 g_return_val_if_fail(str != NULL, 0);
261 g_return_val_if_fail(len >= 0, 0);
264 tmp = g_string_new(NULL);
265 while (*str != '\0' && len > 0) {
266 if (*str == '%' && str[1] != '\0') {
268 if (*str != '%' && format_expand_styles(tmp, *str)) {
273 /* %% or unknown %code, written as-is */
284 g_string_free(tmp, TRUE);
285 return (int) (str-start);
288 char *format_string_expand(const char *text)
293 g_return_val_if_fail(text != NULL, NULL);
295 out = g_string_new(NULL);
298 while (*text != '\0') {
301 if (!format_expand_styles(out, *text)) {
302 g_string_append_c(out, '%');
303 g_string_append_c(out, '%');
304 g_string_append_c(out, *text);
311 g_string_append_c(out, *text);
318 g_string_free(out, FALSE);
322 static char *format_get_text_args(TEXT_DEST_REC *dest,
323 const char *text, char **arglist)
329 out = g_string_new(NULL);
332 while (*text != '\0') {
335 if (!format_expand_styles(out, *text)) {
336 g_string_append_c(out, '%');
337 g_string_append_c(out, '%');
338 g_string_append_c(out, *text);
341 } else if (code == '$') {
345 ret = parse_special((char **) &text,
346 active_win == NULL ? NULL :
347 active_win->active_server,
348 active_win == NULL ? NULL :
349 active_win->active, arglist,
350 &need_free, NULL, 0);
353 /* string shouldn't end with \003 or it could
354 mess up the next one or two characters */
356 int len = strlen(ret);
357 while (len > 0 && ret[len-1] == 3) len--;
358 diff = strlen(ret)-len;
360 g_string_append(out, ret);
362 g_string_truncate(out, out->len-diff);
363 if (need_free) g_free(ret);
367 if (*text == '%' || *text == '$')
370 g_string_append_c(out, *text);
377 g_string_free(out, FALSE);
381 char *format_get_text_theme(THEME_REC *theme, const char *module,
382 TEXT_DEST_REC *dest, int formatnum, ...)
388 theme = dest->window->theme == NULL ? current_theme :
392 va_start(va, formatnum);
393 str = format_get_text_theme_args(theme, module, dest, formatnum, va);
399 char *format_get_text_theme_args(THEME_REC *theme, const char *module,
400 TEXT_DEST_REC *dest, int formatnum,
403 char *arglist[MAX_FORMAT_PARAMS];
404 char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
407 formats = g_hash_table_lookup(default_formats, module);
408 format_read_arglist(va, &formats[formatnum],
409 arglist, sizeof(arglist)/sizeof(char *),
410 buffer, sizeof(buffer));
412 return format_get_text_theme_charargs(theme, module, dest,
416 char *format_get_text_theme_charargs(THEME_REC *theme, const char *module,
417 TEXT_DEST_REC *dest, int formatnum,
420 MODULE_THEME_REC *module_theme;
423 module_theme = g_hash_table_lookup(theme->modules, module);
424 if (module_theme == NULL)
427 text = module_theme->expanded_formats[formatnum];
428 return format_get_text_args(dest, text, args);
431 char *format_get_text(const char *module, WINDOW_REC *window,
432 void *server, const char *target,
440 format_create_dest(&dest, server, target, 0, window);
441 theme = dest.window->theme == NULL ? current_theme :
444 va_start(va, formatnum);
445 str = format_get_text_theme_args(theme, module, &dest, formatnum, va);
451 /* add `linestart' to start of each line in `text'. `text' may contain
452 multiple lines separated with \n. */
453 char *format_add_linestart(const char *text, const char *linestart)
458 if (linestart == NULL)
459 return g_strdup(text);
461 if (strchr(text, '\n') == NULL)
462 return g_strconcat(linestart, text, NULL);
464 str = g_string_new(linestart);
465 while (*text != '\0') {
466 g_string_append_c(str, *text);
468 g_string_append(str, linestart);
473 g_string_free(str, FALSE);
477 #define LINE_START_IRSSI_LEVEL \
478 (MSGLEVEL_CLIENTERROR | MSGLEVEL_CLIENTNOTICE)
480 #define NOT_LINE_START_LEVEL \
481 (MSGLEVEL_NEVER | MSGLEVEL_LASTLOG | MSGLEVEL_CLIENTCRAP | \
482 MSGLEVEL_MSGS | MSGLEVEL_PUBLIC | MSGLEVEL_DCC | MSGLEVEL_DCCMSGS | \
483 MSGLEVEL_ACTIONS | MSGLEVEL_NOTICES | MSGLEVEL_SNOTES | MSGLEVEL_CTCPS)
485 /* return the "-!- " text at the start of the line */
486 char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
490 if (dest->level & LINE_START_IRSSI_LEVEL)
491 format = TXT_LINE_START_IRSSI;
492 else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
493 format = TXT_LINE_START;
497 return format_get_text_theme(theme, MODULE_NAME, dest, format);
500 #define show_timestamp(level) \
501 ((level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) == 0 && \
502 (timestamps || (msgs_timestamps && ((level) & MSGLEVEL_MSGS))))
504 static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
509 if (!show_timestamp(dest->level))
512 if (timestamp_timeout > 0) {
513 diff = t - dest->window->last_timestamp;
514 dest->window->last_timestamp = t;
515 if (diff < timestamp_timeout)
520 return format_get_text_theme(theme, MODULE_NAME, dest, TXT_TIMESTAMP,
522 tm->tm_mon+1, tm->tm_mday,
523 tm->tm_hour, tm->tm_min, tm->tm_sec);
526 static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
531 server = dest->server;
533 if (server == NULL || hide_server_tags ||
534 (dest->window->active != NULL &&
535 dest->window->active->server == server))
538 if (servers != NULL) {
540 if (servers->next != NULL)
543 if (count < 2 && lookup_servers != NULL) {
545 if (lookup_servers->next != NULL)
549 return count < 2 ? NULL :
550 format_get_text_theme(theme, MODULE_NAME, dest,
551 TXT_SERVERTAG, server->tag);
554 char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
556 char *timestamp, *servertag;
559 timestamp = get_timestamp(theme, dest, t);
560 servertag = get_server_tag(theme, dest);
562 if (timestamp == NULL && servertag == NULL)
565 linestart = g_strconcat(timestamp != NULL ? timestamp : "",
568 g_free_not_null(timestamp);
569 g_free_not_null(servertag);
573 void format_newline(WINDOW_REC *window)
575 g_return_if_fail(window != NULL);
577 signal_emit_id(signal_gui_print_text, 6, window,
578 GINT_TO_POINTER(-1), GINT_TO_POINTER(-1),
579 GINT_TO_POINTER(PRINTFLAG_NEWLINE),
580 "", GINT_TO_POINTER(-1));
583 /* parse ANSI color string */
584 static char *get_ansi_color(THEME_REC *theme, char *str,
585 int *fg_ret, int *bg_ret, int *flags_ret)
587 static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
589 int fg, bg, flags, num;
595 fg = fg_ret == NULL || *fg_ret < 0 ? theme->default_color : *fg_ret;
596 bg = bg_ret == NULL || *bg_ret < 0 ? -1 : *bg_ret;
597 flags = flags_ret == NULL ? 0 : *flags_ret;
601 if (*str == '\0') return start;
603 if (isdigit((int) *str)) {
604 num = num*10 + (*str-'0');
608 if (*str != ';' && *str != 'm')
613 /* reset colors back to default */
614 fg = theme->default_color;
616 flags &= ~PRINTFLAG_INDENT;
620 flags |= PRINTFLAG_BOLD;
624 flags |= PRINTFLAG_BLINK;
628 flags |= PRINTFLAG_REVERSE;
631 if (num >= 30 && num <= 37)
632 fg = (fg & 0xf8) | ansitab[num-30];
633 if (num >= 40 && num <= 47) {
634 if (bg == -1) bg = 0;
635 bg = (bg & 0xf8) | ansitab[num-40];
642 if (fg_ret != NULL) *fg_ret = fg;
643 if (bg_ret != NULL) *bg_ret = bg;
644 if (flags_ret != NULL) *flags_ret = flags;
654 /* parse MIRC color string */
655 static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
659 fg = fg_ret == NULL ? -1 : *fg_ret;
660 bg = bg_ret == NULL ? -1 : *bg_ret;
662 if (!isdigit((int) **str) && **str != ',') {
666 /* foreground color */
670 if (isdigit((int) **str)) {
671 fg = fg*10 + (**str-'0');
676 /* background color */
678 if (!isdigit((int) **str))
683 if (isdigit((int) **str)) {
684 bg = bg*10 + (**str-'0');
691 if (fg_ret) *fg_ret = fg;
692 if (bg_ret) *bg_ret = bg;
695 #define IS_COLOR_CODE(c) \
696 ((c) == 2 || (c) == 3 || (c) == 4 || (c) == 6 || (c) == 7 || \
697 (c) == 15 || (c) == 22 || (c) == 27 || (c) == 31)
699 /* Return how many characters in `str' must be skipped before `len'
700 characters of text is skipped. */
701 int strip_real_length(const char *str, int len,
702 int *last_color_pos, int *last_color_len)
704 const char *start = str;
706 if (last_color_pos != NULL)
707 *last_color_pos = -1;
708 if (last_color_len != NULL)
709 *last_color_len = -1;
711 while (*str != '\0') {
713 const char *mircstart = str;
715 if (last_color_pos != NULL)
716 *last_color_pos = (int) (str-start);
718 get_mirc_color(&str, NULL, NULL);
719 if (last_color_len != NULL)
720 *last_color_len = (int) (str-mircstart);
722 } else if (*str == 4 && str[1] != '\0') {
723 if (str[1] < FORMAT_STYLE_SPECIAL && str[2] != '\0') {
724 if (last_color_pos != NULL)
725 *last_color_pos = (int) (str-start);
726 if (last_color_len != NULL)
729 } else if (str[1] == FORMAT_STYLE_DEFAULTS) {
730 if (last_color_pos != NULL)
731 *last_color_pos = (int) (str-start);
732 if (last_color_len != NULL)
737 if (!IS_COLOR_CODE(*str)) {
745 return (int) (str-start);
748 char *strip_codes(const char *input)
753 out = str = g_strdup(input);
754 for (p = input; *p != '\0'; p++) {
759 get_mirc_color(&p, NULL, NULL);
764 if (*p == 4 && p[1] != '\0') {
765 if (p[1] >= FORMAT_STYLE_SPECIAL) {
777 if (!IS_COLOR_CODE(*p))
785 /* send a fully parsed text string for GUI to print */
786 void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
788 char *dup, *str, *ptr, type;
789 int fgcolor, bgcolor;
792 dup = str = g_strdup(text);
794 flags = 0; fgcolor = -1; bgcolor = -1;
795 while (*str != '\0') {
797 for (ptr = str; *ptr != '\0'; ptr++) {
798 if (IS_COLOR_CODE(*ptr) || *ptr == '\n') {
804 *ptr = (char) translation_in[(int) (unsigned char) *ptr];
809 if (settings_get_bool("bell_beeps"))
810 signal_emit("beep", 0);
814 /* send the text to gui handler */
815 signal_emit_id(signal_gui_print_text, 6, dest->window,
816 GINT_TO_POINTER(fgcolor),
817 GINT_TO_POINTER(bgcolor),
818 GINT_TO_POINTER(flags), str,
820 flags &= ~PRINTFLAG_INDENT;
824 format_newline(dest->window);
833 if (!hide_text_style)
834 flags ^= PRINTFLAG_BOLD;
838 get_mirc_color((const char **) &ptr,
839 hide_text_style ? NULL : &fgcolor,
840 hide_text_style ? NULL : &bgcolor);
841 if (!hide_text_style)
842 flags |= PRINTFLAG_MIRC_COLOR;
845 /* user specific colors */
846 flags &= ~PRINTFLAG_MIRC_COLOR;
848 case FORMAT_STYLE_BLINK:
849 flags ^= PRINTFLAG_BLINK;
851 case FORMAT_STYLE_UNDERLINE:
852 flags ^= PRINTFLAG_UNDERLINE;
854 case FORMAT_STYLE_BOLD:
855 flags ^= PRINTFLAG_BOLD;
857 case FORMAT_STYLE_REVERSE:
858 flags ^= PRINTFLAG_REVERSE;
860 case FORMAT_STYLE_INDENT:
861 flags |= PRINTFLAG_INDENT;
863 case FORMAT_STYLE_DEFAULTS:
864 fgcolor = bgcolor = -1;
865 flags &= PRINTFLAG_INDENT;
868 if (*ptr != FORMAT_COLOR_NOCHANGE) {
869 fgcolor = (unsigned char) *ptr-'0';
871 flags &= ~PRINTFLAG_BOLD;
874 if (fgcolor != 8) fgcolor -= 8;
875 flags |= PRINTFLAG_BOLD;
879 if (*ptr != FORMAT_COLOR_NOCHANGE)
886 if (!hide_text_style)
887 flags ^= PRINTFLAG_BLINK;
890 /* remove all styling */
891 fgcolor = bgcolor = -1;
892 flags &= PRINTFLAG_INDENT;
896 if (!hide_text_style)
897 flags ^= PRINTFLAG_REVERSE;
901 if (!hide_text_style)
902 flags ^= PRINTFLAG_UNDERLINE;
905 /* ansi color code */
906 ptr = get_ansi_color(dest->window == NULL || dest->window->theme == NULL ?
907 current_theme : dest->window->theme,
909 hide_text_style ? NULL : &fgcolor,
910 hide_text_style ? NULL : &bgcolor,
911 hide_text_style ? NULL : &flags);
921 static void read_settings(void)
923 hide_server_tags = settings_get_bool("hide_server_tags");
924 hide_text_style = settings_get_bool("hide_text_style");
925 timestamps = settings_get_bool("timestamps");
926 timestamp_timeout = settings_get_int("timestamp_timeout");
927 msgs_timestamps = settings_get_bool("msgs_timestamps");
930 void formats_init(void)
932 signal_gui_print_text = signal_get_uniq_id("gui print text");
935 signal_add("setup changed", (SIGNAL_FUNC) read_settings);
938 void formats_deinit(void)
940 signal_remove("setup changed", (SIGNAL_FUNC) read_settings);