266 lines
8.8 KiB
Python
266 lines
8.8 KiB
Python
# 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 = {}
|
|
|
|
table_256_to_16 = [
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
0, 4, 4, 4, 12, 12, 2, 6, 4, 4, 12, 12, 2, 2, 6, 4,
|
|
12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10,
|
|
10, 10, 10, 14, 1, 5, 4, 4, 12, 12, 3, 8, 4, 4, 12, 12,
|
|
2, 2, 6, 4, 12, 12, 2, 2, 2, 6, 12, 12, 10, 10, 10, 10,
|
|
14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 5, 4, 12, 12, 1, 1,
|
|
5, 4, 12, 12, 3, 3, 8, 4, 12, 12, 2, 2, 2, 6, 12, 12,
|
|
10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14, 1, 1, 1, 5,
|
|
12, 12, 1, 1, 1, 5, 12, 12, 1, 1, 1, 5, 12, 12, 3, 3,
|
|
3, 7, 12, 12, 10, 10, 10, 10, 14, 12, 10, 10, 10, 10, 10, 14,
|
|
9, 9, 9, 9, 13, 12, 9, 9, 9, 9, 13, 12, 9, 9, 9, 9,
|
|
13, 12, 9, 9, 9, 9, 13, 12, 11, 11, 11, 11, 7, 12, 10, 10,
|
|
10, 10, 10, 14, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13,
|
|
9, 9, 9, 9, 9, 13, 9, 9, 9, 9, 9, 13, 9, 9, 9, 9,
|
|
9, 13, 11, 11, 11, 11, 11, 15, 0, 0, 0, 0, 0, 0, 8, 8,
|
|
8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15
|
|
]
|
|
|
|
def color_256_to_16(color):
|
|
if color == -1:
|
|
return color
|
|
return table_256_to_16[color]
|
|
|
|
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
|
|
|
|
bold = False
|
|
if curses.COLORS != 256:
|
|
# We are not in a term supporting 256 colors, so we convert
|
|
# colors to numbers between -1 and 8
|
|
colors = (color_256_to_16(colors[0]), color_256_to_16(colors[1]))
|
|
if colors[0] >= 8:
|
|
colors = (colors[0] - 8, colors[1])
|
|
bold = True
|
|
if colors[1] >= 8:
|
|
colors = (colors[0], colors[1] - 8)
|
|
|
|
# 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
|
|
curses_pair = curses.color_pair(pair)
|
|
if len(color_tuple) == 3:
|
|
additional_val = color_tuple[2]
|
|
if 'b' in additional_val or bold is True:
|
|
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()
|