1
0
mirror of https://github.com/irssi/irssi.git synced 2024-09-01 04:14:16 -04:00

Support for templates in theme files.

module-formats.c files needs to be updated, meanwhile here's default.theme
you can use.


git-svn-id: http://svn.irssi.org/repos/irssi/trunk@791 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
Timo Sirainen 2000-10-27 23:06:26 +00:00 committed by cras
parent 35fd48a0b4
commit 000ba23aa7
4 changed files with 663 additions and 48 deletions

397
default.theme Normal file
View File

@ -0,0 +1,397 @@
# these characters are automatically replaced with specified color
replaces = { "[]<>=" = "%K$0-%n"; };
# %n specifies the color set in higher level, like in
# {ctcp {nick $0-} requested ... }
# if the ctcp was specified as "%g$0-" and nick was "%W$0-%n", the
# "requested" text would be green
abstracts = {
# generic
line_start = "%B-%W!%B-%n";
hilight = "%_$0-%_";
channel = "%_$0-%_";
nick = "%_$0-%_";
server = "%_$0-%_";
reason = "[$0-]";
# channel specific messages
channick_hilight = "%C$0-%n";
chanhost_hilight = "[%c$0-%n]";
channick = "%c$0-%n";
chanhost = "[$0-]";
channelhilight = "%c$0-%n";
ban = "%c$0-%n";
# messages
msgnick = "<$0-> %|";
msgownnick = "%W$0-%n";
msgchannel = "%K:%c$0-%n";
msgme = "%Y$0-%n";
privmsgnick = "[%R$0-%n] ";
privmsghost = "%K(%r$0-%K)%n";
ownprivmsg = "[%r$0-%n] ";
ownprivmsgdest = "%K(%R$0-%K)";
# actions
action = "%W * $0-%n ";
ownaction = "{action $0-}";
pvtaction = "%W (*) $0-%n ";
pvtaction_query = "{action $0-}";
pubaction = "{action $0-}";
# notices
ownnotice = "[%r$0-]%n ";
ownnotice_target = "%K(%R$0-%K)";
notice = "%K-%M$0-%K-%n ";
pubnotice_channel = "%K:%m$1";
pvtnotice_nick = "%K(%m$0-%K)";
servernotice = "%g!$0-%n ";
# ctcp
ownctcp = "[%r$0-] ";
ownctcp_target = "K(%R$0-%K)";
ctcp = "%g$0-%n";
# wall
ownwall = "[%W$0-] ";
ownwall_channel = "%K/%c$0-";
# wallops
wallop = "%W$0-%n: ";
wallop_nick = "%n$0-";
wallop_action = "%W * $0-%n ";
# netsplits
netsplit = "%G$0-%n";
netjoin = "%C$0-%n";
# /names list
names_nickstat = "%_$0-%_";
names_users = "[%g$0-%n]";
names_channel = "%G$0-%n";
# dcc
dcc = "%g$0-%n";
dccfile = "%_$0-%_";
dccownmsg = "[%r$0-%n] ";
dccownnick = "%K(%R$1%K)%n";
dccownaction = "{action $0-}";
dccmsg = "[%G$0-%n] ";
dccmsghost = "%K(%g$0-%K)%n";
dccquerynick = "%G$0-%n";
dccaction = "%W (*dcc*) $0-%n %|";
};
# no color codes below
formats = {
"fe-common/core" = {
line_start = "{line_start} ";
line_start_irssi = "{line_start} {hilight Irssi:} ";
timestamp = "$[-2.0]3:$[-2.0]4 ";
servertag = "[$0] ";
daychange = "Day changed to $[-2.0]{0} $3 $2";
talking_with = "You are now talking with {nick $0}";
refnum_too_low = "Window number must be greater than 1";
windowlist_header = "Ref Name Active item Server Level";
windowlist_line = "$[3]0 %|$[20]1 $[15]2 $[15]3 $4";
windowlist_footer = "";
looking_up = "Looking up {server $0}";
connecting = "Connecting to {server $0} [$1] port {hilight $2}";
connection_established = "Connection to {server $0} established";
cant_connect = "Unable to connect server {server $0} port {hilight $1} {reason $2}";
connection_lost = "Connection lost to {server $0}";
lag_disconnected = "No PONG reply from server {server $0} in $1 seconds, disconnecting";
disconnected = "Disconnected from {server $0} {reason $1}";
server_quit = "Disconnecting from server {server $0}: {reason $1}";
server_changed = "Changed to {hilight $2} server {server $1}";
unknown_server_tag = "Unknown server tag {server $0}";
server_list = "{server $0}: $1:$2 ($3)";
server_lookup_list = "{server $0}: $1:$2 ($3) (connecting...)";
server_reconnect_list = "{server $0}: $1:$2 ($3) ($5 left before reconnecting)";
server_reconnect_removed = "Removed reconnection to server {server $0} port {hilight $1}";
server_reconnect_not_found = "Reconnection tag {server $0} not found";
setupserver_added = "Server {server $0} saved";
setupserver_removed = "Server {server $0} removed";
setupserver_not_found = "Server {server $0} not found";
join = "{channick_hilight $0} {chanhost_hilight $1} has joined {channel $2}";
part = "{channick $0} {chanhost $1} has left {channel $2} {reason $3}";
kick = "{channick $0} was kicked from {channel $1} by {nick $2} {reason $3}";
quit = "{channick $0} {chanhost $1} has quit {reason $2}";
quit_once = "{channel $3} {channick $0} {chanhost $1} has quit {reason $2}";
nick_changed = "{channick $0} is now known as {channick_hilight $1}";
invite = "{nick $0} invites you to {channel $1}";
new_topic = "{nick $0} changed the topic of {channel $1} to: $2";
topic_unset = "Topic unset by {nick $0} on {channel $1}";
your_nick_changed = "You're now known as {nick $1}";
talking_in = "You are now talking in {channel $0}";
not_in_channels = "You are not on any channels";
current_channel = "Current channel {channel $0}";
chanlist_header = "You are on the following channels:";
chanlist_line = "{channel $[-10]0} %|+$1 ($2): $3";
chansetup_not_found = "Channel {channel $0} not found";
chansetup_added = "Channel {channel $0} saved";
chansetup_removed = "Channel {channel $0} removed";
chansetup_header = "Channel IRC net Password Settings";
chansetup_line = "{channel $[15]0} %|$[10]1 $[10]2 $3";
chansetup_footer = "";
own_msg = "{msgnick $2{msgownnick $0}}$1";
own_msg_channel = "{msgnick $3{msgownnick $0}{msgchannel $1}}$2";
own_msg_private = "{ownprivmsg msg{ownprivmsgdest $0}}$1";
own_msg_private_query = "{msgnick {msgownnick $2}}$1";
pubmsg_me = "{msgnick $2{msgme $0}}$1";
pubmsg_me_channel = "{msgnick $3{msgme $0}{msgchannel $1}}$2";
pubmsg_hilight = "{msgnick $3$0$1}$2";
pubmsg_hilight_channel = "{msgnick $4$0$1{msgchannel 2}}$3";
pubmsg = "{msgnick $2$0}$1";
pubmsg_channel = "{msgnick $3$0{msgchannel $1}}$2";
msg_private = "{privmsgnick $0{privmsghost $1}}$2";
msg_private_query = "{msgnick {privmsgnick $0}}$2";
no_msgs_got = "You have not received a message from anyone yet";
no_msgs_sent = "You have not sent a message to anyone yet";
query_start = "Starting query with {nick $0}";
no_query = "No query with {nick $0}";
query_server_changed = "Query with {nick $2} changed to server {server $1}";
hilight_header = "Highlights:";
hilight_line = "$[-4]0 $1 $2 $3$4$5";
hilight_footer = "";
hilight_not_found = "Highlight not found: $0";
hilight_removed = "Highlight removed: $0";
alias_added = "Alias $0 added";
alias_removed = "Alias $0 removed";
alias_not_found = "No such alias: $0";
aliaslist_header = "Aliases:";
aliaslist_line = "$[10]0 $1";
aliaslist_footer = "";
log_opened = "Log file {hilight $0} opened";
log_closed = "Log file {hilight $0} closed";
log_create_failed = "Couldn't create log file {hilight $0}: $1";
log_locked = "Log file {hilight $0} is locked, probably by another running Irssi";
log_not_open = "Log file {hilight $0} not open";
log_started = "Started logging to file {hilight $0}";
log_stopped = "Stopped logging to file {hilight $0}";
log_list_header = "Logs:";
log_list = "$0 $1: $2 $3$4";
log_list_footer = "";
windowlog_file = "Window LOGFILE set to $0";
windowlog_file_logging = "Can't change window's logfile while log is on";
no_away_msgs = "No new messages in awaylog";
away_msgs = "{hilight $1} new messages in awaylog:";
module_already_loaded = "Module {hilight $0} already loaded";
module_load_error = "Error loading module {hilight $0}: $1";
module_invalid = "{hilight $0} isn't Irssi module";
module_loaded = "Loaded module {hilight $0}";
module_unloaded = "Unloaded module {hilight $0}";
command_unknown = "Unknown command: $0";
command_ambiguous = "Ambiguous command: $0";
option_unknown = "Unknown option: $0";
option_ambiguous = "Ambiguous option: $0";
option_missing_arg = "Missing required argument for: $0";
not_enough_params = "Not enough parameters given";
not_connected = "Not connected to IRC server yet";
not_joined = "Not joined to any channels yet";
chan_not_found = "Not joined to such channel";
chan_not_synced = "Channel not fully synchronized yet, try again after a while";
not_good_idea = "Doing this is not a good idea. Add -YES if you really mean it";
theme_saved = "Theme saved to $0";
theme_save_failed = "Error saving theme to $0";
theme_not_found = "Theme {hilight $0} not found";
window_theme_changed = "Using theme {hilight $0} in this window";
format_title = "%:[{hilight $0}] - [{hilight $1}]%:%:";
format_subtitle = "[{hilight $0}]";
format_item = "$0 = $1";
not_toggle = "Value must be either ON, OFF or TOGGLE";
perl_error = "Perl error: $0";
bind_key = "$[10]0 $1 $2";
bind_unknown_id = "Unknown bind action: $0";
config_saved = "Saved configuration to file $0";
config_reloaded = "Reloaded configuration";
};
"fe-common/irc" = {
netsplit = "{netsplit Netsplit} {server $0} {server $1} quits: $2";
netsplit_more = "{netsplit Netsplit} {server $0} {server $1} quits: $2 (+$3 more, use /NETSPLIT to show all of them)";
netsplit_join = "{netjoin Netsplit} over, joins: $0";
netsplit_join_more = "{netjoin Netsplit} over, joins: $0 (+$1 more)";
no_netsplits = "There are no net splits";
netsplits_header = "Nick Channel Server Splitted server";
netsplits_line = "$[9]0 {[10]1 $[20]2 $3";
netsplits_footer = "";
ircnet_added = "Ircnet $0 saved";
ircnet_removed = "Ircnet $0} removed";
ircnet_not_found = "Ircnet $0 not found";
ircnet_header = "Ircnets:";
ircnet_line = "$0: $1";
ircnet_footer = "";
setupserver_header = "Server Port IRC Net Settings";
setupserver_line = "%|$[!20]0 $[5]1 $[10]2 $3";
setupserver_footer = "";
joinerror_toomany = "Cannot join to channel {channel $0} (You have joined to too many channels)";
joinerror_full = "Cannot join to channel {channel $0} (Channel is full)";
joinerror_invite = "Cannot join to channel {channel $0} (You must be invited)";
joinerror_banned = "Cannot join to channel {channel $0} (You are banned)";
joinerror_bad_key = "Cannot join to channel {channel $0} (Bad channel key)";
joinerror_bad_mask = "Cannot join to channel {channel $0} (Bad channel mask)";
joinerror_unavail = "Cannot join to channel {channel $0} (Channel is temporarily unavailable)";
inviting = "Inviting {nick $0} to {channel $1}";
not_invited = "You have not been invited to a channel!";
names = "{names_users Users {names_channel $0}}$1";
names_nick = "[{names_nickstat $0}$1] ";
endofnames = "{channel $0}: Total of {hilight $1} nicks [{hilight $2} ops, {hilight $3} voices, {hilight $4} normal]";
channel_created = "Channel {channelhilight $0} created $1";
topic = "Topic for {channelhilight $0}: $1";
no_topic = "No topic set for {channelhilight $0}";
topic_info = "Topic set by {nick $0} [$1]";
chanmode_change = "mode/{channelhilight $0} [$1] by {nick $2}";
server_chanmode_change = "{netsplit ServerMode}/{channelhilight $0} [$1] by {nick $2}";
channel_mode = "mode/{channelhilight $0} [$1]";
bantype = "Ban type changed to {channel $0}";
no_bans = "No bans in channel {channel $0}";
banlist = "{channel $0}: ban {ban $1}";
banlist_long = "{channel $0}: ban {ban $1} [by {nick $2}, $3 secs ago]";
ebanlist = "{channel $0}: ban exception {ban $1}";
ebanlist_long = "{channel $0}: ban exception {ban $1} [by {nick $2}, $3 secs ago]";
invitelist = "{channel $0}: invite {ban $1}";
no_such_channel = "{channel $0}: No such channel";
channel_synced = "Join to {channel $0} was synced in {hilight $1} secs";
usermode_change = "Mode change [{hilight $0}] for user {nick $1}";
user_mode = "Your user mode is [{hilight $0}]";
away = "You have been marked as being away";
unaway = "You are no longer marked as being away";
nick_away = "{nick $0} is away: $1";
no_such_nick = "{nick $0}: No such nick/channel";
your_nick = "Your nickname is {nick $0}";
nick_in_use = "Nick {nick $0} is already in use";
nick_unavailable = "Nick {nick $0} is temporarily unavailable";
your_nick_owned = "Your nick is owned by {nick $3} [$1@$2]";
whois = "{nick $0} [$1@$2]%: ircname : $3";
whowas = "{nick $0} [$1@$2]%: ircname : $3";
whois_idle = " idle : $1 days $2 hours $3 mins $4 secs";
whois_idle_signon = " idle : $1 days $2 hours $3 mins $4 secs [signon: $5]";
whois_server = " server : $1 [$2]";
whois_oper = " : {hilight IRC operator}";
whois_registered = " : has registered this nick";
whois_channels = " channels : $1";
whois_away = " away : $1";
end_of_whois = "End of WHOIS";
end_of_whowas = "End of WHOWAS";
whois_not_found = "There is no such nick $0";
who = "{channelhilight $[-10]0} %|{nick $[!9]1} $[!3]2 $[!2]3 $4@$5 [{hilight $6}]";
end_of_who = "End of /WHO list";
own_notice = "{ownnotice notice{ownnotice_target $0}}$1";
own_action = "{ownaction $0}$1";
own_ctcp = "{ownctcp ctcp{ownctcp_target $0}}$1 $2";
own_wall = "{ownwall Wall{ownwall_channel}}$1";
notice_server = "{servernotice $0}$1";
notice_public = "{notice $0{pubnotice_channel $1}$2";
notice_public_ops = "{notice $0{pubnotice_channel @$1}$2";
notice_private = "{notice $0{pvtnotice_nick $1}}$2";
action_private = "{pvtaction $0}$2";
action_private_query = "{pvtaction_query $0}$2";
action_public = "{pubaction $0}$1";
action_public_channel = "{pubaction $0{msgchannel $1}}$2";
ctcp_reply = "CTCP {hilight $0} reply from {nick $1}: $2";
ctcp_reply_channel = "CTCP {hilight $0} reply from {nick $1} in channel {channel $3}: $2";
ctcp_ping_reply = "CTCP {hilight PING} reply from {nick $0}: $1.$[-3.0]2 seconds";
ctcp_requested = "{ctcp >>> {hilight $0} [$1] requested {hilight $2} from {nick $3}}";
online = "Users online: {hilight $0}";
pong = "PONG received from $0: $1";
wallops = "{wallop WALLOP{wallop_nick $0}$1";
action_wallops = "{wallop WALLOP{wallop_action $0}}$1";
error = "{hilight ERROR} $0";
unknown_mode = "Unknown mode character $0";
not_chanop = "You're not channel operator in {channel $0}";
silenced = "Silenced {nick $0}";
unsilenced = "Unsilenced {nick $0}";
silence_line = "{nick $0}: silence {ban $1}";
ignored = "Ignoring {hilight $1} from {nick $0}";
unignored = "Unignored {nick $0}";
ignore_not_found = "{nick $0} is not being ignored";
ignore_no_ignores = "There are no ignores";
ignore_header = "Ignorance List:";
ignore_line = "$[-4]0 $1: $2 $3 $4";
ignore_footer = "";
};
"fe-common/irc/dcc" = {
own_dcc = "{dccownmsg dcc{dccownnick $1}}$2";
own_dcc_action = "{dccownaction $0}$1";
own_dcc_ctcp = "{ownctcp ctcp{ownctcp_target $0}}$1 $2";
dcc_msg = "{dccmsg $0{dccmsghost dcc}}$1";
action_dcc = "{dccaction $0}$1";
own_dcc_query = "{msgnick {msgownnick $0}}$2";
dcc_msg_query = "{msgnick {dccquerynick $0}}$1";
dcc_ctcp = "{dcc >>> DCC CTCP received from {hilight $0}: $1}";
dcc_chat = "{dcc DCC CHAT from {nick $0} [$1 port $2]}";
dcc_chat_not_found = "{dcc No DCC CHAT connection open to {nick $0}}";
dcc_chat_connected = "{dcc DCC CHAT connection with {nick $0} [$1 port $2] established}";
dcc_chat_disconnected = "{dcc DCC lost chat to {nick $0}}";
dcc_send = "{dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4 bytes]}";
dcc_send_exists = "{dcc DCC already sending file {dccfile $0} for {nick $1}}";
dcc_send_not_found = "{dcc DCC not sending file {dccfile $1} to {nick $0}}";
dcc_send_file_not_found = "{dcc DCC file not found: {dccfile $0}}";
dcc_send_connected = "{dcc DCC sending file {dccfile $0} for {nick $1} [$2 port $3]}";
dcc_send_complete = "{dcc DCC sent file {dccfile $0} [{hilight $1}kb] for {nick $2} in {hilight $3} secs [{hilight $4kb/s}]}";
dcc_send_aborted = "{dcc DCC aborted sending file {dccfile $0} for {nick $1}}";
dcc_get_not_found = "{dcc DCC no file offered by {nick $0}}";
dcc_get_connected = "{dcc DCC receiving file {dccfile $0} from {nick $1} [$2 port $3]}";
dcc_get_complete = "{dcc DCC received file {dccfile $0} [$1kb] from {nick $2} in {hilight $3} secs [$4kb/s]}";
dcc_get_aborted = "{dcc DCC aborted receiving file {dccfile $0} from {nick $1}}";
dcc_unknown_ctcp = "{dcc DCC unknown ctcp {hilight $0} from {nick $1} [$2]}";
dcc_unknown_reply = "{dcc DCC unknown reply {hilight $0} from {nick $1} [$2]}";
dcc_unknown_type = "{dcc DCC unknown type {hilight $0}}";
dcc_connect_error = "{dcc DCC can't connect to {hilight $0} port {hilight $1}}";
dcc_cant_create = "{dcc DCC can't create file {dccfile $0}}";
dcc_rejected = "{dcc DCC $0 was rejected by {nick $1} [{hilight $2}]}";
dcc_close = "{dcc DCC $0 close for {nick $1} [{hilight $2}]}";
dcc_list_header = "{dcc DCC connections}";
dcc_list_line_chat = "{dcc $0 $1}";
dcc_list_line_file = "{dcc $0 $1: $2k of $3k ($4%%) - $5kB/s - $6}";
dcc_list_footer = "";
};
"fe-common/irc/flood" = {
autoignore = "Flood detected from {nick $0}, autoignoring for {hilight $1} minutes";
autounignore = "Removed autoignore from {nick $0}";
};
"fe-common/irc/notifylist" = {
notify_join = "{nick $0} [$1@$2] [{hilight $3}] has joined to $4";
notify_part = "{nick $0} has left $4";
notify_away = "{nick $0} [$5] [$1@$2] [{hilight $3}] is now away: $4";
notify_unaway = "{nick $0} [$4] [$1@$2] [{hilight $3}] is now unaway";
notify_unidle = "{nick $0} [$5] [$1@$2] [{hilight $3}] just stopped idling";
notify_online = "On $0: {hilight $1}";
notify_offline = "Offline: $0";
notify_list = "$0: $1 $2 $3";
};
"fe-text" = {
lastlog_start = "{hilight Lastlog}:";
lastlog_end = "{hilight End of Lastlog}";
window_too_small = "Not enough room to resize this window";
cant_hide_last = "You can't hide the last window";
};
};

