From 3ed25bdfce85e774b6992d32a112b69154d55f3e Mon Sep 17 00:00:00 2001 From: "louiz@4325f9fc-e183-4c21-96ce-0ab188b42d13" Date: Fri, 20 Aug 2010 20:55:42 +0000 Subject: Themes are working, need a little polishing and documentation --- src/gui.py | 60 ++++++++++----------------- src/room.py | 31 +++++++------- src/theme.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/user.py | 7 ++-- src/window.py | 87 +++++++++++++++++++++++--------------- 5 files changed, 225 insertions(+), 91 deletions(-) create mode 100644 src/theme.py diff --git a/src/gui.py b/src/gui.py index 8038243f..e59a97b0 100644 --- a/src/gui.py +++ b/src/gui.py @@ -31,6 +31,7 @@ import webbrowser from datetime import datetime import common +import theme from handler import Handler from config import config @@ -98,6 +99,7 @@ class Gui(object): 'nick': (self.command_nick, _("Usage: /nick \nNick: Change your nickname in the current room")), 'say': (self.command_say, _('Usage: /say \nSay: Just send the message. Useful if you want your message to begin with a "/"')), 'whois': (self.command_whois, _('Usage: /whois \nWhois: Request many informations about the user.')), + 'theme': (self.command_theme, _('Usage: /theme\nTheme: Reload the theme defined in the config file.')), } self.key_func = { @@ -193,35 +195,10 @@ class Gui(object): """ ncurses initialization """ - curses.start_color() + theme.init_colors() curses.noecho() curses.curs_set(0) - curses.use_default_colors() stdscr.keypad(True) - curses.init_pair(1, curses.COLOR_WHITE, - curses.COLOR_BLUE) - curses.init_pair(2, curses.COLOR_WHITE, -1) # Visitor - curses.init_pair(3, curses.COLOR_CYAN, -1) - curses.init_pair(4, curses.COLOR_RED, -1) # Admin - curses.init_pair(5, curses.COLOR_BLUE, -1) # Participant - curses.init_pair(6, curses.COLOR_CYAN, -1) - curses.init_pair(7, curses.COLOR_GREEN, -1) - curses.init_pair(8, curses.COLOR_MAGENTA, -1) - curses.init_pair(9, curses.COLOR_YELLOW, -1) - curses.init_pair(10, curses.COLOR_WHITE, - curses.COLOR_CYAN) # current room - curses.init_pair(11, curses.COLOR_WHITE, - curses.COLOR_BLUE) # normal room - curses.init_pair(12, curses.COLOR_WHITE, - curses.COLOR_MAGENTA) # new message room - curses.init_pair(13, curses.COLOR_WHITE, - curses.COLOR_RED) # highlight room - curses.init_pair(14, curses.COLOR_WHITE, - curses.COLOR_YELLOW) - curses.init_pair(15, curses.COLOR_WHITE, # new message in private room - curses.COLOR_GREEN) - curses.init_pair(16, curses.COLOR_YELLOW, - curses.COLOR_BLUE) def reset_curses(self): """ @@ -243,7 +220,7 @@ class Gui(object): """ Refresh everything """ - self.current_room().set_color_state(common.ROOM_STATE_CURRENT) + self.current_room().set_color_state(theme.COLOR_TAB_CURRENT) self.window.refresh(self.rooms) def join_room(self, room, nick): @@ -251,7 +228,7 @@ class Gui(object): join the specified room (muc), using the specified nick """ r = Room(room, nick, self.window) - self.current_room().set_color_state(11) + self.current_room().set_color_state(theme.COLOR_TAB_NORMAL) if self.current_room().nb == 0: self.rooms.append(r) else: @@ -305,15 +282,15 @@ class Gui(object): - A Muc with any new message """ for room in self.rooms: - if room.color_state == 15: + if room.color_state == theme.COLOR_TAB_PRIVATE: self.command_win('%s' % room.nb) return for room in self.rooms: - if room.color_state == 13: + if room.color_state == theme.COLOR_TAB_HIGHLIGHT: self.command_win('%s' % room.nb) return for room in self.rooms: - if room.color_state == 12: + if room.color_state == theme.COLOR_TAB_NEW_MESSAGE: self.command_win('%s' % room.nb) return @@ -321,20 +298,20 @@ class Gui(object): """ rotate the rooms list to the right """ - self.current_room().set_color_state(common.ROOM_STATE_NONE) + self.current_room().set_color_state(theme.COLOR_TAB_NORMAL) self.current_room().remove_line_separator() self.rooms.append(self.rooms.pop(0)) - self.current_room().set_color_state(common.ROOM_STATE_CURRENT) + self.current_room().set_color_state(theme.COLOR_TAB_CURRENT) self.refresh_window() def rotate_rooms_left(self, args=None): """ rotate the rooms list to the right """ - self.current_room().set_color_state(common.ROOM_STATE_NONE) + self.current_room().set_color_state(theme.COLOR_TAB_NORMAL) self.current_room().remove_line_separator() self.rooms.insert(0, self.rooms.pop()) - self.current_room().set_color_state(common.ROOM_STATE_CURRENT) + self.current_room().set_color_state(theme.COLOR_TAB_CURRENT) self.refresh_window() def scroll_page_down(self, args=None): @@ -495,7 +472,7 @@ class Gui(object): for child in xtag.getTags('status'): if child.getAttr('code') == '170': self.add_message_to_room(room, 'Warning: this room is publicly logged') - new_user.color = 2 + new_user.color = theme.COLOR_OWN_NICK else: change_nick = stanza.getStatusCode() == '303' kick = stanza.getStatusCode() == '307' @@ -674,6 +651,11 @@ class Gui(object): nickname = args[0] self.muc.request_vcard(room.name, nickname) + def command_theme(self, arg): + """ + """ + theme.reload_theme() + def command_win(self, arg): """ /win @@ -689,17 +671,17 @@ class Gui(object): return if self.current_room().nb == nb: return - self.current_room().set_color_state(common.ROOM_STATE_NONE) + self.current_room().set_color_state(theme.COLOR_TAB_NORMAL) self.current_room().remove_line_separator() start = self.current_room() self.rooms.append(self.rooms.pop(0)) while self.current_room().nb != nb: self.rooms.append(self.rooms.pop(0)) if self.current_room() == start: - self.current_room().set_color_state(common.ROOM_STATE_CURRENT) + self.current_room().set_color_state(theme.COLOR_TAB_CURRENT) self.refresh_window() return - self.current_room().set_color_state(common.ROOM_STATE_CURRENT) + self.current_room().set_color_state(theme.COLOR_TAB_CURRENT) self.refresh_window() def command_kick(self, arg): diff --git a/src/room.py b/src/room.py index 63003229..391f6f79 100644 --- a/src/room.py +++ b/src/room.py @@ -23,6 +23,7 @@ from logging import logger from message import Message import common +import theme class Room(object): """ @@ -32,7 +33,7 @@ class Room(object): self.jid = jid # used for a private chat. None if it's a MUC self.name = name self.own_nick = nick - self.color_state = common.ROOM_STATE_NONE # color used in RoomInfo + self.color_state = theme.COLOR_TAB_NORMAL # color used in RoomInfo self.nb = Room.number # number used in RoomInfo Room.number += 1 self.joined = False # false until self presence is received @@ -68,25 +69,25 @@ class Room(object): """ Set the tab color and returns the txt color """ - color = None + color = theme.COLOR_NORMAL_TEXT if not time and nickname and nickname.encode('utf-8') != self.own_nick and self.joined: # do the highlight try: if self.own_nick in txt.encode('utf-8'): - self.set_color_state(common.ROOM_STATE_HL) - color = 4 + self.set_color_state(theme.COLOR_TAB_HIGHLIGHT) + color = theme.COLOR_HIGHLIGHT_TEXT except UnicodeDecodeError: try: if self.own_nick in txt: - self.set_color_state(common.ROOM_STATE_HL) - color = 4 + self.set_color_state(theme.COLOR_TAB_HIGHLIGHT) + color = theme.COLOR_HIGHLIGHT_TEXT except: pass else: highlight_words = config.get('highlight_on', '').split(':') for word in highlight_words: if word.lower() in txt.lower() and word != '': - self.set_color_state(common.ROOM_STATE_HL) - color = 4 + self.set_color_state(theme.COLOR_TAB_HIGHLIGHT) + color = theme.COLOR_HIGHLIGHT_TEXT break return color @@ -100,20 +101,20 @@ class Room(object): user = self.get_user_by_name(nickname) if nickname is not None else None if user: user.set_last_talked(datetime.now()) - color = None + color = theme.COLOR_NORMAL_TEXT if not time and nickname and\ nickname != self.own_nick and\ - self.color_state != common.ROOM_STATE_CURRENT: - if not self.jid and self.color_state != common.ROOM_STATE_HL: - self.set_color_state(common.ROOM_STATE_MESSAGE) + self.color_state != theme.COLOR_TAB_CURRENT: + if not self.jid and self.color_state != theme.COLOR_TAB_HIGHLIGHT: + self.set_color_state(theme.COLOR_TAB_NEW_MESSAGE) elif self.jid: - self.set_color_state(common.ROOM_STATE_PRIVATE) + self.set_color_state(theme.COLOR_TAB_PRIVATE) if not nickname: - color = 8 + color = theme.COLOR_INFORMATION_TEXT else: color = self.do_highlight(txt, time, nickname) if time: # History messages are colored to be distinguished - color = 8 + color = theme.COLOR_INFORMATION_TEXT time = time if time is not None else datetime.now() self.messages.append(Message(txt, time, nickname, user, color)) diff --git a/src/theme.py b/src/theme.py new file mode 100644 index 00000000..1748e4a3 --- /dev/null +++ b/src/theme.py @@ -0,0 +1,131 @@ +# -*- coding:utf-8 -*- +# +# Copyright 2010 Le Coz Florent +# +# This file is part of Poezio. +# +# Poezio is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# Poezio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Poezio. If not, see . + +""" +Define the variables (colors and some other stuff) that are +used when drawing the interface (mainly colors) +""" + +import curses +import inspect +import imp +from config import config + +## Define the default colors +## Do not change these colors, create a theme file instead. + +# Message text color +COLOR_NORMAL_TEXT = 0 +COLOR_INFORMATION_TEXT = 76 +COLOR_HIGHLIGHT_TEXT = 77 + +# User list color +COLOR_USER_VISITOR = 78 +COLOR_USER_PARTICIPANT = 73 +COLOR_USER_NONE = 80 +COLOR_USER_MODERATOR = 77 + +# Separators +COLOR_VERTICAL_SEPARATOR = 73 +COLOR_NEW_TEXT_SEPARATOR = 75 + +# Time +COLOR_TIME_SEPARATOR = 79 +COLOR_TIME_BRACKETS = 74 +COLOR_TIME_NUMBERS = 0 + +# Tabs +COLOR_TAB_NORMAL = 15 +COLOR_TAB_CURRENT = 24 +COLOR_TAB_NEW_MESSAGE = 42 +COLOR_TAB_HIGHLIGHT = 51 +COLOR_TAB_PRIVATE = 33 + +# Nickname colors +LIST_COLOR_NICKNAMES = [ + 73, 74, 75, 76, 77, 78, 79 + ] +COLOR_OWN_NICK = 72 + +# Status color +COLOR_STATUS_XA = 40 +COLOR_STATUS_NONE = 0 +COLOR_STATUS_DND = 50 +COLOR_STATUS_AWAY = 70 +COLOR_STATUS_CHAT = 30 + +# Bars +COLOR_INFORMATION_BAR = 15 +COLOR_TOPIC_BAR = 15 +COLOR_PRIVATE_ROOM_BAR = 33 +COLOR_SCROLLABLE_NUMBER = 16 + +def init_colors(): + """ + Initilization of all the available ncurses colors + """ + curses.start_color() + curses.use_default_colors() + colors_list = [ + curses.COLOR_BLACK, + curses.COLOR_BLUE, + curses.COLOR_CYAN, + curses.COLOR_GREEN, + curses.COLOR_MAGENTA, + curses.COLOR_RED, + curses.COLOR_WHITE, + curses.COLOR_YELLOW, + -1 + ] + cpt = 0 + for i in colors_list: + for y in colors_list: + curses.init_pair(cpt, y, i) + cpt += 1 + reload_theme() + +def reload_theme(): + current_module = __import__(inspect.getmodulename(__file__)) + path = config.get('theme_file', '../default.theme') + try: + theme = imp.load_source('theme', path) + except: + return + for var in dir(theme): + if var.startswith('COLOR_'): + 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() + for i in xrange(80): + 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/user.py b/src/user.py index 5dbe77ce..2959a76c 100644 --- a/src/user.py +++ b/src/user.py @@ -16,10 +16,11 @@ # You should have received a copy of the GNU General Public License # along with Poezio. If not, see . -from random import randrange +from random import randrange, choice from config import config from datetime import timedelta, datetime import curses +import theme class User(object): """ @@ -29,7 +30,7 @@ class User(object): self.last_talked = None self.update(affiliation, show, status, role) self.change_nick(nick) - self.color = randrange(3, 10) # assign a random color + self.color = choice(theme.LIST_COLOR_NICKNAMES) def update(self, affiliation, show, status, role): self.affiliation = affiliation @@ -60,5 +61,3 @@ class User(object): def __repr__(self): return ">%s<" % (self.nick.decode('utf-8')) - # return ""\ - # % (self.nick.decode('utf-8'), self.show, type(self.show), self.status, self.affiliation) diff --git a/src/window.py b/src/window.py index 6aa44899..584f2f8b 100644 --- a/src/window.py +++ b/src/window.py @@ -29,6 +29,7 @@ from config import config from threading import Lock from message import Line +import theme g_lock = Lock() @@ -59,16 +60,16 @@ class UserList(Win): def __init__(self, height, width, y, x, parent_win, visible): Win.__init__(self, height, width, y, x, parent_win) self.visible = visible - self.color_role = {'moderator': 4, - 'participant':5, - 'visitor':2, - 'none':3 + self.color_role = {'moderator': theme.COLOR_USER_MODERATOR, + 'participant':theme.COLOR_USER_PARTICIPANT, + 'visitor':theme.COLOR_USER_VISITOR, + 'none':theme.COLOR_USER_NONE } - self.color_show = {'xa':12, - 'None':8, - 'dnd':13, - 'away':14, - 'chat':15 + self.color_show = {'xa':theme.COLOR_STATUS_XA, + 'None':theme.COLOR_STATUS_NONE, + 'dnd':theme.COLOR_STATUS_DND, + 'away':theme.COLOR_STATUS_AWAY, + 'chat':theme.COLOR_STATUS_CHAT } def refresh(self, users): @@ -95,11 +96,11 @@ class UserList(Win): try: role_col = self.color_role[user.role] except KeyError: - role_col = 5 + role_col = theme.COLOR_USER_NONE try: show_col = self.color_show[user.show] except KeyError: - show_col = 8 + show_col = theme.COLOR_STATUS_NONE self.win.attron(curses.color_pair(show_col)) self.win.addnstr(y, 0, " ", 1) self.win.attroff(curses.color_pair(show_col)) @@ -136,10 +137,10 @@ class Topic(Win): self.win.erase() if not jid: try: - self.win.addstr(0, 0, topic, curses.color_pair(1)) + self.win.addstr(0, 0, topic, curses.color_pair(theme.COLOR_TOPIC_BAR)) while True: try: - self.win.addch(' ', curses.color_pair(1)) + self.win.addch(' ', curses.color_pair(theme.COLOR_TOPIC_BAR)) except: break except: @@ -149,7 +150,7 @@ class Topic(Win): nick = '/'.join(jid.split('/')[1:]) topic = _('%(nick)s from room %(room)s' % {'nick': nick, 'room':room}) self.win.addnstr(0, 0, topic.encode('utf-8') + " "*(self.width-len(topic)), self.width-1 - , curses.color_pair(15)) + , curses.color_pair(theme.COLOR_PRIVATE_ROOM_BAR)) self.win.refresh() g_lock.release() @@ -169,7 +170,7 @@ class RoomInfo(Win): down """ if current_room.pos > 0: - self.win.addstr(' -PLUS(%s)-' % current_room.pos, curses.color_pair(16) | curses.A_BOLD) + self.win.addstr(' -PLUS(%s)-' % current_room.pos, curses.color_pair(theme.COLOR_SCROLLABLE_NUMBER) | curses.A_BOLD) def refresh(self, rooms, current): if not self.visible: @@ -179,28 +180,28 @@ class RoomInfo(Win): g_lock.acquire() self.win.erase() self.win.addnstr(0, 0, "[", self.width - ,curses.color_pair(1)) + ,curses.color_pair(theme.COLOR_INFORMATION_BAR)) sorted_rooms = sorted(rooms, compare_room) for room in sorted_rooms: color = room.color_state try: self.win.addstr("%s" % str(room.nb), curses.color_pair(color)) - self.win.addstr(u"|".encode('utf-8'), curses.color_pair(1)) + self.win.addstr(u"|".encode('utf-8'), curses.color_pair(theme.COLOR_INFORMATION_BAR)) except: # end of line break (y, x) = self.win.getyx() try: - self.win.addstr(y, x-1, '] '+ current.name, curses.color_pair(1)) + self.win.addstr(y, x-1, '] '+ current.name, curses.color_pair(theme.COLOR_INFORMATION_BAR)) except: try: - self.win.addstr(y, x-1, '] '+ current.name.encode('utf-8'), curses.color_pair(1)) + self.win.addstr(y, x-1, '] '+ current.name.encode('utf-8'), curses.color_pair(theme.COLOR_INFORMATION_BAR)) except: pass pass self.print_scroll_position(current) while True: try: - self.win.addstr(' ', curses.color_pair(1)) + self.win.addstr(' ', curses.color_pair(theme.COLOR_INFORMATION_BAR)) except: break self.win.refresh() @@ -315,9 +316,9 @@ class TextWin(Win): def write_line_separator(self): """ """ - self.win.attron(curses.color_pair(7)) + self.win.attron(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) self.win.addstr(' -'*(self.width/2)) - self.win.attroff(curses.color_pair(7)) + self.win.attroff(curses.color_pair(theme.COLOR_NEW_TEXT_SEPARATOR)) def write_text(self, y, x, txt, color): """ @@ -349,15 +350,35 @@ class TextWin(Win): """ Write the date on the yth line of the window """ - self.win.addnstr('['+time.strftime("%H"), 3) - self.win.attron(curses.color_pair(9)) + self.win.attron(curses.color_pair(theme.COLOR_TIME_BRACKETS)) + self.win.addnstr('[', 1) + self.win.attroff(curses.color_pair(theme.COLOR_TIME_BRACKETS)) + + self.win.attron(curses.color_pair(theme.COLOR_TIME_NUMBERS)) + self.win.addnstr(time.strftime("%H"), 2) + self.win.attroff(curses.color_pair(theme.COLOR_TIME_NUMBERS)) + + self.win.attron(curses.color_pair(theme.COLOR_TIME_SEPARATOR)) self.win.addnstr(':', 1) - self.win.attroff(curses.color_pair(9)) - self.win.addnstr(time.strftime('%M'), 2) - self.win.attron(curses.color_pair(9)) + self.win.attroff(curses.color_pair(theme.COLOR_TIME_SEPARATOR)) + + self.win.attron(curses.color_pair(theme.COLOR_TIME_NUMBERS)) + self.win.addnstr(time.strftime("%M"), 2) + self.win.attroff(curses.color_pair(theme.COLOR_TIME_NUMBERS)) + + self.win.attron(curses.color_pair(theme.COLOR_TIME_SEPARATOR)) self.win.addnstr(':', 1) - self.win.attroff(curses.color_pair(9)) - self.win.addnstr(time.strftime('%S') + "] ", 4) + self.win.attroff(curses.color_pair(theme.COLOR_TIME_SEPARATOR)) + + self.win.attron(curses.color_pair(theme.COLOR_TIME_NUMBERS)) + self.win.addnstr(time.strftime('%S'), 2) + self.win.attroff(curses.color_pair(theme.COLOR_TIME_NUMBERS)) + + self.win.attron(curses.color_pair(theme.COLOR_TIME_BRACKETS)) + self.win.addstr(']') + self.win.attroff(curses.color_pair(theme.COLOR_TIME_BRACKETS)) + + self.win.addstr(' ') def resize(self, height, width, y, x, stdscr, visible): self.visible = visible @@ -726,9 +747,9 @@ class Window(object): else: visible = True if visible: - stdscr.attron(curses.color_pair(5)) + stdscr.attron(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) stdscr.vline(1, 9*(self.width/10), curses.ACS_VLINE, self.height-2) - stdscr.attroff(curses.color_pair(5)) + stdscr.attroff(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) self.user_win = UserList(self.height-3, (self.width/10)-1, 1, 9*(self.width/10)+1, stdscr, visible) self.topic_win = Topic(1, self.width, 0, 0, stdscr, visible) self.info_win = RoomInfo(1, self.width, self.height-2, 0, stdscr, visible) @@ -745,9 +766,9 @@ class Window(object): else: visible = True if visible: - stdscr.attron(curses.color_pair(5)) + stdscr.attron(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) stdscr.vline(1, 9*(self.width/10), curses.ACS_VLINE, self.height-2) - stdscr.attroff(curses.color_pair(5)) + stdscr.attroff(curses.color_pair(theme.COLOR_VERTICAL_SEPARATOR)) text_width = (self.width/10)*9; self.topic_win.resize(1, self.width, 0, 0, stdscr, visible) self.info_win.resize(1, self.width, self.height-2, 0, stdscr, visible) -- cgit v1.2.3