# Copyright (C) 2011-2012 Patrick Totzke # This file is released under the GNU GPL, version 3 or a later revision. # For further details see the COPYING file import os from .errors import ConfigError from .utils import read_config from ..utils import configobj as checks DEFAULTSPATH = os.path.join(os.path.dirname(__file__), '..', 'defaults') DUMMYDEFAULT = ('default',) * 6 class Theme: """Colour theme""" def __init__(self, path): """ :param path: path to theme file :type path: str :raises: :class:`~alot.settings.errors.ConfigError` """ self._spec = os.path.join(DEFAULTSPATH, 'theme.spec') self._config = read_config([path], self._spec, report_extra=True, checks={'align': checks.align_mode, 'widthtuple': checks.width_tuple, 'force_list': checks.force_list, 'attrtriple': checks.attr_triple}) self._colours = [1, 16, 256] # make sure every entry in 'order' lists have their own subsections threadline = self._config['search']['threadline'] for sec in self._config['search']: if sec.startswith('threadline'): tline = self._config['search'][sec] if tline['parts'] is not None: listed = set(tline['parts']) here = set(tline.sections) indefault = set(threadline.sections) diff = listed.difference(here.union(indefault)) if diff: msg = 'missing threadline parts: %s' % ', '.join(diff) raise ConfigError(msg) def get_attribute(self, colourmode, mode, name, part=None): """ returns requested attribute :param mode: ui-mode (e.g. `search`,`thread`...) :type mode: str :param name: of the atttribute :type name: str :param colourmode: colour mode; in [1, 16, 256] :type colourmode: int :rtype: urwid.AttrSpec """ thmble = self._config[mode][name] if part is not None: thmble = thmble[part] thmble = thmble or DUMMYDEFAULT return thmble[self._colours.index(colourmode)] def get_threadline_theming(self, thread, colourmode): """ look up how to display a Threadline wiidget in search mode for a given thread. :param thread: Thread to theme Threadline for :type thread: alot.db.thread.Thread :param colourmode: colourmode to use, one of 1,16,256. :type colourmode: int This will return a dict mapping :normal: to `urwid.AttrSpec`, :focus: to `urwid.AttrSpec`, :parts: to a list of strings indentifying subwidgets to be displayed in this order. Moreover, for every part listed this will map 'part' to a dict mapping :normal: to `urwid.AttrSpec`, :focus: to `urwid.AttrSpec`, :width: to a tuple indicating the width of the subpart. This is either `('fit', min, max)` to force the widget to be at least `min` and at most `max` characters wide, or `('weight', n)` which makes it share remaining space with other 'weight' parts. :alignment: where to place the content if shorter than the widget. This is either 'right', 'left' or 'center'. """ def pickcolour(triple): return triple[self._colours.index(colourmode)] def matches(sec, thread): if sec.get('tagged_with') is not None: if not set(sec['tagged_with']).issubset(thread.get_tags()): return False if sec.get('query') is not None: if not thread.matches(sec['query']): return False return True default = self._config['search']['threadline'] match = default candidates = self._config['search'].sections for candidatename in candidates: candidate = self._config['search'][candidatename] if (candidatename.startswith('threadline') and (not candidatename == 'threadline') and matches(candidate, thread)): match = candidate break # fill in values res = {} res['normal'] = pickcolour(match.get('normal') or default['normal']) res['focus'] = pickcolour(match.get('focus') or default['focus']) res['parts'] = match.get('parts') or default['parts'] for part in res['parts']: defaultsec = default.get(part) partsec = match.get(part) or {} def fill(key, fallback=None): pvalue = partsec.get(key) or defaultsec.get(key) return pvalue or fallback res[part] = {} res[part]['width'] = fill('width', ('fit', 0, 0)) res[part]['alignment'] = fill('alignment', 'right') res[part]['normal'] = pickcolour(fill('normal')) res[part]['focus'] = pickcolour(fill('focus')) return res def get_quote_theming(self, colourmode): """ Get a list of attributes to use for displaying successive levels of nested quotes in a message body. :param colourmode: colourmode to use, one of 1,16,256. :type colourmode: int """ idx = self._colours.index(colourmode) mainsect = self._config['thread']['quote'] ret = [] for sect in sorted(mainsect.sections): ret.append(mainsect[sect]['body'][idx]) return ret