View File

@ -436,7 +436,7 @@ char *output_format_get_text(const char *module, WINDOW_REC *window,
va_start(va, formatnum);
ret = output_format_text_args(&dest, &formats[formatnum],
module_theme == NULL ? NULL :
module_theme->formats[formatnum], va);
module_theme->expanded_formats[formatnum], va);
va_end(va);
return ret;
@ -456,7 +456,7 @@ static char *output_format_text(TEXT_DEST_REC *dest, int formatnum, ...)
va_start(va, formatnum);
ret = output_format_text_args(dest, &fecommon_core_formats[formatnum],
module_theme == NULL ? NULL :
module_theme->formats[formatnum], va);
module_theme->expanded_formats[formatnum], va);
va_end(va);
return ret;
@ -481,7 +481,7 @@ void printformat_module_args(const char *module, void *server,
str = output_format_text_args(&dest, &formats[formatnum],
module_theme == NULL ? NULL :
module_theme->formats[formatnum], va);
module_theme->expanded_formats[formatnum], va);
if (*str != '\0') print_string(&dest, str);
g_free(str);
}
@ -511,7 +511,7 @@ void printformat_module_window_args(const char *module, WINDOW_REC *window, int
formats = g_hash_table_lookup(default_formats, module);
str = output_format_text_args(&dest, &formats[formatnum],
module_theme == NULL ? NULL :
module_theme->formats[formatnum], va);
module_theme->expanded_formats[formatnum], va);
if (*str != '\0') print_string(&dest, str);
g_free(str);
}

