diff options
-rw-r--r-- | src/common.py | 5 | ||||
-rw-r--r-- | src/core.py | 22 | ||||
-rw-r--r-- | src/room.py | 24 | ||||
-rw-r--r-- | src/tabs.py | 144 | ||||
-rw-r--r-- | src/text_buffer.py | 1 | ||||
-rw-r--r-- | src/theme.py | 173 | ||||
-rw-r--r-- | src/theming.py | 232 | ||||
-rw-r--r-- | src/user.py | 8 | ||||
-rw-r--r-- | src/windows.py | 203 |
9 files changed, 421 insertions, 391 deletions
diff --git a/src/common.py b/src/common.py index db750b30..11a09b93 100644 --- a/src/common.py +++ b/src/common.py @@ -186,11 +186,6 @@ def shell_split(st): except ValueError: return st.split(" ") -def curses_color_pair(color): - if color < 0: - return curses.color_pair(-color) | curses.A_BOLD - return curses.color_pair(color) - def replace_key_with_bound(key): if config.has_option('bindings', key): return config.get(key, key, 'bindings') diff --git a/src/core.py b/src/core.py index 96531e5f..c47af752 100644 --- a/src/core.py +++ b/src/core.py @@ -18,7 +18,7 @@ import traceback from datetime import datetime import common -import theme +import theming import logging import singleton import collections @@ -45,6 +45,7 @@ from roster import Roster, RosterGroup, roster from contact import Contact, Resource from text_buffer import TextBuffer from keyboard import read_char +from theming import get_theme # http://xmpp.org/extensions/xep-0045.html#errorstatus ERROR_AND_STATUS_CODES = { @@ -603,7 +604,7 @@ class Core(object): remote_nick = roster.get_contact_by_jid(jid.bare).get_name() or jid.user else: remote_nick = jid.user - conversation.get_room().add_message(body, nickname=remote_nick, nick_color=theme.COLOR_REMOTE_USER) + conversation.get_room().add_message(body, nickname=remote_nick, nick_color=get_theme().COLOR_REMOTE_USER) if conversation.remote_wants_chatstates is None: if message['chat_state']: conversation.remote_wants_chatstates = True @@ -613,7 +614,7 @@ class Core(object): if 'private' in config.get('beep_on', 'highlight private').split(): curses.beep() if self.current_tab() is not conversation: - conversation.set_color_state(theme.COLOR_TAB_PRIVATE) + conversation.set_color_state(get_theme().COLOR_TAB_PRIVATE) self.refresh_tab_win() else: self.refresh_window() @@ -675,7 +676,7 @@ class Core(object): roster.add_contact(contact, jid) roster.edit_groups_of_contact(contact, []) contact.set_ask('asked') - self.get_tab_by_number(0).set_color_state(theme.COLOR_TAB_HIGHLIGHT) + self.get_tab_by_number(0).set_color_state(get_theme().COLOR_TAB_HIGHLIGHT) self.information('%s wants to subscribe to your presence'%jid, 'Roster') if isinstance(self.current_tab(), tabs.RosterInfoTab): self.refresh_window() @@ -804,9 +805,10 @@ class Core(object): curses.noecho() curses.nonl() curses.raw() - theme.init_colors() stdscr.idlok(True) stdscr.keypad(True) + curses.start_color() + curses.use_default_colors() curses.ungetch(" ") # H4X: without this, the screen is stdscr.getkey() # erased on the first "getkey()" @@ -824,7 +826,7 @@ class Core(object): """ Refresh everything """ - self.current_tab().set_color_state(theme.COLOR_TAB_CURRENT) + self.current_tab().set_color_state(get_theme().COLOR_TAB_CURRENT) self.current_tab().refresh() self.doupdate() @@ -872,15 +874,15 @@ class Core(object): - A Muc with any new message """ for tab in self.tabs: - if tab.get_color_state() == theme.COLOR_TAB_PRIVATE: + if tab.get_color_state() == get_theme().COLOR_TAB_PRIVATE: self.command_win('%s' % tab.nb) return for tab in self.tabs: - if tab.get_color_state() == theme.COLOR_TAB_HIGHLIGHT: + if tab.get_color_state() == get_theme().COLOR_TAB_HIGHLIGHT: self.command_win('%s' % tab.nb) return for tab in self.tabs: - if tab.get_color_state() == theme.COLOR_TAB_NEW_MESSAGE: + if tab.get_color_state() == get_theme().COLOR_TAB_NEW_MESSAGE: self.command_win('%s' % tab.nb) return for tab in self.tabs: @@ -1179,7 +1181,7 @@ class Core(object): def command_theme(self, arg): """ """ - theme.reload_theme() + # get_theme().reload_theme() self.refresh_window() def command_win(self, arg): diff --git a/src/room.py b/src/room.py index 83b00e62..37b0de66 100644 --- a/src/room.py +++ b/src/room.py @@ -12,7 +12,7 @@ from config import config from logger import logger import common -import theme +from theming import get_theme import logging import curses @@ -24,7 +24,7 @@ class Room(TextBuffer): TextBuffer.__init__(self, messages_nb_limit) self.name = name self.own_nick = nick - self.color_state = theme.COLOR_TAB_NORMAL # color used in RoomInfo + self.color_state = get_theme().COLOR_TAB_NORMAL # color used in RoomInfo self.joined = False # false until self presence is receied self.users = [] # User objects self.topic = '' @@ -35,7 +35,7 @@ class Room(TextBuffer): we can know if we can join it, send messages to it, etc """ self.users = [] - self.color_state = theme.COLOR_TAB_DISCONNECTED + self.color_state = get_theme().COLOR_TAB_DISCONNECTED self.joined = False def get_single_line_topic(self): @@ -59,16 +59,16 @@ class Room(TextBuffer): color = None if not time and nickname and nickname != self.own_nick and self.joined: if self.own_nick.lower() in txt.lower(): - if self.color_state != theme.COLOR_TAB_CURRENT: - self.set_color_state(theme.COLOR_TAB_HIGHLIGHT) - color = theme.COLOR_HIGHLIGHT_NICK + if self.color_state != get_theme().COLOR_TAB_CURRENT: + self.set_color_state(get_theme().COLOR_TAB_HIGHLIGHT) + color = get_theme().COLOR_HIGHLIGHT_NICK else: highlight_words = config.get('highlight_on', '').split(':') for word in highlight_words: if word and word.lower() in txt.lower(): - if self.color_state != theme.COLOR_TAB_CURRENT: - self.set_color_state(theme.COLOR_TAB_HIGHLIGHT) - color = theme.COLOR_HIGHLIGHT_NICK + if self.color_state != get_theme().COLOR_TAB_CURRENT: + self.set_color_state(get_theme().COLOR_TAB_HIGHLIGHT) + color = get_theme().COLOR_HIGHLIGHT_NICK break if color: beep_on = config.get('beep_on', 'highlight private').split() @@ -110,9 +110,9 @@ class Room(TextBuffer): user = forced_user if not time and nickname and\ nickname != self.own_nick and\ - self.color_state != theme.COLOR_TAB_CURRENT: - if self.color_state != theme.COLOR_TAB_HIGHLIGHT: - self.set_color_state(theme.COLOR_TAB_NEW_MESSAGE) + self.color_state != get_theme().COLOR_TAB_CURRENT: + if self.color_state != get_theme().COLOR_TAB_HIGHLIGHT: + self.set_color_state(get_theme().COLOR_TAB_NEW_MESSAGE) nick_color = nick_color or None if not nickname or time: txt = '\x195%s' % (txt,) diff --git a/src/tabs.py b/src/tabs.py index 5c141f51..ea08d6f5 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -13,13 +13,15 @@ Each Tab object has different refresh() and resize() methods, defining how its Windows are displayed, resized, etc """ +MIN_WIDTH = 50 +MIN_HEIGHT = 22 + import logging log = logging.getLogger(__name__) from gettext import gettext as _ import windows -import theme import curses import difflib import text_buffer @@ -33,6 +35,8 @@ import timed_events import multiuserchat as muc +from theming import get_theme + from sleekxmpp.xmlstream.stanzabase import JID from config import config from roster import RosterGroup, roster @@ -58,7 +62,7 @@ class Tab(object): tab_core = None def __init__(self): self.input = None - self._color_state = theme.COLOR_TAB_NORMAL + self._color_state = get_theme().COLOR_TAB_NORMAL self.need_resize = False self.nb = Tab.number Tab.number += 1 @@ -86,7 +90,10 @@ class Tab(object): @staticmethod def resize(scr): Tab.size = (Tab.height, Tab.width) = scr.getmaxyx() - Tab.visible = True + if Tab.height < MIN_HEIGHT or Tab.width < MIN_WIDTH: + Tab.visible = False + else: + Tab.visible = True def complete_commands(self, the_input): """ @@ -185,13 +192,13 @@ class Tab(object): """ called when this tab loses the focus. """ - self._color_state = theme.COLOR_TAB_NORMAL + self._color_state = get_theme().COLOR_TAB_NORMAL def on_gain_focus(self): """ called when this tab gains the focus. """ - self._color_state = theme.COLOR_TAB_CURRENT + self._color_state = get_theme().COLOR_TAB_CURRENT def add_message(self): """ @@ -467,9 +474,9 @@ class MucTab(ChatTab): for user in users: if user.nick == room.own_nick: users.remove(user) - nb_color = len(theme.LIST_COLOR_NICKNAMES) + nb_color = len(get_theme().LIST_COLOR_NICKNAMES) for i, user in enumerate(sorted(users, key=compare_users, reverse=True)): - user.color = theme.LIST_COLOR_NICKNAMES[i % nb_color] + user.color = get_theme().LIST_COLOR_NICKNAMES[i % nb_color] self.text_win.rebuild_everything(self._room) self.text_win.refresh(self._room) self.input.refresh() @@ -674,7 +681,6 @@ class MucTab(ChatTab): """ if not self.visible: return - self.need_resize = False text_width = (self.width//10)*9 self.topic_win.resize(1, self.width, 0, 0) self.v_separator.resize(self.height-3, 1, 1, 9*(self.width//10)) @@ -744,14 +750,14 @@ class MucTab(ChatTab): return self._room def on_lose_focus(self): - self._room.set_color_state(theme.COLOR_TAB_NORMAL) + self._room.set_color_state(get_theme().COLOR_TAB_NORMAL) self.text_win.remove_line_separator() self.text_win.add_line_separator() if config.get('send_chat_states', 'true') == 'true' and not self.input.get_text(): self.send_chat_state('inactive') def on_gain_focus(self): - self._room.set_color_state(theme.COLOR_TAB_CURRENT) + self._room.set_color_state(get_theme().COLOR_TAB_CURRENT) if self.text_win.built_lines and self.text_win.built_lines[-1] is None: self.text_win.remove_line_separator() curses.curs_set(1) @@ -794,7 +800,7 @@ class MucTab(ChatTab): if from_nick == room.own_nick: room.joined = True self.send_chat_state('active') - new_user.color = theme.COLOR_OWN_NICK + new_user.color = get_theme().COLOR_OWN_NICK room.add_message(_("\x195Your nickname is \x193%s") % (from_nick)) if '170' in status_codes: room.add_message('\x191Warning: \x195this room is publicly logged') @@ -836,9 +842,9 @@ class MucTab(ChatTab): hide_exit_join = config.get('hide_exit_join', -1) if hide_exit_join != 0: if not jid.full: - room.add_message('\x194%(spec)s \x193%(nick)s\x195 joined the room' % {'nick':from_nick, 'spec':theme.CHAR_JOIN}) + room.add_message('\x194%(spec)s \x193%(nick)s\x195 joined the room' % {'nick':from_nick, 'spec':get_theme().CHAR_JOIN}) else: - room.add_message('\x194%(spec)s \x193%(nick)s \x195(\x194%(jid)s\x195) joined the room' % {'spec':theme.CHAR_JOIN, 'nick':from_nick, 'jid':jid.full}) + room.add_message('\x194%(spec)s \x193%(nick)s \x195(\x194%(jid)s\x195) joined the room' % {'spec':get_theme().CHAR_JOIN, 'nick':from_nick, 'jid':jid.full}) self.core.on_user_rejoined_private_conversation(room.name, from_nick) @@ -869,14 +875,14 @@ class MucTab(ChatTab): self.tab_win.refresh() self.core.doupdate() if by: - kick_msg = _('\x191%(spec)s \x193You\x195 have been banned by \x194%(by)s') % {'spec': theme.CHAR_KICK, 'by':by} + kick_msg = _('\x191%(spec)s \x193You\x195 have been banned by \x194%(by)s') % {'spec': get_theme().CHAR_KICK, 'by':by} else: - kick_msg = _('\x191%(spec)s \x193You\x195 have been banned.') % {'spec':theme.CHAR_KICK} + kick_msg = _('\x191%(spec)s \x193You\x195 have been banned.') % {'spec':get_theme().CHAR_KICK} else: if by: - kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been banned by \x194%(by)s') % {'spec':theme.CHAR_KICK, 'nick':from_nick, 'by':by} + kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been banned by \x194%(by)s') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick, 'by':by} else: - kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been banned') % {'spec':theme.CHAR_KICK, 'nick':from_nick.replace('"', '\\"')} + kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been banned') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick.replace('"', '\\"')} if reason is not None and reason.text: kick_msg += _('\x195 Reason: \x196%(reason)s\x195') % {'reason': reason.text} room.add_message(kick_msg) @@ -895,17 +901,17 @@ class MucTab(ChatTab): self.tab_win.refresh() self.core.doupdate() if by: - kick_msg = _('\x191%(spec)s \x193You\x195 have been kicked by \x193%(by)s') % {'spec': theme.CHAR_KICK, 'by':by} + kick_msg = _('\x191%(spec)s \x193You\x195 have been kicked by \x193%(by)s') % {'spec': get_theme().CHAR_KICK, 'by':by} else: - kick_msg = _('\x191%(spec)s \x193You\x195 have been kicked.') % {'spec':theme.CHAR_KICK} + kick_msg = _('\x191%(spec)s \x193You\x195 have been kicked.') % {'spec':get_theme().CHAR_KICK} # try to auto-rejoin if config.get('autorejoin', 'false') == 'true': muc.join_groupchat(self.core.xmpp, room.name, room.own_nick) else: if by: - kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been kicked by \x193%(by)s') % {'spec':theme.CHAR_KICK.replace('"', '\\"'), 'nick':from_nick.replace('"', '\\"'), 'by':by.replace('"', '\\"')} + kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been kicked by \x193%(by)s') % {'spec':get_theme().CHAR_KICK.replace('"', '\\"'), 'nick':from_nick.replace('"', '\\"'), 'by':by.replace('"', '\\"')} else: - kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been kicked') % {'spec':theme.CHAR_KICK, 'nick':from_nick.replace('"', '\\"')} + kick_msg = _('\x191%(spec)s \x193%(nick)s\x195 has been kicked') % {'spec':get_theme().CHAR_KICK, 'nick':from_nick.replace('"', '\\"')} if reason is not None and reason.text: kick_msg += _('\x195 Reason: \x196%(reason)s') % {'reason': reason.text} room.add_message(kick_msg) @@ -924,9 +930,9 @@ class MucTab(ChatTab): hide_exit_join = config.get('hide_exit_join', -1) if config.get('hide_exit_join', -1) >= -1 else -1 if hide_exit_join == -1 or user.has_talked_since(hide_exit_join): if not jid.full: - leave_msg = _('\x191%(spec)s \x193%(nick)s\x195 has left the room') % {'nick':from_nick, 'spec':theme.CHAR_QUIT} + leave_msg = _('\x191%(spec)s \x193%(nick)s\x195 has left the room') % {'nick':from_nick, 'spec':get_theme().CHAR_QUIT} else: - leave_msg = _('\x191%(spec)s \x193%(nick)s\x195 (\x194%(jid)s\x195) has left the room') % {'spec':theme.CHAR_QUIT, 'nick':from_nick, 'jid':jid.full} + leave_msg = _('\x191%(spec)s \x193%(nick)s\x195 (\x194%(jid)s\x195) has left the room') % {'spec':get_theme().CHAR_QUIT, 'nick':from_nick, 'jid':jid.full} if status: leave_msg += ' (%s)' % status room.add_message(leave_msg) @@ -993,12 +999,10 @@ class PrivateTab(ChatTab): # keys self.key_func['^I'] = self.completion # commands - self.commands['info'] = (self.command_info, _('Usage: /info\nInfo: Display some information about the user in the MUC: '), None) + #self.commands['info'] = (self.command_info, _('Usage: /info\nInfo: Display some information about the user in the MUC: '), None) self.commands['unquery'] = (self.command_unquery, _("Usage: /unquery\nUnquery: close the tab"), None) self.commands['part'] = (self.command_unquery, _("Usage: /part\nPart: close the tab"), None) - self.commands['version'] = (self.command_version, _('Usage: /version\nVersion: get the software version of the current interlocutor (usually its XMPP client and Operating System)'), None) self.resize() - self.parent_muc = self.core.get_tab_by_name(JID(room.name).bare, MucTab) self.on = True def completion(self): @@ -1029,35 +1033,9 @@ class PrivateTab(ChatTab): """ self.core.close_tab() - def command_version(self, arg): - """ - /version - """ - def callback(res): - if not res: - return self.core.information('Could not get the software version from %s' % (jid,), 'Warning') - version = '%s is running %s version %s on %s' % (jid, - res.get('name') or _('an unknown software'), - res.get('version') or _('unknown'), - res.get('os') or _('on an unknown platform')) - self.core.information(version, 'Info') - jid = self.get_room().name - self.core.xmpp.plugin['xep_0092'].get_version(jid, callback=callback) - - def command_info(self, arg): - """ - /info - """ - if arg: - self.parent_muc.command_info(arg) - else: - user = JID(self.get_room().name).resource - self.parent_muc.command_info(user) - def resize(self): if self.core.information_win_size >= self.height-3 or not self.visible: return - self.need_resize = False self.text_win.resize(self.height-3-self.core.information_win_size, self.width, 0, 0) self.text_win.rebuild_everything(self._room) self.info_header.resize(1, self.width, self.height-3-self.core.information_win_size, 0) @@ -1078,10 +1056,10 @@ class PrivateTab(ChatTab): self.input.refresh() def get_color_state(self): - if self._room.color_state == theme.COLOR_TAB_NORMAL or\ - self._room.color_state == theme.COLOR_TAB_CURRENT: + if self._room.color_state == get_theme().COLOR_TAB_NORMAL or\ + self._room.color_state == get_theme().COLOR_TAB_CURRENT: return self._room.color_state - return theme.COLOR_TAB_PRIVATE + return get_theme().COLOR_TAB_PRIVATE def set_color_state(self, color): self._room.color_state = color @@ -1103,14 +1081,14 @@ class PrivateTab(ChatTab): return False def on_lose_focus(self): - self._room.set_color_state(theme.COLOR_TAB_NORMAL) + self._room.set_color_state(get_theme().COLOR_TAB_NORMAL) self.text_win.remove_line_separator() self.text_win.add_line_separator() if self.get_room().joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text(): self.send_chat_state('inactive') def on_gain_focus(self): - self._room.set_color_state(theme.COLOR_TAB_CURRENT) + self._room.set_color_state(get_theme().COLOR_TAB_CURRENT) curses.curs_set(1) if self.get_room().joined and config.get('send_chat_states', 'true') == 'true' and not self.input.get_text(): self.send_chat_state('active') @@ -1146,24 +1124,22 @@ class PrivateTab(ChatTab): """ The user left the associated MUC """ - self.deactivate() if not status_message: - self.get_room().add_message(_('\x191%(spec)s \x193%(nick)s\x195 has left the room') % {'nick':from_nick.replace('"', '\\"'), 'spec':theme.CHAR_QUIT.replace('"', '\\"')}) + self.get_room().add_message(_('\x191%(spec)s \x193%(nick)s\x195 has left the room') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT.replace('"', '\\"')}) else: - self.get_room().add_message(_('\x191%(spec)s \x193%(nick)s\x195 has left the room (%(status)s)"') % {'nick':from_nick.replace('"', '\\"'), 'spec':theme.CHAR_QUIT, 'status': status_message.replace('"', '\\"')}) - if self.core.current_tab() is self: - self.refresh() - self.core.doupdate() + self.get_room().add_message(_('\x191%(spec)s \x193%(nick)s\x195 has left the room (%(status)s)"') % {'nick':from_nick.replace('"', '\\"'), 'spec':get_theme().CHAR_QUIT, 'status': status_message.replace('"', '\\"')}) + self.deactivate() + self.refresh() + self.core.doupdate() def user_rejoined(self, nick): """ The user (or at least someone with the same nick) came back in the MUC """ + self.get_room().add_message('\x194%(spec)s \x193%(nick)s\x195 joined the room' % {'nick':nick, 'spec':get_theme().CHAR_JOIN}) self.activate() - self.get_room().add_message('\x194%(spec)s \x193%(nick)s\x195 joined the room' % {'nick':nick, 'spec':theme.CHAR_JOIN}) - if self.core.current_tab() is self: - self.refresh() - self.core.doupdate() + self.refresh() + self.core.doupdate() def activate(self): self.on = True @@ -1185,7 +1161,7 @@ class RosterInfoTab(Tab): self.contact_info_win = windows.ContactInfoWin() self.default_help_message = windows.HelpText("Enter commands with “/”. “o”: toggle offline show") self.input = self.default_help_message - self.set_color_state(theme.COLOR_TAB_NORMAL) + self.set_color_state(get_theme().COLOR_TAB_NORMAL) self.key_func['^I'] = self.completion self.key_func[' '] = self.on_space self.key_func["/"] = self.on_slash @@ -1209,7 +1185,6 @@ class RosterInfoTab(Tab): def resize(self): if not self.visible: return - self.need_resize = False roster_width = self.width//2 info_width = self.width-roster_width-1 self.v_separator.resize(self.height-2, 1, 0, roster_width) @@ -1420,10 +1395,10 @@ class RosterInfoTab(Tab): return self.reset_help_message() def on_lose_focus(self): - self._color_state = theme.COLOR_TAB_NORMAL + self._color_state = get_theme().COLOR_TAB_NORMAL def on_gain_focus(self): - self._color_state = theme.COLOR_TAB_CURRENT + self._color_state = get_theme().COLOR_TAB_CURRENT if isinstance(self.input, windows.HelpText): curses.curs_set(0) else: @@ -1526,7 +1501,7 @@ class ConversationTab(ChatTab): def __init__(self, jid): txt_buff = text_buffer.TextBuffer() ChatTab.__init__(self, txt_buff) - self.color_state = theme.COLOR_TAB_NORMAL + self.color_state = get_theme().COLOR_TAB_NORMAL self._name = jid # a conversation tab is linked to one specific full jid OR bare jid self.text_win = windows.TextWin() txt_buff.add_window(self.text_win) @@ -1566,7 +1541,6 @@ class ConversationTab(ChatTab): def resize(self): if self.core.information_win_size >= self.height-3 or not self.visible: return - self.need_resize = False self.text_win.resize(self.height-4-self.core.information_win_size, self.width, 1, 0) self.text_win.rebuild_everything(self._room) self.upper_bar.resize(1, self.width, 0, 0) @@ -1589,10 +1563,10 @@ class ConversationTab(ChatTab): self.input.refresh() def get_color_state(self): - if self.color_state == theme.COLOR_TAB_NORMAL or\ - self.color_state == theme.COLOR_TAB_CURRENT: + if self.color_state == get_theme().COLOR_TAB_NORMAL or\ + self.color_state == get_theme().COLOR_TAB_CURRENT: return self.color_state - return theme.COLOR_TAB_PRIVATE + return get_theme().COLOR_TAB_PRIVATE def set_color_state(self, color): self.color_state = color @@ -1610,14 +1584,14 @@ class ConversationTab(ChatTab): return False def on_lose_focus(self): - self.set_color_state(theme.COLOR_TAB_NORMAL) + self.set_color_state(get_theme().COLOR_TAB_NORMAL) self.text_win.remove_line_separator() self.text_win.add_line_separator() if config.get('send_chat_states', 'true') == 'true' and not self.input.get_text() or not self.input.get_text().startswith('//'): self.send_chat_state('inactive') def on_gain_focus(self): - self.set_color_state(theme.COLOR_TAB_CURRENT) + self.set_color_state(get_theme().COLOR_TAB_CURRENT) curses.curs_set(1) if config.get('send_chat_states', 'true') == 'true' and not self.input.get_text() or not self.input.get_text().startswith('//'): self.send_chat_state('active') @@ -1652,7 +1626,7 @@ class MucListTab(Tab): """ def __init__(self, server): Tab.__init__(self) - self._color_state = theme.COLOR_TAB_NORMAL + self._color_state = get_theme().COLOR_TAB_NORMAL self.name = server self.upper_message = windows.Topic() self.upper_message.set_message('Chatroom list on server %s (Loading)' % self.name) @@ -1684,7 +1658,6 @@ class MucListTab(Tab): def resize(self): if not self.visible: return - self.need_resize = False self.upper_message.resize(1, self.width, 0, 0) column_size = {'node-part': (self.width-5)//4, 'name': (self.width-5)//4*3, @@ -1766,10 +1739,10 @@ class MucListTab(Tab): return self.key_func[key]() def on_lose_focus(self): - self._color_state = theme.COLOR_TAB_NORMAL + self._color_state = get_theme().COLOR_TAB_NORMAL def on_gain_focus(self): - self._color_state = theme.COLOR_TAB_CURRENT + self._color_state = get_theme().COLOR_TAB_CURRENT curses.curs_set(0) def get_color_state(self): @@ -1789,7 +1762,7 @@ class SimpleTextTab(Tab): """ def __init__(self, text): Tab.__init__(self) - self._color_state = theme.COLOR_TAB_NORMAL + self._color_state = get_theme().COLOR_TAB_NORMAL self.text_win = windows.SimpleTextWin(text) self.default_help_message = windows.HelpText("“Ctrl+q”: close") self.input = self.default_help_message @@ -1819,7 +1792,6 @@ class SimpleTextTab(Tab): def resize(self): if not self.visible: return - self.need_resize = False self.text_win.resize(self.height-2, self.width, 0, 0) self.input.resize(1, self.width, self.height-1, 0) @@ -1832,10 +1804,10 @@ class SimpleTextTab(Tab): self.input.refresh() def on_lose_focus(self): - self._color_state = theme.COLOR_TAB_NORMAL + self._color_state = get_theme().COLOR_TAB_NORMAL def on_gain_focus(self): - self._color_state = theme.COLOR_TAB_CURRENT + self._color_state = get_theme().COLOR_TAB_CURRENT curses.curs_set(0) def get_color_state(self): diff --git a/src/text_buffer.py b/src/text_buffer.py index d89dbfb9..f39f147a 100644 --- a/src/text_buffer.py +++ b/src/text_buffer.py @@ -15,7 +15,6 @@ log = logging.getLogger(__name__) import collections from datetime import datetime -import theme from config import config Message = collections.namedtuple('Message', 'txt nick_color time str_time nickname user') diff --git a/src/theme.py b/src/theme.py deleted file mode 100644 index 2502e5c6..00000000 --- a/src/theme.py +++ /dev/null @@ -1,173 +0,0 @@ -# Copyright 2010-2011 Florent Le Coz <louiz@louiz.org> -# -# This file is part of Poezio. -# -# Poezio is free software: you can redistribute it and/or modify -# it under the terms of the zlib license. See the COPYING file. - -""" -Define the variables (colors and some other stuff) that are -used when drawing the interface (mainly colors) -""" - -import curses -import glob -import imp -import os -from config import config - -import logging -log = logging.getLogger(__name__) - -## Define the default colors -## Do not change these colors, create a theme file instead. - -# Message text color -COLOR_NORMAL_TEXT = 0 -COLOR_INFORMATION_TEXT = 5 -COLOR_HIGHLIGHT_NICK = -46 - -# User list color -COLOR_USER_VISITOR = 7 -COLOR_USER_PARTICIPANT = 4 -COLOR_USER_NONE = 0 -COLOR_USER_MODERATOR = 1 - -# nickname colors -COLOR_REMOTE_USER = 5 - -# The character printed in color (COLOR_STATUS_*) before the nickname -# in the user list -CHAR_STATUS = ' ' - -# Separators -COLOR_VERTICAL_SEPARATOR = 4 -COLOR_NEW_TEXT_SEPARATOR = 2 -COLOR_MORE_INDICATOR = 6 - -# Time -COLOR_TIME_SEPARATOR = 6 -COLOR_TIME_LIMITER = 0 -CHAR_TIME_LEFT = '' -CHAR_TIME_RIGHT = '' -COLOR_TIME_NUMBERS = 0 - -# Tabs -COLOR_TAB_NORMAL = 42 -COLOR_TAB_CURRENT = 56 -COLOR_TAB_NEW_MESSAGE = 49 -COLOR_TAB_HIGHLIGHT = 21 -COLOR_TAB_PRIVATE = 28 -COLOR_TAB_DISCONNECTED = 30 - -# Nickname colors -LIST_COLOR_NICKNAMES = [ - 1, 2, 3, 4, 5, 6, -2, -4, -5, -6 - ] -COLOR_OWN_NICK = 7 - -# Status color -COLOR_STATUS_XA = 49 -COLOR_STATUS_NONE = 0 -COLOR_STATUS_DND = 21 -COLOR_STATUS_AWAY = 35 -COLOR_STATUS_CHAT = 28 -COLOR_STATUS_UNAVAILABLE = 57 -COLOR_STATUS_ONLINE = 41 - -# Bars -COLOR_INFORMATION_BAR = 42 -COLOR_TOPIC_BAR = 42 -COLOR_PRIVATE_ROOM_BAR = 28 -COLOR_SCROLLABLE_NUMBER = -39 -COLOR_SELECTED_ROW = 42 -COLOR_PRIVATE_NAME = 42 -COLOR_CONVERSATION_NAME = 42 -COLOR_GROUPCHAT_NAME = 42 -COLOR_COLUMN_HEADER = 36 - -# Strings for special messages (like join, quit, nick change, etc) -# Special messages -CHAR_JOIN = '---->' -CHAR_QUIT = '<----' -CHAR_KICK = '-!-' - -COLOR_JOIN_CHAR = 4 -COLOR_QUIT_CHAR = 1 -COLOR_KICK_CHAR = 1 - -# words between () -COLOR_CURLYBRACKETED_WORD = 4 -# words between {} -COLOR_ACCOLADE_WORD = 6 -# words between [] -COLOR_BRACKETED_WORD = 3 - -def init_colors(): - """ - Initilization of all the available ncurses colors - limit the number of colors to 64 (because some terminals - don't handle more than that), by removing some useless colors - like 'black on black', etc. - """ - curses.start_color() - curses.use_default_colors() - cpt = 0 - for i in range(-1, 7): - for y in range(0, 8): - if y == i: - continue - curses.init_pair(cpt, y, i) - cpt += 1 - for y in range(0, 7): - # init the default fg on others bg at last - curses.init_pair(cpt, -1, y) - cpt += 1 - # Have the default color be default fg on default bg - reload_theme() - -def reload_theme(): - themes_dir = config.get('themes_dir', '') - themes_dir = themes_dir or\ - os.path.join(os.environ.get('XDG_DATA_HOME') or\ - os.path.join(os.environ.get('HOME'), '.local', 'share'), - 'poezio', 'themes') - try: - os.makedirs(themes_dir) - except OSError: - pass - theme_name = config.get('theme', '') - if not theme_name: - return - try: - theme = imp.load_source('theme', os.path.join(themes_dir, theme_name)) - except: # TODO warning: theme not found - return - for var in dir(theme): - if var.startswith('COLOR_') or var.startswith('CHAR_') or var.startswith('LIST_'): - globals()[var] = getattr(theme, var) - -if __name__ == '__main__': - """ - Launch 'python theme.py' to see the list of all the available colors - in your terminal - """ - s = curses.initscr() - curses.start_color() - curses.use_default_colors() - init_colors() - for i in range(64): - s.attron(curses.color_pair(i) | curses.A_BOLD) - s.addstr(str(curses.color_pair(i) | curses.A_BOLD)) - s.attroff(curses.color_pair(i) | curses.A_BOLD) - s.addstr(' ') - s.addstr('\n') - for i in range(64): - s.attron(curses.color_pair(i)) - s.addstr(str(i)) - s.attroff(curses.color_pair(i)) - s.addstr(' ') - - s.refresh() - s.getch() - s.endwin() diff --git a/src/theming.py b/src/theming.py new file mode 100644 index 00000000..97a09540 --- /dev/null +++ b/src/theming.py @@ -0,0 +1,232 @@ +# Copyright 2010-2011 Florent Le Coz <louiz@louiz.org> +# +# This file is part of Poezio. +# +# Poezio is free software: you can redistribute it and/or modify +# it under the terms of the zlib license. See the COPYING file. + +""" +Define the variables (colors and some other stuff) that are +used when drawing the interface. + +Colors are numbers from -1 to 7 (if only 8 colors are supported) or -1 to 255 +if 256 colors are available. +We check the number of available colors at startup, and we load a theme accordingly. +A 8 color theme should NEVER use colors not in the -1 -> 7 range. We won't check that +at run time. If the case occurs, the THEME should be fixed. +XHTML-IM colors are converted to -1 -> 255 colors if available, or directly to +-1 -> 8 if we are in 8-color-mode. + +A pair_color is a background-foreground pair. All possible pairs are not created +at startup, because that would create 256*256 pairs, and almost all of them +would never be used. +So, a theme should define color tuples, like (200, -1), and when they are to +be used by poezio's interface, they will be created once, and kept in a list for +later usage. +A color tuple is of the form (foreground, background, optional) +A color of -1 means the default color. So if you do not want to have +a background color, use (x, -1). +The optional third value of the tuple defines additional information. It +is a string and can contain one or more of these characteres: +'b': bold +'u': underlined +'x': blink + +For example, (200, 208, 'bu') is bold, underlined and pink foreground on orange background. + +A theme file is a python file containing two objects named 'theme' and 'theme8', which both are +instances of two classes (derived from the Theme class) defined in that same file. +For example, in darktheme.py: + +import theming +class DarkTheme(theming.Theme): + COLOR_NORMAL_TEXT = (200, -1) + [...] + +class DarkTheme8(theming.Theme): + COLOR_NORMAL_TEXT = (1, -1) + [...] + +theme = DarkTheme() +theme8 = DarkTheme8() + +if the command '/theme darktheme' is issued, we import the darktheme.py file +and set the global variable 'theme' to darktheme.theme. + +And in poezio's code we just use theme.COLOR_NORMAL_TEXT etc + +Since a theme inherites from the Theme class (defined here), if a color is not defined in a +theme file, the color is the default one. + +Some values in that class are a list of color tuple. +For example [(1, -1), (2, -1), (3, -1)] +Such a list SHOULD contain at list one color tuple. +It is used for example to define color gradient, etc. +""" + +import logging +log = logging.getLogger(__name__) + +from config import config + +import curses +import imp +import os + +class Theme(object): + """ + The theme class, from which all theme should inherit. + All of the following value can be replaced in subclasses, in + order to create a new theme. + Do not edit this file if you want to change the theme to suit your + needs. Create a new theme and share it if you think it can be useful + for others. + """ + # Message text color + COLOR_NORMAL_TEXT = (-1, -1) + COLOR_INFORMATION_TEXT = (137, -1) + COLOR_HIGHLIGHT_NICK = (208, 22) + + # User list color + COLOR_USER_VISITOR = (13, -1) + COLOR_USER_PARTICIPANT = (7, -1) + COLOR_USER_NONE = (243, -1) + COLOR_USER_MODERATOR = (160, -1) + + # nickname colors + COLOR_REMOTE_USER = (5, -1) + + # The character printed in color (COLOR_STATUS_*) before the nickname + # in the user list + CHAR_STATUS = '|' + + # Separators + COLOR_VERTICAL_SEPARATOR = (4, -1) + COLOR_NEW_TEXT_SEPARATOR = (2, -1) + COLOR_MORE_INDICATOR = (6, 4) + + # Time + COLOR_TIME_SEPARATOR = (6, -1) + COLOR_TIME_LIMITER = (0, -1) + CHAR_TIME_LEFT = '' + CHAR_TIME_RIGHT = '' + COLOR_TIME_NUMBERS = (0, -1) + + # Tabs + COLOR_TAB_NORMAL = (7, 4) + COLOR_TAB_CURRENT = (7, 6) + COLOR_TAB_NEW_MESSAGE = (7, 3) + COLOR_TAB_HIGHLIGHT = (7, 9) + COLOR_TAB_PRIVATE = (7, 2) + COLOR_TAB_DISCONNECTED = (7, 8) + + # Nickname colors + LIST_COLOR_NICKNAMES = [(1, -1), (2, -1), (3, -1), (4, -1), (5, -1), (6, -1), (7, -1), (8, -1), (21, -1), (154, -1), (202, -1), (123, -1), (216, -1)] + COLOR_OWN_NICK = (254, -1) + + # Status color + COLOR_STATUS_XA = (234, 90) + COLOR_STATUS_NONE = (234, 4) + COLOR_STATUS_DND = (234, 1) + COLOR_STATUS_AWAY = (234, 3) + COLOR_STATUS_CHAT = (234, 2) + COLOR_STATUS_UNAVAILABLE = (-1, 247) + COLOR_STATUS_ONLINE = (234, 4) + + # Bars + COLOR_INFORMATION_BAR = (7, 4) + COLOR_TOPIC_BAR = (7, 4) + COLOR_PRIVATE_ROOM_BAR = (-1, 4) + COLOR_SCROLLABLE_NUMBER = (214, 4, 'b') + COLOR_SELECTED_ROW = (-1, 33) + COLOR_PRIVATE_NAME = (-1, 4) + COLOR_CONVERSATION_NAME = (4, 4) + COLOR_GROUPCHAT_NAME = (7, 4) + COLOR_COLUMN_HEADER = (36, 4) + + # Strings for special messages (like join, quit, nick change, etc) + # Special messages + CHAR_JOIN = '--->' + CHAR_QUIT = '<---' + CHAR_KICK = '-!-' + + COLOR_JOIN_CHAR = (4, -1) + COLOR_QUIT_CHAR = (1, -1) + COLOR_KICK_CHAR = (1, -1) + +# This is the default theme object, used if no theme is defined in the conf +theme = Theme() + +# a dict "color tuple -> color_pair" +# Each time we use a color tuple, we check if it has already been used. +# If not we create a new color_pair and keep it in that dict, to use it +# the next time. +curses_colors_dict = {} + +def to_curses_attr(color_tuple): + """ + Takes a color tuple (as defined at the top of this file) and + returns a valid curses attr that can be passed directly to attron() or attroff() + """ + # extract the color from that tuple + if len(color_tuple) == 3: + colors = (color_tuple[0], color_tuple[1]) + else: + colors = color_tuple + + # check if we already used these colors + try: + pair = curses_colors_dict[colors] + except KeyError: + pair = len(curses_colors_dict) + 1 + curses.init_pair(pair, colors[0], colors[1]) + curses_colors_dict[colors] = pair + log.debug('New pair: %s (%s)' % (pair, colors,)) + curses_pair = curses.color_pair(pair) + if len(color_tuple) == 3: + additional_val = color_tuple[2] + if 'b' in additional_val: + curses_pair = curses_pair | curses.A_BOLD + if 'u' in additional_val: + curses_pair = curses_pair | curses.A_UNDERLINE + if 'x' in additional_val: + curses_pair = curses_pair | curses.A_BLINK + return curses_pair + +def get_theme(): + """ + Returns the current theme + """ + return theme + +def reload_theme(): + themes_dir = config.get('themes_dir', '') + themes_dir = themes_dir or\ + os.path.join(os.environ.get('XDG_DATA_HOME') or\ + os.path.join(os.environ.get('HOME'), '.local', 'share'), + 'poezio', 'themes') + try: + os.makedirs(themes_dir) + except OSError: + pass + theme_name = config.get('theme', '') + if not theme_name: + return + try: + new_theme = imp.load_source('theme', os.path.join(themes_dir, theme_name)) + except: # TODO warning: theme not found + return + global theme + theme = new_theme + +if __name__ == '__main__': + """ + Display some nice text with nice colors + """ + s = curses.initscr() + curses.start_color() + curses.use_default_colors() + s.addstr('%s' % curses.COLORS, to_curses_attr((3, -1, 'x'))) + s.refresh() + s.getkey() + curses.endwin() diff --git a/src/user.py b/src/user.py index 0fe0bad4..5867e1f3 100644 --- a/src/user.py +++ b/src/user.py @@ -10,11 +10,13 @@ Define the user class. An user is a MUC participant, not a roster contact (see contact.py) """ +import curses + from random import randrange, choice from config import config from datetime import timedelta, datetime -import curses -import theme + +from theming import get_theme ROLE_DICT = { '':0, @@ -32,7 +34,7 @@ class User(object): self.last_talked = datetime(1, 1, 1) # The oldest possible time self.update(affiliation, show, status, role) self.change_nick(nick) - self.color = choice(theme.LIST_COLOR_NICKNAMES) + self.color = choice(get_theme().LIST_COLOR_NICKNAMES) self.jid = jid self.chatstate = None diff --git a/src/windows.py b/src/windows.py index 3e607541..a0751c29 100644 --- a/src/windows.py +++ b/src/windows.py @@ -33,12 +33,13 @@ from poopt import cut_text from sleekxmpp.xmlstream.stanzabase import JID import core -import theme import common import wcwidth import singleton import collections +from theming import get_theme, to_curses_attr + # msg is a reference to the corresponding Message tuple. text_start and text_end are the position # delimiting the text in this line. # first is a bool telling if this is the first line of the message. @@ -134,7 +135,7 @@ class Win(object): elif attr_char == 'b': self._win.attron(curses.A_BOLD) elif attr_char in string.digits and attr_char != '': - self._win.attron(common.curses_color_pair(int(attr_char))) + self._win.attron(to_curses_attr((int(attr_char), -1))) next_attr_char = text.find('\x19') self.addstr(text) @@ -145,7 +146,7 @@ class Win(object): (y, x) = self._win.getyx() size = self.width-x if color: - self.addnstr(' '*size, size, common.curses_color_pair(color)) + self.addnstr(' '*size, size, to_curses_attr(color)) else: self.addnstr(' '*size, size) @@ -159,18 +160,18 @@ class UserList(Win): def __init__(self): Win.__init__(self) self.pos = 0 - self.color_role = {'moderator': theme.COLOR_USER_MODERATOR, - 'participant':theme.COLOR_USER_PARTICIPANT, - 'visitor':theme.COLOR_USER_VISITOR, - 'none':theme.COLOR_USER_NONE, - '':theme.COLOR_USER_NONE + self.color_role = {'moderator': get_theme().COLOR_USER_MODERATOR, + 'participant':get_theme().COLOR_USER_PARTICIPANT, + 'visitor':get_theme().COLOR_USER_VISITOR, + 'none':get_theme().COLOR_USER_NONE, + '':get_theme().COLOR_USER_NONE } - self.color_show = {'xa':theme.COLOR_STATUS_XA, - 'none':theme.COLOR_STATUS_NONE, - '':theme.COLOR_STATUS_NONE, - 'dnd':theme.COLOR_STATUS_DND, - 'away':theme.COLOR_STATUS_AWAY, - 'chat':theme.COLOR_STATUS_CHAT + self.color_show = {'xa':get_theme().COLOR_STATUS_XA, + 'none':get_theme().COLOR_STATUS_NONE, + '':get_theme().COLOR_STATUS_NONE, + 'dnd':get_theme().COLOR_STATUS_DND, + 'away':get_theme().COLOR_STATUS_AWAY, + 'chat':get_theme().COLOR_STATUS_CHAT } def scroll_up(self): @@ -182,7 +183,7 @@ class UserList(Win): self.pos = 0 def draw_plus(self, y): - self.addstr(y, self.width-2, '++', common.curses_color_pair(theme.COLOR_MORE_INDICATOR)) + self.addstr(y, self.width-2, '++', to_curses_attr(get_theme().COLOR_MORE_INDICATOR)) def refresh(self, users): log.debug('Refresh: %s'%self.__class__.__name__) @@ -194,11 +195,11 @@ class UserList(Win): self.pos = len(users)-1 for user in users[self.pos:]: if not user.role in self.color_role: - role_col = theme.COLOR_USER_NONE + role_col = get_theme().COLOR_USER_NONE else: role_col = self.color_role[user.role] if not user.show in self.color_show: - show_col = theme.COLOR_STATUS_NONE + show_col = get_theme().COLOR_STATUS_NONE else: show_col = self.color_show[user.show] if user.chatstate == 'composing': @@ -208,9 +209,9 @@ class UserList(Win): elif user.chatstate == 'paused': char = 'p' else: - char = theme.CHAR_STATUS - self.addstr(y, 0, char, common.curses_color_pair(show_col)) - self.addstr(y, 1, user.nick[:self.width-2], common.curses_color_pair(role_col)) + char = get_theme().CHAR_STATUS + self.addstr(y, 0, char, to_curses_attr(show_col)) + self.addstr(y, 1, user.nick[:self.width-2], to_curses_attr(role_col)) y += 1 if y == self.height: break @@ -223,9 +224,9 @@ class UserList(Win): def resize(self, height, width, y, x): self._resize(height, width, y, x) - self._win.attron(common.curses_color_pair(theme.COLOR_VERTICAL_SEPARATOR)) + self._win.attron(to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR)) self._win.vline(0, 0, curses.ACS_VLINE, self.height) - self._win.attroff(common.curses_color_pair(theme.COLOR_VERTICAL_SEPARATOR)) + self._win.attroff(to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR)) class Topic(Win): def __init__(self): @@ -240,12 +241,12 @@ class Topic(Win): msg = topic[:self.width-1] else: msg = self._message[:self.width-1] - self.addstr(0, 0, msg, common.curses_color_pair(theme.COLOR_TOPIC_BAR)) + self.addstr(0, 0, msg, to_curses_attr(get_theme().COLOR_TOPIC_BAR)) (y, x) = self._win.getyx() remaining_size = self.width - x if remaining_size: self.addnstr(' '*remaining_size, remaining_size, - common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self._refresh() def set_message(self, message): @@ -262,24 +263,24 @@ class GlobalInfoBar(Win): comp = lambda x: x.nb with g_lock: self._win.erase() - self.addstr(0, 0, "[", common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(0, 0, "[", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) sorted_tabs = sorted(self.core.tabs, key=comp) for tab in sorted_tabs: color = tab.get_color_state() if config.get('show_inactive_tabs', 'true') == 'false' and\ - color == theme.COLOR_TAB_NORMAL: + color == get_theme().COLOR_TAB_NORMAL: continue try: - self.addstr("%s" % str(tab.nb), common.curses_color_pair(color)) - self.addstr("|", common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr("%s" % str(tab.nb), to_curses_attr(color)) + self.addstr("|", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) except: # end of line break (y, x) = self._win.getyx() - self.addstr(y, x-1, '] ', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(y, x-1, '] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) (y, x) = self._win.getyx() remaining_size = self.width - x self.addnstr(' '*remaining_size, remaining_size, - common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) self._refresh() class InfoWin(Win): @@ -298,7 +299,7 @@ class InfoWin(Win): """ if window.pos > 0: plus = ' -PLUS(%s)-' % window.pos - self.addstr(plus, common.curses_color_pair(theme.COLOR_SCROLLABLE_NUMBER)) + self.addstr(plus, to_curses_attr(get_theme().COLOR_SCROLLABLE_NUMBER)) class PrivateInfoWin(InfoWin): """ @@ -315,33 +316,33 @@ class PrivateInfoWin(InfoWin): self.write_room_name(room) self.print_scroll_position(window) self.write_chatstate(chatstate) - self.finish_line(theme.COLOR_INFORMATION_BAR) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) self._refresh() def write_room_name(self, room): jid = JID(room.name) room_name, nick = jid.bare, jid.resource - self.addstr(nick, common.curses_color_pair(theme.COLOR_PRIVATE_NAME)) + self.addstr(nick, to_curses_attr(get_theme().COLOR_PRIVATE_NAME)) txt = ' from room %s' % room_name - self.addstr(txt, common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(txt, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) def write_chatstate(self, state): if state: - self.addstr(' %s' % (state,), common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(' %s' % (state,), to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) class ConversationInfoWin(InfoWin): """ The line above the information window, displaying informations about the user we are talking to """ - color_show = {'xa':theme.COLOR_STATUS_XA, - 'none':theme.COLOR_STATUS_ONLINE, - '':theme.COLOR_STATUS_ONLINE, - 'available':theme.COLOR_STATUS_ONLINE, - 'dnd':theme.COLOR_STATUS_DND, - 'away':theme.COLOR_STATUS_AWAY, - 'chat':theme.COLOR_STATUS_CHAT, - 'unavailable':theme.COLOR_STATUS_UNAVAILABLE + color_show = {'xa':get_theme().COLOR_STATUS_XA, + 'none':get_theme().COLOR_STATUS_ONLINE, + '':get_theme().COLOR_STATUS_ONLINE, + 'available':get_theme().COLOR_STATUS_ONLINE, + 'dnd':get_theme().COLOR_STATUS_DND, + 'away':get_theme().COLOR_STATUS_AWAY, + 'chat':get_theme().COLOR_STATUS_CHAT, + 'unavailable':get_theme().COLOR_STATUS_UNAVAILABLE } def __init__(self): @@ -372,7 +373,7 @@ class ConversationInfoWin(InfoWin): self.write_resource_information(resource) self.print_scroll_position(window) self.write_chatstate(chatstate) - self.finish_line(theme.COLOR_INFORMATION_BAR) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) self._refresh() def write_resource_information(self, resource): @@ -384,31 +385,31 @@ class ConversationInfoWin(InfoWin): else: presence = resource.get_presence() color = RosterWin.color_show[presence] - self.addstr('[', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) - self.addstr(" ", common.curses_color_pair(color)) - self.addstr(']', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self.addstr(" ", to_curses_attr(color)) + self.addstr(']', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) def write_contact_informations(self, contact): """ Write the informations about the contact """ if not contact: - self.addstr("(contact not in roster)", common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr("(contact not in roster)", to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) return display_name = contact.get_name() or contact.get_bare_jid() - self.addstr('%s '%(display_name), common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr('%s '%(display_name), to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) def write_contact_jid(self, jid): """ Just write the jid that we are talking to """ - self.addstr('[', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) - self.addstr(jid.full, common.curses_color_pair(theme.COLOR_CONVERSATION_NAME)) - self.addstr('] ', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self.addstr(jid.full, to_curses_attr(get_theme().COLOR_CONVERSATION_NAME)) + self.addstr('] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) def write_chatstate(self, state): if state: - self.addstr(' %s' % (state,), common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(' %s' % (state,), to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) class ConversationStatusMessageWin(InfoWin): """ @@ -431,11 +432,11 @@ class ConversationStatusMessageWin(InfoWin): self._win.erase() if resource: self.write_status_message(resource) - self.finish_line(theme.COLOR_INFORMATION_BAR) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) self._refresh() def write_status_message(self, resource): - self.addstr(resource.get_status(), common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(resource.get_status(), to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) class MucInfoWin(InfoWin): """ @@ -455,20 +456,20 @@ class MucInfoWin(InfoWin): self.write_role(room) if window: self.print_scroll_position(window) - self.finish_line(theme.COLOR_INFORMATION_BAR) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) self._refresh() def write_room_name(self, room): - self.addstr('[', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) - self.addstr(room.name, common.curses_color_pair(theme.COLOR_GROUPCHAT_NAME)) - self.addstr('] ', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr('[', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self.addstr(room.name, to_curses_attr(get_theme().COLOR_GROUPCHAT_NAME)) + self.addstr('] ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) def write_disconnected(self, room): """ Shows a message if the room is not joined """ if not room.joined: - self.addstr(' -!- Not connected ', common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(' -!- Not connected ', to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) def write_own_nick(self, room): """ @@ -477,7 +478,7 @@ class MucInfoWin(InfoWin): nick = room.own_nick if not nick: return - self.addstr(truncate_nick(nick, 13), common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(truncate_nick(nick, 13), to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) def write_role(self, room): """ @@ -494,7 +495,7 @@ class MucInfoWin(InfoWin): if own_user.affiliation != 'none': txt += own_user.affiliation+', ' txt += own_user.role+')' - self.addstr(txt, common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(txt, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) class TextWin(Win): def __init__(self, lines_nb_limit=config.get('max_lines_in_memory', 2048)): @@ -559,9 +560,9 @@ class TextWin(Win): offset = 1 + len(message.str_time) if nick: offset += wcwidth.wcswidth(nick) + 2 # + nick + spaces length - if theme.CHAR_TIME_LEFT: + if get_theme().CHAR_TIME_LEFT: offset += 1 - if theme.CHAR_TIME_RIGHT: + if get_theme().CHAR_TIME_RIGHT: offset += 1 lines = cut_text(txt, self.width-offset) for line in lines: @@ -610,7 +611,7 @@ class TextWin(Win): self._refresh() def write_line_separator(self): - self.addnstr('- '*(self.width//2-1)+'-', self.width, common.curses_color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) + self.addnstr('- '*(self.width//2-1)+'-', self.width, to_curses_attr(get_theme().COLOR_NEW_TEXT_SEPARATOR)) def write_text(self, y, x, txt): """ @@ -626,10 +627,10 @@ class TextWin(Win): if not nickname: return if color: - self._win.attron(common.curses_color_pair(color)) + self._win.attron(to_curses_attr(color)) self.addstr(truncate_nick(nickname)) if color: - self._win.attroff(common.curses_color_pair(color)) + self._win.attroff(to_curses_attr(color)) self.addstr("> ") def write_time(self, time): @@ -672,8 +673,8 @@ class HelpText(Win): self.txt = txt with g_lock: self._win.erase() - self.addstr(0, 0, self.txt[:self.width-1], common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) - self.finish_line(theme.COLOR_INFORMATION_BAR) + self.addstr(0, 0, self.txt[:self.width-1], to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) self._refresh() def do_command(self, key): @@ -1034,16 +1035,16 @@ class Input(Win): text = self.text.replace('\n', '|') self._win.erase() if self.color: - self._win.attron(common.curses_color_pair(self.color)) + self._win.attron(to_curses_attr(self.color)) displayed_text = text[self.line_pos:self.line_pos+self.width-1] self.addstr(displayed_text) if self.color: (y, x) = self._win.getyx() size = self.width-x - self.addnstr(' '*size, size, common.curses_color_pair(self.color)) + self.addnstr(' '*size, size, to_curses_attr(self.color)) self.addstr(0, wcwidth.wcswidth(displayed_text[:self.pos]), '') if self.color: - self._win.attroff(common.curses_color_pair(self.color)) + self._win.attroff(to_curses_attr(self.color)) self._refresh() def refresh(self): @@ -1138,13 +1139,13 @@ class MessageInput(Input): text = self.text.replace('\n', '|') self._win.erase() if self.color: - self._win.attron(common.curses_color_pair(self.color)) + self._win.attron(to_curses_attr(self.color)) displayed_text = text[self.line_pos:self.line_pos+self.width-1] self._win.attrset(0) self.addstr_colored(displayed_text) self.addstr(0, wcwidth.wcswidth(displayed_text[:self.pos]), '') if self.color: - self._win.attroff(common.curses_color_pair(self.color)) + self._win.attroff(to_curses_attr(self.color)) self._refresh() class CommandInput(Input): @@ -1201,7 +1202,7 @@ class CommandInput(Input): """ with g_lock: self._win.erase() - self.addstr(self.help_message, common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(self.help_message, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) cursor_pos = self.pos + len(self.help_message) if len(self.help_message): self.addstr(' ') @@ -1276,7 +1277,7 @@ class VerticalSeparator(Win): def rewrite_line(self): with g_lock: - self._win.vline(0, 0, curses.ACS_VLINE, self.height, common.curses_color_pair(theme.COLOR_VERTICAL_SEPARATOR)) + self._win.vline(0, 0, curses.ACS_VLINE, self.height, to_curses_attr(get_theme().COLOR_VERTICAL_SEPARATOR)) self._refresh() def refresh(self): @@ -1284,14 +1285,14 @@ class VerticalSeparator(Win): self.rewrite_line() class RosterWin(Win): - color_show = {'xa':theme.COLOR_STATUS_XA, - 'none':theme.COLOR_STATUS_ONLINE, - '':theme.COLOR_STATUS_ONLINE, - 'available':theme.COLOR_STATUS_ONLINE, - 'dnd':theme.COLOR_STATUS_DND, - 'away':theme.COLOR_STATUS_AWAY, - 'chat':theme.COLOR_STATUS_CHAT, - 'unavailable':theme.COLOR_STATUS_UNAVAILABLE + color_show = {'xa':get_theme().COLOR_STATUS_XA, + 'none':get_theme().COLOR_STATUS_ONLINE, + '':get_theme().COLOR_STATUS_ONLINE, + 'available':get_theme().COLOR_STATUS_ONLINE, + 'dnd':get_theme().COLOR_STATUS_DND, + 'away':get_theme().COLOR_STATUS_AWAY, + 'chat':get_theme().COLOR_STATUS_CHAT, + 'unavailable':get_theme().COLOR_STATUS_UNAVAILABLE } def __init__(self): @@ -1388,22 +1389,22 @@ class RosterWin(Win): Draw the indicator that shows that the list is longer than what is displayed """ - self.addstr(y, self.width-5, '++++', common.curses_color_pair(theme.COLOR_MORE_INDICATOR)) + self.addstr(y, self.width-5, '++++', to_curses_attr(get_theme().COLOR_MORE_INDICATOR)) def draw_roster_information(self, roster): """ The header at the top """ self.addstr('Roster: %s/%s contacts' % (roster.get_nb_connected_contacts(), roster.get_contact_len())\ - , common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) - self.finish_line(theme.COLOR_INFORMATION_BAR) + , to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) def draw_group(self, y, group, colored): """ Draw a groupname on a line """ if colored: - self._win.attron(common.curses_color_pair(theme.COLOR_SELECTED_ROW)) + self._win.attron(to_curses_attr(get_theme().COLOR_SELECTED_ROW)) if group.folded: self.addstr(y, 0, '[+] ') else: @@ -1411,7 +1412,7 @@ class RosterWin(Win): contacts = " (%s/%s)" % (group.get_nb_connected_contacts(), len(group)) self.addstr(y, 4, group.name + contacts) if colored: - self._win.attroff(common.curses_color_pair(theme.COLOR_SELECTED_ROW)) + self._win.attroff(to_curses_attr(get_theme().COLOR_SELECTED_ROW)) self.finish_line() def draw_contact_line(self, y, contact, colored): @@ -1438,16 +1439,16 @@ class RosterWin(Win): else: display_name = '%s%s' % (contact.get_bare_jid(), nb,) self.addstr(y, 0, ' ') - self.addstr(" ", common.curses_color_pair(color)) + self.addstr(" ", to_curses_attr(color)) if resource: self.addstr(' [+]' if contact._folded else ' [-]') self.addstr(' ') if colored: - self.addstr(display_name, common.curses_color_pair(theme.COLOR_SELECTED_ROW)) + self.addstr(display_name, to_curses_attr(get_theme().COLOR_SELECTED_ROW)) else: self.addstr(display_name) if contact.get_ask() == 'asked': - self.addstr('?', common.curses_color_pair(theme.COLOR_HIGHLIGHT_NICK)) + self.addstr('?', to_curses_attr(get_theme().COLOR_HIGHLIGHT_NICK)) self.finish_line() def draw_resource_line(self, y, resource, colored): @@ -1455,9 +1456,9 @@ class RosterWin(Win): Draw a specific resource line """ color = RosterWin.color_show[resource.get_presence()] - self.addstr(y, 4, " ", common.curses_color_pair(color)) + self.addstr(y, 4, " ", to_curses_attr(color)) if colored: - self.addstr(y, 6, resource.get_jid().full, common.curses_color_pair(theme.COLOR_SELECTED_ROW)) + self.addstr(y, 6, resource.get_jid().full, to_curses_attr(get_theme().COLOR_SELECTED_ROW)) else: self.addstr(y, 6, resource.get_jid().full) self.finish_line() @@ -1482,12 +1483,12 @@ class ContactInfoWin(Win): presence = resource.get_presence() else: presence = 'unavailable' - self.addstr(0, 0, '%s (%s)'%(jid, presence,), common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) - self.finish_line(theme.COLOR_INFORMATION_BAR) + self.addstr(0, 0, '%s (%s)'%(jid, presence,), to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) self.addstr(1, 0, 'Subscription: %s' % (contact.get_subscription(),)) if contact.get_ask(): if contact.get_ask() == 'asked': - self.addstr(' Ask: %s' % (contact.get_ask(),), common.curses_color_pair(theme.COLOR_HIGHLIGHT_NICK)) + self.addstr(' Ask: %s' % (contact.get_ask(),), to_curses_attr(get_theme().COLOR_HIGHLIGHT_NICK)) else: self.addstr(' Ask: %s' % (contact.get_ask(),)) self.finish_line() @@ -1497,8 +1498,8 @@ class ContactInfoWin(Win): """ draw the group information """ - self.addstr(0, 0, group.name, common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) - self.finish_line(theme.COLOR_INFORMATION_BAR) + self.addstr(0, 0, group.name, to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) + self.finish_line(get_theme().COLOR_INFORMATION_BAR) def refresh(self, selected_row): log.debug('Refresh: %s'%self.__class__.__name__) @@ -1581,7 +1582,7 @@ class ListWin(Win): if not txt: continue if line is self.lines[self._selected_row]: - self.addstr(y, x, txt[:size], common.curses_color_pair(theme.COLOR_INFORMATION_BAR)) + self.addstr(y, x, txt[:size], to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) else: self.addstr(y, x, txt[:size]) x += size @@ -1659,7 +1660,7 @@ class ColumnHeaderWin(Win): txt = col size = self._columns_sizes[col] txt += ' ' * (size-len(txt)) - self.addstr(0, x, txt, common.curses_color_pair(theme.COLOR_COLUMN_HEADER)) + self.addstr(0, x, txt, to_curses_attr(get_theme().COLOR_COLUMN_HEADER)) x += size self._refresh() |