summaryrefslogtreecommitdiff
path: root/src/theming.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/theming.py')
-rw-r--r--src/theming.py232
1 files changed, 232 insertions, 0 deletions
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()