View File

@ -24,6 +24,7 @@
#include "commands.h"
#include "levels.h"
#include "misc.h"
#include "special-vars.h"
#include "lib-config/iconfig.h"
#include "settings.h"
@ -45,21 +46,32 @@ THEME_REC *theme_create(const char *path, const char *name)
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);
rec->abstracts = g_hash_table_new((GHashFunc) g_str_hash,
(GCompareFunc) g_str_equal);
rec->modules = g_hash_table_new((GHashFunc) g_istr_hash,
(GCompareFunc) g_istr_equal);
themes = g_slist_append(themes, rec);
signal_emit("theme created", 1, rec);
return rec;
}
static void theme_abstract_destroy(char *key, char *value)
{
g_free(key);
g_free(value);
}
static void theme_module_destroy(const char *key, MODULE_THEME_REC *rec)
{
int n;
for (n = 0; n < rec->count; n++)
if (rec->formats[n] != NULL)
g_free(rec->formats[n]);
for (n = 0; n < rec->count; n++) {
g_free_not_null(rec->formats[n]);
g_free_not_null(rec->expanded_formats[n]);
}
g_free(rec->formats);
g_free(rec->expanded_formats);
g_free(rec->name);
g_free(rec);
@ -70,14 +82,185 @@ void theme_destroy(THEME_REC *rec)
themes = g_slist_remove(themes, rec);
signal_emit("theme destroyed", 1, rec);
g_hash_table_foreach(rec->abstracts, (GHFunc) theme_abstract_destroy, NULL);
g_hash_table_destroy(rec->abstracts);
g_hash_table_foreach(rec->modules, (GHFunc) theme_module_destroy, NULL);
g_hash_table_destroy(rec->modules);
g_slist_foreach(rec->replace_keys, (GFunc) g_free, NULL);
g_slist_free(rec->replace_keys);
g_slist_foreach(rec->replace_values, (GFunc) g_free, NULL);
g_slist_free(rec->replace_values);
g_free(rec->path);
g_free(rec->name);
g_free(rec);
}
static char *theme_format_expand_data(THEME_REC *theme,
const char **format, int root);
static int theme_replace_find(THEME_REC *theme, char chr)
{
GSList *tmp;
int index = 0;
for (tmp = theme->replace_keys; tmp != NULL; tmp = tmp->next) {
if (strchr(tmp->data, chr) != NULL)
return index;
index++;
}
return -1;
}
static char *theme_replace_expand(THEME_REC *theme, int index, char chr)
{
GSList *rec;
char data[2];
rec = g_slist_nth(theme->replace_values, index);
g_return_val_if_fail(rec != NULL, NULL);
data[0] = chr; data[1] = '\0';
return parse_special_string(rec->data, NULL, NULL, data, NULL);
}
/* append next "item", either a character or $variable */
static void theme_format_append_next(THEME_REC *theme, GString *str,
const char **format)
{
int index;
char *value;
if (**format == '$') {
/* $variable .. we'll always need to skip this, since it
may contain characters that are in replace chars. */
const char *orig;
char *args[1] = { NULL };
int free_ret;
orig = *format;
(*format)++;
value = parse_special((char **) format, NULL, NULL,
args, &free_ret, NULL );
if (free_ret) g_free(value);
(*format)++;
/* append the variable name */
value = g_strndup(orig, (int) (*format-orig));
g_string_append(str, value);
g_free(value);
return;
}
index = theme_replace_find(theme, **format);
if (index == -1)
g_string_append_c(str, **format);
else {
value = theme_replace_expand(theme, index, **format);
g_string_append(str, value);
g_free(value);
}
(*format)++;
}
/* expand a single {abstract ...data... } */
static char *theme_format_expand_abstract(THEME_REC *theme,
const char **formatp)
{
const char *p, *format;
char *abstract, *data, *ret;
int len;
format = *formatp;
/* get abstract name first */
p = format;
while (*p != '\0' && *p != ' ' &&
*p != '{' && *p != '}') p++;
if (*p == '\0' || p == format)
return NULL; /* error */
len = (int) (p-format);
abstract = g_strndup(format, len);
/* skip the following space */
if (*p == ' ') len++;
*formatp = format + len;
/* get the abstract data */
data = g_hash_table_lookup(theme->abstracts, abstract);
g_free(abstract);
if (data == NULL) {
/* unknown abstract */
return NULL;
}
abstract = g_strdup(data);
/* abstract may itself contain abstracts or replaces :) */
p = data = abstract;
abstract = theme_format_expand_data(theme, &p, FALSE);
g_free(data);
/* now we'll need to get the data part. it may contain
more abstracts, they are automatically expanded. */
data = theme_format_expand_data(theme, formatp, FALSE);
ret = parse_special_string(abstract, NULL, NULL, data, NULL);
g_free(abstract);
g_free(data);
return ret;
}
/* expand the data part in {abstract data}. If root is TRUE, we're actually
expanding the original format string so we ignore all extra } chars. */
static char *theme_format_expand_data(THEME_REC *theme,
const char **format, int root)
{
GString *str;
char *ret, *abstract;
str = g_string_new(NULL);
while (**format != '\0') {
if (!root && **format == '}') {
(*format)++;
break;
}
if (**format != '{') {
theme_format_append_next(theme, str, format);
continue;
}
(*format)++;
if (**format == '\0' || **format == '}')
break; /* error */
/* get a single {...} */
abstract = theme_format_expand_abstract(theme, format);
if (abstract != NULL) {
g_string_append(str, abstract);
g_free(abstract);
}
}
ret = str->str;
g_string_free(str, FALSE);
return ret;
}
static char *theme_format_expand(THEME_REC *theme, const char *format)
{
g_return_val_if_fail(theme != NULL, NULL);
g_return_val_if_fail(format != NULL, NULL);
return theme_format_expand_data(theme, &format, TRUE);
}
static MODULE_THEME_REC *theme_module_create(THEME_REC *theme, const char *module)
{
MODULE_THEME_REC *rec;
@ -94,18 +277,78 @@ static MODULE_THEME_REC *theme_module_create(THEME_REC *theme, const char *modul
for (rec->count = 0; formats[rec->count].def != NULL; rec->count++) ;
rec->formats = g_new0(char *, rec->count);
rec->expanded_formats = g_new0(char *, rec->count);
g_hash_table_insert(theme->modules, rec->name, rec);
return rec;
}
static void theme_read_formats(CONFIG_REC *config, THEME_REC *theme, const char *module)
static void theme_read_replaces(CONFIG_REC *config, THEME_REC *theme)
{
GSList *tmp;
CONFIG_NODE *node;
node = config_node_traverse(config, "replaces", FALSE);
if (node == NULL || node->type != NODE_TYPE_BLOCK) return;
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
node = tmp->data;
if (node->key != NULL && node->value != NULL) {
theme->replace_keys =
g_slist_append(theme->replace_keys,
g_strdup(node->key));
theme->replace_values =
g_slist_append(theme->replace_values,
g_strdup(node->value));
}
}
}
static void theme_read_abstracts(CONFIG_REC *config, THEME_REC *theme)
{
GSList *tmp;
CONFIG_NODE *node;
node = config_node_traverse(config, "abstracts", FALSE);
if (node == NULL || node->type != NODE_TYPE_BLOCK) return;
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
node = tmp->data;
if (node->key != NULL && node->value != NULL &&
g_hash_table_lookup(theme->abstracts, node->key) == NULL) {
g_hash_table_insert(theme->abstracts,
g_strdup(node->key),
g_strdup(node->value));
}
}
}
static void theme_set_format(THEME_REC *theme, MODULE_THEME_REC *rec,
FORMAT_REC *formats,
const char *key, const char *value)
{
int n;
for (n = 0; formats[n].def != NULL; n++) {
if (formats[n].tag != NULL &&
g_strcasecmp(formats[n].tag, key) == 0) {
rec->formats[n] = g_strdup(value);
rec->expanded_formats[n] =
theme_format_expand(theme, value);
break;
}
}
}
static void theme_read_formats(CONFIG_REC *config, THEME_REC *theme,
const char *module)
{
MODULE_THEME_REC *rec;
FORMAT_REC *formats;
CONFIG_NODE *node;
GSList *tmp;
int n;
formats = g_hash_table_lookup(default_formats, module);
if (formats == NULL) return;
@ -120,15 +363,9 @@ static void theme_read_formats(CONFIG_REC *config, THEME_REC *theme, const char
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
node = tmp->data;
if (node->key == NULL || node->value == NULL)
continue;
for (n = 0; formats[n].def != NULL; n++) {
if (formats[n].tag != NULL &&
g_strcasecmp(formats[n].tag, node->key) == 0) {
rec->formats[n] = g_strdup(node->value);
break;
}
if (node->key != NULL && node->value != NULL) {
theme_set_format(theme, rec, formats,
node->key, node->value);
}
}
}
@ -241,35 +478,6 @@ THEME_REC *theme_load(const char *name)
return theme;
}
#if 0
/* 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);
}
#endif
typedef struct {
THEME_REC *theme;
CONFIG_REC *config;
@ -295,6 +503,8 @@ static void theme_read(THEME_REC *theme, const char *path)
config_parse(config);
theme->default_color = config_get_int(config, NULL, "default_color", 15);
theme_read_replaces(config, theme);
theme_read_abstracts(config, theme);
rec.theme = theme;
rec.config = config;
@ -377,8 +587,10 @@ static void theme_show(THEME_SEARCH_REC *rec, const char *key, const char *value
if (reset || value != NULL) {
theme = theme_module_create(current_theme, rec->name);
g_free_not_null(theme->formats[n]);
g_free_not_null(theme->expanded_formats[n]);
theme->formats[n] = reset ? NULL : g_strdup(value);
theme->expanded_formats[n] = reset ? NULL : theme_format_expand(current_theme, value);
text = reset ? formats[n].def : value;
}
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, IRCTXT_FORMAT_ITEM, formats[n].tag, text);

View File

@ -8,6 +8,8 @@ typedef struct {
int count;
char **formats; /* in same order as in module's default formats */
char **expanded_formats; /* this contains the formats after
expanding {templates} */
} MODULE_THEME_REC;
typedef struct {
@ -17,6 +19,10 @@ typedef struct {
int default_color;
GHashTable *modules;
GSList *replace_keys;
GSList *replace_values;
GHashTable *abstracts;
void *gui_data;
} THEME_REC;